-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Finish implementing SharedPreHashedString
- Loading branch information
1 parent
9ea6edc
commit 0c5dea4
Showing
7 changed files
with
99 additions
and
288 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,106 +1,40 @@ | ||
#include "StringName.h" | ||
|
||
#include <ankerl/unordered_dense.h> | ||
|
||
#include <oscar/Utils/SynchronizedValue.h> | ||
#include <oscar/Utils/TransparentStringHasher.h> | ||
|
||
#include <concepts> | ||
#include <ankerl/unordered_dense.h> | ||
|
||
#include <functional> | ||
#include <memory> | ||
#include <string> | ||
#include <string_view> | ||
#include <utility> | ||
|
||
using namespace osc; | ||
using osc::detail::StringNameData; | ||
|
||
namespace | ||
{ | ||
// this is what's stored in the lookup table | ||
// | ||
// `StringNameData` _must_ be reference-stable w.r.t. downstream | ||
// code because users are reading/reference incrementing raw | ||
// pointers | ||
struct StringNameDataPtr final { | ||
public: | ||
explicit StringNameDataPtr(std::string_view str) : | ||
ptr_{std::make_unique<StringNameData>(str)} | ||
{} | ||
|
||
friend bool operator==(std::string_view lhs, const StringNameDataPtr& rhs) | ||
{ | ||
return lhs == rhs.ptr_->value(); | ||
} | ||
|
||
StringNameData* operator->() const | ||
{ | ||
return ptr_.get(); | ||
} | ||
|
||
StringNameData& operator*() const | ||
{ | ||
return *ptr_; | ||
} | ||
private: | ||
std::unique_ptr<StringNameData> ptr_; | ||
}; | ||
|
||
struct TransparentHasher : public TransparentStringHasher { | ||
using TransparentStringHasher::operator(); | ||
|
||
size_t operator()(const StringNameDataPtr& ptr) const noexcept | ||
{ | ||
return ptr->hash(); | ||
} | ||
}; | ||
|
||
using StringNameLookup = ankerl::unordered_dense::set< | ||
StringNameDataPtr, | ||
TransparentHasher, | ||
std::equal_to<> | ||
>; | ||
using FastStringLookup = ankerl::unordered_dense::set<SharedPreHashedString, TransparentStringHasher, std::equal_to<>>; | ||
|
||
SynchronizedValue<StringNameLookup>& get_global_string_name_lut() | ||
SynchronizedValue<FastStringLookup>& get_global_lookup() | ||
{ | ||
static SynchronizedValue<StringNameLookup> s_lut; | ||
return s_lut; | ||
static SynchronizedValue<FastStringLookup> s_lookup; | ||
return s_lookup; | ||
} | ||
} | ||
|
||
template<typename StringLike> | ||
requires | ||
std::constructible_from<std::string, StringLike&&> and | ||
std::convertible_to<StringLike&&, std::string_view> | ||
StringNameData& possibly_construct_then_get_data(StringLike&& input) | ||
{ | ||
auto [it, inserted] = get_global_string_name_lut().lock()->emplace(std::forward<StringLike>(input)); | ||
if (not inserted) { | ||
(*it)->increment_owner_count(); | ||
} | ||
return **it; | ||
} | ||
osc::StringName::StringName(std::string_view sv) : | ||
SharedPreHashedString{*get_global_lookup().lock()->emplace(sv).first} | ||
{} | ||
|
||
void decrement_then_possibly_destruct_data(StringNameData& data) | ||
{ | ||
if (data.decrement_owner_count()) { | ||
get_global_string_name_lut().lock()->erase(data.value()); | ||
} | ||
osc::StringName::~StringName() noexcept | ||
{ | ||
if (empty()) { | ||
return; // special case: the default constructor doesn't use the global lookup | ||
} | ||
|
||
// edge-case for blank string (common) | ||
const StringName& get_cached_blank_string_data() | ||
{ | ||
static const StringName s_blank_string{std::string_view{}}; | ||
return s_blank_string; | ||
if (use_count() > 2) { | ||
return; // other `StringName`s with the same value are still using the data | ||
} | ||
} | ||
|
||
osc::StringName::StringName() : StringName{get_cached_blank_string_data()} {} | ||
osc::StringName::StringName(std::string&& tmp) : data_{&possibly_construct_then_get_data(std::move(tmp))} {} | ||
osc::StringName::StringName(std::string_view sv) : data_{&possibly_construct_then_get_data(sv)} {} | ||
osc::StringName::~StringName() noexcept { decrement_then_possibly_destruct_data(*data_); } | ||
|
||
std::ostream& osc::operator<<(std::ostream& out, const StringName& s) | ||
{ | ||
return out << std::string_view{s}; | ||
// else: clear it from the global table | ||
get_global_lookup().lock()->erase(static_cast<const SharedPreHashedString&>(*this)); | ||
} |
Oops, something went wrong.