malloc-h: New module.
[gnulib.git] / lib / aligned-malloc.h
blob9baaab4cfe74b86e0422141882b6338b960e56f2
1 /* Allocate memory with indefinite extent and specified alignment.
3 Copyright (C) 2020 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program 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 General Public License for more details.
15 You should have received a copy of the GNU 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
23 (a power of 2).
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
46 free(). */
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
56 # if (ALIGNMENT) <= MALLOC_ALIGNMENT
57 /* Simply use malloc. */
59 # ifdef aligned_malloc
60 /* The caller wants an inline function, not a macro. */
61 static inline void *
62 aligned_malloc (size_t size)
64 return malloc (size);
66 # else
67 # define aligned_malloc malloc
68 # endif
70 # elif HAVE_POSIX_MEMALIGN
71 /* Use posix_memalign.
72 This is OK since ALIGNMENT > MALLOC_ALIGNMENT >= sizeof (void *). */
74 static inline void *
75 aligned_malloc (size_t size)
77 void *p;
78 int ret = posix_memalign (&p, (ALIGNMENT), size);
79 if (ret == 0)
80 return p;
81 else
82 return NULL;
85 # elif HAVE_ALIGNED_ALLOC
86 /* Use aligned_alloc. */
88 static inline void *
89 aligned_malloc (size_t size)
91 /* Round up SIZE to the next multiple of ALIGNMENT,
92 namely (SIZE + ALIGNMENT - 1) & ~(ALIGNMENT - 1). */
93 size += (ALIGNMENT) - 1;
94 if (size >= (ALIGNMENT) - 1) /* no overflow? */
96 size &= ~(size_t)((ALIGNMENT) - 1);
97 return aligned_alloc ((ALIGNMENT), size);
99 return NULL;
102 # elif HAVE_MEMALIGN /* HP-UX, IRIX, Solaris <= 10 */
103 /* Use memalign. */
105 static inline void *
106 aligned_malloc (size_t size)
108 return memalign ((ALIGNMENT), size);
111 # endif
113 # ifdef aligned_free
114 /* The caller wants an inline function, not a macro. */
115 static inline void
116 aligned_free (void *q)
118 free (q);
120 # else
121 # define aligned_free free
122 # endif
124 #else
125 /* Use malloc and waste a bit of memory. */
127 static inline void *
128 aligned_malloc (size_t size)
130 size += (ALIGNMENT);
131 if (size >= (ALIGNMENT)) /* no overflow? */
133 void *p = malloc (size);
134 if (p != NULL)
136 /* Go to the next multiple of ALIGNMENT. */
137 void *q =
138 (void *) (((uintptr_t) p + (ALIGNMENT)) & -(intptr_t)(ALIGNMENT));
139 /* Now q - p <= ALIGNMENT and
140 q - p >= MALLOC_ALIGNMENT >= sizeof (void *).
141 This is enough to store a back pointer to p. */
142 ((void **) q)[-1] = p;
143 return q;
146 return NULL;
149 static inline void
150 aligned_free (void *q)
152 if (q != NULL)
154 if ((uintptr_t) q & ((ALIGNMENT) - 1))
155 /* Argument not aligned as expected. */
156 abort ();
157 else
159 void *p = ((void **) q)[-1];
160 if (!((uintptr_t) p <= (uintptr_t) q
161 && (uintptr_t) q - (uintptr_t) p >= MALLOC_ALIGNMENT
162 && (uintptr_t) q - (uintptr_t) p <= (ALIGNMENT)))
163 abort ();
164 free (p);
169 #endif