/* SPDX-FileCopyrightText: 2026 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include #ifdef WITH_WEB /* The Web data target does not compile OpenImageIO. Keep the UString API * available for DNA/node data while using a deterministic local representation. */ #else #include #endif #include "BLI_fixed_string.hh" #include "BLI_hash.hh" #include "BLI_string_ref.hh" namespace blender { /** * This is a thin wrapper around OpenImageIO's ustring class. Additionally it also provides * conversions to our StringRef types. * * See the OpenImageIO documentation for more details: * https://openimageio.readthedocs.io/en/stable/imageioapi.html#efficient-unique-strings-ustring */ class UString { private: /** * Using a member instead of inheritance because it simplifies avoiding various ambiguities with * operator overloads (especially equality comparison between UString, StringRef, std::string, * std::string_view, OpenImageIO::string_view, etc.). */ #ifdef WITH_WEB std::string ustr_; #else OIIO::ustring ustr_; #endif public: UString() = default; #ifdef WITH_WEB explicit UString(const char *str) : ustr_(str ? str : "") {} explicit UString(const StringRef str) : ustr_(std::string_view(str)) {} #else explicit UString(const StringRef str) : ustr_(std::string_view(str)) {} #endif /** A constructor that is meant to generate as little code as possible at the call site. */ static UString from_ptr_noinline(const char *str); /** * Access the underlying string as a #StringRefNull. * * Note: This is not an implicit conversion to work around ambiguous function calls. */ StringRefNull ref() const { #ifdef WITH_WEB return StringRefNull(ustr_.c_str(), ustr_.length()); #else return StringRefNull(ustr_.c_str(), ustr_.length()); #endif } const std::string &string() const { #ifdef WITH_WEB return ustr_; #else return ustr_.string(); #endif } const char *c_str() const { return ustr_.c_str(); } friend bool operator==(const UString &a, const UString &b) { return a.ustr_ == b.ustr_; } friend bool operator==(const UString &a, const StringRef b) { return a.ref() == b; } uint64_t hash() const { #ifdef WITH_WEB /* FNV-1a is stable across native/Web builds and does not require OIIO. */ uint64_t result = UINT64_C(14695981039346656037); for (const unsigned char byte : ustr_) { result ^= byte; result *= UINT64_C(1099511628211); } return result; #else return ustr_.hash(); #endif } int64_t size() const { return int64_t(ustr_.size()); } bool is_empty() const { return ustr_.empty(); } char operator[](const int64_t i) const { /* Accessing null char at end is allowed too. */ BLI_assert(i >= 0 && i <= this->size()); return ustr_[i]; } }; /** * Define DefaultHash for UString keys so that it uses the cached hash on ustrings but also * supports hashing arbitrary (non-unique) strings in the same way. * * Note: The string hashes produced here are different from e.g. DefaultHash. That is * fine though. The only requirement is that all hashes defined in this template specialization are * compatible with each other. */ template<> struct DefaultHash { uint64_t operator()(const UString &value) const { return value.hash(); } constexpr uint64_t operator()(const StringRef value) const { #ifdef WITH_WEB uint64_t result = UINT64_C(14695981039346656037); for (const unsigned char byte : value) { result ^= byte; result *= UINT64_C(1099511628211); } return result; #else /* This is the hash function used by OpenImageIO::ustring::make_unique internally. */ return OIIO::Strutil::strhash64(value.size(), value.data()); #endif } }; /** * Create a UString from a string literal. This is a template function so that each string is only * made unique once and not every time the literal is used. * * Note: OpenImageIO defines a similar `_us` string literal operator. However, it newly constructs * the ustring in each invocation instead of caching it in a static variable. Caching it like here * likely only works in C++20. */ template inline UString operator""_ustr() { static const UString static_ustr = UString::from_ptr_noinline(FStr.data); return static_ustr; } /** * Support using the `fmt` library with #UString. */ inline std::string_view format_as(UString str) { return str.string(); } } // namespace blender /** * Disable conflicting range formatter in fmtlib. Otherwise we will get compile errors * where fmtlib doesn't know if it should use the formatter from format.h or ranges.h. */ namespace fmt { template<> struct is_range : std::false_type {}; } // namespace fmt