15 #ifndef GTIRB_AUXDATA_H
16 #define GTIRB_AUXDATA_H
21 #include <boost/endian/conversion.hpp>
28 #include <type_traits>
30 #include <unordered_map>
112 template <
class T>
struct is_sequence<
std::vector<T>> : std::true_type {};
113 template <
class T>
struct is_sequence<
std::list<T>> : std::true_type {};
114 template <
class T>
struct is_sequence<
std::deque<T>> : std::true_type {};
124 template <
class T,
class U>
126 template <
class T,
class U>
127 struct is_mapping<
std::unordered_map<T, U>> : std::true_type {};
130 template <
class T,
class U>
131 struct is_mapping<
std::multimap<T, U>> : std::false_type {};
132 template <
class T,
class U>
133 struct is_mapping<
std::unordered_multimap<T, U>> : std::false_type {};
135 template <
class T>
struct is_tuple : std::false_type {};
136 template <
class... Args>
137 struct is_tuple<
std::tuple<Args...>> : std::true_type {};
139 template <
class... Args>
140 struct is_tuple<
std::pair<Args...>> : std::true_type {};
145 explicit ToByteRange(std::string& Bytes) : It(
std::back_inserter(Bytes)) {}
147 void write(std::byte Byte) { *It =
static_cast<char>(Byte); }
150 std::back_insert_iterator<std::string> It;
154 class FromByteRange {
156 explicit FromByteRange(
const std::string& Bytes)
157 : Curr(Bytes.begin()), End(Bytes.end()) {}
159 bool read(std::byte& Byte) {
163 Byte = std::byte(*Curr);
168 uint64_t remainingBytesToRead()
const {
169 return static_cast<uint64_t
>(std::distance(Curr, End));
173 std::string::const_iterator Curr;
174 std::string::const_iterator End;
190 static void toBytes(
const T& Object, ToByteRange& TBR) =
delete;
198 static bool fromBytes(T& Object, FromByteRange& FBR) =
delete;
210 template <
class... Ts>
struct TypeId {};
213 struct is_endian_type
214 : std::integral_constant<bool, std::is_class_v<T> ||
215 (std::is_integral_v<T> &&
216 !std::is_same_v<T, bool>)> {};
218 template <
typename T,
typename Enable =
void>
struct default_serialization {};
221 template <
typename T>
222 struct default_serialization<
223 T, typename
std::enable_if_t<is_endian_type<T>::value ||
224 std::is_floating_point<T>::value ||
225 std::is_same<T, bool>::value>> {
226 static void toBytes(
const T&
object, ToByteRange& TBR) {
230 if constexpr (!std::is_floating_point<T>::value &&
231 !std::is_same<T, bool>::value) {
233 boost::endian::conditional_reverse_inplace<boost::endian::order::little,
234 boost::endian::order::native>(
237 auto srcBytes_begin =
reinterpret_cast<const std::byte*
>(&ordered);
238 auto srcBytes_end =
reinterpret_cast<const std::byte*
>(&ordered + 1);
239 std::for_each(srcBytes_begin, srcBytes_end, [&](
auto b) { TBR.write(b); });
242 static bool fromBytes(T&
object, FromByteRange& FBR) {
243 auto dest_begin =
reinterpret_cast<std::byte*
>(&object);
244 auto dest_end =
reinterpret_cast<std::byte*
>(&
object + 1);
246 std::for_each(dest_begin, dest_end, [&](
auto& b) {
255 if constexpr (!std::is_floating_point<T>::value &&
256 !std::is_same<T, bool>::value) {
257 boost::endian::conditional_reverse_inplace<boost::endian::order::little,
258 boost::endian::order::native>(
267 struct auxdata_traits<
std::byte> : default_serialization<std::byte> {
268 static std::string
type_name() {
return "byte"; }
270 static void toBytes(std::byte
object, ToByteRange& TBR) { TBR.write(
object); }
272 static bool fromBytes(std::byte&
object, FromByteRange& FBR) {
273 return FBR.read(
object);
277 template <>
struct auxdata_traits<Addr> : default_serialization<Addr> {
278 static std::string
type_name() {
return "Addr"; }
281 template <>
struct auxdata_traits<
UUID> : default_serialization<UUID> {
282 static std::string
type_name() {
return "UUID"; }
286 struct auxdata_traits<T, typename
std::enable_if_t<std::is_integral<T>::value &&
287 std::is_signed<T>::value>>
288 : default_serialization<T> {
290 return "int" + std::to_string(8 *
sizeof(T)) +
"_t";
295 struct auxdata_traits<T, typename
std::enable_if_t<std::is_integral<T>::value &&
296 std::is_unsigned<T>::value>>
297 : default_serialization<T> {
299 return "uint" + std::to_string(8 *
sizeof(T)) +
"_t";
303 template <>
struct auxdata_traits<bool> : default_serialization<bool> {
304 static std::string
type_name() {
return "bool"; }
307 template <>
struct auxdata_traits<float> : default_serialization<float> {
308 static std::string
type_name() {
return "float"; }
311 template <>
struct auxdata_traits<double> : default_serialization<double> {
312 static std::string
type_name() {
return "double"; }
315 template <>
struct auxdata_traits<
std::string> {
316 static std::string
type_name() {
return "string"; }
318 static void toBytes(
const std::string& Object, ToByteRange& TBR) {
320 std::for_each(Object.begin(), Object.end(),
321 [&](
auto& elt) { auxdata_traits<char>::toBytes(elt, TBR); });
324 static bool fromBytes(std::string& Object, FromByteRange& FBR) {
329 if (Count > FBR.remainingBytesToRead())
332 Object.resize(Count);
334 std::for_each(Object.begin(), Object.end(), [&](
auto& elt) {
335 if (!auxdata_traits<char>::fromBytes(elt, FBR))
343 template <>
struct auxdata_traits<Offset> {
344 static std::string
type_name() {
return "Offset"; }
346 static void toBytes(
const Offset& Object, ToByteRange& TBR) {
351 static bool fromBytes(Offset& Object, FromByteRange& FBR) {
359 struct auxdata_traits<T, typename
std::enable_if_t<is_sequence<T>::value>> {
361 return "sequence<" + TypeId<typename T::value_type>::value() +
">";
364 static void toBytes(
const T& Object, ToByteRange& TBR) {
366 std::for_each(Object.begin(), Object.end(), [&](
const auto& Elt) {
367 auxdata_traits<typename T::value_type>::toBytes(Elt, TBR);
371 static bool fromBytes(T& Object, FromByteRange& FBR) {
376 if (Count > FBR.remainingBytesToRead())
379 Object.resize(Count);
381 std::for_each(Object.begin(), Object.end(), [&](
auto& Elt) {
382 if (!auxdata_traits<typename T::value_type>::fromBytes(Elt, FBR))
390 template <
class... Args>
struct auxdata_traits<
std::set<Args...>> {
391 using T = std::set<Args...>;
394 return "set<" + TypeId<typename T::value_type>::value() +
">";
397 static void toBytes(
const T& Object, ToByteRange& TBR) {
399 for (
const auto& Elt : Object)
403 static bool fromBytes(T& Object, FromByteRange& FBR) {
408 if (Count > FBR.remainingBytesToRead())
411 for (uint64_t i = 0; i < Count; i++) {
412 typename T::value_type V;
413 if (!auxdata_traits<decltype(V)>::
fromBytes(V, FBR))
415 Object.emplace(std::move(V));
423 struct auxdata_traits<T, typename
std::enable_if_t<is_mapping<T>::value>> {
426 TypeId<typename T::key_type, typename T::mapped_type>::value() +
">";
429 static void toBytes(
const T& Object, ToByteRange& TBR) {
431 std::for_each(Object.begin(), Object.end(), [&](
const auto& Elt) {
432 auxdata_traits<typename T::key_type>::toBytes(Elt.first, TBR);
433 auxdata_traits<typename T::mapped_type>::toBytes(Elt.second, TBR);
437 static bool fromBytes(T& Object, FromByteRange& FBR) {
442 if (Count > FBR.remainingBytesToRead())
445 for (uint64_t i = 0; i < Count; i++) {
446 typename T::key_type K;
447 if (!auxdata_traits<decltype(K)>::
fromBytes(K, FBR))
449 typename T::mapped_type V;
450 if (!auxdata_traits<decltype(V)>::
fromBytes(V, FBR))
452 Object.emplace(std::move(K), std::move(V));
462 template <
class... Args>
struct auxdata_traits<
std::variant<Args...>> {
463 using T = std::variant<Args...>;
466 return "variant<" + TypeId<Args...>::value() +
">";
469 template <u
int64_t I = 0>
470 static std::optional<std::variant<Args...>> expand_type(uint64_t i) {
471 if constexpr (I >=
sizeof...(Args)) {
474 return i == 0 ? std::variant<Args...>{std::in_place_index<I>,
475 std::variant_alternative_t<I, T>{}}
476 : expand_type<I + 1>(i - 1);
480 static void toBytes(
const T& Object, ToByteRange& TBR) {
481 uint64_t Index = Object.index();
484 [TBR](
auto&& arg)
mutable {
485 auxdata_traits<
typename std::remove_const<
486 typename std::remove_reference<decltype(arg)>::type>::type>
::
492 static bool fromBytes(T& Object, FromByteRange& FBR) {
496 if (Index > FBR.remainingBytesToRead())
499 auto maybeV = expand_type(Index);
504 bool res_code =
false;
506 [&res_code, &FBR](
auto&& arg)
mutable {
507 typename std::remove_reference<decltype(arg)>::type Val;
508 res_code = auxdata_traits<
typename std::remove_reference<decltype(
524 template <
class T>
struct tuple_traits {};
525 template <
class... Ts>
struct tuple_traits<
std::tuple<Ts...>> {
526 using Tuple = std::tuple<Ts...>;
528 static std::string type_name() {
529 return "tuple<" + TypeId<Ts...>::value() +
">";
533 template <
class... Ts>
struct tuple_traits<
std::pair<Ts...>> {
534 using Tuple = std::tuple<Ts...>;
536 static std::string type_name() {
537 return "tuple<" + TypeId<Ts...>::value() +
">";
541 template <
class Func,
size_t... Is>
542 constexpr
void static_for(Func&& f, std::integer_sequence<size_t, Is...>) {
543 (f(std::integral_constant<size_t, Is>{}), ...);
547 struct auxdata_traits<T, typename
std::enable_if_t<is_tuple<T>::value>>
549 static void toBytes(
const T& Object, ToByteRange& TBR) {
552 const auto& F = std::get<i>(Object);
553 auxdata_traits<std::remove_cv_t<
554 std::remove_reference_t<decltype(F)>>>
::toBytes(F, TBR);
556 std::make_index_sequence<std::tuple_size<T>::value>{});
559 static bool fromBytes(T& Object, FromByteRange& FBR) {
563 auto& F = std::get<i>(Object);
564 if (!auxdata_traits<std::remove_cv_t<
565 std::remove_reference_t<decltype(F)>>>::
fromBytes(F, FBR))
568 std::make_index_sequence<std::tuple_size<T>::value>{});
576 template <
class T>
struct TypeId<T> {
580 template <
class T,
class... Ts>
struct TypeId<T, Ts...> {
581 static std::string value() {
592 struct SerializedForm {
593 std::string RawBytes;
594 std::string ProtobufType;
600 virtual ~AuxData() =
default;
614 const SerializedForm& rawData()
const {
return this->SF; }
618 static constexpr std::size_t UNREGISTERED_API_TYPE_ID = 0;
621 virtual std::size_t getApiTypeId()
const {
return UNREGISTERED_API_TYPE_ID; }
625 using MessageType = proto::AuxData;
631 static void fromProtobuf(AuxData& Result,
const MessageType& Message);
636 virtual void toProtobuf(MessageType* Message)
const {
637 toProtobuf(Message, this->SF);
647 void toProtobuf(MessageType* Message,
648 const SerializedForm& SFToSerialize)
const;
653 static bool checkAuxDataMessageType(
const AuxData::MessageType& Message,
654 const std::string& ExpectedName);
657 void save(std::ostream& Out)
const;
660 static std::unique_ptr<AuxData>
661 load(std::istream& In, std::unique_ptr<AuxData> (*FPPtr)(
const MessageType&));
666 friend class AuxDataContainer;
668 template <
typename T>
friend typename T::MessageType toProtobuf(
const T&);
669 friend class SerializationTestHarness;
674 template <
class Schema>
class AuxDataImpl :
public AuxData {
676 AuxDataImpl() =
default;
677 AuxDataImpl(
typename Schema::Type&& Val) : Object(
std::move(Val)){};
680 static std::size_t staticGetApiTypeId() {
681 return typeid(
typename Schema::Type).hash_code();
685 virtual std::size_t getApiTypeId()
const override {
686 return staticGetApiTypeId();
689 const typename Schema::Type* get()
const {
return &Object; }
692 static std::unique_ptr<AuxData> fromProtobuf(
const MessageType& Message) {
695 if (!checkAuxDataMessageType(
704 auto TypedAuxData = std::make_unique<AuxDataImpl<Schema>>();
705 AuxData::fromProtobuf(*TypedAuxData, Message);
706 FromByteRange FBR(TypedAuxData->rawData().RawBytes);
716 virtual void toProtobuf(MessageType* Message)
const override {
721 AuxData::SerializedForm TypedSF;
723 ToByteRange TBR(TypedSF.RawBytes);
725 AuxData::toProtobuf(Message, TypedSF);
729 void save(std::ostream& Out)
const { AuxData::save(Out); }
732 static std::unique_ptr<AuxDataImpl> load([[maybe_unused]] Context& C,
734 return std::unique_ptr<AuxDataImpl>{
735 static_cast<AuxDataImpl*
>(AuxData::load(In, fromProtobuf).release())};
738 typename Schema::Type Object;
740 friend class AuxDataContainer;
741 friend class SerializationTestHarness;
749 #endif // GTIRB_AUXDATA_H