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