Lite³
A JSON-Compatible Zero-Copy Serialization Format
Loading...
Searching...
No Matches
json_enc.c
1/*
2 Lite³: A JSON-Compatible Zero-Copy Serialization Format
3
4 Copyright © 2025 Elias de Jong <elias@fastserial.com>
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23
24 __ __________________ ____
25 _ ___ ___/ /___(_)_/ /_______|_ /
26 _ _____/ / __/ /_ __/ _ \_/_ <
27 ___ __/ /___/ / / /_ / __/____/
28 /_____/_/ \__/ \___/
29*/
30#include "lite3.h"
31
32
33
34#ifdef LITE3_JSON
35#include <stdio.h>
36#include <string.h>
37#include <stdint.h>
38#include <errno.h>
39
40#include "yyjson/yyjson.h"
41#include "nibble_base64/base64.h"
42
43
44
45// Typedef for primitive types
46typedef float f32;
47typedef double f64;
48typedef int8_t i8;
49typedef uint8_t u8;
50typedef int16_t i16;
51typedef uint16_t u16;
52typedef int32_t i32;
53typedef uint32_t u32;
54typedef int64_t i64;
55typedef uint64_t u64;
56
57
58
59// Forward declarations
60int _lite3_json_enc_obj(const unsigned char *buf, size_t buflen, size_t ofs, size_t nesting_depth, yyjson_mut_doc *doc, yyjson_mut_val *coll);
61int _lite3_json_enc_arr(const unsigned char *buf, size_t buflen, size_t ofs, size_t nesting_depth, yyjson_mut_doc *doc, yyjson_mut_val *coll);
62
63int _lite3_json_enc_switch(const unsigned char *buf, size_t buflen, size_t nesting_depth, yyjson_mut_doc *doc, yyjson_mut_val **yy_val, lite3_val *val)
64{
65 enum lite3_type type = lite3_val_type(val);
66 switch (type) {
67 case LITE3_TYPE_NULL:
68 *yy_val = yyjson_mut_null(doc);
69 break;
70 case LITE3_TYPE_BOOL:
71 *yy_val = yyjson_mut_bool(doc, lite3_val_bool(val));
72 break;
73 case LITE3_TYPE_I64:
74 *yy_val = yyjson_mut_sint(doc, lite3_val_i64(val));
75 break;
76 case LITE3_TYPE_F64:
77 *yy_val = yyjson_mut_double(doc, lite3_val_f64(val));
78 break;
80 ;
81 size_t bytes_len;
82 const u8 *bytes = lite3_val_bytes(val, &bytes_len);
83 int b64_len;
84 char *b64 = nibble_base64(bytes, (int)bytes_len, &b64_len);
85 if (!b64) {
86 LITE3_PRINT_ERROR("FAILED TO CONVERT BYTES TO BASE64\n");
87 // No need to free the `b64` pointer, since the allocation would have failed anyways.
88 return -1;
89 }
90 /*
91 According to `yyjson` docs:
92 yyjson_mut_strn(...) --> "The input string is not copied, you should keep this string unmodified for the lifetime of this JSON document."
93 yyjson_mut_strncpy(...) --> "The input string is copied and held by the document."
94
95 So we must use `yyjson_mut_strncpy(...)` here so we can free the temporary base64 buffer.
96 NOTE: `b64_len` does NOT contain the NULL terminator. `yyjson` docs say: "The `str` should be a UTF-8 string, null-terminator is not required."
97 */
98 *yy_val = yyjson_mut_strncpy(doc, b64, (size_t)b64_len);
99 free(b64);
100 break;
102 ;
103 size_t str_len;
104 const char *str = lite3_val_str_n(val, &str_len);
105 /*
106 Here we can use `yyjson_mut_strn(...)` since the value will remain backed up by the Lite³ buffer for the duration of the JSON-conversion process.
107 `str_len` excludes the NULL terminator. `yyjson` docs say: "The `str` should be a UTF-8 string, null-terminator is not required."
108 */
109 *yy_val = yyjson_mut_strn(doc, str, str_len);
110 break;
112 *yy_val = yyjson_mut_obj(doc);
113 size_t obj_ofs = (size_t)((u8 *)val - buf);
114 if (_lite3_json_enc_obj(buf, buflen, obj_ofs, nesting_depth, doc, *yy_val) < 0)
115 return -1;
116 break;
117 case LITE3_TYPE_ARRAY:
118 *yy_val = yyjson_mut_arr(doc);
119 size_t arr_ofs = (size_t)((u8 *)val - buf);
120 if (_lite3_json_enc_arr(buf, buflen, arr_ofs, nesting_depth, doc, *yy_val) < 0)
121 return -1;
122 break;
123 default:
124 LITE3_PRINT_ERROR("FAILED TO BUILD JSON DOCUMENT: VALUE TYPE INVALID\n");
125 errno = EINVAL;
126 return -1;
127 }
128 return 0;
129}
130
131/*
132 Function that recursively builds a JSON document.
133 - Returns 0 on success
134 - Returns < 0 on error
135*/
136int _lite3_json_enc_obj(const unsigned char *buf, size_t buflen, size_t ofs, size_t nesting_depth, yyjson_mut_doc *doc, yyjson_mut_val *coll)
137{
138 if (++nesting_depth > LITE3_JSON_NESTING_DEPTH_MAX) {
139 LITE3_PRINT_ERROR("FAILED TO BUILD JSON DOCUMENT: nesting_depth > LITE3_JSON_NESTING_DEPTH_MAX\n");
140 errno = EINVAL;
141 return -1;
142 }
143 lite3_iter iter;
144 int ret;
145 if ((ret = lite3_iter_create(buf, buflen, ofs, &iter)) < 0)
146 return ret;
147 lite3_str key;
148 lite3_val *val;
149 size_t val_ofs;
150 yyjson_mut_val *yy_val;
151 while ((ret = lite3_iter_next(buf, buflen, &iter, &key, &val_ofs)) == LITE3_ITER_ITEM) {
152 val = (lite3_val *)(buf + val_ofs);
153 if ((ret = _lite3_json_enc_switch(buf, buflen, nesting_depth, doc, &yy_val, val)) < 0)
154 return ret;
155 if (!yyjson_mut_obj_add(coll, yyjson_mut_str(doc, LITE3_STR(buf, key)), yy_val)) {
156 LITE3_PRINT_ERROR("FAILED TO BUILD JSON DOCUMENT: ADDING KEY-VALUE PAIR FAILED\n");
157 errno = EINVAL;
158 return -1;
159 }
160 }
161 return ret;
162}
163
164/*
165 Function that recursively builds a JSON document.
166 - Returns 0 on success
167 - Returns < 0 on error
168*/
169int _lite3_json_enc_arr(const unsigned char *buf, size_t buflen, size_t ofs, size_t nesting_depth, yyjson_mut_doc *doc, yyjson_mut_val *coll)
170{
171 if (++nesting_depth > LITE3_JSON_NESTING_DEPTH_MAX) {
172 LITE3_PRINT_ERROR("FAILED TO BUILD JSON DOCUMENT: nesting_depth > LITE3_JSON_NESTING_DEPTH_MAX\n");
173 errno = EINVAL;
174 return -1;
175 }
176 lite3_iter iter;
177 int ret;
178 if ((ret = lite3_iter_create(buf, buflen, ofs, &iter)) < 0)
179 return ret;
180 lite3_val *val;
181 size_t val_ofs;
182 yyjson_mut_val *yy_val;
183 while ((ret = lite3_iter_next(buf, buflen, &iter, NULL, &val_ofs)) == LITE3_ITER_ITEM) {
184 val = (lite3_val *)(buf + val_ofs);
185 if ((ret = _lite3_json_enc_switch(buf, buflen, nesting_depth, doc, &yy_val, val)) < 0)
186 return ret;
187 if (!yyjson_mut_arr_append(coll, yy_val)) {
188 LITE3_PRINT_ERROR("FAILED TO BUILD JSON DOCUMENT: APPENDING ARRAY ELEMENT FAILED\n");
189 errno = EINVAL;
190 return -1;
191 }
192 }
193 return ret;
194}
195
196yyjson_mut_doc *_lite3_json_enc_doc(const unsigned char *buf, size_t buflen, size_t ofs)
197{
198 if (_lite3_verify_get(buf, buflen, ofs) < 0)
199 return NULL;
200 yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
201 if (!doc)
202 return NULL;
203 yyjson_mut_val *root;
204 switch (*(buf + ofs)) {
206 root = yyjson_mut_obj(doc);
207 if (_lite3_json_enc_obj(buf, buflen, ofs, 0, doc, root) < 0)
208 goto error;
209 break;
210 case LITE3_TYPE_ARRAY:
211 root = yyjson_mut_arr(doc);
212 if (_lite3_json_enc_arr(buf, buflen, ofs, 0, doc, root) < 0)
213 goto error;
214 break;
215 default:
216 LITE3_PRINT_ERROR("INVALID ARGUMENT: EXPECTING ARRAY OR OBJECT TYPE\n");
217 errno = EINVAL;
218 goto error;
219 }
220 yyjson_mut_doc_set_root(doc, root);
221 return doc;
222error:
223 yyjson_mut_doc_free(doc);
224 return NULL;
225}
226
227int lite3_json_print(const unsigned char *buf, size_t buflen, size_t ofs)
228{
229 yyjson_mut_doc *doc = _lite3_json_enc_doc(buf, buflen, ofs);
230 if (!doc)
231 return -1;
232 size_t len;
233 yyjson_write_err err;
234 char *json = yyjson_mut_write_opts(doc, YYJSON_WRITE_PRETTY, NULL, &len, &err);
235 yyjson_mut_doc_free(doc);
236
237 if (!json) {
238 LITE3_PRINT_ERROR("FAILED TO WRITE JSON\tyyjson error code: %u msg:%s\n", err.code, err.msg);
239 errno = EIO;
240 return -1;
241 }
242 fwrite(json, 1, len, stdout);
243 fputc('\n', stdout);
244 free(json);
245 return 0;
246}
247
248char *lite3_json_enc(const unsigned char *buf, size_t buflen, size_t ofs, size_t *restrict out_len)
249{
250 yyjson_mut_doc *doc = _lite3_json_enc_doc(buf, buflen, ofs);
251 if (!doc)
252 return NULL;
253 yyjson_write_err err;
254 char *json = yyjson_mut_write_opts(doc, YYJSON_WRITE_NOFLAG, NULL, out_len, &err);
255 yyjson_mut_doc_free(doc);
256 if (!json) {
257 LITE3_PRINT_ERROR("FAILED TO WRITE JSON\tyyjson error code: %u msg:%s\n", err.code, err.msg);
258 errno = EIO;
259 return NULL;
260 }
261 return json;
262}
263
264char *lite3_json_enc_pretty(const unsigned char *buf, size_t buflen, size_t ofs, size_t *restrict out_len)
265{
266 yyjson_mut_doc *doc = _lite3_json_enc_doc(buf, buflen, ofs);
267 if (!doc)
268 return NULL;
269 yyjson_write_err err;
270 char *json = yyjson_mut_write_opts(doc, YYJSON_WRITE_PRETTY, NULL, out_len, &err);
271 yyjson_mut_doc_free(doc);
272 if (!json) {
273 LITE3_PRINT_ERROR("FAILED TO WRITE JSON\tyyjson error code: %u msg:%s\n", err.code, err.msg);
274 errno = EIO;
275 return NULL;
276 }
277 return json;
278}
279
280int64_t lite3_json_enc_buf(const unsigned char *buf, size_t buflen, size_t ofs, char *restrict json_buf, size_t json_bufsz)
281{
282 yyjson_mut_doc *doc = _lite3_json_enc_doc(buf, buflen, ofs);
283 if (!doc)
284 return -1;
285 yyjson_write_err err;
286 size_t ret = yyjson_mut_write_buf(json_buf, json_bufsz, doc, YYJSON_WRITE_NOFLAG, &err);
287 assert(ret <= INT64_MAX);
288 yyjson_mut_doc_free(doc);
289 if (ret == 0) {
290 LITE3_PRINT_ERROR("FAILED TO WRITE JSON\tyyjson error code: %u msg:%s\n", err.code, err.msg);
291 errno = EIO;
292 return -1;
293 }
294 return (i64)ret;
295}
296
297int64_t lite3_json_enc_buf_pretty(const unsigned char *buf, size_t buflen, size_t ofs, char *restrict json_buf, size_t json_bufsz)
298{
299 yyjson_mut_doc *doc = _lite3_json_enc_doc(buf, buflen, ofs);
300 if (!doc)
301 return -1;
302 yyjson_write_err err;
303 size_t ret = yyjson_mut_write_buf(json_buf, json_bufsz, doc, YYJSON_WRITE_PRETTY, &err);
304 assert(ret <= INT64_MAX);
305 yyjson_mut_doc_free(doc);
306 if (ret == 0) {
307 LITE3_PRINT_ERROR("FAILED TO WRITE JSON\tyyjson error code: %u msg:%s\n", err.code, err.msg);
308 errno = EIO;
309 return -1;
310 }
311 return (i64)ret;
312}
313#endif // LITE3_JSON
#define LITE3_JSON_NESTING_DEPTH_MAX
Maximum nesting limit for JSON documents being encoded or decoded.
Definition lite3.h:2873
#define LITE3_ITER_ITEM
Return value of lite3_iter_next(); iterator produced an item, continue;.
Definition lite3.h:2661
int lite3_iter_next(const unsigned char *buf, size_t buflen, lite3_iter *iter, lite3_str *out_key, size_t *out_val_ofs)
Get the next item from a lite3 iterator.
Definition lite3.c:373
static int lite3_iter_create(const unsigned char *buf, size_t buflen, size_t ofs, lite3_iter *out)
Create a lite3 iterator for the given object or array.
Definition lite3.h:2689
char * lite3_json_enc_pretty(const unsigned char *buf, size_t buflen, size_t ofs, size_t *__restrict out_len)
Convert Lite³ to JSON prettified string.
int lite3_json_print(const unsigned char *buf, size_t buflen, size_t ofs)
Print Lite³ buffer as JSON to stdout
int64_t lite3_json_enc_buf(const unsigned char *buf, size_t buflen, size_t ofs, char *__restrict json_buf, size_t json_bufsz)
Convert Lite³ to JSON and write to output buffer.
char * lite3_json_enc(const unsigned char *buf, size_t buflen, size_t ofs, size_t *__restrict out_len)
Convert Lite³ to JSON string.
lite3_type
enum containing all Lite³ types
Definition lite3.h:500
#define LITE3_STR(buf, val)
Generational pointer / safe access wrapper.
Definition lite3.h:656
@ LITE3_TYPE_ARRAY
maps to 'array' type in JSON
Definition lite3.h:508
@ LITE3_TYPE_STRING
maps to 'string' type in JSON
Definition lite3.h:506
@ LITE3_TYPE_BOOL
maps to 'boolean' type in JSON; underlying datatype: bool
Definition lite3.h:502
@ LITE3_TYPE_BYTES
coverted to base64 string in JSON
Definition lite3.h:505
@ LITE3_TYPE_F64
maps to 'number' type in JSON; underlying datatype: double
Definition lite3.h:504
@ LITE3_TYPE_OBJECT
maps to 'object' type in JSON
Definition lite3.h:507
@ LITE3_TYPE_I64
maps to 'number' type in JSON; underlying datatype: int64_t
Definition lite3.h:503
@ LITE3_TYPE_NULL
maps to 'null' type in JSON
Definition lite3.h:501
static const char * lite3_val_str_n(lite3_val *val, size_t *out_len)
Definition lite3.h:2802
static enum lite3_type lite3_val_type(lite3_val *val)
Returns the value type of *val
Definition lite3.h:2739
Lite³ Buffer API Header.
Struct containing iterator state.
Definition lite3.h:2671
Struct holding a reference to a string inside a Lite³ buffer.
Definition lite3.h:591
Struct representing a value inside a Lite³ buffer.
Definition lite3.h:521