#pragma once
#include <array>
#include <cstdint>
#include <memory>
#include <vector>
#include "common/assert.h"
#include "common/system_config.h"
namespace lbug {
namespace common {
template<typename T, uint64_t BLOCK_SIZE = DEFAULT_VECTOR_CAPACITY,
uint64_t INDEX_SIZE = BLOCK_SIZE>
class ConcurrentVector {
public:
explicit ConcurrentVector(uint64_t initialNumElements, uint64_t initialBlockSize)
: numElements{initialNumElements}, initialBlock{std::make_unique<T[]>(initialBlockSize)},
initialBlockSize{initialBlockSize}, firstIndex{nullptr} {}
void resize(uint64_t newSize) {
while (newSize > initialBlockSize + blocks.size() * BLOCK_SIZE) {
auto newBlock = std::make_unique<Block>();
if (indices.empty()) {
auto index = std::make_unique<BlockIndex>();
index->blocks[0] = newBlock.get();
index->numBlocks = 1;
firstIndex = index.get();
indices.push_back(std::move(index));
} else if (indices.back()->numBlocks < INDEX_SIZE) {
auto& index = indices.back();
index->blocks[index->numBlocks] = newBlock.get();
index->numBlocks++;
} else {
DASSERT(indices.back()->numBlocks == INDEX_SIZE);
auto index = std::make_unique<BlockIndex>();
index->blocks[0] = newBlock.get();
index->numBlocks = 1;
indices.back()->nextIndex = index.get();
indices.push_back(std::move(index));
}
blocks.push_back(std::move(newBlock));
}
numElements = newSize;
}
void push_back(T&& value) {
auto index = numElements;
resize(numElements + 1);
(*this)[index] = std::move(value);
}
T& operator[](uint64_t elemPos) {
if (elemPos < initialBlockSize) {
DASSERT(initialBlock);
return initialBlock[elemPos];
} else {
auto blockNum = (elemPos - initialBlockSize) / BLOCK_SIZE;
auto posInBlock = (elemPos - initialBlockSize) % BLOCK_SIZE;
auto indexNum = blockNum / INDEX_SIZE;
BlockIndex* index = firstIndex;
DASSERT(index != nullptr);
while (indexNum > 0) {
DASSERT(index->nextIndex != nullptr);
index = index->nextIndex;
indexNum--;
}
DASSERT(index->blocks[blockNum % INDEX_SIZE] != nullptr);
return index->blocks[blockNum % INDEX_SIZE]->data[posInBlock];
}
}
uint64_t size() { return numElements; }
private:
uint64_t numElements;
std::unique_ptr<T[]> initialBlock;
uint64_t initialBlockSize;
struct Block {
std::array<T, BLOCK_SIZE> data;
};
struct BlockIndex {
BlockIndex() : nextIndex{nullptr}, blocks{}, numBlocks{0} {}
BlockIndex* nextIndex;
std::array<Block*, INDEX_SIZE> blocks;
uint64_t numBlocks;
};
BlockIndex* firstIndex;
std::vector<std::unique_ptr<Block>> blocks;
std::vector<std::unique_ptr<BlockIndex>> indices;
};
} }