Skip to content

Commit 61d801f

Browse files
committed
pcre2test: make resizing buffers slightly better
Use realloc() for exponential resize of all buffers, which has the advantage of potentially not needing to copy. Add a parameter to `expand_input_buffers()` to indicate the minimum size required and allow a smarter fallback when the buffer can't be doubled.
1 parent 0777daa commit 61d801f

File tree

1 file changed

+80
-37
lines changed

1 file changed

+80
-37
lines changed

src/pcre2test.c

Lines changed: 80 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3874,44 +3874,83 @@ return 0;
38743874
}
38753875
#endif /* SUPPORT_PCRE2_32 */
38763876

3877+
/* A helper to realloc() a buffer to a desired or fallback sizes. */
38773878

3879+
static void*
3880+
realloc_buffer(void *source, size_t fallback, size_t *size)
3881+
{
3882+
void *p;
3883+
3884+
p = realloc(source, *size);
3885+
if (p == NULL)
3886+
{
3887+
p = realloc(source, fallback);
3888+
if (p == NULL)
3889+
{
3890+
*size = 0;
3891+
return NULL;
3892+
}
3893+
*size = fallback;
3894+
}
3895+
return p;
3896+
}
38783897

38793898
/*************************************************
38803899
* Expand input buffers *
38813900
*************************************************/
38823901

38833902
/* This function doubles the size of the input buffer and the buffer for
3884-
keeping an 8-bit copy of patterns (pbuffer8), and copies the current buffers to
3885-
the new ones.
3903+
keeping an 8-bit copy of patterns (pbuffer8) until it is larger than the
3904+
minimum, or to the minimum if it can't be doubled.
38863905
3887-
Arguments: none
3888-
Returns: nothing (aborts if malloc() fails)
3906+
Arguments:
3907+
minimum size on bytes expected as a minimum
3908+
Returns: nothing (aborts if any realloc() fails)
38893909
*/
38903910

38913911
static void
3892-
expand_input_buffers(void)
3912+
expand_input_buffers(size_t minimum)
38933913
{
3894-
size_t new_pbuffer8_size = 2*pbuffer8_size;
3895-
uint8_t *new_buffer = (uint8_t *)malloc(new_pbuffer8_size);
3896-
uint8_t *new_pbuffer8 = (uint8_t *)malloc(new_pbuffer8_size);
3914+
size_t buffer_size, allowed_size;
3915+
void *p;
38973916

3898-
if (new_buffer == NULL || new_pbuffer8 == NULL)
3917+
PCRE2_ASSERT(minimum > pbuffer8_size);
3918+
if (minimum >= SIZE_MAX / 2 || pbuffer8_size >= SIZE_MAX / 2)
3919+
buffer_size = minimum;
3920+
else
38993921
{
3900-
fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed\n",
3901-
new_pbuffer8_size);
3902-
exit(1);
3922+
buffer_size = pbuffer8_size;
3923+
while (buffer_size < minimum) buffer_size *= 2;
39033924
}
39043925

3905-
memcpy(new_buffer, buffer, pbuffer8_size);
3906-
memcpy(new_pbuffer8, pbuffer8, pbuffer8_size);
3926+
p = realloc_buffer(buffer, minimum, &buffer_size);
3927+
if (p == NULL) goto DIE;
39073928

3908-
pbuffer8_size = new_pbuffer8_size;
3929+
allowed_size = buffer_size;
3930+
buffer = p;
39093931

3910-
free(buffer);
3911-
free(pbuffer8);
3932+
p = realloc_buffer(pbuffer8, minimum, &buffer_size);
3933+
if (p == NULL) goto DIE;
39123934

3913-
buffer = new_buffer;
3914-
pbuffer8 = new_pbuffer8;
3935+
if (allowed_size != buffer_size)
3936+
{
3937+
void *q = realloc(buffer, buffer_size);
3938+
if (q == NULL)
3939+
{
3940+
fprintf(stderr, "pcre2test: realloc(%" SIZ_FORM ") failed\n",
3941+
buffer_size);
3942+
exit(1);
3943+
}
3944+
buffer = q;
3945+
}
3946+
else pbuffer8 = p;
3947+
3948+
pbuffer8_size = buffer_size;
3949+
return;
3950+
DIE:
3951+
fprintf(stderr, "pcre2test: realloc(%" SIZ_FORM ") failed\n",
3952+
minimum);
3953+
exit(1);
39153954
}
39163955

39173956

@@ -3950,7 +3989,7 @@ for (;;)
39503989
size_t dlen;
39513990
size_t rlen = (size_t)(pbuffer8_size - (here - buffer));
39523991

3953-
/* If libreadline or libedit support is required, use readline() to read a
3992+
/* If libreadline or libedit support is available, use readline() to read a
39543993
line if the input is a terminal. Note that readline() removes the trailing
39553994
newline, so we must put it back again, to be compatible with fgets(). */
39563995

