/* * 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{}(mut.str); } }; struct MutStringViewEqual { bool operator()(const MutStringView& a, const MutStringView& b) const { return a.str == b.str; } }; using StringSet = std::unordered_set; // 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> 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 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