v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
268
source/core/StarBlockAllocator.hpp
Normal file
268
source/core/StarBlockAllocator.hpp
Normal file
|
@ -0,0 +1,268 @@
|
|||
#ifndef STAR_BLOCK_ALLOCATOR_HPP
|
||||
#define STAR_BLOCK_ALLOCATOR_HPP
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <limits>
|
||||
#include <typeindex>
|
||||
|
||||
#include "StarException.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
// Constant size only allocator using fixed size blocks of memory. much faster
|
||||
// than general purpose allocators, but not thread safe. Useful as the
|
||||
// allocator for containers that mostly allocate one element at a time, such as
|
||||
// std::list, std::map, std::set etc.
|
||||
template <typename T, size_t BlockSize>
|
||||
class BlockAllocator {
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
typedef T* pointer;
|
||||
typedef T const* const_pointer;
|
||||
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
|
||||
// Allocator can be shared, but since it is NOT thread safe this should not
|
||||
// be done by default.
|
||||
typedef std::false_type propagate_on_container_copy_assignment;
|
||||
typedef std::true_type propagate_on_container_move_assignment;
|
||||
typedef std::true_type propagate_on_container_swap;
|
||||
|
||||
template <class U>
|
||||
struct rebind {
|
||||
typedef BlockAllocator<U, BlockSize> other;
|
||||
};
|
||||
|
||||
BlockAllocator();
|
||||
// Copy constructed BlockAllocators of the same type share underlying
|
||||
// resources.
|
||||
BlockAllocator(BlockAllocator const& other) = default;
|
||||
BlockAllocator(BlockAllocator&& other) = default;
|
||||
// Copy constructed BlockAllocators of different type share no resources
|
||||
template <class U>
|
||||
BlockAllocator(BlockAllocator<U, BlockSize> const& other);
|
||||
|
||||
BlockAllocator& operator=(BlockAllocator const& rhs) = default;
|
||||
BlockAllocator& operator=(BlockAllocator&& rhs) = default;
|
||||
|
||||
// If n is != 1, will fall back on std::allocator<T>
|
||||
T* allocate(size_t n);
|
||||
void deallocate(T* p, size_t n);
|
||||
|
||||
template <typename... Args>
|
||||
void construct(pointer p, Args&&... args) const;
|
||||
void destroy(pointer p) const;
|
||||
|
||||
// BlockAllocator will always be != to any other BlockAllocator instance
|
||||
template <class U>
|
||||
bool operator==(BlockAllocator<U, BlockSize> const& rhs) const;
|
||||
template <class U>
|
||||
bool operator!=(BlockAllocator<U, BlockSize> const& rhs) const;
|
||||
|
||||
private:
|
||||
template <typename OtherT, size_t OtherBlockSize>
|
||||
friend class BlockAllocator;
|
||||
|
||||
using ChunkIndex =
|
||||
std::conditional_t<BlockSize <= std::numeric_limits<uint8_t>::max(), uint8_t,
|
||||
std::conditional_t<BlockSize <= std::numeric_limits<uint16_t>::max(), uint16_t,
|
||||
std::conditional_t<BlockSize <= std::numeric_limits<uint32_t>::max(), uint32_t,
|
||||
std::conditional_t<BlockSize <= std::numeric_limits<uint64_t>::max(), uint64_t, uintmax_t>>>>;
|
||||
|
||||
static ChunkIndex const NullChunkIndex = std::numeric_limits<ChunkIndex>::max();
|
||||
|
||||
struct Unallocated {
|
||||
ChunkIndex prev;
|
||||
ChunkIndex next;
|
||||
};
|
||||
|
||||
typedef std::aligned_union_t<0, T, Unallocated> Chunk;
|
||||
|
||||
struct Block {
|
||||
T* allocate();
|
||||
void deallocate(T* ptr);
|
||||
|
||||
bool full() const;
|
||||
bool empty() const;
|
||||
|
||||
Chunk* chunkPointer(ChunkIndex chunkIndex);
|
||||
|
||||
std::array<Chunk, BlockSize> chunks;
|
||||
ChunkIndex firstUnallocated = NullChunkIndex;
|
||||
ChunkIndex allocationCount = 0;
|
||||
};
|
||||
|
||||
struct Data {
|
||||
std::vector<unique_ptr<Block>> blocks;
|
||||
Block* unfilledBlock;
|
||||
std::allocator<T> multiAllocator;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::type_index, shared_ptr<void>> BlockAllocatorFamily;
|
||||
|
||||
static Data* getAllocatorData(BlockAllocatorFamily& family);
|
||||
|
||||
shared_ptr<BlockAllocatorFamily> m_family;
|
||||
Data* m_data;
|
||||
};
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
BlockAllocator<T, BlockSize>::BlockAllocator() {
|
||||
m_family = make_shared<BlockAllocatorFamily>();
|
||||
m_data = getAllocatorData(*m_family);
|
||||
m_data->blocks.reserve(32);
|
||||
m_data->unfilledBlock = nullptr;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
template <class U>
|
||||
BlockAllocator<T, BlockSize>::BlockAllocator(BlockAllocator<U, BlockSize> const& other)
|
||||
: m_family(other.m_family) {
|
||||
m_data = getAllocatorData(*m_family);
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
T* BlockAllocator<T, BlockSize>::allocate(size_t n) {
|
||||
if (n == 1) {
|
||||
if (m_data->unfilledBlock == nullptr) {
|
||||
for (auto const& p : m_data->blocks) {
|
||||
if (!p->full()) {
|
||||
m_data->unfilledBlock = p.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_data->unfilledBlock) {
|
||||
auto block = make_unique<Block>();
|
||||
m_data->unfilledBlock = block.get();
|
||||
auto sortedPosition = std::lower_bound(m_data->blocks.begin(), m_data->blocks.end(), block.get(), [](std::unique_ptr<Block> const& a, Block* b) {
|
||||
return a.get() < b;
|
||||
});
|
||||
m_data->blocks.insert(sortedPosition, move(block));
|
||||
}
|
||||
}
|
||||
|
||||
auto allocated = m_data->unfilledBlock->allocate();
|
||||
if (m_data->unfilledBlock->full())
|
||||
m_data->unfilledBlock = nullptr;
|
||||
return allocated;
|
||||
} else {
|
||||
return m_data->multiAllocator.allocate(n);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
void BlockAllocator<T, BlockSize>::deallocate(T* p, size_t n) {
|
||||
if (n == 1) {
|
||||
starAssert(p);
|
||||
|
||||
auto i = std::upper_bound(m_data->blocks.begin(), m_data->blocks.end(), p, [](T* a, std::unique_ptr<Block> const& b) {
|
||||
return a < (T*)b->chunkPointer(0);
|
||||
});
|
||||
|
||||
starAssert(i != m_data->blocks.begin());
|
||||
--i;
|
||||
|
||||
(*i)->deallocate(p);
|
||||
|
||||
if (!m_data->unfilledBlock) {
|
||||
m_data->unfilledBlock = i->get();
|
||||
} else if ((*i)->empty()) {
|
||||
if (m_data->unfilledBlock != i->get())
|
||||
m_data->blocks.erase(i);
|
||||
}
|
||||
} else {
|
||||
m_data->multiAllocator.deallocate(p, n);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
template <typename... Args>
|
||||
void BlockAllocator<T, BlockSize>::construct(pointer p, Args&&... args) const {
|
||||
new (p) T(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
void BlockAllocator<T, BlockSize>::destroy(pointer p) const {
|
||||
p->~T();
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
template <class U>
|
||||
bool BlockAllocator<T, BlockSize>::operator==(BlockAllocator<U, BlockSize> const& rhs) const {
|
||||
return m_family == rhs.m_family;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
template <class U>
|
||||
bool BlockAllocator<T, BlockSize>::operator!=(BlockAllocator<U, BlockSize> const& rhs) const {
|
||||
return m_family != rhs.m_family;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
T* BlockAllocator<T, BlockSize>::Block::allocate() {
|
||||
starAssert(allocationCount < BlockSize);
|
||||
|
||||
T* allocated;
|
||||
if (firstUnallocated == NullChunkIndex) {
|
||||
allocated = (T*)chunkPointer(allocationCount);
|
||||
} else {
|
||||
void* chunk = chunkPointer(firstUnallocated);
|
||||
starAssert(((Unallocated*)chunk)->prev == NullChunkIndex);
|
||||
firstUnallocated = ((Unallocated*)chunk)->next;
|
||||
if (firstUnallocated != NullChunkIndex)
|
||||
((Unallocated*)chunkPointer(firstUnallocated))->prev = NullChunkIndex;
|
||||
allocated = (T*)chunk;
|
||||
}
|
||||
|
||||
++allocationCount;
|
||||
return allocated;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
void BlockAllocator<T, BlockSize>::Block::deallocate(T* ptr) {
|
||||
starAssert(allocationCount > 0);
|
||||
|
||||
ChunkIndex chunkIndex = ptr - (T*)chunkPointer(0);
|
||||
starAssert((T*)chunkPointer(chunkIndex) == ptr);
|
||||
|
||||
auto c = (Unallocated*)chunkPointer(chunkIndex);
|
||||
c->prev = NullChunkIndex;
|
||||
c->next = firstUnallocated;
|
||||
if (firstUnallocated != NullChunkIndex)
|
||||
((Unallocated*)chunkPointer(firstUnallocated))->prev = chunkIndex;
|
||||
firstUnallocated = chunkIndex;
|
||||
--allocationCount;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
bool BlockAllocator<T, BlockSize>::Block::full() const {
|
||||
return allocationCount == BlockSize;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
bool BlockAllocator<T, BlockSize>::Block::empty() const {
|
||||
return allocationCount == 0;
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
auto BlockAllocator<T, BlockSize>::Block::chunkPointer(ChunkIndex chunkIndex) -> Chunk* {
|
||||
starAssert(chunkIndex < BlockSize);
|
||||
return &chunks[chunkIndex];
|
||||
}
|
||||
|
||||
template <typename T, size_t BlockSize>
|
||||
typename BlockAllocator<T, BlockSize>::Data* BlockAllocator<T, BlockSize>::getAllocatorData(BlockAllocatorFamily& family) {
|
||||
auto& dataptr = family[typeid(Data)];
|
||||
if (!dataptr)
|
||||
dataptr = make_shared<Data>();
|
||||
return (Data*)dataptr.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue