Lite³
A JSON-Compatible Zero-Copy Serialization Format
Loading...
Searching...
No Matches
lite3.c
1/*
2 Lite³: A JSON-Compatible Zero-Copy Serialization Format
3
4 Copyright © 2025 Elias de Jong <elias@fastserial.com>
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23
24 __ __________________ ____
25 _ ___ ___/ /___(_)_/ /_______|_ /
26 _ _____/ / __/ /_ __/ _ \_/_ <
27 ___ __/ /___/ / / /_ / __/____/
28 /_____/_/ \__/ \___/
29*/
30#include "lite3.h"
31
32#include <stddef.h>
33#include <stdlib.h>
34#include <string.h>
35#include <stdint.h>
36#include <errno.h>
37#include <assert.h>
38
39
40
41// Typedef for primitive types
42typedef float f32;
43typedef double f64;
44typedef int8_t i8;
45typedef uint8_t u8;
46typedef int16_t i16;
47typedef uint16_t u16;
48typedef int32_t i32;
49typedef uint32_t u32;
50typedef int64_t i64;
51typedef uint64_t u64;
52
53
54
55/*
56 B-tree node struct
57 Advanced users can adjust the B-tree node size to experiment with different performance characteristics.
58 Larger node sizes will bloat message size, but also reduce the tree height, thus also reducing node walks.
59 Actual effects are dependent on the architecture and workload. To be sure, experiment with different settings and profile.
60
61 IMPORTANT RULES TO OBEY:
62 - hashes[] and kv_ofs[] should always have an element count exactly equal to LITE3_NODE_KEY_COUNT_MASK
63 - hashes[] and kv_ofs[] should always have an uneven element count
64 - hashes[] and kv_ofs[] should always have equal element count
65 - child_ofs[] should always have element count of exactly 1 greater than hashes[] and kv_ofs[]
66
67 How to change:
68 1) uncomment `LITE3_NODE_KEY_COUNT_MASK` to the preferred setting
69 2) adjust the member array sizes inside the `struct node` definition
70 3) uncomment `LITE3_NODE_SIZE` to the correct size inside the header
71 4) uncomment LITE3_NODE_SIZE_KC_OFFSET` to the correct size inside the header
72 5) uncomment `LITE3_TREE_HEIGHT_MAX` to the correct value inside the header
73
74 [ WARNING ] If you change this setting, everyone you communicate with must also change it.
75 Unless you control all communicating parties, you probably should not touch this.
76*/
77struct node {
78 u32 gen_type; // upper 24 bits: gen lower 8 bits: lite3_type
79 u32 hashes[7];
80 u32 size_kc; // upper 26 bits: size lower 6 bits: key_count
81 u32 kv_ofs[7];
82 u32 child_ofs[8];
83};
84static_assert(sizeof(struct node) == LITE3_NODE_SIZE, "sizeof(struct node) must equal LITE3_NODE_SIZE");
85static_assert(offsetof(struct node, gen_type) == 0, "Runtime type checks and LITE3_BYTES() & LITE3_STR() macros expect to read (struct node).gen_type field at offset 0");
86static_assert(sizeof(((struct node *)0)->gen_type) == sizeof(uint32_t), "LITE3_BYTES() & LITE3_STR() macros expect to read (struct node).gen_type as uint32_t");
87static_assert(offsetof(struct node, size_kc) == LITE3_NODE_SIZE_KC_OFFSET, "Offset of (struct node).size_kc must equal LITE3_NODE_SIZE_KC_OFFSET");
88static_assert(sizeof(((struct node *)0)->size_kc) == sizeof(uint32_t), "Node size checks expect to read (struct node).size_kc as uint32_t");
89static_assert(sizeof(((struct node *)0)->gen_type) == sizeof(((lite3_iter *)0)->gen), "Iterator expects to read (struct node).gen_type as uint32_t");
90
91#define LITE3_NODE_TYPE_SHIFT 0
92#define LITE3_NODE_TYPE_MASK ((u32)((1 << 8) - 1)) // 8 LSB
93
94#define LITE3_NODE_GEN_SHIFT 8
95#define LITE3_NODE_GEN_MASK ((u32)~((1 << 8) - 1)) // 24 MSB
96
97#define LITE3_NODE_KEY_COUNT_MAX ((int)(sizeof(((struct node *)0)->hashes) / sizeof(u32)))
98#define LITE3_NODE_KEY_COUNT_MIN ((int)(LITE3_NODE_KEY_COUNT_MAX / 2))
99
100#define LITE3_NODE_KEY_COUNT_SHIFT 0
101// #define LITE3_NODE_KEY_COUNT_MASK ((u32)((1 << 2) - 1)) // 2 LSB key_count: 0-3 hashes[3] kv_ofs[3] child_ofs[4] LITE3_NODE_SIZE: 48 (0.75 cache lines)
102#define LITE3_NODE_KEY_COUNT_MASK ((u32)((1 << 3) - 1)) // 3 LSB key_count: 0-7 hashes[7] kv_ofs[7] child_ofs[8] LITE3_NODE_SIZE: 96 (1.5 cache lines)
103// #define LITE3_NODE_KEY_COUNT_MASK ((u32)((1 << 4) - 1)) // 4 LSB key_count: 0-15 hashes[15] kv_ofs[15] child_ofs[16] LITE3_NODE_SIZE: 192 (3 cache lines)
104// #define LITE3_NODE_KEY_COUNT_MASK ((u32)((1 << 5) - 1)) // 5 LSB key_count: 0-31 hashes[31] kv_ofs[31] child_ofs[32] LITE3_NODE_SIZE: 384 (6 cache lines)
105// #define LITE3_NODE_KEY_COUNT_MASK ((u32)((1 << 6) - 1)) // 6 LSB key_count: 0-63 hashes[63] kv_ofs[63] child_ofs[64] LITE3_NODE_SIZE: 768 (12 cache lines)
106
107
108
109#define LITE3_KEY_TAG_SIZE_MIN 1
110#define LITE3_KEY_TAG_SIZE_MAX 4
111#define LITE3_KEY_TAG_SIZE_MASK ((1 << 2) - 1)
112#define LITE3_KEY_TAG_SIZE_SHIFT 0
113
114#define LITE3_KEY_TAG_KEY_SIZE_MASK (~((1 << 2) - 1))
115#define LITE3_KEY_TAG_KEY_SIZE_SHIFT 2
116/*
117 Verify a key inside the buffer to ensure readers don't go out of bounds.
118 Optionally compare the existing key to an input key; a mismatch implies a hash collision.
119 - Returns LITE3_VERIFY_KEY_OK (== 0) on success
120 - Returns LITE3_VERIFY_KEY_HASH_COLLISION (== 1) on probe hash collision (caller must retry with different hash)
121 - Returns < 0 on failure
122
123 [ NOTE ] For internal use only.
124*/
125static inline int _verify_key(
126 const u8 *buf, // buffer pointer
127 size_t buflen, // buffer length (bytes)
128 const char *restrict key, // key string (string, optionally call with NULL)
129 size_t key_size, // key size (bytes including null-terminator, optionally call with 0)
130 size_t key_tag_size, // key tag size (bytes, optionally call with 0)
131 size_t *restrict inout_ofs, // key entry offset (relative to *buf)
132 size_t *restrict out_key_tag_size) // key tag size (optionally call with NULL)
133{
134 if (LITE3_UNLIKELY(LITE3_KEY_TAG_SIZE_MAX > buflen || *inout_ofs > buflen - LITE3_KEY_TAG_SIZE_MAX)) {
135 LITE3_PRINT_ERROR("KEY ENTRY OUT OF BOUNDS\n");
136 errno = EFAULT;
137 return -1;
138 }
139 size_t _key_tag_size = (size_t)((*((u8 *)(buf + *inout_ofs)) & LITE3_KEY_TAG_SIZE_MASK) + 1);
140 if (key_tag_size) {
141 if (key_tag_size != _key_tag_size) {
142 LITE3_PRINT_ERROR("KEY TAG SIZE DOES NOT MATCH\n");
143 errno = EINVAL;
144 return -1;
145 }
146 }
147 size_t _key_size = 0;
148 memcpy(&_key_size, buf + *inout_ofs, _key_tag_size);
149 _key_size >>= LITE3_KEY_TAG_KEY_SIZE_SHIFT;
150 *inout_ofs += _key_tag_size;
151
152 if (LITE3_UNLIKELY(_key_size > buflen || *inout_ofs > buflen - _key_size)) {
153 LITE3_PRINT_ERROR("KEY ENTRY OUT OF BOUNDS\n");
154 errno = EFAULT;
155 return -1;
156 }
157 if (key_size) {
158 int cmp = memcmp(
159 (const char *)(buf + *inout_ofs),
160 key,
161 (key_size < _key_size) ? key_size : _key_size
162 );
163 if (LITE3_UNLIKELY(cmp != 0)) {
164 LITE3_PRINT_ERROR("HASH COLLISION\n");
165 return LITE3_VERIFY_KEY_HASH_COLLISION;
166 }
167 }
168 *inout_ofs += _key_size;
169 if (out_key_tag_size)
170 *out_key_tag_size = _key_tag_size;
171 return LITE3_VERIFY_KEY_OK;
172}
173
174/*
175 Verify a value inside the buffer to ensure readers don't go out of bounds.
176 - Returns 0 on success
177 - Returns < 0 on failure
178
179 [ NOTE ] For internal use only.
180*/
181static inline int _verify_val(
182 const u8 *buf, // buffer pointer
183 size_t buflen, // buffer length (bytes)
184 size_t *restrict inout_ofs) // val entry offset (relative to *buf)
185{
186 if (LITE3_UNLIKELY(LITE3_VAL_SIZE > buflen || *inout_ofs > buflen - LITE3_VAL_SIZE)) {
187 LITE3_PRINT_ERROR("VALUE OUT OF BOUNDS\n");
188 errno = EFAULT;
189 return -1;
190 }
191 enum lite3_type type = (enum lite3_type)(*(buf + *inout_ofs));
192
193 if (LITE3_UNLIKELY(type >= LITE3_TYPE_INVALID)) {
194 LITE3_PRINT_ERROR("VALUE TYPE INVALID\n");
195 errno = EINVAL;
196 return -1;
197 }
198 size_t _val_entry_size = LITE3_VAL_SIZE + lite3_type_sizes[type];
199
200 if (LITE3_UNLIKELY(_val_entry_size > buflen || *inout_ofs > buflen - _val_entry_size)) {
201 LITE3_PRINT_ERROR("VALUE OUT OF BOUNDS\n");
202 errno = EFAULT;
203 return -1;
204 }
205 if (type == LITE3_TYPE_STRING || type == LITE3_TYPE_BYTES) { // extra check required for str/bytes
206 size_t byte_count = 0;
207 memcpy(&byte_count, buf + *inout_ofs + LITE3_VAL_SIZE, lite3_type_sizes[LITE3_TYPE_BYTES]);
208 _val_entry_size += byte_count;
209 if (LITE3_UNLIKELY(_val_entry_size > buflen || *inout_ofs > buflen - _val_entry_size)) {
210 LITE3_PRINT_ERROR("VALUE OUT OF BOUNDS\n");
211 errno = EFAULT;
212 return -1;
213 }
214 }
215 *inout_ofs += _val_entry_size;
216 return 0;
217}
218
219int lite3_get_impl(
220 const unsigned char *buf, // buffer pointer
221 size_t buflen, // buffer length (bytes)
222 size_t ofs, // start offset (0 == root)
223 const char *restrict key, // key pointer (string)
224 lite3_key_data key_data, // key data struct
225 lite3_val **out) // value entry pointer (out pointer)
226{
227 #ifdef LITE3_DEBUG
228 if (*(buf + ofs) == LITE3_TYPE_OBJECT) {
229 LITE3_PRINT_DEBUG("GET\tkey: %s\n", key);
230 } else if (*(buf + ofs) == LITE3_TYPE_ARRAY) {
231 LITE3_PRINT_DEBUG("GET\tindex: %u\n", key_data.hash);
232 } else {
233 LITE3_PRINT_DEBUG("GET INVALID: EXPECTING ARRAY OR OBJECT TYPE\n");
234 }
235 #endif
236
237 size_t key_tag_size = (size_t)((!!(key_data.size >> (16 - LITE3_KEY_TAG_KEY_SIZE_SHIFT)) << 1)
238 + !!(key_data.size >> (8 - LITE3_KEY_TAG_KEY_SIZE_SHIFT))
239 + !!key_data.size);
240
241 uint32_t probe_attempts = key ? LITE3_HASH_PROBE_MAX : 1U;
242 for (uint32_t attempt = 0; attempt < probe_attempts; attempt++) {
243
244 lite3_key_data attempt_key = key_data;
245 attempt_key.hash = key_data.hash + attempt * attempt;
246 #ifdef LITE3_DEBUG
247 LITE3_PRINT_DEBUG("probe attempt: %u\thash: %u\n", attempt, attempt_key.hash);
248 #endif
249
250 struct node *restrict node = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
251
252 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
253 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
254 errno = EBADMSG;
255 return -1;
256 }
257
258 int key_count;
259 int i;
260 int node_walks = 0;
261 while (1) {
262 key_count = node->size_kc & LITE3_NODE_KEY_COUNT_MASK;
263 i = 0;
264 while (i < key_count && node->hashes[i] < attempt_key.hash)
265 i++;
266 if (i < key_count && node->hashes[i] == attempt_key.hash) { // target key found
267 size_t target_ofs = node->kv_ofs[i];
268 if (key) {
269 int verify = _verify_key(buf, buflen, key, (size_t)attempt_key.size, key_tag_size, &target_ofs, NULL);
270 if (verify == LITE3_VERIFY_KEY_HASH_COLLISION)
271 break; // try next probe
272 if (verify < 0)
273 return -1;
274 }
275 size_t val_start_ofs = target_ofs;
276 if (_verify_val(buf, buflen, &target_ofs) < 0)
277 return -1;
278 *out = (lite3_val *)(buf + val_start_ofs);
279 return 0;
280 }
281 if (node->child_ofs[0]) { // if children, walk to next node
282 size_t next_node_ofs = (size_t)node->child_ofs[i];
283 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
284
285 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
286 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
287 errno = EBADMSG;
288 return -1;
289 }
290 if (LITE3_UNLIKELY(next_node_ofs > buflen - LITE3_NODE_SIZE)) {
291 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
292 errno = EFAULT;
293 return -1;
294 }
295 if (LITE3_UNLIKELY(++node_walks > LITE3_TREE_HEIGHT_MAX)) {
296 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
297 errno = EBADMSG;
298 return -1;
299 }
300 } else {
301 LITE3_PRINT_ERROR("KEY NOT FOUND\n");
302 errno = ENOENT;
303 return -1;
304 }
305 }
306 }
307 LITE3_PRINT_ERROR("LITE3_HASH_PROBE_MAX LIMIT REACHED\n");
308 errno = EINVAL;
309 return -1;
310}
311
312int lite3_iter_create_impl(const unsigned char *buf, size_t buflen, size_t ofs, lite3_iter *out)
313{
314 LITE3_PRINT_DEBUG("CREATE ITER\n");
315
316 struct node *restrict node = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
317
318 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
319 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
320 errno = EBADMSG;
321 return -1;
322 }
323
324 enum lite3_type type = node->gen_type & LITE3_NODE_TYPE_MASK;
325 if (LITE3_UNLIKELY(!(type == LITE3_TYPE_OBJECT || type == LITE3_TYPE_ARRAY))) {
326 LITE3_PRINT_ERROR("INVALID ARGUMENT: EXPECTING ARRAY OR OBJECT TYPE\n");
327 errno = EINVAL;
328 return -1;
329 }
330 out->gen = ((struct node *)buf)->gen_type;
331 out->depth = 0;
332 out->node_ofs[0] = (u32)ofs;
333 out->node_i[0] = 0;
334
335 while (node->child_ofs[0]) { // has children, travel down
336 u32 next_node_ofs = node->child_ofs[0];
337
338 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
339
340 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
341 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
342 errno = EBADMSG;
343 return -1;
344 }
345 if (LITE3_UNLIKELY(++out->depth > LITE3_TREE_HEIGHT_MAX)) {
346 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
347 errno = EBADMSG;
348 return -1;
349 }
350 if (LITE3_UNLIKELY((size_t)next_node_ofs > buflen - LITE3_NODE_SIZE)) {
351 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
352 errno = EFAULT;
353 return -1;
354 }
355 out->node_ofs[out->depth] = next_node_ofs;
356 out->node_i[out->depth] = 0;
357 }
358 #ifdef LITE3_PREFETCHING
359 __builtin_prefetch(buf + node->kv_ofs[0], 0, 0); // prefetch first few items
360 __builtin_prefetch(buf + node->kv_ofs[0] + 64, 0, 0);
361 __builtin_prefetch(buf + node->kv_ofs[1], 0, 0);
362 __builtin_prefetch(buf + node->kv_ofs[1] + 64, 0, 0);
363 __builtin_prefetch(buf + node->kv_ofs[2], 0, 0);
364 __builtin_prefetch(buf + node->kv_ofs[2] + 64, 0, 0);
365 #endif
366 return 0;
367}
368
369int lite3_iter_next(const unsigned char *buf, size_t buflen, lite3_iter *iter, lite3_str *out_key, size_t *out_val_ofs)
370{
371 if (LITE3_UNLIKELY(iter->gen != ((struct node *)buf)->gen_type)) {
372 LITE3_PRINT_ERROR("ITERATOR INVALID: iter->gen != node->gen_type (BUFFER MUTATION INVALIDATES ITERATORS)\n");
373 errno = EINVAL;
374 return -1;
375 }
376
377 struct node *restrict node = __builtin_assume_aligned((struct node *)(buf + iter->node_ofs[iter->depth]), LITE3_NODE_ALIGNMENT);
378
379 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
380 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
381 errno = EBADMSG;
382 return -1;
383 }
384
385 enum lite3_type type = node->gen_type & LITE3_NODE_TYPE_MASK;
386 if (LITE3_UNLIKELY(!(type == LITE3_TYPE_OBJECT || type == LITE3_TYPE_ARRAY))) {
387 LITE3_PRINT_ERROR("INVALID ARGUMENT: EXPECTING ARRAY OR OBJECT TYPE\n");
388 errno = EINVAL;
389 return -1;
390 }
391 if (iter->depth == 0 && (iter->node_i[iter->depth] == (node->size_kc & LITE3_NODE_KEY_COUNT_MASK))) { // key_count reached, done
392 return LITE3_ITER_DONE;
393 }
394 size_t target_ofs = node->kv_ofs[iter->node_i[iter->depth]];
395
396 int ret;
397 if (type == LITE3_TYPE_OBJECT && out_key) { // write back key if not NULL
398 size_t key_tag_size;
399 size_t key_start_ofs = target_ofs;
400 if ((ret = _verify_key(buf, buflen, NULL, 0, 0, &target_ofs, &key_tag_size)) < 0)
401 return ret;
402 out_key->gen = iter->gen;
403 out_key->len = 0;
404 memcpy(&out_key->len, buf + key_start_ofs, key_tag_size);
405 --out_key->len; // Lite³ stores string size including NULL-terminator. Correction required for public API.
406 out_key->ptr = (const char *)(buf + key_start_ofs + key_tag_size);
407 }
408 if (out_val_ofs) { // write back val if not NULL
409 size_t val_start_ofs = target_ofs;
410 if ((ret = _verify_val(buf, buflen, &target_ofs)) < 0)
411 return ret;
412 *out_val_ofs = val_start_ofs;
413 }
414
415 ++iter->node_i[iter->depth];
416
417 while (node->child_ofs[iter->node_i[iter->depth]]) { // has children, travel down
418 u32 next_node_ofs = node->child_ofs[iter->node_i[iter->depth]];
419
420 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
421
422 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
423 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
424 errno = EBADMSG;
425 return -1;
426 }
427 if (LITE3_UNLIKELY(++iter->depth > LITE3_TREE_HEIGHT_MAX)) {
428 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
429 errno = EBADMSG;
430 return -1;
431 }
432 if (LITE3_UNLIKELY((size_t)next_node_ofs > buflen - LITE3_NODE_SIZE)) {
433 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
434 errno = EFAULT;
435 return -1;
436 }
437 iter->node_ofs[iter->depth] = next_node_ofs;
438 iter->node_i[iter->depth] = 0;
439 }
440 while (iter->depth > 0 && (iter->node_i[iter->depth] == (node->size_kc & LITE3_NODE_KEY_COUNT_MASK))) { // key_count reached, go up
441 --iter->depth;
442 node = __builtin_assume_aligned((struct node *)(buf + iter->node_ofs[iter->depth]), LITE3_NODE_ALIGNMENT);
443
444 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
445 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
446 errno = EBADMSG;
447 return -1;
448 }
449 #ifdef LITE3_PREFETCHING
450 __builtin_prefetch(buf + node->child_ofs[(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK], 0, 2); // prefetch next nodes
451 __builtin_prefetch(buf + node->child_ofs[(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 2);
452 __builtin_prefetch(buf + node->child_ofs[(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK], 0, 2);
453 __builtin_prefetch(buf + node->child_ofs[(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 2);
454 #endif
455 }
456 #ifdef LITE3_PREFETCHING
457 __builtin_prefetch(buf + node->kv_ofs[(iter->node_i[iter->depth] + 0) & LITE3_NODE_KEY_COUNT_MASK], 0, 0); // prefetch next items
458 __builtin_prefetch(buf + node->kv_ofs[(iter->node_i[iter->depth] + 0) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 0);
459 __builtin_prefetch(buf + node->kv_ofs[(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK], 0, 0);
460 __builtin_prefetch(buf + node->kv_ofs[(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 0);
461 __builtin_prefetch(buf + node->kv_ofs[(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK], 0, 0);
462 __builtin_prefetch(buf + node->kv_ofs[(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 0);
463 #endif
464 return LITE3_ITER_ITEM;
465}
466
467
468static inline void _lite3_init_impl(unsigned char *buf, size_t ofs, enum lite3_type type)
469{
470 LITE3_PRINT_DEBUG("INITIALIZE %s\n", type == LITE3_TYPE_OBJECT ? "OBJECT" : "ARRAY");
471
472 struct node *node = (struct node *)(buf + ofs);
473 node->gen_type = type & LITE3_NODE_TYPE_MASK;
474 node->size_kc = 0x00;
475 #ifdef LITE3_ZERO_MEM_EXTRA
476 memset(node->hashes, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->hashes));
477 memset(node->kv_ofs, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->kv_ofs));
478 #endif
479 memset(node->child_ofs, 0x00, sizeof(((struct node *)0)->child_ofs));
480}
481
482int lite3_init_obj(unsigned char *buf, size_t *restrict out_buflen, size_t bufsz)
483{
484 if (LITE3_UNLIKELY(bufsz < LITE3_NODE_SIZE)) {
485 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz < LITE3_NODE_SIZE\n");
486 errno = EINVAL;
487 return -1;
488 }
489 _lite3_init_impl(buf, 0, LITE3_TYPE_OBJECT);
490 *out_buflen = LITE3_NODE_SIZE;
491 return 0;
492}
493
494int lite3_init_arr(unsigned char *buf, size_t *restrict out_buflen, size_t bufsz)
495{
496 if (LITE3_UNLIKELY(bufsz < LITE3_NODE_SIZE)) {
497 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz < LITE3_NODE_SIZE\n");
498 errno = EINVAL;
499 return -1;
500 }
501 _lite3_init_impl(buf, 0, LITE3_TYPE_ARRAY);
502 *out_buflen = LITE3_NODE_SIZE;
503 return 0;
504}
505
506/*
507 Inserts entry into the Lite³ structure to prepare for writing of the actual value.
508 - Returns 0 on success
509 - Returns < 0 on failure
510
511 [ NOTE ] This function expects the caller to write to:
512 1) `val->type`: the value type (bytes written should equal to `LITE3_VAL_SIZE`)
513 2) `val->val`: the actual value (bytes written should equal `val_len`)
514 This has the advantage that the responsibility of type-specific logic is also moved to the caller.
515 Otherwise, this function would have to contain branches to account for all types.
516*/
517int lite3_set_impl(
518 unsigned char *buf, // buffer pointer
519 size_t *restrict inout_buflen, // buffer used length (bytes, inout value)
520 size_t ofs, // start offset (0 == root)
521 size_t bufsz, // buffer max size (bytes)
522 const char *restrict key, // key string (string, pass NULL when inserting in array)
523 lite3_key_data key_data, // key data struct
524 size_t val_len, // value length (bytes)
525 lite3_val **out) // value entry pointer (out pointer)
526{
527 #ifdef LITE3_DEBUG
528 if (*(buf + ofs) == LITE3_TYPE_OBJECT) {
529 LITE3_PRINT_DEBUG("SET\tkey: %s\n", key);
530 } else if (*(buf + ofs) == LITE3_TYPE_ARRAY) {
531 LITE3_PRINT_DEBUG("SET\tindex: %u\n", key_data.hash);
532 } else {
533 LITE3_PRINT_DEBUG("SET INVALID: EXPECTING ARRAY OR OBJECT TYPE\n");
534 }
535 #endif
536
537 size_t key_tag_size = (size_t)((!!(key_data.size >> (16 - LITE3_KEY_TAG_KEY_SIZE_SHIFT)) << 1)
538 + !!(key_data.size >> (8 - LITE3_KEY_TAG_KEY_SIZE_SHIFT))
539 + !!key_data.size);
540 size_t base_entry_size = key_tag_size + (size_t)key_data.size + LITE3_VAL_SIZE + val_len;
541
542 struct node *restrict root = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
543
544 if (LITE3_UNLIKELY(((uintptr_t)root & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
545 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
546 errno = EBADMSG;
547 return -1;
548 }
549
550 u32 gen = root->gen_type >> LITE3_NODE_GEN_SHIFT;
551 ++gen;
552 root->gen_type = (root->gen_type & ~LITE3_NODE_GEN_MASK) | (gen << LITE3_NODE_GEN_SHIFT);
553
554 uint32_t probe_attempts = key ? LITE3_HASH_PROBE_MAX : 1U;
555 for (uint32_t attempt = 0; attempt < probe_attempts; attempt++) {
556
557 lite3_key_data attempt_key = key_data;
558 attempt_key.hash = key_data.hash + attempt * attempt;
559 #ifdef LITE3_DEBUG
560 LITE3_PRINT_DEBUG("probe attempt: %u\thash: %u\n", attempt, attempt_key.hash);
561 #endif
562
563 size_t entry_size = base_entry_size;
564 struct node *restrict parent = NULL;
565 struct node *restrict node = root;
566
567 int key_count;
568 int i;
569 int node_walks = 0;
570
571 while (1) {
572 if ((node->size_kc & LITE3_NODE_KEY_COUNT_MASK) == LITE3_NODE_KEY_COUNT_MAX) { // node full, need to split
573
574 size_t buflen_aligned = (*inout_buflen + LITE3_NODE_ALIGNMENT_MASK) & ~(size_t)LITE3_NODE_ALIGNMENT_MASK; // next multiple of LITE3_NODE_ALIGNMENT
575 size_t new_node_size = parent ? LITE3_NODE_SIZE : 2 * LITE3_NODE_SIZE;
576
577 if (LITE3_UNLIKELY(new_node_size > bufsz || buflen_aligned > bufsz - new_node_size)) {
578 LITE3_PRINT_ERROR("NO BUFFER SPACE FOR NODE SPLIT\n");
579 errno = ENOBUFS;
580 return -1;
581 }
582 *inout_buflen = buflen_aligned;
583 // TODO: add lost bytes from alignment to GC index
584 if (!parent) { // if root split, create new root
585 LITE3_PRINT_DEBUG("NEW ROOT\n");
586 memcpy(buf + *inout_buflen, node, LITE3_NODE_SIZE);
587 node = __builtin_assume_aligned((struct node *)(buf + *inout_buflen), LITE3_NODE_ALIGNMENT);
588
589 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
590 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
591 errno = EBADMSG;
592 return -1;
593 }
594 parent = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
595
596 if (LITE3_UNLIKELY(((uintptr_t)parent & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
597 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
598 errno = EBADMSG;
599 return -1;
600 }
601 #ifdef LITE3_ZERO_MEM_EXTRA
602 memset(parent->hashes, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->hashes));
603 memset(parent->kv_ofs, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->kv_ofs));
604 memset(parent->child_ofs, 0x00, sizeof(((struct node *)0)->child_ofs));
605 #endif
606 parent->size_kc &= ~LITE3_NODE_KEY_COUNT_MASK; // set key_count to 0
607 parent->child_ofs[0] = (u32)*inout_buflen; // insert node as child of new root
608 *inout_buflen += LITE3_NODE_SIZE;
609 key_count = 0;
610 i = 0;
611 }
612 LITE3_PRINT_DEBUG("SPLIT NODE\n");
613 for (int j = key_count; j > i; j--) { // shift parent array before separator insert
614 parent->hashes[j] = parent->hashes[j - 1];
615 parent->kv_ofs[j] = parent->kv_ofs[j - 1];
616 parent->child_ofs[j + 1] = parent->child_ofs[j];
617 }
618 parent->hashes[i] = node->hashes[LITE3_NODE_KEY_COUNT_MIN]; // insert new separator key in parent
619 parent->kv_ofs[i] = node->kv_ofs[LITE3_NODE_KEY_COUNT_MIN];
620 parent->child_ofs[i + 1] = (u32)*inout_buflen; // insert sibling as child in parent
621 parent->size_kc = (parent->size_kc & ~LITE3_NODE_KEY_COUNT_MASK)
622 | ((parent->size_kc + 1) & LITE3_NODE_KEY_COUNT_MASK); // key_count++
623 #ifdef LITE3_ZERO_MEM_EXTRA
624 node->hashes[LITE3_NODE_KEY_COUNT_MIN] = LITE3_ZERO_MEM_32;
625 node->kv_ofs[LITE3_NODE_KEY_COUNT_MIN] = LITE3_ZERO_MEM_32;
626 #endif
627 struct node *restrict sibling = __builtin_assume_aligned((struct node *)(buf + *inout_buflen), LITE3_NODE_ALIGNMENT);
628
629 if (LITE3_UNLIKELY(((uintptr_t)sibling & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
630 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
631 errno = EBADMSG;
632 return -1;
633 }
634 #ifdef LITE3_ZERO_MEM_EXTRA
635 memset(sibling->hashes, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->hashes));
636 memset(sibling->kv_ofs, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->kv_ofs));
637 #endif
638 sibling->gen_type = ((struct node *)(buf + ofs))->gen_type & LITE3_NODE_TYPE_MASK;
639 sibling->size_kc = LITE3_NODE_KEY_COUNT_MIN & LITE3_NODE_KEY_COUNT_MASK;
640 node->size_kc = LITE3_NODE_KEY_COUNT_MIN & LITE3_NODE_KEY_COUNT_MASK;
641 memset(sibling->child_ofs, 0x00, sizeof(((struct node *)0)->child_ofs));
642 sibling->child_ofs[0] = node->child_ofs[LITE3_NODE_KEY_COUNT_MIN + 1]; // take child from node
643 node->child_ofs[LITE3_NODE_KEY_COUNT_MIN + 1] = 0x00;
644 for (int j = 0; j < LITE3_NODE_KEY_COUNT_MIN; j++) { // copy half of node's keys to sibling
645 sibling->hashes[j] = node->hashes[j + LITE3_NODE_KEY_COUNT_MIN + 1];
646 sibling->kv_ofs[j] = node->kv_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 1];
647 sibling->child_ofs[j + 1] = node->child_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 2];
648 #ifdef LITE3_ZERO_MEM_EXTRA
649 node->hashes[j + LITE3_NODE_KEY_COUNT_MIN + 1] = LITE3_ZERO_MEM_32;
650 node->kv_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 1] = LITE3_ZERO_MEM_32;
651 node->child_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 2] = 0x00000000;
652 #endif
653 }
654 *inout_buflen += LITE3_NODE_SIZE;
655 if (attempt_key.hash > parent->hashes[i]) { // sibling has target key? then we follow
656 node = __builtin_assume_aligned(sibling, LITE3_NODE_ALIGNMENT);
657
658 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
659 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
660 errno = EBADMSG;
661 return -1;
662 }
663 } else if (attempt_key.hash == parent->hashes[i]) {
664 node = __builtin_assume_aligned(parent, LITE3_NODE_ALIGNMENT);
665 LITE3_PRINT_DEBUG("GOTO SKIP\n");
666 goto key_match_skip;
667 }
668 }
669
670 key_count = node->size_kc & LITE3_NODE_KEY_COUNT_MASK;
671 i = 0;
672 while (i < key_count && node->hashes[i] < attempt_key.hash)
673 i++;
674
675 LITE3_PRINT_DEBUG("i: %i\tkc: %i\tnode->hashes[i]: %u\n", i, key_count, node->hashes[i]);
676
677 if (i < key_count && node->hashes[i] == attempt_key.hash) { // matching key found, already exists?
678key_match_skip:
679 size_t target_ofs = node->kv_ofs[i];
680 size_t key_start_ofs = target_ofs;
681 if (key) {
682 int verify = _verify_key(buf, *inout_buflen, key, (size_t)attempt_key.size, key_tag_size, &target_ofs, NULL);
683 if (verify == LITE3_VERIFY_KEY_HASH_COLLISION)
684 break; // try next probe
685 if (verify < 0)
686 return -1;
687 }
688 size_t val_start_ofs = target_ofs;
689 if (_verify_val(buf, *inout_buflen, &target_ofs) < 0)
690 return -1;
691 if (val_len >= target_ofs - val_start_ofs) { // value is too large, we must append
692 size_t alignment_mask = val_len == lite3_type_sizes[LITE3_TYPE_OBJECT] ? (size_t)LITE3_NODE_ALIGNMENT_MASK : 0;
693 size_t unaligned_val_ofs = *inout_buflen + key_tag_size + (size_t)attempt_key.size;
694 size_t alignment_padding = ((unaligned_val_ofs + alignment_mask) & ~alignment_mask) - unaligned_val_ofs;
695 entry_size += alignment_padding;
696 if (LITE3_UNLIKELY(entry_size > bufsz || *inout_buflen > bufsz - entry_size)) {
697 LITE3_PRINT_ERROR("NO BUFFER SPACE FOR ENTRY INSERTION\n");
698 errno = ENOBUFS;
699 return -1;
700 }
701 (void)key_start_ofs; // silence unused variable warning
702 #ifdef LITE3_ZERO_MEM_DELETED
703 memset(buf + node->kv_ofs[i], LITE3_ZERO_MEM_8, target_ofs - key_start_ofs); // zero out key + value
704 #endif
705 #ifdef LITE3_ZERO_MEM_EXTRA
706 memset(buf + *inout_buflen, LITE3_ZERO_MEM_8, alignment_padding);
707 #endif
708 *inout_buflen += alignment_padding;
709 node->kv_ofs[i] = (u32)*inout_buflen;
710 goto insert_append;
711 // TODO: add lost bytes to GC index
712 }
713 #ifdef LITE3_ZERO_MEM_DELETED
714 memset(buf + val_start_ofs, LITE3_ZERO_MEM_8, target_ofs - val_start_ofs); // zero out value
715 #endif
716 *out = (lite3_val *)(buf + val_start_ofs); // caller overwrites value in place
717 // TODO: add lost bytes to GC index
718 return 0;
719 }
720 if (node->child_ofs[0]) { // if children, walk to next node
721 size_t next_node_ofs = (size_t)node->child_ofs[i];
722
723 parent = __builtin_assume_aligned(node, LITE3_NODE_ALIGNMENT);
724 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
725
726 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
727 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
728 errno = EBADMSG;
729 return -1;
730 }
731 if (LITE3_UNLIKELY(next_node_ofs > *inout_buflen - LITE3_NODE_SIZE)) {
732 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
733 errno = EFAULT;
734 return -1;
735 }
736 if (LITE3_UNLIKELY(++node_walks > LITE3_TREE_HEIGHT_MAX)) {
737 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
738 errno = EBADMSG;
739 return -1;
740 }
741 } else { // insert the kv-pair
742 size_t alignment_mask = val_len == lite3_type_sizes[LITE3_TYPE_OBJECT] ? (size_t)LITE3_NODE_ALIGNMENT_MASK : 0;
743 size_t unaligned_val_ofs = *inout_buflen + key_tag_size + (size_t)attempt_key.size;
744 size_t alignment_padding = ((unaligned_val_ofs + alignment_mask) & ~alignment_mask) - unaligned_val_ofs;
745 entry_size += alignment_padding;
746 if (LITE3_UNLIKELY(entry_size > bufsz || *inout_buflen > bufsz - entry_size)) {
747 LITE3_PRINT_ERROR("NO BUFFER SPACE FOR ENTRY INSERTION\n");
748 errno = ENOBUFS;
749 return -1;
750 }
751 for (int j = key_count; j > i; j--) {
752 node->hashes[j] = node->hashes[j - 1];
753 node->kv_ofs[j] = node->kv_ofs[j - 1];
754 }
755 LITE3_PRINT_DEBUG("INSERTING HASH: %u\ti: %i\n", attempt_key.hash, i);
756 node->hashes[i] = attempt_key.hash;
757 node->size_kc = (node->size_kc & ~LITE3_NODE_KEY_COUNT_MASK)
758 | ((node->size_kc + 1) & LITE3_NODE_KEY_COUNT_MASK); // key_count++
759 #ifdef LITE3_ZERO_MEM_EXTRA
760 memset(buf + *inout_buflen, LITE3_ZERO_MEM_8, alignment_padding);
761 #endif
762 *inout_buflen += alignment_padding;
763 node->kv_ofs[i] = (u32)*inout_buflen;
764
765 root = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT); // set node to root
766 u32 size = root->size_kc >> LITE3_NODE_SIZE_SHIFT;
767 ++size;
768 root->size_kc = (root->size_kc & ~LITE3_NODE_SIZE_MASK) | (size << LITE3_NODE_SIZE_SHIFT); // node size++
769 goto insert_append;
770 }
771 }
772 continue;
773insert_append:
774 if (key) {
775 size_t key_size_tmp = (attempt_key.size << LITE3_KEY_TAG_KEY_SIZE_SHIFT) | (key_tag_size - 1);
776 memcpy(buf + *inout_buflen, &key_size_tmp, key_tag_size);
777 *inout_buflen += key_tag_size;
778 memcpy(buf + *inout_buflen, key, (size_t)attempt_key.size);
779 *inout_buflen += (size_t)attempt_key.size;
780 }
781 *out = (lite3_val *)(buf + *inout_buflen);
782 *inout_buflen += LITE3_VAL_SIZE + val_len;
783 LITE3_PRINT_DEBUG("OK\n");
784 return 0;
785 }
786 LITE3_PRINT_ERROR("LITE3_HASH_PROBE_MAX LIMIT REACHED\n");
787 errno = EINVAL;
788 return -1;
789}
790
791int lite3_set_obj_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, const char *restrict key, lite3_key_data key_data, size_t *restrict out_ofs)
792{
793 lite3_val *val;
794 int ret;
795 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, key, key_data, lite3_type_sizes[LITE3_TYPE_OBJECT], &val)) < 0)
796 return ret;
797 size_t init_ofs = (size_t)((u8 *)val - buf);
798 if (out_ofs)
799 *out_ofs = init_ofs;
800 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_OBJECT);
801 return ret;
802}
803
804int lite3_set_arr_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, const char *restrict key, lite3_key_data key_data, size_t *restrict out_ofs)
805{
806 lite3_val *val;
807 int ret;
808 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, key, key_data, lite3_type_sizes[LITE3_TYPE_ARRAY], &val)) < 0)
809 return ret;
810 size_t init_ofs = (size_t)((u8 *)val - buf);
811 if (out_ofs)
812 *out_ofs = init_ofs;
813 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_ARRAY);
814 return ret;
815}
816
817int lite3_arr_append_obj_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, size_t *restrict out_ofs)
818{
819 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
820 lite3_key_data key_data = {
821 .hash = size,
822 .size = 0,
823 };
824 lite3_val *val;
825 int ret;
826 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_OBJECT], &val)) < 0)
827 return ret;
828 size_t init_ofs = (size_t)((u8 *)val - buf);
829 if (out_ofs)
830 *out_ofs = init_ofs;
831 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_OBJECT);
832 return ret;
833}
834
835int lite3_arr_append_arr_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, size_t *restrict out_ofs)
836{
837 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
838 lite3_key_data key_data = {
839 .hash = size,
840 .size = 0,
841 };
842 lite3_val *val;
843 int ret;
844 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_ARRAY], &val)) < 0)
845 return ret;
846 size_t init_ofs = (size_t)((u8 *)val - buf);
847 if (out_ofs)
848 *out_ofs = init_ofs;
849 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_ARRAY);
850 return ret;
851}
852
853int lite3_arr_set_obj_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, uint32_t index, size_t *restrict out_ofs)
854{
855 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
856 if (LITE3_UNLIKELY(index > size)) {
857 LITE3_PRINT_ERROR("INVALID ARGUMENT: ARRAY INDEX %u OUT OF BOUNDS (size == %u)\n", index, size);
858 errno = EINVAL;
859 return -1;
860 }
861 lite3_key_data key_data = {
862 .hash = index,
863 .size = 0,
864 };
865 lite3_val *val;
866 int ret;
867 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_OBJECT], &val)) < 0)
868 return ret;
869 size_t init_ofs = (size_t)((u8 *)val - buf);
870 if (out_ofs)
871 *out_ofs = init_ofs;
872 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_OBJECT);
873 return ret;
874}
875
876int lite3_arr_set_arr_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, uint32_t index, size_t *restrict out_ofs)
877{
878 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
879 if (LITE3_UNLIKELY(index > size)) {
880 LITE3_PRINT_ERROR("INVALID ARGUMENT: ARRAY INDEX %u OUT OF BOUNDS (size == %u)\n", index, size);
881 errno = EINVAL;
882 return -1;
883 }
884 lite3_key_data key_data = {
885 .hash = index,
886 .size = 0,
887 };
888 lite3_val *val;
889 int ret;
890 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_ARRAY], &val)) < 0)
891 return ret;
892 size_t init_ofs = (size_t)((u8 *)val - buf);
893 if (out_ofs)
894 *out_ofs = init_ofs;
895 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_ARRAY);
896 return ret;
897}
#define LITE3_TREE_HEIGHT_MAX
Maximum B-tree height.
Definition lite3.h:336
#define LITE3_HASH_PROBE_MAX
Enable hash probing to tolerate 32-bit hash collisions.
Definition lite3.h:370
#define LITE3_NODE_ALIGNMENT
B-tree node alignment configuration.
Definition lite3.h:299
#define LITE3_NODE_SIZE
B-tree node size setting.
Definition lite3.h:321
#define LITE3_NODE_SIZE_KC_OFFSET
Offset of the size_kc field inside struct node.
Definition lite3.h:350
#define LITE3_ITER_ITEM
Return value of lite3_iter_next(); iterator produced an item, continue;.
Definition lite3.h:2634
#define LITE3_ITER_DONE
Return value of lite3_iter_next(); iterator finished; stop.
Definition lite3.h:2636
int lite3_iter_next(const unsigned char *buf, size_t buflen, lite3_iter *iter, lite3_str *out_key, size_t *out_val_ofs)
Get the next item from a lite3 iterator.
Definition lite3.c:369
lite3_type
enum containing all Lite³ types
Definition lite3.h:528
@ LITE3_TYPE_ARRAY
maps to 'array' type in JSON
Definition lite3.h:536
@ LITE3_TYPE_INVALID
any type value equal or greater than this is considered invalid
Definition lite3.h:537
@ LITE3_TYPE_STRING
maps to 'string' type in JSON
Definition lite3.h:534
@ LITE3_TYPE_BYTES
coverted to base64 string in JSON
Definition lite3.h:533
@ LITE3_TYPE_OBJECT
maps to 'object' type in JSON
Definition lite3.h:535
Lite³ Buffer API Header.
Struct containing iterator state.
Definition lite3.h:2644
Struct holding a reference to a string inside a Lite³ buffer.
Definition lite3.h:619
uint32_t len
char array length (characters, exclusive of NULL-terminator)
Definition lite3.h:621
const char * ptr
char array pointer to string inside Lite³ buffer
Definition lite3.h:622
uint32_t gen
generation of the Lite³ buffer when this struct was returned
Definition lite3.h:620
Struct representing a value inside a Lite³ buffer.
Definition lite3.h:549
Definition lite3.c:77