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