[683] | 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 ----------------------------------------------------------------------- |
---|