File Coverage

secret_buffer_parse_match_str.c
Criterion Covered Total %
statement 139 147 94.5
branch n/a
condition n/a
subroutine n/a
pod n/a
total 139 147 94.5


line stmt bran cond sub pod time code
1             /* included twice for different pattern element types */
2              
3             /* This scans through a parse span codepoint by codepoint looking for the sequence
4             * passed in the 'pattern' array.
5             * - If flag 'anchored' is set, it stops the loop if the match doesn't happen
6             * on the first iteration.
7             * - If flag 'reverse' is set, it iterates backward over the span's codepoints
8             * comparing to pattern in reverse.
9             * - If flag 'multi' is set, it looks for a contiguous span of matches
10             * (mostly useful when the pattern is a single character)
11             * - If flag 'negate' is set, it negates the check of whether the pattern
12             * matched. For 'multi', this means it returns the span *until* the first
13             * match of the pattern begins.
14             */
15 433           bool SB_PARSE_MATCH_STR_FN(secret_buffer_parse *parse, SB_PATTERN_EL_TYPE *pattern, size_t pattern_len, int flags) {
16 433           bool reverse= 0 != (flags & SECRET_BUFFER_MATCH_REVERSE);
17 433           bool multi= 0 != (flags & SECRET_BUFFER_MATCH_MULTI);
18 433           bool anchored= 0 != (flags & SECRET_BUFFER_MATCH_ANCHORED);
19 433           bool negate= 0 != (flags & SECRET_BUFFER_MATCH_NEGATE);
20 433           bool consttime=0 != (flags & SECRET_BUFFER_MATCH_CONST_TIME);
21 433           bool anchor_fail= false;
22 433           bool encoding_error= false;
23 433           U8 *ret_pos= NULL, *ret_lim= NULL,
24 433           ret_pos_bit= 0, ret_lim_bit= 0;
25             /* U8 *orig= parse->pos; */
26              
27 433           if (!pattern_len) {
28 8           if (reverse)
29 5           parse->pos= parse->lim;
30             else
31 3           parse->lim= parse->pos;
32 8           return true;
33             }
34              
35 425           if (!reverse) {
36             /* When operating in consttime mode, some matches are "fake" and should be ignored until
37             * we reach "real_search_pos". Currently the pointer is enough, no need for the pos_bit. */
38 312           U8 *real_search_pos= parse->pos;
39 312           int first_cp= *pattern;
40 932           while (parse->pos < parse->lim) {
41             /* search_pos keeps track of where this iteration started, and next_search_pos
42             * may get set during the match to indicate we need to backtrack the parse->pos */
43 923           U8 *search_pos= parse->pos, search_pos_bit= parse->pos_bit;
44             U8 *next_char_pos, next_char_pos_bit;
45             bool matched;
46 923           int cp= sb_parse_next_codepoint(parse);
47             #define SB_HANDLE_ENCODING_ERROR { \
48             if (!encoding_error) { \
49             encoding_error= true; \
50             /* record the location of the encoding error as if we stopped there */ \
51             ret_pos= parse->pos; \
52             ret_pos_bit= parse->pos_bit; \
53             ret_lim= parse->lim; \
54             ret_lim_bit= parse->lim_bit; \
55             if (!consttime) /* consttime keeps going */ \
56             break; \
57             } \
58             ++parse->pos; /* ensure forward progress for consttime */ \
59             }
60 923           if (cp < 0) SB_HANDLE_ENCODING_ERROR
61 923           next_char_pos= parse->pos;
62 923           next_char_pos_bit= parse->pos_bit;
63 923           matched= cp == first_cp;
64 923           if ((matched || consttime) && pattern_len > 1) {
65 265           SB_PATTERN_EL_TYPE *pat_pos= pattern+1, *pat_lim= pattern + pattern_len;
66 265           U8 *next_potential_pos= NULL, next_potential_pos_bit= 0;
67 1161           while (parse->pos < parse->lim && pat_pos < pat_lim) {
68 1006           U8 *at_pos= parse->pos, at_pos_bit= parse->pos_bit;
69 1006           cp= sb_parse_next_codepoint(parse);
70 1006           if (cp < 0) SB_HANDLE_ENCODING_ERROR
71             /* speed up outer loop by checking for whether this character could be the start
72             of the next match */
73 1006           if (cp == first_cp && !next_potential_pos) {
74 133           next_potential_pos= at_pos;
75 133           next_potential_pos_bit= at_pos_bit;
76             }
77 1006           if (cp != *pat_pos) {
78 110           matched= false;
79 110           if (!consttime)
80 110           break;
81             }
82 896           ++pat_pos;
83             }
84 265           if (pat_pos < pat_lim)
85 118           matched= false;
86             /* If not a match, backtrack to wherever the next match could start. */
87 265           if (!matched && next_potential_pos) {
88 99           parse->pos= next_potential_pos;
89 99           parse->pos_bit= next_potential_pos_bit;
90             }
91             }
92             #if 0
93             if (consttime) {
94             warn(" @%2d matched=%d parse->pos=%d real_search_pos=%d ret_pos=%d ret_lim=%d anchor_fail=%d",
95             (int)(search_pos-orig), (int)matched, (int)(parse->pos-orig), (int)(real_search_pos-orig),
96             (int)(ret_pos? ret_pos-orig : -1), (int)(ret_lim? ret_lim-orig : -1), (int)anchor_fail);
97             }
98             #endif
99             #undef SB_HANDLE_ENCODING_ERROR
100             /* Code below does not set return values unless `search_pos >= real_search_pos`
101             * so that the consttime busywork iterations don't change any return-value state.
102             */
103             /* Found the goal? (match, or negated match) */
104 923           if (matched != negate) {
105             /* The desired (multi?)match begins here, unless it already began */
106 454           if (!ret_pos && search_pos >= real_search_pos) {
107 249           ret_pos= search_pos;
108 249           ret_pos_bit= search_pos_bit;
109             }
110             /* It also ends here if multi is false, unless already set */
111 454           if (!multi && !ret_lim && search_pos >= real_search_pos) {
112             /* negative matches end at the character following search_pos */
113 201           if (negate) {
114 10           ret_lim= next_char_pos;
115 10           ret_lim_bit= next_char_pos_bit;
116             } else {
117 191           ret_lim= parse->pos;
118 191           ret_lim_bit= parse->pos_bit;
119             }
120 201           if (!consttime || anchored)
121             break;
122             }
123             }
124             /* If not a match (or matches but negated) and the multi-match was started,
125             * search_pos is the end of the multi-match. */
126 469           else if (multi && ret_pos && !ret_lim) {
127 44           if (search_pos >= real_search_pos) {
128 44           ret_lim= search_pos;
129 44           ret_lim_bit= search_pos_bit;
130 44           if (!consttime)
131 44           break;
132             }
133             }
134 425           else if (anchored) { /* not our goal, anchored, and not multi-match */
135 58           if (!ret_pos) {
136 58           anchor_fail= true;
137             /* return essentially the original value of 'parse' */
138 58           ret_pos= search_pos;
139 58           ret_pos_bit= search_pos_bit;
140 58           ret_lim= parse->lim;
141 58           ret_lim_bit= parse->lim_bit;
142             }
143             /* only need to waste time if consttime with multiple matches */
144 58           if (!(consttime && multi))
145             break;
146             }
147             /* constant time iteration always resumes at the following character */
148 620           if (consttime) {
149 0           if (search_pos >= real_search_pos)
150 0           real_search_pos= parse->pos;
151 0           parse->pos= next_char_pos;
152 0           parse->pos_bit= next_char_pos_bit;
153             }
154             }
155             /* If loop exited due to end of input and multi-match was in progress, mark the end */
156 312           if (multi && ret_pos && !ret_lim) {
157 4           ret_lim= parse->lim;
158 4           ret_lim_bit= parse->lim_bit;
159             }
160             }
161             /* Else do the above in reverse from end of parse span */
162             else {
163             /* When operating in consttime mode, some matches are "fake" and should be ignored until
164             * we reach "real_search_pos". */
165 113           U8 *real_search_lim= parse->lim;
166 113           int last_cp= pattern[pattern_len-1];
167 320           while (parse->pos < parse->lim) {
168 317           U8 *search_lim= parse->lim, search_lim_bit= parse->lim_bit;
169             U8 *next_char_lim, next_char_lim_bit;
170             bool matched;
171 317           int cp= sb_parse_prev_codepoint(parse);
172             #define SB_HANDLE_ENCODING_ERROR { \
173             if (!encoding_error) { \
174             encoding_error= true; \
175             /* record the location of the encoding error as if we stopped there */ \
176             ret_pos= parse->pos; \
177             ret_pos_bit= parse->pos_bit; \
178             ret_lim= parse->lim; \
179             ret_lim_bit= parse->lim_bit; \
180             if (!consttime) /* consttime keeps going */ \
181             break; \
182             } \
183             --parse->lim; /* ensure progress for consttime */ \
184             }
185 317           if (cp < 0) SB_HANDLE_ENCODING_ERROR
186 317           next_char_lim= parse->lim;
187 317           next_char_lim_bit= parse->lim_bit;
188 317           matched= cp == last_cp;
189 317           if ((matched || consttime) && pattern_len > 1) {
190 209           SB_PATTERN_EL_TYPE *pat_lim= pattern + pattern_len - 1; /* final char already matched */
191 209           U8 *next_potential_lim= NULL, next_potential_lim_bit= 0;
192 628           while (parse->pos < parse->lim && pattern < pat_lim) {
193 514           U8 *at_lim= parse->lim, at_lim_bit= parse->lim_bit;
194 514           cp= sb_parse_prev_codepoint(parse);
195 514           if (cp < 0) SB_HANDLE_ENCODING_ERROR
196             /* if const time is not requested, speed up outer loop by checking for whether
197             * this character could be the start of the next match */
198 514           if (cp == last_cp && !next_potential_lim) {
199 107           next_potential_lim= at_lim;
200 107           next_potential_lim_bit= at_lim_bit;
201             }
202 514           if (cp != pat_lim[-1]) {
203 95           matched= false;
204 95           if (!consttime)
205 95           break;
206             }
207 419           --pat_lim;
208             }
209 209           if (pattern < pat_lim)
210 103           matched= false;
211             /* If not a match, backtrack to wherever the next match could start. */
212 209           if (!matched && next_potential_lim) {
213 96           parse->lim= next_potential_lim;
214 96           parse->lim_bit= next_potential_lim_bit;
215             }
216             }
217             #if 0
218             if (!anchored && !multi) {
219             warn(" @%2d matched=%d parse->lim=%d real_search_lim=%d ret_pos=%d ret_lim=%d anchor_fail=%d",
220             (int)(search_lim-orig-1), (int)matched, (int)(parse->lim-orig), (int)(real_search_lim-orig),
221             (int)(ret_pos? ret_pos-orig : -1), (int)(ret_lim? ret_lim-orig : -1), (int)anchor_fail);
222             }
223             #endif
224             #undef SB_HANDLE_ENCODING_ERROR
225             /* Code below does not set return values unless `search_pos >= real_search_pos`
226             * so that the consttime busywork iterations don't change any return-value state.
227             */
228             /* Found the goal? (match, or negated match) */
229 317           if (matched != negate) {
230             /* The desired (multi?)match begins here, unless it already began */
231 152           if (!ret_lim && search_lim <= real_search_lim) {
232 61           ret_lim= search_lim;
233 61           ret_lim_bit= search_lim_bit;
234             }
235             /* It also ends here if multi is false, unless already set */
236 152           if (!multi && !ret_pos && search_lim <= real_search_lim) {
237             /* negative matches end at the character following search_pos */
238 40           if (negate) {
239 10           ret_pos= next_char_lim;
240 10           ret_pos_bit= next_char_lim_bit;
241             } else {
242 30           ret_pos= parse->lim;
243 30           ret_pos_bit= parse->lim_bit;
244             }
245 40           if (!consttime || anchored)
246             break;
247             }
248             }
249             /* If not a match (or matches but negated) and the multi-match was started,
250             * search_lim is the front of the multi-match. */
251 165           else if (multi && ret_lim && !ret_pos) {
252 19           if (search_lim <= real_search_lim) {
253 19           ret_pos= search_lim;
254 19           ret_pos_bit= search_lim_bit;
255 19           if (!consttime)
256 19           break;
257             }
258             }
259 146           else if (anchored) { /* not our goal, anchored, and not multi-match */
260 51           if (!ret_lim) {
261 51           anchor_fail= true;
262             /* return essentially the original value of 'parse' */
263 51           ret_lim= search_lim;
264 51           ret_lim_bit= search_lim_bit;
265 51           ret_pos= parse->pos;
266 51           ret_pos_bit= parse->pos_bit;
267             }
268             /* only need to waste time if consttime with multiple matches */
269 51           if (!(consttime && multi))
270             break;
271             }
272             /* constant time iteration always resumes at the following character */
273 207           if (consttime) {
274 0           if (search_lim <= real_search_lim)
275 0           real_search_lim= parse->lim;
276 0           parse->lim= next_char_lim;
277 0           parse->lim_bit= next_char_lim_bit;
278             }
279             }
280             /* If loop exited due to end of input and multi-match was in progress, mark the end */
281 113           if (multi && ret_lim && !ret_pos) {
282 2           ret_pos= parse->pos;
283 2           ret_pos_bit= parse->pos_bit;
284             }
285             }
286             /* If they are both set, overwrite parse and maybe return true.
287             * Otherwise, return false with the existing state of parse. */
288 425           if (ret_pos && ret_lim) {
289 419           parse->pos= ret_pos;
290 419           parse->pos_bit= ret_pos_bit;
291 419           parse->lim= ret_lim;
292 419           parse->lim_bit= ret_lim_bit;
293 419           return !encoding_error && !anchor_fail;
294             }
295 6           return false;
296             }