| 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 ----------------------------------------------------------------------- | 
|---|