| // 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 "crubit/support/annotations_internal.h" | |
| #include "absl/base/attributes.h" | |
| #include "absl/types/span.h" | |
| #include "support/internal/check_no_mutable_aliasing.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) {} | |
| // Style waiver for implicit conversions granted in cl/662479273. | |
| // 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 | |
| namespace crubit::internal { | |
| template <typename T> | |
| struct PtrLike<rs_std::SliceRef<T>> { | |
| static constexpr bool kIsConst = std::is_const_v<T>; | |
| static PtrData AsPtrData(rs_std::SliceRef<T> t) { | |
| uintptr_t start = reinterpret_cast<uintptr_t>(t.data()); | |
| return { | |
| .start = start, | |
| .end = start + t.size() * sizeof(T), | |
| }; | |
| } | |
| }; | |
| } // namespace crubit::internal | |
| #endif // THIRD_PARTY_CRUBIT_SUPPORT_RS_STD_SLICEREF_H_ |