GRPC C++  1.26.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 <grpc/slice.h>
27 
29 
30 namespace grpc_core {
31 extern uint32_t g_hash_seed;
32 } // namespace grpc_core
33 
34 // When we compare two slices, and we know the latter is not inlined, we can
35 // short circuit our comparison operator. We specifically use differs()
36 // semantics instead of equals() semantics due to more favourable code
37 // generation when using differs(). Specifically, we may use the output of
38 // grpc_slice_differs_refcounted for control flow. If we use differs()
39 // semantics, we end with a tailcall to memcmp(). If we use equals() semantics,
40 // we need to invert the result that memcmp provides us, which costs several
41 // instructions to do so. If we're using the result for control flow (i.e.
42 // branching based on the output) then we're just performing the extra
43 // operations to invert the result pointlessly. Concretely, we save 6 ops on
44 // x86-64/clang with differs().
46  const grpc_slice& b_not_inline);
47 
48 // When we compare two slices, and we *know* that one of them is static or
49 // interned, we can short circuit our slice equality function. The second slice
50 // here must be static or interned; slice a can be any slice, inlined or not.
52  const grpc_slice& b_static_interned) {
53  if (a.refcount == b_static_interned.refcount) {
54  return true;
55  }
56  return !grpc_slice_differs_refcounted(a, b_static_interned);
57 }
58 
59 // TODO(arjunroy): These type declarations ought to be in
60 // src/core/lib/slice/slice_internal.h instead; they are here due to a circular
61 // header depedency between slice_internal.h and
62 // src/core/lib/transport/metadata.h. We need to fix this circular reference and
63 // when we do, move these type declarations.
64 //
65 // Internal slice type declarations.
66 // Externally, a grpc_slice is a grpc_slice is a grpc_slice.
67 // Internally, we may have heap allocated slices, static slices, interned
68 // slices, and inlined slices. If we know the specific type of slice
69 // we're dealing with, we can save cycles (e.g. fast-paths when we know we don't
70 // need to take a reference on a slice). Rather than introducing new methods
71 // ad-hoc in these cases, we rely on type-system backed overloads to keep
72 // internal APIs clean.
73 //
74 // For each overload, the definition and layout of the underlying slice does not
75 // change; this is purely type-system information.
76 namespace grpc_core {
77 
78 // There are two main types of slices: those that have their memory
79 // managed by the slice library and those that do not.
80 //
81 // The following types of slices are not managed:
82 // - inlined slices (i.e., refcount is null)
83 // - slices that have a custom refcount type (i.e., not STATIC or INTERNED)
84 // - slices where the memory is managed by some external agent. The slice is not
85 // ref-counted by grpc, and the programmer is responsible for ensuring the
86 // data is valid for the duration of the period that grpc may access it.
87 //
88 // The following types of slices are managed:
89 // - static metadata slices (i.e., refcount type is STATIC)
90 // - interned slices (i.e., refcount type is INTERNED)
91 //
92 // This categorization is reflected in the following hierarchy:
93 //
94 // - grpc_slice
95 // > - UnmanagedMemorySlice
96 // > - ExternallyManagedSlice
97 // - ManagedMemorySlice
98 // > - InternedSlice
99 // - StaticMetadataSlice
100 //
103  refcount = nullptr;
104  data.refcounted.bytes = nullptr;
105  data.refcounted.length = 0;
106  }
107  explicit ManagedMemorySlice(const char* string);
108  ManagedMemorySlice(const char* buf, size_t len);
109  explicit ManagedMemorySlice(const grpc_slice* slice);
110  bool operator==(const grpc_slice& other) const {
111  if (refcount == other.refcount) {
112  return true;
113  }
114  return !grpc_slice_differs_refcounted(other, *this);
115  }
116  bool operator!=(const grpc_slice& other) const { return !(*this == other); }
117  bool operator==(std::pair<const char*, size_t> buflen) const {
118  return data.refcounted.length == buflen.second && buflen.first != nullptr &&
119  memcmp(buflen.first, data.refcounted.bytes, buflen.second) == 0;
120  }
121 };
123  // TODO(arjunroy): Can we use a default=false param instead of this enum?
124  enum class ForceHeapAllocation {};
126  refcount = nullptr;
127  data.inlined.length = 0;
128  }
129  explicit UnmanagedMemorySlice(const char* source);
130  UnmanagedMemorySlice(const char* source, size_t length);
131  // The first constructor creates a slice that may be heap allocated, or
132  // inlined in the slice structure if length is small enough
133  // (< GRPC_SLICE_INLINED_SIZE). The second constructor forces heap alloc.
134  explicit UnmanagedMemorySlice(size_t length);
135  explicit UnmanagedMemorySlice(size_t length, const ForceHeapAllocation&) {
136  HeapInit(length);
137  }
138 
139  private:
140  void HeapInit(size_t length);
141 };
142 
144 
147  : ExternallyManagedSlice(&kNoopRefcount, 0, nullptr) {}
148  explicit ExternallyManagedSlice(const char* s)
149  : ExternallyManagedSlice(s, strlen(s)) {}
150  ExternallyManagedSlice(const void* s, size_t len)
152  &kNoopRefcount, len,
153  reinterpret_cast<uint8_t*>(const_cast<void*>(s))) {}
155  uint8_t* bytes) {
156  refcount = ref;
157  data.refcounted.length = length;
158  data.refcounted.bytes = bytes;
159  }
160  bool operator==(const grpc_slice& other) const {
161  return data.refcounted.length == GRPC_SLICE_LENGTH(other) &&
162  memcmp(data.refcounted.bytes, GRPC_SLICE_START_PTR(other),
163  data.refcounted.length) == 0;
164  }
165  bool operator!=(const grpc_slice& other) const { return !(*this == other); }
166  uint32_t Hash() {
168  g_hash_seed);
169  }
170 };
171 
174  const uint8_t* bytes) {
175  refcount = ref;
176  data.refcounted.length = length;
177  // NB: grpc_slice may or may not point to a static slice, but we are
178  // definitely pointing to static data here. Since we are not changing
179  // the underlying C-type, we need a const_cast here.
180  data.refcounted.bytes = const_cast<uint8_t*>(bytes);
181  }
182 };
183 
184 struct InternedSliceRefcount;
187 };
188 
189 } // namespace grpc_core
190 
191 #endif /* GRPC_CORE_LIB_SLICE_SLICE_UTILS_H */
Definition: slice_internal.h:100
Definition: slice_utils.h:101
struct grpc_slice::grpc_slice_data::grpc_slice_inlined inlined
uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed)
Definition: slice_utils.h:172
grpc_slice_refcount kNoopRefcount
Definition: slice_utils.h:143
UnmanagedMemorySlice(size_t length, const ForceHeapAllocation &)
Definition: slice_utils.h:135
Definition: slice_utils.h:185
bool operator!=(const grpc_slice &other) const
Definition: slice_utils.h:116
A grpc_slice s, if initialized, represents the byte range s.bytes[0..s.length-1]. ...
Definition: slice.h:60
ExternallyManagedSlice(grpc_slice_refcount *ref, size_t length, uint8_t *bytes)
Definition: slice_utils.h:154
InternedSlice(InternedSliceRefcount *s)
ManagedMemorySlice()
Definition: slice_utils.h:102
#define GRPC_SLICE_START_PTR(slice)
Definition: slice.h:96
bool grpc_slice_eq_static_interned(const grpc_slice &a, const grpc_slice &b_static_interned)
Definition: slice_utils.h:51
struct grpc_slice::grpc_slice_data::grpc_slice_refcounted refcounted
int grpc_slice_differs_refcounted(const grpc_slice &a, const grpc_slice &b_not_inline)
Internal thread interface.
Definition: backoff.h:26
ExternallyManagedSlice()
Definition: slice_utils.h:146
ExternallyManagedSlice(const char *s)
Definition: slice_utils.h:148
StaticMetadataSlice(grpc_slice_refcount *ref, size_t length, const uint8_t *bytes)
Definition: slice_utils.h:173
struct grpc_slice_refcount * refcount
Definition: slice.h:61
uint32_t g_hash_seed
bool operator==(const grpc_slice &other) const
Definition: slice_utils.h:110
ExternallyManagedSlice(const void *s, size_t len)
Definition: slice_utils.h:150
Definition: slice_utils.h:122
union grpc_slice::grpc_slice_data data
bool operator!=(const grpc_slice &other) const
Definition: slice_utils.h:165
Definition: slice_utils.h:145
uint32_t Hash()
Definition: slice_utils.h:166
bool operator==(const grpc_slice &other) const
Definition: slice_utils.h:160
Definition: slice_internal.h:189
#define GRPC_SLICE_LENGTH(slice)
Definition: slice.h:99
UnmanagedMemorySlice()
Definition: slice_utils.h:125
bool operator==(std::pair< const char *, size_t > buflen) const
Definition: slice_utils.h:117
ForceHeapAllocation
Definition: slice_utils.h:124