#pragma once #include "tl/detail/prologue.h" #include #include #include #include #include #include "tl/plain_crash.h" //disable the 'strdup' warning #if defined(_MSC_VER) # pragma warning( disable : 4996 ) #endif #ifndef _SCL_SECURE_NO_WARNINGS # define _SCL_SECURE_NO_WARNINGS #endif namespace tl { namespace detail { class string_db; } TL_API detail::string_db*& get_shared_string_db_instance_ptr() noexcept; } namespace tl { class string; namespace detail { struct TL_API string_cell { friend class string_db_map; using length_t = uint32_t; //uint32_t was chosen to avoid the cell to gain 4 bytes in 64 bit configs. We'll never have strings bigger than 4 billion chars (for the foreseeable future) using hash_t = uint32_t; length_t length; hash_t hash; //hash for key std::atomic_int counter; // uint32_t padding; //this increments the ref_count and returns true IF the cell was not resurrected. So true means the cell was alive before the increment void inc_ref_counter() noexcept { counter.fetch_add(1, std::memory_order_relaxed); } //this decrements the ref_count and returns true if the cell is still alive bool dec_ref_counter() noexcept { const int oldRef = counter.fetch_sub(1, std::memory_order_relaxed); TL_PLAIN_ASSERT(oldRef > 0); return oldRef == 1; } }; static_assert(sizeof(string_cell) % sizeof(uint32_t) == 0); class TL_API string_db_map { public: using cell_t = string_cell; string_db_map() noexcept; ~string_db_map() noexcept; //this returns a new or existing cell with the string in the argument cell_t* find(cell_t::hash_t hash, cell_t::length_t length, const char* string) noexcept; cell_t* find_or_add(cell_t::hash_t hash, cell_t::length_t length, const char* string) noexcept; cell_t* allocate_cell(const char* string, cell_t::length_t length, cell_t::hash_t hash) noexcept; void free_cell(cell_t* cell) noexcept; void mark_cell_as_unused(cell_t& cell) noexcept; void lock() noexcept { m_mutex.lock(); } void unlock() noexcept { m_mutex.unlock(); } private: std::mutex m_mutex; uint32_t m_hashmap_size = 0; uint32_t m_hashmap_size_mask = m_hashmap_size - 1; eastl::vector m_buckets; std::atomic_int m_used_cell_count = { 0 }; void rehash(uint32_t new_size) noexcept; //size_t m_garbage_size = 0; }; class TL_API string_db { public: string_db() noexcept; ~string_db() noexcept; using cell_t = string_cell; static string_db*& get_instance_ptr() noexcept; static string_db& get_instance() noexcept; static void free_instance() noexcept; void internalize(const char* str_start, const char* str_end, cell_t*& dst_cell) noexcept; void internalize(const char* str, cell_t*& dst_cell) noexcept; void append_internalize(cell_t& src_cell, const char* str, size_t str_size, cell_t*& dst_cell) noexcept; void free_cell(cell_t& cell) noexcept; private: string_db_map m_map; }; inline string_db*& string_db::get_instance_ptr() noexcept { static string_db*& s_instance = get_shared_string_db_instance_ptr(); return s_instance; } inline string_db& string_db::get_instance() noexcept { string_db* db = get_instance_ptr(); if (!db) TL_PLAIN_CRASH("String DB was deleted"); return *db; } } }