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