summaryrefslogtreecommitdiffstats
path: root/lib/malloca.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/malloca.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/lib/malloca.c b/lib/malloca.c
new file mode 100644
index 0000000..f055b1e
--- /dev/null
+++ b/lib/malloca.c
@@ -0,0 +1,112 @@
+/* Safe automatic memory allocation.
+ Copyright (C) 2003, 2006-2007, 2009-2023 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003, 2018.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#define _GL_USE_STDLIB_ALLOC 1
+#include <config.h>
+
+/* Specification. */
+#include "malloca.h"
+
+#include <stdckdint.h>
+
+#include "idx.h"
+
+/* The speed critical point in this file is freea() applied to an alloca()
+ result: it must be fast, to match the speed of alloca(). The speed of
+ mmalloca() and freea() in the other case are not critical, because they
+ are only invoked for big memory sizes.
+ Here we use a bit in the address as an indicator, an idea by Ondřej Bílka.
+ malloca() can return three types of pointers:
+ - Pointers ≡ 0 mod 2*sa_alignment_max come from stack allocation.
+ - Pointers ≡ sa_alignment_max mod 2*sa_alignment_max come from heap
+ allocation.
+ - NULL comes from a failed heap allocation. */
+
+/* Type for holding very small pointer differences. */
+typedef unsigned char small_t;
+/* Verify that it is wide enough. */
+static_assert (2 * sa_alignment_max - 1 <= (small_t) -1);
+
+void *
+mmalloca (size_t n)
+{
+#if HAVE_ALLOCA
+ /* Allocate one more word, used to determine the address to pass to freea(),
+ and room for the alignment ≡ sa_alignment_max mod 2*sa_alignment_max. */
+ uintptr_t alignment2_mask = 2 * sa_alignment_max - 1;
+ int plus = sizeof (small_t) + alignment2_mask;
+ idx_t nplus;
+ if (!ckd_add (&nplus, n, plus) && !xalloc_oversized (nplus, 1))
+ {
+ char *mem = (char *) malloc (nplus);
+
+ if (mem != NULL)
+ {
+ uintptr_t umem = (uintptr_t)mem, umemplus;
+ /* The ckd_add avoids signed integer overflow on
+ theoretical platforms where UINTPTR_MAX <= INT_MAX. */
+ ckd_add (&umemplus, umem, sizeof (small_t) + sa_alignment_max - 1);
+ idx_t offset = ((umemplus & ~alignment2_mask)
+ + sa_alignment_max - umem);
+ void *vp = mem + offset;
+ small_t *p = vp;
+ /* Here p >= mem + sizeof (small_t),
+ and p <= mem + sizeof (small_t) + 2 * sa_alignment_max - 1
+ hence p + n <= mem + nplus.
+ So, the memory range [p, p+n) lies in the allocated memory range
+ [mem, mem + nplus). */
+ p[-1] = offset;
+ /* p ≡ sa_alignment_max mod 2*sa_alignment_max. */
+ return p;
+ }
+ }
+ /* Out of memory. */
+ return NULL;
+#else
+# if !MALLOC_0_IS_NONNULL
+ if (n == 0)
+ n = 1;
+# endif
+ return malloc (n);
+#endif
+}
+
+#if HAVE_ALLOCA
+void
+freea (void *p)
+{
+ /* Check argument. */
+ if ((uintptr_t) p & (sa_alignment_max - 1))
+ {
+ /* p was not the result of a malloca() call. Invalid argument. */
+ abort ();
+ }
+ /* Determine whether p was a non-NULL pointer returned by mmalloca(). */
+ if ((uintptr_t) p & sa_alignment_max)
+ {
+ void *mem = (char *) p - ((small_t *) p)[-1];
+ free (mem);
+ }
+}
+#endif
+
+/*
+ * Hey Emacs!
+ * Local Variables:
+ * coding: utf-8
+ * End:
+ */