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 167 #include <ostream> 168 #include <string> 169 #include <tuple> 170 #include <type_traits> 171 #include <typeinfo> 172 #include <utility> 173 174 #include "absl/base/config.h" 175 #include "absl/meta/type_traits.h" 176 #include "absl/strings/str_cat.h" 177 #include "absl/types/span.h" 178 #include "absl/utility/utility.h" 179 180 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 181 #include <sanitizer/asan_interface.h> 182 #endif 183 184 #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 192 namespace absl { 193 ABSL_NAMESPACE_BEGIN 194 namespace 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. 203 template <class T, size_t N> 204 struct Aligned; 205 206 namespace internal_layout { 207 208 template <class T> 209 struct NotAligned {}; 210 211 template <class T, size_t N> 212 struct NotAligned<const Aligned<T, N>> { 213 static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified"); 214 }; 215 216 template <size_t> 217 using IntToSize = size_t; 218 219 template <class> 220 using TypeToSize = size_t; 221 222 template <class T> 223 struct Type : NotAligned<T> { 224 using type = T; 225 }; 226 227 template <class T, size_t N> 228 struct Type<Aligned<T, N>> { 229 using type = T; 230 }; 231 232 template <class T> 233 struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {}; 234 235 template <class T, size_t N> 236 struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {}; 237 238 // Note: workaround for https://gcc.gnu.org/PR88115 239 template <class T> 240 struct AlignOf : NotAligned<T> { 241 static constexpr size_t value = alignof(T); 242 }; 243 244 template <class T, size_t N> 245 struct 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`? 252 template <class T, class... Ts> 253 using Contains = absl::disjunction<std::is_same<T, Ts>...>; 254 255 template <class From, class To> 256 using 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. 261 template <class T> 262 using SliceType = Span<T>; 263 264 // This namespace contains no types. It prevents functions defined in it from 265 // being found by ADL. 266 namespace adl_barrier { 267 268 template <class Needle, class... Ts> 269 constexpr size_t Find(Needle, Needle, Ts...) { 270 static_assert(!Contains<Needle, Ts...>(), "Duplicate element type"); 271 return 0; 272 } 273 274 template <class Needle, class T, class... Ts> 275 constexpr size_t Find(Needle, T, Ts...) { 276 return adl_barrier::Find(Needle(), Ts()...) + 1; 277 } 278 279 constexpr 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. 283 constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } 284 285 constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } 286 287 constexpr size_t Max(size_t a) { return a; } 288 289 template <class... Ts> 290 constexpr size_t Max(size_t a, size_t b, Ts... rest) { 291 return adl_barrier::Max(b < a ? a : b, rest...); 292 } 293 294 template <class T> 295 std::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 315 template <bool C> 316 using EnableIf = typename std::enable_if<C, int>::type; 317 318 // Can `T` be a template argument of `Layout`? 319 template <class T> 320 using 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 326 template <class Elements, class SizeSeq, class OffsetSeq> 327 class 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). 340 template <class... Elements, size_t... SizeSeq, size_t... OffsetSeq> 341 class 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>>::value * 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>>::value * 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; 619 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 620 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>>::value * 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>>::value...}; 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 670 template <size_t NumSizes, class... Ts> 671 using 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`. 683 template <class... Ts> 684 class 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 740 ABSL_NAMESPACE_END 741 } // namespace absl 742 743 #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ 744