diff --git a/src/util/SmallHeap.hpp b/src/util/SmallHeap.hpp new file mode 100644 index 00000000..ef590ff0 --- /dev/null +++ b/src/util/SmallHeap.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include +#include + +namespace util { + template + class SmallHeap { + std::vector buffer; + Tindex entriesCount; + public: + SmallHeap() : entriesCount(0) {} + + uint8_t* find(Tindex index) { + auto data = buffer.data(); + for (size_t i = 0; i < entriesCount; i++) { + auto nextIndex = *reinterpret_cast(data); + data += sizeof(Tindex); + auto nextSize = *reinterpret_cast(data); + data += sizeof(Tsize); + if (nextIndex == index) { + return data; + } else if (nextIndex > index) { + return nullptr; + } + data += nextSize; + } + return nullptr; + } + + void free(uint8_t* ptr) { + if (ptr == nullptr) { + return; + } + auto entrySize = sizeOf(ptr); + auto begin = + buffer.begin() + + ((ptr - sizeof(Tsize) - sizeof(Tindex)) - buffer.data()); + buffer.erase( + begin, begin + entrySize + sizeof(Tsize) + sizeof(Tindex) + ); + entriesCount--; + } + + uint8_t* allocate(Tindex index, Tsize size) { + if (size == 0) { + throw std::runtime_error("size is 0"); + } + ptrdiff_t offset = 0; + if (auto found = find(index)) { + auto entrySize = sizeOf(found); + if (size == entrySize) { + std::memset(found, 0, entrySize); + return found; + } + this->free(found); + return allocate(index, size); + } + for (size_t i = 0; i < entriesCount; i++) { + auto data = buffer.data() + offset; + auto nextIndex = *reinterpret_cast(data); + data += sizeof(Tindex); + auto nextSize = *reinterpret_cast(data); + data += sizeof(Tsize); + if (nextIndex > index) { + break; + } + data += nextSize; + offset = data - buffer.data(); + } + buffer.insert( + buffer.begin() + offset, + size + sizeof(Tindex) + sizeof(Tsize), + 0 + ); + entriesCount++; + + auto data = buffer.data() + offset; + *reinterpret_cast(data) = index; + data += sizeof(Tindex); + *reinterpret_cast(data) = size; + return data + sizeof(Tsize); + } + + Tsize sizeOf(uint8_t* ptr) const { + if (ptr == nullptr) { + return 0; + } + return *(reinterpret_cast(ptr)-1); + } + + Tindex count() const { + return entriesCount; + } + + size_t size() const { + return buffer.size(); + } + }; +} diff --git a/test/util/SmallHeap.cpp b/test/util/SmallHeap.cpp new file mode 100644 index 00000000..d8b08a4c --- /dev/null +++ b/test/util/SmallHeap.cpp @@ -0,0 +1,55 @@ +#include + +#include "util/SmallHeap.hpp" + +using namespace util; + +TEST(SmallHeap, Allocation) { + auto index = 0; + auto size = 4; + + SmallHeap map; + auto ptr = map.allocate(index, size); + EXPECT_EQ(map.sizeOf(ptr), size); + EXPECT_EQ(ptr, map.find(index)); +} + +TEST(SmallHeap, MultipleAllocation) { + SmallHeap map; + map.allocate(32, 4); + map.allocate(1, 5); + EXPECT_EQ(map.sizeOf(map.find(32)), 4); + EXPECT_EQ(map.sizeOf(map.find(1)), 5); +} + +TEST(SmallHeap, Free) { + SmallHeap map; + map.free(map.allocate(5, 4)); + EXPECT_EQ(map.find(5), nullptr); +} + +TEST(SmallHeap, ReAllocationSame) { + SmallHeap map; + auto ptr1 = map.allocate(1, 2); + auto ptr2 = map.allocate(1, 2); + EXPECT_EQ(ptr1, ptr2); +} + +TEST(SmallHeap, ReAllocationDifferent) { + SmallHeap map; + map.allocate(1, 34); + map.allocate(1, 2); + EXPECT_EQ(map.sizeOf(map.find(1)), 2); +} + +TEST(SmallHeap, RandomFill) { + SmallHeap map; + int n = 3'000; + map.allocate(n, 123); + for (int i = 0; i < n; i++) { + int index = rand() % n; + int size = rand() % 254 + 1; + map.allocate(index, size); + } + EXPECT_EQ(map.sizeOf(map.find(n)), 123); +}