diff --git a/src/ref_ptr.hh b/src/ref_ptr.hh new file mode 100644 index 00000000..7bd2ed83 --- /dev/null +++ b/src/ref_ptr.hh @@ -0,0 +1,65 @@ +#ifndef ref_ptr_hh_INCLUDED +#define ref_ptr_hh_INCLUDED + +namespace Kakoune +{ + +template +struct ref_ptr +{ + ref_ptr() = default; + ref_ptr(T* ptr) : m_ptr(ptr) { acquire(); } + ~ref_ptr() { release(); } + ref_ptr(const ref_ptr& other) : m_ptr(other.m_ptr) { acquire(); } + ref_ptr(ref_ptr&& other) : m_ptr(other.m_ptr) { other.m_ptr = nullptr; } + + ref_ptr& operator=(const ref_ptr& other) + { + release(); + m_ptr = other.m_ptr; + acquire(); + return *this; + } + ref_ptr& operator=(ref_ptr&& other) + { + release(); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + return *this; + } + + T* operator->() const { return m_ptr; } + T& operator*() const { return *m_ptr; } + + T* get() const { return m_ptr; } + + explicit operator bool() { return m_ptr; } + + friend bool operator==(const ref_ptr& lhs, const ref_ptr& rhs) + { + return lhs.m_ptr == rhs.m_ptr; + } + friend bool operator!=(const ref_ptr& lhs, const ref_ptr& rhs) + { + return lhs.m_ptr != rhs.m_ptr; + } +private: + T* m_ptr = nullptr; + + void acquire() + { + if (m_ptr) + inc_ref_count(m_ptr); + } + + void release() + { + if (m_ptr) + dec_ref_count(m_ptr); + m_ptr = nullptr; + } +}; + +} + +#endif // ref_ptr_hh_INCLUDED diff --git a/src/shared_string.cc b/src/shared_string.cc index d98affe9..01104e00 100644 --- a/src/shared_string.cc +++ b/src/shared_string.cc @@ -10,16 +10,16 @@ SharedString StringRegistry::intern(StringView str) if (it == m_strings.end()) { SharedString shared_str = str; - it = m_strings.emplace(StringView{shared_str}, shared_str.m_storage).first; + it = m_strings.emplace(shared_str, shared_str.m_storage).first; } - return {*it->second, it->second}; + return {it->second->strview(), it->second}; } void StringRegistry::purge_unused() { for (auto it = m_strings.begin(); it != m_strings.end(); ) { - if (it->second.unique()) + if (it->second->refcount == 1) it = m_strings.erase(it); else ++it; @@ -28,15 +28,14 @@ void StringRegistry::purge_unused() void StringRegistry::debug_stats() const { - write_debug("Shared Strings stats:"); size_t total_refcount = 0; size_t total_size = 0; size_t count = m_strings.size(); for (auto& st : m_strings) { - total_refcount += st.second.use_count() - 1; - total_size += (int)st.second->length(); + total_refcount += st.second->refcount - 1; + total_size += (int)st.second->content.size(); } write_debug(" data size: " + to_string(total_size) + ", mean: " + to_string((float)total_size/count)); write_debug(" refcounts: " + to_string(total_refcount) + ", mean: " + to_string((float)total_refcount/count)); diff --git a/src/shared_string.hh b/src/shared_string.hh index 982cb67f..e2240cc9 100644 --- a/src/shared_string.hh +++ b/src/shared_string.hh @@ -2,26 +2,40 @@ #define shared_string_hh_INCLUDED #include "string.hh" +#include "ref_ptr.hh" #include "utils.hh" #include "unordered_map.hh" -#include - namespace Kakoune { class SharedString : public StringView { public: - using Storage = std::basic_string, - Allocator>; + struct Storage + { + int refcount = 0; + Vector content; + + Storage(StringView str) + { + content.reserve((int)str.length() + 1); + content.assign(str.begin(), str.end()); + content.push_back('\0'); + } + StringView strview() const { return {&content.front(), &content.back()}; } + + friend void inc_ref_count(Storage* s) { ++s->refcount; } + friend void dec_ref_count(Storage* s) { if (--s->refcount == 0) delete s; } + }; + SharedString() = default; SharedString(StringView str) { if (not str.empty()) { - m_storage = std::make_shared(str.begin(), str.end()); - StringView::operator=(*m_storage); + m_storage = new Storage{str}; + StringView::operator=(m_storage->strview()); } } struct NoCopy{}; @@ -39,11 +53,11 @@ public: } private: - SharedString(StringView str, std::shared_ptr storage) + SharedString(StringView str, ref_ptr storage) : StringView{str}, m_storage(std::move(storage)) {} friend class StringRegistry; - std::shared_ptr m_storage; + ref_ptr m_storage; }; inline size_t hash_value(const SharedString& str) @@ -59,7 +73,7 @@ public: void purge_unused(); private: - UnorderedMap, MemoryDomain::SharedString> m_strings; + UnorderedMap, MemoryDomain::SharedString> m_strings; }; inline SharedString intern(StringView str)