GCC Code Coverage Report


Directory: ./
File: json.c
Date: 2025-12-31 16:19:05
Exec Total Coverage
Lines: 839 839 100.0%
Functions: 64 64 100.0%
Branches: 502 598 83.9%

Line Branch Exec Source
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "cx/json.h"
30 #include "cx/kv_list.h"
31
32 #include <string.h>
33 #include <assert.h>
34 #include <stdio.h>
35 #include <inttypes.h>
36 #include <ctype.h>
37
38 /*
39 * RFC 8259
40 * https://tools.ietf.org/html/rfc8259
41 */
42
43 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING};
44
45 1168 static void token_destroy(CxJsonToken *token) {
46
2/2
✓ Branch 0 (2→3) taken 6 times.
✓ Branch 1 (2→5) taken 1162 times.
1168 if (token->allocated) {
47 6 cx_strfree(&token->content);
48 6 token->allocated = false;
49 }
50 1168 }
51
52 7 static int num_isexp(const char *content, size_t length, size_t pos) {
53
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 6 times.
7 if (pos >= length) {
54 1 return 0;
55 }
56
57 6 int ok = 0;
58
2/2
✓ Branch 0 (13→5) taken 10 times.
✓ Branch 1 (13→14) taken 3 times.
13 for (size_t i = pos; i < length; i++) {
59 10 char c = content[i];
60
2/2
✓ Branch 0 (5→6) taken 5 times.
✓ Branch 1 (5→7) taken 5 times.
10 if (isdigit((unsigned char)c)) {
61 5 ok = 1;
62
2/2
✓ Branch 0 (7→8) taken 3 times.
✓ Branch 1 (7→11) taken 2 times.
5 } else if (i == pos) {
63
3/4
✓ Branch 0 (8→9) taken 3 times.
✗ Branch 1 (8→12) not taken.
✓ Branch 2 (9→10) taken 1 times.
✓ Branch 3 (9→12) taken 2 times.
3 if (!(c == '+' || c == '-')) {
64 1 return 0;
65 }
66 } else {
67 2 return 0;
68 }
69 }
70
71 3 return ok;
72 }
73
74 166 static CxJsonTokenType token_numbertype(const char *content, size_t length) {
75
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 166 times.
166 if (length == 0) return CX_JSON_TOKEN_ERROR;
76
77
4/4
✓ Branch 0 (4→5) taken 163 times.
✓ Branch 1 (4→7) taken 3 times.
✓ Branch 2 (5→6) taken 2 times.
✓ Branch 3 (5→7) taken 161 times.
166 if (content[0] != '-' && !isdigit((unsigned char)content[0])) {
78 2 return CX_JSON_TOKEN_ERROR;
79 }
80
81 164 CxJsonTokenType type = CX_JSON_TOKEN_INTEGER;
82
2/2
✓ Branch 0 (22→8) taken 288 times.
✓ Branch 1 (22→23) taken 153 times.
441 for (size_t i = 1; i < length; i++) {
83
2/2
✓ Branch 0 (8→9) taken 29 times.
✓ Branch 1 (8→12) taken 259 times.
288 if (content[i] == '.') {
84
2/2
✓ Branch 0 (9→10) taken 1 times.
✓ Branch 1 (9→11) taken 28 times.
29 if (type == CX_JSON_TOKEN_NUMBER) {
85 1 return CX_JSON_TOKEN_ERROR; // more than one decimal separator
86 }
87 28 type = CX_JSON_TOKEN_NUMBER;
88
4/4
✓ Branch 0 (12→13) taken 253 times.
✓ Branch 1 (12→14) taken 6 times.
✓ Branch 2 (13→14) taken 1 times.
✓ Branch 3 (13→19) taken 252 times.
259 } else if (content[i] == 'e' || content[i] == 'E') {
89
2/2
✓ Branch 0 (15→16) taken 3 times.
✓ Branch 1 (15→17) taken 4 times.
7 return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR;
90
2/2
✓ Branch 0 (19→20) taken 3 times.
✓ Branch 1 (19→21) taken 249 times.
252 } else if (!isdigit((unsigned char)content[i])) {
91 3 return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep
92 }
93 }
94
95 153 return type;
96 }
97
98 436 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) {
99 436 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start);
100 436 bool allocated = false;
101
2/2
✓ Branch 0 (4→5) taken 7 times.
✓ Branch 1 (4→20) taken 429 times.
436 if (json->uncompleted_tokentype != CX_JSON_NO_TOKEN) {
102 7 allocated = true;
103 7 str = cx_strcat(json->uncompleted_content, 1, str);
104
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→14) taken 7 times.
7 if (str.ptr == NULL) {
105 return (CxJsonToken){CX_JSON_NO_TOKEN, false, CX_NULLSTR}; // LCOV_EXCL_LINE
106 }
107 7 json->uncompleted_content = CX_NULLSTR;
108 7 json->uncompleted_tokentype = CX_JSON_NO_TOKEN;
109 }
110 CxJsonTokenType ttype;
111
2/2
✓ Branch 0 (20→21) taken 210 times.
✓ Branch 1 (20→22) taken 226 times.
436 if (isstring) {
112 210 ttype = CX_JSON_TOKEN_STRING;
113 } else {
114
4/4
✓ Branch 0 (36→37) taken 207 times.
✓ Branch 1 (36→67) taken 19 times.
✓ Branch 2 (51→52) taken 186 times.
✓ Branch 3 (51→67) taken 21 times.
1299 if (!cx_strcmp(str, "true") || !cx_strcmp(str, "false")
115
2/2
✓ Branch 0 (66→67) taken 20 times.
✓ Branch 1 (66→68) taken 166 times.
558 || !cx_strcmp(str, "null")) {
116 60 ttype = CX_JSON_TOKEN_LITERAL;
117 } else {
118 166 ttype = token_numbertype(str.ptr, str.length);
119 }
120 }
121
2/2
✓ Branch 0 (69→70) taken 10 times.
✓ Branch 1 (69→79) taken 426 times.
436 if (ttype == CX_JSON_TOKEN_ERROR) {
122
2/2
✓ Branch 0 (70→71) taken 1 times.
✓ Branch 1 (70→72) taken 9 times.
10 if (allocated) {
123 1 cx_strfree(&str);
124 }
125 10 return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, CX_NULLSTR};
126 }
127 426 return (CxJsonToken){ttype, allocated, str};
128 }
129
130 3075 static CxJsonTokenType char2ttype(char c) {
131
8/8
✓ Branch 0 (2→3) taken 96 times.
✓ Branch 1 (2→4) taken 96 times.
✓ Branch 2 (2→5) taken 110 times.
✓ Branch 3 (2→6) taken 96 times.
✓ Branch 4 (2→7) taken 136 times.
✓ Branch 5 (2→8) taken 384 times.
✓ Branch 6 (2→9) taken 212 times.
✓ Branch 7 (2→10) taken 1945 times.
3075 switch (c) {
132 96 case '[': {
133 96 return CX_JSON_TOKEN_BEGIN_ARRAY;
134 }
135 96 case '{': {
136 96 return CX_JSON_TOKEN_BEGIN_OBJECT;
137 }
138 110 case ']': {
139 110 return CX_JSON_TOKEN_END_ARRAY;
140 }
141 96 case '}': {
142 96 return CX_JSON_TOKEN_END_OBJECT;
143 }
144 136 case ':': {
145 136 return CX_JSON_TOKEN_NAME_SEPARATOR;
146 }
147 384 case ',': {
148 384 return CX_JSON_TOKEN_VALUE_SEPARATOR;
149 }
150 212 case '"': {
151 212 return CX_JSON_TOKEN_STRING;
152 }
153 1945 default: {
154
2/2
✓ Branch 0 (10→11) taken 1198 times.
✓ Branch 1 (10→12) taken 747 times.
1945 if (isspace((unsigned char)c)) {
155 1198 return CX_JSON_TOKEN_SPACE;
156 }
157 }
158 }
159 747 return CX_JSON_NO_TOKEN;
160 }
161
162 1247 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
163 // check if there is data in the buffer
164
2/2
✓ Branch 0 (3→4) taken 44 times.
✓ Branch 1 (3→8) taken 1203 times.
1247 if (cxBufferEof(&json->buffer)) {
165 44 return json->uncompleted_tokentype == CX_JSON_NO_TOKEN ?
166
2/2
✓ Branch 0 (4→5) taken 43 times.
✓ Branch 1 (4→6) taken 1 times.
44 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA;
167 }
168
169 // current token type and start index
170 1203 CxJsonTokenType ttype = json->uncompleted_tokentype;
171 1203 size_t token_part_start = json->buffer.pos;
172
173 1203 bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING
174
5/6
✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (8→21) taken 1195 times.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 8 times.
✓ Branch 4 (19→20) taken 1 times.
✓ Branch 5 (19→21) taken 7 times.
1227 && cx_strat(json->uncompleted_content, -1) == '\\';
175
176
2/2
✓ Branch 0 (55→23) taken 4714 times.
✓ Branch 1 (55→56) taken 25 times.
4739 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) {
177 4714 char c = json->buffer.space[i];
178
2/2
✓ Branch 0 (23→24) taken 3075 times.
✓ Branch 1 (23→45) taken 1639 times.
4714 if (ttype != CX_JSON_TOKEN_STRING) {
179 // currently non-string token
180 3075 CxJsonTokenType ctype = char2ttype(c); // start of new token?
181
2/2
✓ Branch 0 (25→26) taken 2328 times.
✓ Branch 1 (25→38) taken 747 times.
3075 if (ttype == CX_JSON_NO_TOKEN) {
182
2/2
✓ Branch 0 (26→27) taken 1148 times.
✓ Branch 1 (26→28) taken 1180 times.
2328 if (ctype == CX_JSON_TOKEN_SPACE) {
183 1148 json->buffer.pos++;
184 1148 continue;
185
2/2
✓ Branch 0 (28→29) taken 212 times.
✓ Branch 1 (28→30) taken 968 times.
1180 } else if (ctype == CX_JSON_TOKEN_STRING) {
186 // begin string
187 212 ttype = CX_JSON_TOKEN_STRING;
188 212 token_part_start = i;
189
2/2
✓ Branch 0 (30→31) taken 742 times.
✓ Branch 1 (30→37) taken 226 times.
968 } else if (ctype != CX_JSON_NO_TOKEN) {
190 // single-char token
191
1/2
✗ Branch 0 (31→32) not taken.
✓ Branch 1 (31→33) taken 742 times.
742 json->buffer.pos = i + 1;
192 742 *result = (CxJsonToken){ctype, false, CX_NULLSTR};
193 742 return CX_JSON_NO_ERROR;
194 } else {
195 226 ttype = CX_JSON_TOKEN_LITERAL; // number or literal
196 226 token_part_start = i;
197 }
198 } else {
199 // finish token
200
2/2
✓ Branch 0 (38→39) taken 226 times.
✓ Branch 1 (38→54) taken 521 times.
747 if (ctype != CX_JSON_NO_TOKEN) {
201 226 *result = token_create(json, false, token_part_start, i);
202
1/2
✗ Branch 0 (40→41) not taken.
✓ Branch 1 (40→42) taken 226 times.
226 if (result->tokentype == CX_JSON_NO_TOKEN) {
203 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
204 }
205
2/2
✓ Branch 0 (42→43) taken 10 times.
✓ Branch 1 (42→44) taken 216 times.
226 if (result->tokentype == CX_JSON_TOKEN_ERROR) {
206 10 return CX_JSON_FORMAT_ERROR_NUMBER;
207 }
208 216 json->buffer.pos = i;
209 216 return CX_JSON_NO_ERROR;
210 }
211 }
212 } else {
213 // currently inside a string
214
2/2
✓ Branch 0 (45→46) taken 50 times.
✓ Branch 1 (45→47) taken 1589 times.
1639 if (escape_end_of_string) {
215 50 escape_end_of_string = false;
216 } else {
217
2/2
✓ Branch 0 (47→48) taken 210 times.
✓ Branch 1 (47→52) taken 1379 times.
1589 if (c == '"') {
218 210 *result = token_create(json, true, token_part_start, i + 1);
219
1/2
✗ Branch 0 (49→50) not taken.
✓ Branch 1 (49→51) taken 210 times.
210 if (result->tokentype == CX_JSON_NO_TOKEN) {
220 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
221 }
222 210 json->buffer.pos = i + 1;
223 210 return CX_JSON_NO_ERROR;
224
2/2
✓ Branch 0 (52→53) taken 50 times.
✓ Branch 1 (52→54) taken 1329 times.
1379 } else if (c == '\\') {
225 50 escape_end_of_string = true;
226 }
227 }
228 }
229 }
230
231
2/2
✓ Branch 0 (56→57) taken 10 times.
✓ Branch 1 (56→58) taken 15 times.
25 if (ttype == CX_JSON_NO_TOKEN) {
232 10 return CX_JSON_NO_DATA;
233 } else {
234 // uncompleted token
235 15 cxstring uncompleted = cx_strn(json->buffer.space + token_part_start, json->buffer.size - token_part_start);
236
2/2
✓ Branch 0 (60→61) taken 9 times.
✓ Branch 1 (60→69) taken 6 times.
15 if (json->uncompleted_tokentype == CX_JSON_NO_TOKEN) {
237 assert(json->uncompleted_content.ptr == NULL);
238 18 json->uncompleted_content = cx_strdup(uncompleted);
239
1/2
✗ Branch 0 (66→67) not taken.
✓ Branch 1 (66→68) taken 9 times.
9 if (json->uncompleted_content.ptr == NULL) {
240 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
241 }
242 9 json->uncompleted_tokentype = ttype;
243 } else {
244 // previously we already had an uncompleted token
245 // combine the uncompleted token with the current token
246 6 cxmutstr s = cx_strcat(json->uncompleted_content, 1, uncompleted);
247
1/2
✗ Branch 0 (70→71) not taken.
✓ Branch 1 (70→72) taken 6 times.
6 if (s.ptr == NULL) {
248 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
249 }
250 6 json->uncompleted_content = s;
251 }
252 // advance the buffer position - we saved the stuff in the uncompleted token
253 15 json->buffer.pos += uncompleted.length;
254 15 return CX_JSON_INCOMPLETE_DATA;
255 }
256 }
257
258 // converts a Unicode codepoint to utf8
259 25 static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) {
260
2/2
✓ Branch 0 (2→3) taken 5 times.
✓ Branch 1 (2→4) taken 20 times.
25 if (codepoint <= 0x7F) {
261 5 *output_buf = (char)codepoint;
262 5 return 1;
263
2/2
✓ Branch 0 (4→5) taken 12 times.
✓ Branch 1 (4→6) taken 8 times.
20 } else if (codepoint <= 0x7FF) {
264 12 output_buf[0] = (char)(0xC0 | ((codepoint >> 6) & 0x1F));
265 12 output_buf[1] = (char)(0x80 | (codepoint & 0x3F));
266 12 return 2;
267
2/2
✓ Branch 0 (6→7) taken 5 times.
✓ Branch 1 (6→8) taken 3 times.
8 } else if (codepoint <= 0xFFFF) {
268 5 output_buf[0] = (char)(0xE0 | ((codepoint >> 12) & 0x0F));
269 5 output_buf[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
270 5 output_buf[2] = (char)(0x80 | (codepoint & 0x3F));
271 5 return 3;
272
1/2
✓ Branch 0 (8→9) taken 3 times.
✗ Branch 1 (8→10) not taken.
3 } else if (codepoint <= 0x10FFFF) {
273 3 output_buf[0] = (char)(0xF0 | ((codepoint >> 18) & 0x07));
274 3 output_buf[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F));
275 3 output_buf[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
276 3 output_buf[3] = (char)(0x80 | (codepoint & 0x3F));
277 3 return 4;
278 }
279
280 return 0; // LCOV_EXCL_LINE
281 }
282
283 // converts a utf16 surrogate pair to utf8
284 3 static inline uint32_t utf16pair_to_codepoint(uint16_t c0, uint16_t c1) {
285 3 return ((c0 - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000;
286 }
287
288 32 static unsigned unescape_unicode_string(cxstring str, char *utf8buf) {
289 // str is supposed to start with "\uXXXX" or "\uXXXX\uXXXX"
290 // remaining bytes in the string are ignored (str may be larger!)
291
292
4/6
✓ Branch 0 (2→3) taken 31 times.
✓ Branch 1 (2→5) taken 1 times.
✓ Branch 2 (3→4) taken 31 times.
✗ Branch 3 (3→5) not taken.
✗ Branch 4 (4→5) not taken.
✓ Branch 5 (4→6) taken 31 times.
32 if (str.length < 6 || str.ptr[0] != '\\' || str.ptr[1] != 'u') {
293 1 return 0;
294 }
295
296 31 unsigned utf8len = 0;
297 31 cxstring ustr1 = { str.ptr + 2, 4};
298 uint16_t utf16a, utf16b;
299
2/2
✓ Branch 0 (11→12) taken 30 times.
✓ Branch 1 (11→31) taken 1 times.
62 if (!cx_strtou16_lc(ustr1, &utf16a, 16, "")) {
300 uint32_t codepoint;
301
3/4
✓ Branch 0 (12→13) taken 8 times.
✓ Branch 1 (12→14) taken 22 times.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 8 times.
30 if (utf16a < 0xD800 || utf16a > 0xE000) {
302 // character is in the Basic Multilingual Plane
303 // and encoded as a single utf16 char
304 22 codepoint = utf16a;
305 22 utf8len = codepoint_to_utf8(codepoint, utf8buf);
306
3/4
✓ Branch 0 (15→16) taken 8 times.
✗ Branch 1 (15→31) not taken.
✓ Branch 2 (16→17) taken 7 times.
✓ Branch 3 (16→31) taken 1 times.
8 } else if (utf16a >= 0xD800 && utf16a <= 0xDBFF) {
307 // character is encoded as a surrogate pair
308 // get next 6 bytes
309
1/2
✓ Branch 0 (17→18) taken 7 times.
✗ Branch 1 (17→31) not taken.
7 if (str.length >= 12) {
310
3/4
✓ Branch 0 (18→19) taken 5 times.
✓ Branch 1 (18→31) taken 2 times.
✓ Branch 2 (19→20) taken 5 times.
✗ Branch 3 (19→31) not taken.
7 if (str.ptr[6] == '\\' && str.ptr[7] == 'u') {
311 5 cxstring ustr2 = { str.ptr+8, 4 };
312
2/2
✓ Branch 0 (25→26) taken 4 times.
✓ Branch 1 (25→30) taken 1 times.
10 if (!cx_strtou16_lc(ustr2, &utf16b, 16, "")
313
3/4
✓ Branch 0 (26→27) taken 3 times.
✓ Branch 1 (26→30) taken 1 times.
✓ Branch 2 (27→28) taken 3 times.
✗ Branch 3 (27→30) not taken.
4 && utf16b >= 0xDC00 && utf16b <= 0xDFFF) {
314 3 codepoint = utf16pair_to_codepoint(utf16a, utf16b);
315 3 utf8len = codepoint_to_utf8(codepoint, utf8buf);
316 }
317 }
318 }
319 }
320 }
321 31 return utf8len;
322 }
323
324 209 static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) {
325 // note: this function expects that str contains the enclosing quotes!
326
327 cxmutstr result;
328 209 result.length = 0;
329 209 result.ptr = cxMalloc(a, str.length - 1);
330 if (result.ptr == NULL) return result; // LCOV_EXCL_LINE
331
332 209 bool u = false;
333
2/2
✓ Branch 0 (42→6) taken 1281 times.
✓ Branch 1 (42→43) taken 209 times.
1490 for (size_t i = 1; i < str.length - 1; i++) {
334 1281 char c = str.ptr[i];
335
2/2
✓ Branch 0 (6→7) taken 47 times.
✓ Branch 1 (6→38) taken 1234 times.
1281 if (u) {
336 47 u = false;
337
2/2
✓ Branch 0 (7→8) taken 3 times.
✓ Branch 1 (7→9) taken 44 times.
47 if (c == 'n') {
338 3 c = '\n';
339
2/2
✓ Branch 0 (9→10) taken 6 times.
✓ Branch 1 (9→11) taken 38 times.
44 } else if (c == '"') {
340 6 c = '"';
341
2/2
✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→13) taken 37 times.
38 } else if (c == 't') {
342 1 c = '\t';
343
2/2
✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→15) taken 36 times.
37 } else if (c == 'r') {
344 1 c = '\r';
345
2/2
✓ Branch 0 (15→16) taken 1 times.
✓ Branch 1 (15→17) taken 35 times.
36 } else if (c == '\\') {
346 1 c = '\\';
347
2/2
✓ Branch 0 (17→18) taken 1 times.
✓ Branch 1 (17→19) taken 34 times.
35 } else if (c == '/') {
348 1 c = '/'; // always unescape, we don't need settings here
349
2/2
✓ Branch 0 (19→20) taken 1 times.
✓ Branch 1 (19→21) taken 33 times.
34 } else if (c == 'f') {
350 1 c = '\f';
351
2/2
✓ Branch 0 (21→22) taken 1 times.
✓ Branch 1 (21→23) taken 32 times.
33 } else if (c == 'b') {
352 1 c = '\b';
353
1/2
✓ Branch 0 (23→24) taken 32 times.
✗ Branch 1 (23→36) not taken.
32 } else if (c == 'u') {
354 char utf8buf[4];
355 32 unsigned utf8len = unescape_unicode_string(
356 32 cx_strn(str.ptr + i - 1, str.length - i),
357 utf8buf
358 );
359
2/2
✓ Branch 0 (27→28) taken 25 times.
✓ Branch 1 (27→34) taken 7 times.
32 if(utf8len > 0) {
360
2/2
✓ Branch 0 (28→29) taken 22 times.
✓ Branch 1 (28→30) taken 3 times.
25 i += utf8len < 4 ? 4 : 10;
361 // add all bytes from utf8buf except the last char
362 // to the result (last char will be added below)
363 25 utf8len--;
364 25 c = utf8buf[utf8len];
365
2/2
✓ Branch 0 (33→32) taken 31 times.
✓ Branch 1 (33→35) taken 25 times.
56 for (unsigned x = 0; x < utf8len; x++) {
366 31 result.ptr[result.length++] = utf8buf[x];
367 }
368 } else {
369 // decoding failed, ignore the entire sequence
370 7 result.ptr[result.length++] = '\\';
371 }
372 } else {
373 // TODO: discuss the behavior for unrecognized escape sequences
374 // most parsers throw an error here - we just ignore it
375 result.ptr[result.length++] = '\\'; // LCOV_EXCL_LINE
376 }
377
378 47 result.ptr[result.length++] = c;
379 } else {
380
2/2
✓ Branch 0 (38→39) taken 47 times.
✓ Branch 1 (38→40) taken 1187 times.
1234 if (c == '\\') {
381 47 u = true;
382 } else {
383 1187 result.ptr[result.length++] = c;
384 }
385 }
386 }
387 209 result.ptr[result.length] = 0;
388
389 209 return result;
390 }
391
392 348 static cxmutstr escape_string(cxstring str, bool escape_slash) {
393 // note: this function produces the string without enclosing quotes
394 // the reason is that we don't want to allocate memory just for that
395 348 CxBuffer buf = {0};
396
397 348 bool all_printable = true;
398
2/2
✓ Branch 0 (52→3) taken 1746 times.
✓ Branch 1 (52→53) taken 348 times.
2094 for (size_t i = 0; i < str.length; i++) {
399 1746 unsigned char c = str.ptr[i];
400
4/4
✓ Branch 0 (4→5) taken 1732 times.
✓ Branch 1 (4→8) taken 2 times.
✓ Branch 2 (5→6) taken 1728 times.
✓ Branch 3 (5→8) taken 4 times.
1734 bool escape = c < 0x20 || c == '\\' || c == '"'
401
6/6
✓ Branch 0 (3→4) taken 1734 times.
✓ Branch 1 (3→8) taken 12 times.
✓ Branch 2 (6→7) taken 12 times.
✓ Branch 3 (6→9) taken 1716 times.
✓ Branch 4 (7→8) taken 1 times.
✓ Branch 5 (7→9) taken 11 times.
3480 || (escape_slash && c == '/');
402
403
4/4
✓ Branch 0 (10→11) taken 1659 times.
✓ Branch 1 (10→20) taken 87 times.
✓ Branch 2 (11→12) taken 3 times.
✓ Branch 3 (11→20) taken 1656 times.
1746 if (all_printable && escape) {
404 3 size_t capa = str.length + 32;
405 3 char *space = cxMallocDefault(capa);
406
1/2
✗ Branch 0 (13→14) not taken.
✓ Branch 1 (13→17) taken 3 times.
3 if (space == NULL) return cx_mutstrn(NULL, 0);
407 3 cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND);
408 3 cxBufferWrite(str.ptr, 1, i, &buf);
409 3 all_printable = false;
410 }
411
2/2
✓ Branch 0 (20→21) taken 19 times.
✓ Branch 1 (20→49) taken 1727 times.
1746 if (escape) {
412 19 cxBufferPut(&buf, '\\');
413
2/2
✓ Branch 0 (22→23) taken 4 times.
✓ Branch 1 (22→24) taken 15 times.
19 if (c == '\"') {
414 4 cxBufferPut(&buf, '\"');
415
2/2
✓ Branch 0 (24→25) taken 2 times.
✓ Branch 1 (24→26) taken 13 times.
15 } else if (c == '\n') {
416 2 cxBufferPut(&buf, 'n');
417
2/2
✓ Branch 0 (26→27) taken 2 times.
✓ Branch 1 (26→28) taken 11 times.
13 } else if (c == '\t') {
418 2 cxBufferPut(&buf, 't');
419
2/2
✓ Branch 0 (28→29) taken 2 times.
✓ Branch 1 (28→30) taken 9 times.
11 } else if (c == '\r') {
420 2 cxBufferPut(&buf, 'r');
421
2/2
✓ Branch 0 (30→31) taken 2 times.
✓ Branch 1 (30→32) taken 7 times.
9 } else if (c == '\\') {
422 2 cxBufferPut(&buf, '\\');
423
2/2
✓ Branch 0 (32→33) taken 1 times.
✓ Branch 1 (32→34) taken 6 times.
7 } else if (c == '/') {
424 1 cxBufferPut(&buf, '/');
425
2/2
✓ Branch 0 (34→35) taken 2 times.
✓ Branch 1 (34→36) taken 4 times.
6 } else if (c == '\f') {
426 2 cxBufferPut(&buf, 'f');
427
2/2
✓ Branch 0 (36→37) taken 2 times.
✓ Branch 1 (36→38) taken 2 times.
4 } else if (c == '\b') {
428 2 cxBufferPut(&buf, 'b');
429 } else {
430 char code[6];
431
1/2
✓ Branch 0 (38→39) taken 2 times.
✗ Branch 1 (38→40) not taken.
2 snprintf(code, sizeof(code), "u%04x", (unsigned int) c);
432 2 cxBufferPutString(&buf, code);
433 }
434
2/2
✓ Branch 0 (49→50) taken 71 times.
✓ Branch 1 (49→51) taken 1656 times.
1727 } else if (!all_printable) {
435 71 cxBufferPut(&buf, c);
436 }
437 }
438 cxmutstr ret;
439
2/2
✓ Branch 0 (53→54) taken 345 times.
✓ Branch 1 (53→57) taken 3 times.
348 if (all_printable) {
440 // don't copy the string when we don't need to escape anything
441 345 ret = cx_mutstrn((char*)str.ptr, str.length);
442 } else {
443 3 ret = cx_mutstrn(buf.space, buf.size);
444 }
445 348 cxBufferDestroy(&buf);
446 348 return ret;
447 }
448
449 153 static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
450 153 CxMap *map = cxKvListCreateAsMap(allocator, CX_STORE_POINTERS);
451 if (map == NULL) return NULL; // LCOV_EXCL_LINE
452 153 cxSetCompareFunc(map, cxJsonCompare);
453 153 cxSetDestructor(map, cxJsonValueFree);
454 153 return map;
455 }
456
457 153 static void json_free_object_map(CxJsonObject obj) {
458 153 cxMapFree(obj);
459 153 }
460
461 481 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
462 481 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue));
463 if (v == NULL) return NULL; // LCOV_EXCL_LINE
464
465 // initialize the value
466 481 v->type = type;
467 481 v->allocator = json->allocator;
468
2/2
✓ Branch 0 (5→6) taken 96 times.
✓ Branch 1 (5→9) taken 385 times.
481 if (type == CX_JSON_ARRAY) {
469
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→13) taken 96 times.
96 if (cx_array_init_a(json->allocator, v->array, 16)) {
470 goto create_json_value_exit_error; // LCOV_EXCL_LINE
471 }
472
2/2
✓ Branch 0 (9→10) taken 96 times.
✓ Branch 1 (9→13) taken 289 times.
385 } else if (type == CX_JSON_OBJECT) {
473 96 v->object = json_create_object_map(json->allocator);
474 if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
475 }
476
477 // add the new value to a possible parent
478
2/2
✓ Branch 0 (13→14) taken 389 times.
✓ Branch 1 (13→25) taken 92 times.
481 if (json->vbuf.size > 0) {
479 389 CxJsonValue *parent = json->vbuf.data[json->vbuf.size - 1];
480 assert(parent != NULL);
481
2/2
✓ Branch 0 (14→15) taken 259 times.
✓ Branch 1 (14→18) taken 130 times.
389 if (parent->type == CX_JSON_ARRAY) {
482
1/2
✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→25) taken 259 times.
259 if (cx_array_add_a(json->allocator, parent->array, v)) {
483 goto create_json_value_exit_error; // LCOV_EXCL_LINE
484 }
485
1/2
✓ Branch 0 (18→19) taken 130 times.
✗ Branch 1 (18→25) not taken.
130 } else if (parent->type == CX_JSON_OBJECT) {
486 // the member was already created after parsing the name
487 // store the pointer of the uncompleted value in the map
488 assert(json->uncompleted_member_name.ptr != NULL);
489
1/2
✗ Branch 0 (22→23) not taken.
✓ Branch 1 (22→24) taken 130 times.
260 if (cxMapPut(parent->object, json->uncompleted_member_name, v)) {
490 goto create_json_value_exit_error; // LCOV_EXCL_LINE
491 }
492 130 cx_strfree_a(json->allocator, &json->uncompleted_member_name);
493 } else {
494 assert(false); // LCOV_EXCL_LINE
495 }
496 }
497
498 // add the new value to the stack, if it is an array or object
499
4/4
✓ Branch 0 (25→26) taken 385 times.
✓ Branch 1 (25→27) taken 96 times.
✓ Branch 2 (26→27) taken 96 times.
✓ Branch 3 (26→34) taken 289 times.
481 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) {
500
2/2
✓ Branch 0 (27→28) taken 5 times.
✓ Branch 1 (27→33) taken 187 times.
192 if (json->vbuf.size >= json->vbuf.capacity) {
501 int alloc_error;
502
2/2
✓ Branch 0 (28→29) taken 2 times.
✓ Branch 1 (28→30) taken 3 times.
5 if (json->vbuf.data == json->vbuf_internal) {
503 2 alloc_error = cx_array_copy_to_new(json->vbuf, json->vbuf.size+1);
504 } else {
505 3 alloc_error = cx_array_reserve(json->vbuf, json->vbuf.size+1);
506 }
507
1/2
✗ Branch 0 (31→32) not taken.
✓ Branch 1 (31→33) taken 5 times.
5 if (alloc_error) {
508 goto create_json_value_exit_error; // LCOV_EXCL_LINE
509 }
510 }
511 192 json->vbuf.data[json->vbuf.size] = v;
512 192 json->vbuf.size++;
513 }
514
515 // if currently no value is parsed, this is now the value of interest
516
2/2
✓ Branch 0 (34→35) taken 92 times.
✓ Branch 1 (34→36) taken 389 times.
481 if (json->parsed == NULL) {
517 92 json->parsed = v;
518 }
519
520 481 return v;
521 // LCOV_EXCL_START
522 create_json_value_exit_error:
523 cxJsonValueFree(v);
524 return NULL;
525 // LCOV_EXCL_STOP
526 }
527
528 #define JP_STATE_VALUE_BEGIN 0
529 #define JP_STATE_VALUE_END 10
530 #define JP_STATE_VALUE_BEGIN_OBJ 1
531 #define JP_STATE_OBJ_SEP_OR_CLOSE 11
532 #define JP_STATE_VALUE_BEGIN_AR 2
533 #define JP_STATE_ARRAY_SEP_OR_CLOSE 12
534 #define JP_STATE_OBJ_NAME_OR_CLOSE 5
535 #define JP_STATE_OBJ_NAME 6
536 #define JP_STATE_OBJ_COLON 7
537
538 90 void cxJsonInit(CxJson *json, const CxAllocator *allocator) {
539
2/2
✓ Branch 0 (2→3) taken 74 times.
✓ Branch 1 (2→4) taken 16 times.
90 if (allocator == NULL) {
540 74 allocator = cxDefaultAllocator;
541 }
542
543 90 memset(json, 0, sizeof(CxJson));
544 90 json->allocator = allocator;
545
546 90 cx_array_init_fixed(json->states, json->states_internal, 1);
547 90 json->states.data[0] = JP_STATE_VALUE_BEGIN;
548 90 cx_array_init_fixed(json->vbuf, json->vbuf_internal, 0);
549 90 }
550
551 90 void cxJsonDestroy(CxJson *json) {
552 90 cxBufferDestroy(&json->buffer);
553
2/2
✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→5) taken 86 times.
90 if (json->states.data != json->states_internal) {
554 4 cx_array_free(json->states);
555 }
556
2/2
✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 88 times.
90 if (json->vbuf.data != json->vbuf_internal) {
557 2 cx_array_free(json->vbuf);
558 }
559 90 cxJsonValueFree(json->parsed);
560 90 json->parsed = NULL;
561 90 json->uncompleted_tokentype = CX_JSON_NO_TOKEN;
562 90 cx_strfree(&json->uncompleted_content);
563 90 cx_strfree_a(json->allocator, &json->uncompleted_member_name);
564 90 }
565
566 10 void cxJsonReset(CxJson *json) {
567 10 const CxAllocator *allocator = json->allocator;
568 10 cxJsonDestroy(json);
569 10 cxJsonInit(json, allocator);
570 10 }
571
572 123 int cxJsonFilln(CxJson *json, const char *buf, size_t size) {
573
2/2
✓ Branch 0 (3→4) taken 113 times.
✓ Branch 1 (3→9) taken 10 times.
123 if (cxBufferEof(&json->buffer)) {
574 // reinitialize the buffer
575 113 cxBufferDestroy(&json->buffer);
576
2/2
✓ Branch 0 (5→6) taken 4 times.
✓ Branch 1 (5→7) taken 109 times.
113 if (buf == NULL) buf = ""; // buffer must not be initialized with NULL
577 113 cxBufferInit(&json->buffer, NULL, (char*) buf,
578 size, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
579 113 json->buffer.size = size;
580 113 return 0;
581 } else {
582 10 return size != cxBufferAppend(buf, 1, size, &json->buffer);
583 }
584 }
585
586 1184 static void json_add_state(CxJson *json, int state) {
587 // we have guaranteed the necessary space
588 // therefore, we can safely add the state in the simplest way possible
589 1184 json->states.data[json->states.size++] = state;
590 1184 }
591
592 #define return_rec(code) \
593 token_destroy(&token); \
594 return code
595
596 1247 static enum cx_json_status json_parse(CxJson *json) {
597 // Reserve a pointer for a possibly read value
598 1247 CxJsonValue *vbuf = NULL;
599
600 // grab the next token
601 CxJsonToken token;
602 {
603 1247 enum cx_json_status ret = token_parse_next(json, &token);
604
2/2
✓ Branch 0 (3→4) taken 79 times.
✓ Branch 1 (3→5) taken 1168 times.
1247 if (ret != CX_JSON_NO_ERROR) {
605 79 return ret;
606 }
607 }
608
609 // pop the current state
610 assert(json->states.size > 0);
611 1168 int state = json->states.data[--json->states.size];
612
613 // guarantee that at least two more states fit into the array
614 1168 const size_t required_states_depth = json->states.size + 2;
615
2/2
✓ Branch 0 (5→6) taken 37 times.
✓ Branch 1 (5→11) taken 1131 times.
1168 if (required_states_depth >= json->states.capacity) {
616 int alloc_error;
617
2/2
✓ Branch 0 (6→7) taken 4 times.
✓ Branch 1 (6→8) taken 33 times.
37 if (json->states.data == json->states_internal) {
618 4 alloc_error = cx_array_copy_to_new(json->states, required_states_depth);
619 } else {
620 33 alloc_error = cx_array_reserve(json->states, required_states_depth);
621 }
622
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 37 times.
37 if (alloc_error) {
623 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
624 }
625 }
626
627
628 // 0 JP_STATE_VALUE_BEGIN value begin
629 // 10 JP_STATE_VALUE_END expect value end
630 // 1 JP_STATE_VALUE_BEGIN_OBJ value begin (inside object)
631 // 11 JP_STATE_OBJ_SEP_OR_CLOSE object, expect separator, objclose
632 // 2 JP_STATE_VALUE_BEGIN_AR value begin (inside array)
633 // 12 JP_STATE_ARRAY_SEP_OR_CLOSE array, expect separator or arrayclose
634 // 5 JP_STATE_OBJ_NAME_OR_CLOSE object, expect name or objclose
635 // 6 JP_STATE_OBJ_NAME object, expect name
636 // 7 JP_STATE_OBJ_COLON object, expect ':'
637
638
2/2
✓ Branch 0 (11→12) taken 492 times.
✓ Branch 1 (11→111) taken 676 times.
1168 if (state < 3) {
639 // push expected end state to the stack
640 492 json_add_state(json, 10 + state);
641
7/7
✓ Branch 0 (13→14) taken 96 times.
✓ Branch 1 (13→21) taken 96 times.
✓ Branch 2 (13→28) taken 9 times.
✓ Branch 3 (13→33) taken 74 times.
✓ Branch 4 (13→44) taken 155 times.
✓ Branch 5 (13→70) taken 60 times.
✓ Branch 6 (13→109) taken 2 times.
492 switch (token.tokentype) {
642 96 case CX_JSON_TOKEN_BEGIN_ARRAY: {
643
1/2
✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→18) taken 96 times.
96 if (json_create_value(json, CX_JSON_ARRAY) == NULL) {
644 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
645 }
646 96 json_add_state(json, JP_STATE_VALUE_BEGIN_AR);
647 96 return_rec(CX_JSON_NO_ERROR);
648 }
649 96 case CX_JSON_TOKEN_BEGIN_OBJECT: {
650
1/2
✗ Branch 0 (22→23) not taken.
✓ Branch 1 (22→25) taken 96 times.
96 if (json_create_value(json, CX_JSON_OBJECT) == NULL) {
651 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
652 }
653 96 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE);
654 96 return_rec(CX_JSON_NO_ERROR);
655 }
656 9 case CX_JSON_TOKEN_END_ARRAY: {
657
2/2
✓ Branch 0 (28→29) taken 8 times.
✓ Branch 1 (28→31) taken 1 times.
9 if (state == JP_STATE_VALUE_BEGIN_AR) {
658 // discard the array from the value buffer
659 8 json->vbuf.size--;
660 8 json->states.size--;
661 8 return_rec(CX_JSON_NO_ERROR);
662 } else {
663 1 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
664 }
665 }
666 74 case CX_JSON_TOKEN_STRING: {
667
1/2
✗ Branch 0 (34→35) not taken.
✓ Branch 1 (34→37) taken 74 times.
74 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) {
668 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
669 }
670 74 cxmutstr str = unescape_string(json->allocator, token.content);
671
1/2
✗ Branch 0 (38→39) not taken.
✓ Branch 1 (38→41) taken 74 times.
74 if (str.ptr == NULL) {
672 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
673 }
674 74 vbuf->string = str;
675 74 return_rec(CX_JSON_NO_ERROR);
676 }
677 155 case CX_JSON_TOKEN_INTEGER:
678 case CX_JSON_TOKEN_NUMBER: {
679
2/2
✓ Branch 0 (44→45) taken 133 times.
✓ Branch 1 (44→46) taken 22 times.
155 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER;
680
1/2
✗ Branch 0 (48→49) not taken.
✓ Branch 1 (48→51) taken 155 times.
155 if (NULL == (vbuf = json_create_value(json, type))) {
681 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
682 }
683
2/2
✓ Branch 0 (51→52) taken 133 times.
✓ Branch 1 (51→60) taken 22 times.
155 if (type == CX_JSON_INTEGER) {
684
2/2
✓ Branch 0 (57→58) taken 1 times.
✓ Branch 1 (57→68) taken 132 times.
399 if (cx_strtoi64(token.content, &vbuf->integer, 10)) {
685 1 return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
686 }
687 } else {
688
1/2
✗ Branch 0 (65→66) not taken.
✓ Branch 1 (65→68) taken 22 times.
66 if (cx_strtod(token.content, &vbuf->number)) {
689 // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
690 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE
691 }
692 }
693 154 return_rec(CX_JSON_NO_ERROR);
694 }
695 60 case CX_JSON_TOKEN_LITERAL: {
696
1/2
✗ Branch 0 (71→72) not taken.
✓ Branch 1 (71→74) taken 60 times.
60 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) {
697 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
698 }
699
2/2
✓ Branch 0 (88→89) taken 19 times.
✓ Branch 1 (88→90) taken 41 times.
180 if (0 == cx_strcmp(token.content, "true")) {
700 19 vbuf->literal = CX_JSON_TRUE;
701
2/2
✓ Branch 0 (104→105) taken 21 times.
✓ Branch 1 (104→106) taken 20 times.
123 } else if (0 == cx_strcmp(token.content, "false")) {
702 21 vbuf->literal = CX_JSON_FALSE;
703 } else {
704 20 vbuf->literal = CX_JSON_NULL;
705 }
706 60 return_rec(CX_JSON_NO_ERROR);
707 }
708 2 default: {
709 2 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
710 }
711 }
712
2/2
✓ Branch 0 (111→112) taken 259 times.
✓ Branch 1 (111→121) taken 417 times.
676 } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) {
713 // expect ',' or ']'
714
2/2
✓ Branch 0 (112→113) taken 172 times.
✓ Branch 1 (112→116) taken 87 times.
259 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) {
715 172 json_add_state(json, JP_STATE_VALUE_BEGIN_AR);
716 172 return_rec(CX_JSON_NO_ERROR);
717
2/2
✓ Branch 0 (116→117) taken 85 times.
✓ Branch 1 (116→119) taken 2 times.
87 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) {
718 // discard the array from the value buffer
719 85 json->vbuf.size--;
720 85 return_rec(CX_JSON_NO_ERROR);
721 } else {
722 2 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
723 }
724
4/4
✓ Branch 0 (121→122) taken 321 times.
✓ Branch 1 (121→123) taken 96 times.
✓ Branch 2 (122→123) taken 59 times.
✓ Branch 3 (122→138) taken 262 times.
417 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) {
725
4/4
✓ Branch 0 (123→124) taken 96 times.
✓ Branch 1 (123→127) taken 59 times.
✓ Branch 2 (124→125) taken 19 times.
✓ Branch 3 (124→127) taken 77 times.
155 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
726 // discard the obj from the value buffer
727 19 json->vbuf.size--;
728 19 return_rec(CX_JSON_NO_ERROR);
729 } else {
730 // expect string
731
2/2
✓ Branch 0 (127→128) taken 1 times.
✓ Branch 1 (127→130) taken 135 times.
136 if (token.tokentype != CX_JSON_TOKEN_STRING) {
732 1 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
733 }
734
735 // add new entry
736 135 cxmutstr name = unescape_string(json->allocator, token.content);
737
1/2
✗ Branch 0 (131→132) not taken.
✓ Branch 1 (131→134) taken 135 times.
135 if (name.ptr == NULL) {
738 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
739 }
740 assert(json->uncompleted_member_name.ptr == NULL);
741 135 json->uncompleted_member_name = name;
742 assert(json->vbuf.size > 0);
743
744 // next state
745 135 json_add_state(json, JP_STATE_OBJ_COLON);
746 135 return_rec(CX_JSON_NO_ERROR);
747 }
748
2/2
✓ Branch 0 (138→139) taken 135 times.
✓ Branch 1 (138→145) taken 127 times.
262 } else if (state == JP_STATE_OBJ_COLON) {
749 // expect ':'
750
2/2
✓ Branch 0 (139→140) taken 1 times.
✓ Branch 1 (139→142) taken 134 times.
135 if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) {
751 1 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
752 }
753 // next state
754 134 json_add_state(json, JP_STATE_VALUE_BEGIN_OBJ);
755 134 return_rec(CX_JSON_NO_ERROR);
756
1/2
✓ Branch 0 (145→146) taken 127 times.
✗ Branch 1 (145→155) not taken.
127 } else if (state == JP_STATE_OBJ_SEP_OR_CLOSE) {
757 // expect ',' or '}'
758
2/2
✓ Branch 0 (146→147) taken 59 times.
✓ Branch 1 (146→150) taken 68 times.
127 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) {
759 59 json_add_state(json, JP_STATE_OBJ_NAME);
760 59 return_rec(CX_JSON_NO_ERROR);
761
2/2
✓ Branch 0 (150→151) taken 64 times.
✓ Branch 1 (150→153) taken 4 times.
68 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
762 // discard the obj from the value buffer
763 64 json->vbuf.size--;
764 64 return_rec(CX_JSON_NO_ERROR);
765 } else {
766 4 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
767 }
768 } else {
769 // should be unreachable
770 assert(false);
771 return_rec(-1); // LCOV_EXCL_LINE
772 }
773 }
774
775 170 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
776 // initialize output value
777 170 *value = &cx_json_value_nothing;
778
779 // check if the buffer has been filled
780
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 169 times.
170 if (json->buffer.space == NULL) {
781 1 return CX_JSON_NULL_DATA;
782 }
783
784 // parse data
785 CxJsonStatus result;
786 do {
787 1247 result = json_parse(json);
788
4/4
✓ Branch 0 (6→7) taken 1156 times.
✓ Branch 1 (6→9) taken 91 times.
✓ Branch 2 (7→8) taken 78 times.
✓ Branch 3 (7→9) taken 1078 times.
1247 if (result == CX_JSON_NO_ERROR && json->states.size == 1) {
789 // final state reached
790 assert(json->states.data[0] == JP_STATE_VALUE_END);
791 assert(json->vbuf.size == 0);
792
793 // write output value
794 78 *value = json->parsed;
795 78 json->parsed = NULL;
796
797 // re-initialize state machine
798 78 json->states.data[0] = JP_STATE_VALUE_BEGIN;
799
800 78 return CX_JSON_NO_ERROR;
801 }
802
2/2
✓ Branch 0 (9→10) taken 1078 times.
✓ Branch 1 (9→11) taken 91 times.
1169 } while (result == CX_JSON_NO_ERROR);
803
804 // the parser might think there is no data
805 // but when we did not reach the final state,
806 // we know that there must be more to come
807
4/4
✓ Branch 0 (11→12) taken 53 times.
✓ Branch 1 (11→14) taken 38 times.
✓ Branch 2 (12→13) taken 4 times.
✓ Branch 3 (12→14) taken 49 times.
91 if (result == CX_JSON_NO_DATA && json->states.size > 1) {
808 4 return CX_JSON_INCOMPLETE_DATA;
809 }
810
811 87 return result;
812 }
813
814 50 CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
815 cxstring str, CxJsonValue **value) {
816 50 *value = &cx_json_value_nothing;
817 CxJson parser;
818 50 cxJsonInit(&parser, allocator);
819
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→12) taken 50 times.
150 if (cxJsonFill(&parser, str)) {
820 // LCOV_EXCL_START
821 cxJsonDestroy(&parser);
822 return CX_JSON_BUFFER_ALLOC_FAILED;
823 // LCOV_EXCL_STOP
824 }
825 50 CxJsonStatus status = cxJsonNext(&parser, value);
826 // check if we consume the total string
827 50 CxJsonValue *chk_value = NULL;
828 50 CxJsonStatus chk_status = CX_JSON_NO_DATA;
829
2/2
✓ Branch 0 (13→14) taken 43 times.
✓ Branch 1 (13→15) taken 7 times.
50 if (status == CX_JSON_NO_ERROR) {
830 43 chk_status = cxJsonNext(&parser, &chk_value);
831 }
832 50 cxJsonDestroy(&parser);
833
2/2
✓ Branch 0 (16→17) taken 48 times.
✓ Branch 1 (16→18) taken 2 times.
50 if (chk_status == CX_JSON_NO_DATA) {
834 48 return status;
835 } else {
836 2 cxJsonValueFree(*value);
837 // if chk_value is nothing, the free is harmless
838 2 cxJsonValueFree(chk_value);
839 2 *value = &cx_json_value_nothing;
840 2 return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN;
841 }
842
843 }
844
845 21742 void cxJsonValueFree(CxJsonValue *value) {
846
4/4
✓ Branch 0 (2→3) taken 21666 times.
✓ Branch 1 (2→4) taken 76 times.
✓ Branch 2 (3→4) taken 3 times.
✓ Branch 3 (3→5) taken 21663 times.
21742 if (value == NULL || value->type == CX_JSON_NOTHING) return;
847
4/4
✓ Branch 0 (5→6) taken 153 times.
✓ Branch 1 (5→8) taken 260 times.
✓ Branch 2 (5→14) taken 104 times.
✓ Branch 3 (5→16) taken 21146 times.
21663 switch (value->type) {
848 153 case CX_JSON_OBJECT: {
849 153 json_free_object_map(value->object);
850 153 break;
851 }
852 260 case CX_JSON_ARRAY: {
853
2/2
✓ Branch 0 (11→9) taken 20636 times.
✓ Branch 1 (11→12) taken 260 times.
20896 for (size_t i = 0; i < value->array.size; i++) {
854 20636 cxJsonValueFree(value->array.data[i]);
855 }
856 260 cx_array_free_a(value->allocator, value->array);
857 260 break;
858 }
859 104 case CX_JSON_STRING: {
860 104 cxFree(value->allocator, value->string.ptr);
861 104 break;
862 }
863 21146 default: {
864 21146 break;
865 }
866 }
867 21663 cxFree(value->allocator, value);
868 }
869
870 57 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) {
871
2/2
✓ Branch 0 (2→3) taken 3 times.
✓ Branch 1 (2→4) taken 54 times.
57 if (allocator == NULL) allocator = cxDefaultAllocator;
872 57 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
873
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 57 times.
57 if (v == NULL) return NULL;
874 57 v->allocator = allocator;
875 57 v->type = CX_JSON_OBJECT;
876 57 v->object = json_create_object_map(allocator);
877 if (v->object == NULL) { // LCOV_EXCL_START
878 cxFree(allocator, v);
879 return NULL;
880 // LCOV_EXCL_STOP
881 }
882 57 return v;
883 }
884
885 164 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity) {
886
2/2
✓ Branch 0 (2→3) taken 101 times.
✓ Branch 1 (2→4) taken 63 times.
164 if (allocator == NULL) allocator = cxDefaultAllocator;
887 164 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
888
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 164 times.
164 if (v == NULL) return NULL;
889 164 v->allocator = allocator;
890 164 v->type = CX_JSON_ARRAY;
891
2/2
✓ Branch 0 (7→8) taken 133 times.
✓ Branch 1 (7→12) taken 31 times.
164 if (capacity > 0) {
892
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→13) taken 133 times.
133 if (cx_array_init_a(allocator, v->array, capacity)) {
893 // LCOV_EXCL_START
894 cxFree(allocator, v);
895 return NULL;
896 // LCOV_EXCL_STOP
897 }
898 } else {
899 31 v->array.data = NULL;
900 31 v->array.size = v->array.capacity = 0;
901 }
902 164 return v;
903 }
904
905 51 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) {
906
2/2
✓ Branch 0 (2→3) taken 17 times.
✓ Branch 1 (2→4) taken 34 times.
51 if (allocator == NULL) allocator = cxDefaultAllocator;
907 51 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
908
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 51 times.
51 if (v == NULL) return NULL;
909 51 v->allocator = allocator;
910 51 v->type = CX_JSON_NUMBER;
911 51 v->number = num;
912 51 return v;
913 }
914
915 20825 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) {
916
2/2
✓ Branch 0 (2→3) taken 12 times.
✓ Branch 1 (2→4) taken 20813 times.
20825 if (allocator == NULL) allocator = cxDefaultAllocator;
917 20825 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
918
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 20825 times.
20825 if (v == NULL) return NULL;
919 20825 v->allocator = allocator;
920 20825 v->type = CX_JSON_INTEGER;
921 20825 v->integer = num;
922 20825 return v;
923 }
924
925 30 CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) {
926
2/2
✓ Branch 0 (2→3) taken 8 times.
✓ Branch 1 (2→4) taken 22 times.
30 if (allocator == NULL) allocator = cxDefaultAllocator;
927 30 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
928
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 30 times.
30 if (v == NULL) return NULL;
929 30 v->allocator = allocator;
930 30 v->type = CX_JSON_STRING;
931 60 cxmutstr s = cx_strdup_a(allocator, str);
932
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→15) taken 30 times.
30 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; }
933 30 v->string = s;
934 30 return v;
935 }
936
937 55 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) {
938
2/2
✓ Branch 0 (2→3) taken 9 times.
✓ Branch 1 (2→4) taken 46 times.
55 if (allocator == NULL) allocator = cxDefaultAllocator;
939 55 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
940
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 55 times.
55 if (v == NULL) return NULL;
941 55 v->allocator = allocator;
942 55 v->type = CX_JSON_LITERAL;
943 55 v->literal = lit;
944 55 return v;
945 }
946
947 // LCOV_EXCL_START
948 // never called as long as malloc() does not return NULL
949 static void json_arr_free_temp(CxJsonValue** values, size_t count) {
950 for (size_t i = 0; i < count; i++) {
951 if (values[i] == NULL) break;
952 cxJsonValueFree(values[i]);
953 }
954 cxFreeDefault(values);
955 }
956 // LCOV_EXCL_STOP
957
958 7 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) {
959 7 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
960
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 7 times.
7 if (values == NULL) return -1;
961
2/2
✓ Branch 0 (11→6) taken 21 times.
✓ Branch 1 (11→12) taken 7 times.
28 for (size_t i = 0; i < count; i++) {
962 21 values[i] = cxJsonCreateNumber(arr->allocator, num[i]);
963
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 21 times.
21 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
964 }
965 7 int ret = cxJsonArrAddValues(arr, values, count);
966 7 cxFreeDefault(values);
967 7 return ret;
968 }
969
970 121 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) {
971 121 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
972
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 121 times.
121 if (values == NULL) return -1;
973
2/2
✓ Branch 0 (11→6) taken 20054 times.
✓ Branch 1 (11→12) taken 121 times.
20175 for (size_t i = 0; i < count; i++) {
974 20054 values[i] = cxJsonCreateInteger(arr->allocator, num[i]);
975
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 20054 times.
20054 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
976 }
977 121 int ret = cxJsonArrAddValues(arr, values, count);
978 121 cxFreeDefault(values);
979 121 return ret;
980 }
981
982 1 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) {
983 1 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
984
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 1 times.
1 if (values == NULL) return -1;
985
2/2
✓ Branch 0 (20→6) taken 2 times.
✓ Branch 1 (20→21) taken 1 times.
3 for (size_t i = 0; i < count; i++) {
986
1/2
✓ Branch 0 (6→7) taken 2 times.
✗ Branch 1 (6→8) not taken.
4 values[i] = cxJsonCreateString(arr->allocator, str[i]);
987
1/2
✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→19) taken 2 times.
2 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
988 }
989 1 int ret = cxJsonArrAddValues(arr, values, count);
990 1 cxFreeDefault(values);
991 1 return ret;
992 }
993
994 6 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) {
995 6 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
996
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 6 times.
6 if (values == NULL) return -1;
997
2/2
✓ Branch 0 (15→6) taken 12 times.
✓ Branch 1 (15→16) taken 6 times.
18 for (size_t i = 0; i < count; i++) {
998 36 values[i] = cxJsonCreateString(arr->allocator, str[i]);
999
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→14) taken 12 times.
12 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
1000 }
1001 6 int ret = cxJsonArrAddValues(arr, values, count);
1002 6 cxFreeDefault(values);
1003 6 return ret;
1004 }
1005
1006 7 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) {
1007 7 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
1008
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 7 times.
7 if (values == NULL) return -1;
1009
2/2
✓ Branch 0 (11→6) taken 21 times.
✓ Branch 1 (11→12) taken 7 times.
28 for (size_t i = 0; i < count; i++) {
1010 21 values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]);
1011
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 21 times.
21 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
1012 }
1013 7 int ret = cxJsonArrAddValues(arr, values, count);
1014 7 cxFreeDefault(values);
1015 7 return ret;
1016 }
1017
1018 154 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) {
1019 assert(arr->type == CX_JSON_ARRAY);
1020 154 return cx_array_add_array_a(arr->allocator, arr->array, val, count);
1021 }
1022
1023 592 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
1024 592 return cxMapPut(obj->object, name, child);
1025 }
1026
1027 12 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
1028 12 CxJsonValue* v = cxJsonCreateObj(obj->allocator);
1029
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 12 times.
12 if (v == NULL) return NULL;
1030
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→13) taken 12 times.
24 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1031 12 return v;
1032 }
1033
1034 34 CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity) {
1035 34 CxJsonValue* v = cxJsonCreateArr(obj->allocator, capacity);
1036
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 34 times.
34 if (v == NULL) return NULL;
1037
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→13) taken 34 times.
68 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1038 34 return v;
1039 }
1040
1041 6 CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num) {
1042 6 CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num);
1043
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 6 times.
6 if (v == NULL) return NULL;
1044
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→13) taken 6 times.
12 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1045 6 return v;
1046 }
1047
1048 430 CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num) {
1049 430 CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num);
1050
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 430 times.
430 if (v == NULL) return NULL;
1051
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→13) taken 430 times.
860 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1052 430 return v;
1053 }
1054
1055 2 CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str) {
1056 4 CxJsonValue* v = cxJsonCreateString(obj->allocator, str);
1057
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 2 times.
2 if (v == NULL) return NULL;
1058
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→17) taken 2 times.
4 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1059 2 return v;
1060 }
1061
1062 8 CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
1063 8 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit);
1064
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 8 times.
8 if (v == NULL) return NULL;
1065
1/2
✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→13) taken 8 times.
16 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;}
1066 8 return v;
1067 }
1068
1069 35 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
1070
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 34 times.
35 if (index >= value->array.size) {
1071 1 return &cx_json_value_nothing;
1072 }
1073 34 return value->array.data[index];
1074 }
1075
1076 2 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
1077
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 1 times.
2 if (index >= value->array.size) {
1078 1 return NULL;
1079 }
1080 1 CxJsonValue *ret = value->array.data[index];
1081 1 cx_array_remove(value->array, index);
1082 1 return ret;
1083 }
1084
1085 3 char *cxJsonAsString(const CxJsonValue *value) {
1086 3 return value->string.ptr;
1087 }
1088
1089 24 cxstring cxJsonAsCxString(const CxJsonValue *value) {
1090 48 return cx_strcast(value->string);
1091 }
1092
1093 1 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
1094 1 return value->string;
1095 }
1096
1097 254 double cxJsonAsDouble(const CxJsonValue *value) {
1098
2/2
✓ Branch 0 (2→3) taken 107 times.
✓ Branch 1 (2→4) taken 147 times.
254 if (value->type == CX_JSON_INTEGER) {
1099 107 return (double) value->integer;
1100 } else {
1101 147 return value->number;
1102 }
1103 }
1104
1105 34 int64_t cxJsonAsInteger(const CxJsonValue *value) {
1106
2/2
✓ Branch 0 (2→3) taken 29 times.
✓ Branch 1 (2→4) taken 5 times.
34 if (value->type == CX_JSON_INTEGER) {
1107 29 return value->integer;
1108 } else {
1109 5 return (int64_t) value->number;
1110 }
1111 }
1112
1113 81 CxIterator cxJsonArrIter(const CxJsonValue *value) {
1114 81 return cx_array_iterator_ptr(value->array);
1115 }
1116
1117 74 CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
1118 74 return cxMapIterator(value->object);
1119 }
1120
1121 47 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
1122 47 CxJsonValue *v = cxMapGet(value->object, name);
1123
2/2
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→7) taken 46 times.
47 if (v == NULL) {
1124 1 return &cx_json_value_nothing;
1125 } else {
1126 46 return v;
1127 }
1128 }
1129
1130 2 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
1131 2 CxJsonValue *v = NULL;
1132 2 cxMapRemoveAndGet(value->object, name, &v);
1133 2 return v;
1134 }
1135
1136 83 CxJsonWriter cxJsonWriterCompact(void) {
1137 83 return (CxJsonWriter) {
1138 false,
1139 6,
1140 false,
1141 4,
1142 false
1143 };
1144 }
1145
1146 4 CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
1147 4 return (CxJsonWriter) {
1148 true,
1149 6,
1150 use_spaces,
1151 4,
1152 false
1153 };
1154 }
1155
1156 60 static int cx_json_writer_indent(
1157 void *target,
1158 cx_write_func wfunc,
1159 const CxJsonWriter *settings,
1160 unsigned int depth
1161 ) {
1162
2/2
✓ Branch 0 (2→3) taken 4 times.
✓ Branch 1 (2→4) taken 56 times.
60 if (depth == 0) return 0;
1163
1164 // determine the width and characters to use
1165 const char* indent; // for 32 prepared chars
1166 56 size_t width = depth;
1167
2/2
✓ Branch 0 (4→5) taken 41 times.
✓ Branch 1 (4→8) taken 15 times.
56 if (settings->indent_space) {
1168
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 41 times.
41 if (settings->indent == 0) return 0;
1169 41 width *= settings->indent;
1170 41 indent = " ";
1171 } else {
1172 15 indent = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
1173 }
1174
1175 // calculate the number of write calls and write
1176 56 size_t full = width / 32;
1177 56 size_t remaining = width % 32;
1178
2/2
✓ Branch 0 (14→10) taken 5 times.
✓ Branch 1 (14→15) taken 56 times.
61 for (size_t i = 0; i < full; i++) {
1179
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 5 times.
5 if (32 != wfunc(indent, 1, 32, target)) return 1;
1180 }
1181
1/2
✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→18) taken 56 times.
56 if (remaining != wfunc(indent, 1, remaining, target)) return 1;
1182
1183 56 return 0;
1184 }
1185
1186
1187 1002 int cx_json_write_rec(
1188 void *target,
1189 const CxJsonValue *value,
1190 cx_write_func wfunc,
1191 const CxJsonWriter *settings,
1192 unsigned int depth
1193 ) {
1194 // keep track of written items
1195 // the idea is to reduce the number of jumps for error checking
1196 1002 size_t actual = 0, expected = 0;
1197
1198 // small buffer for number to string conversions
1199 char numbuf[40];
1200
1201 // recursively write the values
1202
7/8
✓ Branch 0 (2→3) taken 73 times.
✓ Branch 1 (2→52) taken 80 times.
✓ Branch 2 (2→71) taken 27 times.
✓ Branch 3 (2→83) taken 43 times.
✓ Branch 4 (2→112) taken 719 times.
✓ Branch 5 (2→114) taken 59 times.
✓ Branch 6 (2→123) taken 1 times.
✗ Branch 7 (2→124) not taken.
1002 switch (value->type) {
1203 73 case CX_JSON_OBJECT: {
1204 73 const char *begin_obj = "{\n";
1205
2/2
✓ Branch 0 (3→4) taken 18 times.
✓ Branch 1 (3→6) taken 55 times.
73 if (settings->pretty) {
1206 18 actual += wfunc(begin_obj, 1, 2, target);
1207 18 expected += 2;
1208 } else {
1209 55 actual += wfunc(begin_obj, 1, 1, target);
1210 55 expected++;
1211 }
1212 73 depth++;
1213 73 CxMapIterator member_iter = cxJsonObjIter(value);
1214
3/4
✓ Branch 0 (42→43) taken 321 times.
✓ Branch 1 (42→45) taken 73 times.
✓ Branch 2 (44→10) taken 321 times.
✗ Branch 3 (44→45) not taken.
394 cx_foreach(const CxMapEntry *, member, member_iter) {
1215 // possible indentation
1216
2/2
✓ Branch 0 (10→11) taken 42 times.
✓ Branch 1 (10→14) taken 279 times.
321 if (settings->pretty) {
1217
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→14) taken 42 times.
42 if (cx_json_writer_indent(target, wfunc, settings, depth)) {
1218 return 1; // LCOV_EXCL_LINE
1219 }
1220 }
1221
1222 // the name
1223 321 actual += wfunc("\"", 1, 1, target);
1224 321 cxstring key = cx_hash_key_as_string(member->key);
1225 321 cxmutstr name = escape_string(key, settings->escape_slash);
1226 321 actual += wfunc(name.ptr, 1, name.length, target);
1227 321 actual += wfunc("\"", 1, 1, target);
1228 321 const char *obj_name_sep = ": ";
1229
2/2
✓ Branch 0 (19→20) taken 42 times.
✓ Branch 1 (19→22) taken 279 times.
321 if (settings->pretty) {
1230 42 actual += wfunc(obj_name_sep, 1, 2, target);
1231 42 expected += 4 + name.length;
1232 } else {
1233 279 actual += wfunc(obj_name_sep, 1, 1, target);
1234 279 expected += 3 + name.length;
1235 }
1236
2/2
✓ Branch 0 (24→25) taken 1 times.
✓ Branch 1 (24→26) taken 320 times.
321 if (name.ptr != key.ptr) {
1237 1 cx_strfree(&name);
1238 }
1239
1240 // the value
1241
1/2
✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→29) taken 321 times.
321 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1;
1242
1243 // end of object-value
1244
2/2
✓ Branch 0 (29→30) taken 256 times.
✓ Branch 1 (29→35) taken 65 times.
321 if (member_iter.index < member_iter.elem_count - 1) {
1245 256 const char *obj_value_sep = ",\n";
1246
2/2
✓ Branch 0 (30→31) taken 24 times.
✓ Branch 1 (30→33) taken 232 times.
256 if (settings->pretty) {
1247 24 actual += wfunc(obj_value_sep, 1, 2, target);
1248 24 expected += 2;
1249 } else {
1250 232 actual += wfunc(obj_value_sep, 1, 1, target);
1251 232 expected++;
1252 }
1253 } else {
1254
2/2
✓ Branch 0 (35→36) taken 18 times.
✓ Branch 1 (35→38) taken 47 times.
65 if (settings->pretty) {
1255 18 actual += wfunc("\n", 1, 1, target);
1256 18 expected ++;
1257 }
1258 }
1259 }
1260 73 depth--;
1261
2/2
✓ Branch 0 (45→46) taken 18 times.
✓ Branch 1 (45→49) taken 55 times.
73 if (settings->pretty) {
1262
1/2
✗ Branch 0 (47→48) not taken.
✓ Branch 1 (47→49) taken 18 times.
18 if (cx_json_writer_indent(target, wfunc, settings, depth)) return 1;
1263 }
1264 73 actual += wfunc("}", 1, 1, target);
1265 73 expected++;
1266 73 break;
1267 }
1268 80 case CX_JSON_ARRAY: {
1269 80 actual += wfunc("[", 1, 1, target);
1270 80 expected++;
1271 80 CxIterator iter = cxJsonArrIter(value);
1272
3/4
✓ Branch 0 (66→67) taken 602 times.
✓ Branch 1 (66→69) taken 80 times.
✓ Branch 2 (68→55) taken 602 times.
✗ Branch 3 (68→69) not taken.
682 cx_foreach(CxJsonValue*, element, iter) {
1273
1/2
✗ Branch 0 (56→57) not taken.
✓ Branch 1 (56→58) taken 602 times.
602 if (cx_json_write_rec(
1274 target, element,
1275 wfunc, settings, depth)
1276 ) {
1277 return 1; // LCOV_EXCL_LINE
1278 }
1279
1280
2/2
✓ Branch 0 (58→59) taken 526 times.
✓ Branch 1 (58→64) taken 76 times.
602 if (iter.index < iter.elem_count - 1) {
1281 526 const char *arr_value_sep = ", ";
1282
2/2
✓ Branch 0 (59→60) taken 33 times.
✓ Branch 1 (59→62) taken 493 times.
526 if (settings->pretty) {
1283 33 actual += wfunc(arr_value_sep, 1, 2, target);
1284 33 expected += 2;
1285 } else {
1286 493 actual += wfunc(arr_value_sep, 1, 1, target);
1287 493 expected++;
1288 }
1289 }
1290 }
1291 80 actual += wfunc("]", 1, 1, target);
1292 80 expected++;
1293 80 break;
1294 }
1295 27 case CX_JSON_STRING: {
1296 27 actual += wfunc("\"", 1, 1, target);
1297 54 cxmutstr str = escape_string(cx_strcast(value->string),
1298 27 settings->escape_slash);
1299 27 actual += wfunc(str.ptr, 1, str.length, target);
1300 27 actual += wfunc("\"", 1, 1, target);
1301 27 expected += 2 + str.length;
1302
2/2
✓ Branch 0 (79→80) taken 2 times.
✓ Branch 1 (79→81) taken 25 times.
27 if (str.ptr != value->string.ptr) {
1303 2 cx_strfree(&str);
1304 }
1305 27 break;
1306 }
1307 43 case CX_JSON_NUMBER: {
1308 43 int precision = settings->frac_max_digits;
1309 // because of the way how %g is defined, we need to
1310 // double the precision and truncate ourselves
1311
2/2
✓ Branch 0 (83→84) taken 42 times.
✓ Branch 1 (83→85) taken 1 times.
43 precision = 1 + (precision > 15 ? 30 : 2 * precision);
1312 43 snprintf(numbuf, 40, "%.*g", precision, value->number);
1313 char *dot, *exp;
1314 unsigned char max_digits;
1315 // find the decimal separator and hope that it's one of . or ,
1316 43 dot = strchr(numbuf, '.');
1317
2/2
✓ Branch 0 (86→87) taken 11 times.
✓ Branch 1 (86→88) taken 32 times.
43 if (dot == NULL) {
1318 11 dot = strchr(numbuf, ',');
1319 }
1320
2/2
✓ Branch 0 (88→89) taken 11 times.
✓ Branch 1 (88→90) taken 32 times.
43 if (dot == NULL) {
1321 // no decimal separator found
1322 // output everything until a possible exponent
1323 11 max_digits = 30;
1324 11 dot = numbuf;
1325 } else {
1326 // found a decimal separator
1327 // output everything until the separator
1328 // and set max digits to what the settings say
1329 32 size_t len = dot - numbuf;
1330 32 actual += wfunc(numbuf, 1, len, target);
1331 32 expected += len;
1332 32 max_digits = settings->frac_max_digits;
1333
2/2
✓ Branch 0 (91→92) taken 1 times.
✓ Branch 1 (91→93) taken 31 times.
32 if (max_digits > 15) {
1334 1 max_digits = 15;
1335 }
1336 // locale independent separator
1337
1/2
✓ Branch 0 (93→94) taken 32 times.
✗ Branch 1 (93→96) not taken.
32 if (max_digits > 0) {
1338 32 actual += wfunc(".", 1, 1, target);
1339 32 expected++;
1340 }
1341 32 dot++;
1342 }
1343 // find the exponent
1344 43 exp = strchr(dot, 'e');
1345
2/2
✓ Branch 0 (97→98) taken 42 times.
✓ Branch 1 (97→103) taken 1 times.
43 if (exp == NULL) {
1346 // no exponent - output the rest
1347
1/2
✓ Branch 0 (98→99) taken 42 times.
✗ Branch 1 (98→111) not taken.
42 if (max_digits > 0) {
1348 42 size_t len = strlen(dot);
1349
2/2
✓ Branch 0 (99→100) taken 10 times.
✓ Branch 1 (99→101) taken 32 times.
42 if (len > max_digits) {
1350 10 len = max_digits;
1351 }
1352 42 actual += wfunc(dot, 1, len, target);
1353 42 expected += len;
1354 }
1355 } else {
1356 // exponent found - truncate the frac digits
1357 // and then output the rest
1358
1/2
✓ Branch 0 (103→104) taken 1 times.
✗ Branch 1 (103→108) not taken.
1 if (max_digits > 0) {
1359 1 size_t len = exp - dot - 1;
1360
1/2
✓ Branch 0 (104→105) taken 1 times.
✗ Branch 1 (104→106) not taken.
1 if (len > max_digits) {
1361 1 len = max_digits;
1362 }
1363 1 actual += wfunc(dot, 1, len, target);
1364 1 expected += len;
1365 }
1366 1 actual += wfunc("e", 1, 1, target);
1367 1 expected++;
1368 1 exp++;
1369 1 size_t len = strlen(exp);
1370 1 actual += wfunc(exp, 1, len, target);
1371 1 expected += len;
1372 }
1373 43 break;
1374 }
1375 719 case CX_JSON_INTEGER: {
1376 719 snprintf(numbuf, 32, "%" PRIi64, value->integer);
1377 719 size_t len = strlen(numbuf);
1378 719 actual += wfunc(numbuf, 1, len, target);
1379 719 expected += len;
1380 719 break;
1381 }
1382 59 case CX_JSON_LITERAL: {
1383
2/2
✓ Branch 0 (114→115) taken 19 times.
✓ Branch 1 (114→117) taken 40 times.
59 if (value->literal == CX_JSON_TRUE) {
1384 19 actual += wfunc("true", 1, 4, target);
1385 19 expected += 4;
1386
2/2
✓ Branch 0 (117→118) taken 24 times.
✓ Branch 1 (117→120) taken 16 times.
40 } else if (value->literal == CX_JSON_FALSE) {
1387 24 actual += wfunc("false", 1, 5, target);
1388 24 expected += 5;
1389 } else {
1390 16 actual += wfunc("null", 1, 4, target);
1391 16 expected += 4;
1392 }
1393 59 break;
1394 }
1395 1 case CX_JSON_NOTHING: {
1396 // deliberately supported as an empty string!
1397 // users might want to just write the result
1398 // of a get operation without testing the value
1399 // and therefore this should not blow up
1400 1 break;
1401 }
1402 default: assert(false); // LCOV_EXCL_LINE
1403 }
1404
1405 1002 return expected != actual;
1406 }
1407
1408 17 int cxJsonWrite(
1409 void *target,
1410 const CxJsonValue *value,
1411 cx_write_func wfunc,
1412 const CxJsonWriter *settings
1413 ) {
1414 assert(target != NULL);
1415 assert(value != NULL);
1416 assert(wfunc != NULL);
1417
1418 17 CxJsonWriter writer_default = cxJsonWriterCompact();
1419
2/2
✓ Branch 0 (3→4) taken 2 times.
✓ Branch 1 (3→5) taken 15 times.
17 if (settings == NULL) {
1420 2 settings = &writer_default;
1421 }
1422 17 return cx_json_write_rec(target, value, wfunc, settings, 0);
1423 }
1424
1425 62 static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) {
1426
2/2
✓ Branch 0 (2→3) taken 60 times.
✓ Branch 1 (2→4) taken 2 times.
62 if (allocator == NULL) allocator = cxDefaultAllocator;
1427 CxBuffer buffer;
1428
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→12) taken 62 times.
62 if (cxBufferInit(&buffer, allocator, NULL, 128,
1429 CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
1430 return CX_NULLSTR; // LCOV_EXCL_LINE
1431 }
1432
1/2
✓ Branch 0 (13→14) taken 62 times.
✗ Branch 1 (13→16) not taken.
62 if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0)
1433
1/2
✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→23) taken 62 times.
62 || cxBufferTerminate(&buffer)) {
1434 // LCOV_EXCL_START
1435 buffer.flags &= ~CX_BUFFER_DO_NOT_FREE;
1436 cxBufferDestroy(&buffer);
1437 return CX_NULLSTR;
1438 // LCOV_EXCL_STOP
1439 } else {
1440 cxmutstr str = cx_bstr_m(&buffer);
1441 62 cxBufferDestroy(&buffer);
1442 62 return str;
1443 }
1444
1445 }
1446
1447 61 cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value) {
1448 61 CxJsonWriter writer = cxJsonWriterCompact();
1449 61 return cx_json_to_string(value, allocator, &writer);
1450 }
1451
1452 1 cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value) {
1453 1 CxJsonWriter writer = cxJsonWriterPretty(true);
1454 1 return cx_json_to_string(value, allocator, &writer);
1455 }
1456
1457 952 int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other) {
1458
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 951 times.
952 if (json == other) return 0;
1459
2/4
✓ Branch 0 (4→5) taken 951 times.
✗ Branch 1 (4→6) not taken.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 951 times.
951 if (json == NULL || other == NULL) return -1;
1460
2/2
✓ Branch 0 (7→8) taken 176 times.
✓ Branch 1 (7→24) taken 775 times.
951 if (json->type != other->type) {
1461
2/2
✓ Branch 0 (14→15) taken 82 times.
✓ Branch 1 (14→16) taken 94 times.
176 if (!cxJsonIsNumber(json)) return -1;
1462
2/2
✓ Branch 0 (22→23) taken 54 times.
✓ Branch 1 (22→24) taken 40 times.
94 if (!cxJsonIsNumber(other)) return -1;
1463 }
1464
7/8
✓ Branch 0 (24→25) taken 1 times.
✓ Branch 1 (24→26) taken 69 times.
✓ Branch 2 (24→27) taken 111 times.
✓ Branch 3 (24→36) taken 49 times.
✓ Branch 4 (24→45) taken 447 times.
✓ Branch 5 (24→51) taken 75 times.
✓ Branch 6 (24→53) taken 63 times.
✗ Branch 7 (24→57) not taken.
815 switch (json->type) {
1465 1 case CX_JSON_NOTHING:
1466 1 return 0;
1467 69 case CX_JSON_OBJECT:
1468 69 return cxMapCompare(json->object, other->object);
1469 111 case CX_JSON_ARRAY:
1470
2/2
✓ Branch 0 (27→28) taken 24 times.
✓ Branch 1 (27→29) taken 87 times.
111 if (json->array.size != other->array.size) return -1;
1471
2/2
✓ Branch 0 (34→30) taken 434 times.
✓ Branch 1 (34→35) taken 79 times.
513 for (size_t i = 0; i < json->array.size; i++) {
1472 434 const int d = cxJsonCompare(json->array.data[i], other->array.data[i]);
1473
2/2
✓ Branch 0 (31→32) taken 8 times.
✓ Branch 1 (31→33) taken 426 times.
434 if (d != 0) return d;
1474 }
1475 79 return 0;
1476 49 case CX_JSON_STRING:
1477 196 return cx_strcmp(json->string, other->string);
1478 447 case CX_JSON_INTEGER:
1479
2/2
✓ Branch 0 (45→46) taken 427 times.
✓ Branch 1 (45→48) taken 20 times.
447 if (other->type == CX_JSON_INTEGER) {
1480 427 return cx_vcmp_int64(json->integer, other->integer);
1481 } else {
1482 20 return cx_vcmp_double(cxJsonAsDouble(json), other->number);
1483 }
1484 75 case CX_JSON_NUMBER:
1485 75 return cx_vcmp_double(json->number, cxJsonAsDouble(other));
1486 63 case CX_JSON_LITERAL:
1487
2/2
✓ Branch 0 (53→54) taken 57 times.
✓ Branch 1 (53→55) taken 6 times.
63 return json->literal == other->literal ? 0 : -1;
1488 default: // LCOV_EXCL_START
1489 // unreachable
1490 assert(false);
1491 return -1;
1492 // LCOV_EXCL_STOP
1493 }
1494 }
1495
1496 32 CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) {
1497 32 return cx_json_clone_func(NULL, value, allocator, NULL);
1498 }
1499
1500 403 CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source,
1501 const CxAllocator* allocator, CX_UNUSED void *data) {
1502
3/4
✓ Branch 0 (2→3) taken 402 times.
✓ Branch 1 (2→4) taken 1 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 402 times.
403 if (source == NULL || source->type == CX_JSON_NOTHING) {
1503 1 return &cx_json_value_nothing;
1504 }
1505
2/2
✓ Branch 0 (5→6) taken 31 times.
✓ Branch 1 (5→7) taken 371 times.
402 if (allocator == NULL) allocator = cxDefaultAllocator;
1506
1507 #define return_value(v) { \
1508 CxJsonValue *ret = v; \
1509 if (target == NULL) { \
1510 return ret; \
1511 } else { \
1512 *target = *ret; \
1513 ret->type = CX_JSON_UNINITIALIZED; \
1514 cxJsonValueFree(ret); \
1515 return target; \
1516 } \
1517 }
1518
1519
6/7
✓ Branch 0 (7→8) taken 21 times.
✓ Branch 1 (7→19) taken 22 times.
✓ Branch 2 (7→33) taken 6 times.
✓ Branch 3 (7→42) taken 329 times.
✓ Branch 4 (7→47) taken 7 times.
✓ Branch 5 (7→52) taken 17 times.
✗ Branch 6 (7→57) not taken.
402 switch (source->type) {
1520 21 case CX_JSON_OBJECT: {
1521 21 CxJsonValue *obj = cxJsonCreateObj(allocator);
1522 if (obj == NULL) return NULL; // LCOV_EXCL_LINE
1523
1/2
✗ Branch 0 (12→13) not taken.
✓ Branch 1 (12→15) taken 21 times.
21 if (cxMapClone(obj->object, source->object, cxJsonCloneFunc, allocator, NULL)) {
1524 // LCOV_EXCL_START
1525 cxJsonValueFree(obj);
1526 return NULL;
1527 // LCOV_EXCL_STOP
1528 }
1529
1/2
✓ Branch 0 (15→16) taken 21 times.
✗ Branch 1 (15→17) not taken.
21 return_value(obj);
1530 }
1531 22 case CX_JSON_ARRAY: {
1532 22 const size_t elem_count = source->array.size;
1533 22 CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count);
1534 if (arr == NULL) return NULL; // LCOV_EXCL_LINE
1535 22 arr->array.size = elem_count;
1536
2/2
✓ Branch 0 (28→23) taken 250 times.
✓ Branch 1 (28→29) taken 22 times.
272 for (size_t i = 0 ; i < elem_count ; i++) {
1537 250 CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL);
1538 if (e == NULL) { // LCOV_EXCL_START
1539 cxJsonValueFree(arr);
1540 return NULL;
1541 // LCOV_EXCL_STOP
1542 }
1543 250 arr->array.data[i] = e;
1544 }
1545
1/2
✓ Branch 0 (29→30) taken 22 times.
✗ Branch 1 (29→31) not taken.
22 return_value(arr);
1546 }
1547 6 case CX_JSON_STRING:
1548
1/2
✓ Branch 0 (38→39) taken 6 times.
✗ Branch 1 (38→40) not taken.
12 return_value(cxJsonCreateString(allocator, source->string));
1549 329 case CX_JSON_INTEGER:
1550
1/2
✓ Branch 0 (43→44) taken 329 times.
✗ Branch 1 (43→45) not taken.
329 return_value(cxJsonCreateInteger(allocator, source->integer));
1551 7 case CX_JSON_NUMBER:
1552
1/2
✓ Branch 0 (48→49) taken 7 times.
✗ Branch 1 (48→50) not taken.
7 return_value(cxJsonCreateNumber(allocator, source->number));
1553 17 case CX_JSON_LITERAL:
1554
1/2
✓ Branch 0 (53→54) taken 17 times.
✗ Branch 1 (53→55) not taken.
17 return_value(cxJsonCreateLiteral(allocator, source->literal));
1555 default: // LCOV_EXCL_START
1556 // unreachable
1557 assert(false);
1558 return NULL;
1559 // LCOV_EXCL_STOP
1560 }
1561 #undef return_value
1562 }
1563