zeus

Unnamed repository; edit this file 'description' to name the repository.
git clone Unknown
Log | Files | Refs

tag_rename.cpp (3839B)


0 /*
1 * Create and maintain an index of tags
2 * that can be renamed while keeping
3 * references in tact
4 */
7 #include <algorithm>
8 #include <format>
9 #include <iostream>
10 #include <string>
11 #include <string_view>
12 #include <unordered_map>
13 #include <vector>
15 struct string_hash {
16 using hash_type = std::hash<std::string_view>;
17 using is_transparent = void;
19 std::size_t operator()(const char *str) const { return hash_type{}(str); }
20 std::size_t operator()(std::string_view str) const {
21 return hash_type{}(str);
22 }
23 std::size_t operator()(std::string const &str) const {
24 return hash_type{}(str);
25 }
26 };
28 class refs {
29 using string_pos_type = std::size_t;
30 using vector_pos_type = std::size_t;
32 std::vector<string_pos_type> m_poss;
33 std::unordered_map<std::string, std::vector<vector_pos_type>, string_hash,
34 std::equal_to<>>
35 m_map;
37 public:
38 auto poss(std::string_view name) const {
39 std::vector<std::size_t> res;
41 const auto itr = m_map.find(name);
42 if (itr == std::end(m_map)) {
43 return res;
44 }
46 const auto &refs = itr->second;
47 res.reserve(std::size(refs));
48 for (const auto idx : refs) {
49 res.emplace_back(m_poss[idx]);
50 }
51 return res;
52 }
54 void insert(std::string_view name, string_pos_type pos) {
55 auto itr = m_map.find(name);
56 if (itr == std::end(m_map)) {
57 itr = m_map.emplace(name, std::vector<std::size_t>()).first;
58 }
59 itr->second.emplace_back(std::size(m_poss));
60 m_poss.emplace_back(pos);
61 }
63 void reindex(std::string_view name, std::string_view new_name) {
64 const auto itr = m_map.find(name);
65 if (itr == std::end(m_map)) {
66 return;
67 }
69 std::size_t diff = std::size(new_name) - std::size(name);
71 std::size_t acc = 0;
72 const auto &idxs = itr->second;
73 auto crnt = std::begin(idxs);
74 for (std::size_t i = 0; i < std::size(m_poss); i++) {
75 m_poss[i] += acc;
76 if (crnt != std::end(idxs) && *crnt == i) {
77 acc += diff;
78 crnt++;
79 }
80 }
82 auto node = m_map.extract(itr);
83 node.key() = new_name;
84 m_map.insert(std::move(node));
85 }
86 };
88 class file {
89 std::string m_text;
90 refs m_refs;
92 public:
93 file(std::string_view input) {
94 for (int i = 0; i < std::size(input); i++) {
95 if (input[i] != '[') {
96 m_text += input[i];
97 continue;
98 }
100 std::string name;
101 while (++i < std::size(input) && input[i] != ']') {
102 name += input[i];
105 if (i == std::size(input)) {
106 m_text += name;
107 continue;
110 m_refs.insert(name, std::size(m_text));
111 m_text += name;
115 std::size_t rename(std::string_view tag, std::string_view new_tag) {
116 std::size_t prev = 0;
117 std::string new_text;
118 const auto &poss = m_refs.poss(tag);
119 for (const auto ref : poss) {
120 new_text += m_text.substr(prev, ref - prev);
121 new_text += new_tag;
122 prev = ref + std::size(tag);
124 new_text += m_text.substr(prev);
125 m_text = new_text;
127 m_refs.reindex(tag, new_tag);
128 return std::size(poss);
131 std::string_view text() const { return m_text; }
132 };
134 int main() {
135 const std::string_view input = R"(
136 this is a [test] that references some [topic]
137 I want to be able to change the name of the [topic]
138 and for it to propagate through the rest of the [test].
139 )";
141 const auto rename = [](file &f, std::string_view tag,
142 std::string_view new_tag) {
143 std::cout << std::format("\nRaplaced {} with {}: {}", tag, new_tag,
144 f.rename(tag, new_tag));
145 std::cout << f.text() << std::endl;
146 };
148 file f(input);
150 std::cout << f.text() << std::endl;
151 rename(f, "test", "example");
152 rename(f, "topic", "other_topic");
153 rename(f, "example", "test");
154 rename(f, "other_topic", "topic");