libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
stringregistry.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <cstdint>
14#include <promeki/namespace.h>
15#include <promeki/string.h>
16#include <promeki/map.h>
17#include <promeki/list.h>
19#include <promeki/fnv1a.h>
20
21PROMEKI_NAMESPACE_BEGIN
22
23namespace detail {
24
34 [[noreturn]] void stringRegistryCollisionAbort(const char *registryName, const String &existing,
35 const String &incoming, uint64_t hash);
36
37} // namespace detail
38
94template <CompiledString Name> class StringRegistry;
95
111template <CompiledString Name> class StringRegistryItem {
112 public:
114 static constexpr uint64_t InvalidID = UINT64_MAX;
115
117 constexpr StringRegistryItem() = default;
118
129 StringRegistryItem(const String &name)
130 : _id(StringRegistry<Name>::instance().findOrCreateProbe(name)) {}
131
136 StringRegistryItem(const char *name)
137 : _id(StringRegistry<Name>::instance().findOrCreateProbe(String(name))) {}
138
155 static constexpr StringRegistryItem literal(const char *name) {
156 StringRegistryItem item;
157 item._id = fnv1a(name);
158 return item;
159 }
160
166 static StringRegistryItem find(const String &name) {
167 StringRegistryItem item;
168 item._id = StringRegistry<Name>::instance().findId(name);
169 return item;
170 }
171
177 static constexpr StringRegistryItem fromId(uint64_t id) {
178 StringRegistryItem item;
179 item._id = id;
180 return item;
181 }
182
187 constexpr uint64_t id() const { return _id; }
188
193 String name() const { return StringRegistry<Name>::instance().name(_id); }
194
199 constexpr bool isValid() const { return _id != InvalidID; }
200
202 constexpr bool operator==(const StringRegistryItem &other) const { return _id == other._id; }
203
205 constexpr bool operator!=(const StringRegistryItem &other) const { return _id != other._id; }
206
208 constexpr bool operator<(const StringRegistryItem &other) const { return _id < other._id; }
209
210 private:
211 uint64_t _id = InvalidID;
212};
213
214template <CompiledString Name> class StringRegistry {
215 public:
217 using Item = StringRegistryItem<Name>;
218
220 static constexpr uint64_t InvalidID = StringRegistryItem<Name>::InvalidID;
221
223 static StringRegistry &instance() {
224 static StringRegistry reg;
225 return reg;
226 }
227
238 uint64_t findId(const String &str) const {
239 ReadWriteLock::ReadLocker lock(_lock);
240 uint64_t slot = fnv1a(str.cstr());
241 while (true) {
242 auto it = _names.find(slot);
243 if (it == _names.end()) return InvalidID;
244 if (it->second == str) return slot;
245 ++slot;
246 }
247 }
248
266 uint64_t findOrCreateStrict(const String &str) {
267 uint64_t h = fnv1a(str.cstr());
268 // Fast path: check under a read lock first.
269 {
270 ReadWriteLock::ReadLocker lock(_lock);
271 auto it = _names.find(h);
272 if (it != _names.end()) {
273 if (it->second == str) return h;
274 detail::stringRegistryCollisionAbort(Name.bytes(), it->second, str, h);
275 }
276 }
277 ReadWriteLock::WriteLocker lock(_lock);
278 auto it = _names.find(h);
279 if (it != _names.end()) {
280 if (it->second == str) return h;
281 detail::stringRegistryCollisionAbort(Name.bytes(), it->second, str, h);
282 }
283 _names.insert(h, str);
284 return h;
285 }
286
299 uint64_t findOrCreateProbe(const String &str) {
300 uint64_t start = fnv1a(str.cstr());
301 // Fast path: read-locked probe.
302 {
303 ReadWriteLock::ReadLocker lock(_lock);
304 uint64_t slot = start;
305 while (true) {
306 auto it = _names.find(slot);
307 if (it == _names.end()) break;
308 if (it->second == str) return slot;
309 ++slot;
310 }
311 }
312 // Slow path: acquire write lock and retry so we can
313 // safely insert if still missing.
314 ReadWriteLock::WriteLocker lock(_lock);
315 uint64_t slot = start;
316 while (true) {
317 auto it = _names.find(slot);
318 if (it == _names.end()) {
319 _names.insert(slot, str);
320 return slot;
321 }
322 if (it->second == str) return slot;
323 ++slot;
324 }
325 }
326
332 String name(uint64_t id) const {
333 ReadWriteLock::ReadLocker lock(_lock);
334 auto it = _names.find(id);
335 if (it == _names.end()) return String();
336 return it->second;
337 }
338
343 size_t count() const {
344 ReadWriteLock::ReadLocker lock(_lock);
345 return _names.size();
346 }
347
353 bool contains(const String &str) const { return findId(str) != InvalidID; }
354
355 private:
356 StringRegistry() = default;
357
358 mutable ReadWriteLock _lock;
359 Map<uint64_t, String> _names;
360};
361
362PROMEKI_NAMESPACE_END
363
374template <promeki::CompiledString Name> struct std::hash<promeki::StringRegistryItem<Name>> {
375 std::size_t operator()(const promeki::StringRegistryItem<Name> &item) const noexcept {
376 return static_cast<std::size_t>(item.id());
377 }
378};
379
380#endif // PROMEKI_ENABLE_CORE