summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--doc/glibc-functions/memalign.texi3
-rw-r--r--doc/posix-functions/posix_memalign.texi7
-rw-r--r--lib/aligned-malloc.h140
-rw-r--r--m4/malloc-align.m4140
-rw-r--r--modules/aligned-malloc25
6 files changed, 322 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 654ad7c1ff..768b209b65 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
2020-07-21 Bruno Haible <bruno@clisp.org>
+ aligned-malloc: New module.
+ * lib/aligned-malloc.h: New file.
+ * m4/malloc-align.m4: New file.
+ * modules/aligned-malloc: New file.
+ * doc/posix-functions/posix_memalign.texi: Mention the new module.
+ * doc/glibc-functions/memalign.texi: Likewise.
+
+2020-07-21 Bruno Haible <bruno@clisp.org>
+
inttypes: Fix PRI*PTR and SCN*PTR on 64-bit native Windows.
* m4/inttypes.m4 (gl_INTTYPES_PRI_SCN): On 64-bit native Windows, make
sure PRIPTR_PREFIX is defined to "ll", not "l".
diff --git a/doc/glibc-functions/memalign.texi b/doc/glibc-functions/memalign.texi
index a7e9715736..0740f7373b 100644
--- a/doc/glibc-functions/memalign.texi
+++ b/doc/glibc-functions/memalign.texi
@@ -27,3 +27,6 @@ Portability problems not fixed by Gnulib:
This function is missing on some platforms:
Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11.00, mingw, MSVC 14.
@end itemize
+
+The Gnulib module @code{aligned-malloc} provides functions for
+allocating and freeing blocks of suitably aligned memory.
diff --git a/doc/posix-functions/posix_memalign.texi b/doc/posix-functions/posix_memalign.texi
index 53abc86291..084841bd9a 100644
--- a/doc/posix-functions/posix_memalign.texi
+++ b/doc/posix-functions/posix_memalign.texi
@@ -17,5 +17,8 @@ This function is missing on some platforms:
Mac OS X 10.5, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 10, Cygwin 1.5.x, mingw, MSVC 14, Android 4.1.
@end itemize
-The Gnulib module @code{pagealign_alloc} provides a similar API
-that returns memory aligned on a system page boundary.
+The Gnulib module @code{aligned-malloc} provides functions for
+allocating and freeing blocks of suitably aligned memory.
+
+The Gnulib module @code{pagealign_alloc} provides a similar API for
+allocating and freeing blocks of memory aligned on a system page boundary.
diff --git a/lib/aligned-malloc.h b/lib/aligned-malloc.h
new file mode 100644
index 0000000000..86382fd25b
--- /dev/null
+++ b/lib/aligned-malloc.h
@@ -0,0 +1,140 @@
+/* Allocate memory with indefinite extent and specified alignment.
+
+ Copyright (C) 2020 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 <bruno@clisp.org>, 2020. */
+
+/* Before including this file, you need to define the following macro:
+
+ ALIGNMENT A constant expression that evaluates to the desired alignment
+ (a power of 2).
+
+ And you also need to #include <stdint.h> and <stdlib.h>. */
+
+/* aligned_malloc allocates a block of memory of SIZE bytes, aligned on a
+ boundary of ALIGNMENT bytes.
+ The block can be freed through aligned_free(), NOT through free().
+ Upon failure, it returns NULL. */
+
+/* This module exists instead of a posix_memalign() or memalign() emulation,
+ because we can't reasonably emulate posix_memalign() or memalign():
+ If malloc() returned p, only free (p) is allowed, not free (p + 1),
+ free (p + 2), free (p + 4), free (p + 8), or similar.
+
+ We can use posix_memalign(). On older systems, we can alternatively use
+ memalign() instead. In the Solaris documentation of memalign() it is not
+ specified how a memory block returned by memalign() can be freed, but
+ it actually can be freed with free(). */
+
+#if ((ALIGNMENT) <= MALLOC_ALIGNMENT) || HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN
+
+# if (ALIGNMENT) <= MALLOC_ALIGNMENT
+/* Simply use malloc. */
+
+# ifdef aligned_malloc
+ /* The caller wants an inline function, not a macro. */
+static inline void *
+aligned_malloc (size_t size)
+{
+ return malloc (size);
+}
+# else
+# define aligned_malloc malloc
+# endif
+
+# elif HAVE_POSIX_MEMALIGN
+/* Use posix_memalign.
+ This is OK since ALIGNMENT > MALLOC_ALIGNMENT >= sizeof (void *). */
+
+static inline void *
+aligned_malloc (size_t size)
+{
+ void *p;
+ int ret = posix_memalign (&p, (ALIGNMENT), size);
+ if (ret == 0)
+ return p;
+ else
+ return NULL;
+}
+
+# elif HAVE_MEMALIGN /* HP-UX, IRIX, Solaris <= 10 */
+/* Use memalign. */
+
+static inline void *
+aligned_malloc (size_t size)
+{
+ return memalign ((ALIGNMENT), size);
+}
+
+# endif
+
+# ifdef aligned_free
+ /* The caller wants an inline function, not a macro. */
+static inline void
+aligned_free (void *q)
+{
+ free (q);
+}
+# else
+# define aligned_free free
+# endif
+
+#else
+/* Use malloc and waste a bit of memory. */
+
+static inline void *
+aligned_malloc (size_t size)
+{
+ size += (ALIGNMENT);
+ if (size >= (ALIGNMENT)) /* no overflow? */
+ {
+ void *p = malloc (size);
+ if (p != NULL)
+ {
+ /* Go to the next multiple of ALIGNMENT. */
+ void *q =
+ (void *) (((uintptr_t) p + (ALIGNMENT)) & -(intptr_t)(ALIGNMENT));
+ /* Now q - p <= ALIGNMENT and
+ q - p >= MALLOC_ALIGNMENT >= sizeof (void *).
+ This is enough to store a back pointer to p. */
+ ((void **) q)[-1] = p;
+ return q;
+ }
+ }
+ return NULL;
+}
+
+static inline void
+aligned_free (void *q)
+{
+ if (q != NULL)
+ {
+ if ((uintptr_t) q & ((ALIGNMENT) - 1))
+ /* Argument not aligned as expected. */
+ abort ();
+ else
+ {
+ void *p = ((void **) q)[-1];
+ if (!((uintptr_t) p <= (uintptr_t) q
+ && (uintptr_t) q - (uintptr_t) p >= MALLOC_ALIGNMENT
+ && (uintptr_t) q - (uintptr_t) p <= (ALIGNMENT)))
+ abort ();
+ free (p);
+ }
+ }
+}
+
+#endif
diff --git a/m4/malloc-align.m4 b/m4/malloc-align.m4
new file mode 100644
index 0000000000..7ee8343680
--- /dev/null
+++ b/m4/malloc-align.m4
@@ -0,0 +1,140 @@
+# malloc-align.m4 serial 1
+dnl Copyright (C) 2020 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Defines a C macro MALLOC_ALIGNMENT, whose value is a numeric constant,
+dnl a power of 2, with the property that
+dnl (uintptr_t) malloc (N)
+dnl is always guaranteed to be a multiple of MALLOC_ALIGNMENT.
+
+AC_DEFUN([gl_MALLOC_ALIGNMENT],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_CACHE_CHECK([for the malloc() alignment],
+ [gl_cv_malloc_alignment],
+ [gl_cv_malloc_alignment=
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdio.h>
+ #include <stdlib.h>
+ #if defined _WIN32 && !defined __CYGWIN__
+ # include <inttypes.h>
+ /* uintptr_t is equivalent to 'unsigned long long' if _WIN64,
+ or to 'unsigned long' otherwise. */
+ #else
+ # undef uintptr_t
+ # define uintptr_t unsigned long
+ #endif
+ ]],
+ [[FILE *fp = fopen ("conftest.out", "w");
+ if (fp == NULL)
+ return 1;
+ {
+ uintptr_t bits = 0;
+ bits |= (uintptr_t) malloc (1);
+ bits |= (uintptr_t) malloc (1);
+ bits |= (uintptr_t) malloc (1);
+ bits |= (uintptr_t) malloc (2);
+ bits |= (uintptr_t) malloc (2);
+ bits |= (uintptr_t) malloc (2);
+ bits |= (uintptr_t) malloc (3);
+ bits |= (uintptr_t) malloc (3);
+ bits |= (uintptr_t) malloc (3);
+ bits |= (uintptr_t) malloc (5);
+ bits |= (uintptr_t) malloc (8);
+ bits |= (uintptr_t) malloc (8);
+ bits |= (uintptr_t) malloc (13);
+ bits |= (uintptr_t) malloc (13);
+ bits |= (uintptr_t) malloc (19);
+ bits |= (uintptr_t) malloc (19);
+ bits |= (uintptr_t) malloc (28);
+ bits |= (uintptr_t) malloc (28);
+ bits |= (uintptr_t) malloc (37);
+ bits |= (uintptr_t) malloc (37);
+ bits |= (uintptr_t) malloc (73);
+ bits |= (uintptr_t) malloc (73);
+ bits |= (uintptr_t) malloc (117);
+ bits |= (uintptr_t) malloc (117);
+ bits |= (uintptr_t) malloc (351);
+ bits |= (uintptr_t) malloc (351);
+ bits |= (uintptr_t) malloc (914);
+ bits |= (uintptr_t) malloc (914);
+ bits |= (uintptr_t) malloc (1712);
+ bits |= (uintptr_t) malloc (1712);
+ bits |= (uintptr_t) malloc (4021);
+ bits |= (uintptr_t) malloc (4021);
+ bits |= (uintptr_t) malloc (7641);
+ bits |= (uintptr_t) malloc (7641);
+ bits |= (uintptr_t) malloc (17027);
+ bits |= (uintptr_t) malloc (17027);
+ bits |= (uintptr_t) malloc (81231);
+ bits |= (uintptr_t) malloc (81231);
+ fprintf (fp, "%u\n", (unsigned int) (((bits ^ (bits - 1)) + 1) >> 1));
+ }
+ if (fclose (fp) != 0)
+ return 2;
+ return 0;
+ ]])
+ ],
+ [gl_cv_malloc_alignment=`cat conftest.out`],
+ [gl_cv_malloc_alignment="unknown"],
+ [dnl When cross-compiling, guess a value. Note that it's OK to return
+ dnl a smaller value (e.g. 4 instead of 8 or 16).
+ gl_cv_malloc_alignment="unknown"
+ case "$host_os" in
+ linux* | mingw*)
+ dnl On Linux:
+ dnl - It's 8 on most 32-bit platforms, except 16 on x86_64-x32 and
+ dnl (with newer versions of glibc) on i386 and powerpc. 8 is a
+ dnl safe guess.
+ dnl - It's 16 on all 64-bit platforms.
+ dnl On Windows: It's 8 on 32-bit Windows, 16 on 64-bit Windows.
+ for nn in 4 8 16 32; do
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #define MALLOC_ALIGN (2 * sizeof (void *))
+ int test [MALLOC_ALIGN <= $nn ? 1 : -1];
+ ]])
+ ],
+ [gl_cv_malloc_alignment="guessing $nn"
+ break
+ ],
+ [:])
+ done
+ ;;
+ *)
+ dnl If we don't know, assume the worst.
+ dnl This minimum is e.g. reached on NetBSD/i386 and NetBSD/sparc.
+ for nn in 4 8 16; do
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #define MALLOC_ALIGN (sizeof (void *))
+ int test [MALLOC_ALIGN <= $nn ? 1 : -1];
+ ]])
+ ],
+ [gl_cv_malloc_alignment="guessing $nn"
+ break
+ ],
+ [:])
+ done
+ ;;
+ esac
+ ])
+ ])
+ case "$gl_cv_malloc_alignment" in
+ "unknown")
+ dnl Assume the worst.
+ value=4
+ ;;
+ "guessing "*)
+ value=`echo "$gl_cv_malloc_alignment" | sed -e 's/guessing //'`
+ ;;
+ *)
+ value="$gl_cv_malloc_alignment"
+ ;;
+ esac
+ AC_DEFINE_UNQUOTED([MALLOC_ALIGNMENT], [$value],
+ [Define to the guaranteed alignment of malloc() return values.])
+])
diff --git a/modules/aligned-malloc b/modules/aligned-malloc
new file mode 100644
index 0000000000..29483d83d7
--- /dev/null
+++ b/modules/aligned-malloc
@@ -0,0 +1,25 @@
+Description:
+Allocate memory with indefinite extent and specified alignment.
+
+Files:
+lib/aligned-malloc.h
+m4/malloc-align.m4
+
+Depends-on:
+stdint
+
+configure.ac:
+gl_MALLOC_ALIGNMENT
+AC_REQUIRE([AC_C_INLINE])
+AC_CHECK_FUNCS([posix_memalign memalign])
+
+Makefile.am:
+
+Include:
+"aligned-malloc.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all