1 /* Allocate memory with indefinite extent and specified alignment. 2 3 Copyright (C) 2020-2021 Free Software Foundation, Inc. 4 5 This file is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1 of the 8 License, or (at your option) any later version. 9 10 This file is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 18 /* Written by Bruno Haible <bruno@clisp.org>, 2020. */ 19 20 /* Before including this file, you need to define the following macro: 21 22 ALIGNMENT A constant expression that evaluates to the desired alignment 23 (a power of 2). 24 25 And you also need to #include <stdint.h> and <stdlib.h>. */ 26 27 /* aligned_malloc allocates a block of memory of SIZE bytes, aligned on a 28 boundary of ALIGNMENT bytes. 29 The block can be freed through aligned_free(), NOT through free(). 30 Upon failure, it returns NULL. */ 31 32 /* This module exists instead of a posix_memalign(), aligned_alloc(), or 33 memalign() emulation, because we can't reasonably emulate posix_memalign(), 34 aligned_alloc(), or memalign(): 35 If malloc() returned p, only free (p) is allowed, not free (p + 1), 36 free (p + 2), free (p + 4), free (p + 8), or similar. 37 38 We can use posix_memalign(), a POSIX function. 39 40 We can also use aligned_alloc(), an ISO C11 and POSIX function. But it's 41 a bit more awkward to use. 42 43 On older systems, we can alternatively use memalign() instead. In the 44 Solaris documentation of memalign() it is not specified how a memory block 45 returned by memalign() can be freed, but it actually can be freed with 46 free(). */ 47 48 #if !defined ALIGNMENT 49 # error "ALIGNMENT is not defined" 50 #endif 51 #if !((ALIGNMENT) > 0 && ((ALIGNMENT) & ((ALIGNMENT) - 1)) == 0) 52 # error "ALIGNMENT is not a power of 2" 53 #endif 54 #if ((ALIGNMENT) <= MALLOC_ALIGNMENT) || HAVE_POSIX_MEMALIGN || HAVE_ALIGNED_ALLOC || HAVE_MEMALIGN 55 56 # if defined aligned_free || __GNUC__ >= 11 57 /* The caller wants an inline function, not a macro, 58 or we can use GCC's -Wmismatched-dealloc warning. */ 59 static inline void 60 aligned_free (void *q) /* */ 61 { 62 free (q); 63 } 64 # else 65 # define aligned_free free 66 # endif 67 68 # if (ALIGNMENT) <= MALLOC_ALIGNMENT 69 /* Simply use malloc. */ 70 71 # if defined aligned_malloc || __GNUC__ >= 11 72 /* The caller wants an inline function, not a macro, 73 or GCC's -Wmismatched-dealloc warning might be in effect. */ 74 static inline 75 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 76 void * 77 aligned_malloc (size_t size) /* */ 78 { 79 return malloc (size); 80 } 81 # else 82 # define aligned_malloc malloc 83 # endif 84 85 # elif HAVE_POSIX_MEMALIGN 86 /* Use posix_memalign. 87 This is OK since ALIGNMENT > MALLOC_ALIGNMENT >= sizeof (void *). */ 88 89 static inline 90 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 91 void * 92 aligned_malloc (size_t size) /* */ 93 { 94 void *p; 95 int ret = posix_memalign (&p, (ALIGNMENT), size); 96 if (ret == 0) 97 return p; 98 else 99 return NULL; 100 } 101 102 # elif HAVE_ALIGNED_ALLOC 103 /* Use aligned_alloc. */ 104 105 static inline 106 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 107 void * 108 aligned_malloc (size_t size) /* */ 109 { 110 /* Round up SIZE to the next multiple of ALIGNMENT, 111 namely (SIZE + ALIGNMENT - 1) & ~(ALIGNMENT - 1). */ 112 size += (ALIGNMENT) - 1; 113 if (size >= (ALIGNMENT) - 1) /* no overflow? */ 114 { 115 size &= ~(size_t)((ALIGNMENT) - 1); 116 return aligned_alloc ((ALIGNMENT), size); 117 } 118 return NULL; 119 } 120 121 # elif HAVE_MEMALIGN /* HP-UX, IRIX, Solaris <= 10 */ 122 /* Use memalign. */ 123 124 static inline 125 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 126 void * 127 aligned_malloc (size_t size) /* */ 128 { 129 return memalign ((ALIGNMENT), size); 130 } 131 132 # endif 133 134 #else 135 /* Use malloc and waste a bit of memory. */ 136 137 static inline void 138 aligned_free (void *q) /* */ 139 { 140 if (q != NULL) 141 { 142 if ((uintptr_t) q & ((ALIGNMENT) - 1)) 143 /* Argument not aligned as expected. */ 144 abort (); 145 else 146 { 147 void *p = ((void **) q)[-1]; 148 if (!((uintptr_t) p <= (uintptr_t) q 149 && (uintptr_t) q - (uintptr_t) p >= MALLOC_ALIGNMENT 150 && (uintptr_t) q - (uintptr_t) p <= (ALIGNMENT))) 151 abort (); 152 free (p); 153 } 154 } 155 } 156 157 static inline 158 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/ 159 void * 160 aligned_malloc (size_t size) /* */ 161 { 162 size += (ALIGNMENT); 163 if (size >= (ALIGNMENT)) /* no overflow? */ 164 { 165 void *p = malloc (size); 166 if (p != NULL) 167 { 168 /* Go to the next multiple of ALIGNMENT. */ 169 void *q = 170 (void *) (((uintptr_t) p + (ALIGNMENT)) & -(intptr_t)(ALIGNMENT)); 171 /* Now q - p <= ALIGNMENT and 172 q - p >= MALLOC_ALIGNMENT >= sizeof (void *). 173 This is enough to store a back pointer to p. */ 174 ((void **) q)[-1] = p; 175 return q; 176 } 177 } 178 return NULL; 179 } 180 181 #endif