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