26 std::unordered_map<int, const state::ParamGroup*> by_id;
27 std::unordered_set<int> duplicate_ids;
28 by_id.reserve(groups.size());
29 for (
const auto& group : groups) {
30 if (group.id <= 0 || group.name.empty())
32 if (!by_id.emplace(group.id, &group).second)
33 duplicate_ids.insert(group.id);
36 enum class Mark { visiting, valid, invalid };
37 std::unordered_map<int, Mark> marks;
38 std::unordered_map<int, std::string> paths;
39 marks.reserve(by_id.size());
40 paths.reserve(by_id.size());
42 const auto validate = [&](
const auto& self,
int id) ->
bool {
43 if (duplicate_ids.contains(
id))
45 if (
const auto mark = marks.find(
id); mark != marks.end()) {
46 if (mark->second == Mark::valid)
50 const auto found = by_id.find(
id);
51 if (found == by_id.end())
53 marks[id] = Mark::visiting;
54 const auto& group = *found->second;
56 if (group.parent_id == 0) {
59 const auto parent_mark = marks.find(group.parent_id);
60 if ((parent_mark != marks.end() && parent_mark->second == Mark::visiting) ||
61 !self(self, group.parent_id)) {
62 marks[id] = Mark::invalid;
65 path = paths[group.parent_id] +
"/" + group.name;
67 paths[id] = std::move(path);
68 marks[id] = Mark::valid;
72 std::unordered_set<int> emitted;
73 entries_.reserve(groups.size());
74 emitted.reserve(groups.size());
77 bool made_progress =
true;
78 while (made_progress) {
79 made_progress =
false;
80 for (
const auto& group : groups) {
81 if (!validate(validate, group.id))
83 if (emitted.contains(group.id))
85 if (group.parent_id != 0 && !emitted.contains(group.parent_id))
87 entries_.push_back({group.id, group.parent_id, group.name, paths[group.id]});
88 emitted.insert(group.id);