31std::unordered_set<InternedString::StringData *, InternedString::Hash,
32 InternedString::Equal>
33 InternedString::pool_;
34std::mutex InternedString::poolMutex_;
37InternedString::StringData::StringData(
const std::string &v) : value_(v) {}
39InternedString::StringData::StringData(std::string &&v)
40 : value_(std::move(v)) {}
44InternedString::Hash::operator()(
const StringData *s)
const noexcept {
45 return std::hash<std::string>()(s->value_);
49InternedString::Hash::operator()(
const std::string &s)
const noexcept {
50 return std::hash<std::string>()(s);
54InternedString::Hash::operator()(std::string_view s)
const noexcept {
55 return std::hash<std::string_view>()(s);
59bool InternedString::Equal::operator()(
const StringData *a,
60 const StringData *b)
const noexcept {
61 return a->value_ == b->value_;
64bool InternedString::Equal::operator()(
const StringData *a,
65 const std::string &b)
const noexcept {
66 return a->value_ == b;
69bool InternedString::Equal::operator()(
const std::string &a,
70 const StringData *b)
const noexcept {
71 return a == b->value_;
74bool InternedString::Equal::operator()(
const StringData *a,
75 std::string_view b)
const noexcept {
76 return a->value_ == b;
79bool InternedString::Equal::operator()(std::string_view a,
80 const StringData *b)
const noexcept {
81 return a == b->value_;
85InternedString::InternedString(
const std::string &v) {
86 data_ = internString(v);
89InternedString::InternedString(std::string &&v) {
90 data_ = internString(std::move(v));
93InternedString::InternedString(
const char *v)
94 : InternedString(std::string_view(v ? v :
"")) {}
96InternedString::InternedString(std::string_view v) { data_ = internString(v); }
98InternedString::InternedString(std::size_t n,
char c)
99 : InternedString(std::string(n, c)) {}
102 : data_(other.data_) {
104 std::lock_guard<std::mutex> lock(poolMutex_);
110 if (
this != &other) {
114 std::lock_guard<std::mutex> lock(poolMutex_);
122 : data_(other.data_) {
123 other.data_ =
nullptr;
127 if (
this != &other) {
130 other.data_ =
nullptr;
135InternedString::~InternedString() { release(); }
139 static const std::string
empty;
140 return data_ ? data_->value_ :
empty;
146 return data_ ? data_->value_.size() : 0;
149InternedString::const_iterator InternedString::begin() const noexcept {
150 return str().begin();
153InternedString::const_iterator InternedString::end() const noexcept {
157const char &InternedString::operator[](std::size_t pos)
const {
161char &InternedString::operator[](std::size_t pos) {
165 if (data_ && pos < data_->value_.size()) {
166 return data_->value_[pos];
169 static char dummy =
'\0';
173const char &InternedString::back()
const {
return str().back(); }
175const char *InternedString::c_str()
const {
return str().c_str(); }
177const char *InternedString::data()
const {
return c_str(); }
182void InternedString::push_back(
char c) {
183 std::string temp =
str();
188InternedString &InternedString::append(
const char *s, std::size_t n) {
189 std::string temp =
str();
191 *
this = InternedString(std::move(temp));
203 std::string combined =
str() + other.
str();
204 *
this = InternedString(std::move(combined));
208void InternedString::resize(std::size_t n,
char c) {
209 std::string temp =
str();
211 *
this = InternedString(std::move(temp));
215bool InternedString::operator==(
const InternedString &other)
const noexcept {
217 return data_ == other.data_;
220bool InternedString::operator!=(
const InternedString &other)
const noexcept {
221 return !(*
this == other);
225 if (lhs.data_ == rhs.data_)
228 return rhs.data_ !=
nullptr;
231 return lhs.data_->value_ < rhs.data_->value_;
236 std::lock_guard<std::mutex> lock(poolMutex_);
241 return data_ ? data_->refCount_ : 0;
245 std::lock_guard<std::mutex> lock(poolMutex_);
246 for (
const auto &v : pool_) {
247 std::cout << v->value_ <<
" (refCount: " << v->refCount_ <<
")\n";
252void InternedString::release() {
254 std::lock_guard<std::mutex> lock(poolMutex_);
255 if (--data_->refCount_ == 0) {
263InternedString::StringData *InternedString::internString(
const std::string &v) {
264 std::lock_guard<std::mutex> lock(poolMutex_);
265 auto it = pool_.find(v);
266 if (it != pool_.end()) {
271 auto *newData =
new StringData(v);
272 newData->refCount_ = 1;
273 pool_.insert(newData);
277InternedString::StringData *InternedString::internString(std::string &&v) {
278 std::lock_guard<std::mutex> lock(poolMutex_);
279 auto it = pool_.find(v);
280 if (it != pool_.end()) {
285 auto *newData =
new StringData(std::move(v));
286 newData->refCount_ = 1;
287 pool_.insert(newData);
291InternedString::StringData *InternedString::internString(std::string_view v) {
292 std::lock_guard<std::mutex> lock(poolMutex_);
293 auto it = pool_.find(v);
294 if (it != pool_.end()) {
298 auto *newData =
new StringData(std::string(v));
299 newData->refCount_ = 1;
300 pool_.insert(newData);
304#ifdef ACAV_ENABLE_STRING_STATS
306StringInterningStats InternedString::getStats() {
307 std::lock_guard<std::mutex> lock(poolMutex_);
309 StringInterningStats stats{};
310 stats.uniqueStrings = pool_.size();
312 for (
const auto *data : pool_) {
313 std::size_t strBytes = data->value_.size();
314 std::size_t strCapacity = data->value_.capacity();
317 stats.poolMemoryBytes += strCapacity;
320 stats.poolOverheadBytes +=
sizeof(StringData);
323 stats.totalReferences += data->refCount_;
326 stats.withoutInterningBytes += data->refCount_ * (strCapacity +
sizeof(std::string));
330 stats.poolOverheadBytes += pool_.bucket_count() *
sizeof(
void *);
333 std::size_t withInterning = stats.poolMemoryBytes + stats.poolOverheadBytes;
334 if (stats.withoutInterningBytes > withInterning) {
335 stats.savedBytes = stats.withoutInterningBytes - withInterning;
336 stats.savingsPercent =
337 100.0 *
static_cast<double>(stats.savedBytes) /
338 static_cast<double>(stats.withoutInterningBytes);
340 stats.savedBytes = 0;
341 stats.savingsPercent = 0.0;
347void InternedString::printStats(
const char *label) {
348 auto stats = getStats();
351 std::cerr <<
"========== String Interning Statistics";
353 std::cerr <<
" [" << label <<
"]";
355 std::cerr <<
" ==========\n";
356 std::cerr << std::fixed << std::setprecision(2);
357 std::cerr <<
" Unique strings in pool: " << stats.uniqueStrings <<
"\n";
358 std::cerr <<
" Total string references: " << stats.totalReferences <<
"\n";
359 std::cerr <<
" Pool string data: " << stats.poolMemoryBytes / 1024.0 <<
" KB\n";
360 std::cerr <<
" Pool overhead: " << stats.poolOverheadBytes / 1024.0 <<
" KB\n";
361 std::cerr <<
" Total with interning: "
362 << (stats.poolMemoryBytes + stats.poolOverheadBytes) / 1024.0 <<
" KB\n";
363 std::cerr <<
" Without interning: " << stats.withoutInterningBytes / 1024.0 <<
" KB\n";
364 std::cerr <<
" Memory saved: " << stats.savedBytes / 1024.0 <<
" KB ("
365 << stats.savingsPercent <<
"%)\n";
366 std::cerr <<
" Deduplication ratio: ";
367 if (stats.uniqueStrings > 0) {
368 std::cerr << static_cast<double>(stats.totalReferences) / stats.uniqueStrings <<
"x\n";
370 std::cerr <<
"N/A\n";
372 std::cerr <<
"=======================================================\n\n";
375void InternedString::resetStats() {
Memory-efficient immutable string with automatic deduplication.
Immutable string with automatic deduplication via global pool.
static void displayPool()
Display all strings in pool (for debugging).
std::size_t refCount() const noexcept
Get reference count for this string.
std::size_t size() const noexcept
Get string length.
bool empty() const noexcept
Check if the string is empty.
const std::string & str() const
Get the underlying string value.
static std::size_t poolSize()
Get current pool size (for debugging).
void clear()
Clear the string (sets to empty interned string).