#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Stream.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/iterator.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/FormatProviders.h"
#include "llvm/Support/RWMutex.h"
#include "llvm/Support/Threading.h"
#include <array>
#include <utility>
#include <cinttypes>
#include <cstdint>
#include <cstring>
using namespace lldb_private;
class Pool {
public:
static const size_t AllocatorSlabSize = 4096;
static const size_t SizeThreshold = AllocatorSlabSize;
static const size_t AllocatorGrowthDelay = 1;
typedef llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, AllocatorSlabSize,
SizeThreshold, AllocatorGrowthDelay>
Allocator;
typedef const char *StringPoolValueType;
typedef llvm::StringMap<StringPoolValueType, Allocator> StringPool;
typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
static StringPoolEntryType &
GetStringMapEntryFromKeyData(const char *keyData) {
return StringPoolEntryType::GetStringMapEntryFromKeyData(keyData);
}
static size_t GetConstCStringLength(const char *ccstr) {
if (ccstr != nullptr) {
const StringPoolEntryType &entry = GetStringMapEntryFromKeyData(ccstr);
return entry.getKey().size();
}
return 0;
}
StringPoolValueType GetMangledCounterpart(const char *ccstr) {
if (ccstr != nullptr) {
const PoolEntry &pool = selectPool(llvm::StringRef(ccstr));
llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
return GetStringMapEntryFromKeyData(ccstr).getValue();
}
return nullptr;
}
const char *GetConstCString(const char *cstr) {
if (cstr != nullptr)
return GetConstCStringWithLength(cstr, strlen(cstr));
return nullptr;
}
const char *GetConstCStringWithLength(const char *cstr, size_t cstr_len) {
if (cstr != nullptr)
return GetConstCStringWithStringRef(llvm::StringRef(cstr, cstr_len));
return nullptr;
}
const char *GetConstCStringWithStringRef(llvm::StringRef string_ref) {
if (string_ref.data()) {
const uint32_t string_hash = StringPool::hash(string_ref);
PoolEntry &pool = selectPool(string_hash);
{
llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
auto it = pool.m_string_map.find(string_ref, string_hash);
if (it != pool.m_string_map.end())
return it->getKeyData();
}
llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);
StringPoolEntryType &entry =
*pool.m_string_map
.insert(std::make_pair(string_ref, nullptr), string_hash)
.first;
return entry.getKeyData();
}
return nullptr;
}
const char *
GetConstCStringAndSetMangledCounterPart(llvm::StringRef demangled,
const char *mangled_ccstr) {
const char *demangled_ccstr = nullptr;
{
const uint32_t demangled_hash = StringPool::hash(demangled);
PoolEntry &pool = selectPool(demangled_hash);
llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);
StringPool &map = pool.m_string_map;
StringPoolEntryType &entry =
*map.try_emplace_with_hash(demangled, demangled_hash).first;
entry.second = mangled_ccstr;
demangled_ccstr = entry.getKeyData();
}
{
PoolEntry &pool = selectPool(llvm::StringRef(mangled_ccstr));
llvm::sys::SmartScopedWriter<false> wlock(pool.m_mutex);
GetStringMapEntryFromKeyData(mangled_ccstr).setValue(demangled_ccstr);
}
return demangled_ccstr;
}
const char *GetConstTrimmedCStringWithLength(const char *cstr,
size_t cstr_len) {
if (cstr != nullptr) {
const size_t trimmed_len = strnlen(cstr, cstr_len);
return GetConstCStringWithLength(cstr, trimmed_len);
}
return nullptr;
}
ConstString::MemoryStats GetMemoryStats() const {
ConstString::MemoryStats stats;
for (const auto &pool : m_string_pools) {
llvm::sys::SmartScopedReader<false> rlock(pool.m_mutex);
const Allocator &alloc = pool.m_string_map.getAllocator();
stats.bytes_total += alloc.getTotalMemory();
stats.bytes_used += alloc.getBytesAllocated();
}
return stats;
}
protected:
struct PoolEntry {
mutable llvm::sys::SmartRWMutex<false> m_mutex;
StringPool m_string_map;
};
std::array<PoolEntry, 256> m_string_pools;
PoolEntry &selectPool(const llvm::StringRef &s) {
return selectPool(StringPool::hash(s));
}
PoolEntry &selectPool(uint32_t h) {
return m_string_pools[((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h) & 0xff];
}
};
static Pool &StringPool() {
static llvm::once_flag g_pool_initialization_flag;
static Pool *g_string_pool = nullptr;
llvm::call_once(g_pool_initialization_flag,
[]() { g_string_pool = new Pool(); });
return *g_string_pool;
}
ConstString::ConstString(const char *cstr)
: m_string(StringPool().GetConstCString(cstr)) {}
ConstString::ConstString(const char *cstr, size_t cstr_len)
: m_string(StringPool().GetConstCStringWithLength(cstr, cstr_len)) {}
ConstString::ConstString(llvm::StringRef s)
: m_string(StringPool().GetConstCStringWithStringRef(s)) {}
bool ConstString::operator<(ConstString rhs) const {
if (m_string == rhs.m_string)
return false;
llvm::StringRef lhs_string_ref(GetStringRef());
llvm::StringRef rhs_string_ref(rhs.GetStringRef());
if (lhs_string_ref.data() && rhs_string_ref.data())
return lhs_string_ref < rhs_string_ref;
return lhs_string_ref.data() == nullptr;
}
Stream &lldb_private::operator<<(Stream &s, ConstString str) {
const char *cstr = str.GetCString();
if (cstr != nullptr)
s << cstr;
return s;
}
size_t ConstString::GetLength() const {
return Pool::GetConstCStringLength(m_string);
}
bool ConstString::Equals(ConstString lhs, ConstString rhs,
const bool case_sensitive) {
if (lhs.m_string == rhs.m_string)
return true;
if (case_sensitive)
return false;
llvm::StringRef lhs_string_ref(lhs.GetStringRef());
llvm::StringRef rhs_string_ref(rhs.GetStringRef());
return lhs_string_ref.equals_insensitive(rhs_string_ref);
}
int ConstString::Compare(ConstString lhs, ConstString rhs,
const bool case_sensitive) {
const char *lhs_cstr = lhs.m_string;
const char *rhs_cstr = rhs.m_string;
if (lhs_cstr == rhs_cstr)
return 0;
if (lhs_cstr && rhs_cstr) {
llvm::StringRef lhs_string_ref(lhs.GetStringRef());
llvm::StringRef rhs_string_ref(rhs.GetStringRef());
if (case_sensitive) {
return lhs_string_ref.compare(rhs_string_ref);
} else {
return lhs_string_ref.compare_insensitive(rhs_string_ref);
}
}
if (lhs_cstr)
return +1;
else
return -1;
}
void ConstString::Dump(Stream *s, const char *fail_value) const {
if (s != nullptr) {
const char *cstr = AsCString(fail_value);
if (cstr != nullptr)
s->PutCString(cstr);
}
}
void ConstString::DumpDebug(Stream *s) const {
const char *cstr = GetCString();
size_t cstr_len = GetLength();
const char *parens = cstr ? "\"" : "";
s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
static_cast<int>(sizeof(void *) * 2),
static_cast<const void *>(this), parens, cstr, parens,
static_cast<uint64_t>(cstr_len));
}
void ConstString::SetCString(const char *cstr) {
m_string = StringPool().GetConstCString(cstr);
}
void ConstString::SetString(llvm::StringRef s) {
m_string = StringPool().GetConstCStringWithStringRef(s);
}
void ConstString::SetStringWithMangledCounterpart(llvm::StringRef demangled,
ConstString mangled) {
m_string = StringPool().GetConstCStringAndSetMangledCounterPart(
demangled, mangled.m_string);
}
bool ConstString::GetMangledCounterpart(ConstString &counterpart) const {
counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
return (bool)counterpart;
}
void ConstString::SetCStringWithLength(const char *cstr, size_t cstr_len) {
m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
}
void ConstString::SetTrimmedCStringWithLength(const char *cstr,
size_t cstr_len) {
m_string = StringPool().GetConstTrimmedCStringWithLength(cstr, cstr_len);
}
ConstString::MemoryStats ConstString::GetMemoryStats() {
return StringPool().GetMemoryStats();
}
void llvm::format_provider<ConstString>::format(const ConstString &CS,
llvm::raw_ostream &OS,
llvm::StringRef Options) {
format_provider<StringRef>::format(CS.GetStringRef(), OS, Options);
}