| // Copyright 2021 gRPC authors. |
| // |
| // 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 GRPC_CORE_LIB_SLICE_SLICE_H |
| #define GRPC_CORE_LIB_SLICE_SLICE_H |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include "absl/strings/string_view.h" |
| |
| #include <grpc/slice.h> |
| |
| #include "src/core/lib/slice/slice_internal.h" |
| |
| // Herein lies grpc_core::Slice and its team of thin wrappers around grpc_slice. |
| // They aim to keep you safe by providing strong guarantees around lifetime and |
| // mutability. |
| // |
| // The team: |
| // Slice - provides a wrapper around an unknown type of slice. |
| // Immutable (since we don't know who else might be referencing |
| // it), and potentially ref counted. |
| // StaticSlice - provides a wrapper around a static slice. Not refcounted, |
| // fast to copy. |
| // MutableSlice - provides a guarantee of unique ownership, meaning the |
| // underlying data can be mutated safely. |
| |
| namespace grpc_core { |
| |
| // Forward declarations |
| class Slice; |
| class StaticSlice; |
| class MutableSlice; |
| |
| namespace slice_detail { |
| |
| // Returns an empty slice. |
| static constexpr grpc_slice EmptySlice() { return {nullptr, {}}; } |
| |
| // BaseSlice holds the grpc_slice object, but does not apply refcounting policy. |
| // It does export immutable access into the slice, such that this can be shared |
| // by all storage policies. |
| class BaseSlice { |
| public: |
| BaseSlice(const BaseSlice&) = delete; |
| BaseSlice& operator=(const BaseSlice&) = delete; |
| BaseSlice(BaseSlice&& other) = delete; |
| BaseSlice& operator=(BaseSlice&& other) = delete; |
| |
| // Iterator access to the underlying bytes |
| const uint8_t* begin() const { return GRPC_SLICE_START_PTR(c_slice()); } |
| const uint8_t* end() const { return GRPC_SLICE_END_PTR(c_slice()); } |
| const uint8_t* cbegin() const { return GRPC_SLICE_START_PTR(c_slice()); } |
| const uint8_t* cend() const { return GRPC_SLICE_END_PTR(c_slice()); } |
| |
| // Retrieve a borrowed reference to the underlying grpc_slice. |
| const grpc_slice& c_slice() const { return slice_; } |
| |
| // Retrieve the underlying grpc_slice, and replace the one in this object with |
| // EmptySlice(). |
| grpc_slice TakeCSlice() { |
| grpc_slice out = slice_; |
| slice_ = EmptySlice(); |
| return out; |
| } |
| |
| // As other things... borrowed references. |
| absl::string_view as_string_view() const { |
| return absl::string_view(reinterpret_cast<const char*>(data()), size()); |
| } |
| |
| // Array access |
| uint8_t operator[](size_t i) const { |
| return GRPC_SLICE_START_PTR(c_slice())[i]; |
| } |
| |
| // Access underlying data |
| const uint8_t* data() const { return GRPC_SLICE_START_PTR(c_slice()); } |
| |
| // Size of the slice |
| size_t size() const { return GRPC_SLICE_LENGTH(c_slice()); } |
| size_t length() const { return size(); } |
| bool empty() const { return size() == 0; } |
| |
| // For inlined slices - are these two slices equal? |
| // For non-inlined slices - do these two slices refer to the same block of |
| // memory? |
| bool is_equivalent(const BaseSlice& other) const { |
| return grpc_slice_is_equivalent(slice_, other.slice_); |
| } |
| |
| protected: |
| BaseSlice() : slice_(EmptySlice()) {} |
| explicit BaseSlice(const grpc_slice& slice) : slice_(slice) {} |
| ~BaseSlice() = default; |
| |
| void Swap(BaseSlice* other) { std::swap(slice_, other->slice_); } |
| void SetCSlice(const grpc_slice& slice) { slice_ = slice; } |
| |
| uint8_t* mutable_data() { return GRPC_SLICE_START_PTR(slice_); } |
| |
| private: |
| grpc_slice slice_; |
| }; |
| |
| inline bool operator==(const BaseSlice& a, const BaseSlice& b) { |
| return grpc_slice_eq(a.c_slice(), b.c_slice()) != 0; |
| } |
| |
| inline bool operator!=(const BaseSlice& a, const BaseSlice& b) { |
| return grpc_slice_eq(a.c_slice(), b.c_slice()) == 0; |
| } |
| |
| inline bool operator==(const BaseSlice& a, absl::string_view b) { |
| return a.as_string_view() == b; |
| } |
| |
| inline bool operator!=(const BaseSlice& a, absl::string_view b) { |
| return a.as_string_view() != b; |
| } |
| |
| inline bool operator==(absl::string_view a, const BaseSlice& b) { |
| return a == b.as_string_view(); |
| } |
| |
| inline bool operator!=(absl::string_view a, const BaseSlice& b) { |
| return a != b.as_string_view(); |
| } |
| |
| inline bool operator==(const BaseSlice& a, const grpc_slice& b) { |
| return grpc_slice_eq(a.c_slice(), b) != 0; |
| } |
| |
| inline bool operator!=(const BaseSlice& a, const grpc_slice& b) { |
| return grpc_slice_eq(a.c_slice(), b) == 0; |
| } |
| |
| inline bool operator==(const grpc_slice& a, const BaseSlice& b) { |
| return grpc_slice_eq(a, b.c_slice()) != 0; |
| } |
| |
| inline bool operator!=(const grpc_slice& a, const BaseSlice& b) { |
| return grpc_slice_eq(a, b.c_slice()) == 0; |
| } |
| |
| template <typename Out> |
| struct CopyConstructors { |
| static Out FromCopiedString(const char* s) { |
| return Out(grpc_slice_from_copied_string(s)); |
| } |
| static Out FromCopiedString(std::string s) { |
| return Out(grpc_slice_from_cpp_string(std::move(s))); |
| } |
| template <typename Buffer> |
| static Out FromCopiedBuffer(const Buffer& buffer) { |
| return Out(UnmanagedMemorySlice( |
| reinterpret_cast<const char*>(buffer.data()), buffer.size())); |
| } |
| }; |
| |
| } // namespace slice_detail |
| |
| class StaticSlice : public slice_detail::BaseSlice { |
| public: |
| StaticSlice() = default; |
| explicit StaticSlice(const grpc_slice& slice) |
| : slice_detail::BaseSlice(slice) { |
| GPR_DEBUG_ASSERT( |
| slice.refcount->GetType() == grpc_slice_refcount::Type::STATIC || |
| slice.refcount->GetType() == grpc_slice_refcount::Type::NOP); |
| } |
| explicit StaticSlice(const StaticMetadataSlice& slice) |
| : slice_detail::BaseSlice(slice) {} |
| |
| static StaticSlice FromStaticString(const char* s) { |
| return StaticSlice(grpc_slice_from_static_string(s)); |
| } |
| |
| StaticSlice(const StaticSlice& other) |
| : slice_detail::BaseSlice(other.c_slice()) {} |
| StaticSlice& operator=(const StaticSlice& other) { |
| SetCSlice(other.c_slice()); |
| return *this; |
| } |
| StaticSlice(StaticSlice&& other) noexcept |
| : slice_detail::BaseSlice(other.TakeCSlice()) {} |
| StaticSlice& operator=(StaticSlice&& other) noexcept { |
| Swap(&other); |
| return *this; |
| } |
| }; |
| |
| class MutableSlice : public slice_detail::BaseSlice, |
| public slice_detail::CopyConstructors<MutableSlice> { |
| public: |
| MutableSlice() = default; |
| explicit MutableSlice(const grpc_slice& slice) |
| : slice_detail::BaseSlice(slice) { |
| GPR_DEBUG_ASSERT(slice.refcount == nullptr || |
| slice.refcount->IsRegularUnique()); |
| } |
| ~MutableSlice() { grpc_slice_unref_internal(c_slice()); } |
| |
| MutableSlice(const MutableSlice&) = delete; |
| MutableSlice& operator=(const MutableSlice&) = delete; |
| MutableSlice(MutableSlice&& other) noexcept |
| : slice_detail::BaseSlice(other.TakeCSlice()) {} |
| MutableSlice& operator=(MutableSlice&& other) noexcept { |
| Swap(&other); |
| return *this; |
| } |
| |
| // Iterator access to the underlying bytes |
| uint8_t* begin() { return mutable_data(); } |
| uint8_t* end() { return mutable_data() + size(); } |
| |
| // Array access |
| uint8_t& operator[](size_t i) { return mutable_data()[i]; } |
| }; |
| |
| class Slice : public slice_detail::BaseSlice, |
| public slice_detail::CopyConstructors<Slice> { |
| public: |
| Slice() = default; |
| ~Slice() { grpc_slice_unref_internal(c_slice()); } |
| explicit Slice(const grpc_slice& slice) : slice_detail::BaseSlice(slice) {} |
| template <class SliceType> |
| explicit Slice(absl::enable_if_t< |
| std::is_base_of<slice_detail::BaseSlice, SliceType>::value, |
| SliceType>&& other) |
| : slice_detail::BaseSlice(other.TakeCSlice()) {} |
| |
| Slice(const Slice&) = delete; |
| Slice& operator=(const Slice&) = delete; |
| Slice(Slice&& other) noexcept : slice_detail::BaseSlice(other.TakeCSlice()) {} |
| Slice& operator=(Slice&& other) noexcept { |
| Swap(&other); |
| return *this; |
| } |
| |
| // A slice might refer to some memory that we keep a refcount to (this is |
| // owned), or some memory that's inlined into the slice (also owned), or some |
| // other block of memory that we know will be available for the lifetime of |
| // some operation in the common case (not owned). In the *less common* case |
| // that we need to keep that slice text for longer than our API's guarantee us |
| // access, we need to take a copy and turn this into something that we do own. |
| |
| // TakeOwned returns an owned slice regardless of current ownership, and |
| // leaves the current slice in a valid but externally unpredictable state - in |
| // doing so it can avoid adding a ref to the underlying slice. |
| Slice TakeOwned() { |
| if (c_slice().refcount == nullptr) { |
| return Slice(c_slice()); |
| } |
| if (c_slice().refcount->GetType() == grpc_slice_refcount::Type::NOP) { |
| return Slice(grpc_slice_copy(c_slice())); |
| } |
| return Slice(TakeCSlice()); |
| } |
| |
| // AsOwned returns an owned slice but does not mutate the current slice, |
| // meaning that it may add a reference to the underlying slice. |
| Slice AsOwned() const { |
| if (c_slice().refcount == nullptr) { |
| return Slice(c_slice()); |
| } |
| if (c_slice().refcount->GetType() == grpc_slice_refcount::Type::NOP) { |
| return Slice(grpc_slice_copy(c_slice())); |
| } |
| return Slice(grpc_slice_ref_internal(c_slice())); |
| } |
| |
| // TakeMutable returns a MutableSlice, and leaves the current slice in an |
| // indeterminate but valid state. |
| // A mutable slice requires only one reference to the bytes of the slice - |
| // this can be achieved either with inlined storage or with a single |
| // reference. |
| // If the current slice is refcounted and there are more than one references |
| // to that slice, then the slice is copied in order to achieve a mutable |
| // version. |
| MutableSlice TakeMutable() { |
| if (c_slice().refcount == nullptr) { |
| return MutableSlice(c_slice()); |
| } |
| if (c_slice().refcount->GetType() == grpc_slice_refcount::Type::REGULAR && |
| c_slice().refcount->IsRegularUnique()) { |
| return MutableSlice(TakeCSlice()); |
| } |
| return MutableSlice(grpc_slice_copy(c_slice())); |
| } |
| |
| Slice Ref() const { return Slice(grpc_slice_ref_internal(c_slice())); } |
| |
| Slice Copy() const { return Slice(grpc_slice_copy(c_slice())); } |
| |
| static Slice FromRefcountAndBytes(grpc_slice_refcount* r, |
| const uint8_t* begin, const uint8_t* end) { |
| grpc_slice out; |
| out.refcount = r; |
| r->Ref(); |
| out.data.refcounted.bytes = const_cast<uint8_t*>(begin); |
| out.data.refcounted.length = end - begin; |
| return Slice(out); |
| } |
| }; |
| |
| } // namespace grpc_core |
| |
| #endif // GRPC_CORE_LIB_SLICE_SLICE_H |