Skip to content

Instantly share code, notes, and snippets.

@scaryghost
Last active June 4, 2019 10:10
Show Gist options
  • Save scaryghost/09a3b12fc43bb09f19216bca9534105e to your computer and use it in GitHub Desktop.
Save scaryghost/09a3b12fc43bb09f19216bca9534105e to your computer and use it in GitHub Desktop.
Nonhomogeneous list that stores its elements sequentially
#include "generic_list.hpp"
#include <algorithm>
#include <cstdlib>
using std::for_each;
using std::free;
using std::malloc;
using std::memcpy;
using std::memmove;
using std::uint8_t;
using std::unordered_map;
GenericList::GenericList() :
elements(nullptr, [](uint8_t* ptr) {
free(ptr);
}),
offsets({0}),
capacity(0) {
}
GenericList::GenericList(size_t capacity) : GenericList() {
resize(capacity);
}
GenericList::~GenericList() {
for(auto &it: deallocators) {
it.second(elements.get() + offsets[it.first]);
}
elements.release();
offsets.clear();
deallocators.clear();
}
size_t GenericList::size() const {
return offsets.size() - 1;
}
void GenericList::resize(size_t capacity) {
auto ptr = (uint8_t*) malloc(capacity);
memcpy(ptr, elements.get(), this->capacity);
elements.reset(ptr);
this->capacity = capacity;
}
void* GenericList::operator[](size_t i) const {
return elements.get() + offsets[i];
}
void GenericList::remove(size_t i) {
if (deallocators.count(i)) {
deallocators.at(i)(elements.get() + offsets[i]);
deallocators.erase(i);
unordered_map<size_t, void(*)(void*)> updated_deallocators;
for_each(deallocators.begin(), deallocators.end(), [&updated_deallocators, i](auto const& e) {
if (e.first < (i + 1)) {
updated_deallocators.insert({ e.first, e.second });
} else {
updated_deallocators.insert({ e.first - 1, e.second });
}
});
deallocators = updated_deallocators;
}
if (i < offsets.size() - 1) {
auto size = offsets[i + 1] - offsets[i];
memmove(elements.get() + offsets[i], elements.get() + offsets[i + 1], occupied() - size);
offsets.erase(offsets.begin() + (i + 1));
for_each(offsets.begin() + (i + 1), offsets.end(), [size](size_t& e) {
e -= size;
});
} else {
offsets.erase(offsets.begin() + i);
}
}
#pragma once
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <unordered_map>
#include <utility>
class GenericList {
public:
GenericList();
GenericList(size_t capacity);
~GenericList();
template <typename T>
void add(const T& value, void(*deallocator)(void*)=nullptr);
template <typename T>
void add(T&& value, void(*deallocator)(void*)=nullptr);
void remove(size_t i);
void resize(size_t capacity);
template <typename T>
T& operator[] (size_t i) const;
void* operator[](size_t i) const;
size_t size() const;
private:
inline size_t occupied() const {
return offsets.back();
}
std::unique_ptr<std::uint8_t, void(*)(std::uint8_t*)> elements;
std::vector<size_t> offsets;
std::unordered_map<size_t, void(*)(void*)> deallocators;
std::size_t capacity;
};
template <typename T>
void GenericList::add(const T& value, void(*deallocator)(void*)) {
auto target_capacity = sizeof(T) + occupied();
if (target_capacity > capacity) {
resize(target_capacity * 2);
}
auto ptr = (T*)(elements.get() + occupied());
*ptr = value;
offsets.push_back(target_capacity);
if (deallocator != nullptr) {
deallocators.insert({size() - 1, deallocator});
}
}
template <typename T>
void GenericList::add(T&& value, void(*deallocator)(void*)) {
auto target_capacity = sizeof(T) + occupied();
if (target_capacity > capacity) {
resize(target_capacity * 2);
}
auto ptr = (T*)(elements.get() + occupied());
std::swap(*ptr, value);
offsets.push_back(target_capacity);
if (deallocator != nullptr) {
deallocators.insert({size() - 1, deallocator});
}
}
template <typename T>
T& GenericList::operator[] (size_t i) const {
return *((T*) (elements.get() + offsets[i]));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment