mirror of https://github.com/grpc/grpc.git
parent
fda6158600
commit
939bbfc336
55 changed files with 1235 additions and 215 deletions
@ -0,0 +1,269 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2016 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 <inttypes.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/lib/gpr/murmur_hash.h" |
||||||
|
#include "src/core/lib/gprpp/sync.h" |
||||||
|
#include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */ |
||||||
|
#include "src/core/lib/profiling/timers.h" |
||||||
|
#include "src/core/lib/slice/slice_internal.h" |
||||||
|
#include "src/core/lib/slice/slice_string_helpers.h" |
||||||
|
#include "src/core/lib/slice/slice_utils.h" |
||||||
|
|
||||||
|
#define LOG2_SHARD_COUNT 5 |
||||||
|
#define SHARD_COUNT (1 << LOG2_SHARD_COUNT) |
||||||
|
#define INITIAL_SHARD_CAPACITY 8 |
||||||
|
|
||||||
|
#define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity)) |
||||||
|
#define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1)) |
||||||
|
|
||||||
|
using grpc_core::InternedSliceRefcount; |
||||||
|
|
||||||
|
typedef struct slice_shard { |
||||||
|
grpc_core::Mutex mu; |
||||||
|
InternedSliceRefcount** strs; |
||||||
|
size_t count; |
||||||
|
size_t capacity; |
||||||
|
} slice_shard; |
||||||
|
|
||||||
|
static slice_shard* g_shards; |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
/* hash seed: decided at initialization time */ |
||||||
|
uint32_t g_hash_seed; |
||||||
|
static bool g_forced_hash_seed = false; |
||||||
|
|
||||||
|
InternedSliceRefcount::~InternedSliceRefcount() { |
||||||
|
slice_shard* shard = &g_shards[SHARD_IDX(this->hash)]; |
||||||
|
MutexLock lock(&shard->mu); |
||||||
|
InternedSliceRefcount** prev_next; |
||||||
|
InternedSliceRefcount* cur; |
||||||
|
for (prev_next = &shard->strs[TABLE_IDX(this->hash, shard->capacity)], |
||||||
|
cur = *prev_next; |
||||||
|
cur != this; prev_next = &cur->bucket_next, cur = cur->bucket_next) { |
||||||
|
} |
||||||
|
*prev_next = cur->bucket_next; |
||||||
|
shard->count--; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
static void grow_shard(slice_shard* shard) { |
||||||
|
GPR_TIMER_SCOPE("grow_strtab", 0); |
||||||
|
|
||||||
|
size_t capacity = shard->capacity * 2; |
||||||
|
size_t i; |
||||||
|
InternedSliceRefcount** strtab; |
||||||
|
InternedSliceRefcount *s, *next; |
||||||
|
|
||||||
|
strtab = static_cast<InternedSliceRefcount**>( |
||||||
|
gpr_zalloc(sizeof(InternedSliceRefcount*) * capacity)); |
||||||
|
|
||||||
|
for (i = 0; i < shard->capacity; i++) { |
||||||
|
for (s = shard->strs[i]; s; s = next) { |
||||||
|
size_t idx = TABLE_IDX(s->hash, capacity); |
||||||
|
next = s->bucket_next; |
||||||
|
s->bucket_next = strtab[idx]; |
||||||
|
strtab[idx] = s; |
||||||
|
} |
||||||
|
} |
||||||
|
gpr_free(shard->strs); |
||||||
|
shard->strs = strtab; |
||||||
|
shard->capacity = capacity; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_core::InternedSlice::InternedSlice(InternedSliceRefcount* s) { |
||||||
|
refcount = &s->base; |
||||||
|
data.refcounted.bytes = reinterpret_cast<uint8_t*>(s + 1); |
||||||
|
data.refcounted.length = s->length; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t grpc_slice_default_hash_impl(grpc_slice s) { |
||||||
|
return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), |
||||||
|
grpc_core::g_hash_seed); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t grpc_slice_hash(grpc_slice s) { return grpc_slice_hash_internal(s); } |
||||||
|
|
||||||
|
grpc_slice grpc_slice_intern(grpc_slice slice) { |
||||||
|
/* TODO(arjunroy): At present, this is capable of returning either a static or
|
||||||
|
an interned slice. This yields weirdness like the constructor for |
||||||
|
ManagedMemorySlice instantiating itself as an instance of a derived type |
||||||
|
(StaticMetadataSlice or InternedSlice). Should reexamine. */ |
||||||
|
return grpc_core::ManagedMemorySlice(&slice); |
||||||
|
} |
||||||
|
|
||||||
|
// Helper methods to enable us to select appropriately overloaded slice methods
|
||||||
|
// whether we're dealing with a slice, or a buffer with length, when interning
|
||||||
|
// strings. Helpers for FindOrCreateInternedSlice().
|
||||||
|
static const char* GetBuffer(const std::pair<const char*, size_t>& buflen) { |
||||||
|
return buflen.first; |
||||||
|
} |
||||||
|
static size_t GetLength(const std::pair<const char*, size_t>& buflen) { |
||||||
|
return buflen.second; |
||||||
|
} |
||||||
|
static const void* GetBuffer(const grpc_slice& slice) { |
||||||
|
return GRPC_SLICE_START_PTR(slice); |
||||||
|
} |
||||||
|
static size_t GetLength(const grpc_slice& slice) { |
||||||
|
return GRPC_SLICE_LENGTH(slice); |
||||||
|
} |
||||||
|
|
||||||
|
// Creates an interned slice for a string that does not currently exist in the
|
||||||
|
// intern table. SliceArgs is either a const grpc_slice& or a const
|
||||||
|
// pair<const char*, size_t>&. Hash is the pre-computed hash value. We must
|
||||||
|
// already hold the shard lock. Helper for FindOrCreateInternedSlice().
|
||||||
|
//
|
||||||
|
// Returns: a newly interned slice.
|
||||||
|
template <typename SliceArgs> |
||||||
|
static InternedSliceRefcount* InternNewStringLocked(slice_shard* shard, |
||||||
|
size_t shard_idx, |
||||||
|
uint32_t hash, |
||||||
|
const SliceArgs& args) { |
||||||
|
/* string data goes after the internal_string header */ |
||||||
|
size_t len = GetLength(args); |
||||||
|
const void* buffer = GetBuffer(args); |
||||||
|
InternedSliceRefcount* s = |
||||||
|
static_cast<InternedSliceRefcount*>(gpr_malloc(sizeof(*s) + len)); |
||||||
|
new (s) grpc_core::InternedSliceRefcount(len, hash, shard->strs[shard_idx]); |
||||||
|
// TODO(arjunroy): Investigate why hpack tried to intern the nullptr string.
|
||||||
|
// https://github.com/grpc/grpc/pull/20110#issuecomment-526729282
|
||||||
|
if (len > 0) { |
||||||
|
memcpy(reinterpret_cast<char*>(s + 1), buffer, len); |
||||||
|
} |
||||||
|
shard->strs[shard_idx] = s; |
||||||
|
shard->count++; |
||||||
|
if (shard->count > shard->capacity * 2) { |
||||||
|
grow_shard(shard); |
||||||
|
} |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
// Attempt to see if the provided slice or string matches an existing interned
|
||||||
|
// slice. SliceArgs... is either a const grpc_slice& or a string and length. In
|
||||||
|
// either case, hash is the pre-computed hash value. We must already hold the
|
||||||
|
// shard lock. Helper for FindOrCreateInternedSlice().
|
||||||
|
//
|
||||||
|
// Returns: a pre-existing matching static slice, or null.
|
||||||
|
template <typename SliceArgs> |
||||||
|
static InternedSliceRefcount* MatchInternedSliceLocked(uint32_t hash, |
||||||
|
size_t idx, |
||||||
|
const SliceArgs& args) { |
||||||
|
InternedSliceRefcount* s; |
||||||
|
slice_shard* shard = &g_shards[SHARD_IDX(hash)]; |
||||||
|
/* search for an existing string */ |
||||||
|
for (s = shard->strs[idx]; s; s = s->bucket_next) { |
||||||
|
if (s->hash == hash && grpc_core::InternedSlice(s) == args) { |
||||||
|
if (grpc_core::IncrementIfNonzero(&s->refcnt)) { |
||||||
|
return s; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
// Attempt to see if the provided slice or string matches an existing interned
|
||||||
|
// slice, and failing that, create an interned slice with its contents. Returns
|
||||||
|
// either the existing matching interned slice or the newly created one.
|
||||||
|
// SliceArgs is either a const grpc_slice& or const pair<const char*, size_t>&.
|
||||||
|
// In either case, hash is the pre-computed hash value. We do not hold the
|
||||||
|
// shard lock here, but do take it.
|
||||||
|
//
|
||||||
|
// Returns: an interned slice, either pre-existing/matched or newly created.
|
||||||
|
template <typename SliceArgs> |
||||||
|
static InternedSliceRefcount* FindOrCreateInternedSlice(uint32_t hash, |
||||||
|
const SliceArgs& args) { |
||||||
|
slice_shard* shard = &g_shards[SHARD_IDX(hash)]; |
||||||
|
grpc_core::MutexLock lock(&shard->mu); |
||||||
|
const size_t idx = TABLE_IDX(hash, shard->capacity); |
||||||
|
InternedSliceRefcount* s = MatchInternedSliceLocked(hash, idx, args); |
||||||
|
if (s == nullptr) { |
||||||
|
s = InternNewStringLocked(shard, idx, hash, args); |
||||||
|
} |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_core::ManagedMemorySlice::ManagedMemorySlice(const char* string) |
||||||
|
: grpc_core::ManagedMemorySlice::ManagedMemorySlice(string, |
||||||
|
strlen(string)) {} |
||||||
|
|
||||||
|
grpc_core::ManagedMemorySlice::ManagedMemorySlice(const char* buf, size_t len) { |
||||||
|
GPR_TIMER_SCOPE("grpc_slice_intern", 0); |
||||||
|
const uint32_t hash = gpr_murmur_hash3(buf, len, g_hash_seed); |
||||||
|
*this = grpc_core::InternedSlice(FindOrCreateInternedSlice( |
||||||
|
hash, std::pair<const char*, size_t>(buf, len))); |
||||||
|
} |
||||||
|
|
||||||
|
grpc_core::ManagedMemorySlice::ManagedMemorySlice(const grpc_slice* slice_ptr) { |
||||||
|
GPR_TIMER_SCOPE("grpc_slice_intern", 0); |
||||||
|
const grpc_slice& slice = *slice_ptr; |
||||||
|
const uint32_t hash = grpc_slice_hash_internal(slice); |
||||||
|
*this = grpc_core::InternedSlice(FindOrCreateInternedSlice(hash, slice)); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_test_only_set_slice_hash_seed(uint32_t seed) { |
||||||
|
grpc_core::g_hash_seed = seed; |
||||||
|
grpc_core::g_forced_hash_seed = true; |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_slice_intern_init(void) { |
||||||
|
if (!grpc_core::g_forced_hash_seed) { |
||||||
|
grpc_core::g_hash_seed = |
||||||
|
static_cast<uint32_t>(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); |
||||||
|
} |
||||||
|
g_shards = new slice_shard[SHARD_COUNT]; |
||||||
|
for (size_t i = 0; i < SHARD_COUNT; i++) { |
||||||
|
slice_shard* shard = &g_shards[i]; |
||||||
|
shard->count = 0; |
||||||
|
shard->capacity = INITIAL_SHARD_CAPACITY; |
||||||
|
shard->strs = static_cast<InternedSliceRefcount**>( |
||||||
|
gpr_zalloc(sizeof(*shard->strs) * shard->capacity)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_slice_intern_shutdown(void) { |
||||||
|
for (size_t i = 0; i < SHARD_COUNT; i++) { |
||||||
|
slice_shard* shard = &g_shards[i]; |
||||||
|
/* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ |
||||||
|
if (shard->count != 0) { |
||||||
|
gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked", |
||||||
|
shard->count); |
||||||
|
for (size_t j = 0; j < shard->capacity; j++) { |
||||||
|
for (InternedSliceRefcount* s = shard->strs[j]; s; s = s->bucket_next) { |
||||||
|
char* text = grpc_dump_slice(grpc_core::InternedSlice(s), |
||||||
|
GPR_DUMP_HEX | GPR_DUMP_ASCII); |
||||||
|
gpr_log(GPR_DEBUG, "LEAKED: %s", text); |
||||||
|
gpr_free(text); |
||||||
|
} |
||||||
|
} |
||||||
|
if (grpc_iomgr_abort_on_leaks()) { |
||||||
|
abort(); |
||||||
|
} |
||||||
|
} |
||||||
|
gpr_free(shard->strs); |
||||||
|
} |
||||||
|
delete[] g_shards; |
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2019 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_UTILS_H |
||||||
|
#define GRPC_CORE_LIB_SLICE_SLICE_UTILS_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <cstring> |
||||||
|
|
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
|
||||||
|
#include <grpc/slice.h> |
||||||
|
|
||||||
|
#include "src/core/lib/gpr/murmur_hash.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
extern uint32_t g_hash_seed; |
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
// When we compare two slices, and we know the latter is not inlined, we can
|
||||||
|
// short circuit our comparison operator. We specifically use differs()
|
||||||
|
// semantics instead of equals() semantics due to more favourable code
|
||||||
|
// generation when using differs(). Specifically, we may use the output of
|
||||||
|
// grpc_slice_differs_refcounted for control flow. If we use differs()
|
||||||
|
// semantics, we end with a tailcall to memcmp(). If we use equals() semantics,
|
||||||
|
// we need to invert the result that memcmp provides us, which costs several
|
||||||
|
// instructions to do so. If we're using the result for control flow (i.e.
|
||||||
|
// branching based on the output) then we're just performing the extra
|
||||||
|
// operations to invert the result pointlessly. Concretely, we save 6 ops on
|
||||||
|
// x86-64/clang with differs().
|
||||||
|
int grpc_slice_differs_refcounted(const grpc_slice& a, |
||||||
|
const grpc_slice& b_not_inline); |
||||||
|
|
||||||
|
// When we compare two slices, and we *know* that one of them is static or
|
||||||
|
// interned, we can short circuit our slice equality function. The second slice
|
||||||
|
// here must be static or interned; slice a can be any slice, inlined or not.
|
||||||
|
inline bool grpc_slice_eq_static_interned(const grpc_slice& a, |
||||||
|
const grpc_slice& b_static_interned) { |
||||||
|
if (a.refcount == b_static_interned.refcount) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return !grpc_slice_differs_refcounted(a, b_static_interned); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO(arjunroy): These type declarations ought to be in
|
||||||
|
// src/core/lib/slice/slice_internal.h instead; they are here due to a circular
|
||||||
|
// header depedency between slice_internal.h and
|
||||||
|
// src/core/lib/transport/metadata.h. We need to fix this circular reference and
|
||||||
|
// when we do, move these type declarations.
|
||||||
|
//
|
||||||
|
// Internal slice type declarations.
|
||||||
|
// Externally, a grpc_slice is a grpc_slice is a grpc_slice.
|
||||||
|
// Internally, we may have heap allocated slices, static slices, interned
|
||||||
|
// slices, and inlined slices. If we know the specific type of slice
|
||||||
|
// we're dealing with, we can save cycles (e.g. fast-paths when we know we don't
|
||||||
|
// need to take a reference on a slice). Rather than introducing new methods
|
||||||
|
// ad-hoc in these cases, we rely on type-system backed overloads to keep
|
||||||
|
// internal APIs clean.
|
||||||
|
//
|
||||||
|
// For each overload, the definition and layout of the underlying slice does not
|
||||||
|
// change; this is purely type-system information.
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
// There are two main types of slices: those that have their memory
|
||||||
|
// managed by the slice library and those that do not.
|
||||||
|
//
|
||||||
|
// The following types of slices are not managed:
|
||||||
|
// - inlined slices (i.e., refcount is null)
|
||||||
|
// - slices that have a custom refcount type (i.e., not STATIC or INTERNED)
|
||||||
|
// - slices where the memory is managed by some external agent. The slice is not
|
||||||
|
// ref-counted by grpc, and the programmer is responsible for ensuring the
|
||||||
|
// data is valid for the duration of the period that grpc may access it.
|
||||||
|
//
|
||||||
|
// The following types of slices are managed:
|
||||||
|
// - static metadata slices (i.e., refcount type is STATIC)
|
||||||
|
// - interned slices (i.e., refcount type is INTERNED)
|
||||||
|
//
|
||||||
|
// This categorization is reflected in the following hierarchy:
|
||||||
|
//
|
||||||
|
// - grpc_slice
|
||||||
|
// > - UnmanagedMemorySlice
|
||||||
|
// > - ExternallyManagedSlice
|
||||||
|
// - ManagedMemorySlice
|
||||||
|
// > - InternedSlice
|
||||||
|
// - StaticMetadataSlice
|
||||||
|
//
|
||||||
|
struct ManagedMemorySlice : public grpc_slice { |
||||||
|
ManagedMemorySlice() { |
||||||
|
refcount = nullptr; |
||||||
|
data.refcounted.bytes = nullptr; |
||||||
|
data.refcounted.length = 0; |
||||||
|
} |
||||||
|
explicit ManagedMemorySlice(const char* string); |
||||||
|
ManagedMemorySlice(const char* buf, size_t len); |
||||||
|
explicit ManagedMemorySlice(const grpc_slice* slice); |
||||||
|
bool operator==(const grpc_slice& other) const { |
||||||
|
if (refcount == other.refcount) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return !grpc_slice_differs_refcounted(other, *this); |
||||||
|
} |
||||||
|
bool operator!=(const grpc_slice& other) const { return !(*this == other); } |
||||||
|
bool operator==(std::pair<const char*, size_t> buflen) const { |
||||||
|
return data.refcounted.length == buflen.second && buflen.first != nullptr && |
||||||
|
memcmp(buflen.first, data.refcounted.bytes, buflen.second) == 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
struct UnmanagedMemorySlice : public grpc_slice { |
||||||
|
// TODO(arjunroy): Can we use a default=false param instead of this enum?
|
||||||
|
enum class ForceHeapAllocation {}; |
||||||
|
UnmanagedMemorySlice() { |
||||||
|
refcount = nullptr; |
||||||
|
data.inlined.length = 0; |
||||||
|
} |
||||||
|
explicit UnmanagedMemorySlice(const char* source); |
||||||
|
UnmanagedMemorySlice(const char* source, size_t length); |
||||||
|
// The first constructor creates a slice that may be heap allocated, or
|
||||||
|
// inlined in the slice structure if length is small enough
|
||||||
|
// (< GRPC_SLICE_INLINED_SIZE). The second constructor forces heap alloc.
|
||||||
|
explicit UnmanagedMemorySlice(size_t length); |
||||||
|
explicit UnmanagedMemorySlice(size_t length, const ForceHeapAllocation&) { |
||||||
|
HeapInit(length); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
void HeapInit(size_t length); |
||||||
|
}; |
||||||
|
|
||||||
|
extern grpc_slice_refcount kNoopRefcount; |
||||||
|
|
||||||
|
struct ExternallyManagedSlice : public UnmanagedMemorySlice { |
||||||
|
ExternallyManagedSlice() |
||||||
|
: ExternallyManagedSlice(&kNoopRefcount, 0, nullptr) {} |
||||||
|
explicit ExternallyManagedSlice(const char* s) |
||||||
|
: ExternallyManagedSlice(s, strlen(s)) {} |
||||||
|
ExternallyManagedSlice(const void* s, size_t len) |
||||||
|
: ExternallyManagedSlice( |
||||||
|
&kNoopRefcount, len, |
||||||
|
reinterpret_cast<uint8_t*>(const_cast<void*>(s))) {} |
||||||
|
ExternallyManagedSlice(grpc_slice_refcount* ref, size_t length, |
||||||
|
uint8_t* bytes) { |
||||||
|
refcount = ref; |
||||||
|
data.refcounted.length = length; |
||||||
|
data.refcounted.bytes = bytes; |
||||||
|
} |
||||||
|
bool operator==(const grpc_slice& other) const { |
||||||
|
return data.refcounted.length == GRPC_SLICE_LENGTH(other) && |
||||||
|
memcmp(data.refcounted.bytes, GRPC_SLICE_START_PTR(other), |
||||||
|
data.refcounted.length) == 0; |
||||||
|
} |
||||||
|
bool operator!=(const grpc_slice& other) const { return !(*this == other); } |
||||||
|
uint32_t Hash() { |
||||||
|
return gpr_murmur_hash3(data.refcounted.bytes, data.refcounted.length, |
||||||
|
g_hash_seed); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct StaticMetadataSlice : public ManagedMemorySlice { |
||||||
|
StaticMetadataSlice(grpc_slice_refcount* ref, size_t length, |
||||||
|
const uint8_t* bytes) { |
||||||
|
refcount = ref; |
||||||
|
data.refcounted.length = length; |
||||||
|
// NB: grpc_slice may or may not point to a static slice, but we are
|
||||||
|
// definitely pointing to static data here. Since we are not changing
|
||||||
|
// the underlying C-type, we need a const_cast here.
|
||||||
|
data.refcounted.bytes = const_cast<uint8_t*>(bytes); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct InternedSliceRefcount; |
||||||
|
struct InternedSlice : public ManagedMemorySlice { |
||||||
|
explicit InternedSlice(InternedSliceRefcount* s); |
||||||
|
}; |
||||||
|
|
||||||
|
// Converts grpc_slice to absl::string_view.
|
||||||
|
inline absl::string_view StringViewFromSlice(const grpc_slice& slice) { |
||||||
|
return absl::string_view( |
||||||
|
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(slice)), |
||||||
|
GRPC_SLICE_LENGTH(slice)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif /* GRPC_CORE_LIB_SLICE_SLICE_UTILS_H */ |
@ -0,0 +1,67 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015 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 <inttypes.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/slice.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/lib/gprpp/memory.h" |
||||||
|
#include "src/core/lib/slice/slice_internal.h" |
||||||
|
#include "test/core/util/test_config.h" |
||||||
|
|
||||||
|
#define LOG_TEST_NAME(x) gpr_log(GPR_INFO, "%s", x); |
||||||
|
|
||||||
|
static void test_slice_interning(void) { |
||||||
|
LOG_TEST_NAME("test_slice_interning"); |
||||||
|
|
||||||
|
grpc_init(); |
||||||
|
grpc_slice src1 = grpc_slice_from_copied_string("hello123456789123456789"); |
||||||
|
grpc_slice src2 = grpc_slice_from_copied_string("hello123456789123456789"); |
||||||
|
|
||||||
|
// Explicitly checking that the slices are at different addresses prevents
|
||||||
|
// failure with windows opt 64bit build.
|
||||||
|
// See https://github.com/grpc/grpc/issues/20519
|
||||||
|
GPR_ASSERT(&src1 != &src2); |
||||||
|
GPR_ASSERT(GRPC_SLICE_START_PTR(src1) != GRPC_SLICE_START_PTR(src2)); |
||||||
|
|
||||||
|
grpc_slice interned1 = grpc_slice_intern(src1); |
||||||
|
grpc_slice interned2 = grpc_slice_intern(src2); |
||||||
|
GPR_ASSERT(GRPC_SLICE_START_PTR(interned1) == |
||||||
|
GRPC_SLICE_START_PTR(interned2)); |
||||||
|
GPR_ASSERT(GRPC_SLICE_START_PTR(interned1) != GRPC_SLICE_START_PTR(src1)); |
||||||
|
GPR_ASSERT(GRPC_SLICE_START_PTR(interned2) != GRPC_SLICE_START_PTR(src2)); |
||||||
|
grpc_slice_unref(src1); |
||||||
|
grpc_slice_unref(src2); |
||||||
|
grpc_slice_unref(interned1); |
||||||
|
grpc_slice_unref(interned2); |
||||||
|
grpc_shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
grpc::testing::TestEnvironment env(argc, argv); |
||||||
|
grpc_init(); |
||||||
|
test_slice_interning(); |
||||||
|
grpc_shutdown(); |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue