15 #ifndef GTIRB_AUXDATA_H
16 #define GTIRB_AUXDATA_H
21 #include <boost/endian/conversion.hpp>
28 #include <type_traits>
30 #include <unordered_map>
31 #include <unordered_set>
113 template <
class T>
struct is_sequence<
std::vector<T>> : std::true_type {};
114 template <
class T>
struct is_sequence<
std::list<T>> : std::true_type {};
115 template <
class T>
struct is_sequence<
std::deque<T>> : std::true_type {};
125 template <
class T,
class U>
127 template <
class T,
class U>
128 struct is_mapping<
std::unordered_map<T, U>> : std::true_type {};
131 template <
class T,
class U>
132 struct is_mapping<
std::multimap<T, U>> : std::false_type {};
133 template <
class T,
class U>
134 struct is_mapping<
std::unordered_multimap<T, U>> : std::false_type {};
142 template <
class T>
struct is_set : std::false_type {};
144 template <
class... Args>
struct is_set<
std::set<Args...>> : std::true_type {};
145 template <
class... Args>
146 struct is_set<
std::unordered_set<Args...>> : std::true_type {};
149 template <
class... Args>
150 struct is_set<
std::multiset<Args...>> : std::false_type {};
151 template <
class... Args>
152 struct is_set<
std::unordered_multiset<Args...>> : std::false_type {};
160 template <
class T>
struct is_tuple : std::false_type {};
162 template <
class... Args>
163 struct is_tuple<
std::tuple<Args...>> : std::true_type {};
165 template <
class... Args>
166 struct is_tuple<
std::pair<Args...>> : std::true_type {};
171 explicit ToByteRange(std::string& Bytes) : It(
std::back_inserter(Bytes)) {}
173 void write(std::byte Byte) { *It =
static_cast<char>(Byte); }
176 std::back_insert_iterator<std::string> It;
180 class FromByteRange {
182 explicit FromByteRange(
const std::string& Bytes)
183 : Curr(Bytes.begin()), End(Bytes.end()) {}
185 bool read(std::byte& Byte) {
189 Byte = std::byte(*Curr);
194 uint64_t remainingBytesToRead()
const {
195 return static_cast<uint64_t
>(std::distance(Curr, End));
199 std::string::const_iterator Curr;
200 std::string::const_iterator End;
216 static void toBytes(
const T& Object, ToByteRange& TBR) =
delete;
224 static bool fromBytes(T& Object, FromByteRange& FBR) =
delete;
236 template <
class... Ts>
struct TypeId {};
239 struct is_endian_type
240 : std::integral_constant<bool, std::is_class_v<T> ||
241 (std::is_integral_v<T> &&
242 !std::is_same_v<T, bool>)> {};
244 template <
typename T,
typename Enable =
void>
struct default_serialization {};
247 template <
typename T>
248 struct default_serialization<
249 T, typename
std::enable_if_t<is_endian_type<T>::value ||
250 std::is_floating_point<T>::value ||
251 std::is_same<T, bool>::value>> {
252 static void toBytes(
const T&
object, ToByteRange& TBR) {
256 if constexpr (!std::is_floating_point<T>::value &&
257 !std::is_same<T, bool>::value) {
259 boost::endian::conditional_reverse_inplace<boost::endian::order::little,
260 boost::endian::order::native>(
263 auto srcBytes_begin =
reinterpret_cast<const std::byte*
>(&ordered);
264 auto srcBytes_end =
reinterpret_cast<const std::byte*
>(&ordered + 1);
265 std::for_each(srcBytes_begin, srcBytes_end, [&](
auto b) { TBR.write(b); });
268 static bool fromBytes(T&
object, FromByteRange& FBR) {
269 auto dest_begin =
reinterpret_cast<std::byte*
>(&object);
270 auto dest_end =
reinterpret_cast<std::byte*
>(&
object + 1);
272 std::for_each(dest_begin, dest_end, [&](
auto& b) {
281 if constexpr (!std::is_floating_point<T>::value &&
282 !std::is_same<T, bool>::value) {
283 boost::endian::conditional_reverse_inplace<boost::endian::order::little,
284 boost::endian::order::native>(
293 struct auxdata_traits<
std::byte> : default_serialization<std::byte> {
294 static std::string
type_name() {
return "byte"; }
296 static void toBytes(std::byte
object, ToByteRange& TBR) { TBR.write(
object); }
298 static bool fromBytes(std::byte&
object, FromByteRange& FBR) {
299 return FBR.read(
object);
303 template <>
struct auxdata_traits<Addr> : default_serialization<Addr> {
304 static std::string
type_name() {
return "Addr"; }
307 template <>
struct auxdata_traits<
UUID> : default_serialization<UUID> {
308 static std::string
type_name() {
return "UUID"; }
312 struct auxdata_traits<T, typename
std::enable_if_t<std::is_integral<T>::value &&
313 std::is_signed<T>::value>>
314 : default_serialization<T> {
316 return "int" + std::to_string(8 *
sizeof(T)) +
"_t";
321 struct auxdata_traits<T, typename
std::enable_if_t<std::is_integral<T>::value &&
322 std::is_unsigned<T>::value>>
323 : default_serialization<T> {
325 return "uint" + std::to_string(8 *
sizeof(T)) +
"_t";
329 template <>
struct auxdata_traits<bool> : default_serialization<bool> {
330 static std::string
type_name() {
return "bool"; }
333 template <>
struct auxdata_traits<float> : default_serialization<float> {
334 static std::string
type_name() {
return "float"; }
337 template <>
struct auxdata_traits<double> : default_serialization<double> {
338 static std::string
type_name() {
return "double"; }
341 template <>
struct auxdata_traits<
std::string> {
342 static std::string
type_name() {
return "string"; }
344 static void toBytes(
const std::string& Object, ToByteRange& TBR) {
346 std::for_each(Object.begin(), Object.end(),
347 [&](
auto& elt) { auxdata_traits<char>::toBytes(elt, TBR); });
350 static bool fromBytes(std::string& Object, FromByteRange& FBR) {
355 if (Count > FBR.remainingBytesToRead())
358 Object.resize(Count);
360 std::for_each(Object.begin(), Object.end(), [&](
auto& elt) {
361 if (!auxdata_traits<char>::fromBytes(elt, FBR))
369 template <>
struct auxdata_traits<Offset> {
370 static std::string
type_name() {
return "Offset"; }
372 static void toBytes(
const Offset& Object, ToByteRange& TBR) {
377 static bool fromBytes(Offset& Object, FromByteRange& FBR) {
385 struct auxdata_traits<T, typename
std::enable_if_t<is_sequence<T>::value>> {
387 return "sequence<" + TypeId<typename T::value_type>::value() +
">";
390 static void toBytes(
const T& Object, ToByteRange& TBR) {
392 std::for_each(Object.begin(), Object.end(), [&](
const auto& Elt) {
393 auxdata_traits<typename T::value_type>::toBytes(Elt, TBR);
397 static bool fromBytes(T& Object, FromByteRange& FBR) {
402 if (Count > FBR.remainingBytesToRead())
405 Object.resize(Count);
407 std::for_each(Object.begin(), Object.end(), [&](
auto& Elt) {
408 if (!auxdata_traits<typename T::value_type>::fromBytes(Elt, FBR))
417 struct auxdata_traits<T, typename
std::enable_if_t<is_set<T>::value>> {
419 return "set<" + TypeId<typename T::value_type>::value() +
">";
422 static void toBytes(
const T& Object, ToByteRange& TBR) {
424 for (
const auto& Elt : Object)
428 static bool fromBytes(T& Object, FromByteRange& FBR) {
433 if (Count > FBR.remainingBytesToRead())
436 for (uint64_t i = 0; i < Count; i++) {
437 typename T::value_type V;
438 if (!auxdata_traits<decltype(V)>::
fromBytes(V, FBR))
440 Object.emplace(std::move(V));
448 struct auxdata_traits<T, typename
std::enable_if_t<is_mapping<T>::value>> {
451 TypeId<typename T::key_type, typename T::mapped_type>::value() +
">";
454 static void toBytes(
const T& Object, ToByteRange& TBR) {
456 std::for_each(Object.begin(), Object.end(), [&](
const auto& Elt) {
457 auxdata_traits<typename T::key_type>::toBytes(Elt.first, TBR);
458 auxdata_traits<typename T::mapped_type>::toBytes(Elt.second, TBR);
462 static bool fromBytes(T& Object, FromByteRange& FBR) {
467 if (Count > FBR.remainingBytesToRead())
470 for (uint64_t i = 0; i < Count; i++) {
471 typename T::key_type K;
472 if (!auxdata_traits<decltype(K)>::
fromBytes(K, FBR))
474 typename T::mapped_type V;
475 if (!auxdata_traits<decltype(V)>::
fromBytes(V, FBR))
477 Object.emplace(std::move(K), std::move(V));
487 template <
class... Args>
struct auxdata_traits<
std::variant<Args...>> {
488 using T = std::variant<Args...>;
491 return "variant<" + TypeId<Args...>::value() +
">";
494 template <u
int64_t I = 0>
495 static std::optional<std::variant<Args...>> expand_type(uint64_t i) {
496 if constexpr (I >=
sizeof...(Args)) {
499 return i == 0 ? std::variant<Args...>{std::in_place_index<I>,
500 std::variant_alternative_t<I, T>{}}
501 : expand_type<I + 1>(i - 1);
505 static void toBytes(
const T& Object, ToByteRange& TBR) {
506 uint64_t Index = Object.index();
509 [TBR](
auto&& arg)
mutable {
510 auxdata_traits<
typename std::remove_const<
511 typename std::remove_reference<decltype(arg)>::type>::type>
::
517 static bool fromBytes(T& Object, FromByteRange& FBR) {
521 if (Index > FBR.remainingBytesToRead())
524 auto maybeV = expand_type(Index);
529 bool res_code =
false;
531 [&res_code, &FBR](
auto&& arg)
mutable {
532 typename std::remove_reference<decltype(arg)>::type Val;
533 res_code = auxdata_traits<
typename std::remove_reference<decltype(
549 template <
class T>
struct tuple_traits {};
550 template <
class... Ts>
struct tuple_traits<
std::tuple<Ts...>> {
551 using Tuple = std::tuple<Ts...>;
553 static std::string type_name() {
554 return "tuple<" + TypeId<Ts...>::value() +
">";
558 template <
class... Ts>
struct tuple_traits<
std::pair<Ts...>> {
559 using Tuple = std::tuple<Ts...>;
561 static std::string type_name() {
562 return "tuple<" + TypeId<Ts...>::value() +
">";
566 template <
class Func,
size_t... Is>
567 constexpr
void static_for(Func&& f, std::integer_sequence<size_t, Is...>) {
568 (f(std::integral_constant<size_t, Is>{}), ...);
572 struct auxdata_traits<T, typename
std::enable_if_t<is_tuple<T>::value>>
574 static void toBytes(
const T& Object, ToByteRange& TBR) {
577 const auto& F = std::get<i>(Object);
578 auxdata_traits<std::remove_cv_t<
579 std::remove_reference_t<decltype(F)>>>
::toBytes(F, TBR);
581 std::make_index_sequence<std::tuple_size<T>::value>{});
584 static bool fromBytes(T& Object, FromByteRange& FBR) {
588 auto& F = std::get<i>(Object);
589 if (!auxdata_traits<std::remove_cv_t<
590 std::remove_reference_t<decltype(F)>>>::
fromBytes(F, FBR))
593 std::make_index_sequence<std::tuple_size<T>::value>{});
601 template <
class T>
struct TypeId<T> {
605 template <
class T,
class... Ts>
struct TypeId<T, Ts...> {
606 static std::string value() {
617 struct SerializedForm {
618 std::string RawBytes;
619 std::string ProtobufType;
625 virtual ~AuxData() =
default;
639 const SerializedForm& rawData()
const {
return this->SF; }
643 static constexpr std::size_t UNREGISTERED_API_TYPE_ID = 0;
646 virtual std::size_t getApiTypeId()
const {
return UNREGISTERED_API_TYPE_ID; }
650 using MessageType = proto::AuxData;
656 static void fromProtobuf(AuxData& Result,
const MessageType& Message);
661 virtual void toProtobuf(MessageType* Message)
const {
662 toProtobuf(Message, this->SF);
672 void toProtobuf(MessageType* Message,
673 const SerializedForm& SFToSerialize)
const;
678 static bool checkAuxDataMessageType(
const AuxData::MessageType& Message,
679 const std::string& ExpectedName);
682 void save(std::ostream& Out)
const;
685 static std::unique_ptr<AuxData>
686 load(std::istream& In, std::unique_ptr<AuxData> (*FPPtr)(
const MessageType&));
691 friend class AuxDataContainer;
693 template <
typename T>
friend typename T::MessageType toProtobuf(
const T&);
694 friend class SerializationTestHarness;
699 template <
class Schema>
class AuxDataImpl :
public AuxData {
701 AuxDataImpl() =
default;
702 AuxDataImpl(
typename Schema::Type&& Val) : Object(
std::move(Val)){};
705 static std::size_t staticGetApiTypeId() {
706 return typeid(
typename Schema::Type).hash_code();
710 virtual std::size_t getApiTypeId()
const override {
711 return staticGetApiTypeId();
714 const typename Schema::Type* get()
const {
return &Object; }
717 static std::unique_ptr<AuxData> fromProtobuf(
const MessageType& Message) {
720 if (!checkAuxDataMessageType(
729 auto TypedAuxData = std::make_unique<AuxDataImpl<Schema>>();
730 AuxData::fromProtobuf(*TypedAuxData, Message);
731 FromByteRange FBR(TypedAuxData->rawData().RawBytes);
741 virtual void toProtobuf(MessageType* Message)
const override {
746 AuxData::SerializedForm TypedSF;
748 ToByteRange TBR(TypedSF.RawBytes);
750 AuxData::toProtobuf(Message, TypedSF);
754 void save(std::ostream& Out)
const { AuxData::save(Out); }
757 static std::unique_ptr<AuxDataImpl> load([[maybe_unused]] Context& C,
759 return std::unique_ptr<AuxDataImpl>{
760 static_cast<AuxDataImpl*
>(AuxData::load(In, fromProtobuf).release())};
763 typename Schema::Type Object;
765 friend class AuxDataContainer;
766 friend class SerializationTestHarness;
774 #endif // GTIRB_AUXDATA_H