| 1 | /** |
| 2 | * This file has no copyright assigned and is placed in the Public Domain. |
| 3 | * This file is part of the mingw-w64 runtime package. |
| 4 | * No warranty is given; refer to the file DISCLAIMER.PD within this package. |
| 5 | */ |
| 6 | /* |
| 7 | __mingw_aligned_malloc and friends, implemented using Microsoft's public |
| 8 | interfaces and with the help of the algorithm description provided |
| 9 | by Wu Yongwei: http://sourceforge.net/mailarchive/message.php?msg_id=3847075 |
| 10 | |
| 11 | I hereby place this implementation in the public domain. |
| 12 | -- Steven G. Johnson (stevenj@alum.mit.edu) |
| 13 | */ |
| 14 | |
| 15 | #include <stdlib.h> |
| 16 | #include <errno.h> |
| 17 | #include <stddef.h>		/* ptrdiff_t */ |
| 18 | #include <stdint.h>		/* uintptr_t */ |
| 19 | #include <string.h>		/* memmove */ |
| 20 | |
| 21 | #define NOT_POWER_OF_TWO(n) (((n) & ((n) - 1))) |
| 22 | #define UI(p) ((uintptr_t) (p)) |
| 23 | #define CP(p) ((char *) p) |
| 24 | |
| 25 | #define GAP(offset) ((0 - offset) & (sizeof (void *) -1)) |
| 26 | #define PTR_ALIGN(p0, alignment, offset)				\ |
| 27 | ((void *) (((UI(p0) + (alignment + GAP(offset) + sizeof(void*)) + offset)	\ |
| 28 | 			& (~UI(alignment)))				\ |
| 29 | 		 - offset)) |
| 30 | |
| 31 | /* Pointer must sometimes be aligned; assume sizeof(void*) is a power of two. */ |
| 32 | #define ORIG_PTR(p) (*(((void **) (UI(p) & (~UI(sizeof(void*) - 1)))) - 1)) |
| 33 | |
| 34 | void * |
| 35 | __mingw_aligned_offset_malloc (size_t size, size_t alignment, size_t offset) |
| 36 | { |
| 37 | void *p0, *p; |
| 38 | |
| 39 | if (NOT_POWER_OF_TWO (alignment)) |
| 40 | { |
| 41 | errno = EINVAL; |
| 42 | return ((void *) 0); |
| 43 | } |
| 44 | if (size == 0) |
| 45 | return ((void *) 0); |
| 46 | if (alignment < sizeof (void *)) |
| 47 | alignment = sizeof (void *); |
| 48 | alignment--; |
| 49 | |
| 50 | /* Including the extra sizeof(void*) is overkill on a 32-bit |
| 51 | machine, since malloc is already 8-byte aligned, as long |
| 52 | as we enforce alignment >= 8 ...but oh well. */ |
| 53 | |
| 54 | p0 = malloc (size + (alignment + GAP (offset) + sizeof (void *))); |
| 55 | if (!p0) |
| 56 | return ((void *) 0); |
| 57 | p = PTR_ALIGN (p0, alignment, offset); |
| 58 | ORIG_PTR (p) = p0; |
| 59 | return p; |
| 60 | } |
| 61 | |
| 62 | void * |
| 63 | __mingw_aligned_malloc (size_t size, size_t alignment) |
| 64 | { |
| 65 | return __mingw_aligned_offset_malloc (size, alignment, 0); |
| 66 | } |
| 67 | |
| 68 | void |
| 69 | __mingw_aligned_free (void *memblock) |
| 70 | { |
| 71 | if (memblock) |
| 72 | free (ORIG_PTR (memblock)); |
| 73 | } |
| 74 | |
| 75 | void * |
| 76 | __mingw_aligned_offset_realloc (void *memblock, size_t size, |
| 77 | 				size_t alignment, size_t offset) |
| 78 | { |
| 79 | void *p0, *p; |
| 80 | ptrdiff_t shift; |
| 81 | |
| 82 | if (!memblock) |
| 83 | return __mingw_aligned_offset_malloc (size, alignment, offset); |
| 84 | if (NOT_POWER_OF_TWO (alignment)) |
| 85 | goto bad; |
| 86 | if (size == 0) |
| 87 | { |
| 88 | __mingw_aligned_free (memblock); |
| 89 | return ((void *) 0); |
| 90 | } |
| 91 | if (alignment < sizeof (void *)) |
| 92 | alignment = sizeof (void *); |
| 93 | alignment--; |
| 94 | |
| 95 | p0 = ORIG_PTR (memblock); |
| 96 | /* It is an error for the alignment to change. */ |
| 97 | if (memblock != PTR_ALIGN (p0, alignment, offset)) |
| 98 | goto bad; |
| 99 | shift = CP (memblock) - CP (p0); |
| 100 | |
| 101 | p0 = realloc (p0, size + (alignment + GAP (offset) + sizeof (void *))); |
| 102 | if (!p0) |
| 103 | return ((void *) 0); |
| 104 | p = PTR_ALIGN (p0, alignment, offset); |
| 105 | |
| 106 | /* Relative shift of actual data may be different from before, ugh. */ |
| 107 | if (shift != CP (p) - CP (p0)) |
| 108 | /* ugh, moves more than necessary if size is increased. */ |
| 109 | memmove (CP (p), CP (p0) + shift, size); |
| 110 | |
| 111 | ORIG_PTR (p) = p0; |
| 112 | return p; |
| 113 | |
| 114 | bad: |
| 115 | errno = EINVAL; |
| 116 | return ((void *) 0); |
| 117 | } |
| 118 | |
| 119 | void * |
| 120 | __mingw_aligned_realloc (void *memblock, size_t size, size_t alignment) |
| 121 | { |
| 122 | return __mingw_aligned_offset_realloc (memblock, size, alignment, 0); |
| 123 | } |
| 124 | |
| 125 | size_t |
| 126 | __mingw_aligned_msize (void *memblock, size_t alignment, size_t offset) |
| 127 | { |
| 128 | void *p0; |
| 129 | |
| 130 | if (!memblock || NOT_POWER_OF_TWO (alignment)) |
| 131 | { |
| 132 | errno = EINVAL; |
| 133 | return (size_t)-1; |
| 134 | } |
| 135 | if (alignment < sizeof (void *)) |
| 136 | alignment = sizeof (void *); |
| 137 | alignment--; |
| 138 | |
| 139 | p0 = ORIG_PTR (memblock); |
| 140 | |
| 141 | /* It is an error if the alignment or offset does not match. */ |
| 142 | if (memblock != PTR_ALIGN (p0, alignment, offset)) |
| 143 | { |
| 144 | errno = EINVAL; |
| 145 | return (size_t)-1; |
| 146 | } |
| 147 | |
| 148 | return _msize (p0) - (alignment + GAP (offset) + sizeof (void *)); |
| 149 | } |