Files
CDAG/Research/core/Util/BlockVector.h

223 lines
5.5 KiB
C++

#pragma once
#include <vector>
#include <array>
#include "IndexIterator.h"
//#include "../BitHelper.h"
template <typename T, unsigned8 BLOCK_POINTER_BITS = 14>
class BlockVector
{
public:
const static unsigned64 BLOCK_SIZE = 1 << BLOCK_POINTER_BITS;
const static size_t BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
protected:
typedef std::vector<T> block;
std::vector<block*> mData;
size_t mSize;
inline size_t block_count(size_t n) const { return (n >> BLOCK_POINTER_BITS) + ((n % BLOCK_SIZE == 0) ? 0 : 1); }
inline size_t block_index(size_t i) const { return i >> BLOCK_POINTER_BITS; }
inline size_t item_index(size_t i) const { return i & BLOCK_SIZE_MASK; }
void shrink_blocks(size_t newBlockCount)
{
// If the size is smaller, delete blocks that are no longer needed.
for (size_t i = newBlockCount; i < mData.size(); i++)
delete mData[i];
}
void init_blocks(size_t oldSize = 0)
{
for (size_t i = oldSize; i < mData.size(); i++)
mData[i] = new block(BLOCK_SIZE);
}
void init_blocks(const T& value, size_t oldSize)
{
for (size_t i = oldSize; i < mData.size(); i++)
mData[i] = new block(BLOCK_SIZE, value);
}
public:
typedef IndexIterator<const BlockVector<T>, const T> const_iterator;
typedef IndexIterator<BlockVector<T>, T> iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
BlockVector() : mData(std::vector<std::array<T, BLOCK_SIZE>*>()), mSize(0) {}
BlockVector(size_t capacity) :
mData(std::vector<block*>()),
mSize(0)
{
reserve(capacity);
}
BlockVector(const BlockVector& r)
{
mData = std::vector<block*>(r.mData.size());
init(0);
for (size_t block = 0; block < r.mData.size(); block++)
std::copy(r.mData[block]->begin(), r.mData[block]->end(), mData[block]->begin());
mSize = r.mSize;
}
BlockVector(BlockVector&& r)
{
mData = std::move(r.mData); r.mData.clear();
mSize = std::move(r.mSize); r.mSize = 0;
}
virtual ~BlockVector()
{
for (block* arr : mData)
{
arr->clear();
delete arr;
}
mData.clear();
}
// Capacity
size_t size() const { return mSize; }
size_t max_size() const { return mData.max_size() * mSize; }
void reserve(size_t n)
{
size_t newBlockCount = block_count(n);
if (mData.size() >= newBlockCount) return; // big enough
size_t oldSize = mData.size();
mData.resize(newBlockCount);
for (size_t i = oldSize; i < mData.size(); i++)
{
mData[i] = new block();
mData[i]->reserve(BLOCK_SIZE);
}
}
// Shrinks the vector so it contains n entries.
void shrink(size_t n)
{
size_t newBlockCount = block_count(n);
shrink_blocks(newBlockCount);
mData.resize(newBlockCount);
mSize = n;
}
void resize(size_t n)
{
size_t oldBlockCount = mData.size();
size_t newBlockCount = block_count(n);
shrink_blocks(newBlockCount);
mData.resize(newBlockCount, NULL);
init_blocks(oldBlockCount);
mSize = n;
}
void resize(size_t n, const T& val)
{
size_t oldBlockCount = mData.size();
size_t newBlockCount = block_count(n);
shrink_blocks(newBlockCount);
mData.resize(newBlockCount, NULL);
init_blocks(val, oldBlockCount);
mSize = n;
}
inline size_t capacity() const { return mData.size() * BLOCK_SIZE; }
inline bool empty() const { return mData.empty(); }
void shrink_to_fit() { resize(mSize); }
// Element access
const T& at(size_t i) const { assert(i < mSize); return mData[block_index(i)]->at(item_index(i)); }
T& at(size_t i) { assert(i < mSize); return mData[block_index(i)]->at(item_index(i)); }
const T& operator[](size_t i) const { return at(i); }
T& operator[](size_t i) { return at(i); }
const T& front() const { return at(0); }
T& front() { return at(0); }
const T& back() const { return at(mSize - 1); }
T& back() { return at(mSize - 1); }
// Modifiers
void push_back(const T& val)
{
// Increase the size if it doesn't fit
if (block_index(mSize) >= mData.size()) reserve(mSize + 1);
// Put the item in the last position
block* b = mData[block_index(mSize)];
size_t itemIndex = item_index(mSize);
if (b->size() == itemIndex) b->push_back(val);
else mData->at(itemIndex) = val;
mSize++;
}
void push_back(T&& val)
{
// Increase the size if it doesn't fit
if (block_index(mSize) >= mData.size()) reserve(mSize + 1);
// Put the item in the last position
block* b = mData[block_index(mSize)];
size_t itemIndex = item_index(mSize);
if (b->size() == itemIndex) b->push_back(val);
else b->at(itemIndex) = val;
mSize++;
}
template <class... Args>
void emplace_back(Args&&... args)
{
size_t blockIndex = block_index(mSize);
if (blockIndex >= mData.size()) reserve(mSize + 1);
block* b = mData[blockIndex];
size_t itemIndex = item_index(mSize);
if (b->size() == itemIndex) b->emplace_back(std::forward<Args>(args)...);
else b->at(itemIndex) = T(std::forward<Args>(args)...);
mSize++;
}
void pop_back() {
mSize--;
}
void clear() {
for (block* block : mData)
delete block;
mData.clear();
mSize = 0;
}
// Iterators
const_iterator begin() const
{
return const_iterator(this, 0);
}
const_iterator end() const
{
return const_iterator(this, mSize);
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}
iterator begin()
{
return iterator(this, 0);
}
iterator end()
{
return iterator(this, mSize);
}
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
};