* lib/alignalloc.c, lib/alignalloc.h, lib/xalignalloc.c: * m4/alignalloc.m4, modules/alignalloc, modules/alignalloc-tests: * modules/xalignalloc, tests/test-alignalloc.c: New files. --- ChangeLog | 8 +++ lib/alignalloc.c | 121 +++++++++++++++++++++++++++++++++++++++ lib/alignalloc.h | 98 +++++++++++++++++++++++++++++++ lib/xalignalloc.c | 33 +++++++++++ m4/alignalloc.m4 | 10 ++++ modules/alignalloc | 32 +++++++++++ modules/alignalloc-tests | 14 +++++ modules/xalignalloc | 23 ++++++++ tests/test-alignalloc.c | 57 ++++++++++++++++++ 9 files changed, 396 insertions(+) create mode 100644 lib/alignalloc.c create mode 100644 lib/alignalloc.h create mode 100644 lib/xalignalloc.c create mode 100644 m4/alignalloc.m4 create mode 100644 modules/alignalloc create mode 100644 modules/alignalloc-tests create mode 100644 modules/xalignalloc create mode 100644 tests/test-alignalloc.c
diff --git a/ChangeLog b/ChangeLog index c5eebe4872..980d214b8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2022-01-23 Paul Eggert <egg...@cs.ucla.edu> + + alignalloc, xalignalloc: new modules + * lib/alignalloc.c, lib/alignalloc.h, lib/xalignalloc.c: + * m4/alignalloc.m4, modules/alignalloc, modules/alignalloc-tests: + * modules/xalignalloc, tests/test-alignalloc.c: + New files. + 2022-01-17 Paul Eggert <egg...@cs.ucla.edu> extern-inline: improve macOS port diff --git a/lib/alignalloc.c b/lib/alignalloc.c new file mode 100644 index 0000000000..03988f11a4 --- /dev/null +++ b/lib/alignalloc.c @@ -0,0 +1,121 @@ +/* aligned memory allocation + + Copyright 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 + 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 Paul Eggert. */ + +#include <config.h> + +#define ALIGNALLOC_INLINE _GL_EXTERN_INLINE +#include "alignalloc.h" + +#include <limits.h> +#include <stdalign.h> +#include <stdint.h> +#include "intprops.h" +#include "verify.h" + +#if !ALIGNALLOC_VIA_ALIGNED_ALLOC +# if HAVE_POSIX_MEMALIGN + +/* posix_memalign requires the alignment to be a power-of-two multiple of + sizeof (void *), whereas alignalloc requires it to be a power of two. + To make it OK for the latter to call the former, check that + sizeof (void *) is a power of two, which is true on all known platforms. + This check is here rather than in alignalloc.h to save the compiler + the trouble of checking it each time alignalloc.h is included. */ +verify (! (sizeof (void *) & (sizeof (void *) - 1))); + +# else /* !HAVE_POSIX_MEMALIGN */ + +/* Return P aligned down to ALIGNMENT, which should be a power of two. */ + +static void * +align_down (void *p, idx_t alignment) +{ + char *c = p; + return c - ((uintptr_t) p & (alignment - 1)); +} + +/* If alignalloc returned R and the base of the originally-allocated + storage is less than R - UCHAR_MAX, return the address of a pointer + holding the base of the originally-allocated storage. */ + +static void ** +address_of_pointer_to_malloced (unsigned char *r) +{ + /* The pointer P is located at the highest address A such that A is + aligned for pointers, and A + sizeof P < R so that there is room + for a 0 byte at R - 1. This approach assumes UCHAR_MAX is large + enough so that there is room for P; although true on all + plausible platforms, check the assumption to be safe. */ + verify (sizeof (void *) + alignof (void *) - 1 <= UCHAR_MAX); + + return align_down (r - 1 - sizeof (void *), alignof (void *)); +} + +/* Return an ALIGNMENT-aligned pointer to new storage of size SIZE, + or a null pointer (setting errno) if memory is exhausted. + ALIGNMENT must be a power of two. + If SIZE is zero, on success return a unique pointer each time. + To free storage later, call alignfree. */ + +void * +alignalloc (idx_t alignment, idx_t size) +{ + /* malloc (ALIGNMENT + SIZE); if it succeeds, there must be at least + one byte available before the returned pointer. It's OK if + ALIGNMENT + SIZE fits in size_t but not idx_t. */ + + size_t malloc_size; + unsigned char *q; + if (INT_ADD_WRAPV (size, alignment, &malloc_size) + || ! (q = malloc (malloc_size))) + { + errno = ENOMEM; + return NULL; + } + + unsigned char *r = align_down (q + alignment, alignment); + idx_t offset = r - q; + + if (offset <= UCHAR_MAX) + r[-1] = offset; + else + { + r[-1] = 0; + *address_of_pointer_to_malloced (r) = q; + } + + return r; +} + +/* Free storage allocated via alignalloc. Do nothing if PTR is null. */ + +void +alignfree (void *ptr) +{ + if (ptr) + { + unsigned char *r = ptr; + unsigned char offset = r[-1]; + void *q = offset ? r - offset : *address_of_pointer_to_malloced (r); + free (q); + } +} + +# endif /* ! HAVE_POSIX_MEMALIGN */ +#endif /* ! ALIGNALLOC_VIA_ALIGNED_ALLOC */ diff --git a/lib/alignalloc.h b/lib/alignalloc.h new file mode 100644 index 0000000000..7e4c4743e9 --- /dev/null +++ b/lib/alignalloc.h @@ -0,0 +1,98 @@ +/* aligned memory allocation + + Copyright 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 + 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 Paul Eggert. */ + +#ifndef ALIGNALLOC_H_ +#define ALIGNALLOC_H_ + +#include <errno.h> +#include <stdlib.h> +#include "idx.h" + +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +#ifndef ALIGNALLOC_INLINE +# define ALIGNALLOC_INLINE _GL_INLINE +#endif + +/* Whether aligned_alloc supports any power-of-two alignment, + returns a nonnull pointer for size-zero allocations, + and sets errno on failure. */ +#if 2 < __GLIBC__ + (15 <= __GLIBC_MINOR__) +# define ALIGNALLOC_VIA_ALIGNED_ALLOC 1 +#else +# define ALIGNALLOC_VIA_ALIGNED_ALLOC 0 +#endif + +#if ALIGNALLOC_VIA_ALIGNED_ALLOC || HAVE_POSIX_MEMALIGN + +/* Free storage allocated via alignalloc. Do nothing if PTR is null. */ + +ALIGNALLOC_INLINE void +alignfree (void *ptr) +{ + free (ptr); +} + +/* Return an ALIGNMENT-aligned pointer to new storage of size SIZE, + or a null pointer (setting errno) if memory is exhausted. + ALIGNMENT must be a power of two. + If SIZE is zero, on success return a unique pointer each time. + To free storage later, call alignfree. */ + +ALIGNALLOC_INLINE +_GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((2)) +/* _GL_ATTRIBUTE_DEALLOC (alignfree, 1) */ +void * +alignalloc (idx_t alignment, idx_t size) +{ + if ((size_t) -1 < alignment) + alignment = (size_t) -1; + if ((size_t) -1 < size) + size = (size_t) -1; + +# if ALIGNALLOC_VIA_ALIGNED_ALLOC + return aligned_alloc (alignment, size); +# else + void *ptr = NULL; + if (alignment < sizeof (void *)) + alignment = sizeof (void *); + errno = posix_memalign (&ptr, alignment, size | !size); + return ptr; +# endif +} + +#else /* ! (ALIGNALLOC_VIA_ALIGNED_ALLOC || HAVE_POSIX_MEMALIGN) */ + +void alignfree (void *); +void *alignalloc (idx_t, idx_t) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((2)) + _GL_ATTRIBUTE_DEALLOC (alignfree, 1); + +#endif + +/* Like alignalloc, but die instead of returning a null pointer. */ +void *xalignalloc (idx_t, idx_t) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((2)) + _GL_ATTRIBUTE_RETURNS_NONNULL /* _GL_ATTRIBUTE_DEALLOC (alignfree, 1) */; + +_GL_INLINE_HEADER_END + +#endif /* !ALIGNALLOC_H_ */ diff --git a/lib/xalignalloc.c b/lib/xalignalloc.c new file mode 100644 index 0000000000..66b682f26a --- /dev/null +++ b/lib/xalignalloc.c @@ -0,0 +1,33 @@ +/* checked aligned memory allocation + + Copyright 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 + 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 Paul Eggert. */ + +#include <config.h> + +#include "alignalloc.h" + +#include "xalloc.h" + +void * +xalignalloc (idx_t alignment, idx_t size) +{ + void *p = alignalloc (alignment, size); + if (!p) + xalloc_die (); + return p; +} diff --git a/m4/alignalloc.m4 b/m4/alignalloc.m4 new file mode 100644 index 0000000000..01627a085a --- /dev/null +++ b/m4/alignalloc.m4 @@ -0,0 +1,10 @@ +dnl Copyright 2022 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. + +AC_DEFUN([gl_ALIGNALLOC], +[ + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([posix_memalign]) +]) diff --git a/modules/alignalloc b/modules/alignalloc new file mode 100644 index 0000000000..68c1b14cd4 --- /dev/null +++ b/modules/alignalloc @@ -0,0 +1,32 @@ +Description: +Aligned memory allocation + +Files: +lib/alignalloc.h +lib/alignalloc.c +m4/alignalloc.m4 + +Depends-on: +extensions +extern-inline +idx +intprops +posix_memalign +stdalign +stdint +verify + +configure.ac: +gl_ALIGNALLOC + +Makefile.am: +lib_SOURCES += alignalloc.c + +Include: +"alignalloc.h" + +License: +LGPLv2+ + +Maintainer: +bug-gnulib@gnu.org diff --git a/modules/alignalloc-tests b/modules/alignalloc-tests new file mode 100644 index 0000000000..359f2975d5 --- /dev/null +++ b/modules/alignalloc-tests @@ -0,0 +1,14 @@ +Files: +tests/test-alignalloc.c +tests/signature.h +tests/macros.h + +Depends-on: +intprops +stdint + +configure.ac: + +Makefile.am: +TESTS += test-alignalloc +check_PROGRAMS += test-alignalloc diff --git a/modules/xalignalloc b/modules/xalignalloc new file mode 100644 index 0000000000..d336df0842 --- /dev/null +++ b/modules/xalignalloc @@ -0,0 +1,23 @@ +Description: +Checked aligned memory allocation + +Files: +lib/xalignalloc.c + +Depends-on: +alignalloc +xalloc-die + +configure.ac: + +Makefile.am: +lib_SOURCES += xalignalloc.c + +Include: +"alignalloc.h" + +License: +GPL + +Maintainer: +bug-gnulib@gnu.org diff --git a/tests/test-alignalloc.c b/tests/test-alignalloc.c new file mode 100644 index 0000000000..161ab384db --- /dev/null +++ b/tests/test-alignalloc.c @@ -0,0 +1,57 @@ +/* Test alignalloc and alignfree. + + Copyright 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 + 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> + +#include <alignalloc.h> + +#include <stdint.h> +#include <string.h> +#include "intprops.h" + +#include "signature.h" +SIGNATURE_CHECK (alignalloc, void *, (idx_t, idx_t)); +SIGNATURE_CHECK (alignfree, void, (void *)); + +#include "macros.h" + +int +main () +{ + /* Check that alignalloc returns properly aligned storage, + when it succeeds. */ + for (idx_t alignment = 1; ; ) + { + for (idx_t size = 0; size <= 1024; size = size ? 2 * size : 1) + { + void *p = alignalloc (alignment, size); + if (p) + { + memset (p, 0, size); + ASSERT ((uintptr_t) p % alignment == 0); + } + alignfree (p); + } + if (INT_MULTIPLY_WRAPV (alignment, 2, &alignment)) + break; + } + + /* Check that alignfree is a no-op on null pointers. */ + alignfree (NULL); + + return 0; +} -- 2.32.0