/src/LPM/external.protobuf/include/absl/container/internal/layout.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 The Abseil Authors. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | // MOTIVATION AND TUTORIAL |
16 | | // |
17 | | // If you want to put in a single heap allocation N doubles followed by M ints, |
18 | | // it's easy if N and M are known at compile time. |
19 | | // |
20 | | // struct S { |
21 | | // double a[N]; |
22 | | // int b[M]; |
23 | | // }; |
24 | | // |
25 | | // S* p = new S; |
26 | | // |
27 | | // But what if N and M are known only in run time? Class template Layout to the |
28 | | // rescue! It's a portable generalization of the technique known as struct hack. |
29 | | // |
30 | | // // This object will tell us everything we need to know about the memory |
31 | | // // layout of double[N] followed by int[M]. It's structurally identical to |
32 | | // // size_t[2] that stores N and M. It's very cheap to create. |
33 | | // const Layout<double, int> layout(N, M); |
34 | | // |
35 | | // // Allocate enough memory for both arrays. `AllocSize()` tells us how much |
36 | | // // memory is needed. We are free to use any allocation function we want as |
37 | | // // long as it returns aligned memory. |
38 | | // std::unique_ptr<unsigned char[]> p(new unsigned char[layout.AllocSize()]); |
39 | | // |
40 | | // // Obtain the pointer to the array of doubles. |
41 | | // // Equivalent to `reinterpret_cast<double*>(p.get())`. |
42 | | // // |
43 | | // // We could have written layout.Pointer<0>(p) instead. If all the types are |
44 | | // // unique you can use either form, but if some types are repeated you must |
45 | | // // use the index form. |
46 | | // double* a = layout.Pointer<double>(p.get()); |
47 | | // |
48 | | // // Obtain the pointer to the array of ints. |
49 | | // // Equivalent to `reinterpret_cast<int*>(p.get() + N * 8)`. |
50 | | // int* b = layout.Pointer<int>(p); |
51 | | // |
52 | | // If we are unable to specify sizes of all fields, we can pass as many sizes as |
53 | | // we can to `Partial()`. In return, it'll allow us to access the fields whose |
54 | | // locations and sizes can be computed from the provided information. |
55 | | // `Partial()` comes in handy when the array sizes are embedded into the |
56 | | // allocation. |
57 | | // |
58 | | // // size_t[0] containing N, size_t[1] containing M, double[N], int[M]. |
59 | | // using L = Layout<size_t, size_t, double, int>; |
60 | | // |
61 | | // unsigned char* Allocate(size_t n, size_t m) { |
62 | | // const L layout(1, 1, n, m); |
63 | | // unsigned char* p = new unsigned char[layout.AllocSize()]; |
64 | | // *layout.Pointer<0>(p) = n; |
65 | | // *layout.Pointer<1>(p) = m; |
66 | | // return p; |
67 | | // } |
68 | | // |
69 | | // void Use(unsigned char* p) { |
70 | | // // First, extract N and M. |
71 | | // // Specify that the first array has only one element. Using `prefix` we |
72 | | // // can access the first two arrays but not more. |
73 | | // constexpr auto prefix = L::Partial(1); |
74 | | // size_t n = *prefix.Pointer<0>(p); |
75 | | // size_t m = *prefix.Pointer<1>(p); |
76 | | // |
77 | | // // Now we can get pointers to the payload. |
78 | | // const L layout(1, 1, n, m); |
79 | | // double* a = layout.Pointer<double>(p); |
80 | | // int* b = layout.Pointer<int>(p); |
81 | | // } |
82 | | // |
83 | | // The layout we used above combines fixed-size with dynamically-sized fields. |
84 | | // This is quite common. Layout is optimized for this use case and generates |
85 | | // optimal code. All computations that can be performed at compile time are |
86 | | // indeed performed at compile time. |
87 | | // |
88 | | // Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to |
89 | | // ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no |
90 | | // padding in between arrays. |
91 | | // |
92 | | // You can manually override the alignment of an array by wrapping the type in |
93 | | // `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API |
94 | | // and behavior as `Layout<..., T, ...>` except that the first element of the |
95 | | // array of `T` is aligned to `N` (the rest of the elements follow without |
96 | | // padding). `N` cannot be less than `alignof(T)`. |
97 | | // |
98 | | // `AllocSize()` and `Pointer()` are the most basic methods for dealing with |
99 | | // memory layouts. Check out the reference or code below to discover more. |
100 | | // |
101 | | // EXAMPLE |
102 | | // |
103 | | // // Immutable move-only string with sizeof equal to sizeof(void*). The |
104 | | // // string size and the characters are kept in the same heap allocation. |
105 | | // class CompactString { |
106 | | // public: |
107 | | // CompactString(const char* s = "") { |
108 | | // const size_t size = strlen(s); |
109 | | // // size_t[1] followed by char[size + 1]. |
110 | | // const L layout(1, size + 1); |
111 | | // p_.reset(new unsigned char[layout.AllocSize()]); |
112 | | // // If running under ASAN, mark the padding bytes, if any, to catch |
113 | | // // memory errors. |
114 | | // layout.PoisonPadding(p_.get()); |
115 | | // // Store the size in the allocation. |
116 | | // *layout.Pointer<size_t>(p_.get()) = size; |
117 | | // // Store the characters in the allocation. |
118 | | // memcpy(layout.Pointer<char>(p_.get()), s, size + 1); |
119 | | // } |
120 | | // |
121 | | // size_t size() const { |
122 | | // // Equivalent to reinterpret_cast<size_t&>(*p). |
123 | | // return *L::Partial().Pointer<size_t>(p_.get()); |
124 | | // } |
125 | | // |
126 | | // const char* c_str() const { |
127 | | // // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)). |
128 | | // // The argument in Partial(1) specifies that we have size_t[1] in front |
129 | | // // of the characters. |
130 | | // return L::Partial(1).Pointer<char>(p_.get()); |
131 | | // } |
132 | | // |
133 | | // private: |
134 | | // // Our heap allocation contains a size_t followed by an array of chars. |
135 | | // using L = Layout<size_t, char>; |
136 | | // std::unique_ptr<unsigned char[]> p_; |
137 | | // }; |
138 | | // |
139 | | // int main() { |
140 | | // CompactString s = "hello"; |
141 | | // assert(s.size() == 5); |
142 | | // assert(strcmp(s.c_str(), "hello") == 0); |
143 | | // } |
144 | | // |
145 | | // DOCUMENTATION |
146 | | // |
147 | | // The interface exported by this file consists of: |
148 | | // - class `Layout<>` and its public members. |
149 | | // - The public members of class `internal_layout::LayoutImpl<>`. That class |
150 | | // isn't intended to be used directly, and its name and template parameter |
151 | | // list are internal implementation details, but the class itself provides |
152 | | // most of the functionality in this file. See comments on its members for |
153 | | // detailed documentation. |
154 | | // |
155 | | // `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a |
156 | | // `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)` |
157 | | // creates a `Layout` object, which exposes the same functionality by inheriting |
158 | | // from `LayoutImpl<>`. |
159 | | |
160 | | #ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ |
161 | | #define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ |
162 | | |
163 | | #include <assert.h> |
164 | | #include <stddef.h> |
165 | | #include <stdint.h> |
166 | | |
167 | | #include <ostream> |
168 | | #include <string> |
169 | | #include <tuple> |
170 | | #include <type_traits> |
171 | | #include <typeinfo> |
172 | | #include <utility> |
173 | | |
174 | | #include "absl/base/config.h" |
175 | | #include "absl/debugging/internal/demangle.h" |
176 | | #include "absl/meta/type_traits.h" |
177 | | #include "absl/strings/str_cat.h" |
178 | | #include "absl/types/span.h" |
179 | | #include "absl/utility/utility.h" |
180 | | |
181 | | #ifdef ABSL_HAVE_ADDRESS_SANITIZER |
182 | | #include <sanitizer/asan_interface.h> |
183 | | #endif |
184 | | |
185 | | namespace absl { |
186 | | ABSL_NAMESPACE_BEGIN |
187 | | namespace container_internal { |
188 | | |
189 | | // A type wrapper that instructs `Layout` to use the specific alignment for the |
190 | | // array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API |
191 | | // and behavior as `Layout<..., T, ...>` except that the first element of the |
192 | | // array of `T` is aligned to `N` (the rest of the elements follow without |
193 | | // padding). |
194 | | // |
195 | | // Requires: `N >= alignof(T)` and `N` is a power of 2. |
196 | | template <class T, size_t N> |
197 | | struct Aligned; |
198 | | |
199 | | namespace internal_layout { |
200 | | |
201 | | template <class T> |
202 | | struct NotAligned {}; |
203 | | |
204 | | template <class T, size_t N> |
205 | | struct NotAligned<const Aligned<T, N>> { |
206 | | static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified"); |
207 | | }; |
208 | | |
209 | | template <size_t> |
210 | | using IntToSize = size_t; |
211 | | |
212 | | template <class> |
213 | | using TypeToSize = size_t; |
214 | | |
215 | | template <class T> |
216 | | struct Type : NotAligned<T> { |
217 | | using type = T; |
218 | | }; |
219 | | |
220 | | template <class T, size_t N> |
221 | | struct Type<Aligned<T, N>> { |
222 | | using type = T; |
223 | | }; |
224 | | |
225 | | template <class T> |
226 | | struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {}; |
227 | | |
228 | | template <class T, size_t N> |
229 | | struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {}; |
230 | | |
231 | | // Note: workaround for https://gcc.gnu.org/PR88115 |
232 | | template <class T> |
233 | | struct AlignOf : NotAligned<T> { |
234 | | static constexpr size_t value = alignof(T); |
235 | | }; |
236 | | |
237 | | template <class T, size_t N> |
238 | | struct AlignOf<Aligned<T, N>> { |
239 | | static_assert(N % alignof(T) == 0, |
240 | | "Custom alignment can't be lower than the type's alignment"); |
241 | | static constexpr size_t value = N; |
242 | | }; |
243 | | |
244 | | // Does `Ts...` contain `T`? |
245 | | template <class T, class... Ts> |
246 | | using Contains = absl::disjunction<std::is_same<T, Ts>...>; |
247 | | |
248 | | template <class From, class To> |
249 | | using CopyConst = |
250 | | typename std::conditional<std::is_const<From>::value, const To, To>::type; |
251 | | |
252 | | // Note: We're not qualifying this with absl:: because it doesn't compile under |
253 | | // MSVC. |
254 | | template <class T> |
255 | | using SliceType = Span<T>; |
256 | | |
257 | | // This namespace contains no types. It prevents functions defined in it from |
258 | | // being found by ADL. |
259 | | namespace adl_barrier { |
260 | | |
261 | | template <class Needle, class... Ts> |
262 | | constexpr size_t Find(Needle, Needle, Ts...) { |
263 | | static_assert(!Contains<Needle, Ts...>(), "Duplicate element type"); |
264 | | return 0; |
265 | | } |
266 | | |
267 | | template <class Needle, class T, class... Ts> |
268 | | constexpr size_t Find(Needle, T, Ts...) { |
269 | | return adl_barrier::Find(Needle(), Ts()...) + 1; |
270 | | } |
271 | | |
272 | 0 | constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } |
273 | | |
274 | | // Returns `q * m` for the smallest `q` such that `q * m >= n`. |
275 | | // Requires: `m` is a power of two. It's enforced by IsLegalElementType below. |
276 | 0 | constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } |
277 | | |
278 | 0 | constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } |
279 | | |
280 | 0 | constexpr size_t Max(size_t a) { return a; } |
281 | | |
282 | | template <class... Ts> |
283 | 0 | constexpr size_t Max(size_t a, size_t b, Ts... rest) { |
284 | 0 | return adl_barrier::Max(b < a ? a : b, rest...); |
285 | 0 | } Unexecuted instantiation: unsigned long absl::lts_20240116::container_internal::internal_layout::adl_barrier::Max<>(unsigned long, unsigned long) Unexecuted instantiation: unsigned long absl::lts_20240116::container_internal::internal_layout::adl_barrier::Max<unsigned long>(unsigned long, unsigned long, unsigned long) Unexecuted instantiation: unsigned long absl::lts_20240116::container_internal::internal_layout::adl_barrier::Max<unsigned long, unsigned long>(unsigned long, unsigned long, unsigned long, unsigned long) Unexecuted instantiation: unsigned long absl::lts_20240116::container_internal::internal_layout::adl_barrier::Max<unsigned long, unsigned long, unsigned long>(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) |
286 | | |
287 | | template <class T> |
288 | | std::string TypeName() { |
289 | | std::string out; |
290 | | #if ABSL_INTERNAL_HAS_RTTI |
291 | | absl::StrAppend(&out, "<", |
292 | | absl::debugging_internal::DemangleString(typeid(T).name()), |
293 | | ">"); |
294 | | #endif |
295 | | return out; |
296 | | } |
297 | | |
298 | | } // namespace adl_barrier |
299 | | |
300 | | template <bool C> |
301 | | using EnableIf = typename std::enable_if<C, int>::type; |
302 | | |
303 | | // Can `T` be a template argument of `Layout`? |
304 | | template <class T> |
305 | | using IsLegalElementType = std::integral_constant< |
306 | | bool, !std::is_reference<T>::value && !std::is_volatile<T>::value && |
307 | | !std::is_reference<typename Type<T>::type>::value && |
308 | | !std::is_volatile<typename Type<T>::type>::value && |
309 | | adl_barrier::IsPow2(AlignOf<T>::value)>; |
310 | | |
311 | | template <class Elements, class SizeSeq, class OffsetSeq> |
312 | | class LayoutImpl; |
313 | | |
314 | | // Public base class of `Layout` and the result type of `Layout::Partial()`. |
315 | | // |
316 | | // `Elements...` contains all template arguments of `Layout` that created this |
317 | | // instance. |
318 | | // |
319 | | // `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments |
320 | | // passed to `Layout::Partial()` or `Layout::Layout()`. |
321 | | // |
322 | | // `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is |
323 | | // `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we |
324 | | // can compute offsets). |
325 | | template <class... Elements, size_t... SizeSeq, size_t... OffsetSeq> |
326 | | class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, |
327 | | absl::index_sequence<OffsetSeq...>> { |
328 | | private: |
329 | | static_assert(sizeof...(Elements) > 0, "At least one field is required"); |
330 | | static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value, |
331 | | "Invalid element type (see IsLegalElementType)"); |
332 | | |
333 | | enum { |
334 | | NumTypes = sizeof...(Elements), |
335 | | NumSizes = sizeof...(SizeSeq), |
336 | | NumOffsets = sizeof...(OffsetSeq), |
337 | | }; |
338 | | |
339 | | // These are guaranteed by `Layout`. |
340 | | static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), |
341 | | "Internal error"); |
342 | | static_assert(NumTypes > 0, "Internal error"); |
343 | | |
344 | | // Returns the index of `T` in `Elements...`. Results in a compilation error |
345 | | // if `Elements...` doesn't contain exactly one instance of `T`. |
346 | | template <class T> |
347 | | static constexpr size_t ElementIndex() { |
348 | | static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(), |
349 | | "Type not found"); |
350 | | return adl_barrier::Find(Type<T>(), |
351 | | Type<typename Type<Elements>::type>()...); |
352 | | } |
353 | | |
354 | | template <size_t N> |
355 | | using ElementAlignment = |
356 | | AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>; |
357 | | |
358 | | public: |
359 | | // Element types of all arrays packed in a tuple. |
360 | | using ElementTypes = std::tuple<typename Type<Elements>::type...>; |
361 | | |
362 | | // Element type of the Nth array. |
363 | | template <size_t N> |
364 | | using ElementType = typename std::tuple_element<N, ElementTypes>::type; |
365 | | |
366 | | constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes) |
367 | | : size_{sizes...} {} |
368 | | |
369 | | // Alignment of the layout, equal to the strictest alignment of all elements. |
370 | | // All pointers passed to the methods of layout must be aligned to this value. |
371 | 0 | static constexpr size_t Alignment() { |
372 | 0 | return adl_barrier::Max(AlignOf<Elements>::value...); |
373 | 0 | } |
374 | | |
375 | | // Offset in bytes of the Nth array. |
376 | | // |
377 | | // // int[3], 4 bytes of padding, double[4]. |
378 | | // Layout<int, double> x(3, 4); |
379 | | // assert(x.Offset<0>() == 0); // The ints starts from 0. |
380 | | // assert(x.Offset<1>() == 16); // The doubles starts from 16. |
381 | | // |
382 | | // Requires: `N <= NumSizes && N < sizeof...(Ts)`. |
383 | | template <size_t N, EnableIf<N == 0> = 0> |
384 | 0 | constexpr size_t Offset() const { |
385 | 0 | return 0; |
386 | 0 | } Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIiN6google8protobuf8internal12ExtensionSet9ExtensionENS4_4lessIiEENS4_9allocatorINS4_4pairIKiSC_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeIiSC_EESM_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESR_E6OffsetILm0ETnNS4_9enable_ifIXeqT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E6OffsetILm0ETnNS4_9enable_ifIXeqT_Li0EEiE4typeELi0EEEmv |
387 | | |
388 | | template <size_t N, EnableIf<N != 0> = 0> |
389 | 0 | constexpr size_t Offset() const { |
390 | 0 | static_assert(N < NumOffsets, "Index out of bounds"); |
391 | 0 | return adl_barrier::Align( |
392 | 0 | Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1], |
393 | 0 | ElementAlignment<N>::value); |
394 | 0 | } Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIiN6google8protobuf8internal12ExtensionSet9ExtensionENS4_4lessIiEENS4_9allocatorINS4_4pairIKiSC_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeIiSC_EESM_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESR_E6OffsetILm1ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIiN6google8protobuf8internal12ExtensionSet9ExtensionENS4_4lessIiEENS4_9allocatorINS4_4pairIKiSC_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeIiSC_EESM_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESR_E6OffsetILm2ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIiN6google8protobuf8internal12ExtensionSet9ExtensionENS4_4lessIiEENS4_9allocatorINS4_4pairIKiSC_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeIiSC_EESM_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESR_E6OffsetILm3ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIiN6google8protobuf8internal12ExtensionSet9ExtensionENS4_4lessIiEENS4_9allocatorINS4_4pairIKiSC_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeIiSC_EESM_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESR_E6OffsetILm4ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E6OffsetILm1ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E6OffsetILm2ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E6OffsetILm3ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E6OffsetILm4ETnNS4_9enable_ifIXneT_Li0EEiE4typeELi0EEEmv |
395 | | |
396 | | // Offset in bytes of the array with the specified element type. There must |
397 | | // be exactly one such array and its zero-based index must be at most |
398 | | // `NumSizes`. |
399 | | // |
400 | | // // int[3], 4 bytes of padding, double[4]. |
401 | | // Layout<int, double> x(3, 4); |
402 | | // assert(x.Offset<int>() == 0); // The ints starts from 0. |
403 | | // assert(x.Offset<double>() == 16); // The doubles starts from 16. |
404 | | template <class T> |
405 | | constexpr size_t Offset() const { |
406 | | return Offset<ElementIndex<T>()>(); |
407 | | } |
408 | | |
409 | | // Offsets in bytes of all arrays for which the offsets are known. |
410 | | constexpr std::array<size_t, NumOffsets> Offsets() const { |
411 | | return {{Offset<OffsetSeq>()...}}; |
412 | | } |
413 | | |
414 | | // The number of elements in the Nth array. This is the Nth argument of |
415 | | // `Layout::Partial()` or `Layout::Layout()` (zero-based). |
416 | | // |
417 | | // // int[3], 4 bytes of padding, double[4]. |
418 | | // Layout<int, double> x(3, 4); |
419 | | // assert(x.Size<0>() == 3); |
420 | | // assert(x.Size<1>() == 4); |
421 | | // |
422 | | // Requires: `N < NumSizes`. |
423 | | template <size_t N> |
424 | | constexpr size_t Size() const { |
425 | | static_assert(N < NumSizes, "Index out of bounds"); |
426 | | return size_[N]; |
427 | | } |
428 | | |
429 | | // The number of elements in the array with the specified element type. |
430 | | // There must be exactly one such array and its zero-based index must be |
431 | | // at most `NumSizes`. |
432 | | // |
433 | | // // int[3], 4 bytes of padding, double[4]. |
434 | | // Layout<int, double> x(3, 4); |
435 | | // assert(x.Size<int>() == 3); |
436 | | // assert(x.Size<double>() == 4); |
437 | | template <class T> |
438 | | constexpr size_t Size() const { |
439 | | return Size<ElementIndex<T>()>(); |
440 | | } |
441 | | |
442 | | // The number of elements of all arrays for which they are known. |
443 | | constexpr std::array<size_t, NumSizes> Sizes() const { |
444 | | return {{Size<SizeSeq>()...}}; |
445 | | } |
446 | | |
447 | | // Pointer to the beginning of the Nth array. |
448 | | // |
449 | | // `Char` must be `[const] [signed|unsigned] char`. |
450 | | // |
451 | | // // int[3], 4 bytes of padding, double[4]. |
452 | | // Layout<int, double> x(3, 4); |
453 | | // unsigned char* p = new unsigned char[x.AllocSize()]; |
454 | | // int* ints = x.Pointer<0>(p); |
455 | | // double* doubles = x.Pointer<1>(p); |
456 | | // |
457 | | // Requires: `N <= NumSizes && N < sizeof...(Ts)`. |
458 | | // Requires: `p` is aligned to `Alignment()`. |
459 | | template <size_t N, class Char> |
460 | 0 | CopyConst<Char, ElementType<N>>* Pointer(Char* p) const { |
461 | 0 | using C = typename std::remove_const<Char>::type; |
462 | 0 | static_assert( |
463 | 0 | std::is_same<C, char>() || std::is_same<C, unsigned char>() || |
464 | 0 | std::is_same<C, signed char>(), |
465 | 0 | "The argument must be a pointer to [const] [signed|unsigned] char"); |
466 | 0 | constexpr size_t alignment = Alignment(); |
467 | 0 | (void)alignment; |
468 | 0 | assert(reinterpret_cast<uintptr_t>(p) % alignment == 0); |
469 | 0 | return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>()); |
470 | 0 | } Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E7PointerILm0EKcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ESQ_E4typeES10_E4typeEPSX_ Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E7PointerILm2EKcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ESQ_E4typeES10_E4typeEPSX_ Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E7PointerILm1EKcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ESQ_E4typeES10_E4typeEPSX_ Unexecuted instantiation: _ZNK4absl12lts_2024011618container_internal15internal_layout10LayoutImplINSt3__15tupleIJPNS1_10btree_nodeINS1_10map_paramsIN6google8protobuf8internal10VariantKeyEPNSA_8NodeBaseENS4_4lessISB_EENSA_12MapAllocatorINS4_4pairIKSB_SD_EEEELi256ELb0EEEEEjhNS1_13map_slot_typeISB_SD_EESN_EEENS4_16integer_sequenceImJLm0ELm1ELm2ELm3ELm4EEEESS_E7PointerILm3EcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ESQ_E4typeESZ_E4typeEPSW_ |
471 | | |
472 | | // Pointer to the beginning of the array with the specified element type. |
473 | | // There must be exactly one such array and its zero-based index must be at |
474 | | // most `NumSizes`. |
475 | | // |
476 | | // `Char` must be `[const] [signed|unsigned] char`. |
477 | | // |
478 | | // // int[3], 4 bytes of padding, double[4]. |
479 | | // Layout<int, double> x(3, 4); |
480 | | // unsigned char* p = new unsigned char[x.AllocSize()]; |
481 | | // int* ints = x.Pointer<int>(p); |
482 | | // double* doubles = x.Pointer<double>(p); |
483 | | // |
484 | | // Requires: `p` is aligned to `Alignment()`. |
485 | | template <class T, class Char> |
486 | | CopyConst<Char, T>* Pointer(Char* p) const { |
487 | | return Pointer<ElementIndex<T>()>(p); |
488 | | } |
489 | | |
490 | | // Pointers to all arrays for which pointers are known. |
491 | | // |
492 | | // `Char` must be `[const] [signed|unsigned] char`. |
493 | | // |
494 | | // // int[3], 4 bytes of padding, double[4]. |
495 | | // Layout<int, double> x(3, 4); |
496 | | // unsigned char* p = new unsigned char[x.AllocSize()]; |
497 | | // |
498 | | // int* ints; |
499 | | // double* doubles; |
500 | | // std::tie(ints, doubles) = x.Pointers(p); |
501 | | // |
502 | | // Requires: `p` is aligned to `Alignment()`. |
503 | | // |
504 | | // Note: We're not using ElementType alias here because it does not compile |
505 | | // under MSVC. |
506 | | template <class Char> |
507 | | std::tuple<CopyConst< |
508 | | Char, typename std::tuple_element<OffsetSeq, ElementTypes>::type>*...> |
509 | | Pointers(Char* p) const { |
510 | | return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>( |
511 | | Pointer<OffsetSeq>(p)...); |
512 | | } |
513 | | |
514 | | // The Nth array. |
515 | | // |
516 | | // `Char` must be `[const] [signed|unsigned] char`. |
517 | | // |
518 | | // // int[3], 4 bytes of padding, double[4]. |
519 | | // Layout<int, double> x(3, 4); |
520 | | // unsigned char* p = new unsigned char[x.AllocSize()]; |
521 | | // Span<int> ints = x.Slice<0>(p); |
522 | | // Span<double> doubles = x.Slice<1>(p); |
523 | | // |
524 | | // Requires: `N < NumSizes`. |
525 | | // Requires: `p` is aligned to `Alignment()`. |
526 | | template <size_t N, class Char> |
527 | | SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const { |
528 | | return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>()); |
529 | | } |
530 | | |
531 | | // The array with the specified element type. There must be exactly one |
532 | | // such array and its zero-based index must be less than `NumSizes`. |
533 | | // |
534 | | // `Char` must be `[const] [signed|unsigned] char`. |
535 | | // |
536 | | // // int[3], 4 bytes of padding, double[4]. |
537 | | // Layout<int, double> x(3, 4); |
538 | | // unsigned char* p = new unsigned char[x.AllocSize()]; |
539 | | // Span<int> ints = x.Slice<int>(p); |
540 | | // Span<double> doubles = x.Slice<double>(p); |
541 | | // |
542 | | // Requires: `p` is aligned to `Alignment()`. |
543 | | template <class T, class Char> |
544 | | SliceType<CopyConst<Char, T>> Slice(Char* p) const { |
545 | | return Slice<ElementIndex<T>()>(p); |
546 | | } |
547 | | |
548 | | // All arrays with known sizes. |
549 | | // |
550 | | // `Char` must be `[const] [signed|unsigned] char`. |
551 | | // |
552 | | // // int[3], 4 bytes of padding, double[4]. |
553 | | // Layout<int, double> x(3, 4); |
554 | | // unsigned char* p = new unsigned char[x.AllocSize()]; |
555 | | // |
556 | | // Span<int> ints; |
557 | | // Span<double> doubles; |
558 | | // std::tie(ints, doubles) = x.Slices(p); |
559 | | // |
560 | | // Requires: `p` is aligned to `Alignment()`. |
561 | | // |
562 | | // Note: We're not using ElementType alias here because it does not compile |
563 | | // under MSVC. |
564 | | template <class Char> |
565 | | std::tuple<SliceType<CopyConst< |
566 | | Char, typename std::tuple_element<SizeSeq, ElementTypes>::type>>...> |
567 | | Slices(Char* p) const { |
568 | | // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed |
569 | | // in 6.1). |
570 | | (void)p; |
571 | | return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>( |
572 | | Slice<SizeSeq>(p)...); |
573 | | } |
574 | | |
575 | | // The size of the allocation that fits all arrays. |
576 | | // |
577 | | // // int[3], 4 bytes of padding, double[4]. |
578 | | // Layout<int, double> x(3, 4); |
579 | | // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes |
580 | | // |
581 | | // Requires: `NumSizes == sizeof...(Ts)`. |
582 | 0 | constexpr size_t AllocSize() const { |
583 | 0 | static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); |
584 | 0 | return Offset<NumTypes - 1>() + |
585 | 0 | SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1]; |
586 | 0 | } Unexecuted instantiation: absl::lts_20240116::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20240116::container_internal::btree_node<absl::lts_20240116::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20240116::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20240116::container_internal::btree_node<absl::lts_20240116::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::AllocSize() const Unexecuted instantiation: absl::lts_20240116::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20240116::container_internal::btree_node<absl::lts_20240116::container_internal::map_params<google::protobuf::internal::VariantKey, google::protobuf::internal::NodeBase*, std::__1::less<google::protobuf::internal::VariantKey>, google::protobuf::internal::MapAllocator<std::__1::pair<google::protobuf::internal::VariantKey const, google::protobuf::internal::NodeBase*> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20240116::container_internal::map_slot_type<google::protobuf::internal::VariantKey, google::protobuf::internal::NodeBase*>, absl::lts_20240116::container_internal::btree_node<absl::lts_20240116::container_internal::map_params<google::protobuf::internal::VariantKey, google::protobuf::internal::NodeBase*, std::__1::less<google::protobuf::internal::VariantKey>, google::protobuf::internal::MapAllocator<std::__1::pair<google::protobuf::internal::VariantKey const, google::protobuf::internal::NodeBase*> >, 256, false> >*>, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::AllocSize() const |
587 | | |
588 | | // If built with --config=asan, poisons padding bytes (if any) in the |
589 | | // allocation. The pointer must point to a memory block at least |
590 | | // `AllocSize()` bytes in length. |
591 | | // |
592 | | // `Char` must be `[const] [signed|unsigned] char`. |
593 | | // |
594 | | // Requires: `p` is aligned to `Alignment()`. |
595 | | template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0> |
596 | | void PoisonPadding(const Char* p) const { |
597 | | Pointer<0>(p); // verify the requirements on `Char` and `p` |
598 | | } |
599 | | |
600 | | template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0> |
601 | | void PoisonPadding(const Char* p) const { |
602 | | static_assert(N < NumOffsets, "Index out of bounds"); |
603 | | (void)p; |
604 | | #ifdef ABSL_HAVE_ADDRESS_SANITIZER |
605 | | PoisonPadding<Char, N - 1>(p); |
606 | | // The `if` is an optimization. It doesn't affect the observable behaviour. |
607 | | if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) { |
608 | | size_t start = |
609 | | Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1]; |
610 | | ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start); |
611 | | } |
612 | | #endif |
613 | | } |
614 | | |
615 | | // Human-readable description of the memory layout. Useful for debugging. |
616 | | // Slow. |
617 | | // |
618 | | // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed |
619 | | // // by an unknown number of doubles. |
620 | | // auto x = Layout<char, int, double>::Partial(5, 3); |
621 | | // assert(x.DebugString() == |
622 | | // "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)"); |
623 | | // |
624 | | // Each field is in the following format: @offset<type>(sizeof)[size] (<type> |
625 | | // may be missing depending on the target platform). For example, |
626 | | // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each |
627 | | // int is 4 bytes, and we have 3 of those ints. The size of the last field may |
628 | | // be missing (as in the example above). Only fields with known offsets are |
629 | | // described. Type names may differ across platforms: one compiler might |
630 | | // produce "unsigned*" where another produces "unsigned int *". |
631 | | std::string DebugString() const { |
632 | | const auto offsets = Offsets(); |
633 | | const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>::value...}; |
634 | | const std::string types[] = { |
635 | | adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; |
636 | | std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); |
637 | | for (size_t i = 0; i != NumOffsets - 1; ++i) { |
638 | | absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], |
639 | | "(", sizes[i + 1], ")"); |
640 | | } |
641 | | // NumSizes is a constant that may be zero. Some compilers cannot see that |
642 | | // inside the if statement "size_[NumSizes - 1]" must be valid. |
643 | | int last = static_cast<int>(NumSizes) - 1; |
644 | | if (NumTypes == NumSizes && last >= 0) { |
645 | | absl::StrAppend(&res, "[", size_[last], "]"); |
646 | | } |
647 | | return res; |
648 | | } |
649 | | |
650 | | private: |
651 | | // Arguments of `Layout::Partial()` or `Layout::Layout()`. |
652 | | size_t size_[NumSizes > 0 ? NumSizes : 1]; |
653 | | }; |
654 | | |
655 | | template <size_t NumSizes, class... Ts> |
656 | | using LayoutType = LayoutImpl< |
657 | | std::tuple<Ts...>, absl::make_index_sequence<NumSizes>, |
658 | | absl::make_index_sequence<adl_barrier::Min(sizeof...(Ts), NumSizes + 1)>>; |
659 | | |
660 | | } // namespace internal_layout |
661 | | |
662 | | // Descriptor of arrays of various types and sizes laid out in memory one after |
663 | | // another. See the top of the file for documentation. |
664 | | // |
665 | | // Check out the public API of internal_layout::LayoutImpl above. The type is |
666 | | // internal to the library but its methods are public, and they are inherited |
667 | | // by `Layout`. |
668 | | template <class... Ts> |
669 | | class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> { |
670 | | public: |
671 | | static_assert(sizeof...(Ts) > 0, "At least one field is required"); |
672 | | static_assert( |
673 | | absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value, |
674 | | "Invalid element type (see IsLegalElementType)"); |
675 | | |
676 | | // The result type of `Partial()` with `NumSizes` arguments. |
677 | | template <size_t NumSizes> |
678 | | using PartialType = internal_layout::LayoutType<NumSizes, Ts...>; |
679 | | |
680 | | // `Layout` knows the element types of the arrays we want to lay out in |
681 | | // memory but not the number of elements in each array. |
682 | | // `Partial(size1, ..., sizeN)` allows us to specify the latter. The |
683 | | // resulting immutable object can be used to obtain pointers to the |
684 | | // individual arrays. |
685 | | // |
686 | | // It's allowed to pass fewer array sizes than the number of arrays. E.g., |
687 | | // if all you need is to the offset of the second array, you only need to |
688 | | // pass one argument -- the number of elements in the first array. |
689 | | // |
690 | | // // int[3] followed by 4 bytes of padding and an unknown number of |
691 | | // // doubles. |
692 | | // auto x = Layout<int, double>::Partial(3); |
693 | | // // doubles start at byte 16. |
694 | | // assert(x.Offset<1>() == 16); |
695 | | // |
696 | | // If you know the number of elements in all arrays, you can still call |
697 | | // `Partial()` but it's more convenient to use the constructor of `Layout`. |
698 | | // |
699 | | // Layout<int, double> x(3, 5); |
700 | | // |
701 | | // Note: The sizes of the arrays must be specified in number of elements, |
702 | | // not in bytes. |
703 | | // |
704 | | // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`. |
705 | | // Requires: all arguments are convertible to `size_t`. |
706 | | template <class... Sizes> |
707 | | static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) { |
708 | | static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); |
709 | | return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...); |
710 | | } |
711 | | |
712 | | // Creates a layout with the sizes of all arrays specified. If you know |
713 | | // only the sizes of the first N arrays (where N can be zero), you can use |
714 | | // `Partial()` defined above. The constructor is essentially equivalent to |
715 | | // calling `Partial()` and passing in all array sizes; the constructor is |
716 | | // provided as a convenient abbreviation. |
717 | | // |
718 | | // Note: The sizes of the arrays must be specified in number of elements, |
719 | | // not in bytes. |
720 | | constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes) |
721 | | : internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) {} |
722 | | }; |
723 | | |
724 | | } // namespace container_internal |
725 | | ABSL_NAMESPACE_END |
726 | | } // namespace absl |
727 | | |
728 | | #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ |