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