|
||||
File indexing completed on 2025-01-18 09:27:12
0001 // Copyright 2018 The Abseil Authors. 0002 // 0003 // Licensed under the Apache License, Version 2.0 (the "License"); 0004 // you may not use this file except in compliance with the License. 0005 // You may obtain a copy of the License at 0006 // 0007 // https://www.apache.org/licenses/LICENSE-2.0 0008 // 0009 // Unless required by applicable law or agreed to in writing, software 0010 // distributed under the License is distributed on an "AS IS" BASIS, 0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0012 // See the License for the specific language governing permissions and 0013 // limitations under the License. 0014 // 0015 // MOTIVATION AND TUTORIAL 0016 // 0017 // If you want to put in a single heap allocation N doubles followed by M ints, 0018 // it's easy if N and M are known at compile time. 0019 // 0020 // struct S { 0021 // double a[N]; 0022 // int b[M]; 0023 // }; 0024 // 0025 // S* p = new S; 0026 // 0027 // But what if N and M are known only in run time? Class template Layout to the 0028 // rescue! It's a portable generalization of the technique known as struct hack. 0029 // 0030 // // This object will tell us everything we need to know about the memory 0031 // // layout of double[N] followed by int[M]. It's structurally identical to 0032 // // size_t[2] that stores N and M. It's very cheap to create. 0033 // const Layout<double, int> layout(N, M); 0034 // 0035 // // Allocate enough memory for both arrays. `AllocSize()` tells us how much 0036 // // memory is needed. We are free to use any allocation function we want as 0037 // // long as it returns aligned memory. 0038 // std::unique_ptr<unsigned char[]> p(new unsigned char[layout.AllocSize()]); 0039 // 0040 // // Obtain the pointer to the array of doubles. 0041 // // Equivalent to `reinterpret_cast<double*>(p.get())`. 0042 // // 0043 // // We could have written layout.Pointer<0>(p) instead. If all the types are 0044 // // unique you can use either form, but if some types are repeated you must 0045 // // use the index form. 0046 // double* a = layout.Pointer<double>(p.get()); 0047 // 0048 // // Obtain the pointer to the array of ints. 0049 // // Equivalent to `reinterpret_cast<int*>(p.get() + N * 8)`. 0050 // int* b = layout.Pointer<int>(p); 0051 // 0052 // If we are unable to specify sizes of all fields, we can pass as many sizes as 0053 // we can to `Partial()`. In return, it'll allow us to access the fields whose 0054 // locations and sizes can be computed from the provided information. 0055 // `Partial()` comes in handy when the array sizes are embedded into the 0056 // allocation. 0057 // 0058 // // size_t[0] containing N, size_t[1] containing M, double[N], int[M]. 0059 // using L = Layout<size_t, size_t, double, int>; 0060 // 0061 // unsigned char* Allocate(size_t n, size_t m) { 0062 // const L layout(1, 1, n, m); 0063 // unsigned char* p = new unsigned char[layout.AllocSize()]; 0064 // *layout.Pointer<0>(p) = n; 0065 // *layout.Pointer<1>(p) = m; 0066 // return p; 0067 // } 0068 // 0069 // void Use(unsigned char* p) { 0070 // // First, extract N and M. 0071 // // Specify that the first array has only one element. Using `prefix` we 0072 // // can access the first two arrays but not more. 0073 // constexpr auto prefix = L::Partial(1); 0074 // size_t n = *prefix.Pointer<0>(p); 0075 // size_t m = *prefix.Pointer<1>(p); 0076 // 0077 // // Now we can get pointers to the payload. 0078 // const L layout(1, 1, n, m); 0079 // double* a = layout.Pointer<double>(p); 0080 // int* b = layout.Pointer<int>(p); 0081 // } 0082 // 0083 // The layout we used above combines fixed-size with dynamically-sized fields. 0084 // This is quite common. Layout is optimized for this use case and attempts to 0085 // generate optimal code. To help the compiler do that in more cases, you can 0086 // specify the fixed sizes using `WithStaticSizes`. This ensures that all 0087 // computations that can be performed at compile time are indeed performed at 0088 // compile time. Note that sometimes the `template` keyword is needed. E.g.: 0089 // 0090 // using SL = L::template WithStaticSizes<1, 1>; 0091 // 0092 // void Use(unsigned char* p) { 0093 // // First, extract N and M. 0094 // // Using `prefix` we can access the first three arrays but not more. 0095 // // 0096 // // More details: The first element always has offset 0. `SL` 0097 // // has offsets for the second and third array based on sizes of 0098 // // the first and second array, specified via `WithStaticSizes`. 0099 // constexpr auto prefix = SL::Partial(); 0100 // size_t n = *prefix.Pointer<0>(p); 0101 // size_t m = *prefix.Pointer<1>(p); 0102 // 0103 // // Now we can get a pointer to the final payload. 0104 // const SL layout(n, m); 0105 // double* a = layout.Pointer<double>(p); 0106 // int* b = layout.Pointer<int>(p); 0107 // } 0108 // 0109 // Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to 0110 // ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no 0111 // padding in between arrays. 0112 // 0113 // You can manually override the alignment of an array by wrapping the type in 0114 // `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API 0115 // and behavior as `Layout<..., T, ...>` except that the first element of the 0116 // array of `T` is aligned to `N` (the rest of the elements follow without 0117 // padding). `N` cannot be less than `alignof(T)`. 0118 // 0119 // `AllocSize()` and `Pointer()` are the most basic methods for dealing with 0120 // memory layouts. Check out the reference or code below to discover more. 0121 // 0122 // EXAMPLE 0123 // 0124 // // Immutable move-only string with sizeof equal to sizeof(void*). The 0125 // // string size and the characters are kept in the same heap allocation. 0126 // class CompactString { 0127 // public: 0128 // CompactString(const char* s = "") { 0129 // const size_t size = strlen(s); 0130 // // size_t[1] followed by char[size + 1]. 0131 // const L layout(size + 1); 0132 // p_.reset(new unsigned char[layout.AllocSize()]); 0133 // // If running under ASAN, mark the padding bytes, if any, to catch 0134 // // memory errors. 0135 // layout.PoisonPadding(p_.get()); 0136 // // Store the size in the allocation. 0137 // *layout.Pointer<size_t>(p_.get()) = size; 0138 // // Store the characters in the allocation. 0139 // memcpy(layout.Pointer<char>(p_.get()), s, size + 1); 0140 // } 0141 // 0142 // size_t size() const { 0143 // // Equivalent to reinterpret_cast<size_t&>(*p). 0144 // return *L::Partial().Pointer<size_t>(p_.get()); 0145 // } 0146 // 0147 // const char* c_str() const { 0148 // // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)). 0149 // return L::Partial().Pointer<char>(p_.get()); 0150 // } 0151 // 0152 // private: 0153 // // Our heap allocation contains a single size_t followed by an array of 0154 // // chars. 0155 // using L = Layout<size_t, char>::WithStaticSizes<1>; 0156 // std::unique_ptr<unsigned char[]> p_; 0157 // }; 0158 // 0159 // int main() { 0160 // CompactString s = "hello"; 0161 // assert(s.size() == 5); 0162 // assert(strcmp(s.c_str(), "hello") == 0); 0163 // } 0164 // 0165 // DOCUMENTATION 0166 // 0167 // The interface exported by this file consists of: 0168 // - class `Layout<>` and its public members. 0169 // - The public members of classes `internal_layout::LayoutWithStaticSizes<>` 0170 // and `internal_layout::LayoutImpl<>`. Those classes aren't intended to be 0171 // used directly, and their name and template parameter list are internal 0172 // implementation details, but the classes themselves provide most of the 0173 // functionality in this file. See comments on their members for detailed 0174 // documentation. 0175 // 0176 // `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a 0177 // `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)` 0178 // creates a `Layout` object, which exposes the same functionality by inheriting 0179 // from `LayoutImpl<>`. 0180 0181 #ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ 0182 #define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ 0183 0184 #include <assert.h> 0185 #include <stddef.h> 0186 #include <stdint.h> 0187 0188 #include <array> 0189 #include <string> 0190 #include <tuple> 0191 #include <type_traits> 0192 #include <typeinfo> 0193 #include <utility> 0194 0195 #include "absl/base/attributes.h" 0196 #include "absl/base/config.h" 0197 #include "absl/debugging/internal/demangle.h" 0198 #include "absl/meta/type_traits.h" 0199 #include "absl/strings/str_cat.h" 0200 #include "absl/types/span.h" 0201 #include "absl/utility/utility.h" 0202 0203 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 0204 #include <sanitizer/asan_interface.h> 0205 #endif 0206 0207 namespace absl { 0208 ABSL_NAMESPACE_BEGIN 0209 namespace container_internal { 0210 0211 // A type wrapper that instructs `Layout` to use the specific alignment for the 0212 // array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API 0213 // and behavior as `Layout<..., T, ...>` except that the first element of the 0214 // array of `T` is aligned to `N` (the rest of the elements follow without 0215 // padding). 0216 // 0217 // Requires: `N >= alignof(T)` and `N` is a power of 2. 0218 template <class T, size_t N> 0219 struct Aligned; 0220 0221 namespace internal_layout { 0222 0223 template <class T> 0224 struct NotAligned {}; 0225 0226 template <class T, size_t N> 0227 struct NotAligned<const Aligned<T, N>> { 0228 static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified"); 0229 }; 0230 0231 template <size_t> 0232 using IntToSize = size_t; 0233 0234 template <class T> 0235 struct Type : NotAligned<T> { 0236 using type = T; 0237 }; 0238 0239 template <class T, size_t N> 0240 struct Type<Aligned<T, N>> { 0241 using type = T; 0242 }; 0243 0244 template <class T> 0245 struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {}; 0246 0247 template <class T, size_t N> 0248 struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {}; 0249 0250 // Note: workaround for https://gcc.gnu.org/PR88115 0251 template <class T> 0252 struct AlignOf : NotAligned<T> { 0253 static constexpr size_t value = alignof(T); 0254 }; 0255 0256 template <class T, size_t N> 0257 struct AlignOf<Aligned<T, N>> { 0258 static_assert(N % alignof(T) == 0, 0259 "Custom alignment can't be lower than the type's alignment"); 0260 static constexpr size_t value = N; 0261 }; 0262 0263 // Does `Ts...` contain `T`? 0264 template <class T, class... Ts> 0265 using Contains = absl::disjunction<std::is_same<T, Ts>...>; 0266 0267 template <class From, class To> 0268 using CopyConst = 0269 typename std::conditional<std::is_const<From>::value, const To, To>::type; 0270 0271 // Note: We're not qualifying this with absl:: because it doesn't compile under 0272 // MSVC. 0273 template <class T> 0274 using SliceType = Span<T>; 0275 0276 // This namespace contains no types. It prevents functions defined in it from 0277 // being found by ADL. 0278 namespace adl_barrier { 0279 0280 template <class Needle, class... Ts> 0281 constexpr size_t Find(Needle, Needle, Ts...) { 0282 static_assert(!Contains<Needle, Ts...>(), "Duplicate element type"); 0283 return 0; 0284 } 0285 0286 template <class Needle, class T, class... Ts> 0287 constexpr size_t Find(Needle, T, Ts...) { 0288 return adl_barrier::Find(Needle(), Ts()...) + 1; 0289 } 0290 0291 constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } 0292 0293 // Returns `q * m` for the smallest `q` such that `q * m >= n`. 0294 // Requires: `m` is a power of two. It's enforced by IsLegalElementType below. 0295 constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } 0296 0297 constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } 0298 0299 constexpr size_t Max(size_t a) { return a; } 0300 0301 template <class... Ts> 0302 constexpr size_t Max(size_t a, size_t b, Ts... rest) { 0303 return adl_barrier::Max(b < a ? a : b, rest...); 0304 } 0305 0306 template <class T> 0307 std::string TypeName() { 0308 std::string out; 0309 #if ABSL_INTERNAL_HAS_RTTI 0310 absl::StrAppend(&out, "<", 0311 absl::debugging_internal::DemangleString(typeid(T).name()), 0312 ">"); 0313 #endif 0314 return out; 0315 } 0316 0317 } // namespace adl_barrier 0318 0319 template <bool C> 0320 using EnableIf = typename std::enable_if<C, int>::type; 0321 0322 // Can `T` be a template argument of `Layout`? 0323 template <class T> 0324 using IsLegalElementType = std::integral_constant< 0325 bool, !std::is_reference<T>::value && !std::is_volatile<T>::value && 0326 !std::is_reference<typename Type<T>::type>::value && 0327 !std::is_volatile<typename Type<T>::type>::value && 0328 adl_barrier::IsPow2(AlignOf<T>::value)>; 0329 0330 template <class Elements, class StaticSizeSeq, class RuntimeSizeSeq, 0331 class SizeSeq, class OffsetSeq> 0332 class LayoutImpl; 0333 0334 // Public base class of `Layout` and the result type of `Layout::Partial()`. 0335 // 0336 // `Elements...` contains all template arguments of `Layout` that created this 0337 // instance. 0338 // 0339 // `StaticSizeSeq...` is an index_sequence containing the sizes specified at 0340 // compile-time. 0341 // 0342 // `RuntimeSizeSeq...` is `[0, NumRuntimeSizes)`, where `NumRuntimeSizes` is the 0343 // number of arguments passed to `Layout::Partial()` or `Layout::Layout()`. 0344 // 0345 // `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is `NumRuntimeSizes` plus 0346 // the number of sizes in `StaticSizeSeq`. 0347 // 0348 // `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is 0349 // `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we 0350 // can compute offsets). 0351 template <class... Elements, size_t... StaticSizeSeq, size_t... RuntimeSizeSeq, 0352 size_t... SizeSeq, size_t... OffsetSeq> 0353 class LayoutImpl< 0354 std::tuple<Elements...>, absl::index_sequence<StaticSizeSeq...>, 0355 absl::index_sequence<RuntimeSizeSeq...>, absl::index_sequence<SizeSeq...>, 0356 absl::index_sequence<OffsetSeq...>> { 0357 private: 0358 static_assert(sizeof...(Elements) > 0, "At least one field is required"); 0359 static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value, 0360 "Invalid element type (see IsLegalElementType)"); 0361 static_assert(sizeof...(StaticSizeSeq) <= sizeof...(Elements), 0362 "Too many static sizes specified"); 0363 0364 enum { 0365 NumTypes = sizeof...(Elements), 0366 NumStaticSizes = sizeof...(StaticSizeSeq), 0367 NumRuntimeSizes = sizeof...(RuntimeSizeSeq), 0368 NumSizes = sizeof...(SizeSeq), 0369 NumOffsets = sizeof...(OffsetSeq), 0370 }; 0371 0372 // These are guaranteed by `Layout`. 0373 static_assert(NumStaticSizes + NumRuntimeSizes == NumSizes, "Internal error"); 0374 static_assert(NumSizes <= NumTypes, "Internal error"); 0375 static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), 0376 "Internal error"); 0377 static_assert(NumTypes > 0, "Internal error"); 0378 0379 static constexpr std::array<size_t, sizeof...(StaticSizeSeq)> kStaticSizes = { 0380 StaticSizeSeq...}; 0381 0382 // Returns the index of `T` in `Elements...`. Results in a compilation error 0383 // if `Elements...` doesn't contain exactly one instance of `T`. 0384 template <class T> 0385 static constexpr size_t ElementIndex() { 0386 static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(), 0387 "Type not found"); 0388 return adl_barrier::Find(Type<T>(), 0389 Type<typename Type<Elements>::type>()...); 0390 } 0391 0392 template <size_t N> 0393 using ElementAlignment = 0394 AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>; 0395 0396 public: 0397 // Element types of all arrays packed in a tuple. 0398 using ElementTypes = std::tuple<typename Type<Elements>::type...>; 0399 0400 // Element type of the Nth array. 0401 template <size_t N> 0402 using ElementType = typename std::tuple_element<N, ElementTypes>::type; 0403 0404 constexpr explicit LayoutImpl(IntToSize<RuntimeSizeSeq>... sizes) 0405 : size_{sizes...} {} 0406 0407 // Alignment of the layout, equal to the strictest alignment of all elements. 0408 // All pointers passed to the methods of layout must be aligned to this value. 0409 static constexpr size_t Alignment() { 0410 return adl_barrier::Max(AlignOf<Elements>::value...); 0411 } 0412 0413 // Offset in bytes of the Nth array. 0414 // 0415 // // int[3], 4 bytes of padding, double[4]. 0416 // Layout<int, double> x(3, 4); 0417 // assert(x.Offset<0>() == 0); // The ints starts from 0. 0418 // assert(x.Offset<1>() == 16); // The doubles starts from 16. 0419 // 0420 // Requires: `N <= NumSizes && N < sizeof...(Ts)`. 0421 template <size_t N, EnableIf<N == 0> = 0> 0422 constexpr size_t Offset() const { 0423 return 0; 0424 } 0425 0426 template <size_t N, EnableIf<N != 0> = 0> 0427 constexpr size_t Offset() const { 0428 static_assert(N < NumOffsets, "Index out of bounds"); 0429 return adl_barrier::Align( 0430 Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * Size<N - 1>(), 0431 ElementAlignment<N>::value); 0432 } 0433 0434 // Offset in bytes of the array with the specified element type. There must 0435 // be exactly one such array and its zero-based index must be at most 0436 // `NumSizes`. 0437 // 0438 // // int[3], 4 bytes of padding, double[4]. 0439 // Layout<int, double> x(3, 4); 0440 // assert(x.Offset<int>() == 0); // The ints starts from 0. 0441 // assert(x.Offset<double>() == 16); // The doubles starts from 16. 0442 template <class T> 0443 constexpr size_t Offset() const { 0444 return Offset<ElementIndex<T>()>(); 0445 } 0446 0447 // Offsets in bytes of all arrays for which the offsets are known. 0448 constexpr std::array<size_t, NumOffsets> Offsets() const { 0449 return {{Offset<OffsetSeq>()...}}; 0450 } 0451 0452 // The number of elements in the Nth array (zero-based). 0453 // 0454 // // int[3], 4 bytes of padding, double[4]. 0455 // Layout<int, double> x(3, 4); 0456 // assert(x.Size<0>() == 3); 0457 // assert(x.Size<1>() == 4); 0458 // 0459 // Requires: `N < NumSizes`. 0460 template <size_t N, EnableIf<(N < NumStaticSizes)> = 0> 0461 constexpr size_t Size() const { 0462 return kStaticSizes[N]; 0463 } 0464 0465 template <size_t N, EnableIf<(N >= NumStaticSizes)> = 0> 0466 constexpr size_t Size() const { 0467 static_assert(N < NumSizes, "Index out of bounds"); 0468 return size_[N - NumStaticSizes]; 0469 } 0470 0471 // The number of elements in the array with the specified element type. 0472 // There must be exactly one such array and its zero-based index must be 0473 // at most `NumSizes`. 0474 // 0475 // // int[3], 4 bytes of padding, double[4]. 0476 // Layout<int, double> x(3, 4); 0477 // assert(x.Size<int>() == 3); 0478 // assert(x.Size<double>() == 4); 0479 template <class T> 0480 constexpr size_t Size() const { 0481 return Size<ElementIndex<T>()>(); 0482 } 0483 0484 // The number of elements of all arrays for which they are known. 0485 constexpr std::array<size_t, NumSizes> Sizes() const { 0486 return {{Size<SizeSeq>()...}}; 0487 } 0488 0489 // Pointer to the beginning of the Nth array. 0490 // 0491 // `Char` must be `[const] [signed|unsigned] char`. 0492 // 0493 // // int[3], 4 bytes of padding, double[4]. 0494 // Layout<int, double> x(3, 4); 0495 // unsigned char* p = new unsigned char[x.AllocSize()]; 0496 // int* ints = x.Pointer<0>(p); 0497 // double* doubles = x.Pointer<1>(p); 0498 // 0499 // Requires: `N <= NumSizes && N < sizeof...(Ts)`. 0500 // Requires: `p` is aligned to `Alignment()`. 0501 template <size_t N, class Char> 0502 CopyConst<Char, ElementType<N>>* Pointer(Char* p) const { 0503 using C = typename std::remove_const<Char>::type; 0504 static_assert( 0505 std::is_same<C, char>() || std::is_same<C, unsigned char>() || 0506 std::is_same<C, signed char>(), 0507 "The argument must be a pointer to [const] [signed|unsigned] char"); 0508 constexpr size_t alignment = Alignment(); 0509 (void)alignment; 0510 assert(reinterpret_cast<uintptr_t>(p) % alignment == 0); 0511 return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>()); 0512 } 0513 0514 // Pointer to the beginning of the array with the specified element type. 0515 // There must be exactly one such array and its zero-based index must be at 0516 // most `NumSizes`. 0517 // 0518 // `Char` must be `[const] [signed|unsigned] char`. 0519 // 0520 // // int[3], 4 bytes of padding, double[4]. 0521 // Layout<int, double> x(3, 4); 0522 // unsigned char* p = new unsigned char[x.AllocSize()]; 0523 // int* ints = x.Pointer<int>(p); 0524 // double* doubles = x.Pointer<double>(p); 0525 // 0526 // Requires: `p` is aligned to `Alignment()`. 0527 template <class T, class Char> 0528 CopyConst<Char, T>* Pointer(Char* p) const { 0529 return Pointer<ElementIndex<T>()>(p); 0530 } 0531 0532 // Pointers to all arrays for which pointers are known. 0533 // 0534 // `Char` must be `[const] [signed|unsigned] char`. 0535 // 0536 // // int[3], 4 bytes of padding, double[4]. 0537 // Layout<int, double> x(3, 4); 0538 // unsigned char* p = new unsigned char[x.AllocSize()]; 0539 // 0540 // int* ints; 0541 // double* doubles; 0542 // std::tie(ints, doubles) = x.Pointers(p); 0543 // 0544 // Requires: `p` is aligned to `Alignment()`. 0545 template <class Char> 0546 auto Pointers(Char* p) const { 0547 return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>( 0548 Pointer<OffsetSeq>(p)...); 0549 } 0550 0551 // The Nth array. 0552 // 0553 // `Char` must be `[const] [signed|unsigned] char`. 0554 // 0555 // // int[3], 4 bytes of padding, double[4]. 0556 // Layout<int, double> x(3, 4); 0557 // unsigned char* p = new unsigned char[x.AllocSize()]; 0558 // Span<int> ints = x.Slice<0>(p); 0559 // Span<double> doubles = x.Slice<1>(p); 0560 // 0561 // Requires: `N < NumSizes`. 0562 // Requires: `p` is aligned to `Alignment()`. 0563 template <size_t N, class Char> 0564 SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const { 0565 return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>()); 0566 } 0567 0568 // The array with the specified element type. There must be exactly one 0569 // such array and its zero-based index must be less than `NumSizes`. 0570 // 0571 // `Char` must be `[const] [signed|unsigned] char`. 0572 // 0573 // // int[3], 4 bytes of padding, double[4]. 0574 // Layout<int, double> x(3, 4); 0575 // unsigned char* p = new unsigned char[x.AllocSize()]; 0576 // Span<int> ints = x.Slice<int>(p); 0577 // Span<double> doubles = x.Slice<double>(p); 0578 // 0579 // Requires: `p` is aligned to `Alignment()`. 0580 template <class T, class Char> 0581 SliceType<CopyConst<Char, T>> Slice(Char* p) const { 0582 return Slice<ElementIndex<T>()>(p); 0583 } 0584 0585 // All arrays with known sizes. 0586 // 0587 // `Char` must be `[const] [signed|unsigned] char`. 0588 // 0589 // // int[3], 4 bytes of padding, double[4]. 0590 // Layout<int, double> x(3, 4); 0591 // unsigned char* p = new unsigned char[x.AllocSize()]; 0592 // 0593 // Span<int> ints; 0594 // Span<double> doubles; 0595 // std::tie(ints, doubles) = x.Slices(p); 0596 // 0597 // Requires: `p` is aligned to `Alignment()`. 0598 // 0599 // Note: We mark the parameter as unused because GCC detects it is not used 0600 // when `SizeSeq` is empty [-Werror=unused-but-set-parameter]. 0601 template <class Char> 0602 auto Slices(ABSL_ATTRIBUTE_UNUSED Char* p) const { 0603 return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>( 0604 Slice<SizeSeq>(p)...); 0605 } 0606 0607 // The size of the allocation that fits all arrays. 0608 // 0609 // // int[3], 4 bytes of padding, double[4]. 0610 // Layout<int, double> x(3, 4); 0611 // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes 0612 // 0613 // Requires: `NumSizes == sizeof...(Ts)`. 0614 constexpr size_t AllocSize() const { 0615 static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); 0616 return Offset<NumTypes - 1>() + 0617 SizeOf<ElementType<NumTypes - 1>>::value * Size<NumTypes - 1>(); 0618 } 0619 0620 // If built with --config=asan, poisons padding bytes (if any) in the 0621 // allocation. The pointer must point to a memory block at least 0622 // `AllocSize()` bytes in length. 0623 // 0624 // `Char` must be `[const] [signed|unsigned] char`. 0625 // 0626 // Requires: `p` is aligned to `Alignment()`. 0627 template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0> 0628 void PoisonPadding(const Char* p) const { 0629 Pointer<0>(p); // verify the requirements on `Char` and `p` 0630 } 0631 0632 template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0> 0633 void PoisonPadding(const Char* p) const { 0634 static_assert(N < NumOffsets, "Index out of bounds"); 0635 (void)p; 0636 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 0637 PoisonPadding<Char, N - 1>(p); 0638 // The `if` is an optimization. It doesn't affect the observable behaviour. 0639 if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) { 0640 size_t start = 0641 Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * Size<N - 1>(); 0642 ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start); 0643 } 0644 #endif 0645 } 0646 0647 // Human-readable description of the memory layout. Useful for debugging. 0648 // Slow. 0649 // 0650 // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed 0651 // // by an unknown number of doubles. 0652 // auto x = Layout<char, int, double>::Partial(5, 3); 0653 // assert(x.DebugString() == 0654 // "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)"); 0655 // 0656 // Each field is in the following format: @offset<type>(sizeof)[size] (<type> 0657 // may be missing depending on the target platform). For example, 0658 // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each 0659 // int is 4 bytes, and we have 3 of those ints. The size of the last field may 0660 // be missing (as in the example above). Only fields with known offsets are 0661 // described. Type names may differ across platforms: one compiler might 0662 // produce "unsigned*" where another produces "unsigned int *". 0663 std::string DebugString() const { 0664 const auto offsets = Offsets(); 0665 const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>::value...}; 0666 const std::string types[] = { 0667 adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; 0668 std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); 0669 for (size_t i = 0; i != NumOffsets - 1; ++i) { 0670 absl::StrAppend(&res, "[", DebugSize(i), "]; @", offsets[i + 1], 0671 types[i + 1], "(", sizes[i + 1], ")"); 0672 } 0673 // NumSizes is a constant that may be zero. Some compilers cannot see that 0674 // inside the if statement "size_[NumSizes - 1]" must be valid. 0675 int last = static_cast<int>(NumSizes) - 1; 0676 if (NumTypes == NumSizes && last >= 0) { 0677 absl::StrAppend(&res, "[", DebugSize(static_cast<size_t>(last)), "]"); 0678 } 0679 return res; 0680 } 0681 0682 private: 0683 size_t DebugSize(size_t n) const { 0684 if (n < NumStaticSizes) { 0685 return kStaticSizes[n]; 0686 } else { 0687 return size_[n - NumStaticSizes]; 0688 } 0689 } 0690 0691 // Arguments of `Layout::Partial()` or `Layout::Layout()`. 0692 size_t size_[NumRuntimeSizes > 0 ? NumRuntimeSizes : 1]; 0693 }; 0694 0695 // Defining a constexpr static class member variable is redundant and deprecated 0696 // in C++17, but required in C++14. 0697 template <class... Elements, size_t... StaticSizeSeq, size_t... RuntimeSizeSeq, 0698 size_t... SizeSeq, size_t... OffsetSeq> 0699 constexpr std::array<size_t, sizeof...(StaticSizeSeq)> LayoutImpl< 0700 std::tuple<Elements...>, absl::index_sequence<StaticSizeSeq...>, 0701 absl::index_sequence<RuntimeSizeSeq...>, absl::index_sequence<SizeSeq...>, 0702 absl::index_sequence<OffsetSeq...>>::kStaticSizes; 0703 0704 template <class StaticSizeSeq, size_t NumRuntimeSizes, class... Ts> 0705 using LayoutType = LayoutImpl< 0706 std::tuple<Ts...>, StaticSizeSeq, 0707 absl::make_index_sequence<NumRuntimeSizes>, 0708 absl::make_index_sequence<NumRuntimeSizes + StaticSizeSeq::size()>, 0709 absl::make_index_sequence<adl_barrier::Min( 0710 sizeof...(Ts), NumRuntimeSizes + StaticSizeSeq::size() + 1)>>; 0711 0712 template <class StaticSizeSeq, class... Ts> 0713 class LayoutWithStaticSizes 0714 : public LayoutType<StaticSizeSeq, 0715 sizeof...(Ts) - adl_barrier::Min(sizeof...(Ts), 0716 StaticSizeSeq::size()), 0717 Ts...> { 0718 private: 0719 using Super = 0720 LayoutType<StaticSizeSeq, 0721 sizeof...(Ts) - 0722 adl_barrier::Min(sizeof...(Ts), StaticSizeSeq::size()), 0723 Ts...>; 0724 0725 public: 0726 // The result type of `Partial()` with `NumSizes` arguments. 0727 template <size_t NumSizes> 0728 using PartialType = 0729 internal_layout::LayoutType<StaticSizeSeq, NumSizes, Ts...>; 0730 0731 // `Layout` knows the element types of the arrays we want to lay out in 0732 // memory but not the number of elements in each array. 0733 // `Partial(size1, ..., sizeN)` allows us to specify the latter. The 0734 // resulting immutable object can be used to obtain pointers to the 0735 // individual arrays. 0736 // 0737 // It's allowed to pass fewer array sizes than the number of arrays. E.g., 0738 // if all you need is to the offset of the second array, you only need to 0739 // pass one argument -- the number of elements in the first array. 0740 // 0741 // // int[3] followed by 4 bytes of padding and an unknown number of 0742 // // doubles. 0743 // auto x = Layout<int, double>::Partial(3); 0744 // // doubles start at byte 16. 0745 // assert(x.Offset<1>() == 16); 0746 // 0747 // If you know the number of elements in all arrays, you can still call 0748 // `Partial()` but it's more convenient to use the constructor of `Layout`. 0749 // 0750 // Layout<int, double> x(3, 5); 0751 // 0752 // Note: The sizes of the arrays must be specified in number of elements, 0753 // not in bytes. 0754 // 0755 // Requires: `sizeof...(Sizes) + NumStaticSizes <= sizeof...(Ts)`. 0756 // Requires: all arguments are convertible to `size_t`. 0757 template <class... Sizes> 0758 static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) { 0759 static_assert(sizeof...(Sizes) + StaticSizeSeq::size() <= sizeof...(Ts), 0760 ""); 0761 return PartialType<sizeof...(Sizes)>( 0762 static_cast<size_t>(std::forward<Sizes>(sizes))...); 0763 } 0764 0765 // Inherit LayoutType's constructor. 0766 // 0767 // Creates a layout with the sizes of all arrays specified. If you know 0768 // only the sizes of the first N arrays (where N can be zero), you can use 0769 // `Partial()` defined above. The constructor is essentially equivalent to 0770 // calling `Partial()` and passing in all array sizes; the constructor is 0771 // provided as a convenient abbreviation. 0772 // 0773 // Note: The sizes of the arrays must be specified in number of elements, 0774 // not in bytes. 0775 // 0776 // Implementation note: we do this via a `using` declaration instead of 0777 // defining our own explicit constructor because the signature of LayoutType's 0778 // constructor depends on RuntimeSizeSeq, which we don't have access to here. 0779 // If we defined our own constructor here, it would have to use a parameter 0780 // pack and then cast the arguments to size_t when calling the superclass 0781 // constructor, similar to what Partial() does. But that would suffer from the 0782 // same problem that Partial() has, which is that the parameter types are 0783 // inferred from the arguments, which may be signed types, which must then be 0784 // cast to size_t. This can lead to negative values being silently (i.e. with 0785 // no compiler warnings) cast to an unsigned type. Having a constructor with 0786 // size_t parameters helps the compiler generate better warnings about 0787 // potential bad casts, while avoiding false warnings when positive literal 0788 // arguments are used. If an argument is a positive literal integer (e.g. 0789 // `1`), the compiler will understand that it can be safely converted to 0790 // size_t, and hence not generate a warning. But if a negative literal (e.g. 0791 // `-1`) or a variable with signed type is used, then it can generate a 0792 // warning about a potentially unsafe implicit cast. It would be great if we 0793 // could do this for Partial() too, but unfortunately as of C++23 there seems 0794 // to be no way to define a function with a variable number of parameters of a 0795 // certain type, a.k.a. homogeneous function parameter packs. So we're forced 0796 // to choose between explicitly casting the arguments to size_t, which 0797 // suppresses all warnings, even potentially valid ones, or implicitly casting 0798 // them to size_t, which generates bogus warnings whenever literal arguments 0799 // are used, even if they're positive. 0800 using Super::Super; 0801 }; 0802 0803 } // namespace internal_layout 0804 0805 // Descriptor of arrays of various types and sizes laid out in memory one after 0806 // another. See the top of the file for documentation. 0807 // 0808 // Check out the public API of internal_layout::LayoutWithStaticSizes and 0809 // internal_layout::LayoutImpl above. Those types are internal to the library 0810 // but their methods are public, and they are inherited by `Layout`. 0811 template <class... Ts> 0812 class Layout : public internal_layout::LayoutWithStaticSizes< 0813 absl::make_index_sequence<0>, Ts...> { 0814 private: 0815 using Super = 0816 internal_layout::LayoutWithStaticSizes<absl::make_index_sequence<0>, 0817 Ts...>; 0818 0819 public: 0820 // If you know the sizes of some or all of the arrays at compile time, you can 0821 // use `WithStaticSizes` or `WithStaticSizeSequence` to create a `Layout` type 0822 // with those sizes baked in. This can help the compiler generate optimal code 0823 // for calculating array offsets and AllocSize(). 0824 // 0825 // Like `Partial()`, the N sizes you specify are for the first N arrays, and 0826 // they specify the number of elements in each array, not the number of bytes. 0827 template <class StaticSizeSeq> 0828 using WithStaticSizeSequence = 0829 internal_layout::LayoutWithStaticSizes<StaticSizeSeq, Ts...>; 0830 0831 template <size_t... StaticSizes> 0832 using WithStaticSizes = 0833 WithStaticSizeSequence<std::index_sequence<StaticSizes...>>; 0834 0835 // Inherit LayoutWithStaticSizes's constructor, which requires you to specify 0836 // all the array sizes. 0837 using Super::Super; 0838 }; 0839 0840 } // namespace container_internal 0841 ABSL_NAMESPACE_END 0842 } // namespace absl 0843 0844 #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |