/* Allocate memory with indefinite extent and specified alignment. Copyright (C) 2020-2023 Free Software Foundation, Inc. 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 . */ /* Written by Bruno Haible , 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 and . */ /* 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(), aligned_alloc(), or memalign() emulation, because we can't reasonably emulate posix_memalign(), aligned_alloc(), 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(), a POSIX function. We can also use aligned_alloc(), an ISO C11 and POSIX function. But it's a bit more awkward to use. 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(). */ /* This file uses MALLOC_ALIGNMENT, HAVE_POSIX_MEMALIGN, HAVE_ALIGNED_ALLOC, HAVE_MEMALIGN. */ #if !_GL_CONFIG_H_INCLUDED #error "Please include config.h first." #endif #if !defined ALIGNMENT # error "ALIGNMENT is not defined" #endif #if !((ALIGNMENT) > 0 && ((ALIGNMENT) & ((ALIGNMENT) - 1)) == 0) # error "ALIGNMENT is not a power of 2" #endif #if ((ALIGNMENT) <= MALLOC_ALIGNMENT) || HAVE_POSIX_MEMALIGN || HAVE_ALIGNED_ALLOC || HAVE_MEMALIGN # if defined aligned_free || __GNUC__ >= 11 /* The caller wants an inline function, not a macro, or we can use GCC's -Wmismatched-dealloc warning. */ static inline void aligned_free (void *q) { free (q); } # else # define aligned_free free # endif # if (ALIGNMENT) <= MALLOC_ALIGNMENT /* Simply use malloc. */ # if defined aligned_malloc || __GNUC__ >= 11 /* The caller wants an inline function, not a macro, or GCC's -Wmismatched-dealloc warning might be in effect. */ static inline /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 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 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 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_ALIGNED_ALLOC /* Use aligned_alloc. */ static inline /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ void * aligned_malloc (size_t size) { /* Round up SIZE to the next multiple of ALIGNMENT, namely (SIZE + ALIGNMENT - 1) & ~(ALIGNMENT - 1). */ size += (ALIGNMENT) - 1; if (size >= (ALIGNMENT) - 1) /* no overflow? */ { size &= ~(size_t)((ALIGNMENT) - 1); return aligned_alloc ((ALIGNMENT), size); } return NULL; } # elif HAVE_MEMALIGN /* HP-UX, IRIX, Solaris <= 10 */ /* Use memalign. */ static inline /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ void * aligned_malloc (size_t size) { return memalign ((ALIGNMENT), size); } # endif #else /* Use malloc and waste a bit of memory. */ 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); } } } static inline /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 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; } #endif