xref: /unit/src/nxt_var.c (revision 2243:dd668fe8d827)
1 
2 /*
3  * Copyright (C) NGINX, Inc.
4  */
5 
6 #include <nxt_main.h>
7 
8 
9 struct nxt_var_s {
10     size_t              length;
11     nxt_uint_t          vars;
12     nxt_var_flags_t     flags;
13     u_char              data[];
14 
15 /*
16     nxt_var_sub_t       subs[vars];
17     u_char              raw[length];
18 */
19 };
20 
21 
22 typedef struct {
23     uint32_t            index;
24     uint32_t            length;
25     uint32_t            position;
26 } nxt_var_sub_t;
27 
28 
29 struct nxt_var_query_s {
30     nxt_mp_t            *pool;
31 
32     nxt_lvlhsh_t        cache;
33     nxt_str_t           *spare;
34 
35     nxt_uint_t          waiting;
36     nxt_uint_t          failed;   /* 1 bit */
37 
38     void                *ctx;
39     void                *data;
40 
41     nxt_work_handler_t  ready;
42     nxt_work_handler_t  error;
43 };
44 
45 
46 #define nxt_var_subs(var)  ((nxt_var_sub_t *) (var)->data)
47 
48 #define nxt_var_raw_start(var)                                                \
49     ((var)->data + (var)->vars * sizeof(nxt_var_sub_t))
50 
51 
52 static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
53 static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name);
54 
55 static nxt_var_decl_t *nxt_var_decl_get(nxt_str_t *name, nxt_array_t *fields,
56     uint32_t *index);
57 static nxt_var_field_t *nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name,
58     uint32_t hash);
59 
60 static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data);
61 static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query,
62     uint32_t index);
63 
64 static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part);
65 
66 
67 static const nxt_lvlhsh_proto_t  nxt_var_hash_proto  nxt_aligned(64) = {
68     NXT_LVLHSH_DEFAULT,
69     nxt_var_hash_test,
70     nxt_lvlhsh_alloc,
71     nxt_lvlhsh_free,
72 };
73 
74 static const nxt_lvlhsh_proto_t  nxt_var_cache_proto  nxt_aligned(64) = {
75     NXT_LVLHSH_DEFAULT,
76     nxt_var_cache_test,
77     nxt_mp_lvlhsh_alloc,
78     nxt_mp_lvlhsh_free,
79 };
80 
81 
82 static nxt_lvlhsh_t       nxt_var_hash;
83 static uint32_t           nxt_var_count;
84 
85 static nxt_var_handler_t  *nxt_var_index;
86 
87 
88 static nxt_int_t
89 nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
90 {
91     nxt_var_decl_t  *decl;
92 
93     decl = data;
94 
95     return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED;
96 }
97 
98 
99 static nxt_var_decl_t *
100 nxt_var_hash_find(nxt_str_t *name)
101 {
102     nxt_lvlhsh_query_t  lhq;
103 
104     lhq.key_hash = nxt_djb_hash(name->start, name->length);
105     lhq.key = *name;
106     lhq.proto = &nxt_var_hash_proto;
107 
108     if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) {
109         return NULL;
110     }
111 
112     return lhq.value;
113 }
114 
115 
116 static nxt_var_decl_t *
117 nxt_var_decl_get(nxt_str_t *name, nxt_array_t *fields, uint32_t *index)
118 {
119     u_char           *p, *end;
120     int64_t          hash;
121     uint16_t         field;
122     nxt_str_t        str;
123     nxt_var_decl_t   *decl;
124     nxt_var_field_t  *f;
125 
126     f = NULL;
127     field = 0;
128     decl = nxt_var_hash_find(name);
129 
130     if (decl == NULL) {
131         p = name->start;
132         end = p + name->length;
133 
134         while (p < end) {
135             if (*p++ == '_') {
136                 break;
137             }
138         }
139 
140         if (p == end) {
141             return NULL;
142         }
143 
144         str.start = name->start;
145         str.length = p - 1 - name->start;
146 
147         decl = nxt_var_hash_find(&str);
148 
149         if (decl != NULL) {
150             str.start = p;
151             str.length = end - p;
152 
153             hash = decl->field_hash(fields->mem_pool, &str);
154             if (nxt_slow_path(hash == -1)) {
155                 return NULL;
156             }
157 
158             f = nxt_var_field_add(fields, &str, (uint32_t) hash);
159             if (nxt_slow_path(f == NULL)) {
160                 return NULL;
161             }
162 
163             field = f->index;
164         }
165     }
166 
167     if (decl != NULL) {
168         if (decl->field_hash != NULL && f == NULL) {
169             return NULL;
170         }
171 
172         if (index != NULL) {
173             *index = (decl->index << 16) | field;
174         }
175     }
176 
177     return decl;
178 }
179 
180 
181 static nxt_var_field_t *
182 nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name, uint32_t hash)
183 {
184     nxt_uint_t       i;
185     nxt_var_field_t  *field;
186 
187     field = fields->elts;
188 
189     for (i = 0; i < fields->nelts; i++) {
190         if (field[i].hash == hash
191             && nxt_strstr_eq(&field[i].name, name))
192         {
193             return field;
194         }
195     }
196 
197     field = nxt_array_add(fields);
198     if (nxt_slow_path(field == NULL)) {
199         return NULL;
200     }
201 
202     field->name = *name;
203     field->hash = hash;
204     field->index = fields->nelts - 1;
205 
206     return field;
207 }
208 
209 
210 nxt_var_field_t *
211 nxt_var_field_get(nxt_array_t *fields, uint16_t index)
212 {
213     nxt_uint_t       nfields;
214     nxt_var_field_t  *field;
215 
216     field = fields->elts;
217     nfields = fields->nelts;
218 
219     if (nfields > 0 && index <= nfields) {
220         return &field[index];
221     }
222 
223     return NULL;
224 }
225 
226 
227 static nxt_int_t
228 nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
229 {
230     return NXT_OK;
231 }
232 
233 
234 static nxt_str_t *
235 nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index)
236 {
237     nxt_int_t           ret;
238     nxt_str_t           *value;
239     nxt_lvlhsh_query_t  lhq;
240 
241     value = query->spare;
242 
243     if (value == NULL) {
244         value = nxt_mp_zget(query->pool, sizeof(nxt_str_t));
245         if (nxt_slow_path(value == NULL)) {
246             return NULL;
247         }
248 
249         query->spare = value;
250     }
251 
252     lhq.key_hash = nxt_murmur_hash2_uint32(&index);
253     lhq.replace = 0;
254     lhq.key.length = sizeof(uint32_t);
255     lhq.key.start = (u_char *) &index;
256     lhq.value = value;
257     lhq.proto = &nxt_var_cache_proto;
258     lhq.pool = query->pool;
259 
260     ret = nxt_lvlhsh_insert(&query->cache, &lhq);
261     if (nxt_slow_path(ret == NXT_ERROR)) {
262         return NULL;
263     }
264 
265     if (ret == NXT_OK) {
266         ret = nxt_var_index[index >> 16](task, value, query->ctx,
267                                          index & 0xffff);
268         if (nxt_slow_path(ret != NXT_OK)) {
269             return NULL;
270         }
271 
272         query->spare = NULL;
273     }
274 
275     return lhq.value;
276 }
277 
278 
279 nxt_int_t
280 nxt_var_register(nxt_var_decl_t *decl, size_t n)
281 {
282     nxt_uint_t          i;
283     nxt_lvlhsh_query_t  lhq;
284 
285     lhq.replace = 0;
286     lhq.proto = &nxt_var_hash_proto;
287 
288     for (i = 0; i < n; i++) {
289         lhq.key = decl[i].name;
290         lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
291         lhq.value = &decl[i];
292 
293         if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) {
294             return NXT_ERROR;
295         }
296     }
297 
298     nxt_var_count += n;
299 
300     return NXT_OK;
301 }
302 
303 
304 nxt_int_t
305 nxt_var_index_init(void)
306 {
307     nxt_uint_t         i;
308     nxt_var_decl_t     *decl;
309     nxt_var_handler_t  *index;
310     nxt_lvlhsh_each_t  lhe;
311 
312     index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t));
313     if (index == NULL) {
314         return NXT_ERROR;
315     }
316 
317     nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto);
318 
319     for (i = 0; i < nxt_var_count; i++) {
320         decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe);
321         decl->index = i;
322         index[i] = decl->handler;
323     }
324 
325     nxt_var_index = index;
326 
327     return NXT_OK;
328 }
329 
330 
331 nxt_var_t *
332 nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields,
333     nxt_var_flags_t flags)
334 {
335     u_char          *p, *end, *next, *src;
336     size_t          size;
337     uint32_t        index;
338     nxt_bool_t      strz;
339     nxt_var_t       *var;
340     nxt_str_t       part;
341     nxt_uint_t      n;
342     nxt_var_sub_t   *subs;
343     nxt_var_decl_t  *decl;
344 
345     strz = (flags & NXT_VAR_STRZ) != 0;
346 
347     n = 0;
348 
349     p = str->start;
350     end = p + str->length;
351 
352     while (p < end) {
353         p = nxt_var_next_part(p, end, &part);
354         if (nxt_slow_path(p == NULL)) {
355             return NULL;
356         }
357 
358         if (part.start != NULL) {
359             n++;
360         }
361     }
362 
363     size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;
364 
365     var = nxt_mp_get(mp, size + strz);
366     if (nxt_slow_path(var == NULL)) {
367         return NULL;
368     }
369 
370     var->length = str->length;
371     var->vars = n;
372     var->flags = flags;
373 
374     subs = nxt_var_subs(var);
375     src = nxt_var_raw_start(var);
376 
377     nxt_memcpy(src, str->start, str->length);
378 
379     if (strz) {
380         src[str->length] = '\0';
381     }
382 
383     n = 0;
384     p = str->start;
385 
386     while (p < end) {
387         next = nxt_var_next_part(p, end, &part);
388 
389         if (part.start != NULL) {
390             decl = nxt_var_decl_get(&part, fields, &index);
391             if (nxt_slow_path(decl == NULL)) {
392                 return NULL;
393             }
394 
395             subs[n].index = index;
396             subs[n].length = next - p;
397             subs[n].position = p - str->start;
398 
399             n++;
400         }
401 
402         p = next;
403     }
404 
405     return var;
406 }
407 
408 
409 nxt_int_t
410 nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error)
411 {
412     u_char          *p, *end, *next;
413     nxt_str_t       part;
414     nxt_var_decl_t  *decl;
415 
416     p = str->start;
417     end = p + str->length;
418 
419     while (p < end) {
420         next = nxt_var_next_part(p, end, &part);
421 
422         if (next == NULL) {
423             nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
424                         "Invalid variable at position %uz%Z", p - str->start);
425 
426             return NXT_ERROR;
427         }
428 
429         if (part.start != NULL) {
430             decl = nxt_var_decl_get(&part, fields, NULL);
431 
432             if (decl == NULL) {
433                 nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
434                             "Unknown variable \"%V\"%Z", &part);
435 
436                 return NXT_ERROR;
437             }
438         }
439 
440         p = next;
441     }
442 
443     return NXT_OK;
444 }
445 
446 
447 static u_char *
448 nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part)
449 {
450     size_t      length;
451     u_char      *p, ch, c;
452     nxt_bool_t  bracket;
453 
454     p = memchr(start, '$', end - start);
455 
456     if (p == start) {
457         p++;
458 
459         if (p == end) {
460             return NULL;
461         }
462 
463         if (*p == '{') {
464             bracket = 1;
465 
466             if (end - p < 2) {
467                 return NULL;
468             }
469 
470             p++;
471 
472         } else {
473             bracket = 0;
474         }
475 
476         length = 0;
477         start = p;
478 
479         while (p < end) {
480             ch = *p;
481 
482             c = (u_char) (ch | 0x20);
483 
484             if ((c >= 'a' && c <= 'z') || ch == '_') {
485                 p++;
486                 length++;
487                 continue;
488             }
489 
490             if (bracket && ch == '}') {
491                 p++;
492                 bracket = 0;
493             }
494 
495             break;
496         }
497 
498         if (bracket || length == 0) {
499             return NULL;
500         }
501 
502         part->length = length;
503         part->start = start;
504 
505     } else {
506         if (p == NULL) {
507             p = end;
508         }
509 
510         nxt_str_null(part);
511     }
512 
513     return p;
514 }
515 
516 
517 inline void
518 nxt_var_raw(nxt_var_t *var, nxt_str_t *str)
519 {
520     str->length = var->length;
521     str->start = nxt_var_raw_start(var);
522 }
523 
524 
525 inline nxt_bool_t
526 nxt_var_is_const(nxt_var_t *var)
527 {
528     return (var->vars == 0);
529 }
530 
531 
532 nxt_int_t
533 nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, nxt_mp_t *mp)
534 {
535     nxt_var_query_t  *query;
536 
537     query = *query_p;
538 
539     if (*query_p == NULL) {
540         query = nxt_mp_zget(mp, sizeof(nxt_var_query_t));
541         if (nxt_slow_path(query == NULL)) {
542             return NXT_ERROR;
543         }
544     }
545 
546     query->pool = mp;
547     query->ctx = ctx;
548 
549     *query_p = query;
550 
551     return NXT_OK;
552 }
553 
554 
555 void
556 nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var,
557     nxt_str_t *str)
558 {
559     u_char         *p, *src;
560     size_t         length, last, next;
561     nxt_str_t      *value, **part;
562     nxt_uint_t     i;
563     nxt_bool_t     strz, logging;
564     nxt_array_t    parts;
565     nxt_var_sub_t  *subs;
566 
567     if (nxt_var_is_const(var)) {
568         nxt_var_raw(var, str);
569         return;
570     }
571 
572     if (nxt_slow_path(query->failed)) {
573         return;
574     }
575 
576     nxt_memzero(&parts, sizeof(nxt_array_t));
577     nxt_array_init(&parts, query->pool, sizeof(nxt_str_t *));
578 
579     strz = (var->flags & NXT_VAR_STRZ) != 0;
580     logging = (var->flags & NXT_VAR_LOGGING) != 0;
581 
582     subs = nxt_var_subs(var);
583 
584     length = var->length;
585 
586     for (i = 0; i < var->vars; i++) {
587         value = nxt_var_cache_value(task, query, subs[i].index);
588         if (nxt_slow_path(value == NULL)) {
589             goto fail;
590         }
591 
592         part = nxt_array_add(&parts);
593         if (nxt_slow_path(part == NULL)) {
594             goto fail;
595         }
596 
597         *part = value;
598 
599         length += value->length - subs[i].length;
600 
601         if (logging && value->start == NULL) {
602             length += 1;
603         }
604     }
605 
606     p = nxt_mp_nget(query->pool, length + strz);
607     if (nxt_slow_path(p == NULL)) {
608         goto fail;
609     }
610 
611     str->length = length;
612     str->start = p;
613 
614     part = parts.elts;
615     src = nxt_var_raw_start(var);
616 
617     last = 0;
618 
619     for (i = 0; i < var->vars; i++) {
620         next = subs[i].position;
621 
622         if (next != last) {
623             p = nxt_cpymem(p, &src[last], next - last);
624         }
625 
626         p = nxt_cpymem(p, part[i]->start, part[i]->length);
627 
628         if (logging && part[i]->start == NULL) {
629             *p++ = '-';
630         }
631 
632         last = next + subs[i].length;
633     }
634 
635     if (last != var->length) {
636         p = nxt_cpymem(p, &src[last], var->length - last);
637     }
638 
639     if (strz) {
640         *p = '\0';
641     }
642 
643     nxt_debug(task, "var: \"%*s\" -> \"%V\"", length, src, str);
644 
645     return;
646 
647 fail:
648 
649     query->failed = 1;
650 }
651 
652 
653 void
654 nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
655     nxt_work_handler_t ready, nxt_work_handler_t error)
656 {
657     query->data = data;
658     query->ready = ready;
659     query->error = error;
660 
661     if (query->waiting == 0) {
662         nxt_work_queue_add(&task->thread->engine->fast_work_queue,
663                            query->failed ? query->error : query->ready,
664                            task, query->ctx, query->data);
665     }
666 }
667 
668 
669 void
670 nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
671     nxt_bool_t failed)
672 {
673     query->failed |= failed;
674 
675     if (--query->waiting == 0) {
676         nxt_work_queue_add(&task->thread->engine->fast_work_queue,
677                            query->failed ? query->error : query->ready,
678                            task, query->ctx, query->data);
679     }
680 }
681