| // Copyright 2025 Google LLC | |
| // | |
| // 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. | |
| #ifndef RIEGELI_BASE_OPTIONAL_COMPACT_STRING_H_ | |
| #define RIEGELI_BASE_OPTIONAL_COMPACT_STRING_H_ | |
| #include <stdint.h> | |
| #include <cstddef> | |
| #include <utility> | |
| #include "absl/base/attributes.h" | |
| #include "absl/hash/hash.h" | |
| #include "absl/strings/string_view.h" | |
| #include "riegeli/base/assert.h" | |
| #include "riegeli/base/bytes_ref.h" | |
| #include "riegeli/base/compact_string.h" | |
| #include "riegeli/base/compare.h" | |
| #include "riegeli/base/iterable.h" | |
| namespace riegeli { | |
| // Similar to `std::optional<CompactString>`, but takes up the same amount of | |
| // space as `CompactString`. | |
| // | |
| // `OptionalCompactString` is either null or stores data equivalent to a | |
| // `CompactString`. It allows examining the contents as `absl::string_view` or | |
| // `const char*`, but not as `CompactString`, except by copying or moving from. | |
| class ABSL_ATTRIBUTE_TRIVIAL_ABI OptionalCompactString | |
| : public WithCompare<OptionalCompactString, std::nullptr_t, CompactString, | |
| absl::string_view> { | |
| public: | |
| // Supports heterogeneous lookup against `nullptr`, `CompactString`, and | |
| // `absl::string_view`. | |
| struct absl_container_hash; | |
| struct absl_container_eq; | |
| // Creates a null `OptionalCompactString`. | |
| OptionalCompactString() = default; | |
| /*implicit*/ OptionalCompactString(std::nullptr_t) {} | |
| OptionalCompactString& operator=(std::nullptr_t) { | |
| if (repr_ != kNullRepr) { | |
| CompactString::MoveFromRaw(repr_); | |
| repr_ = kNullRepr; | |
| } | |
| return *this; | |
| } | |
| // Creates an `OptionalCompactString` which holds a copy of `src`. | |
| explicit OptionalCompactString(BytesRef src) | |
| : repr_(CompactString(src).RawMove()) {} | |
| OptionalCompactString& operator=(BytesRef src) { | |
| const uintptr_t old_repr = | |
| std::exchange(repr_, CompactString(src).RawMove()); | |
| if (old_repr != kNullRepr) CompactString::MoveFromRaw(old_repr); | |
| return *this; | |
| } | |
| // Creates an `OptionalCompactString` which holds a copy of `src`. | |
| explicit OptionalCompactString(const CompactString& src) | |
| : repr_(CompactString(src).RawMove()) {} | |
| OptionalCompactString& operator=(const CompactString& src) { | |
| const uintptr_t old_repr = | |
| std::exchange(repr_, CompactString(src).RawMove()); | |
| if (old_repr != kNullRepr) CompactString::MoveFromRaw(old_repr); | |
| return *this; | |
| } | |
| // Creates an `OptionalCompactString` which holds `src`. The source | |
| // `CompactString` is left empty. | |
| explicit OptionalCompactString(CompactString&& src) | |
| : repr_(std::move(src).RawMove()) {} | |
| OptionalCompactString& operator=(CompactString&& src) { | |
| const uintptr_t old_repr = std::exchange(repr_, std::move(src).RawMove()); | |
| if (old_repr != kNullRepr) CompactString::MoveFromRaw(old_repr); | |
| return *this; | |
| } | |
| OptionalCompactString(const OptionalCompactString& that) noexcept | |
| : repr_(that.repr_ == kNullRepr ? kNullRepr | |
| : CompactString::CopyRaw(that.repr_)) {} | |
| OptionalCompactString& operator=(const OptionalCompactString& that) noexcept { | |
| const uintptr_t old_repr = std::exchange( | |
| repr_, that.repr_ == kNullRepr ? kNullRepr | |
| : CompactString::CopyRaw(that.repr_)); | |
| if (old_repr != kNullRepr) CompactString::MoveFromRaw(old_repr); | |
| return *this; | |
| } | |
| // The source `OptionalCompactString` is left null. | |
| OptionalCompactString(OptionalCompactString&& that) noexcept | |
| : repr_(std::exchange(that.repr_, kNullRepr)) {} | |
| OptionalCompactString& operator=(OptionalCompactString&& that) noexcept { | |
| const uintptr_t old_repr = | |
| std::exchange(repr_, std::exchange(that.repr_, kNullRepr)); | |
| if (old_repr != kNullRepr) CompactString::MoveFromRaw(old_repr); | |
| return *this; | |
| } | |
| ~OptionalCompactString() { | |
| if (repr_ != kNullRepr) CompactString::MoveFromRaw(repr_); | |
| } | |
| // Extracts the value as a `CompactString`. | |
| // | |
| // Precondition: `*this != nullptr`. | |
| CompactString Release() ABSL_ATTRIBUTE_LIFETIME_BOUND { | |
| RIEGELI_ASSERT(*this != nullptr) | |
| << "Failed precondition of OptionalCompactString::Release(): " | |
| "OptionalCompactString is nullptr"; | |
| return CompactString::MoveFromRaw(std::exchange(repr_, kNullRepr)); | |
| } | |
| // Views the value as an `absl::string_view`. | |
| // | |
| // Precondition: `*this != nullptr`. | |
| absl::string_view operator*() const ABSL_ATTRIBUTE_LIFETIME_BOUND { | |
| RIEGELI_ASSERT(*this != nullptr) | |
| << "Failed precondition of OptionalCompactString::operator*: " | |
| "OptionalCompactString is nullptr"; | |
| return CompactString::ViewFromRaw(&repr_); | |
| } | |
| // Views the value as an `absl::string_view`. | |
| // | |
| // Precondition: `*this != nullptr`. | |
| absl::string_view value() const ABSL_ATTRIBUTE_LIFETIME_BOUND { | |
| RIEGELI_CHECK(*this != nullptr) | |
| << "Failed precondition of OptionalCompactString::value(): " | |
| "OptionalCompactString is nullptr"; | |
| return CompactString::ViewFromRaw(&repr_); | |
| } | |
| ArrowProxy<absl::string_view> operator->() const | |
| ABSL_ATTRIBUTE_LIFETIME_BOUND { | |
| RIEGELI_ASSERT(*this != nullptr) | |
| << "Failed precondition of OptionalCompactString::operator->: " | |
| "OptionalCompactString is nullptr"; | |
| return ArrowProxy<absl::string_view>(**this); | |
| } | |
| // Ensures that the value is NUL-terminated after its size and returns | |
| // a pointer to it. Returns `nullptr` for a null `OptionalCompactString`. | |
| // | |
| // In contrast to `std::string::c_str()`, this is a non-const operation. | |
| // It may reallocate the string and it writes the NUL each time. | |
| const char* c_str() ABSL_ATTRIBUTE_LIFETIME_BOUND { | |
| if (repr_ == kNullRepr) return nullptr; | |
| return CompactString::CStrFromRaw(&repr_); | |
| } | |
| friend bool operator==(const OptionalCompactString& a, | |
| const OptionalCompactString& b) { | |
| if (a.repr_ == b.repr_) return true; | |
| if (a.repr_ == kNullRepr || b.repr_ == kNullRepr) return false; | |
| return *a == *b; | |
| } | |
| friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a, | |
| const OptionalCompactString& b) { | |
| if (a.repr_ == b.repr_) return StrongOrdering::equal; | |
| if (a.repr_ == kNullRepr) return StrongOrdering::less; | |
| if (b.repr_ == kNullRepr) return StrongOrdering::greater; | |
| return riegeli::Compare(*a, *b); | |
| } | |
| friend bool operator==(const OptionalCompactString& a, std::nullptr_t) { | |
| return a.repr_ == kNullRepr; | |
| } | |
| friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a, | |
| std::nullptr_t) { | |
| if (a.repr_ == kNullRepr) return StrongOrdering::equal; | |
| return StrongOrdering::greater; | |
| } | |
| friend bool operator==(const OptionalCompactString& a, | |
| const CompactString& b) { | |
| if (a.repr_ == kNullRepr) return false; | |
| if (a.repr_ == *b.RawView()) return true; | |
| return *a == absl::string_view(b); | |
| } | |
| friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a, | |
| const CompactString& b) { | |
| if (a.repr_ == kNullRepr) return StrongOrdering::less; | |
| if (a.repr_ == *b.RawView()) return StrongOrdering::equal; | |
| return riegeli::Compare(*a, absl::string_view(b)); | |
| } | |
| friend bool operator==(const OptionalCompactString& a, absl::string_view b) { | |
| if (a.repr_ == kNullRepr) return false; | |
| return *a == b; | |
| } | |
| friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a, | |
| absl::string_view b) { | |
| if (a.repr_ == kNullRepr) return StrongOrdering::less; | |
| return riegeli::Compare(*a, b); | |
| } | |
| template <typename HashState> | |
| friend HashState AbslHashValue(HashState hash_state, | |
| const OptionalCompactString& self) { | |
| if (self == nullptr) { | |
| return HashState::combine(std::move(hash_state), nullptr); | |
| } | |
| return HashState::combine(std::move(hash_state), *self); | |
| } | |
| // Supports `MemoryEstimator`. | |
| template <typename MemoryEstimator> | |
| friend void RiegeliRegisterSubobjects(const OptionalCompactString* self, | |
| MemoryEstimator& memory_estimator) { | |
| if (self->repr_ == kNullRepr) return; | |
| CompactString::RegisterSubobjects(self->repr_, memory_estimator); | |
| } | |
| // Supports `riegeli::Debug()`. | |
| template <typename DebugStream> | |
| friend void RiegeliDebug(const OptionalCompactString& src, | |
| DebugStream& dest) { | |
| if (src == nullptr) { | |
| dest.Debug(nullptr); | |
| } else { | |
| dest.Debug(*src); | |
| } | |
| } | |
| private: | |
| static constexpr uintptr_t kNullRepr = 0; | |
| uintptr_t repr_ = kNullRepr; | |
| }; | |
| // Implementation details follow. | |
| struct OptionalCompactString::absl_container_hash { | |
| using is_transparent = void; | |
| size_t operator()(const OptionalCompactString& value) const { | |
| return absl::Hash<OptionalCompactString>()(value); | |
| } | |
| size_t operator()(std::nullptr_t) const { | |
| return absl::Hash<std::nullptr_t>()(nullptr); | |
| } | |
| size_t operator()(const CompactString& value) const { | |
| return absl::Hash<CompactString>()(value); | |
| } | |
| size_t operator()(absl::string_view value) const { | |
| return absl::Hash<absl::string_view>()(value); | |
| } | |
| }; | |
| struct OptionalCompactString::absl_container_eq { | |
| using is_transparent = void; | |
| bool operator()(const OptionalCompactString& a, | |
| const OptionalCompactString& b) const { | |
| return a == b; | |
| } | |
| bool operator()(const OptionalCompactString& a, std::nullptr_t b) const { | |
| return a == b; | |
| } | |
| bool operator()(std::nullptr_t a, const OptionalCompactString& b) const { | |
| return a == b; | |
| } | |
| bool operator()(const OptionalCompactString& a, | |
| const CompactString& b) const { | |
| return a == b; | |
| } | |
| bool operator()(const CompactString& a, | |
| const OptionalCompactString& b) const { | |
| return a == b; | |
| } | |
| bool operator()(const OptionalCompactString& a, absl::string_view b) const { | |
| return a == b; | |
| } | |
| bool operator()(absl::string_view a, const OptionalCompactString& b) const { | |
| return a == b; | |
| } | |
| }; | |
| } // namespace riegeli | |
| #endif // RIEGELI_BASE_OPTIONAL_COMPACT_STRING_H_ |