| /* |
| * Copyright 2022 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "istring.h" |
| |
| namespace wasm { |
| |
| std::string_view IString::interned(std::string_view s, bool reuse) { |
| // We need a set of string_views that can be modified in-place to minimize |
| // the number of lookups we do. Since set elements cannot normally be |
| // modified, wrap the string_views in a container that provides mutability |
| // even through a const reference. |
| struct MutStringView { |
| mutable std::string_view str; |
| MutStringView(std::string_view str) : str(str) {} |
| }; |
| struct MutStringViewHash { |
| size_t operator()(const MutStringView& mut) const { |
| return std::hash<std::string_view>{}(mut.str); |
| } |
| }; |
| struct MutStringViewEqual { |
| bool operator()(const MutStringView& a, const MutStringView& b) const { |
| return a.str == b.str; |
| } |
| }; |
| using StringSet = |
| std::unordered_set<MutStringView, MutStringViewHash, MutStringViewEqual>; |
| |
| // The authoritative global set of interned string views. |
| static StringSet globalStrings; |
| |
| // The global backing store for interned strings that do not otherwise have |
| // stable addresses. |
| static std::vector<std::vector<char>> allocated; |
| |
| // Guards access to `globalStrings` and `allocated`. |
| static std::mutex mutex; |
| |
| // A thread-local cache of strings to reduce contention. |
| thread_local static StringSet localStrings; |
| |
| auto [localIt, localInserted] = localStrings.insert(s); |
| if (!localInserted) { |
| // We already had a local copy of this string. |
| return localIt->str; |
| } |
| |
| // No copy yet in the local cache. Check the global cache. |
| std::unique_lock<std::mutex> lock(mutex); |
| auto [globalIt, globalInserted] = globalStrings.insert(s); |
| if (!globalInserted) { |
| // We already had a global copy of this string. Cache it locally. |
| localIt->str = globalIt->str; |
| return localIt->str; |
| } |
| |
| if (!reuse) { |
| // We have a new string, but it doesn't have a stable address. Create a copy |
| // of the data at a stable address we can use. Make sure it is null |
| // terminated so legacy uses that get a C string still work. |
| allocated.emplace_back(); |
| auto& data = allocated.back(); |
| data.reserve(s.size() + 1); |
| data.insert(data.end(), s.begin(), s.end()); |
| data.push_back('\0'); |
| s = std::string_view(allocated.back().data(), s.size()); |
| } |
| |
| // Intern our new string. |
| localIt->str = globalIt->str = s; |
| return s; |
| } |
| |
| } // namespace wasm |