| /* |
| * 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. |
| */ |
| |
| // Interned String type, 100% interned on creation. Comparisons are always just |
| // a pointer comparison |
| |
| #ifndef wasm_support_istring_h |
| #define wasm_support_istring_h |
| |
| #include <set> |
| #include <string_view> |
| #include <unordered_set> |
| |
| #include <assert.h> |
| |
| #include "threads.h" |
| #include "utilities.h" |
| |
| namespace wasm { |
| |
| struct IString { |
| private: |
| static const char* interned(std::string_view s); |
| |
| public: |
| // Strings are stored in Pascal style: a size followed by the characters. We |
| // keep the internal pointer pointing to the data, so that data() is a no-op; |
| // computing the size, which is more rare, requires looking back and doing a |
| // load. |
| // |
| // The size is limited to 4 bytes, so the maximum string we support is 4GB. |
| // |
| // The alternative approach of using a string_view here, i.e., keeping the |
| // pointer and size in the IString, uses more more memory. That is, this |
| // optimization saves a lot of space, because while it adds 4 bytes to each |
| // interned string itself, we tend to have many views on each. |
| // |
| // We provide a View here, which is a simple interface. Users that need more |
| // convert to a std::string_view with .view() or a cast. |
| struct View { |
| const char* internal = nullptr; |
| const char* data() const { return internal; } |
| size_t size() const { |
| return internal ? *(const uint32_t*)(internal - 4) : 0; |
| } |
| char operator[](size_t x) const { return internal[x]; } |
| std::string_view view() const { |
| if (!internal) { |
| // No size to read. |
| return {}; |
| } |
| return {internal, size()}; |
| } |
| }; |
| const View str; |
| |
| std::string_view view() const { return str.view(); } |
| |
| IString() = default; |
| |
| IString(View v) : str(v) {} |
| |
| IString(std::string_view s) : str{interned(s)} {} |
| IString(const char* str) : str{interned(str)} {} |
| IString(const std::string& str) : str{interned(str)} {} |
| |
| IString(const IString& other) = default; |
| |
| IString& operator=(const IString& other) { |
| return *(new (this) IString(other)); |
| } |
| |
| bool operator==(const IString& other) const { |
| // Fast! No need to compare contents due to interning |
| return str.internal == other.str.internal; |
| } |
| bool operator!=(const IString& other) const { return !(*this == other); } |
| bool operator<(const IString& other) const { |
| if (str.internal == other.str.internal) { |
| return false; |
| } |
| return view() < other.view(); |
| } |
| bool operator<=(const IString& other) const { |
| return *this == other || *this < other; |
| } |
| bool operator>(const IString& other) const { return !(*this <= other); } |
| bool operator>=(const IString& other) const { return !(*this < other); } |
| |
| char operator[](int x) const { return str[x]; } |
| |
| explicit operator bool() const { return str.internal != nullptr; } |
| |
| // TODO: deprecate? |
| bool is() const { return bool(*this); } |
| bool isNull() const { return !bool(*this); } |
| |
| std::string toString() const { return {str.data(), str.size()}; } |
| |
| bool equals(std::string_view other) const { return str.view() == other; } |
| |
| bool startsWith(std::string_view prefix) const { |
| // TODO: Use C++20 `starts_with`. |
| return view().substr(0, prefix.size()) == prefix; |
| } |
| bool startsWith(IString other) const { return startsWith(other.view()); } |
| |
| // Disambiguate for string literals. |
| template<int N> bool startsWith(const char (&str)[N]) const { |
| return startsWith(std::string_view(str)); |
| } |
| |
| bool endsWith(std::string_view suffix) const { |
| // TODO: Use C++20 `ends_with`. |
| if (suffix.size() > str.size()) { |
| return false; |
| } |
| return view().substr(str.size() - suffix.size()) == suffix; |
| } |
| bool endsWith(IString other) const { return endsWith(other.view()); } |
| |
| // Disambiguate for string literals. |
| template<int N> bool endsWith(const char (&str)[N]) const { |
| return endsWith(std::string_view(str)); |
| } |
| |
| IString substr(size_t pos, size_t len = std::string_view::npos) const { |
| return IString(view().substr(pos, len)); |
| } |
| |
| size_t size() const { return str.size(); } |
| }; |
| |
| } // namespace wasm |
| |
| namespace std { |
| |
| template<> struct hash<wasm::IString> { |
| size_t operator()(const wasm::IString& str) const { |
| return std::hash<size_t>{}(uintptr_t(str.str.data())); |
| } |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& os, const wasm::IString& str) { |
| return os << str.view(); |
| } |
| |
| } // namespace std |
| |
| #endif // wasm_support_istring_h |