#pragma once #include "util/types.hpp" #include "Utilities/mutex.h" #include #include // Helper namespace namespace id_manager { // Common global mutex extern shared_mutex g_mutex; // ID traits template struct id_traits { static_assert(sizeof(T) == 0, "ID object must specify: id_base, id_step, id_count"); static constexpr u32 base = 1; // First ID (N = 0) static constexpr u32 step = 1; // Any ID: N * id_step + id_base static constexpr u32 count = 65535; // Limit: N < id_count static constexpr u32 invalid = 0; static constexpr std::pair invl_range{0, 0}; }; template struct invl_range_extract_impl { static constexpr std::pair invl_range{0, 0}; }; template struct invl_range_extract_impl> { static constexpr std::pair invl_range = T::id_invl_range; }; template struct id_traits> { static constexpr u32 base = T::id_base; static constexpr u32 step = T::id_step; static constexpr u32 count = T::id_count; static constexpr u32 invalid = -+!base; static constexpr std::pair invl_range = invl_range_extract_impl::invl_range; static_assert(count && step && u64{step} * (count - 1) + base < u64{UINT32_MAX} + (base != 0 ? 1 : 0), "ID traits: invalid object range"); // TODO: Add more conditions static_assert(!invl_range.second || (u64{invl_range.second} + invl_range.first <= 32 /*....*/ )); }; // Correct usage testing template struct id_verify : std::integral_constant::value> { // If common case, T2 shall be derived from or equal to T }; template struct id_verify> : std::integral_constant::value> { // If T2 contains id_type type, T must be equal to it }; class typeinfo { // Global variable for each registered type template struct registered { static const u32 index; }; // Increment type counter static u32 add_type(u32 i) { static atomic_t g_next{0}; return g_next.fetch_add(i); } public: // Get type index template static inline u32 get_index() { return registered::index; } // Get type count static inline u32 get_count() { return add_type(0); } }; template const u32 typeinfo::registered::index = typeinfo::add_type(1); // ID value with additional type stored class id_key { u32 m_value; // ID value u32 m_type; // True object type public: id_key() = default; id_key(u32 value, u32 type) : m_value(value) , m_type(type) { } u32 value() const { return m_value; } u32 type() const { return m_type; } operator u32() const { return m_value; } }; using id_map = std::vector>>; } // Object manager for emulated process. Multiple objects of specified arbitrary type are given unique IDs. class idm { // Last allocated ID for constructors static thread_local u32 g_id; // Type Index -> ID -> Object. Use global since only one process is supported atm. static std::vector g_map; template static inline u32 get_type() { return id_manager::typeinfo::get_index(); } template static constexpr u32 get_index(u32 id) { using traits = id_manager::id_traits; constexpr u32 mask_out = ((1u << traits::invl_range.second) - 1) << traits::invl_range.first; // Note: if id is lower than base, diff / step will be higher than count u32 diff = (id & ~mask_out) - traits::base; if (diff % traits::step) { // id is invalid, return invalid index return traits::count; } // Get actual index return diff / traits::step; } // Helper template struct function_traits; template struct function_traits { using object_type = A2; using result_type = R; }; template struct function_traits { using object_type = A2; using result_type = R; }; template struct function_traits { using object_type = A2; using void_type = void; }; template struct function_traits { using object_type = A2; using void_type = void; }; // Helper type: pointer + return value propagated template struct return_pair { std::shared_ptr ptr; RT ret; explicit operator bool() const { return ptr.operator bool(); } T& operator*() const { return *ptr; } T* operator->() const { return ptr.get(); } }; // Unsafe specialization (not refcounted) template struct return_pair { T* ptr; RT ret; explicit operator bool() const { return ptr != nullptr; } T& operator*() const { return *ptr; } T* operator->() const { return ptr; } }; // Prepare new ID (returns nullptr if out of resources) static id_manager::id_map::pointer allocate_id(const id_manager::id_key& info, u32 base, u32 step, u32 count, std::pair invl_range); // Find ID (additionally check type if types are not equal) template static id_manager::id_map::pointer find_id(u32 id) { static_assert(id_manager::id_verify::value, "Invalid ID type combination"); const u32 index = get_index(id); if (index >= id_manager::id_traits::count) { return nullptr; } auto& vec = g_map[get_type()]; if (index >= vec.size()) { return nullptr; } auto& data = vec[index]; if (data.second) { if (std::is_same::value || data.first.type() == get_type()) { if (!id_manager::id_traits::invl_range.second || data.first.value() == id) { return &data; } } } return nullptr; } // Allocate new ID and assign the object from the provider() template static id_manager::id_map::pointer create_id(F&& provider) { static_assert(id_manager::id_verify::value, "Invalid ID type combination"); // ID info const id_manager::id_key info{get_type(), get_type()}; // ID traits using traits = id_manager::id_traits; // Allocate new id std::lock_guard lock(id_manager::g_mutex); if (auto* place = allocate_id(info, traits::base, traits::step, traits::count, traits::invl_range)) { // Get object, store it place->second = provider(); if (place->second) { return place; } } return nullptr; } public: // Initialize object manager static void init(); // Remove all objects static void clear(); // Remove all objects of a type template static inline void clear() { std::lock_guard lock(id_manager::g_mutex); g_map[id_manager::typeinfo::get_index()].clear(); } // Get last ID (updated in create_id/allocate_id) static inline u32 last_id() { return g_id; } // Add a new ID of specified type with specified constructor arguments (returns object or nullptr) template static inline std::enable_if_t::value, std::shared_ptr> make_ptr(Args&&... args) { if (auto pair = create_id([&] { return std::make_shared(std::forward(args)...); })) { return {pair->second, static_cast(pair->second.get())}; } return nullptr; } // Add a new ID of specified type with specified constructor arguments (returns id) template static inline std::enable_if_t::value, u32> make(Args&&... args) { if (auto pair = create_id([&] { return std::make_shared(std::forward(args)...); })) { return pair->first; } return id_manager::id_traits::invalid; } // Add a new ID for an existing object provided (returns new id) template static inline u32 import_existing(const std::shared_ptr& ptr) { if (auto pair = create_id([&] { return ptr; })) { return pair->first; } return id_manager::id_traits::invalid; } // Add a new ID for an object returned by provider() template > static inline u32 import(F&& provider) { if (auto pair = create_id(std::forward(provider))) { return pair->first; } return id_manager::id_traits::invalid; } // Access the ID record without locking (unsafe) template static inline id_manager::id_map::pointer find_unlocked(u32 id) { return find_id(id); } // Check the ID without locking (can be called from other method) template static inline Get* check_unlocked(u32 id) { if (const auto found = find_id(id)) { return static_cast(found->second.get()); } return nullptr; } // Check the ID template static inline Get* check(u32 id) { reader_lock lock(id_manager::g_mutex); return check_unlocked(id); } // Check the ID, access object under shared lock template > static inline auto check(u32 id, F&& func) { reader_lock lock(id_manager::g_mutex); if (const auto ptr = check_unlocked(id)) { if constexpr (!std::is_void_v) { return return_pair{ptr, func(*ptr)}; } else { func(*ptr); return ptr; } } if constexpr (!std::is_void_v) { return return_pair{nullptr}; } else { return static_cast(nullptr); } } // Get the object without locking (can be called from other method) template static inline std::shared_ptr get_unlocked(u32 id) { const auto found = find_id(id); if (found == nullptr) [[unlikely]] { return nullptr; } return std::static_pointer_cast(found->second); } // Get the object template static inline std::shared_ptr get(u32 id) { reader_lock lock(id_manager::g_mutex); const auto found = find_id(id); if (found == nullptr) [[unlikely]] { return nullptr; } return std::static_pointer_cast(found->second); } // Get the object, access object under reader lock template > static inline std::conditional_t, std::shared_ptr, return_pair> get(u32 id, F&& func) { reader_lock lock(id_manager::g_mutex); const auto found = find_id(id); if (found == nullptr) [[unlikely]] { return {nullptr}; } const auto ptr = static_cast(found->second.get()); if constexpr (std::is_void_v) { func(*ptr); return {found->second, ptr}; } else { return {{found->second, ptr}, func(*ptr)}; } } // Access all objects of specified type. Returns the number of objects processed. template ::operator()), typename FRT = typename function_traits::void_type> static inline u32 select(F&& func, int = 0) { static_assert(id_manager::id_verify::value, "Invalid ID type combination"); reader_lock lock(id_manager::g_mutex); u32 result = 0; for (auto& id : g_map[get_type()]) { if (id.second) { if (std::is_same::value || id.first.type() == get_type()) { func(id.first, *static_cast::object_type*>(id.second.get())); result++; } } } return result; } // Access all objects of specified type. If function result evaluates to true, stop and return the object and the value. template ::operator()), typename FRT = typename function_traits::result_type> static inline auto select(F&& func) { static_assert(id_manager::id_verify::value, "Invalid ID type combination"); using object_type = typename function_traits::object_type; using result_type = return_pair; reader_lock lock(id_manager::g_mutex); for (auto& id : g_map[get_type()]) { if (auto ptr = static_cast(id.second.get())) { if (std::is_same::value || id.first.type() == get_type()) { if (FRT result = func(id.first, *ptr)) { return result_type{{id.second, ptr}, std::move(result)}; } } } } return result_type{nullptr}; } // Remove the ID template static inline bool remove(u32 id) { std::shared_ptr ptr; { std::lock_guard lock(id_manager::g_mutex); if (const auto found = find_id(id)) { ptr = std::move(found->second); } else { return false; } } return true; } // Remove the ID if matches the weak/shared ptr template static inline bool remove_verify(u32 id, Ptr sptr) { std::shared_ptr ptr; { std::lock_guard lock(id_manager::g_mutex); if (const auto found = find_id(id); found && (!found->second.owner_before(sptr) && !sptr.owner_before(found->second))) { ptr = std::move(found->second); } else { return false; } } return true; } // Remove the ID and return the object template static inline std::shared_ptr withdraw(u32 id) { std::shared_ptr ptr; { std::lock_guard lock(id_manager::g_mutex); if (const auto found = find_id(id)) { ptr = std::static_pointer_cast(::as_rvalue(std::move(found->second))); } } return ptr; } // Remove the ID after accessing the object under writer lock, return the object and propagate return value template > static inline std::conditional_t, std::shared_ptr, return_pair> withdraw(u32 id, F&& func) { std::unique_lock lock(id_manager::g_mutex); if (const auto found = find_id(id)) { const auto _ptr = static_cast(found->second.get()); if constexpr (std::is_void_v) { func(*_ptr); return std::static_pointer_cast(::as_rvalue(std::move(found->second))); } else { FRT ret = func(*_ptr); if (ret) { // If return value evaluates to true, don't delete the object (error code) return {{found->second, _ptr}, std::move(ret)}; } return {std::static_pointer_cast(::as_rvalue(std::move(found->second))), std::move(ret)}; } } return {nullptr}; } }; #include "util/fixed_typemap.hpp" extern stx::manual_fixed_typemap g_fixed_typemap; constexpr stx::manual_fixed_typemap* g_fxo = &g_fixed_typemap;