/* * Check the chunk to see if it's bigger than it needs to be * split the chunk in two if it's larger than size * be sure that the "excess" (i.e., the new chunk) is at the * top of the current chunk. i.e., the new signature * written at memory_pool['chunk'] must be at least 'size' */ void splitChunkIfNecessary(unsigned chunk, int size) { if (memory_pool[chunk] > size + min_chunk_size) { /* need to split... */ int left_over_sig = memory_pool[chunk] - size - 2; // left_over_sig is the signature for the excess chunk unsigned left_over_chunk = chunk + size + 2; // left_over_chunk is the index of the new chunk we'll create setBothSignatures(chunk, size); setBothSignatures(left_over_chunk, left_over_sig); } /* that's it. If the chunk's too small, there's nothing to do * so we don't need an else or nothin */ }
void deallocateMemory(void *p) { /* since the available memory is one position above the signature * the signature can be found at one position below the first * byte of available memory */ int *chunk_address = ((int *)p) - 1; unsigned chunk; int top_signature; int bottom_signature; int size; if ((chunk_address < memory_pool) || (chunk_address >= &memory_pool[pool_size])) { /* due to silliness in the MetroWerks compiler, it is possible * for new to be called before the memory heap is on-line. In that * case the system default heap is used, and not this one. * this generally only happens for global constructors, such as * the stuff associated with cout and cin. * When these objects are destroyed (after main ends) the * runtime system calls delete on all these things but * since our heap is now online, we get all the other heap's memory * back. We're just going to ignore it. The OS will clean up * that memory anyway when the application quits, so its * no big deal. */ /* if you want to see this in action, de-comment the following line */ // cout << "hey! that ain't mine, don't throw that junk at me\n"; return; } chunk = chunk_address - memory_pool; bottom_signature = memory_pool[chunk]; top_signature = memory_pool[topSignature(chunk)]; /* another sanity check, not really insane at all. */ assert(bottom_signature == top_signature && bottom_signature < 0); size = -bottom_signature; setBothSignatures(chunk, size); mergeChunkIfOK(chunk); if (chunk > 0) { // this is not quite silly, chunk could be equal to zero unsigned previous_chunk = predecessorChunk(chunk); mergeChunkIfOK(previous_chunk); } if (mem_leak_check && isEmptyHeap()) { std::cout << "Yay, you do not have a memory leak!" << std::endl; } }
/* * check to see if the following chunk is available, if it is * then merge this chunk with the following chunk */ void mergeChunkIfOK(unsigned chunk) { unsigned next_chunk; int next_chunk_size; int size = memory_pool[chunk]; if (size < 0) { return; } // not OK, this one is busy! next_chunk = successorChunk(chunk); if (next_chunk >= pool_size) { // No good, there is no successor! return; // give up now } next_chunk_size = memory_pool[next_chunk]; if (next_chunk_size > 0) { // it's available! int new_size = size + next_chunk_size + 2; setBothSignatures(chunk, new_size); } /* that's it. we can leave the old signatures in the middle * after all, we'll never see them */ }
void *allocateMemory(unsigned size_in_bytes) { int size; unsigned chunk; int chunk_size; if (!initialized) { initializeHeap(); initialized = true; } if (size_in_bytes == 0) { return nullptr; } size = (size_in_bytes + sizeof(int)-1) / sizeof(int); chunk = findFreeChunk(size); if (always_false) { displayChunks(); } if (chunk == bogus_chunk) { printf("Ah NUTS, out of memory\n"); exit(-1); // forces main to return -1 !immediately! } splitChunkIfNecessary(chunk, size); chunk_size = memory_pool[chunk]; assert(chunk_size >= size); // just a sanity check, pretty insane really setBothSignatures(chunk, -chunk_size); /* OK, all done... all we need to do now is return... oh, * we're supposed to return a pointer, not an index..... */ return &memory_pool[chunk + 1]; }