refactor: use std::variant in tr_variant (#5936)

This commit is contained in:
Charles Kerr 2023-08-23 12:57:58 -05:00 committed by GitHub
parent fbfbfac3ae
commit 43030132fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 470 additions and 560 deletions

View File

@ -39,6 +39,21 @@ using namespace std::literals;
namespace
{
[[nodiscard]] constexpr size_t variant_size(tr_variant const& var) noexcept
{
switch (var.index())
{
case tr_variant::MapIndex:
return std::size(*var.get_if<tr_variant::Map>());
case tr_variant::VectorIndex:
return std::size(*var.get_if<tr_variant::Vector>());
default:
return {};
}
}
namespace parse_helpers
{
/* arbitrary value... this is much deeper than our code goes */
@ -339,7 +354,7 @@ void action_callback_POP(jsonsl_t jsn, jsonsl_action_t /*action*/, struct jsonsl
data->stack.pop_back();
if (depth < MaxDepth)
{
data->preallocGuess[depth] = v->val.l.count;
data->preallocGuess[depth] = variant_size(*v);
}
}
else if (state->type == JSONSL_T_SPECIAL)
@ -507,7 +522,7 @@ void jsonPushParent(struct JsonWalk* data, tr_variant const& v)
{
auto const is_dict = v.holds_alternative<tr_variant::Map>();
auto const is_list = v.holds_alternative<tr_variant::Vector>();
auto const n_children = is_dict ? v.val.l.count * 2U : v.val.l.count;
auto const n_children = variant_size(v) * (is_dict ? 2U : 1U);
data->parents.push_back({ is_dict, is_list, 0, n_children });
}
@ -658,7 +673,7 @@ void jsonDictBeginFunc(tr_variant const& var, void* vdata)
jsonPushParent(data, var);
data->out.push_back('{');
if (var.val.l.count != 0U)
if (variant_size(var) != 0U)
{
jsonIndent(data);
}
@ -671,7 +686,7 @@ void jsonListBeginFunc(tr_variant const& var, void* vdata)
jsonPushParent(data, var);
data->out.push_back('[');
if (var.val.l.count != 0U)
if (variant_size(var) != 0U)
{
jsonIndent(data);
}

View File

@ -34,107 +34,118 @@ constexpr bool variant_is_container(tr_variant const* const var)
return var != nullptr && (var->holds_alternative<tr_variant::Vector>() || var->holds_alternative<tr_variant::Map>());
}
// ---
constexpr std::optional<size_t> dict_index_of(tr_variant const* const var, tr_quark const key)
constexpr int variant_index(tr_variant const* const var)
{
if (var == nullptr || !var->holds_alternative<tr_variant::Map>())
if (var != nullptr)
{
return {};
return var->index();
}
for (size_t idx = 0; idx < var->val.l.count; ++idx)
return tr_variant::NoneIndex;
}
} // namespace
// ---
tr_variant::StringHolder::StringHolder(std::string&& str) noexcept
: str_{ std::move(str) }
{
sv_ = str_;
}
tr_variant::StringHolder::StringHolder(StringHolder&& that) noexcept
{
*this = std::move(that);
}
void tr_variant::StringHolder::set_unmanaged(std::string_view sv)
{
str_.clear();
sv_ = sv;
}
tr_variant::StringHolder& tr_variant::StringHolder::operator=(StringHolder&& that) noexcept
{
auto const managed = std::data(that.sv_) == std::data(that.str_);
std::swap(str_, that.str_);
sv_ = managed ? str_ : that.sv_;
return *this;
}
// ---
tr_variant::Merge::Merge(tr_variant& tgt)
: tgt_{ tgt }
{
}
void tr_variant::Merge::operator()(std::monostate const& src)
{
tgt_ = src;
}
void tr_variant::Merge::operator()(bool const& src)
{
tgt_ = src;
}
void tr_variant::Merge::operator()(int64_t const& src)
{
tgt_ = src;
}
void tr_variant::Merge::operator()(double const& src)
{
tgt_ = src;
}
void tr_variant::Merge::operator()(tr_variant::StringHolder const& src)
{
tgt_ = src.sv_;
}
void tr_variant::Merge::operator()(tr_variant::Vector const& src)
{
auto const n_items = std::size(src);
auto& tgt = tgt_.val_.emplace<Vector>();
tgt.resize(n_items);
for (size_t i = 0; i < n_items; ++i)
{
if (var->val.l.vals[idx].key == key)
std::visit(Merge{ tgt[i] }, src[i].val_);
}
}
void tr_variant::Merge::operator()(tr_variant::Map const& src)
{
if (tgt_.index() != tr_variant::MapIndex)
{
tgt_.val_.emplace<tr_variant::Map>();
}
auto* const tgt = tgt_.get_if<tr_variant::MapIndex>();
for (auto const& [key, val] : src)
{
std::visit(Merge{ (*tgt)[key] }, val.val_);
}
}
// ---
tr_variant* tr_variantDictFind(tr_variant* const var, tr_quark key)
{
if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr; map != nullptr)
{
if (auto iter = map->find(key); iter != std::end(*map))
{
return idx;
return &iter->second;
}
}
return {};
}
bool dictFindType(tr_variant* const var, tr_quark key, tr_variant::Type type, tr_variant** setme)
{
auto* const res = tr_variantDictFind(var, key);
*setme = res;
return res != nullptr && res->type == type;
}
tr_variant* containerReserve(tr_variant* var, size_t count)
{
TR_ASSERT(variant_is_container(var));
auto& container = var->val.l;
if (size_t const needed = container.count + count; needed > container.alloc)
{
// scale the alloc size in powers-of-2
auto n = container.alloc != 0 ? container.alloc : 8U;
while (n < needed)
{
n *= 2U;
}
auto* vals = new tr_variant[n];
std::move(container.vals, container.vals + container.count, vals);
delete[] container.vals;
container.vals = vals;
container.alloc = n;
}
return container.vals + container.count;
}
bool variant_remove_child(tr_variant* const var, size_t idx)
{
if (!variant_is_container(var))
{
return false;
}
auto& container = var->val.l;
if (idx >= container.count)
{
return false;
}
std::move(container.vals + idx + 1, container.vals + container.count, container.vals + idx);
--container.count;
// container.vals[container.count--] = {};
return true;
}
} // namespace
tr_variant::~tr_variant()
{
if (type == Type::Vector || type == Type::Map)
{
delete[] val.l.vals;
val.l.vals = nullptr;
val.l.count = {};
}
else if (type == Type::String)
{
val.s.clear();
}
type = Type::None;
}
tr_variant* tr_variantDictFind(tr_variant* const var, tr_quark key)
{
auto const idx = dict_index_of(var, key);
return idx.has_value() ? var->val.l.vals + *idx : nullptr;
}
tr_variant* tr_variantListChild(tr_variant* const var, size_t pos)
{
if (var != nullptr && var->holds_alternative<tr_variant::Vector>())
if (auto* const vec = var != nullptr ? var->get_if<tr_variant::VectorIndex>() : nullptr; vec != nullptr)
{
if (auto& container = var->val.l; pos < container.count)
if (pos < std::size(*vec))
{
return container.vals + pos;
return &vec->at(pos);
}
}
@ -143,48 +154,44 @@ tr_variant* tr_variantListChild(tr_variant* const var, size_t pos)
bool tr_variantListRemove(tr_variant* const var, size_t pos)
{
return variant_remove_child(var, pos);
if (auto* const vec = var != nullptr ? var->get_if<tr_variant::VectorIndex>() : nullptr;
vec != nullptr && pos < std::size(*vec))
{
vec->erase(std::begin(*vec) + pos);
return true;
}
return false;
}
bool tr_variantGetInt(tr_variant const* const var, int64_t* setme)
{
if (var == nullptr)
switch (variant_index(var))
{
case tr_variant::IntIndex:
*setme = *var->get_if<tr_variant::IntIndex>();
return true;
case tr_variant::BoolIndex:
*setme = *var->get_if<tr_variant::BoolIndex>() ? 1 : 0;
return true;
default:
return false;
}
if (var->holds_alternative<int64_t>())
{
if (setme != nullptr)
{
*setme = var->val.i;
}
return true;
}
if (var->holds_alternative<bool>())
{
if (setme != nullptr)
{
*setme = var->val.b ? 1 : 0;
}
return true;
}
return false;
}
bool tr_variantGetStrView(tr_variant const* const var, std::string_view* setme)
{
if (var != nullptr && var->holds_alternative<std::string_view>())
switch (variant_index(var))
{
*setme = var->val.s.get();
case tr_variant::StringIndex:
*setme = *var->get_if<tr_variant::StringIndex>();
return true;
}
return false;
default:
return false;
}
}
bool tr_variantGetRaw(tr_variant const* v, std::byte const** setme_raw, size_t* setme_len)
@ -213,36 +220,35 @@ bool tr_variantGetRaw(tr_variant const* v, uint8_t const** setme_raw, size_t* se
bool tr_variantGetBool(tr_variant const* const var, bool* setme)
{
if (var == nullptr)
switch (variant_index(var))
{
return false;
}
if (var->holds_alternative<bool>())
{
*setme = var->val.b;
case tr_variant::BoolIndex:
*setme = *var->get_if<tr_variant::BoolIndex>();
return true;
}
if (var->holds_alternative<int64_t>() && (var->val.i == 0 || var->val.i == 1))
{
*setme = var->val.i != 0;
return true;
}
case tr_variant::IntIndex:
if (auto const val = *var->get_if<tr_variant::IntIndex>(); val == 0 || val == 1)
{
*setme = val != 0;
return true;
}
break;
if (auto sv = std::string_view{}; tr_variantGetStrView(var, &sv))
{
if (sv == "true"sv)
case tr_variant::StringIndex:
if (auto const val = *var->get_if<tr_variant::StringIndex>(); val == "true"sv)
{
*setme = true;
return true;
}
if (sv == "false"sv)
else if (val == "false"sv)
{
*setme = false;
return true;
}
break;
default:
break;
}
return false;
@ -250,33 +256,27 @@ bool tr_variantGetBool(tr_variant const* const var, bool* setme)
bool tr_variantGetReal(tr_variant const* const var, double* setme)
{
if (var == nullptr)
switch (variant_index(var))
{
return false;
}
if (var->holds_alternative<double>())
{
*setme = var->val.d;
case tr_variant::DoubleIndex:
*setme = *var->get_if<tr_variant::DoubleIndex>();
return true;
}
if (var->holds_alternative<int64_t>())
{
*setme = static_cast<double>(var->val.i);
case tr_variant::IntIndex:
*setme = static_cast<double>(*var->get_if<tr_variant::IntIndex>());
return true;
}
if (var->holds_alternative<std::string_view>())
{
if (auto val = tr_num_parse<double>(var->val.s.get()); val)
case tr_variant::StringIndex:
if (auto const val = tr_num_parse<double>(*var->get_if<tr_variant::StringIndex>()); val)
{
*setme = *val;
return true;
}
}
[[fallthrough]];
return false;
default:
return false;
}
}
bool tr_variantDictFindInt(tr_variant* const var, tr_quark key, int64_t* setme)
@ -305,12 +305,24 @@ bool tr_variantDictFindStrView(tr_variant* const var, tr_quark key, std::string_
bool tr_variantDictFindList(tr_variant* const var, tr_quark key, tr_variant** setme)
{
return dictFindType(var, key, tr_variant::Type::Vector, setme);
if (auto* const res = tr_variantDictFind(var, key); res != nullptr && res->holds_alternative<tr_variant::Vector>())
{
*setme = res;
return true;
}
return false;
}
bool tr_variantDictFindDict(tr_variant* const var, tr_quark key, tr_variant** setme)
{
return dictFindType(var, key, tr_variant::Type::Map, setme);
if (auto* const res = tr_variantDictFind(var, key); res != nullptr && res->holds_alternative<tr_variant::Map>())
{
*setme = res;
return true;
}
return false;
}
bool tr_variantDictFindRaw(tr_variant* const var, tr_quark key, uint8_t const** setme_raw, size_t* setme_len)
@ -327,10 +339,24 @@ bool tr_variantDictFindRaw(tr_variant* const var, tr_quark key, std::byte const*
// ---
void tr_variantInitReal(tr_variant* initme, double value)
{
*initme = value;
}
void tr_variantInitBool(tr_variant* initme, bool value)
{
*initme = value;
}
void tr_variantInitInt(tr_variant* initme, int64_t value)
{
*initme = value;
}
void tr_variantInitStrView(tr_variant* initme, std::string_view val)
{
tr_variantInit(initme, tr_variant::Type::String);
initme->val.s.set_shallow(val);
*initme = tr_variant::unmanaged_string(val);
}
void tr_variantInitRaw(tr_variant* initme, void const* value, size_t value_len)
@ -345,14 +371,14 @@ void tr_variantInitQuark(tr_variant* initme, tr_quark value)
void tr_variantInitStr(tr_variant* initme, std::string_view value)
{
tr_variantInit(initme, tr_variant::Type::String);
initme->val.s.set(value);
*initme = value;
}
void tr_variantInitList(tr_variant* initme, size_t reserve_count)
{
tr_variantInit(initme, tr_variant::Type::Vector);
tr_variantListReserve(initme, reserve_count);
auto vec = tr_variant::Vector{};
vec.reserve(reserve_count);
*initme = std::move(vec);
}
void tr_variantListReserve(tr_variant* const var, size_t count)
@ -360,21 +386,19 @@ void tr_variantListReserve(tr_variant* const var, size_t count)
TR_ASSERT(var != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Vector>());
containerReserve(var, count);
if (auto* const vec = var != nullptr ? var->get_if<tr_variant::VectorIndex>() : nullptr; vec != nullptr)
{
vec->reserve(std::size(*vec) + count);
}
}
void tr_variantInitDict(tr_variant* initme, size_t reserve_count)
void tr_variantInitDict(tr_variant* initme, size_t /*reserve_count*/)
{
tr_variantInit(initme, tr_variant::Type::Map);
tr_variantDictReserve(initme, reserve_count);
*initme = tr_variant::Map{};
}
void tr_variantDictReserve(tr_variant* const var, size_t reserve_count)
void tr_variantDictReserve(tr_variant* const /*var*/, size_t /*reserve_count*/)
{
TR_ASSERT(var != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Map>());
containerReserve(var, reserve_count);
}
tr_variant* tr_variantListAdd(tr_variant* const var)
@ -382,66 +406,67 @@ tr_variant* tr_variantListAdd(tr_variant* const var)
TR_ASSERT(var != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Vector>());
auto* const child = containerReserve(var, 1);
++var->val.l.count;
*child = tr_variant{};
if (auto* const vec = var != nullptr ? var->get_if<tr_variant::VectorIndex>() : nullptr; vec != nullptr)
{
return &vec->emplace_back();
}
return child;
return nullptr;
}
tr_variant* tr_variantListAddInt(tr_variant* const var, int64_t value)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitInt(child, value);
*child = value;
return child;
}
tr_variant* tr_variantListAddReal(tr_variant* const var, double value)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitReal(child, value);
*child = value;
return child;
}
tr_variant* tr_variantListAddBool(tr_variant* const var, bool value)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitBool(child, value);
*child = value;
return child;
}
tr_variant* tr_variantListAddStr(tr_variant* const var, std::string_view value)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitStr(child, value);
*child = value;
return child;
}
tr_variant* tr_variantListAddStrView(tr_variant* const var, std::string_view value)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitStrView(child, value);
*child = tr_variant::unmanaged_string(value);
return child;
}
tr_variant* tr_variantListAddQuark(tr_variant* const var, tr_quark value)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitQuark(child, value);
return child;
return tr_variantListAddStrView(var, tr_quark_get_string_view(value));
}
tr_variant* tr_variantListAddRaw(tr_variant* const var, void const* value, size_t value_len)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitRaw(child, value, value_len);
*child = std::string_view{ static_cast<char const*>(value), value_len };
return child;
}
tr_variant* tr_variantListAddList(tr_variant* const var, size_t reserve_count)
{
auto* const child = tr_variantListAdd(var);
tr_variantInitList(child, reserve_count);
auto vec = tr_variant::Vector{};
vec.reserve(reserve_count);
*child = std::move(vec);
return child;
}
@ -457,12 +482,12 @@ tr_variant* tr_variantDictAdd(tr_variant* const var, tr_quark key)
TR_ASSERT(var != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Map>());
auto* const child = containerReserve(var, 1);
++var->val.l.count;
*child = tr_variant{};
child->key = key;
if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr; map != nullptr)
{
return &(*map)[key];
}
return child;
return {};
}
tr_variant* tr_variantDictAddInt(tr_variant* const var, tr_quark key, int64_t val)
@ -537,8 +562,12 @@ tr_variant* tr_variantDictAddDict(tr_variant* const var, tr_quark key, size_t re
bool tr_variantDictRemove(tr_variant* const var, tr_quark key)
{
auto const idx = dict_index_of(var, key);
return idx.has_value() && variant_remove_child(var, *idx);
if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr; map != nullptr)
{
return map->erase(key) != 0U;
}
return false;
}
// --- BENC WALKING
@ -553,20 +582,31 @@ public:
{
}
tr_variant const* next_child()
std::pair<tr_quark, tr_variant const*> next_child()
{
if (!variant_is_container(var_) || (child_index_ >= var_->val.l.count))
if (var_ == nullptr)
{
return nullptr;
return {};
}
auto idx = child_index_++;
if (!sorted.empty())
if (auto const* const map = var_->get_if<tr_variant::MapIndex>(); map != nullptr)
{
idx = sorted[idx];
if (auto idx = next_index(); idx < std::size(*map))
{
auto iter = std::cbegin(*map);
std::advance(iter, idx);
return { iter->first, &iter->second };
}
}
else if (auto const* const vec = var_->get_if<tr_variant::VectorIndex>(); vec != nullptr)
{
if (auto idx = next_index(); idx < std::size(*vec))
{
return { {}, &vec->at(idx) };
}
}
return var_->val.l.vals + idx;
return {};
}
[[nodiscard]] constexpr auto is_visited() const noexcept
@ -608,18 +648,19 @@ protected:
template<typename Container>
void sort(Container& sortbuf)
{
if (var_ == nullptr || !var_->holds_alternative<tr_variant::Map>())
auto const* const map = var_ != nullptr ? var_->get_if<tr_variant::MapIndex>() : nullptr;
if (map == nullptr)
{
return;
}
auto const n = var_->val.l.count;
auto const* children = var_->val.l.vals;
auto idx = size_t{};
auto const n = std::size(*map);
sortbuf.resize(n);
for (size_t i = 0; i < n; ++i)
for (auto const& [key, val] : *map)
{
sortbuf[i] = { tr_quark_get_string_view(children[i].key), i };
sortbuf[idx] = { tr_quark_get_string_view(key), idx };
++idx;
}
std::sort(std::begin(sortbuf), std::end(sortbuf), [](ByKey const& a, ByKey const& b) { return a.key < b.key; });
@ -640,6 +681,18 @@ private:
// When `v` is a dict, this is its children's indices sorted by key.
// Bencoded dicts must be sorted, so this is useful when writing benc.
small::vector<size_t, 128U> sorted;
[[nodiscard]] size_t next_index()
{
auto idx = child_index_++;
if (idx < std::size(sorted))
{
idx = sorted[idx];
}
return idx;
}
};
class VariantWalker
@ -700,15 +753,17 @@ void tr_variant_serde::walk(tr_variant const& top, WalkFuncs const& walk_funcs,
}
else
{
v = node.next_child();
auto [key, child] = node.next_child();
v = child;
if (v != nullptr)
{
if (node.current()->holds_alternative<tr_variant::Map>())
{
auto const keystr = tr_quark_get_string_view(v->key);
auto const keystr = tr_quark_get_string_view(key);
auto tmp = tr_variant{};
tr_variantInitQuark(&tmp, v->key);
tr_variantInitQuark(&tmp, key);
walk_funcs.string_func(tmp, keystr, user_data);
}
}
@ -724,53 +779,48 @@ void tr_variant_serde::walk(tr_variant const& top, WalkFuncs const& walk_funcs,
}
}
if (v != nullptr)
switch (variant_index(v))
{
switch (v->type)
case tr_variant::BoolIndex:
walk_funcs.bool_func(*v, *v->get_if<tr_variant::BoolIndex>(), user_data);
break;
case tr_variant::IntIndex:
walk_funcs.int_func(*v, *v->get_if<tr_variant::IntIndex>(), user_data);
break;
case tr_variant::DoubleIndex:
walk_funcs.double_func(*v, *v->get_if<tr_variant::DoubleIndex>(), user_data);
break;
case tr_variant::StringIndex:
walk_funcs.string_func(*v, *v->get_if<tr_variant::StringIndex>(), user_data);
break;
case tr_variant::VectorIndex:
if (v == node.current())
{
case tr_variant::Type::Int:
walk_funcs.int_func(*v, v->val.i, user_data);
break;
case tr_variant::Type::Bool:
walk_funcs.bool_func(*v, v->val.b, user_data);
break;
case tr_variant::Type::Double:
walk_funcs.double_func(*v, v->val.d, user_data);
break;
case tr_variant::Type::String:
walk_funcs.string_func(*v, v->val.s.get(), user_data);
break;
case tr_variant::Type::Vector:
if (v == node.current())
{
walk_funcs.list_begin_func(*v, user_data);
}
else
{
stack.emplace(v, sort_dicts);
}
break;
case tr_variant::Type::Map:
if (v == node.current())
{
walk_funcs.dict_begin_func(*v, user_data);
}
else
{
stack.emplace(v, sort_dicts);
}
break;
default:
/* did caller give us an uninitialized val? */
tr_logAddError(_("Invalid metadata"));
break;
walk_funcs.list_begin_func(*v, user_data);
}
else
{
stack.emplace(v, sort_dicts);
}
break;
case tr_variant::MapIndex:
if (v == node.current())
{
walk_funcs.dict_begin_func(*v, user_data);
}
else
{
stack.emplace(v, sort_dicts);
}
break;
default: // NoneIndex:
break;
}
}
}
@ -782,164 +832,24 @@ bool tr_variantDictChild(tr_variant* const var, size_t pos, tr_quark* key, tr_va
TR_ASSERT(var != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Map>());
bool success = false;
if (var != nullptr && var->holds_alternative<tr_variant::Map>())
if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr;
map != nullptr && pos < std::size(*map))
{
if (auto& container = var->val.l; pos < container.count)
{
*key = container.vals[pos].key;
*setme_value = container.vals + pos;
success = true;
}
auto iter = std::begin(*map);
std::advance(iter, pos);
*key = iter->first;
*setme_value = &iter->second;
return true;
}
return success;
return false;
}
namespace
{
namespace merge_helpers
{
void tr_variantListCopy(tr_variant* target, tr_variant const* src)
{
for (size_t i = 0;; ++i)
{
auto const* const child = tr_variantListChild(const_cast<tr_variant*>(src), i);
if (child == nullptr)
{
break;
}
if (child->holds_alternative<bool>())
{
auto val = bool{};
tr_variantGetBool(child, &val);
tr_variantListAddBool(target, val);
}
else if (child->holds_alternative<double>())
{
auto val = double{};
tr_variantGetReal(child, &val);
tr_variantListAddReal(target, val);
}
else if (child->holds_alternative<int64_t>())
{
auto val = int64_t{};
tr_variantGetInt(child, &val);
tr_variantListAddInt(target, val);
}
else if (child->holds_alternative<std::string_view>())
{
auto val = std::string_view{};
(void)tr_variantGetStrView(child, &val);
tr_variantListAddRaw(target, std::data(val), std::size(val));
}
else if (child->holds_alternative<tr_variant::Map>())
{
tr_variantMergeDicts(tr_variantListAddDict(target, 0), child);
}
else if (child->holds_alternative<tr_variant::Vector>())
{
tr_variantListCopy(tr_variantListAddList(target, 0), child);
}
else
{
tr_logAddWarn("tr_variantListCopy skipping item");
}
}
}
constexpr size_t tr_variantDictSize(tr_variant const* const var)
{
return var != nullptr && var->holds_alternative<tr_variant::Map>() ? var->val.l.count : 0U;
}
} // namespace merge_helpers
} // namespace
void tr_variantMergeDicts(tr_variant* const tgt, tr_variant const* const src)
{
using namespace merge_helpers;
TR_ASSERT(tgt != nullptr);
TR_ASSERT(tgt->holds_alternative<tr_variant::Map>());
TR_ASSERT(src != nullptr);
TR_ASSERT(src->holds_alternative<tr_variant::Map>());
size_t const source_count = tr_variantDictSize(src);
tr_variantDictReserve(tgt, source_count + tr_variantDictSize(tgt));
for (size_t i = 0; i < source_count; ++i)
{
auto key = tr_quark{};
tr_variant* child = nullptr;
if (tr_variantDictChild(const_cast<tr_variant*>(src), i, &key, &child))
{
tr_variant* t = nullptr;
// if types differ, ensure that target will overwrite source
auto const* const target_child = tr_variantDictFind(tgt, key);
if ((target_child != nullptr) && child->type != target_child->type)
{
tr_variantDictRemove(tgt, key);
}
if (child->holds_alternative<bool>())
{
auto val = bool{};
tr_variantGetBool(child, &val);
tr_variantDictAddBool(tgt, key, val);
}
else if (child->holds_alternative<double>())
{
auto val = double{};
tr_variantGetReal(child, &val);
tr_variantDictAddReal(tgt, key, val);
}
else if (child->holds_alternative<int64_t>())
{
auto val = int64_t{};
tr_variantGetInt(child, &val);
tr_variantDictAddInt(tgt, key, val);
}
else if (child->holds_alternative<std::string_view>())
{
auto val = std::string_view{};
(void)tr_variantGetStrView(child, &val);
tr_variantDictAddRaw(tgt, key, std::data(val), std::size(val));
}
else if (child->holds_alternative<tr_variant::Map>() && tr_variantDictFindDict(tgt, key, &t))
{
tr_variantMergeDicts(t, child);
}
else if (child->holds_alternative<tr_variant::Vector>())
{
if (tr_variantDictFind(tgt, key) == nullptr)
{
tr_variantListCopy(tr_variantDictAddList(tgt, key, tr_variantListSize(child)), child);
}
}
else if (child->holds_alternative<tr_variant::Map>())
{
tr_variant* target_dict = tr_variantDictFind(tgt, key);
if (target_dict == nullptr)
{
target_dict = tr_variantDictAddDict(tgt, key, tr_variantDictSize(child));
}
if (target_dict->holds_alternative<tr_variant::Map>())
{
tr_variantMergeDicts(target_dict, child);
}
}
else
{
tr_logAddDebug(fmt::format("tr_variantMergeDicts skipping '{}'", tr_quark_get_string_view(key)));
}
}
}
tgt->merge(*src);
}
// ---

