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