-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
136 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,141 @@ | ||
#include "lcf/dbstring.h" | ||
#include <unordered_map> | ||
#include <memory> | ||
#include <iostream> | ||
|
||
namespace lcf { | ||
|
||
constexpr DBString::size_type DBString::npos; | ||
alignas(DBString::size_type) constexpr char DBString::_empty_str[sizeof(size_type)]; | ||
|
||
static constexpr size_t AllocSize(size_t len) { | ||
return sizeof(DBString::size_type) + len + 1; | ||
} | ||
struct DBStringData { | ||
using size_type = DBString::size_type; | ||
|
||
static char* Alloc(size_t len) { | ||
return reinterpret_cast<char*>(::operator new(AllocSize(len))); | ||
} | ||
size_type refcnt; | ||
size_type size; | ||
|
||
static char* Dup(const char* other, size_t size) { | ||
if (size > 0) { | ||
auto* s = Alloc(size); | ||
std::memcpy(s, other, AllocSize(size)); | ||
return s; | ||
const char* data() const { | ||
return reinterpret_cast<const char*>(this + 1); | ||
} | ||
return nullptr; | ||
} | ||
|
||
static void Free(void* p) { | ||
::operator delete(p); | ||
} | ||
|
||
DBString::DBString(const char* s, size_t len) | ||
{ | ||
if (len > 0) { | ||
auto* ptr = Alloc(len); | ||
_storage = ptr; | ||
|
||
*reinterpret_cast<size_type*>(ptr) = len; | ||
ptr += sizeof(size_type); | ||
char* data() { | ||
return reinterpret_cast<char*>(this + 1); | ||
} | ||
|
||
std::memcpy(ptr, s, len); | ||
ptr += len; | ||
static size_type alloc_size(StringView str) { | ||
return sizeof(DBStringData) + str.size() + 1; | ||
} | ||
|
||
*ptr = '\0'; | ||
static DBStringData* from_data(char* s) { | ||
return reinterpret_cast<DBStringData*>(s) - 1; | ||
} | ||
}; | ||
|
||
struct DBStringDataDeleter { | ||
void operator()(DBStringData* p); | ||
}; | ||
|
||
using DBStringDataPtr = std::unique_ptr<DBStringData,DBStringDataDeleter>; | ||
|
||
class DBStringAllocator { | ||
public: | ||
using size_type = DBString::size_type; | ||
|
||
static DBStringDataPtr Alloc(StringView str) { | ||
auto* raw = ::operator new(DBStringData::alloc_size(str)); | ||
auto data = DBStringDataPtr(new (raw) DBStringData()); | ||
data->refcnt = 1; | ||
data->size = str.size(); | ||
std::memcpy(data->data(), str.data(), data->size); | ||
data->data()[data->size] = '\0'; | ||
|
||
return data; | ||
} | ||
|
||
static void Free(DBStringData* data) { | ||
data->~DBStringData(); | ||
::operator delete(data); | ||
} | ||
|
||
const char* Acquire(StringView str) { | ||
if (str.empty()) { | ||
return DBString::empty_str(); | ||
} | ||
|
||
auto iter = _map.find(str); | ||
if (iter != _map.end()) { | ||
iter->second->refcnt += 1; | ||
} else { | ||
auto ptr = Alloc(str); | ||
auto sv = StringView(ptr->data(), ptr->size); | ||
// FIXME: Double hash lookup because the key changes.. | ||
iter = _map.insert({ sv, std::move(ptr) }).first; | ||
} | ||
return iter->second->data(); | ||
} | ||
|
||
const char* Dup(const char* s) { | ||
if (s != DBString::empty_str()) { | ||
auto* data = DBStringData::from_data(const_cast<char*>(s)); | ||
data->refcnt += 1; | ||
} | ||
return s; | ||
} | ||
|
||
void Release(StringView str) { | ||
if (str.empty()) { | ||
// This is needed, due to global DBStrings which are initialized to null. | ||
// They may be destroyed *after* DBStringAllocator is destroyed! | ||
// FIMXE: To fix this, use a hash table with constexpr default constructor. | ||
return; | ||
} | ||
auto iter = _map.find(str); | ||
if (iter != _map.end()) { | ||
auto& data = iter->second; | ||
data->refcnt -= 1; | ||
assert(data->refcnt >= 0); | ||
if (data->refcnt == 0) { | ||
_map.erase(iter); | ||
} | ||
} | ||
} | ||
|
||
static DBStringAllocator& instance() { | ||
static DBStringAllocator alloc; | ||
return alloc; | ||
} | ||
private: | ||
DBStringAllocator() = default; | ||
private: | ||
std::unordered_map<StringView,DBStringDataPtr> _map; | ||
}; | ||
|
||
void DBStringDataDeleter::operator()(DBStringData* p) { | ||
DBStringAllocator::Free(p); | ||
} | ||
|
||
DBString::DBString(StringView s) | ||
: _storage(DBStringAllocator::instance().Acquire(s)) | ||
{ | ||
} | ||
|
||
DBString::DBString(const DBString& o) | ||
: _storage(Dup(o._storage, o.size())) | ||
{ } | ||
: _storage(DBStringAllocator::instance().Dup(o._storage)) | ||
{ | ||
} | ||
|
||
DBString& DBString::operator=(const DBString& o) { | ||
if (this != &o) { | ||
// What is strings are the same, skip double lookup? | ||
_reset(); | ||
_storage = Dup(o._storage, o.size()); | ||
_storage = DBStringAllocator::instance().Dup(o._storage); | ||
} | ||
return *this; | ||
} | ||
|
||
|
||
|
||
void DBString::_reset() noexcept { | ||
Free(_storage); | ||
_storage = nullptr; | ||
assert(_storage != nullptr); | ||
DBStringAllocator::instance().Release(*this); | ||
} | ||
|
||
} // namespace lcf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters