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