-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathheapmgrbase.c
241 lines (194 loc) · 7 KB
/
heapmgrbase.c
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*--------------------------------------------------------------------*/
/* heapmgrbase.c */
/* Author: Bob Dondero (similar to K&R version) */
/*--------------------------------------------------------------------*/
#include "heapmgr.h"
#include "checkerbase.h"
#include "chunkbase.h"
#include <stddef.h>
#include <assert.h>
#define __USE_XOPEN_EXTENDED
#include <unistd.h>
/* In lieu of a boolean data type. */
enum {FALSE, TRUE};
/* The minimum number of units to request of the OS. */
enum {MIN_UNITS_FROM_OS = 512};
/*--------------------------------------------------------------------*/
/* The state of the HeapMgr. */
/* The address of the start of the heap. */
static Chunk_T oHeapStart = NULL;
/* The address immediately beyond the end of the heap. */
static Chunk_T oHeapEnd = NULL;
/* The free list is a list of all free Chunks. It is kept in
ascending order by memory address. */
static Chunk_T oFreeList = NULL;
/*--------------------------------------------------------------------*/
/* Request more memory from the operating system -- enough to store
uUnits units. Create a new chunk, and either append it to the
free list after oPrevChunk or increase the size of oPrevChunk.
Return the address of the new (or enlarged) chunk. */
static Chunk_T HeapMgr_getMoreMemory(Chunk_T oPrevChunk,
size_t uUnits)
{
Chunk_T oChunk;
Chunk_T oNewHeapEnd;
size_t uBytes;
if (uUnits < (size_t)MIN_UNITS_FROM_OS)
uUnits = (size_t)MIN_UNITS_FROM_OS;
/* Move the program break. */
uBytes = Chunk_unitsToBytes(uUnits);
oNewHeapEnd = (Chunk_T)((char*)oHeapEnd + uBytes);
if (oNewHeapEnd < oHeapEnd) /* Check for overflow */
return NULL;
if (brk(oNewHeapEnd) == -1)
return NULL;
oChunk = oHeapEnd;
oHeapEnd = oNewHeapEnd;
/* Set the fields of the new chunk. */
Chunk_setUnits(oChunk, uUnits);
Chunk_setNextInList(oChunk, NULL);
/* Add the new chunk to the end of the free list. */
if (oPrevChunk == NULL)
oFreeList = oChunk;
else
Chunk_setNextInList(oPrevChunk, oChunk);
/* Coalesce the new chunk and the previous one if appropriate. */
if (oPrevChunk != NULL)
if (Chunk_getNextInMem(oPrevChunk, oHeapEnd) == oChunk)
{
Chunk_setUnits(oPrevChunk,
Chunk_getUnits(oPrevChunk) + uUnits);
Chunk_setNextInList(oPrevChunk, NULL);
oChunk = oPrevChunk;
}
return oChunk;
}
/*--------------------------------------------------------------------*/
/* If oChunk is close to the right size (as specified by uUnits),
then splice oChunk out of the free list (using oPrevChunk to do
so), and return oChunk. If oChunk is too big, split it and return
the address of the tail end. */
static Chunk_T HeapMgr_useChunk(Chunk_T oChunk,
Chunk_T oPrevChunk, size_t uUnits)
{
Chunk_T oNewChunk;
size_t uChunkUnits;
assert(Chunk_isValid(oChunk, oHeapStart, oHeapEnd));
uChunkUnits = Chunk_getUnits(oChunk);
/* If oChunk is close to the right size, then use it. */
if (uChunkUnits < uUnits + (size_t)MIN_UNITS_PER_CHUNK)
{
if (oPrevChunk == NULL)
oFreeList = Chunk_getNextInList(oChunk);
else
Chunk_setNextInList(oPrevChunk, Chunk_getNextInList(oChunk));
return oChunk;
}
/* oChunk is too big, so use the tail end of it. */
Chunk_setUnits(oChunk, uChunkUnits - uUnits);
oNewChunk = Chunk_getNextInMem(oChunk, oHeapEnd);
Chunk_setUnits(oNewChunk, uUnits);
return oNewChunk;
}
/*--------------------------------------------------------------------*/
void *HeapMgr_malloc(size_t uBytes)
{
Chunk_T oChunk;
Chunk_T oPrevChunk;
Chunk_T oPrevPrevChunk;
size_t uUnits;
if (uBytes == 0)
return NULL;
/* Step 1: Initialize the heap manager if this is the first call. */
if (oHeapStart == NULL)
{
oHeapStart = (Chunk_T)sbrk(0);
oHeapEnd = oHeapStart;
}
assert(Checker_isValid(oHeapStart, oHeapEnd, oFreeList));
/* Step 2: Determine the number of units the new chunk should
contain. */
uUnits = Chunk_bytesToUnits(uBytes);
/* Step 3: For each chunk in the free list... */
oPrevPrevChunk = NULL;
oPrevChunk = NULL;
for (oChunk = oFreeList;
oChunk != NULL;
oChunk = Chunk_getNextInList(oChunk))
{
/* If oChunk is big enough, then use it. */
if (Chunk_getUnits(oChunk) >= uUnits)
{
oChunk = HeapMgr_useChunk(oChunk, oPrevChunk, uUnits);
assert(Checker_isValid(oHeapStart, oHeapEnd, oFreeList));
return Chunk_toPayload(oChunk);
}
oPrevPrevChunk = oPrevChunk;
oPrevChunk = oChunk;
}
/* Step 4: Ask the OS for more memory, and create a new chunk (or
expand the existing chunk) at the end of the free list. */
oChunk = HeapMgr_getMoreMemory(oPrevChunk, uUnits);
if (oChunk == NULL)
{
assert(Checker_isValid(oHeapStart, oHeapEnd, oFreeList));
return NULL;
}
/* If the new large chunk was coalesced with the previous chunk,
then reset the previous chunk. */
if (oChunk == oPrevChunk)
oPrevChunk = oPrevPrevChunk;
/* Step 5: oChunk is big enough, so use it. */
oChunk = HeapMgr_useChunk(oChunk, oPrevChunk, uUnits);
assert(Checker_isValid(oHeapStart, oHeapEnd, oFreeList));
return Chunk_toPayload(oChunk);
}
/*--------------------------------------------------------------------*/
void HeapMgr_free(void *pv)
{
Chunk_T oChunk;
Chunk_T oNextChunk;
Chunk_T oPrevChunk;
assert(Checker_isValid(oHeapStart, oHeapEnd, oFreeList));
if (pv == NULL)
return;
oChunk = Chunk_fromPayload(pv);
assert(Chunk_isValid(oChunk, oHeapStart, oHeapEnd));
/* Step 1: Traverse the free list to find the correct spot for
oChunk. (The free list is kept in ascending order by memory
address.) */
oPrevChunk = NULL;
oNextChunk = oFreeList;
while ((oNextChunk != NULL) && (oNextChunk < oChunk))
{
oPrevChunk = oNextChunk;
oNextChunk = Chunk_getNextInList(oNextChunk);
}
/* Step 2: Insert oChunk into the free list. */
if (oPrevChunk == NULL)
oFreeList = oChunk;
else
Chunk_setNextInList(oPrevChunk, oChunk);
Chunk_setNextInList(oChunk, oNextChunk);
/* Step 3: If appropriate, coalesce the given chunk and the next
one. */
if (oNextChunk != NULL)
if (Chunk_getNextInMem(oChunk, oHeapEnd) == oNextChunk)
{
Chunk_setUnits(oChunk,
Chunk_getUnits(oChunk) + Chunk_getUnits(oNextChunk));
Chunk_setNextInList(oChunk,
Chunk_getNextInList(oNextChunk));
}
/* Step 4: If appropriate, coalesce the given chunk and the previous
one. */
if (oPrevChunk != NULL)
if (Chunk_getNextInMem(oPrevChunk, oHeapEnd) == oChunk)
{
Chunk_setUnits(oPrevChunk,
Chunk_getUnits(oPrevChunk) + Chunk_getUnits(oChunk));
Chunk_setNextInList(oPrevChunk,
Chunk_getNextInList(oChunk));
}
assert(Checker_isValid(oHeapStart, oHeapEnd, oFreeList));
}