]> git.sur5r.net Git - cc65/blob - libsrc/common/_aligned_malloc.c
a74608b35a385ce0e9c1165ca3b151a4688d23b3
[cc65] / libsrc / common / _aligned_malloc.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                              _aligned_malloc                              */
4 /*                                                                           */
5 /*                     Allocate an aligned memory block                      */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2004-2005 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdlib.h>
37 #include <_heap.h>
38
39
40
41 /* This is a very simple version of an aligned memory allocator. We will
42  * allocate a greater block, so we can place the aligned block within it
43  * that is returned. We use our knowledge about the internal heap
44  * structures to free the unused parts of the bigger block (the two chunks
45  * below and above the aligned block).
46  */
47
48
49
50 void* __fastcall__ _aligned_malloc (size_t size, size_t alignment)
51 /* Allocate a block of memory with the given size, which is aligned to a
52  * memory address that is a multiple of alignment. alignment MUST NOT be
53  * zero and MUST be a power of two, otherwise a call to this function will 
54  * cause undefined behaviour. The function returns NULL if not enough memory 
55  * is available to satisfy the request. To free the allocated block, use the
56  * free() function.
57  */
58 {
59     size_t rawsize;
60     size_t uppersize;
61     size_t lowersize;
62     register struct usedblock* b;
63     register struct usedblock* u;
64     register struct usedblock* p;
65
66     /* Handle requests for zero sized blocks */
67     if (size == 0) {
68         return 0;
69     }
70
71     /* We don't really need alignment, but alignment-1 */
72     --alignment;
73
74     /* Round up the block size and allocate memory. We don't need to account
75      * for the additional admin data needed to manage the used block, since
76      * the block returned by malloc has this overhead added one time, and
77      * the worst thing that may happen is that we cannot free the upper and
78      * lower blocks.
79      */
80     b = malloc (size + alignment);
81
82     /* Handle out of memory */
83     if (b == 0) {
84         return 0;
85     }
86
87     /* Create a new pointer that points to the user visible aligned block. */
88     u = (struct usedblock*) (((unsigned)b + alignment) & ~alignment);
89
90     /* Get the raw block pointer, which is located just below the user visible
91      * block. The first word of this raw block is the total size of the block
92      * including the admin space.
93      */
94     b = (b-1)->start;
95     rawsize = b->size;
96
97     /* Get a pointer to the (raw) upper block */
98     p = (struct usedblock*) (size + (unsigned)u);
99
100     /* Check if we can free the space above the allocated block. This is the
101      * case if the size of the block is at least sizeof (struct freeblock)
102      * bytes and the size of the remaining block is at least of this size,
103      * too. If the upper block is smaller, we will just pass it to the caller
104      * together with the requested aligned block.
105      */
106     uppersize = rawsize + (unsigned)b - (unsigned)p;
107     if (uppersize >= sizeof (struct freeblock) &&
108         (rawsize - uppersize) >= sizeof (struct freeblock)) {
109
110         /* Setup the usedblock structure */
111         p->size  = uppersize;
112         p->start = p;
113
114         /* Generate a pointer to the user space and free the block */
115         free (p + 1);
116
117         /* Decrement the raw block size by the amount of space just free'd */
118         rawsize -= uppersize;
119     }
120
121     /* Check if we can free the space below the allocated block. This is the
122      * case, if the size of the block is at least sizeof (struct freeblock)
123      * bytes and the size of the remaining block is at least of this size,
124      * too. If the lower block is smaller, we will just pass it to the caller
125      * together with the requested aligned block.
126      * Beware: We need an additional struct usedblock in the lower block which
127      * is part of the block that is passed back to the caller.
128      */
129     lowersize = ((unsigned)u - (unsigned)b) - sizeof (struct usedblock);
130     if (lowersize >= sizeof (struct freeblock) &&
131         (rawsize - lowersize) >= sizeof (struct freeblock)) {
132
133         /* b does already point to the raw lower block. Setup the usedblock
134          * structure.
135          */
136         b->size  = lowersize;
137         b->start = b;
138
139         /* Generate a pointer to the user space and free the block */
140         free (b + 1);
141
142         /* Decrement the raw block size by the amount of space just free'd */
143         rawsize -= lowersize;
144
145         /* Set b to the raw user block */
146         b = u - 1;
147     }
148
149     /* u does now point to the user visible block, while b points to the raw
150      * block, and rawsize contains the size of the raw block. Setup the
151      * usedblock structure but beware: If we didn't free the lower block, it
152      * is splitted, which means that we must use u to write the start field,
153      * and b to write the size.
154      */
155     (u-1)->start = b;
156     b->size  = rawsize;
157
158     /* Return the user portion of the aligned block */
159     return u;
160 }
161
162
163