summaryrefslogtreecommitdiff
path: root/tests/xmalloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/xmalloc.c')
-rw-r--r--tests/xmalloc.c301
1 files changed, 259 insertions, 42 deletions
diff --git a/tests/xmalloc.c b/tests/xmalloc.c
index 9a30d67b..7df74df5 100644
--- a/tests/xmalloc.c
+++ b/tests/xmalloc.c
@@ -1,6 +1,6 @@
/* xmalloc.c -- malloc with out of memory checking
- Copyright (C) 1990-2000, 2002-2006, 2008-2018 Free Software Foundation, Inc.
+ Copyright (C) 1990-2000, 2002-2006, 2008-2022 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
@@ -21,59 +21,255 @@
#include "xalloc.h"
+#include "ialloc.h"
+#include "intprops.h"
+#include "minmax.h"
+
#include <stdlib.h>
#include <string.h>
-/* 1 if calloc is known to be compatible with GNU calloc. This
- matters if we are not also using the calloc module, which defines
- HAVE_CALLOC_GNU and supports the GNU API even on non-GNU platforms. */
-#if defined HAVE_CALLOC_GNU || (defined __GLIBC__ && !defined __UCLIBC__)
-enum { HAVE_GNU_CALLOC = 1 };
-#else
-enum { HAVE_GNU_CALLOC = 0 };
-#endif
+static void * _GL_ATTRIBUTE_PURE
+nonnull (void *p)
+{
+ if (!p)
+ xalloc_die ();
+ return p;
+}
-/* Allocate N bytes of memory dynamically, with error checking. */
+/* Allocate S bytes of memory dynamically, with error checking. */
void *
-xmalloc (size_t n)
+xmalloc (size_t s)
{
- void *p = malloc (n);
- if (!p && n != 0)
- xalloc_die ();
- return p;
+ return nonnull (malloc (s));
+}
+
+void *
+ximalloc (idx_t s)
+{
+ return nonnull (imalloc (s));
}
-/* Change the size of an allocated block of memory P to N bytes,
+char *
+xcharalloc (size_t n)
+{
+ return XNMALLOC (n, char);
+}
+
+/* Change the size of an allocated block of memory P to S bytes,
with error checking. */
void *
-xrealloc (void *p, size_t n)
+xrealloc (void *p, size_t s)
+{
+ void *r = realloc (p, s);
+ if (!r && (!p || s))
+ xalloc_die ();
+ return r;
+}
+
+void *
+xirealloc (void *p, idx_t s)
+{
+ return nonnull (irealloc (p, s));
+}
+
+/* Change the size of an allocated block of memory P to an array of N
+ objects each of S bytes, with error checking. */
+
+void *
+xreallocarray (void *p, size_t n, size_t s)
+{
+ void *r = reallocarray (p, n, s);
+ if (!r && (!p || (n && s)))
+ xalloc_die ();
+ return r;
+}
+
+void *
+xireallocarray (void *p, idx_t n, idx_t s)
+{
+ return nonnull (ireallocarray (p, n, s));
+}
+
+/* Allocate an array of N objects, each with S bytes of memory,
+ dynamically, with error checking. S must be nonzero. */
+
+void *
+xnmalloc (size_t n, size_t s)
+{
+ return xreallocarray (NULL, n, s);
+}
+
+void *
+xinmalloc (idx_t n, idx_t s)
+{
+ return xireallocarray (NULL, n, s);
+}
+
+/* If P is null, allocate a block of at least *PS bytes; otherwise,
+ reallocate P so that it contains more than *PS bytes. *PS must be
+ nonzero unless P is null. Set *PS to the new block's size, and
+ return the pointer to the new block. *PS is never set to zero, and
+ the returned pointer is never null. */
+
+void *
+x2realloc (void *p, size_t *ps)
+{
+ return x2nrealloc (p, ps, 1);
+}
+
+/* If P is null, allocate a block of at least *PN such objects;
+ otherwise, reallocate P so that it contains more than *PN objects
+ each of S bytes. S must be nonzero. Set *PN to the new number of
+ objects, and return the pointer to the new block. *PN is never set
+ to zero, and the returned pointer is never null.
+
+ Repeated reallocations are guaranteed to make progress, either by
+ allocating an initial block with a nonzero size, or by allocating a
+ larger block.
+
+ In the following implementation, nonzero sizes are increased by a
+ factor of approximately 1.5 so that repeated reallocations have
+ O(N) overall cost rather than O(N**2) cost, but the
+ specification for this function does not guarantee that rate.
+
+ Here is an example of use:
+
+ int *p = NULL;
+ size_t used = 0;
+ size_t allocated = 0;
+
+ void
+ append_int (int value)
+ {
+ if (used == allocated)
+ p = x2nrealloc (p, &allocated, sizeof *p);
+ p[used++] = value;
+ }
+
+ This causes x2nrealloc to allocate a block of some nonzero size the
+ first time it is called.
+
+ To have finer-grained control over the initial size, set *PN to a
+ nonzero value before calling this function with P == NULL. For
+ example:
+
+ int *p = NULL;
+ size_t used = 0;
+ size_t allocated = 0;
+ size_t allocated1 = 1000;
+
+ void
+ append_int (int value)
+ {
+ if (used == allocated)
+ {
+ p = x2nrealloc (p, &allocated1, sizeof *p);
+ allocated = allocated1;
+ }
+ p[used++] = value;
+ }
+
+ */
+
+void *
+x2nrealloc (void *p, size_t *pn, size_t s)
{
- if (!n && p)
+ size_t n = *pn;
+
+ if (! p)
{
- /* The GNU and C99 realloc behaviors disagree here. Act like
- GNU, even if the underlying realloc is C99. */
- free (p);
- return NULL;
+ if (! n)
+ {
+ /* The approximate size to use for initial small allocation
+ requests, when the invoking code specifies an old size of
+ zero. This is the largest "small" request for the GNU C
+ library malloc. */
+ enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
+
+ n = DEFAULT_MXFAST / s;
+ n += !n;
+ }
+ }
+ else
+ {
+ /* Set N = floor (1.5 * N) + 1 to make progress even if N == 0. */
+ if (INT_ADD_WRAPV (n, (n >> 1) + 1, &n))
+ xalloc_die ();
}
- p = realloc (p, n);
- if (!p && n)
- xalloc_die ();
+ p = xreallocarray (p, n, s);
+ *pn = n;
return p;
}
-/* If P is null, allocate a block of at least *PN bytes; otherwise,
- reallocate P so that it contains more than *PN bytes. *PN must be
- nonzero unless P is null. Set *PN to the new block's size, and
- return the pointer to the new block. *PN is never set to zero, and
- the returned pointer is never null. */
+/* Grow PA, which points to an array of *PN items, and return the
+ location of the reallocated array, updating *PN to reflect its
+ new size. The new array will contain at least N_INCR_MIN more
+ items, but will not contain more than N_MAX items total.
+ S is the size of each item, in bytes.
+
+ S and N_INCR_MIN must be positive. *PN must be
+ nonnegative. If N_MAX is -1, it is treated as if it were
+ infinity.
+
+ If PA is null, then allocate a new array instead of reallocating
+ the old one.
+
+ Thus, to grow an array A without saving its old contents, do
+ { free (A); A = xpalloc (NULL, &AITEMS, ...); }. */
void *
-x2realloc (void *p, size_t *pn)
+xpalloc (void *pa, idx_t *pn, idx_t n_incr_min, ptrdiff_t n_max, idx_t s)
{
- return x2nrealloc (p, pn, 1);
+ idx_t n0 = *pn;
+
+ /* The approximate size to use for initial small allocation
+ requests. This is the largest "small" request for the GNU C
+ library malloc. */
+ enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
+
+ /* If the array is tiny, grow it to about (but no greater than)
+ DEFAULT_MXFAST bytes. Otherwise, grow it by about 50%.
+ Adjust the growth according to three constraints: N_INCR_MIN,
+ N_MAX, and what the C language can represent safely. */
+
+ idx_t n;
+ if (INT_ADD_WRAPV (n0, n0 >> 1, &n))
+ n = IDX_MAX;
+ if (0 <= n_max && n_max < n)
+ n = n_max;
+
+ /* NBYTES is of a type suitable for holding the count of bytes in an object.
+ This is typically idx_t, but it should be size_t on (theoretical?)
+ platforms where SIZE_MAX < IDX_MAX so xpalloc does not pass
+ values greater than SIZE_MAX to xrealloc. */
+#if IDX_MAX <= SIZE_MAX
+ idx_t nbytes;
+#else
+ size_t nbytes;
+#endif
+ idx_t adjusted_nbytes
+ = (INT_MULTIPLY_WRAPV (n, s, &nbytes)
+ ? MIN (IDX_MAX, SIZE_MAX)
+ : nbytes < DEFAULT_MXFAST ? DEFAULT_MXFAST : 0);
+ if (adjusted_nbytes)
+ {
+ n = adjusted_nbytes / s;
+ nbytes = adjusted_nbytes - adjusted_nbytes % s;
+ }
+
+ if (! pa)
+ *pn = 0;
+ if (n - n0 < n_incr_min
+ && (INT_ADD_WRAPV (n0, n_incr_min, &n)
+ || (0 <= n_max && n_max < n)
+ || INT_MULTIPLY_WRAPV (n, s, &nbytes)))
+ xalloc_die ();
+ pa = xrealloc (pa, nbytes);
+ *pn = n;
+ return pa;
}
/* Allocate S bytes of zeroed memory dynamically, with error checking.
@@ -83,7 +279,13 @@ x2realloc (void *p, size_t *pn)
void *
xzalloc (size_t s)
{
- return memset (xmalloc (s), 0, s);
+ return xcalloc (s, 1);
+}
+
+void *
+xizalloc (idx_t s)
+{
+ return xicalloc (s, 1);
}
/* Allocate zeroed memory for N elements of S bytes, with error
@@ -92,15 +294,13 @@ xzalloc (size_t s)
void *
xcalloc (size_t n, size_t s)
{
- void *p;
- /* Test for overflow, since objects with size greater than
- PTRDIFF_MAX cause pointer subtraction to go awry. Omit size-zero
- tests if HAVE_GNU_CALLOC, since GNU calloc never returns NULL if
- successful. */
- if (xalloc_oversized (n, s)
- || (! (p = calloc (n, s)) && (HAVE_GNU_CALLOC || n != 0)))
- xalloc_die ();
- return p;
+ return nonnull (calloc (n, s));
+}
+
+void *
+xicalloc (idx_t n, idx_t s)
+{
+ return nonnull (icalloc (n, s));
}
/* Clone an object P of size S, with error checking. There's no need
@@ -113,6 +313,23 @@ xmemdup (void const *p, size_t s)
return memcpy (xmalloc (s), p, s);
}
+void *
+ximemdup (void const *p, idx_t s)
+{
+ return memcpy (ximalloc (s), p, s);
+}
+
+/* Clone an object P of size S, with error checking. Append
+ a terminating NUL byte. */
+
+char *
+ximemdup0 (void const *p, idx_t s)
+{
+ char *result = ximalloc (s + 1);
+ result[s] = 0;
+ return memcpy (result, p, s);
+}
+
/* Clone STRING. */
char *