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