blob: 4fc6f58e09e3d595927c5f1392563eb6d8b4227c [file] [log] [blame] [edit]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_
#define THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <span> // NOLINT(build/c++20); <internal link>
#include <type_traits>
#include "absl/base/attributes.h"
#include "absl/types/span.h"
#include "support/internal/attribute_macros.h"
namespace rs_std {
// `rs_std::SliceRef` is a C++ representation of a pointer or reference to a
// Rust slice. `SliceRef<int const>` is like a `&[c_int]` or `*const [c_int]`,
// while `SliceRef<int>` is like a `&mut [c_int]` or `*mut [c_int]`. `SliceRef`
// is trivially destructible, copyable, and moveable.
// `rust_builtin_type_abi_assumptions.md` documents the ABI compatibility of
// these types.
template <typename T>
class CRUBIT_INTERNAL_RUST_TYPE("&[]")
ABSL_ATTRIBUTE_TRIVIAL_ABI SliceRef final {
public:
// Creates a default `SliceRef` - one that represents an empty slice.
// To mirror slices in Rust, the data pointer is not null.
constexpr SliceRef() noexcept : dangling_ptr_(alignof(T)), size_(0) {}
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr SliceRef(absl::Span<T> span) noexcept
// Store a dangling pointer assuming `span` is empty-- we have to
// initialize the union to something.
: dangling_ptr_(alignof(T)), size_(span.size()) {
// Store a valid pointer when `span` is not empty.
if (!span.empty()) {
ptr_ = span.data();
}
}
// Re-use implicit conversions to `absl::Span`. Prevent a delegation circle
// by excluding `absl::Span<T>` as the converted type.
template <typename Container>
requires(std::convertible_to<Container &&, absl::Span<T>> &&
!std::is_same_v<Container, absl::Span<T>>)
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr SliceRef(Container&& container) noexcept
: SliceRef(
// This is using `static_cast` instead of `absl::implicit_cast` to
// avoid a dependency on `absl/base/casts.h`, which has a lot of
// transitive dependencies. Doing so is safe, because the extra
// guarantees are already checked by std::convertible_to.
static_cast<absl::Span<T>>(std::forward<Container>(container))) {}
// Also mirror explicit conversions from `absl::Span`.
template <typename Container>
requires(std::constructible_from<absl::Span<T>, Container &&> &&
!std::convertible_to<Container &&, absl::Span<T>> &&
!std::is_same_v<Container, absl::Span<T>>)
constexpr explicit SliceRef(Container&& container) noexcept
: SliceRef(absl::Span<T>(std::forward<Container>(container))) {}
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator std::span<T>() const noexcept {
return std::span<T>(size_ > 0 ? ptr_ : nullptr, size_);
}
constexpr SliceRef(const SliceRef&) = default;
constexpr SliceRef& operator=(const SliceRef&) = default;
constexpr SliceRef(SliceRef&&) noexcept = default;
constexpr SliceRef& operator=(SliceRef&&) noexcept = default;
~SliceRef() = default;
constexpr T* data() const noexcept { return size_ > 0 ? ptr_ : nullptr; }
constexpr size_t size() const noexcept { return size_; }
constexpr absl::Span<T> to_span() const noexcept {
return absl::Span<T>(data(), size());
}
private:
// Stick to the following invariants when changing the data member values:
// (1) `ptr_` and `dangling_ptr_` must never be 0 (to mirror slices in Rust).
// (2) if `size_ > 0` then `ptr_` is a valid pointer, otherwise
// `dangling_ptr_` is a dangling pointer.
//
// `dangling_ptr_` is never read from in C++, and `ptr_` must only ever be
// read from when `size_ > 0`.
union {
T* ptr_;
uintptr_t dangling_ptr_;
};
size_t size_;
};
} // namespace rs_std
#endif // THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_