Skip to content

Commit 7b9689f

Browse files
committed
TS-3143: Create new Regex class that uses PCRE JIT
1 parent 4ef8d39 commit 7b9689f

6 files changed

Lines changed: 198 additions & 36 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ lib/ts/test_atomic
6767
lib/ts/test_freelist
6868
lib/ts/test_Map
6969
lib/ts/test_Vec
70+
lib/ts/test_geometry
71+
lib/ts/test_Regex
7072
lib/perl/lib/Apache/TS.pm
7173

7274
iocore/net/test_certlookup

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
-*- coding: utf-8 -*-
22
Changes with Apache Traffic Server 5.2.0
33

4+
*) [TS-3143] Create new Regex class that uses PCRE JIT.
5+
46
*) [TS-3115] Add server response time logging fields.
57
Author: Acácio Centeno <[email protected]>
68

lib/ts/Makefile.am

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ library_includedir=$(includedir)/ts
2121
library_include_HEADERS = apidefs.h
2222

2323
noinst_PROGRAMS = mkdfa CompileParseRules
24-
check_PROGRAMS = test_atomic test_freelist test_arena test_List test_Map test_Vec test_geometry
24+
check_PROGRAMS = test_arena test_atomic test_freelist test_geometry test_List test_Map test_Regex test_Vec
2525
TESTS = $(check_PROGRAMS)
2626

2727
AM_CPPFLAGS = -I$(top_srcdir)/lib
@@ -215,6 +215,10 @@ test_Map_SOURCES = test_Map.cc
215215
test_Map_LDADD = libtsutil.la @LIBTCL@ @LIBPCRE@
216216
test_Map_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@
217217

218+
test_Regex_SOURCES = test_Regex.cc
219+
test_Regex_LDADD = libtsutil.la @LIBTCL@ @LIBPCRE@
220+
test_Regex_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@
221+
218222
test_Vec_SOURCES = test_Vec.cc
219223
test_Vec_LDADD = libtsutil.la @LIBTCL@ @LIBPCRE@
220224
test_Vec_LDFLAGS = @EXTRA_CXX_LDFLAGS@ @LIBTOOL_LINK_FLAGS@

lib/ts/Regex.cc

Lines changed: 103 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,106 @@
2424
#include "libts.h"
2525
#include "Regex.h"
2626

