GRPC Core  18.0.0
slice_utils.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2019 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef GRPC_CORE_LIB_SLICE_SLICE_UTILS_H
20 #define GRPC_CORE_LIB_SLICE_SLICE_UTILS_H
21 
23 
24 #include <cstring>
25 
26 #include "absl/strings/string_view.h"
27 
28 #include <grpc/slice.h>
29 
31 
32 namespace grpc_core {
33 extern uint32_t g_hash_seed;
34 } // namespace grpc_core
35 
36 // When we compare two slices, and we know the latter is not inlined, we can
37 // short circuit our comparison operator. We specifically use differs()
38 // semantics instead of equals() semantics due to more favourable code
39 // generation when using differs(). Specifically, we may use the output of
40 // grpc_slice_differs_refcounted for control flow. If we use differs()
41 // semantics, we end with a tailcall to memcmp(). If we use equals() semantics,
42 // we need to invert the result that memcmp provides us, which costs several
43 // instructions to do so. If we're using the result for control flow (i.e.
44 // branching based on the output) then we're just performing the extra
45 // operations to invert the result pointlessly. Concretely, we save 6 ops on
46 // x86-64/clang with differs().
48  const grpc_slice& b_not_inline);
49 
50 // When we compare two slices, and we *know* that one of them is static or
51 // interned, we can short circuit our slice equality function. The second slice
52 // here must be static or interned; slice a can be any slice, inlined or not.
54  const grpc_slice& b_static_interned) {
55  if (a.refcount == b_static_interned.refcount) {
56  return true;
57  }
58  return !grpc_slice_differs_refcounted(a, b_static_interned);
59 }
60 
61 // TODO(arjunroy): These type declarations ought to be in
62 // src/core/lib/slice/slice_internal.h instead; they are here due to a circular
63 // header depedency between slice_internal.h and
64 // src/core/lib/transport/metadata.h. We need to fix this circular reference and
65 // when we do, move these type declarations.
66 //
67 // Internal slice type declarations.
68 // Externally, a grpc_slice is a grpc_slice is a grpc_slice.
69 // Internally, we may have heap allocated slices, static slices, interned
70 // slices, and inlined slices. If we know the specific type of slice
71 // we're dealing with, we can save cycles (e.g. fast-paths when we know we don't
72 // need to take a reference on a slice). Rather than introducing new methods
73 // ad-hoc in these cases, we rely on type-system backed overloads to keep
74 // internal APIs clean.
75 //
76 // For each overload, the definition and layout of the underlying slice does not
77 // change; this is purely type-system information.
78 namespace grpc_core {
79 
80 // There are two main types of slices: those that have their memory
81 // managed by the slice library and those that do not.
82 //
83 // The following types of slices are not managed:
84 // - inlined slices (i.e., refcount is null)
85 // - slices that have a custom refcount type (i.e., not STATIC or INTERNED)
86 // - slices where the memory is managed by some external agent. The slice is not
87 // ref-counted by grpc, and the programmer is responsible for ensuring the
88 // data is valid for the duration of the period that grpc may access it.
89 //
90 // The following types of slices are managed:
91 // - static metadata slices (i.e., refcount type is STATIC)
92 // - interned slices (i.e., refcount type is INTERNED)
93 //
94 // This categorization is reflected in the following hierarchy:
95 //
96 // - grpc_slice
97 // > - UnmanagedMemorySlice
98 // > - ExternallyManagedSlice
99 // - ManagedMemorySlice
100 // > - InternedSlice
101 // - StaticMetadataSlice
102 //
105  refcount = nullptr;
106  data.refcounted.bytes = nullptr;
107  data.refcounted.length = 0;
108  }
109  explicit ManagedMemorySlice(const char* string);
110  ManagedMemorySlice(const char* buf, size_t len);
111  explicit ManagedMemorySlice(const grpc_slice* slice);
112  bool operator==(const grpc_slice& other) const {
113  if (refcount == other.refcount) {
114  return true;
115  }
116  return !grpc_slice_differs_refcounted(other, *this);
117  }
118  bool operator!=(const grpc_slice& other) const { return !(*this == other); }
119  bool operator==(std::pair<const char*, size_t> buflen) const {
120  return data.refcounted.length == buflen.second && buflen.first != nullptr &&
121  memcmp(buflen.first, data.refcounted.bytes, buflen.second) == 0;
122  }
123 };
125  // TODO(arjunroy): Can we use a default=false param instead of this enum?
126  enum class ForceHeapAllocation {};
128  refcount = nullptr;
129  data.inlined.length = 0;
130  }
131  explicit UnmanagedMemorySlice(const char* source);
132  UnmanagedMemorySlice(const char* source, size_t length);
133  // The first constructor creates a slice that may be heap allocated, or
134  // inlined in the slice structure if length is small enough
135  // (< GRPC_SLICE_INLINED_SIZE). The second constructor forces heap alloc.
136  explicit UnmanagedMemorySlice(size_t length);
137  explicit UnmanagedMemorySlice(size_t length, const ForceHeapAllocation&) {
138  HeapInit(length);
139  }
140 
141  private:
142  void HeapInit(size_t length);
143 };
144 
146 
149  : ExternallyManagedSlice(&kNoopRefcount, 0, nullptr) {}
150  explicit ExternallyManagedSlice(const char* s)
151  : ExternallyManagedSlice(s, strlen(s)) {}
152  ExternallyManagedSlice(const void* s, size_t len)
154  &kNoopRefcount, len,
155  reinterpret_cast<uint8_t*>(const_cast<void*>(s))) {}
157  uint8_t* bytes) {
158  refcount = ref;
159  data.refcounted.length = length;
160  data.refcounted.bytes = bytes;
161  }
162  bool operator==(const grpc_slice& other) const {
163  return data.refcounted.length == GRPC_SLICE_LENGTH(other) &&
164  memcmp(data.refcounted.bytes, GRPC_SLICE_START_PTR(other),
165  data.refcounted.length) == 0;
166  }
167  bool operator!=(const grpc_slice& other) const { return !(*this == other); }
168  uint32_t Hash() {
170  g_hash_seed);
171  }
172 };
173 
176  const uint8_t* bytes) {
177  refcount = ref;
178  data.refcounted.length = length;
179  // NB: grpc_slice may or may not point to a static slice, but we are
180  // definitely pointing to static data here. Since we are not changing
181  // the underlying C-type, we need a const_cast here.
182  data.refcounted.bytes = const_cast<uint8_t*>(bytes);
183  }
184 };
185 
186 struct InternedSliceRefcount;
189 };
190 
191 // Converts grpc_slice to absl::string_view.
192 inline absl::string_view StringViewFromSlice(const grpc_slice& slice) {
193  return absl::string_view(
194  reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(slice)),
196 }
197 
198 } // namespace grpc_core
199 
200 #endif /* GRPC_CORE_LIB_SLICE_SLICE_UTILS_H */
#define GRPC_SLICE_START_PTR(slice)
Definition: slice.h:96
#define GRPC_SLICE_LENGTH(slice)
Definition: slice.h:99
uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed)
Definition: murmur_hash.cc:34
Round Robin Policy.
Definition: backend_metric.cc:26
uint32_t g_hash_seed
Definition: slice_intern.cc:67
absl::string_view StringViewFromSlice(const grpc_slice &slice)
Definition: slice_utils.h:192
grpc_slice_refcount kNoopRefcount(grpc_slice_refcount::Type::NOP)
Definition: slice_internal.h:187
grpc_slice slice
Definition: server.cc:461
int grpc_slice_differs_refcounted(const grpc_slice &a, const grpc_slice &b_not_inline)
Definition: slice.cc:503
bool grpc_slice_eq_static_interned(const grpc_slice &a, const grpc_slice &b_static_interned)
Definition: slice_utils.h:53
Definition: slice_utils.h:147
bool operator==(const grpc_slice &other) const
Definition: slice_utils.h:162
ExternallyManagedSlice(grpc_slice_refcount *ref, size_t length, uint8_t *bytes)
Definition: slice_utils.h:156
ExternallyManagedSlice(const char *s)
Definition: slice_utils.h:150
ExternallyManagedSlice(const void *s, size_t len)
Definition: slice_utils.h:152
bool operator!=(const grpc_slice &other) const
Definition: slice_utils.h:167
uint32_t Hash()
Definition: slice_utils.h:168
ExternallyManagedSlice()
Definition: slice_utils.h:148
Definition: slice_utils.h:187
InternedSlice(InternedSliceRefcount *s)
Definition: slice_intern.cc:109
Definition: slice_internal.h:189
Definition: slice_utils.h:103
ManagedMemorySlice()
Definition: slice_utils.h:104
bool operator!=(const grpc_slice &other) const
Definition: slice_utils.h:118
bool operator==(std::pair< const char *, size_t > buflen) const
Definition: slice_utils.h:119
bool operator==(const grpc_slice &other) const
Definition: slice_utils.h:112
Definition: slice_utils.h:174
StaticMetadataSlice(grpc_slice_refcount *ref, size_t length, const uint8_t *bytes)
Definition: slice_utils.h:175
Definition: slice_utils.h:124
ForceHeapAllocation
Definition: slice_utils.h:126
UnmanagedMemorySlice(size_t length, const ForceHeapAllocation &)
Definition: slice_utils.h:137
UnmanagedMemorySlice()
Definition: slice_utils.h:127
Definition: slice_internal.h:100
A grpc_slice s, if initialized, represents the byte range s.bytes[0..s.length-1].
Definition: slice.h:60
struct grpc_slice_refcount * refcount
Definition: slice.h:61
union grpc_slice::grpc_slice_data data
struct grpc_slice::grpc_slice_data::grpc_slice_refcounted refcounted
struct grpc_slice::grpc_slice_data::grpc_slice_inlined inlined