#ifndef shared_string_hh_INCLUDED #define shared_string_hh_INCLUDED #include "string.hh" #include "ref_ptr.hh" #include "utils.hh" #include "unordered_map.hh" #include namespace Kakoune { struct StringData : UseMemoryDomain { uint32_t refcount; const int length; [[gnu::always_inline]] const char* data() const { return reinterpret_cast(this + 1); } [[gnu::always_inline]] StringView strview() const { return {data(), length}; } private: StringData(int len) : refcount(0), length(len) {} static constexpr uint32_t interned_flag = 1 << 31; static constexpr uint32_t refcount_mask = ~interned_flag; struct PtrPolicy { static void inc_ref(StringData* r, void*) noexcept { ++r->refcount; } static void dec_ref(StringData* r, void*) noexcept { if ((--r->refcount & refcount_mask) == 0) { if (r->refcount & interned_flag) Registry::instance().remove(r->strview()); StringData::operator delete(r, sizeof(StringData) + r->length + 1); } } static void ptr_moved(StringData*, void*, void*) noexcept {} }; public: using Ptr = RefPtr; class Registry : public Singleton { public: void debug_stats() const; Ptr intern(StringView str); void remove(StringView str); private: UnorderedMap m_strings; }; static Ptr create(ArrayView strs); }; using StringDataPtr = StringData::Ptr; using StringRegistry = StringData::Registry; inline StringDataPtr intern(StringView str) { return StringRegistry::instance().intern(str); } } #endif // shared_string_hh_INCLUDED