xref: /unit/src/nxt_var.c (revision 2128:1ed9f036a2df)
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 static nxt_int_t
nxt_var_hash_test(nxt_lvlhsh_query_t * lhq,void * data)95 nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
96 {
97     nxt_var_decl_t  *decl;
98 
99     decl = data;
100 
101     return nxt_strstr_eq(&lhq->key, &decl->name) ? NXT_OK : NXT_DECLINED;
102 }
103 
104 
105 static nxt_var_decl_t *
nxt_var_hash_find(nxt_str_t * name)106 nxt_var_hash_find(nxt_str_t *name)
107 {
108     nxt_lvlhsh_query_t  lhq;
109 
110     lhq.key_hash = nxt_djb_hash(name->start, name->length);
111     lhq.key = *name;
112     lhq.proto = &nxt_var_hash_proto;
113 
114     if (nxt_lvlhsh_find(&nxt_var_hash, &lhq) != NXT_OK) {
115         return NULL;
116     }
117 
118     return lhq.value;
119 }
120 
121 
122 static nxt_int_t
nxt_var_cache_test(nxt_lvlhsh_query_t * lhq,void * data)123 nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data)
124 {
125     return NXT_OK;
126 }
127 
128 
129 static nxt_str_t *
nxt_var_cache_find(nxt_lvlhsh_t * lh,uint32_t index)130 nxt_var_cache_find(nxt_lvlhsh_t *lh, uint32_t index)
131 {
132     nxt_lvlhsh_query_t  lhq;
133 
134     lhq.key_hash = nxt_murmur_hash2_uint32(&index);
135     lhq.key.length = sizeof(uint32_t);
136     lhq.key.start = (u_char *) &index;
137     lhq.proto = &nxt_var_cache_proto;
138 
139     if (nxt_lvlhsh_find(lh, &lhq) != NXT_OK) {
140         return NULL;
141     }
142 
143     return lhq.value;
144 }
145 
146 
147 static nxt_int_t
nxt_var_cache_add(nxt_lvlhsh_t * lh,uint32_t index,nxt_str_t * value,nxt_mp_t * mp)148 nxt_var_cache_add(nxt_lvlhsh_t *lh, uint32_t index, nxt_str_t *value,
149     nxt_mp_t *mp)
150 {
151     nxt_lvlhsh_query_t  lhq;
152 
153     lhq.key_hash = nxt_murmur_hash2_uint32(&index);
154     lhq.replace = 0;
155     lhq.key.length = sizeof(uint32_t);
156     lhq.key.start = (u_char *) &index;
157     lhq.value = value;
158     lhq.proto = &nxt_var_cache_proto;
159     lhq.pool = mp;
160 
161     return nxt_lvlhsh_insert(lh, &lhq);
162 }
163 
164 
165 nxt_int_t
nxt_var_register(nxt_var_decl_t * decl,size_t n)166 nxt_var_register(nxt_var_decl_t *decl, size_t n)
167 {
168     nxt_uint_t          i;
169     nxt_lvlhsh_query_t  lhq;
170 
171     lhq.replace = 0;
172     lhq.proto = &nxt_var_hash_proto;
173 
174     for (i = 0; i < n; i++) {
175         lhq.key = decl[i].name;
176         lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
177         lhq.value = &decl[i];
178 
179         if (nxt_slow_path(nxt_lvlhsh_insert(&nxt_var_hash, &lhq) != NXT_OK)) {
180             return NXT_ERROR;
181         }
182     }
183 
184     nxt_var_count += n;
185 
186     return NXT_OK;
187 }
188 
189 
190 nxt_int_t
nxt_var_index_init(void)191 nxt_var_index_init(void)
192 {
193     nxt_uint_t         i;
194     nxt_var_decl_t     *decl;
195     nxt_var_handler_t  *index;
196     nxt_lvlhsh_each_t  lhe;
197 
198     index = nxt_memalign(64, nxt_var_count * sizeof(nxt_var_handler_t));
199     if (index == NULL) {
200         return NXT_ERROR;
201     }
202 
203     nxt_lvlhsh_each_init(&lhe, &nxt_var_hash_proto);
204 
205     for (i = 0; i < nxt_var_count; i++) {
206         decl = nxt_lvlhsh_each(&nxt_var_hash, &lhe);
207         decl->index = i;
208         index[i] = decl->handler;
209     }
210 
211     nxt_var_index = index;
212 
213     return NXT_OK;
214 }
215 
216 
217 nxt_var_t *
nxt_var_compile(nxt_str_t * str,nxt_mp_t * mp,nxt_bool_t strz)218 nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_bool_t strz)
219 {
220     u_char          *p, *end, *next, *src;
221     size_t          size;
222     nxt_var_t       *var;
223     nxt_str_t       part;
224     nxt_uint_t      n;
225     nxt_bool_t      is_var;
226     nxt_var_sub_t   *subs;
227     nxt_var_decl_t  *decl;
228 
229     n = 0;
230 
231     p = str->start;
232     end = p + str->length;
233 
234     while (p < end) {
235         p = nxt_var_next_part(p, end - p, &part, &is_var);
236         if (nxt_slow_path(p == NULL)) {
237             return NULL;
238         }
239 
240         if (is_var) {
241             n++;
242         }
243     }
244 
245     size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length;
246 
247     var = nxt_mp_get(mp, size + strz);
248     if (nxt_slow_path(var == NULL)) {
249         return NULL;
250     }
251 
252     var->length = str->length;
253     var->vars = n;
254     var->strz = strz;
255 
256     subs = nxt_var_subs(var);
257     src = nxt_var_raw_start(var);
258 
259     nxt_memcpy(src, str->start, str->length);
260 
261     if (strz) {
262         src[str->length] = '\0';
263     }
264 
265     n = 0;
266     p = str->start;
267 
268     while (p < end) {
269         next = nxt_var_next_part(p, end - p, &part, &is_var);
270 
271         if (is_var) {
272             decl = nxt_var_hash_find(&part);
273             if (nxt_slow_path(decl == NULL)) {
274                 return NULL;
275             }
276 
277             subs[n].index = decl->index;
278             subs[n].length = next - p;
279             subs[n].position = p - str->start;
280 
281             n++;
282         }
283 
284         p = next;
285     }
286 
287     return var;
288 }
289 
290 
291 nxt_int_t
nxt_var_test(nxt_str_t * str,u_char * error)292 nxt_var_test(nxt_str_t *str, u_char *error)
293 {
294     u_char          *p, *end, *next;
295     nxt_str_t       part;
296     nxt_bool_t      is_var;
297     nxt_var_decl_t  *decl;
298 
299     p = str->start;
300     end = p + str->length;
301 
302     while (p < end) {
303         next = nxt_var_next_part(p, end - p, &part, &is_var);
304 
305         if (next == NULL) {
306             nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
307                         "Invalid variable at position %uz%Z", p - str->start);
308 
309             return NXT_ERROR;
310         }
311 
312         if (is_var) {
313             decl = nxt_var_hash_find(&part);
314 
315             if (decl == NULL) {
316                 nxt_sprintf(error, error + NXT_MAX_ERROR_STR,
317                             "Unknown variable \"%V\"%Z", &part);
318 
319                 return NXT_ERROR;
320             }
321         }
322 
323         p = next;
324     }
325 
326     return NXT_OK;
327 }
328 
329 
330 static u_char *
nxt_var_next_part(u_char * start,size_t length,nxt_str_t * part,nxt_bool_t * is_var)331 nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part,
332     nxt_bool_t *is_var)
333 {
334     u_char      *p, *end, ch, c;
335     nxt_bool_t  bracket;
336 
337     end = start + length;
338 
339     p = nxt_memchr(start, '$', length);
340 
341     if (p == start) {
342         *is_var = 1;
343 
344         p++;
345 
346         if (p == end) {
347             return NULL;
348         }
349 
350         if (*p == '{') {
351             bracket = 1;
352 
353             if (end - p < 2) {
354                 return NULL;
355             }
356 
357             p++;
358 
359         } else {
360             bracket = 0;
361         }
362 
363         start = p;
364 
365         for ( ;; ) {
366             ch = *p;
367 
368             c = (u_char) (ch | 0x20);
369             if ((c < 'a' || c > 'z') && ch != '_') {
370 
371                 if (bracket && ch != '}') {
372                     return NULL;
373                 }
374 
375                 break;
376             }
377 
378             p++;
379 
380             if (p == end) {
381                 if (bracket) {
382                     return NULL;
383                 }
384 
385                 break;
386             }
387         }
388 
389         length = p - start;
390         end = p + bracket;
391 
392     } else {
393         *is_var = 0;
394 
395         if (p != NULL) {
396             length = p - start;
397             end = p;
398         }
399     }
400 
401     part->length = length;
402     part->start = start;
403 
404     return end;
405 }
406 
407 
408 inline void
nxt_var_raw(nxt_var_t * var,nxt_str_t * str)409 nxt_var_raw(nxt_var_t *var, nxt_str_t *str)
410 {
411     str->length = var->length;
412     str->start = nxt_var_raw_start(var);
413 }
414 
415 
416 inline nxt_bool_t
nxt_var_is_const(nxt_var_t * var)417 nxt_var_is_const(nxt_var_t *var)
418 {
419     return (var->vars == 0);
420 }
421 
422 
423 nxt_int_t
nxt_var_query_init(nxt_var_query_t ** query_p,void * ctx,nxt_mp_t * mp)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
nxt_var_query(nxt_task_t * task,nxt_var_query_t * query,nxt_var_t * var,nxt_str_t * str)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, value, query->ctx);
498         if (nxt_slow_path(ret != NXT_OK)) {
499             goto fail;
500         }
501 
502         value = NULL;
503     }
504 
505     query->spare = value;
506 
507     val = nxt_array_add(&query->values);
508     if (nxt_slow_path(val == NULL)) {
509         goto fail;
510     }
511 
512     val->var = var;
513     val->value = str;
514 
515     return;
516 
517 fail:
518 
519     query->failed = 1;
520 }
521 
522 
523 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)524 nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data,
525     nxt_work_handler_t ready, nxt_work_handler_t error)
526 {
527     query->data = data;
528     query->ready = ready;
529     query->error = error;
530 
531     if (query->waiting == 0) {
532         nxt_var_query_finish(task, query);
533     }
534 }
535 
536 
537 void
nxt_var_query_handle(nxt_task_t * task,nxt_var_query_t * query,nxt_bool_t failed)538 nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query,
539     nxt_bool_t failed)
540 {
541     query->failed |= failed;
542 
543     if (--query->waiting == 0) {
544         nxt_var_query_finish(task, query);
545     }
546 }
547 
548 
549 static void
nxt_var_query_finish(nxt_task_t * task,nxt_var_query_t * query)550 nxt_var_query_finish(nxt_task_t *task, nxt_var_query_t *query)
551 {
552     u_char           *p, *src;
553     size_t           length, last, next;
554     nxt_str_t        *str, **part;
555     nxt_var_t        *var;
556     nxt_uint_t       i, j;
557     nxt_var_sub_t    *subs;
558     nxt_var_value_t  *val;
559 
560     if (query->failed) {
561         goto done;
562     }
563 
564     val = query->values.elts;
565 
566     for (i = 0; i < query->values.nelts; i++) {
567         var = val[i].var;
568 
569         subs = nxt_var_subs(var);
570         length = var->length;
571 
572         for (j = 0; j < var->vars; j++) {
573             str = nxt_var_cache_find(&query->cache, subs[j].index);
574 
575             nxt_assert(str != NULL);
576 
577             part = nxt_array_add(&query->parts);
578 
579             if (nxt_slow_path(part == NULL)) {
580                 query->failed = 1;
581                 goto done;
582             }
583 
584             *part = str;
585 
586             length += str->length - subs[j].length;
587         }
588 
589         p = nxt_mp_nget(query->values.mem_pool, length + var->strz);
590         if (nxt_slow_path(p == NULL)) {
591             query->failed = 1;
592             goto done;
593         }
594 
595         val[i].value->length = length;
596         val[i].value->start = p;
597 
598         part = query->parts.elts;
599         src = nxt_var_raw_start(var);
600 
601         last = 0;
602 
603         for (j = 0; j < var->vars; j++) {
604             next = subs[j].position;
605 
606             if (next != last) {
607                 p = nxt_cpymem(p, &src[last], next - last);
608             }
609 
610             p = nxt_cpymem(p, part[j]->start, part[j]->length);
611 
612             last = next + subs[j].length;
613         }
614 
615         if (last != var->length) {
616             p = nxt_cpymem(p, &src[last], var->length - last);
617         }
618 
619         if (var->strz) {
620             *p = '\0';
621         }
622 
623         nxt_array_reset(&query->parts);
624 
625         nxt_debug(task, "var: \"%*s\" -> \"%V\"", var->length, src,
626                   val[i].value);
627     }
628 
629 done:
630 
631     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
632                        query->failed ? query->error : query->ready,
633                        task, query->ctx, query->data);
634 }
635