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