Skip to content

Commit

Permalink
[util] add: ChunkVector
Browse files Browse the repository at this point in the history
  • Loading branch information
jd28 committed Oct 11, 2024
1 parent c603ea2 commit 3fabdf8
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
146 changes: 146 additions & 0 deletions lib/nw/util/ChunkVector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#pragma once

#include "../log.hpp"
#include "Allocator.hpp"

#include <memory>

namespace nw {

/// ChunkVector is an intrusively linked list of chunks of arrays.
template <typename T>
struct ChunkVector {
using reference = T&;

ChunkVector(size_t chunk_size, Allocator<T> allocator)
: chunk_size_(chunk_size)
, allocator_{allocator}
{
}

~ChunkVector()
{
clear();
}

T& operator[](size_t index)
{
return const_cast<T&>(static_cast<const ChunkVector&>(*this)[index]);
}

const T& operator[](size_t index) const
{
auto chunk = find_chunk(index);
size_t ele = index % chunk_size_;
LOG_F(INFO, "index: {}, blocks: {}", ele, allocated_);
CHECK_F(!!chunk && !!chunk->data, "attempting to address invalid chunk");
return chunk->data[ele];
}

reference back()
{
auto chunk = find_chunk(size_ - 1);
CHECK_F(!!chunk && !!chunk->data, "attempting to address invalid chunk");
return chunk->data[(size_ - 1) % chunk_size_];
}

template <class... Args>
reference emplace_back(Args&&... args)
{
push_back(T(std::forward<Args>(args)...));
return back();
}

/// Clears the container
void clear()
{
auto c = blocks_;
size_t total_size = 0;
while (c) {
if constexpr (!std::is_trivially_destructible_v<T>) {
for (size_t i = 0; i < chunk_size_; ++i) {
LOG_F(INFO, "i {} chunk_size_ {} total_size {} size_ {}", i, chunk_size_, total_size, size_);
if (total_size == size_) { break; }
c->data[i].~T();
++total_size;
}
}
std::allocator_traits<Allocator<T>>::deallocate(allocator_, c->data, chunk_size_);

auto temp = c;
c = c->next;
auto ta = Allocator<Chunk>(allocator_);
std::allocator_traits<Allocator<Chunk>>::deallocate(ta, temp, 1);
}
blocks_ = nullptr;
size_ = allocated_ = 0;
}

/// Determines if container is empty
bool empty() const noexcept { return size_ == 0; }

void push_back(T&& ele)
{
if (!blocks_ || size_ == allocated_) { alloc_block(); }

auto chunk = find_chunk(size_);
CHECK_F(!!chunk && !!chunk->data, "attempting to address invalid chunk");
new (&chunk->data[size_ % chunk_size_]) T(std::forward<T>(ele));
++size_;
}

void reserve(size_t n)
{
if (n < allocated_) { return; }
}

size_t size() const noexcept { return size_; }

private:
struct Chunk {
T* data = nullptr;
Chunk* next = nullptr;
};

size_t chunk_size_;
Chunk* blocks_ = nullptr;
Chunk* tail_ = nullptr;
size_t size_ = 0;
size_t allocated_ = 0;
Allocator<T> allocator_;

void alloc_block()
{
auto ta = Allocator<Chunk>(allocator_);
Chunk* chunk = std::allocator_traits<Allocator<Chunk>>::allocate(ta, 1);
chunk->data = allocator_.allocate(chunk_size_);
chunk->next = nullptr;

if (!blocks_) {
blocks_ = tail_ = chunk;
} else {
tail_->next = chunk;
tail_ = chunk;
}
allocated_ += chunk_size_;
}

Chunk* find_chunk(size_t index) const noexcept
{
size_t chunk = index / chunk_size_;
LOG_F(INFO, "chunk: {}", chunk);
size_t i = 0;

auto c = blocks_;
while (c) {
if (i == chunk) { break; }
++i;
c = c->next;
}
LOG_F(INFO, "valid chunk: {}", !!c && !!c->data);

return c;
}
};

} // namespace nw
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ add_executable(rollnw_test

serial_gff.cpp

util_containers.cpp
util_memory.cpp
util_string.cpp
util_tokenizer.cpp
Expand Down
26 changes: 26 additions & 0 deletions tests/util_containers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <gtest/gtest.h>

#include "nw/util/memory.hpp"
#include <nw/util/ChunkVector.hpp>

using namespace std::literals;

TEST(ChunkVector, Basics)
{
nw::MemoryArena ma;
nw::ChunkVector<std::string> cv(3, &ma);

cv.push_back("1");
cv.push_back("2");
cv.push_back("3");
cv.push_back("4");
cv.push_back("5");
EXPECT_EQ(cv[3], "4");

cv.emplace_back("6");

EXPECT_EQ(cv.back(), "6");

cv.clear();
EXPECT_TRUE(cv.empty());
}

0 comments on commit 3fabdf8

Please sign in to comment.