15 #ifndef GTIRB_AUXDATA_H
16 #define GTIRB_AUXDATA_H
21 #include <boost/endian/conversion.hpp>
27 #include <type_traits>
29 #include <unordered_map>
111 template <
class T>
struct is_sequence<
std::vector<T>> : std::true_type {};
112 template <
class T>
struct is_sequence<
std::list<T>> : std::true_type {};
113 template <
class T>
struct is_sequence<
std::deque<T>> : std::true_type {};
123 template <
class T,
class U>
125 template <
class T,
class U>
126 struct is_mapping<
std::unordered_map<T, U>> : std::true_type {};
129 template <
class T,
class U>
130 struct is_mapping<
std::multimap<T, U>> : std::false_type {};
131 template <
class T,
class U>
132 struct is_mapping<
std::unordered_multimap<T, U>> : std::false_type {};
134 template <
class T>
struct is_tuple : std::false_type {};
135 template <
class... Args>
136 struct is_tuple<
std::tuple<Args...>> : std::true_type {};
138 template <
class... Args>
139 struct is_tuple<
std::pair<Args...>> : std::true_type {};
144 explicit ToByteRange(std::string& Bytes) : It(
std::back_inserter(Bytes)) {}
146 void write(std::byte Byte) { *It =
static_cast<char>(Byte); }
149 std::back_insert_iterator<std::string> It;
153 class FromByteRange {
155 explicit FromByteRange(
const std::string& Bytes)
156 : Curr(Bytes.begin()), End(Bytes.end()) {}
158 bool read(std::byte& Byte) {
162 Byte = std::byte(*Curr);
167 uint64_t remainingBytesToRead()
const {
168 return static_cast<uint64_t
>(std::distance(Curr, End));
172 std::string::const_iterator Curr;
173 std::string::const_iterator End;
189 static void toBytes(
const T& Object, ToByteRange& TBR) =
delete;
197 static bool fromBytes(T& Object, FromByteRange& FBR) =
delete;
209 template <
class... Ts>
struct TypeId {};
212 struct is_endian_type
213 : std::integral_constant<bool, std::is_class_v<T> ||
214 (std::is_integral_v<T> &&
215 !std::is_same_v<T, bool>)> {};
217 template <
typename T,
typename Enable =
void>
struct default_serialization {};
220 template <
typename T>
221 struct default_serialization<
222 T, typename
std::enable_if_t<is_endian_type<T>::value ||
223 std::is_floating_point<T>::value ||
224 std::is_same<T, bool>::value>> {
225 static void toBytes(
const T&
object, ToByteRange& TBR) {
229 if constexpr (!std::is_floating_point<T>::value &&
230 !std::is_same<T, bool>::value) {
232 boost::endian::conditional_reverse_inplace<boost::endian::order::little,
233 boost::endian::order::native>(
236 auto srcBytes_begin =
reinterpret_cast<const std::byte*
>(&ordered);
237 auto srcBytes_end =
reinterpret_cast<const std::byte*
>(&ordered + 1);
238 std::for_each(srcBytes_begin, srcBytes_end, [&](
auto b) { TBR.write(b); });
241 static bool fromBytes(T&
object, FromByteRange& FBR) {
242 auto dest_begin =
reinterpret_cast<std::byte*
>(&object);
243 auto dest_end =
reinterpret_cast<std::byte*
>(&
object + 1);
245 std::for_each(dest_begin, dest_end, [&](
auto& b) {
254 if constexpr (!std::is_floating_point<T>::value &&
255 !std::is_same<T, bool>::value) {
256 boost::endian::conditional_reverse_inplace<boost::endian::order::little,
257 boost::endian::order::native>(
266 struct auxdata_traits<
std::byte> : default_serialization<std::byte> {
267 static std::string
type_name() {
return "byte"; }
269 static void toBytes(std::byte
object, ToByteRange& TBR) { TBR.write(
object); }
271 static bool fromBytes(std::byte&
object, FromByteRange& FBR) {
272 return FBR.read(
object);
276 template <>
struct auxdata_traits<Addr> : default_serialization<Addr> {
277 static std::string
type_name() {
return "Addr"; }
280 template <>
struct auxdata_traits<
UUID> : default_serialization<UUID> {
281 static std::string
type_name() {
return "UUID"; }
285 struct auxdata_traits<T, typename
std::enable_if_t<std::is_integral<T>::value &&
286 std::is_signed<T>::value>>
287 : default_serialization<T> {
289 return "int" + std::to_string(8 *
sizeof(T)) +
"_t";
294 struct auxdata_traits<T, typename
std::enable_if_t<std::is_integral<T>::value &&
295 std::is_unsigned<T>::value>>
296 : default_serialization<T> {
298 return "uint" + std::to_string(8 *
sizeof(T)) +
"_t";
302 template <>
struct auxdata_traits<bool> : default_serialization<bool> {
303 static std::string
type_name() {
return "bool"; }
306 template <>
struct auxdata_traits<float> : default_serialization<float> {
307 static std::string
type_name() {
return "float"; }
310 template <>
struct auxdata_traits<double> : default_serialization<double> {
311 static std::string
type_name() {
return "double"; }
314 template <>
struct auxdata_traits<
std::string> {
315 static std::string
type_name() {
return "string"; }
317 static void toBytes(
const std::string& Object, ToByteRange& TBR) {
319 std::for_each(Object.begin(), Object.end(),
320 [&](
auto& elt) { auxdata_traits<char>::toBytes(elt, TBR); });
323 static bool fromBytes(std::string& Object, FromByteRange& FBR) {
328 if (Count > FBR.remainingBytesToRead())
331 Object.resize(Count);
333 std::for_each(Object.begin(), Object.end(), [&](
auto& elt) {
334 if (!auxdata_traits<char>::fromBytes(elt, FBR))
342 template <>
struct auxdata_traits<Offset> {
343 static std::string
type_name() {
return "Offset"; }
345 static void toBytes(
const Offset& Object, ToByteRange& TBR) {
350 static bool fromBytes(Offset& Object, FromByteRange& FBR) {
358 struct auxdata_traits<T, typename
std::enable_if_t<is_sequence<T>::value>> {
360 return "sequence<" + TypeId<typename T::value_type>::value() +
">";
363 static void toBytes(
const T& Object, ToByteRange& TBR) {
365 std::for_each(Object.begin(), Object.end(), [&](
const auto& Elt) {
366 auxdata_traits<typename T::value_type>::toBytes(Elt, TBR);
370 static bool fromBytes(T& Object, FromByteRange& FBR) {
375 if (Count > FBR.remainingBytesToRead())
378 Object.resize(Count);
380 std::for_each(Object.begin(), Object.end(), [&](
auto& Elt) {
381 if (!auxdata_traits<typename T::value_type>::fromBytes(Elt, FBR))
389 template <
class... Args>
struct auxdata_traits<
std::set<Args...>> {
390 using T = std::set<Args...>;
393 return "set<" + TypeId<typename T::value_type>::value() +
">";
396 static void toBytes(
const T& Object, ToByteRange& TBR) {
398 for (
const auto& Elt : Object)
402 static bool fromBytes(T& Object, FromByteRange& FBR) {
407 if (Count > FBR.remainingBytesToRead())
410 for (uint64_t i = 0; i < Count; i++) {
411 typename T::value_type V;
412 if (!auxdata_traits<decltype(V)>::
fromBytes(V, FBR))
414 Object.emplace(std::move(V));
422 struct auxdata_traits<T, typename
std::enable_if_t<is_mapping<T>::value>> {
425 TypeId<typename T::key_type, typename T::mapped_type>::value() +
">";
428 static void toBytes(
const T& Object, ToByteRange& TBR) {
430 std::for_each(Object.begin(), Object.end(), [&](
const auto& Elt) {
431 auxdata_traits<typename T::key_type>::toBytes(Elt.first, TBR);
432 auxdata_traits<typename T::mapped_type>::toBytes(Elt.second, TBR);
436 static bool fromBytes(T& Object, FromByteRange& FBR) {
441 if (Count > FBR.remainingBytesToRead())
444 for (uint64_t i = 0; i < Count; i++) {
445 typename T::key_type K;
446 if (!auxdata_traits<decltype(K)>::
fromBytes(K, FBR))
448 typename T::mapped_type V;
449 if (!auxdata_traits<decltype(V)>::
fromBytes(V, FBR))
451 Object.emplace(std::move(K), std::move(V));
461 template <
class... Args>
struct auxdata_traits<
std::variant<Args...>> {
462 using T = std::variant<Args...>;
465 return "variant<" + TypeId<Args...>::value() +
">";
468 template <u
int64_t I = 0>
469 static std::optional<std::variant<Args...>> expand_type(uint64_t i) {
470 if constexpr (I >=
sizeof...(Args)) {
473 return i == 0 ? std::variant<Args...>{std::in_place_index<I>,
474 std::variant_alternative_t<I, T>{}}
475 : expand_type<I + 1>(i - 1);
479 static void toBytes(
const T& Object, ToByteRange& TBR) {
480 uint64_t Index = Object.index();
483 [TBR](
auto&& arg)
mutable {
484 auxdata_traits<
typename std::remove_const<
485 typename std::remove_reference<decltype(arg)>::type>::type>
::
491 static bool fromBytes(T& Object, FromByteRange& FBR) {
495 if (Index > FBR.remainingBytesToRead())
498 auto maybeV = expand_type(Index);
503 bool res_code =
false;
505 [&res_code, &FBR](
auto&& arg)
mutable {
506 typename std::remove_reference<decltype(arg)>::type Val;
507 res_code = auxdata_traits<
typename std::remove_reference<decltype(
523 template <
class T>
struct tuple_traits {};
524 template <
class... Ts>
struct tuple_traits<
std::tuple<Ts...>> {
525 using Tuple = std::tuple<Ts...>;
527 static std::string type_name() {
528 return "tuple<" + TypeId<Ts...>::value() +
">";
532 template <
class... Ts>
struct tuple_traits<
std::pair<Ts...>> {
533 using Tuple = std::tuple<Ts...>;
535 static std::string type_name() {
536 return "tuple<" + TypeId<Ts...>::value() +
">";
540 template <
class Func,
size_t... Is>
541 constexpr
void static_for(Func&& f, std::integer_sequence<size_t, Is...>) {
542 (f(std::integral_constant<size_t, Is>{}), ...);
546 struct auxdata_traits<T, typename
std::enable_if_t<is_tuple<T>::value>>
548 static void toBytes(
const T& Object, ToByteRange& TBR) {
551 const auto& F = std::get<i>(Object);
552 auxdata_traits<std::remove_cv_t<
553 std::remove_reference_t<decltype(F)>>>
::toBytes(F, TBR);
555 std::make_index_sequence<std::tuple_size<T>::value>{});
558 static bool fromBytes(T& Object, FromByteRange& FBR) {
562 auto& F = std::get<i>(Object);
563 if (!auxdata_traits<std::remove_cv_t<
564 std::remove_reference_t<decltype(F)>>>::
fromBytes(F, FBR))
567 std::make_index_sequence<std::tuple_size<T>::value>{});
575 template <
class T>
struct TypeId<T> {
579 template <
class T,
class... Ts>
struct TypeId<T, Ts...> {
580 static std::string value() {
591 struct SerializedForm {
592 std::string RawBytes;
593 std::string ProtobufType;
599 virtual ~AuxData() =
default;
613 const SerializedForm& rawData()
const {
return this->SF; }
617 static constexpr std::size_t UNREGISTERED_API_TYPE_ID = 0;
620 virtual std::size_t getApiTypeId()
const {
return UNREGISTERED_API_TYPE_ID; }
624 using MessageType = proto::AuxData;
630 static void fromProtobuf(AuxData& Result,
const MessageType& Message);
635 virtual void toProtobuf(MessageType* Message)
const {
636 toProtobuf(Message, this->SF);
646 void toProtobuf(MessageType* Message,
647 const SerializedForm& SFToSerialize)
const;
652 static bool checkAuxDataMessageType(
const AuxData::MessageType& Message,
653 const std::string& ExpectedName);
656 void save(std::ostream& Out)
const;
659 static std::unique_ptr<AuxData>
660 load(std::istream& In, std::unique_ptr<AuxData> (*FPPtr)(
const MessageType&));
665 friend class AuxDataContainer;
667 template <
typename T>
friend typename T::MessageType toProtobuf(
const T&);
668 friend class SerializationTestHarness;
673 template <
class Schema>
class AuxDataImpl :
public AuxData {
675 AuxDataImpl() =
default;
676 AuxDataImpl(
typename Schema::Type&& Val) : Object(
std::move(Val)){};
679 static std::size_t staticGetApiTypeId() {
680 return typeid(
typename Schema::Type).hash_code();
684 virtual std::size_t getApiTypeId()
const override {
685 return staticGetApiTypeId();
688 const typename Schema::Type* get()
const {
return &Object; }
691 static std::unique_ptr<AuxData> fromProtobuf(
const MessageType& Message) {
694 if (!checkAuxDataMessageType(
703 auto TypedAuxData = std::make_unique<AuxDataImpl<Schema>>();
704 AuxData::fromProtobuf(*TypedAuxData, Message);
705 FromByteRange FBR(TypedAuxData->rawData().RawBytes);
715 virtual void toProtobuf(MessageType* Message)
const override {
720 AuxData::SerializedForm TypedSF;
722 ToByteRange TBR(TypedSF.RawBytes);
724 AuxData::toProtobuf(Message, TypedSF);
728 void save(std::ostream& Out)
const { AuxData::save(Out); }
731 static std::unique_ptr<AuxDataImpl> load([[maybe_unused]] Context& C,
733 return std::unique_ptr<AuxDataImpl>{
734 static_cast<AuxDataImpl*
>(AuxData::load(In, fromProtobuf).release())};
737 typename Schema::Type Object;
739 friend class AuxDataContainer;
740 friend class SerializationTestHarness;
748 #endif // GTIRB_AUXDATA_H