libcubescript/src/cs_strman.cc

161 lines
4.3 KiB
C++
Raw Normal View History

2021-03-23 01:25:47 +01:00
#include <cubescript/cubescript.hh>
#include "cs_strman.hh"
namespace cscript {
2021-03-23 23:29:32 +01:00
struct string_ref_state {
2021-03-23 01:25:47 +01:00
std::size_t length;
std::size_t refcount;
};
2021-03-23 23:29:32 +01:00
inline string_ref_state *get_ref_state(char const *ptr) {
return const_cast<string_ref_state *>(
reinterpret_cast<string_ref_state const *>(ptr)
2021-03-23 01:25:47 +01:00
) - 1;
}
2021-03-23 23:29:32 +01:00
char const *string_pool::add(std::string_view str) {
2021-03-23 01:25:47 +01:00
auto it = counts.find(str);
/* already present: just increment ref */
if (it != counts.end()) {
auto *st = it->second;
/* having a null pointer is the same as non-existence */
if (st) {
++st->refcount;
return reinterpret_cast<char const *>(st + 1);
}
}
/* not present: allocate brand new data */
auto ss = str.size();
auto strp = alloc_buf(ss);
/* write string data, it's already pre-terminated */
memcpy(strp, str.data(), ss);
/* store it */
counts.emplace(std::string_view{strp, ss}, get_ref_state(strp));
return strp;
}
2021-03-23 23:29:32 +01:00
char const *string_pool::ref(char const *ptr) {
2021-03-23 01:25:47 +01:00
auto *ss = get_ref_state(ptr);
++ss->refcount;
return ptr;
}
2021-03-23 23:29:32 +01:00
string_ref string_pool::steal(char *ptr) {
2021-03-23 01:25:47 +01:00
auto *ss = get_ref_state(ptr);
auto sr = std::string_view{ptr, ss->length};
/* much like add(), but we already have memory */
auto it = counts.find(sr);
if (it != counts.end()) {
auto *st = it->second;
if (st) {
/* the buffer is superfluous now */
2021-03-23 23:29:32 +01:00
cstate->alloc(ss, ss->length + sizeof(string_ref_state) + 1, 0);
return string_ref{reinterpret_cast<char const *>(st + 1), cstate};
2021-03-23 01:25:47 +01:00
}
}
2021-03-23 23:29:32 +01:00
ss->refcount = 0; /* string_ref will increment it */
2021-03-23 01:25:47 +01:00
counts.emplace(sr, ss);
2021-03-23 23:29:32 +01:00
return string_ref{ptr, cstate};
2021-03-23 01:25:47 +01:00
}
2021-03-23 23:29:32 +01:00
void string_pool::unref(char const *ptr) {
2021-03-23 01:25:47 +01:00
auto *ss = get_ref_state(ptr);
if (!--ss->refcount) {
/* refcount zero, so ditch it
* this path is a little slow...
*/
auto sr = std::string_view{ptr, ss->length};
auto it = counts.find(sr);
if (it == counts.end()) {
/* internal error: this should *never* happen */
2021-03-23 23:29:32 +01:00
throw internal_error{"no refcount"};
2021-03-23 01:25:47 +01:00
}
/* we're freeing the key */
counts.erase(it);
/* dealloc */
2021-03-23 23:29:32 +01:00
cstate->alloc(ss, ss->length + sizeof(string_ref_state) + 1, 0);
2021-03-23 01:25:47 +01:00
}
}
2021-03-23 23:29:32 +01:00
char const *string_pool::find(std::string_view str) const {
2021-03-23 01:25:47 +01:00
auto it = counts.find(str);
if (it == counts.end()) {
return nullptr;
}
return reinterpret_cast<char const *>(it->second + 1);
}
2021-03-23 23:29:32 +01:00
std::string_view string_pool::get(char const *ptr) const {
2021-03-23 01:25:47 +01:00
auto *ss = get_ref_state(ptr);
return std::string_view{ptr, ss->length};
}
2021-03-23 23:29:32 +01:00
char *string_pool::alloc_buf(std::size_t len) const {
auto mem = cstate->alloc(nullptr, 0, len + sizeof(string_ref_state) + 1);
2021-03-23 01:25:47 +01:00
if (!mem) {
2021-03-23 23:29:32 +01:00
throw internal_error{"allocation failed"};
2021-03-23 01:25:47 +01:00
}
/* write length and initial refcount */
2021-03-23 23:29:32 +01:00
auto *sst = static_cast<string_ref_state *>(mem);
2021-03-23 01:25:47 +01:00
sst->length = len;
sst->refcount = 1;
/* pre-terminate */
auto *strp = reinterpret_cast<char *>(sst + 1);
strp[len] = '\0';
/* now the user can fill it */
return strp;
};
2021-03-23 01:49:29 +01:00
/* strref implementation */
/* strref */
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT string_ref::string_ref(
internal_state *cs, std::string_view str
2021-03-23 01:49:29 +01:00
): p_state{cs}
{
p_str = cs->strman->add(str);
}
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT string_ref::string_ref(state &cs, std::string_view str):
2021-03-23 01:49:29 +01:00
p_state{cs.p_state}
{
p_str = p_state->strman->add(str);
}
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT string_ref::string_ref(string_ref const &ref):
2021-03-23 01:49:29 +01:00
p_state{ref.p_state}, p_str{ref.p_str}
{
p_state->strman->ref(p_str);
}
2021-03-23 23:29:32 +01:00
/* this can be used by friends to do quick string_ref creation */
LIBCUBESCRIPT_EXPORT string_ref::string_ref(char const *p, internal_state *cs):
2021-03-23 01:49:29 +01:00
p_state{cs}
{
p_str = p_state->strman->ref(p);
}
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT string_ref::~string_ref() {
2021-03-23 01:49:29 +01:00
p_state->strman->unref(p_str);
}
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT string_ref &string_ref::operator=(string_ref const &ref) {
2021-03-23 01:49:29 +01:00
p_str = ref.p_str;
p_state = ref.p_state;
p_state->strman->ref(p_str);
return *this;
}
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT string_ref::operator std::string_view() const {
2021-03-23 01:49:29 +01:00
return p_state->strman->get(p_str);
}
2021-03-23 23:29:32 +01:00
LIBCUBESCRIPT_EXPORT bool string_ref::operator==(string_ref const &s) const {
2021-03-23 01:49:29 +01:00
return p_str == s.p_str;
}
2021-03-23 01:25:47 +01:00
} /* namespace cscript */