Coverage Report

Created: 2023-06-07 07:09

/src/LPM/external.protobuf/include/absl/container/internal/layout.h
Line
Count
Source (jump to first uncovered line)
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
0
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
0
constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); }
284
285
0
constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; }
286
287
0
constexpr size_t Max(size_t a) { return a; }
288
289
template <class... Ts>
290
0
constexpr size_t Max(size_t a, size_t b, Ts... rest) {
291
0
  return adl_barrier::Max(b < a ? a : b, rest...);
292
0
}
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::adl_barrier::Max<>(unsigned long, unsigned long)
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::adl_barrier::Max<unsigned long>(unsigned long, unsigned long, unsigned long)
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
0
  static constexpr size_t Alignment() {
387
0
    return adl_barrier::Max(AlignOf<Elements>::value...);
388
0
  }
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long>, absl::lts_20230125::integer_sequence<unsigned long, 0ul> >::Alignment()
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul> >::Alignment()
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Alignment()
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
0
  constexpr size_t Offset() const {
400
0
    return 0;
401
0
  }
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Offset<0ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20230125::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::Offset<0ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long>, absl::lts_20230125::integer_sequence<unsigned long, 0ul> >::Offset<0ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul> >::Offset<0ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Offset<0ul, 0>() const
402
403
  template <size_t N, EnableIf<N != 0> = 0>
404
0
  constexpr size_t Offset() const {
405
0
    static_assert(N < NumOffsets, "Index out of bounds");
406
0
    return adl_barrier::Align(
407
0
        Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1],
408
0
        ElementAlignment<N>::value);
409
0
  }
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Offset<1ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Offset<2ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20230125::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::Offset<1ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20230125::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::Offset<2ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20230125::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::Offset<3ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20230125::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::Offset<4ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul> >::Offset<1ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Offset<1ul, 0>() const
Unexecuted instantiation: unsigned long absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::Offset<2ul, 0>() const
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
0
  CopyConst<Char, ElementType<N>>* Pointer(Char* p) const {
476
0
    using C = typename std::remove_const<Char>::type;
477
0
    static_assert(
478
0
        std::is_same<C, char>() || std::is_same<C, unsigned char>() ||
479
0
            std::is_same<C, signed char>(),
480
0
        "The argument must be a pointer to [const] [signed|unsigned] char");
481
0
    constexpr size_t alignment = Alignment();
482
0
    (void)alignment;
483
0
    assert(reinterpret_cast<uintptr_t>(p) % alignment == 0);
484
0
    return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>());
485
0
  }
Unexecuted instantiation: _ZNK4absl12lts_2023012518container_internal15internal_layout10LayoutImplINSt3__15tupleIJmPNS0_13cord_internal7CordRepEjEEENS0_16integer_sequenceImJEEENSA_ImJLm0EEEEE7PointerILm0EKcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ES9_E4typeESK_E4typeEPSH_
Unexecuted instantiation: _ZNK4absl12lts_2023012518container_internal15internal_layout10LayoutImplINSt3__15tupleIJmPNS0_13cord_internal7CordRepEjEEENS0_16integer_sequenceImJLm0EEEENSA_ImJLm0ELm1EEEEE7PointerILm1EKcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ES9_E4typeESK_E4typeEPSH_
Unexecuted instantiation: _ZNK4absl12lts_2023012518container_internal15internal_layout10LayoutImplINSt3__15tupleIJmPNS0_13cord_internal7CordRepEjEEENS0_16integer_sequenceImJLm0ELm1EEEENSA_ImJLm0ELm1ELm2EEEEE7PointerILm2EKcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ES9_E4typeESK_E4typeEPSH_
Unexecuted instantiation: _ZNK4absl12lts_2023012518container_internal15internal_layout10LayoutImplINSt3__15tupleIJmPNS0_13cord_internal7CordRepEjEEENS0_16integer_sequenceImJEEENSA_ImJLm0EEEEE7PointerILm0EcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ES9_E4typeESJ_E4typeEPSG_
Unexecuted instantiation: _ZNK4absl12lts_2023012518container_internal15internal_layout10LayoutImplINSt3__15tupleIJmPNS0_13cord_internal7CordRepEjEEENS0_16integer_sequenceImJLm0EEEENSA_ImJLm0ELm1EEEEE7PointerILm1EcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ES9_E4typeESJ_E4typeEPSG_
Unexecuted instantiation: _ZNK4absl12lts_2023012518container_internal15internal_layout10LayoutImplINSt3__15tupleIJmPNS0_13cord_internal7CordRepEjEEENS0_16integer_sequenceImJLm0ELm1EEEENSA_ImJLm0ELm1ELm2EEEEE7PointerILm2EcEEPNS4_11conditionalIXsr3std8is_constIT0_EE5valueEKNS4_13tuple_elementIXT_ES9_E4typeESJ_E4typeEPSG_
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
0
  constexpr size_t AllocSize() const {
598
0
    static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
599
0
    return Offset<NumTypes - 1>() +
600
0
        SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1];
601
0
  }
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul> >::AllocSize() const
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*, unsigned int, unsigned char, absl::lts_20230125::container_internal::map_slot_type<int, google::protobuf::internal::ExtensionSet::Extension>, absl::lts_20230125::container_internal::btree_node<absl::lts_20230125::container_internal::map_params<int, google::protobuf::internal::ExtensionSet::Extension, std::__1::less<int>, std::__1::allocator<std::__1::pair<int const, google::protobuf::internal::ExtensionSet::Extension> >, 256, false> >*>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>, absl::lts_20230125::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul> >::AllocSize() const
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
0
  static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) {
723
0
    static_assert(sizeof...(Sizes) <= sizeof...(Ts), "");
724
0
    return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...);
725
0
  }
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::utility_internal::Gen<unsigned long, 0>::type, absl::lts_20230125::utility_internal::Gen<unsigned long, absl::lts_20230125::container_internal::internal_layout::adl_barrier::Min(0, (0)+(1))>::type> absl::lts_20230125::container_internal::Layout<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>::Partial<>()
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::utility_internal::Gen<unsigned long, 1>::type, absl::lts_20230125::utility_internal::Gen<unsigned long, absl::lts_20230125::container_internal::internal_layout::adl_barrier::Min(0, (1)+(1))>::type> absl::lts_20230125::container_internal::Layout<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>::Partial<unsigned int>(unsigned int&&)
Unexecuted instantiation: absl::lts_20230125::container_internal::internal_layout::LayoutImpl<std::__1::tuple<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>, absl::lts_20230125::utility_internal::Gen<unsigned long, 2>::type, absl::lts_20230125::utility_internal::Gen<unsigned long, absl::lts_20230125::container_internal::internal_layout::adl_barrier::Min(0, (2)+(1))>::type> absl::lts_20230125::container_internal::Layout<unsigned long, absl::lts_20230125::cord_internal::CordRep*, unsigned int>::Partial<unsigned int, unsigned int>(unsigned int&&, unsigned int&&)
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_