cMCP 0.4.1
Model Context Protocol library in pure C11
Loading...
Searching...
No Matches
cmcp_json.h
Go to the documentation of this file.
1/**
2 * @file cmcp_json.h
3 * @brief Hand-rolled JSON value tree, parser, and emitter.
4 *
5 * Library-wide JSON representation. Every JSON-RPC message that
6 * crosses a transport boundary is parsed into / built from a tree of
7 * `cmcp_json_t` nodes. The parser is strict (RFC 8259 subset — no
8 * trailing commas, no comments, no NaN/Inf) and the emitter has a
9 * `_stable` variant that orders object keys deterministically for
10 * test fixtures and replay gates.
11 *
12 * Memory ownership: every `cmcp_json_new_*` returns a freshly
13 * allocated node the caller owns. `cmcp_json_array_append` and
14 * `cmcp_json_object_set` **take ownership** of the value pointer
15 * (the container will free it). `cmcp_json_free` recursively frees
16 * a node and everything it contains.
17 */
18#ifndef CMCP_JSON_H
19#define CMCP_JSON_H
20
21#include <stddef.h>
22
23/** @brief Tag of a JSON value tree node. */
24typedef enum {
25 CMCP_JSON_NULL, /**< `null` */
26 CMCP_JSON_BOOL, /**< `true` / `false` */
27 CMCP_JSON_INT, /**< Integer that fit in `long long`. */
28 CMCP_JSON_DOUBLE, /**< Fractional or out-of-`int64` numeric. */
29 CMCP_JSON_STRING, /**< UTF-8 string (length-prefixed; embedded NULs allowed). */
30 CMCP_JSON_ARRAY, /**< Ordered list of values. */
31 CMCP_JSON_OBJECT, /**< Key/value bag (insertion order preserved). */
33
34/** @brief Opaque-by-convention JSON tree node. Treat as opaque except
35 * via the accessor functions below; struct layout is exposed for
36 * efficient construction in hot paths only.
37 *
38 * @warning Host code MUST go through the typed accessors:
39 * - cmcp_json_string() / cmcp_json_string_len()
40 * - cmcp_json_int() / cmcp_json_double()
41 * - cmcp_json_bool()
42 * - cmcp_json_array_at() / cmcp_json_array_len()
43 * - cmcp_json_object_get() / cmcp_json_object_len()
44 * Do not read the `union` members (`b`, `i`, `d`, `str.s`,
45 * `arr.items`, `obj.keys`, ...) directly from outside the
46 * library. The struct layout is published only so the
47 * library's own hot paths can stack-build and pun cheaply;
48 * it is NOT part of the SemVer-stable public surface and
49 * may grow new tags, reorder fields, or change union shape
50 * in a minor release. The accessors are. Code that pokes
51 * the union will silently break on those bumps; code that
52 * uses the accessors will not. */
53typedef struct cmcp_json cmcp_json_t;
54
55struct cmcp_json {
57 union {
58 int b;
59 long long i;
60 double d;
61 struct {
62 char *s;
63 size_t len;
64 } str;
65 struct {
66 cmcp_json_t **items;
67 size_t len;
68 size_t cap;
69 } arr;
70 struct {
71 char **keys;
72 size_t *key_lens;
73 cmcp_json_t **values;
74 size_t len;
75 size_t cap;
76 } obj;
77 };
78};
79
80/** @name Constructors
81 * Allocate a fresh node. Caller owns the returned pointer. Strings
82 * are copied in. NULL on allocation failure.
83 * @{ */
84cmcp_json_t *cmcp_json_new_null(void);
85cmcp_json_t *cmcp_json_new_bool(int b);
86cmcp_json_t *cmcp_json_new_int(long long i);
87cmcp_json_t *cmcp_json_new_double(double d);
88cmcp_json_t *cmcp_json_new_string(const char *s);
89cmcp_json_t *cmcp_json_new_string_n(const char *s, size_t n);
90cmcp_json_t *cmcp_json_new_array(void);
91cmcp_json_t *cmcp_json_new_object(void);
92/** @} */
93
94/** @name Mutators (take ownership of `v`)
95 * Append / insert. The container takes ownership of the value
96 * pointer — do not free `v` after a successful call. Return
97 * `CMCP_OK` or a negative error code.
98 * @{ */
99int cmcp_json_array_append(cmcp_json_t *arr, cmcp_json_t *v);
100int cmcp_json_object_set(cmcp_json_t *obj, const char *key, cmcp_json_t *v);
101int cmcp_json_object_set_n(cmcp_json_t *obj, const char *key, size_t key_len,
102 cmcp_json_t *v);
103/** @} */
104
105/** @name Accessors (borrowed views; never free) */
106/** @{ */
107const cmcp_json_t *cmcp_json_object_get(const cmcp_json_t *obj, const char *key);
108const cmcp_json_t *cmcp_json_array_at(const cmcp_json_t *arr, size_t i);
109size_t cmcp_json_array_len(const cmcp_json_t *arr);
110size_t cmcp_json_object_len(const cmcp_json_t *obj);
111
112const char *cmcp_json_string(const cmcp_json_t *v);
113size_t cmcp_json_string_len(const cmcp_json_t *v);
114long long cmcp_json_int(const cmcp_json_t *v);
115double cmcp_json_double(const cmcp_json_t *v);
116int cmcp_json_bool(const cmcp_json_t *v);
117int cmcp_json_is_null(const cmcp_json_t *v);
118/** @} */
119
120/** @name Parse / emit
121 * `_stable` orders object keys lexicographically — use it for
122 * golden-file fixtures and replay gates. The default emitter
123 * preserves insertion order, which is cheaper.
124 * @{ */
125cmcp_json_t *cmcp_json_parse(const char *text, size_t len);
126cmcp_json_t *cmcp_json_parse_cstr(const char *text);
127
128char *cmcp_json_emit(const cmcp_json_t *v);
129char *cmcp_json_emit_stable(const cmcp_json_t *v);
130/** @} */
131
132/** @name Tree lifecycle */
133/** @{ */
134void cmcp_json_free(cmcp_json_t *v);
135cmcp_json_t *cmcp_json_clone(const cmcp_json_t *v);
136int cmcp_json_equal(const cmcp_json_t *a, const cmcp_json_t *b);
137/** @} */
138
139/** @name String escaping helpers (rarely needed by callers) */
140/** @{ */
141int cmcp_json_escape(const char *in, char *out, size_t out_sz);
142char *cmcp_json_escape_dup(const char *in);
143/** @} */
144
145/**
146 * @brief In-place scrub of credential-shaped values (Tier 6 axis 6.5.4).
147 *
148 * Recursively walks `v`. For any object entry whose **key** matches a
149 * sensitive name (`password`, `passwd`, `token`, `secret`, `apikey`,
150 * `authorization`, `bearer`, `credential` — matched case-insensitively
151 * against the key with non-alphanumeric characters stripped, so
152 * `api_key`, `API-Key`, `apiKey` all hit), the value is replaced with
153 * the string `"[REDACTED]"` regardless of its original type. Other
154 * entries are unchanged and recursed into.
155 *
156 * The match is substring on the normalized key: `myApiKey` and
157 * `customer_secret` both redact.
158 *
159 * Caller retains ownership of `v`. Safe on `NULL` or scalar values
160 * (no-op). Allocation failure during replacement leaves the original
161 * value in place (best-effort, never aborts).
162 */
163void cmcp_json_redact(cmcp_json_t *v);
164
165#endif
cmcp_json_t * cmcp_json_new_null(void)
cmcp_json_type_t
Tag of a JSON value tree node.
Definition cmcp_json.h:24
@ CMCP_JSON_DOUBLE
Fractional or out-of-int64 numeric.
Definition cmcp_json.h:28
@ CMCP_JSON_NULL
null
Definition cmcp_json.h:25
@ CMCP_JSON_ARRAY
Ordered list of values.
Definition cmcp_json.h:30
@ CMCP_JSON_OBJECT
Key/value bag (insertion order preserved).
Definition cmcp_json.h:31
@ CMCP_JSON_INT
Integer that fit in long long.
Definition cmcp_json.h:27
@ CMCP_JSON_BOOL
true / false
Definition cmcp_json.h:26
@ CMCP_JSON_STRING
UTF-8 string (length-prefixed; embedded NULs allowed).
Definition cmcp_json.h:29
cmcp_json_t * cmcp_json_parse_cstr(const char *text)
char * cmcp_json_emit_stable(const cmcp_json_t *v)
const cmcp_json_t * cmcp_json_array_at(const cmcp_json_t *arr, size_t i)
int cmcp_json_escape(const char *in, char *out, size_t out_sz)
int cmcp_json_equal(const cmcp_json_t *a, const cmcp_json_t *b)
int cmcp_json_object_set_n(cmcp_json_t *obj, const char *key, size_t key_len, cmcp_json_t *v)
cmcp_json_t * cmcp_json_new_object(void)
char * cmcp_json_emit(const cmcp_json_t *v)
cmcp_json_t * cmcp_json_new_array(void)
cmcp_json_t * cmcp_json_new_bool(int b)
int cmcp_json_object_set(cmcp_json_t *obj, const char *key, cmcp_json_t *v)
cmcp_json_t * cmcp_json_parse(const char *text, size_t len)
long long cmcp_json_int(const cmcp_json_t *v)
int cmcp_json_bool(const cmcp_json_t *v)
cmcp_json_t * cmcp_json_new_string_n(const char *s, size_t n)
size_t cmcp_json_string_len(const cmcp_json_t *v)
size_t cmcp_json_object_len(const cmcp_json_t *obj)
const cmcp_json_t * cmcp_json_object_get(const cmcp_json_t *obj, const char *key)
cmcp_json_t * cmcp_json_new_int(long long i)
double cmcp_json_double(const cmcp_json_t *v)
int cmcp_json_array_append(cmcp_json_t *arr, cmcp_json_t *v)
char * cmcp_json_escape_dup(const char *in)
cmcp_json_t * cmcp_json_new_double(double d)
cmcp_json_t * cmcp_json_new_string(const char *s)
const char * cmcp_json_string(const cmcp_json_t *v)
size_t cmcp_json_array_len(const cmcp_json_t *arr)
void cmcp_json_free(cmcp_json_t *v)
cmcp_json_t * cmcp_json_clone(const cmcp_json_t *v)
void cmcp_json_redact(cmcp_json_t *v)
In-place scrub of credential-shaped values (Tier 6 axis 6.5.4).
int cmcp_json_is_null(const cmcp_json_t *v)
Opaque-by-convention JSON tree node.
Definition cmcp_json.h:55
double d
Definition cmcp_json.h:60
size_t len
Definition cmcp_json.h:63
struct cmcp_json::@0::@2 str
cmcp_json_t ** items
Definition cmcp_json.h:66
struct cmcp_json::@0::@3 arr
cmcp_json_t ** values
Definition cmcp_json.h:73
cmcp_json_type_t type
Definition cmcp_json.h:56
char ** keys
Definition cmcp_json.h:71
struct cmcp_json::@0::@4 obj
char * s
Definition cmcp_json.h:62
size_t * key_lens
Definition cmcp_json.h:72
size_t cap
Definition cmcp_json.h:68
long long i
Definition cmcp_json.h:59