]> git.sur5r.net Git - cc65/blob - libsrc/common/pmemalign.c
Merge pull request #249 from polluks/master
[cc65] / libsrc / common / pmemalign.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                              posix_memalign                               */
4 /*                                                                           */
5 /*                     Allocate an aligned memory block                      */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2004-2005 Ullrich von Bassewitz                                       */
10 /*               Roemerstrasse 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. Alterred source versions must be marked plainly as such, and must not  */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or alterred from any source             */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stddef.h>                     /* define NULL */
37 #include <stdlib.h>                     /* declare function's prototype */
38 #include <_heap.h>
39
40 #include <errno.h>
41 #define EOK             0               /* No errors (non-standard name) */
42
43
44
45 /* This is a very simple version of an aligned memory allocator.  We will
46 ** allocate a greater block, so that we can place the aligned block (that is
47 ** returned) within it.  We use our knowledge about the internal heap
48 ** structures to free the unused parts of the bigger block (the two chunks
49 ** below and above the aligned block).
50 */
51
52
53
54 int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
55 /* Allocate a block of memory with the given "size", which is aligned to a
56 ** memory address that is a multiple of "alignment".  "alignment" MUST NOT be
57 ** zero, and MUST be a power of two; otherwise, this function will return
58 ** EINVAL.  The function returns ENOMEM if not enough memory is available
59 ** to satisfy the request.  "memptr" must point to a variable; that variable
60 ** will return the address of the allocated memory.  Use free() to release that
61 ** allocated block.
62 */
63 {
64     size_t rawsize;
65     size_t uppersize;
66     size_t lowersize;
67     register struct usedblock* b;       /* points to raw Block */
68     register struct usedblock* u;       /* points to User block */
69     register struct usedblock* p;       /* Points to upper block */
70
71     /* Handle requests for zero-sized blocks */
72     if (size == 0) {
73         *memptr = NULL;
74         return EINVAL;
75     }
76
77     /* Test alignment: is it a power of two? There must be only one bit set. */
78     if (alignment == 0 || (alignment & --alignment) != 0) {
79         *memptr = NULL;
80         return EINVAL;
81     }
82
83     /* Augment the block size up to the alignment, and allocate memory.
84     ** We don't need to account for the additional admin. data that's needed to
85     ** manage the used block, because the block returned by malloc() has that
86     ** overhead added one time; and, the worst thing that might happen is that
87     ** we cannot free the upper and lower blocks.
88     */
89     b = malloc (size + alignment);
90
91     /* Handle out-of-memory */
92     if (b == NULL) {
93         *memptr = NULL;
94         return ENOMEM;
95     }
96
97     /* Create (and return) a new pointer that points to the user-visible
98     ** aligned block.
99     */
100     u = *memptr = (struct usedblock*) (((unsigned)b + alignment) & ~alignment);
101
102     /* Get a pointer to the (raw) upper block */
103     p = (struct usedblock*) ((char*)u + size);
104
105     /* Get the raw-block pointer, which is located just below the visible
106     ** unaligned block.  The first word of this raw block is the total size
107     ** of the block, including the admin. space.
108     */
109     b = (b-1)->start;
110     rawsize = b->size;
111
112     /* Check if we can free the space above the user block.  That is the case
113     ** if the size of the block is at least sizeof (struct freeblock) bytes,
114     ** and the size of the remaining block is at least that size, too.
115     ** If the upper block is smaller, then we just will pass it to the caller,
116     ** together with the requested aligned block.
117     */
118     uppersize = rawsize - (lowersize = (char*)p - (char*)b);
119     if (uppersize >= sizeof (struct freeblock) &&
120         lowersize >= sizeof (struct freeblock)) {
121
122         /* Setup the usedblock structure */
123         p->size  = uppersize;
124         p->start = p;
125
126         /* Generate a pointer to the (upper) user space, and free that block */
127         free (p + 1);
128
129         /* Decrease the raw-block size by the amount of space just freed */
130         rawsize = lowersize;
131     }
132
133     /* Check if we can free the space below the user block.  That is the case
134     ** if the size of the block is at least sizeof (struct freeblock) bytes,
135     ** and the size of the remaining block is at least that size, too.  If the
136     ** lower block is smaller, we just will pass it to the caller, together
137     ** with the requested aligned block.
138     ** Beware:  We need an additional struct usedblock, in the lower block,
139     ** which is part of the block that is passed back to the caller.
140     */
141     lowersize = ((char*)u - (char*)b) - sizeof (struct usedblock);
142     if (           lowersize  >= sizeof (struct freeblock) &&
143         (rawsize - lowersize) >= sizeof (struct freeblock)) {
144
145         /* b already points to the raw lower-block.
146         ** Set up the usedblock structure.
147         */
148         b->size  = lowersize;
149         b->start = b;
150
151         /* Generate a pointer to the (lower) user space, and free that block */
152         free (b + 1);
153
154         /* Decrease the raw-block size by the amount of space just freed */
155         rawsize -= lowersize;
156
157         /* Set b to the raw user-block (that will be returned) */
158         b = u - 1;
159     }
160
161     /* u points to the user-visible block, while b points to the raw block,
162     ** and rawsize contains the length of the raw block.  Set up the usedblock
163     ** structure, but beware:  If we didn't free the lower block, then it is
164     ** split; which means that we must use b to write the size,
165     ** and u to write the start field.
166     */
167     b->size = rawsize;
168     (u-1)->start = b;
169
170     return EOK;
171 }
172
173
174