mirror of https://github.com/grpc/grpc.git
A starter slice buffer implementation to unblock event engine endpoints (#29367)
* A starter SliceBuffer implementation * Add comments and fix sanity checks * Minor fixes * more minor fixes * Addressing review comments and adding a slice_buffer_test * fix sanity checks * regenerate projects * fixing undefined function error * reverting changes from memory_allocator.cc and adding them to src/core/lib/slice/slice_buffer.cc to allow memory allocator lib to build correctly * fix sanity checks * adding an open source slice definition * regnerate projects * fix asan error * Automated change: Fix sanity tests * addressing review comments * fix sanity checks * regenerate projects * update * fix sanity checks * Converting slice buffer to retarin ownership of the underlying ctype * fix nits Co-authored-by: Vignesh2208 <Vignesh2208@users.noreply.github.com>pull/29647/head
parent
4ab3103293
commit
1494b1ef26
26 changed files with 841 additions and 77 deletions
@ -0,0 +1,286 @@ |
||||
// Copyright 2022 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_EVENT_ENGINE_SLICE_H |
||||
#define GRPC_EVENT_ENGINE_SLICE_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <cstdint> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
|
||||
#include <grpc/slice.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
// This public slice definition largely based of the internal grpc_core::Slice
|
||||
// implementation. Changes to this implementation might warrant changes to the
|
||||
// internal grpc_core::Slice type as well.
|
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
// Forward declarations
|
||||
class Slice; |
||||
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_); |
||||
} |
||||
|
||||
uint32_t Hash() const; |
||||
|
||||
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_); } |
||||
|
||||
grpc_slice* c_slice_ptr() { return &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 FromCopiedBuffer(s, strlen(s)); |
||||
} |
||||
static Out FromCopiedString(absl::string_view s) { |
||||
return FromCopiedBuffer(s.data(), s.size()); |
||||
} |
||||
static Out FromCopiedString(std::string s); |
||||
|
||||
static Out FromCopiedBuffer(const char* p, size_t len) { |
||||
return Out(grpc_slice_from_copied_buffer(p, len)); |
||||
} |
||||
|
||||
template <typename Buffer> |
||||
static Out FromCopiedBuffer(const Buffer& buffer) { |
||||
return FromCopiedBuffer(reinterpret_cast<const char*>(buffer.data()), |
||||
buffer.size()); |
||||
} |
||||
}; |
||||
|
||||
} // namespace slice_detail
|
||||
|
||||
class MutableSlice : public slice_detail::BaseSlice, |
||||
public slice_detail::CopyConstructors<MutableSlice> { |
||||
public: |
||||
MutableSlice() = default; |
||||
explicit MutableSlice(const grpc_slice& slice); |
||||
~MutableSlice(); |
||||
|
||||
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; |
||||
} |
||||
|
||||
static MutableSlice CreateUninitialized(size_t length) { |
||||
return MutableSlice(grpc_slice_malloc(length)); |
||||
} |
||||
|
||||
// Return a sub slice of this one. Leaves this slice in an indeterminate but
|
||||
// valid state.
|
||||
MutableSlice TakeSubSlice(size_t pos, size_t n) { |
||||
return MutableSlice(grpc_slice_sub_no_ref(TakeCSlice(), pos, pos + n)); |
||||
} |
||||
|
||||
// Iterator access to the underlying bytes
|
||||
uint8_t* begin() { return mutable_data(); } |
||||
uint8_t* end() { return mutable_data() + size(); } |
||||
uint8_t* data() { return mutable_data(); } |
||||
|
||||
// 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(); |
||||
explicit Slice(const grpc_slice& slice) : slice_detail::BaseSlice(slice) {} |
||||
explicit Slice(slice_detail::BaseSlice&& 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(); |
||||
|
||||
// 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; |
||||
|
||||
// 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(); |
||||
|
||||
// Return a sub slice of this one. Leaves this slice in an indeterminate but
|
||||
// valid state.
|
||||
Slice TakeSubSlice(size_t pos, size_t n) { |
||||
return Slice(grpc_slice_sub_no_ref(TakeCSlice(), pos, pos + n)); |
||||
} |
||||
|
||||
// Return a sub slice of this one. Adds a reference to the underlying slice.
|
||||
Slice RefSubSlice(size_t pos, size_t n) const { |
||||
return Slice(grpc_slice_sub(c_slice(), pos, pos + n)); |
||||
} |
||||
|
||||
// Split this slice, returning a new slice containing (split:end] and
|
||||
// leaving this slice with [begin:split).
|
||||
Slice Split(size_t split) { |
||||
return Slice(grpc_slice_split_tail(c_slice_ptr(), split)); |
||||
} |
||||
|
||||
Slice Ref() const; |
||||
|
||||
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); |
||||
}; |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
||||
|
||||
#endif // GRPC_EVENT_ENGINE_SLICE_H
|
@ -0,0 +1,112 @@ |
||||
// Copyright 2022 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_EVENT_ENGINE_SLICE_BUFFER_H |
||||
#define GRPC_EVENT_ENGINE_SLICE_BUFFER_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <cstdint> |
||||
#include <string> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/utility/utility.h" |
||||
|
||||
#include <grpc/event_engine/slice.h> |
||||
#include <grpc/slice.h> |
||||
#include <grpc/slice_buffer.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
/// A Wrapper around \a grpc_slice_buffer pointer.
|
||||
///
|
||||
/// A slice buffer holds the memory for a collection of slices.
|
||||
/// The SliceBuffer object itself is meant to only hide the C-style API,
|
||||
/// and won't hold the data itself. In terms of lifespan, the
|
||||
/// grpc_slice_buffer ought to be kept somewhere inside the caller's objects,
|
||||
/// like a transport or an endpoint.
|
||||
///
|
||||
/// This lifespan rule is likely to change in the future, as we may
|
||||
/// collapse the grpc_slice_buffer structure straight into this class.
|
||||
///
|
||||
/// The SliceBuffer API is basically a replica of the grpc_slice_buffer's,
|
||||
/// and its documentation will move here once we remove the C structure,
|
||||
/// which should happen before the Event Engine's API is no longer
|
||||
/// an experimental API.
|
||||
class SliceBuffer { |
||||
public: |
||||
explicit SliceBuffer() { grpc_slice_buffer_init(&slice_buffer_); } |
||||
SliceBuffer(const SliceBuffer& other) = delete; |
||||
SliceBuffer(SliceBuffer&& other) noexcept |
||||
: slice_buffer_(other.slice_buffer_) { |
||||
grpc_slice_buffer_reset_and_unref(&slice_buffer_); |
||||
grpc_slice_buffer_swap(&slice_buffer_, &other.slice_buffer_); |
||||
} |
||||
/// Upon destruction, the underlying raw slice buffer is cleaned out and all
|
||||
/// slices are unreffed.
|
||||
~SliceBuffer() { grpc_slice_buffer_destroy(&slice_buffer_); } |
||||
|
||||
/// Appends a new slice into the SliceBuffer and makes an attempt to merge
|
||||
/// this slice with the last slice in the SliceBuffer.
|
||||
void Append(Slice slice); |
||||
|
||||
/// Adds a new slice into the SliceBuffer at the next available index.
|
||||
/// Returns the index at which the new slice is added.
|
||||
size_t AppendIndexed(Slice slice); |
||||
|
||||
/// Returns the number of slices held by the SliceBuffer.
|
||||
size_t Count() { return slice_buffer_.count; } |
||||
|
||||
/// Removes/deletes the last n bytes in the SliceBuffer.
|
||||
void RemoveLastNBytes(size_t n) { |
||||
grpc_slice_buffer_trim_end(&slice_buffer_, n, nullptr); |
||||
} |
||||
|
||||
/// Move the first n bytes of the SliceBuffer into a memory pointed to by dst.
|
||||
void MoveFirstNBytesIntoBuffer(size_t n, void* dst) { |
||||
grpc_slice_buffer_move_first_into_buffer(&slice_buffer_, n, dst); |
||||
} |
||||
|
||||
/// Removes and unrefs all slices in the SliceBuffer.
|
||||
void Clear() { grpc_slice_buffer_reset_and_unref(&slice_buffer_); } |
||||
|
||||
/// Removes the first slice in the SliceBuffer and returns it.
|
||||
Slice TakeFirst(); |
||||
|
||||
/// Prepends the slice to the the front of the SliceBuffer.
|
||||
void Prepend(Slice slice); |
||||
|
||||
/// Increased the ref-count of slice at the specified index and returns the
|
||||
/// associated slice.
|
||||
Slice RefSlice(size_t index); |
||||
|
||||
/// The total number of bytes held by the SliceBuffer
|
||||
size_t Length() { return slice_buffer_.length; } |
||||
|
||||
/// Return a pointer to the back raw grpc_slice_buffer
|
||||
grpc_slice_buffer* RawSliceBuffer() { return &slice_buffer_; } |
||||
|
||||
private: |
||||
/// The backing raw slice buffer.
|
||||
grpc_slice_buffer slice_buffer_; |
||||
}; |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
||||
|
||||
#endif // GRPC_EVENT_ENGINE_SLICE_BUFFER_H
|
@ -0,0 +1,102 @@ |
||||
// Copyright 2022 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.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
|
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include <grpc/event_engine/slice.h> |
||||
#include <grpc/slice.h> |
||||
|
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "src/core/lib/slice/slice_refcount.h" |
||||
#include "src/core/lib/slice/slice_refcount_base.h" |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
namespace slice_detail { |
||||
|
||||
uint32_t BaseSlice::Hash() const { return grpc_slice_hash_internal(slice_); } |
||||
|
||||
template <> |
||||
MutableSlice CopyConstructors<MutableSlice>::FromCopiedString(std::string s) { |
||||
return MutableSlice(grpc_slice_from_cpp_string(std::move(s))); |
||||
} |
||||
|
||||
template <> |
||||
Slice CopyConstructors<Slice>::FromCopiedString(std::string s) { |
||||
return Slice(grpc_slice_from_cpp_string(std::move(s))); |
||||
} |
||||
|
||||
} // namespace slice_detail
|
||||
|
||||
MutableSlice::MutableSlice(const grpc_slice& slice) |
||||
: slice_detail::BaseSlice(slice) { |
||||
GPR_DEBUG_ASSERT(slice.refcount == nullptr || slice.refcount->IsUnique()); |
||||
} |
||||
|
||||
MutableSlice::~MutableSlice() { grpc_slice_unref_internal(c_slice()); } |
||||
|
||||
Slice Slice::TakeOwned() { |
||||
if (c_slice().refcount == nullptr) { |
||||
return Slice(c_slice()); |
||||
} |
||||
if (c_slice().refcount == grpc_slice_refcount::NoopRefcount()) { |
||||
return Slice(grpc_slice_copy(c_slice())); |
||||
} |
||||
return Slice(TakeCSlice()); |
||||
} |
||||
|
||||
Slice Slice::AsOwned() const { |
||||
if (c_slice().refcount == nullptr) { |
||||
return Slice(c_slice()); |
||||
} |
||||
if (c_slice().refcount == grpc_slice_refcount::NoopRefcount()) { |
||||
return Slice(grpc_slice_copy(c_slice())); |
||||
} |
||||
return Slice(grpc_slice_ref_internal(c_slice())); |
||||
} |
||||
|
||||
MutableSlice Slice::TakeMutable() { |
||||
if (c_slice().refcount == nullptr) { |
||||
return MutableSlice(c_slice()); |
||||
} |
||||
if (c_slice().refcount != grpc_slice_refcount::NoopRefcount() && |
||||
c_slice().refcount->IsUnique()) { |
||||
return MutableSlice(TakeCSlice()); |
||||
} |
||||
return MutableSlice(grpc_slice_copy(c_slice())); |
||||
} |
||||
|
||||
Slice::~Slice() { grpc_slice_unref_internal(c_slice()); } |
||||
|
||||
Slice Slice::Ref() const { return Slice(grpc_slice_ref_internal(c_slice())); } |
||||
|
||||
Slice Slice::FromRefcountAndBytes(grpc_slice_refcount* r, const uint8_t* begin, |
||||
const uint8_t* end) { |
||||
grpc_slice out; |
||||
out.refcount = r; |
||||
if (r != grpc_slice_refcount::NoopRefcount()) r->Ref(); |
||||
out.data.refcounted.bytes = const_cast<uint8_t*>(begin); |
||||
out.data.refcounted.length = end - begin; |
||||
return Slice(out); |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
@ -0,0 +1,49 @@ |
||||
// Copyright 2022 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.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/event_engine/slice_buffer.h> |
||||
#include <grpc/slice.h> |
||||
#include <grpc/slice_buffer.h> |
||||
|
||||
#include "src/core/lib/slice/slice.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "src/core/lib/slice/slice_refcount.h" |
||||
|
||||
namespace grpc_event_engine { |
||||
namespace experimental { |
||||
|
||||
void SliceBuffer::Append(Slice slice) { |
||||
grpc_slice_buffer_add(&slice_buffer_, slice.TakeCSlice()); |
||||
} |
||||
|
||||
size_t SliceBuffer::AppendIndexed(Slice slice) { |
||||
return grpc_slice_buffer_add_indexed(&slice_buffer_, slice.TakeCSlice()); |
||||
} |
||||
|
||||
Slice SliceBuffer::TakeFirst() { |
||||
return Slice(grpc_slice_buffer_take_first(&slice_buffer_)); |
||||
} |
||||
|
||||
void SliceBuffer::Prepend(Slice slice) { |
||||
grpc_slice_buffer_undo_take_first(&slice_buffer_, slice.TakeCSlice()); |
||||
} |
||||
|
||||
Slice SliceBuffer::RefSlice(size_t index) { |
||||
return Slice(grpc_slice_ref_internal(slice_buffer_.slices[index])); |
||||
} |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc_event_engine
|
@ -0,0 +1,79 @@ |
||||
// Copyright 2022 The 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.
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/event_engine/slice_buffer.h> |
||||
#include <grpc/grpc.h> |
||||
|
||||
#include "src/core/lib/slice/slice.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
using ::grpc_event_engine::experimental::Slice; |
||||
using ::grpc_event_engine::experimental::SliceBuffer; |
||||
|
||||
static constexpr int kNewSliceLength = 100; |
||||
|
||||
Slice MakeSlice(size_t len) { |
||||
GPR_ASSERT(len > 0); |
||||
unsigned char* contents = reinterpret_cast<unsigned char*>(new char[len]); |
||||
memset(contents, 'a', len); |
||||
return Slice(grpc_slice_new(contents, len, gpr_free)); |
||||
} |
||||
|
||||
TEST(SliceBufferTest, AddAndRemoveTest) { |
||||
SliceBuffer sb; |
||||
Slice first_slice = MakeSlice(kNewSliceLength); |
||||
Slice second_slice = MakeSlice(kNewSliceLength); |
||||
Slice first_slice_copy = first_slice.Copy(); |
||||
sb.Append(std::move(first_slice)); |
||||
sb.Append(std::move(second_slice)); |
||||
ASSERT_EQ(sb.Count(), 2); |
||||
ASSERT_EQ(sb.Length(), 2 * kNewSliceLength); |
||||
Slice popped = sb.TakeFirst(); |
||||
ASSERT_EQ(popped, first_slice_copy); |
||||
ASSERT_EQ(sb.Count(), 1); |
||||
ASSERT_EQ(sb.Length(), kNewSliceLength); |
||||
sb.Prepend(std::move(popped)); |
||||
ASSERT_EQ(sb.Count(), 2); |
||||
ASSERT_EQ(sb.Length(), 2 * kNewSliceLength); |
||||
sb.Clear(); |
||||
ASSERT_EQ(sb.Count(), 0); |
||||
ASSERT_EQ(sb.Length(), 0); |
||||
} |
||||
|
||||
TEST(SliceBufferTest, SliceRefTest) { |
||||
SliceBuffer sb; |
||||
Slice first_slice = MakeSlice(kNewSliceLength); |
||||
Slice second_slice = MakeSlice(kNewSliceLength + 1); |
||||
Slice first_slice_copy = first_slice.Copy(); |
||||
Slice second_slice_copy = second_slice.Copy(); |
||||
ASSERT_EQ(sb.AppendIndexed(std::move(first_slice)), 0); |
||||
ASSERT_EQ(sb.AppendIndexed(std::move(second_slice)), 1); |
||||
Slice first_reffed = sb.RefSlice(0); |
||||
Slice second_reffed = sb.RefSlice(1); |
||||
ASSERT_EQ(first_reffed, first_slice_copy); |
||||
ASSERT_EQ(second_reffed, second_slice_copy); |
||||
ASSERT_EQ(sb.Count(), 2); |
||||
ASSERT_EQ(sb.Length(), 2 * kNewSliceLength + 1); |
||||
sb.Clear(); |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue