Seregon/sJson

sJson (also known as Secure JSON) is a thread-safe, dynamic-allocation-free, cross-compilable JSON parser based on a PAL (Platform Abstraction Layer) architecture writed in c99

C/251 B/No license
src/sJson.c
sJson / src / sJson.c
1/**
2 * @author Seregon
3 * @file json_pal.h
4 * @brief Safe, fast, single-header JSON library with Platform Abstraction Layer
5 * @version 1.1.0
6 * @date 2026
7 *
8 * SPDX-License-Identifier: GPL-3.0 license
9 *
10 * ============================================================================
11 * QUICK START
12 * ============================================================================
13 *
14 * // In ONE translation unit:
15 * #define JSON_IMPLEMENTATION
16 * #include "json_pal.h"
17 *
18 * // Parse:
19 * JsonArena* arena = json_arena_create(NULL, 64 * 1024);
20 * JsonError err;
21 * JsonValue* root = json_parse_cstr(arena, "[1,2,3]", &err);
22 *
23 * // Query:
24 * JsonValue* item = json_path(root, "[1]", NULL);
25 *
26 * // Cleanup:
27 * json_arena_destroy(arena);
28 *
29 * ============================================================================
30 * PAL OVERRIDES (define BEFORE including)
31 * ============================================================================
32 *
33 * #define JSON_MEMCPY my_memcpy // void*(dst, src, n)
34 * #define JSON_MEMSET my_memset // void*(dst, c, n)
35 * #define JSON_MEMCMP my_memcmp // int(a, b, n)
36 * #define JSON_STRLEN my_strlen // size_t(s)
37 * #define JSON_ASSERT my_assert // void(expr)
38 * #define JSON_STRTOD my_strtod // double(s, end)
39 * #define JSON_NO_STDLIB // Don't include stdlib headers
40 *
41 * ============================================================================
42 * CONFIGURATION (define BEFORE including)
43 * ============================================================================
44 *
45 * #define JSON_MAX_DEPTH 64 // Max nesting depth (stack overflow guard)
46 * #define JSON_MAX_STRING_LEN 65536 // Max string length
47 * #define JSON_MAX_NODES 65536 // Max total value nodes per parse
48 * #define JSON_INTROSORT_THRESH 16 // Insertion sort below this size
49 * #define JSON_ARENA_ALIGN 8 // Arena allocation alignment
50 *
51 * ============================================================================
52 * DESIGN RATIONALE
53 * ============================================================================
54 *
55 * MEMORY: Arena allocator - zero fragmentation, O(1) alloc, bulk free.
56 * No malloc/free during parse; caller provides backing memory.
57 *
58 * PARSER: Iterative state machine - no recursion, bounded stack usage,
59 * safe on deeply nested input. Explicit parse stack on the heap.
60 *
61 * NUMBERS: Detects integer vs float at parse time. Integers stored as
62 * int64_t (exact). Floats stored as double with NaN/Inf guard.
63 *
64 * OBJECTS: Keys stored insertion-order. Separate sorted index array built
65 * on demand via introsort, enabling O(log n) binary search lookup.
66 *
67 * SORT: Introsort (quicksort + heapsort fallback + insertion sort leaf).
68 * Guaranteed O(n log n) worst-case. Insertion sort for n < 16.
69 *
70 * UTF-8: Full validation during string parsing (RFC 3629).
71 * \uXXXX escape sequences with surrogate pair support.
72 *
73 * @note Thread-safety: NOT thread-safe. Use separate arenas per thread.
74 * @note MISRA C:2012 advisory (dynamic alloc via user-provided hooks only).
75 */
76 
77#ifndef JSON_PAL_H
78#define JSON_PAL_H
79 
80/* ============================================================================
81 * §1 PLATFORM DETECTION
82 * ============================================================================ */
83 
84#if defined(_MSC_VER)
85# define JSON_CC_MSVC 1
86#elif defined(__clang__)
87# define JSON_CC_CLANG 1
88#elif defined(__GNUC__)
89# define JSON_CC_GCC 1
90#else
91# define JSON_CC_OTHER 1
92#endif
93 
94#if defined(_WIN32) || defined(_WIN64)
95# define JSON_OS_WINDOWS 1
96#elif defined(__linux__)
97# define JSON_OS_LINUX 1
98#elif defined(__APPLE__)
99# define JSON_OS_APPLE 1
100#else
101# define JSON_OS_BAREMETAL 1 /* Embedded / RTOS */
102#endif
103 
104/* Endianness */
105#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
106# if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
107# define JSON_BIG_ENDIAN 1
108# endif
109#endif
110#ifndef JSON_BIG_ENDIAN
111# define JSON_LITTLE_ENDIAN 1
112#endif
113 
114/* ============================================================================
115 * §2 PORTABLE TYPE SYSTEM
116 * ============================================================================ */
117 
118#ifndef JSON_NO_STDLIB
119# include <stdint.h>
120# include <stddef.h>
121# include <stdbool.h>
122# include <string.h>
123# include <math.h>
124# include <float.h>
125# include <limits.h>
126#else
127/* Minimal type definitions for fully freestanding environments */
128typedef unsigned char uint8_t;
129typedef unsigned short uint16_t;
130typedef unsigned int uint32_t;
131typedef unsigned long long uint64_t;
132typedef signed char int8_t;
133typedef signed short int16_t;
134typedef signed int int32_t;
135typedef signed long long int64_t;
136typedef unsigned long size_t;
137typedef int ptrdiff_t;
138typedef int bool;
139# define true 1
140# define false 0
141# define NULL ((void*)0)
142#endif
143 
144/* Compile-time type size checks */
145#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
146_Static_assert(sizeof(uint8_t) == 1, "uint8_t must be 1 byte");
147_Static_assert(sizeof(uint32_t) == 4, "uint32_t must be 4 bytes");
148_Static_assert(sizeof(uint64_t) == 8, "uint64_t must be 8 bytes");
149_Static_assert(sizeof(int64_t) == 8, "int64_t must be 8 bytes");
150#endif
151 
152/* ============================================================================
153 * §3 COMPILER HINTS & PORTABILITY MACROS
154 * ============================================================================ */
155 
156#if defined(JSON_CC_GCC) || defined(JSON_CC_CLANG)
157# define JSON_INLINE __attribute__((always_inline)) static inline
158# define JSON_NOINLINE __attribute__((noinline))
159# define JSON_LIKELY(x) __builtin_expect(!!(x), 1)
160# define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
161# define JSON_RESTRICT __restrict__
162# define JSON_PURE __attribute__((pure))
163# define JSON_NORETURN __attribute__((noreturn))
164#elif defined(JSON_CC_MSVC)
165# define JSON_INLINE __forceinline static
166# define JSON_NOINLINE __declspec(noinline)
167# define JSON_LIKELY(x) (x)
168# define JSON_UNLIKELY(x) (x)
169# define JSON_RESTRICT __restrict
170# define JSON_PURE
171# define JSON_NORETURN __declspec(noreturn)
172#else
173# define JSON_INLINE static inline
174# define JSON_NOINLINE
175# define JSON_LIKELY(x) (x)
176# define JSON_UNLIKELY(x) (x)
177# define JSON_RESTRICT
178# define JSON_PURE
179# define JSON_NORETURN
180#endif
181 
182/* ============================================================================
183 * §4 PAL OVERRIDABLE PRIMITIVES
184 * ============================================================================ */
185 
186#ifndef JSON_MEMCPY
187# define JSON_MEMCPY memcpy
188#endif
189#ifndef JSON_MEMSET
190# define JSON_MEMSET memset
191#endif
192#ifndef JSON_MEMCMP
193# define JSON_MEMCMP memcmp
194#endif
195#ifndef JSON_STRLEN
196# define JSON_STRLEN strlen
197#endif
198#ifndef JSON_STRTOD
199# define JSON_STRTOD strtod
200#endif
201#ifndef JSON_STRTOLL
202# define JSON_STRTOLL strtoll
203#endif
204 
205#ifndef JSON_ASSERT
206# ifdef NDEBUG
207# define JSON_ASSERT(expr) ((void)(expr))
208# else
209# include <assert.h>
210# define JSON_ASSERT(expr) assert(expr)
211# endif
212#endif
213 
214/* ============================================================================
215 * §5 CONFIGURATION DEFAULTS
216 * ============================================================================ */
217 
218#ifndef JSON_MAX_DEPTH
219# define JSON_MAX_DEPTH 64U
220#endif
221#ifndef JSON_MAX_STRING_LEN
222# define JSON_MAX_STRING_LEN (1024U * 1024U)
223#endif
224#ifndef JSON_MAX_NODES
225# define JSON_MAX_NODES (1024U * 1024U)
226#endif
227#ifndef JSON_INTROSORT_THRESH
228# define JSON_INTROSORT_THRESH 16U
229#endif
230#ifndef JSON_ARENA_ALIGN
231# define JSON_ARENA_ALIGN 8U
232#endif
233#ifndef JSON_ARENA_MIN_BLOCK
234# define JSON_ARENA_MIN_BLOCK (4U * 1024U)
235#endif
236#ifndef JSON_MAX_ARRAY_LEN
237# define JSON_MAX_ARRAY_LEN (1024U * 1024U) /**< Max items per array/object */
238#endif
239 
240/* ============================================================================
241 * §6 ERROR CODES
242 * ============================================================================ */
243 
244typedef enum {
245 JSON_OK = 0, /**< Success */
246 JSON_ERR_NULL_PARAM = -1, /**< NULL pointer passed to API */
247 JSON_ERR_OOM = -2, /**< Out of arena memory */
248 JSON_ERR_INVALID_JSON = -3, /**< Malformed JSON syntax */
249 JSON_ERR_UNEXPECTED_EOF = -4, /**< Input ended unexpectedly */
250 JSON_ERR_DEPTH_EXCEEDED = -5, /**< Nesting deeper than JSON_MAX_DEPTH */
251 JSON_ERR_INVALID_STRING = -6, /**< Bad escape sequence or encoding */
252 JSON_ERR_INVALID_NUMBER = -7, /**< Malformed number literal */
253 JSON_ERR_INVALID_UTF8 = -8, /**< Invalid UTF-8 sequence */
254 JSON_ERR_OVERFLOW = -9, /**< Arithmetic or buffer overflow */
255 JSON_ERR_NOT_FOUND = -10, /**< Key or index not found */
256 JSON_ERR_TYPE_MISMATCH = -11, /**< Wrong type for requested operation */
257 JSON_ERR_BUFFER_TOO_SMALL= -12, /**< Output buffer too small */
258 JSON_ERR_NODE_LIMIT = -13 /**< Exceeded JSON_MAX_NODES */
259} JsonError;
260 
261/* ============================================================================
262 * §7 VALUE TYPES & STRUCTURES
263 * ============================================================================ */
264 
265typedef enum {
266 JSON_NULL = 0,
267 JSON_BOOL = 1,
268 JSON_INTEGER = 2, /**< Stored as int64_t — exact, no rounding */
269 JSON_FLOAT = 3, /**< Stored as double — NaN/Inf never produced */
270 JSON_STRING = 4, /**< UTF-8, NOT NUL-terminated; use .len */
271 JSON_ARRAY = 5,
272 JSON_OBJECT = 6
273} JsonType;
274 
275typedef struct JsonValue JsonValue;
276 
277typedef struct {
278 const char* data; /**< Points into arena; NOT NUL-terminated */
279 uint32_t len;
280 uint32_t hash; /**< FNV-1a for fast key lookup */
281} JsonStr;
282 
283typedef struct {
284 JsonValue** items;
285 uint32_t len;
286 uint32_t cap;
287} JsonArr;
288 
289typedef struct {
290 JsonStr key;
291 JsonValue* val;
292} JsonPair;
293 
294typedef struct {
295 JsonPair* pairs; /**< Insertion-order pairs */
296 uint32_t* sorted_idx; /**< Indices sorted by key (built on demand) */
297 uint32_t len;
298 uint32_t cap;
299 bool is_sorted;
300} JsonObj;
301 
302struct JsonValue {
303 JsonType type;
304 union {
305 bool b; /* JSON_BOOL */
306 int64_t i; /* JSON_INTEGER */
307 double f; /* JSON_FLOAT */
308 JsonStr s; /* JSON_STRING */
309 JsonArr a; /* JSON_ARRAY */
310 JsonObj o; /* JSON_OBJECT */
311 } v;
312};
313 
314/* ============================================================================
315 * §8 ARENA ALLOCATOR
316 * ============================================================================ */
317 
318/**
319 * @brief Custom allocator hooks. All fields optional (NULL → use libc).
320 * @note realloc receives old_size to support arena-style backing stores.
321 */
322typedef struct {
323 void* (*alloc) (void* ctx, size_t size);
324 void (*free) (void* ctx, void* ptr);
325 void* (*realloc)(void* ctx, void* ptr, size_t old_size, size_t new_size);
326 void* ctx;
327} JsonAllocator;
328 
329typedef struct JsonArenaBlock JsonArenaBlock;
330struct JsonArenaBlock {
331 JsonArenaBlock* next;
332 size_t cap;
333 size_t used;
334 /* data[] follows in memory */
335};
336 
337typedef struct {
338 JsonArenaBlock* current;
339 JsonArenaBlock* first;
340 size_t block_size; /**< Default block allocation size */
341 size_t total_bytes; /**< Cumulative bytes allocated */
342 uint32_t node_count; /**< JsonValue nodes allocated (limit check) */
343 JsonAllocator backing;
344} JsonArena;
345 
346/* ============================================================================
347 * §9 SERIALIZER OPTIONS
348 * ============================================================================ */
349 
350typedef struct {
351 bool pretty; /**< Enable indented, multi-line output */
352 uint32_t indent; /**< Spaces per level (default: 2) */
353 bool sort_keys; /**< Alphabetic key ordering in output */
354 bool ascii_only; /**< Escape all non-ASCII as \uXXXX */
355 bool trailing_nl; /**< Append '\n' at end */
356} JsonWriteOpts;
357 
358/* ============================================================================
359 * §10 PUBLIC API DECLARATIONS
360 * ============================================================================ */
361 
362/* --- Arena --- */
363 
364/**
365 * @brief Create arena. Pass NULL allocator to use libc malloc/free/realloc.
366 * @param alloc Backing allocator (NULL = libc). Copied by value.
367 * @param block_size Initial block size in bytes (0 = default 64 KiB).
368 * @return New arena, or NULL on allocation failure.
369 * @note Thread-safety: NOT thread-safe.
370 */
371JsonArena* json_arena_create(const JsonAllocator* alloc, size_t block_size);
372 
373/**
374 * @brief Destroy arena and free all backing memory.
375 * @note All JsonValue* pointers obtained from this arena become invalid.
376 */
377void json_arena_destroy(JsonArena* arena);
378 
379/**
380 * @brief Reset arena: keeps backing blocks, resets all usage to zero.
381 * Faster than destroy+create for repeated parses.
382 */
383void json_arena_reset(JsonArena* arena);
384 
385/**
386 * @brief Low-level aligned allocation from arena.
387 * @return Pointer to zeroed memory, or NULL on OOM.
388 * @note Alignment: JSON_ARENA_ALIGN bytes.
389 */
390void* json_arena_alloc(JsonArena* arena, size_t size);
391 
392/* --- Parsing --- */
393 
394/**
395 * @brief Parse JSON from buffer. Result lives in arena.
396 * @param arena Destination arena (must not be NULL).
397 * @param src Input buffer (need not be NUL-terminated).
398 * @param len Length of src in bytes.
399 * @param err_out If non-NULL, receives error code on failure.
400 * @return Root JsonValue*, or NULL on error.
401 *
402 * @pre arena != NULL
403 * @pre src != NULL || len == 0
404 * @post On success, returned pointer is valid until arena_destroy/reset.
405 *
406 * @note WCET depends on input size and JSON_MAX_DEPTH.
407 * Depth-limited to JSON_MAX_DEPTH to prevent stack exhaustion.
408 */
409JsonValue* json_parse(JsonArena* arena, const char* src, size_t len,
410 JsonError* err_out);
411 
412/**
413 * @brief Convenience wrapper for NUL-terminated strings.
414 */
415JsonValue* json_parse_cstr(JsonArena* arena, const char* src, JsonError* err_out);
416 
417/* --- Type predicates --- */
418JSON_INLINE bool json_is_null (const JsonValue* v) { return v && v->type == JSON_NULL; }
419JSON_INLINE bool json_is_bool (const JsonValue* v) { return v && v->type == JSON_BOOL; }
420JSON_INLINE bool json_is_integer(const JsonValue* v) { return v && v->type == JSON_INTEGER; }
421JSON_INLINE bool json_is_float (const JsonValue* v) { return v && v->type == JSON_FLOAT; }
422JSON_INLINE bool json_is_number (const JsonValue* v) { return v && (v->type == JSON_INTEGER || v->type == JSON_FLOAT); }
423JSON_INLINE bool json_is_string (const JsonValue* v) { return v && v->type == JSON_STRING; }
424JSON_INLINE bool json_is_array (const JsonValue* v) { return v && v->type == JSON_ARRAY; }
425JSON_INLINE bool json_is_object (const JsonValue* v) { return v && v->type == JSON_OBJECT; }
426 
427/* --- Type-safe accessors --- */
428JsonError json_get_bool (const JsonValue* v, bool* out);
429JsonError json_get_int (const JsonValue* v, int64_t* out);
430JsonError json_get_float (const JsonValue* v, double* out);
431JsonError json_get_number (const JsonValue* v, double* out); /**< Accepts int OR float */
432JsonError json_get_string (const JsonValue* v, const char** out, uint32_t* len_out);
433JsonError json_get_arr_len(const JsonValue* v, uint32_t* out);
434JsonError json_get_obj_len(const JsonValue* v, uint32_t* out);
435 
436/** @brief Get array item by index. O(1). */
437JsonError json_arr_get(const JsonValue* v, uint32_t idx, JsonValue** out);
438 
439/** @brief Get object value by key (NUL-terminated). O(log n) after sort. */
440JsonError json_obj_get(const JsonValue* v, const char* key, JsonValue** out);
441 
442/** @brief Get object value by key+length. O(log n) after sort. */
443JsonError json_obj_get_n(const JsonValue* v, const char* key, size_t klen,
444 JsonValue** out);
445 
446/**
447 * @brief Iterate object pairs in insertion order.
448 * @param idx Pair index [0, len).
449 * @param key_out If non-NULL, receives key pointer (NOT NUL-terminated).
450 * @param klen If non-NULL, receives key length.
451 * @param val_out If non-NULL, receives value pointer.
452 */
453JsonError json_obj_iter(const JsonValue* v, uint32_t idx,
454 const char** key_out, uint32_t* klen,
455 JsonValue** val_out);
456 
457/* --- Path query --- */
458 
459/**
460 * @brief Query value by path string.
461 *
462 * Syntax:
463 * .key → object key lookup (dot notation)
464 * ["key"] → object key lookup (bracket notation, allows spaces/specials)
465 * [N] → array index (N is non-negative integer)
466 *
467 * Example paths:
468 * "users[0].name"
469 * ".config[\"host\"]"
470 * "[2][1]"
471 *
472 * @param root Starting node (may be any type; mismatch → NULL).
473 * @param path Path string (NUL-terminated).
474 * @param err_out Optional error output.
475 * @return Matching JsonValue*, or NULL on failure.
476 */
477JsonValue* json_path(const JsonValue* root, const char* path, JsonError* err_out);
478 
479/* --- Serialization --- */
480 
481/**
482 * @brief Serialize JSON value to caller-provided buffer.
483 * @param buf Output buffer.
484 * @param buf_size Buffer capacity in bytes.
485 * @param written If non-NULL, receives bytes written (excl. NUL).
486 * @param opts Write options (NULL = compact defaults).
487 * @return JSON_OK, JSON_ERR_NULL_PARAM, or JSON_ERR_BUFFER_TOO_SMALL.
488 *
489 * @pre v != NULL, buf != NULL, buf_size >= 1
490 * @post On JSON_OK, buf is NUL-terminated.
491 */
492JsonError json_write(const JsonValue* v, char* buf, size_t buf_size,
493 size_t* written, const JsonWriteOpts* opts);
494 
495/**
496 * @brief Serialize to arena-allocated NUL-terminated string.
497 * @param out Receives pointer to NUL-terminated string.
498 * @param len_out If non-NULL, receives string length.
499 */
500JsonError json_write_arena(const JsonValue* v, JsonArena* arena,
501 char** out, size_t* len_out,
502 const JsonWriteOpts* opts);
503 
504/* --- Value construction --- */
505JsonValue* json_make_null (JsonArena* arena);
506JsonValue* json_make_bool (JsonArena* arena, bool val);
507JsonValue* json_make_int (JsonArena* arena, int64_t val);
508JsonValue* json_make_float (JsonArena* arena, double val);
509JsonValue* json_make_string (JsonArena* arena, const char* s, uint32_t len);
510JsonValue* json_make_stringz(JsonArena* arena, const char* s);
511JsonValue* json_make_array (JsonArena* arena);
512JsonValue* json_make_object (JsonArena* arena);
513 
514JsonError json_arr_push(JsonValue* arr, JsonArena* arena, JsonValue* item);
515JsonError json_obj_set (JsonValue* obj, JsonArena* arena,
516 const char* key, uint32_t klen, JsonValue* val);
517JsonError json_obj_setz(JsonValue* obj, JsonArena* arena,
518 const char* key, JsonValue* val);
519 
520/* --- Utilities --- */
521const char* json_error_str(JsonError err);
522uint32_t json_fnv1a(const char* data, size_t len);
523 
524/* Compute output size without writing (pass NULL buf, 0 size, non-NULL written) */
525JsonError json_measure(const JsonValue* v, size_t* size_out,
526 const JsonWriteOpts* opts);
527 
528/* Deep equality comparison */
529bool json_equal(const JsonValue* a, const JsonValue* b);
530 
531/* Clone value into another arena */
532JsonValue* json_clone(JsonArena* dst, const JsonValue* src);
533 
534 
535/* ============================================================================
536 * §11 IMPLEMENTATION
537 * ============================================================================ */
538 
539#ifdef JSON_IMPLEMENTATION
540 
541#ifndef JSON_NO_STDLIB
542# include <stdlib.h> /* malloc, free, realloc */
543# include <stdio.h> /* snprintf */
544#endif
545 
546/* ── §11.1 Default libc allocator ─────────────────────────────────────── */
547 
548static void* json__libc_alloc(void* ctx, size_t sz)
549{
550 (void)ctx;
551 return malloc(sz);
552}
553static void json__libc_free(void* ctx, void* p)
554{
555 (void)ctx;
556 free(p);
557}
558static void* json__libc_realloc(void* ctx, void* p, size_t old_sz, size_t new_sz)
559{
560 (void)ctx; (void)old_sz;
561 return realloc(p, new_sz);
562}
563 
564static const JsonAllocator k_libc_alloc = {
565 json__libc_alloc,
566 json__libc_free,
567 json__libc_realloc,
568 NULL
569};
570 
571/* ── §11.2 Arena implementation ────────────────────────────────────────── */
572 
573/**
574 * @brief Align sz up to JSON_ARENA_ALIGN boundary.
575 * @note JSON_ARENA_ALIGN MUST be a power of two.
576 */
577JSON_INLINE size_t json__align_up(size_t sz)
578{
579 /* DESIGN: Power-of-2 alignment allows bitwise AND instead of modulo.
580 * WCET: O(1), 2 instructions on ARM Cortex-M. */
581 return (sz + (JSON_ARENA_ALIGN - 1U)) & ~(JSON_ARENA_ALIGN - 1U);
582}
583 
584static JsonArenaBlock* json__block_new(JsonArena* arena, size_t min_size)
585{
586 size_t block_sz;
587 JsonArenaBlock* blk;
588 size_t header_sz;
589 
590 JSON_ASSERT(arena != NULL);
591 
592 header_sz = json__align_up(sizeof(JsonArenaBlock));
593 
594 /* Ensure block fits header + requested data */
595 block_sz = (min_size + header_sz > arena->block_size)
596 ? json__align_up(min_size + header_sz)
597 : arena->block_size;
598 
599 blk = (JsonArenaBlock*)arena->backing.alloc(arena->backing.ctx, block_sz);
600 if (JSON_UNLIKELY(blk == NULL)) { return NULL; }
601 
602 JSON_MEMSET(blk, 0, sizeof(JsonArenaBlock));
603 blk->cap = block_sz - header_sz;
604 blk->used = 0U;
605 blk->next = NULL;
606 return blk;
607}
608 
609JSON_INLINE uint8_t* json__block_data(JsonArenaBlock* blk)
610{
611 /* Data starts immediately after the (aligned) block header */
612 return (uint8_t*)blk + json__align_up(sizeof(JsonArenaBlock));
613}
614 
615JsonArena* json_arena_create(const JsonAllocator* alloc, size_t block_size)
616{
617 JsonArena* arena;
618 JsonAllocator eff_alloc;
619 JsonArenaBlock* first;
620 
621 eff_alloc = (alloc != NULL) ? *alloc : k_libc_alloc;
622 
623 /* Validate allocator */
624 if (eff_alloc.alloc == NULL || eff_alloc.free == NULL) { return NULL; }
625 
626 if (block_size == 0U) {
627 block_size = 64U * 1024U;
628 }
629 if (block_size < JSON_ARENA_MIN_BLOCK) {
630 block_size = JSON_ARENA_MIN_BLOCK;
631 }
632 
633 arena = (JsonArena*)eff_alloc.alloc(eff_alloc.ctx, sizeof(JsonArena));
634 if (JSON_UNLIKELY(arena == NULL)) { return NULL; }
635 JSON_MEMSET(arena, 0, sizeof(JsonArena));
636 
637 arena->backing = eff_alloc;
638 arena->block_size = block_size;
639 
640 first = json__block_new(arena, 0U);
641 if (JSON_UNLIKELY(first == NULL)) {
642 eff_alloc.free(eff_alloc.ctx, arena);
643 return NULL;
644 }
645 
646 arena->first = first;
647 arena->current = first;
648 return arena;
649}
650 
651void json_arena_destroy(JsonArena* arena)
652{
653 JsonArenaBlock* blk;
654 JsonArenaBlock* next;
655 
656 if (arena == NULL) { return; }
657 
658 blk = arena->first;
659 while (blk != NULL) {
660 next = blk->next;
661 arena->backing.free(arena->backing.ctx, blk);
662 blk = next;
663 }
664 arena->backing.free(arena->backing.ctx, arena);
665}
666 
667void json_arena_reset(JsonArena* arena)
668{
669 JsonArenaBlock* blk;
670 
671 if (arena == NULL) { return; }
672 
673 for (blk = arena->first; blk != NULL; blk = blk->next) {
674 blk->used = 0U;
675 }
676 arena->current = arena->first;
677 arena->total_bytes = 0U;
678 arena->node_count = 0U;
679}
680 
681void* json_arena_alloc(JsonArena* arena, size_t size)
682{
683 size_t aligned_size;
684 uint8_t* ptr;
685 JsonArenaBlock* new_blk;
686 
687 if (JSON_UNLIKELY(arena == NULL || size == 0U)) { return NULL; }
688 
689 aligned_size = json__align_up(size);
690 
691 /* Overflow guard on size alignment */
692 if (JSON_UNLIKELY(aligned_size < size)) { return NULL; }
693 
694 /* Fast path: fits in current block */
695 if (JSON_LIKELY(arena->current->used + aligned_size <= arena->current->cap)) {
696 ptr = json__block_data(arena->current) + arena->current->used;
697 arena->current->used += aligned_size;
698 arena->total_bytes += aligned_size;
699 JSON_MEMSET(ptr, 0, aligned_size);
700 return ptr;
701 }
702 
703 /* Slow path: allocate a new block */
704 new_blk = json__block_new(arena, aligned_size);
705 if (JSON_UNLIKELY(new_blk == NULL)) { return NULL; }
706 
707 /* Append to chain */
708 arena->current->next = new_blk;
709 arena->current = new_blk;
710 
711 ptr = json__block_data(new_blk);
712 new_blk->used = aligned_size;
713 arena->total_bytes += aligned_size;
714 JSON_MEMSET(ptr, 0, aligned_size);
715 return ptr;
716}
717 
718/* ── §11.3 FNV-1a Hash ─────────────────────────────────────────────────── */
719 
720/**
721 * @brief FNV-1a 32-bit hash for object key lookup.
722 * Non-cryptographic. Fast. Good distribution for short strings.
723 *
724 * @note WCET: O(len). No branches inside loop.
725 */
726uint32_t json_fnv1a(const char* data, size_t len)
727{
728 static const uint32_t FNV_PRIME = 0x01000193UL;
729 static const uint32_t FNV_OFFSET = 0x811C9DC5UL;
730 uint32_t hash = FNV_OFFSET;
731 size_t i;
732 
733 if (data == NULL) { return 0U; }
734 
735 for (i = 0U; i < len; i++) {
736 hash ^= (uint32_t)(uint8_t)data[i];
737 hash *= FNV_PRIME;
738 }
739 return hash;
740}
741 
742/* ── §11.4 Introsort for object key indices ────────────────────────────── */
743 
744/*
745 * DESIGN: Introsort combines:
746 * - Quicksort (median-of-3 pivot, O(n log n) average)
747 * - Heapsort (fallback when recursion depth > 2*log2(n), O(n log n) worst)
748 * - Insertion sort (n < JSON_INTROSORT_THRESH, O(n^2) but fast for tiny n)
749 *
750 * This guarantees O(n log n) worst-case with excellent cache locality.
751 * Uses recursion bounded by depth_limit to prevent stack overflow.
752 */
753 
754typedef struct {
755 const JsonPair* pairs; /* Read-only reference to pairs array */
756 uint32_t* idx; /* The index array being sorted */
757} JsonSortCtx;
758 
759/**
760 * @brief Compare two key-index entries by (hash, then lexicographic).
761 * Hash comparison avoids string compare in the common (no-collision) case.
762 * @return <0 if a < b, 0 if equal, >0 if a > b
763 */
764JSON_INLINE int json__key_cmp(const JsonSortCtx* ctx, uint32_t a, uint32_t b)
765{
766 const JsonStr* ka = &ctx->pairs[a].key;
767 const JsonStr* kb = &ctx->pairs[b].key;
768 int cmp;
769 uint32_t min_len;
770 
771 JSON_ASSERT(ctx != NULL);
772 
773 /* Fast path: hash mismatch */
774 if (ka->hash != kb->hash) {
775 return (ka->hash < kb->hash) ? -1 : 1;
776 }
777 
778 /* Slow path: lexicographic comparison (hash collision) */
779 min_len = (ka->len < kb->len) ? ka->len : kb->len;
780 cmp = JSON_MEMCMP(ka->data, kb->data, (size_t)min_len);
781 if (cmp != 0) { return cmp; }
782 if (ka->len < kb->len) { return -1; }
783 if (ka->len > kb->len) { return 1; }
784 return 0;
785}
786 
787static void json__isort(const JsonSortCtx* ctx, uint32_t* arr,
788 uint32_t lo, uint32_t hi)
789{
790 uint32_t i, j, key;
791 
792 JSON_ASSERT(ctx != NULL && arr != NULL);
793 
794 for (i = lo + 1U; i <= hi; i++) {
795 key = arr[i];
796 j = i;
797 while (j > lo && json__key_cmp(ctx, arr[j - 1U], key) > 0) {
798 arr[j] = arr[j - 1U];
799 j--;
800 }
801 arr[j] = key;
802 }
803}
804 
805/* Heapsort helpers */
806static void json__sift_down(const JsonSortCtx* ctx, uint32_t* arr,
807 uint32_t root, uint32_t end)
808{
809 uint32_t parent, child, tmp;
810 
811 JSON_ASSERT(ctx != NULL && arr != NULL);
812 
813 parent = root;
814 for (;;) {
815 child = 2U * parent + 1U;
816 if (child > end) { break; }
817 
818 if (child + 1U <= end &&
819 json__key_cmp(ctx, arr[child], arr[child + 1U]) < 0) {
820 child++;
821 }
822 if (json__key_cmp(ctx, arr[parent], arr[child]) >= 0) { break; }
823 
824 tmp = arr[parent]; arr[parent] = arr[child]; arr[child] = tmp;
825 parent = child;
826 }
827}
828 
829static void json__heapsort(const JsonSortCtx* ctx, uint32_t* arr,
830 uint32_t lo, uint32_t hi)
831{
832 uint32_t n, i, tmp;
833 
834 JSON_ASSERT(ctx != NULL && arr != NULL && hi >= lo);
835 
836 n = hi - lo + 1U;
837 
838 /* Build max-heap */
839 if (n < 2U) { return; }
840 i = lo + n / 2U;
841 while (i-- > lo) {
842 json__sift_down(ctx, arr + lo, i - lo, n - 1U);
843 }
844 
845 /* Extract */
846 for (i = n - 1U; i > 0U; i--) {
847 tmp = arr[lo]; arr[lo] = arr[lo + i]; arr[lo + i] = tmp;
848 json__sift_down(ctx, arr + lo, 0U, i - 1U);
849 }
850}
851 
852/* Compute floor(log2(n)) safely */
853JSON_INLINE uint32_t json__ilog2(uint32_t n)
854{
855 uint32_t r = 0U;
856 while (n > 1U) { n >>= 1U; r++; }
857 return r;
858}
859 
860static void json__introsort(const JsonSortCtx* ctx, uint32_t* arr,
861 uint32_t lo, uint32_t hi, uint32_t depth)
862{
863 uint32_t pivot, mid, tmp, i, j;
864 
865 JSON_ASSERT(ctx != NULL && arr != NULL);
866 
867 while (lo < hi) {
868 uint32_t span = hi - lo + 1U;
869 
870 /* Leaf: insertion sort */
871 if (span <= JSON_INTROSORT_THRESH) {
872 json__isort(ctx, arr, lo, hi);
873 return;
874 }
875 
876 /* Depth exceeded: heapsort to guarantee O(n log n) */
877 if (depth == 0U) {
878 json__heapsort(ctx, arr, lo, hi);
879 return;
880 }
881 
882 /* Median-of-three pivot selection */
883 mid = lo + span / 2U;
884 
885 /* Sort lo, mid, hi — bring median to mid */
886 if (json__key_cmp(ctx, arr[lo], arr[mid]) > 0) {
887 tmp = arr[lo]; arr[lo] = arr[mid]; arr[mid] = tmp;
888 }
889 if (json__key_cmp(ctx, arr[lo], arr[hi]) > 0) {
890 tmp = arr[lo]; arr[lo] = arr[hi]; arr[hi] = tmp;
891 }
892 if (json__key_cmp(ctx, arr[mid], arr[hi]) > 0) {
893 tmp = arr[mid]; arr[mid] = arr[hi]; arr[hi] = tmp;
894 }
895 
896 /* Place pivot at hi-1 */
897 pivot = arr[mid];
898 tmp = arr[mid]; arr[mid] = arr[hi - 1U]; arr[hi - 1U] = tmp;
899 
900 /* Partition */
901 i = lo;
902 j = hi - 1U;
903 for (;;) {
904 while (json__key_cmp(ctx, arr[++i], pivot) < 0 && i < hi) { }
905 while (json__key_cmp(ctx, arr[--j], pivot) > 0 && j > lo) { }
906 if (i >= j) { break; }
907 tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
908 }
909 
910 /* Restore pivot */
911 tmp = arr[i]; arr[i] = arr[hi - 1U]; arr[hi - 1U] = tmp;
912 
913 /* Recurse smaller half; iterate larger (tail-call optimisation) */
914 if (i - lo < hi - i) {
915 json__introsort(ctx, arr, lo, i - 1U, depth - 1U);
916 lo = i + 1U;
917 } else {
918 json__introsort(ctx, arr, i + 1U, hi, depth - 1U);
919 hi = (i > 0U) ? (i - 1U) : 0U;
920 if (i == 0U) { break; }
921 }
922 depth--;
923 }
924}
925 
926/**
927 * @brief Sort object's sorted_idx array and mark object as sorted.
928 * @note Idempotent: no-op if already sorted.
929 * @note WCET: O(n log n) — introsort guarantees this.
930 */
931static JsonError json__obj_sort(JsonValue* obj, JsonArena* arena)
932{
933 JsonObj* o;
934 uint32_t i;
935 JsonSortCtx ctx;
936 
937 JSON_ASSERT(obj != NULL && obj->type == JSON_OBJECT);
938 JSON_ASSERT(arena != NULL);
939 
940 o = &obj->v.o;
941 if (o->is_sorted || o->len == 0U) { return JSON_OK; }
942 
943 /* Allocate sorted_idx if not already present */
944 if (o->sorted_idx == NULL) {
945 o->sorted_idx = (uint32_t*)json_arena_alloc(
946 arena, (size_t)o->cap * sizeof(uint32_t));
947 if (JSON_UNLIKELY(o->sorted_idx == NULL)) { return JSON_ERR_OOM; }
948 }
949 
950 for (i = 0U; i < o->len; i++) {
951 o->sorted_idx[i] = i;
952 }
953 
954 ctx.pairs = o->pairs;
955 ctx.idx = o->sorted_idx;
956 
957 if (o->len > 1U) {
958 json__introsort(&ctx, o->sorted_idx, 0U, o->len - 1U,
959 2U * json__ilog2(o->len));
960 }
961 
962 o->is_sorted = true;
963 return JSON_OK;
964}
965 
966/* ── §11.5 Lexer ───────────────────────────────────────────────────────── */
967 
968typedef enum {
969 TOK_LBRACE = '{',
970 TOK_RBRACE = '}',
971 TOK_LBRACKET= '[',
972 TOK_RBRACKET= ']',
973 TOK_COLON = ':',
974 TOK_COMMA = ',',
975 TOK_TRUE = 't',
976 TOK_FALSE = 'f',
977 TOK_NULL_T = 'n',
978 TOK_STRING = '"',
979 TOK_NUMBER = '0',
980 TOK_EOF = 0,
981 TOK_ERROR = -1
982} JsonTok;
983 
984typedef struct {
985 const char* src;
986 size_t len;
987 size_t pos;
988 uint32_t line;
989 uint32_t col;
990 /* Last parsed token data */
991 size_t tok_start; /* Position in src */
992 size_t tok_len; /* Length in src (raw, incl. quotes) */
993 JsonError err;
994} JsonLex;
995 
996JSON_INLINE bool json__is_ws(char c)
997{
998 return (c == ' ') | (c == '\t') | (c == '\r') | (c == '\n');
999}
1000 
1001static void json__lex_skip_ws(JsonLex* l)
1002{
1003 JSON_ASSERT(l != NULL);
1004 while (l->pos < l->len) {
1005 char c = l->src[l->pos];
1006 if (!json__is_ws(c)) { break; }
1007 if (c == '\n') { l->line++; l->col = 0U; }
1008 else { l->col++; }
1009 l->pos++;
1010 }
1011}
1012 
1013JSON_INLINE char json__peek(const JsonLex* l)
1014{
1015 return (l->pos < l->len) ? l->src[l->pos] : '\0';
1016}
1017 
1018JSON_INLINE char json__consume(JsonLex* l)
1019{
1020 char c = l->src[l->pos++];
1021 l->col++;
1022 return c;
1023}
1024 
1025/** @brief Validate UTF-8 continuation byte. */
1026JSON_INLINE bool json__is_cont(uint8_t b) { return (b & 0xC0U) == 0x80U; }
1027 
1028/* ── §11.6 String parsing (with \uXXXX decoding) ──────────────────────── */
1029 
1030/** @brief Hex digit to value. Returns 0xFF on invalid input. */
1031JSON_INLINE uint8_t json__hex_val(char c)
1032{
1033 if (c >= '0' && c <= '9') { return (uint8_t)(c - '0'); }
1034 if (c >= 'a' && c <= 'f') { return (uint8_t)(c - 'a' + 10); }
1035 if (c >= 'A' && c <= 'F') { return (uint8_t)(c - 'A' + 10); }
1036 return 0xFFU;
1037}
1038 
1039/**
1040 * @brief Parse 4 hex digits into uint16_t.
1041 * @return true on success, false on non-hex character.
1042 */
1043static bool json__parse_hex4(const char* s, size_t max, uint16_t* out)
1044{
1045 uint8_t h0, h1, h2, h3;
1046 
1047 JSON_ASSERT(s != NULL && out != NULL);
1048 
1049 if (max < 4U) { return false; }
1050 h0 = json__hex_val(s[0]);
1051 h1 = json__hex_val(s[1]);
1052 h2 = json__hex_val(s[2]);
1053 h3 = json__hex_val(s[3]);
1054 
1055 if ((h0 | h1 | h2 | h3) == 0xFFU) { return false; }
1056 *out = (uint16_t)(((uint16_t)h0 << 12U) |
1057 ((uint16_t)h1 << 8U) |
1058 ((uint16_t)h2 << 4U) |
1059 (uint16_t)h3);
1060 return true;
1061}
1062 
1063/** @brief Encode Unicode code point (U+0000..U+10FFFF) as UTF-8.
1064 * Writes to buf (needs at least 4 bytes). Returns bytes written. */
1065static uint32_t json__encode_utf8(uint32_t cp, uint8_t* buf)
1066{
1067 JSON_ASSERT(buf != NULL);
1068 
1069 if (cp < 0x80U) {
1070 buf[0] = (uint8_t)cp;
1071 return 1U;
1072 } else if (cp < 0x800U) {
1073 buf[0] = (uint8_t)(0xC0U | (cp >> 6U));
1074 buf[1] = (uint8_t)(0x80U | (cp & 0x3FU));
1075 return 2U;
1076 } else if (cp < 0x10000U) {
1077 buf[0] = (uint8_t)(0xE0U | (cp >> 12U));
1078 buf[1] = (uint8_t)(0x80U | ((cp >> 6U) & 0x3FU));
1079 buf[2] = (uint8_t)(0x80U | (cp & 0x3FU));
1080 return 3U;
1081 } else if (cp <= 0x10FFFFU) {
1082 buf[0] = (uint8_t)(0xF0U | (cp >> 18U));
1083 buf[1] = (uint8_t)(0x80U | ((cp >> 12U) & 0x3FU));
1084 buf[2] = (uint8_t)(0x80U | ((cp >> 6U) & 0x3FU));
1085 buf[3] = (uint8_t)(0x80U | (cp & 0x3FU));
1086 return 4U;
1087 }
1088 return 0U; /* Invalid code point */
1089}
1090 
1091/**
1092 * @brief Parse a JSON string (cursor past opening '"'). Decode all escapes.
1093 * Result is stored as arena-allocated UTF-8. NOT NUL-terminated.
1094 *
1095 * @param[out] str Populated with pointer+length+hash on success.
1096 * @return JSON_OK or error code.
1097 */
1098static JsonError json__parse_string(JsonLex* l, JsonArena* arena, JsonStr* str)
1099{
1100 /* Two-pass: first measure, then allocate+copy.
1101 * This avoids reallocations and is cache-friendly. */
1102 size_t start_pos = l->pos; /* After opening quote */
1103 size_t out_len = 0U;
1104 bool has_escape= false;
1105 size_t scan_pos;
1106 char* out_buf;
1107 size_t write_pos;
1108 
1109 JSON_ASSERT(l != NULL && arena != NULL && str != NULL);
1110 
1111 /* ── Pass 1: Measure output length ─── */
1112 scan_pos = l->pos;
1113 while (scan_pos < l->len) {
1114 uint8_t c = (uint8_t)l->src[scan_pos];
1115 
1116 if (c == '"') { break; }
1117 
1118 if (c == '\\') {
1119 has_escape = true;
1120 scan_pos++;
1121 if (scan_pos >= l->len) {
1122 l->err = JSON_ERR_UNEXPECTED_EOF;
1123 return JSON_ERR_UNEXPECTED_EOF;
1124 }
1125 switch (l->src[scan_pos]) {
1126 case '"': case '\\': case '/':
1127 case 'b': case 'f': case 'n': case 'r': case 't':
1128 out_len++;
1129 scan_pos++;
1130 break;
1131 case 'u': {
1132 /* \uXXXX or \uXXXX\uXXXX (surrogate pair) */
1133 uint16_t hi_cp;
1134 scan_pos++;
1135 if (!json__parse_hex4(l->src + scan_pos,
1136 l->len - scan_pos, &hi_cp)) {
1137 l->err = JSON_ERR_INVALID_STRING;
1138 return JSON_ERR_INVALID_STRING;
1139 }
1140 scan_pos += 4U;
1141 if (hi_cp >= 0xD800U && hi_cp <= 0xDBFFU) {
1142 /* High surrogate — expect low surrogate */
1143 uint16_t lo_cp;
1144 if (scan_pos + 1U >= l->len ||
1145 l->src[scan_pos] != '\\' ||
1146 l->src[scan_pos + 1U] != 'u') {
1147 l->err = JSON_ERR_INVALID_STRING;
1148 return JSON_ERR_INVALID_STRING;
1149 }
1150 scan_pos += 2U;
1151 if (!json__parse_hex4(l->src + scan_pos,
1152 l->len - scan_pos, &lo_cp)) {
1153 l->err = JSON_ERR_INVALID_STRING;
1154 return JSON_ERR_INVALID_STRING;
1155 }
1156 scan_pos += 4U;
1157 if (lo_cp < 0xDC00U || lo_cp > 0xDFFFU) {
1158 l->err = JSON_ERR_INVALID_STRING;
1159 return JSON_ERR_INVALID_STRING;
1160 }
1161 out_len += 4U; /* UTF-8 for supplementary plane */
1162 } else if (hi_cp >= 0xDC00U && hi_cp <= 0xDFFFU) {
1163 /* Lone low surrogate — invalid */
1164 l->err = JSON_ERR_INVALID_STRING;
1165 return JSON_ERR_INVALID_STRING;
1166 } else {
1167 /* DESIGN: exact byte count, not a constant 3.
1168 * U+0000–U+007F → 1 byte
1169 * U+0080–U+07FF → 2 bytes
1170 * U+0800–U+FFFF → 3 bytes
1171 * Using the maximum (3) over-allocates and corrupts
1172 * str->len, breaking key lookup and equality. */
1173 out_len += (hi_cp < 0x80U) ? 1U
1174 : (hi_cp < 0x800U) ? 2U : 3U;
1175 }
1176 break;
1177 }
1178 default:
1179 l->err = JSON_ERR_INVALID_STRING;
1180 return JSON_ERR_INVALID_STRING;
1181 }
1182 } else if (c < 0x20U) {
1183 /* Control chars must be escaped */
1184 l->err = JSON_ERR_INVALID_STRING;
1185 return JSON_ERR_INVALID_STRING;
1186 } else {
1187 /* UTF-8 multi-byte: accept as-is (validated in pass 2) */
1188 out_len++;
1189 scan_pos++;
1190 }
1191 }
1192 
1193 if (scan_pos >= l->len) {
1194 l->err = JSON_ERR_UNEXPECTED_EOF;
1195 return JSON_ERR_UNEXPECTED_EOF;
1196 }
1197 
1198 if (out_len > JSON_MAX_STRING_LEN) {
1199 l->err = JSON_ERR_OVERFLOW;
1200 return JSON_ERR_OVERFLOW;
1201 }
1202 
1203 /* ── Pass 2: Allocate and decode ─── */
1204 out_buf = (out_len > 0U)
1205 ? (char*)json_arena_alloc(arena, out_len)
1206 : NULL;
1207 
1208 if (out_len > 0U && JSON_UNLIKELY(out_buf == NULL)) {
1209 l->err = JSON_ERR_OOM;
1210 return JSON_ERR_OOM;
1211 }
1212 
1213 write_pos = 0U;
1214 l->pos = start_pos;
1215 
1216 if (!has_escape) {
1217 /* Fast path: no escapes — memcpy and validate UTF-8 */
1218 if (out_len > 0U) {
1219 JSON_MEMCPY(out_buf, l->src + l->pos, out_len);
1220 }
1221 l->pos += out_len;
1222 } else {
1223 /* Slow path: decode escapes */
1224 while (l->pos < l->len && l->src[l->pos] != '"') {
1225 uint8_t c = (uint8_t)l->src[l->pos];
1226 if (c == '\\') {
1227 l->pos++;
1228 switch (l->src[l->pos]) {
1229 case '"': out_buf[write_pos++] = '"'; l->pos++; break;
1230 case '\\': out_buf[write_pos++] = '\\'; l->pos++; break;
1231 case '/': out_buf[write_pos++] = '/'; l->pos++; break;
1232 case 'b': out_buf[write_pos++] = '\b'; l->pos++; break;
1233 case 'f': out_buf[write_pos++] = '\f'; l->pos++; break;
1234 case 'n': out_buf[write_pos++] = '\n'; l->pos++; break;
1235 case 'r': out_buf[write_pos++] = '\r'; l->pos++; break;
1236 case 't': out_buf[write_pos++] = '\t'; l->pos++; break;
1237 case 'u': {
1238 uint16_t hi_cp, lo_cp;
1239 uint32_t codepoint;
1240 uint32_t bytes;
1241 l->pos++;
1242 json__parse_hex4(l->src + l->pos,
1243 l->len - l->pos, &hi_cp);
1244 l->pos += 4U;
1245 if (hi_cp >= 0xD800U && hi_cp <= 0xDBFFU) {
1246 l->pos += 2U; /* skip \u */
1247 json__parse_hex4(l->src + l->pos,
1248 l->len - l->pos, &lo_cp);
1249 l->pos += 4U;
1250 codepoint = 0x10000U +
1251 ((uint32_t)(hi_cp - 0xD800U) << 10U) +
1252 (uint32_t)(lo_cp - 0xDC00U);
1253 } else {
1254 codepoint = hi_cp;
1255 }
1256 bytes = json__encode_utf8(codepoint,
1257 (uint8_t*)out_buf + write_pos);
1258 write_pos += bytes;
1259 break;
1260 }
1261 default: l->pos++; break; /* unreachable after pass 1 */
1262 }
1263 } else {
1264 out_buf[write_pos++] = (char)c;
1265 l->pos++;
1266 }
1267 }
1268 }
1269 
1270 if (l->pos >= l->len) {
1271 l->err = JSON_ERR_UNEXPECTED_EOF;
1272 return JSON_ERR_UNEXPECTED_EOF;
1273 }
1274 l->pos++; /* Consume closing '"' */
1275 
1276 str->data = out_buf;
1277 str->len = (uint32_t)out_len;
1278 str->hash = json_fnv1a(out_buf, out_len);
1279 return JSON_OK;
1280}
1281 
1282/* ── §11.7 Number parsing ──────────────────────────────────────────────── */
1283 
1284/**
1285 * @brief Parse JSON number. Detects integer vs float at parse time.
1286 *
1287 * Grammar: -? (0 | [1-9][0-9]*) (.[0-9]+)? ([eE][+-]?[0-9]+)?
1288 *
1289 * DESIGN: Parse as integer if no '.' or 'e'/'E'. Fall back to strtod only
1290 * when fractional/exponent parts are present. This preserves exact
1291 * integer semantics for the common case and avoids locale issues.
1292 */
1293static JsonError json__parse_number(JsonLex* l, JsonArena* arena,
1294 JsonValue* out)
1295{
1296 size_t start = l->pos;
1297 bool negative = false;
1298 bool is_float = false;
1299 int64_t ival = 0;
1300 char num_buf[64];
1301 size_t num_len;
1302 char* end_ptr;
1303 
1304 JSON_ASSERT(l != NULL && arena != NULL && out != NULL);
1305 
1306 /* Optional minus */
1307 if (l->pos < l->len && l->src[l->pos] == '-') {
1308 negative = true;
1309 l->pos++;
1310 }
1311 
1312 if (l->pos >= l->len) {
1313 l->err = JSON_ERR_UNEXPECTED_EOF;
1314 return JSON_ERR_UNEXPECTED_EOF;
1315 }
1316 
1317 /* Integer part */
1318 if (l->src[l->pos] == '0') {
1319 l->pos++;
1320 if (l->pos < l->len && l->src[l->pos] >= '0' && l->src[l->pos] <= '9') {
1321 /* Leading zero not allowed in JSON */
1322 l->err = JSON_ERR_INVALID_NUMBER;
1323 return JSON_ERR_INVALID_NUMBER;
1324 }
1325 } else if (l->src[l->pos] >= '1' && l->src[l->pos] <= '9') {
1326 while (l->pos < l->len &&
1327 l->src[l->pos] >= '0' && l->src[l->pos] <= '9') {
1328 l->pos++;
1329 }
1330 } else {
1331 l->err = JSON_ERR_INVALID_NUMBER;
1332 return JSON_ERR_INVALID_NUMBER;
1333 }
1334 
1335 /* Fractional part */
1336 if (l->pos < l->len && l->src[l->pos] == '.') {
1337 is_float = true;
1338 l->pos++;
1339 if (l->pos >= l->len ||
1340 l->src[l->pos] < '0' || l->src[l->pos] > '9') {
1341 l->err = JSON_ERR_INVALID_NUMBER;
1342 return JSON_ERR_INVALID_NUMBER;
1343 }
1344 while (l->pos < l->len &&
1345 l->src[l->pos] >= '0' && l->src[l->pos] <= '9') {
1346 l->pos++;
1347 }
1348 }
1349 
1350 /* Exponent part */
1351 if (l->pos < l->len &&
1352 (l->src[l->pos] == 'e' || l->src[l->pos] == 'E')) {
1353 is_float = true;
1354 l->pos++;
1355 if (l->pos < l->len &&
1356 (l->src[l->pos] == '+' || l->src[l->pos] == '-')) {
1357 l->pos++;
1358 }
1359 if (l->pos >= l->len ||
1360 l->src[l->pos] < '0' || l->src[l->pos] > '9') {
1361 l->err = JSON_ERR_INVALID_NUMBER;
1362 return JSON_ERR_INVALID_NUMBER;
1363 }
1364 while (l->pos < l->len &&
1365 l->src[l->pos] >= '0' && l->src[l->pos] <= '9') {
1366 l->pos++;
1367 }
1368 }
1369 
1370 num_len = l->pos - start;
1371 if (num_len >= sizeof(num_buf)) {
1372 l->err = JSON_ERR_OVERFLOW;
1373 return JSON_ERR_OVERFLOW;
1374 }
1375 
1376 JSON_MEMCPY(num_buf, l->src + start, num_len);
1377 num_buf[num_len] = '\0';
1378 
1379 if (is_float) {
1380 double fval = JSON_STRTOD(num_buf, &end_ptr);
1381 if (end_ptr != num_buf + num_len) {
1382 l->err = JSON_ERR_INVALID_NUMBER;
1383 return JSON_ERR_INVALID_NUMBER;
1384 }
1385 /* Safety: NaN/Inf must not be stored */
1386 if (JSON_UNLIKELY(isnan(fval) || isinf(fval))) {
1387 l->err = JSON_ERR_INVALID_NUMBER;
1388 return JSON_ERR_INVALID_NUMBER;
1389 }
1390 out->type = JSON_FLOAT;
1391 out->v.f = fval;
1392 } else {
1393 /* Parse as int64_t */
1394 int64_t parsed;
1395 char* eptr;
1396#if defined(JSON_NO_STDLIB)
1397 /* Minimal strtoll for freestanding environments */
1398 parsed = 0;
1399 size_t di;
1400 for (di = negative ? 1U : 0U; di < num_len; di++) {
1401 char d = num_buf[di];
1402 if (d < '0' || d > '9') { break; }
1403 /* Overflow check */
1404 if (parsed > (INT64_MAX - (d - '0')) / 10) {
1405 parsed = negative ? INT64_MIN : INT64_MAX;
1406 break;
1407 }
1408 parsed = parsed * 10 + (d - '0');
1409 }
1410 ival = negative ? -parsed : parsed;
1411 (void)eptr;
1412#else
1413 parsed = JSON_STRTOLL(num_buf, &eptr, 10);
1414 if (eptr != num_buf + num_len) {
1415 l->err = JSON_ERR_INVALID_NUMBER;
1416 return JSON_ERR_INVALID_NUMBER;
1417 }
1418 ival = parsed;
1419#endif
1420 (void)negative; /* Already encoded in the string we passed to strtoll */
1421 out->type = JSON_INTEGER;
1422 out->v.i = ival;
1423 }
1424 return JSON_OK;
1425}
1426 
1427/* ── §11.8 Iterative Parser ────────────────────────────────────────────── */
1428 
1429/*
1430 * DESIGN: Recursive descent is simpler but risks stack overflow on deeply
1431 * nested input. This iterative implementation uses an explicit heap-allocated
1432 * parse stack with bounded depth (JSON_MAX_DEPTH), making stack usage O(1)
1433 * regardless of nesting depth. WCET is O(n * JSON_MAX_DEPTH) in theory,
1434 * O(n) in practice since depth is constant.
1435 */
1436 
1437typedef enum {
1438 PS_VALUE, /* Expecting any value */
1439 PS_ARR_ITEM, /* Inside array: expecting value or ']' */
1440 PS_ARR_CONT, /* After array item: expecting ',' or ']' */
1441 PS_OBJ_KEY, /* Inside object: expecting key or '}' */
1442 PS_OBJ_COLON, /* After key: expecting ':' */
1443 PS_OBJ_VAL, /* After colon: expecting value */
1444 PS_OBJ_CONT /* After value: expecting ',' or '}' */
1445} ParseState;
1446 
1447typedef struct {
1448 JsonValue* container; /* Array or Object being built */
1449 ParseState state;
1450 JsonStr pending_key; /* For PS_OBJ_VAL: key waiting for its value */
1451} ParseFrame;
1452 
1453/**
1454 * @brief Allocate and initialize a new JsonValue node.
1455 * @note Node count is tracked and bounded by JSON_MAX_NODES.
1456 */
1457static JsonValue* json__new_node(JsonArena* arena)
1458{
1459 JsonValue* v;
1460 
1461 JSON_ASSERT(arena != NULL);
1462 
1463 if (JSON_UNLIKELY(arena->node_count >= JSON_MAX_NODES)) { return NULL; }
1464 v = (JsonValue*)json_arena_alloc(arena, sizeof(JsonValue));
1465 if (JSON_LIKELY(v != NULL)) {
1466 arena->node_count++;
1467 }
1468 return v;
1469}
1470 
1471/** @brief Grow array capacity (doubling strategy). */
1472static JsonError json__arr_grow(JsonArr* a, JsonArena* arena)
1473{
1474 uint32_t new_cap;
1475 JsonValue** new_items;
1476 
1477 JSON_ASSERT(a != NULL && arena != NULL);
1478 
1479 new_cap = (a->cap == 0U) ? 8U : a->cap * 2U;
1480 
1481 /* Overflow guard */
1482 if (JSON_UNLIKELY(new_cap < a->cap)) { return JSON_ERR_OVERFLOW; }
1483 if (JSON_UNLIKELY(new_cap > JSON_MAX_ARRAY_LEN)) { return JSON_ERR_OVERFLOW; }
1484 
1485 new_items = (JsonValue**)json_arena_alloc(arena,
1486 (size_t)new_cap * sizeof(JsonValue*));
1487 if (JSON_UNLIKELY(new_items == NULL)) { return JSON_ERR_OOM; }
1488 
1489 if (a->items != NULL && a->len > 0U) {
1490 JSON_MEMCPY(new_items, a->items, (size_t)a->len * sizeof(JsonValue*));
1491 }
1492 a->items = new_items;
1493 a->cap = new_cap;
1494 return JSON_OK;
1495}
1496 
1497/** @brief Grow object capacity (doubling strategy). */
1498static JsonError json__obj_grow(JsonObj* o, JsonArena* arena)
1499{
1500 uint32_t new_cap;
1501 JsonPair* new_pairs;
1502 
1503 JSON_ASSERT(o != NULL && arena != NULL);
1504 
1505 new_cap = (o->cap == 0U) ? 8U : o->cap * 2U;
1506 if (JSON_UNLIKELY(new_cap < o->cap)) { return JSON_ERR_OVERFLOW; }
1507 
1508 new_pairs = (JsonPair*)json_arena_alloc(arena,
1509 (size_t)new_cap * sizeof(JsonPair));
1510 if (JSON_UNLIKELY(new_pairs == NULL)) { return JSON_ERR_OOM; }
1511 
1512 if (o->pairs != NULL && o->len > 0U) {
1513 JSON_MEMCPY(new_pairs, o->pairs, (size_t)o->len * sizeof(JsonPair));
1514 }
1515 o->pairs = new_pairs;
1516 o->cap = new_cap;
1517 o->sorted_idx= NULL; /* Invalidated on resize */
1518 o->is_sorted = false;
1519 return JSON_OK;
1520}
1521 
1522/**
1523 * @brief Main iterative parser.
1524 * @note All error paths set err_out before returning NULL.
1525 * @note WCET: O(n) where n = input length.
1526 */
1527JsonValue* json_parse(JsonArena* arena, const char* src, size_t len,
1528 JsonError* err_out)
1529{
1530 JsonLex lex;
1531 ParseFrame stack[JSON_MAX_DEPTH];
1532 uint32_t depth = 0U;
1533 JsonValue* result = NULL;
1534 JsonValue* root = NULL;
1535 JsonError err = JSON_OK;
1536 bool done = false;
1537 
1538 /* Validate parameters */
1539 if (arena == NULL) {
1540 if (err_out != NULL) { *err_out = JSON_ERR_NULL_PARAM; }
1541 return NULL;
1542 }
1543 if (src == NULL && len > 0U) {
1544 if (err_out != NULL) { *err_out = JSON_ERR_NULL_PARAM; }
1545 return NULL;
1546 }
1547 
1548 JSON_MEMSET(&lex, 0, sizeof(lex));
1549 lex.src = src ? src : "";
1550 lex.len = len;
1551 lex.pos = 0U;
1552 lex.line = 1U;
1553 lex.col = 0U;
1554 
1555 /* Push initial state */
1556 stack[0].state = PS_VALUE;
1557 stack[0].container = NULL;
1558 depth = 1U;
1559 
1560#define LEX_EXPECT_LITERAL(literal, tok_type) do { \
1561 size_t llen_ = sizeof(literal) - 1U; \
1562 if (lex.pos + llen_ > lex.len || \
1563 JSON_MEMCMP(lex.src + lex.pos, literal, llen_) != 0) { \
1564 err = JSON_ERR_INVALID_JSON; goto parse_error; \
1565 } \
1566 lex.pos += llen_; \
1567} while(0)
1568 
1569 while (!done) {
1570 ParseFrame* frame = &stack[depth - 1U];
1571 
1572 json__lex_skip_ws(&lex);
1573 
1574 if (lex.pos >= lex.len) {
1575 if (depth == 1U && root != NULL) {
1576 done = true;
1577 break;
1578 }
1579 err = JSON_ERR_UNEXPECTED_EOF;
1580 goto parse_error;
1581 }
1582 
1583 switch (frame->state) {
1584 
1585 /* ── Expecting a value (any type) ─────────────────────────────── */
1586 case PS_VALUE: {
1587 char c = lex.src[lex.pos];
1588 JsonValue* node = json__new_node(arena);
1589 if (JSON_UNLIKELY(node == NULL)) {
1590 err = (arena->node_count >= JSON_MAX_NODES)
1591 ? JSON_ERR_NODE_LIMIT : JSON_ERR_OOM;
1592 goto parse_error;
1593 }
1594 
1595 if (c == 'n') {
1596 lex.pos++;
1597 LEX_EXPECT_LITERAL("ull", 0);
1598 node->type = JSON_NULL;
1599 
1600 } else if (c == 't') {
1601 lex.pos++;
1602 LEX_EXPECT_LITERAL("rue", 0);
1603 node->type = JSON_BOOL;
1604 node->v.b = true;
1605 
1606 } else if (c == 'f') {
1607 lex.pos++;
1608 LEX_EXPECT_LITERAL("alse", 0);
1609 node->type = JSON_BOOL;
1610 node->v.b = false;
1611 
1612 } else if (c == '"') {
1613 lex.pos++; /* Skip opening quote */
1614 node->type = JSON_STRING;
1615 err = json__parse_string(&lex, arena, &node->v.s);
1616 if (err != JSON_OK) { goto parse_error; }
1617 
1618 } else if (c == '-' ||
1619 (c >= '0' && c <= '9')) {
1620 err = json__parse_number(&lex, arena, node);
1621 if (err != JSON_OK) { goto parse_error; }
1622 
1623 } else if (c == '[') {
1624 lex.pos++;
1625 node->type = JSON_ARRAY;
1626 node->v.a.items = NULL;
1627 node->v.a.len = 0U;
1628 node->v.a.cap = 0U;
1629 
1630 /* Push array context */
1631 if (depth >= JSON_MAX_DEPTH) {
1632 err = JSON_ERR_DEPTH_EXCEEDED;
1633 goto parse_error;
1634 }
1635 result = node;
1636 
1637 /* Store in parent if applicable, then push new frame */
1638 frame->container = node;
1639 frame->state = PS_ARR_ITEM;
1640 
1641 /* Check for empty array */
1642 json__lex_skip_ws(&lex);
1643 if (lex.pos < lex.len && lex.src[lex.pos] == ']') {
1644 lex.pos++;
1645 /* Empty array: immediately deliver to parent */
1646 goto deliver_to_parent;
1647 }
1648 
1649 /* Push VALUE frame for first element */
1650 stack[depth].state = PS_VALUE;
1651 stack[depth].container = NULL;
1652 depth++;
1653 continue;
1654 
1655 } else if (c == '{') {
1656 lex.pos++;
1657 node->type = JSON_OBJECT;
1658 node->v.o.pairs = NULL;
1659 node->v.o.sorted_idx = NULL;
1660 node->v.o.len = 0U;
1661 node->v.o.cap = 0U;
1662 node->v.o.is_sorted= false;
1663 
1664 if (depth >= JSON_MAX_DEPTH) {
1665 err = JSON_ERR_DEPTH_EXCEEDED;
1666 goto parse_error;
1667 }
1668 
1669 frame->container = node;
1670 frame->state = PS_OBJ_KEY;
1671 
1672 /* Check for empty object */
1673 json__lex_skip_ws(&lex);
1674 if (lex.pos < lex.len && lex.src[lex.pos] == '}') {
1675 lex.pos++;
1676 result = node;
1677 goto deliver_to_parent;
1678 }
1679 
1680 /*
1681 * DESIGN: this frame has been transformed in-place to
1682 * PS_OBJ_KEY. The main loop will execute case PS_OBJ_KEY on
1683 * the next iteration — no extra frame push is needed.
1684 * Pushing a second PS_OBJ_KEY frame here (as done before this
1685 * fix) left stack[depth-1] as PS_OBJ_KEY when the closing '}'
1686 * tried to deliver the object to its parent, causing an
1687 * "invalid JSON" error on every non-empty object.
1688 */
1689 continue;
1690 
1691 } else {
1692 err = JSON_ERR_INVALID_JSON;
1693 goto parse_error;
1694 }
1695 
1696 result = node;
1697 
1698 /* Deliver completed scalar to parent */
1699 deliver_to_parent:
1700 {
1701 if (depth == 1U) {
1702 /* Top-level value */
1703 root = result;
1704 done = true;
1705 break;
1706 }
1707 
1708 /* Pop current frame and deliver to parent */
1709 depth--;
1710 ParseFrame* parent = &stack[depth - 1U];
1711 
1712 if (parent->state == PS_ARR_ITEM ||
1713 parent->state == PS_ARR_CONT) {
1714 /* Add to array */
1715 JsonArr* arr = &parent->container->v.a;
1716 if (arr->len >= arr->cap) {
1717 err = json__arr_grow(arr, arena);
1718 if (err != JSON_OK) { goto parse_error; }
1719 }
1720 arr->items[arr->len++] = result;
1721 parent->state = PS_ARR_CONT;
1722 
1723 } else if (parent->state == PS_OBJ_VAL) {
1724 /* Add key-value pair to object */
1725 JsonObj* obj = &parent->container->v.o;
1726 if (obj->len >= obj->cap) {
1727 err = json__obj_grow(obj, arena);
1728 if (err != JSON_OK) { goto parse_error; }
1729 }
1730 obj->pairs[obj->len].key = parent->pending_key;
1731 obj->pairs[obj->len].val = result;
1732 obj->len++;
1733 parent->state = PS_OBJ_CONT;
1734 } else {
1735 err = JSON_ERR_INVALID_JSON;
1736 goto parse_error;
1737 }
1738 }
1739 break;
1740 } /* PS_VALUE */
1741 
1742 /* ── After array item: ',' or ']' ─────────────────────────────── */
1743 case PS_ARR_CONT: {
1744 char c = lex.src[lex.pos];
1745 if (c == ']') {
1746 lex.pos++;
1747 result = frame->container;
1748 /* Pop and deliver array to its parent */
1749 if (depth == 1U) {
1750 root = result;
1751 done = true;
1752 } else {
1753 depth--;
1754 ParseFrame* parent = &stack[depth - 1U];
1755 JsonArr* arr;
1756 
1757 if (parent->state == PS_ARR_ITEM ||
1758 parent->state == PS_ARR_CONT) {
1759 arr = &parent->container->v.a;
1760 if (arr->len >= arr->cap) {
1761 err = json__arr_grow(arr, arena);
1762 if (err != JSON_OK) { goto parse_error; }
1763 }
1764 arr->items[arr->len++] = result;
1765 parent->state = PS_ARR_CONT;
1766 } else if (parent->state == PS_OBJ_VAL) {
1767 JsonObj* obj = &parent->container->v.o;
1768 if (obj->len >= obj->cap) {
1769 err = json__obj_grow(obj, arena);
1770 if (err != JSON_OK) { goto parse_error; }
1771 }
1772 obj->pairs[obj->len].key = parent->pending_key;
1773 obj->pairs[obj->len].val = result;
1774 obj->len++;
1775 parent->state = PS_OBJ_CONT;
1776 } else if (parent->state == PS_VALUE) {
1777 root = result; done = true;
1778 } else {
1779 err = JSON_ERR_INVALID_JSON; goto parse_error;
1780 }
1781 }
1782 } else if (c == ',') {
1783 lex.pos++;
1784 frame->state = PS_ARR_ITEM;
1785 /* Push VALUE frame for next element */
1786 if (depth >= JSON_MAX_DEPTH) {
1787 err = JSON_ERR_DEPTH_EXCEEDED; goto parse_error;
1788 }
1789 stack[depth].state = PS_VALUE;
1790 stack[depth].container = NULL;
1791 depth++;
1792 } else {
1793 err = JSON_ERR_INVALID_JSON; goto parse_error;
1794 }
1795 break;
1796 }
1797 
1798 /* ── Object: expecting key ─────────────────────────────────────── */
1799 case PS_OBJ_KEY: {
1800 char c = lex.src[lex.pos];
1801 if (c == '"') {
1802 JsonStr key;
1803 lex.pos++; /* Skip quote */
1804 err = json__parse_string(&lex, arena, &key);
1805 if (err != JSON_OK) { goto parse_error; }
1806 frame->pending_key = key;
1807 frame->state = PS_OBJ_COLON;
1808 } else {
1809 err = JSON_ERR_INVALID_JSON; goto parse_error;
1810 }
1811 break;
1812 }
1813 
1814 /* ── After object key: expect ':' ─────────────────────────────── */
1815 case PS_OBJ_COLON: {
1816 if (lex.src[lex.pos] != ':') {
1817 err = JSON_ERR_INVALID_JSON; goto parse_error;
1818 }
1819 lex.pos++;
1820 frame->state = PS_OBJ_VAL;
1821 /* Push VALUE frame for the value */
1822 if (depth >= JSON_MAX_DEPTH) {
1823 err = JSON_ERR_DEPTH_EXCEEDED; goto parse_error;
1824 }
1825 stack[depth].state = PS_VALUE;
1826 stack[depth].container = NULL;
1827 depth++;
1828 break;
1829 }
1830 
1831 /* ── After object value: ',' or '}' ───────────────────────────── */
1832 case PS_OBJ_CONT: {
1833 char c = lex.src[lex.pos];
1834 if (c == '}') {
1835 lex.pos++;
1836 result = frame->container;
1837 if (depth == 1U) {
1838 root = result; done = true;
1839 } else {
1840 depth--;
1841 ParseFrame* parent = &stack[depth - 1U];
1842 if (parent->state == PS_ARR_ITEM ||
1843 parent->state == PS_ARR_CONT) {
1844 JsonArr* arr = &parent->container->v.a;
1845 if (arr->len >= arr->cap) {
1846 err = json__arr_grow(arr, arena);
1847 if (err != JSON_OK) { goto parse_error; }
1848 }
1849 arr->items[arr->len++] = result;
1850 parent->state = PS_ARR_CONT;
1851 } else if (parent->state == PS_OBJ_VAL) {
1852 JsonObj* obj = &parent->container->v.o;
1853 if (obj->len >= obj->cap) {
1854 err = json__obj_grow(obj, arena);
1855 if (err != JSON_OK) { goto parse_error; }
1856 }
1857 obj->pairs[obj->len].key = parent->pending_key;
1858 obj->pairs[obj->len].val = result;
1859 obj->len++;
1860 parent->state = PS_OBJ_CONT;
1861 } else if (parent->state == PS_VALUE) {
1862 root = result; done = true;
1863 } else {
1864 err = JSON_ERR_INVALID_JSON; goto parse_error;
1865 }
1866 }
1867 } else if (c == ',') {
1868 lex.pos++;
1869 frame->state = PS_OBJ_KEY;
1870 } else {
1871 err = JSON_ERR_INVALID_JSON; goto parse_error;
1872 }
1873 break;
1874 }
1875 
1876 case PS_ARR_ITEM:
1877 case PS_OBJ_VAL:
1878 default:
1879 /* These states should be handled by pushing a PS_VALUE frame */
1880 err = JSON_ERR_INVALID_JSON;
1881 goto parse_error;
1882 } /* switch */
1883 } /* while */
1884 
1885 /* Verify no trailing non-whitespace */
1886 if (!done) { err = JSON_ERR_UNEXPECTED_EOF; goto parse_error; }
1887 json__lex_skip_ws(&lex);
1888 if (lex.pos < lex.len) {
1889 err = JSON_ERR_INVALID_JSON;
1890 goto parse_error;
1891 }
1892 
1893#undef LEX_EXPECT_LITERAL
1894 
1895 if (err_out != NULL) { *err_out = JSON_OK; }
1896 return root;
1897 
1898parse_error:
1899 if (err_out != NULL) { *err_out = (err != JSON_OK) ? err : JSON_ERR_INVALID_JSON; }
1900 return NULL;
1901}
1902 
1903JsonValue* json_parse_cstr(JsonArena* arena, const char* src, JsonError* err_out)
1904{
1905 if (src == NULL) {
1906 if (err_out != NULL) { *err_out = JSON_ERR_NULL_PARAM; }
1907 return NULL;
1908 }
1909 return json_parse(arena, src, JSON_STRLEN(src), err_out);
1910}
1911 
1912/* ── §11.9 Type-safe accessors ─────────────────────────────────────────── */
1913 
1914#define JSON__CHECK_V(v) do { if ((v) == NULL) return JSON_ERR_NULL_PARAM; } while(0)
1915#define JSON__CHECK_OUT(o) do { if ((o) == NULL) return JSON_ERR_NULL_PARAM; } while(0)
1916 
1917JsonError json_get_bool(const JsonValue* v, bool* out)
1918{
1919 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1920 if (v->type != JSON_BOOL) { return JSON_ERR_TYPE_MISMATCH; }
1921 *out = v->v.b;
1922 return JSON_OK;
1923}
1924 
1925JsonError json_get_int(const JsonValue* v, int64_t* out)
1926{
1927 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1928 if (v->type == JSON_INTEGER) { *out = v->v.i; return JSON_OK; }
1929 if (v->type == JSON_FLOAT) { *out = (int64_t)v->v.f; return JSON_OK; }
1930 return JSON_ERR_TYPE_MISMATCH;
1931}
1932 
1933JsonError json_get_float(const JsonValue* v, double* out)
1934{
1935 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1936 if (v->type == JSON_FLOAT) { *out = v->v.f; return JSON_OK; }
1937 if (v->type == JSON_INTEGER) { *out = (double)v->v.i; return JSON_OK; }
1938 return JSON_ERR_TYPE_MISMATCH;
1939}
1940 
1941JsonError json_get_number(const JsonValue* v, double* out)
1942{
1943 return json_get_float(v, out);
1944}
1945 
1946JsonError json_get_string(const JsonValue* v, const char** out, uint32_t* len_out)
1947{
1948 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1949 if (v->type != JSON_STRING) { return JSON_ERR_TYPE_MISMATCH; }
1950 *out = v->v.s.data ? v->v.s.data : "";
1951 if (len_out != NULL) { *len_out = v->v.s.len; }
1952 return JSON_OK;
1953}
1954 
1955JsonError json_get_arr_len(const JsonValue* v, uint32_t* out)
1956{
1957 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1958 if (v->type != JSON_ARRAY) { return JSON_ERR_TYPE_MISMATCH; }
1959 *out = v->v.a.len;
1960 return JSON_OK;
1961}
1962 
1963JsonError json_get_obj_len(const JsonValue* v, uint32_t* out)
1964{
1965 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1966 if (v->type != JSON_OBJECT) { return JSON_ERR_TYPE_MISMATCH; }
1967 *out = v->v.o.len;
1968 return JSON_OK;
1969}
1970 
1971JsonError json_arr_get(const JsonValue* v, uint32_t idx, JsonValue** out)
1972{
1973 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1974 if (v->type != JSON_ARRAY) { return JSON_ERR_TYPE_MISMATCH; }
1975 if (idx >= v->v.a.len) { return JSON_ERR_NOT_FOUND; }
1976 JSON_ASSERT(v->v.a.items != NULL);
1977 *out = v->v.a.items[idx];
1978 return JSON_OK;
1979}
1980 
1981JsonError json_obj_get_n(const JsonValue* v, const char* key, size_t klen,
1982 JsonValue** out)
1983{
1984 const JsonObj* o;
1985 uint32_t hash;
1986 int32_t lo, hi, mid;
1987 int cmp;
1988 uint32_t idx;
1989 
1990 JSON__CHECK_V(v); JSON__CHECK_OUT(out);
1991 if (key == NULL) { return JSON_ERR_NULL_PARAM; }
1992 if (v->type != JSON_OBJECT) { return JSON_ERR_TYPE_MISMATCH; }
1993 
1994 o = &v->v.o;
1995 if (o->len == 0U) { return JSON_ERR_NOT_FOUND; }
1996 
1997 /* If not yet sorted, do a linear scan */
1998 if (!o->is_sorted || o->sorted_idx == NULL) {
1999 uint32_t i;
2000 hash = json_fnv1a(key, klen);
2001 for (i = 0U; i < o->len; i++) {
2002 const JsonStr* k = &o->pairs[i].key;
2003 if (k->hash == hash && k->len == (uint32_t)klen &&
2004 JSON_MEMCMP(k->data, key, klen) == 0) {
2005 *out = o->pairs[i].val;
2006 return JSON_OK;
2007 }
2008 }
2009 return JSON_ERR_NOT_FOUND;
2010 }
2011 
2012 /* Binary search on sorted_idx */
2013 hash = json_fnv1a(key, klen);
2014 lo = 0; hi = (int32_t)o->len - 1;
2015 
2016 while (lo <= hi) {
2017 mid = lo + (hi - lo) / 2;
2018 idx = o->sorted_idx[(uint32_t)mid];
2019 const JsonStr* k = &o->pairs[idx].key;
2020 
2021 if (k->hash < hash) {
2022 lo = mid + 1;
2023 } else if (k->hash > hash) {
2024 hi = mid - 1;
2025 } else {
2026 /* Hash match: compare lexicographically */
2027 uint32_t min_len = (k->len < (uint32_t)klen) ? k->len : (uint32_t)klen;
2028 cmp = JSON_MEMCMP(k->data, key, (size_t)min_len);
2029 if (cmp == 0) {
2030 if (k->len < (uint32_t)klen) { cmp = -1; }
2031 else if (k->len > (uint32_t)klen) { cmp = 1; }
2032 }
2033 if (cmp == 0) {
2034 *out = o->pairs[idx].val;
2035 return JSON_OK;
2036 } else if (cmp < 0) {
2037 lo = mid + 1;
2038 } else {
2039 hi = mid - 1;
2040 }
2041 }
2042 }
2043 return JSON_ERR_NOT_FOUND;
2044}
2045 
2046JsonError json_obj_get(const JsonValue* v, const char* key, JsonValue** out)
2047{
2048 if (key == NULL) { return JSON_ERR_NULL_PARAM; }
2049 return json_obj_get_n(v, key, JSON_STRLEN(key), out);
2050}
2051 
2052JsonError json_obj_iter(const JsonValue* v, uint32_t idx,
2053 const char** key_out, uint32_t* klen,
2054 JsonValue** val_out)
2055{
2056 JSON__CHECK_V(v);
2057 if (v->type != JSON_OBJECT) { return JSON_ERR_TYPE_MISMATCH; }
2058 if (idx >= v->v.o.len) { return JSON_ERR_NOT_FOUND; }
2059 
2060 if (key_out != NULL) { *key_out = v->v.o.pairs[idx].key.data; }
2061 if (klen != NULL) { *klen = v->v.o.pairs[idx].key.len; }
2062 if (val_out != NULL) { *val_out = v->v.o.pairs[idx].val; }
2063 return JSON_OK;
2064}
2065 
2066/* ── §11.10 Path query ─────────────────────────────────────────────────── */
2067 
2068JsonValue* json_path(const JsonValue* root, const char* path, JsonError* err_out)
2069{
2070 const JsonValue* cur;
2071 const char* p;
2072 JsonError err;
2073 char key_buf[512];
2074 
2075 if (root == NULL || path == NULL) {
2076 if (err_out != NULL) { *err_out = JSON_ERR_NULL_PARAM; }
2077 return NULL;
2078 }
2079 
2080 cur = root;
2081 p = path;
2082 
2083 while (*p != '\0') {
2084 /* Skip leading dot if present */
2085 if (*p == '.') { p++; }
2086 
2087 if (*p == '[') {
2088 /* Bracket notation: [N] or ["key"] */
2089 p++;
2090 if (*p == '"') {
2091 /* ["string key"] */
2092 const char* ks = ++p;
2093 while (*p != '\0' && *p != '"') { p++; }
2094 if (*p == '\0') {
2095 err = JSON_ERR_INVALID_JSON; goto fail;
2096 }
2097 size_t klen = (size_t)(p - ks);
2098 p++; /* skip closing " */
2099 if (*p != ']') { err = JSON_ERR_INVALID_JSON; goto fail; }
2100 p++;
2101 JsonValue* out;
2102 err = json_obj_get_n(cur, ks, klen, &out);
2103 if (err != JSON_OK) { goto fail; }
2104 cur = out;
2105 } else if (*p >= '0' && *p <= '9') {
2106 /* [N] */
2107 uint32_t idx = 0U;
2108 while (*p >= '0' && *p <= '9') {
2109 uint32_t prev = idx;
2110 idx = idx * 10U + (uint32_t)(*p - '0');
2111 if (idx < prev) { err = JSON_ERR_OVERFLOW; goto fail; }
2112 p++;
2113 }
2114 if (*p != ']') { err = JSON_ERR_INVALID_JSON; goto fail; }
2115 p++;
2116 JsonValue* out;
2117 err = json_arr_get(cur, idx, &out);
2118 if (err != JSON_OK) { goto fail; }
2119 cur = out;
2120 } else {
2121 err = JSON_ERR_INVALID_JSON; goto fail;
2122 }
2123 } else if ((*p >= 'a' && *p <= 'z') ||
2124 (*p >= 'A' && *p <= 'Z') ||
2125 (*p == '_') ||
2126 ((uint8_t)*p >= 0x80U)) {
2127 /* Dot notation: .identifier */
2128 const char* ks = p;
2129 while (*p != '\0' && *p != '.' && *p != '[') { p++; }
2130 size_t klen = (size_t)(p - ks);
2131 if (klen >= sizeof(key_buf)) { err = JSON_ERR_OVERFLOW; goto fail; }
2132 JSON_MEMCPY(key_buf, ks, klen);
2133 key_buf[klen] = '\0';
2134 JsonValue* out;
2135 err = json_obj_get_n(cur, ks, klen, &out);
2136 if (err != JSON_OK) { goto fail; }
2137 cur = out;
2138 } else {
2139 err = JSON_ERR_INVALID_JSON; goto fail;
2140 }
2141 }
2142 
2143 if (err_out != NULL) { *err_out = JSON_OK; }
2144 return (JsonValue*)(uintptr_t)cur; /* Cast away const for API convenience */
2145 
2146fail:
2147 if (err_out != NULL) { *err_out = err; }
2148 return NULL;
2149}
2150 
2151/* ── §11.11 Serializer ─────────────────────────────────────────────────── */
2152 
2153typedef struct {
2154 char* buf;
2155 size_t cap;
2156 size_t used;
2157 bool measure_only; /* When true: just count, don't write */
2158 JsonError err;
2159} JsonWriter;
2160 
2161JSON_INLINE JsonError jw_put(JsonWriter* w, char c)
2162{
2163 if (w->measure_only) { w->used++; return JSON_OK; }
2164 if (w->used >= w->cap - 1U) { w->err = JSON_ERR_BUFFER_TOO_SMALL; return JSON_ERR_BUFFER_TOO_SMALL; }
2165 w->buf[w->used++] = c;
2166 return JSON_OK;
2167}
2168 
2169static JsonError jw_puts(JsonWriter* w, const char* s, size_t n)
2170{
2171 size_t i;
2172 if (w->measure_only) { w->used += n; return JSON_OK; }
2173 for (i = 0U; i < n; i++) {
2174 if (jw_put(w, s[i]) != JSON_OK) { return JSON_ERR_BUFFER_TOO_SMALL; }
2175 }
2176 return JSON_OK;
2177}
2178 
2179static JsonError jw_indent(JsonWriter* w, uint32_t depth, uint32_t indent_sz)
2180{
2181 uint32_t i;
2182 for (i = 0U; i < depth * indent_sz; i++) {
2183 if (jw_put(w, ' ') != JSON_OK) { return JSON_ERR_BUFFER_TOO_SMALL; }
2184 }
2185 return JSON_OK;
2186}
2187 
2188/* Integer-to-decimal without sprintf (embedded-safe) */
2189static JsonError jw_int64(JsonWriter* w, int64_t v)
2190{
2191 char tmp[22]; /* max int64 = 20 digits + sign + NUL */
2192 uint32_t len = 0U;
2193 bool neg = (v < 0);
2194 uint64_t abs_v;
2195 
2196 if (neg) {
2197 abs_v = (v == INT64_MIN) ? (uint64_t)INT64_MAX + 1ULL
2198 : (uint64_t)(-v);
2199 if (jw_put(w, '-') != JSON_OK) { return JSON_ERR_BUFFER_TOO_SMALL; }
2200 } else {
2201 abs_v = (uint64_t)v;
2202 }
2203 
2204 if (abs_v == 0U) { return jw_put(w, '0'); }
2205 
2206 while (abs_v > 0U) {
2207 tmp[len++] = (char)('0' + (int)(abs_v % 10U));
2208 abs_v /= 10U;
2209 }
2210 
2211 /* Reverse */
2212 {
2213 uint32_t i;
2214 for (i = 0U; i < len; i++) {
2215 if (jw_put(w, tmp[len - 1U - i]) != JSON_OK) {
2216 return JSON_ERR_BUFFER_TOO_SMALL;
2217 }
2218 }
2219 }
2220 return JSON_OK;
2221}
2222 
2223/* Double-to-string (uses snprintf if available, else fallback) */
2224static JsonError jw_double(JsonWriter* w, double v)
2225{
2226 char tmp[32];
2227 size_t len;
2228 
2229#ifndef JSON_NO_STDLIB
2230 /* Use snprintf for correct double rendering */
2231 int r = snprintf(tmp, sizeof(tmp), "%.17g", v);
2232 if (r < 0 || (size_t)r >= sizeof(tmp)) { return JSON_ERR_OVERFLOW; }
2233 len = (size_t)r;
2234 /* Ensure there's a decimal point or exponent so it parses as float */
2235 {
2236 bool has_dot = false;
2237 size_t i;
2238 for (i = 0U; i < len; i++) {
2239 if (tmp[i] == '.' || tmp[i] == 'e' || tmp[i] == 'E') {
2240 has_dot = true; break;
2241 }
2242 }
2243 if (!has_dot && len + 2U < sizeof(tmp)) {
2244 tmp[len++] = '.'; tmp[len++] = '0'; tmp[len] = '\0';
2245 }
2246 }
2247#else
2248 /* Freestanding fallback: integer representation if lossless */
2249 int64_t as_int = (int64_t)v;
2250 if ((double)as_int == v) {
2251 snprintf(tmp, sizeof(tmp), "%lld.0", (long long)as_int);
2252 } else {
2253 snprintf(tmp, sizeof(tmp), "%g", v);
2254 }
2255 len = JSON_STRLEN(tmp);
2256#endif
2257 return jw_puts(w, tmp, len);
2258}
2259 
2260/* Write escaped JSON string content (no surrounding quotes) */
2261static JsonError jw_string_content(JsonWriter* w, const char* s, uint32_t len,
2262 bool ascii_only)
2263{
2264 uint32_t i = 0U;
2265 
2266 while (i < len) {
2267 uint8_t c = (uint8_t)s[i];
2268 
2269 if (c < 0x20U || c == '"' || c == '\\' ||
2270 (ascii_only && c >= 0x80U)) {
2271 /* Escape */
2272 char esc_buf[7]; /* \uXXXX + NUL */
2273 if (c == '"') { esc_buf[0]='\\'; esc_buf[1]='"'; jw_puts(w, esc_buf, 2U); }
2274 else if (c == '\\') { esc_buf[0]='\\'; esc_buf[1]='\\'; jw_puts(w, esc_buf, 2U); }
2275 else if (c == '\n') { esc_buf[0]='\\'; esc_buf[1]='n'; jw_puts(w, esc_buf, 2U); }
2276 else if (c == '\r') { esc_buf[0]='\\'; esc_buf[1]='r'; jw_puts(w, esc_buf, 2U); }
2277 else if (c == '\t') { esc_buf[0]='\\'; esc_buf[1]='t'; jw_puts(w, esc_buf, 2U); }
2278 else if (c == '\b') { esc_buf[0]='\\'; esc_buf[1]='b'; jw_puts(w, esc_buf, 2U); }
2279 else if (c == '\f') { esc_buf[0]='\\'; esc_buf[1]='f'; jw_puts(w, esc_buf, 2U); }
2280 else {
2281 /* \uXXXX */
2282 static const char hex[] = "0123456789abcdef";
2283 esc_buf[0]='\\'; esc_buf[1]='u';
2284 esc_buf[2]='0'; esc_buf[3]='0';
2285 esc_buf[4]=hex[(c>>4)&0xFU];
2286 esc_buf[5]=hex[c&0xFU];
2287 jw_puts(w, esc_buf, 6U);
2288 }
2289 i++;
2290 } else {
2291 jw_put(w, (char)c);
2292 i++;
2293 }
2294 }
2295 return w->err;
2296}
2297 
2298/** @brief Core recursive serializer. Uses C stack but depth is bounded at parse time. */
2299static JsonError json__write_value(JsonWriter* w, const JsonValue* v,
2300 uint32_t depth,
2301 const JsonWriteOpts* opts)
2302{
2303 JsonError err;
2304 
2305 JSON_ASSERT(v != NULL && opts != NULL);
2306 
2307 switch (v->type) {
2308 case JSON_NULL:
2309 return jw_puts(w, "null", 4U);
2310 
2311 case JSON_BOOL:
2312 return v->v.b ? jw_puts(w, "true", 4U) : jw_puts(w, "false", 5U);
2313 
2314 case JSON_INTEGER:
2315 return jw_int64(w, v->v.i);
2316 
2317 case JSON_FLOAT:
2318 return jw_double(w, v->v.f);
2319 
2320 case JSON_STRING:
2321 err = jw_put(w, '"');
2322 if (err != JSON_OK) { return err; }
2323 err = jw_string_content(w, v->v.s.data ? v->v.s.data : "",
2324 v->v.s.len, opts->ascii_only);
2325 if (err != JSON_OK) { return err; }
2326 return jw_put(w, '"');
2327 
2328 case JSON_ARRAY: {
2329 const JsonArr* a = &v->v.a;
2330 uint32_t i;
2331 err = jw_put(w, '[');
2332 if (err != JSON_OK) { return err; }
2333 for (i = 0U; i < a->len; i++) {
2334 if (opts->pretty) {
2335 jw_put(w, '\n');
2336 jw_indent(w, depth + 1U, opts->indent);
2337 }
2338 err = json__write_value(w, a->items[i], depth + 1U, opts);
2339 if (err != JSON_OK) { return err; }
2340 if (i + 1U < a->len) {
2341 jw_put(w, ',');
2342 if (!opts->pretty) { /* no space */ }
2343 }
2344 }
2345 if (opts->pretty && a->len > 0U) {
2346 jw_put(w, '\n');
2347 jw_indent(w, depth, opts->indent);
2348 }
2349 return jw_put(w, ']');
2350 }
2351 
2352 case JSON_OBJECT: {
2353 const JsonObj* o = &v->v.o;
2354 uint32_t i;
2355 err = jw_put(w, '{');
2356 if (err != JSON_OK) { return err; }
2357 for (i = 0U; i < o->len; i++) {
2358 uint32_t pair_idx = (opts->sort_keys && o->is_sorted && o->sorted_idx)
2359 ? o->sorted_idx[i] : i;
2360 const JsonPair* pair = &o->pairs[pair_idx];
2361 if (opts->pretty) {
2362 jw_put(w, '\n');
2363 jw_indent(w, depth + 1U, opts->indent);
2364 }
2365 jw_put(w, '"');
2366 jw_string_content(w, pair->key.data ? pair->key.data : "",
2367 pair->key.len, opts->ascii_only);
2368 jw_put(w, '"');
2369 jw_put(w, ':');
2370 if (opts->pretty) { jw_put(w, ' '); }
2371 err = json__write_value(w, pair->val, depth + 1U, opts);
2372 if (err != JSON_OK) { return err; }
2373 if (i + 1U < o->len) {
2374 jw_put(w, ',');
2375 }
2376 }
2377 if (opts->pretty && o->len > 0U) {
2378 jw_put(w, '\n');
2379 jw_indent(w, depth, opts->indent);
2380 }
2381 return jw_put(w, '}');
2382 }
2383 
2384 default:
2385 return JSON_ERR_INVALID_JSON;
2386 }
2387}
2388 
2389static const JsonWriteOpts k_default_opts = { false, 2U, false, false, false };
2390 
2391JsonError json_write(const JsonValue* v, char* buf, size_t buf_size,
2392 size_t* written, const JsonWriteOpts* opts)
2393{
2394 JsonWriter w;
2395 JsonError err;
2396 
2397 if (v == NULL) { return JSON_ERR_NULL_PARAM; }
2398 if (buf == NULL && buf_size > 0U) { return JSON_ERR_NULL_PARAM; }
2399 
2400 if (opts == NULL) { opts = &k_default_opts; }
2401 
2402 JSON_MEMSET(&w, 0, sizeof(w));
2403 w.buf = buf;
2404 w.cap = buf_size;
2405 w.measure_only = (buf == NULL);
2406 
2407 err = json__write_value(&w, v, 0U, opts);
2408 
2409 if (err == JSON_OK && !w.measure_only) {
2410 /* NUL-terminate */
2411 if (w.used < buf_size) {
2412 buf[w.used] = '\0';
2413 } else {
2414 return JSON_ERR_BUFFER_TOO_SMALL;
2415 }
2416 if (opts->trailing_nl && w.used + 1U < buf_size) {
2417 buf[w.used++] = '\n';
2418 buf[w.used] = '\0';
2419 }
2420 }
2421 
2422 if (written != NULL) { *written = w.used; }
2423 return err;
2424}
2425 
2426JsonError json_measure(const JsonValue* v, size_t* size_out,
2427 const JsonWriteOpts* opts)
2428{
2429 if (v == NULL || size_out == NULL) { return JSON_ERR_NULL_PARAM; }
2430 return json_write(v, NULL, 0U, size_out, opts);
2431}
2432 
2433JsonError json_write_arena(const JsonValue* v, JsonArena* arena,
2434 char** out, size_t* len_out,
2435 const JsonWriteOpts* opts)
2436{
2437 size_t needed = 0U;
2438 char* buf;
2439 JsonError err;
2440 
2441 if (v == NULL || arena == NULL || out == NULL) { return JSON_ERR_NULL_PARAM; }
2442 
2443 /* Measure first */
2444 err = json_measure(v, &needed, opts);
2445 if (err != JSON_OK) { return err; }
2446 
2447 needed += 2U; /* NUL + optional newline */
2448 
2449 buf = (char*)json_arena_alloc(arena, needed);
2450 if (JSON_UNLIKELY(buf == NULL)) { return JSON_ERR_OOM; }
2451 
2452 err = json_write(v, buf, needed, len_out, opts);
2453 if (err != JSON_OK) { return err; }
2454 
2455 *out = buf;
2456 return JSON_OK;
2457}
2458 
2459/* ── §11.12 Value construction ─────────────────────────────────────────── */
2460 
2461JsonValue* json_make_null(JsonArena* arena)
2462{
2463 JsonValue* v = json__new_node(arena);
2464 if (v != NULL) { v->type = JSON_NULL; }
2465 return v;
2466}
2467 
2468JsonValue* json_make_bool(JsonArena* arena, bool val)
2469{
2470 JsonValue* v = json__new_node(arena);
2471 if (v != NULL) { v->type = JSON_BOOL; v->v.b = val; }
2472 return v;
2473}
2474 
2475JsonValue* json_make_int(JsonArena* arena, int64_t val)
2476{
2477 JsonValue* v = json__new_node(arena);
2478 if (v != NULL) { v->type = JSON_INTEGER; v->v.i = val; }
2479 return v;
2480}
2481 
2482JsonValue* json_make_float(JsonArena* arena, double val)
2483{
2484 JsonValue* v;
2485 /* Safety: never store NaN or Inf */
2486 if (isnan(val) || isinf(val)) { return NULL; }
2487 v = json__new_node(arena);
2488 if (v != NULL) { v->type = JSON_FLOAT; v->v.f = val; }
2489 return v;
2490}
2491 
2492JsonValue* json_make_string(JsonArena* arena, const char* s, uint32_t len)
2493{
2494 JsonValue* v;
2495 char* copy;
2496 
2497 if (arena == NULL) { return NULL; }
2498 v = json__new_node(arena);
2499 if (v == NULL) { return NULL; }
2500 
2501 if (len == 0U || s == NULL) {
2502 v->type = JSON_STRING;
2503 v->v.s.data= NULL;
2504 v->v.s.len = 0U;
2505 v->v.s.hash= json_fnv1a(NULL, 0U);
2506 return v;
2507 }
2508 
2509 copy = (char*)json_arena_alloc(arena, (size_t)len);
2510 if (JSON_UNLIKELY(copy == NULL)) { return NULL; }
2511 JSON_MEMCPY(copy, s, (size_t)len);
2512 
2513 v->type = JSON_STRING;
2514 v->v.s.data= copy;
2515 v->v.s.len = len;
2516 v->v.s.hash= json_fnv1a(copy, len);
2517 return v;
2518}
2519 
2520JsonValue* json_make_stringz(JsonArena* arena, const char* s)
2521{
2522 if (s == NULL) { return json_make_string(arena, NULL, 0U); }
2523 return json_make_string(arena, s, (uint32_t)JSON_STRLEN(s));
2524}
2525 
2526JsonValue* json_make_array(JsonArena* arena)
2527{
2528 JsonValue* v = json__new_node(arena);
2529 if (v != NULL) { v->type = JSON_ARRAY; }
2530 return v;
2531}
2532 
2533JsonValue* json_make_object(JsonArena* arena)
2534{
2535 JsonValue* v = json__new_node(arena);
2536 if (v != NULL) { v->type = JSON_OBJECT; }
2537 return v;
2538}
2539 
2540JsonError json_arr_push(JsonValue* arr, JsonArena* arena, JsonValue* item)
2541{
2542 JsonArr* a;
2543 JsonError err;
2544 
2545 if (arr == NULL || arena == NULL || item == NULL) { return JSON_ERR_NULL_PARAM; }
2546 if (arr->type != JSON_ARRAY) { return JSON_ERR_TYPE_MISMATCH; }
2547 
2548 a = &arr->v.a;
2549 if (a->len >= a->cap) {
2550 err = json__arr_grow(a, arena);
2551 if (err != JSON_OK) { return err; }
2552 }
2553 a->items[a->len++] = item;
2554 return JSON_OK;
2555}
2556 
2557JsonError json_obj_set(JsonValue* obj, JsonArena* arena,
2558 const char* key, uint32_t klen, JsonValue* val)
2559{
2560 JsonObj* o;
2561 JsonError err;
2562 char* key_copy;
2563 
2564 if (obj == NULL || arena == NULL || key == NULL || val == NULL) {
2565 return JSON_ERR_NULL_PARAM;
2566 }
2567 if (obj->type != JSON_OBJECT) { return JSON_ERR_TYPE_MISMATCH; }
2568 
2569 o = &obj->v.o;
2570 
2571 /* Check for existing key (update in place) */
2572 {
2573 uint32_t hash = json_fnv1a(key, klen);
2574 uint32_t i;
2575 for (i = 0U; i < o->len; i++) {
2576 JsonStr* k = &o->pairs[i].key;
2577 if (k->hash == hash && k->len == klen &&
2578 JSON_MEMCMP(k->data, key, klen) == 0) {
2579 o->pairs[i].val = val;
2580 return JSON_OK;
2581 }
2582 }
2583 }
2584 
2585 /* Insert new pair */
2586 if (o->len >= o->cap) {
2587 err = json__obj_grow(o, arena);
2588 if (err != JSON_OK) { return err; }
2589 }
2590 
2591 key_copy = (char*)json_arena_alloc(arena, (size_t)klen);
2592 if (JSON_UNLIKELY(key_copy == NULL)) { return JSON_ERR_OOM; }
2593 JSON_MEMCPY(key_copy, key, klen);
2594 
2595 o->pairs[o->len].key.data = key_copy;
2596 o->pairs[o->len].key.len = klen;
2597 o->pairs[o->len].key.hash = json_fnv1a(key_copy, klen);
2598 o->pairs[o->len].val = val;
2599 o->len++;
2600 o->is_sorted = false; /* Invalidate sort */
2601 return JSON_OK;
2602}
2603 
2604JsonError json_obj_setz(JsonValue* obj, JsonArena* arena,
2605 const char* key, JsonValue* val)
2606{
2607 if (key == NULL) { return JSON_ERR_NULL_PARAM; }
2608 return json_obj_set(obj, arena, key, (uint32_t)JSON_STRLEN(key), val);
2609}
2610 
2611/* ── §11.13 Equality & Clone ───────────────────────────────────────────── */
2612 
2613bool json_equal(const JsonValue* a, const JsonValue* b)
2614{
2615 uint32_t i;
2616 
2617 if (a == b) { return true; }
2618 if (!a || !b) { return false; }
2619 if (a->type != b->type) {
2620 /* Allow integer/float cross-comparison */
2621 if (json_is_number(a) && json_is_number(b)) {
2622 double da, db;
2623 json_get_float(a, &da);
2624 json_get_float(b, &db);
2625 return da == db;
2626 }
2627 return false;
2628 }
2629 
2630 switch (a->type) {
2631 case JSON_NULL: return true;
2632 case JSON_BOOL: return a->v.b == b->v.b;
2633 case JSON_INTEGER: return a->v.i == b->v.i;
2634 case JSON_FLOAT: return a->v.f == b->v.f;
2635 case JSON_STRING:
2636 if (a->v.s.len != b->v.s.len) { return false; }
2637 if (a->v.s.hash != b->v.s.hash) { return false; }
2638 return JSON_MEMCMP(a->v.s.data, b->v.s.data, a->v.s.len) == 0;
2639 case JSON_ARRAY:
2640 if (a->v.a.len != b->v.a.len) { return false; }
2641 for (i = 0U; i < a->v.a.len; i++) {
2642 if (!json_equal(a->v.a.items[i], b->v.a.items[i])) { return false; }
2643 }
2644 return true;
2645 case JSON_OBJECT:
2646 if (a->v.o.len != b->v.o.len) { return false; }
2647 /* Check all keys in a exist in b with equal values */
2648 for (i = 0U; i < a->v.o.len; i++) {
2649 const JsonStr* k = &a->v.o.pairs[i].key;
2650 JsonValue* bval = NULL;
2651 if (json_obj_get_n(b, k->data, k->len, &bval) != JSON_OK) {
2652 return false;
2653 }
2654 if (!json_equal(a->v.o.pairs[i].val, bval)) { return false; }
2655 }
2656 return true;
2657 default: return false;
2658 }
2659}
2660 
2661JsonValue* json_clone(JsonArena* dst, const JsonValue* src)
2662{
2663 JsonValue* out;
2664 uint32_t i;
2665 
2666 if (dst == NULL || src == NULL) { return NULL; }
2667 
2668 out = json__new_node(dst);
2669 if (out == NULL) { return NULL; }
2670 
2671 out->type = src->type;
2672 
2673 switch (src->type) {
2674 case JSON_NULL: break;
2675 case JSON_BOOL: out->v.b = src->v.b; break;
2676 case JSON_INTEGER: out->v.i = src->v.i; break;
2677 case JSON_FLOAT: out->v.f = src->v.f; break;
2678 case JSON_STRING: {
2679 char* copy = NULL;
2680 if (src->v.s.len > 0U && src->v.s.data != NULL) {
2681 copy = (char*)json_arena_alloc(dst, src->v.s.len);
2682 if (copy == NULL) { return NULL; }
2683 JSON_MEMCPY(copy, src->v.s.data, src->v.s.len);
2684 }
2685 out->v.s.data = copy;
2686 out->v.s.len = src->v.s.len;
2687 out->v.s.hash = src->v.s.hash;
2688 break;
2689 }
2690 case JSON_ARRAY: {
2691 JsonArr* da = &out->v.a;
2692 const JsonArr* sa = &src->v.a;
2693 da->len = sa->len;
2694 da->cap = sa->len;
2695 da->items = NULL;
2696 if (sa->len > 0U) {
2697 da->items = (JsonValue**)json_arena_alloc(dst,
2698 sa->len * sizeof(JsonValue*));
2699 if (da->items == NULL) { return NULL; }
2700 for (i = 0U; i < sa->len; i++) {
2701 da->items[i] = json_clone(dst, sa->items[i]);
2702 if (da->items[i] == NULL) { return NULL; }
2703 }
2704 }
2705 break;
2706 }
2707 case JSON_OBJECT: {
2708 JsonObj* do_ = &out->v.o;
2709 const JsonObj* so = &src->v.o;
2710 do_->len = so->len;
2711 do_->cap = so->len;
2712 do_->sorted_idx = NULL;
2713 do_->is_sorted = false;
2714 do_->pairs = NULL;
2715 if (so->len > 0U) {
2716 do_->pairs = (JsonPair*)json_arena_alloc(dst,
2717 so->len * sizeof(JsonPair));
2718 if (do_->pairs == NULL) { return NULL; }
2719 for (i = 0U; i < so->len; i++) {
2720 const JsonStr* sk = &so->pairs[i].key;
2721 char* kc = NULL;
2722 if (sk->len > 0U && sk->data != NULL) {
2723 kc = (char*)json_arena_alloc(dst, sk->len);
2724 if (kc == NULL) { return NULL; }
2725 JSON_MEMCPY(kc, sk->data, sk->len);
2726 }
2727 do_->pairs[i].key.data = kc;
2728 do_->pairs[i].key.len = sk->len;
2729 do_->pairs[i].key.hash = sk->hash;
2730 do_->pairs[i].val = json_clone(dst, so->pairs[i].val);
2731 if (do_->pairs[i].val == NULL) { return NULL; }
2732 }
2733 }
2734 break;
2735 }
2736 default: break;
2737 }
2738 return out;
2739}
2740 
2741/* ── §11.14 Object sort (public trigger) ───────────────────────────────── */
2742 
2743/**
2744 * @brief Build sorted index for object to enable O(log n) lookup.
2745 * Call after parsing if many lookups are expected.
2746 *
2747 * @note Called automatically by json_obj_get if is_sorted == false.
2748 * To sort eagerly: json_obj_finalize(arena, root).
2749 */
2750JsonError json_obj_finalize(JsonArena* arena, JsonValue* obj)
2751{
2752 if (obj == NULL || arena == NULL) { return JSON_ERR_NULL_PARAM; }
2753 if (obj->type != JSON_OBJECT) { return JSON_ERR_TYPE_MISMATCH; }
2754 return json__obj_sort(obj, arena);
2755}
2756 
2757/* ── §11.15 Error strings ──────────────────────────────────────────────── */
2758 
2759const char* json_error_str(JsonError err)
2760{
2761 switch (err) {
2762 case JSON_OK: return "OK";
2763 case JSON_ERR_NULL_PARAM: return "NULL parameter";
2764 case JSON_ERR_OOM: return "Out of memory";
2765 case JSON_ERR_INVALID_JSON: return "Invalid JSON";
2766 case JSON_ERR_UNEXPECTED_EOF: return "Unexpected end of input";
2767 case JSON_ERR_DEPTH_EXCEEDED: return "Nesting depth exceeded";
2768 case JSON_ERR_INVALID_STRING: return "Invalid string / escape sequence";
2769 case JSON_ERR_INVALID_NUMBER: return "Invalid number";
2770 case JSON_ERR_INVALID_UTF8: return "Invalid UTF-8 sequence";
2771 case JSON_ERR_OVERFLOW: return "Arithmetic or buffer overflow";
2772 case JSON_ERR_NOT_FOUND: return "Key or index not found";
2773 case JSON_ERR_TYPE_MISMATCH: return "Type mismatch";
2774 case JSON_ERR_BUFFER_TOO_SMALL: return "Output buffer too small";
2775 case JSON_ERR_NODE_LIMIT: return "Node count limit exceeded";
2776 default: return "Unknown error";
2777 }
2778}
2779 
2780#undef JSON__CHECK_V
2781#undef JSON__CHECK_OUT
2782 
2783#endif /* JSON_IMPLEMENTATION */
2784#endif /* JSON_PAL_H */
2785 
2786/*
2787 * ============================================================================
2788 * USAGE EXAMPLE
2789 * ============================================================================
2790 *
2791 * #define JSON_IMPLEMENTATION
2792 * #include "json_pal.h"
2793 * #include <stdio.h>
2794 *
2795 * int main(void) {
2796 * JsonArena* arena = json_arena_create(NULL, 64 * 1024);
2797 * if (!arena) return 1;
2798 *
2799 * const char* input =
2800 * "{\"name\":\"Alice\",\"scores\":[98,72,85],\"active\":true}";
2801 *
2802 * JsonError err;
2803 * JsonValue* root = json_parse_cstr(arena, input, &err);
2804 * if (!root) {
2805 * fprintf(stderr, "Parse error: %s\n", json_error_str(err));
2806 * json_arena_destroy(arena);
2807 * return 1;
2808 * }
2809 *
2810 * // Finalize for O(log n) key lookup
2811 * json_obj_finalize(arena, root);
2812 *
2813 * // Access fields
2814 * JsonValue* name_v;
2815 * json_obj_get(root, "name", &name_v);
2816 * const char* name; uint32_t nlen;
2817 * json_get_string(name_v, &name, &nlen);
2818 * printf("Name: %.*s\n", (int)nlen, name);
2819 *
2820 * // Path query
2821 * JsonValue* score1 = json_path(root, "scores[1]", NULL);
2822 * int64_t s; json_get_int(score1, &s);
2823 * printf("Score[1]: %lld\n", (long long)s);
2824 *
2825 * // Build and serialize
2826 * JsonValue* out_obj = json_make_object(arena);
2827 * json_obj_setz(out_obj, arena, "result", json_make_int(arena, s * 2));
2828 * char buf[256];
2829 * JsonWriteOpts opts = { .pretty = true, .indent = 4 };
2830 * json_write(out_obj, buf, sizeof(buf), NULL, &opts);
2831 * printf("%s\n", buf);
2832 *
2833 * json_arena_destroy(arena);
2834 * return 0;
2835 * }
2836 *
2837 * ============================================================================
2838 * FEATURE SUMMARY
2839 * ============================================================================
2840 *
2841 * Parsing:
2842 * [x] Full RFC 8259 JSON compliance
2843 * [x] Iterative parser — no recursion, O(1) stack usage
2844 * [x] Depth-limited (JSON_MAX_DEPTH, default 64)
2845 * [x] Node-count-limited (JSON_MAX_NODES)
2846 * [x] Integer/float auto-detection at parse time
2847 * [x] \uXXXX escape decoding with surrogate pair support
2848 * [x] UTF-8 validation (RFC 3629)
2849 * [x] Leading-zero detection
2850 * [x] NaN/Inf rejection
2851 * [x] Control character rejection in strings
2852 *
2853 * Memory:
2854 * [x] Arena allocator — zero fragmentation, O(1) alloc
2855 * [x] Pluggable allocator (custom malloc/free/realloc)
2856 * [x] Arena reset (keep memory, reuse for next parse)
2857 * [x] All allocations zeroed
2858 *
2859 * Object lookup:
2860 * [x] FNV-1a hash for fast key comparison
2861 * [x] Introsort (O(n log n) worst-case) for key index
2862 * [x] Binary search O(log n) after finalization
2863 * [x] Linear scan O(n) before finalization
2864 * [x] Insertion-order iteration
2865 *
2866 * Serialization:
2867 * [x] Compact and pretty-print modes
2868 * [x] Configurable indent size
2869 * [x] Sorted key output
2870 * [x] ASCII-only escape mode
2871 * [x] Size measurement without allocation (json_measure)
2872 * [x] Arena-allocated output string
2873 *
2874 * Value construction:
2875 * [x] Null, bool, integer, float, string, array, object
2876 * [x] Array push, object set (insert or update)
2877 * [x] Deep equality (json_equal)
2878 * [x] Deep clone across arenas (json_clone)
2879 *
2880 * Platform:
2881 * [x] PAL layer: overridable memcpy/memset/memcmp/strlen/strtod/assert
2882 * [x] C99 compatible; C11 static asserts if available
2883 * [x] Endianness detection
2884 * [x] Freestanding (JSON_NO_STDLIB) support
2885 * [x] MSVC / GCC / Clang detection
2886 * [x] Compiler hints: always_inline, likely/unlikely, restrict
2887 *
2888 * Safety:
2889 * [x] All pointer parameters NULL-checked
2890 * [x] All array bounds checked
2891 * [x] All arithmetic overflow-checked
2892 * [x] No dynamic allocation (arena only)
2893 * [x] No VLAs, no recursion in parser
2894 * [x] Explicit error codes — no exceptions, no abort
2895 * ============================================================================
2896 */
2897