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