Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions lib/tre-match-approx.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,9 @@ tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
pretending that all transitions are epsilon transitions, until
no more states can be reached with better costs. */
{
/* XXX - dynamic ringbuffer size */
tre_tnfa_approx_reach_t *ringbuffer[512];
int rb_size = 256;
tre_tnfa_approx_reach_t *static_ringbuffer[256];
tre_tnfa_approx_reach_t **ringbuffer = static_ringbuffer;
tre_tnfa_approx_reach_t **deque_start, **deque_end;

deque_start = deque_end = ringbuffer;
Expand All @@ -487,7 +488,34 @@ tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
continue;
*deque_end = &reach_next[id];
deque_end++;
assert(deque_end != deque_start);
/* Grow the buffer (moving to the heap) if full. */
if (deque_end >= (ringbuffer + rb_size))
{
tre_tnfa_approx_reach_t **larger_buf;
size_t os = deque_start - ringbuffer;
size_t oe = deque_end - ringbuffer;
rb_size += 512;
if (ringbuffer == static_ringbuffer)
larger_buf = xmalloc(sizeof(*ringbuffer) * rb_size);
else
larger_buf = xrealloc(ringbuffer, sizeof(*ringbuffer) * rb_size);
if (larger_buf == NULL)
{
if (ringbuffer != static_ringbuffer)
xfree(ringbuffer);
#ifndef TRE_USE_ALLOCA
if (buf)
xfree(buf);
#endif /* !TRE_USE_ALLOCA */
return REG_ESPACE;
}
if (ringbuffer == static_ringbuffer)
/* Moving from stack to heap: copy existing contents. */
memcpy(larger_buf, ringbuffer, sizeof(static_ringbuffer));
ringbuffer = larger_buf;
deque_start = ringbuffer + os;
deque_end = ringbuffer + oe;
}
}

/* Repeat until the deque is empty. */
Expand Down Expand Up @@ -519,7 +547,7 @@ tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
/* Too many errors or cost too large. */
DPRINT((" delete: from %03d: cost too large\n", id));
deque_start++;
if (deque_start >= (ringbuffer + 512))
if (deque_start >= (ringbuffer + rb_size))
deque_start = ringbuffer;
continue;
}
Expand Down Expand Up @@ -617,15 +645,17 @@ tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
/* Add to the end of the deque. */
*deque_end = &reach_next[dest_id];
deque_end++;
if (deque_end >= (ringbuffer + 512))
if (deque_end >= (ringbuffer + rb_size))
deque_end = ringbuffer;
assert(deque_end != deque_start);
}
deque_start++;
if (deque_start >= (ringbuffer + 512))
if (deque_start >= (ringbuffer + rb_size))
deque_start = ringbuffer;
}

if (ringbuffer != static_ringbuffer)
xfree(ringbuffer);
}

#ifdef TRE_DEBUG
Expand Down
16 changes: 16 additions & 0 deletions tests/retest.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,22 @@ main(int argc, char **argv)
test_comp("^!pfast [0-9]{1,15} ([0-9]{1,3}\\.){3}[0-9]{1,3}[0-9]{1,5}$",
REG_EXTENDED, 0);

/* An alternation with many branches. The resulting TNFA has well over
512 states reachable at one position, so the approximate matcher's
delete-handling deque must grow beyond its old fixed size (this
matters here because test_exec() also runs each test with
REG_APPROX_MATCHER). */
{
static char many_branches[600 * 5];
char *p = many_branches;
int i;
for (i = 0; i < 600; i++)
p += sprintf(p, "%sx%03d", i > 0 ? "|" : "", i);
test_comp(many_branches, REG_EXTENDED, 0);
test_exec("zzx059zz", 0, REG_OK, 2, 6, END);
test_exec("zzzzzz", 0, REG_NOMATCH);
}

#if KNOWN_BUG
/* Should these match or not? */
test_comp("(a)*-\\1b", REG_EXTENDED, 0);
Expand Down