summaryrefslogtreecommitdiff
path: root/src/malloc/oldmalloc/aligned_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/malloc/oldmalloc/aligned_alloc.c')
-rw-r--r--src/malloc/oldmalloc/aligned_alloc.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/src/malloc/oldmalloc/aligned_alloc.c b/src/malloc/oldmalloc/aligned_alloc.c
new file mode 100644
index 00000000..e06c76ed
--- /dev/null
+++ b/src/malloc/oldmalloc/aligned_alloc.c
@@ -0,0 +1,52 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include "malloc_impl.h"
+
+void *aligned_alloc(size_t align, size_t len)
+{
+ unsigned char *mem, *new;
+
+ if ((align & -align) != align) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (len > SIZE_MAX - align || __malloc_replaced) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ if (align <= SIZE_ALIGN)
+ return malloc(len);
+
+ if (!(mem = malloc(len + align-1)))
+ return 0;
+
+ new = (void *)((uintptr_t)mem + align-1 & -align);
+ if (new == mem) return mem;
+
+ struct chunk *c = MEM_TO_CHUNK(mem);
+ struct chunk *n = MEM_TO_CHUNK(new);
+
+ if (IS_MMAPPED(c)) {
+ /* Apply difference between aligned and original
+ * address to the "extra" field of mmapped chunk. */
+ n->psize = c->psize + (new-mem);
+ n->csize = c->csize - (new-mem);
+ return new;
+ }
+
+ struct chunk *t = NEXT_CHUNK(c);
+
+ /* Split the allocated chunk into two chunks. The aligned part
+ * that will be used has the size in its footer reduced by the
+ * difference between the aligned and original addresses, and
+ * the resulting size copied to its header. A new header and
+ * footer are written for the split-off part to be freed. */
+ n->psize = c->csize = C_INUSE | (new-mem);
+ n->csize = t->psize -= new-mem;
+
+ __bin_chunk(c);
+ return new;
+}