-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathSerialization.hpp
250 lines (226 loc) · 8.78 KB
/
Serialization.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//===- Serialization.hpp ----------------------------------------*- C++ -*-===//
//
// Copyright (C) 2020 GrammaTech, Inc.
//
// This code is licensed under the MIT license. See the LICENSE file in the
// project root for license terms.
//
// This project is sponsored by the Office of Naval Research, One Liberty
// Center, 875 N. Randolph Street, Arlington, VA 22203 under contract #
// N68335-17-C-0700. The content of the information does not necessarily
// reflect the position or policy of the Government and no official
// endorsement should be inferred.
//
//===----------------------------------------------------------------------===//
#ifndef GTIRB_SERIALIZATION_H
#define GTIRB_SERIALIZATION_H
#include <gtirb/Addr.hpp>
#include <gtirb/Node.hpp>
#include <gtirb/Offset.hpp>
#include <google/protobuf/map.h>
#include <google/protobuf/repeated_field.h>
#include <type_traits>
// Utilities for serialization
namespace gtirb {
/// \brief Create UUID from string containing raw bytes.
///
/// \param Bytes A string containing the raw bytes of the UUID.
/// \param Uuid A reference to the resulting UUID.
///
/// \return true if the bytes can be decoded into a UUID, false otherwise.
bool uuidFromBytes(const std::string& Bytes, UUID& Uuid);
/// \brief Copy raw bytes of UUID into a string.
///
/// \param Uuid The UUID to store.
/// \param Bytes The string in which to store the UUID bytes.
///
/// \return void
void uuidToBytes(UUID Uuid, std::string& Bytes);
/// \brief Copy raw bytes of Node's UUID into a string.
///
/// \param Node Store this Node's UUID.
/// \param Bytes The string in which to store the UUID bytes.
///
/// \return void
void nodeUUIDToBytes(const Node* Node, std::string& Bytes);
// Generic protobuf conversion for IR classes which implement toProtobuf.
template <typename T> typename T::MessageType toProtobuf(const T& Val) {
typename T::MessageType Message;
Val.toProtobuf(&Message);
return Message;
}
// Serialize Addr to uint64_t
uint64_t toProtobuf(const Addr Val);
// Overloads for various standard types
std::string toProtobuf(const std::string& Val);
int64_t toProtobuf(const int64_t& Val);
uint64_t toProtobuf(const uint64_t& Val);
std::string toProtobuf(const UUID& Val);
template <typename T> T& deref_if_ptr(T& V) { return V; }
template <typename T> const T& deref_if_ptr(const T& V) { return V; }
template <typename T> T& deref_if_ptr(T* V) { return *V; }
template <typename T> const T& deref_if_ptr(const T* V) { return *V; }
template <typename T> const T& deref_if_ptr(const std::unique_ptr<T>& V) {
return *V;
}
template <typename T, typename U>
auto toProtobuf(const std::pair<T, U>& Val) -> google::protobuf::MapPair<
decltype(toProtobuf(Val.first)),
decltype(toProtobuf(deref_if_ptr(Val.second)))> {
return {toProtobuf(Val.first), toProtobuf(deref_if_ptr(Val.second))};
}
// Generic interface for setting up a container. Clear and reserve space
// if the container supports it.
template <typename T>
void initContainer(google::protobuf::RepeatedField<T>* Container, size_t Size) {
Container->Clear();
Container->Reserve(static_cast<int>(Size));
}
template <typename T>
void initContainer(google::protobuf::RepeatedPtrField<T>* Container,
size_t Size) {
Container->Clear();
Container->Reserve(static_cast<int>(Size));
}
template <typename T>
void initContainer(std::vector<T>& Container, size_t Size) {
Container.clear();
Container.reserve(Size);
}
template <typename T> void initContainer(T* Container, size_t) {
Container->clear();
}
template <typename T> void initContainer(T& Container, size_t) {
Container.clear();
}
// Generic interface for adding elements to a container.
template <typename T>
void addElement(google::protobuf::RepeatedField<T>* Container, T&& Element) {
Container->Add(std::move(Element));
}
template <typename T>
void addElement(google::protobuf::RepeatedPtrField<T>* Container, T&& Element) {
*Container->Add() = std::move(Element);
}
template <typename T, typename U>
void addElement(google::protobuf::Map<T, U>* Container,
typename google::protobuf::Map<T, U>::value_type&& Element) {
Container->insert(std::move(Element));
}
template <typename T> void addElement(std::vector<T>& Container, T&& Element) {
Container.push_back(std::move(Element));
}
template <typename T, typename U>
void addElement(std::map<T, U>* Container,
typename std::map<T, U>::value_type&& Element) {
Container->insert(std::move(Element));
}
template <typename T, typename ContainerType>
std::enable_if_t<std::is_destructible_v<
decltype(std::declval<ContainerType>().insert(std::declval<T>()))*>>
addElement(ContainerType& Container, T&& Element) {
Container.insert(std::move(Element));
}
// Convert the contents of a Container into protobuf messages.
template <typename ContainerT, typename MessageT>
void containerToProtobuf(const ContainerT& Values, MessageT* Message) {
initContainer(Message, Values.size());
std::for_each(Values.begin(), Values.end(), [Message](const auto& N) {
addElement(Message, toProtobuf(deref_if_ptr(N)));
});
}
template <typename IterT, typename MessageT>
void sequenceToProtobuf(IterT First, IterT Last, MessageT* Message) {
while (First != Last)
addElement(Message, toProtobuf(deref_if_ptr(*First++)));
}
// Generic conversion from protobuf for IR classes which implement fromProtobuf;
template <typename T, typename U>
T* fromProtobuf(Context& C, const U& Message) {
return T::fromProtobuf(C, Message);
}
// Generic template for simple types which require no conversion.
template <typename T> bool fromProtobuf(Context&, T& Result, const T& Message) {
Result = Message;
return true;
}
inline bool fromProtobuf(Context& C, Offset& Result,
const Offset::MessageType& Message) {
return Result.fromProtobuf(C, Message);
}
/// @cond INTERNAL
namespace details {
template <typename T> struct remove_pointer_ref_quals {
using type =
std::remove_pointer_t<std::remove_reference_t<std::remove_cv_t<T>>>;
};
template <typename T>
using remove_pointer_ref_quals_t = typename remove_pointer_ref_quals<T>::type;
} // namespace details
/// @endcond
// Overrides for various other types.
template <typename T, typename U, typename V, typename W>
bool fromProtobuf(Context& C, std::pair<T, U>& Val,
const google::protobuf::MapPair<V, W>& Message) {
return fromProtobuf(C, Val.first, Message.first) &&
fromProtobuf(C, Val.second, Message.second);
}
bool fromProtobuf(Context&, Addr& Result, const uint64_t& Message);
bool fromProtobuf(Context&, UUID& Result, const std::string& Message);
// Convert the contents for a Container into IR classes; does not participate
// in overload resolution if the container stores Node subclasses.
template <typename ContainerT, typename MessageT>
bool containerFromProtobuf(
Context& C, ContainerT& Values, MessageT& Message,
std::enable_if_t<
!std::is_base_of_v<Node, details::remove_pointer_ref_quals_t<
typename ContainerT::value_type>>>* =
nullptr) {
initContainer(Values, Message.size());
std::for_each(Message.begin(), Message.end(), [&Values, &C](const auto& M) {
typename ContainerT::value_type Val;
if (!fromProtobuf(C, Val, M))
return false;
addElement(Values, std::move(Val));
});
return true;
}
// Convert the contents for a Container into IR classes. Only participates in
// overload resolution if the container stores Node subclasses.
template <typename ContainerT, typename MessageT>
bool containerFromProtobuf(
Context& C, ContainerT& Values, MessageT& Message,
std::enable_if_t<
std::is_base_of_v<Node, details::remove_pointer_ref_quals_t<
typename ContainerT::value_type>>>* =
nullptr) {
using BaseType =
details::remove_pointer_ref_quals_t<typename ContainerT::value_type>;
initContainer(Values, Message.size());
std::for_each(Message.begin(), Message.end(), [&Values, &C](const auto& M) {
BaseType* Elem = BaseType::fromProtobuf(C, M);
if (!Elem)
return false;
addElement(Values, Elem);
});
return true;
}
// Special case for std::map
template <typename KeyType, typename ValueType, typename MessageT>
bool containerFromProtobuf(Context& C, std::map<KeyType, ValueType>& Values,
MessageT& Message) {
Values.clear();
std::for_each(Message.begin(), Message.end(), [&Values, &C](const auto& M) {
// NOTE: if we could use MapT::value_type here, then this could
// all be rolled into containerFromProtobuf. But that gives us a
// pair where the first Element is const, so we can't pass it to
// fromProtobuf().
std::pair<KeyType, ValueType> Val;
if (!fromProtobuf(C, Val, M))
return false;
Values.insert(std::move(Val));
});
return true;
}
} // namespace gtirb
#endif // GTIRB_SERIALIZATION_H