11 changed files with 94 additions and 9314 deletions
File diff suppressed because it is too large
@ -1,49 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
namespace detail::refcount_hive |
|
||||
{ |
|
||||
// The element as allocated in memory needs to be at-least 2*skipfield_type width in order to support free list indexes in
|
|
||||
// erased element memory space, so: make the size of this struct the larger of alignof(T), sizeof(T) or 2*skipfield_type
|
|
||||
// (the latter is only relevant for type char/uchar), and make the alignment alignof(T). This type is used mainly for
|
|
||||
// correct pointer arithmetic while iterating over elements in memory.
|
|
||||
template <typename element_type, typename skipfield_type, typename refcount_type> |
|
||||
struct alignas(alignof(element_type)) aligned_element_struct { |
|
||||
// Using char as sizeof is always guaranteed to be 1 byte regardless of the number of bits in a byte on given computer,
|
|
||||
// whereas for example, uint8_t would fail on machines where there are more than 8 bits in a byte eg. Texas Instruments
|
|
||||
// C54x DSPs.
|
|
||||
char |
|
||||
data[(sizeof(element_type) < (sizeof(skipfield_type) * 2)) |
|
||||
? ((sizeof(skipfield_type) * 2) < alignof(element_type) ? alignof(element_type) : (sizeof(skipfield_type) * 2)) |
|
||||
: ((sizeof(element_type) < alignof(element_type)) ? alignof(element_type) : sizeof(element_type))]; |
|
||||
}; |
|
||||
|
|
||||
// We combine the allocation of elements and skipfield into one allocation to save performance. This memory must be
|
|
||||
// allocated as an aligned type with the same alignment as T in order for the elements to align with memory boundaries
|
|
||||
// correctly (which won't happen if we allocate as char or uint_8). But the larger the sizeof in the type we use for
|
|
||||
// allocation, the greater the chance of creating a lot of unused memory in the skipfield portion of the allocated block. So
|
|
||||
// we create a type that is sizeof(alignof(T)), as in most cases alignof(T) < sizeof(T). If alignof(t) >= sizeof(t) this
|
|
||||
// makes no difference.
|
|
||||
template <typename element_type> |
|
||||
struct alignas(alignof(element_type)) aligned_allocation_struct { |
|
||||
char data[alignof(element_type)]; |
|
||||
}; |
|
||||
|
|
||||
// Calculate the capacity of a group's elements+skipfield memory block when expressed in multiples of the value_type's
|
|
||||
// alignment (rounding up).
|
|
||||
template <typename element_type, typename skipfield_type, typename refcount_type> |
|
||||
static size_t get_aligned_block_capacity(const skipfield_type elements_per_group) |
|
||||
{ |
|
||||
using aligned_element_struct = aligned_element_struct<element_type, skipfield_type, refcount_type>; |
|
||||
using aligned_allocation_struct = aligned_allocation_struct<element_type>; |
|
||||
return ((elements_per_group * (sizeof(aligned_element_struct) + sizeof(skipfield_type))) + sizeof(skipfield_type) |
|
||||
+ sizeof(aligned_allocation_struct) - 1) |
|
||||
/ sizeof(aligned_allocation_struct); |
|
||||
} |
|
||||
|
|
||||
// To enable conversion when allocator supplies non-raw pointers:
|
|
||||
template <class destination_pointer_type, class source_pointer_type> |
|
||||
static constexpr destination_pointer_type pointer_cast(const source_pointer_type source_pointer) noexcept |
|
||||
{ |
|
||||
return destination_pointer_type(&*source_pointer); |
|
||||
} |
|
||||
} // namespace detail::refcount_hive
|
|
||||
@ -1,27 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <memory> |
|
||||
|
|
||||
namespace detail::refcount_hive |
|
||||
{ |
|
||||
// To enable conversion to void * when allocator supplies non-raw pointers:
|
|
||||
template <class source_pointer_type> |
|
||||
static constexpr void *void_cast(const source_pointer_type source_pointer) noexcept |
|
||||
{ |
|
||||
return static_cast<void *>(&*source_pointer); |
|
||||
} |
|
||||
|
|
||||
template <class iterator_type> |
|
||||
static constexpr std::move_iterator<iterator_type> make_move_iterator(iterator_type it) |
|
||||
{ |
|
||||
return std::move_iterator<iterator_type>(std::move(it)); |
|
||||
} |
|
||||
|
|
||||
enum class priority { performance = 1, memory_use = 4 }; |
|
||||
|
|
||||
struct limits { |
|
||||
size_t min, max; |
|
||||
|
|
||||
constexpr limits(const size_t minimum, const size_t maximum) noexcept : min(minimum), max(maximum) {} |
|
||||
}; |
|
||||
} // namespace detail::refcount_hive
|
|
||||
File diff suppressed because it is too large
@ -1,132 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "hive.hpp" |
|
||||
#include "hashmap.hpp" |
|
||||
|
|
||||
namespace detail |
|
||||
{ |
|
||||
template <typename K> |
|
||||
struct hasher { |
|
||||
size_t operator()(const K& k) const; |
|
||||
}; |
|
||||
|
|
||||
// template <typename K>
|
|
||||
// struct eq_compare
|
|
||||
// {
|
|
||||
// bool operator()(const K& lhs, const K& rhs) const;
|
|
||||
// };
|
|
||||
|
|
||||
template <typename K, typename V> |
|
||||
struct default_elem_ctor { |
|
||||
V operator()(const K& k) const; |
|
||||
}; |
|
||||
|
|
||||
template <typename K, typename V, typename Allocator = std::allocator<V>> |
|
||||
struct hashed_refcount_hive { |
|
||||
using iterator = typename hive<V, Allocator>::iterator; |
|
||||
|
|
||||
public: |
|
||||
std::pair<iterator, bool> acquire(const K& key) |
|
||||
{ |
|
||||
size_t hash_key = hasher<K>()(key); |
|
||||
auto iter = refcount_data_map.find(hash_key); |
|
||||
if (iter == refcount_data_map.end()) { |
|
||||
auto data_iter = data.emplace(default_elem_ctor<K, V>{}(key)); |
|
||||
refcount_data_map.emplace(hash_key, std::make_pair(1, data_iter)); |
|
||||
return {data_iter, true}; |
|
||||
} else { |
|
||||
iter->second.first += 1; |
|
||||
return {iter->second.second, false}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void release(const K& key) |
|
||||
{ |
|
||||
size_t hash_key = hasher<K>()(key); |
|
||||
auto iter = refcount_data_map.find(hash_key); |
|
||||
if (iter == refcount_data_map.end()) throw std::runtime_error("Key not found in refcount map."); |
|
||||
if (--iter->second.first == 0) { |
|
||||
data.erase(iter->second.second); |
|
||||
refcount_data_map.erase(iter); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void release(const iterator& ptr) |
|
||||
{ |
|
||||
for (auto iter = refcount_data_map.begin(); iter != refcount_data_map.end(); ++iter) { |
|
||||
if (iter->second.second == ptr) { |
|
||||
if (--iter->second.first == 0) { |
|
||||
data.erase(iter->second.second); |
|
||||
refcount_data_map.erase(iter); |
|
||||
} |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
throw std::runtime_error("Pointer not found in refcount map."); |
|
||||
} |
|
||||
|
|
||||
protected: |
|
||||
hive<V, Allocator> data{}; |
|
||||
// manually calculate hash for the key, so that we do not need to store the key
|
|
||||
flat_hash_map<size_t, std::pair<size_t, iterator>> refcount_data_map{}; |
|
||||
}; |
|
||||
|
|
||||
template <typename T, typename Tag> |
|
||||
struct tagged_hasher; |
|
||||
|
|
||||
template <typename K, typename V, typename Tag, typename Allocator = std::allocator<V>> |
|
||||
struct tagged_hashed_refcount_hive { |
|
||||
using iterator = typename hive<V, Allocator>::iterator; |
|
||||
|
|
||||
public: |
|
||||
std::pair<iterator, bool> acquire(const K& key) |
|
||||
{ |
|
||||
size_t hash_key = tagged_hasher<K, Tag>()(key); |
|
||||
auto iter = refcount_data_map.find(hash_key); |
|
||||
if (iter == refcount_data_map.end()) { |
|
||||
auto data_iter = data.emplace(default_elem_ctor<K, V>{}(key)); |
|
||||
refcount_data_map.emplace(hash_key, std::make_pair(1, data_iter)); |
|
||||
return {data_iter, true}; |
|
||||
} else { |
|
||||
iter->second.first += 1; |
|
||||
return {iter->second.second, false}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void release(const K& key) |
|
||||
{ |
|
||||
size_t hash_key = tagged_hasher<K, Tag>()(key); |
|
||||
auto iter = refcount_data_map.find(hash_key); |
|
||||
if (iter == refcount_data_map.end()) throw std::runtime_error("Key not found in refcount map."); |
|
||||
if (--iter->second.first == 0) { |
|
||||
data.erase(iter->second.second); |
|
||||
refcount_data_map.erase(iter); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void release(const iterator& ptr) |
|
||||
{ |
|
||||
for (auto iter = refcount_data_map.begin(); iter != refcount_data_map.end(); ++iter) { |
|
||||
if (iter->second.second == ptr) { |
|
||||
if (--iter->second.first == 0) { |
|
||||
data.erase(iter->second.second); |
|
||||
refcount_data_map.erase(iter); |
|
||||
} |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
throw std::runtime_error("Pointer not found in refcount map."); |
|
||||
} |
|
||||
|
|
||||
protected: |
|
||||
hive<V, Allocator> data{}; |
|
||||
// manually calculate hash for the key, so that we do not need to store the key
|
|
||||
flat_hash_map<size_t, std::pair<size_t, iterator>> refcount_data_map{}; |
|
||||
}; |
|
||||
} // namespace detail
|
|
||||
|
|
||||
template <typename K, typename V> |
|
||||
using hashed_refcount_hive_mp = detail::hashed_refcount_hive<K, V, mi_stl_allocator<V>>; |
|
||||
|
|
||||
template <typename K, typename V, typename Tag> |
|
||||
using tagged_hashed_refcount_hive_mp = detail::tagged_hashed_refcount_hive<K, V, Tag, mi_stl_allocator<V>>; |
|
||||
@ -0,0 +1,71 @@ |
|||||
|
#include <container/hashmap.hpp> |
||||
|
#include <utils/pointer_wrapper.hpp> |
||||
|
|
||||
|
namespace detail |
||||
|
{ |
||||
|
template <typename K> |
||||
|
struct hasher { |
||||
|
size_t operator()(const K& k) const; |
||||
|
}; |
||||
|
|
||||
|
template <typename K, typename Tag> |
||||
|
struct tagged_hasher { |
||||
|
size_t operator()(const K& k) const; |
||||
|
}; |
||||
|
|
||||
|
template <typename K> |
||||
|
struct eq_compare { |
||||
|
bool operator()(const K& lhs, const K& rhs) const { return lhs == rhs; } |
||||
|
}; |
||||
|
|
||||
|
template <typename K, typename V> |
||||
|
struct default_elem_ctor { |
||||
|
V operator()(const K& k) const; |
||||
|
}; |
||||
|
|
||||
|
template <typename K, typename V, typename Hasher = hasher<K>, template <typename> typename Allocator = std::allocator> |
||||
|
struct flat_object_map { |
||||
|
using value_pointer_t = pointer_wrapper<V>; |
||||
|
|
||||
|
std::pair<value_pointer_t, bool> acquire(const K& key) |
||||
|
{ |
||||
|
auto [iter, is_new] = data.try_emplace(key, object_with_refcount{}); |
||||
|
auto& value = iter->second; |
||||
|
if (is_new) { |
||||
|
value.refcount = 1; |
||||
|
value.object_pointer = mi_aligned_alloc(alignof(V), sizeof(V)); |
||||
|
if constexpr (std::is_same_v<V, K>) |
||||
|
*value.object_pointer = key; |
||||
|
else |
||||
|
*value.object_pointer = std::move(default_elem_ctor<K, V>{}(key)); |
||||
|
return {value.object_pointer, true}; |
||||
|
} else { |
||||
|
value.refcount++; |
||||
|
return {value.object_pointer, false}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void release(const K& key) |
||||
|
{ |
||||
|
if (auto iter = data.find(key); iter == data.end()) { |
||||
|
throw std::runtime_error("Key not found in refcount map."); |
||||
|
} else if (--iter->second.refcount == 0) { |
||||
|
data.erase(iter); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected: |
||||
|
struct object_with_refcount { |
||||
|
size_t refcount{}; |
||||
|
value_pointer_t object_pointer{}; |
||||
|
}; |
||||
|
|
||||
|
flat_hash_map<K, object_with_refcount, Hasher, eq_compare<K>, Allocator<std::pair<const K, V>>> data{}; |
||||
|
}; |
||||
|
} // namespace detail
|
||||
|
|
||||
|
template <typename K, typename V> |
||||
|
using flat_object_map_mp = detail::flat_object_map<K, V, detail::hasher<K>, mi_stl_allocator>; |
||||
|
|
||||
|
template <typename K, typename V, typename Tag> |
||||
|
using tagged_flat_object_map_mp = detail::flat_object_map<K, V, detail::tagged_hasher<K, Tag>, mi_stl_allocator>; |
||||
Loading…
Reference in new issue