123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- /* vim: set expandtab ts=4 sw=4: */
- /*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <stdint.h>
- #include "exception/Except.h"
- #include "memory/BufferAllocator.h"
- #include "util/Bits.h"
- #include "util/Identity.h"
- /**
- * TODO: addOnFreeJob adds a job which is only run when the root allocator is freed
- * and it needs to be run when the allocator which called it, or any of that allocator's
- * ancestors is freed, not just the root.
- */
- /* Define alignment as the size of a pointer which is usually 4 or 8 bytes. */
- #define ALIGNMENT sizeof(char*)
- struct Job {
- struct Allocator_OnFreeJob generic;
- struct Allocator* alloc;
- struct Job* next;
- Identity
- };
- /** Internal state for Allocator. */
- struct BufferAllocator {
- struct Allocator generic;
- /** Pointer to the beginning of the buffer. */
- char* basePointer;
- /** Pointer to a pointer to the place in the buffer to allocate the next block of memory. */
- char** pPointer;
- /** Pointer to the end of the buffer. */
- char* const endPointer;
- struct Job* onFree;
- /** Number of onfree jobs which are not yet complete. */
- int outstandingJobs;
- struct Except* onOOM;
- const char* file;
- int line;
- Identity
- };
- /**
- * Get a pointer which is aligned on memory boundries.
- *
- * @param pointer the location where the pointer should be.
- * @param alignedOn how big the word is that the boundry should be aligned on.
- */
- #define getAligned(pointer, alignedOn) \
- ((char*) ((uintptr_t)( ((char*)(pointer)) + (alignedOn) - 1) & ~ ((alignedOn) - 1)))
- /** @see Allocator_malloc() */
- static void* allocatorMalloc(unsigned long length,
- struct Allocator* allocator,
- const char* identFile,
- int identLine)
- {
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) allocator);
- char* pointer = getAligned((*context->pPointer), ALIGNMENT);
- char* endOfAlloc = pointer + length;
- if (endOfAlloc >= context->endPointer) {
- Except_raise(context->onOOM, -1, "BufferAllocator ran out of memory [%s:%d]",
- identFile, identLine);
- }
- if (endOfAlloc < *(context->pPointer)) {
- Except_raise(context->onOOM, -2, "BufferAllocator integer overflow [%s:%d]",
- identFile, identLine);
- }
- (*context->pPointer) = endOfAlloc;
- return (void*) pointer;
- }
- /** @see Allocator->calloc() */
- static void* allocatorCalloc(unsigned long length,
- unsigned long count,
- struct Allocator* allocator,
- const char* identFile,
- int identLine)
- {
- void* pointer = allocatorMalloc(length * count, allocator, identFile, identLine);
- Bits_memset(pointer, 0, length * count);
- return pointer;
- }
- /** @see Allocator->clone() */
- static void* allocatorClone(unsigned long length,
- struct Allocator* allocator,
- const void* toClone,
- const char* identFile,
- int identLine)
- {
- void* pointer = allocatorMalloc(length, allocator, identFile, identLine);
- Bits_memcpy(pointer, toClone, length);
- return pointer;
- }
- /** @see Allocator->realloc() */
- static void* allocatorRealloc(const void* original,
- unsigned long length,
- struct Allocator* allocator,
- const char* identFile,
- int identLine)
- {
- if (original == NULL) {
- return allocatorMalloc(length, allocator, identFile, identLine);
- }
- // Need to pointer to make sure we dont copy too much.
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) allocator);
- char* pointer = *context->pPointer;
- uint32_t amountToClone = (length < (uint32_t)(pointer - (char*)original))
- ? length
- : (uint32_t)(pointer - (char*)original);
- // The likelyhood of nothing having been allocated since is
- // almost 0 so we will always create a new
- // allocation and copy into it.
- void* newAlloc = allocatorMalloc(length, allocator, identFile, identLine);
- Bits_memcpy(newAlloc, original, amountToClone);
- return newAlloc;
- }
- /** @see Allocator->free() */
- static void freeAllocator(struct Allocator* allocator, const char* identFile, int identLine)
- {
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) allocator);
- struct Job* job = context->onFree;
- while (job != NULL) {
- if (job->generic.callback) {
- job->generic.callback(&job->generic);
- context->outstandingJobs++;
- }
- job = job->next;
- }
- }
- static int removeOnFreeJob(struct Allocator_OnFreeJob* toRemove)
- {
- struct Job* j = Identity_cast((struct Job*) toRemove);
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) j->alloc);
- struct Job** jobPtr = &(context->onFree);
- while (*jobPtr != NULL) {
- if (*jobPtr == j) {
- *jobPtr = (*jobPtr)->next;
- return 0;
- }
- jobPtr = &(*jobPtr)->next;
- }
- return -1;
- }
- static int onFreeComplete(struct Allocator_OnFreeJob* job)
- {
- struct BufferAllocator* context =
- Identity_cast((struct BufferAllocator*) ((struct Job*)job)->alloc);
- if (!--context->outstandingJobs) {
- if ((uintptr_t) context > (uintptr_t) context->pPointer) {
- // pPointer points to a destination which is > context unless this is a child alloc.
- return 0;
- }
- // complete
- (*context->pPointer) = context->basePointer;
- }
- return 0;
- }
- /** @see Allocator->onFree() */
- static struct Allocator_OnFreeJob* onFree(struct Allocator* alloc,
- const char* file,
- int line)
- {
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) alloc);
- struct Job* newJob = Allocator_clone(alloc, (&(struct Job) {
- .generic = {
- .cancel = removeOnFreeJob,
- .complete = onFreeComplete
- },
- .alloc = alloc,
- }));
- Identity_set(&newJob->generic);
- struct Job* job = context->onFree;
- if (job == NULL) {
- context->onFree = newJob;
- } else {
- while (job->next != NULL) {
- job = job->next;
- }
- job->next = newJob;
- }
- return &newJob->generic;
- }
- /** @see Allocator_child() */
- static struct Allocator* childAllocator(struct Allocator* alloc,
- const char* file,
- int line)
- {
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) alloc);
- struct BufferAllocator* child =
- allocatorClone(sizeof(struct BufferAllocator), alloc, context, file, line);
- child->file = file;
- child->line = line;
- return &child->generic;
- }
- static void adopt(struct Allocator* alloc, struct Allocator* allocB, const char* file, int line)
- {
- Assert_always(!"Unimplemented");
- }
- /** @see BufferAllocator.h */
- struct Allocator* BufferAllocator_newWithIdentity(void* buffer,
- unsigned long length,
- char* file,
- int line)
- {
- struct FirstAlloc {
- struct BufferAllocator alloc;
- char* pointer;
- };
- struct FirstAlloc tempAlloc = {
- .alloc = {
- .generic = {
- .free = freeAllocator,
- .malloc = allocatorMalloc,
- .calloc = allocatorCalloc,
- .clone = allocatorClone,
- .realloc = allocatorRealloc,
- .child = childAllocator,
- .onFree = onFree,
- .adopt = adopt
- },
- // Align the pointer to do the first write manually.
- .pPointer = NULL,
- .basePointer = getAligned(buffer, sizeof(char*)),
- .endPointer = ((char*)buffer) + length,
- .file = file,
- .line = line
- },
- .pointer = getAligned(buffer, sizeof(char*))
- };
- tempAlloc.alloc.pPointer = &tempAlloc.pointer;
- Identity_set(&tempAlloc.alloc);
- if (tempAlloc.alloc.endPointer < (*tempAlloc.alloc.pPointer)) {
- // int64_t overflow.
- return NULL;
- }
- if (length + (char*) buffer < (*tempAlloc.alloc.pPointer) + sizeof(struct BufferAllocator)) {
- // Not enough space to allocate the context.
- return NULL;
- }
- struct FirstAlloc* alloc = (struct FirstAlloc*) (*tempAlloc.alloc.pPointer);
- Bits_memcpyConst(alloc, &tempAlloc, sizeof(struct FirstAlloc));
- alloc->pointer += sizeof(struct FirstAlloc);
- alloc->alloc.pPointer = &alloc->pointer;
- return &alloc->alloc.generic;
- }
- void BufferAllocator_onOOM(struct Allocator* alloc,
- struct Except* exceptionHandler)
- {
- struct BufferAllocator* context = Identity_cast((struct BufferAllocator*) alloc);
- context->onOOM = exceptionHandler;
- }
|