190 lines
4.8 KiB
C++
190 lines
4.8 KiB
C++
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#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 <OpenImageIO/ustring.h>
|
|
#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<StringRef>. That is
|
|
* fine though. The only requirement is that all hashes defined in this template specialization are
|
|
* compatible with each other.
|
|
*/
|
|
template<> struct DefaultHash<UString> {
|
|
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<FixedString FStr> 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<blender::UString, char> : std::false_type {};
|
|
|
|
} // namespace fmt
|