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