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