• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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