neovim

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

packer.c (8599B)


      1 #include <assert.h>
      2 #include <lauxlib.h>
      3 #include <stddef.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 
      7 #include "klib/kvec.h"
      8 #include "nvim/api/private/defs.h"
      9 #include "nvim/api/private/helpers.h"
     10 #include "nvim/lua/executor.h"
     11 #include "nvim/macros_defs.h"
     12 #include "nvim/memory.h"
     13 #include "nvim/msgpack_rpc/packer.h"
     14 #include "nvim/types_defs.h"
     15 
     16 #include "msgpack_rpc/packer.c.generated.h"
     17 
     18 void mpack_check_buffer(PackerBuffer *packer)
     19 {
     20  if (mpack_remaining(packer) < 2 * MPACK_ITEM_SIZE) {
     21    packer->packer_flush(packer);
     22  }
     23 }
     24 
     25 static void mpack_w8(char **b, const char *data)
     26 {
     27 #ifdef ORDER_BIG_ENDIAN
     28  memcpy(*b, data, 8);
     29  *b += 8;
     30 #else
     31  for (int i = 7; i >= 0; i--) {
     32    *(*b)++ = data[i];
     33  }
     34 #endif
     35 }
     36 
     37 void mpack_uint64(char **ptr, uint64_t i)
     38 {
     39  if (i > 0xfffffff) {
     40    mpack_w(ptr, 0xcf);
     41    mpack_w8(ptr, (char *)&i);
     42  } else {
     43    mpack_uint(ptr, (uint32_t)i);
     44  }
     45 }
     46 
     47 void mpack_integer(char **ptr, Integer i)
     48 {
     49  if (i >= 0) {
     50    mpack_uint64(ptr, (uint64_t)i);
     51  } else {
     52    if (i < -0x80000000LL) {
     53      mpack_w(ptr, 0xd3);
     54      mpack_w8(ptr, (char *)&i);
     55    } else if (i < -0x8000) {
     56      mpack_w(ptr, 0xd2);
     57      mpack_w4(ptr, (uint32_t)i);
     58    } else if (i < -0x80) {
     59      mpack_w(ptr, 0xd1);
     60      mpack_w2(ptr, (uint32_t)i);
     61    } else if (i < -0x20) {
     62      mpack_w(ptr, 0xd0);
     63      mpack_w(ptr, (char)i);
     64    } else {
     65      mpack_w(ptr, (char)i);
     66    }
     67  }
     68 }
     69 
     70 void mpack_float8(char **ptr, double i)
     71 {
     72  mpack_w(ptr, 0xcb);
     73  mpack_w8(ptr, (char *)&i);
     74 }
     75 
     76 void mpack_str(String str, PackerBuffer *packer)
     77 {
     78  const size_t len = str.size;
     79  if (len < 32) {
     80    mpack_w(&packer->ptr, 0xa0 | len);
     81  } else if (len < 0xff) {
     82    mpack_w(&packer->ptr, 0xd9);
     83    mpack_w(&packer->ptr, len);
     84  } else if (len < 0xffff) {
     85    mpack_w(&packer->ptr, 0xda);
     86    mpack_w2(&packer->ptr, (uint32_t)len);
     87  } else if (len < 0xffffffff) {
     88    mpack_w(&packer->ptr, 0xdb);
     89    mpack_w4(&packer->ptr, (uint32_t)len);
     90  } else {
     91    abort();
     92  }
     93 
     94  mpack_raw(str.data, len, packer);
     95 }
     96 
     97 void mpack_bin(String str, PackerBuffer *packer)
     98 {
     99  const size_t len = str.size;
    100  if (len < 0xff) {
    101    mpack_w(&packer->ptr, 0xc4);
    102    mpack_w(&packer->ptr, len);
    103  } else if (len < 0xffff) {
    104    mpack_w(&packer->ptr, 0xc5);
    105    mpack_w2(&packer->ptr, (uint32_t)len);
    106  } else if (len < 0xffffffff) {
    107    mpack_w(&packer->ptr, 0xc6);
    108    mpack_w4(&packer->ptr, (uint32_t)len);
    109  } else {
    110    abort();
    111  }
    112 
    113  mpack_raw(str.data, len, packer);
    114 }
    115 
    116 void mpack_raw(const char *data, size_t len, PackerBuffer *packer)
    117 {
    118  size_t pos = 0;
    119  while (pos < len) {
    120    ptrdiff_t remaining = packer->endptr - packer->ptr;
    121    size_t to_copy = MIN(len - pos, (size_t)remaining);
    122    memcpy(packer->ptr, data + pos, to_copy);
    123    packer->ptr += to_copy;
    124    pos += to_copy;
    125 
    126    if (pos < len) {
    127      packer->packer_flush(packer);
    128    }
    129  }
    130  mpack_check_buffer(packer);
    131 }
    132 
    133 void mpack_ext(char *buf, size_t len, int8_t type, PackerBuffer *packer)
    134 {
    135  if (len == 1) {
    136    mpack_w(&packer->ptr, 0xd4);
    137  } else if (len == 2) {
    138    mpack_w(&packer->ptr, 0xd5);
    139  } else if (len <= 0xff) {
    140    mpack_w(&packer->ptr, 0xc7);
    141  } else if (len < 0xffff) {
    142    mpack_w(&packer->ptr, 0xc8);
    143    mpack_w2(&packer->ptr, (uint32_t)len);
    144  } else if (len < 0xffffffff) {
    145    mpack_w(&packer->ptr, 0xc9);
    146    mpack_w4(&packer->ptr, (uint32_t)len);
    147  } else {
    148    abort();
    149  }
    150  mpack_w(&packer->ptr, type);
    151  mpack_raw(buf, len, packer);
    152 }
    153 
    154 void mpack_handle(ObjectType type, handle_T handle, PackerBuffer *packer)
    155 {
    156  char exttype = (char)(type - EXT_OBJECT_TYPE_SHIFT);
    157  if (-0x1f <= handle && handle <= 0x7f) {
    158    mpack_w(&packer->ptr, 0xd4);
    159    mpack_w(&packer->ptr, exttype);
    160    mpack_w(&packer->ptr, (char)handle);
    161  } else {
    162    // we want to encode some small negative sentinel like -1. This is handled above
    163    assert(handle >= 0);
    164    // FAIL: we cannot use fixext 4/8 due to a design error
    165    // (in theory fixext 2 for handle<=0xff but we don't gain much from it)
    166    char buf[MPACK_ITEM_SIZE];
    167    char *pos = buf;
    168    mpack_uint(&pos, (uint32_t)handle);
    169    ptrdiff_t packsize = pos - buf;
    170    mpack_w(&packer->ptr, 0xc7);
    171    mpack_w(&packer->ptr, packsize);
    172    mpack_w(&packer->ptr, exttype);
    173    memcpy(packer->ptr, buf, (size_t)packsize);
    174    packer->ptr += packsize;
    175  }
    176 }
    177 
    178 void mpack_object(Object *obj, PackerBuffer *packer)
    179 {
    180  mpack_object_inner(obj, NULL, 0, packer);
    181 }
    182 
    183 void mpack_object_array(Array arr, PackerBuffer *packer)
    184 {
    185  mpack_array(&packer->ptr, (uint32_t)arr.size);
    186  if (arr.size > 0) {
    187    Object container = ARRAY_OBJ(arr);
    188    mpack_object_inner(&arr.items[0], arr.size > 1 ? &container : NULL, 1, packer);
    189  }
    190 }
    191 
    192 typedef struct {
    193  Object *container;
    194  size_t idx;
    195 } ContainerStackItem;
    196 
    197 void mpack_object_inner(Object *current, Object *container, size_t container_idx,
    198                        PackerBuffer *packer)
    199  FUNC_ATTR_NONNULL_ARG(1, 4)
    200 {
    201  // The inner loop of this function packs "current" and then fetches the next
    202  // value from "container". "stack" is only used for nested containers.
    203  kvec_withinit_t(ContainerStackItem, 2) stack = KV_INITIAL_VALUE;
    204  kvi_init(stack);
    205 
    206  while (true) {
    207    mpack_check_buffer(packer);
    208    switch (current->type) {
    209    case kObjectTypeLuaRef:
    210      // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef
    211      // should only appear when the caller has opted in to handle references,
    212      // see nlua_pop_Object.
    213      api_free_luaref(current->data.luaref);
    214      current->data.luaref = LUA_NOREF;
    215      FALLTHROUGH;
    216    case kObjectTypeNil:
    217      mpack_nil(&packer->ptr);
    218      break;
    219    case kObjectTypeBoolean:
    220      mpack_bool(&packer->ptr, current->data.boolean);
    221      break;
    222    case kObjectTypeInteger:
    223      mpack_integer(&packer->ptr, current->data.integer);
    224      break;
    225    case kObjectTypeFloat:
    226      mpack_float8(&packer->ptr, current->data.floating);
    227      break;
    228    case kObjectTypeString:
    229      mpack_str(current->data.string, packer);
    230      break;
    231    case kObjectTypeBuffer:
    232    case kObjectTypeWindow:
    233    case kObjectTypeTabpage:
    234      mpack_handle(current->type, (handle_T)current->data.integer, packer);
    235      break;
    236    case kObjectTypeDict:
    237    case kObjectTypeArray: {}
    238      size_t current_size;
    239      if (current->type == kObjectTypeArray) {
    240        current_size = current->data.array.size;
    241        mpack_array(&packer->ptr, (uint32_t)current_size);
    242      } else {
    243        current_size = current->data.dict.size;
    244        mpack_map(&packer->ptr, (uint32_t)current_size);
    245      }
    246      if (current_size > 0) {
    247        if (current->type == kObjectTypeArray && current_size == 1) {
    248          current = &current->data.array.items[0];
    249          continue;
    250        }
    251        if (container) {
    252          kvi_push(stack, ((ContainerStackItem) {
    253            .container = container,
    254            .idx = container_idx,
    255          }));
    256        }
    257        container = current;
    258        container_idx = 0;
    259      }
    260      break;
    261    }
    262 
    263    if (!container) {
    264      if (kv_size(stack)) {
    265        ContainerStackItem it = kv_pop(stack);
    266        container = it.container;
    267        container_idx = it.idx;
    268      } else {
    269        break;
    270      }
    271    }
    272 
    273    if (container->type == kObjectTypeArray) {
    274      Array arr = container->data.array;
    275      current = &arr.items[container_idx++];
    276      if (container_idx >= arr.size) {
    277        container = NULL;
    278      }
    279    } else {
    280      Dict dict = container->data.dict;
    281      KeyValuePair *it = &dict.items[container_idx++];
    282      mpack_check_buffer(packer);
    283      mpack_str(it->key, packer);
    284      current = &it->value;
    285      if (container_idx >= dict.size) {
    286        container = NULL;
    287      }
    288    }
    289  }
    290  kvi_destroy(stack);
    291 }
    292 
    293 PackerBuffer packer_string_buffer(void)
    294 {
    295  const size_t initial_size = 64;  // must be larger than SHADA_MPACK_FREE_SPACE
    296  char *alloc = xmalloc(initial_size);
    297  return (PackerBuffer) {
    298    .startptr = alloc,
    299    .ptr = alloc,
    300    .endptr = alloc + initial_size,
    301    .packer_flush = flush_string_buffer,
    302  };
    303 }
    304 
    305 static void flush_string_buffer(PackerBuffer *buffer)
    306 {
    307  size_t current_capacity = (size_t)(buffer->endptr - buffer->startptr);
    308  size_t new_capacity = 2 * current_capacity;
    309  size_t len = (size_t)(buffer->ptr - buffer->startptr);
    310 
    311  buffer->startptr = xrealloc(buffer->startptr, new_capacity);
    312  buffer->ptr = buffer->startptr + len;
    313  buffer->endptr = buffer->startptr + new_capacity;
    314 }
    315 
    316 /// can only be used with a PackerBuffer from `packer_string_buffer`
    317 String packer_take_string(PackerBuffer *buffer)
    318 {
    319  return (String){ .data = buffer->startptr, .size = (size_t)(buffer->ptr - buffer->startptr) };
    320 }