The next step towards the *zprintf family of functions. Part of step 1 of <https://lists.gnu.org/archive/html/bug-gnulib/2024-04/msg00352.html>.
I added three unit tests for %s with large arguments. (You need to pass option --with-longrunning-tests to gnulib-tool, in order to include them.) Without a change to lib/vasnprintf.c, all three failed: FAIL: test-u8-asnprintf-big =========================== ../../gltests/unistdio/test-u8-asnprintf-big.c:131: assertion 'errno == ENOMEM' failed FAIL test-u8-asnprintf-big (exit status: 134) FAIL: test-ulc-asnprintf-big ============================ ../../gltests/unistdio/test-ulc-asnprintf-big.c:156: assertion 'errno == ENOMEM' failed FAIL test-ulc-asnprintf-big (exit status: 134) FAIL: test-vasnprintf-big ========================= ../../gltests/test-vasnprintf-big.c:131: assertion 'errno == ENOMEM' failed FAIL test-vasnprintf-big (exit status: 134) So, here's the vasnprintf update that makes all these tests work. 2024-06-19 Bruno Haible <br...@clisp.org> ulc-asnprintf tests: Add test of %U, %s directives with large arguments. * tests/unistdio/test-ulc-asnprintf-big.c: New file, based on tests/test-vasnprintf-big.c. * modules/unistdio/ulc-asnprintf-extra-tests: New file. * modules/unistdio/ulc-asnprintf-tests (Depends-on): Depend on it. u8-asnprintf tests: Add test of %U, %s directives with large arguments. * tests/unistdio/test-u8-asnprintf-big.c: New file, based on tests/test-vasnprintf-big.c. * modules/unistdio/u8-asnprintf-extra-tests: New file. * modules/unistdio/u8-asnprintf-tests (Depends-on): Depend on it. vasnprintf tests: Add test of %s directive with large arguments. * tests/test-vasnprintf-big.c: New file. * modules/vasnprintf-extra-tests: New file. * modules/vasnprintf-tests (Depends-on): Depend on it. vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB. * lib/vasnprintf.c: Include <stdint.h>. (VASNPRINTF): In 64-bit builds, handle the %s directive ourselves. (local_strnlen): Adjust #if condition. * modules/unistdio/u8-vasnprintf (Depends-on): Add stdint. * modules/unistdio/u8-u8-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-u16-vasnprintf (Depends-on): Likewise. * modules/unistdio/u32-vasnprintf (Depends-on): Likewise. * modules/unistdio/u32-u32-vasnprintf (Depends-on): Likewise. * modules/unistdio/ulc-vasnprintf (Depends-on): Likewise.
>From 4cada7ff4ddfc45f204f042ff2acfbac851eb25f Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 19 Jun 2024 20:46:47 +0200 Subject: [PATCH 1/4] vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB. * lib/vasnprintf.c: Include <stdint.h>. (VASNPRINTF): In 64-bit builds, handle the %s directive ourselves. (local_strnlen): Adjust #if condition. * modules/unistdio/u8-vasnprintf (Depends-on): Add stdint. * modules/unistdio/u8-u8-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-u16-vasnprintf (Depends-on): Likewise. * modules/unistdio/u32-vasnprintf (Depends-on): Likewise. * modules/unistdio/u32-u32-vasnprintf (Depends-on): Likewise. * modules/unistdio/ulc-vasnprintf (Depends-on): Likewise. --- ChangeLog | 14 +++ lib/vasnprintf.c | 186 +++++++++++++++++++++++++++- modules/unistdio/u16-u16-vasnprintf | 1 + modules/unistdio/u16-vasnprintf | 1 + modules/unistdio/u32-u32-vasnprintf | 1 + modules/unistdio/u32-vasnprintf | 1 + modules/unistdio/u8-u8-vasnprintf | 1 + modules/unistdio/u8-vasnprintf | 1 + modules/unistdio/ulc-vasnprintf | 1 + 9 files changed, 206 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 0f89dacf4a..fc98fc99af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2024-06-19 Bruno Haible <br...@clisp.org> + + vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB. + * lib/vasnprintf.c: Include <stdint.h>. + (VASNPRINTF): In 64-bit builds, handle the %s directive ourselves. + (local_strnlen): Adjust #if condition. + * modules/unistdio/u8-vasnprintf (Depends-on): Add stdint. + * modules/unistdio/u8-u8-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u16-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u16-u16-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u32-vasnprintf (Depends-on): Likewise. + * modules/unistdio/u32-u32-vasnprintf (Depends-on): Likewise. + * modules/unistdio/ulc-vasnprintf (Depends-on): Likewise. + 2024-06-19 Bruno Haible <br...@clisp.org> vasnwprintf: Optimize handling of %c directive. diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 1838ded22d..1178822df8 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -80,6 +80,7 @@ #endif #include <locale.h> /* localeconv() */ +#include <stdint.h> /* PTRDIFF_MAX */ #include <stdio.h> /* snprintf(), sprintf() */ #include <stdlib.h> /* abort(), malloc(), realloc(), free() */ #include <string.h> /* memcpy(), strlen() */ @@ -231,7 +232,7 @@ #undef remainder #define remainder rem -#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && !WIDE_CHAR_VERSION +#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (PTRDIFF_MAX > INT_MAX)) && !WIDE_CHAR_VERSION # if (HAVE_STRNLEN && !defined _AIX) # define local_strnlen strnlen # else @@ -2809,6 +2810,189 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, } } #endif +#if !WIDE_CHAR_VERSION && (PTRDIFF_MAX > INT_MAX) + else if (dp->conversion == 's' + && a.arg[dp->arg_index].type != TYPE_WIDE_STRING) + { + /* %s in vasnprintf. See the specification of fprintf. + We handle it ourselves here, because the string may be longer + than INT_MAX characters, whence snprintf or sprintf would + fail to process it. */ + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + width = arg; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = -width; + } + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + if (width > (size_t) INT_MAX) + goto overflow; + has_width = 1; + } + + has_precision = 0; + precision = 6; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + { + const char *arg = a.arg[dp->arg_index].a.a_string; + size_t bytes; +# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR + size_t characters; +# endif +# if !DCHAR_IS_TCHAR + /* This code assumes that TCHAR_T is 'char'. */ + static_assert (sizeof (TCHAR_T) == 1); + DCHAR_T *tmpdst; + size_t tmpdst_len; +# endif + size_t w; + + if (has_precision) + { + /* Use only at most PRECISION bytes, from the left. */ + bytes = local_strnlen (arg, precision); + } + else + { + /* Use the entire string, and count the number of + bytes. */ + bytes = strlen (arg); + } + +# if ENABLE_UNISTDIO && DCHAR_IS_TCHAR + if (has_width) + characters = mbsnlen (arg, bytes); + else + { + /* The number of characters doesn't matter, + because !has_width and therefore width==0. */ + characters = 0; + } +# endif + +# if !DCHAR_IS_TCHAR + /* Convert from TCHAR_T[] to DCHAR_T[]. */ + tmpdst = + DCHAR_CONV_FROM_ENCODING (locale_charset (), + iconveh_question_mark, + arg, bytes, + NULL, + NULL, &tmpdst_len); + if (tmpdst == NULL) + goto fail_with_errno; +# endif + + if (has_width) + { +# if ENABLE_UNISTDIO + /* Outside POSIX, it's preferable to compare the width + against the number of _characters_ of the converted + value. */ +# if DCHAR_IS_TCHAR + w = characters; +# else + w = DCHAR_MBSNLEN (tmpdst, tmpdst_len); +# endif +# else + /* The width is compared against the number of _bytes_ + of the converted value, says POSIX. */ + w = bytes; +# endif + } + else + /* w doesn't matter. */ + w = 0; + + { +# if DCHAR_IS_TCHAR + size_t total = bytes + (w < width ? width - w : 0); + ENSURE_ALLOCATION (xsum (length, total)); +# else + size_t total = tmpdst_len + (w < width ? width - w : 0); + ENSURE_ALLOCATION_ELSE (xsum (length, total), + { free (tmpdst); goto out_of_memory; }); +# endif + + if (w < width && !(flags & FLAG_LEFT)) + { + size_t n = width - w; + DCHAR_SET (result + length, ' ', n); + length += n; + } + +# if DCHAR_IS_TCHAR + memcpy (result + length, arg, bytes); + length += bytes; +# else + DCHAR_CPY (result + length, tmpdst, tmpdst_len); + free (tmpdst); + length += tmpdst_len; +# endif + + if (w < width && (flags & FLAG_LEFT)) + { + size_t n = width - w; + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + } + } +#endif #if WIDE_CHAR_VERSION && (!DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) else if ((dp->conversion == 's' && a.arg[dp->arg_index].type == TYPE_WIDE_STRING) diff --git a/modules/unistdio/u16-u16-vasnprintf b/modules/unistdio/u16-u16-vasnprintf index ef5886cb9d..98adbbb37a 100644 --- a/modules/unistdio/u16-u16-vasnprintf +++ b/modules/unistdio/u16-u16-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u16-vasnprintf b/modules/unistdio/u16-vasnprintf index 7769617562..de541ef1ca 100644 --- a/modules/unistdio/u16-vasnprintf +++ b/modules/unistdio/u16-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u32-u32-vasnprintf b/modules/unistdio/u32-u32-vasnprintf index 587e4adf99..168cdbd7a9 100644 --- a/modules/unistdio/u32-u32-vasnprintf +++ b/modules/unistdio/u32-u32-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u32-vasnprintf b/modules/unistdio/u32-vasnprintf index 84466c8b87..bdea7468b1 100644 --- a/modules/unistdio/u32-vasnprintf +++ b/modules/unistdio/u32-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u8-u8-vasnprintf b/modules/unistdio/u8-u8-vasnprintf index d34f513485..5168794bf1 100644 --- a/modules/unistdio/u8-u8-vasnprintf +++ b/modules/unistdio/u8-u8-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/u8-vasnprintf b/modules/unistdio/u8-vasnprintf index dd71ae96a9..cf83b9343c 100644 --- a/modules/unistdio/u8-vasnprintf +++ b/modules/unistdio/u8-vasnprintf @@ -30,6 +30,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint isnand-nolibm isnanl-nolibm frexpl-nolibm diff --git a/modules/unistdio/ulc-vasnprintf b/modules/unistdio/ulc-vasnprintf index b2476b1e65..bea310b874 100644 --- a/modules/unistdio/ulc-vasnprintf +++ b/modules/unistdio/ulc-vasnprintf @@ -27,6 +27,7 @@ unistr/u16-strmblen unistr/u32-strlen unistr/u32-strmblen attribute +stdint mbsnlen isnand-nolibm isnanl-nolibm -- 2.34.1
>From 8b43f5a6745c67bd7e6de6be0334685d4f6b88db Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 19 Jun 2024 20:52:08 +0200 Subject: [PATCH 2/4] vasnprintf tests: Add test of %s directive with large arguments. * tests/test-vasnprintf-big.c: New file. * modules/vasnprintf-extra-tests: New file. * modules/vasnprintf-tests (Depends-on): Depend on it. --- ChangeLog | 5 + modules/vasnprintf-extra-tests | 18 +++ modules/vasnprintf-tests | 2 +- tests/test-vasnprintf-big.c | 200 +++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 modules/vasnprintf-extra-tests create mode 100644 tests/test-vasnprintf-big.c diff --git a/ChangeLog b/ChangeLog index fc98fc99af..cc1671b2b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2024-06-19 Bruno Haible <br...@clisp.org> + vasnprintf tests: Add test of %s directive with large arguments. + * tests/test-vasnprintf-big.c: New file. + * modules/vasnprintf-extra-tests: New file. + * modules/vasnprintf-tests (Depends-on): Depend on it. + vasnprintf, u*-vasnprintf: Support string arguments longer than 2 GiB. * lib/vasnprintf.c: Include <stdint.h>. (VASNPRINTF): In 64-bit builds, handle the %s directive ourselves. diff --git a/modules/vasnprintf-extra-tests b/modules/vasnprintf-extra-tests new file mode 100644 index 0000000000..b86c8a05d4 --- /dev/null +++ b/modules/vasnprintf-extra-tests @@ -0,0 +1,18 @@ +Status: +longrunning-test + +Files: +tests/test-vasnprintf-big.c +tests/macros.h + +Depends-on: +stdbool +stdint +physmem + +configure.ac: +AC_CHECK_FUNCS_ONCE([setrlimit]) + +Makefile.am: +TESTS += test-vasnprintf-big +check_PROGRAMS += test-vasnprintf-big diff --git a/modules/vasnprintf-tests b/modules/vasnprintf-tests index 7ae88c6b4c..062a4fd37e 100644 --- a/modules/vasnprintf-tests +++ b/modules/vasnprintf-tests @@ -3,10 +3,10 @@ tests/test-vasnprintf.c tests/macros.h Depends-on: +vasnprintf-extra-tests configure.ac: Makefile.am: TESTS += test-vasnprintf check_PROGRAMS += test-vasnprintf - diff --git a/tests/test-vasnprintf-big.c b/tests/test-vasnprintf-big.c new file mode 100644 index 0000000000..e4bb28c95c --- /dev/null +++ b/tests/test-vasnprintf-big.c @@ -0,0 +1,200 @@ +/* Test of [v]asnprintf() with big results. + Copyright (C) 2024 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +#include "vasnprintf.h" + +#include "physmem.h" + +/* Get INT_MAX. */ +#include <limits.h> + +/* Get PTRDIFF_MAX. */ +#include <stdint.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#if HAVE_SETRLIMIT +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#include "macros.h" + +int +main () +{ +#if PTRDIFF_MAX == INT_MAX + fputs ("Skipping test: ptrdiff_t is not 64-bits wide\n", stderr); + return 77; +#else + bool skipped = false; + /* Disable core files that would be huge. */ +# if HAVE_SETRLIMIT && defined RLIMIT_CORE + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = 0; + setrlimit (RLIMIT_CORE, &rl); +# endif + /* The test below needs about 12 GiB of memory: + 3 GiB for the inputs and up to 9 GiB for temporary output buffers. */ + double needed = 12.0 * 1024 * 1024 * 1024; + double avail = physmem_claimable (1.0); + printf ("memory needed = %g MiB, available = %g MiB\n", + needed / 1024 / 1024, avail / 1024 / 1024); + if (avail >= needed) + { + /* Note: The malloc() calls can fail, due to ulimit of RLIMIT_DATA. + For example, on OpenBSD 7.5, the soft limit is 1.0 GiB or 1.5 GiB, + and you need "ulimit -d 15728640". */ + + /* Verify that asnprintf() can return a string of size > 4 GiB. */ + { + size_t n1 = 3 * (INT_MAX / 4) + 10; + size_t n2 = 3 * (INT_MAX / 4) + 20; + char *s1; + char *s2; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + s2 = (char *) malloc (n2 + 1); + if (s2 != NULL) + { + memset (s2, 'b', n2); + s1[n1] = '\0'; + + size_t len; + char *s = asnprintf (NULL, &len, "x%sy%sz", s1, s2); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + n2 + 3); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + i <= n1 + n2 + 1 ? 'b' : + i == n1 + n2 + 2 ? 'z' : + '\0'); + free (s); + } + free (s2); + } + free (s1); + } + } + + /* Verify that asnprintf() can take a string of size > 2 GiB, < 4 GiB + as argument. */ + { + size_t n1 = 3 * (size_t) (INT_MAX / 2) + 10; + char *s1; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + size_t len; + char *s = asnprintf (NULL, &len, "x%sy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + free (s1); + } + } + + /* Verify that asnprintf() can take a string of size > 4 GiB + as argument. */ + { + size_t n1 = 5 * (size_t) (INT_MAX / 2) + 10; + if (n1 > (size_t) INT_MAX) + { + char *s1; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + size_t len; + char *s = asnprintf (NULL, &len, "x%sy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + free (s1); + } + } + } + } + else + skipped = true; + + if (test_exit_status != EXIT_SUCCESS) + return test_exit_status; + if (skipped) + { + fputs ("Skipping test: not enough memory available\n", stderr); + return 77; + } + return 0; +#endif +} -- 2.34.1
>From 19660fa2c5d265b46d1f5b8910637f4b71cc3827 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 19 Jun 2024 20:56:17 +0200 Subject: [PATCH 3/4] u8-asnprintf tests: Add test of %U, %s directives with large arguments. * tests/unistdio/test-u8-asnprintf-big.c: New file, based on tests/test-vasnprintf-big.c. * modules/unistdio/u8-asnprintf-extra-tests: New file. * modules/unistdio/u8-asnprintf-tests (Depends-on): Depend on it. --- ChangeLog | 6 + modules/unistdio/u8-asnprintf-extra-tests | 20 +++ modules/unistdio/u8-asnprintf-tests | 1 + tests/unistdio/test-u8-asnprintf-big.c | 200 ++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 modules/unistdio/u8-asnprintf-extra-tests create mode 100644 tests/unistdio/test-u8-asnprintf-big.c diff --git a/ChangeLog b/ChangeLog index cc1671b2b5..cb05d354c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2024-06-19 Bruno Haible <br...@clisp.org> + u8-asnprintf tests: Add test of %U, %s directives with large arguments. + * tests/unistdio/test-u8-asnprintf-big.c: New file, based on + tests/test-vasnprintf-big.c. + * modules/unistdio/u8-asnprintf-extra-tests: New file. + * modules/unistdio/u8-asnprintf-tests (Depends-on): Depend on it. + vasnprintf tests: Add test of %s directive with large arguments. * tests/test-vasnprintf-big.c: New file. * modules/vasnprintf-extra-tests: New file. diff --git a/modules/unistdio/u8-asnprintf-extra-tests b/modules/unistdio/u8-asnprintf-extra-tests new file mode 100644 index 0000000000..c4f7a18e19 --- /dev/null +++ b/modules/unistdio/u8-asnprintf-extra-tests @@ -0,0 +1,20 @@ +Status: +longrunning-test + +Files: +tests/unistdio/test-u8-asnprintf-big.c +tests/macros.h + +Depends-on: +stdbool +stdint +physmem + +configure.ac: +AC_CHECK_FUNCS_ONCE([setrlimit]) + +Makefile.am: +TESTS += test-u8-asnprintf-big +check_PROGRAMS += test-u8-asnprintf-big +test_u8_asnprintf_big_SOURCES = unistdio/test-u8-asnprintf-big.c +test_u8_asnprintf_big_LDADD = $(LDADD) $(LIBUNISTRING) @LIBICONV@ diff --git a/modules/unistdio/u8-asnprintf-tests b/modules/unistdio/u8-asnprintf-tests index d39689feaa..6c46c112c5 100644 --- a/modules/unistdio/u8-asnprintf-tests +++ b/modules/unistdio/u8-asnprintf-tests @@ -5,6 +5,7 @@ tests/unistdio/test-u8-printf1.h tests/macros.h Depends-on: +unistdio/u8-asnprintf-extra-tests configure.ac: diff --git a/tests/unistdio/test-u8-asnprintf-big.c b/tests/unistdio/test-u8-asnprintf-big.c new file mode 100644 index 0000000000..25a5880c0a --- /dev/null +++ b/tests/unistdio/test-u8-asnprintf-big.c @@ -0,0 +1,200 @@ +/* Test of u8_asnprintf() with big results. + Copyright (C) 2024 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +#include <unistdio.h> + +#include "physmem.h" + +/* Get INT_MAX. */ +#include <limits.h> + +/* Get PTRDIFF_MAX. */ +#include <stdint.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#if HAVE_SETRLIMIT +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#include "macros.h" + +int +main () +{ +#if PTRDIFF_MAX == INT_MAX + fputs ("Skipping test: ptrdiff_t is not 64-bits wide\n", stderr); + return 77; +#else + bool skipped = false; + /* Disable core files that would be huge. */ +# if HAVE_SETRLIMIT && defined RLIMIT_CORE + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = 0; + setrlimit (RLIMIT_CORE, &rl); +# endif + /* The test below needs about 12 GiB of memory: + 3 GiB for the inputs and up to 9 GiB for temporary output buffers. */ + double needed = 12.0 * 1024 * 1024 * 1024; + double avail = physmem_claimable (1.0); + printf ("memory needed = %g MiB, available = %g MiB\n", + needed / 1024 / 1024, avail / 1024 / 1024); + if (avail >= needed) + { + /* Note: The malloc() calls can fail, due to ulimit of RLIMIT_DATA. + For example, on OpenBSD 7.5, the soft limit is 1.0 GiB or 1.5 GiB, + and you need "ulimit -d 15728640". */ + + /* Verify that u8_asnprintf() can return a string of size > 4 GiB. */ + { + size_t n1 = 3 * (INT_MAX / 4) + 10; + size_t n2 = 3 * (INT_MAX / 4) + 20; + char *s1; + char *s2; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + s2 = (char *) malloc (n2 + 1); + if (s2 != NULL) + { + memset (s2, 'b', n2); + s1[n1] = '\0'; + + size_t len; + uint8_t *s = u8_asnprintf (NULL, &len, "x%sy%sz", s1, s2); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + n2 + 3); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + i <= n1 + n2 + 1 ? 'b' : + i == n1 + n2 + 2 ? 'z' : + '\0'); + free (s); + } + free (s2); + } + free (s1); + } + } + + /* Verify that u8_asnprintf() can take a string of size > 2 GiB, < 4 GiB + as argument. */ + { + size_t n1 = 3 * (size_t) (INT_MAX / 2) + 10; + char *s1; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + size_t len; + uint8_t *s = u8_asnprintf (NULL, &len, "x%sy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + free (s1); + } + } + + /* Verify that u8_asnprintf() can take a string of size > 4 GiB + as argument. */ + { + size_t n1 = 5 * (size_t) (INT_MAX / 2) + 10; + if (n1 > (size_t) INT_MAX) + { + char *s1; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + size_t len; + uint8_t *s = u8_asnprintf (NULL, &len, "x%sy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + free (s1); + } + } + } + } + else + skipped = true; + + if (test_exit_status != EXIT_SUCCESS) + return test_exit_status; + if (skipped) + { + fputs ("Skipping test: not enough memory available\n", stderr); + return 77; + } + return 0; +#endif +} -- 2.34.1
>From 3af57020d3bc3be38a60590a2ad4ff8fe2a1eeb3 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 19 Jun 2024 20:58:18 +0200 Subject: [PATCH 4/4] ulc-asnprintf tests: Add test of %U, %s directives with large arguments. * tests/unistdio/test-ulc-asnprintf-big.c: New file, based on tests/test-vasnprintf-big.c. * modules/unistdio/ulc-asnprintf-extra-tests: New file. * modules/unistdio/ulc-asnprintf-tests (Depends-on): Depend on it. --- ChangeLog | 6 + modules/unistdio/ulc-asnprintf-extra-tests | 20 ++ modules/unistdio/ulc-asnprintf-tests | 1 + tests/unistdio/test-ulc-asnprintf-big.c | 254 +++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 modules/unistdio/ulc-asnprintf-extra-tests create mode 100644 tests/unistdio/test-ulc-asnprintf-big.c diff --git a/ChangeLog b/ChangeLog index cb05d354c2..fc37bc6d79 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2024-06-19 Bruno Haible <br...@clisp.org> + ulc-asnprintf tests: Add test of %U, %s directives with large arguments. + * tests/unistdio/test-ulc-asnprintf-big.c: New file, based on + tests/test-vasnprintf-big.c. + * modules/unistdio/ulc-asnprintf-extra-tests: New file. + * modules/unistdio/ulc-asnprintf-tests (Depends-on): Depend on it. + u8-asnprintf tests: Add test of %U, %s directives with large arguments. * tests/unistdio/test-u8-asnprintf-big.c: New file, based on tests/test-vasnprintf-big.c. diff --git a/modules/unistdio/ulc-asnprintf-extra-tests b/modules/unistdio/ulc-asnprintf-extra-tests new file mode 100644 index 0000000000..4d3c1cd0c4 --- /dev/null +++ b/modules/unistdio/ulc-asnprintf-extra-tests @@ -0,0 +1,20 @@ +Status: +longrunning-test + +Files: +tests/unistdio/test-ulc-asnprintf-big.c +tests/macros.h + +Depends-on: +stdbool +stdint +physmem + +configure.ac: +AC_CHECK_FUNCS_ONCE([setrlimit]) + +Makefile.am: +TESTS += test-ulc-asnprintf-big +check_PROGRAMS += test-ulc-asnprintf-big +test_ulc_asnprintf_big_SOURCES = unistdio/test-ulc-asnprintf-big.c +test_ulc_asnprintf_big_LDADD = $(LDADD) $(LIBUNISTRING) @LIBICONV@ $(MBRTOWC_LIB) diff --git a/modules/unistdio/ulc-asnprintf-tests b/modules/unistdio/ulc-asnprintf-tests index 99a5851624..982f3279a4 100644 --- a/modules/unistdio/ulc-asnprintf-tests +++ b/modules/unistdio/ulc-asnprintf-tests @@ -5,6 +5,7 @@ tests/unistdio/test-ulc-printf1.h tests/macros.h Depends-on: +unistdio/ulc-asnprintf-extra-tests configure.ac: diff --git a/tests/unistdio/test-ulc-asnprintf-big.c b/tests/unistdio/test-ulc-asnprintf-big.c new file mode 100644 index 0000000000..b73ff302be --- /dev/null +++ b/tests/unistdio/test-ulc-asnprintf-big.c @@ -0,0 +1,254 @@ +/* Test of ulc_asnprintf() with big results. + Copyright (C) 2024 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2024. */ + +#include <config.h> + +#include <unistdio.h> + +#include "physmem.h" + +/* Get INT_MAX. */ +#include <limits.h> + +/* Get PTRDIFF_MAX. */ +#include <stdint.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#if HAVE_SETRLIMIT +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#include "macros.h" + +int +main () +{ +#if PTRDIFF_MAX == INT_MAX + fputs ("Skipping test: ptrdiff_t is not 64-bits wide\n", stderr); + return 77; +#else + bool skipped = false; + /* Disable core files that would be huge. */ +# if HAVE_SETRLIMIT && defined RLIMIT_CORE + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = 0; + setrlimit (RLIMIT_CORE, &rl); +# endif + /* The test below needs about 12 GiB of memory: + 3 GiB for the inputs and up to 9 GiB for temporary output buffers. */ + double needed = 12.0 * 1024 * 1024 * 1024; + double avail = physmem_claimable (1.0); + printf ("memory needed = %g MiB, available = %g MiB\n", + needed / 1024 / 1024, avail / 1024 / 1024); + if (avail >= needed) + { + /* Note: The malloc() calls can fail, due to ulimit of RLIMIT_DATA. + For example, on OpenBSD 7.5, the soft limit is 1.0 GiB or 1.5 GiB, + and you need "ulimit -d 15728640". */ + + /* Verify that ulc_asnprintf() can return a string of size > 4 GiB. */ + { + size_t n1 = 3 * (INT_MAX / 4) + 10; + size_t n2 = 3 * (INT_MAX / 4) + 20; + char *s1; + char *s2; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + s2 = (char *) malloc (n2 + 1); + if (s2 != NULL) + { + memset (s2, 'b', n2); + s1[n1] = '\0'; + + size_t len; + char *s = ulc_asnprintf (NULL, &len, "x%sy%sz", s1, s2); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + n2 + 3); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + i <= n1 + n2 + 1 ? 'b' : + i == n1 + n2 + 2 ? 'z' : + '\0'); + free (s); + } + free (s2); + } + free (s1); + } + } + + /* Verify that ulc_asnprintf() can take a string of size > 2 GiB, < 4 GiB + as argument. */ + { + size_t n1 = 3 * (size_t) (INT_MAX / 2) + 10; + char *s1; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + /* Once with %U. */ + { + size_t len; + char *s = ulc_asnprintf (NULL, &len, "x%Uy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + } + + /* Once with %s. */ + { + size_t len; + char *s = ulc_asnprintf (NULL, &len, "x%sy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + } + + free (s1); + } + } + + /* Verify that ulc_asnprintf() can take a string of size > 4 GiB + as argument. */ + { + size_t n1 = 5 * (size_t) (INT_MAX / 2) + 10; + if (n1 > (size_t) INT_MAX) + { + char *s1; + + s1 = (char *) malloc (n1 + 1); + if (s1 != NULL) + { + memset (s1, 'a', n1); + s1[n1] = '\0'; + + /* Once with %U. */ + { + size_t len; + char *s = ulc_asnprintf (NULL, &len, "x%Uy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + } + + /* Once with %s. */ + { + size_t len; + char *s = ulc_asnprintf (NULL, &len, "x%sy", s1); + if (s == NULL) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + ASSERT (strlen (s) == len); + ASSERT (len == n1 + 2); + size_t i; + for (i = 0; i <= len; i++) + s[i] = (i == 0 ? 'x' : + i <= n1 ? 'a' : + i == n1 + 1 ? 'y' : + '\0'); + free (s); + } + } + + free (s1); + } + } + } + } + else + skipped = true; + + if (test_exit_status != EXIT_SUCCESS) + return test_exit_status; + if (skipped) + { + fputs ("Skipping test: not enough memory available\n", stderr); + return 77; + } + return 0; +#endif +} -- 2.34.1