aboutsummaryrefslogtreecommitdiffstats
path: root/src/json.c
blob: a8fb99564ea32167ff716995c2a08a632efcbe48 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#define _GNU_SOURCE
#include "json.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void reserve(json_t *j, size_t n) {
  if (j->len + n <= j->cap) return;
  size_t cap = j->cap ? j->cap : 4096;
  while (cap < j->len + n) cap *= 2;
  j->buf = realloc(j->buf, cap);
  j->cap = cap;
}

static void raw(json_t *j, const char *s, size_t n) {
  reserve(j, n + 1);
  memcpy(j->buf + j->len, s, n);
  j->len += n;
  j->buf[j->len] = 0;
}

static void rawz(json_t *j, const char *s) { raw(j, s, strlen(s)); }

void json_init(json_t *j) {
  memset(j, 0, sizeof *j);
  reserve(j, 4096);
  j->buf[0] = 0;
}

void json_free(json_t *j) {
  free(j->buf);
  memset(j, 0, sizeof *j);
}

static void sep(json_t *j, const char *key) {
  if (j->need_comma) rawz(j, ",");
  if (key) {
    rawz(j, "\"");
    rawz(j, key);
    rawz(j, "\":");
  }
  j->need_comma = true;
}

static void escaped(json_t *j, const char *s, size_t len) {
  rawz(j, "\"");
  for (size_t i = 0; i < len; i++) {
    unsigned char c = (unsigned char)s[i];
    switch (c) {
    case '"': rawz(j, "\\\""); break;
    case '\\': rawz(j, "\\\\"); break;
    case '\n': rawz(j, "\\n"); break;
    case '\r': rawz(j, "\\r"); break;
    case '\t': rawz(j, "\\t"); break;
    default:
      if (c < 0x20) {
        char b[8];
        snprintf(b, sizeof b, "\\u%04x", c);
        rawz(j, b);
      } else {
        raw(j, (const char *)&c, 1); /* UTF-8 passes through */
      }
    }
  }
  rawz(j, "\"");
}

void json_obj_open(json_t *j, const char *key) {
  sep(j, key);
  rawz(j, "{");
  j->need_comma = false;
}

void json_obj_close(json_t *j) {
  rawz(j, "}");
  j->need_comma = true;
}

void json_arr_open(json_t *j, const char *key) {
  sep(j, key);
  rawz(j, "[");
  j->need_comma = false;
}

void json_arr_close(json_t *j) {
  rawz(j, "]");
  j->need_comma = true;
}

void json_str(json_t *j, const char *key, const char *val, size_t len) {
  sep(j, key);
  escaped(j, val, len);
}

void json_int(json_t *j, const char *key, long long val) {
  sep(j, key);
  char b[32];
  snprintf(b, sizeof b, "%lld", val);
  rawz(j, b);
}

void json_bool(json_t *j, const char *key, bool val) {
  sep(j, key);
  rawz(j, val ? "true" : "false");
}

void json_null(json_t *j, const char *key) {
  sep(j, key);
  rawz(j, "null");
}