tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TiedFields.h (7692B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef DOM_CANVAS_TIED_FIELDS_H
      6 #define DOM_CANVAS_TIED_FIELDS_H
      7 
      8 #include <array>
      9 
     10 #include "TupleUtils.h"
     11 
     12 namespace mozilla {
     13 
     14 // -
     15 
     16 /**
     17 * TiedFields(T&) -> std::tuple<Fields&...>
     18 * TiedFields(const T&) -> std::tuple<const Fields&...>
     19 *
     20 * You can also overload TiedFields without adding T::MutTiedFields:
     21 * template<>
     22 * inline auto TiedFields<gfx::IntSize>(gfx::IntSize& a) {
     23 *   return std::tie(a.width, a.height);
     24 * }
     25 */
     26 template <class T>
     27 constexpr auto TiedFields(T& t) {
     28  const auto fields = t.MutTiedFields();
     29  return fields;
     30 }
     31 template <class T, class... Args, class Tup = std::tuple<Args&...>>
     32 constexpr auto TiedFields(const T& t) {
     33  // Uncast const to get mutable-fields tuple, but reapply const to tuple args.
     34  // We should do better than this when C++ gets a solution other than macros.
     35  const auto mutFields = TiedFields(const_cast<T&>(t));
     36  return ToTupleOfConstRefs(mutFields);
     37 }
     38 
     39 /**
     40 * Returns true if all bytes in T are accounted for via size of all tied fields.
     41 * Returns false if there's bytes unaccounted for, which might indicate either
     42 * unaccounted-for padding or missing fields.
     43 * The goal is to check that TiedFields returns every field in T, and this
     44 * returns false if it suspects there are bytes that are not accounted for by
     45 * TiedFields.
     46 *
     47 * `constexpr` effectively cannot do math on pointers, so it's not possible to
     48 * figure out via `constexpr` whether fields are consecutive or dense.
     49 * However, we can at least compare `sizeof(T)` to the sum of `sizeof(Args...)`
     50 * for `TiedFields(T) -> std::tuple<Args...>`.
     51 *
     52 * See TiedFieldsExamples.
     53 */
     54 template <class T>
     55 constexpr bool AreAllBytesTiedFields() {
     56  using fieldsT = decltype(TiedFields(std::declval<T>()));
     57  const auto fields_size_sum = SizeofTupleArgs<fieldsT>::value;
     58  const auto t_size = sizeof(T);
     59  return fields_size_sum == t_size;
     60 }
     61 
     62 // It's also possible to determine AreAllBytesRecursiveTiedFields:
     63 // https://hackmd.io/@jgilbert/B16qa0Fa9
     64 
     65 // -
     66 
     67 template <class StructT, size_t FieldId, size_t PrevFieldBeginOffset,
     68          class PrevFieldT, size_t PrevFieldEndOffset, class FieldT,
     69          size_t FieldAlignment = alignof(FieldT)>
     70 struct FieldDebugInfoT {
     71  static constexpr bool IsTightlyPacked() {
     72    return PrevFieldEndOffset % FieldAlignment == 0;
     73  }
     74 };
     75 
     76 template <class StructT, class TupleOfFields, size_t FieldId>
     77 struct TightlyPackedFieldEndOffsetT {
     78  template <size_t I>
     79  using FieldTAt = std::remove_reference_t<
     80      typename std::tuple_element<I, TupleOfFields>::type>;
     81 
     82  static constexpr size_t Fn() {
     83    constexpr auto num_fields = std::tuple_size_v<TupleOfFields>;
     84    static_assert(FieldId < num_fields);
     85 
     86    using PrevFieldT = FieldTAt<FieldId - 1>;
     87    using FieldT = FieldTAt<FieldId>;
     88    constexpr auto prev_field_end_offset =
     89        TightlyPackedFieldEndOffsetT<StructT, TupleOfFields, FieldId - 1>::Fn();
     90    constexpr auto prev_field_begin_offset =
     91        prev_field_end_offset - sizeof(PrevFieldT);
     92 
     93    using FieldDebugInfoT =
     94        FieldDebugInfoT<StructT, FieldId, prev_field_begin_offset, PrevFieldT,
     95                        prev_field_end_offset, FieldT>;
     96    static_assert(FieldDebugInfoT::IsTightlyPacked(),
     97                  "This field was not tightly packed. Is there padding between "
     98                  "it and its predecessor?");
     99 
    100    return prev_field_end_offset + sizeof(FieldT);
    101  }
    102 };
    103 
    104 template <class StructT, class TupleOfFields>
    105 struct TightlyPackedFieldEndOffsetT<StructT, TupleOfFields, 0> {
    106  static constexpr size_t Fn() {
    107    using FieldT = typename std::tuple_element<0, TupleOfFields>::type;
    108    return sizeof(FieldT);
    109  }
    110 };
    111 template <class StructT, class TupleOfFields>
    112 struct TightlyPackedFieldEndOffsetT<StructT, TupleOfFields, size_t(-1)> {
    113  static constexpr size_t Fn() {
    114    // -1 means tuple_size_v<TupleOfFields> -> 0.
    115    static_assert(sizeof(StructT) == 0);
    116    return 0;
    117  }
    118 };
    119 
    120 template <class StructT>
    121 constexpr bool AssertTiedFieldsAreExhaustive() {
    122  static_assert(AreAllBytesTiedFields<StructT>());
    123 
    124  using TupleOfFields = decltype(TiedFields(std::declval<StructT&>()));
    125  constexpr auto num_fields = std::tuple_size_v<TupleOfFields>;
    126  constexpr auto end_offset_of_last_field =
    127      TightlyPackedFieldEndOffsetT<StructT, TupleOfFields,
    128                                   num_fields - 1>::Fn();
    129  static_assert(
    130      end_offset_of_last_field == sizeof(StructT),
    131      "Incorrect field list in MutTiedFields()? (or not tightly-packed?)");
    132  return true;  // Support `static_assert(AssertTiedFieldsAreExhaustive())`.
    133 }
    134 
    135 // -
    136 
    137 /**
    138 * PaddingField<T,N=1> can be used to pad out a struct so that it's not
    139 * implicitly padded by struct rules, but also can't be accidentally initialized
    140 * via Aggregate Initialization. (TiedFields serialization checks rely on object
    141 * fields leaving no implicit padding bytes, but explicit padding fields are
    142 * fine) While you can use e.g. `uint8_t _padding[3];`, consider instead
    143 * `PaddingField<uint8_t,3> _padding;` for clarity and to move the `3` nearer
    144 * to the `uint8_t`.
    145 */
    146 template <class T, size_t N = 1>
    147 struct PaddingField {
    148  static_assert(!std::is_array_v<T>, "Use PaddingField<T,N> not <T[N]>.");
    149 
    150  std::array<T, N> ignored = {};
    151 
    152  PaddingField() {}
    153 
    154  friend constexpr bool operator==(const PaddingField&, const PaddingField&) {
    155    return true;
    156  }
    157  friend constexpr bool operator<(const PaddingField&, const PaddingField&) {
    158    return false;
    159  }
    160 
    161  auto MutTiedFields() { return std::tie(ignored); }
    162 };
    163 static_assert(sizeof(PaddingField<bool>) == 1);
    164 static_assert(sizeof(PaddingField<bool, 2>) == 2);
    165 static_assert(sizeof(PaddingField<int>) == 4);
    166 
    167 // -
    168 
    169 namespace TiedFieldsExamples {
    170 
    171 struct Cat {
    172  int i;
    173  bool b;
    174 
    175  constexpr auto MutTiedFields() { return std::tie(i, b); }
    176 };
    177 static_assert(sizeof(Cat) == 8);
    178 static_assert(!AreAllBytesTiedFields<Cat>());
    179 
    180 struct Dog {
    181  bool b;
    182  int i;
    183 
    184  constexpr auto MutTiedFields() { return std::tie(i, b); }
    185 };
    186 static_assert(sizeof(Dog) == 8);
    187 static_assert(!AreAllBytesTiedFields<Dog>());
    188 
    189 struct Fish {
    190  bool b;
    191  bool padding[3];
    192  int i;
    193 
    194  constexpr auto MutTiedFields() { return std::tie(i, b, padding); }
    195 };
    196 static_assert(sizeof(Fish) == 8);
    197 static_assert(AreAllBytesTiedFields<Fish>());
    198 
    199 struct Eel {  // Like a Fish, but you can skip serializing the padding.
    200  bool b;
    201  PaddingField<bool, 3> padding;
    202  int i;
    203 
    204  constexpr auto MutTiedFields() { return std::tie(i, b, padding); }
    205 };
    206 static_assert(sizeof(Eel) == 8);
    207 static_assert(AreAllBytesTiedFields<Eel>());
    208 
    209 // -
    210 
    211 // #define LETS_USE_BIT_FIELDS
    212 #ifdef LETS_USE_BIT_FIELDS
    213 #  undef LETS_USE_BIT_FIELDS
    214 
    215 struct Platypus {
    216  short s : 1;
    217  short s2 : 1;
    218  int i;
    219 
    220  constexpr auto MutTiedFields() {
    221    return std::tie(s, s2, i);  // Error: Can't take reference to bit-field.
    222  }
    223 };
    224 
    225 #endif
    226 
    227 // -
    228 
    229 struct FishTank {
    230  Fish f;
    231  int i2;
    232 
    233  constexpr auto MutTiedFields() { return std::tie(f, i2); }
    234 };
    235 static_assert(sizeof(FishTank) == 12);
    236 static_assert(AreAllBytesTiedFields<FishTank>());
    237 
    238 struct CatCarrier {
    239  Cat c;
    240  int i2;
    241 
    242  constexpr auto MutTiedFields() { return std::tie(c, i2); }
    243 };
    244 static_assert(sizeof(CatCarrier) == 12);
    245 static_assert(AreAllBytesTiedFields<CatCarrier>());
    246 static_assert(
    247    !AreAllBytesTiedFields<decltype(CatCarrier::c)>());  // BUT BEWARE THIS!
    248 // For example, if we had AreAllBytesRecursiveTiedFields:
    249 // static_assert(!AreAllBytesRecursiveTiedFields<CatCarrier>());
    250 
    251 }  // namespace TiedFieldsExamples
    252 }  // namespace mozilla
    253 
    254 #endif  // DOM_CANVAS_TIED_FIELDS_H