[PATCH v2] stdio-common: Optimize scanf %ms series array expansion
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Thu May 21 12:06:50 GMT 2026
On 21/05/26 00:22, Rocket Ma wrote:
> * stdio-common/vfscanf-internal.c: If user explicitly set the maximum
> size of the string, respect it when reading characters. Instead of
> always expanding exponentially, try to expand array to the exact size
> user requested when `user_size < current_size * 2`.
>
> Signed-off-by: Rocket Ma <marocketbd@gmail.com>
LGTM, thanks.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> ---
> Fixed some comments.
> ---
> stdio-common/vfscanf-internal.c | 65 ++++++++++++++++++++++-----------
> 1 file changed, 44 insertions(+), 21 deletions(-)
>
> diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
> index 3d11ac261e..9871cff6a4 100644
> --- a/stdio-common/vfscanf-internal.c
> +++ b/stdio-common/vfscanf-internal.c
> @@ -265,6 +265,19 @@ char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
> *buffer->current++ = ch;
> }
>
> +/* Calculate the result size of expanded char array in %ms, %mS, %m[
> + or %lm[. OLDSIZE is current allocation size and NEED is the
> + remaining field-width budget (chars still to read) or negative if
> + unbounded. */
> +static __always_inline size_t
> +grow_to_fit (size_t oldsize, int need)
> +{
> + if (need < 0 || oldsize < need)
> + return oldsize * 2;
> + /* oldsize >= need: grow requested capacity and 1 byte for `\0' */
> + return oldsize + need + 1;
> +}
> +
> /* Read formatted input from S according to the format string
> FORMAT, using the argument list in ARG.
> Return the number of assignments made, or -1 for an input error. */
> @@ -804,7 +817,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && *strptr + strsize - str <= MB_LEN_MAX)
> {
> /* We have to enlarge the buffer if the `m' flag
> - was given. */
> + was given. And we may not expand str by width
> + as the wcrtomb may return various bytes. */
> size_t strleng = str - *strptr;
> char *newstr;
>
> @@ -1098,7 +1112,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && *strptr + strsize - str <= MB_LEN_MAX)
> {
> /* We have to enlarge the buffer if the `a' or `m'
> - flag was given. */
> + flag was given. And we may not expand str by
> + width as the wcrtomb may return various bytes. */
> size_t strleng = str - *strptr;
> char *newstr;
>
> @@ -1156,7 +1171,9 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && (char *) str == *strptr + strsize)
> {
> /* Enlarge the buffer. */
> - str = (char *) realloc (*strptr, 2 * strsize);
> + size_t newsize = grow_to_fit (strsize, width);
> +
> + str = (char *) realloc (*strptr, newsize);
> if (str == NULL)
> {
> /* Can't allocate that much. Last-ditch
> @@ -1188,7 +1205,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> {
> *strptr = (char *) str;
> str += strsize;
> - strsize *= 2;
> + strsize = newsize;
> }
> }
> }
> @@ -1286,9 +1303,10 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && wstr == (wchar_t *) *strptr + strsize)
> {
> /* Enlarge the buffer. */
> - wstr = (wchar_t *) realloc (*strptr,
> - (2 * strsize)
> - * sizeof (wchar_t));
> + size_t newsize = grow_to_fit (strsize, width);
> +
> + wstr = (wchar_t *) realloc (
> + *strptr, newsize * sizeof (wchar_t));
> if (wstr == NULL)
> {
> /* Can't allocate that much. Last-ditch
> @@ -1322,7 +1340,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> {
> *strptr = (char *) wstr;
> wstr += strsize;
> - strsize *= 2;
> + strsize = newsize;
> }
> }
> }
> @@ -1362,9 +1380,10 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && wstr == (wchar_t *) *strptr + strsize)
> {
> /* Enlarge the buffer. */
> + size_t newsize = grow_to_fit (strsize, width);
> +
> wstr = (wchar_t *) realloc (*strptr,
> - (2 * strsize
> - * sizeof (wchar_t)));
> + newsize * sizeof (wchar_t));
> if (wstr == NULL)
> {
> /* Can't allocate that much. Last-ditch effort. */
> @@ -1397,7 +1416,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> {
> *strptr = (char *) wstr;
> wstr += strsize;
> - strsize *= 2;
> + strsize = newsize;
> }
> }
> }
> @@ -2754,9 +2773,10 @@ digits_extended_fail:
> && wstr == (wchar_t *) *strptr + strsize)
> {
> /* Enlarge the buffer. */
> - wstr = (wchar_t *) realloc (*strptr,
> - (2 * strsize)
> - * sizeof (wchar_t));
> + size_t newsize = grow_to_fit (strsize, width);
> +
> + wstr = (wchar_t *) realloc (
> + *strptr, newsize * sizeof (wchar_t));
> if (wstr == NULL)
> {
> /* Can't allocate that much. Last-ditch
> @@ -2790,7 +2810,7 @@ digits_extended_fail:
> {
> *strptr = (char *) wstr;
> wstr += strsize;
> - strsize *= 2;
> + strsize = newsize;
> }
> }
> }
> @@ -2839,9 +2859,10 @@ digits_extended_fail:
> && wstr == (wchar_t *) *strptr + strsize)
> {
> /* Enlarge the buffer. */
> - wstr = (wchar_t *) realloc (*strptr,
> - (2 * strsize
> - * sizeof (wchar_t)));
> + size_t newsize = grow_to_fit (strsize, width);
> +
> + wstr = (wchar_t *) realloc (
> + *strptr, newsize * sizeof (wchar_t));
> if (wstr == NULL)
> {
> /* Can't allocate that much. Last-ditch
> @@ -2875,7 +2896,7 @@ digits_extended_fail:
> {
> *strptr = (char *) wstr;
> wstr += strsize;
> - strsize *= 2;
> + strsize = newsize;
> }
> }
> }
> @@ -2983,7 +3004,9 @@ digits_extended_fail:
> if ((flags & MALLOC)
> && *strptr + strsize - str <= MB_LEN_MAX)
> {
> - /* Enlarge the buffer. */
> + /* Enlarge the buffer. And we may not
> + expand str by width as the wcrtomb may
> + return various bytes. */
> size_t strleng = str - *strptr;
> char *newstr;
>
> @@ -3051,7 +3074,7 @@ digits_extended_fail:
> && (char *) str == *strptr + strsize)
> {
> /* Enlarge the buffer. */
> - size_t newsize = 2 * strsize;
> + size_t newsize = grow_to_fit (strsize, width);
>
> allocagain:
> str = (char *) realloc (*strptr, newsize);
More information about the Libc-alpha
mailing list