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)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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