This series of patches introduces an 'obstack-zprintf' module, that fixes the INT_MAX limitation (and also the unpredictable errno value in case of failure) of the 'obstack-printf' module.
I originally planned this set of module dependencies: obstack-printf -> obstack-zprintf obstack-zprintf -> vasnprintf but what I did is: obstack-printf -> vasnprintf obstack-zprintf -> vasnprintf because it's simpler. (In order to implement obstack-printf on top of obstack-zprintf, one needs to be able to undo a previous obstack_grow invocation. That sounds inefficient.) It means that obstack_printf.o and obstack_zprintf.o will contain essentially the same binary code. But since a package will usually only use of them, not both, it's not a problem. 2024-06-22 Bruno Haible <br...@clisp.org> obstack-zprintf: Add more tests. * tests/test-obstack-zprintf-big.c: New file, based on tests/test-vasnprintf-big.c. * modules/obstack-zprintf-extra-tests: New file. * modules/obstack-zprintf-tests (Depends-on): Add it. obstack-zprintf: Add tests. * tests/test-obstack-printf.h: New file, based on tests/test-obstack-printf.c. * tests/test-obstack-printf.c: Include test-obstack-printf.h. (obstack_chunk_alloc, obstack_chunk_free, test_function): Moved to tests/test-obstack-printf.h. (test_obstack_vprintf, test_obstack_printf): Remove functions. (main): Inline them here. * tests/test-obstack-zprintf.c: New file, based on tests/test-obstack-printf.c. * modules/obstack-printf-tests (Files): Add tests/test-obstack-printf.h. * modules/obstack-zprintf-tests: New file. obstack-zprintf: New module. * lib/stdio.in.h (obstack_zprintf, obstack_vzprintf): New declarations. (obstack_printf, obstack_vprintf): Tweak comment. * lib/obstack_printf.c: Parameterize. (RESULT_TYPE, OBSTACK_PRINTF, OBSTACK_VPRINTF): New macros. * lib/obstack_zprintf.c: New file. * m4/stdio_h.m4 (gl_STDIO_H_REQUIRE_DEFAULTS): Initialize GNULIB_OBSTACK_ZPRINTF. * modules/stdio (Makefile.am): Substitute GNULIB_OBSTACK_ZPRINTF. * modules/obstack-zprintf: New file. 2024-06-22 Bruno Haible <br...@clisp.org> physmem: Fix typo. * lib/physmem.h (physmem_claimable): Fix typo in comment.
>From aa910f1628d4adcfa967d4ef09bc2e82e6bbba89 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 22 Jun 2024 13:05:11 +0200 Subject: [PATCH 1/4] physmem: Fix typo. * lib/physmem.h (physmem_claimable): Fix typo in comment. --- ChangeLog | 5 +++++ lib/physmem.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index f81ae03de7..8d28459959 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-06-22 Bruno Haible <br...@clisp.org> + + physmem: Fix typo. + * lib/physmem.h (physmem_claimable): Fix typo in comment. + 2024-06-22 Bruno Haible <br...@clisp.org> c-xvasprintf: Guarantee a non-NULL result. diff --git a/lib/physmem.h b/lib/physmem.h index 542dbf214c..9c5438d016 100644 --- a/lib/physmem.h +++ b/lib/physmem.h @@ -39,7 +39,7 @@ double physmem_available (void); aggressivity. For AGGRESSIVITY == 0.0, the result is like physmem_available (): the amount of memory the application can use without hindering any other process. - For AGGRESSIVITY == 1,0, the result is the amount of memory the application + For AGGRESSIVITY == 1.0, the result is the amount of memory the application can use, while causing memory shortage to other processes, but without bringing the machine into an out-of-memory state. Values in between, for example AGGRESSIVITY == 0.5, are a reasonable middle -- 2.34.1
>From b216546a9d3e3fd6097d9a8ab3dd1e67a8a68434 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 22 Jun 2024 21:31:46 +0200 Subject: [PATCH 2/4] obstack-zprintf: New module. * lib/stdio.in.h (obstack_zprintf, obstack_vzprintf): New declarations. (obstack_printf, obstack_vprintf): Tweak comment. * lib/obstack_printf.c: Parameterize. (RESULT_TYPE, OBSTACK_PRINTF, OBSTACK_VPRINTF): New macros. * lib/obstack_zprintf.c: New file. * m4/stdio_h.m4 (gl_STDIO_H_REQUIRE_DEFAULTS): Initialize GNULIB_OBSTACK_ZPRINTF. * modules/stdio (Makefile.am): Substitute GNULIB_OBSTACK_ZPRINTF. * modules/obstack-zprintf: New file. --- ChangeLog | 13 +++++++++++++ lib/obstack_printf.c | 18 ++++++++++++------ lib/obstack_zprintf.c | 20 ++++++++++++++++++++ lib/stdio.in.h | 34 ++++++++++++++++++++++++++++++---- m4/stdio_h.m4 | 3 ++- modules/obstack-zprintf | 26 ++++++++++++++++++++++++++ modules/stdio | 1 + 7 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 lib/obstack_zprintf.c create mode 100644 modules/obstack-zprintf diff --git a/ChangeLog b/ChangeLog index 8d28459959..156dd40138 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2024-06-22 Bruno Haible <br...@clisp.org> + + obstack-zprintf: New module. + * lib/stdio.in.h (obstack_zprintf, obstack_vzprintf): New declarations. + (obstack_printf, obstack_vprintf): Tweak comment. + * lib/obstack_printf.c: Parameterize. + (RESULT_TYPE, OBSTACK_PRINTF, OBSTACK_VPRINTF): New macros. + * lib/obstack_zprintf.c: New file. + * m4/stdio_h.m4 (gl_STDIO_H_REQUIRE_DEFAULTS): Initialize + GNULIB_OBSTACK_ZPRINTF. + * modules/stdio (Makefile.am): Substitute GNULIB_OBSTACK_ZPRINTF. + * modules/obstack-zprintf: New file. + 2024-06-22 Bruno Haible <br...@clisp.org> physmem: Fix typo. diff --git a/lib/obstack_printf.c b/lib/obstack_printf.c index 57979e3509..ed996dd180 100644 --- a/lib/obstack_printf.c +++ b/lib/obstack_printf.c @@ -26,20 +26,26 @@ #include <stdarg.h> #include <stdlib.h> +#ifndef RESULT_TYPE +# define RESULT_TYPE int +# define OBSTACK_PRINTF obstack_printf +# define OBSTACK_VPRINTF obstack_vprintf +#endif + /* Grow an obstack with formatted output. Return the number of bytes added to OBS. No trailing nul byte is added, and the object should be closed with obstack_finish before use. Upon memory allocation error, call obstack_alloc_failed_handler. Upon other error, return -1. */ -int -obstack_printf (struct obstack *obs, const char *format, ...) +RESULT_TYPE +OBSTACK_PRINTF (struct obstack *obs, const char *format, ...) { va_list args; - int result; + RESULT_TYPE result; va_start (args, format); - result = obstack_vprintf (obs, format, args); + result = OBSTACK_VPRINTF (obs, format, args); va_end (args); return result; } @@ -50,8 +56,8 @@ obstack_printf (struct obstack *obs, const char *format, ...) Upon memory allocation error, call obstack_alloc_failed_handler. Upon other error, return -1. */ -int -obstack_vprintf (struct obstack *obs, const char *format, va_list args) +RESULT_TYPE +OBSTACK_VPRINTF (struct obstack *obs, const char *format, va_list args) { /* If we are close to the end of the current obstack chunk, use a stack-allocated buffer and copy, to reduce the likelihood of a diff --git a/lib/obstack_zprintf.c b/lib/obstack_zprintf.c new file mode 100644 index 0000000000..b802f4dc26 --- /dev/null +++ b/lib/obstack_zprintf.c @@ -0,0 +1,20 @@ +/* Formatted output to obstacks. + Copyright (C) 2008-2024 Free Software Foundation, Inc. + + This file 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 file 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/>. */ + +#define RESULT_TYPE ptrdiff_t +#define OBSTACK_PRINTF obstack_zprintf +#define OBSTACK_VPRINTF obstack_vzprintf +#include "obstack_printf.c" diff --git a/lib/stdio.in.h b/lib/stdio.in.h index 4844ea1ebf..cf2d8c999b 100644 --- a/lib/stdio.in.h +++ b/lib/stdio.in.h @@ -1075,13 +1075,39 @@ _GL_CXXALIASWARN (getw); # endif #endif +#if @GNULIB_OBSTACK_ZPRINTF@ +struct obstack; +/* Grows an obstack with formatted output. Returns the number of + bytes added to OBS. No trailing nul byte is added, and the + object should be closed with obstack_finish before use. + Upon memory allocation error, calls obstack_alloc_failed_handler. + Upon other error, returns -1 with errno set. + + Failure code EOVERFLOW can only occur when a width > INT_MAX is used. + Therefore, if the format string is valid and does not use %ls/%lc + directives nor widths, the only possible failure code is through + obstack_alloc_failed_handler. */ +_GL_FUNCDECL_SYS (obstack_zprintf, ptrdiff_t, + (struct obstack *obs, const char *format, ...) + _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD (2, 3) + _GL_ARG_NONNULL ((1, 2))); +_GL_CXXALIAS_SYS (obstack_zprintf, ptrdiff_t, + (struct obstack *obs, const char *format, ...)); +_GL_FUNCDECL_SYS (obstack_vzprintf, ptrdiff_t, + (struct obstack *obs, const char *format, va_list args) + _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD (2, 0) + _GL_ARG_NONNULL ((1, 2))); +_GL_CXXALIAS_SYS (obstack_vzprintf, ptrdiff_t, + (struct obstack *obs, const char *format, va_list args)); +#endif + #if @GNULIB_OBSTACK_PRINTF@ || @GNULIB_OBSTACK_PRINTF_POSIX@ struct obstack; -/* Grow an obstack with formatted output. Return the number of +/* Grows an obstack with formatted output. Returns the number of bytes added to OBS. No trailing nul byte is added, and the - object should be closed with obstack_finish before use. Upon - memory allocation error, call obstack_alloc_failed_handler. Upon - other error, return -1. */ + object should be closed with obstack_finish before use. + Upon memory allocation error, calls obstack_alloc_failed_handler. + Upon other error, returns -1. */ # if @REPLACE_OBSTACK_PRINTF@ # if !(defined __cplusplus && defined GNULIB_NAMESPACE) # define obstack_printf rpl_obstack_printf diff --git a/m4/stdio_h.m4 b/m4/stdio_h.m4 index aa06d77027..10e1fbb8aa 100644 --- a/m4/stdio_h.m4 +++ b/m4/stdio_h.m4 @@ -1,5 +1,5 @@ # stdio_h.m4 -# serial 68 +# serial 69 dnl Copyright (C) 2007-2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -159,6 +159,7 @@ AC_DEFUN([gl_STDIO_H_REQUIRE_DEFAULTS] gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_GETLINE]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_OBSTACK_PRINTF]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_OBSTACK_PRINTF_POSIX]) + gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_OBSTACK_ZPRINTF]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_PCLOSE]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_PERROR]) gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_POPEN]) diff --git a/modules/obstack-zprintf b/modules/obstack-zprintf new file mode 100644 index 0000000000..d78196f4d5 --- /dev/null +++ b/modules/obstack-zprintf @@ -0,0 +1,26 @@ +Description: +Formatted printing into an obstack (without INT_MAX limitation). + +Files: +lib/obstack_zprintf.c +lib/obstack_printf.c + +Depends-on: +obstack +stdio +vasnprintf + +configure.ac: +gl_STDIO_MODULE_INDICATOR([obstack-zprintf]) + +Makefile.am: +lib_SOURCES += obstack_zprintf.c + +Include: +<stdio.h> + +License: +GPL + +Maintainer: +all diff --git a/modules/stdio b/modules/stdio index b0cf4ea207..64fdc5fb60 100644 --- a/modules/stdio +++ b/modules/stdio @@ -94,6 +94,7 @@ stdio.h: stdio.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e 's/@''GNULIB_GETLINE''@/$(GNULIB_GETLINE)/g' \ -e 's/@''GNULIB_OBSTACK_PRINTF''@/$(GNULIB_OBSTACK_PRINTF)/g' \ -e 's/@''GNULIB_OBSTACK_PRINTF_POSIX''@/$(GNULIB_OBSTACK_PRINTF_POSIX)/g' \ + -e 's/@''GNULIB_OBSTACK_ZPRINTF''@/$(GNULIB_OBSTACK_ZPRINTF)/g' \ -e 's/@''GNULIB_PCLOSE''@/$(GNULIB_PCLOSE)/g' \ -e 's/@''GNULIB_PERROR''@/$(GNULIB_PERROR)/g' \ -e 's/@''GNULIB_POPEN''@/$(GNULIB_POPEN)/g' \ -- 2.34.1
>From 94c5a52ccbef8fbc3d1af56d12e120e51b4ca75f Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 22 Jun 2024 21:59:50 +0200 Subject: [PATCH 3/4] obstack-zprintf: Add tests. * tests/test-obstack-printf.h: New file, based on tests/test-obstack-printf.c. * tests/test-obstack-printf.c: Include test-obstack-printf.h. (obstack_chunk_alloc, obstack_chunk_free, test_function): Moved to tests/test-obstack-printf.h. (test_obstack_vprintf, test_obstack_printf): Remove functions. (main): Inline them here. * tests/test-obstack-zprintf.c: New file, based on tests/test-obstack-printf.c. * modules/obstack-printf-tests (Files): Add tests/test-obstack-printf.h. * modules/obstack-zprintf-tests: New file. --- ChangeLog | 13 +++++ modules/obstack-printf-tests | 1 + modules/obstack-zprintf-tests | 15 ++++++ tests/test-obstack-printf.c | 90 +++-------------------------------- tests/test-obstack-printf.h | 85 +++++++++++++++++++++++++++++++++ tests/test-obstack-zprintf.c | 58 ++++++++++++++++++++++ 6 files changed, 179 insertions(+), 83 deletions(-) create mode 100644 modules/obstack-zprintf-tests create mode 100644 tests/test-obstack-printf.h create mode 100644 tests/test-obstack-zprintf.c diff --git a/ChangeLog b/ChangeLog index 156dd40138..96f1e397da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,18 @@ 2024-06-22 Bruno Haible <br...@clisp.org> + obstack-zprintf: Add tests. + * tests/test-obstack-printf.h: New file, based on + tests/test-obstack-printf.c. + * tests/test-obstack-printf.c: Include test-obstack-printf.h. + (obstack_chunk_alloc, obstack_chunk_free, test_function): Moved to + tests/test-obstack-printf.h. + (test_obstack_vprintf, test_obstack_printf): Remove functions. + (main): Inline them here. + * tests/test-obstack-zprintf.c: New file, based on + tests/test-obstack-printf.c. + * modules/obstack-printf-tests (Files): Add tests/test-obstack-printf.h. + * modules/obstack-zprintf-tests: New file. + obstack-zprintf: New module. * lib/stdio.in.h (obstack_zprintf, obstack_vzprintf): New declarations. (obstack_printf, obstack_vprintf): Tweak comment. diff --git a/modules/obstack-printf-tests b/modules/obstack-printf-tests index 2790e6942b..d319a79d20 100644 --- a/modules/obstack-printf-tests +++ b/modules/obstack-printf-tests @@ -1,5 +1,6 @@ Files: tests/test-obstack-printf.c +tests/test-obstack-printf.h tests/signature.h tests/macros.h diff --git a/modules/obstack-zprintf-tests b/modules/obstack-zprintf-tests new file mode 100644 index 0000000000..8ee697934f --- /dev/null +++ b/modules/obstack-zprintf-tests @@ -0,0 +1,15 @@ +Files: +tests/test-obstack-zprintf.c +tests/test-obstack-printf.h +tests/signature.h +tests/macros.h + +Depends-on: +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-obstack-zprintf +check_PROGRAMS += test-obstack-zprintf +test_obstack_zprintf_LDADD = $(LDADD) @LIBINTL@ diff --git a/tests/test-obstack-printf.c b/tests/test-obstack-printf.c index 2c7694320b..c2048c5cee 100644 --- a/tests/test-obstack-printf.c +++ b/tests/test-obstack-printf.c @@ -18,12 +18,13 @@ #include <config.h> +/* Specification. */ #include <stdio.h> #include "signature.h" SIGNATURE_CHECK (obstack_printf, int, (struct obstack *, char const *, ...)); -SIGNATURE_CHECK (obstack_vprintf, int, (struct obstack *, char const *, - va_list)); +SIGNATURE_CHECK (obstack_vprintf, int, + (struct obstack *, char const *, va_list)); #include "obstack.h" #include "xalloc.h" @@ -34,73 +35,8 @@ SIGNATURE_CHECK (obstack_vprintf, int, (struct obstack *, char const *, #include "macros.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free - -static void -test_function (int (*my_obstack_printf) (struct obstack *, const char *, ...)) -{ - struct obstack obs; - obstack_init (&obs); - /* In general, be careful that arguments to obstack_* don't have - side effects, as not all compilers evaluate macro arguments only - once. */ - - /* Grow the obstack to near its boundary, then check that short - output longer than the obstack free space grows the obstack. */ - { - char *base = obstack_base (&obs); - char *new_base; - int result; - int room = obstack_room (&obs) - 4; - - obstack_blank_fast (&obs, room); - result = my_obstack_printf (&obs, "%d %s", 123, "456"); - ASSERT (result == 7); - ASSERT (result + room == obstack_object_size (&obs)); - obstack_1grow (&obs, 0); - new_base = obstack_finish (&obs); - ASSERT (base != new_base); - ASSERT (strcmp (new_base + room, "123 456") == 0); - } - - /* Check that strings shorter than the obstack free space don't - cause a reshuffling of the obstack. */ - { - char *base = obstack_base (&obs); - char *new_base; - int result; - int room = obstack_room (&obs); - - ASSERT (8 < room); - result = my_obstack_printf (&obs, "%d %s", 123, "456"); - ASSERT (result == 7); - ASSERT (result == obstack_object_size (&obs)); - new_base = obstack_base (&obs); - ASSERT (base == new_base); - ASSERT (strncmp (base, "123 456", result) == 0); - obstack_finish (&obs); - } - - /* Check for generating much more output than a chunk size. */ - { - char *base = obstack_base (&obs); - char *new_base; - int result; - int i; - - ASSERT (obstack_chunk_size (&obs) < 10000); - result = my_obstack_printf (&obs, "%010000d", 0); - ASSERT (result == 10000); - ASSERT (result == obstack_object_size (&obs)); - new_base = obstack_base (&obs); - ASSERT (base != new_base); - for (i = 0; i < 10000; i++) - ASSERT (new_base[i] == '0'); - } - - obstack_free (&obs, NULL); -} +#define RETTYPE int +#include "test-obstack-printf.h" static int my_obstack_printf (struct obstack *obs, const char *format, ...) @@ -114,22 +50,10 @@ my_obstack_printf (struct obstack *obs, const char *format, ...) return ret; } -static void -test_obstack_vprintf () -{ - test_function (my_obstack_printf); -} - -static void -test_obstack_printf () -{ - test_function (obstack_printf); -} - int main (int argc, char *argv[]) { - test_obstack_vprintf (); - test_obstack_printf (); + test_function (my_obstack_printf); + test_function (obstack_printf); return test_exit_status; } diff --git a/tests/test-obstack-printf.h b/tests/test-obstack-printf.h new file mode 100644 index 0000000000..560decf2ee --- /dev/null +++ b/tests/test-obstack-printf.h @@ -0,0 +1,85 @@ +/* Test of obstack_[v][z]printf() functions. + Copyright (C) 2008-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 Eric Blake <e...@byu.net>, 2008. */ + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +static void +test_function (RETTYPE (*my_obstack_printf) (struct obstack *, const char *, ...)) +{ + struct obstack obs; + obstack_init (&obs); + /* In general, be careful that arguments to obstack_* don't have + side effects, as not all compilers evaluate macro arguments only + once. */ + + /* Grow the obstack to near its boundary, then check that short + output longer than the obstack free space grows the obstack. */ + { + char *base = obstack_base (&obs); + char *new_base; + RETTYPE result; + int room = obstack_room (&obs) - 4; + + obstack_blank_fast (&obs, room); + result = my_obstack_printf (&obs, "%d %s", 123, "456"); + ASSERT (result == 7); + ASSERT (result + room == obstack_object_size (&obs)); + obstack_1grow (&obs, 0); + new_base = obstack_finish (&obs); + ASSERT (base != new_base); + ASSERT (strcmp (new_base + room, "123 456") == 0); + } + + /* Check that strings shorter than the obstack free space don't + cause a reshuffling of the obstack. */ + { + char *base = obstack_base (&obs); + char *new_base; + RETTYPE result; + int room = obstack_room (&obs); + + ASSERT (8 < room); + result = my_obstack_printf (&obs, "%d %s", 123, "456"); + ASSERT (result == 7); + ASSERT (result == obstack_object_size (&obs)); + new_base = obstack_base (&obs); + ASSERT (base == new_base); + ASSERT (strncmp (base, "123 456", result) == 0); + obstack_finish (&obs); + } + + /* Check for generating much more output than a chunk size. */ + { + char *base = obstack_base (&obs); + char *new_base; + RETTYPE result; + int i; + + ASSERT (obstack_chunk_size (&obs) < 10000); + result = my_obstack_printf (&obs, "%010000d", 0); + ASSERT (result == 10000); + ASSERT (result == obstack_object_size (&obs)); + new_base = obstack_base (&obs); + ASSERT (base != new_base); + for (i = 0; i < 10000; i++) + ASSERT (new_base[i] == '0'); + } + + obstack_free (&obs, NULL); +} diff --git a/tests/test-obstack-zprintf.c b/tests/test-obstack-zprintf.c new file mode 100644 index 0000000000..143424604e --- /dev/null +++ b/tests/test-obstack-zprintf.c @@ -0,0 +1,58 @@ +/* Test of obstack_zprintf() and obstack_vzprintf() functions. + Copyright (C) 2008-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/>. */ + +#include <config.h> + +/* Specification. */ +#include <stdio.h> + +#include "signature.h" +SIGNATURE_CHECK (obstack_zprintf, ptrdiff_t, + (struct obstack *, char const *, ...)); +SIGNATURE_CHECK (obstack_vzprintf, ptrdiff_t, + (struct obstack *, char const *, va_list)); + +#include "obstack.h" +#include "xalloc.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "macros.h" + +#define RETTYPE ptrdiff_t +#include "test-obstack-printf.h" + +static ptrdiff_t +my_obstack_zprintf (struct obstack *obs, const char *format, ...) +{ + va_list args; + ptrdiff_t ret; + + va_start (args, format); + ret = obstack_vzprintf (obs, format, args); + va_end (args); + return ret; +} + +int +main (int argc, char *argv[]) +{ + test_function (my_obstack_zprintf); + test_function (obstack_zprintf); + return test_exit_status; +} -- 2.34.1
>From 32871f4b576c5f50fbee8d7e6d5125037e428eed Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sat, 22 Jun 2024 22:02:53 +0200 Subject: [PATCH 4/4] obstack-zprintf: Add more tests. * tests/test-obstack-zprintf-big.c: New file, based on tests/test-vasnprintf-big.c. * modules/obstack-zprintf-extra-tests: New file. * modules/obstack-zprintf-tests (Depends-on): Add it. --- ChangeLog | 6 + modules/obstack-zprintf-extra-tests | 17 +++ modules/obstack-zprintf-tests | 1 + tests/test-obstack-zprintf-big.c | 210 ++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 modules/obstack-zprintf-extra-tests create mode 100644 tests/test-obstack-zprintf-big.c diff --git a/ChangeLog b/ChangeLog index 96f1e397da..bf82a188fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2024-06-22 Bruno Haible <br...@clisp.org> + obstack-zprintf: Add more tests. + * tests/test-obstack-zprintf-big.c: New file, based on + tests/test-vasnprintf-big.c. + * modules/obstack-zprintf-extra-tests: New file. + * modules/obstack-zprintf-tests (Depends-on): Add it. + obstack-zprintf: Add tests. * tests/test-obstack-printf.h: New file, based on tests/test-obstack-printf.c. diff --git a/modules/obstack-zprintf-extra-tests b/modules/obstack-zprintf-extra-tests new file mode 100644 index 0000000000..65f7b7096e --- /dev/null +++ b/modules/obstack-zprintf-extra-tests @@ -0,0 +1,17 @@ +Status: +longrunning-test + +Files: +tests/test-obstack-zprintf-big.c +tests/macros.h + +Depends-on: +physmem + +configure.ac: +AC_CHECK_FUNCS_ONCE([setrlimit]) + +Makefile.am: +TESTS += test-obstack-zprintf-big +check_PROGRAMS += test-obstack-zprintf-big +test_obstack_zprintf_big_LDADD = $(LDADD) @LIBINTL@ diff --git a/modules/obstack-zprintf-tests b/modules/obstack-zprintf-tests index 8ee697934f..99187eb87c 100644 --- a/modules/obstack-zprintf-tests +++ b/modules/obstack-zprintf-tests @@ -6,6 +6,7 @@ tests/macros.h Depends-on: xalloc +obstack-zprintf-extra-tests configure.ac: diff --git a/tests/test-obstack-zprintf-big.c b/tests/test-obstack-zprintf-big.c new file mode 100644 index 0000000000..7e66aac9cd --- /dev/null +++ b/tests/test-obstack-zprintf-big.c @@ -0,0 +1,210 @@ +/* Test of obstack_[v]zprintf() 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 <stdio.h> + +#include "obstack.h" +#include "physmem.h" + +/* Get INT_MAX. */ +#include <limits.h> + +/* Get PTRDIFF_MAX. */ +#include <stdint.h> + +#include <errno.h> +#include <string.h> + +#if HAVE_SETRLIMIT +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#include "macros.h" + +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + +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 15 GiB of memory: + $ time /usr/bin/time -f "Max RSS: %M KiB" ./test-obstack-zprintf-big + Max RSS: 15730184 KiB + real 0m38,356s + user 0m29,715s + sys 0m8,640s + 5 GiB for the inputs and up to 10 GiB for temporary output buffers. */ + double needed = 15.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 16777216". */ + + struct obstack obs; + obstack_init (&obs); + + /* Verify that obstack_zprintf() 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'; + + ptrdiff_t len = obstack_zprintf (&obs, "x%sy%sz", s1, s2); + if (len < 0) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + char *s = obstack_finish (&obs); + 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'); + obstack_free (&obs, s); + } + free (s2); + } + free (s1); + } + } + + /* Verify that obstack_zprintf() 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'; + + ptrdiff_t len = obstack_zprintf (&obs, "x%sy", s1); + if (len < 0) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + char *s = obstack_finish (&obs); + 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'); + obstack_free (&obs, s); + } + free (s1); + } + } + + /* Verify that obstack_zprintf() 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'; + + ptrdiff_t len = obstack_zprintf (&obs, "x%sy", s1); + if (len < 0) + { + ASSERT (errno == ENOMEM); + skipped = true; + } + else + { + char *s = obstack_finish (&obs); + 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'); + obstack_free (&obs, s); + } + free (s1); + } + } + } + + obstack_free (&obs, NULL); + } + 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