Bug 1839170 - Refactor Snap pulling, Add Firefox Snap Core22 and GNOME 42 SDK symbols...
[gecko.git] / dom / canvas / TiedFields.h
blob4b0630056844b8839db23a98e187bed0ac24d1b7
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/. */
5 #ifndef DOM_CANVAS_TIED_FIELDS_H
6 #define DOM_CANVAS_TIED_FIELDS_H
8 #include "TupleUtils.h"
10 namespace mozilla {
12 // -
14 /**
15 * TiedFields(T&) -> std::tuple<Fields&...>
16 * TiedFields(const T&) -> std::tuple<const Fields&...>
18 * You can also overload TiedFields without adding T::MutTiedFields:
19 * template<>
20 * inline auto TiedFields<gfx::IntSize>(gfx::IntSize& a) {
21 * return std::tie(a.width, a.height);
22 * }
24 template <class T>
25 constexpr auto TiedFields(T& t) {
26 const auto fields = t.MutTiedFields();
27 return fields;
29 template <class T, class... Args, class Tup = std::tuple<Args&...>>
30 constexpr auto TiedFields(const T& t) {
31 // Uncast const to get mutable-fields tuple, but reapply const to tuple args.
32 // We should do better than this when C++ gets a solution other than macros.
33 const auto mutFields = TiedFields(const_cast<T&>(t));
34 return ToTupleOfConstRefs(mutFields);
37 /**
38 * Returns true if all bytes in T are accounted for via size of all tied fields.
39 * Returns false if there's bytes unaccounted for, which might indicate either
40 * unaccounted-for padding or missing fields.
41 * The goal is to check that TiedFields returns every field in T, and this
42 * returns false if it suspects there are bytes that are not accounted for by
43 * TiedFields.
45 * `constexpr` effectively cannot do math on pointers, so it's not possible to
46 * figure out via `constexpr` whether fields are consecutive or dense.
47 * However, we can at least compare `sizeof(T)` to the sum of `sizeof(Args...)`
48 * for `TiedFields(T) -> std::tuple<Args...>`.
50 * See TiedFieldsExamples.
52 template <class T>
53 constexpr bool AreAllBytesTiedFields() {
54 using fieldsT = decltype(TiedFields(std::declval<T>()));
55 const auto fields_size_sum = SizeofTupleArgs<fieldsT>::value;
56 const auto t_size = sizeof(T);
57 return fields_size_sum == t_size;
60 // It's also possible to determine AreAllBytesRecursiveTiedFields:
61 // https://hackmd.io/@jgilbert/B16qa0Fa9
63 // -
65 /**
66 * Padding<T> can be used to pad out a struct so that it's not implicitly
67 * padded by struct rules.
68 * You can also just add your padding to TiedFields, but by explicitly typing
69 * padding like this, serialization can make a choice whether to copy Padding,
70 * or instead to omit the copy.
72 * Omitting the copy isn't always faster.
73 * struct Entry {
74 * uint16_t key;
75 * Padding<uint16_t> padding;
76 * uint32_t val;
77 * auto MutTiedFields() { return std::tie(key, padding, val); }
78 * };
79 * If you serialize Padding, the serialized size is 8, and the compiler will
80 * optimize serialization to a single 8-byte memcpy.
81 * If your serialization omits Padding, the serialized size of Entry shrinks
82 * by 25%. If you have a big list of Entrys, maybe this is a big savings!
83 * However, by optimizing for size here you sacrifice speed, because this splits
84 * the single memcpy into two: a 2-byte memcpy and a 4-byte memcpy.
86 * Explicitly marking padding gives callers the option of choosing.
88 template <class T>
89 struct Padding {
90 T ignored;
92 friend constexpr bool operator==(const Padding&, const Padding&) {
93 return true;
95 friend constexpr bool operator<(const Padding&, const Padding&) {
96 return false;
99 static_assert(sizeof(Padding<bool>) == 1);
100 static_assert(sizeof(Padding<bool[2]>) == 2);
101 static_assert(sizeof(Padding<int>) == 4);
103 // -
105 namespace TiedFieldsExamples {
107 struct Cat {
108 int i;
109 bool b;
111 constexpr auto MutTiedFields() { return std::tie(i, b); }
113 static_assert(sizeof(Cat) == 8);
114 static_assert(!AreAllBytesTiedFields<Cat>());
116 struct Dog {
117 bool b;
118 int i;
120 constexpr auto MutTiedFields() { return std::tie(i, b); }
122 static_assert(sizeof(Dog) == 8);
123 static_assert(!AreAllBytesTiedFields<Dog>());
125 struct Fish {
126 bool b;
127 bool padding[3];
128 int i;
130 constexpr auto MutTiedFields() { return std::tie(i, b, padding); }
132 static_assert(sizeof(Fish) == 8);
133 static_assert(AreAllBytesTiedFields<Fish>());
135 struct Eel { // Like a Fish, but you can skip serializing the padding.
136 bool b;
137 Padding<bool> padding[3];
138 int i;
140 constexpr auto MutTiedFields() { return std::tie(i, b, padding); }
142 static_assert(sizeof(Eel) == 8);
143 static_assert(AreAllBytesTiedFields<Eel>());
145 // -
147 // #define LETS_USE_BIT_FIELDS
148 #ifdef LETS_USE_BIT_FIELDS
149 # undef LETS_USE_BIT_FIELDS
151 struct Platypus {
152 short s : 1;
153 short s2 : 1;
154 int i;
156 constexpr auto MutTiedFields() {
157 return std::tie(s, s2, i); // Error: Can't take reference to bit-field.
161 #endif
163 // -
165 struct FishTank {
166 Fish f;
167 int i2;
169 constexpr auto MutTiedFields() { return std::tie(f, i2); }
171 static_assert(sizeof(FishTank) == 12);
172 static_assert(AreAllBytesTiedFields<FishTank>());
174 struct CatCarrier {
175 Cat c;
176 int i2;
178 constexpr auto MutTiedFields() { return std::tie(c, i2); }
180 static_assert(sizeof(CatCarrier) == 12);
181 static_assert(AreAllBytesTiedFields<CatCarrier>());
182 static_assert(
183 !AreAllBytesTiedFields<decltype(CatCarrier::c)>()); // BUT BEWARE THIS!
184 // For example, if we had AreAllBytesRecursiveTiedFields:
185 // static_assert(!AreAllBytesRecursiveTiedFields<CatCarrier>());
187 } // namespace TiedFieldsExamples
188 } // namespace mozilla
190 #endif // DOM_CANVAS_TIED_FIELDS_H