comparison src/http/modules/ngx_http_sub_filter_module.c @ 6228:b9447fc457b4

Sub filter: support of multiple strings to replace.
author Dmitry Volyntsev <xeioex@nginx.com>
date Mon, 17 Aug 2015 17:42:02 +0300
parents 5322be87fc02
children 2c045e5b8291
comparison
equal deleted inserted replaced
6227:bd55d75a1410 6228:b9447fc457b4
11 11
12 12
13 typedef struct { 13 typedef struct {
14 ngx_str_t match; 14 ngx_str_t match;
15 ngx_http_complex_value_t value; 15 ngx_http_complex_value_t value;
16 } ngx_http_sub_match_t;
17
18
19 typedef struct {
20 ngx_uint_t min_match_len;
21 ngx_uint_t max_match_len;
22
23 u_char index[257];
24 u_char shift[256];
25 } ngx_http_sub_tables_t;
26
27
28 typedef struct {
29 ngx_http_sub_tables_t *tables;
16 30
17 ngx_hash_t types; 31 ngx_hash_t types;
18 32
19 ngx_flag_t once; 33 ngx_flag_t once;
20 ngx_flag_t last_modified; 34 ngx_flag_t last_modified;
21 35
22 ngx_array_t *types_keys; 36 ngx_array_t *types_keys;
37 ngx_array_t *matches;
23 } ngx_http_sub_loc_conf_t; 38 } ngx_http_sub_loc_conf_t;
24 39
25 40
26 typedef enum {
27 sub_start_state = 0,
28 sub_match_state,
29 } ngx_http_sub_state_e;
30
31
32 typedef struct { 41 typedef struct {
33 ngx_str_t match;
34 ngx_str_t saved; 42 ngx_str_t saved;
35 ngx_str_t looked; 43 ngx_str_t looked;
36 44
37 ngx_uint_t once; /* unsigned once:1 */ 45 ngx_uint_t once; /* unsigned once:1 */
38 46
46 ngx_chain_t *out; 54 ngx_chain_t *out;
47 ngx_chain_t **last_out; 55 ngx_chain_t **last_out;
48 ngx_chain_t *busy; 56 ngx_chain_t *busy;
49 ngx_chain_t *free; 57 ngx_chain_t *free;
50 58
51 ngx_str_t sub; 59 ngx_str_t *sub;
52 60 ngx_uint_t applied;
53 ngx_uint_t state; 61
62 ngx_int_t offset;
63 ngx_uint_t index;
54 } ngx_http_sub_ctx_t; 64 } ngx_http_sub_ctx_t;
65
66
67 static ngx_uint_t ngx_http_sub_cmp_index;
55 68
56 69
57 static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, 70 static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,
58 ngx_http_sub_ctx_t *ctx); 71 ngx_http_sub_ctx_t *ctx);
59 static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r, 72 static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,
62 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, 75 static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
63 void *conf); 76 void *conf);
64 static void *ngx_http_sub_create_conf(ngx_conf_t *cf); 77 static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
65 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf, 78 static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
66 void *parent, void *child); 79 void *parent, void *child);
80 static void ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,
81 ngx_http_sub_match_t *match, ngx_uint_t n);
82 static ngx_int_t ngx_http_sub_cmp_matches(const void *one, const void *two);
67 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf); 83 static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);
68 84
69 85
70 static ngx_command_t ngx_http_sub_filter_commands[] = { 86 static ngx_command_t ngx_http_sub_filter_commands[] = {
71 87
142 ngx_http_sub_ctx_t *ctx; 158 ngx_http_sub_ctx_t *ctx;
143 ngx_http_sub_loc_conf_t *slcf; 159 ngx_http_sub_loc_conf_t *slcf;
144 160
145 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); 161 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
146 162
147 if (slcf->match.len == 0 163 if (slcf->matches == NULL
148 || r->headers_out.content_length_n == 0 164 || r->headers_out.content_length_n == 0
149 || ngx_http_test_content_type(r, &slcf->types) == NULL) 165 || ngx_http_test_content_type(r, &slcf->types) == NULL)
150 { 166 {
151 return ngx_http_next_header_filter(r); 167 return ngx_http_next_header_filter(r);
152 } 168 }
154 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); 170 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));
155 if (ctx == NULL) { 171 if (ctx == NULL) {
156 return NGX_ERROR; 172 return NGX_ERROR;
157 } 173 }
158 174
159 ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len); 175 ctx->saved.data = ngx_pnalloc(r->pool, slcf->tables->max_match_len - 1);
160 if (ctx->saved.data == NULL) { 176 if (ctx->saved.data == NULL) {
161 return NGX_ERROR; 177 return NGX_ERROR;
162 } 178 }
163 179
164 ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len); 180 ctx->looked.data = ngx_pnalloc(r->pool, slcf->tables->max_match_len - 1);
165 if (ctx->looked.data == NULL) { 181 if (ctx->looked.data == NULL) {
166 return NGX_ERROR; 182 return NGX_ERROR;
167 } 183 }
168 184
169 ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module); 185 ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);
170 186
171 ctx->match = slcf->match; 187 ctx->offset = slcf->tables->min_match_len - 1;
172 ctx->last_out = &ctx->out; 188 ctx->last_out = &ctx->out;
173 189
174 r->filter_need_in_memory = 1; 190 r->filter_need_in_memory = 1;
175 191
176 if (r == r->main) { 192 if (r == r->main) {
192 static ngx_int_t 208 static ngx_int_t
193 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 209 ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
194 { 210 {
195 ngx_int_t rc; 211 ngx_int_t rc;
196 ngx_buf_t *b; 212 ngx_buf_t *b;
213 ngx_str_t *sub;
197 ngx_chain_t *cl; 214 ngx_chain_t *cl;
198 ngx_http_sub_ctx_t *ctx; 215 ngx_http_sub_ctx_t *ctx;
216 ngx_http_sub_match_t *match;
199 ngx_http_sub_loc_conf_t *slcf; 217 ngx_http_sub_loc_conf_t *slcf;
200 218
201 ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module); 219 ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);
202 220
203 if (ctx == NULL) { 221 if (ctx == NULL) {
240 ctx->buf = ctx->in->buf; 258 ctx->buf = ctx->in->buf;
241 ctx->in = ctx->in->next; 259 ctx->in = ctx->in->next;
242 ctx->pos = ctx->buf->pos; 260 ctx->pos = ctx->buf->pos;
243 } 261 }
244 262
245 if (ctx->state == sub_start_state) {
246 ctx->copy_start = ctx->pos;
247 ctx->copy_end = ctx->pos;
248 }
249
250 b = NULL; 263 b = NULL;
251 264
252 while (ctx->pos < ctx->buf->last) { 265 while (ctx->pos < ctx->buf->last) {
253
254 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
255 "saved: \"%V\" state: %d", &ctx->saved, ctx->state);
256 266
257 rc = ngx_http_sub_parse(r, ctx); 267 rc = ngx_http_sub_parse(r, ctx);
258 268
259 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 269 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
260 "parse: %d, looked: \"%V\" %p-%p", 270 "parse: %d, looked: \"%V\" %p-%p",
318 328
319 *ctx->last_out = cl; 329 *ctx->last_out = cl;
320 ctx->last_out = &cl->next; 330 ctx->last_out = &cl->next;
321 } 331 }
322 332
323 if (ctx->state == sub_start_state) {
324 ctx->copy_start = ctx->pos;
325 ctx->copy_end = ctx->pos;
326
327 } else {
328 ctx->copy_start = NULL;
329 ctx->copy_end = NULL;
330 }
331
332 if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) {
333 ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos);
334 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
335 }
336
337 if (rc == NGX_AGAIN) { 333 if (rc == NGX_AGAIN) {
338 continue; 334 continue;
339 } 335 }
340 336
341 337
350 346
351 ngx_memzero(b, sizeof(ngx_buf_t)); 347 ngx_memzero(b, sizeof(ngx_buf_t));
352 348
353 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); 349 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
354 350
355 if (ctx->sub.data == NULL) { 351 if (ctx->sub == NULL) {
356 352 ctx->sub = ngx_pcalloc(r->pool, sizeof(ngx_str_t)
357 if (ngx_http_complex_value(r, &slcf->value, &ctx->sub) 353 * slcf->matches->nelts);
354 if (ctx->sub == NULL) {
355 return NGX_ERROR;
356 }
357 }
358
359 sub = &ctx->sub[ctx->index];
360
361 if (sub->data == NULL) {
362 match = slcf->matches->elts;
363
364 if (ngx_http_complex_value(r, &match[ctx->index].value, sub)
358 != NGX_OK) 365 != NGX_OK)
359 { 366 {
360 return NGX_ERROR; 367 return NGX_ERROR;
361 } 368 }
362 } 369 }
363 370
364 if (ctx->sub.len) { 371 if (sub->len) {
365 b->memory = 1; 372 b->memory = 1;
366 b->pos = ctx->sub.data; 373 b->pos = sub->data;
367 b->last = ctx->sub.data + ctx->sub.len; 374 b->last = sub->data + sub->len;
368 375
369 } else { 376 } else {
370 b->sync = 1; 377 b->sync = 1;
371 } 378 }
372 379
373 *ctx->last_out = cl; 380 *ctx->last_out = cl;
374 ctx->last_out = &cl->next; 381 ctx->last_out = &cl->next;
375 382
376 ctx->once = slcf->once; 383 ctx->index = 0;
384 ctx->once = slcf->once && (++ctx->applied == slcf->matches->nelts);
377 385
378 continue; 386 continue;
379 } 387 }
380 388
381 if (ctx->looked.len 389 if (ctx->looked.len
426 434
427 b->recycled = ctx->buf->recycled; 435 b->recycled = ctx->buf->recycled;
428 } 436 }
429 437
430 ctx->buf = NULL; 438 ctx->buf = NULL;
431
432 ctx->saved.len = ctx->looked.len;
433 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);
434 } 439 }
435 440
436 if (ctx->out == NULL && ctx->busy == NULL) { 441 if (ctx->out == NULL && ctx->busy == NULL) {
437 return NGX_OK; 442 return NGX_OK;
438 } 443 }
511 516
512 517
513 static ngx_int_t 518 static ngx_int_t
514 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx) 519 ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
515 { 520 {
516 u_char *p, *last, *copy_end, ch, match; 521 u_char *p, *last, *pat, *pat_end, c;
517 size_t looked, i; 522 ngx_str_t *m;
518 ngx_http_sub_state_e state; 523 ngx_int_t offset, start, next, end, len, rc;
524 ngx_uint_t shift, i, j;
525 ngx_http_sub_match_t *match;
526 ngx_http_sub_tables_t *tables;
527 ngx_http_sub_loc_conf_t *slcf;
528
529 slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
530 tables = slcf->tables;
531
532 offset = ctx->offset;
533 end = ctx->buf->last - ctx->pos;
519 534
520 if (ctx->once) { 535 if (ctx->once) {
521 ctx->copy_start = ctx->pos; 536 /* sets start and next to end */
522 ctx->copy_end = ctx->buf->last; 537 offset = end + (ngx_int_t) tables->min_match_len - 1;
523 ctx->pos = ctx->buf->last; 538 goto again;
524 ctx->looked.len = 0; 539 }
525 540
526 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once"); 541 while (offset < end) {
527 542
528 return NGX_AGAIN; 543 c = offset < 0 ? ctx->looked.data[ctx->looked.len + offset]
529 } 544 : ctx->pos[offset];
530 545
531 state = ctx->state; 546 c = ngx_tolower(c);
532 looked = ctx->looked.len; 547
533 last = ctx->buf->last; 548 shift = tables->shift[c];
534 copy_end = ctx->copy_end; 549 if (shift > 0) {
535 550 offset += shift;
536 for (p = ctx->pos; p < last; p++) { 551 continue;
537 552 }
538 ch = *p; 553
539 ch = ngx_tolower(ch); 554 /* a potential match */
540 555
541 if (state == sub_start_state) { 556 start = offset - (ngx_int_t) tables->min_match_len + 1;
542 557 match = slcf->matches->elts;
543 /* the tight loop */ 558
544 559 i = ngx_max(tables->index[c], ctx->index);
545 match = ctx->match.data[0]; 560 j = tables->index[c + 1];
546 561
547 for ( ;; ) { 562 while (i != j) {
548 if (ch == match) { 563
549 564 if (slcf->once && ctx->sub && ctx->sub[i].data) {
550 if (ctx->match.len == 1) { 565 goto next;
551 ctx->pos = p + 1; 566 }
552 ctx->copy_end = p; 567
553 568 m = &match[i].match;
554 return NGX_OK; 569
570 pat = m->data;
571 pat_end = m->data + m->len;
572
573 if (start >= 0) {
574 p = ctx->pos + start;
575
576 } else {
577 last = ctx->looked.data + ctx->looked.len;
578 p = last + start;
579
580 while (p < last && pat < pat_end) {
581 if (ngx_tolower(*p) != *pat) {
582 goto next;
555 } 583 }
556 584
557 copy_end = p; 585 p++;
558 ctx->looked.data[0] = *p; 586 pat++;
559 looked = 1;
560 state = sub_match_state;
561
562 goto match_started;
563 } 587 }
564 588
565 if (++p == last) { 589 p = ctx->pos;
566 break; 590 }
591
592 while (p < ctx->buf->last && pat < pat_end) {
593 if (ngx_tolower(*p) != *pat) {
594 goto next;
567 } 595 }
568 596
569 ch = *p;
570 ch = ngx_tolower(ch);
571 }
572
573 ctx->state = state;
574 ctx->pos = p;
575 ctx->looked.len = looked;
576 ctx->copy_end = p;
577
578 if (ctx->copy_start == NULL) {
579 ctx->copy_start = ctx->buf->pos;
580 }
581
582 return NGX_AGAIN;
583
584 match_started:
585
586 continue;
587 }
588
589 /* state == sub_match_state */
590
591 if (ch == ctx->match.data[looked]) {
592 ctx->looked.data[looked] = *p;
593 looked++;
594
595 if (looked == ctx->match.len) {
596
597 ctx->state = sub_start_state;
598 ctx->pos = p + 1;
599 ctx->looked.len = 0;
600 ctx->saved.len = 0;
601 ctx->copy_end = copy_end;
602
603 if (ctx->copy_start == NULL && copy_end) {
604 ctx->copy_start = ctx->buf->pos;
605 }
606
607 return NGX_OK;
608 }
609
610 } else {
611 /*
612 * check if there is another partial match in previously
613 * matched substring to catch cases like "aab" in "aaab"
614 */
615
616 ctx->looked.data[looked] = *p;
617 looked++;
618
619 for (i = 1; i < looked; i++) {
620 if (ngx_strncasecmp(ctx->looked.data + i,
621 ctx->match.data, looked - i)
622 == 0)
623 {
624 break;
625 }
626 }
627
628 if (i < looked) {
629 if (ctx->saved.len > i) {
630 ctx->saved.len = i;
631 }
632
633 if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) {
634 copy_end = p + 1 - (looked - i);
635 }
636
637 ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i);
638 looked = looked - i;
639
640 } else {
641 copy_end = p;
642 looked = 0;
643 state = sub_start_state;
644 }
645
646 if (ctx->saved.len) {
647 p++; 597 p++;
648 goto out; 598 pat++;
649 } 599 }
650 } 600
651 } 601 ctx->index = i;
652 602
653 ctx->saved.len = 0; 603 if (pat != pat_end) {
654 604 /* partial match */
655 out: 605 goto again;
656 606 }
657 ctx->state = state; 607
658 ctx->pos = p; 608 ctx->offset = offset + (ngx_int_t) m->len;
659 ctx->looked.len = looked; 609 next = start + (ngx_int_t) m->len;
660 610 end = ngx_max(next, 0);
661 ctx->copy_end = (state == sub_start_state) ? p : copy_end; 611 rc = NGX_OK;
662 612
663 if (ctx->copy_start == NULL && ctx->copy_end) { 613 goto done;
664 ctx->copy_start = ctx->buf->pos; 614
665 } 615 next:
666 616
667 return NGX_AGAIN; 617 i++;
618 }
619
620 offset++;
621 ctx->index = 0;
622 }
623
624 again:
625
626 ctx->offset = offset;
627 start = offset - (ngx_int_t) tables->min_match_len + 1;
628 next = start;
629 rc = NGX_AGAIN;
630
631 done:
632
633 /* send [ - looked.len, start ] to client */
634
635 ctx->saved.len = ctx->looked.len + ngx_min(start, 0);
636 ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
637
638 ctx->copy_start = ctx->pos;
639 ctx->copy_end = ctx->pos + ngx_max(start, 0);
640
641 /* save [ next, end ] in looked */
642
643 len = ngx_min(next, 0);
644 p = ctx->looked.data;
645 p = ngx_movemem(p, p + ctx->looked.len + len, - len);
646
647 len = ngx_max(next, 0);
648 p = ngx_cpymem(p, ctx->pos + len, end - len);
649 ctx->looked.len = p - ctx->looked.data;
650
651 /* update position */
652
653 ctx->pos += end;
654 ctx->offset -= end;
655
656 return rc;
668 } 657 }
669 658
670 659
671 static char * 660 static char *
672 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 661 ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
673 { 662 {
674 ngx_http_sub_loc_conf_t *slcf = conf; 663 ngx_http_sub_loc_conf_t *slcf = conf;
675 664
676 ngx_str_t *value; 665 ngx_str_t *value;
666 ngx_http_sub_match_t *match;
677 ngx_http_compile_complex_value_t ccv; 667 ngx_http_compile_complex_value_t ccv;
678 668
679 if (slcf->match.data) {
680 return "is duplicate";
681 }
682
683 value = cf->args->elts; 669 value = cf->args->elts;
684 670
671 if (value[1].len == 0) {
672 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty search pattern");
673 return NGX_CONF_ERROR;
674 }
675
676 if (slcf->matches == NULL) {
677 slcf->matches = ngx_array_create(cf->pool, 1,
678 sizeof(ngx_http_sub_match_t));
679 if (slcf->matches == NULL) {
680 return NGX_CONF_ERROR;
681 }
682 }
683
684 if (slcf->matches->nelts == 255) {
685 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
686 "number of search patterns exceeds 255");
687 return NGX_CONF_ERROR;
688 }
689
685 ngx_strlow(value[1].data, value[1].data, value[1].len); 690 ngx_strlow(value[1].data, value[1].data, value[1].len);
686 691
687 slcf->match = value[1]; 692 match = ngx_array_push(slcf->matches);
693 if (match == NULL) {
694 return NGX_CONF_ERROR;
695 }
696
697 match->match = value[1];
688 698
689 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 699 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
690 700
691 ccv.cf = cf; 701 ccv.cf = cf;
692 ccv.value = &value[2]; 702 ccv.value = &value[2];
693 ccv.complex_value = &slcf->value; 703 ccv.complex_value = &match->value;
694 704
695 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 705 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
696 return NGX_CONF_ERROR; 706 return NGX_CONF_ERROR;
697 } 707 }
698 708
711 } 721 }
712 722
713 /* 723 /*
714 * set by ngx_pcalloc(): 724 * set by ngx_pcalloc():
715 * 725 *
716 * conf->match = { 0, NULL }; 726 * conf->tables = NULL;
717 * conf->types = { NULL }; 727 * conf->types = { NULL };
718 * conf->types_keys = NULL; 728 * conf->types_keys = NULL;
729 * conf->matches = NULL;
719 */ 730 */
720 731
721 slcf->once = NGX_CONF_UNSET; 732 slcf->once = NGX_CONF_UNSET;
722 slcf->last_modified = NGX_CONF_UNSET; 733 slcf->last_modified = NGX_CONF_UNSET;
723 734
730 { 741 {
731 ngx_http_sub_loc_conf_t *prev = parent; 742 ngx_http_sub_loc_conf_t *prev = parent;
732 ngx_http_sub_loc_conf_t *conf = child; 743 ngx_http_sub_loc_conf_t *conf = child;
733 744
734 ngx_conf_merge_value(conf->once, prev->once, 1); 745 ngx_conf_merge_value(conf->once, prev->once, 1);
735 ngx_conf_merge_str_value(conf->match, prev->match, "");
736 ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0); 746 ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
737
738 if (conf->value.value.data == NULL) {
739 conf->value = prev->value;
740 }
741 747
742 if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, 748 if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
743 &prev->types_keys, &prev->types, 749 &prev->types_keys, &prev->types,
744 ngx_http_html_default_types) 750 ngx_http_html_default_types)
745 != NGX_OK) 751 != NGX_OK)
746 { 752 {
747 return NGX_CONF_ERROR; 753 return NGX_CONF_ERROR;
748 } 754 }
749 755
756 if (conf->matches == NULL) {
757 conf->matches = prev->matches;
758 conf->tables = prev->tables;
759
760 } else {
761 conf->tables = ngx_palloc(cf->pool, sizeof(ngx_http_sub_tables_t));
762 if (conf->tables == NULL) {
763 return NGX_CONF_ERROR;
764 }
765
766 ngx_http_sub_init_tables(conf->tables, conf->matches->elts,
767 conf->matches->nelts);
768 }
769
750 return NGX_CONF_OK; 770 return NGX_CONF_OK;
771 }
772
773
774 static void
775 ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,
776 ngx_http_sub_match_t *match, ngx_uint_t n)
777 {
778 u_char c;
779 ngx_uint_t i, j, min, max, ch;
780
781 min = match[0].match.len;
782 max = match[0].match.len;
783
784 for (i = 1; i < n; i++) {
785 min = ngx_min(min, match[i].match.len);
786 max = ngx_max(max, match[i].match.len);
787 }
788
789 tables->min_match_len = min;
790 tables->max_match_len = max;
791
792 ngx_http_sub_cmp_index = tables->min_match_len - 1;
793 ngx_sort(match, n, sizeof(ngx_http_sub_match_t), ngx_http_sub_cmp_matches);
794
795 min = ngx_min(min, 255);
796 ngx_memset(tables->shift, min, 256);
797
798 ch = 0;
799
800 for (i = 0; i < n; i++) {
801
802 for (j = 0; j < min; j++) {
803 c = match[i].match.data[tables->min_match_len - 1 - j];
804 tables->shift[c] = ngx_min(tables->shift[c], (u_char) j);
805 }
806
807 c = match[i].match.data[tables->min_match_len - 1];
808 while (ch <= c) {
809 tables->index[ch++] = (u_char) i;
810 }
811 }
812
813 while (ch < 257) {
814 tables->index[ch++] = (u_char) n;
815 }
816 }
817
818
819 static ngx_int_t
820 ngx_http_sub_cmp_matches(const void *one, const void *two)
821 {
822 ngx_int_t c1, c2;
823 ngx_http_sub_match_t *first, *second;
824
825 first = (ngx_http_sub_match_t *) one;
826 second = (ngx_http_sub_match_t *) two;
827
828 c1 = first->match.data[ngx_http_sub_cmp_index];
829 c2 = second->match.data[ngx_http_sub_cmp_index];
830
831 return c1 - c2;
751 } 832 }
752 833
753 834
754 static ngx_int_t 835 static ngx_int_t
755 ngx_http_sub_filter_init(ngx_conf_t *cf) 836 ngx_http_sub_filter_init(ngx_conf_t *cf)