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