blob: e14ae19819eb6287ad955887c39fc60dc3c779ea [file] [log] [blame]
// 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 <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/nullability.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/type_traits.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_NULLABILITY_COMPATIBLE ABSL_ATTRIBUTE_TRIVIAL_ABI
OptionalCompactString : public WithCompare<OptionalCompactString> {
private:
class StringViewPointer {
public:
const absl::string_view* operator->() const { return &ref_; }
private:
friend class OptionalCompactString;
explicit StringViewPointer(absl::string_view ref) : ref_(ref) {}
absl::string_view ref_;
};
public:
// 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_);
}
StringViewPointer operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
RIEGELI_ASSERT(*this != nullptr)
<< "Failed precondition of OptionalCompactString::operator->: "
"OptionalCompactString is nullptr";
return StringViewPointer(**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;
}
template <
typename T,
std::enable_if_t<std::conjunction_v<NotSameRef<OptionalCompactString, T>,
NotSameRef<std::nullptr_t, T>,
std::is_convertible<T&&, BytesRef>>,
int> = 0>
friend bool operator==(const OptionalCompactString& a, T&& b) {
if (a.repr_ == kNullRepr) return false;
return *a == absl::string_view(b);
}
template <
typename T,
std::enable_if_t<std::conjunction_v<NotSameRef<OptionalCompactString, T>,
NotSameRef<std::nullptr_t, T>,
std::is_convertible<T&&, BytesRef>>,
int> = 0>
friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a, T&& b) {
if (a.repr_ == kNullRepr) return StrongOrdering::less;
return riegeli::Compare(*a, absl::string_view(b));
}
private:
// For `ABSL_NULLABILITY_COMPATIBLE`.
using pointer = const absl::string_view*;
static constexpr uintptr_t kNullRepr = 0;
uintptr_t repr_ = kNullRepr;
};
} // namespace riegeli
#endif // RIEGELI_BASE_OPTIONAL_COMPACT_STRING_H_