/* 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 .
*/
struct BufferAllocator_pvt;
#define Allocator_Provider_CONTEXT_TYPE struct BufferAllocator_pvt
#include "exception/Except.h"
#include "memory/BufferAllocator.h"
#include "util/Bits.h"
#include "util/Identity.h"
#include
/**
* TODO(cjd): 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*)
/** Internal state for Allocator. */
struct BufferAllocator_pvt
{
/** Pointer to the beginning of the buffer. */
char* const basePointer;
/** Pointer to the place in the buffer to allocate the next block of memory. */
char* pointer;
/** Pointer to the end of the buffer. */
char* const endPointer;
struct Except* onOOM;
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(struct BufferAllocator_pvt* context, unsigned long length)
{
Identity_check(context);
char* pointer = getAligned(context->pointer, ALIGNMENT);
char* endOfAlloc = pointer + length;
if (endOfAlloc >= context->endPointer) {
Except_throw(context->onOOM, "BufferAllocator ran out of memory.");
}
if (endOfAlloc < context->pointer) {
Except_throw(context->onOOM, "BufferAllocator integer overflow.");
}
context->pointer = endOfAlloc;
return (void*) pointer;
}
static void* provideMemory(struct BufferAllocator_pvt* context,
struct Allocator_Allocation* original,
unsigned long size,
struct Allocator* group)
{
if (original == NULL) {
return allocatorMalloc(context, size);
}
if (((char*)original) + original->size == context->pointer) {
// This is reallocating the last allocation.
// clear the allocation then let allocatorMalloc() recreate it.
context->pointer = (char*)original;
}
if (size == 0) {
return NULL;
}
void* newAlloc = allocatorMalloc(context, size);
if (newAlloc != original) {
Assert_true((char*)newAlloc > (char*)original + original->size);
Bits_memcpy(newAlloc, original, original->size);
}
return newAlloc;
}
/** @see BufferAllocator.h */
struct Allocator* BufferAllocator__new(void* buffer,
unsigned long length,
char* file,
int line)
{
struct BufferAllocator_pvt stackAlloc = {
.basePointer = buffer,
.pointer = buffer,
.endPointer = ((char*)buffer) + length
};
Identity_set(&stackAlloc);
struct BufferAllocator_pvt* alloc =
allocatorMalloc(&stackAlloc, sizeof(struct BufferAllocator_pvt));
Bits_memcpyConst(alloc, &stackAlloc, sizeof(struct BufferAllocator_pvt));
return Allocator_new(0, provideMemory, alloc, file, line);
}