13# include <absl/container/flat_hash_map.h>
14# include <absl/container/flat_hash_set.h>
15# include <absl/hash/hash.h>
17# include <unordered_map>
18# include <unordered_set>
43 constexpr String() noexcept = default;
52 bool res = s1->size == s2->size;
53 for (
size_t i = 0, e = s1->size; res && i != e; ++i)
54 res &= s1->chars[i] == s2->chars[i];
62 auto sv = std::string_view(s->chars, s->size);
64 return absl::HashOf(sv);
72 static_assert(
sizeof(String) ==
sizeof(size_t),
"String.chars should be 0");
75 constexpr Sym(uintptr_t ptr) noexcept
79 constexpr Sym() noexcept = default;
83 [[nodiscard]] constexpr
bool empty() const noexcept {
return ptr_ == 0; }
84 [[nodiscard]]
constexpr size_t size() const noexcept {
85 if (
empty())
return 0;
87 return ((
const String*)ptr_)->size;
89 [[nodiscard]]
constexpr uintptr_t
raw() const noexcept {
return ptr_; }
98 constexpr char front() const noexcept {
return (*
this)[0]; }
99 constexpr char back() const noexcept {
return (*
this)[
size() - 1]; }
104 constexpr auto begin() const noexcept {
return view().data(); }
107 constexpr auto cend() const noexcept {
return end(); }
108 constexpr auto rbegin() const noexcept {
return std::reverse_iterator(
end()); }
109 constexpr auto rend() const noexcept {
return std::reverse_iterator(
begin()); }
111 constexpr auto crend() const noexcept {
return rend(); }
116 friend constexpr auto operator<=>(Sym s1, Sym s2)
noexcept {
return s1.view() <=> s2.view(); }
117 friend constexpr bool operator==(Sym s1, Sym s2)
noexcept {
return s1.ptr_ == s2.ptr_; }
122 friend constexpr std::strong_ordering
operator<=>(Sym s,
char c)
noexcept {
return cmp<false>(s, c); }
123 friend constexpr std::strong_ordering
operator<=>(
char c, Sym s)
noexcept {
return cmp<true>(s, c); }
124 friend constexpr bool operator==(Sym s,
char c)
noexcept {
return (s.size() == 1) && (s[0] == c); }
125 friend constexpr bool operator==(
char c, Sym s)
noexcept {
return (s.size() == 1) && (s[0] == c); }
130 friend constexpr auto operator<=>(Sym lhs,
const std::convertible_to<std::string_view>
auto& rhs)
noexcept {
131 return lhs.view() <=> std::string_view(rhs);
133 friend constexpr auto operator<=>(
const std::convertible_to<std::string_view>
auto& lhs, Sym rhs)
noexcept {
134 return std::string_view(lhs) <=> rhs.view();
137 friend constexpr bool operator==(Sym lhs,
const std::convertible_to<std::string_view>
auto& rhs)
noexcept {
138 return lhs.view() == std::string_view(rhs);
141 friend constexpr bool operator==(
const std::convertible_to<std::string_view>
auto& lhs, Sym rhs)
noexcept {
142 return std::string_view(lhs) == rhs.view();
151 [[nodiscard]]
constexpr std::string_view
view() const noexcept {
152 if (
empty())
return {std::bit_cast<const char*>(&ptr_), 0};
155 uintptr_t offset = std::endian::native == std::endian::little ? 1 : 0;
157 auto S = std::bit_cast<const String*>(ptr_);
158 return std::string_view(S->chars, S->size);
160 constexpr operator std::string_view() const noexcept {
return view(); }
165 constexpr std::string
str()
const {
return std::string(
view()); }
166 constexpr explicit operator std::string()
const {
return str(); }
167 constexpr explicit operator bool() const noexcept {
return ptr_; }
172 return H::combine(std::move(h), sym.ptr_);
174 friend struct ::std::hash<fe::Sym>;
188 constexpr bool operator()(Sym a, Sym b)
const noexcept {
return a.ptr_ == b.ptr_; }
194 static constexpr std::strong_ordering cmp(
Sym s,
char c)
noexcept {
195 const auto n = s.size();
196 if (n == 0)
return Rev ? std::strong_ordering::greater : std::strong_ordering::less;
198 auto cmp = s[0] <=> c;
199 if (cmp != 0)
return cmp;
201 return (n == 1) ? std::strong_ordering::equal
202 : (Rev ? std::strong_ordering::less : std::strong_ordering::greater);
216struct std::hash<
fe::Sym> {
217 size_t operator()(
fe::Sym sym)
const noexcept {
return fe::hash(sym.ptr_); }
229using SymMap = absl::flat_hash_map<Sym, V, Sym::Hash, Sym::Eq>;
230using SymSet = absl::flat_hash_set<Sym, Sym::Hash, Sym::Eq>;
233using SymMap = std::unordered_map<Sym, V, Sym::Hash, Sym::Eq>;
234using SymSet = std::unordered_set<Sym, Sym::Hash, Sym::Eq>;
245template<
class V,
size_t Size>
249 static constexpr size_t Capacity = std::bit_ceil(2 * Size);
254 assert(!sym.
empty() &&
"the empty Sym%bol marks a free slot");
255 for (
auto i = idx(sym);; i = (i + 1) & Mask) {
256 assert(slots_[i].sym != sym &&
"already present");
257 if (slots_[i].sym.
empty()) {
258 slots_[i] = {sym, std::move(v)};
267 for (
auto i = idx(sym);; i = (i + 1) & Mask) {
268 if (slots_[i].sym.
empty())
return {};
269 if (slots_[i].sym == sym)
return slots_[i].v;
277 static constexpr size_t Mask =
Capacity - 1;
278 static constexpr size_t Shift = 64 - std::bit_width(Mask);
279 static constexpr uint64_t Magic = 0x9E3779B97F4A7C15ull;
283 static size_t idx(
Sym sym) {
return size_t((uint64_t(sym.
raw()) * Magic) >> Shift); }
290 std::array<Slot, Capacity> slots_ = {};
313 Sym sym(
const std::string& s) {
return sym((std::string_view)s); }
315 Sym sym(
const char* s) {
return s ==
nullptr ?
Sym() :
sym(std::string_view(s)); }
322 swap(p1.strings_, p2.strings_);
323 swap(p1.pool_, p2.pool_ );
330 absl::flat_hash_set<const String*, String::Hash, String::Equal> pool_;
332 std::unordered_set<const String*, String::Hash, String::Equal> pool_;
336static_assert(std::is_trivially_copyable_v<Sym>);
337static_assert(
sizeof(uintptr_t) ==
sizeof(
void*),
"uintptr_t must match pointer size");
338static_assert(std::has_unique_object_representations_v<uintptr_t>);
339static_assert(std::endian::native == std::endian::little || std::endian::native == std::endian::big,
340 "mixed endianness not supported");
An arena pre-allocates so-called pages of size Arena::page_size_.
Sym sym(const char *s)
s is a null-terminated C-string.
Sym sym(std::string_view s)
SymPool & operator=(SymPool)=delete
SymPool(const SymPool &)=delete
Sym sym(const std::string &s)
SymPool(SymPool &&other) noexcept
friend void swap(SymPool &p1, SymPool &p2) noexcept
A fixed-capacity Symbol -> V map for a closed set of Size entries: filled once, then only looked up -...
static constexpr size_t Capacity
Twice Size, rounded up to a power of two, so the load factor stays below 1/2.
void emplace(Sym sym, V v)
bool contains(Sym sym) const
std::optional< V > find(Sym sym) const
Yields nothing if sym is not present - the empty Symbol never is.
A Symbol just wraps a pointer to Sym::String, so pass Sym itself around as value.
constexpr uintptr_t raw() const noexcept
friend constexpr std::strong_ordering operator<=>(char c, Sym s) noexcept
friend constexpr bool operator==(Sym lhs, const std::convertible_to< std::string_view > auto &rhs) noexcept
friend constexpr auto operator<=>(Sym s1, Sym s2) noexcept
constexpr auto rend() const noexcept
constexpr auto begin() const noexcept
constexpr char front() const noexcept
constexpr std::string_view operator*() const noexcept
constexpr bool empty() const noexcept
static constexpr size_t Short_String_Mask
static constexpr size_t Short_String_Bytes
constexpr auto cend() const noexcept
friend constexpr bool operator==(char c, Sym s) noexcept
constexpr size_t size() const noexcept
constexpr Sym() noexcept=default
constexpr char back() const noexcept
friend constexpr auto operator<=>(const std::convertible_to< std::string_view > auto &lhs, Sym rhs) noexcept
friend constexpr bool operator==(Sym s1, Sym s2) noexcept
constexpr auto crbegin() const noexcept
constexpr char operator[](size_t i) const noexcept
friend constexpr bool operator==(Sym s, char c) noexcept
constexpr auto crend() const noexcept
friend std::ostream & operator<<(std::ostream &os, Sym sym)
constexpr auto rbegin() const noexcept
friend constexpr bool operator==(const std::convertible_to< std::string_view > auto &lhs, Sym rhs) noexcept
friend constexpr H AbslHashValue(H h, Sym sym) noexcept
constexpr std::string str() const
This involves a copy.
constexpr auto cbegin() const noexcept
friend constexpr auto operator<=>(Sym lhs, const std::convertible_to< std::string_view > auto &rhs) noexcept
constexpr auto end() const noexcept
constexpr std::string_view view() const noexcept
friend constexpr std::strong_ordering operator<=>(Sym s, char c) noexcept
constexpr size_t hash(size_t h) noexcept
Mixes h with murmur3 or splitmix64 - whichever matches sizeof(size_t).
std::unordered_map< Sym, V, Sym::Hash, Sym::Eq > SymMap
constexpr size_t hash_begin() noexcept
Seeds a hash chain with the FNV-1 offset basis.
std::unordered_set< Sym, Sym::Hash, Sym::Eq > SymSet
constexpr bool operator()(Sym a, Sym b) const noexcept
size_t operator()(Sym s) const noexcept
constexpr bool operator()(const String *s1, const String *s2) const noexcept
Hashes the characters, not the pointer - String::Equal compares them, and the two have to agree.
size_t operator()(const String *s) const noexcept
constexpr String() noexcept=default