From 222fe7e3f99415a7013201e803cd35772bc104a3 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 9 Sep 2026 13:49:56 +0800 Subject: [PATCH] 3rdparty (yyjson): upgrades to 0.13.0 --- src/3rdparty/yyjson/repo.json | 2 +- src/3rdparty/yyjson/yyjson.c | 985 ++++++++++++++------ src/3rdparty/yyjson/yyjson.h | 1585 +++++++++++++++++++-------------- 3 files changed, 1612 insertions(+), 960 deletions(-) diff --git a/src/3rdparty/yyjson/repo.json b/src/3rdparty/yyjson/repo.json index bfa523fb5..02c0cd7e8 100644 --- a/src/3rdparty/yyjson/repo.json +++ b/src/3rdparty/yyjson/repo.json @@ -1,6 +1,6 @@ { "home": "https://github.com/ibireme/yyjson", "license": "MIT ( embed in source )", - "version": "0.12.0", + "version": "0.13.0", "author": "ibireme" } diff --git a/src/3rdparty/yyjson/yyjson.c b/src/3rdparty/yyjson/yyjson.c index c16d92581..a9bc5f87a 100644 --- a/src/3rdparty/yyjson/yyjson.c +++ b/src/3rdparty/yyjson/yyjson.c @@ -21,7 +21,6 @@ *============================================================================*/ #include "yyjson.h" -#include /* for `HUGE_VAL/INFINIY/NAN` macros, no libm required */ @@ -35,7 +34,7 @@ # pragma clang diagnostic ignored "-Wunused-label" # pragma clang diagnostic ignored "-Wunused-macros" # pragma clang diagnostic ignored "-Wunused-variable" -#elif defined(__GNUC__) +#elif YYJSON_IS_REAL_GCC && yyjson_gcc_available(4, 2, 0) # pragma GCC diagnostic ignored "-Wunused-function" # pragma GCC diagnostic ignored "-Wunused-parameter" # pragma GCC diagnostic ignored "-Wunused-label" @@ -46,6 +45,7 @@ # pragma warning(disable:4101) /* unreferenced variable */ # pragma warning(disable:4102) /* unreferenced label */ # pragma warning(disable:4127) /* conditional expression is constant */ +# pragma warning(disable:4702) /* unreachable code */ # pragma warning(disable:4706) /* assignment within conditional expression */ #endif @@ -109,37 +109,58 @@ uint32_t yyjson_version(void) { #endif /* int128 type */ -#if defined(__SIZEOF_INT128__) && (__SIZEOF_INT128__ == 16) && \ - (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)) -# define YYJSON_HAS_INT128 1 -#else -# define YYJSON_HAS_INT128 0 +#ifndef YYJSON_HAS_INT128 +# if defined(__SIZEOF_INT128__) && (__SIZEOF_INT128__ == 16) && \ + (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)) && \ + (!defined(__EMSCRIPTEN__) && !defined(__wasm__)) +# define YYJSON_HAS_INT128 1 +# else +# define YYJSON_HAS_INT128 0 +# endif #endif /* IEEE 754 floating-point binary representation */ -#if defined(__STDC_IEC_559__) || defined(__STDC_IEC_60559_BFP__) -# define YYJSON_HAS_IEEE_754 1 -#elif FLT_RADIX == 2 && \ +#ifndef YYJSON_HAS_IEEE_754 +# if defined(__STDC_IEC_559__) || defined(__STDC_IEC_60559_BFP__) +# define YYJSON_HAS_IEEE_754 1 +# elif FLT_RADIX == 2 && \ FLT_MANT_DIG == 24 && FLT_DIG == 6 && \ FLT_MIN_EXP == -125 && FLT_MAX_EXP == 128 && \ FLT_MIN_10_EXP == -37 && FLT_MAX_10_EXP == 38 && \ DBL_MANT_DIG == 53 && DBL_DIG == 15 && \ DBL_MIN_EXP == -1021 && DBL_MAX_EXP == 1024 && \ DBL_MIN_10_EXP == -307 && DBL_MAX_10_EXP == 308 -# define YYJSON_HAS_IEEE_754 1 -#else -# define YYJSON_HAS_IEEE_754 0 -# undef YYJSON_DISABLE_FAST_FP_CONV -# define YYJSON_DISABLE_FAST_FP_CONV 1 +# define YYJSON_HAS_IEEE_754 1 +# else +# define YYJSON_HAS_IEEE_754 0 +# undef YYJSON_DISABLE_FAST_FP_CONV +# define YYJSON_DISABLE_FAST_FP_CONV 1 +# endif +#endif + +#if YYJSON_DISABLE_FAST_FP_CONV && YYJSON_FREESTANDING +# error DISABLE_FAST_FP_CONV and FREESTANDING cannot be used together +#endif + +/* Inf and NaN */ +#ifndef INFINITY +# ifndef HUGE_VAL +# define INFINITY ((double)(1.0 / 0.0)) +# else +# define INFINITY HUGE_VAL +# endif +#endif +#ifndef NAN +# define NAN ((double)(0.0 / 0.0)) #endif /* Correct rounding in double number computations. On the x86 architecture, some compilers may use x87 FPU instructions for - floating-point arithmetic. The x87 FPU loads all floating point number as - 80-bit double-extended precision internally, then rounds the result to original - precision, which may produce inaccurate results. For a more detailed + floating-point arithmetic. The x87 FPU loads all floating-point numbers as + 80-bit double-extended precision internally, then rounds the result to the + original precision, which may produce inaccurate results. For a more detailed explanation, see the paper: https://arxiv.org/abs/cs/0701192 Here are some examples of double precision calculation error: @@ -155,7 +176,7 @@ uint32_t yyjson_version(void) { If we are sure that there's no similar error described above, we can define the YYJSON_DOUBLE_MATH_CORRECT as 1 to enable the fast path calculation. This is - not an accurate detection, it's just try to avoid the error at compile-time. + not an accurate detection; it just tries to avoid the error at compile-time. An accurate detection can be done at run-time: bool is_double_math_correct(void) { @@ -317,35 +338,20 @@ uint32_t yyjson_version(void) { #define YYJSON_ALC_DYN_MIN_SIZE 0x1000 /* Default value for compile-time options. */ -#ifndef YYJSON_DISABLE_READER -#define YYJSON_DISABLE_READER 0 -#endif -#ifndef YYJSON_DISABLE_WRITER -#define YYJSON_DISABLE_WRITER 0 -#endif -#ifndef YYJSON_DISABLE_INCR_READER -#define YYJSON_DISABLE_INCR_READER 0 -#endif -#ifndef YYJSON_DISABLE_UTILS -#define YYJSON_DISABLE_UTILS 0 -#endif -#ifndef YYJSON_DISABLE_FAST_FP_CONV -#define YYJSON_DISABLE_FAST_FP_CONV 0 -#endif -#ifndef YYJSON_DISABLE_NON_STANDARD -#define YYJSON_DISABLE_NON_STANDARD 0 -#endif -#ifndef YYJSON_DISABLE_UTF8_VALIDATION -#define YYJSON_DISABLE_UTF8_VALIDATION 0 + +#ifndef YYJSON_READER_DEPTH_LIMIT +#define YYJSON_READER_DEPTH_LIMIT 0 #endif - +#ifndef YYJSON_WRITER_DEPTH_LIMIT +#define YYJSON_WRITER_DEPTH_LIMIT 0 +#endif /*============================================================================== * MARK: - Macros (Private) *============================================================================*/ -/* Macros used for loop unrolling and other purpose. */ +/* Macros used for loop unrolling and other purposes. */ #define repeat2(x) { x x } #define repeat4(x) { x x x x } #define repeat8(x) { x x x x x x x x } @@ -385,7 +391,7 @@ uint32_t yyjson_version(void) { #define U32(hi) ((u32)(hi##UL)) /* Used to cast away (remove) const qualifier. */ -#define constcast(type) (type)(void *)(size_t)(const void *) +#define constcast yyjson_constcast /* Compiler barriers for single variables. @@ -438,6 +444,7 @@ uint32_t yyjson_version(void) { #define MSG_ERR_UTF8 "invalid utf-8 encoding in string" #define MSG_ERR_UTF16 "UTF-16 encoding is not supported" #define MSG_ERR_UTF32 "UTF-32 encoding is not supported" +#define MSG_DEPTH "depth limit exceeded" /* U64 constant values */ #undef U64_MAX @@ -524,7 +531,7 @@ uint32_t yyjson_version(void) { * MARK: - Types (Private) *============================================================================*/ -/** Type define for primitive types. */ +/** Type aliases for primitive types. */ typedef float f32; typedef double f64; typedef int8_t i8; @@ -929,7 +936,7 @@ static_inline bool char_is_sign(u8 d) { return !!(char_table3[d] & CHAR_TYPE_SIGN); } -/** Match a none-zero digit: [1-9] */ +/** Match a non-zero digit: [1-9] */ static_inline bool char_is_nonzero(u8 d) { return !!(char_table3[d] & CHAR_TYPE_NONZERO); } @@ -939,7 +946,7 @@ static_inline bool char_is_digit(u8 d) { return !!(char_table3[d] & CHAR_TYPE_DIGIT); } -/** Match an exponent sign: [eE]. */ +/** Match an exponent character: [eE]. */ static_inline bool char_is_exp(u8 d) { return !!(char_table3[d] & CHAR_TYPE_EXP); } @@ -1016,10 +1023,10 @@ static_inline usize ext_space_len(const u8 *cur) { *============================================================================*/ /** - This table is used to convert 4 hex character sequence to a number. - A valid hex character [0-9A-Fa-f] will mapped to it's raw number [0x00, 0x0F], - an invalid hex character will mapped to [0xF0]. - (generate with misc/make_tables.c) + This table is used to convert a 4-hex-character sequence to a number. + A valid hex character [0-9A-Fa-f] is mapped to its raw value [0x00, 0x0F]; + an invalid hex character is mapped to [0xF0]. + (generated with misc/make_tables.c) */ static const u8 hex_conv_table[256] = { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, @@ -1187,16 +1194,18 @@ utf8_seq_def(b4_req2, 03, 30, 00, 00) /** Maximum pow10 exponent that can be represented exactly as a float64. */ #define F64_POW10_MAX_EXACT_EXP 22 +#if YYJSON_DOUBLE_MATH_CORRECT /** Cached pow10 table. */ static const f64 f64_pow10_table[F64_POW10_MAX_EXACT_EXP + 1] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 }; +#endif /** Maximum pow10 exponent that can be represented exactly as a uint64. */ #define U64_POW10_MAX_EXACT_EXP 19 -/** Table: [ 10^0, ..., 10^19 ] (generate with misc/make_tables.c) */ +/** Table: [ 10^0, ..., 10^19 ] (generated with misc/make_tables.c) */ static const u64 u64_pow10_table[U64_POW10_MAX_EXACT_EXP + 1] = { U64(0x00000000, 0x00000001), U64(0x00000000, 0x0000000A), U64(0x00000000, 0x00000064), U64(0x00000000, 0x000003E8), @@ -1222,9 +1231,9 @@ static const u64 u64_pow10_table[U64_POW10_MAX_EXACT_EXP + 1] = { /** Maximum exact decimal exponent in pow10_sig_table */ #define POW10_SIG_TABLE_MAX_EXACT_EXP 55 -/** Normalized significant 128 bits of pow10, no rounded up (size: 10.4KB). +/** Normalized significant 128 bits of pow10, not rounded up (size: 10.4KB). This lookup table is used by both the double number reader and writer. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ static const u64 pow10_sig_table[] = { U64(0xBF29DCAB, 0xA82FDEAE), U64(0x7432EE87, 0x3880FC33), /* ~= 10^-343 */ U64(0xEEF453D6, 0x923BD65A), U64(0x113FAA29, 0x06A13B3F), /* ~= 10^-342 */ @@ -1898,7 +1907,7 @@ static const u64 pow10_sig_table[] = { /** Get the cached pow10 value from `pow10_sig_table`. - @param exp10 The exponent of pow(10, e). This value must in range + @param exp10 The exponent of pow(10, e). This value must be in the range `POW10_SIG_TABLE_MIN_EXP` to `POW10_SIG_TABLE_MAX_EXP`. @param hi The highest 64 bits of pow(10, e). @param lo The lower 64 bits after `hi`. @@ -1910,7 +1919,8 @@ static_inline void pow10_table_get_sig(i32 exp10, u64 *hi, u64 *lo) { } /** - Get the exponent (base 2) for highest 64 bits significand in `pow10_sig_table`. + Get the exponent (base 2) for the highest 64-bit significand in + `pow10_sig_table`. */ static_inline void pow10_table_get_exp(i32 exp10, i32 *exp2) { /* e2 = floor(log2(pow(10, e))) - 64 + 1 */ @@ -1940,7 +1950,7 @@ static_inline u64 f64_to_bits(f64 f) { return u; } -/** Convert double to bits. */ +/** Convert float to bits. */ static_inline u32 f32_to_bits(f32 f) { u32 u; memcpy(&u, &f, sizeof(u)); @@ -1951,10 +1961,17 @@ static_inline u32 f32_to_bits(f32 f) { static_inline u64 f64_bits_inf(bool sign) { #if YYJSON_HAS_IEEE_754 return F64_BITS_INF | ((u64)sign << 63); -#elif defined(INFINITY) - return f64_to_bits(sign ? -INFINITY : INFINITY); #else - return f64_to_bits(sign ? -HUGE_VAL : HUGE_VAL); + return f64_to_bits(sign ? (f64)-INFINITY : (f64)INFINITY); +#endif +} + +/** Returns whether the double value is infinity (not NaN). */ +static_inline bool f64_is_inf(f64 val) { +#if YYJSON_HAS_IEEE_754 + return (f64_to_bits(val) & F64_EXP_MASK) == F64_BITS_INF; +#else + return val >= (f64)INFINITY || val <= (f64)-INFINITY; #endif } @@ -1962,10 +1979,8 @@ static_inline u64 f64_bits_inf(bool sign) { static_inline u64 f64_bits_nan(bool sign) { #if YYJSON_HAS_IEEE_754 return F64_BITS_NAN | ((u64)sign << 63); -#elif defined(NAN) - return f64_to_bits(sign ? (f64)-NAN : (f64)NAN); #else - return f64_to_bits((sign ? -0.0 : 0.0) / 0.0); + return f64_to_bits(sign ? (f64)-NAN : (f64)NAN); #endif } @@ -2086,6 +2101,8 @@ static_inline void u128_mul_add(u64 a, u64 b, u64 c, u64 *hi, u64 *lo) { * These functions are used to read and write JSON files. *============================================================================*/ +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + #define YYJSON_FOPEN_E #if !defined(_MSC_VER) && defined(__GLIBC__) && defined(__GLIBC_PREREQ) # if __GLIBC_PREREQ(2, 7) @@ -2120,6 +2137,8 @@ static_inline usize fread_safe(void *buf, usize size, FILE *file) { #endif } +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + /*============================================================================== @@ -2166,29 +2185,6 @@ static_inline void *mem_align_up(void *mem, usize align) { -/*============================================================================== - * MARK: - Default Memory Allocator (Private) - * This is a simple libc memory allocator wrapper. - *============================================================================*/ - -static void *default_malloc(void *ctx, usize size) { - return malloc(size); -} - -static void *default_realloc(void *ctx, void *ptr, usize old_size, usize size) { - return realloc(ptr, size); -} - -static void default_free(void *ctx, void *ptr) { - free(ptr); -} - -static const yyjson_alc YYJSON_DEFAULT_ALC = { - default_malloc, default_realloc, default_free, NULL -}; - - - /*============================================================================== * MARK: - Null Memory Allocator (Private) * This allocator is just a placeholder to ensure that the internal @@ -2213,6 +2209,44 @@ static const yyjson_alc YYJSON_NULL_ALC = { +/*============================================================================== + * MARK: - Default Memory Allocator (Private) + * This is a simple libc memory allocator wrapper. + *============================================================================*/ + +#if defined(YYJSON_CUSTOM_ALC) + +/* user-provided via macro */ +extern const yyjson_alc YYJSON_CUSTOM_ALC; +#define YYJSON_DEFAULT_ALC YYJSON_CUSTOM_ALC + +#elif YYJSON_FREESTANDING + +/* null allocator */ +static const yyjson_alc YYJSON_DEFAULT_ALC = { + null_malloc, null_realloc, null_free, NULL +}; + +#else /* YYJSON_FREESTANDING */ + +/* default libc allocator */ +static void *default_malloc(void *ctx, usize size) { + return malloc(size); +} +static void *default_realloc(void *ctx, void *ptr, usize old_size, usize size) { + return realloc(ptr, size); +} +static void default_free(void *ctx, void *ptr) { + free(ptr); +} +static const yyjson_alc YYJSON_DEFAULT_ALC = { + default_malloc, default_realloc, default_free, NULL +}; + +#endif /* YYJSON_FREESTANDING */ + + + /*============================================================================== * MARK: - Pool Memory Allocator (Public) * This allocator is initialized with a fixed-size buffer. @@ -2221,14 +2255,14 @@ static const yyjson_alc YYJSON_NULL_ALC = { /** memory chunk header */ typedef struct pool_chunk { - usize size; /* chunk memory size, include chunk header */ + usize size; /* chunk memory size, including chunk header */ struct pool_chunk *next; /* linked list, nullable */ /* char mem[]; flexible array member */ } pool_chunk; /** allocator ctx header */ typedef struct pool_ctx { - usize size; /* total memory size, include ctx header */ + usize size; /* total memory size, including ctx header */ pool_chunk *free_list; /* linked list, nullable */ /* pool_chunk chunks[]; flexible array member */ } pool_ctx; @@ -2380,7 +2414,7 @@ bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, usize size) { /** memory chunk header */ typedef struct dyn_chunk { - usize size; /* chunk size, include header */ + usize size; /* chunk size, including header */ struct dyn_chunk *next; /* char mem[]; flexible array member */ } dyn_chunk; @@ -2494,9 +2528,11 @@ static void dyn_free(void *ctx_ptr, void *ptr) { yyjson_alc *yyjson_alc_dyn_new(void) { const yyjson_alc def = YYJSON_DEFAULT_ALC; usize hdr_len = sizeof(yyjson_alc) + sizeof(dyn_ctx); - yyjson_alc *alc = (yyjson_alc *)def.malloc(def.ctx, hdr_len); - dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + yyjson_alc *alc; + dyn_ctx *ctx; + alc = (yyjson_alc *)def.malloc(def.ctx, hdr_len); if (unlikely(!alc)) return NULL; + ctx = (dyn_ctx *)(void *)(alc + 1); alc->malloc = dyn_malloc; alc->realloc = dyn_realloc; alc->free = dyn_free; @@ -2507,9 +2543,10 @@ yyjson_alc *yyjson_alc_dyn_new(void) { void yyjson_alc_dyn_free(yyjson_alc *alc) { const yyjson_alc def = YYJSON_DEFAULT_ALC; - dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + dyn_ctx *ctx; dyn_chunk *chunk, *next; if (unlikely(!alc)) return; + ctx = (dyn_ctx *)(void *)(alc + 1); for (chunk = ctx->free_list.next; chunk; chunk = next) { next = chunk->next; def.free(def.ctx, chunk); @@ -2642,7 +2679,8 @@ yyjson_mut_doc *yyjson_mut_doc_new(const yyjson_alc *alc) { return doc; } -yyjson_mut_doc *yyjson_doc_mut_copy(yyjson_doc *doc, const yyjson_alc *alc) { +yyjson_mut_doc *yyjson_doc_mut_copy(const yyjson_doc *doc, + const yyjson_alc *alc) { yyjson_mut_doc *m_doc; yyjson_mut_val *m_val; @@ -2658,7 +2696,7 @@ yyjson_mut_doc *yyjson_doc_mut_copy(yyjson_doc *doc, const yyjson_alc *alc) { return m_doc; } -yyjson_mut_doc *yyjson_mut_doc_mut_copy(yyjson_mut_doc *doc, +yyjson_mut_doc *yyjson_mut_doc_mut_copy(const yyjson_mut_doc *doc, const yyjson_alc *alc) { yyjson_mut_doc *m_doc; yyjson_mut_val *m_val; @@ -2678,7 +2716,7 @@ yyjson_mut_doc *yyjson_mut_doc_mut_copy(yyjson_mut_doc *doc, } yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *m_doc, - yyjson_val *i_vals) { + const yyjson_val *i_vals) { /* The immutable object or array stores all sub-values in a contiguous memory, We copy them to another contiguous memory as mutable values, @@ -2693,7 +2731,7 @@ yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *m_doc, i_vals_len = (usize)(unsafe_yyjson_get_next(i_vals) - i_vals); m_vals = unsafe_yyjson_mut_val(m_doc, i_vals_len); if (!m_vals) return NULL; - i_val = i_vals; + i_val = constcast(yyjson_val *)i_vals; m_val = m_vals; for (; i_val < i_end; i_val++, m_val++) { @@ -2743,8 +2781,8 @@ yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *m_doc, return m_vals; } -static yyjson_mut_val *unsafe_yyjson_mut_val_mut_copy(yyjson_mut_doc *m_doc, - yyjson_mut_val *m_vals) { +static yyjson_mut_val *unsafe_yyjson_mut_val_mut_copy( + yyjson_mut_doc *m_doc, const yyjson_mut_val *m_vals) { /* The mutable object or array stores all sub-values in a circular linked list, so we can traverse them in the same loop. The traversal starts from @@ -2790,13 +2828,13 @@ static yyjson_mut_val *unsafe_yyjson_mut_val_mut_copy(yyjson_mut_doc *m_doc, } yyjson_mut_val *yyjson_mut_val_mut_copy(yyjson_mut_doc *doc, - yyjson_mut_val *val) { + const yyjson_mut_val *val) { if (doc && val) return unsafe_yyjson_mut_val_mut_copy(doc, val); return NULL; } /* Count the number of values and the total length of the strings. */ -static void yyjson_mut_stat(yyjson_mut_val *val, +static void yyjson_mut_stat(const yyjson_mut_val *val, usize *val_sum, usize *str_sum) { yyjson_type type = unsafe_yyjson_get_type(val); *val_sum += 1; @@ -2822,7 +2860,7 @@ static void yyjson_mut_stat(yyjson_mut_val *val, /* Copy mutable values to immutable value pool. */ static usize yyjson_imut_copy(yyjson_val **val_ptr, char **buf_ptr, - yyjson_mut_val *mval) { + const yyjson_mut_val *mval) { yyjson_val *val = *val_ptr; yyjson_type type = unsafe_yyjson_get_type(mval); if (type == YYJSON_TYPE_ARR || type == YYJSON_TYPE_OBJ) { @@ -2861,13 +2899,13 @@ static usize yyjson_imut_copy(yyjson_val **val_ptr, char **buf_ptr, } } -yyjson_doc *yyjson_mut_doc_imut_copy(yyjson_mut_doc *mdoc, +yyjson_doc *yyjson_mut_doc_imut_copy(const yyjson_mut_doc *mdoc, const yyjson_alc *alc) { if (!mdoc) return NULL; return yyjson_mut_val_imut_copy(mdoc->root, alc); } -yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *mval, +yyjson_doc *yyjson_mut_val_imut_copy(const yyjson_mut_val *mval, const yyjson_alc *alc) { usize val_num = 0, str_sum = 0, hdr_size, buf_size; yyjson_doc *doc = NULL; @@ -2908,9 +2946,9 @@ yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *mval, return doc; } -static_inline bool unsafe_yyjson_num_equals(void *lhs, void *rhs) { - yyjson_val_uni *luni = &((yyjson_val *)lhs)->uni; - yyjson_val_uni *runi = &((yyjson_val *)rhs)->uni; +static_inline bool unsafe_yyjson_num_equals(const void *lhs, const void *rhs) { + const yyjson_val_uni *luni = &((const yyjson_val *)lhs)->uni; + const yyjson_val_uni *runi = &((const yyjson_val *)rhs)->uni; yyjson_subtype lt = unsafe_yyjson_get_subtype(lhs); yyjson_subtype rt = unsafe_yyjson_get_subtype(rhs); if (lt == rt) return luni->u64 == runi->u64; @@ -2923,14 +2961,14 @@ static_inline bool unsafe_yyjson_num_equals(void *lhs, void *rhs) { return false; } -static_inline bool unsafe_yyjson_str_equals(void *lhs, void *rhs) { +static_inline bool unsafe_yyjson_str_equals(const void *lhs, const void *rhs) { usize len = unsafe_yyjson_get_len(lhs); if (len != unsafe_yyjson_get_len(rhs)) return false; return !memcmp(unsafe_yyjson_get_str(lhs), unsafe_yyjson_get_str(rhs), len); } -bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { +bool unsafe_yyjson_equals(const yyjson_val *lhs, const yyjson_val *rhs) { yyjson_type type = unsafe_yyjson_get_type(lhs); if (type != unsafe_yyjson_get_type(rhs)) return false; @@ -2985,7 +3023,8 @@ bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { } } -bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, yyjson_mut_val *rhs) { +bool unsafe_yyjson_mut_equals(const yyjson_mut_val *lhs, + const yyjson_mut_val *rhs) { yyjson_type type = unsafe_yyjson_get_type(lhs); if (type != unsafe_yyjson_get_type(rhs)) return false; @@ -2995,7 +3034,7 @@ bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, yyjson_mut_val *rhs) { if (len != unsafe_yyjson_get_len(rhs)) return false; if (len > 0) { yyjson_mut_obj_iter iter; - yyjson_mut_obj_iter_init(rhs, &iter); + yyjson_mut_obj_iter_init(constcast(yyjson_mut_val *)rhs, &iter); lhs = (yyjson_mut_val *)lhs->uni.ptr; while (len-- > 0) { rhs = yyjson_mut_obj_iter_getn(&iter, lhs->uni.str, @@ -3700,7 +3739,7 @@ static_noinline void bigint_set_buf(bigint *big, u64 sig, i32 *exp, u64 val = 0; bool dig_big_cut = false; bool has_dot = (hdr < dot_pos) & (dot_pos < sig_end); - u32 dig_len_total = U64_SAFE_DIG + (u32)(sig_end - hdr) - has_dot; + usize dig_len_total = U64_SAFE_DIG + (usize)(sig_end - hdr) - has_dot; sig -= (*sig_cut >= '5'); /* sig was rounded before */ if (dig_len_total > F64_MAX_DEC_DIG) { @@ -3748,8 +3787,8 @@ typedef struct diy_fp { i32 pad; /* padding, useless */ } diy_fp; -/** Get cached rounded diy_fp with pow(10, e) The input value must in range - [POW10_SIG_TABLE_MIN_EXP, POW10_SIG_TABLE_MAX_EXP]. */ +/** Get cached rounded diy_fp for pow(10, e). The input value must be in the + range [POW10_SIG_TABLE_MIN_EXP, POW10_SIG_TABLE_MAX_EXP]. */ static_inline diy_fp diy_fp_get_cached_pow10(i32 exp10) { diy_fp fp; u64 sig_ext; @@ -4142,7 +4181,7 @@ digi_finish: 1. The floating-point number calculation should be accurate, see the comments of macro `YYJSON_DOUBLE_MATH_CORRECT`. 2. Correct rounding should be performed (fegetround() == FE_TONEAREST). - 3. The input of floating point number calculation does not lose precision, + 3. The input to floating-point calculations does not lose precision, which means: 64 - leading_zero(input) - trailing_zero(input) < 53. We don't check all available inputs here, because that would make the code @@ -4161,12 +4200,13 @@ digi_finish: return_f64(dbl); } #endif + if (unlikely(sig == 0)) return_f64_bin(0); /* Fast path 2: To keep it simple, we only accept normal number here, - let the slow path to handle subnormal and infinity number. + let the slow path handle subnormal and infinite numbers. */ if (likely(!sig_cut && exp > -F64_MAX_DEC_EXP + 1 && @@ -4632,7 +4672,7 @@ read_double: return_err(hdr, "strtod() failed to parse the number"); } } - if (unlikely(val->uni.f64 >= HUGE_VAL || val->uni.f64 <= -HUGE_VAL)) { + if (unlikely(f64_is_inf(val->uni.f64))) { return_inf(); } val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; @@ -4742,7 +4782,6 @@ static_inline bool read_str_opt(u8 quo, u8 **ptr, u8 *eof, yyjson_read_flag flg, u8 *hdr = *ptr + 1; u8 **end = ptr; u8 *src = hdr, *dst = NULL, *pos; - u16 hi, lo; u32 uni, tmp; /* Resume incremental parsing. */ @@ -5074,7 +5113,6 @@ static_noinline bool read_str_id(u8 **ptr, u8 *eof, yyjson_read_flag flg, u8 *hdr = *ptr; u8 **end = ptr; u8 *src = hdr, *dst = NULL; - u16 hi, lo; u32 uni, tmp; /* add null-terminator for previous raw string */ @@ -5306,6 +5344,7 @@ fail_literal_null: return_err(cur, LITERAL, MSG_CHAR_N); fail_character: return_err(cur, UNEXPECTED_CHARACTER, MSG_CHAR); fail_comment: return_err(cur, INVALID_COMMENT, MSG_COMMENT); fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); +fail_depth: return_err(cur, DEPTH, MSG_DEPTH); #undef return_err } @@ -5366,6 +5405,10 @@ static_inline yyjson_doc *read_root_minify(u8 *hdr, u8 *cur, u8 *eof, u8 *raw_ptr = raw_end; u8 **pre = &raw_ptr; /* previous raw end pointer */ +#if YYJSON_READER_DEPTH_LIMIT + usize ctn_depth = 0; /* current array/object depth */ +#endif + dat_len = has_flg(STOP_WHEN_DONE) ? 256 : (usize)(eof - cur); hdr_len = sizeof(yyjson_doc) / sizeof(yyjson_val); hdr_len += (sizeof(yyjson_doc) % sizeof(yyjson_val)) > 0; @@ -5391,6 +5434,12 @@ static_inline yyjson_doc *read_root_minify(u8 *hdr, u8 *cur, u8 *eof, } arr_begin: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_READER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif /* save current container */ ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | (ctn->tag & YYJSON_TAG_MASK); @@ -5450,7 +5499,7 @@ arr_val_begin: cur++; if (likely(ctn_len == 0)) goto arr_end; if (has_allow(TRAILING_COMMAS)) goto arr_end; - while (*cur != ',') cur--; + do { cur--; } while (*cur != ','); goto fail_trailing_comma; } if (char_is_space(*cur)) { @@ -5496,6 +5545,9 @@ arr_val_end: goto fail_character_arr_end; arr_end: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth--; +#endif /* get parent container */ ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); @@ -5514,6 +5566,12 @@ arr_end: } obj_begin: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_READER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif /* push container */ ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | (ctn->tag & YYJSON_TAG_MASK); @@ -5535,7 +5593,7 @@ obj_key_begin: cur++; if (likely(ctn_len == 0)) goto obj_end; if (has_allow(TRAILING_COMMAS)) goto obj_end; - while (*cur != ',') cur--; + do { cur--; } while (*cur != ','); goto fail_trailing_comma; } if (char_is_space(*cur)) { @@ -5660,6 +5718,9 @@ obj_val_end: goto fail_character_obj_end; obj_end: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth--; +#endif /* pop container */ ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); /* point to the next value */ @@ -5709,6 +5770,7 @@ fail_character_obj_sep: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_SEP); fail_character_obj_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_END); fail_comment: return_err(cur, INVALID_COMMENT, MSG_COMMENT); fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); +fail_depth: return_err(cur, DEPTH, MSG_DEPTH); #undef val_incr #undef return_err @@ -5769,6 +5831,9 @@ static_inline yyjson_doc *read_root_pretty(u8 *hdr, u8 *cur, u8 *eof, u8 raw_end[1]; /* raw end for null-terminator */ u8 *raw_ptr = raw_end; u8 **pre = &raw_ptr; /* previous raw end pointer */ +#if YYJSON_READER_DEPTH_LIMIT + usize ctn_depth = 0; /* current array/object depth */ +#endif dat_len = has_flg(STOP_WHEN_DONE) ? 256 : (usize)(eof - cur); hdr_len = sizeof(yyjson_doc) / sizeof(yyjson_val); @@ -5797,6 +5862,13 @@ static_inline yyjson_doc *read_root_pretty(u8 *hdr, u8 *cur, u8 *eof, } arr_begin: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_READER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif + /* save current container */ ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | (ctn->tag & YYJSON_TAG_MASK); @@ -5869,7 +5941,7 @@ arr_val_begin: cur++; if (likely(ctn_len == 0)) goto arr_end; if (has_allow(TRAILING_COMMAS)) goto arr_end; - while (*cur != ',') cur--; + do { cur--; } while (*cur != ','); goto fail_trailing_comma; } if (char_is_space(*cur)) { @@ -5919,6 +5991,9 @@ arr_val_end: goto fail_character_arr_end; arr_end: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth--; +#endif /* get parent container */ ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); @@ -5938,6 +6013,13 @@ arr_end: } obj_begin: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_READER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif + /* push container */ ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | (ctn->tag & YYJSON_TAG_MASK); @@ -5971,7 +6053,7 @@ obj_key_begin: cur++; if (likely(ctn_len == 0)) goto obj_end; if (has_allow(TRAILING_COMMAS)) goto obj_end; - while (*cur != ',') cur--; + do { cur--; } while (*cur != ','); goto fail_trailing_comma; } if (char_is_space(*cur)) { @@ -6104,6 +6186,10 @@ obj_val_end: goto fail_character_obj_end; obj_end: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth--; +#endif + /* pop container */ ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); /* point to the next value */ @@ -6154,6 +6240,7 @@ fail_character_obj_sep: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_SEP); fail_character_obj_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_END); fail_comment: return_err(cur, INVALID_COMMENT, MSG_COMMENT); fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); +fail_depth: return_err(cur, DEPTH, MSG_DEPTH); #undef val_incr #undef return_err @@ -6253,6 +6340,8 @@ yyjson_doc *yyjson_read_opts(char *dat, usize len, #undef return_err } +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + yyjson_doc *yyjson_read_file(const char *path, yyjson_read_flag flg, const yyjson_alc *alc_ptr, @@ -6300,6 +6389,7 @@ yyjson_doc *yyjson_read_fp(FILE *file, long file_size = 0, file_pos; void *buf = NULL; usize buf_size = 0; + usize dat_size = 0; /* validate input parameters */ if (!err) err = &tmp_err; @@ -6309,22 +6399,26 @@ yyjson_doc *yyjson_read_fp(FILE *file, file_pos = ftell(file); if (file_pos != -1) { /* get total file size, may fail */ - if (fseek(file, 0, SEEK_END) == 0) file_size = ftell(file); + if (fseek(file, 0, SEEK_END) == 0) { + file_size = ftell(file); + if (file_size == -1) file_size = 0; + } /* reset to original position, may fail */ if (fseek(file, file_pos, SEEK_SET) != 0) file_size = 0; - /* get file size from current postion to end */ + /* get file size from current position to end */ if (file_size > 0) file_size -= file_pos; } /* read file */ if (file_size > 0) { /* read the entire file in one call */ - buf_size = (usize)file_size + YYJSON_PADDING_SIZE; + dat_size = (usize)file_size; + buf_size = dat_size + YYJSON_PADDING_SIZE; buf = alc.malloc(alc.ctx, buf_size); if (buf == NULL) { return_err(MEMORY_ALLOCATION, MSG_MALLOC); } - if (fread_safe(buf, (usize)file_size, file) != (usize)file_size) { + if (fread_safe(buf, dat_size, file) != dat_size) { return_err(FILE_READ, MSG_FREAD); } } else { @@ -6351,8 +6445,11 @@ yyjson_doc *yyjson_read_fp(FILE *file, } tmp = ((u8 *)buf) + buf_size - YYJSON_PADDING_SIZE - chunk_now; read_size = fread_safe(tmp, chunk_now, file); - file_size += (long)read_size; - if (read_size != chunk_now) break; + dat_size += read_size; + if (read_size != chunk_now) { + if (ferror(file)) return_err(FILE_READ, MSG_FREAD); + break; + } chunk_now *= 2; if (chunk_now > chunk_max) chunk_now = chunk_max; @@ -6360,9 +6457,9 @@ yyjson_doc *yyjson_read_fp(FILE *file, } /* read JSON */ - memset((u8 *)buf + file_size, 0, YYJSON_PADDING_SIZE); + memset((u8 *)buf + dat_size, 0, YYJSON_PADDING_SIZE); flg |= YYJSON_READ_INSITU; - doc = yyjson_read_opts((char *)buf, (usize)file_size, flg, &alc, err); + doc = yyjson_read_opts((char *)buf, dat_size, flg, &alc, err); if (doc) { doc->str_pool = (char *)buf; return doc; @@ -6374,6 +6471,8 @@ yyjson_doc *yyjson_read_fp(FILE *file, #undef return_err } +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + const char *yyjson_read_number(const char *dat, yyjson_val *val, yyjson_read_flag flg, @@ -6471,11 +6570,16 @@ struct yyjson_incr_state { usize hdr_len; /* value count used by yyjson_doc */ usize alc_len; /* value count allocated */ usize ctn_len; /* the number of elements in current container */ +#if YYJSON_READER_DEPTH_LIMIT + usize ctn_depth; /* current array/object depth */ +#endif yyjson_val *val_hdr; /* the head of allocated values */ yyjson_val *val_end; /* the end of allocated values */ yyjson_val *val; /* current JSON value */ yyjson_val *ctn; /* current container */ u8 *str_con[2]; /* string parser incremental state */ + u8 *raw_ptr; /* pending position for a deferred raw null-terminator */ + u8 raw_end[1]; /* dummy target for the first deferred null-terminator */ }; yyjson_incr_state *yyjson_incr_new(char *buf, size_t buf_len, @@ -6511,6 +6615,7 @@ yyjson_incr_state *yyjson_incr_new(char *buf, size_t buf_len, } memset(state->hdr + buf_len, 0, YYJSON_PADDING_SIZE); state->cur = state->hdr; + state->raw_ptr = state->raw_end; state->label = LABEL_doc_begin; return state; } @@ -6569,12 +6674,19 @@ yyjson_doc *yyjson_incr_read(yyjson_incr_state *state, size_t len, } while (false) /* save position where it's possible to resume incremental parsing */ +#if YYJSON_READER_DEPTH_LIMIT +#define save_incr_depth() (state->ctn_depth = ctn_depth) +#else +#define save_incr_depth() ((void)0) +#endif #define save_incr_state(_label) do { \ state->label = LABEL_##_label; \ state->cur = cur; \ state->val = val; \ state->ctn_len = ctn_len; \ + save_incr_depth(); \ state->hdr_len = hdr_len; \ + state->raw_ptr = raw_ptr; \ if (unlikely(cur >= end)) goto unexpected_end; \ } while (false) @@ -6606,12 +6718,15 @@ yyjson_doc *yyjson_incr_read(yyjson_incr_state *state, size_t len, const char *msg; /* error message */ yyjson_read_err tmp_err; - u8 raw_end[1]; /* raw end for null-terminator */ - u8 *raw_ptr = raw_end; + u8 *raw_ptr; /* deferred raw null-terminator position, committed at save */ u8 **pre = &raw_ptr; /* previous raw end pointer */ u8 **con = NULL; /* for incremental string parsing */ u8 saved_end = '\0'; /* saved end char */ +#if YYJSON_READER_DEPTH_LIMIT + usize ctn_depth = 0; /* current array/object depth */ +#endif + /* validate input parameters */ if (!err) err = &tmp_err; if (unlikely(!state)) { @@ -6631,6 +6746,9 @@ yyjson_doc *yyjson_incr_read(yyjson_incr_state *state, size_t len, flg = state->flg; alc = state->alc; ctn_len = state->ctn_len; +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth = state->ctn_depth; +#endif hdr_len = state->hdr_len; alc_len = state->alc_len; val = state->val; @@ -6638,6 +6756,7 @@ yyjson_doc *yyjson_incr_read(yyjson_incr_state *state, size_t len, val_end = state->val_end; ctn = state->ctn; con = state->str_con; + raw_ptr = state->raw_ptr; alc_max = USIZE_MAX / sizeof(yyjson_val); /* insert null terminator to make us stop at the specified end, even if @@ -6703,7 +6822,11 @@ doc_begin: goto arr_val_begin; } if (char_is_num(*cur)) { - if (likely(read_num(&cur, pre, flg, val, &msg))) goto doc_end; + if (likely(read_num(&cur, pre, flg, val, &msg))) { + /* a root number may continue with more digits in a later chunk */ + if (unlikely(len < state->buf_len)) check_maybe_truncated_number(); + goto doc_end; + } goto fail_number; } if (*cur == '"') { @@ -6733,6 +6856,13 @@ doc_begin: return_err(cur, UNEXPECTED_CHARACTER, msg); arr_begin: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_READER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif + /* save current container */ ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | (ctn->tag & YYJSON_TAG_MASK); @@ -6791,7 +6921,7 @@ arr_val_continue: if (*cur == ']') { cur++; if (likely(ctn_len == 0)) goto arr_end; - while (*cur != ',') cur--; + do { cur--; } while (*cur != ','); goto fail_trailing_comma; } if (char_is_space(*cur)) { @@ -6822,6 +6952,9 @@ arr_val_end: goto fail_character_arr_end; arr_end: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth--; +#endif /* get parent container */ ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); @@ -6840,6 +6973,13 @@ arr_end: } obj_begin: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_READER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif + /* push container */ ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | (ctn->tag & YYJSON_TAG_MASK); @@ -6863,7 +7003,7 @@ obj_key_continue: if (likely(*cur == '}')) { cur++; if (likely(ctn_len == 0)) goto obj_end; - while (*cur != ',') cur--; + do { cur--; } while (*cur != ','); goto fail_trailing_comma; } if (char_is_space(*cur)) { @@ -6954,6 +7094,10 @@ obj_val_end: goto fail_character_obj_end; obj_end: +#if YYJSON_READER_DEPTH_LIMIT + ctn_depth--; +#endif + /* pop container */ ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); /* point to the next value */ @@ -6970,10 +7114,15 @@ obj_end: doc_end: /* check invalid contents after json document */ - if (unlikely(cur < end) && !has_flg(STOP_WHEN_DONE)) { + if (unlikely(cur < end || len < state->buf_len) && + !has_flg(STOP_WHEN_DONE)) { save_incr_state(doc_end); while (char_is_space(*cur)) cur++; if (unlikely(cur < end)) goto fail_garbage; + /* the document is complete for the bytes seen so far, but more input + is still pending; it may hold trailing content that has to be + rejected, so request the remaining data before finalizing */ + if (unlikely(len < state->buf_len)) goto unexpected_end; } **pre = '\0'; @@ -6990,7 +7139,7 @@ doc_end: unexpected_end: err->pos = len; - /* if no nore data, stop the incr read */ + /* if no more data, stop the incr read */ if (unlikely(len >= state->buf_len)) { err->code = YYJSON_READ_ERROR_UNEXPECTED_END; err->msg = MSG_NOT_END; @@ -7020,11 +7169,13 @@ fail_character_obj_key: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_KEY); fail_character_obj_sep: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_SEP); fail_character_obj_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_END); fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); +fail_depth: return_err(cur, DEPTH, MSG_DEPTH); #undef val_incr #undef return_err #undef return_err_inv_param #undef save_incr_state +#undef save_incr_depth #undef check_maybe_truncated_number } @@ -7230,7 +7381,7 @@ static_inline u8 *write_u64(u64 val, u8 *buf) { #if !YYJSON_DISABLE_FAST_FP_CONV /* FP_WRITER */ /** Trailing zero count table for number 0 to 99. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ static const u8 dec_trailing_zero_table[] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7742,7 +7893,7 @@ static_inline u8 *write_inf_or_nan(u8 *buf, yyjson_write_flag flg, We follow the ECMAScript specification for printing floating-point numbers, similar to `Number.prototype.toString()`, but with the following changes: 1. Keep the negative sign of `-0.0` to preserve input information. - 2. Keep decimal point to indicate the number is floating point. + 2. Keep the decimal point to indicate that the number is floating-point. 3. Remove positive sign in the exponent part. */ static_noinline u8 *write_f32_raw(u8 *buf, u64 raw_f64, @@ -7815,7 +7966,7 @@ static_noinline u8 *write_f32_raw(u8 *buf, u64 raw_f64, num_hdr = buf + pre_ofs; num_end = write_u32_len_7_to_9_trim(sig_dec, num_hdr); - /* seperate these digits to leave a space for dot */ + /* separate these digits to leave a space for dot */ num_sep_pos = no_pre_zero ? dot_ofs : 0; num_sep = num_hdr + num_sep_pos; byte_move_8(num_sep + no_pre_zero, num_sep); @@ -7869,7 +8020,7 @@ static_noinline u8 *write_f32_raw(u8 *buf, u64 raw_f64, We follow the ECMAScript specification for printing floating-point numbers, similar to `Number.prototype.toString()`, but with the following changes: 1. Keep the negative sign of `-0.0` to preserve input information. - 2. Keep decimal point to indicate the number is floating point. + 2. Keep the decimal point to indicate that the number is floating-point. 3. Remove positive sign in the exponent part. */ static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) { @@ -7938,7 +8089,7 @@ static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) { num_hdr = buf + pre_ofs; num_end = write_u64_len_16_to_17_trim(sig_dec, num_hdr); - /* seperate these digits to leave a space for dot */ + /* separate these digits to leave a space for dot */ num_sep_pos = no_pre_zero ? dot_ofs : 0; num_sep = num_hdr + num_sep_pos; byte_move_16(num_sep + no_pre_zero, num_sep); @@ -7993,7 +8144,7 @@ static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) { We follow the ECMAScript specification for printing floating-point numbers, similar to `Number.prototype.toFixed(prec)`, but with the following changes: 1. Keep the negative sign of `-0.0` to preserve input information. - 2. Keep decimal point to indicate the number is floating point. + 2. Keep the decimal point to indicate that the number is floating-point. 3. Remove positive sign in the exponent part. 4. Remove trailing zeros and reduce unnecessary precision. */ @@ -8098,7 +8249,7 @@ static_noinline u8 *write_f64_raw_fixed(u8 *buf, u64 raw, yyjson_write_flag flg, num_hdr = buf + pre_ofs; num_end = write_u64_len_1_to_17(sig_dec, num_hdr); - /* seperate these digits to leave a space for dot */ + /* separate these digits to leave a space for dot */ num_sep_pos = no_pre_zero ? dot_ofs : -dot_ofs; num_sep = buf + num_sep_pos; byte_move_16(num_sep + 1, num_sep); @@ -8353,7 +8504,7 @@ typedef u8 char_enc_type; #define CHAR_ENC_ESC_4 9 /* 4-byte UTF-8, escaped as '\uXXXX\uXXXX'. */ /** Character encode type table: don't escape unicode, don't escape '/'. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ static const char_enc_type enc_table_cpy[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -8374,7 +8525,7 @@ static const char_enc_type enc_table_cpy[256] = { }; /** Character encode type table: don't escape unicode, escape '/'. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ static const char_enc_type enc_table_cpy_slash[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -8395,7 +8546,7 @@ static const char_enc_type enc_table_cpy_slash[256] = { }; /** Character encode type table: escape unicode, don't escape '/'. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ static const char_enc_type enc_table_esc[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -8416,7 +8567,7 @@ static const char_enc_type enc_table_esc[256] = { }; /** Character encode type table: escape unicode, escape '/'. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ static const char_enc_type enc_table_esc_slash[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -8437,7 +8588,7 @@ static const char_enc_type enc_table_esc_slash[256] = { }; /** Escaped hex character table: ["00" "01" "02" ... "FD" "FE" "FF"]. - (generate with misc/make_tables.c) */ + (generated with misc/make_tables.c) */ yyjson_align(2) static const u8 esc_hex_char_table[512] = { '0', '0', '0', '1', '0', '2', '0', '3', @@ -8506,7 +8657,76 @@ static const u8 esc_hex_char_table[512] = { 'F', 'C', 'F', 'D', 'F', 'E', 'F', 'F' }; -/** Escaped single character table. (generate with misc/make_tables.c) */ +/** Lowercase variant of esc_hex_char_table. */ +yyjson_align(2) +static const u8 esc_hex_char_table_lower[512] = { + '0', '0', '0', '1', '0', '2', '0', '3', + '0', '4', '0', '5', '0', '6', '0', '7', + '0', '8', '0', '9', '0', 'a', '0', 'b', + '0', 'c', '0', 'd', '0', 'e', '0', 'f', + '1', '0', '1', '1', '1', '2', '1', '3', + '1', '4', '1', '5', '1', '6', '1', '7', + '1', '8', '1', '9', '1', 'a', '1', 'b', + '1', 'c', '1', 'd', '1', 'e', '1', 'f', + '2', '0', '2', '1', '2', '2', '2', '3', + '2', '4', '2', '5', '2', '6', '2', '7', + '2', '8', '2', '9', '2', 'a', '2', 'b', + '2', 'c', '2', 'd', '2', 'e', '2', 'f', + '3', '0', '3', '1', '3', '2', '3', '3', + '3', '4', '3', '5', '3', '6', '3', '7', + '3', '8', '3', '9', '3', 'a', '3', 'b', + '3', 'c', '3', 'd', '3', 'e', '3', 'f', + '4', '0', '4', '1', '4', '2', '4', '3', + '4', '4', '4', '5', '4', '6', '4', '7', + '4', '8', '4', '9', '4', 'a', '4', 'b', + '4', 'c', '4', 'd', '4', 'e', '4', 'f', + '5', '0', '5', '1', '5', '2', '5', '3', + '5', '4', '5', '5', '5', '6', '5', '7', + '5', '8', '5', '9', '5', 'a', '5', 'b', + '5', 'c', '5', 'd', '5', 'e', '5', 'f', + '6', '0', '6', '1', '6', '2', '6', '3', + '6', '4', '6', '5', '6', '6', '6', '7', + '6', '8', '6', '9', '6', 'a', '6', 'b', + '6', 'c', '6', 'd', '6', 'e', '6', 'f', + '7', '0', '7', '1', '7', '2', '7', '3', + '7', '4', '7', '5', '7', '6', '7', '7', + '7', '8', '7', '9', '7', 'a', '7', 'b', + '7', 'c', '7', 'd', '7', 'e', '7', 'f', + '8', '0', '8', '1', '8', '2', '8', '3', + '8', '4', '8', '5', '8', '6', '8', '7', + '8', '8', '8', '9', '8', 'a', '8', 'b', + '8', 'c', '8', 'd', '8', 'e', '8', 'f', + '9', '0', '9', '1', '9', '2', '9', '3', + '9', '4', '9', '5', '9', '6', '9', '7', + '9', '8', '9', '9', '9', 'a', '9', 'b', + '9', 'c', '9', 'd', '9', 'e', '9', 'f', + 'a', '0', 'a', '1', 'a', '2', 'a', '3', + 'a', '4', 'a', '5', 'a', '6', 'a', '7', + 'a', '8', 'a', '9', 'a', 'a', 'a', 'b', + 'a', 'c', 'a', 'd', 'a', 'e', 'a', 'f', + 'b', '0', 'b', '1', 'b', '2', 'b', '3', + 'b', '4', 'b', '5', 'b', '6', 'b', '7', + 'b', '8', 'b', '9', 'b', 'a', 'b', 'b', + 'b', 'c', 'b', 'd', 'b', 'e', 'b', 'f', + 'c', '0', 'c', '1', 'c', '2', 'c', '3', + 'c', '4', 'c', '5', 'c', '6', 'c', '7', + 'c', '8', 'c', '9', 'c', 'a', 'c', 'b', + 'c', 'c', 'c', 'd', 'c', 'e', 'c', 'f', + 'd', '0', 'd', '1', 'd', '2', 'd', '3', + 'd', '4', 'd', '5', 'd', '6', 'd', '7', + 'd', '8', 'd', '9', 'd', 'a', 'd', 'b', + 'd', 'c', 'd', 'd', 'd', 'e', 'd', 'f', + 'e', '0', 'e', '1', 'e', '2', 'e', '3', + 'e', '4', 'e', '5', 'e', '6', 'e', '7', + 'e', '8', 'e', '9', 'e', 'a', 'e', 'b', + 'e', 'c', 'e', 'd', 'e', 'e', 'e', 'f', + 'f', '0', 'f', '1', 'f', '2', 'f', '3', + 'f', '4', 'f', '5', 'f', '6', 'f', '7', + 'f', '8', 'f', '9', 'f', 'a', 'f', 'b', + 'f', 'c', 'f', 'd', 'f', 'e', 'f', 'f' +}; + +/** Escaped single character table. (generated with misc/make_tables.c) */ yyjson_align(2) static const u8 esc_single_char_table[512] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', @@ -8575,6 +8795,13 @@ static const u8 esc_single_char_table[512] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; +/** Returns the hex digit table to use for \uXXXX escapes. */ +static_inline const u8 *get_hex_table_with_flag(yyjson_write_flag flg) { + return has_flg(LOWERCASE_HEX) + ? esc_hex_char_table_lower + : esc_hex_char_table; +} + /** Returns the encode table with options. */ static_inline const char_enc_type *get_enc_table_with_flag( yyjson_write_flag flg) { @@ -8640,9 +8867,11 @@ static_inline u8 *write_str_noesc(u8 *cur, const u8 *str, usize str_len) { */ static_inline u8 *write_str(u8 *cur, bool esc, bool inv, const u8 *str, usize str_len, - const char_enc_type *enc_table) { - /* The replacement character U+FFFD, used to indicate invalid character. */ - const v32 rep = {{ 'F', 'F', 'F', 'D' }}; + const char_enc_type *enc_table, + const u8 *hex_table) { + /* The replacement character U+FFFD, used to indicate invalid character. + Looked up via hex_table so that LOWERCASE_HEX produces "fffd" while + the default produces "FFFD". */ const v32 pre = {{ '\\', 'u', '0', '0' }}; const u8 *src = str; @@ -8759,7 +8988,7 @@ copy_utf8: } case CHAR_ENC_ESC_1: { byte_copy_4(cur + 0, &pre); - byte_copy_2(cur + 4, &esc_hex_char_table[*src * 2]); + byte_copy_2(cur + 4, &hex_table[*src * 2]); cur += 6; src += 1; goto copy_utf8; @@ -8775,8 +9004,8 @@ copy_utf8: u = (u16)(((u16)(src[0] & 0x1F) << 6) | ((u16)(src[1] & 0x3F) << 0)); byte_copy_2(cur + 0, &pre); - byte_copy_2(cur + 2, &esc_hex_char_table[(u >> 8) * 2]); - byte_copy_2(cur + 4, &esc_hex_char_table[(u & 0xFF) * 2]); + byte_copy_2(cur + 2, &hex_table[(u >> 8) * 2]); + byte_copy_2(cur + 4, &hex_table[(u & 0xFF) * 2]); cur += 6; src += 2; goto copy_utf8; @@ -8792,8 +9021,8 @@ copy_utf8: ((u16)(src[1] & 0x3F) << 6) | ((u16)(src[2] & 0x3F) << 0)); byte_copy_2(cur + 0, &pre); - byte_copy_2(cur + 2, &esc_hex_char_table[(u >> 8) * 2]); - byte_copy_2(cur + 4, &esc_hex_char_table[(u & 0xFF) * 2]); + byte_copy_2(cur + 2, &hex_table[(u >> 8) * 2]); + byte_copy_2(cur + 4, &hex_table[(u & 0xFF) * 2]); cur += 6; src += 3; goto copy_utf8; @@ -8812,11 +9041,11 @@ copy_utf8: hi = (u >> 10) + 0xD800; lo = (u & 0x3FF) + 0xDC00; byte_copy_2(cur + 0, &pre); - byte_copy_2(cur + 2, &esc_hex_char_table[(hi >> 8) * 2]); - byte_copy_2(cur + 4, &esc_hex_char_table[(hi & 0xFF) * 2]); + byte_copy_2(cur + 2, &hex_table[(hi >> 8) * 2]); + byte_copy_2(cur + 4, &hex_table[(hi & 0xFF) * 2]); byte_copy_2(cur + 6, &pre); - byte_copy_2(cur + 8, &esc_hex_char_table[(lo >> 8) * 2]); - byte_copy_2(cur + 10, &esc_hex_char_table[(lo & 0xFF) * 2]); + byte_copy_2(cur + 8, &hex_table[(lo >> 8) * 2]); + byte_copy_2(cur + 10, &hex_table[(lo & 0xFF) * 2]); cur += 12; src += 4; goto copy_utf8; @@ -8843,7 +9072,12 @@ err_cpy: err_esc: if (!inv) return NULL; byte_copy_2(cur + 0, &pre); - byte_copy_4(cur + 2, &rep); + /* U+FFFD = 0xFFFD, written as two pairs from hex_table so that + LOWERCASE_HEX produces "fffd". Replaces a single byte_copy_4 + from a hardcoded uppercase "FFFD" v32; same total output, one + extra load on the (rare) invalid-UTF-8-with-ALLOW path. */ + byte_copy_2(cur + 2, &hex_table[0xFF * 2]); + byte_copy_2(cur + 4, &hex_table[0xFD * 2]); cur += 6; src += 1; goto copy_utf8; @@ -8884,10 +9118,12 @@ static_inline u8 *write_indent(u8 *cur, usize level, usize spaces) { return cur; } +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + /** Write data to file pointer. */ static bool write_dat_to_fp(FILE *fp, u8 *dat, usize len, yyjson_write_err *err) { - if (fwrite(dat, len, 1, fp) != 1) { + if (fwrite(dat, 1, len, fp) != len) { err->msg = "file writing failed"; err->code = YYJSON_WRITE_ERROR_FILE_WRITE; return false; @@ -8909,7 +9145,7 @@ static bool write_dat_to_file(const char *path, u8 *dat, usize len, if (file == NULL) { return_err(FILE_OPEN, MSG_FOPEN); } - if (fwrite(dat, len, 1, file) != 1) { + if (fwrite(dat, 1, len, file) != len) { return_err(FILE_WRITE, MSG_FWRITE); } if (fclose(file) != 0) { @@ -8921,6 +9157,8 @@ static bool write_dat_to_file(const char *path, u8 *dat, usize len, #undef return_err } +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + /*============================================================================== @@ -8944,11 +9182,11 @@ static_inline void yyjson_write_ctx_get(yyjson_write_ctx *ctx, } /** Write single JSON value. */ -static_inline u8 *yyjson_write_single(yyjson_val *val, - yyjson_write_flag flg, - yyjson_alc alc, - usize *dat_len, - yyjson_write_err *err) { +static_inline u8 *write_root_single(yyjson_val *val, + yyjson_write_flag flg, + yyjson_alc alc, + char *buf, usize *dat_len, + yyjson_write_err *err) { #define return_err(_code, _msg) do { \ if (hdr) alc.free(alc.ctx, (void *)hdr); \ *dat_len = 0; \ @@ -8958,7 +9196,8 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, } while (false) #define incr_len(_len) do { \ - hdr = (u8 *)alc.malloc(alc.ctx, _len); \ + if (buf) hdr = *dat_len >= _len ? (u8 *)buf : (u8 *)NULL; \ + else hdr = (u8 *)alc.malloc(alc.ctx, _len); \ if (!hdr) goto fail_alloc; \ cur = hdr; \ } while (false) @@ -8972,6 +9211,7 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, usize str_len; const u8 *str_ptr; const char_enc_type *enc_table = get_enc_table_with_flag(flg); + const u8 *hex_table = get_hex_table_with_flag(flg); bool cpy = (enc_table == enc_table_cpy); bool esc = has_flg(ESCAPE_UNICODE) != 0; bool inv = has_allow(INVALID_UNICODE) != 0; @@ -8995,7 +9235,8 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { cur = write_str_noesc(cur, str_ptr, str_len); } else { - cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + cur = write_str(cur, esc, inv, str_ptr, str_len, + enc_table, hex_table); if (unlikely(!cur)) goto fail_str; } break; @@ -9050,11 +9291,11 @@ fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); /** Write JSON document minify. The root of this document should be a non-empty container. */ -static_inline u8 *yyjson_write_minify(const yyjson_val *root, - const yyjson_write_flag flg, - const yyjson_alc alc, - usize *dat_len, - yyjson_write_err *err) { +static_inline u8 *write_root_minify(const yyjson_val *root, + const yyjson_write_flag flg, + const yyjson_alc alc, + char *buf, usize *dat_len, + yyjson_write_err *err) { #define return_err(_code, _msg) do { \ *dat_len = 0; \ err->code = YYJSON_WRITE_ERROR_##_code; \ @@ -9068,7 +9309,8 @@ static_inline u8 *yyjson_write_minify(const yyjson_val *root, if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ usize ctx_pos = (usize)((u8 *)ctx - hdr); \ usize cur_pos = (usize)(cur - hdr); \ - ctx_len = (usize)(end - (u8 *)ctx); \ + yyjson_assume((u8 *)ctx <= (u8 *)end); \ + ctx_len = (usize)((u8 *)end - (u8 *)ctx); \ alc_inc = yyjson_max(alc_len / 2, ext_len); \ alc_inc = size_align_up(alc_inc, sizeof(yyjson_write_ctx)); \ if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ @@ -9097,18 +9339,29 @@ static_inline u8 *yyjson_write_minify(const yyjson_val *root, u8 *hdr, *cur, *end, *tmp; yyjson_write_ctx *ctx, *ctx_tmp; usize alc_len, alc_inc, ctx_len, ext_len, str_len; +#if YYJSON_WRITER_DEPTH_LIMIT + usize ctn_depth = 0; +#endif const u8 *str_ptr; const char_enc_type *enc_table = get_enc_table_with_flag(flg); + const u8 *hex_table = get_hex_table_with_flag(flg); bool cpy = (enc_table == enc_table_cpy); bool esc = has_flg(ESCAPE_UNICODE) != 0; bool inv = has_allow(INVALID_UNICODE) != 0; bool newline = has_flg(NEWLINE_AT_END) != 0; - alc_len = root->uni.ofs / sizeof(yyjson_val); - alc_len = alc_len * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; - alc_len = size_align_up(alc_len, sizeof(yyjson_write_ctx)); - hdr = (u8 *)alc.malloc(alc.ctx, alc_len); - if (!hdr) goto fail_alloc; + if (buf) { + hdr = (u8 *)buf; + alc_len = *dat_len; + alc_len = size_align_down(alc_len, sizeof(yyjson_write_ctx)); + if (alc_len <= sizeof(yyjson_write_ctx)) goto fail_alloc; + } else { + alc_len = root->uni.ofs / sizeof(yyjson_val); + alc_len = alc_len * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + } cur = hdr; end = hdr + alc_len; ctx = (yyjson_write_ctx *)(void *)end; @@ -9132,7 +9385,8 @@ val_begin: if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { cur = write_str_noesc(cur, str_ptr, str_len); } else { - cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + cur = write_str(cur, esc, inv, str_ptr, str_len, + enc_table, hex_table); if (unlikely(!cur)) goto fail_str; } *cur++ = is_key ? ':' : ','; @@ -9149,8 +9403,17 @@ val_begin: (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) { ctn_len_tmp = unsafe_yyjson_get_len(val); ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); - incr_len(16); + incr_len(2 * sizeof(*ctx)); +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_WRITER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif if (unlikely(ctn_len_tmp == 0)) { +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif /* write empty container */ *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); @@ -9196,6 +9459,9 @@ val_end: goto val_begin; ctn_end: +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif cur--; *cur++ = (u8)(']' | ((u8)ctn_obj << 5)); *cur++ = ','; @@ -9223,6 +9489,9 @@ fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); +#if YYJSON_WRITER_DEPTH_LIMIT +fail_depth: return_err(DEPTH, MSG_DEPTH); +#endif #undef return_err #undef incr_len @@ -9231,11 +9500,11 @@ fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); /** Write JSON document pretty. The root of this document should be a non-empty container. */ -static_inline u8 *yyjson_write_pretty(const yyjson_val *root, - const yyjson_write_flag flg, - const yyjson_alc alc, - usize *dat_len, - yyjson_write_err *err) { +static_inline u8 *write_root_pretty(const yyjson_val *root, + const yyjson_write_flag flg, + const yyjson_alc alc, + char *buf, usize *dat_len, + yyjson_write_err *err) { #define return_err(_code, _msg) do { \ *dat_len = 0; \ err->code = YYJSON_WRITE_ERROR_##_code; \ @@ -9249,7 +9518,8 @@ static_inline u8 *yyjson_write_pretty(const yyjson_val *root, if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ usize ctx_pos = (usize)((u8 *)ctx - hdr); \ usize cur_pos = (usize)(cur - hdr); \ - ctx_len = (usize)(end - (u8 *)ctx); \ + yyjson_assume((u8 *)ctx <= (u8 *)end); \ + ctx_len = (usize)((u8 *)end - (u8 *)ctx); \ alc_inc = yyjson_max(alc_len / 2, ext_len); \ alc_inc = size_align_up(alc_inc, sizeof(yyjson_write_ctx)); \ if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ @@ -9278,19 +9548,30 @@ static_inline u8 *yyjson_write_pretty(const yyjson_val *root, u8 *hdr, *cur, *end, *tmp; yyjson_write_ctx *ctx, *ctx_tmp; usize alc_len, alc_inc, ctx_len, ext_len, str_len, level; +#if YYJSON_WRITER_DEPTH_LIMIT + usize ctn_depth = 0; +#endif const u8 *str_ptr; const char_enc_type *enc_table = get_enc_table_with_flag(flg); + const u8 *hex_table = get_hex_table_with_flag(flg); bool cpy = (enc_table == enc_table_cpy); bool esc = has_flg(ESCAPE_UNICODE) != 0; bool inv = has_allow(INVALID_UNICODE) != 0; usize spaces = has_flg(PRETTY_TWO_SPACES) ? 2 : 4; bool newline = has_flg(NEWLINE_AT_END) != 0; - alc_len = root->uni.ofs / sizeof(yyjson_val); - alc_len = alc_len * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; - alc_len = size_align_up(alc_len, sizeof(yyjson_write_ctx)); - hdr = (u8 *)alc.malloc(alc.ctx, alc_len); - if (!hdr) goto fail_alloc; + if (buf) { + hdr = (u8 *)buf; + alc_len = *dat_len; + alc_len = size_align_down(alc_len, sizeof(yyjson_write_ctx)); + if (alc_len <= sizeof(yyjson_write_ctx)) goto fail_alloc; + } else { + alc_len = root->uni.ofs / sizeof(yyjson_val); + alc_len = alc_len * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + } cur = hdr; end = hdr + alc_len; ctx = (yyjson_write_ctx *)(void *)end; @@ -9313,12 +9594,15 @@ val_begin: str_len = unsafe_yyjson_get_len(val); str_ptr = (const u8 *)unsafe_yyjson_get_str(val); check_str_len(str_len); + if ((sizeof(usize) < 8) && !no_indent && + level > (USIZE_MAX - 16 - str_len * 6) / 4) goto fail_alloc; incr_len(str_len * 6 + 16 + (no_indent ? 0 : level * 4)); cur = write_indent(cur, no_indent ? 0 : level, spaces); if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { cur = write_str_noesc(cur, str_ptr, str_len); } else { - cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + cur = write_str(cur, esc, inv, str_ptr, str_len, + enc_table, hex_table); if (unlikely(!cur)) goto fail_str; } *cur++ = is_key ? ':' : ','; @@ -9340,9 +9624,18 @@ val_begin: no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); ctn_len_tmp = unsafe_yyjson_get_len(val); ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); + incr_len(2 * sizeof(*ctx) + (no_indent ? 0 : level * 4)); +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_WRITER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif if (unlikely(ctn_len_tmp == 0)) { +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif /* write empty container */ - incr_len(16 + (no_indent ? 0 : level * 4)); cur = write_indent(cur, no_indent ? 0 : level, spaces); *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); @@ -9351,7 +9644,6 @@ val_begin: goto val_end; } else { /* push context, setup new container */ - incr_len(32 + (no_indent ? 0 : level * 4)); yyjson_write_ctx_set(--ctx, ctn_len, ctn_obj); ctn_len = ctn_len_tmp << (u8)ctn_obj_tmp; ctn_obj = ctn_obj_tmp; @@ -9400,6 +9692,9 @@ val_end: goto val_begin; ctn_end: +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif cur -= 2; *cur++ = '\n'; incr_len(level * 4); @@ -9430,23 +9725,20 @@ fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); +#if YYJSON_WRITER_DEPTH_LIMIT +fail_depth: return_err(DEPTH, MSG_DEPTH); +#endif #undef return_err #undef incr_len #undef check_str_len } - - -/*============================================================================== - * MARK: - JSON Writer (Public) - *============================================================================*/ - -char *yyjson_val_write_opts(const yyjson_val *val, - yyjson_write_flag flg, - const yyjson_alc *alc_ptr, - usize *dat_len, - yyjson_write_err *err) { +static char *write_root(const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + char *buf, usize *dat_len, + yyjson_write_err *err) { yyjson_write_err tmp_err; usize tmp_dat_len; yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; @@ -9463,23 +9755,39 @@ char *yyjson_val_write_opts(const yyjson_val *val, } if (!unsafe_yyjson_is_ctn(root) || unsafe_yyjson_get_len(root) == 0) { - return (char *)yyjson_write_single(root, flg, alc, dat_len, err); + return (char *)write_root_single(root, flg, alc, buf, dat_len, err); } else if (flg & (YYJSON_WRITE_PRETTY | YYJSON_WRITE_PRETTY_TWO_SPACES)) { - return (char *)yyjson_write_pretty(root, flg, alc, dat_len, err); + return (char *)write_root_pretty(root, flg, alc, buf, dat_len, err); } else { - return (char *)yyjson_write_minify(root, flg, alc, dat_len, err); + return (char *)write_root_minify(root, flg, alc, buf, dat_len, err); } } + + +/*============================================================================== + * MARK: - JSON Writer (Public) + *============================================================================*/ + +char *yyjson_val_write_opts(const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + usize *dat_len, + yyjson_write_err *err) { + return write_root(val, flg, alc_ptr, NULL, dat_len, err); +} + char *yyjson_write_opts(const yyjson_doc *doc, yyjson_write_flag flg, const yyjson_alc *alc_ptr, usize *dat_len, yyjson_write_err *err) { yyjson_val *root = doc ? doc->root : NULL; - return yyjson_val_write_opts(root, flg, alc_ptr, dat_len, err); + return write_root(root, flg, alc_ptr, NULL, dat_len, err); } +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + bool yyjson_val_write_file(const char *path, const yyjson_val *val, yyjson_write_flag flg, @@ -9499,7 +9807,7 @@ bool yyjson_val_write_file(const char *path, return false; } - dat = (u8 *)yyjson_val_write_opts(root, flg, &alc, &dat_len, err); + dat = (u8 *)write_root(root, flg, &alc, NULL, &dat_len, err); if (unlikely(!dat)) return false; suc = write_dat_to_file(path, dat, dat_len, err); alc.free(alc.ctx, dat); @@ -9525,7 +9833,7 @@ bool yyjson_val_write_fp(FILE *fp, return false; } - dat = (u8 *)yyjson_val_write_opts(root, flg, &alc, &dat_len, err); + dat = (u8 *)write_root(root, flg, &alc, NULL, &dat_len, err); if (unlikely(!dat)) return false; suc = write_dat_to_fp(fp, dat, dat_len, err); alc.free(alc.ctx, dat); @@ -9550,6 +9858,30 @@ bool yyjson_write_fp(FILE *fp, return yyjson_val_write_fp(fp, root, flg, alc_ptr, err); } +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + +size_t yyjson_val_write_buf(char *buf, size_t buf_len, + const yyjson_val *val, + yyjson_write_flag flg, + yyjson_write_err *err) { + if (unlikely(!buf || !buf_len)) { + if (err) err->code = YYJSON_WRITE_ERROR_INVALID_PARAMETER; + if (err) err->msg = "input buf or buf_len is invalid"; + return 0; + } else { + write_root(val, flg, &YYJSON_NULL_ALC, buf, &buf_len, err); + return buf_len; + } +} + +size_t yyjson_write_buf(char *buf, size_t buf_len, + const yyjson_doc *doc, + yyjson_write_flag flg, + yyjson_write_err *err) { + yyjson_val *root = doc ? doc->root : NULL; + return yyjson_val_write_buf(buf, buf_len, root, flg, err); +} + /*============================================================================== @@ -9593,22 +9925,22 @@ static_inline usize yyjson_mut_doc_estimated_val_num( } /** Write single JSON value. */ -static_inline u8 *yyjson_mut_write_single(yyjson_mut_val *val, - yyjson_write_flag flg, - yyjson_alc alc, - usize *dat_len, - yyjson_write_err *err) { - return yyjson_write_single((yyjson_val *)val, flg, alc, dat_len, err); +static_inline u8 *mut_write_root_single(yyjson_mut_val *val, + yyjson_write_flag flg, + yyjson_alc alc, + char *buf, usize *dat_len, + yyjson_write_err *err) { + return write_root_single((yyjson_val *)val, flg, alc, buf, dat_len, err); } /** Write JSON document minify. The root of this document should be a non-empty container. */ -static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root, - usize estimated_val_num, - yyjson_write_flag flg, - yyjson_alc alc, - usize *dat_len, - yyjson_write_err *err) { +static_inline u8 *mut_write_root_minify(const yyjson_mut_val *root, + usize estimated_val_num, + yyjson_write_flag flg, + yyjson_alc alc, + char *buf, usize *dat_len, + yyjson_write_err *err) { #define return_err(_code, _msg) do { \ *dat_len = 0; \ err->code = YYJSON_WRITE_ERROR_##_code; \ @@ -9622,7 +9954,8 @@ static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root, if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ usize ctx_pos = (usize)((u8 *)ctx - hdr); \ usize cur_pos = (usize)(cur - hdr); \ - ctx_len = (usize)(end - (u8 *)ctx); \ + yyjson_assume((u8 *)ctx <= (u8 *)end); \ + ctx_len = (usize)((u8 *)end - (u8 *)ctx); \ alc_inc = yyjson_max(alc_len / 2, ext_len); \ alc_inc = size_align_up(alc_inc, sizeof(yyjson_mut_write_ctx)); \ if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ @@ -9651,17 +9984,28 @@ static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root, u8 *hdr, *cur, *end, *tmp; yyjson_mut_write_ctx *ctx, *ctx_tmp; usize alc_len, alc_inc, ctx_len, ext_len, str_len; +#if YYJSON_WRITER_DEPTH_LIMIT + usize ctn_depth = 0; +#endif const u8 *str_ptr; const char_enc_type *enc_table = get_enc_table_with_flag(flg); + const u8 *hex_table = get_hex_table_with_flag(flg); bool cpy = (enc_table == enc_table_cpy); bool esc = has_flg(ESCAPE_UNICODE) != 0; bool inv = has_allow(INVALID_UNICODE) != 0; bool newline = has_flg(NEWLINE_AT_END) != 0; - alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; - alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); - hdr = (u8 *)alc.malloc(alc.ctx, alc_len); - if (!hdr) goto fail_alloc; + if (buf) { + hdr = (u8 *)buf; + alc_len = *dat_len; + alc_len = size_align_down(alc_len, sizeof(yyjson_mut_write_ctx)); + if (alc_len <= sizeof(yyjson_mut_write_ctx)) goto fail_alloc; + } else { + alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + } cur = hdr; end = hdr + alc_len; ctx = (yyjson_mut_write_ctx *)(void *)end; @@ -9687,7 +10031,8 @@ val_begin: if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { cur = write_str_noesc(cur, str_ptr, str_len); } else { - cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + cur = write_str(cur, esc, inv, str_ptr, str_len, + enc_table, hex_table); if (unlikely(!cur)) goto fail_str; } *cur++ = is_key ? ':' : ','; @@ -9704,8 +10049,17 @@ val_begin: (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) { ctn_len_tmp = unsafe_yyjson_get_len(val); ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); - incr_len(16); + incr_len(2 * sizeof(*ctx)); +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_WRITER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif if (unlikely(ctn_len_tmp == 0)) { +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif /* write empty container */ *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); @@ -9753,6 +10107,9 @@ val_end: goto val_begin; ctn_end: +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif cur--; *cur++ = (u8)(']' | ((u8)ctn_obj << 5)); *cur++ = ','; @@ -9782,6 +10139,9 @@ fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); +#if YYJSON_WRITER_DEPTH_LIMIT +fail_depth: return_err(DEPTH, MSG_DEPTH); +#endif #undef return_err #undef incr_len @@ -9790,12 +10150,12 @@ fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); /** Write JSON document pretty. The root of this document should be a non-empty container. */ -static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root, - usize estimated_val_num, - yyjson_write_flag flg, - yyjson_alc alc, - usize *dat_len, - yyjson_write_err *err) { +static_inline u8 *mut_write_root_pretty(const yyjson_mut_val *root, + usize estimated_val_num, + yyjson_write_flag flg, + yyjson_alc alc, + char *buf, usize *dat_len, + yyjson_write_err *err) { #define return_err(_code, _msg) do { \ *dat_len = 0; \ err->code = YYJSON_WRITE_ERROR_##_code; \ @@ -9809,7 +10169,8 @@ static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root, if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ usize ctx_pos = (usize)((u8 *)ctx - hdr); \ usize cur_pos = (usize)(cur - hdr); \ - ctx_len = (usize)(end - (u8 *)ctx); \ + yyjson_assume((u8 *)ctx <= (u8 *)end); \ + ctx_len = (usize)((u8 *)end - (u8 *)ctx); \ alc_inc = yyjson_max(alc_len / 2, ext_len); \ alc_inc = size_align_up(alc_inc, sizeof(yyjson_mut_write_ctx)); \ if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ @@ -9838,18 +10199,29 @@ static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root, u8 *hdr, *cur, *end, *tmp; yyjson_mut_write_ctx *ctx, *ctx_tmp; usize alc_len, alc_inc, ctx_len, ext_len, str_len, level; +#if YYJSON_WRITER_DEPTH_LIMIT + usize ctn_depth = 0; +#endif const u8 *str_ptr; const char_enc_type *enc_table = get_enc_table_with_flag(flg); + const u8 *hex_table = get_hex_table_with_flag(flg); bool cpy = (enc_table == enc_table_cpy); bool esc = has_flg(ESCAPE_UNICODE) != 0; bool inv = has_allow(INVALID_UNICODE) != 0; usize spaces = has_flg(PRETTY_TWO_SPACES) ? 2 : 4; bool newline = has_flg(NEWLINE_AT_END) != 0; - alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; - alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); - hdr = (u8 *)alc.malloc(alc.ctx, alc_len); - if (!hdr) goto fail_alloc; + if (buf) { + hdr = (u8 *)buf; + alc_len = *dat_len; + alc_len = size_align_down(alc_len, sizeof(yyjson_mut_write_ctx)); + if (alc_len <= sizeof(yyjson_mut_write_ctx)) goto fail_alloc; + } else { + alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + } cur = hdr; end = hdr + alc_len; ctx = (yyjson_mut_write_ctx *)(void *)end; @@ -9874,12 +10246,15 @@ val_begin: str_len = unsafe_yyjson_get_len(val); str_ptr = (const u8 *)unsafe_yyjson_get_str(val); check_str_len(str_len); + if ((sizeof(usize) < 8) && !no_indent && + level > (USIZE_MAX - 16 - str_len * 6) / 4) goto fail_alloc; incr_len(str_len * 6 + 16 + (no_indent ? 0 : level * 4)); cur = write_indent(cur, no_indent ? 0 : level, spaces); if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { cur = write_str_noesc(cur, str_ptr, str_len); } else { - cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + cur = write_str(cur, esc, inv, str_ptr, str_len, + enc_table, hex_table); if (unlikely(!cur)) goto fail_str; } *cur++ = is_key ? ':' : ','; @@ -9901,9 +10276,18 @@ val_begin: no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); ctn_len_tmp = unsafe_yyjson_get_len(val); ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); + incr_len(2 * sizeof(*ctx) + (no_indent ? 0 : level * 4)); +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth++; + if (unlikely(ctn_depth >= (usize)YYJSON_WRITER_DEPTH_LIMIT)) { + goto fail_depth; + } +#endif if (unlikely(ctn_len_tmp == 0)) { +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif /* write empty container */ - incr_len(16 + (no_indent ? 0 : level * 4)); cur = write_indent(cur, no_indent ? 0 : level, spaces); *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); @@ -9912,7 +10296,6 @@ val_begin: goto val_end; } else { /* push context, setup new container */ - incr_len(32 + (no_indent ? 0 : level * 4)); yyjson_mut_write_ctx_set(--ctx, ctn, ctn_len, ctn_obj); ctn_len = ctn_len_tmp << (u8)ctn_obj_tmp; ctn_obj = ctn_obj_tmp; @@ -9963,6 +10346,9 @@ val_end: goto val_begin; ctn_end: +#if YYJSON_WRITER_DEPTH_LIMIT + ctn_depth--; +#endif cur -= 2; *cur++ = '\n'; incr_len(level * 4); @@ -9995,18 +10381,21 @@ fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); +#if YYJSON_WRITER_DEPTH_LIMIT +fail_depth: return_err(DEPTH, MSG_DEPTH); +#endif #undef return_err #undef incr_len #undef check_str_len } -static char *yyjson_mut_write_opts_impl(const yyjson_mut_val *val, - usize estimated_val_num, - yyjson_write_flag flg, - const yyjson_alc *alc_ptr, - usize *dat_len, - yyjson_write_err *err) { +static char *mut_write_root(const yyjson_mut_val *val, + usize estimated_val_num, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + char *buf, usize *dat_len, + yyjson_write_err *err) { yyjson_write_err tmp_err; usize tmp_dat_len; yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; @@ -10023,13 +10412,13 @@ static char *yyjson_mut_write_opts_impl(const yyjson_mut_val *val, } if (!unsafe_yyjson_is_ctn(root) || unsafe_yyjson_get_len(root) == 0) { - return (char *)yyjson_mut_write_single(root, flg, alc, dat_len, err); + return (char *)mut_write_root_single(root, flg, alc, buf, dat_len, err); } else if (flg & (YYJSON_WRITE_PRETTY | YYJSON_WRITE_PRETTY_TWO_SPACES)) { - return (char *)yyjson_mut_write_pretty(root, estimated_val_num, - flg, alc, dat_len, err); + return (char *)mut_write_root_pretty(root, estimated_val_num, + flg, alc, buf, dat_len, err); } else { - return (char *)yyjson_mut_write_minify(root, estimated_val_num, - flg, alc, dat_len, err); + return (char *)mut_write_root_minify(root, estimated_val_num, + flg, alc, buf, dat_len, err); } } @@ -10044,7 +10433,7 @@ char *yyjson_mut_val_write_opts(const yyjson_mut_val *val, const yyjson_alc *alc_ptr, usize *dat_len, yyjson_write_err *err) { - return yyjson_mut_write_opts_impl(val, 0, flg, alc_ptr, dat_len, err); + return mut_write_root(val, 0, flg, alc_ptr, NULL, dat_len, err); } char *yyjson_mut_write_opts(const yyjson_mut_doc *doc, @@ -10061,10 +10450,34 @@ char *yyjson_mut_write_opts(const yyjson_mut_doc *doc, root = NULL; estimated_val_num = 0; } - return yyjson_mut_write_opts_impl(root, estimated_val_num, - flg, alc_ptr, dat_len, err); + return mut_write_root(root, estimated_val_num, + flg, alc_ptr, NULL, dat_len, err); } +size_t yyjson_mut_val_write_buf(char *buf, size_t buf_len, + const yyjson_mut_val *val, + yyjson_write_flag flg, + yyjson_write_err *err) { + if (unlikely(!buf || !buf_len)) { + if (err) err->code = YYJSON_WRITE_ERROR_INVALID_PARAMETER; + if (err) err->msg = "input buf or buf_len is invalid"; + return 0; + } else { + mut_write_root(val, 0, flg, &YYJSON_NULL_ALC, buf, &buf_len, err); + return buf_len; + } +} + +size_t yyjson_mut_write_buf(char *buf, size_t buf_len, + const yyjson_mut_doc *doc, + yyjson_write_flag flg, + yyjson_write_err *err) { + yyjson_mut_val *root = doc ? doc->root : NULL; + return yyjson_mut_val_write_buf(buf, buf_len, root, flg, err); +} + +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + bool yyjson_mut_val_write_file(const char *path, const yyjson_mut_val *val, yyjson_write_flag flg, @@ -10135,6 +10548,8 @@ bool yyjson_mut_write_fp(FILE *fp, return yyjson_mut_val_write_fp(fp, root, flg, alc_ptr, err); } +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + #undef has_flg #undef has_allow #endif /* YYJSON_DISABLE_WRITER */ @@ -10222,7 +10637,7 @@ static_inline bool ptr_token_to_idx(const char *cur, usize len, usize *idx) { @param token a JSON pointer token @param len unescaped token length @param esc number of escaped characters in this token - @return true if `str` is equals to `token` + @return true if `str` is equal to `token` */ static_inline bool ptr_token_eq(void *key, const char *token, usize len, usize esc) { @@ -10251,7 +10666,7 @@ static_inline bool ptr_token_eq(void *key, @param esc number of escaped characters in this token @return value at index, or NULL if token is not index or index is out of range */ -static_inline yyjson_val *ptr_arr_get(yyjson_val *arr, const char *token, +static_inline yyjson_val *ptr_arr_get(const yyjson_val *arr, const char *token, usize len, usize esc) { yyjson_val *val = unsafe_yyjson_get_first(arr); usize num = unsafe_yyjson_get_len(arr), idx = 0; @@ -10274,7 +10689,7 @@ static_inline yyjson_val *ptr_arr_get(yyjson_val *arr, const char *token, @param esc [in] number of escaped characters in this token @return value associated with the token, or NULL if no value */ -static_inline yyjson_val *ptr_obj_get(yyjson_val *obj, const char *token, +static_inline yyjson_val *ptr_obj_get(const yyjson_val *obj, const char *token, usize len, usize esc) { yyjson_val *key = unsafe_yyjson_get_first(obj); usize num = unsafe_yyjson_get_len(obj); @@ -10295,7 +10710,7 @@ static_inline yyjson_val *ptr_obj_get(yyjson_val *obj, const char *token, @param last [out] whether index is last @return value at index, or NULL if token is not index or index is out of range */ -static_inline yyjson_mut_val *ptr_mut_arr_get(yyjson_mut_val *arr, +static_inline yyjson_mut_val *ptr_mut_arr_get(const yyjson_mut_val *arr, const char *token, usize len, usize esc, yyjson_mut_val **pre, @@ -10325,7 +10740,7 @@ static_inline yyjson_mut_val *ptr_mut_arr_get(yyjson_mut_val *arr, @param pre [out] previous (sibling) key of the returned value's key @return value associated with the token, or NULL if no value */ -static_inline yyjson_mut_val *ptr_mut_obj_get(yyjson_mut_val *obj, +static_inline yyjson_mut_val *ptr_mut_obj_get(const yyjson_mut_val *obj, const char *token, usize len, usize esc, yyjson_mut_val **pre) { @@ -10388,7 +10803,7 @@ static_inline yyjson_mut_val *ptr_new_key(const char *token, #define return_err_alloc(_ret) \ return_err(_ret, MEMORY_ALLOCATION, 0, "failed to create value") -yyjson_val *unsafe_yyjson_ptr_getx(yyjson_val *val, +yyjson_val *unsafe_yyjson_ptr_getx(const yyjson_val *val, const char *ptr, size_t ptr_len, yyjson_ptr_err *err) { @@ -10408,12 +10823,12 @@ yyjson_val *unsafe_yyjson_ptr_getx(yyjson_val *val, val = NULL; } if (!val) return_err_resolve(NULL, token - hdr); - if (ptr == end) return val; + if (ptr == end) return constcast(yyjson_val *)val; } } yyjson_mut_val *unsafe_yyjson_mut_ptr_getx( - yyjson_mut_val *val, const char *ptr, size_t ptr_len, + const yyjson_mut_val *val, const char *ptr, size_t ptr_len, yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { const char *hdr = ptr, *end = ptr + ptr_len, *token; @@ -10425,7 +10840,7 @@ yyjson_mut_val *unsafe_yyjson_mut_ptr_getx( while (true) { token = ptr_next_token(&ptr, end, &len, &esc); if (unlikely(!token)) return_err_syntax(NULL, ptr - hdr); - ctn = val; + ctn = constcast(yyjson_mut_val *)val; type = unsafe_yyjson_get_type(val); if (type == YYJSON_TYPE_OBJ) { val = ptr_mut_obj_get(val, token, len, esc, &pre); @@ -10442,7 +10857,7 @@ yyjson_mut_val *unsafe_yyjson_mut_ptr_getx( } } if (!val) return_err_resolve(NULL, token - hdr); - if (ptr == end) return val; + if (ptr == end) return constcast(yyjson_mut_val *)val; } } @@ -10496,7 +10911,7 @@ bool unsafe_yyjson_mut_ptr_putx( val = NULL; ctn_type = YYJSON_TYPE_OBJ; token = ptr_next_token(&ptr, end, &token_len, &esc); - if (unlikely(!token)) return_err_resolve(false, token - hdr); + if (unlikely(!token)) return_err_syntax(false, ptr - hdr); } /* container is object, create parent nodes */ @@ -10701,8 +11116,8 @@ static patch_op patch_op_get(yyjson_val *op) { root, _ptr->uni.str, _ptr##_len, _val, NULL, &err->ptr) yyjson_mut_val *yyjson_patch(yyjson_mut_doc *doc, - yyjson_val *orig, - yyjson_val *patch, + const yyjson_val *orig, + const yyjson_val *patch, yyjson_patch_err *err) { yyjson_mut_val *root; @@ -10822,8 +11237,8 @@ yyjson_mut_val *yyjson_patch(yyjson_mut_doc *doc, } yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, - yyjson_mut_val *orig, - yyjson_mut_val *patch, + const yyjson_mut_val *orig, + const yyjson_mut_val *patch, yyjson_patch_err *err) { yyjson_mut_val *root, *obj; yyjson_mut_arr_iter iter; @@ -10842,7 +11257,7 @@ yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, if (unlikely(!root)) return_err_copy(); /* iterate through the patch array */ - yyjson_mut_arr_iter_init(patch, &iter); + yyjson_mut_arr_iter_init(constcast(yyjson_mut_val *)patch, &iter); while ((obj = yyjson_mut_arr_iter_next(&iter))) { patch_op op_enum; yyjson_mut_val *op, *path, *from = NULL, *value; @@ -10959,8 +11374,8 @@ yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, *============================================================================*/ yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, - yyjson_val *orig, - yyjson_val *patch) { + const yyjson_val *orig, + const yyjson_val *patch) { usize idx, max; yyjson_val *key, *orig_val, *patch_val, local_orig; yyjson_mut_val *builder, *mut_key, *mut_val, *merged_val; @@ -10974,9 +11389,9 @@ yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, memset(&local_orig, 0, sizeof(local_orig)); if (!yyjson_is_obj(orig)) { + local_orig.tag = builder->tag; + local_orig.uni = builder->uni; orig = &local_orig; - orig->tag = builder->tag; - orig->uni = builder->uni; } /* If orig is contributing, copy any items not modified by the patch */ @@ -11011,8 +11426,8 @@ yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, } yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, - yyjson_mut_val *orig, - yyjson_mut_val *patch) { + const yyjson_mut_val *orig, + const yyjson_mut_val *patch) { usize idx, max; yyjson_mut_val *key, *orig_val, *patch_val, local_orig; yyjson_mut_val *builder, *mut_key, *mut_val, *merged_val; @@ -11026,9 +11441,9 @@ yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, memset(&local_orig, 0, sizeof(local_orig)); if (!yyjson_mut_is_obj(orig)) { + local_orig.tag = builder->tag; + local_orig.uni = builder->uni; orig = &local_orig; - orig->tag = builder->tag; - orig->uni = builder->uni; } /* If orig is contributing, copy any items not modified by the patch */ diff --git a/src/3rdparty/yyjson/yyjson.h b/src/3rdparty/yyjson/yyjson.h index 5eb6d4680..60561d535 100644 --- a/src/3rdparty/yyjson/yyjson.h +++ b/src/3rdparty/yyjson/yyjson.h @@ -31,99 +31,106 @@ -/*============================================================================== - * MARK: - Header Files - *============================================================================*/ - -#include -#include -#include -#include -#include -#include - - - /*============================================================================== * MARK: - Compile-time Options *============================================================================*/ -/* - Define as 1 to disable JSON reader at compile-time. - This disables functions with "read" in their name. - Reduces binary size by about 60%. - */ +/* Define as 1 to disable JSON reader at compile-time. + This disables functions with "read" in their name. + Reduces binary size by about 60%. */ #ifndef YYJSON_DISABLE_READER +#define YYJSON_DISABLE_READER 0 #endif -/* - Define as 1 to disable JSON writer at compile-time. - This disables functions with "write" in their name. - Reduces binary size by about 30%. - */ +/* Define as 1 to disable JSON writer at compile-time. + This disables functions with "write" in their name. + Reduces binary size by about 30%. */ #ifndef YYJSON_DISABLE_WRITER +#define YYJSON_DISABLE_WRITER 0 #endif -/* - Define as 1 to disable JSON incremental reader at compile-time. - This disables functions with "incr" in their name. - */ +/* Define as 1 to disable JSON incremental reader at compile-time. + This disables functions with "incr" in their name. */ #ifndef YYJSON_DISABLE_INCR_READER +#define YYJSON_DISABLE_INCR_READER 0 #endif -/* - Define as 1 to disable JSON Pointer, JSON Patch and JSON Merge Patch supports. - This disables functions with "ptr" or "patch" in their name. - */ +/* Define as 1 to disable file/fp read and write APIs. */ +#ifndef YYJSON_DISABLE_FILE +#define YYJSON_DISABLE_FILE 0 +#endif + +/* Define as 1 to disable JSON Pointer, JSON Patch and JSON Merge Patch. + This disables functions with "ptr" or "patch" in their name. */ #ifndef YYJSON_DISABLE_UTILS +#define YYJSON_DISABLE_UTILS 0 #endif -/* - Define as 1 to disable the fast floating-point number conversion in yyjson. - Libc's `strtod/snprintf` will be used instead. +/* Define as 1 to disable the fast floating-point number conversion in yyjson. + Libc's `strtod/snprintf` will be used instead. - This reduces binary size by about 30%, but significantly slows down the - floating-point read/write speed. - */ + This reduces binary size by about 30%, but significantly slows down the + floating-point read/write speed. */ #ifndef YYJSON_DISABLE_FAST_FP_CONV +#define YYJSON_DISABLE_FAST_FP_CONV 0 #endif -/* - Define as 1 to disable non-standard JSON features support at compile-time, - such as YYJSON_READ_ALLOW_XXX and YYJSON_WRITE_ALLOW_XXX. +/* Define as 1 to disable non-standard JSON features support at compile-time, + such as YYJSON_READ_ALLOW_XXX and YYJSON_WRITE_ALLOW_XXX. - This reduces binary size by about 10%, and slightly improves performance. - */ + This reduces binary size by about 10%, and slightly improves performance. */ #ifndef YYJSON_DISABLE_NON_STANDARD +#define YYJSON_DISABLE_NON_STANDARD 0 #endif -/* - Define as 1 to disable UTF-8 validation at compile-time. +/* Define as 1 to disable UTF-8 validation at compile-time. - Use this if all input strings are guaranteed to be valid UTF-8 - (e.g. language-level String types are already validated). + Use this if all input strings are guaranteed to be valid UTF-8 + (e.g. language-level String types are already validated). - Disabling UTF-8 validation improves performance for non-ASCII strings by about - 3% to 7%. + Disabling UTF-8 validation improves performance for non-ASCII strings by + about 3% to 7%. - Note: If this flag is enabled while passing illegal UTF-8 strings, - the following errors may occur: - - Escaped characters may be ignored when parsing JSON strings. - - Ending quotes may be ignored when parsing JSON strings, causing the - string to merge with the next value. - - When serializing with `yyjson_mut_val`, the string's end may be accessed - out of bounds, potentially causing a segmentation fault. - */ + Note: If this flag is enabled while passing illegal UTF-8 strings, + the following errors may occur: + - Escaped characters may be ignored when parsing JSON strings. + - Ending quotes may be ignored when parsing JSON strings, causing the + string to merge with the next value. + - When serializing with `yyjson_mut_val`, the string's end may be accessed + out of bounds, potentially causing a segmentation fault. */ #ifndef YYJSON_DISABLE_UTF8_VALIDATION +#define YYJSON_DISABLE_UTF8_VALIDATION 0 #endif -/* - Define as 1 to improve performance on architectures that do not support - unaligned memory access. +/* Define as 1 to improve performance on architectures that do not support + unaligned memory access. - Normally, this does not need to be set manually. See the C file for details. - */ + Normally, this does not need to be set manually. */ #ifndef YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS +/* auto detected in yyjson.c */ +#endif + +/* Define to an integer to set a depth limit for reading nested arrays/objects. + 0 disables the policy limit. */ +#ifndef YYJSON_READER_DEPTH_LIMIT +#define YYJSON_READER_DEPTH_LIMIT 0 +#endif + +/* Define to an integer to set a depth limit for writing nested arrays/objects. + 0 disables the policy limit. */ +#ifndef YYJSON_WRITER_DEPTH_LIMIT +#define YYJSON_WRITER_DEPTH_LIMIT 0 +#endif + +/* Define as 1 to build without libc (stdlib, string, math, stdio). + Inline fallbacks for memcpy/memmove/memset/memcmp/strlen are provided. + Optional `YYJSON_FREESTANDING_HEADER` for custom replacements. + + `malloc`/`free` are unavailable; pass `yyjson_alc` per call or define + `YYJSON_CUSTOM_ALC`. Also disables file/fp APIs. Cannot be used with + `YYJSON_DISABLE_FAST_FP_CONV`. */ +#ifndef YYJSON_FREESTANDING +#define YYJSON_FREESTANDING 0 #endif /* Define as 1 to export symbols when building this library as a Windows DLL. */ @@ -293,6 +300,16 @@ # endif #endif +/** assume for compiler */ +#undef yyjson_assume +#if yyjson_has_builtin(__builtin_unreachable) || yyjson_gcc_available(4, 5, 0) +# define yyjson_assume(expr) ((expr) ? (void)0 : __builtin_unreachable()) +#elif YYJSON_MSC_VER >= 1300 +# define yyjson_assume(expr) __assume(expr) +#else +# define yyjson_assume(expr) ((void)0) +#endif + /** compile-time constant check for compiler */ #ifndef yyjson_constant_p # if yyjson_has_builtin(__builtin_constant_p) || (YYJSON_GCC_VER >= 3) @@ -340,6 +357,82 @@ # define yyjson_api_inline static yyjson_inline #endif +/** Used to cast away (remove) const qualifier. */ +#ifndef yyjson_constcast +# define yyjson_constcast(type) (type)(void *)(size_t)(const void *) +#endif + +/** Microsoft Visual C++ 6.0 doesn't support converting number from u64 to f64: + error C2520: conversion from unsigned __int64 to double not implemented. */ +#ifndef YYJSON_U64_TO_F64_NO_IMPL +# if (0 < YYJSON_MSC_VER) && (YYJSON_MSC_VER <= 1200) +# define YYJSON_U64_TO_F64_NO_IMPL 1 +# else +# define YYJSON_U64_TO_F64_NO_IMPL 0 +# endif +#endif + + + +/*============================================================================== + * MARK: - Header Files + *============================================================================*/ + +#include /* for size_t, NULL */ +#include /* for CHAR_BIT, *_MAX */ +#include /* for floating-point limit macros */ + +/** freestanding */ +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE +#include /* for FILE, fopen, fread, fwrite, sprintf */ +#endif +#if !YYJSON_FREESTANDING +#include /* for malloc, realloc, free, strtod */ +#include /* for memcpy, memmove, memset, memcmp, strlen */ +#include /* for HUGE_VAL, INFINITY, NAN (no libm required) */ +#elif defined(YYJSON_FREESTANDING_HEADER) +# include YYJSON_FREESTANDING_HEADER /* custom replacement for string.h */ +#else +# ifndef memcpy +# define memcpy(d,s,n) yyjson_memcpy(d,s,n) +# endif +# ifndef memmove +# define memmove(d,s,n) yyjson_memmove(d,s,n) +# endif +# ifndef memset +# define memset(d,v,n) yyjson_memset(d,v,n) +# endif +# ifndef memcmp +# define memcmp(a,b,n) yyjson_memcmp(a,b,n) +# endif +# ifndef strlen +# define strlen(s) yyjson_strlen(s) +# endif +yyjson_api_inline void *yyjson_memcpy(void *d, const void *s, size_t n) { + char *p = (char *)d; const char *q = (const char *)s; + while (n--) *p++ = *q++; return d; +} +yyjson_api_inline void *yyjson_memmove(void *d, const void *s, size_t n) { + char *p = (char *)d; const char *q = (const char *)s; + if (p == q || !n) return d; + if (p < q) { while (n--) *p++ = *q++; } + else { p += n; q += n; while (n--) *--p = *--q; } + return d; +} +yyjson_api_inline void *yyjson_memset(void *d, int v, size_t n) { + char *p = (char *)d, x = (char)v; + while (n--) *p++ = x; return d; +} +yyjson_api_inline int yyjson_memcmp(const void *a, const void *b, size_t n) { + const unsigned char *p = (const unsigned char *)a; + const unsigned char *q = (const unsigned char *)b; + while (n--) { if (*p != *q) return (int)(*p - *q); p++; q++; } return 0; +} +yyjson_api_inline size_t yyjson_strlen(const char *s) { + const char *p = s; while (*p) p++; return (size_t)(p - s); +} +#endif + /** stdint (C89 compatible) */ #if (defined(YYJSON_HAS_STDINT_H) && YYJSON_HAS_STDINT_H) || \ YYJSON_MSC_VER >= 1600 || YYJSON_STDC_VER >= 199901L || \ @@ -418,8 +511,8 @@ /** stdbool (C89 compatible) */ #if (defined(YYJSON_HAS_STDBOOL_H) && YYJSON_HAS_STDBOOL_H) || \ - (yyjson_has_include() && !defined(__STRICT_ANSI__)) || \ - YYJSON_MSC_VER >= 1800 || YYJSON_STDC_VER >= 199901L + YYJSON_MSC_VER >= 1800 || YYJSON_STDC_VER >= 199901L || \ + (yyjson_has_include() && !defined(__STRICT_ANSI__)) # include #elif !defined(__bool_true_false_are_defined) # define __bool_true_false_are_defined 1 @@ -446,18 +539,6 @@ # endif #endif -/** - Microsoft Visual C++ 6.0 doesn't support converting number from u64 to f64: - error C2520: conversion from unsigned __int64 to double not implemented. - */ -#ifndef YYJSON_U64_TO_F64_NO_IMPL -# if (0 < YYJSON_MSC_VER) && (YYJSON_MSC_VER <= 1200) -# define YYJSON_U64_TO_F64_NO_IMPL 1 -# else -# define YYJSON_U64_TO_F64_NO_IMPL 0 -# endif -#endif - /*============================================================================== @@ -474,12 +555,14 @@ extern "C" { # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-function" # pragma clang diagnostic ignored "-Wunused-parameter" -#elif defined(__GNUC__) -# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#elif YYJSON_IS_REAL_GCC +# if yyjson_gcc_available(4, 6, 0) # pragma GCC diagnostic push # endif -# pragma GCC diagnostic ignored "-Wunused-function" -# pragma GCC diagnostic ignored "-Wunused-parameter" +# if yyjson_gcc_available(4, 2, 0) +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-parameter" +# endif #elif defined(_MSC_VER) # pragma warning(push) # pragma warning(disable:4800) /* 'int': forcing value to 'true' or 'false' */ @@ -495,16 +578,16 @@ extern "C" { #define YYJSON_VERSION_MAJOR 0 /** The minor version of yyjson. */ -#define YYJSON_VERSION_MINOR 12 +#define YYJSON_VERSION_MINOR 13 /** The patch version of yyjson. */ #define YYJSON_VERSION_PATCH 0 /** The version of yyjson in hex: `(major << 16) | (minor << 8) | (patch)`. */ -#define YYJSON_VERSION_HEX 0x000C00 +#define YYJSON_VERSION_HEX 0x000D00 /** The version string of yyjson. */ -#define YYJSON_VERSION_STRING "0.12.0" +#define YYJSON_VERSION_STRING "0.13.0" /** The version of yyjson in hex, same as `YYJSON_VERSION_HEX`. */ yyjson_api uint32_t yyjson_version(void); @@ -548,7 +631,7 @@ typedef uint8_t yyjson_subtype; #define YYJSON_SUBTYPE_SINT ((uint8_t)(1 << 3)) /* ___01___ */ /** Real number subtype: `double`. */ #define YYJSON_SUBTYPE_REAL ((uint8_t)(2 << 3)) /* ___10___ */ -/** String that do not need to be escaped for writing (internal use). */ +/** String that does not need to be escaped for writing (internal use). */ #define YYJSON_SUBTYPE_NOESC ((uint8_t)(1 << 3)) /* ___01___ */ /** The mask used to extract the type of a JSON value. */ @@ -604,19 +687,19 @@ typedef struct yyjson_alc { calculated. This is not a general-purpose allocator. It is designed to handle a single JSON - data at a time. If it is used for overly complex memory tasks, such as parsing - multiple JSON documents using the same allocator but releasing only a few of - them, it may cause memory fragmentation, resulting in performance degradation - and memory waste. + document at a time. If it is used for overly complex memory tasks, such as + parsing multiple JSON documents using the same allocator but releasing only a + few of them, it may cause memory fragmentation, resulting in performance + degradation and memory waste. @param alc The allocator to be initialized. - If this parameter is NULL, the function will fail and return false. + If `alc` is NULL, returns false. If `buf` or `size` is invalid, this will be set to an empty allocator. @param buf The buffer memory for this allocator. - If this parameter is NULL, the function will fail and return false. + If `buf` is NULL, returns false. @param size The size of `buf`, in bytes. - If this parameter is less than 8 words (32/64 bytes on 32/64-bit OS), the - function will fail and return false. + If `size` is less than 8 words (32/64 bytes on 32/64-bit OS), + returns false. @return true if the `alc` has been successfully initialized. @b Example @@ -626,7 +709,7 @@ typedef struct yyjson_alc { yyjson_alc alc; yyjson_alc_pool_init(&alc, buf, 1024); - const char *json = "{\"name\":\"Helvetica\",\"size\":16}" + const char *json = "{\"name\":\"Helvetica\",\"size\":16}"; yyjson_doc *doc = yyjson_read_opts(json, strlen(json), 0, &alc, NULL); // the memory of `doc` is on the stack @endcode @@ -695,7 +778,7 @@ typedef struct yyjson_doc yyjson_doc; /** An immutable value for reading JSON. A JSON Value has the same lifetime as its document. The memory is held by its - document and and cannot be freed alone. + document and cannot be freed alone. */ typedef struct yyjson_val yyjson_val; @@ -709,7 +792,7 @@ typedef struct yyjson_mut_doc yyjson_mut_doc; /** A mutable value for building JSON. A JSON Value has the same lifetime as its document. The memory is held by its - document and and cannot be freed alone. + document and cannot be freed alone. */ typedef struct yyjson_mut_val yyjson_mut_val; @@ -735,7 +818,7 @@ static const yyjson_read_flag YYJSON_READ_NOFLAG = 0; /** Read the input data in-situ. This option allows the reader to modify and use input data to store string values, which can increase reading speed slightly. - The caller should hold the input data before free the document. + The caller should hold the input data before freeing the document. The input data must be padded by at least `YYJSON_PADDING_SIZE` bytes. For example: `[1,2]` should be `[1,2]\0\0\0\0`, input length should be 5. */ static const yyjson_read_flag YYJSON_READ_INSITU = 1 << 0; @@ -749,7 +832,7 @@ static const yyjson_read_flag YYJSON_READ_STOP_WHEN_DONE = 1 << 1; such as `[1,2,3,]`, `{"a":1,"b":2,}` (non-standard). */ static const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS = 1 << 2; -/** Allow C-style single-line and mult-line comments (non-standard). */ +/** Allow C-style single-line and multi-line comments (non-standard). */ static const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS = 1 << 3; /** Allow inf/nan number and literal, case-insensitive, @@ -875,6 +958,9 @@ static const yyjson_read_code YYJSON_READ_ERROR_FILE_READ = 13; /** Incomplete input during incremental parsing; parsing state is preserved. */ static const yyjson_read_code YYJSON_READ_ERROR_MORE = 14; +/** Read depth limit exceeded. */ +static const yyjson_read_code YYJSON_READ_ERROR_DEPTH = 15; + /** Error information for JSON reader. */ typedef struct yyjson_read_err { /** Error code, see `yyjson_read_code` for all possible values. */ @@ -897,12 +983,12 @@ typedef struct yyjson_read_err { 2. The `alc` is thread-safe or NULL. @param dat The JSON data (UTF-8 without BOM), null-terminator is not required. - If this parameter is NULL, the function will fail and return NULL. + If `dat` is NULL, returns NULL. The `dat` will not be modified without the flag `YYJSON_READ_INSITU`, so you - can pass a `const char *` string and case it to `char *` if you don't use + can pass a `const char *` string and cast it to `char *` if you don't use the `YYJSON_READ_INSITU` flag. @param len The length of JSON data in bytes. - If this parameter is 0, the function will fail and return NULL. + If `len` is 0, returns NULL. @param flg The JSON read options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON reader. @@ -918,6 +1004,8 @@ yyjson_api yyjson_doc *yyjson_read_opts(char *dat, const yyjson_alc *alc, yyjson_read_err *err); +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + /** Read a JSON file. @@ -927,7 +1015,7 @@ yyjson_api yyjson_doc *yyjson_read_opts(char *dat, @param path The JSON file's path. This should be a null-terminated string using the system's native encoding. - If this path is NULL or invalid, the function will fail and return NULL. + If `path` is NULL or invalid, returns NULL. @param flg The JSON read options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON reader. @@ -949,7 +1037,7 @@ yyjson_api yyjson_doc *yyjson_read_file(const char *path, @param fp The file pointer. The data will be read from the current position of the FILE to the end. - If this fp is NULL or invalid, the function will fail and return NULL. + If `fp` is NULL or invalid, returns NULL. @param flg The JSON read options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON reader. @@ -966,15 +1054,17 @@ yyjson_api yyjson_doc *yyjson_read_fp(FILE *fp, const yyjson_alc *alc, yyjson_read_err *err); +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + /** Read a JSON string. This function is thread-safe. @param dat The JSON data (UTF-8 without BOM), null-terminator is not required. - If this parameter is NULL, the function will fail and return NULL. + If `dat` is NULL, returns NULL. @param len The length of JSON data in bytes. - If this parameter is 0, the function will fail and return NULL. + If `len` is 0, returns NULL. @param flg The JSON read options. Multiple options can be combined with `|` operator. 0 means no options. @return A new JSON document, or NULL if an error occurs. @@ -1007,9 +1097,9 @@ typedef struct yyjson_incr_state yyjson_incr_state; Flags for non-standard features (e.g. comments, trailing commas) are ignored. @param buf The JSON data, null-terminator is not required. - If this parameter is NULL, the function will fail and return NULL. + If `buf` is NULL, returns NULL. @param buf_len The length of the JSON data in `buf`. - If use `YYJSON_READ_INSITU`, `buf_len` should not include the padding size. + If using `YYJSON_READ_INSITU`, buf_len should not include the padding size. @param flg The JSON read options. Multiple options can be combined with `|` operator. @param alc The memory allocator used by JSON reader. @@ -1036,7 +1126,7 @@ yyjson_api yyjson_incr_state *yyjson_incr_new(char *buf, size_t buf_len, @param state The state for incremental reading, created using `yyjson_incr_new()`. @param len The number of bytes of JSON data available to parse. - If this parameter is 0, the function will fail and return NULL. + If `len` is 0, returns NULL. @param err A pointer to receive error information. @return A new JSON document, or NULL if an error occurs. When the document is no longer needed, it should be freed with @@ -1051,7 +1141,7 @@ yyjson_api void yyjson_incr_free(yyjson_incr_state *state); #endif /* YYJSON_DISABLE_INCR_READER */ /** - Returns the size of maximum memory usage to read a JSON data. + Returns the maximum memory usage to read a JSON document. You may use this value to avoid malloc() or calloc() call inside the reader to get better performance, or read multiple JSON with one piece of memory. @@ -1075,7 +1165,7 @@ yyjson_api void yyjson_incr_free(yyjson_incr_state *state); yyjson_alc alc; yyjson_alc_pool_init(&alc, buf, size); - // no more alloc() or realloc() call during reading + // no more malloc() or realloc() call during reading doc = yyjson_read_opts(dat1, len1, 0, &alc, NULL); yyjson_doc_free(doc); doc = yyjson_read_opts(dat2, len2, 0, &alc, NULL); @@ -1094,7 +1184,7 @@ yyjson_api_inline size_t yyjson_read_max_memory_usage(size_t len, for example: "[1,2,3,4]" size is 9, value count is 5. 2. Some broken JSON may cost more memory during reading, but fail at end, for example: "[[[[[[[[". - 3. yyjson use 16 bytes per value, see struct yyjson_val. + 3. yyjson uses 16 bytes per value, see struct yyjson_val. 4. yyjson use dynamic memory with a growth factor of 1.5. The max memory size is (json_size / 2 * 16 * 1.5 + padding). @@ -1113,9 +1203,9 @@ yyjson_api_inline size_t yyjson_read_max_memory_usage(size_t len, This function is thread-safe when data is not modified by other threads. @param dat The JSON data (UTF-8 without BOM), null-terminator is required. - If this parameter is NULL, the function will fail and return NULL. + If `dat` is NULL, returns NULL. @param val The output value where result is stored. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns NULL. The value will hold either UINT or SINT or REAL number; @param flg The JSON read options. Multiple options can be combined with `|` operator. 0 means no options. @@ -1192,6 +1282,10 @@ static const yyjson_write_flag YYJSON_WRITE_PRETTY_TWO_SPACES = 1 << 6; This can be helpful for text editors or NDJSON. */ static const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7; +/** Use lowercase hex digits in `\uXXXX` escape sequences instead of the default + uppercase. Only effective when `YYJSON_WRITE_ESCAPE_UNICODE` is also set. */ +static const yyjson_write_flag YYJSON_WRITE_LOWERCASE_HEX = 1 << 8; + /** The highest 8 bits of `yyjson_write_flag` and real number value's `tag` @@ -1225,7 +1319,7 @@ static const yyjson_write_code YYJSON_WRITE_SUCCESS = 0; /** Invalid parameter, such as NULL document. */ static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_PARAMETER = 1; -/** Memory allocation failure occurs. */ +/** Memory allocation failed. */ static const yyjson_write_code YYJSON_WRITE_ERROR_MEMORY_ALLOCATION = 2; /** Invalid value type in JSON document. */ @@ -1243,6 +1337,9 @@ static const yyjson_write_code YYJSON_WRITE_ERROR_FILE_WRITE = 6; /** Invalid unicode in string. */ static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_STRING = 7; +/** Nesting depth limit exceeded. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_DEPTH = 8; + /** Error information for JSON writer. */ typedef struct yyjson_write_err { /** Error code, see `yyjson_write_code` for all possible values. */ @@ -1266,7 +1363,7 @@ typedef struct yyjson_write_err { The `alc` is thread-safe or NULL. @param doc The JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1285,6 +1382,8 @@ yyjson_api char *yyjson_write_opts(const yyjson_doc *doc, size_t *len, yyjson_write_err *err); +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + /** Write a document to JSON file with options. @@ -1294,10 +1393,10 @@ yyjson_api char *yyjson_write_opts(const yyjson_doc *doc, @param path The JSON file's path. This should be a null-terminated string using the system's native encoding. - If this path is NULL or invalid, the function will fail and return false. - If this file is not empty, the content will be discarded. + If `path` is NULL or invalid, returns false. + If the file is not empty, its content is discarded. @param doc The JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1319,9 +1418,9 @@ yyjson_api bool yyjson_write_file(const char *path, @param fp The file pointer. The data will be written to the current position of the file. - If this fp is NULL or invalid, the function will fail and return false. + If `fp` is NULL or invalid, returns false. @param doc The JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1338,13 +1437,39 @@ yyjson_api bool yyjson_write_fp(FILE *fp, const yyjson_alc *alc, yyjson_write_err *err); +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + +/** + Write a document into a buffer. + + This function does not allocate memory, but the buffer must be larger than the + final JSON size to allow temporary space. See `API.md` for details. + + @param buf The output buffer. + If `buf` is NULL, returns 0. + @param buf_len The buffer length. + If `buf_len` is too small, returns 0. + @param doc The JSON document. + If `doc` is NULL or has no root, returns 0. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return The number of bytes written (excluding the null terminator), + or 0 on failure. + */ +yyjson_api size_t yyjson_write_buf(char *buf, size_t buf_len, + const yyjson_doc *doc, + yyjson_write_flag flg, + yyjson_write_err *err); + /** Write a document to JSON string. This function is thread-safe. @param doc The JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param len A pointer to receive output length in bytes (not including the @@ -1369,7 +1494,7 @@ yyjson_api_inline char *yyjson_write(const yyjson_doc *doc, 2. The `alc` is thread-safe or NULL. @param doc The mutable JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1388,6 +1513,8 @@ yyjson_api char *yyjson_mut_write_opts(const yyjson_mut_doc *doc, size_t *len, yyjson_write_err *err); +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + /** Write a document to JSON file with options. @@ -1398,10 +1525,10 @@ yyjson_api char *yyjson_mut_write_opts(const yyjson_mut_doc *doc, @param path The JSON file's path. This should be a null-terminated string using the system's native encoding. - If this path is NULL or invalid, the function will fail and return false. - If this file is not empty, the content will be discarded. + If `path` is NULL or invalid, returns false. + If the file is not empty, its content is discarded. @param doc The mutable JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1423,9 +1550,9 @@ yyjson_api bool yyjson_mut_write_file(const char *path, @param fp The file pointer. The data will be written to the current position of the file. - If this fp is NULL or invalid, the function will fail and return false. + If `fp` is NULL or invalid, returns false. @param doc The mutable JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1442,6 +1569,32 @@ yyjson_api bool yyjson_mut_write_fp(FILE *fp, const yyjson_alc *alc, yyjson_write_err *err); +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + +/** + Write a document into a buffer. + + This function does not allocate memory, but the buffer must be larger than the + final JSON size to allow temporary space. See `API.md` for details. + + @param buf The output buffer. + If `buf` is NULL, returns 0. + @param buf_len The buffer length. + If `buf_len` is too small, returns 0. + @param doc The JSON document. + If `doc` is NULL or has no root, returns 0. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return The number of bytes written (excluding the null terminator), + or 0 on failure. + */ +yyjson_api size_t yyjson_mut_write_buf(char *buf, size_t buf_len, + const yyjson_mut_doc *doc, + yyjson_write_flag flg, + yyjson_write_err *err); + /** Write a document to JSON string. @@ -1449,7 +1602,7 @@ yyjson_api bool yyjson_mut_write_fp(FILE *fp, The `doc` is not modified by other threads. @param doc The JSON document. - If this doc is NULL or has no root, the function will fail and return false. + If `doc` is NULL or has no root, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param len A pointer to receive output length in bytes (not including the @@ -1477,7 +1630,7 @@ yyjson_api_inline char *yyjson_mut_write(const yyjson_mut_doc *doc, The `alc` is thread-safe or NULL. @param val The JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1496,6 +1649,8 @@ yyjson_api char *yyjson_val_write_opts(const yyjson_val *val, size_t *len, yyjson_write_err *err); +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + /** Write a value to JSON file with options. @@ -1505,10 +1660,10 @@ yyjson_api char *yyjson_val_write_opts(const yyjson_val *val, @param path The JSON file's path. This should be a null-terminated string using the system's native encoding. - If this path is NULL or invalid, the function will fail and return false. - If this file is not empty, the content will be discarded. + If `path` is NULL or invalid, returns false. + If the file is not empty, its content is discarded. @param val The JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1530,9 +1685,9 @@ yyjson_api bool yyjson_val_write_file(const char *path, @param fp The file pointer. The data will be written to the current position of the file. - If this path is NULL or invalid, the function will fail and return false. + If `fp` is NULL or invalid, returns false. @param val The JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1549,13 +1704,39 @@ yyjson_api bool yyjson_val_write_fp(FILE *fp, const yyjson_alc *alc, yyjson_write_err *err); +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + +/** + Write a value into a buffer. + + This function does not allocate memory, but the buffer must be larger than the + final JSON size to allow temporary space. See `API.md` for details. + + @param buf The output buffer. + If `buf` is NULL, returns 0. + @param buf_len The buffer length. + If `buf_len` is too small, returns 0. + @param val The JSON root value. + If `val` is NULL, returns 0. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return The number of bytes written (excluding the null terminator), + or 0 on failure. + */ +yyjson_api size_t yyjson_val_write_buf(char *buf, size_t buf_len, + const yyjson_val *val, + yyjson_write_flag flg, + yyjson_write_err *err); + /** Write a value to JSON string. This function is thread-safe. @param val The JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param len A pointer to receive output length in bytes (not including the @@ -1578,7 +1759,7 @@ yyjson_api_inline char *yyjson_val_write(const yyjson_val *val, 2. The `alc` is thread-safe or NULL. @param val The mutable JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1587,7 +1768,7 @@ yyjson_api_inline char *yyjson_val_write(const yyjson_val *val, null-terminator). Pass NULL if you don't need length information. @param err A pointer to receive error information. Pass NULL if you don't need error information. - @return A new JSON string, or NULL if an error occurs. + @return A new JSON string, or NULL if an error occurs. This string is encoded as UTF-8 with a null-terminator. When it's no longer needed, it should be freed with free() or alc->free(). */ @@ -1597,6 +1778,8 @@ yyjson_api char *yyjson_mut_val_write_opts(const yyjson_mut_val *val, size_t *len, yyjson_write_err *err); +#if !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE + /** Write a value to JSON file with options. @@ -1607,10 +1790,10 @@ yyjson_api char *yyjson_mut_val_write_opts(const yyjson_mut_val *val, @param path The JSON file's path. This should be a null-terminated string using the system's native encoding. - If this path is NULL or invalid, the function will fail and return false. - If this file is not empty, the content will be discarded. + If `path` is NULL or invalid, returns false. + If the file is not empty, its content is discarded. @param val The mutable JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1628,13 +1811,13 @@ yyjson_api bool yyjson_mut_val_write_file(const char *path, yyjson_write_err *err); /** - Write a value to JSON file with options. + Write a value to file pointer with options. @param fp The file pointer. The data will be written to the current position of the file. - If this path is NULL or invalid, the function will fail and return false. + If `fp` is NULL or invalid, returns false. @param val The mutable JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns false. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param alc The memory allocator used by JSON writer. @@ -1651,6 +1834,32 @@ yyjson_api bool yyjson_mut_val_write_fp(FILE *fp, const yyjson_alc *alc, yyjson_write_err *err); +#endif /* !YYJSON_FREESTANDING && !YYJSON_DISABLE_FILE */ + +/** + Write a value into a buffer. + + This function does not allocate memory, but the buffer must be larger than the + final JSON size to allow temporary space. See `API.md` for details. + + @param buf The output buffer. + If `buf` is NULL, returns 0. + @param buf_len The buffer length. + If `buf_len` is too small, returns 0. + @param val The JSON root value. + If `val` is NULL, returns 0. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return The number of bytes written (excluding the null terminator), + or 0 on failure. + */ +yyjson_api size_t yyjson_mut_val_write_buf(char *buf, size_t buf_len, + const yyjson_mut_val *val, + yyjson_write_flag flg, + yyjson_write_err *err); + /** Write a value to JSON string. @@ -1658,7 +1867,7 @@ yyjson_api bool yyjson_mut_val_write_fp(FILE *fp, The `val` is not modified by other threads. @param val The JSON root value. - If this parameter is NULL, the function will fail and return NULL. + If `val` is NULL, returns NULL. @param flg The JSON write options. Multiple options can be combined with `|` operator. 0 means no options. @param len A pointer to receive output length in bytes (not including the @@ -1677,9 +1886,9 @@ yyjson_api_inline char *yyjson_mut_val_write(const yyjson_mut_val *val, Write a JSON number. @param val A JSON number value to be converted to a string. - If this parameter is invalid, the function will fail and return NULL. + If `val` is invalid, returns NULL. @param buf A buffer to store the resulting null-terminated string. - If this parameter is NULL, the function will fail and return NULL. + If `buf` is NULL, returns NULL. For integer values, the buffer must be at least 21 bytes. For floating-point values, the buffer must be at least 40 bytes. @return On success, returns a pointer to the character after the last @@ -1711,17 +1920,17 @@ yyjson_api_inline char *yyjson_mut_write_number(const yyjson_mut_val *val, /** Returns the root value of this JSON document. Returns NULL if `doc` is NULL. */ -yyjson_api_inline yyjson_val *yyjson_doc_get_root(yyjson_doc *doc); +yyjson_api_inline yyjson_val *yyjson_doc_get_root(const yyjson_doc *doc); /** Returns read size of input JSON data. Returns 0 if `doc` is NULL. For example: the read size of `[1,2,3]` is 7 bytes. */ -yyjson_api_inline size_t yyjson_doc_get_read_size(yyjson_doc *doc); +yyjson_api_inline size_t yyjson_doc_get_read_size(const yyjson_doc *doc); /** Returns total value count in this JSON document. Returns 0 if `doc` is NULL. For example: the value count of `[1,2,3]` is 4. */ -yyjson_api_inline size_t yyjson_doc_get_val_count(yyjson_doc *doc); +yyjson_api_inline size_t yyjson_doc_get_val_count(const yyjson_doc *doc); /** Release the JSON document and free the memory. After calling this function, the `doc` and all values from the `doc` are no @@ -1736,59 +1945,59 @@ yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc); /** Returns whether the JSON value is raw. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_raw(yyjson_val *val); +yyjson_api_inline bool yyjson_is_raw(const yyjson_val *val); /** Returns whether the JSON value is `null`. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_null(yyjson_val *val); +yyjson_api_inline bool yyjson_is_null(const yyjson_val *val); /** Returns whether the JSON value is `true`. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_true(yyjson_val *val); +yyjson_api_inline bool yyjson_is_true(const yyjson_val *val); /** Returns whether the JSON value is `false`. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_false(yyjson_val *val); +yyjson_api_inline bool yyjson_is_false(const yyjson_val *val); /** Returns whether the JSON value is bool (true/false). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_bool(yyjson_val *val); +yyjson_api_inline bool yyjson_is_bool(const yyjson_val *val); /** Returns whether the JSON value is unsigned integer (uint64_t). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_uint(yyjson_val *val); +yyjson_api_inline bool yyjson_is_uint(const yyjson_val *val); /** Returns whether the JSON value is signed integer (int64_t). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_sint(yyjson_val *val); +yyjson_api_inline bool yyjson_is_sint(const yyjson_val *val); /** Returns whether the JSON value is integer (uint64_t/int64_t). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_int(yyjson_val *val); +yyjson_api_inline bool yyjson_is_int(const yyjson_val *val); /** Returns whether the JSON value is real number (double). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_real(yyjson_val *val); +yyjson_api_inline bool yyjson_is_real(const yyjson_val *val); /** Returns whether the JSON value is number (uint64_t/int64_t/double). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_num(yyjson_val *val); +yyjson_api_inline bool yyjson_is_num(const yyjson_val *val); /** Returns whether the JSON value is string. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_str(yyjson_val *val); +yyjson_api_inline bool yyjson_is_str(const yyjson_val *val); /** Returns whether the JSON value is array. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_arr(yyjson_val *val); +yyjson_api_inline bool yyjson_is_arr(const yyjson_val *val); /** Returns whether the JSON value is object. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_obj(yyjson_val *val); +yyjson_api_inline bool yyjson_is_obj(const yyjson_val *val); /** Returns whether the JSON value is container (array/object). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val); +yyjson_api_inline bool yyjson_is_ctn(const yyjson_val *val); @@ -1798,139 +2007,142 @@ yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val); /** Returns the JSON value's type. Returns YYJSON_TYPE_NONE if `val` is NULL. */ -yyjson_api_inline yyjson_type yyjson_get_type(yyjson_val *val); +yyjson_api_inline yyjson_type yyjson_get_type(const yyjson_val *val); /** Returns the JSON value's subtype. Returns YYJSON_SUBTYPE_NONE if `val` is NULL. */ -yyjson_api_inline yyjson_subtype yyjson_get_subtype(yyjson_val *val); +yyjson_api_inline yyjson_subtype yyjson_get_subtype(const yyjson_val *val); /** Returns the JSON value's tag. Returns 0 if `val` is NULL. */ -yyjson_api_inline uint8_t yyjson_get_tag(yyjson_val *val); +yyjson_api_inline uint8_t yyjson_get_tag(const yyjson_val *val); /** Returns the JSON value's type description. The return value should be one of these strings: "raw", "null", "string", "array", "object", "true", "false", "uint", "sint", "real", "unknown". */ -yyjson_api_inline const char *yyjson_get_type_desc(yyjson_val *val); +yyjson_api_inline const char *yyjson_get_type_desc(const yyjson_val *val); /** Returns the content if the value is raw. Returns NULL if `val` is NULL or type is not raw. */ -yyjson_api_inline const char *yyjson_get_raw(yyjson_val *val); +yyjson_api_inline const char *yyjson_get_raw(const yyjson_val *val); /** Returns the content if the value is bool. Returns false if `val` is NULL or type is not bool. */ -yyjson_api_inline bool yyjson_get_bool(yyjson_val *val); +yyjson_api_inline bool yyjson_get_bool(const yyjson_val *val); -/** Returns the content and cast to uint64_t. +/** Returns the content cast to uint64_t. Returns 0 if `val` is NULL or type is not integer(sint/uint). */ -yyjson_api_inline uint64_t yyjson_get_uint(yyjson_val *val); +yyjson_api_inline uint64_t yyjson_get_uint(const yyjson_val *val); -/** Returns the content and cast to int64_t. +/** Returns the content cast to int64_t. Returns 0 if `val` is NULL or type is not integer(sint/uint). */ -yyjson_api_inline int64_t yyjson_get_sint(yyjson_val *val); +yyjson_api_inline int64_t yyjson_get_sint(const yyjson_val *val); -/** Returns the content and cast to int. +/** Returns the content cast to int (may overflow). Returns 0 if `val` is NULL or type is not integer(sint/uint). */ -yyjson_api_inline int yyjson_get_int(yyjson_val *val); +yyjson_api_inline int yyjson_get_int(const yyjson_val *val); /** Returns the content if the value is real number, or 0.0 on error. Returns 0.0 if `val` is NULL or type is not real(double). */ -yyjson_api_inline double yyjson_get_real(yyjson_val *val); +yyjson_api_inline double yyjson_get_real(const yyjson_val *val); -/** Returns the content and typecast to `double` if the value is number. +/** Returns the content cast to `double` if the value is a number. Returns 0.0 if `val` is NULL or type is not number(uint/sint/real). */ -yyjson_api_inline double yyjson_get_num(yyjson_val *val); +yyjson_api_inline double yyjson_get_num(const yyjson_val *val); /** Returns the content if the value is string. Returns NULL if `val` is NULL or type is not string. */ -yyjson_api_inline const char *yyjson_get_str(yyjson_val *val); +yyjson_api_inline const char *yyjson_get_str(const yyjson_val *val); -/** Returns the content length (string length, array size, object size. - Returns 0 if `val` is NULL or type is not string/array/object. */ -yyjson_api_inline size_t yyjson_get_len(yyjson_val *val); +/** Returns the content length for raw/string/array/object values. + Returns 0 if `val` is NULL. + The return value is unspecified for other types. */ +yyjson_api_inline size_t yyjson_get_len(const yyjson_val *val); -/** Returns whether the JSON value is equals to a string. - Returns false if input is NULL or type is not string. */ -yyjson_api_inline bool yyjson_equals_str(yyjson_val *val, const char *str); +/** Returns whether the JSON value is equal to a string. + Returns false if `val` is NULL or type is not string. */ +yyjson_api_inline bool yyjson_equals_str(const yyjson_val *val, + const char *str); -/** Returns whether the JSON value is equals to a string. +/** Returns whether the JSON value is equal to a string. The `str` should be a UTF-8 string, null-terminator is not required. - Returns false if input is NULL or type is not string. */ -yyjson_api_inline bool yyjson_equals_strn(yyjson_val *val, const char *str, - size_t len); + Returns false if `val` is NULL or type is not string. */ +yyjson_api_inline bool yyjson_equals_strn(const yyjson_val *val, + const char *str, size_t len); /** Returns whether two JSON values are equal (deep compare). - Returns false if input is NULL. + Returns false if `lhs` or `rhs` is NULL. @note the result may be inaccurate if object has duplicate keys. @warning This function is recursive and may cause a stack overflow if the object level is too deep. */ -yyjson_api_inline bool yyjson_equals(yyjson_val *lhs, yyjson_val *rhs); +yyjson_api_inline bool yyjson_equals(const yyjson_val *lhs, + const yyjson_val *rhs); /** Set the value to raw. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_raw(yyjson_val *val, const char *raw, size_t len); /** Set the value to null. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_null(yyjson_val *val); /** Set the value to bool. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_bool(yyjson_val *val, bool num); /** Set the value to uint. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_uint(yyjson_val *val, uint64_t num); /** Set the value to sint. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_sint(yyjson_val *val, int64_t num); /** Set the value to int. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ -yyjson_api_inline bool yyjson_set_int(yyjson_val *val, int num); +yyjson_api_inline bool yyjson_set_int(yyjson_val *val, int64_t num); /** Set the value to float. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_float(yyjson_val *val, float num); /** Set the value to double. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_double(yyjson_val *val, double num); /** Set the value to real. - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_real(yyjson_val *val, double num); /** Set the floating-point number's output format to fixed-point notation. - Returns false if input is NULL or `val` is not real type. + Returns false if `val` is NULL or is not real type. @see YYJSON_WRITE_FP_TO_FIXED flag. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_fp_to_fixed(yyjson_val *val, int prec); /** Set the floating-point number's output format to single-precision. - Returns false if input is NULL or `val` is not real type. + Returns false if `val` is NULL or is not real type. @see YYJSON_WRITE_FP_TO_FLOAT flag. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_fp_to_float(yyjson_val *val, bool flt); /** Set the value to string (null-terminated). - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_str(yyjson_val *val, const char *str); /** Set the value to string (with length). - Returns false if input is NULL or `val` is object or array. + Returns false if `val` is NULL or is object or array. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_strn(yyjson_val *val, const char *str, size_t len); @@ -1938,7 +2150,7 @@ yyjson_api_inline bool yyjson_set_strn(yyjson_val *val, /** Marks this string as not needing to be escaped during JSON writing. This can be used to avoid the overhead of escaping if the string contains only characters that do not require escaping. - Returns false if input is NULL or `val` is not string. + Returns false if `val` is NULL or is not string. @see YYJSON_SUBTYPE_NOESC subtype. @warning This will modify the `immutable` value, use with caution. */ yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc); @@ -1951,23 +2163,23 @@ yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc); /** Returns the number of elements in this array. Returns 0 if `arr` is NULL or type is not array. */ -yyjson_api_inline size_t yyjson_arr_size(yyjson_val *arr); +yyjson_api_inline size_t yyjson_arr_size(const yyjson_val *arr); /** Returns the element at the specified position in this array. Returns NULL if array is NULL/empty or the index is out of bounds. @warning This function takes a linear search time if array is not flat. For example: `[1,{},3]` is flat, `[1,[2],3]` is not flat. */ -yyjson_api_inline yyjson_val *yyjson_arr_get(yyjson_val *arr, size_t idx); +yyjson_api_inline yyjson_val *yyjson_arr_get(const yyjson_val *arr, size_t idx); /** Returns the first element of this array. Returns NULL if `arr` is NULL/empty or type is not array. */ -yyjson_api_inline yyjson_val *yyjson_arr_get_first(yyjson_val *arr); +yyjson_api_inline yyjson_val *yyjson_arr_get_first(const yyjson_val *arr); /** Returns the last element of this array. Returns NULL if `arr` is NULL/empty or type is not array. @warning This function takes a linear search time if array is not flat. For example: `[1,{},3]` is flat, `[1,[2],3]` is not flat.*/ -yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr); +yyjson_api_inline yyjson_val *yyjson_arr_get_last(const yyjson_val *arr); @@ -1997,36 +2209,36 @@ typedef struct yyjson_arr_iter { Initialize an iterator for this array. @param arr The array to be iterated over. - If this parameter is NULL or not an array, `iter` will be set to empty. + If `arr` is NULL or not an array, `iter` is cleared. @param iter The iterator to be initialized. - If this parameter is NULL, the function will fail and return false. + If `iter` is NULL, returns false. @return true if the `iter` has been successfully initialized. @note The iterator does not need to be destroyed. */ -yyjson_api_inline bool yyjson_arr_iter_init(yyjson_val *arr, +yyjson_api_inline bool yyjson_arr_iter_init(const yyjson_val *arr, yyjson_arr_iter *iter); /** - Create an iterator with an array , same as `yyjson_arr_iter_init()`. + Create an iterator with an array, same as `yyjson_arr_iter_init()`. @param arr The array to be iterated over. - If this parameter is NULL or not an array, an empty iterator will returned. + If `arr` is NULL or not an array, returns an empty iterator. @return A new iterator for the array. @note The iterator does not need to be destroyed. */ -yyjson_api_inline yyjson_arr_iter yyjson_arr_iter_with(yyjson_val *arr); +yyjson_api_inline yyjson_arr_iter yyjson_arr_iter_with(const yyjson_val *arr); /** Returns whether the iteration has more elements. - If `iter` is NULL, this function will return false. + If `iter` is NULL, returns false. */ yyjson_api_inline bool yyjson_arr_iter_has_next(yyjson_arr_iter *iter); /** Returns the next element in the iteration, or NULL on end. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter); @@ -2059,7 +2271,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter); /** Returns the number of key-value pairs in this object. Returns 0 if `obj` is NULL or type is not object. */ -yyjson_api_inline size_t yyjson_obj_size(yyjson_val *obj); +yyjson_api_inline size_t yyjson_obj_size(const yyjson_val *obj); /** Returns the value to which the specified key is mapped. Returns NULL if this object contains no mapping for the key. @@ -2068,7 +2280,8 @@ yyjson_api_inline size_t yyjson_obj_size(yyjson_val *obj); The `key` should be a null-terminated UTF-8 string. @warning This function takes a linear search time. */ -yyjson_api_inline yyjson_val *yyjson_obj_get(yyjson_val *obj, const char *key); +yyjson_api_inline yyjson_val *yyjson_obj_get(const yyjson_val *obj, + const char *key); /** Returns the value to which the specified key is mapped. Returns NULL if this object contains no mapping for the key. @@ -2078,8 +2291,8 @@ yyjson_api_inline yyjson_val *yyjson_obj_get(yyjson_val *obj, const char *key); The `key_len` should be the length of the key, in bytes. @warning This function takes a linear search time. */ -yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj, const char *key, - size_t key_len); +yyjson_api_inline yyjson_val *yyjson_obj_getn(const yyjson_val *obj, + const char *key, size_t key_len); @@ -2122,42 +2335,42 @@ typedef struct yyjson_obj_iter { Initialize an iterator for this object. @param obj The object to be iterated over. - If this parameter is NULL or not an object, `iter` will be set to empty. + If `obj` is NULL or not an object, `iter` is cleared. @param iter The iterator to be initialized. - If this parameter is NULL, the function will fail and return false. + If `iter` is NULL, returns false. @return true if the `iter` has been successfully initialized. @note The iterator does not need to be destroyed. */ -yyjson_api_inline bool yyjson_obj_iter_init(yyjson_val *obj, +yyjson_api_inline bool yyjson_obj_iter_init(const yyjson_val *obj, yyjson_obj_iter *iter); /** Create an iterator with an object, same as `yyjson_obj_iter_init()`. @param obj The object to be iterated over. - If this parameter is NULL or not an object, an empty iterator will returned. + If `obj` is NULL or not an object, returns an empty iterator. @return A new iterator for the object. @note The iterator does not need to be destroyed. */ -yyjson_api_inline yyjson_obj_iter yyjson_obj_iter_with(yyjson_val *obj); +yyjson_api_inline yyjson_obj_iter yyjson_obj_iter_with(const yyjson_val *obj); /** Returns whether the iteration has more elements. - If `iter` is NULL, this function will return false. + If `iter` is NULL, returns false. */ yyjson_api_inline bool yyjson_obj_iter_has_next(yyjson_obj_iter *iter); /** Returns the next key in the iteration, or NULL on end. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_val *yyjson_obj_iter_next(yyjson_obj_iter *iter); /** Returns the value for key inside the iteration. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_val *yyjson_obj_iter_get_val(yyjson_val *key); @@ -2173,7 +2386,7 @@ yyjson_api_inline yyjson_val *yyjson_obj_iter_get_val(yyjson_val *key); @param iter The object iterator, should not be NULL. @param key The key, should be a UTF-8 string with null-terminator. @return The value to which the specified key is mapped. - NULL if this object contains no mapping for the key or input is invalid. + NULL if the key is not found or arguments are invalid. @warning This function takes a linear search time if the key is not nearby. */ @@ -2191,9 +2404,9 @@ yyjson_api_inline yyjson_val *yyjson_obj_iter_get(yyjson_obj_iter *iter, @param iter The object iterator, should not be NULL. @param key The key, should be a UTF-8 string, null-terminator is not required. - @param key_len The the length of `key`, in bytes. + @param key_len The length of `key`, in bytes. @return The value to which the specified key is mapped. - NULL if this object contains no mapping for the key or input is invalid. + NULL if the key is not found or arguments are invalid. @warning This function takes a linear search time if the key is not nearby. */ @@ -2284,31 +2497,31 @@ yyjson_api yyjson_mut_doc *yyjson_mut_doc_new(const yyjson_alc *alc); This makes a `deep-copy` on the immutable document. If allocator is NULL, the default allocator will be used. @note `imut_doc` -> `mut_doc`. */ -yyjson_api yyjson_mut_doc *yyjson_doc_mut_copy(yyjson_doc *doc, +yyjson_api yyjson_mut_doc *yyjson_doc_mut_copy(const yyjson_doc *doc, const yyjson_alc *alc); /** Copies and returns a new mutable document from input, returns NULL on error. This makes a `deep-copy` on the mutable document. If allocator is NULL, the default allocator will be used. @note `mut_doc` -> `mut_doc`. */ -yyjson_api yyjson_mut_doc *yyjson_mut_doc_mut_copy(yyjson_mut_doc *doc, +yyjson_api yyjson_mut_doc *yyjson_mut_doc_mut_copy(const yyjson_mut_doc *doc, const yyjson_alc *alc); /** Copies and returns a new mutable value from input, returns NULL on error. This makes a `deep-copy` on the immutable value. - The memory was managed by mutable document. + The memory is managed by the mutable document. @note `imut_val` -> `mut_val`. */ yyjson_api yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *doc, - yyjson_val *val); + const yyjson_val *val); /** Copies and returns a new mutable value from input, returns NULL on error. This makes a `deep-copy` on the mutable value. - The memory was managed by mutable document. + The memory is managed by the mutable document. @note `mut_val` -> `mut_val`. @warning This function is recursive and may cause a stack overflow if the object level is too deep. */ yyjson_api yyjson_mut_val *yyjson_mut_val_mut_copy(yyjson_mut_doc *doc, - yyjson_mut_val *val); + const yyjson_mut_val *val); /** Copies and returns a new immutable document from input, returns NULL on error. This makes a `deep-copy` on the mutable document. @@ -2316,7 +2529,7 @@ yyjson_api yyjson_mut_val *yyjson_mut_val_mut_copy(yyjson_mut_doc *doc, @note `mut_doc` -> `imut_doc`. @warning This function is recursive and may cause a stack overflow if the object level is too deep. */ -yyjson_api yyjson_doc *yyjson_mut_doc_imut_copy(yyjson_mut_doc *doc, +yyjson_api yyjson_doc *yyjson_mut_doc_imut_copy(const yyjson_mut_doc *doc, const yyjson_alc *alc); /** Copies and returns a new immutable document from input, @@ -2325,7 +2538,7 @@ yyjson_api yyjson_doc *yyjson_mut_doc_imut_copy(yyjson_mut_doc *doc, @note `mut_val` -> `imut_doc`. @warning This function is recursive and may cause a stack overflow if the object level is too deep. */ -yyjson_api yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *val, +yyjson_api yyjson_doc *yyjson_mut_val_imut_copy(const yyjson_mut_val *val, const yyjson_alc *alc); @@ -2336,59 +2549,59 @@ yyjson_api yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *val, /** Returns whether the JSON value is raw. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_raw(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_raw(const yyjson_mut_val *val); /** Returns whether the JSON value is `null`. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_null(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_null(const yyjson_mut_val *val); /** Returns whether the JSON value is `true`. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_true(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_true(const yyjson_mut_val *val); /** Returns whether the JSON value is `false`. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_false(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_false(const yyjson_mut_val *val); /** Returns whether the JSON value is bool (true/false). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_bool(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_bool(const yyjson_mut_val *val); /** Returns whether the JSON value is unsigned integer (uint64_t). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_uint(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_uint(const yyjson_mut_val *val); /** Returns whether the JSON value is signed integer (int64_t). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_sint(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_sint(const yyjson_mut_val *val); /** Returns whether the JSON value is integer (uint64_t/int64_t). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_int(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_int(const yyjson_mut_val *val); /** Returns whether the JSON value is real number (double). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_real(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_real(const yyjson_mut_val *val); /** Returns whether the JSON value is number (uint/sint/real). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_num(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_num(const yyjson_mut_val *val); /** Returns whether the JSON value is string. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_str(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_str(const yyjson_mut_val *val); /** Returns whether the JSON value is array. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_arr(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_arr(const yyjson_mut_val *val); /** Returns whether the JSON value is object. Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_obj(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_obj(const yyjson_mut_val *val); /** Returns whether the JSON value is container (array/object). Returns false if `val` is NULL. */ -yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_is_ctn(const yyjson_mut_val *val); @@ -2398,144 +2611,147 @@ yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val); /** Returns the JSON value's type. Returns `YYJSON_TYPE_NONE` if `val` is NULL. */ -yyjson_api_inline yyjson_type yyjson_mut_get_type(yyjson_mut_val *val); +yyjson_api_inline yyjson_type yyjson_mut_get_type(const yyjson_mut_val *val); /** Returns the JSON value's subtype. Returns `YYJSON_SUBTYPE_NONE` if `val` is NULL. */ -yyjson_api_inline yyjson_subtype yyjson_mut_get_subtype(yyjson_mut_val *val); +yyjson_api_inline yyjson_subtype yyjson_mut_get_subtype( + const yyjson_mut_val *val); /** Returns the JSON value's tag. Returns 0 if `val` is NULL. */ -yyjson_api_inline uint8_t yyjson_mut_get_tag(yyjson_mut_val *val); +yyjson_api_inline uint8_t yyjson_mut_get_tag(const yyjson_mut_val *val); /** Returns the JSON value's type description. The return value should be one of these strings: "raw", "null", "string", "array", "object", "true", "false", "uint", "sint", "real", "unknown". */ -yyjson_api_inline const char *yyjson_mut_get_type_desc(yyjson_mut_val *val); +yyjson_api_inline const char *yyjson_mut_get_type_desc( + const yyjson_mut_val *val); /** Returns the content if the value is raw. Returns NULL if `val` is NULL or type is not raw. */ -yyjson_api_inline const char *yyjson_mut_get_raw(yyjson_mut_val *val); +yyjson_api_inline const char *yyjson_mut_get_raw(const yyjson_mut_val *val); /** Returns the content if the value is bool. Returns NULL if `val` is NULL or type is not bool. */ -yyjson_api_inline bool yyjson_mut_get_bool(yyjson_mut_val *val); +yyjson_api_inline bool yyjson_mut_get_bool(const yyjson_mut_val *val); /** Returns the content and cast to uint64_t. Returns 0 if `val` is NULL or type is not integer(sint/uint). */ -yyjson_api_inline uint64_t yyjson_mut_get_uint(yyjson_mut_val *val); +yyjson_api_inline uint64_t yyjson_mut_get_uint(const yyjson_mut_val *val); /** Returns the content and cast to int64_t. Returns 0 if `val` is NULL or type is not integer(sint/uint). */ -yyjson_api_inline int64_t yyjson_mut_get_sint(yyjson_mut_val *val); +yyjson_api_inline int64_t yyjson_mut_get_sint(const yyjson_mut_val *val); /** Returns the content and cast to int. Returns 0 if `val` is NULL or type is not integer(sint/uint). */ -yyjson_api_inline int yyjson_mut_get_int(yyjson_mut_val *val); +yyjson_api_inline int yyjson_mut_get_int(const yyjson_mut_val *val); /** Returns the content if the value is real number. Returns 0.0 if `val` is NULL or type is not real(double). */ -yyjson_api_inline double yyjson_mut_get_real(yyjson_mut_val *val); +yyjson_api_inline double yyjson_mut_get_real(const yyjson_mut_val *val); -/** Returns the content and typecast to `double` if the value is number. +/** Returns the content cast to `double` if the value is a number. Returns 0.0 if `val` is NULL or type is not number(uint/sint/real). */ -yyjson_api_inline double yyjson_mut_get_num(yyjson_mut_val *val); +yyjson_api_inline double yyjson_mut_get_num(const yyjson_mut_val *val); /** Returns the content if the value is string. Returns NULL if `val` is NULL or type is not string. */ -yyjson_api_inline const char *yyjson_mut_get_str(yyjson_mut_val *val); +yyjson_api_inline const char *yyjson_mut_get_str(const yyjson_mut_val *val); -/** Returns the content length (string length, array size, object size. - Returns 0 if `val` is NULL or type is not string/array/object. */ -yyjson_api_inline size_t yyjson_mut_get_len(yyjson_mut_val *val); +/** Returns the content length for raw/string/array/object values. + Returns 0 if `val` is NULL. + The return value is unspecified for other types. */ +yyjson_api_inline size_t yyjson_mut_get_len(const yyjson_mut_val *val); -/** Returns whether the JSON value is equals to a string. +/** Returns whether the JSON value is equal to a string. The `str` should be a null-terminated UTF-8 string. - Returns false if input is NULL or type is not string. */ -yyjson_api_inline bool yyjson_mut_equals_str(yyjson_mut_val *val, + Returns false if `val` is NULL or type is not string. */ +yyjson_api_inline bool yyjson_mut_equals_str(const yyjson_mut_val *val, const char *str); -/** Returns whether the JSON value is equals to a string. +/** Returns whether the JSON value is equal to a string. The `str` should be a UTF-8 string, null-terminator is not required. - Returns false if input is NULL or type is not string. */ -yyjson_api_inline bool yyjson_mut_equals_strn(yyjson_mut_val *val, + Returns false if `val` is NULL or type is not string. */ +yyjson_api_inline bool yyjson_mut_equals_strn(const yyjson_mut_val *val, const char *str, size_t len); /** Returns whether two JSON values are equal (deep compare). - Returns false if input is NULL. + Returns false if `lhs` or `rhs` is NULL. @note the result may be inaccurate if object has duplicate keys. @warning This function is recursive and may cause a stack overflow if the object level is too deep. */ -yyjson_api_inline bool yyjson_mut_equals(yyjson_mut_val *lhs, - yyjson_mut_val *rhs); +yyjson_api_inline bool yyjson_mut_equals(const yyjson_mut_val *lhs, + const yyjson_mut_val *rhs); /** Set the value to raw. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_raw(yyjson_mut_val *val, const char *raw, size_t len); /** Set the value to null. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_null(yyjson_mut_val *val); /** Set the value to bool. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_bool(yyjson_mut_val *val, bool num); /** Set the value to uint. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_uint(yyjson_mut_val *val, uint64_t num); /** Set the value to sint. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_sint(yyjson_mut_val *val, int64_t num); /** Set the value to int. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ -yyjson_api_inline bool yyjson_mut_set_int(yyjson_mut_val *val, int num); +yyjson_api_inline bool yyjson_mut_set_int(yyjson_mut_val *val, int64_t num); /** Set the value to float. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_float(yyjson_mut_val *val, float num); /** Set the value to double. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_double(yyjson_mut_val *val, double num); /** Set the value to real. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_real(yyjson_mut_val *val, double num); /** Set the floating-point number's output format to fixed-point notation. - Returns false if input is NULL or `val` is not real type. + Returns false if `val` is NULL or is not real type. @see YYJSON_WRITE_FP_TO_FIXED flag. - @warning This will modify the `immutable` value, use with caution. */ + @warning This will modify the `mutable` value, use with caution. */ yyjson_api_inline bool yyjson_mut_set_fp_to_fixed(yyjson_mut_val *val, int prec); /** Set the floating-point number's output format to single-precision. - Returns false if input is NULL or `val` is not real type. + Returns false if `val` is NULL or is not real type. @see YYJSON_WRITE_FP_TO_FLOAT flag. - @warning This will modify the `immutable` value, use with caution. */ + @warning This will modify the `mutable` value, use with caution. */ yyjson_api_inline bool yyjson_mut_set_fp_to_float(yyjson_mut_val *val, bool flt); /** Set the value to string (null-terminated). - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_str(yyjson_mut_val *val, const char *str); /** Set the value to string (with length). - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_strn(yyjson_mut_val *val, const char *str, size_t len); @@ -2543,19 +2759,19 @@ yyjson_api_inline bool yyjson_mut_set_strn(yyjson_mut_val *val, /** Marks this string as not needing to be escaped during JSON writing. This can be used to avoid the overhead of escaping if the string contains only characters that do not require escaping. - Returns false if input is NULL or `val` is not string. + Returns false if `val` is NULL or is not string. @see YYJSON_SUBTYPE_NOESC subtype. - @warning This will modify the `immutable` value, use with caution. */ + @warning This will modify the `mutable` value, use with caution. */ yyjson_api_inline bool yyjson_mut_set_str_noesc(yyjson_mut_val *val, bool noesc); /** Set the value to array. - Returns false if input is NULL. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_arr(yyjson_mut_val *val); -/** Set the value to array. - Returns false if input is NULL. +/** Set the value to object. + Returns false if `val` is NULL. @warning This function should not be used on an existing object or array. */ yyjson_api_inline bool yyjson_mut_set_obj(yyjson_mut_val *val); @@ -2668,21 +2884,23 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_strncpy(yyjson_mut_doc *doc, /** Returns the number of elements in this array. Returns 0 if `arr` is NULL or type is not array. */ -yyjson_api_inline size_t yyjson_mut_arr_size(yyjson_mut_val *arr); +yyjson_api_inline size_t yyjson_mut_arr_size(const yyjson_mut_val *arr); /** Returns the element at the specified position in this array. Returns NULL if array is NULL/empty or the index is out of bounds. @warning This function takes a linear search time. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(yyjson_mut_val *arr, +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(const yyjson_mut_val *arr, size_t idx); /** Returns the first element of this array. Returns NULL if `arr` is NULL/empty or type is not array. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_first(yyjson_mut_val *arr); +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_first( + const yyjson_mut_val *arr); /** Returns the last element of this array. Returns NULL if `arr` is NULL/empty or type is not array. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last(yyjson_mut_val *arr); +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last( + const yyjson_mut_val *arr); @@ -2720,9 +2938,9 @@ typedef struct yyjson_mut_arr_iter { Initialize an iterator for this array. @param arr The array to be iterated over. - If this parameter is NULL or not an array, `iter` will be set to empty. + If `arr` is NULL or not an array, `iter` is cleared. @param iter The iterator to be initialized. - If this parameter is NULL, the function will fail and return false. + If `iter` is NULL, returns false. @return true if the `iter` has been successfully initialized. @note The iterator does not need to be destroyed. @@ -2731,10 +2949,10 @@ yyjson_api_inline bool yyjson_mut_arr_iter_init(yyjson_mut_val *arr, yyjson_mut_arr_iter *iter); /** - Create an iterator with an array , same as `yyjson_mut_arr_iter_init()`. + Create an iterator with an array, same as `yyjson_mut_arr_iter_init()`. @param arr The array to be iterated over. - If this parameter is NULL or not an array, an empty iterator will returned. + If `arr` is NULL or not an array, returns an empty iterator. @return A new iterator for the array. @note The iterator does not need to be destroyed. @@ -2744,21 +2962,21 @@ yyjson_api_inline yyjson_mut_arr_iter yyjson_mut_arr_iter_with( /** Returns whether the iteration has more elements. - If `iter` is NULL, this function will return false. + If `iter` is NULL, returns false. */ yyjson_api_inline bool yyjson_mut_arr_iter_has_next( yyjson_mut_arr_iter *iter); /** Returns the next element in the iteration, or NULL on end. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_next( yyjson_mut_arr_iter *iter); /** Removes and returns current element in the iteration. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove( yyjson_mut_arr_iter *iter); @@ -2795,7 +3013,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove( /** Creates and returns an empty mutable array. @param doc A mutable document, used for memory allocation only. - @return The new array. NULL if input is NULL or memory allocation failed. + @return The new array. NULL if `doc` is NULL or allocation fails. */ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr(yyjson_mut_doc *doc); @@ -2803,10 +3021,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr(yyjson_mut_doc *doc); Creates and returns a new mutable array with the given boolean values. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of boolean values. - @param count The value count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The value count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2821,10 +3039,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_bool( Creates and returns a new mutable array with the given sint numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of sint numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2839,10 +3057,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint( Creates and returns a new mutable array with the given uint numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of uint numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2857,10 +3075,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint( Creates and returns a new mutable array with the given real numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of real numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2875,10 +3093,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_real( Creates and returns a new mutable array with the given int8 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of int8 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2893,10 +3111,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint8( Creates and returns a new mutable array with the given int16 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of int16 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2911,10 +3129,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint16( Creates and returns a new mutable array with the given int32 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of int32 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2929,10 +3147,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint32( Creates and returns a new mutable array with the given int64 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of int64 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2947,10 +3165,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint64( Creates and returns a new mutable array with the given uint8 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of uint8 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2965,10 +3183,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint8( Creates and returns a new mutable array with the given uint16 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of uint16 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -2983,10 +3201,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint16( Creates and returns a new mutable array with the given uint32 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of uint32 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -3001,10 +3219,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint32( Creates and returns a new mutable array with the given uint64 numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of uint64 numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -3019,10 +3237,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint64( Creates and returns a new mutable array with the given float numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of float numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -3037,10 +3255,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_float( Creates and returns a new mutable array with the given double numbers. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of double numbers. - @param count The number count. If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + @param count The number count. If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -3056,12 +3274,12 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_double( will not be copied. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of UTF-8 null-terminator strings. - If this array contains NULL, the function will fail and return NULL. + If `vals` contains NULL, returns NULL. @param count The number of values in `vals`. - If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @warning The input strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. If these strings will be @@ -3081,13 +3299,13 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_str( lengths, these strings will not be copied. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of UTF-8 strings, null-terminator is not required. - If this array contains NULL, the function will fail and return NULL. + If `vals` contains NULL, returns NULL. @param lens A C array of string lengths, in bytes. @param count The number of strings in `vals`. - If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @warning The input strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. If these strings will be @@ -3108,12 +3326,12 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strn( will be copied. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of UTF-8 null-terminator strings. - If this array contains NULL, the function will fail and return NULL. + If `vals` contains NULL, returns NULL. @param count The number of values in `vals`. - If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -3129,13 +3347,13 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strcpy( lengths, these strings will be copied. @param doc A mutable document, used for memory allocation only. - If this parameter is NULL, the function will fail and return NULL. + If `doc` is NULL, returns NULL. @param vals A C array of UTF-8 strings, null-terminator is not required. - If this array contains NULL, the function will fail and return NULL. + If `vals` contains NULL, returns NULL. @param lens A C array of string lengths, in bytes. @param count The number of strings in `vals`. - If this value is 0, an empty array will return. - @return The new array. NULL if input is invalid or memory allocation failed. + If this value is 0, an empty array is returned. + @return The new array. NULL if arguments are invalid or allocation fails. @b Example @code @@ -3160,7 +3378,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strncpy( @param val The value to be inserted. Returns false if it is NULL. @param idx The index to which to insert the new value. Returns false if the index is out of range. - @return Whether successful. + @return Whether the operation was successful. @warning This function takes a linear search time. */ yyjson_api_inline bool yyjson_mut_arr_insert(yyjson_mut_val *arr, @@ -3171,7 +3389,7 @@ yyjson_api_inline bool yyjson_mut_arr_insert(yyjson_mut_val *arr, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param val The value to be inserted. Returns false if it is NULL. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_append(yyjson_mut_val *arr, yyjson_mut_val *val); @@ -3236,7 +3454,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove_last( Returns false if it is NULL or not an array. @param idx The start index of the range (0 is the first). @param len The number of items in the range (can be 0). - @return Whether successful. + @return Whether the operation was successful. @warning This function takes a linear search time. */ yyjson_api_inline bool yyjson_mut_arr_remove_range(yyjson_mut_val *arr, @@ -3246,7 +3464,7 @@ yyjson_api_inline bool yyjson_mut_arr_remove_range(yyjson_mut_val *arr, Removes all values in this array. @param arr The array from which all of the values are to be removed. Returns false if it is NULL or not an array. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_clear(yyjson_mut_val *arr); @@ -3271,7 +3489,7 @@ yyjson_api_inline bool yyjson_mut_arr_rotate(yyjson_mut_val *arr, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param val The value to be inserted. Returns false if it is NULL. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_val(yyjson_mut_val *arr, yyjson_mut_val *val); @@ -3281,7 +3499,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_val(yyjson_mut_val *arr, @param doc The `doc` is only used for memory allocation. @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_null(yyjson_mut_doc *doc, yyjson_mut_val *arr); @@ -3291,7 +3509,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_null(yyjson_mut_doc *doc, @param doc The `doc` is only used for memory allocation. @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_true(yyjson_mut_doc *doc, yyjson_mut_val *arr); @@ -3301,7 +3519,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_true(yyjson_mut_doc *doc, @param doc The `doc` is only used for memory allocation. @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_false(yyjson_mut_doc *doc, yyjson_mut_val *arr); @@ -3312,7 +3530,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_false(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param val The bool value to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_bool(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3324,7 +3542,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_bool(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param num The number to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_uint(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3336,7 +3554,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_uint(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param num The number to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_sint(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3348,7 +3566,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_sint(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param num The number to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_int(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3360,7 +3578,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_int(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param num The number to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_float(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3372,7 +3590,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_float(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param num The number to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_double(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3384,7 +3602,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_double(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param num The number to be added. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_real(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3396,7 +3614,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_real(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param str A null-terminated UTF-8 string. - @return Whether successful. + @return Whether the operation was successful. @warning The input string is not copied, you should keep this string unmodified for the lifetime of this JSON document. */ @@ -3411,7 +3629,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_str(yyjson_mut_doc *doc, Returns false if it is NULL or not an array. @param str A UTF-8 string, null-terminator is not required. @param len The length of the string, in bytes. - @return Whether successful. + @return Whether the operation was successful. @warning The input string is not copied, you should keep this string unmodified for the lifetime of this JSON document. */ @@ -3426,7 +3644,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_strn(yyjson_mut_doc *doc, @param arr The array to which the value is to be inserted. Returns false if it is NULL or not an array. @param str A null-terminated UTF-8 string. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_strcpy(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3439,7 +3657,7 @@ yyjson_api_inline bool yyjson_mut_arr_add_strcpy(yyjson_mut_doc *doc, Returns false if it is NULL or not an array. @param str A UTF-8 string, null-terminator is not required. @param len The length of the string, in bytes. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_arr_add_strncpy(yyjson_mut_doc *doc, yyjson_mut_val *arr, @@ -3474,7 +3692,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_obj(yyjson_mut_doc *doc, /** Returns the number of key-value pairs in this object. Returns 0 if `obj` is NULL or type is not object. */ -yyjson_api_inline size_t yyjson_mut_obj_size(yyjson_mut_val *obj); +yyjson_api_inline size_t yyjson_mut_obj_size(const yyjson_mut_val *obj); /** Returns the value to which the specified key is mapped. Returns NULL if this object contains no mapping for the key. @@ -3483,7 +3701,7 @@ yyjson_api_inline size_t yyjson_mut_obj_size(yyjson_mut_val *obj); The `key` should be a null-terminated UTF-8 string. @warning This function takes a linear search time. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(yyjson_mut_val *obj, +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(const yyjson_mut_val *obj, const char *key); /** Returns the value to which the specified key is mapped. @@ -3494,7 +3712,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(yyjson_mut_val *obj, The `key_len` should be the length of the key, in bytes. @warning This function takes a linear search time. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(yyjson_mut_val *obj, +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(const yyjson_mut_val *obj, const char *key, size_t key_len); @@ -3546,9 +3764,9 @@ typedef struct yyjson_mut_obj_iter { Initialize an iterator for this object. @param obj The object to be iterated over. - If this parameter is NULL or not an array, `iter` will be set to empty. + If `obj` is NULL or not an object, `iter` is cleared. @param iter The iterator to be initialized. - If this parameter is NULL, the function will fail and return false. + If `iter` is NULL, returns false. @return true if the `iter` has been successfully initialized. @note The iterator does not need to be destroyed. @@ -3557,10 +3775,10 @@ yyjson_api_inline bool yyjson_mut_obj_iter_init(yyjson_mut_val *obj, yyjson_mut_obj_iter *iter); /** - Create an iterator with an object, same as `yyjson_obj_iter_init()`. + Create an iterator with an object, same as `yyjson_mut_obj_iter_init()`. @param obj The object to be iterated over. - If this parameter is NULL or not an object, an empty iterator will returned. + If `obj` is NULL or not an object, returns an empty iterator. @return A new iterator for the object. @note The iterator does not need to be destroyed. @@ -3570,28 +3788,28 @@ yyjson_api_inline yyjson_mut_obj_iter yyjson_mut_obj_iter_with( /** Returns whether the iteration has more elements. - If `iter` is NULL, this function will return false. + If `iter` is NULL, returns false. */ yyjson_api_inline bool yyjson_mut_obj_iter_has_next( yyjson_mut_obj_iter *iter); /** Returns the next key in the iteration, or NULL on end. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_next( yyjson_mut_obj_iter *iter); /** Returns the value for key inside the iteration. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get_val( yyjson_mut_val *key); /** Removes current key-value pair in the iteration, returns the removed value. - If `iter` is NULL, this function will return NULL. + If `iter` is NULL, returns NULL. */ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_remove( yyjson_mut_obj_iter *iter); @@ -3608,7 +3826,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_remove( @param iter The object iterator, should not be NULL. @param key The key, should be a UTF-8 string with null-terminator. @return The value to which the specified key is mapped. - NULL if this object contains no mapping for the key or input is invalid. + NULL if the key is not found or arguments are invalid. @warning This function takes a linear search time if the key is not nearby. */ @@ -3626,9 +3844,9 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get( @param iter The object iterator, should not be NULL. @param key The key, should be a UTF-8 string, null-terminator is not required. - @param key_len The the length of `key`, in bytes. + @param key_len The length of `key`, in bytes. @return The value to which the specified key is mapped. - NULL if this object contains no mapping for the key or input is invalid. + NULL if the key is not found or arguments are invalid. @warning This function takes a linear search time if the key is not nearby. */ @@ -3671,10 +3889,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj(yyjson_mut_doc *doc); /** Creates and returns a mutable object with keys and values, returns NULL on - error. The keys and values are not copied. The strings should be a - null-terminated UTF-8 string. + error. The keys and values are not copied. They should be null-terminated + UTF-8 strings. - @warning The input string is not copied, you should keep this string + @warning The input strings are not copied; you should keep them unmodified for the lifetime of this JSON document. @b Example @@ -3691,10 +3909,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_str(yyjson_mut_doc *doc, /** Creates and returns a mutable object with key-value pairs and pair count, - returns NULL on error. The keys and values are not copied. The strings should - be a null-terminated UTF-8 string. + returns NULL on error. The keys and values are not copied. They should be + null-terminated UTF-8 strings. - @warning The input string is not copied, you should keep this string + @warning The input strings are not copied; you should keep them unmodified for the lifetime of this JSON document. @b Example @@ -3715,25 +3933,25 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc, /** Adds a key-value pair at the end of the object. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @param obj The object to which the new key-value pair is to be added. @param key The key, should be a string which is created by `yyjson_mut_str()`, `yyjson_mut_strn()`, `yyjson_mut_strcpy()` or `yyjson_mut_strncpy()`. @param val The value to add to the object. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_obj_add(yyjson_mut_val *obj, yyjson_mut_val *key, yyjson_mut_val *val); /** Sets a key-value pair at the end of the object. - This function may remove all key-value pairs for the given key before add. + This function may remove all key-value pairs for the given key before adding. @param obj The object to which the new key-value pair is to be added. @param key The key, should be a string which is created by `yyjson_mut_str()`, `yyjson_mut_strn()`, `yyjson_mut_strcpy()` or `yyjson_mut_strncpy()`. @param val The value to add to the object. If this value is null, the behavior - is same as `yyjson_mut_obj_remove()`. - @return Whether successful. + is the same as `yyjson_mut_obj_remove()`. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, yyjson_mut_val *key, @@ -3741,13 +3959,13 @@ yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, /** Inserts a key-value pair to the object at the given position. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @param obj The object to which the new key-value pair is to be added. @param key The key, should be a string which is created by `yyjson_mut_str()`, `yyjson_mut_strn()`, `yyjson_mut_strcpy()` or `yyjson_mut_strncpy()`. @param val The value to add to the object. @param idx The index to which to insert the new pair. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_obj_insert(yyjson_mut_val *obj, yyjson_mut_val *key, @@ -3755,7 +3973,7 @@ yyjson_api_inline bool yyjson_mut_obj_insert(yyjson_mut_val *obj, size_t idx); /** - Removes all key-value pair from the object with given key. + Removes all key-value pairs from the object with the given key. @param obj The object from which the key-value pair is to be removed. @param key The key, should be a string value. @return The first matched value, or NULL if no matched value. @@ -3765,7 +3983,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove(yyjson_mut_val *obj, yyjson_mut_val *key); /** - Removes all key-value pair from the object with given key. + Removes all key-value pairs from the object with the given key. @param obj The object from which the key-value pair is to be removed. @param key The key, should be a UTF-8 string with null-terminator. @return The first matched value, or NULL if no matched value. @@ -3775,7 +3993,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_key( yyjson_mut_val *obj, const char *key); /** - Removes all key-value pair from the object with given key. + Removes all key-value pairs from the object with the given key. @param obj The object from which the key-value pair is to be removed. @param key The key, should be a UTF-8 string, null-terminator is not required. @param key_len The length of the key. @@ -3788,17 +4006,17 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_keyn( /** Removes all key-value pairs in this object. @param obj The object from which all of the values are to be removed. - @return Whether successful. + @return Whether the operation was successful. */ yyjson_api_inline bool yyjson_mut_obj_clear(yyjson_mut_val *obj); /** Replaces value from the object with given key. - If the key is not exist, or the value is NULL, it will fail. + If the key does not exist, or the value is NULL, it will fail. @param obj The object to which the value is to be replaced. @param key The key, should be a string value. @param val The value to replace into the object. - @return Whether successful. + @return Whether the operation was successful. @warning This function takes a linear search time. */ yyjson_api_inline bool yyjson_mut_obj_replace(yyjson_mut_val *obj, @@ -3811,7 +4029,7 @@ yyjson_api_inline bool yyjson_mut_obj_replace(yyjson_mut_val *obj, `{"b":2,"c":3,"d":4,"a":1}`. @param obj The object to be rotated. @param idx Index (or times) to rotate. - @return Whether successful. + @return Whether the operation was successful. @warning This function takes a linear search time. */ yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj, @@ -3825,7 +4043,7 @@ yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj, /** Adds a `null` value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3835,7 +4053,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, /** Adds a `true` value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3845,7 +4063,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, /** Adds a `false` value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3855,7 +4073,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, /** Adds a bool value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3865,7 +4083,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, /** Adds an unsigned integer value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3875,7 +4093,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, /** Adds a signed integer value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3885,7 +4103,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, /** Adds an int value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3895,7 +4113,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, /** Adds a float value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3905,7 +4123,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_float(yyjson_mut_doc *doc, /** Adds a double value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3915,7 +4133,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_double(yyjson_mut_doc *doc, /** Adds a real value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3925,7 +4143,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, /** Adds a string value at the end of the object. The `key` and `val` should be null-terminated UTF-8 strings. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ @@ -3937,7 +4155,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. The `val` should be a UTF-8 string, null-terminator is not required. The `len` should be the length of the `val`, in bytes. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ @@ -3949,7 +4167,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, /** Adds a string value at the end of the object. The `key` and `val` should be null-terminated UTF-8 strings. The value string is copied. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -3962,7 +4180,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. The `val` should be a UTF-8 string, null-terminator is not required. The `len` should be the length of the `val`, in bytes. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ @@ -3974,7 +4192,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, /** Creates and adds a new array to the target object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep these strings unmodified for the lifetime of this JSON document. @@ -3987,7 +4205,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, /** Creates and adds a new object to the target object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep these strings unmodified for the lifetime of this JSON document. @@ -3999,7 +4217,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, /** Adds a JSON value at the end of the object. The `key` should be a null-terminated UTF-8 string. - This function allows duplicated key in one object. + This function allows duplicate keys in one object. @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ @@ -4073,7 +4291,7 @@ static const yyjson_ptr_code YYJSON_PTR_ERR_NONE = 0; /** Invalid input parameter, such as NULL input. */ static const yyjson_ptr_code YYJSON_PTR_ERR_PARAMETER = 1; -/** JSON pointer syntax error, such as invalid escape, token no prefix. */ +/** JSON pointer syntax error, such as invalid escape or missing prefix. */ static const yyjson_ptr_code YYJSON_PTR_ERR_SYNTAX = 2; /** JSON pointer resolve failed, such as index out of range, key not found. */ @@ -4149,7 +4367,7 @@ typedef struct yyjson_ptr_ctx { @return The value referenced by the JSON pointer. NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(yyjson_doc *doc, +yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(const yyjson_doc *doc, const char *ptr); /** @@ -4160,7 +4378,7 @@ yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(yyjson_doc *doc, @return The value referenced by the JSON pointer. NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(yyjson_doc *doc, +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(const yyjson_doc *doc, const char *ptr, size_t len); /** @@ -4172,7 +4390,7 @@ yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(yyjson_doc *doc, @return The value referenced by the JSON pointer. NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(yyjson_doc *doc, +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(const yyjson_doc *doc, const char *ptr, size_t len, yyjson_ptr_err *err); @@ -4183,7 +4401,7 @@ yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(yyjson_doc *doc, @return The value referenced by the JSON pointer. NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_val *yyjson_ptr_get(yyjson_val *val, +yyjson_api_inline yyjson_val *yyjson_ptr_get(const yyjson_val *val, const char *ptr); /** @@ -4194,7 +4412,7 @@ yyjson_api_inline yyjson_val *yyjson_ptr_get(yyjson_val *val, @return The value referenced by the JSON pointer. NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_val *yyjson_ptr_getn(yyjson_val *val, +yyjson_api_inline yyjson_val *yyjson_ptr_getn(const yyjson_val *val, const char *ptr, size_t len); /** @@ -4206,7 +4424,7 @@ yyjson_api_inline yyjson_val *yyjson_ptr_getn(yyjson_val *val, @return The value referenced by the JSON pointer. NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, +yyjson_api_inline yyjson_val *yyjson_ptr_getx(const yyjson_val *val, const char *ptr, size_t len, yyjson_ptr_err *err); @@ -4217,8 +4435,8 @@ yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, @return The value referenced by the JSON pointer. NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get(yyjson_mut_doc *doc, - const char *ptr); +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get( + const yyjson_mut_doc *doc, const char *ptr); /** Get value by a JSON Pointer. @@ -4228,9 +4446,8 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get(yyjson_mut_doc *doc, @return The value referenced by the JSON pointer. NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn(yyjson_mut_doc *doc, - const char *ptr, - size_t len); +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn( + const yyjson_mut_doc *doc, const char *ptr, size_t len); /** Get value by a JSON Pointer. @@ -4242,11 +4459,9 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn(yyjson_mut_doc *doc, @return The value referenced by the JSON pointer. NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx(yyjson_mut_doc *doc, - const char *ptr, - size_t len, - yyjson_ptr_ctx *ctx, - yyjson_ptr_err *err); +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx( + const yyjson_mut_doc *doc, const char *ptr, size_t len, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err); /** Get value by a JSON Pointer. @@ -4255,7 +4470,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx(yyjson_mut_doc *doc, @return The value referenced by the JSON pointer. NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(yyjson_mut_val *val, +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(const yyjson_mut_val *val, const char *ptr); /** @@ -4266,7 +4481,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(yyjson_mut_val *val, @return The value referenced by the JSON pointer. NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(yyjson_mut_val *val, +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(const yyjson_mut_val *val, const char *ptr, size_t len); @@ -4280,7 +4495,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(yyjson_mut_val *val, @return The value referenced by the JSON pointer. NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. */ -yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(yyjson_mut_val *val, +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(const yyjson_mut_val *val, const char *ptr, size_t len, yyjson_ptr_ctx *ctx, @@ -4317,7 +4532,7 @@ yyjson_api_inline bool yyjson_mut_doc_ptr_addn(yyjson_mut_doc *doc, @param ptr The JSON pointer string (UTF-8, null-terminator is not required). @param len The length of `ptr` in bytes. @param new_val The value to be added. - @param create_parent Whether to create parent nodes if not exist. + @param create_parent Whether to create parent nodes if they do not exist. @param ctx A pointer to store the result context, or NULL if not needed. @param err A pointer to store the error information, or NULL if not needed. @return true if JSON pointer is valid and new value is added, false otherwise. @@ -4365,7 +4580,7 @@ yyjson_api_inline bool yyjson_mut_ptr_addn(yyjson_mut_val *val, @param len The length of `ptr` in bytes. @param doc Only used to create new values when needed. @param new_val The value to be added. - @param create_parent Whether to create parent nodes if not exist. + @param create_parent Whether to create parent nodes if they do not exist. @param ctx A pointer to store the result context, or NULL if not needed. @param err A pointer to store the error information, or NULL if not needed. @return true if JSON pointer is valid and new value is added, false otherwise. @@ -4411,7 +4626,7 @@ yyjson_api_inline bool yyjson_mut_doc_ptr_setn(yyjson_mut_doc *doc, @param ptr The JSON pointer string (UTF-8, null-terminator is not required). @param len The length of `ptr` in bytes. @param new_val The value to be set, pass NULL to remove. - @param create_parent Whether to create parent nodes if not exist. + @param create_parent Whether to create parent nodes if they do not exist. @param ctx A pointer to store the result context, or NULL if not needed. @param err A pointer to store the error information, or NULL if not needed. @return true if JSON pointer is valid and new value is set, false otherwise. @@ -4462,7 +4677,7 @@ yyjson_api_inline bool yyjson_mut_ptr_setn(yyjson_mut_val *val, @param len The length of `ptr` in bytes. @param new_val The value to be set, pass NULL to remove. @param doc Only used to create new values when needed. - @param create_parent Whether to create parent nodes if not exist. + @param create_parent Whether to create parent nodes if they do not exist. @param ctx A pointer to store the result context, or NULL if not needed. @param err A pointer to store the error information, or NULL if not needed. @return true if JSON pointer is valid and new value is set, false otherwise. @@ -4629,7 +4844,7 @@ yyjson_api_inline bool yyjson_ptr_ctx_append(yyjson_ptr_ctx *ctx, @param ctx The context from the `yyjson_mut_ptr_xxx()` calls. @param val New value to be replaced. @return true on success or false on fail. - @note If success, the old value will be returned via `ctx->old`. + @note On success, the old value will be returned via `ctx->old`. */ yyjson_api_inline bool yyjson_ptr_ctx_replace(yyjson_ptr_ctx *ctx, yyjson_mut_val *val); @@ -4638,7 +4853,7 @@ yyjson_api_inline bool yyjson_ptr_ctx_replace(yyjson_ptr_ctx *ctx, Remove value by JSON pointer context. @param ctx The context from the `yyjson_mut_ptr_xxx()` calls. @return true on success or false on fail. - @note If success, the old value will be returned via `ctx->old`. + @note On success, the old value will be returned via `ctx->old`. */ yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx); @@ -4658,7 +4873,7 @@ static const yyjson_patch_code YYJSON_PATCH_SUCCESS = 0; /** Invalid parameter, such as NULL input or non-array patch. */ static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_PARAMETER = 1; -/** Memory allocation failure occurs. */ +/** Memory allocation failed. */ static const yyjson_patch_code YYJSON_PATCH_ERROR_MEMORY_ALLOCATION = 2; /** JSON patch operation is not object type. */ @@ -4670,7 +4885,7 @@ static const yyjson_patch_code YYJSON_PATCH_ERROR_MISSING_KEY = 4; /** JSON patch operation member is invalid. */ static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_MEMBER = 5; -/** JSON patch operation `test` not equal. */ +/** JSON patch `test` operation failed (values not equal). */ static const yyjson_patch_code YYJSON_PATCH_ERROR_EQUAL = 6; /** JSON patch operation failed on JSON pointer. */ @@ -4695,8 +4910,8 @@ typedef struct yyjson_patch_err { Returns NULL if the patch could not be applied. */ yyjson_api yyjson_mut_val *yyjson_patch(yyjson_mut_doc *doc, - yyjson_val *orig, - yyjson_val *patch, + const yyjson_val *orig, + const yyjson_val *patch, yyjson_patch_err *err); /** @@ -4706,8 +4921,8 @@ yyjson_api yyjson_mut_val *yyjson_patch(yyjson_mut_doc *doc, Returns NULL if the patch could not be applied. */ yyjson_api yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, - yyjson_mut_val *orig, - yyjson_mut_val *patch, + const yyjson_mut_val *orig, + const yyjson_mut_val *patch, yyjson_patch_err *err); @@ -4726,8 +4941,8 @@ yyjson_api yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, object level is too deep. */ yyjson_api yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, - yyjson_val *orig, - yyjson_val *patch); + const yyjson_val *orig, + const yyjson_val *patch); /** Creates and returns a merge-patched JSON value (RFC 7386). @@ -4738,8 +4953,8 @@ yyjson_api yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, object level is too deep. */ yyjson_api yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, - yyjson_mut_val *orig, - yyjson_mut_val *patch); + const yyjson_mut_val *orig, + const yyjson_mut_val *patch); #endif /* YYJSON_DISABLE_UTILS */ @@ -4774,7 +4989,7 @@ struct yyjson_doc { yyjson_alc alc; /** The total number of bytes read when parsing JSON (nonzero). */ size_t dat_read; - /** The total number of value read when parsing JSON (nonzero). */ + /** The total number of values read when parsing JSON (nonzero). */ size_t val_read; /** The string pool used by JSON values (nullable). */ char *str_pool; @@ -4799,7 +5014,7 @@ struct yyjson_doc { earlier versions are uncertain. @param str The C string. - @param len The returnd value from strlen(str). + @param len The returned value from strlen(str). */ yyjson_api_inline bool unsafe_yyjson_is_str_noesc(const char *str, size_t len) { #if YYJSON_HAS_CONSTANT_P && \ @@ -4851,154 +5066,156 @@ yyjson_api_inline double unsafe_yyjson_u64_to_f64(uint64_t num) { #endif } -yyjson_api_inline yyjson_type unsafe_yyjson_get_type(void *val) { - uint8_t tag = (uint8_t)((yyjson_val *)val)->tag; +yyjson_api_inline yyjson_type unsafe_yyjson_get_type(const void *val) { + uint8_t tag = (uint8_t)((const yyjson_val *)val)->tag; return (yyjson_type)(tag & YYJSON_TYPE_MASK); } -yyjson_api_inline yyjson_subtype unsafe_yyjson_get_subtype(void *val) { - uint8_t tag = (uint8_t)((yyjson_val *)val)->tag; +yyjson_api_inline yyjson_subtype unsafe_yyjson_get_subtype(const void *val) { + uint8_t tag = (uint8_t)((const yyjson_val *)val)->tag; return (yyjson_subtype)(tag & YYJSON_SUBTYPE_MASK); } -yyjson_api_inline uint8_t unsafe_yyjson_get_tag(void *val) { - uint8_t tag = (uint8_t)((yyjson_val *)val)->tag; +yyjson_api_inline uint8_t unsafe_yyjson_get_tag(const void *val) { + uint8_t tag = (uint8_t)((const yyjson_val *)val)->tag; return (uint8_t)(tag & YYJSON_TAG_MASK); } -yyjson_api_inline bool unsafe_yyjson_is_raw(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_raw(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_RAW; } -yyjson_api_inline bool unsafe_yyjson_is_null(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_null(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_NULL; } -yyjson_api_inline bool unsafe_yyjson_is_bool(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_bool(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_BOOL; } -yyjson_api_inline bool unsafe_yyjson_is_num(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_num(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_NUM; } -yyjson_api_inline bool unsafe_yyjson_is_str(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_str(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_STR; } -yyjson_api_inline bool unsafe_yyjson_is_arr(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_arr(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_ARR; } -yyjson_api_inline bool unsafe_yyjson_is_obj(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_obj(const void *val) { return unsafe_yyjson_get_type(val) == YYJSON_TYPE_OBJ; } -yyjson_api_inline bool unsafe_yyjson_is_ctn(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_ctn(const void *val) { uint8_t mask = YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ; return (unsafe_yyjson_get_tag(val) & mask) == mask; } -yyjson_api_inline bool unsafe_yyjson_is_uint(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_uint(const void *val) { const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT; return unsafe_yyjson_get_tag(val) == patt; } -yyjson_api_inline bool unsafe_yyjson_is_sint(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_sint(const void *val) { const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT; return unsafe_yyjson_get_tag(val) == patt; } -yyjson_api_inline bool unsafe_yyjson_is_int(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_int(const void *val) { const uint8_t mask = YYJSON_TAG_MASK & (~YYJSON_SUBTYPE_SINT); const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT; return (unsafe_yyjson_get_tag(val) & mask) == patt; } -yyjson_api_inline bool unsafe_yyjson_is_real(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_real(const void *val) { const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; return unsafe_yyjson_get_tag(val) == patt; } -yyjson_api_inline bool unsafe_yyjson_is_true(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_true(const void *val) { const uint8_t patt = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_TRUE; return unsafe_yyjson_get_tag(val) == patt; } -yyjson_api_inline bool unsafe_yyjson_is_false(void *val) { +yyjson_api_inline bool unsafe_yyjson_is_false(const void *val) { const uint8_t patt = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_FALSE; return unsafe_yyjson_get_tag(val) == patt; } -yyjson_api_inline bool unsafe_yyjson_arr_is_flat(yyjson_val *val) { +yyjson_api_inline bool unsafe_yyjson_arr_is_flat(const yyjson_val *val) { size_t ofs = val->uni.ofs; size_t len = (size_t)(val->tag >> YYJSON_TAG_BIT); return len * sizeof(yyjson_val) + sizeof(yyjson_val) == ofs; } -yyjson_api_inline const char *unsafe_yyjson_get_raw(void *val) { - return ((yyjson_val *)val)->uni.str; +yyjson_api_inline const char *unsafe_yyjson_get_raw(const void *val) { + return ((const yyjson_val *)val)->uni.str; } -yyjson_api_inline bool unsafe_yyjson_get_bool(void *val) { +yyjson_api_inline bool unsafe_yyjson_get_bool(const void *val) { uint8_t tag = unsafe_yyjson_get_tag(val); return (bool)((tag & YYJSON_SUBTYPE_MASK) >> YYJSON_TYPE_BIT); } -yyjson_api_inline uint64_t unsafe_yyjson_get_uint(void *val) { - return ((yyjson_val *)val)->uni.u64; +yyjson_api_inline uint64_t unsafe_yyjson_get_uint(const void *val) { + return ((const yyjson_val *)val)->uni.u64; } -yyjson_api_inline int64_t unsafe_yyjson_get_sint(void *val) { - return ((yyjson_val *)val)->uni.i64; +yyjson_api_inline int64_t unsafe_yyjson_get_sint(const void *val) { + return ((const yyjson_val *)val)->uni.i64; } -yyjson_api_inline int unsafe_yyjson_get_int(void *val) { - return (int)((yyjson_val *)val)->uni.i64; +yyjson_api_inline int unsafe_yyjson_get_int(const void *val) { + return (int)((const yyjson_val *)val)->uni.i64; } -yyjson_api_inline double unsafe_yyjson_get_real(void *val) { - return ((yyjson_val *)val)->uni.f64; +yyjson_api_inline double unsafe_yyjson_get_real(const void *val) { + return ((const yyjson_val *)val)->uni.f64; } -yyjson_api_inline double unsafe_yyjson_get_num(void *val) { +yyjson_api_inline double unsafe_yyjson_get_num(const void *val) { uint8_t tag = unsafe_yyjson_get_tag(val); if (tag == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL)) { - return ((yyjson_val *)val)->uni.f64; + return ((const yyjson_val *)val)->uni.f64; } else if (tag == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT)) { - return (double)((yyjson_val *)val)->uni.i64; + return (double)((const yyjson_val *)val)->uni.i64; } else if (tag == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT)) { - return unsafe_yyjson_u64_to_f64(((yyjson_val *)val)->uni.u64); + return unsafe_yyjson_u64_to_f64(((const yyjson_val *)val)->uni.u64); } return 0.0; } -yyjson_api_inline const char *unsafe_yyjson_get_str(void *val) { - return ((yyjson_val *)val)->uni.str; +yyjson_api_inline const char *unsafe_yyjson_get_str(const void *val) { + return ((const yyjson_val *)val)->uni.str; } -yyjson_api_inline size_t unsafe_yyjson_get_len(void *val) { - return (size_t)(((yyjson_val *)val)->tag >> YYJSON_TAG_BIT); +yyjson_api_inline size_t unsafe_yyjson_get_len(const void *val) { + return (size_t)(((const yyjson_val *)val)->tag >> YYJSON_TAG_BIT); } -yyjson_api_inline yyjson_val *unsafe_yyjson_get_first(yyjson_val *ctn) { - return ctn + 1; +yyjson_api_inline yyjson_val *unsafe_yyjson_get_first(const yyjson_val *ctn) { + return yyjson_constcast(yyjson_val *)ctn + 1; } -yyjson_api_inline yyjson_val *unsafe_yyjson_get_next(yyjson_val *val) { +yyjson_api_inline yyjson_val *unsafe_yyjson_get_next(const yyjson_val *val) { bool is_ctn = unsafe_yyjson_is_ctn(val); size_t ctn_ofs = val->uni.ofs; size_t ofs = (is_ctn ? ctn_ofs : sizeof(yyjson_val)); - return (yyjson_val *)(void *)((uint8_t *)val + ofs); + uint8_t *ptr = yyjson_constcast(uint8_t *)val; + return (yyjson_val *)(void *)(ptr + ofs); } -yyjson_api_inline bool unsafe_yyjson_equals_strn(void *val, const char *str, - size_t len) { +yyjson_api_inline bool unsafe_yyjson_equals_strn(const void *val, + const char *str, size_t len) { return unsafe_yyjson_get_len(val) == len && - memcmp(((yyjson_val *)val)->uni.str, str, len) == 0; + memcmp(((const yyjson_val *)val)->uni.str, str, len) == 0; } -yyjson_api_inline bool unsafe_yyjson_equals_str(void *val, const char *str) { +yyjson_api_inline bool unsafe_yyjson_equals_str(const void *val, + const char *str) { return unsafe_yyjson_equals_strn(val, str, strlen(str)); } @@ -5115,15 +5332,15 @@ yyjson_api_inline void unsafe_yyjson_set_obj(void *val, size_t size) { * MARK: - JSON Document API (Implementation) *============================================================================*/ -yyjson_api_inline yyjson_val *yyjson_doc_get_root(yyjson_doc *doc) { +yyjson_api_inline yyjson_val *yyjson_doc_get_root(const yyjson_doc *doc) { return doc ? doc->root : NULL; } -yyjson_api_inline size_t yyjson_doc_get_read_size(yyjson_doc *doc) { +yyjson_api_inline size_t yyjson_doc_get_read_size(const yyjson_doc *doc) { return doc ? doc->dat_read : 0; } -yyjson_api_inline size_t yyjson_doc_get_val_count(yyjson_doc *doc) { +yyjson_api_inline size_t yyjson_doc_get_val_count(const yyjson_doc *doc) { return doc ? doc->val_read : 0; } @@ -5142,59 +5359,59 @@ yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc) { * MARK: - JSON Value Type API (Implementation) *============================================================================*/ -yyjson_api_inline bool yyjson_is_raw(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_raw(const yyjson_val *val) { return val ? unsafe_yyjson_is_raw(val) : false; } -yyjson_api_inline bool yyjson_is_null(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_null(const yyjson_val *val) { return val ? unsafe_yyjson_is_null(val) : false; } -yyjson_api_inline bool yyjson_is_true(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_true(const yyjson_val *val) { return val ? unsafe_yyjson_is_true(val) : false; } -yyjson_api_inline bool yyjson_is_false(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_false(const yyjson_val *val) { return val ? unsafe_yyjson_is_false(val) : false; } -yyjson_api_inline bool yyjson_is_bool(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_bool(const yyjson_val *val) { return val ? unsafe_yyjson_is_bool(val) : false; } -yyjson_api_inline bool yyjson_is_uint(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_uint(const yyjson_val *val) { return val ? unsafe_yyjson_is_uint(val) : false; } -yyjson_api_inline bool yyjson_is_sint(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_sint(const yyjson_val *val) { return val ? unsafe_yyjson_is_sint(val) : false; } -yyjson_api_inline bool yyjson_is_int(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_int(const yyjson_val *val) { return val ? unsafe_yyjson_is_int(val) : false; } -yyjson_api_inline bool yyjson_is_real(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_real(const yyjson_val *val) { return val ? unsafe_yyjson_is_real(val) : false; } -yyjson_api_inline bool yyjson_is_num(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_num(const yyjson_val *val) { return val ? unsafe_yyjson_is_num(val) : false; } -yyjson_api_inline bool yyjson_is_str(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_str(const yyjson_val *val) { return val ? unsafe_yyjson_is_str(val) : false; } -yyjson_api_inline bool yyjson_is_arr(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_arr(const yyjson_val *val) { return val ? unsafe_yyjson_is_arr(val) : false; } -yyjson_api_inline bool yyjson_is_obj(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_obj(const yyjson_val *val) { return val ? unsafe_yyjson_is_obj(val) : false; } -yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val) { +yyjson_api_inline bool yyjson_is_ctn(const yyjson_val *val) { return val ? unsafe_yyjson_is_ctn(val) : false; } @@ -5204,19 +5421,19 @@ yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val) { * MARK: - JSON Value Content API (Implementation) *============================================================================*/ -yyjson_api_inline yyjson_type yyjson_get_type(yyjson_val *val) { +yyjson_api_inline yyjson_type yyjson_get_type(const yyjson_val *val) { return val ? unsafe_yyjson_get_type(val) : YYJSON_TYPE_NONE; } -yyjson_api_inline yyjson_subtype yyjson_get_subtype(yyjson_val *val) { +yyjson_api_inline yyjson_subtype yyjson_get_subtype(const yyjson_val *val) { return val ? unsafe_yyjson_get_subtype(val) : YYJSON_SUBTYPE_NONE; } -yyjson_api_inline uint8_t yyjson_get_tag(yyjson_val *val) { +yyjson_api_inline uint8_t yyjson_get_tag(const yyjson_val *val) { return val ? unsafe_yyjson_get_tag(val) : 0; } -yyjson_api_inline const char *yyjson_get_type_desc(yyjson_val *val) { +yyjson_api_inline const char *yyjson_get_type_desc(const yyjson_val *val) { switch (yyjson_get_tag(val)) { case YYJSON_TYPE_RAW | YYJSON_SUBTYPE_NONE: return "raw"; case YYJSON_TYPE_NULL | YYJSON_SUBTYPE_NONE: return "null"; @@ -5233,43 +5450,44 @@ yyjson_api_inline const char *yyjson_get_type_desc(yyjson_val *val) { } } -yyjson_api_inline const char *yyjson_get_raw(yyjson_val *val) { +yyjson_api_inline const char *yyjson_get_raw(const yyjson_val *val) { return yyjson_is_raw(val) ? unsafe_yyjson_get_raw(val) : NULL; } -yyjson_api_inline bool yyjson_get_bool(yyjson_val *val) { +yyjson_api_inline bool yyjson_get_bool(const yyjson_val *val) { return yyjson_is_bool(val) ? unsafe_yyjson_get_bool(val) : false; } -yyjson_api_inline uint64_t yyjson_get_uint(yyjson_val *val) { +yyjson_api_inline uint64_t yyjson_get_uint(const yyjson_val *val) { return yyjson_is_int(val) ? unsafe_yyjson_get_uint(val) : 0; } -yyjson_api_inline int64_t yyjson_get_sint(yyjson_val *val) { +yyjson_api_inline int64_t yyjson_get_sint(const yyjson_val *val) { return yyjson_is_int(val) ? unsafe_yyjson_get_sint(val) : 0; } -yyjson_api_inline int yyjson_get_int(yyjson_val *val) { +yyjson_api_inline int yyjson_get_int(const yyjson_val *val) { return yyjson_is_int(val) ? unsafe_yyjson_get_int(val) : 0; } -yyjson_api_inline double yyjson_get_real(yyjson_val *val) { +yyjson_api_inline double yyjson_get_real(const yyjson_val *val) { return yyjson_is_real(val) ? unsafe_yyjson_get_real(val) : 0.0; } -yyjson_api_inline double yyjson_get_num(yyjson_val *val) { +yyjson_api_inline double yyjson_get_num(const yyjson_val *val) { return val ? unsafe_yyjson_get_num(val) : 0.0; } -yyjson_api_inline const char *yyjson_get_str(yyjson_val *val) { +yyjson_api_inline const char *yyjson_get_str(const yyjson_val *val) { return yyjson_is_str(val) ? unsafe_yyjson_get_str(val) : NULL; } -yyjson_api_inline size_t yyjson_get_len(yyjson_val *val) { +yyjson_api_inline size_t yyjson_get_len(const yyjson_val *val) { return val ? unsafe_yyjson_get_len(val) : 0; } -yyjson_api_inline bool yyjson_equals_str(yyjson_val *val, const char *str) { +yyjson_api_inline bool yyjson_equals_str(const yyjson_val *val, + const char *str) { if (yyjson_likely(val && str)) { return unsafe_yyjson_is_str(val) && unsafe_yyjson_equals_str(val, str); @@ -5277,8 +5495,8 @@ yyjson_api_inline bool yyjson_equals_str(yyjson_val *val, const char *str) { return false; } -yyjson_api_inline bool yyjson_equals_strn(yyjson_val *val, const char *str, - size_t len) { +yyjson_api_inline bool yyjson_equals_strn(const yyjson_val *val, + const char *str, size_t len) { if (yyjson_likely(val && str)) { return unsafe_yyjson_is_str(val) && unsafe_yyjson_equals_strn(val, str, len); @@ -5286,9 +5504,11 @@ yyjson_api_inline bool yyjson_equals_strn(yyjson_val *val, const char *str, return false; } -yyjson_api bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs); +yyjson_api bool unsafe_yyjson_equals(const yyjson_val *lhs, + const yyjson_val *rhs); -yyjson_api_inline bool yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { +yyjson_api_inline bool yyjson_equals(const yyjson_val *lhs, + const yyjson_val *rhs) { if (yyjson_unlikely(!lhs || !rhs)) return false; return unsafe_yyjson_equals(lhs, rhs); } @@ -5296,6 +5516,7 @@ yyjson_api_inline bool yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { yyjson_api_inline bool yyjson_set_raw(yyjson_val *val, const char *raw, size_t len) { if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + if (yyjson_unlikely(!raw)) return false; unsafe_yyjson_set_raw(val, raw, len); return true; } @@ -5324,9 +5545,9 @@ yyjson_api_inline bool yyjson_set_sint(yyjson_val *val, int64_t num) { return true; } -yyjson_api_inline bool yyjson_set_int(yyjson_val *val, int num) { +yyjson_api_inline bool yyjson_set_int(yyjson_val *val, int64_t num) { if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; - unsafe_yyjson_set_sint(val, (int64_t)num); + unsafe_yyjson_set_sint(val, num); return true; } @@ -5387,11 +5608,12 @@ yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc) { * MARK: - JSON Array API (Implementation) *============================================================================*/ -yyjson_api_inline size_t yyjson_arr_size(yyjson_val *arr) { +yyjson_api_inline size_t yyjson_arr_size(const yyjson_val *arr) { return yyjson_is_arr(arr) ? unsafe_yyjson_get_len(arr) : 0; } -yyjson_api_inline yyjson_val *yyjson_arr_get(yyjson_val *arr, size_t idx) { +yyjson_api_inline yyjson_val *yyjson_arr_get(const yyjson_val *arr, + size_t idx) { if (yyjson_likely(yyjson_is_arr(arr))) { if (yyjson_likely(unsafe_yyjson_get_len(arr) > idx)) { yyjson_val *val = unsafe_yyjson_get_first(arr); @@ -5406,7 +5628,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_get(yyjson_val *arr, size_t idx) { return NULL; } -yyjson_api_inline yyjson_val *yyjson_arr_get_first(yyjson_val *arr) { +yyjson_api_inline yyjson_val *yyjson_arr_get_first(const yyjson_val *arr) { if (yyjson_likely(yyjson_is_arr(arr))) { if (yyjson_likely(unsafe_yyjson_get_len(arr) > 0)) { return unsafe_yyjson_get_first(arr); @@ -5415,7 +5637,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_get_first(yyjson_val *arr) { return NULL; } -yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr) { +yyjson_api_inline yyjson_val *yyjson_arr_get_last(const yyjson_val *arr) { if (yyjson_likely(yyjson_is_arr(arr))) { size_t len = unsafe_yyjson_get_len(arr); if (yyjson_likely(len > 0)) { @@ -5437,7 +5659,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr) { * MARK: - JSON Array Iterator API (Implementation) *============================================================================*/ -yyjson_api_inline bool yyjson_arr_iter_init(yyjson_val *arr, +yyjson_api_inline bool yyjson_arr_iter_init(const yyjson_val *arr, yyjson_arr_iter *iter) { if (yyjson_likely(yyjson_is_arr(arr) && iter)) { iter->idx = 0; @@ -5449,7 +5671,7 @@ yyjson_api_inline bool yyjson_arr_iter_init(yyjson_val *arr, return false; } -yyjson_api_inline yyjson_arr_iter yyjson_arr_iter_with(yyjson_val *arr) { +yyjson_api_inline yyjson_arr_iter yyjson_arr_iter_with(const yyjson_val *arr) { yyjson_arr_iter iter; yyjson_arr_iter_init(arr, &iter); return iter; @@ -5476,16 +5698,16 @@ yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter) { * MARK: - JSON Object API (Implementation) *============================================================================*/ -yyjson_api_inline size_t yyjson_obj_size(yyjson_val *obj) { +yyjson_api_inline size_t yyjson_obj_size(const yyjson_val *obj) { return yyjson_is_obj(obj) ? unsafe_yyjson_get_len(obj) : 0; } -yyjson_api_inline yyjson_val *yyjson_obj_get(yyjson_val *obj, +yyjson_api_inline yyjson_val *yyjson_obj_get(const yyjson_val *obj, const char *key) { return yyjson_obj_getn(obj, key, key ? strlen(key) : 0); } -yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj, +yyjson_api_inline yyjson_val *yyjson_obj_getn(const yyjson_val *obj, const char *_key, size_t key_len) { if (yyjson_likely(yyjson_is_obj(obj) && _key)) { @@ -5505,20 +5727,20 @@ yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj, * MARK: - JSON Object Iterator API (Implementation) *============================================================================*/ -yyjson_api_inline bool yyjson_obj_iter_init(yyjson_val *obj, +yyjson_api_inline bool yyjson_obj_iter_init(const yyjson_val *obj, yyjson_obj_iter *iter) { if (yyjson_likely(yyjson_is_obj(obj) && iter)) { iter->idx = 0; iter->max = unsafe_yyjson_get_len(obj); iter->cur = unsafe_yyjson_get_first(obj); - iter->obj = obj; + iter->obj = yyjson_constcast(yyjson_val *)obj; return true; } if (iter) memset(iter, 0, sizeof(yyjson_obj_iter)); return false; } -yyjson_api_inline yyjson_obj_iter yyjson_obj_iter_with(yyjson_val *obj) { +yyjson_api_inline yyjson_obj_iter yyjson_obj_iter_with(const yyjson_val *obj) { yyjson_obj_iter iter; yyjson_obj_iter_init(obj, &iter); return iter; @@ -5584,7 +5806,7 @@ yyjson_api_inline yyjson_val *yyjson_obj_iter_getn(yyjson_obj_iter *iter, /** Mutable JSON value, 24 bytes. - The 'tag' and 'uni' field is same as immutable value. + The 'tag' and 'uni' fields are the same as immutable value. The 'next' field links all elements inside the container to be a cycle. */ struct yyjson_mut_val { @@ -5615,7 +5837,7 @@ typedef struct yyjson_str_pool { /** A memory chunk in value memory pool. - `sizeof(yyjson_val_chunk)` should not larger than `sizeof(yyjson_mut_val)`. + `sizeof(yyjson_val_chunk)` should not be larger than `sizeof(yyjson_mut_val)`. */ typedef struct yyjson_val_chunk { struct yyjson_val_chunk *next; /* next chunk linked list */ @@ -5658,6 +5880,10 @@ yyjson_api_inline char *unsafe_yyjson_mut_str_alc(yyjson_mut_doc *doc, char *mem; const yyjson_alc *alc = &doc->alc; yyjson_str_pool *pool = &doc->str_pool; + /* `len + 1` is used below to reserve space for a null terminator; + reject the value that would wrap it to 0 and produce an under-sized + allocation with an out-of-bounds memcpy at the call sites. */ + if (yyjson_unlikely(len == (size_t)-1)) return NULL; if (yyjson_unlikely((size_t)(pool->end - pool->cur) <= len)) { if (yyjson_unlikely(!unsafe_yyjson_str_pool_grow(pool, alc, len + 1))) { return NULL; @@ -5713,59 +5939,59 @@ yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc, * MARK: - Mutable JSON Value Type API (Implementation) *============================================================================*/ -yyjson_api_inline bool yyjson_mut_is_raw(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_raw(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_raw(val) : false; } -yyjson_api_inline bool yyjson_mut_is_null(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_null(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_null(val) : false; } -yyjson_api_inline bool yyjson_mut_is_true(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_true(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_true(val) : false; } -yyjson_api_inline bool yyjson_mut_is_false(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_false(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_false(val) : false; } -yyjson_api_inline bool yyjson_mut_is_bool(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_bool(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_bool(val) : false; } -yyjson_api_inline bool yyjson_mut_is_uint(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_uint(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_uint(val) : false; } -yyjson_api_inline bool yyjson_mut_is_sint(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_sint(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_sint(val) : false; } -yyjson_api_inline bool yyjson_mut_is_int(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_int(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_int(val) : false; } -yyjson_api_inline bool yyjson_mut_is_real(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_real(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_real(val) : false; } -yyjson_api_inline bool yyjson_mut_is_num(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_num(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_num(val) : false; } -yyjson_api_inline bool yyjson_mut_is_str(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_str(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_str(val) : false; } -yyjson_api_inline bool yyjson_mut_is_arr(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_arr(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_arr(val) : false; } -yyjson_api_inline bool yyjson_mut_is_obj(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_obj(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_obj(val) : false; } -yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val) { +yyjson_api_inline bool yyjson_mut_is_ctn(const yyjson_mut_val *val) { return val ? unsafe_yyjson_is_ctn(val) : false; } @@ -5775,73 +6001,75 @@ yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val) { * MARK: - Mutable JSON Value Content API (Implementation) *============================================================================*/ -yyjson_api_inline yyjson_type yyjson_mut_get_type(yyjson_mut_val *val) { - return yyjson_get_type((yyjson_val *)val); +yyjson_api_inline yyjson_type yyjson_mut_get_type(const yyjson_mut_val *val) { + return yyjson_get_type((const yyjson_val *)val); } -yyjson_api_inline yyjson_subtype yyjson_mut_get_subtype(yyjson_mut_val *val) { - return yyjson_get_subtype((yyjson_val *)val); +yyjson_api_inline yyjson_subtype yyjson_mut_get_subtype( + const yyjson_mut_val *val) { + return yyjson_get_subtype((const yyjson_val *)val); } -yyjson_api_inline uint8_t yyjson_mut_get_tag(yyjson_mut_val *val) { - return yyjson_get_tag((yyjson_val *)val); +yyjson_api_inline uint8_t yyjson_mut_get_tag(const yyjson_mut_val *val) { + return yyjson_get_tag((const yyjson_val *)val); } -yyjson_api_inline const char *yyjson_mut_get_type_desc(yyjson_mut_val *val) { - return yyjson_get_type_desc((yyjson_val *)val); +yyjson_api_inline const char *yyjson_mut_get_type_desc( + const yyjson_mut_val *val) { + return yyjson_get_type_desc((const yyjson_val *)val); } -yyjson_api_inline const char *yyjson_mut_get_raw(yyjson_mut_val *val) { - return yyjson_get_raw((yyjson_val *)val); +yyjson_api_inline const char *yyjson_mut_get_raw(const yyjson_mut_val *val) { + return yyjson_get_raw((const yyjson_val *)val); } -yyjson_api_inline bool yyjson_mut_get_bool(yyjson_mut_val *val) { - return yyjson_get_bool((yyjson_val *)val); +yyjson_api_inline bool yyjson_mut_get_bool(const yyjson_mut_val *val) { + return yyjson_get_bool((const yyjson_val *)val); } -yyjson_api_inline uint64_t yyjson_mut_get_uint(yyjson_mut_val *val) { - return yyjson_get_uint((yyjson_val *)val); +yyjson_api_inline uint64_t yyjson_mut_get_uint(const yyjson_mut_val *val) { + return yyjson_get_uint((const yyjson_val *)val); } -yyjson_api_inline int64_t yyjson_mut_get_sint(yyjson_mut_val *val) { - return yyjson_get_sint((yyjson_val *)val); +yyjson_api_inline int64_t yyjson_mut_get_sint(const yyjson_mut_val *val) { + return yyjson_get_sint((const yyjson_val *)val); } -yyjson_api_inline int yyjson_mut_get_int(yyjson_mut_val *val) { - return yyjson_get_int((yyjson_val *)val); +yyjson_api_inline int yyjson_mut_get_int(const yyjson_mut_val *val) { + return yyjson_get_int((const yyjson_val *)val); } -yyjson_api_inline double yyjson_mut_get_real(yyjson_mut_val *val) { - return yyjson_get_real((yyjson_val *)val); +yyjson_api_inline double yyjson_mut_get_real(const yyjson_mut_val *val) { + return yyjson_get_real((const yyjson_val *)val); } -yyjson_api_inline double yyjson_mut_get_num(yyjson_mut_val *val) { - return yyjson_get_num((yyjson_val *)val); +yyjson_api_inline double yyjson_mut_get_num(const yyjson_mut_val *val) { + return yyjson_get_num((const yyjson_val *)val); } -yyjson_api_inline const char *yyjson_mut_get_str(yyjson_mut_val *val) { - return yyjson_get_str((yyjson_val *)val); +yyjson_api_inline const char *yyjson_mut_get_str(const yyjson_mut_val *val) { + return yyjson_get_str((const yyjson_val *)val); } -yyjson_api_inline size_t yyjson_mut_get_len(yyjson_mut_val *val) { - return yyjson_get_len((yyjson_val *)val); +yyjson_api_inline size_t yyjson_mut_get_len(const yyjson_mut_val *val) { + return yyjson_get_len((const yyjson_val *)val); } -yyjson_api_inline bool yyjson_mut_equals_str(yyjson_mut_val *val, +yyjson_api_inline bool yyjson_mut_equals_str(const yyjson_mut_val *val, const char *str) { - return yyjson_equals_str((yyjson_val *)val, str); + return yyjson_equals_str((const yyjson_val *)val, str); } -yyjson_api_inline bool yyjson_mut_equals_strn(yyjson_mut_val *val, +yyjson_api_inline bool yyjson_mut_equals_strn(const yyjson_mut_val *val, const char *str, size_t len) { - return yyjson_equals_strn((yyjson_val *)val, str, len); + return yyjson_equals_strn((const yyjson_val *)val, str, len); } -yyjson_api bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, - yyjson_mut_val *rhs); +yyjson_api bool unsafe_yyjson_mut_equals(const yyjson_mut_val *lhs, + const yyjson_mut_val *rhs); -yyjson_api_inline bool yyjson_mut_equals(yyjson_mut_val *lhs, - yyjson_mut_val *rhs) { +yyjson_api_inline bool yyjson_mut_equals(const yyjson_mut_val *lhs, + const yyjson_mut_val *rhs) { if (yyjson_unlikely(!lhs || !rhs)) return false; return unsafe_yyjson_mut_equals(lhs, rhs); } @@ -5877,9 +6105,9 @@ yyjson_api_inline bool yyjson_mut_set_sint(yyjson_mut_val *val, int64_t num) { return true; } -yyjson_api_inline bool yyjson_mut_set_int(yyjson_mut_val *val, int num) { +yyjson_api_inline bool yyjson_mut_set_int(yyjson_mut_val *val, int64_t num) { if (yyjson_unlikely(!val)) return false; - unsafe_yyjson_set_sint(val, (int64_t)num); + unsafe_yyjson_set_sint(val, num); return true; } @@ -6095,11 +6323,11 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_strncpy(yyjson_mut_doc *doc, * MARK: - Mutable JSON Array API (Implementation) *============================================================================*/ -yyjson_api_inline size_t yyjson_mut_arr_size(yyjson_mut_val *arr) { +yyjson_api_inline size_t yyjson_mut_arr_size(const yyjson_mut_val *arr) { return yyjson_mut_is_arr(arr) ? unsafe_yyjson_get_len(arr) : 0; } -yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(yyjson_mut_val *arr, +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(const yyjson_mut_val *arr, size_t idx) { if (yyjson_likely(idx < yyjson_mut_arr_size(arr))) { yyjson_mut_val *val = (yyjson_mut_val *)arr->uni.ptr; @@ -6110,7 +6338,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(yyjson_mut_val *arr, } yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_first( - yyjson_mut_val *arr) { + const yyjson_mut_val *arr) { if (yyjson_likely(yyjson_mut_arr_size(arr) > 0)) { return ((yyjson_mut_val *)arr->uni.ptr)->next; } @@ -6118,7 +6346,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_first( } yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last( - yyjson_mut_val *arr) { + const yyjson_mut_val *arr) { if (yyjson_likely(yyjson_mut_arr_size(arr) > 0)) { return ((yyjson_mut_val *)arr->uni.ptr); } @@ -6170,7 +6398,8 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_next( yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove( yyjson_mut_arr_iter *iter) { - if (yyjson_likely(iter && 0 < iter->idx && iter->idx <= iter->max)) { + if (yyjson_likely(iter && iter->pre && + 0 < iter->idx && iter->idx <= iter->max)) { yyjson_mut_val *prev = iter->pre; yyjson_mut_val *cur = iter->cur; yyjson_mut_val *next = cur->next; @@ -6180,6 +6409,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove( unsafe_yyjson_set_len(iter->arr, iter->max); prev->next = next; iter->cur = prev; + iter->pre = NULL; return cur; } return NULL; @@ -6539,7 +6769,7 @@ yyjson_api_inline bool yyjson_mut_arr_remove_range(yyjson_mut_val *arr, yyjson_mut_val *prev, *next; bool tail_removed; size_t len = unsafe_yyjson_get_len(arr); - if (yyjson_unlikely(_idx + _len > len)) return false; + if (yyjson_unlikely(_len > len || _idx > len - _len)) return false; if (yyjson_unlikely(_len == 0)) return true; unsafe_yyjson_set_len(arr, len - _len); if (yyjson_unlikely(len == _len)) return true; @@ -6747,16 +6977,16 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_obj(yyjson_mut_doc *doc, * MARK: - Mutable JSON Object API (Implementation) *============================================================================*/ -yyjson_api_inline size_t yyjson_mut_obj_size(yyjson_mut_val *obj) { +yyjson_api_inline size_t yyjson_mut_obj_size(const yyjson_mut_val *obj) { return yyjson_mut_is_obj(obj) ? unsafe_yyjson_get_len(obj) : 0; } -yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(yyjson_mut_val *obj, +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(const yyjson_mut_val *obj, const char *key) { return yyjson_mut_obj_getn(obj, key, key ? strlen(key) : 0); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(yyjson_mut_val *obj, +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(const yyjson_mut_val *obj, const char *_key, size_t key_len) { size_t len = yyjson_mut_obj_size(obj); @@ -6820,7 +7050,8 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get_val( yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_remove( yyjson_mut_obj_iter *iter) { - if (yyjson_likely(iter && 0 < iter->idx && iter->idx <= iter->max)) { + if (yyjson_likely(iter && iter->pre && + 0 < iter->idx && iter->idx <= iter->max)) { yyjson_mut_val *prev = iter->pre; yyjson_mut_val *cur = iter->cur; yyjson_mut_val *next = cur->next->next; @@ -6830,6 +7061,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_remove( unsafe_yyjson_set_len(iter->obj, iter->max); prev->next->next = next; iter->cur = prev; + iter->pre = NULL; return cur->next; } return NULL; @@ -6851,7 +7083,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_getn( cur = cur->next->next; if (unsafe_yyjson_equals_strn(cur, key, key_len)) { iter->idx += idx; - if (iter->idx > max) iter->idx -= max + 1; + if (iter->idx > max) iter->idx -= max; iter->pre = pre; iter->cur = cur; return cur->next; @@ -6882,7 +7114,9 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_str(yyjson_mut_doc *doc, const char **keys, const char **vals, size_t count) { - if (yyjson_likely(doc && ((count > 0 && keys && vals) || (count == 0)))) { + if (yyjson_likely(doc && ((count > 0 && count < + (~(size_t)0) / sizeof(yyjson_mut_val) / 2 && + keys && vals) || (count == 0)))) { yyjson_mut_val *obj = unsafe_yyjson_mut_val(doc, 1 + count * 2); if (yyjson_likely(obj)) { obj->tag = ((uint64_t)count << YYJSON_TAG_BIT) | YYJSON_TYPE_OBJ; @@ -6891,8 +7125,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_str(yyjson_mut_doc *doc, for (i = 0; i < count; i++) { yyjson_mut_val *key = obj + (i * 2 + 1); yyjson_mut_val *val = obj + (i * 2 + 2); - uint64_t key_len = (uint64_t)strlen(keys[i]); - uint64_t val_len = (uint64_t)strlen(vals[i]); + uint64_t key_len, val_len; + if (yyjson_unlikely(!keys[i] || !vals[i])) return NULL; + key_len = (uint64_t)strlen(keys[i]); + val_len = (uint64_t)strlen(vals[i]); key->tag = (key_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; val->tag = (val_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; key->uni.str = keys[i]; @@ -6912,7 +7148,9 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_str(yyjson_mut_doc *doc, yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc, const char **pairs, size_t count) { - if (yyjson_likely(doc && ((count > 0 && pairs) || (count == 0)))) { + if (yyjson_likely(doc && ((count > 0 && count < + (~(size_t)0) / sizeof(yyjson_mut_val) / 2 && + pairs) || (count == 0)))) { yyjson_mut_val *obj = unsafe_yyjson_mut_val(doc, 1 + count * 2); if (yyjson_likely(obj)) { obj->tag = ((uint64_t)count << YYJSON_TAG_BIT) | YYJSON_TYPE_OBJ; @@ -6923,8 +7161,10 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc, yyjson_mut_val *val = obj + (i * 2 + 2); const char *key_str = pairs[i * 2 + 0]; const char *val_str = pairs[i * 2 + 1]; - uint64_t key_len = (uint64_t)strlen(key_str); - uint64_t val_len = (uint64_t)strlen(val_str); + uint64_t key_len, val_len; + if (yyjson_unlikely(!key_str || !val_str)) return NULL; + key_len = (uint64_t)strlen(key_str); + val_len = (uint64_t)strlen(val_str); key->tag = (key_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; val->tag = (val_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; key->uni.str = key_str; @@ -7376,12 +7616,12 @@ yyjson_api_inline bool yyjson_mut_obj_rename_keyn(yyjson_mut_doc *doc, } while(false) /* require: val != NULL, *ptr == '/', len > 0 */ -yyjson_api yyjson_val *unsafe_yyjson_ptr_getx(yyjson_val *val, +yyjson_api yyjson_val *unsafe_yyjson_ptr_getx(const yyjson_val *val, const char *ptr, size_t len, yyjson_ptr_err *err); /* require: val != NULL, *ptr == '/', len > 0 */ -yyjson_api yyjson_mut_val *unsafe_yyjson_mut_ptr_getx(yyjson_mut_val *val, +yyjson_api yyjson_mut_val *unsafe_yyjson_mut_ptr_getx(const yyjson_mut_val *val, const char *ptr, size_t len, yyjson_ptr_ctx *ctx, @@ -7408,18 +7648,18 @@ yyjson_api yyjson_mut_val *unsafe_yyjson_mut_ptr_removex(yyjson_mut_val *val, yyjson_ptr_ctx *ctx, yyjson_ptr_err *err); -yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(yyjson_doc *doc, +yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(const yyjson_doc *doc, const char *ptr) { if (yyjson_unlikely(!ptr)) return NULL; return yyjson_doc_ptr_getn(doc, ptr, strlen(ptr)); } -yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(yyjson_doc *doc, +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(const yyjson_doc *doc, const char *ptr, size_t len) { return yyjson_doc_ptr_getx(doc, ptr, len, NULL); } -yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(yyjson_doc *doc, +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(const yyjson_doc *doc, const char *ptr, size_t len, yyjson_ptr_err *err) { yyjson_ptr_set_err(NONE, NULL); @@ -7441,18 +7681,18 @@ yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(yyjson_doc *doc, return unsafe_yyjson_ptr_getx(doc->root, ptr, len, err); } -yyjson_api_inline yyjson_val *yyjson_ptr_get(yyjson_val *val, +yyjson_api_inline yyjson_val *yyjson_ptr_get(const yyjson_val *val, const char *ptr) { if (yyjson_unlikely(!ptr)) return NULL; return yyjson_ptr_getn(val, ptr, strlen(ptr)); } -yyjson_api_inline yyjson_val *yyjson_ptr_getn(yyjson_val *val, +yyjson_api_inline yyjson_val *yyjson_ptr_getn(const yyjson_val *val, const char *ptr, size_t len) { return yyjson_ptr_getx(val, ptr, len, NULL); } -yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, +yyjson_api_inline yyjson_val *yyjson_ptr_getx(const yyjson_val *val, const char *ptr, size_t len, yyjson_ptr_err *err) { yyjson_ptr_set_err(NONE, NULL); @@ -7461,7 +7701,7 @@ yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, return NULL; } if (yyjson_unlikely(len == 0)) { - return val; + return yyjson_constcast(yyjson_val *)val; } if (yyjson_unlikely(*ptr != '/')) { yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); @@ -7470,23 +7710,20 @@ yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, return unsafe_yyjson_ptr_getx(val, ptr, len, err); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get(yyjson_mut_doc *doc, - const char *ptr) { +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get( + const yyjson_mut_doc *doc, const char *ptr) { if (!ptr) return NULL; return yyjson_mut_doc_ptr_getn(doc, ptr, strlen(ptr)); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn(yyjson_mut_doc *doc, - const char *ptr, - size_t len) { +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn( + const yyjson_mut_doc *doc, const char *ptr, size_t len) { return yyjson_mut_doc_ptr_getx(doc, ptr, len, NULL, NULL); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx(yyjson_mut_doc *doc, - const char *ptr, - size_t len, - yyjson_ptr_ctx *ctx, - yyjson_ptr_err *err) { +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx( + const yyjson_mut_doc *doc, const char *ptr, size_t len, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { yyjson_ptr_set_err(NONE, NULL); if (ctx) memset(ctx, 0, sizeof(*ctx)); @@ -7508,19 +7745,19 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx(yyjson_mut_doc *doc, return unsafe_yyjson_mut_ptr_getx(doc->root, ptr, len, ctx, err); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(yyjson_mut_val *val, +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(const yyjson_mut_val *val, const char *ptr) { if (!ptr) return NULL; return yyjson_mut_ptr_getn(val, ptr, strlen(ptr)); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(yyjson_mut_val *val, +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(const yyjson_mut_val *val, const char *ptr, size_t len) { return yyjson_mut_ptr_getx(val, ptr, len, NULL, NULL); } -yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(yyjson_mut_val *val, +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(const yyjson_mut_val *val, const char *ptr, size_t len, yyjson_ptr_ctx *ctx, @@ -7533,7 +7770,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(yyjson_mut_val *val, return NULL; } if (yyjson_unlikely(len == 0)) { - return val; + return yyjson_constcast(yyjson_mut_val *)val; } if (yyjson_unlikely(*ptr != '/')) { yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); @@ -8031,7 +8268,7 @@ yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx) { Returns true if value at `ptr` exists and is the correct type, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_bool( - yyjson_val *root, const char *ptr, bool *value) { + const yyjson_val *root, const char *ptr, bool *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); if (value && yyjson_is_bool(val)) { *value = unsafe_yyjson_get_bool(val); @@ -8046,7 +8283,7 @@ yyjson_api_inline bool yyjson_ptr_get_bool( that fits in `uint64_t`. Returns true if successful, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_uint( - yyjson_val *root, const char *ptr, uint64_t *value) { + const yyjson_val *root, const char *ptr, uint64_t *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); if (value && val) { uint64_t ret = val->uni.u64; @@ -8064,7 +8301,7 @@ yyjson_api_inline bool yyjson_ptr_get_uint( that fits in `int64_t`. Returns true if successful, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_sint( - yyjson_val *root, const char *ptr, int64_t *value) { + const yyjson_val *root, const char *ptr, int64_t *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); if (value && val) { int64_t ret = val->uni.i64; @@ -8082,7 +8319,7 @@ yyjson_api_inline bool yyjson_ptr_get_sint( Returns true if value at `ptr` exists and is the correct type, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_real( - yyjson_val *root, const char *ptr, double *value) { + const yyjson_val *root, const char *ptr, double *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); if (value && yyjson_is_real(val)) { *value = unsafe_yyjson_get_real(val); @@ -8098,7 +8335,7 @@ yyjson_api_inline bool yyjson_ptr_get_real( Returns true if value at `ptr` exists and is the correct type, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_num( - yyjson_val *root, const char *ptr, double *value) { + const yyjson_val *root, const char *ptr, double *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); if (value && yyjson_is_num(val)) { *value = unsafe_yyjson_get_num(val); @@ -8113,7 +8350,7 @@ yyjson_api_inline bool yyjson_ptr_get_num( Returns true if value at `ptr` exists and is the correct type, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_str( - yyjson_val *root, const char *ptr, const char **value) { + const yyjson_val *root, const char *ptr, const char **value) { yyjson_val *val = yyjson_ptr_get(root, ptr); if (value && yyjson_is_str(val)) { *value = unsafe_yyjson_get_str(val); @@ -8215,8 +8452,8 @@ yyjson_api_inline yyjson_mut_val *unsafe_yyjson_mut_get_pointer( #if defined(__clang__) # pragma clang diagnostic pop -#elif defined(__GNUC__) -# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#elif YYJSON_IS_REAL_GCC +# if yyjson_gcc_available(4, 6, 0) # pragma GCC diagnostic pop # endif #elif defined(_MSC_VER)