xref: /aosp_15_r20/external/abseil-cpp/absl/container/internal/layout.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
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 attempts to
85 // generate optimal code. To help the compiler do that in more cases, you can
86 // specify the fixed sizes using `WithStaticSizes`. This ensures that all
87 // computations that can be performed at compile time are indeed performed at
88 // compile time. Note that sometimes the `template` keyword is needed. E.g.:
89 //
90 //   using SL = L::template WithStaticSizes<1, 1>;
91 //
92 //   void Use(unsigned char* p) {
93 //     // First, extract N and M.
94 //     // Using `prefix` we can access the first three arrays but not more.
95 //     //
96 //     // More details: The first element always has offset 0. `SL`
97 //     // has offsets for the second and third array based on sizes of
98 //     // the first and second array, specified via `WithStaticSizes`.
99 //     constexpr auto prefix = SL::Partial();
100 //     size_t n = *prefix.Pointer<0>(p);
101 //     size_t m = *prefix.Pointer<1>(p);
102 //
103 //     // Now we can get a pointer to the final payload.
104 //     const SL layout(n, m);
105 //     double* a = layout.Pointer<double>(p);
106 //     int* b = layout.Pointer<int>(p);
107 //   }
108 //
109 // Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to
110 // ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no
111 // padding in between arrays.
112 //
113 // You can manually override the alignment of an array by wrapping the type in
114 // `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
115 // and behavior as `Layout<..., T, ...>` except that the first element of the
116 // array of `T` is aligned to `N` (the rest of the elements follow without
117 // padding). `N` cannot be less than `alignof(T)`.
118 //
119 // `AllocSize()` and `Pointer()` are the most basic methods for dealing with
120 // memory layouts. Check out the reference or code below to discover more.
121 //
122 //                            EXAMPLE
123 //
124 //   // Immutable move-only string with sizeof equal to sizeof(void*). The
125 //   // string size and the characters are kept in the same heap allocation.
126 //   class CompactString {
127 //    public:
128 //     CompactString(const char* s = "") {
129 //       const size_t size = strlen(s);
130 //       // size_t[1] followed by char[size + 1].
131 //       const L layout(size + 1);
132 //       p_.reset(new unsigned char[layout.AllocSize()]);
133 //       // If running under ASAN, mark the padding bytes, if any, to catch
134 //       // memory errors.
135 //       layout.PoisonPadding(p_.get());
136 //       // Store the size in the allocation.
137 //       *layout.Pointer<size_t>(p_.get()) = size;
138 //       // Store the characters in the allocation.
139 //       memcpy(layout.Pointer<char>(p_.get()), s, size + 1);
140 //     }
141 //
142 //     size_t size() const {
143 //       // Equivalent to reinterpret_cast<size_t&>(*p).
144 //       return *L::Partial().Pointer<size_t>(p_.get());
145 //     }
146 //
147 //     const char* c_str() const {
148 //       // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
149 //       return L::Partial().Pointer<char>(p_.get());
150 //     }
151 //
152 //    private:
153 //     // Our heap allocation contains a single size_t followed by an array of
154 //     // chars.
155 //     using L = Layout<size_t, char>::WithStaticSizes<1>;
156 //     std::unique_ptr<unsigned char[]> p_;
157 //   };
158 //
159 //   int main() {
160 //     CompactString s = "hello";
161 //     assert(s.size() == 5);
162 //     assert(strcmp(s.c_str(), "hello") == 0);
163 //   }
164 //
165 //                               DOCUMENTATION
166 //
167 // The interface exported by this file consists of:
168 // - class `Layout<>` and its public members.
169 // - The public members of classes `internal_layout::LayoutWithStaticSizes<>`
170 //   and `internal_layout::LayoutImpl<>`. Those classes aren't intended to be
171 //   used directly, and their name and template parameter list are internal
172 //   implementation details, but the classes themselves provide most of the
173 //   functionality in this file. See comments on their members for detailed
174 //   documentation.
175 //
176 // `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a
177 // `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)`
178 // creates a `Layout` object, which exposes the same functionality by inheriting
179 // from `LayoutImpl<>`.
180 
181 #ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_
182 #define ABSL_CONTAINER_INTERNAL_LAYOUT_H_
183 
184 #include <assert.h>
185 #include <stddef.h>
186 #include <stdint.h>
187 
188 #include <array>
189 #include <string>
190 #include <tuple>
191 #include <type_traits>
192 #include <typeinfo>
193 #include <utility>
194 
195 #include "absl/base/attributes.h"
196 #include "absl/base/config.h"
197 #include "absl/debugging/internal/demangle.h"
198 #include "absl/meta/type_traits.h"
199 #include "absl/strings/str_cat.h"
200 #include "absl/types/span.h"
201 #include "absl/utility/utility.h"
202 
203 #ifdef ABSL_HAVE_ADDRESS_SANITIZER
204 #include <sanitizer/asan_interface.h>
205 #endif
206 
207 namespace absl {
208 ABSL_NAMESPACE_BEGIN
209 namespace container_internal {
210 
211 // A type wrapper that instructs `Layout` to use the specific alignment for the
212 // array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
213 // and behavior as `Layout<..., T, ...>` except that the first element of the
214 // array of `T` is aligned to `N` (the rest of the elements follow without
215 // padding).
216 //
217 // Requires: `N >= alignof(T)` and `N` is a power of 2.
218 template <class T, size_t N>
219 struct Aligned;
220 
221 namespace internal_layout {
222 
223 template <class T>
224 struct NotAligned {};
225 
226 template <class T, size_t N>
227 struct NotAligned<const Aligned<T, N>> {
228   static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified");
229 };
230 
231 template <size_t>
232 using IntToSize = size_t;
233 
234 template <class T>
235 struct Type : NotAligned<T> {
236   using type = T;
237 };
238 
239 template <class T, size_t N>
240 struct Type<Aligned<T, N>> {
241   using type = T;
242 };
243 
244 template <class T>
245 struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {};
246 
247 template <class T, size_t N>
248 struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {};
249 
250 // Note: workaround for https://gcc.gnu.org/PR88115
251 template <class T>
252 struct AlignOf : NotAligned<T> {
253   static constexpr size_t value = alignof(T);
254 };
255 
256 template <class T, size_t N>
257 struct AlignOf<Aligned<T, N>> {
258   static_assert(N % alignof(T) == 0,
259                 "Custom alignment can't be lower than the type's alignment");
260   static constexpr size_t value = N;
261 };
262 
263 // Does `Ts...` contain `T`?
264 template <class T, class... Ts>
265 using Contains = absl::disjunction<std::is_same<T, Ts>...>;
266 
267 template <class From, class To>
268 using CopyConst =
269     typename std::conditional<std::is_const<From>::value, const To, To>::type;
270 
271 // Note: We're not qualifying this with absl:: because it doesn't compile under
272 // MSVC.
273 template <class T>
274 using SliceType = Span<T>;
275 
276 // This namespace contains no types. It prevents functions defined in it from
277 // being found by ADL.
278 namespace adl_barrier {
279 
280 template <class Needle, class... Ts>
281 constexpr size_t Find(Needle, Needle, Ts...) {
282   static_assert(!Contains<Needle, Ts...>(), "Duplicate element type");
283   return 0;
284 }
285 
286 template <class Needle, class T, class... Ts>
287 constexpr size_t Find(Needle, T, Ts...) {
288   return adl_barrier::Find(Needle(), Ts()...) + 1;
289 }
290 
291 constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); }
292 
293 // Returns `q * m` for the smallest `q` such that `q * m >= n`.
294 // Requires: `m` is a power of two. It's enforced by IsLegalElementType below.
295 constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); }
296 
297 constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; }
298 
299 constexpr size_t Max(size_t a) { return a; }
300 
301 template <class... Ts>
302 constexpr size_t Max(size_t a, size_t b, Ts... rest) {
303   return adl_barrier::Max(b < a ? a : b, rest...);
304 }
305 
306 template <class T>
307 std::string TypeName() {
308   std::string out;
309 #if ABSL_INTERNAL_HAS_RTTI
310   absl::StrAppend(&out, "<",
311                   absl::debugging_internal::DemangleString(typeid(T).name()),
312                   ">");
313 #endif
314   return out;
315 }
316 
317 }  // namespace adl_barrier
318 
319 template <bool C>
320 using EnableIf = typename std::enable_if<C, int>::type;
321 
322 // Can `T` be a template argument of `Layout`?
323 template <class T>
324 using IsLegalElementType = std::integral_constant<
325     bool, !std::is_reference<T>::value && !std::is_volatile<T>::value &&
326               !std::is_reference<typename Type<T>::type>::value &&
327               !std::is_volatile<typename Type<T>::type>::value &&
328               adl_barrier::IsPow2(AlignOf<T>::value)>;
329 
330 template <class Elements, class StaticSizeSeq, class RuntimeSizeSeq,
331           class SizeSeq, class OffsetSeq>
332 class LayoutImpl;
333 
334 // Public base class of `Layout` and the result type of `Layout::Partial()`.
335 //
336 // `Elements...` contains all template arguments of `Layout` that created this
337 // instance.
338 //
339 // `StaticSizeSeq...` is an index_sequence containing the sizes specified at
340 // compile-time.
341 //
342 // `RuntimeSizeSeq...` is `[0, NumRuntimeSizes)`, where `NumRuntimeSizes` is the
343 // number of arguments passed to `Layout::Partial()` or `Layout::Layout()`.
344 //
345 // `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is `NumRuntimeSizes` plus
346 // the number of sizes in `StaticSizeSeq`.
347 //
348 // `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is
349 // `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we
350 // can compute offsets).
351 template <class... Elements, size_t... StaticSizeSeq, size_t... RuntimeSizeSeq,
352           size_t... SizeSeq, size_t... OffsetSeq>
353 class LayoutImpl<
354     std::tuple<Elements...>, absl::index_sequence<StaticSizeSeq...>,
355     absl::index_sequence<RuntimeSizeSeq...>, absl::index_sequence<SizeSeq...>,
356     absl::index_sequence<OffsetSeq...>> {
357  private:
358   static_assert(sizeof...(Elements) > 0, "At least one field is required");
359   static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value,
360                 "Invalid element type (see IsLegalElementType)");
361   static_assert(sizeof...(StaticSizeSeq) <= sizeof...(Elements),
362                 "Too many static sizes specified");
363 
364   enum {
365     NumTypes = sizeof...(Elements),
366     NumStaticSizes = sizeof...(StaticSizeSeq),
367     NumRuntimeSizes = sizeof...(RuntimeSizeSeq),
368     NumSizes = sizeof...(SizeSeq),
369     NumOffsets = sizeof...(OffsetSeq),
370   };
371 
372   // These are guaranteed by `Layout`.
373   static_assert(NumStaticSizes + NumRuntimeSizes == NumSizes, "Internal error");
374   static_assert(NumSizes <= NumTypes, "Internal error");
375   static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1),
376                 "Internal error");
377   static_assert(NumTypes > 0, "Internal error");
378 
379   static constexpr std::array<size_t, sizeof...(StaticSizeSeq)> kStaticSizes = {
380       StaticSizeSeq...};
381 
382   // Returns the index of `T` in `Elements...`. Results in a compilation error
383   // if `Elements...` doesn't contain exactly one instance of `T`.
384   template <class T>
385   static constexpr size_t ElementIndex() {
386     static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(),
387                   "Type not found");
388     return adl_barrier::Find(Type<T>(),
389                              Type<typename Type<Elements>::type>()...);
390   }
391 
392   template <size_t N>
393   using ElementAlignment =
394       AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>;
395 
396  public:
397   // Element types of all arrays packed in a tuple.
398   using ElementTypes = std::tuple<typename Type<Elements>::type...>;
399 
400   // Element type of the Nth array.
401   template <size_t N>
402   using ElementType = typename std::tuple_element<N, ElementTypes>::type;
403 
404   constexpr explicit LayoutImpl(IntToSize<RuntimeSizeSeq>... sizes)
405       : size_{sizes...} {}
406 
407   // Alignment of the layout, equal to the strictest alignment of all elements.
408   // All pointers passed to the methods of layout must be aligned to this value.
409   static constexpr size_t Alignment() {
410     return adl_barrier::Max(AlignOf<Elements>::value...);
411   }
412 
413   // Offset in bytes of the Nth array.
414   //
415   //   // int[3], 4 bytes of padding, double[4].
416   //   Layout<int, double> x(3, 4);
417   //   assert(x.Offset<0>() == 0);   // The ints starts from 0.
418   //   assert(x.Offset<1>() == 16);  // The doubles starts from 16.
419   //
420   // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
421   template <size_t N, EnableIf<N == 0> = 0>
422   constexpr size_t Offset() const {
423     return 0;
424   }
425 
426   template <size_t N, EnableIf<N != 0> = 0>
427   constexpr size_t Offset() const {
428     static_assert(N < NumOffsets, "Index out of bounds");
429     return adl_barrier::Align(
430         Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * Size<N - 1>(),
431         ElementAlignment<N>::value);
432   }
433 
434   // Offset in bytes of the array with the specified element type. There must
435   // be exactly one such array and its zero-based index must be at most
436   // `NumSizes`.
437   //
438   //   // int[3], 4 bytes of padding, double[4].
439   //   Layout<int, double> x(3, 4);
440   //   assert(x.Offset<int>() == 0);      // The ints starts from 0.
441   //   assert(x.Offset<double>() == 16);  // The doubles starts from 16.
442   template <class T>
443   constexpr size_t Offset() const {
444     return Offset<ElementIndex<T>()>();
445   }
446 
447   // Offsets in bytes of all arrays for which the offsets are known.
448   constexpr std::array<size_t, NumOffsets> Offsets() const {
449     return {{Offset<OffsetSeq>()...}};
450   }
451 
452   // The number of elements in the Nth array (zero-based).
453   //
454   //   // int[3], 4 bytes of padding, double[4].
455   //   Layout<int, double> x(3, 4);
456   //   assert(x.Size<0>() == 3);
457   //   assert(x.Size<1>() == 4);
458   //
459   // Requires: `N < NumSizes`.
460   template <size_t N, EnableIf<(N < NumStaticSizes)> = 0>
461   constexpr size_t Size() const {
462     return kStaticSizes[N];
463   }
464 
465   template <size_t N, EnableIf<(N >= NumStaticSizes)> = 0>
466   constexpr size_t Size() const {
467     static_assert(N < NumSizes, "Index out of bounds");
468     return size_[N - NumStaticSizes];
469   }
470 
471   // The number of elements in the array with the specified element type.
472   // There must be exactly one such array and its zero-based index must be
473   // at most `NumSizes`.
474   //
475   //   // int[3], 4 bytes of padding, double[4].
476   //   Layout<int, double> x(3, 4);
477   //   assert(x.Size<int>() == 3);
478   //   assert(x.Size<double>() == 4);
479   template <class T>
480   constexpr size_t Size() const {
481     return Size<ElementIndex<T>()>();
482   }
483 
484   // The number of elements of all arrays for which they are known.
485   constexpr std::array<size_t, NumSizes> Sizes() const {
486     return {{Size<SizeSeq>()...}};
487   }
488 
489   // Pointer to the beginning of the Nth array.
490   //
491   // `Char` must be `[const] [signed|unsigned] char`.
492   //
493   //   // int[3], 4 bytes of padding, double[4].
494   //   Layout<int, double> x(3, 4);
495   //   unsigned char* p = new unsigned char[x.AllocSize()];
496   //   int* ints = x.Pointer<0>(p);
497   //   double* doubles = x.Pointer<1>(p);
498   //
499   // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
500   // Requires: `p` is aligned to `Alignment()`.
501   template <size_t N, class Char>
502   CopyConst<Char, ElementType<N>>* Pointer(Char* p) const {
503     using C = typename std::remove_const<Char>::type;
504     static_assert(
505         std::is_same<C, char>() || std::is_same<C, unsigned char>() ||
506             std::is_same<C, signed char>(),
507         "The argument must be a pointer to [const] [signed|unsigned] char");
508     constexpr size_t alignment = Alignment();
509     (void)alignment;
510     assert(reinterpret_cast<uintptr_t>(p) % alignment == 0);
511     return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>());
512   }
513 
514   // Pointer to the beginning of the array with the specified element type.
515   // There must be exactly one such array and its zero-based index must be at
516   // most `NumSizes`.
517   //
518   // `Char` must be `[const] [signed|unsigned] char`.
519   //
520   //   // int[3], 4 bytes of padding, double[4].
521   //   Layout<int, double> x(3, 4);
522   //   unsigned char* p = new unsigned char[x.AllocSize()];
523   //   int* ints = x.Pointer<int>(p);
524   //   double* doubles = x.Pointer<double>(p);
525   //
526   // Requires: `p` is aligned to `Alignment()`.
527   template <class T, class Char>
528   CopyConst<Char, T>* Pointer(Char* p) const {
529     return Pointer<ElementIndex<T>()>(p);
530   }
531 
532   // Pointers to all arrays for which pointers are known.
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   //
540   //   int* ints;
541   //   double* doubles;
542   //   std::tie(ints, doubles) = x.Pointers(p);
543   //
544   // Requires: `p` is aligned to `Alignment()`.
545   template <class Char>
546   auto Pointers(Char* p) const {
547     return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>(
548         Pointer<OffsetSeq>(p)...);
549   }
550 
551   // The Nth array.
552   //
553   // `Char` must be `[const] [signed|unsigned] char`.
554   //
555   //   // int[3], 4 bytes of padding, double[4].
556   //   Layout<int, double> x(3, 4);
557   //   unsigned char* p = new unsigned char[x.AllocSize()];
558   //   Span<int> ints = x.Slice<0>(p);
559   //   Span<double> doubles = x.Slice<1>(p);
560   //
561   // Requires: `N < NumSizes`.
562   // Requires: `p` is aligned to `Alignment()`.
563   template <size_t N, class Char>
564   SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const {
565     return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>());
566   }
567 
568   // The array with the specified element type. There must be exactly one
569   // such array and its zero-based index must be less than `NumSizes`.
570   //
571   // `Char` must be `[const] [signed|unsigned] char`.
572   //
573   //   // int[3], 4 bytes of padding, double[4].
574   //   Layout<int, double> x(3, 4);
575   //   unsigned char* p = new unsigned char[x.AllocSize()];
576   //   Span<int> ints = x.Slice<int>(p);
577   //   Span<double> doubles = x.Slice<double>(p);
578   //
579   // Requires: `p` is aligned to `Alignment()`.
580   template <class T, class Char>
581   SliceType<CopyConst<Char, T>> Slice(Char* p) const {
582     return Slice<ElementIndex<T>()>(p);
583   }
584 
585   // All arrays with known sizes.
586   //
587   // `Char` must be `[const] [signed|unsigned] char`.
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()];
592   //
593   //   Span<int> ints;
594   //   Span<double> doubles;
595   //   std::tie(ints, doubles) = x.Slices(p);
596   //
597   // Requires: `p` is aligned to `Alignment()`.
598   //
599   // Note: We mark the parameter as unused because GCC detects it is not used
600   // when `SizeSeq` is empty [-Werror=unused-but-set-parameter].
601   template <class Char>
602   auto Slices(ABSL_ATTRIBUTE_UNUSED Char* p) const {
603     return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>(
604         Slice<SizeSeq>(p)...);
605   }
606 
607   // The size of the allocation that fits all arrays.
608   //
609   //   // int[3], 4 bytes of padding, double[4].
610   //   Layout<int, double> x(3, 4);
611   //   unsigned char* p = new unsigned char[x.AllocSize()];  // 48 bytes
612   //
613   // Requires: `NumSizes == sizeof...(Ts)`.
614   constexpr size_t AllocSize() const {
615     static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
616     return Offset<NumTypes - 1>() +
617            SizeOf<ElementType<NumTypes - 1>>::value * Size<NumTypes - 1>();
618   }
619 
620   // If built with --config=asan, poisons padding bytes (if any) in the
621   // allocation. The pointer must point to a memory block at least
622   // `AllocSize()` bytes in length.
623   //
624   // `Char` must be `[const] [signed|unsigned] char`.
625   //
626   // Requires: `p` is aligned to `Alignment()`.
627   template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0>
628   void PoisonPadding(const Char* p) const {
629     Pointer<0>(p);  // verify the requirements on `Char` and `p`
630   }
631 
632   template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0>
633   void PoisonPadding(const Char* p) const {
634     static_assert(N < NumOffsets, "Index out of bounds");
635     (void)p;
636 #ifdef ABSL_HAVE_ADDRESS_SANITIZER
637     PoisonPadding<Char, N - 1>(p);
638     // The `if` is an optimization. It doesn't affect the observable behaviour.
639     if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
640       size_t start =
641           Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * Size<N - 1>();
642       ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
643     }
644 #endif
645   }
646 
647   // Human-readable description of the memory layout. Useful for debugging.
648   // Slow.
649   //
650   //   // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed
651   //   // by an unknown number of doubles.
652   //   auto x = Layout<char, int, double>::Partial(5, 3);
653   //   assert(x.DebugString() ==
654   //          "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)");
655   //
656   // Each field is in the following format: @offset<type>(sizeof)[size] (<type>
657   // may be missing depending on the target platform). For example,
658   // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each
659   // int is 4 bytes, and we have 3 of those ints. The size of the last field may
660   // be missing (as in the example above). Only fields with known offsets are
661   // described. Type names may differ across platforms: one compiler might
662   // produce "unsigned*" where another produces "unsigned int *".
663   std::string DebugString() const {
664     const auto offsets = Offsets();
665     const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>::value...};
666     const std::string types[] = {
667         adl_barrier::TypeName<ElementType<OffsetSeq>>()...};
668     std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")");
669     for (size_t i = 0; i != NumOffsets - 1; ++i) {
670       absl::StrAppend(&res, "[", DebugSize(i), "]; @", offsets[i + 1],
671                       types[i + 1], "(", sizes[i + 1], ")");
672     }
673     // NumSizes is a constant that may be zero. Some compilers cannot see that
674     // inside the if statement "size_[NumSizes - 1]" must be valid.
675     int last = static_cast<int>(NumSizes) - 1;
676     if (NumTypes == NumSizes && last >= 0) {
677       absl::StrAppend(&res, "[", DebugSize(static_cast<size_t>(last)), "]");
678     }
679     return res;
680   }
681 
682  private:
683   size_t DebugSize(size_t n) const {
684     if (n < NumStaticSizes) {
685       return kStaticSizes[n];
686     } else {
687       return size_[n - NumStaticSizes];
688     }
689   }
690 
691   // Arguments of `Layout::Partial()` or `Layout::Layout()`.
692   size_t size_[NumRuntimeSizes > 0 ? NumRuntimeSizes : 1];
693 };
694 
695 // Defining a constexpr static class member variable is redundant and deprecated
696 // in C++17, but required in C++14.
697 template <class... Elements, size_t... StaticSizeSeq, size_t... RuntimeSizeSeq,
698           size_t... SizeSeq, size_t... OffsetSeq>
699 constexpr std::array<size_t, sizeof...(StaticSizeSeq)> LayoutImpl<
700     std::tuple<Elements...>, absl::index_sequence<StaticSizeSeq...>,
701     absl::index_sequence<RuntimeSizeSeq...>, absl::index_sequence<SizeSeq...>,
702     absl::index_sequence<OffsetSeq...>>::kStaticSizes;
703 
704 template <class StaticSizeSeq, size_t NumRuntimeSizes, class... Ts>
705 using LayoutType = LayoutImpl<
706     std::tuple<Ts...>, StaticSizeSeq,
707     absl::make_index_sequence<NumRuntimeSizes>,
708     absl::make_index_sequence<NumRuntimeSizes + StaticSizeSeq::size()>,
709     absl::make_index_sequence<adl_barrier::Min(
710         sizeof...(Ts), NumRuntimeSizes + StaticSizeSeq::size() + 1)>>;
711 
712 template <class StaticSizeSeq, class... Ts>
713 class LayoutWithStaticSizes
714     : public LayoutType<StaticSizeSeq,
715                         sizeof...(Ts) - adl_barrier::Min(sizeof...(Ts),
716                                                          StaticSizeSeq::size()),
717                         Ts...> {
718  private:
719   using Super =
720       LayoutType<StaticSizeSeq,
721                  sizeof...(Ts) -
722                      adl_barrier::Min(sizeof...(Ts), StaticSizeSeq::size()),
723                  Ts...>;
724 
725  public:
726   // The result type of `Partial()` with `NumSizes` arguments.
727   template <size_t NumSizes>
728   using PartialType =
729       internal_layout::LayoutType<StaticSizeSeq, NumSizes, Ts...>;
730 
731   // `Layout` knows the element types of the arrays we want to lay out in
732   // memory but not the number of elements in each array.
733   // `Partial(size1, ..., sizeN)` allows us to specify the latter. The
734   // resulting immutable object can be used to obtain pointers to the
735   // individual arrays.
736   //
737   // It's allowed to pass fewer array sizes than the number of arrays. E.g.,
738   // if all you need is to the offset of the second array, you only need to
739   // pass one argument -- the number of elements in the first array.
740   //
741   //   // int[3] followed by 4 bytes of padding and an unknown number of
742   //   // doubles.
743   //   auto x = Layout<int, double>::Partial(3);
744   //   // doubles start at byte 16.
745   //   assert(x.Offset<1>() == 16);
746   //
747   // If you know the number of elements in all arrays, you can still call
748   // `Partial()` but it's more convenient to use the constructor of `Layout`.
749   //
750   //   Layout<int, double> x(3, 5);
751   //
752   // Note: The sizes of the arrays must be specified in number of elements,
753   // not in bytes.
754   //
755   // Requires: `sizeof...(Sizes) + NumStaticSizes <= sizeof...(Ts)`.
756   // Requires: all arguments are convertible to `size_t`.
757   template <class... Sizes>
758   static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) {
759     static_assert(sizeof...(Sizes) + StaticSizeSeq::size() <= sizeof...(Ts),
760                   "");
761     return PartialType<sizeof...(Sizes)>(
762         static_cast<size_t>(std::forward<Sizes>(sizes))...);
763   }
764 
765   // Inherit LayoutType's constructor.
766   //
767   // Creates a layout with the sizes of all arrays specified. If you know
768   // only the sizes of the first N arrays (where N can be zero), you can use
769   // `Partial()` defined above. The constructor is essentially equivalent to
770   // calling `Partial()` and passing in all array sizes; the constructor is
771   // provided as a convenient abbreviation.
772   //
773   // Note: The sizes of the arrays must be specified in number of elements,
774   // not in bytes.
775   //
776   // Implementation note: we do this via a `using` declaration instead of
777   // defining our own explicit constructor because the signature of LayoutType's
778   // constructor depends on RuntimeSizeSeq, which we don't have access to here.
779   // If we defined our own constructor here, it would have to use a parameter
780   // pack and then cast the arguments to size_t when calling the superclass
781   // constructor, similar to what Partial() does. But that would suffer from the
782   // same problem that Partial() has, which is that the parameter types are
783   // inferred from the arguments, which may be signed types, which must then be
784   // cast to size_t. This can lead to negative values being silently (i.e. with
785   // no compiler warnings) cast to an unsigned type. Having a constructor with
786   // size_t parameters helps the compiler generate better warnings about
787   // potential bad casts, while avoiding false warnings when positive literal
788   // arguments are used. If an argument is a positive literal integer (e.g.
789   // `1`), the compiler will understand that it can be safely converted to
790   // size_t, and hence not generate a warning. But if a negative literal (e.g.
791   // `-1`) or a variable with signed type is used, then it can generate a
792   // warning about a potentially unsafe implicit cast. It would be great if we
793   // could do this for Partial() too, but unfortunately as of C++23 there seems
794   // to be no way to define a function with a variable number of parameters of a
795   // certain type, a.k.a. homogeneous function parameter packs. So we're forced
796   // to choose between explicitly casting the arguments to size_t, which
797   // suppresses all warnings, even potentially valid ones, or implicitly casting
798   // them to size_t, which generates bogus warnings whenever literal arguments
799   // are used, even if they're positive.
800   using Super::Super;
801 };
802 
803 }  // namespace internal_layout
804 
805 // Descriptor of arrays of various types and sizes laid out in memory one after
806 // another. See the top of the file for documentation.
807 //
808 // Check out the public API of internal_layout::LayoutWithStaticSizes and
809 // internal_layout::LayoutImpl above. Those types are internal to the library
810 // but their methods are public, and they are inherited by `Layout`.
811 template <class... Ts>
812 class Layout : public internal_layout::LayoutWithStaticSizes<
813                    absl::make_index_sequence<0>, Ts...> {
814  private:
815   using Super =
816       internal_layout::LayoutWithStaticSizes<absl::make_index_sequence<0>,
817                                              Ts...>;
818 
819  public:
820   // If you know the sizes of some or all of the arrays at compile time, you can
821   // use `WithStaticSizes` or `WithStaticSizeSequence` to create a `Layout` type
822   // with those sizes baked in. This can help the compiler generate optimal code
823   // for calculating array offsets and AllocSize().
824   //
825   // Like `Partial()`, the N sizes you specify are for the first N arrays, and
826   // they specify the number of elements in each array, not the number of bytes.
827   template <class StaticSizeSeq>
828   using WithStaticSizeSequence =
829       internal_layout::LayoutWithStaticSizes<StaticSizeSeq, Ts...>;
830 
831   template <size_t... StaticSizes>
832   using WithStaticSizes =
833       WithStaticSizeSequence<std::index_sequence<StaticSizes...>>;
834 
835   // Inherit LayoutWithStaticSizes's constructor, which requires you to specify
836   // all the array sizes.
837   using Super::Super;
838 };
839 
840 }  // namespace container_internal
841 ABSL_NAMESPACE_END
842 }  // namespace absl
843 
844 #endif  // ABSL_CONTAINER_INTERNAL_LAYOUT_H_
845