blob: a567eb82f1217120fc6229e0767aaa04eb933e57 [file] [edit]
/*
* 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