perf: recycle variant tree walking helpers (#2100)
* perf: recycle the helper classes that walk through variant trees
This commit is contained in:
parent
be22677f10
commit
bbdb488224
|
@ -755,13 +755,9 @@ bool tr_variantDictRemove(tr_variant* dict, tr_quark const key)
|
||||||
class WalkNode
|
class WalkNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WalkNode(tr_variant const* v_in, bool sort_dicts)
|
explicit WalkNode(tr_variant const* v_in)
|
||||||
: v{ *v_in }
|
|
||||||
{
|
{
|
||||||
if (sort_dicts && tr_variantIsDict(v_in))
|
assign(v_in);
|
||||||
{
|
|
||||||
sortByKey();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tr_variant const* nextChild()
|
tr_variant const* nextChild()
|
||||||
|
@ -782,35 +778,50 @@ public:
|
||||||
|
|
||||||
bool is_visited = false;
|
bool is_visited = false;
|
||||||
|
|
||||||
// Shallow bitwise copy of the variant passed to the constructor
|
// shallow bitwise copy of the variant passed to the constructor
|
||||||
tr_variant const v = {};
|
tr_variant v = {};
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
void sortByKey()
|
friend class VariantWalker;
|
||||||
|
|
||||||
|
void assign(tr_variant const* v_in)
|
||||||
{
|
{
|
||||||
auto const n = v.val.l.count;
|
is_visited = false;
|
||||||
|
v = *v_in;
|
||||||
|
child_index = 0;
|
||||||
|
sorted.clear();
|
||||||
|
}
|
||||||
|
|
||||||
struct ByKey
|
struct ByKey
|
||||||
{
|
{
|
||||||
char const* key;
|
std::string_view key;
|
||||||
size_t idx;
|
size_t idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto const* children = v.val.l.vals;
|
void sort(std::vector<ByKey>& sortbuf)
|
||||||
auto tmp = std::vector<ByKey>(n);
|
{
|
||||||
for (size_t i = 0; i < n; ++i)
|
if (!tr_variantIsDict(&v))
|
||||||
{
|
{
|
||||||
tmp[i] = { tr_quark_get_string(children[i].key), i };
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(std::begin(tmp), std::end(tmp), [](ByKey const& a, ByKey const& b) { return strcmp(a.key, b.key) < 0; });
|
auto const n = v.val.l.count;
|
||||||
|
auto const* children = v.val.l.vals;
|
||||||
|
|
||||||
|
sortbuf.resize(n);
|
||||||
|
for (size_t i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
sortbuf[i] = { tr_quark_get_string(children[i].key), i };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(std::begin(sortbuf), std::end(sortbuf), [](ByKey const& a, ByKey const& b) { return a.key < b.key; });
|
||||||
|
|
||||||
// keep the sorted indices
|
// keep the sorted indices
|
||||||
|
|
||||||
sorted.resize(n);
|
sorted.resize(n);
|
||||||
for (size_t i = 0; i < n; ++i)
|
for (size_t i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
sorted[i] = tmp[i].idx;
|
sorted[i] = sortbuf[i].idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,6 +833,54 @@ private:
|
||||||
std::vector<size_t> sorted;
|
std::vector<size_t> sorted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VariantWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void emplace(tr_variant const* v_in, bool sort_dicts)
|
||||||
|
{
|
||||||
|
if (size == std::size(stack))
|
||||||
|
{
|
||||||
|
stack.emplace_back(v_in);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stack[size].assign(v_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
++size;
|
||||||
|
|
||||||
|
if (sort_dicts)
|
||||||
|
{
|
||||||
|
top().sort(sortbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
TR_ASSERT(size > 0);
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WalkNode& top()
|
||||||
|
{
|
||||||
|
TR_ASSERT(size > 0);
|
||||||
|
return stack[size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t size = 0;
|
||||||
|
std::vector<WalkNode> stack;
|
||||||
|
std::vector<WalkNode::ByKey> sortbuf;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function's previous recursive implementation was
|
* This function's previous recursive implementation was
|
||||||
* easier to read, but was vulnerable to a smash-stacking
|
* easier to read, but was vulnerable to a smash-stacking
|
||||||
|
@ -829,7 +888,7 @@ private:
|
||||||
*/
|
*/
|
||||||
void tr_variantWalk(tr_variant const* v_in, struct VariantWalkFuncs const* walkFuncs, void* user_data, bool sort_dicts)
|
void tr_variantWalk(tr_variant const* v_in, struct VariantWalkFuncs const* walkFuncs, void* user_data, bool sort_dicts)
|
||||||
{
|
{
|
||||||
auto stack = std::stack<WalkNode>{};
|
auto stack = VariantWalker{};
|
||||||
stack.emplace(v_in, sort_dicts);
|
stack.emplace(v_in, sort_dicts);
|
||||||
|
|
||||||
while (!stack.empty())
|
while (!stack.empty())
|
||||||
|
|
Loading…
Reference in New Issue