diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 16:58:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 16:58:41 +0000 |
commit | e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe (patch) | |
tree | f5cc731bedcac0fb7fe14d952e4581e749f8bb87 /lib/alignalloc.c | |
parent | Initial commit. (diff) | |
download | coreutils-e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe.tar.xz coreutils-e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe.zip |
Adding upstream version 9.4.upstream/9.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/alignalloc.c')
-rw-r--r-- | lib/alignalloc.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/lib/alignalloc.c b/lib/alignalloc.c new file mode 100644 index 0000000..7d94ce2 --- /dev/null +++ b/lib/alignalloc.c @@ -0,0 +1,119 @@ +/* aligned memory allocation + + Copyright 2022-2023 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 <stdckdint.h> +#include <stdint.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. */ +static_assert (! (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. */ + static_assert (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 (ckd_add (&malloc_size, size, alignment) + || ! (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 */ |