1 /* Allocate memory with indefinite extent and specified alignment.
3 Copyright (C) 2020-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
18 /* Written by Bruno Haible <bruno@clisp.org>, 2020. */
20 /* Before including this file, you need to define the following macro:
22 ALIGNMENT A constant expression that evaluates to the desired alignment
25 And you also need to #include <stdint.h> and <stdlib.h>. */
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. */
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.
38 We can use posix_memalign(), a POSIX function.
40 We can also use aligned_alloc(), an ISO C11 and POSIX function. But it's
41 a bit more awkward to use.
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
48 /* This file uses MALLOC_ALIGNMENT, HAVE_POSIX_MEMALIGN, HAVE_ALIGNED_ALLOC,
50 #if !_GL_CONFIG_H_INCLUDED
51 #error "Please include config.h first."
59 #if !defined ALIGNMENT
60 # error "ALIGNMENT is not defined"
62 #if !((ALIGNMENT) > 0 && ((ALIGNMENT) & ((ALIGNMENT) - 1)) == 0)
63 # error "ALIGNMENT is not a power of 2"
65 #if ((ALIGNMENT) <= MALLOC_ALIGNMENT) || HAVE_POSIX_MEMALIGN || HAVE_ALIGNED_ALLOC || HAVE_MEMALIGN
67 # if defined aligned_free || (__GNUC__ >= 11 && !defined __clang__)
68 /* The caller wants an inline function, not a macro,
69 or we can use GCC's -Wmismatched-dealloc warning. */
71 aligned_free (void *q
)
76 # define aligned_free free
79 # if (ALIGNMENT) <= MALLOC_ALIGNMENT
80 /* Simply use malloc. */
82 # if defined aligned_malloc || (__GNUC__ >= 11 && !defined __clang__)
83 /* The caller wants an inline function, not a macro,
84 or GCC's -Wmismatched-dealloc warning might be in effect. */
86 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/
88 aligned_malloc (size_t size
)
93 # define aligned_malloc malloc
96 # elif HAVE_POSIX_MEMALIGN
97 /* Use posix_memalign.
98 This is OK since ALIGNMENT > MALLOC_ALIGNMENT >= sizeof (void *). */
101 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/
103 aligned_malloc (size_t size
)
106 int ret
= posix_memalign (&p
, (ALIGNMENT
), size
);
113 # elif HAVE_ALIGNED_ALLOC
114 /* Use aligned_alloc. */
117 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/
119 aligned_malloc (size_t size
)
121 /* Round up SIZE to the next multiple of ALIGNMENT,
122 namely (SIZE + ALIGNMENT - 1) & ~(ALIGNMENT - 1). */
123 size
+= (ALIGNMENT
) - 1;
124 if (size
>= (ALIGNMENT
) - 1) /* no overflow? */
126 size
&= ~(size_t)((ALIGNMENT
) - 1);
127 return aligned_alloc ((ALIGNMENT
), size
);
132 # elif HAVE_MEMALIGN /* HP-UX, IRIX, Solaris <= 10 */
136 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/
138 aligned_malloc (size_t size
)
140 return memalign ((ALIGNMENT
), size
);
146 /* Use malloc and waste a bit of memory. */
149 aligned_free (void *q
)
153 if ((uintptr_t) q
& ((ALIGNMENT
) - 1))
154 /* Argument not aligned as expected. */
158 void *p
= ((void **) q
)[-1];
159 if (!((uintptr_t) p
<= (uintptr_t) q
160 && (uintptr_t) q
- (uintptr_t) p
>= MALLOC_ALIGNMENT
161 && (uintptr_t) q
- (uintptr_t) p
<= (ALIGNMENT
)))
169 /*_GL_ATTRIBUTE_DEALLOC (aligned_free, 1)*/
171 aligned_malloc (size_t size
)
174 if (size
>= (ALIGNMENT
)) /* no overflow? */
176 void *p
= malloc (size
);
179 /* Go to the next multiple of ALIGNMENT. */
181 (void *) (((uintptr_t) p
+ (ALIGNMENT
)) & -(intptr_t)(ALIGNMENT
));
182 /* Now q - p <= ALIGNMENT and
183 q - p >= MALLOC_ALIGNMENT >= sizeof (void *).
184 This is enough to store a back pointer to p. */
185 ((void **) q
)[-1] = p
;