mirror of https://github.com/neovim/neovim.git
201 lines
6.7 KiB
C
201 lines
6.7 KiB
C
#include <string.h>
|
|
|
|
#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 *d, mpack_parser_t *s)
|
|
{
|
|
// workaround UBSAN being NOT happy with a flexible array member with arr[N>1] initial size
|
|
mpack_one_parser_t *dst = (mpack_one_parser_t *)d;
|
|
mpack_one_parser_t *src = (mpack_one_parser_t *)s;
|
|
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 *p)
|
|
{
|
|
mpack_one_parser_t *parser = (mpack_one_parser_t *)p;
|
|
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 *p)
|
|
{
|
|
mpack_one_parser_t *parser = (mpack_one_parser_t *)p;
|
|
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;
|
|
}
|
|
|