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
22 #include <grpc/support/port_platform.h>
23
24 #include <cstring>
25
26 #include "absl/strings/string_view.h"
27
28 #include <grpc/slice.h>
29
30 #include "src/core/lib/gpr/murmur_hash.h"
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().
47 int grpc_slice_differs_refcounted(const grpc_slice& a,
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.
grpc_slice_eq_static_interned(const grpc_slice & a,const grpc_slice & b_static_interned)53 inline bool grpc_slice_eq_static_interned(const grpc_slice& a,
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 //
103 struct ManagedMemorySlice : public grpc_slice {
ManagedMemorySliceManagedMemorySlice104 ManagedMemorySlice() {
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 };
124 struct UnmanagedMemorySlice : public grpc_slice {
125 // TODO(arjunroy): Can we use a default=false param instead of this enum?
126 enum class ForceHeapAllocation {};
UnmanagedMemorySliceUnmanagedMemorySlice127 UnmanagedMemorySlice() {
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);
UnmanagedMemorySliceUnmanagedMemorySlice137 explicit UnmanagedMemorySlice(size_t length, const ForceHeapAllocation&) {
138 HeapInit(length);
139 }
140
141 private:
142 void HeapInit(size_t length);
143 };
144
145 extern grpc_slice_refcount kNoopRefcount;
146
147 struct ExternallyManagedSlice : public UnmanagedMemorySlice {
ExternallyManagedSliceExternallyManagedSlice148 ExternallyManagedSlice()
149 : ExternallyManagedSlice(&kNoopRefcount, 0, nullptr) {}
ExternallyManagedSliceExternallyManagedSlice150 explicit ExternallyManagedSlice(const char* s)
151 : ExternallyManagedSlice(s, strlen(s)) {}
ExternallyManagedSliceExternallyManagedSlice152 ExternallyManagedSlice(const void* s, size_t len)
153 : ExternallyManagedSlice(
154 &kNoopRefcount, len,
155 reinterpret_cast<uint8_t*>(const_cast<void*>(s))) {}
ExternallyManagedSliceExternallyManagedSlice156 ExternallyManagedSlice(grpc_slice_refcount* ref, size_t length,
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); }
HashExternallyManagedSlice168 uint32_t Hash() {
169 return gpr_murmur_hash3(data.refcounted.bytes, data.refcounted.length,
170 g_hash_seed);
171 }
172 };
173
174 struct StaticMetadataSlice : public ManagedMemorySlice {
StaticMetadataSliceStaticMetadataSlice175 StaticMetadataSlice(grpc_slice_refcount* ref, size_t length,
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;
187 struct InternedSlice : public ManagedMemorySlice {
188 explicit InternedSlice(InternedSliceRefcount* s);
189 };
190
191 // Converts grpc_slice to absl::string_view.
StringViewFromSlice(const grpc_slice & slice)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)),
195 GRPC_SLICE_LENGTH(slice));
196 }
197
198 } // namespace grpc_core
199
200 #endif /* GRPC_CORE_LIB_SLICE_SLICE_UTILS_H */
201