File-Raw-JSON
view release on metacpan or search on metacpan
#define U64(hi, lo) ((((u64)hi##UL) << 32U) + lo##UL)
/* Used to cast away (remove) const qualifier. */
#define constcast(type) (type)(void *)(size_t)(const void *)
/* flag test */
#define has_read_flag(_flag) unlikely(read_flag_eq(flg, YYJSON_READ_##_flag))
#define has_write_flag(_flag) unlikely(write_flag_eq(flg, YYJSON_WRITE_##_flag))
static_inline bool read_flag_eq(yyjson_read_flag flg, yyjson_read_flag chk) {
#if YYJSON_DISABLE_NON_STANDARD
if (chk == YYJSON_READ_ALLOW_INF_AND_NAN ||
chk == YYJSON_READ_ALLOW_COMMENTS ||
chk == YYJSON_READ_ALLOW_TRAILING_COMMAS ||
chk == YYJSON_READ_ALLOW_INVALID_UNICODE)
return false; /* this should be evaluated at compile-time */
#endif
return (flg & chk) != 0;
}
static_inline bool write_flag_eq(yyjson_write_flag flg, yyjson_write_flag chk) {
#if YYJSON_DISABLE_NON_STANDARD
if (chk == YYJSON_WRITE_ALLOW_INF_AND_NAN ||
chk == YYJSON_WRITE_ALLOW_INVALID_UNICODE)
return false; /* this should be evaluated at compile-time */
#endif
return (flg & chk) != 0;
}
/*==============================================================================
* Integer Constants
*============================================================================*/
/* U64 constant values */
#undef U64_MAX
#define U64_MAX U64(0xFFFFFFFF, 0xFFFFFFFF)
#undef I64_MAX
#define I64_MAX U64(0x7FFFFFFF, 0xFFFFFFFF)
#undef USIZE_MAX
#define USIZE_MAX ((usize)(~(usize)0))
/* Maximum number of digits for reading u32/u64/usize safety (not overflow). */
#undef U32_SAFE_DIG
#define U32_SAFE_DIG 9 /* u32 max is 4294967295, 10 digits */
#undef U64_SAFE_DIG
#define U64_SAFE_DIG 19 /* u64 max is 18446744073709551615, 20 digits */
#undef USIZE_SAFE_DIG
#define USIZE_SAFE_DIG (sizeof(usize) == 8 ? U64_SAFE_DIG : U32_SAFE_DIG)
/*==============================================================================
* IEEE-754 Double Number Constants
*============================================================================*/
/* Inf raw value (positive) */
#define F64_RAW_INF U64(0x7FF00000, 0x00000000)
/* NaN raw value (quiet NaN, no payload, no sign) */
#if defined(__hppa__) || (defined(__mips__) && !defined(__mips_nan2008))
#define F64_RAW_NAN U64(0x7FF7FFFF, 0xFFFFFFFF)
#else
#define F64_RAW_NAN U64(0x7FF80000, 0x00000000)
#endif
/* double number bits */
#define F64_BITS 64
/* double number exponent part bits */
#define F64_EXP_BITS 11
/* double number significand part bits */
#define F64_SIG_BITS 52
/* double number significand part bits (with 1 hidden bit) */
#define F64_SIG_FULL_BITS 53
/* double number significand bit mask */
#define F64_SIG_MASK U64(0x000FFFFF, 0xFFFFFFFF)
/* double number exponent bit mask */
#define F64_EXP_MASK U64(0x7FF00000, 0x00000000)
/* double number exponent bias */
#define F64_EXP_BIAS 1023
/* double number significant digits count in decimal */
#define F64_DEC_DIG 17
/* max significant digits count in decimal when reading double number */
#define F64_MAX_DEC_DIG 768
/* maximum decimal power of double number (1.7976931348623157e308) */
#define F64_MAX_DEC_EXP 308
/* minimum decimal power of double number (4.9406564584124654e-324) */
#define F64_MIN_DEC_EXP (-324)
/* maximum binary power of double number */
#define F64_MAX_BIN_EXP 1024
/* minimum binary power of double number */
#define F64_MIN_BIN_EXP (-1021)
/*==============================================================================
* Types
*============================================================================*/
/** Type define for primitive types. */
typedef float f32;
typedef double f64;
typedef int8_t i8;
typedef uint8_t u8;
typedef int16_t i16;
typedef uint16_t u16;
typedef int32_t i32;
typedef uint32_t u32;
memcpy(&tmp, src, 4);
memcpy(dst, &tmp, 4);
}
static_inline void byte_move_8(void *dst, const void *src) {
u64 tmp;
memcpy(&tmp, src, 8);
memcpy(dst, &tmp, 8);
}
static_inline void byte_move_16(void *dst, const void *src) {
char *pdst = (char *)dst;
const char *psrc = (const char *)src;
u64 tmp1, tmp2;
memcpy(&tmp1, psrc, 8);
memcpy(&tmp2, psrc + 8, 8);
memcpy(pdst, &tmp1, 8);
memcpy(pdst + 8, &tmp2, 8);
}
static_inline bool byte_match_2(void *buf, const char *pat) {
v16_uni u1, u2;
memcpy(&u1, buf, 2);
memcpy(&u2, pat, 2);
return u1.u == u2.u;
}
static_inline bool byte_match_4(void *buf, const char *pat) {
v32_uni u1, u2;
memcpy(&u1, buf, 4);
memcpy(&u2, pat, 4);
return u1.u == u2.u;
}
static_inline u16 byte_load_2(const void *src) {
v16_uni uni;
memcpy(&uni, src, 2);
return uni.u;
}
static_inline u32 byte_load_3(const void *src) {
v32_uni uni;
memcpy(&uni, src, 2);
uni.v.c[2] = ((const char *)src)[2];
uni.v.c[3] = 0;
return uni.u;
}
static_inline u32 byte_load_4(const void *src) {
v32_uni uni;
memcpy(&uni, src, 4);
return uni.u;
}
#endif
/*==============================================================================
* Number Utils
* These functions are used to detect and convert NaN and Inf numbers.
*============================================================================*/
/** Convert raw binary to double. */
static_inline f64 f64_from_raw(u64 u) {
/* use memcpy to avoid violating the strict aliasing rule */
f64 f;
memcpy(&f, &u, 8);
return f;
}
/** Convert double to raw binary. */
static_inline u64 f64_to_raw(f64 f) {
/* use memcpy to avoid violating the strict aliasing rule */
u64 u;
memcpy(&u, &f, 8);
return u;
}
/** Get raw 'infinity' with sign. */
static_inline u64 f64_raw_get_inf(bool sign) {
#if YYJSON_HAS_IEEE_754
return F64_RAW_INF | ((u64)sign << 63);
#elif defined(INFINITY)
return f64_to_raw(sign ? -INFINITY : INFINITY);
#else
return f64_to_raw(sign ? -HUGE_VAL : HUGE_VAL);
#endif
}
/** Get raw 'nan' with sign. */
static_inline u64 f64_raw_get_nan(bool sign) {
#if YYJSON_HAS_IEEE_754
return F64_RAW_NAN | ((u64)sign << 63);
#elif defined(NAN)
return f64_to_raw(sign ? (f64)-NAN : (f64)NAN);
#else
return f64_to_raw((sign ? -0.0 : 0.0) / 0.0);
#endif
}
/**
Convert normalized u64 (highest bit is 1) to f64.
Some compiler (such as Microsoft Visual C++ 6.0) do not support converting
number from u64 to f64. This function will first convert u64 to i64 and then
to f64, with `to nearest` rounding mode.
*/
static_inline f64 normalized_u64_to_f64(u64 val) {
#if YYJSON_U64_TO_F64_NO_IMPL
i64 sig = (i64)((val >> 1) | (val & 1));
return ((f64)sig) * (f64)2.0;
#else
return (f64)val;
#endif
}
/*==============================================================================
* Size Utils
return false;
}
/** Read 'false' literal, '*cur' should be 'f'. */
static_inline bool read_false(u8 **ptr, yyjson_val *val) {
u8 *cur = *ptr;
u8 **end = ptr;
if (likely(byte_match_4(cur + 1, "alse"))) {
val->tag = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_FALSE;
*end = cur + 5;
return true;
}
return false;
}
/** Read 'null' literal, '*cur' should be 'n'. */
static_inline bool read_null(u8 **ptr, yyjson_val *val) {
u8 *cur = *ptr;
u8 **end = ptr;
if (likely(byte_match_4(cur, "null"))) {
val->tag = YYJSON_TYPE_NULL;
*end = cur + 4;
return true;
}
return false;
}
/** Read 'Inf' or 'Infinity' literal (ignoring case). */
static_inline bool read_inf(bool sign, u8 **ptr, u8 **pre, yyjson_val *val) {
u8 *hdr = *ptr - sign;
u8 *cur = *ptr;
u8 **end = ptr;
if ((cur[0] == 'I' || cur[0] == 'i') &&
(cur[1] == 'N' || cur[1] == 'n') &&
(cur[2] == 'F' || cur[2] == 'f')) {
if ((cur[3] == 'I' || cur[3] == 'i') &&
(cur[4] == 'N' || cur[4] == 'n') &&
(cur[5] == 'I' || cur[5] == 'i') &&
(cur[6] == 'T' || cur[6] == 't') &&
(cur[7] == 'Y' || cur[7] == 'y')) {
cur += 8;
} else {
cur += 3;
}
*end = cur;
if (pre) {
/* add null-terminator for previous raw string */
if (*pre) **pre = '\0';
*pre = cur;
val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW;
val->uni.str = (const char *)hdr;
} else {
val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL;
val->uni.u64 = f64_raw_get_inf(sign);
}
return true;
}
return false;
}
/** Read 'NaN' literal (ignoring case). */
static_inline bool read_nan(bool sign, u8 **ptr, u8 **pre, yyjson_val *val) {
u8 *hdr = *ptr - sign;
u8 *cur = *ptr;
u8 **end = ptr;
if ((cur[0] == 'N' || cur[0] == 'n') &&
(cur[1] == 'A' || cur[1] == 'a') &&
(cur[2] == 'N' || cur[2] == 'n')) {
cur += 3;
*end = cur;
if (pre) {
/* add null-terminator for previous raw string */
if (*pre) **pre = '\0';
*pre = cur;
val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW;
val->uni.str = (const char *)hdr;
} else {
val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL;
val->uni.u64 = f64_raw_get_nan(sign);
}
return true;
}
return false;
}
/** Read 'Inf', 'Infinity' or 'NaN' literal (ignoring case). */
static_inline bool read_inf_or_nan(bool sign, u8 **ptr, u8 **pre,
yyjson_val *val) {
if (read_inf(sign, ptr, pre, val)) return true;
if (read_nan(sign, ptr, pre, val)) return true;
return false;
}
/** Read a JSON number as raw string. */
static_noinline bool read_number_raw(u8 **ptr,
u8 **pre,
yyjson_read_flag flg,
yyjson_val *val,
const char **msg) {
#define return_err(_pos, _msg) do { \
*msg = _msg; \
*end = _pos; \
return false; \
} while (false)
#define return_raw() do { \
val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; \
val->uni.str = (const char *)hdr; \
*pre = cur; *end = cur; return true; \
} while (false)
u8 *hdr = *ptr;
u8 *cur = *ptr;
u8 **end = ptr;
/* add null-terminator for previous raw string */
if (*pre) **pre = '\0';
/* skip sign */
cur += (*cur == '-');
/* read first digit, check leading zero */
if (unlikely(!digi_is_digit(*cur))) {
if (has_read_flag(ALLOW_INF_AND_NAN)) {
if (read_inf_or_nan(*hdr == '-', &cur, pre, val)) return_raw();
}
return_err(cur, "no digit after minus sign");
}
/* read integral part */
if (*cur == '0') {
cur++;
if (unlikely(digi_is_digit(*cur))) {
return_err(cur - 1, "number with leading zero is not allowed");
}
if (!digi_is_fp(*cur)) return_raw();
} else {
while (digi_is_digit(*cur)) cur++;
if (!digi_is_fp(*cur)) return_raw();
}
/* read fraction part */
if (*cur == '.') {
cur++;
if (!digi_is_digit(*cur++)) {
lower = vbl + !is_even;
upper = vbr - !is_even;
s = vb / 4;
if (s >= 10) {
sp = s / 10;
u_inside = (lower <= 40 * sp);
w_inside = (upper >= 40 * sp + 40);
if (u_inside != w_inside) {
*sig_dec = sp + w_inside;
*exp_dec = k + 1;
return;
}
}
u_inside = (lower <= 4 * s);
w_inside = (upper >= 4 * s + 4);
mid = 4 * s + 2;
round_up = (vb > mid) || (vb == mid && (s & 1) != 0);
*sig_dec = s + ((u_inside != w_inside) ? w_inside : round_up);
*exp_dec = k;
}
/**
Write a double number (requires 32 bytes buffer).
We follows the ECMAScript specification to print floating point numbers,
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.
3. Remove positive sign of exponent part.
*/
static_inline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) {
u64 sig_bin, sig_dec, sig_raw;
i32 exp_bin, exp_dec, sig_len, dot_pos, i, max;
u32 exp_raw, hi, lo;
u8 *hdr, *num_hdr, *num_end, *dot_end;
bool sign;
/* decode raw bytes from IEEE-754 double format. */
sign = (bool)(raw >> (F64_BITS - 1));
sig_raw = raw & F64_SIG_MASK;
exp_raw = (u32)((raw & F64_EXP_MASK) >> F64_SIG_BITS);
/* return inf and nan */
if (unlikely(exp_raw == ((u32)1 << F64_EXP_BITS) - 1)) {
if (has_write_flag(INF_AND_NAN_AS_NULL)) {
byte_copy_4(buf, "null");
return buf + 4;
}
else if (has_write_flag(ALLOW_INF_AND_NAN)) {
if (sig_raw == 0) {
buf[0] = '-';
buf += sign;
byte_copy_8(buf, "Infinity");
buf += 8;
return buf;
} else {
byte_copy_4(buf, "NaN");
return buf + 3;
}
}
return NULL;
}
/* add sign for all finite double value, including 0.0 and inf */
buf[0] = '-';
buf += sign;
hdr = buf;
/* return zero */
if ((raw << 1) == 0) {
byte_copy_4(buf, "0.0");
buf += 3;
return buf;
}
if (likely(exp_raw != 0)) {
/* normal number */
sig_bin = sig_raw | ((u64)1 << F64_SIG_BITS);
exp_bin = (i32)exp_raw - F64_EXP_BIAS - F64_SIG_BITS;
/* fast path for small integer number without fraction */
if (-F64_SIG_BITS <= exp_bin && exp_bin <= 0) {
if (u64_tz_bits(sig_bin) >= (u32)-exp_bin) {
/* number is integer in range 1 to 0x1FFFFFFFFFFFFF */
sig_dec = sig_bin >> -exp_bin;
buf = write_u64_len_1_to_16(sig_dec, buf);
byte_copy_2(buf, ".0");
buf += 2;
return buf;
}
}
/* binary to decimal */
f64_bin_to_dec(sig_raw, exp_raw, sig_bin, exp_bin, &sig_dec, &exp_dec);
/* the sig length is 15 to 17 */
sig_len = 17;
sig_len -= (sig_dec < (u64)100000000 * 100000000);
sig_len -= (sig_dec < (u64)100000000 * 10000000);
/* the decimal point position relative to the first digit */
dot_pos = sig_len + exp_dec;
if (-6 < dot_pos && dot_pos <= 21) {
/* no need to write exponent part */
if (dot_pos <= 0) {
/* dot before first digit */
/* such as 0.1234, 0.000001234 */
num_hdr = hdr + (2 - dot_pos);
num_end = write_u64_len_15_to_17_trim(num_hdr, sig_dec);
hdr[0] = '0';
hdr[1] = '.';
hdr += 2;
max = -dot_pos;
for (i = 0; i < max; i++) hdr[i] = '0';
return num_end;
} else {
buf++;
/* write exponent part */
buf[0] = '-';
buf++;
exp_dec = -exp_dec;
hi = ((u32)exp_dec * 656) >> 16; /* exp / 100 */
lo = (u32)exp_dec - hi * 100; /* exp % 100 */
buf[0] = (u8)((u8)hi + (u8)'0');
byte_copy_2(buf + 1, digit_table + lo * 2);
buf += 3;
return buf;
}
}
#else /* FP_WRITER */
/** Write a double number (requires 32 bytes buffer). */
static_inline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) {
/*
For IEEE 754, `DBL_DECIMAL_DIG` is 17 for round-trip.
For non-IEEE formats, 17 is used to avoid buffer overflow,
round-trip is not guaranteed.
*/
#if defined(DBL_DECIMAL_DIG) && DBL_DECIMAL_DIG != 17
int dig = DBL_DECIMAL_DIG > 17 ? 17 : DBL_DECIMAL_DIG;
#else
int dig = 17;
#endif
/*
The snprintf() function is locale-dependent. For currently known locales,
(en, zh, ja, ko, am, he, hi) use '.' as the decimal point, while other
locales use ',' as the decimal point. we need to replace ',' with '.'
to avoid the locale setting.
*/
f64 val = f64_from_raw(raw);
#if YYJSON_MSC_VER >= 1400
int len = sprintf_s((char *)buf, 32, "%.*g", dig, val);
#elif defined(snprintf) || (YYJSON_STDC_VER >= 199901L)
int len = snprintf((char *)buf, 32, "%.*g", dig, val);
#else
int len = sprintf((char *)buf, "%.*g", dig, val);
#endif
u8 *cur = buf;
if (unlikely(len < 1)) return NULL;
cur += (*cur == '-');
if (unlikely(!digi_is_digit(*cur))) {
/* nan, inf, or bad output */
if (has_write_flag(INF_AND_NAN_AS_NULL)) {
byte_copy_4(buf, "null");
return buf + 4;
}
else if (has_write_flag(ALLOW_INF_AND_NAN)) {
if (*cur == 'i') {
byte_copy_8(cur, "Infinity");
cur += 8;
return cur;
} else if (*cur == 'n') {
byte_copy_4(buf, "NaN");
return buf + 3;
}
}
return NULL;
} else {
/* finite number */
int i = 0;
bool fp = false;
for (; i < len; i++) {
if (buf[i] == ',') buf[i] = '.';
if (digi_is_fp((u8)buf[i])) fp = true;
}
if (!fp) {
buf[len++] = '.';
buf[len++] = '0';
}
}
return buf + len;
}
#endif /* FP_WRITER */
/** Write a JSON number (requires 32 bytes buffer). */
static_inline u8 *write_number(u8 *cur, yyjson_val *val,
yyjson_write_flag flg) {
if (val->tag & YYJSON_SUBTYPE_REAL) {
u64 raw = val->uni.u64;
return write_f64_raw(cur, raw, flg);
} else {
u64 pos = val->uni.u64;
u64 neg = ~pos + 1;
usize sgn = ((val->tag & YYJSON_SUBTYPE_SINT) > 0) & ((i64)pos < 0);
*cur = '-';
return write_u64(sgn ? neg : pos, cur + sgn);
}
}
/*==============================================================================
* String Writer
*============================================================================*/
/** Character encode type, if (type > CHAR_ENC_ERR_1) bytes = type / 2; */
typedef u8 char_enc_type;
#define CHAR_ENC_CPY_1 0 /* 1-byte UTF-8, copy. */
#define CHAR_ENC_ERR_1 1 /* 1-byte UTF-8, error. */
#define CHAR_ENC_ESC_A 2 /* 1-byte ASCII, escaped as '\x'. */
#define CHAR_ENC_ESC_1 3 /* 1-byte UTF-8, escaped as '\uXXXX'. */
#define CHAR_ENC_CPY_2 4 /* 2-byte UTF-8, copy. */
#define CHAR_ENC_ESC_2 5 /* 2-byte UTF-8, escaped as '\uXXXX'. */
#define CHAR_ENC_CPY_3 6 /* 3-byte UTF-8, copy. */
#define CHAR_ENC_ESC_3 7 /* 3-byte UTF-8, escaped as '\uXXXX'. */
#define CHAR_ENC_CPY_4 8 /* 4-byte UTF-8, copy. */
#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) */
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,
( run in 1.433 second using v1.01-cache-2.11-cpan-9581c071862 )