// This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include #include "object.h" static int mpack_parser_full(mpack_parser_t *w); static mpack_node_t *mpack_parser_push(mpack_parser_t *w); static mpack_node_t *mpack_parser_pop(mpack_parser_t *w); MPACK_API void mpack_parser_init(mpack_parser_t *parser, mpack_uint32_t capacity) { mpack_tokbuf_init(&parser->tokbuf); parser->data.p = NULL; parser->capacity = capacity ? capacity : MPACK_MAX_OBJECT_DEPTH; parser->size = 0; parser->exiting = 0; memset(parser->items, 0, sizeof(mpack_node_t) * (parser->capacity + 1)); parser->items[0].pos = (size_t)-1; parser->status = 0; } #define MPACK_EXCEPTION_CHECK(parser) \ do { \ if (parser->status == MPACK_EXCEPTION) { \ return MPACK_EXCEPTION; \ } \ } while (0) #define MPACK_WALK(action) \ do { \ mpack_node_t *n; \ \ if (parser->exiting) goto exit; \ if (mpack_parser_full(parser)) return MPACK_NOMEM; \ n = mpack_parser_push(parser); \ action; \ MPACK_EXCEPTION_CHECK(parser); \ parser->exiting = 1; \ return MPACK_EOF; \ \ exit: \ parser->exiting = 0; \ while ((n = mpack_parser_pop(parser))) { \ exit_cb(parser, n); \ MPACK_EXCEPTION_CHECK(parser); \ if (!parser->size) return MPACK_OK; \ } \ \ return MPACK_EOF; \ } while (0) MPACK_API int mpack_parse_tok(mpack_parser_t *parser, mpack_token_t tok, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) { MPACK_EXCEPTION_CHECK(parser); MPACK_WALK({n->tok = tok; enter_cb(parser, n);}); } MPACK_API int mpack_unparse_tok(mpack_parser_t *parser, mpack_token_t *tok, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) { MPACK_EXCEPTION_CHECK(parser); MPACK_WALK({enter_cb(parser, n); *tok = n->tok;}); } MPACK_API int mpack_parse(mpack_parser_t *parser, const char **buf, size_t *buflen, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) { int status = MPACK_EOF; MPACK_EXCEPTION_CHECK(parser); while (*buflen && status) { mpack_token_t tok; mpack_tokbuf_t *tb = &parser->tokbuf; const char *buf_save = *buf; size_t buflen_save = *buflen; if ((status = mpack_read(tb, buf, buflen, &tok)) == MPACK_EOF) continue; else if (status == MPACK_ERROR) goto rollback; do { status = mpack_parse_tok(parser, tok, enter_cb, exit_cb); MPACK_EXCEPTION_CHECK(parser); } while (parser->exiting); if (status != MPACK_NOMEM) continue; rollback: /* restore buf/buflen so the next call will try to read the same token */ *buf = buf_save; *buflen = buflen_save; break; } return status; } MPACK_API int mpack_unparse(mpack_parser_t *parser, char **buf, size_t *buflen, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) { int status = MPACK_EOF; MPACK_EXCEPTION_CHECK(parser); while (*buflen && status) { int write_status; mpack_token_t tok; mpack_tokbuf_t *tb = &parser->tokbuf; if (!tb->plen) parser->status = mpack_unparse_tok(parser, &tok, enter_cb, exit_cb); MPACK_EXCEPTION_CHECK(parser); status = parser->status; if (status == MPACK_NOMEM) break; if (parser->exiting) { write_status = mpack_write(tb, buf, buflen, &tok); status = write_status ? write_status : status; } } return status; } MPACK_API void mpack_parser_copy(mpack_parser_t *dst, mpack_parser_t *src) { mpack_uint32_t i; mpack_uint32_t dst_capacity = dst->capacity; assert(src->capacity <= dst_capacity); /* copy all fields except the stack */ memcpy(dst, src, sizeof(mpack_one_parser_t) - sizeof(mpack_node_t)); /* reset capacity */ dst->capacity = dst_capacity; /* copy the stack */ for (i = 0; i <= src->capacity; i++) { dst->items[i] = src->items[i]; } } static int mpack_parser_full(mpack_parser_t *parser) { return parser->size == parser->capacity; } static mpack_node_t *mpack_parser_push(mpack_parser_t *parser) { mpack_node_t *top; assert(parser->size < parser->capacity); top = parser->items + parser->size + 1; top->data[0].p = NULL; top->data[1].p = NULL; top->pos = 0; top->key_visited = 0; /* increase size and invoke callback, passing parent node if any */ parser->size++; return top; } static mpack_node_t *mpack_parser_pop(mpack_parser_t *parser) { mpack_node_t *top, *parent; assert(parser->size); top = parser->items + parser->size; if (top->tok.type > MPACK_TOKEN_CHUNK && top->pos < top->tok.length) { /* continue processing children */ return NULL; } parent = MPACK_PARENT_NODE(top); if (parent) { /* we use parent->tok.length to keep track of how many children remain. * update it to reflect the processed node. */ if (top->tok.type == MPACK_TOKEN_CHUNK) { parent->pos += top->tok.length; } else if (parent->tok.type == MPACK_TOKEN_MAP) { /* maps allow up to 2^32 - 1 pairs, so to allow this many items in a * 32-bit length variable we use an additional flag to determine if the * key of a certain position was visited */ if (parent->key_visited) { parent->pos++; } parent->key_visited = !parent->key_visited; } else { parent->pos++; } } parser->size--; return top; }