1 // Copyright 2018 The Abseil Authors.
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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.
15 // MOTIVATION AND TUTORIAL
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.
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.
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);
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()]);
40 // // Obtain the pointer to the array of doubles.
41 // // Equivalent to `reinterpret_cast<double*>(p.get())`.
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());
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);
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
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>;
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;
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);
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);
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.
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.
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)`.
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.
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 {
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
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);
121 // size_t size() const {
122 // // Equivalent to reinterpret_cast<size_t&>(*p).
123 // return *L::Partial().Pointer<size_t>(p_.get());
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());
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_;
140 // CompactString s = "hello";
141 // assert(s.size() == 5);
142 // assert(strcmp(s.c_str(), "hello") == 0);
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.
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<>`.
160 #ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_
161 #define ABSL_CONTAINER_INTERNAL_LAYOUT_H_
169 #include <type_traits>
173 #ifdef ADDRESS_SANITIZER
174 #include <sanitizer/asan_interface.h>
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"
182 #if defined(__GXX_RTTI)
183 #define ABSL_INTERNAL_HAS_CXA_DEMANGLE
186 #ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
191 namespace container_internal {
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
199 // Requires: `N >= alignof(T)` and `N` is a power of 2.
200 template <class T, size_t N>
203 namespace internal_layout {
206 struct NotAligned {};
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");
214 using IntToSize = size_t;
217 using TypeToSize = size_t;
220 struct Type : NotAligned<T> {
224 template <class T, size_t N>
225 struct Type<Aligned<T, N>> {
230 struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {};
232 template <class T, size_t N>
233 struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {};
235 // Note: workaround for https://gcc.gnu.org/PR88115
237 struct AlignOf : NotAligned<T> {
238 static constexpr size_t value = alignof(T);
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;
248 // Does `Ts...` contain `T`?
249 template <class T, class... Ts>
250 using Contains = absl::disjunction<std::is_same<T, Ts>...>;
252 template <class From, class To>
254 typename std::conditional<std::is_const<From>::value, const To, To>::type;
256 // Note: We're not qualifying this with absl:: because it doesn't compile under
259 using SliceType = Span<T>;
261 // This namespace contains no types. It prevents functions defined in it from
262 // being found by ADL.
263 namespace adl_barrier {
265 template <class Needle, class... Ts>
266 constexpr size_t Find(Needle, Needle, Ts...) {
267 static_assert(!Contains<Needle, Ts...>(), "Duplicate element type");
271 template <class Needle, class T, class... Ts>
272 constexpr size_t Find(Needle, T, Ts...) {
273 return adl_barrier::Find(Needle(), Ts()...) + 1;
276 constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); }
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); }
282 constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; }
284 constexpr size_t Max(size_t a) { return a; }
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...);
292 std::string TypeName() {
295 char* demangled = nullptr;
296 #ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
297 demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
299 if (status == 0 && demangled != nullptr) { // Demangling succeeded.
300 absl::StrAppend(&out, "<", demangled, ">");
303 #if defined(__GXX_RTTI) || defined(_CPPRTTI)
304 absl::StrAppend(&out, "<", typeid(T).name(), ">");
310 } // namespace adl_barrier
313 using EnableIf = typename std::enable_if<C, int>::type;
315 // Can `T` be a template argument of `Layout`?
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)>;
323 template <class Elements, class SizeSeq, class OffsetSeq>
326 // Public base class of `Layout` and the result type of `Layout::Partial()`.
328 // `Elements...` contains all template arguments of `Layout` that created this
331 // `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments
332 // passed to `Layout::Partial()` or `Layout::Layout()`.
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...>> {
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)");
346 NumTypes = sizeof...(Elements),
347 NumSizes = sizeof...(SizeSeq),
348 NumOffsets = sizeof...(OffsetSeq),
351 // These are guaranteed by `Layout`.
352 static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1),
354 static_assert(NumTypes > 0, "Internal error");
356 // Returns the index of `T` in `Elements...`. Results in a compilation error
357 // if `Elements...` doesn't contain exactly one instance of `T`.
359 static constexpr size_t ElementIndex() {
360 static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(),
362 return adl_barrier::Find(Type<T>(),
363 Type<typename Type<Elements>::type>()...);
367 using ElementAlignment =
368 AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>;
371 // Element types of all arrays packed in a tuple.
372 using ElementTypes = std::tuple<typename Type<Elements>::type...>;
374 // Element type of the Nth array.
376 using ElementType = typename std::tuple_element<N, ElementTypes>::type;
378 constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes)
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...);
387 // Offset in bytes of the Nth array.
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.
394 // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
395 template <size_t N, EnableIf<N == 0> = 0>
396 constexpr size_t Offset() const {
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);
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
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.
417 constexpr size_t Offset() const {
418 return Offset<ElementIndex<T>()>();
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>()...}};
426 // The number of elements in the Nth array. This is the Nth argument of
427 // `Layout::Partial()` or `Layout::Layout()` (zero-based).
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);
434 // Requires: `N < NumSizes`.
436 constexpr size_t Size() const {
437 static_assert(N < NumSizes, "Index out of bounds");
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`.
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);
450 constexpr size_t Size() const {
451 return Size<ElementIndex<T>()>();
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>()...}};
459 // Pointer to the beginning of the Nth array.
461 // `Char` must be `[const] [signed|unsigned] char`.
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);
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;
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();
480 assert(reinterpret_cast<uintptr_t>(p) % alignment == 0);
481 return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>());
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
488 // `Char` must be `[const] [signed|unsigned] char`.
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);
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);
502 // Pointers to all arrays for which pointers are known.
504 // `Char` must be `[const] [signed|unsigned] char`.
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()];
512 // std::tie(ints, doubles) = x.Pointers(p);
514 // Requires: `p` is aligned to `Alignment()`.
516 // Note: We're not using ElementType alias here because it does not compile
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)...);
528 // `Char` must be `[const] [signed|unsigned] char`.
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);
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>());
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`.
546 // `Char` must be `[const] [signed|unsigned] char`.
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);
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);
560 // All arrays with known sizes.
562 // `Char` must be `[const] [signed|unsigned] char`.
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()];
569 // Span<double> doubles;
570 // std::tie(ints, doubles) = x.Slices(p);
572 // Requires: `p` is aligned to `Alignment()`.
574 // Note: We're not using ElementType alias here because it does not compile
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
583 return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>(
584 Slice<SizeSeq>(p)...);
587 // The size of the allocation that fits all arrays.
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
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];
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.
604 // `Char` must be `[const] [signed|unsigned] char`.
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`
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");
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) {
621 Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
622 ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
627 // Human-readable description of the memory layout. Useful for debugging.
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)");
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], ")");
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], "]");
663 // Arguments of `Layout::Partial()` or `Layout::Layout()`.
664 size_t size_[NumSizes > 0 ? NumSizes : 1];
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)>>;
672 } // namespace internal_layout
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.
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
680 template <class... Ts>
681 class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> {
683 static_assert(sizeof...(Ts) > 0, "At least one field is required");
685 absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value,
686 "Invalid element type (see IsLegalElementType)");
688 // The result type of `Partial()` with `NumSizes` arguments.
689 template <size_t NumSizes>
690 using PartialType = internal_layout::LayoutType<NumSizes, Ts...>;
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.
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.
702 // // int[3] followed by 4 bytes of padding and an unknown number of
704 // auto x = Layout<int, double>::Partial(3);
705 // // doubles start at byte 16.
706 // assert(x.Offset<1>() == 16);
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`.
711 // Layout<int, double> x(3, 5);
713 // Note: The sizes of the arrays must be specified in number of elements,
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)...);
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.
730 // Note: The sizes of the arrays must be specified in number of elements,
732 constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes)
733 : internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) {}
736 } // namespace container_internal
739 #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_