Merge branch origin/release-2020 into master
[alexxy/gromacs.git] / src / gromacs / utility / inmemoryserializer.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2016,2017,2018,2019,2020, 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 /*! \internal \file
36  * \brief
37  * Defines gmx::ISerializer implementation for in-memory serialization.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_utility
41  */
42 #include "gmxpre.h"
43
44 #include "inmemoryserializer.h"
45
46 #include "config.h"
47
48 #include <algorithm>
49 #include <vector>
50
51 namespace gmx
52 {
53
54 namespace
55 {
56
57 template<typename T>
58 class CharBuffer
59 {
60 public:
61     static constexpr size_t ValueSize = sizeof(T);
62
63     explicit CharBuffer(T value) { u.v = value; }
64     explicit CharBuffer(const char buffer[]) { std::copy(buffer, buffer + ValueSize, u.c); }
65
66     T value() const { return u.v; }
67
68     void appendTo(std::vector<char>* buffer)
69     {
70         buffer->insert(buffer->end(), u.c, u.c + ValueSize);
71     }
72
73 private:
74     union {
75         char c[ValueSize];
76         T    v;
77     } u;
78 };
79
80 //! Return \c value with the byte order swapped.
81 template<typename T>
82 T swapEndian(const T& value)
83 {
84     union {
85         T                           value_;
86         std::array<char, sizeof(T)> valueAsCharArray_;
87     } endianessSwappedValue;
88
89     endianessSwappedValue.value_ = value;
90     int hiByte                   = sizeof(T) - 1;
91     for (int loByte = 0; hiByte > loByte; loByte++, hiByte--)
92     {
93         std::swap(endianessSwappedValue.valueAsCharArray_[loByte],
94                   endianessSwappedValue.valueAsCharArray_[hiByte]);
95     }
96
97     return endianessSwappedValue.value_;
98 }
99
100 /*! \brief Change the host-dependent endian settings to either Swap or DoNotSwap.
101  *
102  * \param endianSwapBehavior input swap behavior, might depend on host.
103  *
104  * \return Host-independent setting, either Swap or DoNotSwap. */
105 EndianSwapBehavior setEndianSwapBehaviorFromHost(EndianSwapBehavior endianSwapBehavior)
106 {
107     if (endianSwapBehavior == EndianSwapBehavior::SwapIfHostIsBigEndian)
108     {
109         return GMX_INTEGER_BIG_ENDIAN ? EndianSwapBehavior::Swap : EndianSwapBehavior::DoNotSwap;
110     }
111     else if (endianSwapBehavior == EndianSwapBehavior::SwapIfHostIsLittleEndian)
112     {
113         return GMX_INTEGER_BIG_ENDIAN ? EndianSwapBehavior::DoNotSwap : EndianSwapBehavior::Swap;
114     }
115     else
116     {
117         return endianSwapBehavior;
118     }
119 }
120
121 } // namespace
122
123 /********************************************************************
124  * InMemorySerializer
125  */
126
127 class InMemorySerializer::Impl
128 {
129 public:
130     Impl(EndianSwapBehavior endianSwapBehavior) :
131         endianSwapBehavior_(setEndianSwapBehaviorFromHost(endianSwapBehavior))
132     {
133     }
134
135     template<typename T>
136     void doValue(T value)
137     {
138         if (endianSwapBehavior_ == EndianSwapBehavior::Swap)
139         {
140             CharBuffer<T>(swapEndian(value)).appendTo(&buffer_);
141         }
142         else
143         {
144             CharBuffer<T>(value).appendTo(&buffer_);
145         }
146     }
147     void doString(const std::string& value)
148     {
149         doValue<uint64_t>(value.size());
150         buffer_.insert(buffer_.end(), value.begin(), value.end());
151     }
152     void doOpaque(const char* data, std::size_t size)
153     {
154         buffer_.insert(buffer_.end(), data, data + size);
155     }
156
157     std::vector<char>  buffer_;
158     EndianSwapBehavior endianSwapBehavior_;
159 };
160
161 InMemorySerializer::InMemorySerializer(EndianSwapBehavior endianSwapBehavior) :
162     impl_(new Impl(endianSwapBehavior))
163 {
164 }
165
166 InMemorySerializer::~InMemorySerializer() = default;
167
168 std::vector<char> InMemorySerializer::finishAndGetBuffer()
169 {
170     return std::move(impl_->buffer_);
171 }
172
173 void InMemorySerializer::doBool(bool* value)
174 {
175     impl_->doValue(*value);
176 }
177
178 void InMemorySerializer::doUChar(unsigned char* value)
179 {
180     impl_->doValue(*value);
181 }
182
183 void InMemorySerializer::doChar(char* value)
184 {
185     impl_->doValue(*value);
186 }
187
188 void InMemorySerializer::doUShort(unsigned short* value)
189 {
190     impl_->doValue(*value);
191 }
192
193 void InMemorySerializer::doInt(int* value)
194 {
195     impl_->doValue(*value);
196 }
197
198 void InMemorySerializer::doInt32(int32_t* value)
199 {
200     impl_->doValue(*value);
201 }
202
203 void InMemorySerializer::doInt64(int64_t* value)
204 {
205     impl_->doValue(*value);
206 }
207
208 void InMemorySerializer::doFloat(float* value)
209 {
210     impl_->doValue(*value);
211 }
212
213 void InMemorySerializer::doDouble(double* value)
214 {
215     impl_->doValue(*value);
216 }
217
218 void InMemorySerializer::doReal(real* value)
219 {
220     impl_->doValue(*value);
221 }
222
223 void InMemorySerializer::doRvec(rvec* value)
224 {
225     for (int d = 0; d < DIM; d++)
226     {
227         doReal(&(*value)[d]);
228     }
229 }
230
231 void InMemorySerializer::doIvec(ivec* value)
232 {
233     for (int d = 0; d < DIM; d++)
234     {
235         doInt(&(*value)[d]);
236     }
237 }
238
239 void InMemorySerializer::doString(std::string* value)
240 {
241     impl_->doString(*value);
242 }
243
244 void InMemorySerializer::doOpaque(char* data, std::size_t size)
245 {
246     impl_->doOpaque(data, size);
247 }
248
249 /********************************************************************
250  * InMemoryDeserializer
251  */
252
253 class InMemoryDeserializer::Impl
254 {
255 public:
256     explicit Impl(ArrayRef<const char> buffer, bool sourceIsDouble, EndianSwapBehavior endianSwapBehavior) :
257         buffer_(buffer),
258         sourceIsDouble_(sourceIsDouble),
259         pos_(0),
260         endianSwapBehavior_(setEndianSwapBehaviorFromHost(endianSwapBehavior))
261     {
262     }
263
264     template<typename T>
265     void doValue(T* value)
266     {
267         if (endianSwapBehavior_ == EndianSwapBehavior::Swap)
268         {
269             *value = swapEndian(CharBuffer<T>(&buffer_[pos_]).value());
270         }
271         else
272         {
273             *value = CharBuffer<T>(&buffer_[pos_]).value();
274         }
275         pos_ += CharBuffer<T>::ValueSize;
276     }
277     void doString(std::string* value)
278     {
279         uint64_t size;
280         doValue<uint64_t>(&size);
281         *value = std::string(&buffer_[pos_], size);
282         pos_ += size;
283     }
284     void doOpaque(char* data, std::size_t size)
285     {
286         std::copy(&buffer_[pos_], &buffer_[pos_ + size], data);
287         pos_ += size;
288     }
289
290     ArrayRef<const char> buffer_;
291     bool                 sourceIsDouble_;
292     size_t               pos_;
293     EndianSwapBehavior   endianSwapBehavior_;
294 };
295
296 InMemoryDeserializer::InMemoryDeserializer(ArrayRef<const char> buffer,
297                                            bool                 sourceIsDouble,
298                                            EndianSwapBehavior   endianSwapBehavior) :
299     impl_(new Impl(buffer, sourceIsDouble, endianSwapBehavior))
300 {
301 }
302
303 InMemoryDeserializer::~InMemoryDeserializer() = default;
304
305 bool InMemoryDeserializer::sourceIsDouble() const
306 {
307     return impl_->sourceIsDouble_;
308 }
309
310 void InMemoryDeserializer::doBool(bool* value)
311 {
312     impl_->doValue(value);
313 }
314
315 void InMemoryDeserializer::doUChar(unsigned char* value)
316 {
317     impl_->doValue(value);
318 }
319
320 void InMemoryDeserializer::doChar(char* value)
321 {
322     impl_->doValue(value);
323 }
324
325 void InMemoryDeserializer::doUShort(unsigned short* value)
326 {
327     impl_->doValue(value);
328 }
329
330 void InMemoryDeserializer::doInt(int* value)
331 {
332     impl_->doValue(value);
333 }
334
335 void InMemoryDeserializer::doInt32(int32_t* value)
336 {
337     impl_->doValue(value);
338 }
339
340 void InMemoryDeserializer::doInt64(int64_t* value)
341 {
342     impl_->doValue(value);
343 }
344
345 void InMemoryDeserializer::doFloat(float* value)
346 {
347     impl_->doValue(value);
348 }
349
350 void InMemoryDeserializer::doDouble(double* value)
351 {
352     impl_->doValue(value);
353 }
354
355 void InMemoryDeserializer::doReal(real* value)
356 {
357     if (sourceIsDouble())
358     {
359         double temp = 0.0;
360         doDouble(&temp);
361         *value = temp;
362     }
363     else
364     {
365         float temp = 0.0;
366         doFloat(&temp);
367         *value = temp;
368     }
369 }
370
371 void InMemoryDeserializer::doRvec(rvec* value)
372 {
373     for (int d = 0; d < DIM; d++)
374     {
375         doReal(&(*value)[d]);
376     }
377 }
378
379 void InMemoryDeserializer::doIvec(ivec* value)
380 {
381     for (int d = 0; d < DIM; d++)
382     {
383         doInt(&(*value)[d]);
384     }
385 }
386
387 void InMemoryDeserializer::doString(std::string* value)
388 {
389     impl_->doString(value);
390 }
391
392 void InMemoryDeserializer::doOpaque(char* data, std::size_t size)
393 {
394     impl_->doOpaque(data, size);
395 }
396
397 } // namespace gmx