Skip to content

Commit

Permalink
MVKMap half-done implementation
Browse files Browse the repository at this point in the history
A half done implementation of MVKMap. MVKMap aims to use the same API as std::unordered_map, and I used MVKSmallVector as an example of how to write MVKMap. I hope there aren't any bugs however, I'll probably do some tests off of the repository once I'm done
  • Loading branch information
AntarticCoder committed Jul 10, 2023
1 parent 0fb82af commit eab6f6f
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 5 deletions.
2 changes: 2 additions & 0 deletions MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "MVKResource.h"
#include "MVKCommandBuffer.h"

#include "MVKMap.h"

class MVKCommandEncoder;


Expand Down
105 changes: 102 additions & 3 deletions MoltenVK/MoltenVK/Utility/MVKMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,106 @@
#include "MVKFoundation.h"
#include "MVKMapAllocator.h"

// Storage type, and Lookup Type
template<typename ST, typename KT>
class MVKMapImpl : public MVKBaseObject {
template<typename Key,
typename T,
typename Allocator = mvk_map_allocator<Key, T, 0>,
class Hash = std::hash<Key>>
class MVKMapImpl {

private:
Allocator alc;

class iterator
{
const MVKMapImpl* map;
size_t index;
public:
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
typedef difference_type diff_type;

iterator() : map{ nullptr }, index{ 0 } { }
iterator(const size_t _index, const MVKMapImpl &_map) : map{ &_map }, index{ _index } { }

iterator &operator=(const iterator &it)
{
map = it.map;
index = it.index;
return *this;
}

T *operator->() { return &map->alc.ptr[index]; }
T &operator*() { return map->alc.ptr[index]; }
operator T*() { return &map->alc.ptr[index]; }

bool operator==( const iterator &it ) const { return map == it.map && index == it.index; }
bool operator!=( const iterator &it ) const { return map != it.map || index != it.index; }

iterator& operator++() { ++index; return *this; }
iterator operator++( int ) { auto t = *this; ++index; return t; }
iterator& operator--() { --index; return *this; }
iterator operator--( int ) { auto t = *this; --index; return t; }

iterator operator+ (const diff_type n) { return iterator( index + n, *map ); }
iterator& operator+= (const diff_type n) { index += n; return *this; }
iterator operator- (const diff_type n) { return iterator( index - n, *map ); }
iterator& operator-= (const diff_type n) { index -= n; return *this; }

diff_type operator- (const iterator& it) { return index - it.index; }

bool operator< (const iterator& it) { return index < it.index; }
bool operator<= (const iterator& it) { return index <= it.index; }
bool operator> (const iterator& it) { return index > it.index; }
bool operator>= (const iterator& it) { return index >= it.index; }

const T &operator[]( const diff_type i ) const { return map->alc.ptr[index + i]; }
T &operator[]( const diff_type i ) { return map->alc.ptr[index + i]; }

bool is_valid() const { return index < map->alc.size(); }
size_t get_position() const { return index; }
};
protected:
bool empty() { return alc.num_elements_used == 0;}
size_t size() { return alc.size(); }

T* &at( const size_t i ) { return alc[i]; }
const T* const at(const size_t i) const { return alc[i]; }

iterator begin() { return iterator(0, this); }
iterator end() { return iterator(size(), this); }

void erase(const iterator it)
{
if(it.is_valid())
{
--alc.num_elements_used;

for(size_t i = it.get_position(); i < alc.num_elements_used; ++i)
{
alc.ptr[i] = alc.ptr[i + 1];
}
}
}

void erase(const iterator first, const iterator last)
{
if(first.is_valid())
{
size_t last_pos = last.is_valid() ? last.get_position() : size();
size_t n = last_pos - first.get_position();
alc.num_elements_used -= n;

for(size_t i = first.get_position(), e = last_pos; i < alc.num_elements_used && e < alc.num_elements_used + n; ++i, ++e)
{
alc.ptr[i] = alc.ptr[e];
}
}
}

std::pair<iterator, bool> insert(const T& value)
{
alc.re_allocate(size() + 1);
alc.ptr[size()] = value;
return std::make_pair<iterator, bool>(end(), true);
}
};
105 changes: 103 additions & 2 deletions MoltenVK/MoltenVK/Utility/MVKMapAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,108 @@
#pragma once

#include "MVKFoundation.h"
#include <unordered_map>

template <typename ST, typename KT>
class mvk_unordered_map_allocator final {
namespace mvk_map_memory_allocator
{
inline char *alloc(const size_t num_bytes)
{
return new char[num_bytes];
}

inline void free(void *ptr)
{
delete[] (char*)ptr;
}
};

template <typename Key, typename T, int M>
class mvk_map_allocator final {

public:
std::pair<Key, T>* ptr;
size_t num_elements_used;
private:
static constexpr size_t CAP_CNT_SIZE = sizeof(size_t);
static constexpr size_t ALIGN_CNT = CAP_CNT_SIZE / sizeof(std::pair<Key, T>);
static constexpr size_t ALIGN_MASK = (ALIGN_CNT> 0) ? (ALIGN_CNT - 1) : 0;

static constexpr size_t MIN_CNT = M> ALIGN_CNT ? M : ALIGN_CNT;
static constexpr size_t N = (MIN_CNT + ALIGN_MASK) & ~ALIGN_MASK;

static constexpr size_t MIN_STACK_SIZE = (N * sizeof(std::pair<Key, T>));
static constexpr size_t STACK_SIZE = MIN_STACK_SIZE> CAP_CNT_SIZE ? MIN_STACK_SIZE : CAP_CNT_SIZE;
alignas(alignof(std::pair<Key, T>)) unsigned char elements_stack[ STACK_SIZE ];

void set_num_elements_reserved(const size_t num_elements_reserved)
{
*reinterpret_cast<size_t*>(&elements_stack[0]) = num_elements_reserved;
}
public:
const T &operator[](const size_t i) const { return ptr[i]; }
T &operator[](const size_t i) { return ptr[i]; }

size_t size() const { return num_elements_used; }

constexpr T *get_default_ptr() const
{
return reinterpret_cast<T*>(const_cast<unsigned char *>(&elements_stack[0]));
}

template<class S, class... Args> typename std::enable_if<!std::is_trivially_constructible<S, Args...>::value>::type
construct(S *_ptr, Args&&... _args)
{
new (_ptr) S(std::forward<Args>(_args)...);
}

template<class S, class... Args> typename std::enable_if<std::is_trivially_constructible<S, Args...>::value>::type
construct(S *_ptr, Args&&... _args)
{
*_ptr = S(std::forward<Args>(_args)...);
}

template<class S> typename std::enable_if<!std::is_trivially_destructible<S>::value>::type
destruct(S *_ptr)
{
_ptr->~S();
}

template<class S> typename std::enable_if<std::is_trivially_destructible<S>::value>::type
destruct(S *_ptr) {}

template<class S> typename std::enable_if<!std::is_trivially_destructible<S>::value>::type
destruct_all()
{
for(size_t i = 0; i < num_elements_used; ++i)
{
ptr[i].~S();
}

num_elements_used = 0;
}

template<class S> typename std::enable_if<std::is_trivially_destructible<S>::value>::type
destruct_all()
{
num_elements_used = 0;
}

void re_allocate(const size_t num_elements_to_reserve)
{
auto *new_ptr = reinterpret_cast<T*>(mvk_smallvector_memory_allocator::alloc(num_elements_to_reserve * sizeof(T)));

for(size_t i = 0; i < num_elements_used; ++i)
{
construct(&new_ptr[i], std::move(ptr[i]));
destruct(&ptr[i]);
}

if(ptr != get_default_ptr())
{
mvk_smallvector_memory_allocator::free(ptr);
}

ptr = new_ptr;
set_num_elements_reserved(num_elements_to_reserve);
}
};

0 comments on commit eab6f6f

Please sign in to comment.