blob: ca0414ebe5286e2d57fdcef58073556de4237a31 [file] [log] [blame]
// 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/base/casts.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 `&[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 : ptr_(alignof(T)), size_(0) {}
// This constructor cannot be constexpr because it calls `reinterpret_cast`.
// The `reinterpret_cast`, in turn, is needed if we want to keep `ptr_` an
// `uintptr_t` instead of a `T*`, which we do to avoid storing dangling
// pointers. The `reinterpret_cast` preserves both invariants required for
// `ptr_`, because the resulting value comes from a valid, non-null pointer.
// NOLINTNEXTLINE(google-explicit-constructor)
SliceRef(absl::Span<T> span) noexcept
: ptr_(span.empty() ? alignof(T)
: reinterpret_cast<uintptr_t>(span.data())),
size_(span.size()) {}
// 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)
SliceRef(Container&& container) noexcept
: SliceRef(absl::implicit_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>>)
explicit SliceRef(Container&& container) noexcept
: SliceRef(absl::Span<T>(std::forward<Container>(container))) {}
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator std::span<T>() const {
return std::span<T>(size_ > 0 ? static_cast<T*>(ptr_) : nullptr, size_);
}
constexpr SliceRef(const SliceRef&) = default;
constexpr SliceRef& operator=(const SliceRef&) = default;
constexpr SliceRef(SliceRef&&) = default;
constexpr SliceRef& operator=(SliceRef&&) = default;
~SliceRef() = default;
// The `reinterpret_cast` is safe thanks to invariant (2) (see the definition
// of `ptr_`).
T* data() const { return size_ > 0 ? reinterpret_cast<T*>(ptr_) : nullptr; }
size_t size() const { return size_; }
absl::Span<T> to_span() const { return absl::Span<T>(data(), size()); }
private:
// Stick to the following invariants when changing the data member values:
// (1) `ptr_` is never 0 (to mirror slices in Rust).
// (2) if `size_ > 0` then `reinterpret_cast<T*>(ptr_)` is a valid pointer.
uintptr_t ptr_;
size_t size_;
};
} // namespace rs_std
#endif // THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_