// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #ifndef vtkStringToken_h #define vtkStringToken_h /** * @class vtkStringToken * @brief Represent a string by its integer hash. * * This class does not inherit vtkObject; it is a lightweight * object for representing a string as a 32-bit integer token. * Tokens can be constructed at compile-time (via the ""_token * string-literal operator below) or run-time (via the constructor). * * Equality comparisons are simple integer tests, while * inequality operators attempt to locate the original source * strings and compare them alphanumerically to preserve * lexicographic ordering. * * This class can be used inside ordered and unordered * STL containers. */ #include #include "vtkCompiler.h" // for VTK_COMPILER_GCC #include "vtkSmartPointer.h" // for ivar //clang-format off #include #include VTK_NLOHMANN_JSON(json.hpp) //clang-format on #include // for `std::uint*_t` #include // for membership API // GCC 4.8.5 requires a space between the quotes and suffix operator. // Whereas, C++23 deprecates the space here though. #ifdef VTK_COMPILER_GCC #if VTK_COMPILER_GCC_VERSION <= 40805 #define VTK_STRING_TOKEN_SPACE_BEFORE_SUFFIX 1 #else #define VTK_STRING_TOKEN_SPACE_BEFORE_SUFFIX 0 #endif #else #define VTK_STRING_TOKEN_SPACE_BEFORE_SUFFIX 0 #endif VTK_ABI_NAMESPACE_BEGIN class VTKCOMMONCORE_EXPORT vtkStringToken { public: using Hash = std::uint32_t; /// Construct a token from a string literal. VTK_WRAPEXCLUDE vtkStringToken(const char* data = nullptr, std::size_t size = std::string::npos); /// Construct a token from a std::string. vtkStringToken(const std::string& data); /// Construct a token given its hash value. /// NOTE: This will NOT insert a string into the manager as other constructors do. inline constexpr vtkStringToken(Hash tokenId) noexcept : Id(tokenId) { } /// Return the token's ID (usually its hash but possibly not in the case of collisions). Hash GetId() const { return this->Id; } /// A Python-wrappable (but less strongly typed) alternative to GetId() unsigned int GetHash() const { return static_cast(this->Id); } /// Return the string corresponding to the token. const std::string& Data() const; /// Return whether the token is valid or not. /// /// Valid tokens are those whose hash is not equal to the hash of an empty string. bool IsValid() const; /// Return whether a string is available for the token's hash ID. bool HasData() const; /// Fast equality comparison (compares hashes, not strings). bool operator==(const vtkStringToken& other) const; /// Fast inequality comparison (compares hashes, not strings). bool operator!=(const vtkStringToken& other) const; /// Slow, but unsurprising string comparison (preserves lexical string ordering). bool operator<(const vtkStringToken& other) const; bool operator>(const vtkStringToken& other) const; bool operator<=(const vtkStringToken& other) const; bool operator>=(const vtkStringToken& other) const; /// Return the hash of a string /// This is used internally but also by the ""_token() literal operator inline static constexpr Hash StringHash(const char* data, std::size_t size) noexcept { return token_NAMESPACE::Token::stringHash(data, size); } /// Return the hash code used to indicate an invalid (empty) token. static Hash InvalidHash(); /// Methods to manage groups of tokens underneath a parent. /// /// Grouping tokens provides applications a way to create and discover /// dynamic enumerations or sets of strings. /// For example, you might tokenize `car` and add tokens for car parts /// such `body`, `motor`, and `wheels` as children of `car`. /// Another library might then add more children such as `windshield` /// and `hood`; the application can present all of these children by /// asking for all the children of `car`. ///@{ /// Add \a member as a child of this token. /// /// If true is returned, the \a member was added. /// If false is returned, the \a member was already part of the group /// or one of the tokens (this token or \a member) was invalid. bool AddChild(vtkStringToken member); /// Remove a \a member from this token's children. /// /// If this returns false, \a member was not a child of this token. bool RemoveChild(vtkStringToken member); /// Return all the children of this token. std::unordered_set Children(bool recursive = true); /// Return all the tokens that have children. static std::unordered_set AllGroups(); ///@} protected: Hash Id; }; /// Convert a JSON value into a string token. /// /// The value \a jj must be an integer or string. /// If it is a string, it will be hashed into a token. /// If it is an integer, it will be treated as a hash. inline void VTKCOMMONCORE_EXPORT from_json(const nlohmann::json& jj, vtkStringToken& tt) { if (jj.is_number_integer()) { tt = vtkStringToken(jj.get()); } else if (jj.is_string()) { tt = jj.get(); } else { throw std::runtime_error("String tokens must be JSON integers or strings."); } } /// Convert a string token into a JSON value. /// /// If \a tt has a valid string, \a jj will be set to the string. /// Otherwise, \a jj will be set to the integer hash held by \a tt. inline void VTKCOMMONCORE_EXPORT to_json(nlohmann::json& jj, const vtkStringToken& tt) { if (tt.HasData()) { jj = tt.Data(); } else { jj = tt.GetId(); } } VTK_ABI_NAMESPACE_END namespace vtk { namespace literals { VTK_ABI_NAMESPACE_BEGIN /// Construct the hash of a string literal, like so: /// /// ```c++ /// using namespace vtk::literals; /// vtkStringToken::Hash t = "test"_hash; /// bool ok = false; /// switch (t) /// { /// case "foo"_hash: ok = false; /// case "test"_hash: ok = true; /// } /// std::cout << t << "\n"; // Prints a hash-code. /// std::cout << ok << "\n"; // Prints a true value. /// ``` /// /// As the example above shows, it is possible to use /// hashed strings in switch statements since the hashing /// occurs at build time. This is more efficient than /// a sequence of if-conditionals performing string /// comparisons. #if VTK_STRING_TOKEN_SPACE_BEFORE_SUFFIX inline constexpr VTKCOMMONCORE_EXPORT vtkStringToken::Hash operator"" _hash( const char* data, std::size_t size) #else inline constexpr VTKCOMMONCORE_EXPORT vtkStringToken::Hash operator""_hash( const char* data, std::size_t size) #endif { return vtkStringToken::StringHash(data, size); } /// Construct a token from a string literal, like so: /// /// ```c++ /// using namespace vtk::literals; /// vtkStringToken t = "test"_token; /// std::cout << t.id() << "\n"; // Prints a hash-code. /// // std::cout << t.value() << "\n"; // Prints "test" if someone else constructed the token from a /// ctor; else throws exception. /// ``` #if VTK_STRING_TOKEN_SPACE_BEFORE_SUFFIX inline constexpr VTKCOMMONCORE_EXPORT vtkStringToken operator"" _token( const char* data, std::size_t size) #else inline constexpr VTKCOMMONCORE_EXPORT vtkStringToken operator""_token( const char* data, std::size_t size) #endif { return vtkStringToken(vtkStringToken::StringHash(data, size)); } VTK_ABI_NAMESPACE_END } // namespace literals } // namespace vtk VTK_ABI_NAMESPACE_BEGIN bool VTKCOMMONCORE_EXPORT operator==(const std::string& a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator!=(const std::string& a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator>(const std::string& a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator<(const std::string& a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator>=(const std::string& a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator<=(const std::string& a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator==(const vtkStringToken& a, const std::string& b); bool VTKCOMMONCORE_EXPORT operator!=(const vtkStringToken& a, const std::string& b); bool VTKCOMMONCORE_EXPORT operator>(const vtkStringToken& a, const std::string& b); bool VTKCOMMONCORE_EXPORT operator<(const vtkStringToken& a, const std::string& b); bool VTKCOMMONCORE_EXPORT operator>=(const vtkStringToken& a, const std::string& b); bool VTKCOMMONCORE_EXPORT operator<=(const vtkStringToken& a, const std::string& b); bool VTKCOMMONCORE_EXPORT operator==(const char* a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator!=(const char* a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator>(const char* a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator<(const char* a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator>=(const char* a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator<=(const char* a, const vtkStringToken& b); bool VTKCOMMONCORE_EXPORT operator==(const vtkStringToken& a, const char* b); bool VTKCOMMONCORE_EXPORT operator!=(const vtkStringToken& a, const char* b); bool VTKCOMMONCORE_EXPORT operator>(const vtkStringToken& a, const char* b); bool VTKCOMMONCORE_EXPORT operator<(const vtkStringToken& a, const char* b); bool VTKCOMMONCORE_EXPORT operator>=(const vtkStringToken& a, const char* b); bool VTKCOMMONCORE_EXPORT operator<=(const vtkStringToken& a, const char* b); VTK_ABI_NAMESPACE_END namespace std { /// vtkStringTokens provide a specialization of std::hash so they can be used in unordered /// containers. template <> struct VTKCOMMONCORE_EXPORT hash { std::size_t operator()(const vtkStringToken& t) const { return t.GetId(); } }; } // namespace std #undef VTK_STRING_TOKEN_SPACE_BEFORE_SUFFIX #endif // vtkStringToken_h