20 : _buf(buf), _cap(cap), _len(0), _ok(buf != nullptr && cap >= 1), _after_key(false), _depth(0)
28void JsonWriter::put(
char c)
43void JsonWriter::put_raw(
const char *s)
55void JsonWriter::put_escaped(
const char *s)
63 unsigned char c = (
unsigned char)*
s;
98 static const char hexd[] =
"0123456789abcdef";
103 put(
hexd[(c >> 4) & 0x0f]);
115void JsonWriter::value_prefix()
125 if (_need_comma[
lvl])
129 _need_comma[
lvl] =
true;
133void JsonWriter::push(
char open)
139 _need_comma[_depth] =
false;
148void JsonWriter::pop(
char close)
225 put_raw(v ?
"true" :
"false");
275static bool is_ws(
char c)
277 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
280static const char *skip_ws(
const char *p)
282 while (*p && is_ws(*p))
291static const char *skip_string(
const char *p)
296 if (*p ==
'\\' && p[1])
312static const char *skip_value(
const char *p)
316 return skip_string(p);
318 if (*p ==
'{' || *p ==
'[')
321 char close = (open ==
'{') ?
'}' :
']';
334 else if (*p == close)
347 while (*p && *p !=
',' && *p !=
'}' && *p !=
']' && !is_ws(*p))
356static const char *json_find_value(
const char *json,
const char *key)
362 const char *p = skip_ws(json);
369#define PC_key_max 256
374 if (*p ==
'}' || *p ==
'\0')
383 const char *kstart = p + 1;
384 const char *kend = skip_string(p);
385 size_t klen = (kend > kstart) ? (
size_t)((kend - 1) - kstart) : 0;
386 bool match = (klen == keylen) && (strncmp(kstart, key, klen) == 0);
411static int hex_val(
char c)
413 if (c >=
'0' && c <=
'9')
417 if (c >=
'a' && c <=
'f')
421 if (c >=
'A' && c <=
'F')
440static int json_hex4(
const char *src)
442 int h0 = src[0] ? hex_val(src[0]) : -1;
443 int h1 = (h0 >= 0 && src[1]) ? hex_val(src[1]) : -1;
444 int h2 = (h1 >= 0 && src[2]) ? hex_val(src[2]) : -1;
445 int h3 = (h2 >= 0 && src[3]) ? hex_val(src[3]) : -1;
450 return (h0 << 12) | (h1 << 8) | (h2 << 4) | h3;
453static long json_decode_u(
const char *&p)
455 int v = json_hex4(p + 1);
460 unsigned cp = (unsigned)v;
462 if (cp >= 0xD800 && cp <= 0xDBFF)
465 int lo = (p[1] ==
'\\' && p[2] ==
'u') ? json_hex4(p + 3) : -1;
466 if (lo >= 0xDC00 && lo <= 0xDFFF)
468 cp = 0x10000u + ((cp - 0xD800u) << 10) + ((unsigned)lo - 0xDC00u);
476 else if (cp >= 0xDC00 && cp <= 0xDFFF)
485static JsonEsc json_emit_utf8(
unsigned cp,
char *out,
size_t &i,
size_t out_cap)
491 u8[0] = (
unsigned char)cp;
494 else if (cp < 0x800u)
496 u8[0] = (
unsigned char)(0xC0u | (cp >> 6));
497 u8[1] = (
unsigned char)(0x80u | (cp & 0x3Fu));
500 else if (cp < 0x10000u)
502 u8[0] = (
unsigned char)(0xE0u | (cp >> 12));
503 u8[1] = (
unsigned char)(0x80u | ((cp >> 6) & 0x3Fu));
504 u8[2] = (
unsigned char)(0x80u | (cp & 0x3Fu));
509 u8[0] = (
unsigned char)(0xF0u | (cp >> 18));
510 u8[1] = (
unsigned char)(0x80u | ((cp >> 12) & 0x3Fu));
511 u8[2] = (
unsigned char)(0x80u | ((cp >> 6) & 0x3Fu));
512 u8[3] = (
unsigned char)(0x80u | (cp & 0x3Fu));
515 if (i + (
size_t)un >= out_cap)
520 for (
int k = 0; k < un; k++)
522 out[i++] = (char)u8[k];
531static JsonEsc json_decode_escape(
const char *&p,
char *out,
size_t &i,
size_t out_cap,
char *c_out)
568 long cp = json_decode_u(p);
574 return json_emit_utf8((
unsigned)cp, out, i, out_cap);
577bool json_get_str(
const char *json,
const char *key,
char *out,
size_t out_cap)
579 if (!out || out_cap == 0)
583 const char *v = json_find_value(json, key);
589 const char *p = v + 1;
591 while (*p && *p !=
'"')
594 if (c ==
'\\' && p[1])
597 JsonEsc r = json_decode_escape(p, out, i, out_cap, &c);
629 const char *v = json_find_value(json, key);
634 const char *end =
nullptr;
650 const char *v = json_find_value(json, key);
655 if (strncmp(v,
"true", 4) == 0 && (v[4] ==
'\0' || v[4] ==
',' || v[4] ==
'}' || v[4] ==
']' || is_ws(v[4])))
660 if (strncmp(v,
"false", 5) == 0 && (v[5] ==
'\0' || v[5] ==
',' || v[5] ==
'}' || v[5] ==
']' || is_ws(v[5])))
void str(const char *v)
Emit a quoted, escaped string value.
void begin_object()
Open { (as a value/element where applicable).
void key(const char *k)
Emit an object member name ("k":); follow with one value.
void raw(const char *literal)
Emit a pre-formatted literal verbatim.
JsonWriter(char *buf, size_t cap)
Construct over a caller buffer.
void kv_bool(const char *k, bool v)
"k":true|false.
void kv_str(const char *k, const char *v)
"k":"v" (escaped).
void boolean(bool v)
Emit true/false.
void integer(long v)
Emit a signed integer value.
void kv_uint(const char *k, unsigned long v)
"k":<uint>.
void kv_int(const char *k, long v)
"k":<int>.
void uinteger(unsigned long v)
Emit an unsigned integer value.
void end_object()
Close }.
void kv_raw(const char *k, const char *literal)
"k":<literal>.
void null_value()
Emit null.
void kv_null(const char *k)
"k":null.
void begin_array()
Open [.
bool json_get_str(const char *json, const char *key, char *out, size_t out_cap)
Read a top-level string member from a JSON object body.
bool json_get_bool(const char *json, const char *key, bool *out)
Read a top-level boolean member (true/false) from a JSON object body.
bool json_get_int(const char *json, const char *key, long *out)
Read a top-level integer member from a JSON object body.
Layer 6 (Presentation) - zero-heap JSON: a bounded writer and top-level reader.
Tiny no-stdlib base-10 number parsers (strtol/strtoul/strtof replacements).
long pc_strtol(const char *s, const char **end)
Parse a base-10 long; sets end past the digits (or to s if none).
#define JSON_MAX_DEPTH
Maximum object/array nesting depth for the JsonWriter (see json.h).
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
void pc_sb_i64(pc_sb *b, int64_t v)
Append v as signed decimal (64-bit), with a leading '-' when negative.
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
Bump-append target; ok latches false once an append would overflow cap.