1 | // CustomNew.p by François Pottier (pottier@clipper.ens.fr)
|
---|
2 | // December 1st, 1994
|
---|
3 |
|
---|
4 | // --- WHAT IT IS - HOW TO USE IT ---------------------------------------------------------------------------------------
|
---|
5 |
|
---|
6 | // This file redefines the standard new and delete operators.
|
---|
7 |
|
---|
8 | // Advantages over MetroWerks's definition:
|
---|
9 | // 1. Memory is taken from the application heap or the temporary heap (preferrably the first) for greater flexibility.
|
---|
10 | // 2. Unused memory pools are released to the system.
|
---|
11 | // 3. Less fragmentation, because the MetroWerks delete operator doesn't always join adjacent free blocks.
|
---|
12 | // 4. If pool allocation fails, new doesn't revert to calling NewPtr for each block. It's a good thing, because otherwise the program
|
---|
13 | // would just slow down to a crawl (NewPtr is much slower than new) and die a horrible death, should the Toolbox run out of space.
|
---|
14 |
|
---|
15 | // Finally, I keep a count of used blocks and free blocks, which can help assess the program's memory usage and fragmentation.
|
---|
16 |
|
---|
17 | // How to use:
|
---|
18 | // - Add CustomNew.cp to your project.
|
---|
19 | // - Use CPlusPlusNoNew.Lib instead of CPlusPlus.Lib on 68k; use MWCRuntimeNoNew.Lib instead of MWCRuntime.Lib on PPC.
|
---|
20 | // MWCRuntimeNoNew.Lib isn't provided by MetroWerks, you have to compile it yourself using MetroWerks' sources.
|
---|
21 |
|
---|
22 | // --- LEGAL TERMS -----------------------------------------------------------------------------------------------------
|
---|
23 |
|
---|
24 | // Although I wrote these routines entirely myself, they are loosely based on MetroWerks code.
|
---|
25 | // As far as I am concerned, you can use them freely in any of your programs. My only request is that you notify me if you find
|
---|
26 | // bugs or devise improvements. My email address is pottier@clipper.ens.fr
|
---|
27 |
|
---|
28 | // Remember, this code has NOT been THOROUGHLY TESTED!
|
---|
29 |
|
---|
30 | // --- OK FOLKS, LET'S HAVE THE CODE ------------------------------------------------------------------------------------
|
---|
31 |
|
---|
32 | #include <stddef.h>
|
---|
33 | extern void (*new_handler)();
|
---|
34 |
|
---|
35 |
|
---|
36 | enum {
|
---|
37 | kPoolSize = 65536L, // Memory is allocated in 64K chunks
|
---|
38 | kBigSize = 1024L // Any allocations bigger than 1K go through the OS directly
|
---|
39 | };
|
---|
40 |
|
---|
41 | typedef struct FreeMemList { // Free blocks are linked together in a list
|
---|
42 | struct FreeMemList *next; // Pointer to next free block
|
---|
43 | long size; // Size of this block
|
---|
44 | } FreeMemList;
|
---|
45 |
|
---|
46 | typedef struct MemPool { // Memory is allocated from pools (locked handles)
|
---|
47 | struct MemPool *next; // Pointer to next pool
|
---|
48 | FreeMemList *freeHead; // Start of free blocks list
|
---|
49 | long usedBlocks, freeBlocks; // Statistics
|
---|
50 | Byte data[]; // Blocks
|
---|
51 | } MemPool;
|
---|
52 |
|
---|
53 | MemPool* gPoolHead = NULL; // List of pools
|
---|
54 |
|
---|
55 | static Handle MakeLockedHandle (long size) // Create a locked handle from the application heap
|
---|
56 | { // or the temporary zone
|
---|
57 | Handle h;
|
---|
58 | OSErr e;
|
---|
59 |
|
---|
60 | h = NewHandle(size);
|
---|
61 | if (h == NULL)
|
---|
62 | h = TempNewHandle(size, &e);
|
---|
63 | if (h)
|
---|
64 | HLock(h);
|
---|
65 | return h;
|
---|
66 | }
|
---|
67 |
|
---|
68 | static Boolean NewPool (void) // Add a new pool to the pool list
|
---|
69 | {
|
---|
70 | Handle h;
|
---|
71 | MemPool* pool;
|
---|
72 |
|
---|
73 | h = MakeLockedHandle(kPoolSize);
|
---|
74 | if (h == NULL)
|
---|
75 | return false;
|
---|
76 | pool = (MemPool*) *h;
|
---|
77 |
|
---|
78 | pool->next = gPoolHead;
|
---|
79 | pool->freeHead = (FreeMemList*) pool->data;
|
---|
80 | pool->freeHead->next = NULL;
|
---|
81 | pool->freeHead->size = (Ptr) pool + kPoolSize - (Ptr) pool->data;
|
---|
82 | pool->usedBlocks = 0;
|
---|
83 | pool->freeBlocks = 1;
|
---|
84 | gPoolHead = pool;
|
---|
85 |
|
---|
86 | return true;
|
---|
87 | }
|
---|
88 |
|
---|
89 | static void* AllocFromPool (MemPool* pool, long size)
|
---|
90 | {
|
---|
91 | FreeMemList *block; // The block under consideration
|
---|
92 | FreeMemList* *link; // The place which points to block in the free blocks list
|
---|
93 | Ptr result;
|
---|
94 |
|
---|
95 | for (link = &pool->freeHead; (block = *link); link = &block->next) // Walk the free blocks list
|
---|
96 | if (size <= block->size) { // If a free block is big enough
|
---|
97 | if (block->size >= size + sizeof(FreeMemList)) { // If it's really big enough, we can split it
|
---|
98 | block->size -= size; // The low part remains a free block
|
---|
99 | result = (Ptr) block + block->size; // The high part becomes allocated
|
---|
100 | }
|
---|
101 | else { // If it's just big enough, we can't split it
|
---|
102 | *link = block->next; // So we remove it from the free blocks list
|
---|
103 | result = (Ptr) block; // And we mark it all as allocated
|
---|
104 | pool->freeBlocks--;
|
---|
105 | }
|
---|
106 | pool->usedBlocks++;
|
---|
107 | * (long*) result = size; // The first four bytes contain the new block's size
|
---|
108 | return (result + 4);
|
---|
109 | }
|
---|
110 |
|
---|
111 | return NULL;
|
---|
112 | }
|
---|
113 |
|
---|
114 | // We assume the free blocks list to be always ordered from bottom to top, and it is because we insert new free blocks in the right
|
---|
115 | // place. This allows us to easily join adjacent free blocks into a single one when deleting a block, thus reducing stupid fragmentation.
|
---|
116 |
|
---|
117 | static void DeleteFromPool (MemPool* pool, Ptr pointer, long size)
|
---|
118 | {
|
---|
119 | FreeMemList *block, *previous, *next;
|
---|
120 | FreeMemList* *link;
|
---|
121 |
|
---|
122 | previous = next = NULL; // Look for any adjacent free blocks
|
---|
123 | for (link = &pool->freeHead; (block = *link); link = &block->next) // Walk the free blocks list
|
---|
124 | if (pointer == (Ptr) block + block->size) // We have found a free block just before ours
|
---|
125 | previous = block;
|
---|
126 | else if (pointer + size == (Ptr) block) { // We have found a free block just after ours
|
---|
127 | next = block;
|
---|
128 | goto Coalesce; // Stop searching now, there's nothing more up there and we will need the value of link
|
---|
129 | }
|
---|
130 | else if (pointer + size < (Ptr) block) // We have found no adjacent block
|
---|
131 | goto Coalesce; // The current value of link tells us where to insert the new free block
|
---|
132 |
|
---|
133 | Coalesce:
|
---|
134 | pool->usedBlocks--;
|
---|
135 | if (previous != NULL && next != NULL) { // We now have 3 free blocks, join them
|
---|
136 | previous->size += size + next->size;
|
---|
137 | previous->next = next->next;
|
---|
138 | pool->freeBlocks--;
|
---|
139 | }
|
---|
140 | else if (previous) // Expand the free block below
|
---|
141 | previous->size += size;
|
---|
142 | else if (next) { // Expand the free block above
|
---|
143 | ((FreeMemList*) pointer)->next = next->next;
|
---|
144 | ((FreeMemList*) pointer)->size = next->size + size;
|
---|
145 | *link = (FreeMemList*) pointer;
|
---|
146 | }
|
---|
147 | else { // Add a free block to the list
|
---|
148 | ((FreeMemList*) pointer)->next = *link;
|
---|
149 | ((FreeMemList*) pointer)->size = size;
|
---|
150 | *link = (FreeMemList*) pointer;
|
---|
151 | pool->freeBlocks++;
|
---|
152 | }
|
---|
153 | }
|
---|
154 |
|
---|
155 | void *operator new (size_t size)
|
---|
156 | {
|
---|
157 | MemPool* pool;
|
---|
158 | void* result;
|
---|
159 |
|
---|
160 | size = (size & 0xFFFFFFFC) + 8; // Make sure size is a multiple of 4, and add 4 bytes to store the block size
|
---|
161 |
|
---|
162 | while(1) {
|
---|
163 | if (size >= kBigSize) { // If the requested size is big
|
---|
164 | Handle h = MakeLockedHandle(size); // Use the Memory Manager directly
|
---|
165 | if (h == NULL)
|
---|
166 | if (new_handler) new_handler(); else return(NULL);
|
---|
167 | * (long*) *h = -1L; // This special value serves to recognize standalone blocks
|
---|
168 | return (*h + 4);
|
---|
169 | }
|
---|
170 |
|
---|
171 | pool = gPoolHead; // Try to find a pool with enough room
|
---|
172 | while (pool) {
|
---|
173 | if (result = AllocFromPool(pool, size))
|
---|
174 | return result;
|
---|
175 | pool = pool->next;
|
---|
176 | }
|
---|
177 |
|
---|
178 | if (NewPool()) // If no pool has enough space, create a new pool
|
---|
179 | return AllocFromPool(gPoolHead, size);
|
---|
180 | if (new_handler) new_handler(); else return(NULL); // If we can't create a new pool, fail - *don't* revert to NewPtr, it's too damn slow and would hopelessly fill up the heap
|
---|
181 | }
|
---|
182 | }
|
---|
183 |
|
---|
184 | void operator delete (void *pointer)
|
---|
185 | {
|
---|
186 | long size;
|
---|
187 | MemPool* pool;
|
---|
188 | MemPool* *link;
|
---|
189 |
|
---|
190 | if (pointer) {
|
---|
191 | pointer = (Ptr) pointer - 4; // Retrieve the block size, which is stored 4 bytes below
|
---|
192 | size = * (long*) pointer;
|
---|
193 |
|
---|
194 | if (size == -1L) // If the size tag is -1, the block is a handle
|
---|
195 | DisposeHandle(RecoverHandle((Ptr) pointer));
|
---|
196 | else {
|
---|
197 | link = &gPoolHead; // Find which pools the block belongs to
|
---|
198 | while ((pool = *link) && ((Ptr) pointer < (Ptr) pool || (Ptr) pointer > (Ptr) pool + kPoolSize))
|
---|
199 | link = &pool->next;
|
---|
200 |
|
---|
201 | DeleteFromPool(pool, (Ptr) pointer, size); // Delete the block from this pool
|
---|
202 |
|
---|
203 | if (pool->usedBlocks == 0) { // If this pool isn't used any more
|
---|
204 | *link = pool->next; // Remove it from the pool queue
|
---|
205 | DisposeHandle(RecoverHandle((Ptr) pool)); // And release it
|
---|
206 | }
|
---|
207 | }
|
---|
208 | }
|
---|
209 | }
|
---|
210 |
|
---|
211 | long BytesTakenFromTempZone (void) // For informational purposes, returns the number of
|
---|
212 | { // bytes taken from the Multifinder zone. Note that this
|
---|
213 | MemPool* pool; // figure takes only pools into accounts, not stand-alone
|
---|
214 | Handle h; // blocks.
|
---|
215 | long size;
|
---|
216 |
|
---|
217 | size = 0;
|
---|
218 | for (pool = gPoolHead; pool; pool = pool->next) {
|
---|
219 | h = RecoverHandle((Ptr) pool);
|
---|
220 | if (HandleZone(h) != ApplicZone())
|
---|
221 | size += GetHandleSize(h);
|
---|
222 | }
|
---|
223 | return size;
|
---|
224 | }
|
---|
225 |
|
---|
226 | // THAT'S ALL FOLKS. HOPE YOU ENJOYED THE SHOW ----------------------------------------------------------------------- |
---|