27+
#ifdef PCRE_CONFIG_JIT
28+
struct RegexThreadKey
29+
{
30+
RegexThreadKey() {
31+
pthread_key_create(&this->key, (void (*)(void *)) &pcre_jit_stack_free);
32+
}
33+
34+
pthread_key_t key;
35+
};
36+
37+
static RegexThreadKey k;
38+
39+
static pcre_jit_stack *
40+
get_jit_stack(void *data ATS_UNUSED)
41+
{
42+
pcre_jit_stack *jit_stack;
43+
44+
if ((jit_stack = (pcre_jit_stack *) pthread_getspecific(k.key)) == NULL) {
45+
jit_stack = pcre_jit_stack_alloc(ats_pagesize(), 1024 * 1024); // 1 page min and 1MB max
46+
pthread_setspecific(k.key, (void *)jit_stack);
47+
}
48+
49+
return jit_stack;
50+
}
51+
#endif
52+
53+
bool
54+
Regex::compile(const char *pattern, unsigned flags)
55+
{
56+
const char *error;
57+
int erroffset;
58+
int options = 0;
59+
int study_opts = 0;
60+
61+
if (regex)
62+
return false;
63+
64+
if (flags & RE_CASE_INSENSITIVE) {
65+
options |= PCRE_CASELESS;
66+
}
67+
68+
if (flags & RE_ANCHORED) {
69+
options |= PCRE_ANCHORED;
70+
}
71+
72+
regex = pcre_compile(pattern, options, &error, &erroffset, NULL);
73+
if (error) {
74+
regex = NULL;
75+
return false;
76+
}
77+
78+
#ifdef PCRE_CONFIG_JIT
79+
study_opts |= PCRE_STUDY_JIT_COMPILE;
80+
#endif
81+
82+
regex_extra = pcre_study(regex, study_opts, &error);
83+
84+
#ifdef PCRE_CONFIG_JIT
85+
if (regex_extra)
86+
pcre_assign_jit_stack(regex_extra, &get_jit_stack, NULL);
87+
#endif
88+
89+
return true;
90+
}
91+
92+
bool
93+
Regex::exec(const char *str)
94+
{
95+
return exec(str, strlen(str));
96+
}
97+
98+
bool
99+
Regex::exec(const char *str, int length)
100+
{
101+
int ovector[30], rv;
102+
103+
rv = pcre_exec(regex, regex_extra, str, length , 0, 0, ovector, countof(ovector));
104+
return rv > 0 ? true : false;
105+
}
106+
107+
Regex::~Regex()
108+
{
109+
if (regex_extra)
110+
#ifdef PCRE_CONFIG_JIT
111+
pcre_free_study(regex_extra);
112+
#else
113+
pcre_free(regex_extra);
114+
#endif
115+
if (regex)
116+
pcre_free(regex);
117+
}
118+
27119
DFA::~DFA()
28120
{
29121
dfa_pattern * p = _my_patterns;
30122
dfa_pattern * t;
31123

32124
while(p) {
33-
if (p->_pe)
34-
pcre_free(p->_pe);
35125
if (p->_re)
36-
pcre_free(p->_re);
126+
delete p->_re;
37127
if(p->_p)
38128
ats_free(p->_p);
39129
t = p->_next;
@@ -45,31 +135,20 @@ DFA::~DFA()
45135
dfa_pattern *
46136
DFA::build(const char *pattern, unsigned flags)
47137
{
48-
const char *error;
49-
int erroffset;
50138
dfa_pattern* ret;
51-
int options = PCRE_ANCHORED;
52-
53-
ret = (dfa_pattern*)ats_malloc(sizeof(dfa_pattern));
54-
ret->_p = NULL;
55-
56-
if (flags & RE_CASE_INSENSITIVE) {
57-
options |= PCRE_CASELESS;
58-
}
59-
60-
if (flags & RE_UNANCHORED) {
61-
options &= ~PCRE_ANCHORED;
62-
}
139+
int rv;
63140

64-
ret->_re = pcre_compile(pattern, options, &error, &erroffset, NULL);
65-
if (error) {
66-
ats_free(ret);
67-
return NULL;
141+
if (!(flags & RE_UNANCHORED)) {
142+
flags |= RE_ANCHORED;
68143
}
69144

70-
ret->_pe = pcre_study(ret->_re, 0, &error);
145+
ret = (dfa_pattern*)ats_malloc(sizeof(dfa_pattern));
146+
ret->_p = NULL;
71147

72-
if (error) {
148+
ret->_re = new Regex();
149+
rv = ret->_re->compile(pattern, flags);
150+
if (rv == -1) {
151+
delete ret->_re;
73152
ats_free(ret);
74153
return NULL;
75154
}
@@ -96,11 +175,9 @@ DFA::compile(const char **patterns, int npatterns, unsigned flags)
96175
dfa_pattern *ret = NULL;
97176
dfa_pattern *end = NULL;
98177
int i;
99-
//char buf[128];
100178

101179
for (i = 0; i < npatterns; i++) {
102180
pattern = patterns[i];
103-
//snprintf(buf,128,"%s",pattern);
104181
ret = build(pattern,flags);
105182
if (!ret) {
106183
continue;
@@ -135,12 +212,10 @@ int
135212
DFA::match(const char *str, int length) const
136213
{
137214
int rc;
138-
int ovector[30];
139-
//int wspace[20];
140215
dfa_pattern * p = _my_patterns;
141216

142217
while(p) {
143-
rc = pcre_exec(p->_re, p->_pe, str, length , 0, 0, ovector, 30/*,wspace,20*/);
218+
rc = p->_re->exec(str, length);
144219
if (rc > 0) {
145220
return p->_idx;
146221
}

lib/ts/Regex.h

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,32 @@
3232
#include <pcre.h>
3333
#endif
3434

35-
3635
enum REFlags
3736
{
3837
RE_CASE_INSENSITIVE = 0x0001, // default is case sensitive
39-
RE_UNANCHORED = 0x0002 // default is to anchor at the first matching position
38+
RE_UNANCHORED = 0x0002, // default (for DFA) is to anchor at the first matching position
39+
RE_ANCHORED = 0x0004, // default (for Regex) is unanchored
40+
};
41+
42+
class Regex
43+
{
44+
public:
45+
Regex():regex(NULL), regex_extra(NULL) {
46+
}
47+
bool compile(const char *pattern, unsigned flags = 0);
48+
// It is safe to call exec() concurrently on the same object instance
49+
bool exec(const char *str);
50+
bool exec(const char *str, int length);
51+
~Regex();
52+
53+
private:
54+
pcre *regex;
55+
pcre_extra *regex_extra;
4056
};
4157

4258
typedef struct __pat {
4359
int _idx;
44-
pcre *_re;
45-
pcre_extra *_pe;
60+
Regex *_re;
4661
char *_p;
4762
__pat * _next;
4863
} dfa_pattern;
@@ -52,12 +67,12 @@ class DFA
5267
public:
5368
DFA():_my_patterns(0) {
5469
}
55-
70+
5671
~DFA();
5772

5873
int compile(const char *pattern, unsigned flags = 0);
5974
int compile(const char **patterns, int npatterns, unsigned flags = 0);
60-
75+
6176
int match(const char *str) const;
6277
int match(const char *str, int length) const;
6378

@@ -67,5 +82,4 @@ class DFA
6782
dfa_pattern * _my_patterns;
6883
};
6984

70-
7185
#endif /* __TS_REGEX_H__ */

lib/ts/test_Regex.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
3+
@section license License
4+
5+
Licensed to the Apache Software Foundation (ASF) under one
6+
or more contributor license agreements. See the NOTICE file
7+
distributed with this work for additional information
8+
regarding copyright ownership. The ASF licenses this file
9+
to you under the Apache License, Version 2.0 (the
10+
"License"); you may not use this file except in compliance
11+
with the License. You may obtain a copy of the License at
12+
13+
http://www.apache.org/licenses/LICENSE-2.0
14+
15+
Unless required by applicable law or agreed to in writing, software
16+
distributed under the License is distributed on an "AS IS" BASIS,
17+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
See the License for the specific language governing permissions and
19+
limitations under the License.
20+
*/
21+
22+
#include <ink_assert.h>
23+
#include <ink_defs.h>
24+
#include "Regex.h"
25+
26+
typedef struct {
27+
char subject[100];
28+
bool match;
29+
} subject_match_t;
30+
31+
typedef struct {
32+
char regex[100];
33+
subject_match_t tests[4];
34+
} test_t;
35+
36+
static const test_t test_data[] = {
37+
{"^foo", {{"foo", true},
38+
{"bar", false},
39+
{"foobar", true},
40+
{"foobarbaz", true}}},
41+
{"foo$", {{"foo", true},
42+
{"bar", false},
43+
{"foobar", false},
44+
{"foobarbaz", false}}},
45+
};
46+
47+
static void test_basic()
48+
{
49+
for (unsigned int i = 0; i < countof(test_data); i++) {
50+
Regex r;
51+
52+
printf("Regex: %s\n", test_data[i].regex);
53+
r.compile(test_data[i].regex);
54+
for (unsigned int j = 0; j < countof(test_data[i].tests); j++) {
55+
printf("Subject: %s Result: %s\n", test_data[i].tests[j].subject, test_data[i].tests[j].match ? "true" : "false");
56+
ink_assert(r.exec(test_data[i].tests[j].subject) == test_data[i].tests[j].match);
57+
}
58+
}
59+
}
60+
61+
int main(int /* argc ATS_UNUSED */, char **/* argv ATS_UNUSED */)
62+
{
63+
test_basic();
64+
printf("test_Regex PASSED\n");
65+
}

0 commit comments

Comments
 (0)