| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- /*
- * Copyright (c) 2012-2022 Daniele Bartolini et al.
- * License: https://github.com/crownengine/crown/blob/master/LICENSE
- */
- #include "core/error/error.inl"
- #include "core/memory/allocator.h"
- #include "core/memory/globals.h"
- #include "core/memory/memory.inl"
- #include "core/thread/scoped_mutex.inl"
- #include <stdlib.h> // malloc
- #include <string.h> // memcpy
- // void* operator new(size_t) throw (std::bad_alloc)
- // {
- // CE_FATAL("operator new forbidden");
- // return NULL;
- // }
- // void* operator new[](size_t) throw (std::bad_alloc)
- // {
- // CE_FATAL("operator new[] forbidden");
- // return NULL;
- // }
- // void operator delete(void*) throw ()
- // {
- // CE_FATAL("operator delete forbidden");
- // }
- // void operator delete[](void*) throw ()
- // {
- // CE_FATAL("operator delete[] forbidden");
- // }
- namespace crown
- {
- void *Allocator::reallocate(void *data, u32 size, u32 align)
- {
- CE_UNUSED(data);
- CE_UNUSED(size);
- CE_UNUSED(align);
- CE_FATAL("reallocate() not supported.");
- return NULL;
- }
- namespace memory
- {
- // Header stored at the beginning of a memory allocation to indicate the
- // size of the allocated data.
- struct Header
- {
- u32 size;
- };
- // If we need to align the memory allocation we pad the header with this
- // value after storing the size. That way we can
- const u32 HEADER_PAD_VALUE = 0xffffffffu;
- // Given a pointer to the header, returns a pointer to the data that follows it.
- inline void *data_pointer(Header *header, u32 align)
- {
- void *p = header + 1;
- return memory::align_top(p, align);
- }
- // Given a pointer to the data, returns a pointer to the header before it.
- inline Header *header(const void *data)
- {
- u32 *p = (u32 *)data;
- while (p[-1] == HEADER_PAD_VALUE)
- --p;
- return (Header *)p - 1;
- }
- // Stores the size in the header and pads with HEADER_PAD_VALUE up to the
- // data pointer.
- inline void fill(Header *header, const void *data, u32 size)
- {
- header->size = size;
- u32 *p = (u32 *)(header + 1);
- while (p < data)
- *p++ = HEADER_PAD_VALUE;
- }
- inline u32 actual_allocation_size(u32 size, u32 align)
- {
- return size + align + sizeof(Header);
- }
- inline void pad(Header *header, const void *data)
- {
- u32 *p = (u32 *)(header + 1);
- while (p != data) {
- *p = HEADER_PAD_VALUE;
- p++;
- }
- }
- /// Allocator based on C malloc().
- struct HeapAllocator : public Allocator
- {
- Mutex _mutex;
- u32 _allocated_size;
- u32 _allocation_count;
- HeapAllocator()
- : _allocated_size(0)
- , _allocation_count(0)
- {
- }
- ~HeapAllocator()
- {
- CE_ASSERT(_allocation_count == 0 && total_allocated() == 0
- , "Missing %u deallocations causing a leak of %u bytes"
- , _allocation_count
- , total_allocated()
- );
- }
- /// @copydoc Allocator::allocate()
- void *allocate(u32 size, u32 align = Allocator::DEFAULT_ALIGN) override
- {
- ScopedMutex sm(_mutex);
- u32 actual_size = actual_allocation_size(size, align);
- Header *h = (Header *)malloc(actual_size);
- h->size = actual_size;
- void *data = memory::align_top(h + 1, align);
- pad(h, data);
- _allocated_size += actual_size;
- _allocation_count++;
- return data;
- }
- /// @copydoc Allocator::deallocate()
- void deallocate(void *data) override
- {
- ScopedMutex sm(_mutex);
- if (!data)
- return;
- Header *h = header(data);
- _allocated_size -= h->size;
- _allocation_count--;
- free(h);
- }
- void *reallocate(void *data, u32 size, u32 align) override
- {
- if (!data)
- return allocate((u32)size, (u32)align == 0 ? 16 : (u32)align);
- if (size == 0) {
- deallocate(data);
- return NULL;
- }
- // Figure out the size of data.
- const Header *data_header = header(data);
- const char *data_end = (char *)data_header + data_header->size;
- const u32 data_size = u32(data_end - (char *)data);
- // Simulate realloc().
- void *p = allocate((u32)size, (u32)align == 0 ? 16 : (u32)align);
- memcpy(p, data, min(data_size, size));
- deallocate(data);
- return p;
- }
- /// @copydoc Allocator::allocated_size()
- u32 allocated_size(const void *ptr) override
- {
- return get_size(ptr);
- }
- /// @copydoc Allocator::total_allocated()
- u32 total_allocated() override
- {
- ScopedMutex sm(_mutex);
- return _allocated_size;
- }
- /// Returns the size in bytes of the block of memory pointed by @a data
- u32 get_size(const void *data)
- {
- ScopedMutex sm(_mutex);
- Header *h = header(data);
- return h->size;
- }
- };
- // Copyright (C) 2012 Bitsquid AB
- // License: https://bitbucket.org/bitsquid/foundation/src/default/LICENCSE
- //
- // An allocator used to allocate temporary "scratch" memory. The allocator
- // uses a fixed size ring buffer to services the requests.
- //
- // Memory is always always allocated linearly. An allocation pointer is
- // advanced through the buffer as memory is allocated and wraps around at
- // the end of the buffer. Similarly, a free pointer is advanced as memory
- // is freed.
- //
- // It is important that the scratch allocator is only used for short-lived
- // memory allocations. A long lived allocator will lock the "free" pointer
- // and prevent the "allocate" pointer from proceeding past it, which means
- // the ring buffer can't be used.
- //
- // If the ring buffer is exhausted, the scratch allocator will use its backing
- // allocator to allocate memory instead.
- struct ScratchAllocator : public Allocator
- {
- Mutex _mutex;
- Allocator &_backing;
- // Start and end of the ring buffer.
- char *_begin, *_end;
- // Pointers to where to allocate memory and where to free memory.
- char *_allocate, *_free;
- /// Creates a ScratchAllocator. The allocator will use the backing
- /// allocator to create the ring buffer and to service any requests
- /// that don't fit in the ring buffer.
- ///
- /// size specifies the size of the ring buffer.
- ScratchAllocator(Allocator &backing, u32 size)
- : _backing(backing)
- {
- _begin = (char *)_backing.allocate(size);
- _end = _begin + size;
- _allocate = _begin;
- _free = _begin;
- }
- ~ScratchAllocator()
- {
- CE_ASSERT(_free == _allocate, "Memory leak");
- _backing.deallocate(_begin);
- }
- bool in_use(void *p)
- {
- if (_free == _allocate)
- return false;
- if (_allocate > _free)
- return p >= _free && p < _allocate;
- return p >= _free || p < _allocate;
- }
- void *allocate(u32 size, u32 align) override
- {
- ScopedMutex sm(_mutex);
- CE_ASSERT(align % 4 == 0, "Must be 4-byte aligned");
- size = ((size + 3)/4)*4;
- char *p = _allocate;
- Header *h = (Header *)p;
- char *data = (char *)data_pointer(h, align);
- p = data + size;
- // Reached the end of the buffer, wrap around to the beginning.
- if (p > _end) {
- h->size = u32(_end - (char *)h) | 0x80000000u;
- p = _begin;
- h = (Header *)p;
- data = (char *)data_pointer(h, align);
- p = data + size;
- }
- // If the buffer is exhausted use the backing allocator instead.
- if (in_use(p))
- return _backing.allocate(size, align);
- fill(h, data, u32(p - (char *)h));
- _allocate = p;
- return data;
- }
- void deallocate(void *p) override
- {
- ScopedMutex sm(_mutex);
- if (!p)
- return;
- if (p < _begin || p >= _end) {
- _backing.deallocate(p);
- return;
- }
- // Mark this slot as free
- Header *h = header(p);
- CE_ASSERT((h->size & 0x80000000u) == 0, "Not free");
- h->size = h->size | 0x80000000u;
- // Advance the free pointer past all free slots.
- while (_free != _allocate) {
- Header *h = (Header *)_free;
- if ((h->size & 0x80000000u) == 0)
- break;
- _free += h->size & 0x7fffffffu;
- if (_free == _end)
- _free = _begin;
- }
- }
- u32 allocated_size(const void *p) override
- {
- ScopedMutex sm(_mutex);
- Header *h = header(p);
- return h->size - u32((char *)p - (char *)h);
- }
- u32 total_allocated() override
- {
- ScopedMutex sm(_mutex);
- return u32(_end - _begin);
- }
- };
- } // namespace memory
- namespace memory_globals
- {
- using namespace memory;
- static char _buffer[sizeof(HeapAllocator) + sizeof(ScratchAllocator)];
- static HeapAllocator *_default_allocator;
- static ScratchAllocator *_default_scratch_allocator;
- void init()
- {
- _default_allocator = new (_buffer) HeapAllocator();
- _default_scratch_allocator = new (_buffer + sizeof(HeapAllocator)) ScratchAllocator(*_default_allocator, 1024*1024);
- }
- void shutdown()
- {
- _default_scratch_allocator->~ScratchAllocator();
- _default_allocator->~HeapAllocator();
- }
- } // namespace memory_globals
- Allocator &default_allocator()
- {
- return *memory_globals::_default_allocator;
- }
- Allocator &default_scratch_allocator()
- {
- return *memory_globals::_default_scratch_allocator;
- }
- } // namespace crown
|