| /** | |
| * This file has no copyright assigned and is placed in the Public Domain. | |
| * This file is part of the w64 mingw-runtime package. | |
| * No warranty is given; refer to the file DISCLAIMER within this package. | |
| */ | |
| /* | |
| __mingw_aligned_malloc and friends, implemented using Microsoft's public | |
| interfaces and with the help of the algorithm description provided | |
| by Wu Yongwei: http://sourceforge.net/mailarchive/message.php?msg_id=3847075 | |
| I hereby place this implementation in the public domain. | |
| -- Steven G. Johnson (stevenj@alum.mit.edu) | |
| */ | |
| #include <stdlib.h> | |
| #include <errno.h> | |
| #include <stddef.h> /* ptrdiff_t */ | |
| #include <string.h> /* memmove */ | |
| #ifdef HAVE_STDINT_H | |
| # include <stdint.h> /* uintptr_t */ | |
| #else | |
| # define uintptr_t size_t | |
| #endif | |
| #define NOT_POWER_OF_TWO(n) (((n) & ((n) - 1))) | |
| #define UI(p) ((uintptr_t) (p)) | |
| #define CP(p) ((char *) p) | |
| #define PTR_ALIGN(p0, alignment, offset) \ | |
| ((void *) (((UI(p0) + (alignment + sizeof(void*)) + offset) \ | |
| & (~UI(alignment - 1))) \ | |
| - offset)) | |
| /* Pointer must sometimes be aligned; assume sizeof(void*) is a power of two. */ | |
| #define ORIG_PTR(p) (*(((void **) (UI(p) & (~UI(sizeof(void*) - 1)))) - 1)) | |
| void * | |
| __mingw_aligned_offset_malloc (size_t size, size_t alignment, size_t offset) | |
| { | |
| void *p0, *p; | |
| if (NOT_POWER_OF_TWO (alignment)) | |
| { | |
| errno = EINVAL; | |
| return ((void *) 0); | |
| } | |
| if (size == 0) | |
| return ((void *) 0); | |
| if (alignment < sizeof (void *)) | |
| alignment = sizeof (void *); | |
| /* Including the extra sizeof(void*) is overkill on a 32-bit | |
| machine, since malloc is already 8-byte aligned, as long | |
| as we enforce alignment >= 8 ...but oh well. */ | |
| p0 = malloc (size + (alignment + sizeof (void *))); | |
| if (!p0) | |
| return ((void *) 0); | |
| p = PTR_ALIGN (p0, alignment, offset); | |
| ORIG_PTR (p) = p0; | |
| return p; | |
| } | |
| void * | |
| __mingw_aligned_malloc (size_t size, size_t alignment) | |
| { | |
| return __mingw_aligned_offset_malloc (size, alignment, 0); | |
| } | |
| void | |
| __mingw_aligned_free (void *memblock) | |
| { | |
| if (memblock) | |
| free (ORIG_PTR (memblock)); | |
| } | |
| void * | |
| __mingw_aligned_offset_realloc (void *memblock, size_t size, | |
| size_t alignment, size_t offset) | |
| { | |
| void *p0, *p; | |
| ptrdiff_t shift; | |
| if (!memblock) | |
| return __mingw_aligned_offset_malloc (size, alignment, offset); | |
| if (NOT_POWER_OF_TWO (alignment)) | |
| goto bad; | |
| if (size == 0) | |
| { | |
| __mingw_aligned_free (memblock); | |
| return ((void *) 0); | |
| } | |
| if (alignment < sizeof (void *)) | |
| alignment = sizeof (void *); | |
| p0 = ORIG_PTR (memblock); | |
| /* It is an error for the alignment to change. */ | |
| if (memblock != PTR_ALIGN (p0, alignment, offset)) | |
| goto bad; | |
| shift = CP (memblock) - CP (p0); | |
| p0 = realloc (p0, size + (alignment + sizeof (void *))); | |
| if (!p0) | |
| return ((void *) 0); | |
| p = PTR_ALIGN (p0, alignment, offset); | |
| /* Relative shift of actual data may be different from before, ugh. */ | |
| if (shift != CP (p) - CP (p0)) | |
| /* ugh, moves more than necessary if size is increased. */ | |
| memmove (CP (p), CP (p0) + shift, size); | |
| ORIG_PTR (p) = p0; | |
| return p; | |
| bad: | |
| errno = EINVAL; | |
| return ((void *) 0); | |
| } | |
| void * | |
| __mingw_aligned_realloc (void *memblock, size_t size, size_t alignment) | |
| { | |
| return __mingw_aligned_offset_realloc (memblock, size, alignment, 0); | |
| } |