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