File: pmemalign.c

package info (click to toggle)
cc65 2.19-2
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 20,268 kB
  • sloc: ansic: 117,151; asm: 66,339; pascal: 4,248; makefile: 1,009; perl: 607
file content (174 lines) | stat: -rw-r--r-- 7,741 bytes parent folder | download | duplicates (3)
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;
}