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