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 /* Forward declarations: */
22 void *__mingw_aligned_offset_malloc (size_t, size_t, size_t);
23
24 #define NOT_POWER_OF_TWO(n) (((n) & ((n) - 1)))
25 #define UI(p) ((uintptr_t) (p))
26 #define CP(p) ((char *) p)
27
28 #define PTR_ALIGN(p0, alignment, offset) \
29 ((void *) (((UI(p0) + (alignment + sizeof(void*)) + offset) \
30 & (~UI(alignment - 1))) \
31 - offset))
32
33 /* Pointer must sometimes be aligned; assume sizeof(void*) is a power of two. */
34 #define ORIG_PTR(p) (*(((void **) (UI(p) & (~UI(sizeof(void*) - 1)))) - 1))
35
36 void *
__mingw_aligned_offset_malloc(size_t size,size_t alignment,size_t offset)37 __mingw_aligned_offset_malloc (size_t size, size_t alignment, size_t offset)
38 {
39 void *p0, *p;
40
41 if (NOT_POWER_OF_TWO (alignment))
42 {
43 errno = EINVAL;
44 return ((void *) 0);
45 }
46 if (size == 0)
47 return ((void *) 0);
48 if (alignment < sizeof (void *))
49 alignment = sizeof (void *);
50
51 /* Including the extra sizeof(void*) is overkill on a 32-bit
52 machine, since malloc is already 8-byte aligned, as long
53 as we enforce alignment >= 8 ...but oh well. */
54
55 p0 = malloc (size + (alignment + sizeof (void *)));
56 if (!p0)
57 return ((void *) 0);
58 p = PTR_ALIGN (p0, alignment, offset);
59 ORIG_PTR (p) = p0;
60 return p;
61 }
62
63 void *
__mingw_aligned_malloc(size_t size,size_t alignment)64 __mingw_aligned_malloc (size_t size, size_t alignment)
65 {
66 return __mingw_aligned_offset_malloc (size, alignment, 0);
67 }
68
69 void
__mingw_aligned_free(void * memblock)70 __mingw_aligned_free (void *memblock)
71 {
72 if (memblock)
73 free (ORIG_PTR (memblock));
74 }
75
76 void *
__mingw_aligned_offset_realloc(void * memblock,size_t size,size_t alignment,size_t offset)77 __mingw_aligned_offset_realloc (void *memblock, size_t size,
78 size_t alignment, size_t offset)
79 {
80 void *p0, *p;
81 ptrdiff_t shift;
82
83 if (!memblock)
84 return __mingw_aligned_offset_malloc (size, alignment, offset);
85 if (NOT_POWER_OF_TWO (alignment))
86 goto bad;
87 if (size == 0)
88 {
89 __mingw_aligned_free (memblock);
90 return ((void *) 0);
91 }
92 if (alignment < sizeof (void *))
93 alignment = sizeof (void *);
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 + 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 *
__mingw_aligned_realloc(void * memblock,size_t size,size_t alignment)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