/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef DOM_CANVAS_TIED_FIELDS_H #define DOM_CANVAS_TIED_FIELDS_H #include "TupleUtils.h" namespace mozilla { // - /** * TiedFields(T&) -> std::tuple * TiedFields(const T&) -> std::tuple * * You can also overload TiedFields without adding T::MutTiedFields: * template<> * inline auto TiedFields(gfx::IntSize& a) { * return std::tie(a.width, a.height); * } */ template constexpr auto TiedFields(T& t) { const auto fields = t.MutTiedFields(); return fields; } template > constexpr auto TiedFields(const T& t) { // Uncast const to get mutable-fields tuple, but reapply const to tuple args. // We should do better than this when C++ gets a solution other than macros. const auto mutFields = TiedFields(const_cast(t)); return ToTupleOfConstRefs(mutFields); } /** * Returns true if all bytes in T are accounted for via size of all tied fields. * Returns false if there's bytes unaccounted for, which might indicate either * unaccounted-for padding or missing fields. * The goal is to check that TiedFields returns every field in T, and this * returns false if it suspects there are bytes that are not accounted for by * TiedFields. * * `constexpr` effectively cannot do math on pointers, so it's not possible to * figure out via `constexpr` whether fields are consecutive or dense. * However, we can at least compare `sizeof(T)` to the sum of `sizeof(Args...)` * for `TiedFields(T) -> std::tuple`. * * See TiedFieldsExamples. */ template constexpr bool AreAllBytesTiedFields() { using fieldsT = decltype(TiedFields(std::declval())); const auto fields_size_sum = SizeofTupleArgs::value; const auto t_size = sizeof(T); return fields_size_sum == t_size; } // It's also possible to determine AreAllBytesRecursiveTiedFields: // https://hackmd.io/@jgilbert/B16qa0Fa9 // - /** * Padding can be used to pad out a struct so that it's not implicitly * padded by struct rules. * You can also just add your padding to TiedFields, but by explicitly typing * padding like this, serialization can make a choice whether to copy Padding, * or instead to omit the copy. * * Omitting the copy isn't always faster. * struct Entry { * uint16_t key; * Padding padding; * uint32_t val; * auto MutTiedFields() { return std::tie(key, padding, val); } * }; * If you serialize Padding, the serialized size is 8, and the compiler will * optimize serialization to a single 8-byte memcpy. * If your serialization omits Padding, the serialized size of Entry shrinks * by 25%. If you have a big list of Entrys, maybe this is a big savings! * However, by optimizing for size here you sacrifice speed, because this splits * the single memcpy into two: a 2-byte memcpy and a 4-byte memcpy. * * Explicitly marking padding gives callers the option of choosing. */ template struct Padding { T ignored; friend constexpr bool operator==(const Padding&, const Padding&) { return true; } friend constexpr bool operator<(const Padding&, const Padding&) { return false; } }; static_assert(sizeof(Padding) == 1); static_assert(sizeof(Padding) == 2); static_assert(sizeof(Padding) == 4); // - namespace TiedFieldsExamples { struct Cat { int i; bool b; constexpr auto MutTiedFields() { return std::tie(i, b); } }; static_assert(sizeof(Cat) == 8); static_assert(!AreAllBytesTiedFields()); struct Dog { bool b; int i; constexpr auto MutTiedFields() { return std::tie(i, b); } }; static_assert(sizeof(Dog) == 8); static_assert(!AreAllBytesTiedFields()); struct Fish { bool b; bool padding[3]; int i; constexpr auto MutTiedFields() { return std::tie(i, b, padding); } }; static_assert(sizeof(Fish) == 8); static_assert(AreAllBytesTiedFields()); struct Eel { // Like a Fish, but you can skip serializing the padding. bool b; Padding padding[3]; int i; constexpr auto MutTiedFields() { return std::tie(i, b, padding); } }; static_assert(sizeof(Eel) == 8); static_assert(AreAllBytesTiedFields()); // - // #define LETS_USE_BIT_FIELDS #ifdef LETS_USE_BIT_FIELDS # undef LETS_USE_BIT_FIELDS struct Platypus { short s : 1; short s2 : 1; int i; constexpr auto MutTiedFields() { return std::tie(s, s2, i); // Error: Can't take reference to bit-field. } }; #endif // - struct FishTank { Fish f; int i2; constexpr auto MutTiedFields() { return std::tie(f, i2); } }; static_assert(sizeof(FishTank) == 12); static_assert(AreAllBytesTiedFields()); struct CatCarrier { Cat c; int i2; constexpr auto MutTiedFields() { return std::tie(c, i2); } }; static_assert(sizeof(CatCarrier) == 12); static_assert(AreAllBytesTiedFields()); static_assert( !AreAllBytesTiedFields()); // BUT BEWARE THIS! // For example, if we had AreAllBytesRecursiveTiedFields: // static_assert(!AreAllBytesRecursiveTiedFields()); } // namespace TiedFieldsExamples } // namespace mozilla #endif // DOM_CANVAS_TIED_FIELDS_H