neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

object.c (6875B)


      1 #include <string.h>
      2 
      3 #include "object.h"
      4 
      5 static int mpack_parser_full(mpack_parser_t *w);
      6 static mpack_node_t *mpack_parser_push(mpack_parser_t *w);
      7 static mpack_node_t *mpack_parser_pop(mpack_parser_t *w);
      8 
      9 MPACK_API void mpack_parser_init(mpack_parser_t *parser,
     10    mpack_uint32_t capacity)
     11 {
     12  mpack_tokbuf_init(&parser->tokbuf);
     13  parser->data.p = NULL;
     14  parser->capacity = capacity ? capacity : MPACK_MAX_OBJECT_DEPTH;
     15  parser->size = 0;
     16  parser->exiting = 0;
     17  memset(parser->items, 0, sizeof(mpack_node_t) * (parser->capacity + 1));
     18  parser->items[0].pos = (size_t)-1;
     19  parser->status = 0;
     20 }
     21 
     22 #define MPACK_EXCEPTION_CHECK(parser)                                           \
     23  do {                                                                      \
     24    if (parser->status == MPACK_EXCEPTION) {                                    \
     25      return MPACK_EXCEPTION;                                                   \
     26    }                                                                       \
     27  } while (0)
     28 
     29 #define MPACK_WALK(action)                                                  \
     30  do {                                                                      \
     31    mpack_node_t *n;                                                        \
     32                                                                            \
     33    if (parser->exiting) goto exit;                                         \
     34    if (mpack_parser_full(parser)) return MPACK_NOMEM;                      \
     35    n = mpack_parser_push(parser);                                          \
     36    action;                                                                 \
     37    MPACK_EXCEPTION_CHECK(parser);                                              \
     38    parser->exiting = 1;                                                    \
     39    return MPACK_EOF;                                                       \
     40                                                                            \
     41 exit:                                                                       \
     42    parser->exiting = 0;                                                    \
     43    while ((n = mpack_parser_pop(parser))) {                                \
     44      exit_cb(parser, n);                                                   \
     45      MPACK_EXCEPTION_CHECK(parser);                                            \
     46      if (!parser->size) return MPACK_OK;                                   \
     47    }                                                                       \
     48                                                                            \
     49    return MPACK_EOF;                                                       \
     50  } while (0)
     51 
     52 MPACK_API int mpack_parse_tok(mpack_parser_t *parser, mpack_token_t tok,
     53    mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
     54 {
     55  MPACK_EXCEPTION_CHECK(parser);
     56  MPACK_WALK({n->tok = tok; enter_cb(parser, n);});
     57 }
     58 
     59 MPACK_API int mpack_unparse_tok(mpack_parser_t *parser, mpack_token_t *tok,
     60    mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
     61 {
     62  MPACK_EXCEPTION_CHECK(parser);
     63  MPACK_WALK({enter_cb(parser, n); *tok = n->tok;});
     64 }
     65 
     66 MPACK_API int mpack_parse(mpack_parser_t *parser, const char **buf,
     67    size_t *buflen, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
     68 {
     69  int status = MPACK_EOF;
     70  MPACK_EXCEPTION_CHECK(parser);
     71 
     72  while (*buflen && status) {
     73    mpack_token_t tok;
     74    mpack_tokbuf_t *tb = &parser->tokbuf;
     75    const char *buf_save = *buf;
     76    size_t buflen_save = *buflen;
     77 
     78    if ((status = mpack_read(tb, buf, buflen, &tok)) == MPACK_EOF) continue;
     79    else if (status == MPACK_ERROR) goto rollback;
     80 
     81    do {
     82      status = mpack_parse_tok(parser, tok, enter_cb, exit_cb);
     83      MPACK_EXCEPTION_CHECK(parser);
     84    } while (parser->exiting);
     85 
     86    if (status != MPACK_NOMEM) continue;
     87 
     88 rollback:
     89    /* restore buf/buflen so the next call will try to read the same token */
     90    *buf = buf_save;
     91    *buflen = buflen_save;
     92    break;
     93  }
     94 
     95  return status;
     96 }
     97 
     98 MPACK_API int mpack_unparse(mpack_parser_t *parser, char **buf, size_t *buflen,
     99    mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
    100 {
    101  int status = MPACK_EOF;
    102  MPACK_EXCEPTION_CHECK(parser);
    103 
    104  while (*buflen && status) {
    105    int write_status;
    106    mpack_token_t tok;
    107    mpack_tokbuf_t *tb = &parser->tokbuf;
    108 
    109    if (!tb->plen)
    110      parser->status = mpack_unparse_tok(parser, &tok, enter_cb, exit_cb);
    111 
    112    MPACK_EXCEPTION_CHECK(parser);
    113 
    114    status = parser->status;
    115 
    116    if (status == MPACK_NOMEM)
    117      break;
    118 
    119    if (parser->exiting) {
    120      write_status = mpack_write(tb, buf, buflen, &tok);
    121      status = write_status ? write_status : status;
    122    }
    123  }
    124 
    125  return status;
    126 }
    127 
    128 MPACK_API void mpack_parser_copy(mpack_parser_t *d, mpack_parser_t *s)
    129 {
    130  // workaround UBSAN being NOT happy with a flexible array member with arr[N>1] initial size
    131  mpack_one_parser_t *dst = (mpack_one_parser_t *)d;
    132  mpack_one_parser_t *src = (mpack_one_parser_t *)s;
    133  mpack_uint32_t i;
    134  mpack_uint32_t dst_capacity = dst->capacity; 
    135  assert(src->capacity <= dst_capacity);
    136  /* copy all fields except the stack */
    137  memcpy(dst, src, sizeof(mpack_one_parser_t) - sizeof(mpack_node_t));
    138  /* reset capacity */
    139  dst->capacity = dst_capacity;
    140  /* copy the stack */
    141  for (i = 0; i <= src->capacity; i++) {
    142    dst->items[i] = src->items[i];
    143  }
    144 }
    145 
    146 static int mpack_parser_full(mpack_parser_t *parser)
    147 {
    148  return parser->size == parser->capacity;
    149 }
    150 
    151 static mpack_node_t *mpack_parser_push(mpack_parser_t *p)
    152 {
    153  mpack_one_parser_t *parser = (mpack_one_parser_t *)p;
    154  mpack_node_t *top;
    155  assert(parser->size < parser->capacity);
    156  top = parser->items + parser->size + 1;
    157  top->data[0].p = NULL;
    158  top->data[1].p = NULL;
    159  top->pos = 0;
    160  top->key_visited = 0;
    161  /* increase size and invoke callback, passing parent node if any */
    162  parser->size++;
    163  return top;
    164 }
    165 
    166 static mpack_node_t *mpack_parser_pop(mpack_parser_t *p)
    167 {
    168  mpack_one_parser_t *parser = (mpack_one_parser_t *)p;
    169  mpack_node_t *top, *parent;
    170  assert(parser->size);
    171  top = parser->items + parser->size;
    172 
    173  if (top->tok.type > MPACK_TOKEN_CHUNK && top->pos < top->tok.length) {
    174    /* continue processing children */
    175    return NULL;
    176  }
    177 
    178  parent = MPACK_PARENT_NODE(top);
    179  if (parent) {
    180    /* we use parent->tok.length to keep track of how many children remain.
    181     * update it to reflect the processed node. */
    182    if (top->tok.type == MPACK_TOKEN_CHUNK) {
    183      parent->pos += top->tok.length;
    184    } else if (parent->tok.type == MPACK_TOKEN_MAP) {
    185      /* maps allow up to 2^32 - 1 pairs, so to allow this many items in a
    186       * 32-bit length variable we use an additional flag to determine if the
    187       * key of a certain position was visited */
    188      if (parent->key_visited) {
    189        parent->pos++;
    190      }
    191      parent->key_visited = !parent->key_visited;
    192    } else {
    193      parent->pos++;
    194    }
    195  }
    196 
    197  parser->size--;
    198  return top;
    199 }