1414 * limitations under the License.
1515 */
1616
17+ #include < limits>
18+ #include < mutex>
19+
1720#include " istring.h"
1821#include " mixed_arena.h"
1922
2023namespace wasm {
2124
22- std::string_view IString::interned (std::string_view s, bool reuse) {
23- // We need a set of string_views that can be modified in-place to minimize
24- // the number of lookups we do. Since set elements cannot normally be
25- // modified, wrap the string_views in a container that provides mutability
26- // even through a const reference.
27- struct MutStringView {
28- mutable std::string_view str;
29- MutStringView (std::string_view str) : str(str) {}
30- };
31- struct MutStringViewHash {
32- size_t operator ()(const MutStringView& mut) const {
33- return std::hash<std::string_view>{}(mut.str );
25+ const char * IString::interned (std::string_view s) {
26+ // A set of interned Views, i.e., that contains our pascal-style strings. We
27+ // need to query this using a std::string_view, as that is what we receive as
28+ // input (turning it into pascal-style storage would add overhead). To do so,
29+ // use overloading in the hash and equality functions (which works thanks to
30+ // `is_transparent`).
31+ struct InternedHash {
32+ using is_transparent = void ;
33+ size_t operator ()(View v) const {
34+ return std::hash<std::string_view>{}(v.view ());
35+ }
36+ size_t operator ()(std::string_view sv) const {
37+ return std::hash<std::string_view>{}(sv);
3438 }
3539 };
36- struct MutStringViewEqual {
37- bool operator ()(const MutStringView& a, const MutStringView& b) const {
38- return a.str == b.str ;
40+ struct InternedEqual {
41+ using is_transparent = void ;
42+ bool operator ()(View a, View b) const { return a.view () == b.view (); }
43+ bool operator ()(std::string_view a, View b) const { return a == b.view (); }
44+ bool operator ()(View a, std::string_view b) const { return a.view () == b; }
45+ bool operator ()(std::string_view a, std::string_view b) const {
46+ return a == b;
3947 }
4048 };
41- using StringSet =
42- std::unordered_set<MutStringView, MutStringViewHash, MutStringViewEqual>;
49+ using StringSet = std::unordered_set<View, InternedHash, InternedEqual>;
4350
4451 // The authoritative global set of interned string views.
4552 static StringSet globalStrings;
@@ -54,34 +61,37 @@ std::string_view IString::interned(std::string_view s, bool reuse) {
5461 // A thread-local cache of strings to reduce contention.
5562 thread_local static StringSet localStrings;
5663
57- auto [localIt, localInserted] = localStrings.insert (s);
58- if (!localInserted) {
64+ if (auto it = localStrings.find (s); it != localStrings.end ()) {
5965 // We already had a local copy of this string.
60- return localIt-> str ;
66+ return it-> internal ;
6167 }
6268
6369 // No copy yet in the local cache. Check the global cache.
6470 std::unique_lock<std::mutex> lock (mutex);
65- auto [globalIt, globalInserted] = globalStrings.insert (s);
66- if (!globalInserted) {
71+ if (auto it = globalStrings.find (s); it != globalStrings.end ()) {
6772 // We already had a global copy of this string. Cache it locally.
68- localIt-> str = globalIt-> str ;
69- return localIt-> str ;
73+ localStrings. insert (*it) ;
74+ return it-> internal ;
7075 }
7176
72- if (!reuse) {
73- // We have a new string, but it doesn't have a stable address. Create a copy
74- // of the data at a stable address we can use. Make sure it is null
75- // terminated so legacy uses that get a C string still work.
76- char * data = (char *)arena.allocSpace (s.size () + 1 , 1 );
77- std::copy (s.begin (), s.end (), data);
78- data[s.size ()] = ' \0 ' ;
79- s = std::string_view (data, s.size ());
80- }
77+ // We have a new string. Create a copy of the data at a stable address with a
78+ // header we can use. Make sure it is null terminated so legacy uses that get
79+ // a C string still work.
80+ size_t size = s.size ();
81+ // The string's size must fit in 32 bits.
82+ assert (size <= std::numeric_limits<uint32_t >::max ());
83+ char * buffer =
84+ (char *)arena.allocSpace (sizeof (uint32_t ) + size + 1 , alignof (uint32_t ));
85+ *(uint32_t *)(buffer) = size;
86+ char * data = buffer + sizeof (uint32_t );
87+ std::copy (s.begin (), s.end (), data);
88+ data[size] = ' \0 ' ;
8189
8290 // Intern our new string.
83- localIt->str = globalIt->str = s;
84- return s;
91+ View v{data};
92+ globalStrings.insert (v);
93+ localStrings.insert (v);
94+ return data;
8595}
8696
8797} // namespace wasm
0 commit comments