Improve MessageStringCollector
[alexxy/gromacs.git] / src / gromacs / utility / keyvaluetreeserializer.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #include "gmxpre.h"
36
37 #include "keyvaluetreeserializer.h"
38
39 #include <mutex>
40
41 #include "gromacs/utility/iserializer.h"
42 #include "gromacs/utility/keyvaluetree.h"
43 #include "gromacs/utility/keyvaluetreebuilder.h"
44
45 namespace gmx
46 {
47
48 namespace
49 {
50
51 class ValueSerializer
52 {
53 public:
54     static void initSerializers();
55
56     static void              serialize(const KeyValueTreeValue& value, ISerializer* serializer);
57     static KeyValueTreeValue deserialize(ISerializer* serializer);
58
59 private:
60     ValueSerializer() = delete;
61
62     typedef void (*SerializerFunction)(const KeyValueTreeValue& value, ISerializer* serializer);
63     typedef void (*DeserializerFunction)(KeyValueTreeValueBuilder* builder, ISerializer* serializer);
64
65     struct Serializer
66     {
67         unsigned char        typeTag;
68         SerializerFunction   serialize;
69         DeserializerFunction deserialize;
70     };
71     // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
72     static std::mutex s_initMutex;
73     // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
74     static std::map<std::type_index, Serializer> s_serializers;
75     // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
76     static std::map<unsigned char, DeserializerFunction> s_deserializers;
77 };
78
79 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
80 std::mutex ValueSerializer::s_initMutex;
81 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
82 std::map<std::type_index, ValueSerializer::Serializer> ValueSerializer::s_serializers;
83 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
84 std::map<unsigned char, ValueSerializer::DeserializerFunction> ValueSerializer::s_deserializers;
85
86 template<typename T>
87 struct SerializationTraits
88 {
89 };
90
91 template<>
92 struct SerializationTraits<KeyValueTreeObject>
93 {
94     static void serialize(const KeyValueTreeObject& value, ISerializer* serializer)
95     {
96         int count = value.properties().size();
97         serializer->doInt(&count);
98         for (const auto& prop : value.properties())
99         {
100             serializer->doString(const_cast<std::string*>(&prop.key()));
101             ValueSerializer::serialize(prop.value(), serializer);
102         }
103     }
104     static void deserialize(KeyValueTreeValueBuilder* value, ISerializer* serializer)
105     {
106         KeyValueTreeObjectBuilder builder(value->createObject());
107         deserializeObject(&builder, serializer);
108     }
109     static void deserializeObject(KeyValueTreeObjectBuilder* builder, ISerializer* serializer)
110     {
111         int         count = 0;
112         std::string key;
113         serializer->doInt(&count);
114         for (int i = 0; i < count; ++i)
115         {
116             serializer->doString(&key);
117             builder->addRawValue(key, ValueSerializer::deserialize(serializer));
118         }
119     }
120 };
121
122 template<>
123 struct SerializationTraits<KeyValueTreeArray>
124 {
125     static void serialize(const KeyValueTreeArray& array, ISerializer* serializer)
126     {
127         int count = array.values().size();
128         serializer->doInt(&count);
129         for (const auto& value : array.values())
130         {
131             ValueSerializer::serialize(value, serializer);
132         }
133     }
134     static void deserialize(KeyValueTreeValueBuilder* value, ISerializer* serializer)
135     {
136         KeyValueTreeArrayBuilder builder(value->createArray());
137         int                      count = 0;
138         serializer->doInt(&count);
139         for (int i = 0; i < count; ++i)
140         {
141             builder.addRawValue(ValueSerializer::deserialize(serializer));
142         }
143     }
144 };
145
146 template<>
147 struct SerializationTraits<std::string>
148 {
149     static void serialize(const std::string& value, ISerializer* serializer)
150     {
151         serializer->doString(const_cast<std::string*>(&value));
152     }
153     static void deserialize(KeyValueTreeValueBuilder* builder, ISerializer* serializer)
154     {
155         std::string value;
156         serializer->doString(&value);
157         builder->setValue<std::string>(value);
158     }
159 };
160
161 template<>
162 struct SerializationTraits<bool>
163 {
164     static void serialize(bool value, ISerializer* serializer) { serializer->doBool(&value); }
165     static void deserialize(KeyValueTreeValueBuilder* builder, ISerializer* serializer)
166     {
167         bool value = false;
168         serializer->doBool(&value);
169         builder->setValue<bool>(value);
170     }
171 };
172
173 template<>
174 struct SerializationTraits<int>
175 {
176     static void serialize(int value, ISerializer* serializer) { serializer->doInt(&value); }
177     static void deserialize(KeyValueTreeValueBuilder* builder, ISerializer* serializer)
178     {
179         int value = 0;
180         serializer->doInt(&value);
181         builder->setValue<int>(value);
182     }
183 };
184
185 template<>
186 struct SerializationTraits<int64_t>
187 {
188     static void serialize(int64_t value, ISerializer* serializer) { serializer->doInt64(&value); }
189     static void deserialize(KeyValueTreeValueBuilder* builder, ISerializer* serializer)
190     {
191         int64_t value = 0;
192         serializer->doInt64(&value);
193         builder->setValue<int64_t>(value);
194     }
195 };
196
197 template<>
198 struct SerializationTraits<float>
199 {
200     static void serialize(float value, ISerializer* serializer) { serializer->doFloat(&value); }
201     static void deserialize(KeyValueTreeValueBuilder* builder, ISerializer* serializer)
202     {
203         float value = 0;
204         serializer->doFloat(&value);
205         builder->setValue<float>(value);
206     }
207 };
208
209 template<>
210 struct SerializationTraits<double>
211 {
212     static void serialize(double value, ISerializer* serializer) { serializer->doDouble(&value); }
213     static void deserialize(KeyValueTreeValueBuilder* builder, ISerializer* serializer)
214     {
215         double value = 0;
216         serializer->doDouble(&value);
217         builder->setValue<double>(value);
218     }
219 };
220
221 //! Helper function for serializing values of a certain type.
222 template<typename T>
223 void serializeValueType(const KeyValueTreeValue& value, ISerializer* serializer)
224 {
225     SerializationTraits<T>::serialize(value.cast<T>(), serializer);
226 }
227
228 #define SERIALIZER(tag, type)                                                       \
229     {                                                                               \
230         std::type_index(typeid(type)),                                              \
231         {                                                                           \
232             tag, &serializeValueType<type>, &SerializationTraits<type>::deserialize \
233         }                                                                           \
234     }
235
236 // static
237 void ValueSerializer::initSerializers()
238 {
239     std::lock_guard<std::mutex> lock(s_initMutex);
240     if (!s_serializers.empty())
241     {
242         return;
243     }
244     s_serializers = {
245         SERIALIZER('O', KeyValueTreeObject),
246         SERIALIZER('A', KeyValueTreeArray),
247         SERIALIZER('s', std::string),
248         SERIALIZER('b', bool),
249         SERIALIZER('i', int),
250         SERIALIZER('l', int64_t),
251         SERIALIZER('f', float),
252         SERIALIZER('d', double),
253     };
254     for (const auto& item : s_serializers)
255     {
256         s_deserializers[item.second.typeTag] = item.second.deserialize;
257     }
258 }
259
260 void ValueSerializer::serialize(const KeyValueTreeValue& value, ISerializer* serializer)
261 {
262     auto iter = s_serializers.find(value.type());
263     GMX_RELEASE_ASSERT(iter != s_serializers.end(), "Unknown value type for serializization");
264     unsigned char typeTag = iter->second.typeTag;
265     serializer->doUChar(&typeTag);
266     iter->second.serialize(value, serializer);
267 }
268
269 KeyValueTreeValue ValueSerializer::deserialize(ISerializer* serializer)
270 {
271     unsigned char typeTag = 0;
272     serializer->doUChar(&typeTag);
273     auto iter = s_deserializers.find(typeTag);
274     GMX_RELEASE_ASSERT(iter != s_deserializers.end(), "Unknown type tag for deserializization");
275     KeyValueTreeValueBuilder builder;
276     iter->second(&builder, serializer);
277     return builder.build();
278 }
279
280 } // namespace
281
282 //! \cond libapi
283 void serializeKeyValueTree(const KeyValueTreeObject& root, ISerializer* serializer)
284 {
285     GMX_RELEASE_ASSERT(!serializer->reading(), "Incorrect serializer direction");
286     ValueSerializer::initSerializers();
287     SerializationTraits<KeyValueTreeObject>::serialize(root, serializer);
288 }
289
290 KeyValueTreeObject deserializeKeyValueTree(ISerializer* serializer)
291 {
292     GMX_RELEASE_ASSERT(serializer->reading(), "Incorrect serializer direction");
293     ValueSerializer::initSerializers();
294     KeyValueTreeBuilder       builder;
295     KeyValueTreeObjectBuilder obj(builder.rootObject());
296     SerializationTraits<KeyValueTreeObject>::deserializeObject(&obj, serializer);
297     return builder.build();
298 }
299 //! \endcond
300
301 } // namespace gmx