1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-01-30 10:52:00 +00:00

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 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 namespace parse_helpers
{ {
/* arbitrary value... this is much deeper than our code goes */ /* 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(); data->stack.pop_back();
if (depth < MaxDepth) if (depth < MaxDepth)
{ {
data->preallocGuess[depth] = v->val.l.count; data->preallocGuess[depth] = variant_size(*v);
} }
} }
else if (state->type == JSONSL_T_SPECIAL) 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_dict = v.holds_alternative<tr_variant::Map>();
auto const is_list = v.holds_alternative<tr_variant::Vector>(); 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 }); 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); jsonPushParent(data, var);
data->out.push_back('{'); data->out.push_back('{');
if (var.val.l.count != 0U) if (variant_size(var) != 0U)
{ {
jsonIndent(data); jsonIndent(data);
} }
@ -671,7 +686,7 @@ void jsonListBeginFunc(tr_variant const& var, void* vdata)
jsonPushParent(data, var); jsonPushParent(data, var);
data->out.push_back('['); data->out.push_back('[');
if (var.val.l.count != 0U) if (variant_size(var) != 0U)
{ {
jsonIndent(data); 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>()); return var != nullptr && (var->holds_alternative<tr_variant::Vector>() || var->holds_alternative<tr_variant::Map>());
} }
// --- constexpr int variant_index(tr_variant const* const var)
constexpr std::optional<size_t> dict_index_of(tr_variant const* const var, tr_quark const key)
{ {
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 {}; 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) 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) 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) 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; 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) 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 true;
}
return false; default:
return false;
}
} }
bool tr_variantGetRaw(tr_variant const* v, std::byte const** setme_raw, size_t* setme_len) 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) bool tr_variantGetBool(tr_variant const* const var, bool* setme)
{ {
if (var == nullptr) switch (variant_index(var))
{ {
return false; case tr_variant::BoolIndex:
} *setme = *var->get_if<tr_variant::BoolIndex>();
if (var->holds_alternative<bool>())
{
*setme = var->val.b;
return true; return true;
}
if (var->holds_alternative<int64_t>() && (var->val.i == 0 || var->val.i == 1)) case tr_variant::IntIndex:
{ if (auto const val = *var->get_if<tr_variant::IntIndex>(); val == 0 || val == 1)
*setme = var->val.i != 0; {
return true; *setme = val != 0;
} return true;
}
break;
if (auto sv = std::string_view{}; tr_variantGetStrView(var, &sv)) case tr_variant::StringIndex:
{ if (auto const val = *var->get_if<tr_variant::StringIndex>(); val == "true"sv)
if (sv == "true"sv)
{ {
*setme = true; *setme = true;
return true; return true;
} }
else if (val == "false"sv)
if (sv == "false"sv)
{ {
*setme = false; *setme = false;
return true; return true;
} }
break;
default:
break;
} }
return false; 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) bool tr_variantGetReal(tr_variant const* const var, double* setme)
{ {
if (var == nullptr) switch (variant_index(var))
{ {
return false; case tr_variant::DoubleIndex:
} *setme = *var->get_if<tr_variant::DoubleIndex>();
if (var->holds_alternative<double>())
{
*setme = var->val.d;
return true; return true;
}
if (var->holds_alternative<int64_t>()) case tr_variant::IntIndex:
{ *setme = static_cast<double>(*var->get_if<tr_variant::IntIndex>());
*setme = static_cast<double>(var->val.i);
return true; return true;
}
if (var->holds_alternative<std::string_view>()) case tr_variant::StringIndex:
{ if (auto const val = tr_num_parse<double>(*var->get_if<tr_variant::StringIndex>()); val)
if (auto val = tr_num_parse<double>(var->val.s.get()); val)
{ {
*setme = *val; *setme = *val;
return true; return true;
} }
} [[fallthrough]];
return false; default:
return false;
}
} }
bool tr_variantDictFindInt(tr_variant* const var, tr_quark key, int64_t* setme) 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) 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) 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) 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) void tr_variantInitStrView(tr_variant* initme, std::string_view val)
{ {
tr_variantInit(initme, tr_variant::Type::String); *initme = tr_variant::unmanaged_string(val);
initme->val.s.set_shallow(val);
} }
void tr_variantInitRaw(tr_variant* initme, void const* value, size_t value_len) 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) void tr_variantInitStr(tr_variant* initme, std::string_view value)
{ {
tr_variantInit(initme, tr_variant::Type::String); *initme = value;
initme->val.s.set(value);
} }
void tr_variantInitList(tr_variant* initme, size_t reserve_count) void tr_variantInitList(tr_variant* initme, size_t reserve_count)
{ {
tr_variantInit(initme, tr_variant::Type::Vector); auto vec = tr_variant::Vector{};
tr_variantListReserve(initme, reserve_count); vec.reserve(reserve_count);
*initme = std::move(vec);
} }
void tr_variantListReserve(tr_variant* const var, size_t count) 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 != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Vector>()); 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); *initme = tr_variant::Map{};
tr_variantDictReserve(initme, reserve_count);
} }
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) 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 != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Vector>()); TR_ASSERT(var->holds_alternative<tr_variant::Vector>());
auto* const child = containerReserve(var, 1); if (auto* const vec = var != nullptr ? var->get_if<tr_variant::VectorIndex>() : nullptr; vec != nullptr)
++var->val.l.count; {
*child = tr_variant{}; return &vec->emplace_back();
}
return child; return nullptr;
} }
tr_variant* tr_variantListAddInt(tr_variant* const var, int64_t value) tr_variant* tr_variantListAddInt(tr_variant* const var, int64_t value)
{ {
auto* const child = tr_variantListAdd(var); auto* const child = tr_variantListAdd(var);
tr_variantInitInt(child, value); *child = value;
return child; return child;
} }
tr_variant* tr_variantListAddReal(tr_variant* const var, double value) tr_variant* tr_variantListAddReal(tr_variant* const var, double value)
{ {
auto* const child = tr_variantListAdd(var); auto* const child = tr_variantListAdd(var);
tr_variantInitReal(child, value); *child = value;
return child; return child;
} }
tr_variant* tr_variantListAddBool(tr_variant* const var, bool value) tr_variant* tr_variantListAddBool(tr_variant* const var, bool value)
{ {
auto* const child = tr_variantListAdd(var); auto* const child = tr_variantListAdd(var);
tr_variantInitBool(child, value); *child = value;
return child; return child;
} }
tr_variant* tr_variantListAddStr(tr_variant* const var, std::string_view value) tr_variant* tr_variantListAddStr(tr_variant* const var, std::string_view value)
{ {
auto* const child = tr_variantListAdd(var); auto* const child = tr_variantListAdd(var);
tr_variantInitStr(child, value); *child = value;
return child; return child;
} }
tr_variant* tr_variantListAddStrView(tr_variant* const var, std::string_view value) tr_variant* tr_variantListAddStrView(tr_variant* const var, std::string_view value)
{ {
auto* const child = tr_variantListAdd(var); auto* const child = tr_variantListAdd(var);
tr_variantInitStrView(child, value); *child = tr_variant::unmanaged_string(value);
return child; return child;
} }
tr_variant* tr_variantListAddQuark(tr_variant* const var, tr_quark value) tr_variant* tr_variantListAddQuark(tr_variant* const var, tr_quark value)
{ {
auto* const child = tr_variantListAdd(var); return tr_variantListAddStrView(var, tr_quark_get_string_view(value));
tr_variantInitQuark(child, value);
return child;
} }
tr_variant* tr_variantListAddRaw(tr_variant* const var, void const* value, size_t value_len) tr_variant* tr_variantListAddRaw(tr_variant* const var, void const* value, size_t value_len)
{ {
auto* const child = tr_variantListAdd(var); 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; return child;
} }
tr_variant* tr_variantListAddList(tr_variant* const var, size_t reserve_count) tr_variant* tr_variantListAddList(tr_variant* const var, size_t reserve_count)
{ {
auto* const child = tr_variantListAdd(var); 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; return child;
} }
@ -457,12 +482,12 @@ tr_variant* tr_variantDictAdd(tr_variant* const var, tr_quark key)
TR_ASSERT(var != nullptr); TR_ASSERT(var != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Map>()); TR_ASSERT(var->holds_alternative<tr_variant::Map>());
auto* const child = containerReserve(var, 1); if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr; map != nullptr)
++var->val.l.count; {
*child = tr_variant{}; return &(*map)[key];
child->key = key; }
return child; return {};
} }
tr_variant* tr_variantDictAddInt(tr_variant* const var, tr_quark key, int64_t val) 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) bool tr_variantDictRemove(tr_variant* const var, tr_quark key)
{ {
auto const idx = dict_index_of(var, key); if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr; map != nullptr)
return idx.has_value() && variant_remove_child(var, *idx); {
return map->erase(key) != 0U;
}
return false;
} }
// --- BENC WALKING // --- 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 (auto const* const map = var_->get_if<tr_variant::MapIndex>(); map != nullptr)
if (!sorted.empty())
{ {
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 [[nodiscard]] constexpr auto is_visited() const noexcept
@ -608,18 +648,19 @@ protected:
template<typename Container> template<typename Container>
void sort(Container& sortbuf) 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; return;
} }
auto const n = var_->val.l.count; auto idx = size_t{};
auto const* children = var_->val.l.vals; auto const n = std::size(*map);
sortbuf.resize(n); 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; }); 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. // 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. // Bencoded dicts must be sorted, so this is useful when writing benc.
small::vector<size_t, 128U> sorted; 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 class VariantWalker
@ -700,15 +753,17 @@ void tr_variant_serde::walk(tr_variant const& top, WalkFuncs const& walk_funcs,
} }
else else
{ {
v = node.next_child(); auto [key, child] = node.next_child();
v = child;
if (v != nullptr) if (v != nullptr)
{ {
if (node.current()->holds_alternative<tr_variant::Map>()) 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{}; auto tmp = tr_variant{};
tr_variantInitQuark(&tmp, v->key); tr_variantInitQuark(&tmp, key);
walk_funcs.string_func(tmp, keystr, user_data); 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.list_begin_func(*v, user_data);
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;
} }
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 != nullptr);
TR_ASSERT(var->holds_alternative<tr_variant::Map>()); TR_ASSERT(var->holds_alternative<tr_variant::Map>());
bool success = false; if (auto* const map = var != nullptr ? var->get_if<tr_variant::MapIndex>() : nullptr;
map != nullptr && pos < std::size(*map))
if (var != nullptr && var->holds_alternative<tr_variant::Map>())
{ {
if (auto& container = var->val.l; pos < container.count) auto iter = std::begin(*map);
{ std::advance(iter, pos);
*key = container.vals[pos].key; *key = iter->first;
*setme_value = container.vals + pos; *setme_value = &iter->second;
success = true; 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) void tr_variantMergeDicts(tr_variant* const tgt, tr_variant const* const src)
{ {
using namespace merge_helpers;
TR_ASSERT(tgt != nullptr); TR_ASSERT(tgt != nullptr);
TR_ASSERT(tgt->holds_alternative<tr_variant::Map>());
TR_ASSERT(src != nullptr); TR_ASSERT(src != nullptr);
TR_ASSERT(src->holds_alternative<tr_variant::Map>()); tgt->merge(*src);
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)));
}
}
}
} }
// --- // ---

View file

@ -5,14 +5,16 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm> // std::move()
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // int64_t #include <cstdint> // int64_t
#include <map> #include <map>
#include <optional> #include <optional>
#include <string> #include <string>
#include <string_view> #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 <vector>
#include "libtransmission/quark.h" #include "libtransmission/quark.h"
@ -20,188 +22,181 @@
struct tr_error; struct tr_error;
/** /**
* A variant that holds typical benc/json types: bool, int, double, string, * A variant that holds typical benc/json types: bool, int,
* vectors of variants, and maps of string-to-variant. * double, string, vectors of variants, and maps of variants.
* Useful when serializing / deserializing benc/json data. * Useful when serializing / deserializing benc/json data.
* *
* @see tr_variant_serde * @see tr_variant_serde
*/ */
struct tr_variant 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: public:
enum class Type enum Type
{ {
None, NoneIndex = 0,
Bool, BoolIndex = 1,
Int, IntIndex = 2,
Double, DoubleIndex = 3,
String, StringIndex = 4,
Vector, VectorIndex = 5,
Map MapIndex = 6
}; };
using Vector = std::vector<tr_variant>; using Vector = std::vector<tr_variant>;
using Map = std::map<tr_quark, 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 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(); template<typename Val>
tr_variant& operator=(Val value)
tr_variant& operator=(tr_variant const&) = delete;
tr_variant& operator=(tr_variant&& that) noexcept
{ {
std::swap(key, that.key); val_ = std::move(value);
std::swap(type, that.type);
std::swap(val, that.val);
return *this; 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 [[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> template<typename Val>
[[nodiscard]] constexpr bool holds_alternative() const noexcept [[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>) if constexpr (std::is_same_v<Val, std::string_view>)
{ {
return type == Type::String; return std::holds_alternative<StringHolder>(val_);
} }
else
if constexpr (std::is_same_v<Val, Vector>)
{ {
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() void clear()
{ {
*this = tr_variant{}; val_.emplace<std::monostate>();
} }
Type type = Type::None; void merge(tr_variant const& that)
tr_quark key = TR_KEY_NONE;
union
{ {
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 private:
{ tr_variant& tgt_;
size_t alloc; };
size_t count;
struct tr_variant* vals; std::variant<std::monostate, bool, int64_t, double, StringHolder, Vector, Map> val_;
} l;
} val = {};
}; };
// --- Strings // --- 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_variantInitRaw(tr_variant* initme, void const* value, size_t value_len);
void tr_variantInitStrView(tr_variant* initme, std::string_view val); 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, 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); 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); bool tr_variantGetReal(tr_variant const* variant, double* value_setme);
constexpr void tr_variantInitReal(tr_variant* initme, double value) void tr_variantInitReal(tr_variant* initme, double value);
{
tr_variantInit(initme, tr_variant::Type::Double);
initme->val.d = value;
}
// --- Booleans // --- Booleans
bool tr_variantGetBool(tr_variant const* variant, bool* setme); bool tr_variantGetBool(tr_variant const* variant, bool* setme);
constexpr void tr_variantInitBool(tr_variant* initme, bool value) void tr_variantInitBool(tr_variant* initme, bool value);
{
tr_variantInit(initme, tr_variant::Type::Bool);
initme->val.b = value;
}
// --- Ints // --- Ints
bool tr_variantGetInt(tr_variant const* var, int64_t* setme); bool tr_variantGetInt(tr_variant const* var, int64_t* setme);
constexpr void tr_variantInitInt(tr_variant* initme, int64_t value) void tr_variantInitInt(tr_variant* initme, int64_t value);
{
tr_variantInit(initme, tr_variant::Type::Int);
initme->val.i = value;
}
// --- Lists // --- 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) [[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 // --- Dictionaries