зеркало из https://github.com/mozilla/gecko-dev.git
191 строка
5.3 KiB
C++
191 строка
5.3 KiB
C++
/* 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<Fields&...>
|
|
* TiedFields(const T&) -> std::tuple<const Fields&...>
|
|
*
|
|
* You can also overload TiedFields without adding T::MutTiedFields:
|
|
* template<>
|
|
* inline auto TiedFields<gfx::IntSize>(gfx::IntSize& a) {
|
|
* return std::tie(a.width, a.height);
|
|
* }
|
|
*/
|
|
template <class T>
|
|
constexpr auto TiedFields(T& t) {
|
|
const auto fields = t.MutTiedFields();
|
|
return fields;
|
|
}
|
|
template <class T, class... Args, class Tup = std::tuple<Args&...>>
|
|
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&>(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<Args...>`.
|
|
*
|
|
* See TiedFieldsExamples.
|
|
*/
|
|
template <class T>
|
|
constexpr bool AreAllBytesTiedFields() {
|
|
using fieldsT = decltype(TiedFields(std::declval<T>()));
|
|
const auto fields_size_sum = SizeofTupleArgs<fieldsT>::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<T> 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<uint16_t> 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 <class T>
|
|
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<bool>) == 1);
|
|
static_assert(sizeof(Padding<bool[2]>) == 2);
|
|
static_assert(sizeof(Padding<int>) == 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<Cat>());
|
|
|
|
struct Dog {
|
|
bool b;
|
|
int i;
|
|
|
|
constexpr auto MutTiedFields() { return std::tie(i, b); }
|
|
};
|
|
static_assert(sizeof(Dog) == 8);
|
|
static_assert(!AreAllBytesTiedFields<Dog>());
|
|
|
|
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<Fish>());
|
|
|
|
struct Eel { // Like a Fish, but you can skip serializing the padding.
|
|
bool b;
|
|
Padding<bool> padding[3];
|
|
int i;
|
|
|
|
constexpr auto MutTiedFields() { return std::tie(i, b, padding); }
|
|
};
|
|
static_assert(sizeof(Eel) == 8);
|
|
static_assert(AreAllBytesTiedFields<Eel>());
|
|
|
|
// -
|
|
|
|
// #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<FishTank>());
|
|
|
|
struct CatCarrier {
|
|
Cat c;
|
|
int i2;
|
|
|
|
constexpr auto MutTiedFields() { return std::tie(c, i2); }
|
|
};
|
|
static_assert(sizeof(CatCarrier) == 12);
|
|
static_assert(AreAllBytesTiedFields<CatCarrier>());
|
|
static_assert(
|
|
!AreAllBytesTiedFields<decltype(CatCarrier::c)>()); // BUT BEWARE THIS!
|
|
// For example, if we had AreAllBytesRecursiveTiedFields:
|
|
// static_assert(!AreAllBytesRecursiveTiedFields<CatCarrier>());
|
|
|
|
} // namespace TiedFieldsExamples
|
|
} // namespace mozilla
|
|
|
|
#endif // DOM_CANVAS_TIED_FIELDS_H
|