xref: /unit/src/nxt_conf.c (revision 2713:e95d2ce7b7e7)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Valentin V. Bartenev
5  * Copyright (C) NGINX, Inc.
6  * Copyright 2024, Alejandro Colomar <alx@kernel.org>
7  */
8 
9 #include <nxt_main.h>
10 #include <nxt_conf.h>
11 
12 #include <float.h>
13 #include <math.h>
14 
15 
16 #define NXT_CONF_MAX_SHORT_STRING  14
17 #define NXT_CONF_MAX_NUMBER_LEN    14
18 #define NXT_CONF_MAX_STRING        NXT_INT32_T_MAX
19 
20 #define NXT_CONF_MAX_TOKEN_LEN     256
21 
22 
23 typedef enum {
24     NXT_CONF_VALUE_NULL = 0,
25     NXT_CONF_VALUE_BOOLEAN,
26     NXT_CONF_VALUE_INTEGER,
27     NXT_CONF_VALUE_NUMBER,
28     NXT_CONF_VALUE_SHORT_STRING,
29     NXT_CONF_VALUE_STRING,
30     NXT_CONF_VALUE_ARRAY,
31     NXT_CONF_VALUE_OBJECT,
32 } nxt_conf_value_type_t;
33 
34 
35 typedef enum {
36     NXT_CONF_OP_PASS = 0,
37     NXT_CONF_OP_CREATE,
38     NXT_CONF_OP_REPLACE,
39     NXT_CONF_OP_DELETE,
40 } nxt_conf_op_action_t;
41 
42 
43 typedef struct nxt_conf_array_s   nxt_conf_array_t;
44 typedef struct nxt_conf_object_s  nxt_conf_object_t;
45 
46 
47 struct nxt_conf_value_s {
48     union {
49         uint8_t               boolean;  /* 1 bit. */
50         u_char                number[NXT_CONF_MAX_NUMBER_LEN + 1];
51 
52         struct {
53             u_char            start[NXT_CONF_MAX_SHORT_STRING];
54             uint8_t           length;
55         } str;
56 
57         struct {
58             u_char            *start;
59             uint32_t          length;
60         } nxt_packed string;
61 
62         nxt_conf_array_t      *array;
63         nxt_conf_object_t     *object;
64     } nxt_packed u;
65 
66     uint8_t                   type;  /* 3 bits. */
67 } nxt_aligned(8);
68 
69 
70 struct nxt_conf_array_s {
71     nxt_uint_t                count;
72     nxt_conf_value_t          elements[];
73 };
74 
75 
76 typedef struct {
77     nxt_conf_value_t          name;
78     nxt_conf_value_t          value;
79 } nxt_conf_object_member_t;
80 
81 
82 struct nxt_conf_object_s {
83     nxt_uint_t                count;
84     nxt_conf_object_member_t  members[];
85 };
86 
87 
88 struct nxt_conf_op_s {
89     uint32_t                  index;
90     uint32_t                  action;  /* nxt_conf_op_action_t */
91     void                      *ctx;
92 };
93 
94 
95 typedef struct {
96     u_char                    *start;
97     u_char                    *end;
98     nxt_bool_t                last;
99     u_char                    buf[NXT_CONF_MAX_TOKEN_LEN];
100 } nxt_conf_path_parse_t;
101 
102 
103 static nxt_int_t nxt_conf_path_next_token(nxt_conf_path_parse_t *parse,
104     nxt_str_t *token);
105 
106 static u_char *nxt_conf_json_skip_space(u_char *start, const u_char *end);
107 static u_char *nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value,
108     u_char *start, u_char *end, nxt_conf_json_error_t *error);
109 static u_char *nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value,
110     u_char *start, u_char *end, nxt_conf_json_error_t *error);
111 static nxt_int_t nxt_conf_object_hash_add(nxt_mp_t *mp,
112     nxt_lvlhsh_t *lvlhsh, nxt_conf_object_member_t *member);
113 static nxt_int_t nxt_conf_object_hash_test(nxt_lvlhsh_query_t *lhq,
114     void *data);
115 static void *nxt_conf_object_hash_alloc(void *data, size_t size);
116 static void nxt_conf_object_hash_free(void *data, void *p);
117 static u_char *nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value,
118     u_char *start, u_char *end, nxt_conf_json_error_t *error);
119 static u_char *nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value,
120     u_char *start, u_char *end, nxt_conf_json_error_t *error);
121 static u_char *nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value,
122     u_char *start, u_char *end, nxt_conf_json_error_t *error);
123 static void nxt_conf_json_parse_error(nxt_conf_json_error_t *error, u_char *pos,
124     const char *detail);
125 
126 static nxt_int_t nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op,
127     nxt_conf_value_t *dst, nxt_conf_value_t *src);
128 static nxt_int_t nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op,
129     nxt_conf_value_t *dst, nxt_conf_value_t *src);
130 static nxt_int_t nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op,
131     nxt_conf_value_t *dst, nxt_conf_value_t *src);
132 
133 static size_t nxt_conf_json_string_length(nxt_conf_value_t *value);
134 static u_char *nxt_conf_json_print_string(u_char *p, nxt_conf_value_t *value);
135 static size_t nxt_conf_json_array_length(nxt_conf_value_t *value,
136     nxt_conf_json_pretty_t *pretty);
137 static u_char *nxt_conf_json_print_array(u_char *p, nxt_conf_value_t *value,
138     nxt_conf_json_pretty_t *pretty);
139 static size_t nxt_conf_json_object_length(nxt_conf_value_t *value,
140     nxt_conf_json_pretty_t *pretty);
141 static u_char *nxt_conf_json_print_object(u_char *p, nxt_conf_value_t *value,
142     nxt_conf_json_pretty_t *pretty);
143 
144 static size_t nxt_conf_json_escape_length(u_char *p, size_t size);
145 static u_char *nxt_conf_json_escape(u_char *dst, u_char *src, size_t size);
146 
147 
148 #define nxt_conf_json_newline(p)                                              \
149     ((p)[0] = '\r', (p)[1] = '\n', (p) + 2)
150 
151 
152 nxt_inline u_char *
nxt_conf_json_indentation(u_char * p,uint32_t level)153 nxt_conf_json_indentation(u_char *p, uint32_t level)
154 {
155     while (level) {
156         *p++ = '\t';
157         level--;
158     }
159 
160     return p;
161 }
162 
163 
164 void
nxt_conf_get_string(nxt_conf_value_t * value,nxt_str_t * str)165 nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str)
166 {
167     if (value->type == NXT_CONF_VALUE_SHORT_STRING) {
168         str->length = value->u.str.length;
169         str->start = value->u.str.start;
170 
171     } else {
172         str->length = value->u.string.length;
173         str->start = value->u.string.start;
174     }
175 }
176 
177 
178 nxt_str_t *
nxt_conf_get_string_dup(nxt_conf_value_t * value,nxt_mp_t * mp,nxt_str_t * str)179 nxt_conf_get_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, nxt_str_t *str)
180 {
181     nxt_str_t  s;
182 
183     nxt_conf_get_string(value, &s);
184     return nxt_str_dup(mp, str, &s);
185 }
186 
187 
188 void
nxt_conf_set_string(nxt_conf_value_t * value,nxt_str_t * str)189 nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str)
190 {
191     if (str->length > NXT_CONF_MAX_SHORT_STRING) {
192         value->type = NXT_CONF_VALUE_STRING;
193         value->u.string.length = str->length;
194         value->u.string.start = str->start;
195 
196     } else {
197         value->type = NXT_CONF_VALUE_SHORT_STRING;
198         value->u.str.length = str->length;
199 
200         nxt_memcpy(value->u.str.start, str->start, str->length);
201     }
202 }
203 
204 
205 nxt_int_t
nxt_conf_set_string_dup(nxt_conf_value_t * value,nxt_mp_t * mp,const nxt_str_t * str)206 nxt_conf_set_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp,
207     const nxt_str_t *str)
208 {
209     nxt_str_t  tmp, *ptr;
210 
211     if (str->length > NXT_CONF_MAX_SHORT_STRING) {
212         value->type = NXT_CONF_VALUE_STRING;
213 
214         ptr = nxt_str_dup(mp, &tmp, str);
215         if (nxt_slow_path(ptr == NULL)) {
216             return NXT_ERROR;
217         }
218 
219         value->u.string.length = tmp.length;
220         value->u.string.start = tmp.start;
221 
222     } else {
223         value->type = NXT_CONF_VALUE_SHORT_STRING;
224         value->u.str.length = str->length;
225 
226         nxt_memcpy(value->u.str.start, str->start, str->length);
227     }
228 
229     return NXT_OK;
230 }
231 
232 
233 double
nxt_conf_get_number(nxt_conf_value_t * value)234 nxt_conf_get_number(nxt_conf_value_t *value)
235 {
236     return nxt_strtod(value->u.number, NULL);
237 }
238 
239 
240 uint8_t
nxt_conf_get_boolean(nxt_conf_value_t * value)241 nxt_conf_get_boolean(nxt_conf_value_t *value)
242 {
243     return value->u.boolean;
244 }
245 
246 
247 nxt_uint_t
nxt_conf_object_members_count(nxt_conf_value_t * value)248 nxt_conf_object_members_count(nxt_conf_value_t *value)
249 {
250     return value->u.object->count;
251 }
252 
253 
254 nxt_conf_value_t *
nxt_conf_create_object(nxt_mp_t * mp,nxt_uint_t count)255 nxt_conf_create_object(nxt_mp_t *mp, nxt_uint_t count)
256 {
257     size_t            size;
258     nxt_conf_value_t  *value;
259 
260     size = sizeof(nxt_conf_value_t)
261            + sizeof(nxt_conf_object_t)
262            + count * sizeof(nxt_conf_object_member_t);
263 
264     value = nxt_mp_get(mp, size);
265     if (nxt_slow_path(value == NULL)) {
266         return NULL;
267     }
268 
269     value->u.object = nxt_pointer_to(value, sizeof(nxt_conf_value_t));
270     value->u.object->count = count;
271 
272     value->type = NXT_CONF_VALUE_OBJECT;
273 
274     return value;
275 }
276 
277 
278 void
nxt_conf_set_member(nxt_conf_value_t * object,nxt_str_t * name,const nxt_conf_value_t * value,uint32_t index)279 nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name,
280     const nxt_conf_value_t *value, uint32_t index)
281 {
282     nxt_conf_object_member_t  *member;
283 
284     member = &object->u.object->members[index];
285 
286     nxt_conf_set_string(&member->name, name);
287 
288     member->value = *value;
289 }
290 
291 
292 nxt_int_t
nxt_conf_set_member_dup(nxt_conf_value_t * object,nxt_mp_t * mp,nxt_str_t * name,nxt_conf_value_t * value,uint32_t index)293 nxt_conf_set_member_dup(nxt_conf_value_t *object, nxt_mp_t *mp, nxt_str_t *name,
294     nxt_conf_value_t *value, uint32_t index)
295 {
296     nxt_conf_object_member_t  *member;
297 
298     member = &object->u.object->members[index];
299 
300     member->value = *value;
301 
302     return nxt_conf_set_string_dup(&member->name, mp, name);
303 }
304 
305 
306 void
nxt_conf_set_member_string(nxt_conf_value_t * object,nxt_str_t * name,nxt_str_t * value,uint32_t index)307 nxt_conf_set_member_string(nxt_conf_value_t *object, nxt_str_t *name,
308     nxt_str_t *value, uint32_t index)
309 {
310     nxt_conf_object_member_t  *member;
311 
312     member = &object->u.object->members[index];
313 
314     nxt_conf_set_string(&member->name, name);
315 
316     nxt_conf_set_string(&member->value, value);
317 }
318 
319 
320 nxt_int_t
nxt_conf_set_member_string_dup(nxt_conf_value_t * object,nxt_mp_t * mp,nxt_str_t * name,nxt_str_t * value,uint32_t index)321 nxt_conf_set_member_string_dup(nxt_conf_value_t *object, nxt_mp_t *mp,
322     nxt_str_t *name, nxt_str_t *value, uint32_t index)
323 {
324     nxt_conf_object_member_t  *member;
325 
326     member = &object->u.object->members[index];
327 
328     nxt_conf_set_string(&member->name, name);
329 
330     return nxt_conf_set_string_dup(&member->value, mp, value);
331 }
332 
333 
334 void
nxt_conf_set_member_integer(nxt_conf_value_t * object,nxt_str_t * name,int64_t value,uint32_t index)335 nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name,
336     int64_t value, uint32_t index)
337 {
338     u_char                    *p, *end;
339     nxt_conf_object_member_t  *member;
340 
341     member = &object->u.object->members[index];
342 
343     nxt_conf_set_string(&member->name, name);
344 
345     p = member->value.u.number;
346     end = p + NXT_CONF_MAX_NUMBER_LEN;
347 
348     end = nxt_sprintf(p, end, "%L", value);
349     *end = '\0';
350 
351     member->value.type = NXT_CONF_VALUE_INTEGER;
352 }
353 
354 
355 void
nxt_conf_set_member_null(nxt_conf_value_t * object,nxt_str_t * name,uint32_t index)356 nxt_conf_set_member_null(nxt_conf_value_t *object, nxt_str_t *name,
357     uint32_t index)
358 {
359     nxt_conf_object_member_t  *member;
360 
361     member = &object->u.object->members[index];
362 
363     nxt_conf_set_string(&member->name, name);
364 
365     member->value.type = NXT_CONF_VALUE_NULL;
366 }
367 
368 
369 nxt_conf_value_t *
nxt_conf_create_array(nxt_mp_t * mp,nxt_uint_t count)370 nxt_conf_create_array(nxt_mp_t *mp, nxt_uint_t count)
371 {
372     size_t            size;
373     nxt_conf_value_t  *value;
374 
375     size = sizeof(nxt_conf_value_t)
376            + sizeof(nxt_conf_array_t)
377            + count * sizeof(nxt_conf_value_t);
378 
379     value = nxt_mp_get(mp, size);
380     if (nxt_slow_path(value == NULL)) {
381         return NULL;
382     }
383 
384     value->u.array = nxt_pointer_to(value, sizeof(nxt_conf_value_t));
385     value->u.array->count = count;
386 
387     value->type = NXT_CONF_VALUE_ARRAY;
388 
389     return value;
390 }
391 
392 
393 void
nxt_conf_set_element(nxt_conf_value_t * array,nxt_uint_t index,const nxt_conf_value_t * value)394 nxt_conf_set_element(nxt_conf_value_t *array, nxt_uint_t index,
395     const nxt_conf_value_t *value)
396 {
397     array->u.array->elements[index] = *value;
398 }
399 
400 
401 nxt_int_t
nxt_conf_set_element_string_dup(nxt_conf_value_t * array,nxt_mp_t * mp,nxt_uint_t index,nxt_str_t * value)402 nxt_conf_set_element_string_dup(nxt_conf_value_t *array, nxt_mp_t *mp,
403     nxt_uint_t index, nxt_str_t *value)
404 {
405     nxt_conf_value_t  *element;
406 
407     element = &array->u.array->elements[index];
408 
409     return nxt_conf_set_string_dup(element, mp, value);
410 }
411 
412 
413 nxt_uint_t
nxt_conf_array_elements_count(nxt_conf_value_t * value)414 nxt_conf_array_elements_count(nxt_conf_value_t *value)
415 {
416     return value->u.array->count;
417 }
418 
419 
420 nxt_uint_t
nxt_conf_array_elements_count_or_1(nxt_conf_value_t * value)421 nxt_conf_array_elements_count_or_1(nxt_conf_value_t *value)
422 {
423     return (value->type == NXT_CONF_VALUE_ARRAY) ? value->u.array->count : 1;
424 }
425 
426 
427 nxt_uint_t
nxt_conf_type(nxt_conf_value_t * value)428 nxt_conf_type(nxt_conf_value_t *value)
429 {
430     switch (value->type) {
431 
432     case NXT_CONF_VALUE_NULL:
433         return NXT_CONF_NULL;
434 
435     case NXT_CONF_VALUE_BOOLEAN:
436         return NXT_CONF_BOOLEAN;
437 
438     case NXT_CONF_VALUE_INTEGER:
439         return NXT_CONF_INTEGER;
440 
441     case NXT_CONF_VALUE_NUMBER:
442         return NXT_CONF_NUMBER;
443 
444     case NXT_CONF_VALUE_SHORT_STRING:
445     case NXT_CONF_VALUE_STRING:
446         return NXT_CONF_STRING;
447 
448     case NXT_CONF_VALUE_ARRAY:
449         return NXT_CONF_ARRAY;
450 
451     case NXT_CONF_VALUE_OBJECT:
452         return NXT_CONF_OBJECT;
453     }
454 
455     nxt_unreachable();
456 
457     return 0;
458 }
459 
460 
461 nxt_conf_value_t *
nxt_conf_get_path(nxt_conf_value_t * value,nxt_str_t * path)462 nxt_conf_get_path(nxt_conf_value_t *value, nxt_str_t *path)
463 {
464     nxt_str_t              token;
465     nxt_int_t              ret, index;
466     nxt_conf_path_parse_t  parse;
467 
468     parse.start = path->start;
469     parse.end = path->start + path->length;
470     parse.last = 0;
471 
472     do {
473         ret = nxt_conf_path_next_token(&parse, &token);
474         if (nxt_slow_path(ret != NXT_OK)) {
475             return NULL;
476         }
477 
478         if (token.length == 0) {
479 
480             if (parse.last) {
481                 break;
482             }
483 
484             return NULL;
485         }
486 
487         switch (value->type) {
488 
489         case NXT_CONF_VALUE_OBJECT:
490             value = nxt_conf_get_object_member(value, &token, NULL);
491             break;
492 
493         case NXT_CONF_VALUE_ARRAY:
494             index = nxt_int_parse(token.start, token.length);
495 
496             if (index < 0 || index > NXT_INT32_T_MAX) {
497                 return NULL;
498             }
499 
500             value = nxt_conf_get_array_element(value, index);
501             break;
502 
503         default:
504             return NULL;
505         }
506 
507         if (value == NULL) {
508             return NULL;
509         }
510 
511     } while (parse.last == 0);
512 
513     return value;
514 }
515 
516 
517 static nxt_int_t
nxt_conf_path_next_token(nxt_conf_path_parse_t * parse,nxt_str_t * token)518 nxt_conf_path_next_token(nxt_conf_path_parse_t *parse, nxt_str_t *token)
519 {
520     u_char  *p, *start, *end;
521     size_t  length;
522 
523     start = parse->start + 1;
524 
525     p = start;
526 
527     while (p < parse->end && *p != '/') {
528         p++;
529     }
530 
531     parse->start = p;
532     parse->last = (p >= parse->end);
533 
534     length = p - start;
535 
536     if (nxt_slow_path(length > NXT_CONF_MAX_TOKEN_LEN)) {
537         return NXT_ERROR;
538     }
539 
540     end = nxt_decode_uri(parse->buf, start, length);
541     if (nxt_slow_path(end == NULL)) {
542         return NXT_ERROR;
543     }
544 
545     token->length = end - parse->buf;
546     token->start = parse->buf;
547 
548     return NXT_OK;
549 }
550 
551 
552 nxt_conf_value_t *
nxt_conf_get_object_member(nxt_conf_value_t * value,nxt_str_t * name,uint32_t * index)553 nxt_conf_get_object_member(nxt_conf_value_t *value, nxt_str_t *name,
554     uint32_t *index)
555 {
556     nxt_str_t                 str;
557     nxt_uint_t                n;
558     nxt_conf_object_t         *object;
559     nxt_conf_object_member_t  *member;
560 
561     if (value->type != NXT_CONF_VALUE_OBJECT) {
562         return NULL;
563     }
564 
565     object = value->u.object;
566 
567     for (n = 0; n < object->count; n++) {
568         member = &object->members[n];
569 
570         nxt_conf_get_string(&member->name, &str);
571 
572         if (nxt_strstr_eq(&str, name)) {
573 
574             if (index != NULL) {
575                 *index = n;
576             }
577 
578             return &member->value;
579         }
580     }
581 
582     return NULL;
583 }
584 
585 
586 nxt_int_t
nxt_conf_map_object(nxt_mp_t * mp,nxt_conf_value_t * value,nxt_conf_map_t * map,nxt_uint_t n,void * data)587 nxt_conf_map_object(nxt_mp_t *mp, nxt_conf_value_t *value, nxt_conf_map_t *map,
588     nxt_uint_t n, void *data)
589 {
590     double            num;
591     nxt_str_t         str, *s;
592     nxt_uint_t        i;
593     nxt_conf_value_t  *v;
594 
595     union {
596         uint8_t     ui8;
597         int32_t     i32;
598         int64_t     i64;
599         int         i;
600         ssize_t     size;
601         off_t       off;
602         nxt_msec_t  msec;
603         double      dbl;
604         nxt_str_t   str;
605         char        *cstrz;
606         void        *v;
607     } *ptr;
608 
609     for (i = 0; i < n; i++) {
610 
611         v = nxt_conf_get_object_member(value, &map[i].name, NULL);
612 
613         if (v == NULL || v->type == NXT_CONF_VALUE_NULL) {
614             continue;
615         }
616 
617         ptr = nxt_pointer_to(data, map[i].offset);
618 
619         switch (map[i].type) {
620 
621         case NXT_CONF_MAP_INT8:
622 
623             if (v->type == NXT_CONF_VALUE_BOOLEAN) {
624                 ptr->ui8 = v->u.boolean;
625             }
626 
627             break;
628 
629         case NXT_CONF_MAP_INT32:
630         case NXT_CONF_MAP_INT64:
631         case NXT_CONF_MAP_INT:
632         case NXT_CONF_MAP_SIZE:
633         case NXT_CONF_MAP_OFF:
634         case NXT_CONF_MAP_MSEC:
635 
636             if (v->type != NXT_CONF_VALUE_INTEGER) {
637                 break;
638             }
639 
640             num = nxt_strtod(v->u.number, NULL);
641 
642             switch (map[i].type) {
643 
644             case NXT_CONF_MAP_INT32:
645                 ptr->i32 = num;
646                 break;
647 
648             case NXT_CONF_MAP_INT64:
649                 ptr->i64 = num;
650                 break;
651 
652             case NXT_CONF_MAP_INT:
653                 ptr->i = num;
654                 break;
655 
656             case NXT_CONF_MAP_SIZE:
657                 ptr->size = num;
658                 break;
659 
660             case NXT_CONF_MAP_OFF:
661                 ptr->off = num;
662                 break;
663 
664             case NXT_CONF_MAP_MSEC:
665                 ptr->msec = (nxt_msec_t) num * 1000;
666                 break;
667 
668             default:
669                 nxt_unreachable();
670             }
671 
672             break;
673 
674         case NXT_CONF_MAP_DOUBLE:
675 
676             if (v->type == NXT_CONF_VALUE_NUMBER) {
677                 ptr->dbl = nxt_strtod(v->u.number, NULL);
678             }
679 
680             break;
681 
682         case NXT_CONF_MAP_STR:
683         case NXT_CONF_MAP_STR_COPY:
684         case NXT_CONF_MAP_CSTRZ:
685 
686             if (v->type != NXT_CONF_VALUE_SHORT_STRING
687                 && v->type != NXT_CONF_VALUE_STRING)
688             {
689                 break;
690             }
691 
692             nxt_conf_get_string(v, &str);
693 
694             switch (map[i].type) {
695 
696             case NXT_CONF_MAP_STR:
697                 ptr->str = str;
698                 break;
699 
700             case NXT_CONF_MAP_STR_COPY:
701 
702                 s = nxt_str_dup(mp, &ptr->str, &str);
703 
704                 if (nxt_slow_path(s == NULL)) {
705                     return NXT_ERROR;
706                 }
707 
708                 break;
709 
710             case NXT_CONF_MAP_CSTRZ:
711 
712                 ptr->cstrz = nxt_str_cstrz(mp, &str);
713 
714                 if (nxt_slow_path(ptr->cstrz == NULL)) {
715                     return NXT_ERROR;
716                 }
717 
718                 break;
719 
720             default:
721                 nxt_unreachable();
722             }
723 
724             break;
725 
726         case NXT_CONF_MAP_PTR:
727 
728             ptr->v = v;
729 
730             break;
731         }
732     }
733 
734     return NXT_OK;
735 }
736 
737 
738 nxt_conf_value_t *
nxt_conf_next_object_member(nxt_conf_value_t * value,nxt_str_t * name,uint32_t * next)739 nxt_conf_next_object_member(nxt_conf_value_t *value, nxt_str_t *name,
740     uint32_t *next)
741 {
742     uint32_t                  n;
743     nxt_conf_object_t         *object;
744     nxt_conf_object_member_t  *member;
745 
746     if (value->type != NXT_CONF_VALUE_OBJECT) {
747         return NULL;
748     }
749 
750     n = *next;
751     object = value->u.object;
752 
753     if (n >= object->count) {
754         return NULL;
755     }
756 
757     member = &object->members[n];
758     *next = n + 1;
759 
760     nxt_conf_get_string(&member->name, name);
761 
762     return &member->value;
763 }
764 
765 
766 nxt_conf_value_t *
nxt_conf_get_array_element(nxt_conf_value_t * value,uint32_t index)767 nxt_conf_get_array_element(nxt_conf_value_t *value, uint32_t index)
768 {
769     nxt_conf_array_t  *array;
770 
771     if (value->type != NXT_CONF_VALUE_ARRAY) {
772         return NULL;
773     }
774 
775     array = value->u.array;
776 
777     if (index >= array->count) {
778         return NULL;
779     }
780 
781     return &array->elements[index];
782 }
783 
784 
785 nxt_conf_value_t *
nxt_conf_get_array_element_or_itself(nxt_conf_value_t * value,uint32_t index)786 nxt_conf_get_array_element_or_itself(nxt_conf_value_t *value, uint32_t index)
787 {
788     nxt_conf_array_t  *array;
789 
790     if (value->type != NXT_CONF_VALUE_ARRAY) {
791         return (index == 0) ? value : NULL;
792     }
793 
794     array = value->u.array;
795 
796     if (index >= array->count) {
797         return NULL;
798     }
799 
800     return &array->elements[index];
801 }
802 
803 
804 void
nxt_conf_array_qsort(nxt_conf_value_t * value,int (* compare)(const void *,const void *))805 nxt_conf_array_qsort(nxt_conf_value_t *value,
806     int (*compare)(const void *, const void *))
807 {
808     nxt_conf_array_t  *array;
809 
810     if (value->type != NXT_CONF_VALUE_ARRAY) {
811         return;
812     }
813 
814     array = value->u.array;
815 
816     nxt_qsort(array->elements, array->count, sizeof(nxt_conf_value_t), compare);
817 }
818 
819 
820 nxt_conf_op_ret_t
nxt_conf_op_compile(nxt_mp_t * mp,nxt_conf_op_t ** ops,nxt_conf_value_t * root,nxt_str_t * path,nxt_conf_value_t * value,nxt_bool_t add)821 nxt_conf_op_compile(nxt_mp_t *mp, nxt_conf_op_t **ops, nxt_conf_value_t *root,
822     nxt_str_t *path, nxt_conf_value_t *value, nxt_bool_t add)
823 {
824     nxt_str_t                 token;
825     nxt_int_t                 ret, index;
826     nxt_conf_op_t             *op, **parent;
827     nxt_conf_value_t          *node;
828     nxt_conf_path_parse_t     parse;
829     nxt_conf_object_member_t  *member;
830 
831     parse.start = path->start;
832     parse.end = path->start + path->length;
833     parse.last = 0;
834 
835     parent = ops;
836 
837     for ( ;; ) {
838         op = nxt_mp_zget(mp, sizeof(nxt_conf_op_t));
839         if (nxt_slow_path(op == NULL)) {
840             return NXT_CONF_OP_ERROR;
841         }
842 
843         *parent = op;
844         parent = (nxt_conf_op_t **) &op->ctx;
845 
846         ret = nxt_conf_path_next_token(&parse, &token);
847         if (nxt_slow_path(ret != NXT_OK)) {
848             return NXT_CONF_OP_ERROR;
849         }
850 
851         switch (root->type) {
852 
853         case NXT_CONF_VALUE_OBJECT:
854             node = nxt_conf_get_object_member(root, &token, &op->index);
855             break;
856 
857         case NXT_CONF_VALUE_ARRAY:
858             index = nxt_int_parse(token.start, token.length);
859 
860             if (index < 0 || index > NXT_INT32_T_MAX) {
861                 return NXT_CONF_OP_NOT_FOUND;
862             }
863 
864             op->index = index;
865 
866             node = nxt_conf_get_array_element(root, index);
867             break;
868 
869         default:
870             node = NULL;
871         }
872 
873         if (parse.last) {
874             break;
875         }
876 
877         if (node == NULL) {
878             return NXT_CONF_OP_NOT_FOUND;
879         }
880 
881         op->action = NXT_CONF_OP_PASS;
882         root = node;
883     }
884 
885     if (value == NULL) {
886 
887         if (node == NULL) {
888             return NXT_CONF_OP_NOT_FOUND;
889         }
890 
891         op->action = NXT_CONF_OP_DELETE;
892 
893         return NXT_CONF_OP_OK;
894     }
895 
896     if (add) {
897         if (node == NULL) {
898             return NXT_CONF_OP_NOT_FOUND;
899         }
900 
901         if (node->type != NXT_CONF_VALUE_ARRAY) {
902             return NXT_CONF_OP_NOT_ALLOWED;
903         }
904 
905         op->action = NXT_CONF_OP_PASS;
906 
907         op = nxt_mp_zget(mp, sizeof(nxt_conf_op_t));
908         if (nxt_slow_path(op == NULL)) {
909             return NXT_CONF_OP_ERROR;
910         }
911 
912         *parent = op;
913 
914         op->index = node->u.array->count;
915         op->action = NXT_CONF_OP_CREATE;
916         op->ctx = value;
917 
918         return NXT_CONF_OP_OK;
919     }
920 
921     if (node != NULL) {
922         op->action = NXT_CONF_OP_REPLACE;
923         op->ctx = value;
924 
925         return NXT_CONF_OP_OK;
926     }
927 
928     op->action = NXT_CONF_OP_CREATE;
929 
930     if (root->type == NXT_CONF_VALUE_ARRAY) {
931         if (op->index > root->u.array->count) {
932             return NXT_CONF_OP_NOT_FOUND;
933         }
934 
935         op->ctx = value;
936 
937     } else {
938         member = nxt_mp_zget(mp, sizeof(nxt_conf_object_member_t));
939         if (nxt_slow_path(member == NULL)) {
940             return NXT_CONF_OP_ERROR;
941         }
942 
943         ret = nxt_conf_set_string_dup(&member->name, mp, &token);
944         if (nxt_slow_path(ret != NXT_OK)) {
945             return NXT_CONF_OP_ERROR;
946         }
947 
948         member->value = *value;
949 
950         op->index = root->u.object->count;
951         op->ctx = member;
952     }
953 
954     return NXT_CONF_OP_OK;
955 }
956 
957 
958 nxt_conf_value_t *
nxt_conf_clone(nxt_mp_t * mp,nxt_conf_op_t * op,nxt_conf_value_t * value)959 nxt_conf_clone(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *value)
960 {
961     nxt_int_t         rc;
962     nxt_conf_value_t  *copy;
963 
964     copy = nxt_mp_get(mp, sizeof(nxt_conf_value_t));
965     if (nxt_slow_path(copy == NULL)) {
966         return NULL;
967     }
968 
969     rc = nxt_conf_copy_value(mp, op, copy, value);
970 
971     if (nxt_slow_path(rc != NXT_OK)) {
972         return NULL;
973     }
974 
975     return copy;
976 }
977 
978 
979 static nxt_int_t
nxt_conf_copy_value(nxt_mp_t * mp,nxt_conf_op_t * op,nxt_conf_value_t * dst,nxt_conf_value_t * src)980 nxt_conf_copy_value(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
981     nxt_conf_value_t *src)
982 {
983     if (op != NULL
984         && src->type != NXT_CONF_VALUE_ARRAY
985         && src->type != NXT_CONF_VALUE_OBJECT)
986     {
987         return NXT_ERROR;
988     }
989 
990     switch (src->type) {
991 
992     case NXT_CONF_VALUE_STRING:
993 
994         dst->u.string.start = nxt_mp_nget(mp, src->u.string.length);
995         if (nxt_slow_path(dst->u.string.start == NULL)) {
996             return NXT_ERROR;
997         }
998 
999         nxt_memcpy(dst->u.string.start, src->u.string.start,
1000                    src->u.string.length);
1001 
1002         dst->u.string.length = src->u.string.length;
1003 
1004         break;
1005 
1006     case NXT_CONF_VALUE_ARRAY:
1007         return nxt_conf_copy_array(mp, op, dst, src);
1008 
1009     case NXT_CONF_VALUE_OBJECT:
1010         return nxt_conf_copy_object(mp, op, dst, src);
1011 
1012     default:
1013         dst->u = src->u;
1014     }
1015 
1016     dst->type = src->type;
1017 
1018     return NXT_OK;
1019 }
1020 
1021 
1022 static nxt_int_t
nxt_conf_copy_array(nxt_mp_t * mp,nxt_conf_op_t * op,nxt_conf_value_t * dst,nxt_conf_value_t * src)1023 nxt_conf_copy_array(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
1024     nxt_conf_value_t *src)
1025 {
1026     size_t            size;
1027     nxt_int_t         rc;
1028     nxt_uint_t        s, d, count, index;
1029     nxt_conf_op_t     *pass_op;
1030     nxt_conf_value_t  *value;
1031 
1032     count = src->u.array->count;
1033 
1034     if (op != NULL) {
1035         if (op->action == NXT_CONF_OP_CREATE) {
1036             count++;
1037 
1038         } else if (op->action == NXT_CONF_OP_DELETE) {
1039             count--;
1040         }
1041     }
1042 
1043     size = sizeof(nxt_conf_array_t) + count * sizeof(nxt_conf_value_t);
1044 
1045     dst->u.array = nxt_mp_get(mp, size);
1046     if (nxt_slow_path(dst->u.array == NULL)) {
1047         return NXT_ERROR;
1048     }
1049 
1050     dst->u.array->count = count;
1051 
1052     s = 0;
1053     d = 0;
1054 
1055     pass_op = NULL;
1056 
1057     /*
1058      * This initialization is needed only to
1059      * suppress a warning on GCC 4.8 and older.
1060      */
1061     index = 0;
1062 
1063     do {
1064         if (pass_op == NULL) {
1065             index = (op == NULL) ? src->u.array->count : op->index;
1066         }
1067 
1068         while (s != index) {
1069             rc = nxt_conf_copy_value(mp, pass_op, &dst->u.array->elements[d],
1070                                                   &src->u.array->elements[s]);
1071             if (nxt_slow_path(rc != NXT_OK)) {
1072                 return NXT_ERROR;
1073             }
1074 
1075             s++;
1076             d++;
1077         }
1078 
1079         if (pass_op != NULL) {
1080             pass_op = NULL;
1081             continue;
1082         }
1083 
1084         if (op != NULL) {
1085             switch (op->action) {
1086             case NXT_CONF_OP_PASS:
1087                 pass_op = op->ctx;
1088                 index++;
1089                 break;
1090 
1091             case NXT_CONF_OP_CREATE:
1092                 value = op->ctx;
1093                 dst->u.array->elements[d] = *value;
1094 
1095                 d++;
1096                 break;
1097 
1098             case NXT_CONF_OP_REPLACE:
1099                 value = op->ctx;
1100                 dst->u.array->elements[d] = *value;
1101 
1102                 s++;
1103                 d++;
1104                 break;
1105 
1106             case NXT_CONF_OP_DELETE:
1107                 s++;
1108                 break;
1109             }
1110 
1111             op = NULL;
1112         }
1113 
1114     } while (d != count);
1115 
1116     dst->type = src->type;
1117 
1118     return NXT_OK;
1119 }
1120 
1121 
1122 static nxt_int_t
nxt_conf_copy_object(nxt_mp_t * mp,nxt_conf_op_t * op,nxt_conf_value_t * dst,nxt_conf_value_t * src)1123 nxt_conf_copy_object(nxt_mp_t *mp, nxt_conf_op_t *op, nxt_conf_value_t *dst,
1124     nxt_conf_value_t *src)
1125 {
1126     size_t                    size;
1127     nxt_int_t                 rc;
1128     nxt_uint_t                s, d, count, index;
1129     nxt_conf_op_t             *pass_op;
1130     nxt_conf_value_t          *value;
1131     nxt_conf_object_member_t  *member;
1132 
1133     count = src->u.object->count;
1134 
1135     if (op != NULL) {
1136         if (op->action == NXT_CONF_OP_CREATE) {
1137             count++;
1138 
1139         } else if (op->action == NXT_CONF_OP_DELETE) {
1140             count--;
1141         }
1142     }
1143 
1144     size = sizeof(nxt_conf_object_t)
1145            + count * sizeof(nxt_conf_object_member_t);
1146 
1147     dst->u.object = nxt_mp_get(mp, size);
1148     if (nxt_slow_path(dst->u.object == NULL)) {
1149         return NXT_ERROR;
1150     }
1151 
1152     dst->u.object->count = count;
1153 
1154     s = 0;
1155     d = 0;
1156 
1157     pass_op = NULL;
1158 
1159     /*
1160      * This initialization is needed only to
1161      * suppress a warning on GCC 4.8 and older.
1162      */
1163     index = 0;
1164 
1165     do {
1166         if (pass_op == NULL) {
1167             index = (op == NULL) ? src->u.object->count : op->index;
1168         }
1169 
1170         while (s != index) {
1171             rc = nxt_conf_copy_value(mp, NULL,
1172                                      &dst->u.object->members[d].name,
1173                                      &src->u.object->members[s].name);
1174 
1175             if (nxt_slow_path(rc != NXT_OK)) {
1176                 return NXT_ERROR;
1177             }
1178 
1179             rc = nxt_conf_copy_value(mp, pass_op,
1180                                      &dst->u.object->members[d].value,
1181                                      &src->u.object->members[s].value);
1182 
1183             if (nxt_slow_path(rc != NXT_OK)) {
1184                 return NXT_ERROR;
1185             }
1186 
1187             s++;
1188             d++;
1189         }
1190 
1191         if (pass_op != NULL) {
1192             pass_op = NULL;
1193             continue;
1194         }
1195 
1196         if (op != NULL) {
1197             switch (op->action) {
1198             case NXT_CONF_OP_PASS:
1199                 pass_op = op->ctx;
1200                 index++;
1201                 break;
1202 
1203             case NXT_CONF_OP_CREATE:
1204                 member = op->ctx;
1205 
1206                 rc = nxt_conf_copy_value(mp, NULL,
1207                                          &dst->u.object->members[d].name,
1208                                          &member->name);
1209 
1210                 if (nxt_slow_path(rc != NXT_OK)) {
1211                     return NXT_ERROR;
1212                 }
1213 
1214                 dst->u.object->members[d].value = member->value;
1215 
1216                 d++;
1217                 break;
1218 
1219             case NXT_CONF_OP_REPLACE:
1220                 rc = nxt_conf_copy_value(mp, NULL,
1221                                          &dst->u.object->members[d].name,
1222                                          &src->u.object->members[s].name);
1223 
1224                 if (nxt_slow_path(rc != NXT_OK)) {
1225                     return NXT_ERROR;
1226                 }
1227 
1228                 value = op->ctx;
1229 
1230                 dst->u.object->members[d].value = *value;
1231 
1232                 s++;
1233                 d++;
1234                 break;
1235 
1236             case NXT_CONF_OP_DELETE:
1237                 s++;
1238                 break;
1239             }
1240 
1241             op = NULL;
1242         }
1243 
1244     } while (d != count);
1245 
1246     dst->type = src->type;
1247 
1248     return NXT_OK;
1249 }
1250 
1251 
1252 nxt_conf_value_t *
nxt_conf_json_parse(nxt_mp_t * mp,u_char * start,u_char * end,nxt_conf_json_error_t * error)1253 nxt_conf_json_parse(nxt_mp_t *mp, u_char *start, u_char *end,
1254     nxt_conf_json_error_t *error)
1255 {
1256     u_char            *p;
1257     nxt_conf_value_t  *value;
1258 
1259     value = nxt_mp_get(mp, sizeof(nxt_conf_value_t));
1260     if (nxt_slow_path(value == NULL)) {
1261         return NULL;
1262     }
1263 
1264     p = nxt_conf_json_skip_space(start, end);
1265 
1266     if (nxt_slow_path(p == end)) {
1267 
1268         nxt_conf_json_parse_error(error, start,
1269             "An empty JSON payload isn't allowed.  It must be either a literal "
1270             "(null, true, or false), a number, a string (in double quotes "
1271             "\"\"), an array (with brackets []), or an object (with braces {})."
1272         );
1273 
1274         return NULL;
1275     }
1276 
1277     p = nxt_conf_json_parse_value(mp, value, p, end, error);
1278 
1279     if (nxt_slow_path(p == NULL)) {
1280         return NULL;
1281     }
1282 
1283     p = nxt_conf_json_skip_space(p, end);
1284 
1285     if (nxt_slow_path(p != end)) {
1286 
1287         nxt_conf_json_parse_error(error, p,
1288             "Unexpected character after the end of a valid JSON value."
1289         );
1290 
1291         return NULL;
1292     }
1293 
1294     return value;
1295 }
1296 
1297 
1298 static u_char *
nxt_conf_json_skip_space(u_char * start,const u_char * end)1299 nxt_conf_json_skip_space(u_char *start, const u_char *end)
1300 {
1301     u_char  *p, ch;
1302 
1303     enum {
1304         sw_normal = 0,
1305         sw_after_slash,
1306         sw_single_comment,
1307         sw_multi_comment,
1308         sw_after_asterisk,
1309     } state;
1310 
1311     state = sw_normal;
1312 
1313     for (p = start; nxt_fast_path(p != end); p++) {
1314         ch = *p;
1315 
1316         switch (state) {
1317 
1318         case sw_normal:
1319             switch (ch) {
1320             case ' ':
1321             case '\t':
1322             case '\n':
1323             case '\r':
1324                 continue;
1325             case '/':
1326                 start = p;
1327                 state = sw_after_slash;
1328                 continue;
1329             }
1330 
1331             break;
1332 
1333         case sw_after_slash:
1334             switch (ch) {
1335             case '/':
1336                 state = sw_single_comment;
1337                 continue;
1338             case '*':
1339                 state = sw_multi_comment;
1340                 continue;
1341             }
1342 
1343             break;
1344 
1345         case sw_single_comment:
1346             if (ch == '\n') {
1347                 state = sw_normal;
1348             }
1349 
1350             continue;
1351 
1352         case sw_multi_comment:
1353             if (ch == '*') {
1354                 state = sw_after_asterisk;
1355             }
1356 
1357             continue;
1358 
1359         case sw_after_asterisk:
1360             switch (ch) {
1361             case '/':
1362                 state = sw_normal;
1363                 continue;
1364             case '*':
1365                 continue;
1366             }
1367 
1368             state = sw_multi_comment;
1369             continue;
1370         }
1371 
1372         break;
1373     }
1374 
1375     if (nxt_slow_path(state != sw_normal)) {
1376         return start;
1377     }
1378 
1379     return p;
1380 }
1381 
1382 
1383 static u_char *
nxt_conf_json_parse_value(nxt_mp_t * mp,nxt_conf_value_t * value,u_char * start,u_char * end,nxt_conf_json_error_t * error)1384 nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
1385     u_char *end, nxt_conf_json_error_t *error)
1386 {
1387     u_char  ch, *p;
1388 
1389     ch = *start;
1390 
1391     switch (ch) {
1392     case '{':
1393         return nxt_conf_json_parse_object(mp, value, start, end, error);
1394 
1395     case '[':
1396         return nxt_conf_json_parse_array(mp, value, start, end, error);
1397 
1398     case '"':
1399         return nxt_conf_json_parse_string(mp, value, start, end, error);
1400 
1401     case 't':
1402         if (nxt_fast_path(end - start >= 4
1403                           && memcmp(start, "true", 4) == 0))
1404         {
1405             value->u.boolean = 1;
1406             value->type = NXT_CONF_VALUE_BOOLEAN;
1407 
1408             return start + 4;
1409         }
1410 
1411         goto error;
1412 
1413     case 'f':
1414         if (nxt_fast_path(end - start >= 5
1415                           && memcmp(start, "false", 5) == 0))
1416         {
1417             value->u.boolean = 0;
1418             value->type = NXT_CONF_VALUE_BOOLEAN;
1419 
1420             return start + 5;
1421         }
1422 
1423         goto error;
1424 
1425     case 'n':
1426         if (nxt_fast_path(end - start >= 4
1427                           && memcmp(start, "null", 4) == 0))
1428         {
1429             value->type = NXT_CONF_VALUE_NULL;
1430             return start + 4;
1431         }
1432 
1433         goto error;
1434 
1435     case '-':
1436         if (nxt_fast_path(end - start > 1)) {
1437             ch = start[1];
1438             break;
1439         }
1440 
1441         goto error;
1442     }
1443 
1444     if (nxt_fast_path((ch - '0') <= 9)) {
1445         p = nxt_conf_json_parse_number(mp, value, start, end, error);
1446 
1447         if (nxt_slow_path(p == NULL)) {
1448             return NULL;
1449         }
1450 
1451         if (p == end) {
1452             return end;
1453         }
1454 
1455         switch (*p) {
1456         case ' ':
1457         case '\t':
1458         case '\r':
1459         case '\n':
1460         case ',':
1461         case '}':
1462         case ']':
1463         case '{':
1464         case '[':
1465         case '"':
1466         case '/':
1467             return p;
1468         }
1469     }
1470 
1471 error:
1472 
1473     nxt_conf_json_parse_error(error, start,
1474         "A valid JSON value is expected here.  It must be either a literal "
1475         "(null, true, or false), a number, a string (in double quotes \"\"), "
1476         "an array (with brackets []), or an object (with braces {})."
1477     );
1478 
1479     return NULL;
1480 }
1481 
1482 
1483 static const nxt_lvlhsh_proto_t  nxt_conf_object_hash_proto
1484     nxt_aligned(64) =
1485 {
1486     NXT_LVLHSH_DEFAULT,
1487     nxt_conf_object_hash_test,
1488     nxt_conf_object_hash_alloc,
1489     nxt_conf_object_hash_free,
1490 };
1491 
1492 
1493 static u_char *
nxt_conf_json_parse_object(nxt_mp_t * mp,nxt_conf_value_t * value,u_char * start,u_char * end,nxt_conf_json_error_t * error)1494 nxt_conf_json_parse_object(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
1495     u_char *end, nxt_conf_json_error_t *error)
1496 {
1497     u_char                    *p, *name;
1498     nxt_mp_t                  *mp_temp;
1499     nxt_int_t                 rc;
1500     nxt_uint_t                count;
1501     nxt_lvlhsh_t              hash;
1502     nxt_lvlhsh_each_t         lhe;
1503     nxt_conf_object_t         *object;
1504     nxt_conf_object_member_t  *member, *element;
1505 
1506     mp_temp = nxt_mp_create(1024, 128, 256, 32);
1507     if (nxt_slow_path(mp_temp == NULL)) {
1508         return NULL;
1509     }
1510 
1511     nxt_lvlhsh_init(&hash);
1512 
1513     count = 0;
1514     p = start;
1515 
1516     for ( ;; ) {
1517         p = nxt_conf_json_skip_space(p + 1, end);
1518 
1519         if (nxt_slow_path(p == end)) {
1520 
1521             nxt_conf_json_parse_error(error, p,
1522                 "Unexpected end of JSON payload.  There's an object without "
1523                 "a closing brace (})."
1524             );
1525 
1526             goto error;
1527         }
1528 
1529         if (*p != '"') {
1530             if (nxt_fast_path(*p == '}')) {
1531                 break;
1532             }
1533 
1534             nxt_conf_json_parse_error(error, p,
1535                 "A double quote (\") is expected here.  There must be a valid "
1536                 "JSON object member starts with a name, which is a string "
1537                 "enclosed in double quotes."
1538             );
1539 
1540             goto error;
1541         }
1542 
1543         name = p;
1544 
1545         count++;
1546 
1547         member = nxt_mp_get(mp_temp, sizeof(nxt_conf_object_member_t));
1548         if (nxt_slow_path(member == NULL)) {
1549             goto error;
1550         }
1551 
1552         p = nxt_conf_json_parse_string(mp, &member->name, p, end, error);
1553 
1554         if (nxt_slow_path(p == NULL)) {
1555             goto error;
1556         }
1557 
1558         rc = nxt_conf_object_hash_add(mp_temp, &hash, member);
1559 
1560         if (nxt_slow_path(rc != NXT_OK)) {
1561 
1562             if (rc == NXT_DECLINED) {
1563                 nxt_conf_json_parse_error(error, name,
1564                     "Duplicate object member.  All JSON object members must "
1565                     "have unique names."
1566                 );
1567             }
1568 
1569             goto error;
1570         }
1571 
1572         p = nxt_conf_json_skip_space(p, end);
1573 
1574         if (nxt_slow_path(p == end)) {
1575 
1576             nxt_conf_json_parse_error(error, p,
1577                 "Unexpected end of JSON payload.  There's an object member "
1578                 "without a value."
1579             );
1580 
1581             goto error;
1582         }
1583 
1584         if (nxt_slow_path(*p != ':')) {
1585 
1586             nxt_conf_json_parse_error(error, p,
1587                 "A colon (:) is expected here.  There must be a colon after "
1588                 "a JSON member name."
1589             );
1590 
1591             goto error;
1592         }
1593 
1594         p = nxt_conf_json_skip_space(p + 1, end);
1595 
1596         if (nxt_slow_path(p == end)) {
1597 
1598             nxt_conf_json_parse_error(error, p,
1599                 "Unexpected end of JSON payload.  There's an object member "
1600                 "without a value."
1601             );
1602 
1603             goto error;
1604         }
1605 
1606         p = nxt_conf_json_parse_value(mp, &member->value, p, end, error);
1607 
1608         if (nxt_slow_path(p == NULL)) {
1609             goto error;
1610         }
1611 
1612         p = nxt_conf_json_skip_space(p, end);
1613 
1614         if (nxt_slow_path(p == end)) {
1615 
1616             nxt_conf_json_parse_error(error, p,
1617                 "Unexpected end of JSON payload.  There's an object without "
1618                 "a closing brace (})."
1619             );
1620 
1621             goto error;
1622         }
1623 
1624         if (*p != ',') {
1625             if (nxt_fast_path(*p == '}')) {
1626                 break;
1627             }
1628 
1629             nxt_conf_json_parse_error(error, p,
1630                 "Either a closing brace (}) or a comma (,) is expected here.  "
1631                 "Each JSON object must be enclosed in braces and its members "
1632                 "must be separated by commas."
1633             );
1634 
1635             goto error;
1636         }
1637     }
1638 
1639     object = nxt_mp_get(mp, sizeof(nxt_conf_object_t)
1640                             + count * sizeof(nxt_conf_object_member_t));
1641     if (nxt_slow_path(object == NULL)) {
1642         goto error;
1643     }
1644 
1645     value->u.object = object;
1646     value->type = NXT_CONF_VALUE_OBJECT;
1647 
1648     object->count = count;
1649     member = object->members;
1650 
1651     nxt_lvlhsh_each_init(&lhe, &nxt_conf_object_hash_proto);
1652 
1653     for ( ;; ) {
1654         element = nxt_lvlhsh_each(&hash, &lhe);
1655 
1656         if (element == NULL) {
1657             break;
1658         }
1659 
1660         *member++ = *element;
1661     }
1662 
1663     nxt_mp_destroy(mp_temp);
1664 
1665     return p + 1;
1666 
1667 error:
1668 
1669     nxt_mp_destroy(mp_temp);
1670     return NULL;
1671 }
1672 
1673 
1674 static nxt_int_t
nxt_conf_object_hash_add(nxt_mp_t * mp,nxt_lvlhsh_t * lvlhsh,nxt_conf_object_member_t * member)1675 nxt_conf_object_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *lvlhsh,
1676     nxt_conf_object_member_t *member)
1677 {
1678     nxt_lvlhsh_query_t  lhq;
1679 
1680     nxt_conf_get_string(&member->name, &lhq.key);
1681 
1682     lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
1683     lhq.replace = 0;
1684     lhq.value = member;
1685     lhq.proto = &nxt_conf_object_hash_proto;
1686     lhq.pool = mp;
1687 
1688     return nxt_lvlhsh_insert(lvlhsh, &lhq);
1689 }
1690 
1691 
1692 static nxt_int_t
nxt_conf_object_hash_test(nxt_lvlhsh_query_t * lhq,void * data)1693 nxt_conf_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
1694 {
1695     nxt_str_t                 str;
1696     nxt_conf_object_member_t  *member;
1697 
1698     member = data;
1699 
1700     nxt_conf_get_string(&member->name, &str);
1701 
1702     return nxt_strstr_eq(&lhq->key, &str) ? NXT_OK : NXT_DECLINED;
1703 }
1704 
1705 
1706 static void *
nxt_conf_object_hash_alloc(void * data,size_t size)1707 nxt_conf_object_hash_alloc(void *data, size_t size)
1708 {
1709     return nxt_mp_align(data, size, size);
1710 }
1711 
1712 
1713 static void
nxt_conf_object_hash_free(void * data,void * p)1714 nxt_conf_object_hash_free(void *data, void *p)
1715 {
1716     nxt_mp_free(data, p);
1717 }
1718 
1719 
1720 static u_char *
nxt_conf_json_parse_array(nxt_mp_t * mp,nxt_conf_value_t * value,u_char * start,u_char * end,nxt_conf_json_error_t * error)1721 nxt_conf_json_parse_array(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
1722     u_char *end, nxt_conf_json_error_t *error)
1723 {
1724     u_char            *p;
1725     nxt_mp_t          *mp_temp;
1726     nxt_uint_t        count;
1727     nxt_list_t        *list;
1728     nxt_conf_array_t  *array;
1729     nxt_conf_value_t  *element;
1730 
1731     mp_temp = nxt_mp_create(1024, 128, 256, 32);
1732     if (nxt_slow_path(mp_temp == NULL)) {
1733         return NULL;
1734     }
1735 
1736     list = nxt_list_create(mp_temp, 8, sizeof(nxt_conf_value_t));
1737     if (nxt_slow_path(list == NULL)) {
1738         goto error;
1739     }
1740 
1741     count = 0;
1742     p = start;
1743 
1744     for ( ;; ) {
1745         p = nxt_conf_json_skip_space(p + 1, end);
1746 
1747         if (nxt_slow_path(p == end)) {
1748 
1749             nxt_conf_json_parse_error(error, p,
1750                 "Unexpected end of JSON payload.  There's an array without "
1751                 "a closing bracket (])."
1752             );
1753 
1754             goto error;
1755         }
1756 
1757         if (*p == ']') {
1758             break;
1759         }
1760 
1761         count++;
1762 
1763         element = nxt_list_add(list);
1764         if (nxt_slow_path(element == NULL)) {
1765             goto error;
1766         }
1767 
1768         p = nxt_conf_json_parse_value(mp, element, p, end, error);
1769 
1770         if (nxt_slow_path(p == NULL)) {
1771             goto error;
1772         }
1773 
1774         p = nxt_conf_json_skip_space(p, end);
1775 
1776         if (nxt_slow_path(p == end)) {
1777 
1778             nxt_conf_json_parse_error(error, p,
1779                 "Unexpected end of JSON payload.  There's an array without "
1780                 "a closing bracket (])."
1781             );
1782 
1783             goto error;
1784         }
1785 
1786         if (*p != ',') {
1787             if (nxt_fast_path(*p == ']')) {
1788                 break;
1789             }
1790 
1791             nxt_conf_json_parse_error(error, p,
1792                 "Either a closing bracket (]) or a comma (,) is expected "
1793                 "here.  Each array must be enclosed in brackets and its "
1794                 "members must be separated by commas."
1795             );
1796 
1797             goto error;
1798         }
1799     }
1800 
1801     array = nxt_mp_get(mp, sizeof(nxt_conf_array_t)
1802                            + count * sizeof(nxt_conf_value_t));
1803     if (nxt_slow_path(array == NULL)) {
1804         goto error;
1805     }
1806 
1807     value->u.array = array;
1808     value->type = NXT_CONF_VALUE_ARRAY;
1809 
1810     array->count = count;
1811     element = array->elements;
1812 
1813     nxt_list_each(value, list) {
1814         *element++ = *value;
1815     } nxt_list_loop;
1816 
1817     nxt_mp_destroy(mp_temp);
1818 
1819     return p + 1;
1820 
1821 error:
1822 
1823     nxt_mp_destroy(mp_temp);
1824     return NULL;
1825 }
1826 
1827 
1828 static u_char *
nxt_conf_json_parse_string(nxt_mp_t * mp,nxt_conf_value_t * value,u_char * start,u_char * end,nxt_conf_json_error_t * error)1829 nxt_conf_json_parse_string(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
1830     u_char *end, nxt_conf_json_error_t *error)
1831 {
1832     u_char      *p, ch, *last, *s;
1833     size_t      size, surplus;
1834     uint32_t    utf, utf_high;
1835     nxt_uint_t  i;
1836     enum {
1837         sw_usual = 0,
1838         sw_escape,
1839         sw_encoded1,
1840         sw_encoded2,
1841         sw_encoded3,
1842         sw_encoded4,
1843     } state;
1844 
1845     start++;
1846 
1847     state = 0;
1848     surplus = 0;
1849 
1850     for (p = start; nxt_fast_path(p != end); p++) {
1851         ch = *p;
1852 
1853         switch (state) {
1854 
1855         case sw_usual:
1856 
1857             if (ch == '"') {
1858                 break;
1859             }
1860 
1861             if (ch == '\\') {
1862                 state = sw_escape;
1863                 continue;
1864             }
1865 
1866             if (nxt_fast_path(ch >= ' ')) {
1867                 continue;
1868             }
1869 
1870             nxt_conf_json_parse_error(error, p,
1871                 "Unexpected character.  All control characters in a JSON "
1872                 "string must be escaped."
1873             );
1874 
1875             return NULL;
1876 
1877         case sw_escape:
1878 
1879             switch (ch) {
1880             case '"':
1881             case '\\':
1882             case '/':
1883             case 'n':
1884             case 'r':
1885             case 't':
1886             case 'b':
1887             case 'f':
1888                 surplus++;
1889                 state = sw_usual;
1890                 continue;
1891 
1892             case 'u':
1893                 /*
1894                  * Basic unicode 6 bytes "\uXXXX" in JSON
1895                  * and up to 3 bytes in UTF-8.
1896                  *
1897                  * Surrogate pair: 12 bytes "\uXXXX\uXXXX" in JSON
1898                  * and 3 or 4 bytes in UTF-8.
1899                  */
1900                 surplus += 3;
1901                 state = sw_encoded1;
1902                 continue;
1903             }
1904 
1905             nxt_conf_json_parse_error(error, p - 1,
1906                 "Unexpected backslash.  A literal backslash in a JSON string "
1907                 "must be escaped with a second backslash (\\\\)."
1908             );
1909 
1910             return NULL;
1911 
1912         case sw_encoded1:
1913         case sw_encoded2:
1914         case sw_encoded3:
1915         case sw_encoded4:
1916 
1917             if (nxt_fast_path((ch >= '0' && ch <= '9')
1918                               || (ch >= 'A' && ch <= 'F')
1919                               || (ch >= 'a' && ch <= 'f')))
1920             {
1921                 state = (state == sw_encoded4) ? sw_usual : state + 1;
1922                 continue;
1923             }
1924 
1925             nxt_conf_json_parse_error(error, p,
1926                 "Invalid escape sequence.  An escape sequence in a JSON "
1927                 "string must start with a backslash, followed by the lowercase "
1928                 "letter u, followed by four hexadecimal digits (\\uXXXX)."
1929             );
1930 
1931             return NULL;
1932         }
1933 
1934         break;
1935     }
1936 
1937     if (nxt_slow_path(p == end)) {
1938 
1939         nxt_conf_json_parse_error(error, p,
1940             "Unexpected end of JSON payload.  There's a string without "
1941             "a final double quote (\")."
1942         );
1943 
1944         return NULL;
1945     }
1946 
1947     /* Points to the ending quote mark. */
1948     last = p;
1949 
1950     size = last - start - surplus;
1951 
1952     if (size > NXT_CONF_MAX_SHORT_STRING) {
1953 
1954         if (nxt_slow_path(size > NXT_CONF_MAX_STRING)) {
1955 
1956             nxt_conf_json_parse_error(error, start,
1957                 "The string is too long.  Such a long JSON string value "
1958                 "is not supported."
1959             );
1960 
1961             return NULL;
1962         }
1963 
1964         value->type = NXT_CONF_VALUE_STRING;
1965 
1966         value->u.string.start = nxt_mp_nget(mp, size);
1967         if (nxt_slow_path(value->u.string.start == NULL)) {
1968             return NULL;
1969         }
1970 
1971         value->u.string.length = size;
1972 
1973         s = value->u.string.start;
1974 
1975     } else {
1976         value->type = NXT_CONF_VALUE_SHORT_STRING;
1977         value->u.str.length = size;
1978 
1979         s = value->u.str.start;
1980     }
1981 
1982     if (surplus == 0) {
1983         nxt_memcpy(s, start, size);
1984         return last + 1;
1985     }
1986 
1987     p = start;
1988 
1989     do {
1990         ch = *p++;
1991 
1992         if (ch != '\\') {
1993             *s++ = ch;
1994             continue;
1995         }
1996 
1997         ch = *p++;
1998 
1999         switch (ch) {
2000         case '"':
2001         case '\\':
2002         case '/':
2003             *s++ = ch;
2004             continue;
2005 
2006         case 'n':
2007             *s++ = '\n';
2008             continue;
2009 
2010         case 'r':
2011             *s++ = '\r';
2012             continue;
2013 
2014         case 't':
2015             *s++ = '\t';
2016             continue;
2017 
2018         case 'b':
2019             *s++ = '\b';
2020             continue;
2021 
2022         case 'f':
2023             *s++ = '\f';
2024             continue;
2025         }
2026 
2027         utf = 0;
2028         utf_high = 0;
2029 
2030         for ( ;; ) {
2031             for (i = 0; i < 4; i++) {
2032                 utf = (utf << 4) | (p[i] >= 'A' ? 10 + ((p[i] & ~0x20) - 'A')
2033                                                 : p[i] - '0');
2034             }
2035 
2036             p += 4;
2037 
2038             if (utf_high != 0) {
2039                 if (nxt_slow_path(utf < 0xDC00 || utf > 0xDFFF)) {
2040 
2041                     nxt_conf_json_parse_error(error, p - 12,
2042                         "Invalid JSON encoding sequence.  This 12-byte "
2043                         "sequence composes an illegal UTF-16 surrogate pair."
2044                     );
2045 
2046                     return NULL;
2047                 }
2048 
2049                 utf = ((utf_high - 0xD800) << 10) + (utf - 0xDC00) + 0x10000;
2050 
2051                 break;
2052             }
2053 
2054             if (utf < 0xD800 || utf > 0xDFFF) {
2055                 break;
2056             }
2057 
2058             if (utf > 0xDBFF || p[0] != '\\' || p[1] != 'u') {
2059 
2060                 nxt_conf_json_parse_error(error, p - 6,
2061                     "Invalid JSON encoding sequence.  This 6-byte sequence "
2062                     "does not represent a valid UTF character."
2063                 );
2064 
2065                 return NULL;
2066             }
2067 
2068             p += 2;
2069 
2070             utf_high = utf;
2071             utf = 0;
2072         }
2073 
2074         s = nxt_utf8_encode(s, utf);
2075 
2076     } while (p != last);
2077 
2078     if (size > NXT_CONF_MAX_SHORT_STRING) {
2079         value->u.string.length = s - value->u.string.start;
2080 
2081     } else {
2082         value->u.str.length = s - value->u.str.start;
2083     }
2084 
2085     return last + 1;
2086 }
2087 
2088 
2089 static u_char *
nxt_conf_json_parse_number(nxt_mp_t * mp,nxt_conf_value_t * value,u_char * start,u_char * end,nxt_conf_json_error_t * error)2090 nxt_conf_json_parse_number(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start,
2091     u_char *end, nxt_conf_json_error_t *error)
2092 {
2093     u_char  *p, *s, ch, c, *dot_pos;
2094     size_t  size;
2095     double  num;
2096 
2097     s = start;
2098     ch = *s;
2099 
2100     if (ch == '-') {
2101         s++;
2102     }
2103 
2104     dot_pos = NULL;
2105 
2106     for (p = s; nxt_fast_path(p != end); p++) {
2107         ch = *p;
2108 
2109         /* Values below '0' become >= 208. */
2110         c = ch - '0';
2111 
2112         if (c > 9) {
2113             if (ch == '.' && nxt_fast_path(dot_pos == NULL)) {
2114                 dot_pos = p;
2115                 continue;
2116             }
2117 
2118             break;
2119         }
2120     }
2121 
2122     if (dot_pos != NULL) {
2123         if (nxt_slow_path(p - dot_pos <= 1)) {
2124             nxt_conf_json_parse_error(error, s,
2125                 "The number is invalid.  A fraction part in JSON numbers "
2126                 "must contain at least one digit."
2127             );
2128 
2129             return NULL;
2130         }
2131 
2132     } else {
2133         dot_pos = p;
2134     }
2135 
2136     if (nxt_slow_path(dot_pos - s > 1 && *start == '0')) {
2137         nxt_conf_json_parse_error(error, s,
2138             "The number is invalid.  Leading zeros are not allowed in JSON "
2139             "numbers."
2140         );
2141 
2142         return NULL;
2143     }
2144 
2145     if (ch == 'e' || ch == 'E') {
2146         p++;
2147         s = p;
2148 
2149         if (nxt_fast_path(s != end)) {
2150             ch = *s;
2151 
2152             if (ch == '-' || ch == '+') {
2153                 s++;
2154             }
2155 
2156             for (p = s; nxt_fast_path(p != end); p++) {
2157                 ch = *p;
2158 
2159                 /* Values below '0' become >= 208. */
2160                 c = ch - '0';
2161 
2162                 if (c > 9) {
2163                     break;
2164                 }
2165             }
2166         }
2167 
2168         if (nxt_slow_path(p == s)) {
2169             nxt_conf_json_parse_error(error, start,
2170                 "The number is invalid.  An exponent part in JSON numbers "
2171                 "must contain at least one digit."
2172             );
2173 
2174             return NULL;
2175         }
2176     }
2177 
2178     size = p - start;
2179 
2180     if (size > NXT_CONF_MAX_NUMBER_LEN) {
2181         nxt_conf_json_parse_error(error, start,
2182             "The number is too long.  Such a long JSON number value "
2183             "is not supported."
2184         );
2185 
2186         return NULL;
2187     }
2188 
2189     nxt_memcpy(value->u.number, start, size);
2190     value->u.number[size] = '\0';
2191 
2192     nxt_errno = 0;
2193     end = NULL;
2194 
2195     num = nxt_strtod(value->u.number, &end);
2196 
2197     if (nxt_slow_path(nxt_errno == NXT_ERANGE
2198         || fabs(num) > (double) NXT_INT64_T_MAX))
2199     {
2200         nxt_conf_json_parse_error(error, start,
2201             "The number is out of representable range.  Such JSON number "
2202             "value is not supported."
2203         );
2204 
2205         return NULL;
2206     }
2207 
2208     if (nxt_slow_path(end == NULL || *end != '\0')) {
2209         nxt_thread_log_alert("strtod(\"%s\", %s) failed %E", value->u.number,
2210                              end == NULL ? (u_char *) "NULL" : end, nxt_errno);
2211         return NULL;
2212     }
2213 
2214     value->type = (num == trunc(num)) ? NXT_CONF_VALUE_INTEGER
2215                                       : NXT_CONF_VALUE_NUMBER;
2216 
2217     return p;
2218 }
2219 
2220 
2221 static void
nxt_conf_json_parse_error(nxt_conf_json_error_t * error,u_char * pos,const char * detail)2222 nxt_conf_json_parse_error(nxt_conf_json_error_t *error, u_char *pos,
2223     const char *detail)
2224 {
2225     if (error == NULL) {
2226         return;
2227     }
2228 
2229     error->pos = pos;
2230     error->detail = (u_char *) detail;
2231 }
2232 
2233 
2234 size_t
nxt_conf_json_length(nxt_conf_value_t * value,nxt_conf_json_pretty_t * pretty)2235 nxt_conf_json_length(nxt_conf_value_t *value, nxt_conf_json_pretty_t *pretty)
2236 {
2237     switch (value->type) {
2238 
2239     case NXT_CONF_VALUE_NULL:
2240         return nxt_length("null");
2241 
2242     case NXT_CONF_VALUE_BOOLEAN:
2243         return value->u.boolean ? nxt_length("true") : nxt_length("false");
2244 
2245     case NXT_CONF_VALUE_INTEGER:
2246     case NXT_CONF_VALUE_NUMBER:
2247         return nxt_strlen(value->u.number);
2248 
2249     case NXT_CONF_VALUE_SHORT_STRING:
2250     case NXT_CONF_VALUE_STRING:
2251         return nxt_conf_json_string_length(value);
2252 
2253     case NXT_CONF_VALUE_ARRAY:
2254         return nxt_conf_json_array_length(value, pretty);
2255 
2256     case NXT_CONF_VALUE_OBJECT:
2257         return nxt_conf_json_object_length(value, pretty);
2258     }
2259 
2260     nxt_unreachable();
2261 
2262     return 0;
2263 }
2264 
2265 
2266 u_char *
nxt_conf_json_print(u_char * p,nxt_conf_value_t * value,nxt_conf_json_pretty_t * pretty)2267 nxt_conf_json_print(u_char *p, nxt_conf_value_t *value,
2268     nxt_conf_json_pretty_t *pretty)
2269 {
2270     switch (value->type) {
2271 
2272     case NXT_CONF_VALUE_NULL:
2273         return nxt_cpymem(p, "null", 4);
2274 
2275     case NXT_CONF_VALUE_BOOLEAN:
2276         return value->u.boolean ? nxt_cpymem(p, "true", 4)
2277                                 : nxt_cpymem(p, "false", 5);
2278 
2279     case NXT_CONF_VALUE_INTEGER:
2280     case NXT_CONF_VALUE_NUMBER:
2281         return nxt_cpystr(p, value->u.number);
2282 
2283     case NXT_CONF_VALUE_SHORT_STRING:
2284     case NXT_CONF_VALUE_STRING:
2285         return nxt_conf_json_print_string(p, value);
2286 
2287     case NXT_CONF_VALUE_ARRAY:
2288         return nxt_conf_json_print_array(p, value, pretty);
2289 
2290     case NXT_CONF_VALUE_OBJECT:
2291         return nxt_conf_json_print_object(p, value, pretty);
2292     }
2293 
2294     nxt_unreachable();
2295 
2296     return p;
2297 }
2298 
2299 
2300 static size_t
nxt_conf_json_string_length(nxt_conf_value_t * value)2301 nxt_conf_json_string_length(nxt_conf_value_t *value)
2302 {
2303     nxt_str_t  str;
2304 
2305     nxt_conf_get_string(value, &str);
2306 
2307     return 2 + nxt_conf_json_escape_length(str.start, str.length);
2308 }
2309 
2310 
2311 static u_char *
nxt_conf_json_print_string(u_char * p,nxt_conf_value_t * value)2312 nxt_conf_json_print_string(u_char *p, nxt_conf_value_t *value)
2313 {
2314     nxt_str_t  str;
2315 
2316     nxt_conf_get_string(value, &str);
2317 
2318     *p++ = '"';
2319 
2320     p = nxt_conf_json_escape(p, str.start, str.length);
2321 
2322     *p++ = '"';
2323 
2324     return p;
2325 }
2326 
2327 
2328 static size_t
nxt_conf_json_array_length(nxt_conf_value_t * value,nxt_conf_json_pretty_t * pretty)2329 nxt_conf_json_array_length(nxt_conf_value_t *value,
2330     nxt_conf_json_pretty_t *pretty)
2331 {
2332     size_t            len;
2333     nxt_uint_t        n;
2334     nxt_conf_array_t  *array;
2335 
2336     array = value->u.array;
2337 
2338     /* [] */
2339     len = 2;
2340 
2341     if (pretty != NULL) {
2342         pretty->level++;
2343     }
2344 
2345     value = array->elements;
2346 
2347     for (n = 0; n < array->count; n++) {
2348         len += nxt_conf_json_length(&value[n], pretty);
2349 
2350         if (pretty != NULL) {
2351             /* Indentation and new line. */
2352             len += pretty->level + 2;
2353         }
2354     }
2355 
2356     if (pretty != NULL) {
2357         pretty->level--;
2358 
2359         if (n != 0) {
2360             /* Indentation and new line. */
2361             len += pretty->level + 2;
2362         }
2363     }
2364 
2365     /* Reserve space for "n" commas. */
2366     return len + n;
2367 }
2368 
2369 
2370 static u_char *
nxt_conf_json_print_array(u_char * p,nxt_conf_value_t * value,nxt_conf_json_pretty_t * pretty)2371 nxt_conf_json_print_array(u_char *p, nxt_conf_value_t *value,
2372     nxt_conf_json_pretty_t *pretty)
2373 {
2374     nxt_uint_t        n;
2375     nxt_conf_array_t  *array;
2376 
2377     array = value->u.array;
2378 
2379     *p++ = '[';
2380 
2381     if (array->count != 0) {
2382         value = array->elements;
2383 
2384         if (pretty != NULL) {
2385             p = nxt_conf_json_newline(p);
2386 
2387             pretty->level++;
2388             p = nxt_conf_json_indentation(p, pretty->level);
2389         }
2390 
2391         p = nxt_conf_json_print(p, &value[0], pretty);
2392 
2393         for (n = 1; n < array->count; n++) {
2394             *p++ = ',';
2395 
2396             if (pretty != NULL) {
2397                 p = nxt_conf_json_newline(p);
2398                 p = nxt_conf_json_indentation(p, pretty->level);
2399 
2400                 pretty->more_space = 0;
2401             }
2402 
2403             p = nxt_conf_json_print(p, &value[n], pretty);
2404         }
2405 
2406         if (pretty != NULL) {
2407             p = nxt_conf_json_newline(p);
2408 
2409             pretty->level--;
2410             p = nxt_conf_json_indentation(p, pretty->level);
2411 
2412             pretty->more_space = 1;
2413         }
2414     }
2415 
2416     *p++ = ']';
2417 
2418     return p;
2419 }
2420 
2421 
2422 static size_t
nxt_conf_json_object_length(nxt_conf_value_t * value,nxt_conf_json_pretty_t * pretty)2423 nxt_conf_json_object_length(nxt_conf_value_t *value,
2424     nxt_conf_json_pretty_t *pretty)
2425 {
2426     size_t                    len;
2427     nxt_uint_t                n;
2428     nxt_conf_object_t         *object;
2429     nxt_conf_object_member_t  *member;
2430 
2431     object = value->u.object;
2432 
2433     /* {} */
2434     len = 2;
2435 
2436     if (pretty != NULL) {
2437         pretty->level++;
2438     }
2439 
2440     member = object->members;
2441 
2442     for (n = 0; n < object->count; n++) {
2443         len += nxt_conf_json_string_length(&member[n].name) + 1
2444                + nxt_conf_json_length(&member[n].value, pretty) + 1;
2445 
2446         if (pretty != NULL) {
2447             /*
2448              * Indentation, space after ":", new line, and possible
2449              * additional empty line between non-empty objects.
2450              */
2451             len += pretty->level + 1 + 2 + 2;
2452         }
2453     }
2454 
2455     if (pretty != NULL) {
2456         pretty->level--;
2457 
2458         /* Indentation and new line. */
2459         len += pretty->level + 2;
2460     }
2461 
2462     return len;
2463 }
2464 
2465 
2466 static u_char *
nxt_conf_json_print_object(u_char * p,nxt_conf_value_t * value,nxt_conf_json_pretty_t * pretty)2467 nxt_conf_json_print_object(u_char *p, nxt_conf_value_t *value,
2468     nxt_conf_json_pretty_t *pretty)
2469 {
2470     nxt_uint_t                n;
2471     nxt_conf_object_t         *object;
2472     nxt_conf_object_member_t  *member;
2473 
2474     object = value->u.object;
2475 
2476     *p++ = '{';
2477 
2478     if (object->count != 0) {
2479 
2480         if (pretty != NULL) {
2481             p = nxt_conf_json_newline(p);
2482             pretty->level++;
2483         }
2484 
2485         member = object->members;
2486 
2487         n = 0;
2488 
2489         for ( ;; ) {
2490             if (pretty != NULL) {
2491                 p = nxt_conf_json_indentation(p, pretty->level);
2492             }
2493 
2494             p = nxt_conf_json_print_string(p, &member[n].name);
2495 
2496             *p++ = ':';
2497 
2498             if (pretty != NULL) {
2499                 *p++ = ' ';
2500             }
2501 
2502             p = nxt_conf_json_print(p, &member[n].value, pretty);
2503 
2504             n++;
2505 
2506             if (n == object->count) {
2507                 break;
2508             }
2509 
2510             *p++ = ',';
2511 
2512             if (pretty != NULL) {
2513                 p = nxt_conf_json_newline(p);
2514 
2515                 if (pretty->more_space) {
2516                     pretty->more_space = 0;
2517                     p = nxt_conf_json_newline(p);
2518                 }
2519             }
2520         }
2521 
2522         if (pretty != NULL) {
2523             p = nxt_conf_json_newline(p);
2524 
2525             pretty->level--;
2526             p = nxt_conf_json_indentation(p, pretty->level);
2527 
2528             pretty->more_space = 1;
2529         }
2530     }
2531 
2532     *p++ = '}';
2533 
2534     return p;
2535 }
2536 
2537 
2538 static size_t
nxt_conf_json_escape_length(u_char * p,size_t size)2539 nxt_conf_json_escape_length(u_char *p, size_t size)
2540 {
2541     u_char  ch;
2542     size_t  len;
2543 
2544     len = size;
2545 
2546     while (size) {
2547         ch = *p++;
2548 
2549         if (ch == '\\' || ch == '"') {
2550             len++;
2551 
2552         } else if (ch <= 0x1F) {
2553 
2554             switch (ch) {
2555             case '\n':
2556             case '\r':
2557             case '\t':
2558             case '\b':
2559             case '\f':
2560                 len++;
2561                 break;
2562 
2563             default:
2564                 len += sizeof("\\u001F") - 2;
2565             }
2566         }
2567 
2568         size--;
2569     }
2570 
2571     return len;
2572 }
2573 
2574 
2575 static u_char *
nxt_conf_json_escape(u_char * dst,u_char * src,size_t size)2576 nxt_conf_json_escape(u_char *dst, u_char *src, size_t size)
2577 {
2578     u_char  ch;
2579 
2580     while (size) {
2581         ch = *src++;
2582 
2583         if (ch > 0x1F) {
2584 
2585             if (ch == '\\' || ch == '"') {
2586                 *dst++ = '\\';
2587             }
2588 
2589             *dst++ = ch;
2590 
2591         } else {
2592             *dst++ = '\\';
2593 
2594             switch (ch) {
2595             case '\n':
2596                 *dst++ = 'n';
2597                 break;
2598 
2599             case '\r':
2600                 *dst++ = 'r';
2601                 break;
2602 
2603             case '\t':
2604                 *dst++ = 't';
2605                 break;
2606 
2607             case '\b':
2608                 *dst++ = 'b';
2609                 break;
2610 
2611             case '\f':
2612                 *dst++ = 'f';
2613                 break;
2614 
2615             default:
2616                 *dst++ = 'u'; *dst++ = '0'; *dst++ = '0';
2617                 *dst++ = '0' + (ch >> 4);
2618 
2619                 ch &= 0xF;
2620 
2621                 *dst++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10);
2622             }
2623         }
2624 
2625         size--;
2626     }
2627 
2628     return dst;
2629 }
2630 
2631 
2632 void
nxt_conf_json_position(u_char * start,const u_char * pos,nxt_uint_t * line,nxt_uint_t * column)2633 nxt_conf_json_position(u_char *start, const u_char *pos, nxt_uint_t *line,
2634     nxt_uint_t *column)
2635 {
2636     u_char      *p;
2637     ssize_t     symbols;
2638     nxt_uint_t  lines;
2639 
2640     lines = 1;
2641 
2642     for (p = start; p != pos; p++) {
2643 
2644         if (*p != '\n') {
2645             continue;
2646         }
2647 
2648         lines++;
2649         start = p + 1;
2650     }
2651 
2652     symbols = nxt_utf8_length(start, p - start);
2653 
2654     if (symbols != -1) {
2655         *line = lines;
2656         *column = 1 + symbols;
2657     }
2658 }
2659