View File

@ -5,14 +5,16 @@
#pragma once
#include <algorithm>
#include <algorithm> // std::move()
#include <cstddef> // size_t
#include <cstdint> // int64_t
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <type_traits> // std::is_same_v
#include <utility> // std::as_const, std::pair
#include <variant>
#include <vector>
#include "libtransmission/quark.h"
@ -20,188 +22,181 @@
struct tr_error;
/**
* A variant that holds typical benc/json types: bool, int, double, string,
* vectors of variants, and maps of string-to-variant.
* A variant that holds typical benc/json types: bool, int,
* double, string, vectors of variants, and maps of variants.
* Useful when serializing / deserializing benc/json data.
*
* @see tr_variant_serde
*/
struct tr_variant
{
private:
struct String
{
void set_shallow(std::string_view newval)
{
clear();
type_ = Type::View;
str_.str = std::data(newval);
len_ = std::size(newval);
}
void set(std::string_view newval)
{
clear();
len_ = std::size(newval);
if (len_ < sizeof(str_.buf))
{
type_ = Type::Buf;
std::copy_n(std::data(newval), len_, str_.buf);
str_.buf[len_] = '\0';
}
else
{
char* const newstr = new char[len_ + 1];
std::copy_n(std::data(newval), len_, newstr);
newstr[len_] = '\0';
type_ = Type::Heap;
str_.str = newstr;
}
}
[[nodiscard]] constexpr std::string_view get() const noexcept
{
return { type_ == Type::Buf ? str_.buf : str_.str, len_ };
}
void clear()
{
if (type_ == Type::Heap)
{
delete[] str_.str;
}
*this = {};
}
private:
enum class Type
{
Heap,
Buf,
View
};
Type type_ = Type::View;
size_t len_ = 0U;
union
{
char buf[16];
char const* str;
} str_ = {};
};
public:
enum class Type
enum Type
{
None,
Bool,
Int,
Double,
String,
Vector,
Map
NoneIndex = 0,
BoolIndex = 1,
IntIndex = 2,
DoubleIndex = 3,
StringIndex = 4,
VectorIndex = 5,
MapIndex = 6
};
using Vector = std::vector<tr_variant>;
using Map = std::map<tr_quark, tr_variant>;
tr_variant() noexcept = default;
constexpr tr_variant() noexcept = default;
tr_variant(tr_variant const&) = delete;
tr_variant(tr_variant&& that) noexcept = default;
tr_variant& operator=(tr_variant const&) = delete;
tr_variant& operator=(tr_variant&& that) noexcept = default;
tr_variant(tr_variant&& that) noexcept
template<typename Val>
explicit tr_variant(Val value)
{
*this = std::move(that);
*this = std::move(value);
}
~tr_variant();
tr_variant& operator=(tr_variant const&) = delete;
tr_variant& operator=(tr_variant&& that) noexcept
template<typename Val>
tr_variant& operator=(Val value)
{
std::swap(key, that.key);
std::swap(type, that.type);
std::swap(val, that.val);
val_ = std::move(value);
return *this;
}
tr_variant& operator=(std::string&& value)
{
val_.emplace<StringHolder>(std::move(value));
return *this;
}
tr_variant& operator=(std::string const& value)
{
val_.emplace<StringHolder>(std::string(value));
return *this;
}
tr_variant& operator=(std::string_view value)
{
val_.emplace<StringHolder>(std::string(value));
return *this;
}
[[nodiscard]] constexpr auto index() const noexcept
{
return val_.index();
}
[[nodiscard]] constexpr auto has_value() const noexcept
{
return type != Type::None;
return index() != NoneIndex;
}
template<typename Val>
[[nodiscard]] constexpr auto* get_if() noexcept
{
if constexpr (std::is_same_v<Val, std::string_view>)
{
auto const* const str = std::get_if<StringHolder>(&val_);
return str != nullptr ? &str->sv_ : nullptr;
}
else
{
return std::get_if<Val>(&val_);
}
}
template<typename Val>
[[nodiscard]] constexpr auto const* get_if() const noexcept
{
return const_cast<tr_variant*>(this)->get_if<Val>();
}
template<size_t Index>
[[nodiscard]] auto* get_if() noexcept
{
if constexpr (Index == StringIndex)
{
auto const* const str = std::get_if<StringIndex>(&val_);
return str != nullptr ? &str->sv_ : nullptr;
}
else
{
return std::get_if<Index>(&val_);
}
}
template<size_t Index>
[[nodiscard]] constexpr auto const* get_if() const noexcept
{
return const_cast<tr_variant*>(this)->get_if<Index>();
}
[[nodiscard]] static tr_variant unmanaged_string(std::string_view val)
{
auto ret = tr_variant{};
ret.val_.emplace<StringHolder>().set_unmanaged(val);
return ret;
}
template<typename Val>
[[nodiscard]] constexpr bool holds_alternative() const noexcept
{
static_assert(
std::is_same_v<Val, bool> || std::is_same_v<Val, int64_t> || std::is_same_v<Val, double> ||
std::is_same_v<Val, std::string_view> || std::is_same_v<Val, Vector> || std::is_same_v<Val, Map>);
if constexpr (std::is_same_v<Val, bool>)
{
return type == Type::Bool;
}
if constexpr (std::is_same_v<Val, int64_t>)
{
return type == Type::Int;
}
if constexpr (std::is_same_v<Val, double>)
{
return type == Type::Double;
}
if constexpr (std::is_same_v<Val, std::string_view>)
{
return type == Type::String;
return std::holds_alternative<StringHolder>(val_);
}
if constexpr (std::is_same_v<Val, Vector>)
else
{
return type == Type::Vector;
return std::holds_alternative<Val>(val_);
}
if constexpr (std::is_same_v<Val, Map>)
{
return type == Type::Map;
}
return false;
}
void clear()
{
*this = tr_variant{};
val_.emplace<std::monostate>();
}
Type type = Type::None;
tr_quark key = TR_KEY_NONE;
union
void merge(tr_variant const& that)
{
bool b;
std::visit(Merge{ *this }, that.val_);
}
double d;
private:
// Holds a string_view to either an unmanaged/external string or to
// one owned by the class. If the string is unmanaged, only sv_ is used.
// If we own the string, then sv_ points to the managed str_.
class StringHolder
{
public:
StringHolder() = default;
explicit StringHolder(std::string&& str) noexcept;
explicit StringHolder(StringHolder&& that) noexcept;
void set_unmanaged(std::string_view sv);
StringHolder& operator=(StringHolder&& that) noexcept;
std::string_view sv_;
int64_t i;
private:
std::string str_;
};
String s;
class Merge
{
public:
explicit Merge(tr_variant& tgt);
void operator()(std::monostate const& src);
void operator()(bool const& src);
void operator()(int64_t const& src);
void operator()(double const& src);
void operator()(tr_variant::StringHolder const& src);
void operator()(tr_variant::Vector const& src);
void operator()(tr_variant::Map const& src);
struct
{
size_t alloc;
size_t count;
struct tr_variant* vals;
} l;
} val = {};
private:
tr_variant& tgt_;
};
std::variant<std::monostate, bool, int64_t, double, StringHolder, Vector, Map> val_;
};
// --- Strings
@ -213,12 +208,6 @@ void tr_variantInitQuark(tr_variant* initme, tr_quark value);
void tr_variantInitRaw(tr_variant* initme, void const* value, size_t value_len);
void tr_variantInitStrView(tr_variant* initme, std::string_view val);
constexpr void tr_variantInit(tr_variant* initme, tr_variant::Type type)
{
initme->val = {};
initme->type = type;
}
bool tr_variantGetRaw(tr_variant const* variant, std::byte const** setme_raw, size_t* setme_len);
bool tr_variantGetRaw(tr_variant const* variant, uint8_t const** setme_raw, size_t* setme_len);
@ -226,31 +215,19 @@ bool tr_variantGetRaw(tr_variant const* variant, uint8_t const** setme_raw, size
bool tr_variantGetReal(tr_variant const* variant, double* value_setme);
constexpr void tr_variantInitReal(tr_variant* initme, double value)
{
tr_variantInit(initme, tr_variant::Type::Double);
initme->val.d = value;
}
void tr_variantInitReal(tr_variant* initme, double value);
// --- Booleans
bool tr_variantGetBool(tr_variant const* variant, bool* setme);
constexpr void tr_variantInitBool(tr_variant* initme, bool value)
{
tr_variantInit(initme, tr_variant::Type::Bool);
initme->val.b = value;
}
void tr_variantInitBool(tr_variant* initme, bool value);
// --- Ints
bool tr_variantGetInt(tr_variant const* var, int64_t* setme);
constexpr void tr_variantInitInt(tr_variant* initme, int64_t value)
{
tr_variantInit(initme, tr_variant::Type::Int);
initme->val.i = value;
}
void tr_variantInitInt(tr_variant* initme, int64_t value);
// --- Lists
@ -273,7 +250,15 @@ bool tr_variantListRemove(tr_variant* var, size_t pos);
[[nodiscard]] constexpr size_t tr_variantListSize(tr_variant const* const var)
{
return var != nullptr && var->holds_alternative<tr_variant::Vector>() ? var->val.l.count : 0;
if (var != nullptr)
{
if (auto const* const vec = var->get_if<tr_variant::Vector>(); vec != nullptr)
{
return std::size(*vec);
}
}
return {};
}
// --- Dictionaries