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_MIN > buflen || *inout_ofs > buflen - LITE3_KEY_TAG_SIZE_MIN)) {
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
141 if (LITE3_UNLIKELY(_key_tag_size > buflen || *inout_ofs > buflen - _key_tag_size)) {
142 LITE3_PRINT_ERROR("KEY ENTRY OUT OF BOUNDS\n");
143 errno = EFAULT;
144 return -1;
145 }
146 if (key_tag_size) {
147 if (key_tag_size != _key_tag_size) {
148 LITE3_PRINT_ERROR("KEY TAG SIZE DOES NOT MATCH\n");
149 errno = EINVAL;
150 return -1;
151 }
152 }
153 size_t _key_size = 0;
154 memcpy(&_key_size, buf + *inout_ofs, _key_tag_size);
155 _key_size >>= LITE3_KEY_TAG_KEY_SIZE_SHIFT;
156 *inout_ofs += _key_tag_size;
157
158 if (LITE3_UNLIKELY(_key_size > buflen || *inout_ofs > buflen - _key_size)) {
159 LITE3_PRINT_ERROR("KEY ENTRY OUT OF BOUNDS\n");
160 errno = EFAULT;
161 return -1;
162 }
163 if (key_size) {
164 int cmp = memcmp(
165 (const char *)(buf + *inout_ofs),
166 key,
167 (key_size < _key_size) ? key_size : _key_size
168 );
169 if (LITE3_UNLIKELY(cmp != 0)) {
170 LITE3_PRINT_ERROR("HASH COLLISION\n");
171 return LITE3_VERIFY_KEY_HASH_COLLISION;
172 }
173 }
174 *inout_ofs += _key_size;
175 if (out_key_tag_size)
176 *out_key_tag_size = _key_tag_size;
177 return LITE3_VERIFY_KEY_OK;
178}
179
180/*
181 Verify a value inside the buffer to ensure readers don't go out of bounds.
182 - Returns 0 on success
183 - Returns < 0 on failure
184
185 [ NOTE ] For internal use only.
186*/
187static inline int _verify_val(
188 const u8 *buf, // buffer pointer
189 size_t buflen, // buffer length (bytes)
190 size_t *restrict inout_ofs) // val entry offset (relative to *buf)
191{
192 if (LITE3_UNLIKELY(LITE3_VAL_SIZE > buflen || *inout_ofs > buflen - LITE3_VAL_SIZE)) {
193 LITE3_PRINT_ERROR("VALUE OUT OF BOUNDS\n");
194 errno = EFAULT;
195 return -1;
196 }
197 enum lite3_type type = (enum lite3_type)(*(buf + *inout_ofs));
198
199 if (LITE3_UNLIKELY(type >= LITE3_TYPE_INVALID)) {
200 LITE3_PRINT_ERROR("VALUE TYPE INVALID\n");
201 errno = EINVAL;
202 return -1;
203 }
204 size_t _val_entry_size = LITE3_VAL_SIZE + lite3_type_sizes[type];
205
206 if (LITE3_UNLIKELY(_val_entry_size > buflen || *inout_ofs > buflen - _val_entry_size)) {
207 LITE3_PRINT_ERROR("VALUE OUT OF BOUNDS\n");
208 errno = EFAULT;
209 return -1;
210 }
211 if (type == LITE3_TYPE_STRING || type == LITE3_TYPE_BYTES) { // extra check required for str/bytes
212 size_t byte_count = 0;
213 memcpy(&byte_count, buf + *inout_ofs + LITE3_VAL_SIZE, lite3_type_sizes[LITE3_TYPE_BYTES]);
214 _val_entry_size += byte_count;
215 if (LITE3_UNLIKELY(_val_entry_size > buflen || *inout_ofs > buflen - _val_entry_size)) {
216 LITE3_PRINT_ERROR("VALUE OUT OF BOUNDS\n");
217 errno = EFAULT;
218 return -1;
219 }
220 }
221 *inout_ofs += _val_entry_size;
222 return 0;
223}
224
225int lite3_get_impl(
226 const unsigned char *buf, // buffer pointer
227 size_t buflen, // buffer length (bytes)
228 size_t ofs, // start offset (0 == root)
229 const char *restrict key, // key pointer (string)
230 lite3_key_data key_data, // key data struct
231 lite3_val **out) // value entry pointer (out pointer)
232{
233 #ifdef LITE3_DEBUG
234 if (*(buf + ofs) == LITE3_TYPE_OBJECT) {
235 LITE3_PRINT_DEBUG("GET\tkey: %s\n", key);
236 } else if (*(buf + ofs) == LITE3_TYPE_ARRAY) {
237 LITE3_PRINT_DEBUG("GET\tindex: %u\n", key_data.hash);
238 } else {
239 LITE3_PRINT_DEBUG("GET INVALID: EXPECTING ARRAY OR OBJECT TYPE\n");
240 }
241 #endif
242
243 size_t key_tag_size = (size_t)((!!(key_data.size >> (16 - LITE3_KEY_TAG_KEY_SIZE_SHIFT)) << 1)
244 + !!(key_data.size >> (8 - LITE3_KEY_TAG_KEY_SIZE_SHIFT))
245 + !!key_data.size);
246
247 uint32_t probe_attempts = key ? LITE3_HASH_PROBE_MAX : 1U;
248 for (uint32_t attempt = 0; attempt < probe_attempts; attempt++) {
249
250 lite3_key_data attempt_key = key_data;
251 attempt_key.hash = key_data.hash + attempt * attempt;
252 // LITE3_PRINT_DEBUG("probe attempt: %u\thash: %u\n", attempt, attempt_key.hash);
253
254 struct node *restrict node = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
255
256 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
257 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
258 errno = EBADMSG;
259 return -1;
260 }
261
262 int key_count;
263 int i;
264 int node_walks = 0;
265 while (1) {
266 key_count = node->size_kc & LITE3_NODE_KEY_COUNT_MASK;
267 i = 0;
268 while (i < key_count && node->hashes[i] < attempt_key.hash)
269 i++;
270 if (i < key_count && node->hashes[i] == attempt_key.hash) { // target key found
271 size_t target_ofs = node->kv_ofs[i];
272 if (key) {
273 int verify = _verify_key(buf, buflen, key, (size_t)attempt_key.size, key_tag_size, &target_ofs, NULL);
274 if (verify == LITE3_VERIFY_KEY_HASH_COLLISION)
275 break; // try next probe
276 if (verify < 0)
277 return -1;
278 }
279 size_t val_start_ofs = target_ofs;
280 if (_verify_val(buf, buflen, &target_ofs) < 0)
281 return -1;
282 *out = (lite3_val *)(buf + val_start_ofs);
283 return 0;
284 }
285 if (node->child_ofs[0]) { // if children, walk to next node
286 size_t next_node_ofs = (size_t)node->child_ofs[i];
287 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
288
289 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
290 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
291 errno = EBADMSG;
292 return -1;
293 }
294 if (LITE3_UNLIKELY(next_node_ofs > buflen - LITE3_NODE_SIZE)) {
295 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
296 errno = EFAULT;
297 return -1;
298 }
299 if (LITE3_UNLIKELY(++node_walks > LITE3_TREE_HEIGHT_MAX)) {
300 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
301 errno = EBADMSG;
302 return -1;
303 }
304 } else {
305 LITE3_PRINT_ERROR("KEY NOT FOUND\n");
306 errno = ENOENT;
307 return -1;
308 }
309 }
310 }
311 LITE3_PRINT_ERROR("LITE3_HASH_PROBE_MAX LIMIT REACHED\n");
312 errno = EINVAL;
313 return -1;
314}
315
316int lite3_iter_create_impl(const unsigned char *buf, size_t buflen, size_t ofs, lite3_iter *out)
317{
318 LITE3_PRINT_DEBUG("CREATE ITER\n");
319
320 struct node *restrict node = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
321
322 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
323 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
324 errno = EBADMSG;
325 return -1;
326 }
327
328 enum lite3_type type = node->gen_type & LITE3_NODE_TYPE_MASK;
329 if (LITE3_UNLIKELY(!(type == LITE3_TYPE_OBJECT || type == LITE3_TYPE_ARRAY))) {
330 LITE3_PRINT_ERROR("INVALID ARGUMENT: EXPECTING ARRAY OR OBJECT TYPE\n");
331 errno = EINVAL;
332 return -1;
333 }
334 out->gen = ((struct node *)buf)->gen_type;
335 out->depth = 0;
336 out->node_ofs[0] = (u32)ofs;
337 out->node_i[0] = 0;
338
339 while (node->child_ofs[0]) { // has children, travel down
340 u32 next_node_ofs = node->child_ofs[0];
341
342 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
343
344 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
345 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
346 errno = EBADMSG;
347 return -1;
348 }
349 if (LITE3_UNLIKELY(++out->depth > LITE3_TREE_HEIGHT_MAX)) {
350 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
351 errno = EBADMSG;
352 return -1;
353 }
354 if (LITE3_UNLIKELY((size_t)next_node_ofs > buflen - LITE3_NODE_SIZE)) {
355 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
356 errno = EFAULT;
357 return -1;
358 }
359 out->node_ofs[out->depth] = next_node_ofs;
360 out->node_i[out->depth] = 0;
361 }
362 #ifdef LITE3_PREFETCHING
363 __builtin_prefetch(buf + node->kv_ofs[0], 0, 0); // prefetch first few items
364 __builtin_prefetch(buf + node->kv_ofs[0] + 64, 0, 0);
365 __builtin_prefetch(buf + node->kv_ofs[1], 0, 0);
366 __builtin_prefetch(buf + node->kv_ofs[1] + 64, 0, 0);
367 __builtin_prefetch(buf + node->kv_ofs[2], 0, 0);
368 __builtin_prefetch(buf + node->kv_ofs[2] + 64, 0, 0);
369 #endif
370 return 0;
371}
372
373int lite3_iter_next(const unsigned char *buf, size_t buflen, lite3_iter *iter, lite3_str *out_key, size_t *out_val_ofs)
374{
375 if (LITE3_UNLIKELY(iter->gen != ((struct node *)buf)->gen_type)) {
376 LITE3_PRINT_ERROR("ITERATOR INVALID: iter->gen != node->gen_type (BUFFER MUTATION INVALIDATES ITERATORS)\n");
377 errno = EINVAL;
378 return -1;
379 }
380
381 struct node *restrict node = __builtin_assume_aligned((struct node *)(buf + iter->node_ofs[iter->depth]), LITE3_NODE_ALIGNMENT);
382
383 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
384 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
385 errno = EBADMSG;
386 return -1;
387 }
388
389 enum lite3_type type = node->gen_type & LITE3_NODE_TYPE_MASK;
390 if (LITE3_UNLIKELY(!(type == LITE3_TYPE_OBJECT || type == LITE3_TYPE_ARRAY))) {
391 LITE3_PRINT_ERROR("INVALID ARGUMENT: EXPECTING ARRAY OR OBJECT TYPE\n");
392 errno = EINVAL;
393 return -1;
394 }
395 if (iter->depth == 0 && (iter->node_i[iter->depth] == (node->size_kc & LITE3_NODE_KEY_COUNT_MASK))) { // key_count reached, done
396 return LITE3_ITER_DONE;
397 }
398 size_t target_ofs = node->kv_ofs[iter->node_i[iter->depth]];
399
400 int ret;
401 if (type == LITE3_TYPE_OBJECT) { // write back key if not NULL
402 size_t key_tag_size;
403 size_t key_start_ofs = target_ofs;
404 if ((ret = _verify_key(buf, buflen, NULL, 0, 0, &target_ofs, &key_tag_size)) < 0)
405 return ret;
406 if (out_key) {
407 out_key->gen = iter->gen;
408 out_key->len = 0;
409 memcpy(&out_key->len, buf + key_start_ofs, key_tag_size);
410 --out_key->len; // Lite³ stores string size including NULL-terminator. Correction required for public API.
411 out_key->ptr = (const char *)(buf + key_start_ofs + key_tag_size);
412 }
413 }
414 if (out_val_ofs) { // write back val if not NULL
415 size_t val_start_ofs = target_ofs;
416 if ((ret = _verify_val(buf, buflen, &target_ofs)) < 0)
417 return ret;
418 *out_val_ofs = val_start_ofs;
419 }
420
421 ++iter->node_i[iter->depth];
422
423 while (node->child_ofs[iter->node_i[iter->depth]]) { // has children, travel down
424 u32 next_node_ofs = node->child_ofs[iter->node_i[iter->depth]];
425
426 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
427
428 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
429 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
430 errno = EBADMSG;
431 return -1;
432 }
433 if (LITE3_UNLIKELY(++iter->depth > LITE3_TREE_HEIGHT_MAX)) {
434 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
435 errno = EBADMSG;
436 return -1;
437 }
438 if (LITE3_UNLIKELY((size_t)next_node_ofs > buflen - LITE3_NODE_SIZE)) {
439 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
440 errno = EFAULT;
441 return -1;
442 }
443 iter->node_ofs[iter->depth] = next_node_ofs;
444 iter->node_i[iter->depth] = 0;
445 }
446 while (iter->depth > 0 && (iter->node_i[iter->depth] == (node->size_kc & LITE3_NODE_KEY_COUNT_MASK))) { // key_count reached, go up
447 --iter->depth;
448 node = __builtin_assume_aligned((struct node *)(buf + iter->node_ofs[iter->depth]), LITE3_NODE_ALIGNMENT);
449
450 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
451 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
452 errno = EBADMSG;
453 return -1;
454 }
455 #ifdef LITE3_PREFETCHING
456 __builtin_prefetch(buf + node->child_ofs[(u32)(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK], 0, 2); // prefetch next nodes
457 __builtin_prefetch(buf + node->child_ofs[(u32)(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 2);
458 __builtin_prefetch(buf + node->child_ofs[(u32)(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK], 0, 2);
459 __builtin_prefetch(buf + node->child_ofs[(u32)(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 2);
460 #endif
461 }
462 #ifdef LITE3_PREFETCHING
463 __builtin_prefetch(buf + node->kv_ofs[(u32)(iter->node_i[iter->depth] + 0) & LITE3_NODE_KEY_COUNT_MASK], 0, 0); // prefetch next items
464 __builtin_prefetch(buf + node->kv_ofs[(u32)(iter->node_i[iter->depth] + 0) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 0);
465 __builtin_prefetch(buf + node->kv_ofs[(u32)(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK], 0, 0);
466 __builtin_prefetch(buf + node->kv_ofs[(u32)(iter->node_i[iter->depth] + 1) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 0);
467 __builtin_prefetch(buf + node->kv_ofs[(u32)(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK], 0, 0);
468 __builtin_prefetch(buf + node->kv_ofs[(u32)(iter->node_i[iter->depth] + 2) & LITE3_NODE_KEY_COUNT_MASK] + 64, 0, 0);
469 #endif
470 return LITE3_ITER_ITEM;
471}
472
473
474static inline void _lite3_init_impl(unsigned char *buf, size_t ofs, enum lite3_type type)
475{
476 LITE3_PRINT_DEBUG("INITIALIZE %s\n", type == LITE3_TYPE_OBJECT ? "OBJECT" : "ARRAY");
477
478 struct node *node = (struct node *)(buf + ofs);
479 node->gen_type = type & LITE3_NODE_TYPE_MASK;
480 node->size_kc = 0x00;
481 #ifdef LITE3_ZERO_MEM_EXTRA
482 memset(node->hashes, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->hashes));
483 memset(node->kv_ofs, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->kv_ofs));
484 #endif
485 memset(node->child_ofs, 0x00, sizeof(((struct node *)0)->child_ofs));
486}
487
488int lite3_init_obj(unsigned char *buf, size_t *restrict out_buflen, size_t bufsz)
489{
490 if (LITE3_UNLIKELY(bufsz < LITE3_NODE_SIZE)) {
491 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz < LITE3_NODE_SIZE\n");
492 errno = EINVAL;
493 return -1;
494 }
495 _lite3_init_impl(buf, 0, LITE3_TYPE_OBJECT);
496 *out_buflen = LITE3_NODE_SIZE;
497 return 0;
498}
499
500int lite3_init_arr(unsigned char *buf, size_t *restrict out_buflen, size_t bufsz)
501{
502 if (LITE3_UNLIKELY(bufsz < LITE3_NODE_SIZE)) {
503 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz < LITE3_NODE_SIZE\n");
504 errno = EINVAL;
505 return -1;
506 }
507 _lite3_init_impl(buf, 0, LITE3_TYPE_ARRAY);
508 *out_buflen = LITE3_NODE_SIZE;
509 return 0;
510}
511
512/*
513 Inserts entry into the Lite³ structure to prepare for writing of the actual value.
514 - Returns 0 on success
515 - Returns < 0 on failure
516
517 [ NOTE ] This function expects the caller to write to:
518 1) `val->type`: the value type (bytes written should equal to `LITE3_VAL_SIZE`)
519 2) `val->val`: the actual value (bytes written should equal `val_len`)
520 This has the advantage that the responsibility of type-specific logic is also moved to the caller.
521 Otherwise, this function would have to contain branches to account for all types.
522*/
523int lite3_set_impl(
524 unsigned char *buf, // buffer pointer
525 size_t *restrict inout_buflen, // buffer used length (bytes, inout value)
526 size_t ofs, // start offset (0 == root)
527 size_t bufsz, // buffer max size (bytes)
528 const char *restrict key, // key string (string, pass NULL when inserting in array)
529 lite3_key_data key_data, // key data struct
530 size_t val_len, // value length (bytes)
531 lite3_val **out) // value entry pointer (out pointer)
532{
533 #ifdef LITE3_DEBUG
534 if (*(buf + ofs) == LITE3_TYPE_OBJECT) {
535 LITE3_PRINT_DEBUG("SET\tkey: %s\n", key);
536 } else if (*(buf + ofs) == LITE3_TYPE_ARRAY) {
537 LITE3_PRINT_DEBUG("SET\tindex: %u\n", key_data.hash);
538 } else {
539 LITE3_PRINT_DEBUG("SET INVALID: EXPECTING ARRAY OR OBJECT TYPE\n");
540 }
541 #endif
542
543 size_t key_tag_size = (size_t)((!!(key_data.size >> (16 - LITE3_KEY_TAG_KEY_SIZE_SHIFT)) << 1)
544 + !!(key_data.size >> (8 - LITE3_KEY_TAG_KEY_SIZE_SHIFT))
545 + !!key_data.size);
546 size_t base_entry_size = key_tag_size + (size_t)key_data.size + LITE3_VAL_SIZE + val_len;
547
548 struct node *restrict root = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
549
550 if (LITE3_UNLIKELY(((uintptr_t)root & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
551 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
552 errno = EBADMSG;
553 return -1;
554 }
555
556 u32 gen = root->gen_type >> LITE3_NODE_GEN_SHIFT;
557 ++gen;
558 root->gen_type = (root->gen_type & ~LITE3_NODE_GEN_MASK) | (gen << LITE3_NODE_GEN_SHIFT);
559
560 uint32_t probe_attempts = key ? LITE3_HASH_PROBE_MAX : 1U;
561 for (uint32_t attempt = 0; attempt < probe_attempts; attempt++) {
562
563 lite3_key_data attempt_key = key_data;
564 attempt_key.hash = key_data.hash + attempt * attempt;
565 // LITE3_PRINT_DEBUG("probe attempt: %u\thash: %u\n", attempt, attempt_key.hash);
566
567
568 size_t entry_size = base_entry_size;
569 struct node *restrict parent = NULL;
570 struct node *restrict node = root;
571
572 int key_count;
573 int i;
574 int node_walks = 0;
575
576 while (1) {
577 if ((node->size_kc & LITE3_NODE_KEY_COUNT_MASK) == LITE3_NODE_KEY_COUNT_MAX) { // node full, need to split
578
579 size_t buflen_aligned = (*inout_buflen + LITE3_NODE_ALIGNMENT_MASK) & ~(size_t)LITE3_NODE_ALIGNMENT_MASK; // next multiple of LITE3_NODE_ALIGNMENT
580 size_t new_node_size = parent ? LITE3_NODE_SIZE : 2 * LITE3_NODE_SIZE;
581
582 if (LITE3_UNLIKELY(new_node_size > bufsz || buflen_aligned > bufsz - new_node_size)) {
583 LITE3_PRINT_ERROR("NO BUFFER SPACE FOR NODE SPLIT\n");
584 errno = ENOBUFS;
585 return -1;
586 }
587 *inout_buflen = buflen_aligned;
588 // TODO: add lost bytes from alignment to GC index
589 if (!parent) { // if root split, create new root
590 LITE3_PRINT_DEBUG("NEW ROOT\n");
591 memcpy(buf + *inout_buflen, node, LITE3_NODE_SIZE);
592 node = __builtin_assume_aligned((struct node *)(buf + *inout_buflen), LITE3_NODE_ALIGNMENT);
593
594 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
595 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
596 errno = EBADMSG;
597 return -1;
598 }
599 parent = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT);
600
601 if (LITE3_UNLIKELY(((uintptr_t)parent & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
602 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
603 errno = EBADMSG;
604 return -1;
605 }
606 #ifdef LITE3_ZERO_MEM_EXTRA
607 memset(parent->hashes, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->hashes));
608 memset(parent->kv_ofs, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->kv_ofs));
609 memset(parent->child_ofs, 0x00, sizeof(((struct node *)0)->child_ofs));
610 #endif
611 parent->size_kc &= ~LITE3_NODE_KEY_COUNT_MASK; // set key_count to 0
612 parent->child_ofs[0] = (u32)*inout_buflen; // insert node as child of new root
613 *inout_buflen += LITE3_NODE_SIZE;
614 key_count = 0;
615 i = 0;
616 }
617 LITE3_PRINT_DEBUG("SPLIT NODE\n");
618 for (int j = key_count; j > i; j--) { // shift parent array before separator insert
619 parent->hashes[j] = parent->hashes[j - 1];
620 parent->kv_ofs[j] = parent->kv_ofs[j - 1];
621 parent->child_ofs[j + 1] = parent->child_ofs[j];
622 }
623 parent->hashes[i] = node->hashes[LITE3_NODE_KEY_COUNT_MIN]; // insert new separator key in parent
624 parent->kv_ofs[i] = node->kv_ofs[LITE3_NODE_KEY_COUNT_MIN];
625 parent->child_ofs[i + 1] = (u32)*inout_buflen; // insert sibling as child in parent
626 parent->size_kc = (parent->size_kc & ~LITE3_NODE_KEY_COUNT_MASK)
627 | ((parent->size_kc + 1) & LITE3_NODE_KEY_COUNT_MASK); // key_count++
628 #ifdef LITE3_ZERO_MEM_EXTRA
629 node->hashes[LITE3_NODE_KEY_COUNT_MIN] = LITE3_ZERO_MEM_32;
630 node->kv_ofs[LITE3_NODE_KEY_COUNT_MIN] = LITE3_ZERO_MEM_32;
631 #endif
632 struct node *restrict sibling = __builtin_assume_aligned((struct node *)(buf + *inout_buflen), LITE3_NODE_ALIGNMENT);
633
634 if (LITE3_UNLIKELY(((uintptr_t)sibling & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
635 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
636 errno = EBADMSG;
637 return -1;
638 }
639 #ifdef LITE3_ZERO_MEM_EXTRA
640 memset(sibling->hashes, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->hashes));
641 memset(sibling->kv_ofs, LITE3_ZERO_MEM_8, sizeof(((struct node *)0)->kv_ofs));
642 #endif
643 sibling->gen_type = ((struct node *)(buf + ofs))->gen_type & LITE3_NODE_TYPE_MASK;
644 sibling->size_kc = LITE3_NODE_KEY_COUNT_MIN & LITE3_NODE_KEY_COUNT_MASK;
645 node->size_kc = LITE3_NODE_KEY_COUNT_MIN & LITE3_NODE_KEY_COUNT_MASK;
646 memset(sibling->child_ofs, 0x00, sizeof(((struct node *)0)->child_ofs));
647 sibling->child_ofs[0] = node->child_ofs[LITE3_NODE_KEY_COUNT_MIN + 1]; // take child from node
648 node->child_ofs[LITE3_NODE_KEY_COUNT_MIN + 1] = 0x00;
649 for (int j = 0; j < LITE3_NODE_KEY_COUNT_MIN; j++) { // copy half of node's keys to sibling
650 sibling->hashes[j] = node->hashes[j + LITE3_NODE_KEY_COUNT_MIN + 1];
651 sibling->kv_ofs[j] = node->kv_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 1];
652 sibling->child_ofs[j + 1] = node->child_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 2];
653 #ifdef LITE3_ZERO_MEM_EXTRA
654 node->hashes[j + LITE3_NODE_KEY_COUNT_MIN + 1] = LITE3_ZERO_MEM_32;
655 node->kv_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 1] = LITE3_ZERO_MEM_32;
656 node->child_ofs[j + LITE3_NODE_KEY_COUNT_MIN + 2] = 0x00000000;
657 #endif
658 }
659 *inout_buflen += LITE3_NODE_SIZE;
660 if (attempt_key.hash > parent->hashes[i]) { // sibling has target key? then we follow
661 node = __builtin_assume_aligned(sibling, LITE3_NODE_ALIGNMENT);
662
663 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
664 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
665 errno = EBADMSG;
666 return -1;
667 }
668 } else if (attempt_key.hash == parent->hashes[i]) {
669 node = __builtin_assume_aligned(parent, LITE3_NODE_ALIGNMENT);
670 LITE3_PRINT_DEBUG("GOTO SKIP\n");
671 goto key_match_skip;
672 }
673 }
674
675 key_count = node->size_kc & LITE3_NODE_KEY_COUNT_MASK;
676 i = 0;
677 while (i < key_count && node->hashes[i] < attempt_key.hash)
678 i++;
679
680 // LITE3_PRINT_DEBUG("i: %i\tkc: %i\tnode->hashes[i]: %u\n", i, key_count, node->hashes[i]);
681
682 if (i < key_count && node->hashes[i] == attempt_key.hash) { // matching key found, already exists?
683key_match_skip:
684 ;
685 size_t target_ofs = node->kv_ofs[i];
686 size_t key_start_ofs = target_ofs;
687 if (key) {
688 int verify = _verify_key(buf, *inout_buflen, key, (size_t)attempt_key.size, key_tag_size, &target_ofs, NULL);
689 if (verify == LITE3_VERIFY_KEY_HASH_COLLISION)
690 break; // try next probe
691 if (verify < 0)
692 return -1;
693 }
694 size_t val_start_ofs = target_ofs;
695 if (_verify_val(buf, *inout_buflen, &target_ofs) < 0)
696 return -1;
697 if (val_len >= target_ofs - val_start_ofs) { // value is too large, we must append
698 size_t alignment_mask = val_len == lite3_type_sizes[LITE3_TYPE_OBJECT] ? (size_t)LITE3_NODE_ALIGNMENT_MASK : 0;
699 size_t unaligned_val_ofs = *inout_buflen + key_tag_size + (size_t)attempt_key.size;
700 size_t alignment_padding = ((unaligned_val_ofs + alignment_mask) & ~alignment_mask) - unaligned_val_ofs;
701 entry_size += alignment_padding;
702 if (LITE3_UNLIKELY(entry_size > bufsz || *inout_buflen > bufsz - entry_size)) {
703 LITE3_PRINT_ERROR("NO BUFFER SPACE FOR ENTRY INSERTION\n");
704 errno = ENOBUFS;
705 return -1;
706 }
707 (void)key_start_ofs; // silence unused variable warning
708 #ifdef LITE3_ZERO_MEM_DELETED
709 memset(buf + node->kv_ofs[i], LITE3_ZERO_MEM_8, target_ofs - key_start_ofs); // zero out key + value
710 #endif
711 #ifdef LITE3_ZERO_MEM_EXTRA
712 memset(buf + *inout_buflen, LITE3_ZERO_MEM_8, alignment_padding);
713 #endif
714 *inout_buflen += alignment_padding;
715 node->kv_ofs[i] = (u32)*inout_buflen;
716 goto insert_append;
717 // TODO: add lost bytes to GC index
718 }
719 #ifdef LITE3_ZERO_MEM_DELETED
720 memset(buf + val_start_ofs, LITE3_ZERO_MEM_8, target_ofs - val_start_ofs); // zero out value
721 #endif
722 *out = (lite3_val *)(buf + val_start_ofs); // caller overwrites value in place
723 // TODO: add lost bytes to GC index
724 return 0;
725 }
726 if (node->child_ofs[0]) { // if children, walk to next node
727 size_t next_node_ofs = (size_t)node->child_ofs[i];
728
729 parent = __builtin_assume_aligned(node, LITE3_NODE_ALIGNMENT);
730 node = __builtin_assume_aligned((struct node *)(buf + next_node_ofs), LITE3_NODE_ALIGNMENT);
731
732 if (LITE3_UNLIKELY(((uintptr_t)node & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
733 LITE3_PRINT_ERROR("NODE OFFSET NOT ALIGNED TO LITE3_NODE_ALIGNMENT\n");
734 errno = EBADMSG;
735 return -1;
736 }
737 if (LITE3_UNLIKELY(next_node_ofs > *inout_buflen - LITE3_NODE_SIZE)) {
738 LITE3_PRINT_ERROR("NODE WALK OFFSET OUT OF BOUNDS\n");
739 errno = EFAULT;
740 return -1;
741 }
742 if (LITE3_UNLIKELY(++node_walks > LITE3_TREE_HEIGHT_MAX)) {
743 LITE3_PRINT_ERROR("NODE WALKS EXCEEDED LITE3_TREE_HEIGHT_MAX\n");
744 errno = EBADMSG;
745 return -1;
746 }
747 } else { // insert the kv-pair
748 size_t alignment_mask = val_len == lite3_type_sizes[LITE3_TYPE_OBJECT] ? (size_t)LITE3_NODE_ALIGNMENT_MASK : 0;
749 size_t unaligned_val_ofs = *inout_buflen + key_tag_size + (size_t)attempt_key.size;
750 size_t alignment_padding = ((unaligned_val_ofs + alignment_mask) & ~alignment_mask) - unaligned_val_ofs;
751 entry_size += alignment_padding;
752 if (LITE3_UNLIKELY(entry_size > bufsz || *inout_buflen > bufsz - entry_size)) {
753 LITE3_PRINT_ERROR("NO BUFFER SPACE FOR ENTRY INSERTION\n");
754 errno = ENOBUFS;
755 return -1;
756 }
757 for (int j = key_count; j > i; j--) {
758 node->hashes[j] = node->hashes[j - 1];
759 node->kv_ofs[j] = node->kv_ofs[j - 1];
760 }
761 // LITE3_PRINT_DEBUG("INSERTING HASH: %u\ti: %i\n", attempt_key.hash, i);
762 node->hashes[i] = attempt_key.hash;
763 node->size_kc = (node->size_kc & ~LITE3_NODE_KEY_COUNT_MASK)
764 | ((node->size_kc + 1) & LITE3_NODE_KEY_COUNT_MASK); // key_count++
765 #ifdef LITE3_ZERO_MEM_EXTRA
766 memset(buf + *inout_buflen, LITE3_ZERO_MEM_8, alignment_padding);
767 #endif
768 *inout_buflen += alignment_padding;
769 node->kv_ofs[i] = (u32)*inout_buflen;
770
771 root = __builtin_assume_aligned((struct node *)(buf + ofs), LITE3_NODE_ALIGNMENT); // set node to root
772 u32 size = root->size_kc >> LITE3_NODE_SIZE_SHIFT;
773 ++size;
774 root->size_kc = (root->size_kc & ~LITE3_NODE_SIZE_MASK) | (size << LITE3_NODE_SIZE_SHIFT); // node size++
775 goto insert_append;
776 }
777 }
778 continue;
779insert_append:
780 if (key) {
781 size_t key_size_tmp = (attempt_key.size << LITE3_KEY_TAG_KEY_SIZE_SHIFT) | (key_tag_size - 1);
782 memcpy(buf + *inout_buflen, &key_size_tmp, key_tag_size);
783 *inout_buflen += key_tag_size;
784 memcpy(buf + *inout_buflen, key, (size_t)attempt_key.size);
785 *inout_buflen += (size_t)attempt_key.size;
786 }
787 *out = (lite3_val *)(buf + *inout_buflen);
788 *inout_buflen += LITE3_VAL_SIZE + val_len;
789 LITE3_PRINT_DEBUG("OK\n");
790 return 0;
791 }
792 LITE3_PRINT_ERROR("LITE3_HASH_PROBE_MAX LIMIT REACHED\n");
793 errno = EINVAL;
794 return -1;
795}
796
797int 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)
798{
799 lite3_val *val;
800 int ret;
801 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, key, key_data, lite3_type_sizes[LITE3_TYPE_OBJECT], &val)) < 0)
802 return ret;
803 size_t init_ofs = (size_t)((u8 *)val - buf);
804 if (out_ofs)
805 *out_ofs = init_ofs;
806 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_OBJECT);
807 return ret;
808}
809
810int 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)
811{
812 lite3_val *val;
813 int ret;
814 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, key, key_data, lite3_type_sizes[LITE3_TYPE_ARRAY], &val)) < 0)
815 return ret;
816 size_t init_ofs = (size_t)((u8 *)val - buf);
817 if (out_ofs)
818 *out_ofs = init_ofs;
819 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_ARRAY);
820 return ret;
821}
822
823int lite3_arr_append_obj_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, size_t *restrict out_ofs)
824{
825 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
826 lite3_key_data key_data = {
827 .hash = size,
828 .size = 0,
829 };
830 lite3_val *val;
831 int ret;
832 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_OBJECT], &val)) < 0)
833 return ret;
834 size_t init_ofs = (size_t)((u8 *)val - buf);
835 if (out_ofs)
836 *out_ofs = init_ofs;
837 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_OBJECT);
838 return ret;
839}
840
841int lite3_arr_append_arr_impl(unsigned char *buf, size_t *restrict inout_buflen, size_t ofs, size_t bufsz, size_t *restrict out_ofs)
842{
843 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
844 lite3_key_data key_data = {
845 .hash = size,
846 .size = 0,
847 };
848 lite3_val *val;
849 int ret;
850 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_ARRAY], &val)) < 0)
851 return ret;
852 size_t init_ofs = (size_t)((u8 *)val - buf);
853 if (out_ofs)
854 *out_ofs = init_ofs;
855 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_ARRAY);
856 return ret;
857}
858
859int 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)
860{
861 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
862 if (LITE3_UNLIKELY(index > size)) {
863 LITE3_PRINT_ERROR("INVALID ARGUMENT: ARRAY INDEX %u OUT OF BOUNDS (size == %u)\n", index, size);
864 errno = EINVAL;
865 return -1;
866 }
867 lite3_key_data key_data = {
868 .hash = index,
869 .size = 0,
870 };
871 lite3_val *val;
872 int ret;
873 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_OBJECT], &val)) < 0)
874 return ret;
875 size_t init_ofs = (size_t)((u8 *)val - buf);
876 if (out_ofs)
877 *out_ofs = init_ofs;
878 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_OBJECT);
879 return ret;
880}
881
882int 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)
883{
884 u32 size = ((struct node *)(buf + ofs))->size_kc >> LITE3_NODE_SIZE_SHIFT;
885 if (LITE3_UNLIKELY(index > size)) {
886 LITE3_PRINT_ERROR("INVALID ARGUMENT: ARRAY INDEX %u OUT OF BOUNDS (size == %u)\n", index, size);
887 errno = EINVAL;
888 return -1;
889 }
890 lite3_key_data key_data = {
891 .hash = index,
892 .size = 0,
893 };
894 lite3_val *val;
895 int ret;
896 if ((ret = lite3_set_impl(buf, inout_buflen, ofs, bufsz, NULL, key_data, lite3_type_sizes[LITE3_TYPE_ARRAY], &val)) < 0)
897 return ret;
898 size_t init_ofs = (size_t)((u8 *)val - buf);
899 if (out_ofs)
900 *out_ofs = init_ofs;
901 _lite3_init_impl(buf, init_ofs, LITE3_TYPE_ARRAY);
902 return ret;
903}
#define LITE3_TREE_HEIGHT_MAX
Maximum B-tree height.
Definition lite3.h:308
#define LITE3_HASH_PROBE_MAX
Enable hash probing to tolerate 32-bit hash collisions.
Definition lite3.h:342
#define LITE3_NODE_ALIGNMENT
B-tree node alignment.
Definition lite3.h:275
#define LITE3_NODE_SIZE
B-tree node size setting.
Definition lite3.h:293
#define LITE3_NODE_SIZE_KC_OFFSET
Offset of the size_kc field inside struct node.
Definition lite3.h:322
#define LITE3_ITER_ITEM
Return value of lite3_iter_next(); iterator produced an item, continue;.
Definition lite3.h:2661
#define LITE3_ITER_DONE
Return value of lite3_iter_next(); iterator finished; stop.
Definition lite3.h:2663
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:373
lite3_type
enum containing all Lite³ types
Definition lite3.h:500
@ LITE3_TYPE_ARRAY
maps to 'array' type in JSON
Definition lite3.h:508
@ LITE3_TYPE_INVALID
any type value equal or greater than this is considered invalid
Definition lite3.h:509
@ LITE3_TYPE_STRING
maps to 'string' type in JSON
Definition lite3.h:506
@ LITE3_TYPE_BYTES
coverted to base64 string in JSON
Definition lite3.h:505
@ LITE3_TYPE_OBJECT
maps to 'object' type in JSON
Definition lite3.h:507
Lite³ Buffer API Header.
Struct containing iterator state.
Definition lite3.h:2671
Struct holding a reference to a string inside a Lite³ buffer.
Definition lite3.h:591
uint32_t len
char array length (characters, exclusive of NULL-terminator)
Definition lite3.h:593
const char * ptr
char array pointer to string inside Lite³ buffer
Definition lite3.h:594
uint32_t gen
generation of the Lite³ buffer when this struct was returned
Definition lite3.h:592
Struct representing a value inside a Lite³ buffer.
Definition lite3.h:521
Definition lite3.c:77