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:
parent
fbfbfac3ae
commit
43030132fc
3 changed files with 470 additions and 560 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue