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[1] 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 | #include <ostream> |
167 | #include <string> |
168 | #include <tuple> |
169 | #include <type_traits> |
170 | #include <typeinfo> |
171 | #include <utility> |
172 | |
173 | #ifdef ADDRESS_SANITIZER |
174 | #include <sanitizer/asan_interface.h> |
175 | #endif |
176 | |
177 | #include "absl/meta/type_traits.h" |
178 | #include "absl/strings/str_cat.h" |
179 | #include "absl/types/span.h" |
180 | #include "absl/utility/utility.h" |
181 | |
182 | #if defined(__GXX_RTTI) |
183 | #define ABSL_INTERNAL_HAS_CXA_DEMANGLE |
184 | #endif |
185 | |
186 | #ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE |
187 | #include <cxxabi.h> |
188 | #endif |
189 | |
190 | namespace absl { |
191 | namespace container_internal { |
192 | |
193 | // A type wrapper that instructs `Layout` to use the specific alignment for the |
194 | // array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API |
195 | // and behavior as `Layout<..., T, ...>` except that the first element of the |
196 | // array of `T` is aligned to `N` (the rest of the elements follow without |
197 | // padding). |
198 | // |
199 | // Requires: `N >= alignof(T)` and `N` is a power of 2. |
200 | template <class T, size_t N> |
201 | struct Aligned; |
202 | |
203 | namespace internal_layout { |
204 | |
205 | template <class T> |
206 | struct NotAligned {}; |
207 | |
208 | template <class T, size_t N> |
209 | struct NotAligned<const Aligned<T, N>> { |
210 | static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified" ); |
211 | }; |
212 | |
213 | template <size_t> |
214 | using IntToSize = size_t; |
215 | |
216 | template <class> |
217 | using TypeToSize = size_t; |
218 | |
219 | template <class T> |
220 | struct Type : NotAligned<T> { |
221 | using type = T; |
222 | }; |
223 | |
224 | template <class T, size_t N> |
225 | struct Type<Aligned<T, N>> { |
226 | using type = T; |
227 | }; |
228 | |
229 | template <class T> |
230 | struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {}; |
231 | |
232 | template <class T, size_t N> |
233 | struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {}; |
234 | |
235 | // Note: workaround for https://gcc.gnu.org/PR88115 |
236 | template <class T> |
237 | struct AlignOf : NotAligned<T> { |
238 | static constexpr size_t value = alignof(T); |
239 | }; |
240 | |
241 | template <class T, size_t N> |
242 | struct AlignOf<Aligned<T, N>> { |
243 | static_assert(N % alignof(T) == 0, |
244 | "Custom alignment can't be lower than the type's alignment" ); |
245 | static constexpr size_t value = N; |
246 | }; |
247 | |
248 | // Does `Ts...` contain `T`? |
249 | template <class T, class... Ts> |
250 | using Contains = absl::disjunction<std::is_same<T, Ts>...>; |
251 | |
252 | template <class From, class To> |
253 | using CopyConst = |
254 | typename std::conditional<std::is_const<From>::value, const To, To>::type; |
255 | |
256 | // Note: We're not qualifying this with absl:: because it doesn't compile under |
257 | // MSVC. |
258 | template <class T> |
259 | using SliceType = Span<T>; |
260 | |
261 | // This namespace contains no types. It prevents functions defined in it from |
262 | // being found by ADL. |
263 | namespace adl_barrier { |
264 | |
265 | template <class Needle, class... Ts> |
266 | constexpr size_t Find(Needle, Needle, Ts...) { |
267 | static_assert(!Contains<Needle, Ts...>(), "Duplicate element type" ); |
268 | return 0; |
269 | } |
270 | |
271 | template <class Needle, class T, class... Ts> |
272 | constexpr size_t Find(Needle, T, Ts...) { |
273 | return adl_barrier::Find(Needle(), Ts()...) + 1; |
274 | } |
275 | |
276 | constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } |
277 | |
278 | // Returns `q * m` for the smallest `q` such that `q * m >= n`. |
279 | // Requires: `m` is a power of two. It's enforced by IsLegalElementType below. |
280 | constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } |
281 | |
282 | constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } |
283 | |
284 | constexpr size_t Max(size_t a) { return a; } |
285 | |
286 | template <class... Ts> |
287 | constexpr size_t Max(size_t a, size_t b, Ts... rest) { |
288 | return adl_barrier::Max(b < a ? a : b, rest...); |
289 | } |
290 | |
291 | template <class T> |
292 | std::string TypeName() { |
293 | std::string out; |
294 | int status = 0; |
295 | char* demangled = nullptr; |
296 | #ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE |
297 | demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); |
298 | #endif |
299 | if (status == 0 && demangled != nullptr) { // Demangling succeeded. |
300 | absl::StrAppend(&out, "<" , demangled, ">" ); |
301 | free(demangled); |
302 | } else { |
303 | #if defined(__GXX_RTTI) || defined(_CPPRTTI) |
304 | absl::StrAppend(&out, "<" , typeid(T).name(), ">" ); |
305 | #endif |
306 | } |
307 | return out; |
308 | } |
309 | |
310 | } // namespace adl_barrier |
311 | |
312 | template <bool C> |
313 | using EnableIf = typename std::enable_if<C, int>::type; |
314 | |
315 | // Can `T` be a template argument of `Layout`? |
316 | template <class T> |
317 | using IsLegalElementType = std::integral_constant< |
318 | bool, !std::is_reference<T>::value && !std::is_volatile<T>::value && |
319 | !std::is_reference<typename Type<T>::type>::value && |
320 | !std::is_volatile<typename Type<T>::type>::value && |
321 | adl_barrier::IsPow2(AlignOf<T>::value)>; |
322 | |
323 | template <class Elements, class SizeSeq, class OffsetSeq> |
324 | class LayoutImpl; |
325 | |
326 | // Public base class of `Layout` and the result type of `Layout::Partial()`. |
327 | // |
328 | // `Elements...` contains all template arguments of `Layout` that created this |
329 | // instance. |
330 | // |
331 | // `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments |
332 | // passed to `Layout::Partial()` or `Layout::Layout()`. |
333 | // |
334 | // `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is |
335 | // `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we |
336 | // can compute offsets). |
337 | template <class... Elements, size_t... SizeSeq, size_t... OffsetSeq> |
338 | class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, |
339 | absl::index_sequence<OffsetSeq...>> { |
340 | private: |
341 | static_assert(sizeof...(Elements) > 0, "At least one field is required" ); |
342 | static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value, |
343 | "Invalid element type (see IsLegalElementType)" ); |
344 | |
345 | enum { |
346 | NumTypes = sizeof...(Elements), |
347 | NumSizes = sizeof...(SizeSeq), |
348 | NumOffsets = sizeof...(OffsetSeq), |
349 | }; |
350 | |
351 | // These are guaranteed by `Layout`. |
352 | static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), |
353 | "Internal error" ); |
354 | static_assert(NumTypes > 0, "Internal error" ); |
355 | |
356 | // Returns the index of `T` in `Elements...`. Results in a compilation error |
357 | // if `Elements...` doesn't contain exactly one instance of `T`. |
358 | template <class T> |
359 | static constexpr size_t ElementIndex() { |
360 | static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(), |
361 | "Type not found" ); |
362 | return adl_barrier::Find(Type<T>(), |
363 | Type<typename Type<Elements>::type>()...); |
364 | } |
365 | |
366 | template <size_t N> |
367 | using ElementAlignment = |
368 | AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>; |
369 | |
370 | public: |
371 | // Element types of all arrays packed in a tuple. |
372 | using ElementTypes = std::tuple<typename Type<Elements>::type...>; |
373 | |
374 | // Element type of the Nth array. |
375 | template <size_t N> |
376 | using ElementType = typename std::tuple_element<N, ElementTypes>::type; |
377 | |
378 | constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes) |
379 | : size_{sizes...} {} |
380 | |
381 | // Alignment of the layout, equal to the strictest alignment of all elements. |
382 | // All pointers passed to the methods of layout must be aligned to this value. |
383 | static constexpr size_t Alignment() { |
384 | return adl_barrier::Max(AlignOf<Elements>::value...); |
385 | } |
386 | |
387 | // Offset in bytes of the Nth array. |
388 | // |
389 | // // int[3], 4 bytes of padding, double[4]. |
390 | // Layout<int, double> x(3, 4); |
391 | // assert(x.Offset<0>() == 0); // The ints starts from 0. |
392 | // assert(x.Offset<1>() == 16); // The doubles starts from 16. |
393 | // |
394 | // Requires: `N <= NumSizes && N < sizeof...(Ts)`. |
395 | template <size_t N, EnableIf<N == 0> = 0> |
396 | constexpr size_t Offset() const { |
397 | return 0; |
398 | } |
399 | |
400 | template <size_t N, EnableIf<N != 0> = 0> |
401 | constexpr size_t Offset() const { |
402 | static_assert(N < NumOffsets, "Index out of bounds" ); |
403 | return adl_barrier::Align( |
404 | Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1], |
405 | ElementAlignment<N>::value); |
406 | } |
407 | |
408 | // Offset in bytes of the array with the specified element type. There must |
409 | // be exactly one such array and its zero-based index must be at most |
410 | // `NumSizes`. |
411 | // |
412 | // // int[3], 4 bytes of padding, double[4]. |
413 | // Layout<int, double> x(3, 4); |
414 | // assert(x.Offset<int>() == 0); // The ints starts from 0. |
415 | // assert(x.Offset<double>() == 16); // The doubles starts from 16. |
416 | template <class T> |
417 | constexpr size_t Offset() const { |
418 | return Offset<ElementIndex<T>()>(); |
419 | } |
420 | |
421 | // Offsets in bytes of all arrays for which the offsets are known. |
422 | constexpr std::array<size_t, NumOffsets> Offsets() const { |
423 | return {{Offset<OffsetSeq>()...}}; |
424 | } |
425 | |
426 | // The number of elements in the Nth array. This is the Nth argument of |
427 | // `Layout::Partial()` or `Layout::Layout()` (zero-based). |
428 | // |
429 | // // int[3], 4 bytes of padding, double[4]. |
430 | // Layout<int, double> x(3, 4); |
431 | // assert(x.Size<0>() == 3); |
432 | // assert(x.Size<1>() == 4); |
433 | // |
434 | // Requires: `N < NumSizes`. |
435 | template <size_t N> |
436 | constexpr size_t Size() const { |
437 | static_assert(N < NumSizes, "Index out of bounds" ); |
438 | return size_[N]; |
439 | } |
440 | |
441 | // The number of elements in the array with the specified element type. |
442 | // There must be exactly one such array and its zero-based index must be |
443 | // at most `NumSizes`. |
444 | // |
445 | // // int[3], 4 bytes of padding, double[4]. |
446 | // Layout<int, double> x(3, 4); |
447 | // assert(x.Size<int>() == 3); |
448 | // assert(x.Size<double>() == 4); |
449 | template <class T> |
450 | constexpr size_t Size() const { |
451 | return Size<ElementIndex<T>()>(); |
452 | } |
453 | |
454 | // The number of elements of all arrays for which they are known. |
455 | constexpr std::array<size_t, NumSizes> Sizes() const { |
456 | return {{Size<SizeSeq>()...}}; |
457 | } |
458 | |
459 | // Pointer to the beginning of the Nth array. |
460 | // |
461 | // `Char` must be `[const] [signed|unsigned] char`. |
462 | // |
463 | // // int[3], 4 bytes of padding, double[4]. |
464 | // Layout<int, double> x(3, 4); |
465 | // unsigned char* p = new unsigned char[x.AllocSize()]; |
466 | // int* ints = x.Pointer<0>(p); |
467 | // double* doubles = x.Pointer<1>(p); |
468 | // |
469 | // Requires: `N <= NumSizes && N < sizeof...(Ts)`. |
470 | // Requires: `p` is aligned to `Alignment()`. |
471 | template <size_t N, class Char> |
472 | CopyConst<Char, ElementType<N>>* Pointer(Char* p) const { |
473 | using C = typename std::remove_const<Char>::type; |
474 | static_assert( |
475 | std::is_same<C, char>() || std::is_same<C, unsigned char>() || |
476 | std::is_same<C, signed char>(), |
477 | "The argument must be a pointer to [const] [signed|unsigned] char" ); |
478 | constexpr size_t alignment = Alignment(); |
479 | (void)alignment; |
480 | assert(reinterpret_cast<uintptr_t>(p) % alignment == 0); |
481 | return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>()); |
482 | } |
483 | |
484 | // Pointer to the beginning of the array with the specified element type. |
485 | // There must be exactly one such array and its zero-based index must be at |
486 | // most `NumSizes`. |
487 | // |
488 | // `Char` must be `[const] [signed|unsigned] char`. |
489 | // |
490 | // // int[3], 4 bytes of padding, double[4]. |
491 | // Layout<int, double> x(3, 4); |
492 | // unsigned char* p = new unsigned char[x.AllocSize()]; |
493 | // int* ints = x.Pointer<int>(p); |
494 | // double* doubles = x.Pointer<double>(p); |
495 | // |
496 | // Requires: `p` is aligned to `Alignment()`. |
497 | template <class T, class Char> |
498 | CopyConst<Char, T>* Pointer(Char* p) const { |
499 | return Pointer<ElementIndex<T>()>(p); |
500 | } |
501 | |
502 | // Pointers to all arrays for which pointers are known. |
503 | // |
504 | // `Char` must be `[const] [signed|unsigned] char`. |
505 | // |
506 | // // int[3], 4 bytes of padding, double[4]. |
507 | // Layout<int, double> x(3, 4); |
508 | // unsigned char* p = new unsigned char[x.AllocSize()]; |
509 | // |
510 | // int* ints; |
511 | // double* doubles; |
512 | // std::tie(ints, doubles) = x.Pointers(p); |
513 | // |
514 | // Requires: `p` is aligned to `Alignment()`. |
515 | // |
516 | // Note: We're not using ElementType alias here because it does not compile |
517 | // under MSVC. |
518 | template <class Char> |
519 | std::tuple<CopyConst< |
520 | Char, typename std::tuple_element<OffsetSeq, ElementTypes>::type>*...> |
521 | Pointers(Char* p) const { |
522 | return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>( |
523 | Pointer<OffsetSeq>(p)...); |
524 | } |
525 | |
526 | // The Nth array. |
527 | // |
528 | // `Char` must be `[const] [signed|unsigned] char`. |
529 | // |
530 | // // int[3], 4 bytes of padding, double[4]. |
531 | // Layout<int, double> x(3, 4); |
532 | // unsigned char* p = new unsigned char[x.AllocSize()]; |
533 | // Span<int> ints = x.Slice<0>(p); |
534 | // Span<double> doubles = x.Slice<1>(p); |
535 | // |
536 | // Requires: `N < NumSizes`. |
537 | // Requires: `p` is aligned to `Alignment()`. |
538 | template <size_t N, class Char> |
539 | SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const { |
540 | return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>()); |
541 | } |
542 | |
543 | // The array with the specified element type. There must be exactly one |
544 | // such array and its zero-based index must be less than `NumSizes`. |
545 | // |
546 | // `Char` must be `[const] [signed|unsigned] char`. |
547 | // |
548 | // // int[3], 4 bytes of padding, double[4]. |
549 | // Layout<int, double> x(3, 4); |
550 | // unsigned char* p = new unsigned char[x.AllocSize()]; |
551 | // Span<int> ints = x.Slice<int>(p); |
552 | // Span<double> doubles = x.Slice<double>(p); |
553 | // |
554 | // Requires: `p` is aligned to `Alignment()`. |
555 | template <class T, class Char> |
556 | SliceType<CopyConst<Char, T>> Slice(Char* p) const { |
557 | return Slice<ElementIndex<T>()>(p); |
558 | } |
559 | |
560 | // All arrays with known sizes. |
561 | // |
562 | // `Char` must be `[const] [signed|unsigned] char`. |
563 | // |
564 | // // int[3], 4 bytes of padding, double[4]. |
565 | // Layout<int, double> x(3, 4); |
566 | // unsigned char* p = new unsigned char[x.AllocSize()]; |
567 | // |
568 | // Span<int> ints; |
569 | // Span<double> doubles; |
570 | // std::tie(ints, doubles) = x.Slices(p); |
571 | // |
572 | // Requires: `p` is aligned to `Alignment()`. |
573 | // |
574 | // Note: We're not using ElementType alias here because it does not compile |
575 | // under MSVC. |
576 | template <class Char> |
577 | std::tuple<SliceType<CopyConst< |
578 | Char, typename std::tuple_element<SizeSeq, ElementTypes>::type>>...> |
579 | Slices(Char* p) const { |
580 | // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed |
581 | // in 6.1). |
582 | (void)p; |
583 | return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>( |
584 | Slice<SizeSeq>(p)...); |
585 | } |
586 | |
587 | // The size of the allocation that fits all arrays. |
588 | // |
589 | // // int[3], 4 bytes of padding, double[4]. |
590 | // Layout<int, double> x(3, 4); |
591 | // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes |
592 | // |
593 | // Requires: `NumSizes == sizeof...(Ts)`. |
594 | constexpr size_t AllocSize() const { |
595 | static_assert(NumTypes == NumSizes, "You must specify sizes of all fields" ); |
596 | return Offset<NumTypes - 1>() + |
597 | SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1]; |
598 | } |
599 | |
600 | // If built with --config=asan, poisons padding bytes (if any) in the |
601 | // allocation. The pointer must point to a memory block at least |
602 | // `AllocSize()` bytes in length. |
603 | // |
604 | // `Char` must be `[const] [signed|unsigned] char`. |
605 | // |
606 | // Requires: `p` is aligned to `Alignment()`. |
607 | template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0> |
608 | void PoisonPadding(const Char* p) const { |
609 | Pointer<0>(p); // verify the requirements on `Char` and `p` |
610 | } |
611 | |
612 | template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0> |
613 | void PoisonPadding(const Char* p) const { |
614 | static_assert(N < NumOffsets, "Index out of bounds" ); |
615 | (void)p; |
616 | #ifdef ADDRESS_SANITIZER |
617 | PoisonPadding<Char, N - 1>(p); |
618 | // The `if` is an optimization. It doesn't affect the observable behaviour. |
619 | if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) { |
620 | size_t start = |
621 | Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1]; |
622 | ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start); |
623 | } |
624 | #endif |
625 | } |
626 | |
627 | // Human-readable description of the memory layout. Useful for debugging. |
628 | // Slow. |
629 | // |
630 | // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed |
631 | // // by an unknown number of doubles. |
632 | // auto x = Layout<char, int, double>::Partial(5, 3); |
633 | // assert(x.DebugString() == |
634 | // "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)"); |
635 | // |
636 | // Each field is in the following format: @offset<type>(sizeof)[size] (<type> |
637 | // may be missing depending on the target platform). For example, |
638 | // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each |
639 | // int is 4 bytes, and we have 3 of those ints. The size of the last field may |
640 | // be missing (as in the example above). Only fields with known offsets are |
641 | // described. Type names may differ across platforms: one compiler might |
642 | // produce "unsigned*" where another produces "unsigned int *". |
643 | std::string DebugString() const { |
644 | const auto offsets = Offsets(); |
645 | const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...}; |
646 | const std::string types[] = { |
647 | adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; |
648 | std::string res = absl::StrCat("@0" , types[0], "(" , sizes[0], ")" ); |
649 | for (size_t i = 0; i != NumOffsets - 1; ++i) { |
650 | absl::StrAppend(&res, "[" , size_[i], "]; @" , offsets[i + 1], types[i + 1], |
651 | "(" , sizes[i + 1], ")" ); |
652 | } |
653 | // NumSizes is a constant that may be zero. Some compilers cannot see that |
654 | // inside the if statement "size_[NumSizes - 1]" must be valid. |
655 | int last = static_cast<int>(NumSizes) - 1; |
656 | if (NumTypes == NumSizes && last >= 0) { |
657 | absl::StrAppend(&res, "[" , size_[last], "]" ); |
658 | } |
659 | return res; |
660 | } |
661 | |
662 | private: |
663 | // Arguments of `Layout::Partial()` or `Layout::Layout()`. |
664 | size_t size_[NumSizes > 0 ? NumSizes : 1]; |
665 | }; |
666 | |
667 | template <size_t NumSizes, class... Ts> |
668 | using LayoutType = LayoutImpl< |
669 | std::tuple<Ts...>, absl::make_index_sequence<NumSizes>, |
670 | absl::make_index_sequence<adl_barrier::Min(sizeof...(Ts), NumSizes + 1)>>; |
671 | |
672 | } // namespace internal_layout |
673 | |
674 | // Descriptor of arrays of various types and sizes laid out in memory one after |
675 | // another. See the top of the file for documentation. |
676 | // |
677 | // Check out the public API of internal_layout::LayoutImpl above. The type is |
678 | // internal to the library but its methods are public, and they are inherited |
679 | // by `Layout`. |
680 | template <class... Ts> |
681 | class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> { |
682 | public: |
683 | static_assert(sizeof...(Ts) > 0, "At least one field is required" ); |
684 | static_assert( |
685 | absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value, |
686 | "Invalid element type (see IsLegalElementType)" ); |
687 | |
688 | // The result type of `Partial()` with `NumSizes` arguments. |
689 | template <size_t NumSizes> |
690 | using PartialType = internal_layout::LayoutType<NumSizes, Ts...>; |
691 | |
692 | // `Layout` knows the element types of the arrays we want to lay out in |
693 | // memory but not the number of elements in each array. |
694 | // `Partial(size1, ..., sizeN)` allows us to specify the latter. The |
695 | // resulting immutable object can be used to obtain pointers to the |
696 | // individual arrays. |
697 | // |
698 | // It's allowed to pass fewer array sizes than the number of arrays. E.g., |
699 | // if all you need is to the offset of the second array, you only need to |
700 | // pass one argument -- the number of elements in the first array. |
701 | // |
702 | // // int[3] followed by 4 bytes of padding and an unknown number of |
703 | // // doubles. |
704 | // auto x = Layout<int, double>::Partial(3); |
705 | // // doubles start at byte 16. |
706 | // assert(x.Offset<1>() == 16); |
707 | // |
708 | // If you know the number of elements in all arrays, you can still call |
709 | // `Partial()` but it's more convenient to use the constructor of `Layout`. |
710 | // |
711 | // Layout<int, double> x(3, 5); |
712 | // |
713 | // Note: The sizes of the arrays must be specified in number of elements, |
714 | // not in bytes. |
715 | // |
716 | // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`. |
717 | // Requires: all arguments are convertible to `size_t`. |
718 | template <class... Sizes> |
719 | static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) { |
720 | static_assert(sizeof...(Sizes) <= sizeof...(Ts), "" ); |
721 | return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...); |
722 | } |
723 | |
724 | // Creates a layout with the sizes of all arrays specified. If you know |
725 | // only the sizes of the first N arrays (where N can be zero), you can use |
726 | // `Partial()` defined above. The constructor is essentially equivalent to |
727 | // calling `Partial()` and passing in all array sizes; the constructor is |
728 | // provided as a convenient abbreviation. |
729 | // |
730 | // Note: The sizes of the arrays must be specified in number of elements, |
731 | // not in bytes. |
732 | constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes) |
733 | : internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) {} |
734 | }; |
735 | |
736 | } // namespace container_internal |
737 | } // namespace absl |
738 | |
739 | #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ |
740 | |