@@ -4011,7 +4050,7 @@ for (;;)
40114050
{
40124051
size_t start_offset = start - buffer;
40134052
size_t here_offset = here - buffer;
4014-
expand_input_buffers();
4053+
expand_input_buffers(pbuffer8_size + 1);
40154054
start = buffer + start_offset;
40164055
here = buffer + here_offset;
40174056
}
@@ -6202,14 +6241,14 @@ if ((pat_patctl.control & CTL_HEXPAT) != 0)
62026241
else if ((pat_patctl.control & CTL_EXPAND) != 0)
62036242
{
62046243
uint8_t *pp, *pt;
6244+
size_t needlen = 0;
62056245

62066246
pt = pbuffer8;
62076247
for (pp = buffer + 1; *pp != 0; pp++)
62086248
{
62096249
uint8_t *pc = pp;
62106250
uint32_t count = 1;
62116251
size_t length = 1;
6212-
size_t m = 1;
62136252

62146253
/* Check for replication syntax; if not found, the defaults just set will
62156254
prevail and `length` characters will be copied once. */
@@ -6252,7 +6291,7 @@ else if ((pat_patctl.control & CTL_EXPAND) != 0)
62526291
pc -= 2;
62536292
length = pe - pc;
62546293
}
6255-
m = length * count;
6294+
needlen += length * count;
62566295
pp = pe;
62576296

62586297
/* The main loop increments pp, so if we are already at the end of
@@ -6264,12 +6303,12 @@ else if ((pat_patctl.control & CTL_EXPAND) != 0)
62646303
expanding buffers always keeps buffer and pbuffer8 in step as far as their
62656304
size goes. */
62666305

6267-
while (pt + m > pbuffer8 + pbuffer8_size)
6306+
if (needlen > pbuffer8_size)
62686307
{
62696308
size_t pc_offset = pc - buffer;
62706309
size_t pp_offset = pp - buffer;
62716310
size_t pt_offset = pt - pbuffer8;
6272-
expand_input_buffers();
6311+
expand_input_buffers(needlen);
62736312
pc = buffer + pc_offset;
62746313
pp = buffer + pp_offset;
62756314
pt = pbuffer8 + pt_offset;
@@ -8099,17 +8138,17 @@ if (dbuffer != NULL)
80998138
}
81008139
#endif
81018140

8102-
/* Allocate a buffer to hold the data line; len+1 is an upper bound on
8141+
/* Allocate a buffer to hold the data line; len + 1 is an upper bound on
81038142
the number of code units that will be needed (though the buffer may have to be
81048143
extended if replication is involved). */
81058144

8106-
needlen = (len+1) * code_unit_size;
8107-
if (dbuffer == NULL || needlen >= dbuffer_size)
8145+
needlen = (len + 1) * code_unit_size;
8146+
if (dbuffer == NULL || needlen > dbuffer_size)
81088147
{
8109-
while (needlen >= dbuffer_size)
8148+
while (needlen > dbuffer_size)
81108149
{
81118150
if (dbuffer_size < SIZE_MAX/2) dbuffer_size *= 2;
8112-
else dbuffer_size = needlen + 1;
8151+
else dbuffer_size = needlen;
81138152
}
81148153
dbuffer = (uint8_t *)realloc(dbuffer, dbuffer_size);
81158154
if (dbuffer == NULL)
@@ -8146,7 +8185,7 @@ while ((c = *p++) != 0)
81468185

81478186
errno = 0;
81488187
li = strtol((const char *)p, &endptr, 10);
8149-
if (!isdigit(*p) || errno != 0 || li < 1 || S32OVERFLOW(li))
8188+
if (!isdigit(*p) || li < 1 || S32OVERFLOWE(li))
81508189
{
81518190
fprintf(outfile, "** Replication count missing or invalid (1..INT_MAX)\n");
81528191
return PR_OK;
@@ -8170,22 +8209,26 @@ while ((c = *p++) != 0)
81708209
}
81718210
needlen += replen * i;
81728211

8173-
if (needlen >= dbuffer_size)
8212+
if (needlen > dbuffer_size)
81748213
{
81758214
size_t qoffset = CAST8VAR(q) - dbuffer;
81768215
size_t rep_offset = start_rep - dbuffer;
8177-
while (needlen >= dbuffer_size)
8216+
void *tp;
8217+
8218+
while (needlen > dbuffer_size)
81788219
{
81798220
if (dbuffer_size < SIZE_MAX / 2) dbuffer_size *= 2;
8180-
else dbuffer_size = needlen + 1;
8221+
else dbuffer_size = needlen;
81818222
}
8182-
dbuffer = (uint8_t *)realloc(dbuffer, dbuffer_size);
8183-
if (dbuffer == NULL)
8223+
8224+
tp = realloc_buffer(dbuffer, needlen, &dbuffer_size);
8225+
if (tp == NULL)
81848226
{
81858227
fprintf(stderr, "pcre2test: realloc(%" SIZ_FORM ") failed\n",
8186-
dbuffer_size);
8228+
needlen);
81878229
exit(1);
81888230
}
8231+
dbuffer = (uint8_t *)tp;
81898232
SETCASTPTR(q, dbuffer + qoffset);
81908233
start_rep = dbuffer + rep_offset;
81918234
}

0 commit comments

Comments
 (0)