Concord - C Discord API library
A Discord API wrapper library written in C
json-build.h
Go to the documentation of this file.
1/*
2 * Special thanks to Christopher Wellons (aka skeeto) for giving valuable
3 * feedback that helped improve this lib.
4 *
5 * See: https://www.reddit.com/r/C_Programming/comments/sf95m3/comment/huojrjn
6 */
7#ifndef JSON_BUILD_H
8#define JSON_BUILD_H
9
10#include <stddef.h>
11
12#ifdef __cplusplus
13extern "C" {
14#endif
15
16#ifdef JSONB_STATIC
17#define JSONB_API static
18#else
19#define JSONB_API extern
20#endif
21
22#ifndef JSONB_MAX_DEPTH
30#define JSONB_MAX_DEPTH 128
31#endif /* JSONB_MAX_DEPTH */
32
34typedef enum jsonbcode {
46
58};
59
61typedef struct jsonb {
67 size_t pos;
69
75JSONB_API void jsonb_init(jsonb *builder);
76
85JSONB_API jsonbcode jsonb_object(jsonb *builder, char buf[], size_t bufsize);
86
96 char buf[],
97 size_t bufsize);
98
110 jsonb *builder, char buf[], size_t bufsize, const char key[], size_t len);
111
120JSONB_API jsonbcode jsonb_array(jsonb *builder, char buf[], size_t bufsize);
121
131 char buf[],
132 size_t bufsize);
133
145 char buf[],
146 size_t bufsize,
147 const char token[],
148 size_t len);
149
160 char buf[],
161 size_t bufsize,
162 int boolean);
163
172JSONB_API jsonbcode jsonb_null(jsonb *builder, char buf[], size_t bufsize);
173
185 jsonb *builder, char buf[], size_t bufsize, const char str[], size_t len);
186
197 char buf[],
198 size_t bufsize,
199 double number);
200
201#ifndef JSONB_HEADER
202#include <stdio.h>
203#ifndef JSONB_DEBUG
204#define TRACE(prev, next) next
205#define DECORATOR(a)
206#else
207static const char *
208_jsonb_eval_state(enum jsonbstate state)
209{
210 switch (state) {
211 case JSONB_ARRAY_OR_OBJECT_OR_VALUE: return "array or object or value";
212 case JSONB_OBJECT_KEY_OR_CLOSE: return "object key or close";
213 case JSONB_OBJECT_NEXT_KEY_OR_CLOSE: return "object next key or close";
214 case JSONB_OBJECT_VALUE: return "object value";
215 case JSONB_ARRAY_VALUE_OR_CLOSE: return "array value or close";
216 case JSONB_ARRAY_NEXT_VALUE_OR_CLOSE: return "array next value or close";
217 case JSONB_ERROR: return "error";
218 case JSONB_DONE: return "done";
219 default: return "unknown";
220 }
221}
222#define TRACE(prev, next) \
223 do { \
224 enum jsonbstate _prev = prev, _next = next; \
225 fprintf(stderr, "%s():L%d | %s -> %s\n", __func__, __LINE__, \
226 _jsonb_eval_state(_prev), _jsonb_eval_state(_next)); \
227 } while (0)
228#define DECORATOR(d) d
229#endif /* JSONB_DEBUG */
230
231#define STACK_HEAD(b, state) *(b)->top = (state)
232#define STACK_PUSH(b, state) TRACE(*(b)->top, *++(b)->top = (state))
233#define STACK_POP(b) TRACE(*(b)->top, DECORATOR(*)--(b)->top)
234
235#define BUFFER_COPY_CHAR(b, c, _pos, buf, bufsize) \
236 do { \
237 if ((b)->pos + (_pos) + 1 + 1 > (bufsize)) { \
238 return JSONB_ERROR_NOMEM; \
239 } \
240 (buf)[(b)->pos + (_pos)++] = (c); \
241 (buf)[(b)->pos + (_pos)] = '\0'; \
242 } while (0)
243#define BUFFER_COPY(b, value, len, _pos, buf, bufsize) \
244 do { \
245 size_t i; \
246 if ((b)->pos + (_pos) + (len) + 1 > (bufsize)) { \
247 return JSONB_ERROR_NOMEM; \
248 } \
249 for (i = 0; i < (len); ++i) \
250 (buf)[(b)->pos + (_pos) + i] = (value)[i]; \
251 (_pos) += (len); \
252 (buf)[(b)->pos + (_pos)] = '\0'; \
253 } while (0)
254
255JSONB_API void
257{
258 static jsonb empty_builder;
259 *b = empty_builder;
260 b->top = b->stack;
261}
262
264jsonb_object(jsonb *b, char buf[], size_t bufsize)
265{
266 enum jsonbstate new_state;
267 size_t pos = 0;
268 if (b->top - b->stack >= JSONB_MAX_DEPTH) return JSONB_ERROR_STACK;
269 switch (*b->top) {
271 BUFFER_COPY_CHAR(b, ',', pos, buf, bufsize);
272 /* fall-through */
275 break;
278 break;
280 new_state = JSONB_DONE;
281 break;
282 default:
284 /* fall-through */
285 case JSONB_DONE:
286 case JSONB_ERROR:
287 return JSONB_ERROR_INPUT;
288 }
289 BUFFER_COPY_CHAR(b, '{', pos, buf, bufsize);
290 STACK_HEAD(b, new_state);
292 b->pos += pos;
293 return JSONB_OK;
294}
295
297jsonb_object_pop(jsonb *b, char buf[], size_t bufsize)
298{
299 enum jsonbcode code;
300 size_t pos = 0;
301 switch (*b->top) {
304 code = b->stack == b->top - 1 ? JSONB_END : JSONB_OK;
305 break;
306 default:
308 /* fall-through */
309 case JSONB_DONE:
310 case JSONB_ERROR:
311 return JSONB_ERROR_INPUT;
312 }
313 BUFFER_COPY_CHAR(b, '}', pos, buf, bufsize);
314 STACK_POP(b);
315 b->pos += pos;
316 return code;
317}
318
319static long
320_jsonb_escape(
321 size_t *pos, char buf[], size_t bufsize, const char str[], size_t len)
322{
323 char *esc_tok = NULL, _esc_tok[8] = "\\u00";
324 char *esc_buf = NULL;
325 int extra_bytes = 0;
326 size_t i;
327second_iter:
328 /* 1st iteration, esc_buf is NULL and count extra_bytes needed for escaping
329 * 2st iteration, esc_buf is not NULL, and does escaping. */
330 for (i = 0; i < len; ++i) {
331 unsigned char c = str[i];
332 esc_tok = NULL;
333 switch (c) {
334 case 0x22: esc_tok = "\\\""; break;
335 case 0x5C: esc_tok = "\\\\"; break;
336 case '\b': esc_tok = "\\b"; break;
337 case '\f': esc_tok = "\\f"; break;
338 case '\n': esc_tok = "\\n"; break;
339 case '\r': esc_tok = "\\r"; break;
340 case '\t': esc_tok = "\\t"; break;
341 default: if (c <= 0x1F) {
342 static const char tohex[] = "0123456789abcdef";
343 _esc_tok[4] = tohex[c >> 4];
344 _esc_tok[5] = tohex[c & 0xF];
345 _esc_tok[6] = 0;
346 esc_tok = _esc_tok;
347 }
348 }
349 if (esc_tok) {
350 int j;
351 for (j = 0; esc_tok[j]; j++) {
352 if (!esc_buf) /* count how many extra bytes are needed */
353 continue;
354 *esc_buf++ = esc_tok[j];
355 }
356 extra_bytes += j - 1;
357 }
358 else if (esc_buf) {
359 *esc_buf++ = c;
360 }
361 }
362
363 if (*pos + len + extra_bytes > bufsize) return JSONB_ERROR_NOMEM;
364
365 if (esc_buf) {
366 *pos += len + extra_bytes;
367 return JSONB_OK;
368 }
369 if (!extra_bytes) {
370 size_t j;
371 for (j = 0; j < len; ++j)
372 buf[*pos + j] = str[j];
373 *pos += len;
374 return JSONB_OK;
375 }
376 esc_buf = buf + *pos;
377 extra_bytes = 0;
378 goto second_iter;
379}
380
382jsonb_key(jsonb *b, char buf[], size_t bufsize, const char key[], size_t len)
383{
384 size_t pos = 0;
385 switch (*b->top) {
387 BUFFER_COPY_CHAR(b, ',', pos, buf, bufsize);
388 /* fall-through */
390 enum jsonbcode ret;
391 BUFFER_COPY_CHAR(b, '"', pos, buf, bufsize);
392 ret = _jsonb_escape(&pos, buf + b->pos, bufsize, key, len);
393 if (ret != JSONB_OK) return ret;
394 BUFFER_COPY(b, "\":", 2, pos, buf, bufsize);
396 } break;
397 default:
399 /* fall-through */
400 case JSONB_DONE:
401 return JSONB_ERROR_INPUT;
402 }
403 b->pos += pos;
404 return JSONB_OK;
405}
406
408jsonb_array(jsonb *b, char buf[], size_t bufsize)
409{
410 enum jsonbstate new_state;
411 size_t pos = 0;
412 if (b->top - b->stack >= JSONB_MAX_DEPTH) return JSONB_ERROR_STACK;
413 switch (*b->top) {
415 BUFFER_COPY_CHAR(b, ',', pos, buf, bufsize);
416 /* fall-through */
419 break;
422 break;
424 new_state = JSONB_DONE;
425 break;
426 default:
428 /* fall-through */
429 case JSONB_DONE:
430 case JSONB_ERROR:
431 return JSONB_ERROR_INPUT;
432 }
433 BUFFER_COPY_CHAR(b, '[', pos, buf, bufsize);
434 STACK_HEAD(b, new_state);
436 b->pos += pos;
437 return JSONB_OK;
438}
439
441jsonb_array_pop(jsonb *b, char buf[], size_t bufsize)
442{
443 enum jsonbcode code;
444 size_t pos = 0;
445 switch (*b->top) {
448 code = b->stack == b->top - 1 ? JSONB_END : JSONB_OK;
449 break;
450 default:
452 /* fall-through */
453 case JSONB_DONE:
454 case JSONB_ERROR:
455 return JSONB_ERROR_INPUT;
456 }
457 BUFFER_COPY_CHAR(b, ']', pos, buf, bufsize);
458 STACK_POP(b);
459 b->pos += pos;
460 return code;
461}
462
465 jsonb *b, char buf[], size_t bufsize, const char token[], size_t len)
466{
467 enum jsonbstate next_state;
468 enum jsonbcode code;
469 size_t pos = 0;
470 switch (*b->top) {
472 next_state = JSONB_DONE;
473 code = JSONB_END;
474 break;
476 BUFFER_COPY_CHAR(b, ',', pos, buf, bufsize);
477 /* fall-through */
480 code = JSONB_OK;
481 break;
484 code = JSONB_OK;
485 break;
486 default:
488 /* fall-through */
489 case JSONB_DONE:
490 case JSONB_ERROR:
491 return JSONB_ERROR_INPUT;
492 }
493 BUFFER_COPY(b, token, len, pos, buf, bufsize);
494 STACK_HEAD(b, next_state);
495 b->pos += pos;
496 return code;
497}
498
500jsonb_bool(jsonb *b, char buf[], size_t bufsize, int boolean)
501{
502 if (boolean) return jsonb_token(b, buf, bufsize, "true", 4);
503 return jsonb_token(b, buf, bufsize, "false", 5);
504}
505
507jsonb_null(jsonb *b, char buf[], size_t bufsize)
508{
509 return jsonb_token(b, buf, bufsize, "null", 4);
510}
511
514 jsonb *b, char buf[], size_t bufsize, const char str[], size_t len)
515{
516 enum jsonbstate next_state;
517 enum jsonbcode code, ret;
518 size_t pos = 0;
519 switch (*b->top) {
521 next_state = JSONB_DONE;
522 code = JSONB_END;
523 break;
525 BUFFER_COPY_CHAR(b, ',', pos, buf, bufsize);
526 /* fall-through */
529 code = JSONB_OK;
530 break;
533 code = JSONB_OK;
534 break;
535 default:
537 /* fall-through */
538 case JSONB_DONE:
539 case JSONB_ERROR:
540 return JSONB_ERROR_INPUT;
541 }
542 BUFFER_COPY_CHAR(b, '"', pos, buf, bufsize);
543 ret = _jsonb_escape(&pos, buf + b->pos, bufsize, str, len);
544 if (ret != JSONB_OK) return ret;
545 BUFFER_COPY_CHAR(b, '"', pos, buf, bufsize);
546 STACK_HEAD(b, next_state);
547 b->pos += pos;
548 return code;
549}
550
552jsonb_number(jsonb *b, char buf[], size_t bufsize, double number)
553{
554 char token[32];
555 long len = sprintf(token, "%.17G", number);
556 if (len < 0) return JSONB_ERROR_INPUT;
557 return jsonb_token(b, buf, bufsize, token, len);
558}
559#endif /* JSONB_HEADER */
560
561#ifdef __cplusplus
562}
563#endif
564
565#endif /* JSON_BUILD_H */
struct jsonb jsonb
Handle for building a JSON string.
jsonbcode jsonb_array(jsonb *builder, char buf[], size_t bufsize)
Push an array to the builder.
Definition: json-build.h:408
jsonbstate
json-builder serializing state
Definition: json-build.h:48
@ JSONB_OBJECT_VALUE
Definition: json-build.h:52
@ JSONB_INIT
Definition: json-build.h:49
@ JSONB_ARRAY_VALUE_OR_CLOSE
Definition: json-build.h:54
@ JSONB_OBJECT_NEXT_KEY_OR_CLOSE
Definition: json-build.h:53
@ JSONB_ARRAY_NEXT_VALUE_OR_CLOSE
Definition: json-build.h:55
@ JSONB_ERROR
Definition: json-build.h:56
@ JSONB_DONE
Definition: json-build.h:57
@ JSONB_OBJECT_KEY_OR_CLOSE
Definition: json-build.h:51
@ JSONB_ARRAY_OR_OBJECT_OR_VALUE
Definition: json-build.h:50
#define JSONB_MAX_DEPTH
Definition: json-build.h:30
jsonbcode jsonb_key(jsonb *builder, char buf[], size_t bufsize, const char key[], size_t len)
Push a key to the builder.
Definition: json-build.h:382
#define BUFFER_COPY_CHAR(b, c, _pos, buf, bufsize)
Definition: json-build.h:235
jsonbcode jsonb_token(jsonb *builder, char buf[], size_t bufsize, const char token[], size_t len)
Push a raw JSON token to the builder.
Definition: json-build.h:464
jsonbcode jsonb_string(jsonb *builder, char buf[], size_t bufsize, const char str[], size_t len)
Push a string token to the builder.
Definition: json-build.h:513
jsonbcode jsonb_object(jsonb *builder, char buf[], size_t bufsize)
Push an object to the builder.
Definition: json-build.h:264
#define STACK_PUSH(b, state)
Definition: json-build.h:232
jsonbcode jsonb_bool(jsonb *builder, char buf[], size_t bufsize, int boolean)
Push a boolean token to the builder.
Definition: json-build.h:500
jsonbcode jsonb_array_pop(jsonb *builder, char buf[], size_t bufsize)
Pop an array from the builder.
Definition: json-build.h:441
#define BUFFER_COPY(b, value, len, _pos, buf, bufsize)
Definition: json-build.h:243
jsonbcode jsonb_null(jsonb *builder, char buf[], size_t bufsize)
Push a null token to the builder.
Definition: json-build.h:507
jsonbcode jsonb_number(jsonb *builder, char buf[], size_t bufsize, double number)
Push a number token to the builder.
Definition: json-build.h:552
void jsonb_init(jsonb *builder)
Initialize a jsonb handle.
Definition: json-build.h:256
#define STACK_POP(b)
Definition: json-build.h:233
#define STACK_HEAD(b, state)
Definition: json-build.h:231
jsonbcode
json-builder return codes
Definition: json-build.h:34
@ JSONB_END
Definition: json-build.h:38
@ JSONB_ERROR_INPUT
Definition: json-build.h:42
@ JSONB_ERROR_STACK
Definition: json-build.h:44
@ JSONB_ERROR_NOMEM
Definition: json-build.h:40
@ JSONB_OK
Definition: json-build.h:36
jsonbcode jsonb_object_pop(jsonb *builder, char buf[], size_t bufsize)
Pop an object from the builder.
Definition: json-build.h:297
#define JSONB_API
Definition: json-build.h:19
Handle for building a JSON string.
Definition: json-build.h:61
enum jsonbstate * top
Definition: json-build.h:65
size_t pos
Definition: json-build.h:67
enum jsonbstate stack[128+1]
Definition: json-build.h:63