perf: recycle variant tree walking helpers (#2100)

* perf: recycle the helper classes that walk through variant trees
This commit is contained in:
Charles Kerr 2021-11-05 09:54:44 -05:00 committed by GitHub
parent be22677f10
commit bbdb488224
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 82 additions and 23 deletions

View File

@ -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)
{ {
tmp[i] = { tr_quark_get_string(children[i].key), i }; if (!tr_variantIsDict(&v))
{
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())