openMSX
StringReplacer.hh
Go to the documentation of this file.
1#ifndef STRING_REPLACER_HH
2#define STRING_REPLACER_HH
3
4// First create a 'replacer' by passing a list of replacement-pairs
5// (creation can fully be evaluated at-compile-time):
6// static constexpr auto replacer = StringReplacer::create(
7// "original", "replacement",
8// ...
9// "some string", "another string");
10// Then use it to replace strings:
11// assert(replacer("original") == "replacement"); // enumerate strings are replaced
12// assert(replacer("not-in-list") == "not-in-list"); // not-enumerated strings are left unchanged
13//
14// So this is equivalent to
15// string_view input = ...
16// if (input == "original" ) input = "replacement";
17// ...
18// else if (input == "some string) input = "another string";
19// else // leave 'input' unchanged
20// Though (for a long list of replacement-pairs) this utility may run faster
21// than a manual chain of comparisons. This is because internally uses
22// perfect-hashing to speed up the string comparisons.
23//
24// Note: the return type of StringReplacer::create() is intentionally
25// unspecified (and it can indeed be different for different inputs).
26
27#include "MinimalPerfectHash.hh"
28
29#include "narrow.hh"
30
31#include <array>
32#include <cassert>
33#include <string_view>
34#include <tuple>
35#include <utility>
36
37namespace StringReplacer {
38namespace detail {
39
40[[nodiscard]] constexpr unsigned fnvHash(std::string_view s)
41{
42 constexpr unsigned PRIME = 0x811C9DC5;
43 auto hash = narrow<unsigned>(s.size());
44 for (size_t i = 0; i < s.size(); ++i) {
45 hash *= PRIME;
46 hash ^= s[i];
47 }
48 return hash;
49}
50
51
52[[nodiscard]] constexpr auto create_simple_replacer()
53{
54 return [](std::string_view s) { return s; };
55}
56
57[[nodiscard]] constexpr auto create_simple_replacer(
58 std::string_view from1, std::string_view to1)
59{
60 return [=](std::string_view s) {
61 if (s == from1) return to1;
62 return s;
63 };
64}
65
66[[nodiscard]] constexpr auto create_simple_replacer(
67 std::string_view from1, std::string_view to1,
68 std::string_view from2, std::string_view to2)
69{
70 return [=](std::string_view s) {
71 if (s == from1) return to1;
72 if (s == from2) return to2;
73 return s;
74 };
75}
76
77[[nodiscard]] constexpr auto create_simple_replacer(
78 std::string_view from1, std::string_view to1,
79 std::string_view from2, std::string_view to2,
80 std::string_view from3, std::string_view to3)
81{
82 return [=](std::string_view s) {
83 if (s == from1) return to1;
84 if (s == from2) return to2;
85 if (s == from3) return to3;
86 return s;
87 };
88}
89
90[[nodiscard]] constexpr auto create_simple_replacer(
91 std::string_view from1, std::string_view to1,
92 std::string_view from2, std::string_view to2,
93 std::string_view from3, std::string_view to3,
94 std::string_view from4, std::string_view to4)
95{
96 return [=](std::string_view s) {
97 if (s == from1) return to1;
98 if (s == from2) return to2;
99 if (s == from3) return to3;
100 if (s == from4) return to4;
101 return s;
102 };
103}
104
105struct FromTo {
106 std::string_view from;
107 std::string_view to;
108};
109
110template<size_t N, typename PMH>
112 std::array<FromTo, N> map;
113 PMH pmh;
114
115 constexpr std::string_view operator()(std::string_view input) const
116 {
117 auto idx = pmh.lookupIndex(input);
118 assert(idx < map.size());
119
120 if (map[idx].from == input) {
121 return map[idx].to;
122 }
123 return input;
124 }
125};
126
127template<typename Tuple, size_t... Is>
128[[nodiscard]] constexpr auto create_pmh_replacer(Tuple&& tuple, std::index_sequence<Is...>)
129{
130 constexpr size_t N = sizeof...(Is);
131 std::array<FromTo, N> arr{FromTo{std::get<2 * Is>(tuple), std::get<2 * Is + 1>(tuple)}...};
132
133 auto hash = [](std::string_view s) { return fnvHash(s); };
134 auto getKey = [&](size_t i) { return arr[i].from; };
135 auto pmh = PerfectMinimalHash::create<N>(hash, getKey);
136
137 return PmhReplacer<N, decltype(pmh)>{arr, pmh};
138}
139
140} // namespace detail
141
142template<typename ...Args>
143[[nodiscard]] constexpr auto create(Args ...args)
144{
145 constexpr size_t N2 = sizeof...(args);
146 static_assert((N2 & 1) == 0, "Must pass an even number of strings");
147 if constexpr (N2 <= 2 * 4) {
148 return detail::create_simple_replacer(args...);
149 } else {
150 return detail::create_pmh_replacer(std::tuple(args...), std::make_index_sequence<N2 / 2>());
151 }
152}
153
154} // namespace StringReplacer
155
156#endif
constexpr auto create_simple_replacer()
constexpr unsigned fnvHash(std::string_view s)
constexpr auto create_pmh_replacer(Tuple &&tuple, std::index_sequence< Is... >)
constexpr auto create(Args ...args)
Definition join.hh:10
constexpr std::string_view operator()(std::string_view input) const