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