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 = ¤t->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 }