source: Sophya/trunk/SophyaLib/UnixMac/src/CustomNew.cp

Last change on this file was 683, checked in by ansari, 26 years ago

Compilation Mac pour CodeWarrior PRO 5

File size: 8.5 KB
Line 
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>
33extern void (*new_handler)();
34
35
36enum {
37 kPoolSize = 65536L, // Memory is allocated in 64K chunks
38 kBigSize = 1024L // Any allocations bigger than 1K go through the OS directly
39};
40
41typedef 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
46typedef 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
53MemPool* gPoolHead = NULL; // List of pools
54
55static 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
68static 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
89static 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
117static 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
133Coalesce:
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
155void *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
184void 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
211long 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 -----------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.