xref: /5.5.2/subjson/subdoc/match.cc (revision cfc64675)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3*     Copyright 2015 Couchbase, Inc
4*
5*   Licensed under the Apache License, Version 2.0 (the "License");
6*   you may not use this file except in compliance with the License.
7*   You may obtain a copy of the License at
8*
9*       http://www.apache.org/licenses/LICENSE-2.0
10*
11*   Unless required by applicable law or agreed to in writing, software
12*   distributed under the License is distributed on an "AS IS" BASIS,
13*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*   See the License for the specific language governing permissions and
15*   limitations under the License.
16*/
17
18/* This file does the brunt of iterating through a buffer and determining the
19 * offset where a given path may begin or end.. */
20
21#define INCLUDE_JSONSL_SRC
22#include "jsonsl_header.h"
23#include "subdoc-api.h"
24#include "match.h"
25#include "uescape.h"
26#include "validate.h"
27#include <assert.h>
28
29using namespace Subdoc;
30
31namespace {
32struct ParseContext {
33    ParseContext(Match *match, Path::CompInfo *jpr) : jpr(jpr), match(match){
34    }
35
36    Path::CompInfo* jpr;
37
38    // Internal pointer/length pair used to hold the pointer of either the
39    // current key, or the unique array value (if in "unique" mode).
40    // These are actually accessed by get_hk()
41    const char *hkbuf_ = NULL;
42    unsigned hklen_ = 0;
43
44    // Internal flag set to true if the current key is actually found in
45    // 'hkstr' (in which case `hkstr` contains the properly unescaped version
46    // of the key).
47    bool hkesc = false;
48
49    Match *match;
50
51    // Contains the unescaped key (if the original contained u-escapes)
52    std::string hkstr;
53
54    void set_unique_begin(const jsonsl_state_st *, const jsonsl_char_t *at) {
55        hkbuf_ = at;
56    }
57
58    const char *get_unique() const { return hkbuf_; }
59
60    void set_hk_begin(const jsonsl_state_st *, const jsonsl_char_t *at) {
61        hkbuf_ = at + 1;
62    }
63
64    void set_hk_end(const jsonsl_state_st *state) {
65        hklen_ = state->pos_cur - (state->pos_begin + 1);
66
67        if (!state->nescapes) {
68            hkesc = false;
69        } else {
70            hkstr.clear();
71            hkesc = true;
72            UescapeConverter::convert(hkbuf_, hklen_, hkstr);
73        }
74    }
75    const char *get_hk(size_t& nkey) const {
76        if (!hkesc) {
77            nkey = hklen_;
78            return hkbuf_;
79        } else {
80            nkey = hkstr.size();
81            return hkstr.c_str();
82        }
83    }
84    void set_hk_loc(Loc& loc) {
85        loc.at = hkbuf_ - 1;
86        loc.length = hklen_ + 2;
87    }
88};
89}
90
91static void push_callback(jsonsl_t jsn,jsonsl_action_t, struct jsonsl_state_st *, const jsonsl_char_t *);
92static void pop_callback(jsonsl_t jsn,jsonsl_action_t, struct jsonsl_state_st *, const jsonsl_char_t *);
93
94static ParseContext *get_ctx(const jsonsl_t jsn)
95{
96    return (ParseContext *)jsn->data;
97}
98
99static int
100err_callback(jsonsl_t jsn, jsonsl_error_t err, struct jsonsl_state_st *state,
101    jsonsl_char_t *at)
102{
103    ParseContext *ctx = get_ctx(jsn);
104    ctx->match->status = err;
105    (void)state; (void)at;
106    return 0;
107}
108
109// Updates the state to reflect information on the parent. This is used by
110// the subdoc code for insertion/deletion operations.
111static void
112update_possible(ParseContext *ctx, const struct jsonsl_state_st *state, const char *at)
113{
114    ctx->match->loc_deepest.at = at;
115    ctx->match->match_level = state->level;
116}
117
118static void
119unique_callback(jsonsl_t jsn, jsonsl_action_t action, struct jsonsl_state_st *st,
120    const jsonsl_char_t *at)
121{
122    ParseContext *ctx = get_ctx(jsn);
123    Match *m = ctx->match;
124    int rv;
125    size_t slen;
126
127    if (action == JSONSL_ACTION_PUSH) {
128        /* abs. beginning of token */
129        ctx->set_unique_begin(st, at);
130        return;
131    }
132
133    if (st->mres == JSONSL_MATCH_COMPLETE) {
134        // Popping the parent level!
135        jsn->action_callback_POP = pop_callback;
136        jsn->action_callback_PUSH = push_callback;
137
138        // Prevent descent into parser again
139        jsn->max_callback_level = st->level+1;
140        pop_callback(jsn, action, st, at);
141        return;
142    }
143
144    slen = st->pos_cur - st->pos_begin;
145
146    assert(st->level == m->match_level + 1);
147
148    if (st->type == JSONSL_T_STRING) {
149        slen++;
150
151        if (m->ensure_unique.length < 2) {
152            /* not a string */
153            return;
154        } else if (slen != m->ensure_unique.length) {
155            return; /* Length mismatch */
156        }
157
158        rv = strncmp(ctx->get_unique() + 1, m->ensure_unique.at + 1, slen-2);
159
160    } else if (st->type == JSONSL_T_SPECIAL) {
161        if (m->ensure_unique.length != slen) {
162            return;
163        } else if (slen != m->ensure_unique.length) {
164            return;
165        }
166        rv = strncmp(ctx->get_unique(), m->ensure_unique.at, slen);
167    } else {
168        /* We can't reliably indicate uniqueness for non-primitives */
169        m->matchres = JSONSL_MATCH_TYPE_MISMATCH;
170        jsonsl_stop(jsn);
171        return;
172    }
173
174    if (rv == 0) {
175        m->unique_item_found = 1;
176        jsonsl_stop(jsn);
177    }
178}
179
180/* Make code a bit more readable */
181#define M_POSSIBLE JSONSL_MATCH_POSSIBLE
182#define M_NOMATCH JSONSL_MATCH_NOMATCH
183#define M_COMPLETE JSONSL_MATCH_COMPLETE
184#define IS_CONTAINER JSONSL_STATE_IS_CONTAINER
185
186
187
188static void
189push_callback(jsonsl_t jsn, jsonsl_action_t action, struct jsonsl_state_st *st,
190    const jsonsl_char_t *at)
191{
192    ParseContext *ctx = get_ctx(jsn);
193    Match *m = ctx->match;
194    struct jsonsl_state_st *parent = jsonsl_last_state(jsn, st);
195    unsigned prtype = parent ? parent->type : JSONSL_T_UNKNOWN;
196
197    if (st->type == JSONSL_T_HKEY) {
198        ctx->set_hk_begin(st, at);
199        return;
200    }
201
202    // If the parent is a match candidate
203    if (parent == NULL || parent->mres == M_POSSIBLE) {
204        // Key is either the array offset or the dictionary key.
205        size_t nkey;
206        const char *key;
207        unsigned prlevel = parent ? parent->level : 0 ;
208        if (prtype == JSONSL_T_OBJECT) {
209            key = ctx->get_hk(nkey);
210        } else if (prtype == JSONSL_T_LIST) {
211            nkey = static_cast<size_t>(parent->nelem - 1);
212            key = NULL;
213        } else {
214            nkey = 0;
215            key = NULL;
216        }
217
218        /* Run the match */
219        st->mres = jsonsl_jpr_match(ctx->jpr, prtype, prlevel, key, nkey);
220
221        // jsonsl's jpr is a bit laxer here. but in our case
222        // it's impossible for a primitive to result in a "possible" result,
223        // since a partial result should always mean a container
224        if (st->mres == M_POSSIBLE && IS_CONTAINER(st) == 0) {
225            st->mres = JSONSL_MATCH_TYPE_MISMATCH;
226        }
227
228        if (st->mres == JSONSL_MATCH_COMPLETE) {
229            m->matchres = JSONSL_MATCH_COMPLETE;
230            m->type = st->type;
231            update_possible(ctx, st, at);
232
233            if (prtype == JSONSL_T_OBJECT) {
234                m->has_key = true;
235
236                // The beginning of the key (for "subdoc" purposes) actually
237                // _includes_ the opening and closing quotes
238                ctx->set_hk_loc(m->loc_key);
239
240                // I'm not sure if it's used.
241                m->position = static_cast<unsigned>((parent->nelem - 1) / 2);
242            } else if (prtype == JSONSL_T_LIST) {
243
244                // Array doesn't have a key
245                m->has_key = 0;
246                // array[n]
247                m->position = static_cast<unsigned>(parent->nelem - 1);
248            }
249
250            if (m->ensure_unique.at) {
251                if (st->type != JSONSL_T_LIST) {
252                    // Can't check "uniquness" in an array!
253                    m->matchres = JSONSL_MATCH_TYPE_MISMATCH;
254                    jsonsl_stop(jsn);
255                    return;
256                } else {
257                    // Set up callbacks for string comparison. This will
258                    // be invoked for each push/pop combination.
259                    jsn->action_callback_POP = unique_callback;
260                    jsn->action_callback_PUSH = unique_callback;
261                    jsn->max_callback_level = st->level + 2;
262                }
263            }
264
265        } else if (st->mres == JSONSL_MATCH_NOMATCH) {
266            // Can't have a match on this tree. Ignore subsequent callbacks here.
267            st->ignore_callback = 1;
268
269        } else if (st->mres == JSONSL_MATCH_POSSIBLE) {
270            // Update our depth thus far
271            update_possible(ctx, st, at);
272        } else if (st->mres == JSONSL_MATCH_TYPE_MISMATCH) {
273            st->ignore_callback = 1;
274            m->matchres = JSONSL_MATCH_TYPE_MISMATCH;
275        }
276    }
277    (void)action; /* always push */
278}
279
280/*
281 * This callback is only invoked if GET_FOLLOWING_SIBLINGS is the option
282 * mode.
283 */
284static void
285next_sibling_push_callback(jsonsl_t jsn, jsonsl_action_t action,
286    struct jsonsl_state_st *state, const jsonsl_char_t *at)
287{
288    const jsonsl_state_st *parent;
289    ParseContext *ctx = get_ctx(jsn);
290    Match *m = ctx->match;
291
292    if (action == JSONSL_ACTION_POP) {
293        // We can get a POP callback before a PUSH callback if the match
294        // was the last child in the parent. In this case, we are being
295        // invoked on a child, not a sibling.
296        assert(state->level == m->match_level - 1);
297        parent = state;
298
299    } else {
300        parent = jsonsl_last_state(jsn, state);
301    }
302
303    assert(ctx->match->matchres == JSONSL_MATCH_COMPLETE);
304    assert(ctx->match->extra_options == Match::GET_FOLLOWING_SIBLINGS);
305
306    if (parent != NULL && parent->mres == JSONSL_MATCH_POSSIBLE) {
307        /* Is the immediate parent! */
308        if (parent->type == JSONSL_T_OBJECT) {
309            unsigned nobj_elem = static_cast<unsigned>(parent->nelem);
310            if (action == JSONSL_ACTION_PUSH) {
311                assert(state->type == JSONSL_T_HKEY);
312                nobj_elem++;
313            }
314            m->num_siblings = static_cast<unsigned>(nobj_elem / 2);
315
316        } else if (parent->type == JSONSL_T_LIST) {
317            m->num_siblings = static_cast<unsigned>(parent->nelem);
318        }
319        m->num_siblings--;
320    }
321    jsonsl_stop(jsn);
322}
323
324static void
325pop_callback(jsonsl_t jsn, jsonsl_action_t, struct jsonsl_state_st *state,
326    const jsonsl_char_t *)
327{
328    ParseContext *ctx = get_ctx(jsn);
329    Match *m = ctx->match;
330
331    if (state->type == JSONSL_T_HKEY) {
332        // All we care about is recording the length of the key. We'll use
333        // this later on when matching (in the PUSH callback of a new element)
334        ctx->set_hk_end(state);
335        return;
336    }
337
338    if (state->mres == JSONSL_MATCH_COMPLETE) {
339        // This is the matched element. Record the end location of the
340        // match.
341        m->loc_deepest.length = jsn->pos - state->pos_begin;
342        m->immediate_parent_found = 1;
343        m->num_children = state->nelem;
344
345        if (state->type != JSONSL_T_SPECIAL) {
346            m->loc_deepest.length++; /* Include the terminating token */
347        } else {
348            m->sflags = state->special_flags;
349        }
350
351        if (m->get_last) {
352            if (state->type != JSONSL_T_LIST) {
353                // Not a list!
354                m->matchres = JSONSL_MATCH_TYPE_MISMATCH;
355                jsonsl_stop(jsn);
356                return;
357
358            } else if (!state->nelem) {
359                // List is empty
360                m->matchres = JSONSL_MATCH_UNKNOWN;
361                jsonsl_stop(jsn);
362                return;
363            }
364
365            // Transpose the match
366            const jsonsl_state_st *child = jsonsl_last_child(jsn, state);
367
368            // For containers, pos does not include the terminating token
369            size_t child_endpos = jsn->pos;
370
371            m->loc_deepest.length = child_endpos - child->pos_begin;
372            m->loc_deepest.at = jsn->base + child->pos_begin;
373
374            // Remove trailing whitespace. This is because the child is
375            // deemed to end one character before the parent does, however
376            // there may be whitespace. This is usually OK but confuses tests.
377            while (isspace(m->loc_deepest.at[m->loc_deepest.length-1])) {
378                --m->loc_deepest.length;
379            }
380
381            m->match_level = child->level;
382            m->sflags = child->special_flags;
383            m->type = child->type;
384            m->num_children = child->nelem;
385
386            // Parent is an array, get pos/sibling information directly from it
387            m->num_siblings = state->nelem - 1;
388            m->position = m->num_siblings;
389            m->has_key = 0;
390            jsonsl_stop(jsn);
391
392        } else {
393            // Maybe we need another round to check for siblings?
394            if (m->extra_options == Match::GET_FOLLOWING_SIBLINGS) {
395                jsn->action_callback_POP = NULL;
396                jsn->action_callback_PUSH = NULL;
397
398                // We only care for this on push, but in the off chance where
399                // this is the final element in the parent *anyway*, the parsing
400                // itself should just stop.
401                jsn->action_callback = next_sibling_push_callback;
402            } else {
403                jsonsl_stop(jsn);
404            }
405        }
406
407        return;
408    }
409
410    // TODO: Determine if all these checks are necessary, since if everything
411    // is correct, parsing should stop as soon as the deepest match is
412    // terminated
413
414    // Not a container and not a full match. We can't do anything here.
415    if (!JSONSL_STATE_IS_CONTAINER(state)) {
416        return;
417    }
418
419    // This isn't part of the match tree, so we just ignore it
420    if (state->mres != JSONSL_MATCH_POSSIBLE) {
421        return;
422    }
423
424    // If we haven't bailed out yet, it means this is the deepest most parent
425    // match. This can either be the actual parent of the match (if a match
426    // is found), or the deepest existing parent which exists in the document.
427    // The latter information is needed for MKDIR_P semantics.
428    // Record the length of the parent
429    m->loc_deepest.length = jsn->pos - state->pos_begin;
430    m->loc_deepest.length++; // Always a container. Use trailing token.
431
432    // Record the number of "siblings" in the container. This is used by
433    // insertion/removal operations to determine comma placement, if any,
434    // before or after the current match.
435    if (state->type == JSONSL_T_OBJECT) {
436        m->num_siblings = static_cast<unsigned>(state->nelem / 2);
437    } else {
438        m->num_siblings = static_cast<unsigned>(state->nelem);
439    }
440
441    m->type = state->type;
442
443    // Do some verification to ensure that the parent type matches its level.
444    // JPR won't do this for us, as it only analyzes one element at a time.
445    // TODO: Impelement this directly in jsonsl, to allow JPR to accept the
446    // child type!
447    auto *next_comp = &ctx->jpr->components[state->level];
448    if (next_comp->is_arridx) {
449        if (state->type != JSONSL_T_LIST) {
450            m->matchres = JSONSL_MATCH_TYPE_MISMATCH;
451        }
452    } else {
453        if (state->type != JSONSL_T_OBJECT) {
454            m->matchres = JSONSL_MATCH_TYPE_MISMATCH;
455        }
456    }
457
458    // Is this the actual parent of the match?
459    if (state->level == ctx->jpr->ncomponents-1) {
460        m->immediate_parent_found = 1;
461    }
462
463    // Since this is the deepest parent we've found, we exit here!
464    jsonsl_stop(jsn);
465}
466
467int
468Match::exec_match_simple(const char *value, size_t nvalue,
469    const Path::CompInfo *jpr, jsonsl_t jsn)
470{
471    ParseContext ctx(this, const_cast<Path::CompInfo*>(jpr));
472    status = JSONSL_ERROR_SUCCESS;
473
474    jsonsl_enable_all_callbacks(jsn);
475    jsn->action_callback_PUSH = push_callback;
476    jsn->action_callback_POP = pop_callback;
477    jsn->error_callback = err_callback;
478    jsn->max_callback_level = ctx.jpr->ncomponents + 1;
479    jsn->data = &ctx;
480
481    jsonsl_feed(jsn, value, nvalue);
482    jsonsl_reset(jsn);
483    return 0;
484}
485
486int
487Match::exec_match_negix(const char *value, size_t nvalue, const Path *pth,
488    jsonsl_t jsn)
489{
490    /* First component to scan in next iteration */
491    size_t cur_start = 1;
492
493    /* For levels, compensate this much for the match level */
494    size_t level_offset = 0;
495
496    const Path::CompInfo& orig = *pth;
497    Path::Component comp_s[Limits::PATH_COMPONENTS_ALLOC];
498
499    /* Last length of the subdocument */
500    size_t last_len = nvalue;
501    /* Pointer to the beginning of the current subdocument */
502    const char *last_start = value;
503    std::copy(orig.begin(), orig.end(), comp_s);
504
505    while (cur_start < orig.size()) {
506        size_t ii;
507        int rv, is_last_neg = 0;
508
509        /* If the last match was not a list or an object,
510         * but we still need to descend, then throw an error now. */
511        if (last_start != value && type != JSONSL_T_LIST && type != JSONSL_T_OBJECT) {
512            matchres = JSONSL_MATCH_TYPE_MISMATCH;
513            return 0;
514        }
515
516        for (ii = cur_start; ii < orig.size(); ii++) {
517            /* Seek to the next negative index */
518            if (orig[ii].is_neg) {
519                /* Convert this to the first array index; then switch it
520                 * around to extract parent information */
521                is_last_neg = 1;
522                break;
523            }
524        }
525
526        // XXX: The next few lines could probably be more concise, but I find
527        // it better to explain what's going on.
528
529        // Set the components to use for the current match. These are the
530        // components between the termination of the *last* match and the
531        // next negative index (or the end!)
532        Path::Component *tmpcomps = &comp_s[cur_start];
533        size_t ntmpcomp = ii - cur_start;
534
535        // We need the root element here as well, so add one more component
536        // at the beginning
537        tmpcomps--;
538        ntmpcomp++;
539
540        // Declare the component info itself, this is fed as the path
541        // to exec_match
542        Path::CompInfo tmp(tmpcomps, ntmpcomp);
543
544        tmp[0].ptype = JSONSL_PATH_ROOT;
545
546        /* Clear the match. There's no good way to preserve info here,
547         * unfortunately. */
548        clear();
549
550        // Transpose array's last element as the match itself
551        if (is_last_neg) {
552            get_last = 1;
553        }
554
555        rv = exec_match_simple(last_start, last_len, &tmp, jsn);
556        match_level += level_offset;
557        if (level_offset) {
558            // 'nth iteration
559            match_level--;
560        }
561
562        level_offset = match_level;
563
564        if (rv != 0) { /* error */
565            return rv;
566        } else if (status != JSONSL_ERROR_SUCCESS) {
567            return 0;
568        } else if (matchres != JSONSL_MATCH_COMPLETE) {
569            return 0;
570        }
571
572        last_start = loc_deepest.at;
573        last_len = loc_deepest.length;
574
575        /* Chomp off the current component */
576        cur_start = ii + 1;
577    }
578
579    return 0;
580}
581
582/**
583 * Retrieves a buffer for the designated _path_ in the JSON document. The
584 * actual length of the remaining components may be determined by using
585 * pointer arithmetic.
586 *
587 * @param value The value buffer to search
588 * @param nvalue Length of the value buffer
589 * @param nj The path component to search for
590 * @param[out] a result structure for the path
591 * @return 0 if found, otherwise an error code
592 */
593int
594Match::exec_match(const char *value, size_t nvalue, const Path *pth, jsonsl_t jsn)
595{
596    if (!pth->has_negix) {
597        return exec_match_simple(value, nvalue, pth, jsn);
598    } else {
599        return exec_match_negix(value, nvalue, pth, jsn);
600    }
601}
602
603Match::Match()
604{
605    clear();
606}
607
608Match::~Match()
609{
610}
611
612void
613Match::clear()
614{
615    // TODO: Memberwise reset
616    memset(this, 0, sizeof(*this));
617}
618
619jsonsl_t
620Match::jsn_alloc()
621{
622    return jsonsl_new(Limits::PARSER_DEPTH);
623}
624
625void
626Match::jsn_free(jsonsl_t jsn)
627{
628    jsonsl_destroy(jsn);
629}
630
631typedef struct {
632    int err;
633    int rootcount;
634    int flags;
635    int maxdepth;
636} validate_ctx;
637
638static int
639validate_err_callback(jsonsl_t jsn,
640    jsonsl_error_t err, jsonsl_state_st *, jsonsl_char_t *)
641{
642    validate_ctx *ctx = (validate_ctx *)jsn->data;
643    ctx->err = err;
644    // printf("ERROR: AT=%s\n", at);
645    return 0;
646}
647
648static void
649validate_callback(jsonsl_t jsn, jsonsl_action_t action,
650    jsonsl_state_st *state, const jsonsl_char_t *)
651{
652    validate_ctx *ctx = reinterpret_cast<validate_ctx*>(jsn->data);
653
654    if (ctx->maxdepth > -1 &&
655            state->level - 1 == static_cast<unsigned>(ctx->maxdepth)) {
656        ctx->err = Validator::ETOODEEP;
657        jsonsl_stop(jsn);
658        return;
659    }
660
661    if (state->level == 1) {
662        // Root element
663        ctx->rootcount++;
664
665    } else if (state->level == 2 && action == JSONSL_ACTION_PUSH) {
666        if (ctx->flags & Validator::VALUE_PRIMITIVE) {
667            if (JSONSL_STATE_IS_CONTAINER(state)) {
668                ctx->err = Validator::ENOTPRIMITIVE;
669                jsonsl_stop(jsn);
670            }
671        }
672
673        if (ctx->flags & Validator::VALUE_SINGLE) {
674            auto *parent = jsonsl_last_state(jsn, state);
675            int has_multielem = 0;
676            if (parent->type == JSONSL_T_LIST && parent->nelem > 1) {
677                has_multielem = 1;
678            } else if (parent->type == JSONSL_T_OBJECT && parent->nelem > 2) {
679                has_multielem = 1;
680            }
681
682            if (has_multielem) {
683                ctx->err = Validator::EMULTIELEM;
684                jsonsl_stop(jsn);
685            }
686        }
687    }
688}
689
690static const Loc validate_ARRAY_PRE("[", 1);
691static const Loc validate_ARRAY_POST("]", 1);
692static const Loc validate_DICT_PRE("{\"k\":", 5);
693static const Loc validate_DICT_POST("}", 1);
694static const Loc validate_NOOP(NULL, 0);
695
696int
697Validator::validate(const char *s, size_t n, jsonsl_t jsn, int maxdepth, int mode)
698{
699    int need_free_jsn = 0;
700    int type = mode & PARENT_MASK;
701    int flags = mode & VALUE_MASK;
702    const Loc *l_pre, *l_post;
703
704    validate_ctx ctx = { 0,0 };
705    if (jsn == NULL) {
706        jsn = jsonsl_new(Limits::PARSER_DEPTH);
707        need_free_jsn = 1;
708    }
709
710    jsn->action_callback_POP = NULL;
711    jsn->action_callback_PUSH = NULL;
712
713    jsn->action_callback = validate_callback;
714    jsn->error_callback = validate_err_callback;
715
716    // Set the maximum level. This name is a bit misleading as it's
717    // actually the level number (inclusive) beyond which we won't get
718    // called. Cutting down on the number of callbacks can significantly
719    // benefit CPU, especially for larger JSON documents with many tokens.
720    if (maxdepth != -1) {
721        if (type != PARENT_NONE) {
722            // Don't count the wrapper in the depth
723            maxdepth++;
724        }
725        // Set the callback to be 1 more (i.e. max_level+2) so we can
726        // get notified about depth violations
727        jsn->max_callback_level = maxdepth + 2;
728    } else if (flags) {
729        // Set the callback to include the "wrapper" container and our
730        // actual value
731        jsn->max_callback_level = 3;
732    } else {
733        // only care about the actual value.
734        jsn->max_callback_level = 2;
735    }
736
737    ctx.maxdepth = maxdepth;
738    ctx.flags = flags;
739
740    jsn->call_OBJECT = 1;
741    jsn->call_LIST = 1;
742    jsn->call_STRING = 1;
743    jsn->call_SPECIAL = 1;
744    jsn->call_HKEY = 0;
745    jsn->call_UESCAPE = 0;
746    jsn->data = &ctx;
747
748    if (type == PARENT_NONE) {
749        l_pre = l_post = &validate_NOOP;
750    } else if (type == PARENT_ARRAY) {
751        l_pre = &validate_ARRAY_PRE;
752        l_post = &validate_ARRAY_POST;
753    } else if (type == PARENT_DICT) {
754        l_pre = &validate_DICT_PRE;
755        l_post = &validate_DICT_POST;
756    } else {
757        ctx.err = JSONSL_ERROR_GENERIC;
758        goto GT_ERR;
759    }
760
761    jsonsl_feed(jsn, l_pre->at, l_pre->length);
762    jsonsl_feed(jsn, s, n);
763
764    if (ctx.err == JSONSL_ERROR_SUCCESS) {
765        jsonsl_feed(jsn, l_post->at, l_post->length);
766    }
767
768    if (ctx.err == JSONSL_ERROR_SUCCESS) {
769        if (ctx.rootcount < 2) {
770            ctx.err = Validator::EPARTIAL;
771        } else if (ctx.rootcount > 2) {
772            ctx.err = Validator::EMULTIELEM;
773        }
774    }
775
776    GT_ERR:
777    if (need_free_jsn) {
778        jsonsl_destroy(jsn);
779    } else {
780        jsonsl_reset(jsn);
781    }
782    return ctx.err;
783}
784
785const char *
786Validator::errstr(int rv)
787{
788    if (rv <= JSONSL_ERROR_GENERIC) {
789        return jsonsl_strerror(static_cast<jsonsl_error_t>(rv));
790    }
791    switch (rv) {
792    case EMULTIELEM:
793        return "Found multiple elements (single expected)";
794    case EPARTIAL:
795        return "Found incomplete JSON";
796    default:
797        return "UNKNOWN";
798    }
799}
800