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