184 lines
5.9 KiB
C++
184 lines
5.9 KiB
C++
/** @file StringTable.cpp
|
|
*
|
|
* @author Roland Conybeare, Jan 2026
|
|
**/
|
|
|
|
#include "StringTable.hpp"
|
|
#include <xo/alloc2/Allocator.hpp>
|
|
#include <xo/alloc2/arena/IAllocator_DArena.hpp>
|
|
|
|
namespace xo {
|
|
using xo::mm::ArenaConfig;
|
|
using xo::mm::AAllocator;
|
|
using xo::mm::MemorySizeInfo;
|
|
using xo::facet::with_facet;
|
|
using xo::facet::obj;
|
|
|
|
namespace scm {
|
|
StringTable::StringTable(size_type hint_max_capacity,
|
|
bool debug_flag)
|
|
: strings_{DArena::map(ArenaConfig{.name_ = "strings",
|
|
.size_ = hint_max_capacity})},
|
|
map_{hint_max_capacity}
|
|
{
|
|
(void)debug_flag;
|
|
}
|
|
|
|
const DUniqueString *
|
|
StringTable::lookup(std::string_view key) const
|
|
{
|
|
auto ix = map_.find(key);
|
|
|
|
if (ix != map_.end())
|
|
return ix->second;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const DUniqueString *
|
|
StringTable::intern(std::string_view key)
|
|
{
|
|
// 1a. lookup key in map_.
|
|
// 1b. if present, return existing DString*
|
|
|
|
auto ix = map_.find(key);
|
|
|
|
if (ix != map_.end())
|
|
return ix->second;
|
|
|
|
// 2. otherwise need to add.
|
|
//
|
|
// 2d. return key2 address
|
|
|
|
// 2a. allocate DUniqueString copy 'interned' of key in strings_
|
|
auto mm = with_facet<AAllocator>::mkobj(&strings_);
|
|
DUniqueString * interned = DUniqueString::from_view(mm, key);
|
|
|
|
assert(interned);
|
|
if (interned) {
|
|
// 2b. make string_view from *interned
|
|
std::string_view interned_key = std::string_view(*interned);
|
|
|
|
// interned_key has same lifetime as StringTable,
|
|
// we can use it in map_
|
|
|
|
// 2c. store address of 'interned' in map_
|
|
auto & slot = this->map_[interned_key];
|
|
|
|
slot = interned;
|
|
|
|
return slot;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const DUniqueString *
|
|
StringTable::gensym(std::string_view prefix)
|
|
{
|
|
static std::size_t s_counter = 0;
|
|
|
|
while (true) {
|
|
++s_counter;
|
|
|
|
char buf[80];
|
|
assert(prefix.size() + 20 < sizeof(buf));
|
|
|
|
int n = snprintf(buf, sizeof(buf),
|
|
"%s:%lu",
|
|
prefix.data(), s_counter);
|
|
|
|
if ((0 < n) && (std::size_t(n) < sizeof(buf)))
|
|
buf[n] = '\0';
|
|
else
|
|
buf[sizeof(buf)-1] = '\0';
|
|
|
|
std::string_view sv(buf);
|
|
const DUniqueString * retval = this->lookup(sv);
|
|
if (!retval) {
|
|
/* not already in string view -> we have viable candidate */
|
|
retval = this->intern(sv);
|
|
return retval;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
StringTable::verify_ok(verify_policy policy) const
|
|
{
|
|
using xo::scope;
|
|
using xo::xtag;
|
|
|
|
constexpr const char * c_self = "StringTable::verify_ok";
|
|
scope log(XO_DEBUG(false));
|
|
|
|
/* ST1: underlying hash map passes its invariants */
|
|
if (!map_.verify_ok(policy)) {
|
|
return policy.report_error(log,
|
|
c_self, ": map_.verify_ok failed");
|
|
}
|
|
|
|
/* ST2: for each entry, key points to value's string data */
|
|
for (const auto & kv : map_) {
|
|
const std::string_view & key = kv.first;
|
|
const DUniqueString * value = kv.second;
|
|
|
|
/* ST2.1: value is not null */
|
|
if (value == nullptr) {
|
|
return policy.report_error(log,
|
|
c_self, ": null value in map",
|
|
xtag("key", key));
|
|
}
|
|
|
|
/* ST2.2: value lies within strings_ arena */
|
|
if (!strings_.contains(value)) {
|
|
return policy.report_error(log,
|
|
c_self, ": value not in strings_ arena",
|
|
xtag("key", key),
|
|
xtag("value", (void*)value));
|
|
}
|
|
|
|
/* ST2.3: key.data() points to value's chars */
|
|
if (key.data() != value->chars()) {
|
|
return policy.report_error(log,
|
|
c_self, ": key.data() != value->chars()",
|
|
xtag("key", key),
|
|
xtag("key.data()", (void*)key.data()),
|
|
xtag("value->chars()", (void*)value->chars()));
|
|
}
|
|
|
|
/* ST2.4: key.size() == value->size() */
|
|
if (key.size() != value->size()) {
|
|
return policy.report_error(log,
|
|
c_self, ": key.size() != value->size()",
|
|
xtag("key", key),
|
|
xtag("key.size()", key.size()),
|
|
xtag("value->size()", value->size()));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::size_t
|
|
StringTable::_n_store() const noexcept
|
|
{
|
|
return 1 + map_._n_store();
|
|
}
|
|
|
|
MemorySizeInfo
|
|
StringTable::_store_info(std::size_t i) const noexcept
|
|
{
|
|
if (i == 0)
|
|
return strings_._store_info();
|
|
|
|
if (i+1 < map_._n_store())
|
|
return map_._store_info(i-1);
|
|
|
|
return MemorySizeInfo::sentinel();
|
|
}
|
|
|
|
} /*namespace scm*/
|
|
} /*namespace xo*/
|
|
|
|
/* end StringTable.cpp */
|