#include <algorithm>
#include <vector>
-#include "gromacs/utility/gmxassert.h"
-
namespace gmx
{
class CharBuffer
{
public:
- static const size_t ValueSize = sizeof(T);
+ static constexpr size_t ValueSize = sizeof(T);
explicit CharBuffer(T value) { u.v = value; }
explicit CharBuffer(const char buffer[]) { std::copy(buffer, buffer + ValueSize, u.c); }
} u;
};
+//! Return \c value with the byte order swapped.
+template<typename T>
+T swapEndian(const T& value)
+{
+ union {
+ T value_;
+ std::array<char, sizeof(T)> valueAsCharArray_;
+ } endianessSwappedValue;
+
+ endianessSwappedValue.value_ = value;
+ int hiByte = sizeof(T) - 1;
+ for (int loByte = 0; hiByte > loByte; loByte++, hiByte--)
+ {
+ std::swap(endianessSwappedValue.valueAsCharArray_[loByte],
+ endianessSwappedValue.valueAsCharArray_[hiByte]);
+ }
+
+ return endianessSwappedValue.value_;
+}
+
} // namespace
/********************************************************************
class InMemorySerializer::Impl
{
public:
+ Impl(EndianSwapBehavior endianSwapBehavior) : endianSwapBehavior_(endianSwapBehavior) {}
template<typename T>
void doValue(T value)
{
- CharBuffer<T>(value).appendTo(&buffer_);
+ if (endianSwapBehavior_ == EndianSwapBehavior::DoSwap)
+ {
+ CharBuffer<T>(swapEndian(value)).appendTo(&buffer_);
+ }
+ else
+ {
+ CharBuffer<T>(value).appendTo(&buffer_);
+ }
}
void doString(const std::string& value)
{
buffer_.insert(buffer_.end(), value.begin(), value.end());
}
- std::vector<char> buffer_;
+ std::vector<char> buffer_;
+ EndianSwapBehavior endianSwapBehavior_;
};
-InMemorySerializer::InMemorySerializer() : impl_(new Impl) {}
+InMemorySerializer::InMemorySerializer(EndianSwapBehavior endianSwapBehavior) :
+ impl_(new Impl(endianSwapBehavior))
+{
+}
-InMemorySerializer::~InMemorySerializer() {}
+InMemorySerializer::~InMemorySerializer() = default;
std::vector<char> InMemorySerializer::finishAndGetBuffer()
{
class InMemoryDeserializer::Impl
{
public:
- explicit Impl(ArrayRef<const char> buffer, bool sourceIsDouble) :
+ explicit Impl(ArrayRef<const char> buffer, bool sourceIsDouble, EndianSwapBehavior endianSwapBehavior) :
buffer_(buffer),
sourceIsDouble_(sourceIsDouble),
- pos_(0)
+ pos_(0),
+ endianSwapBehavior_(endianSwapBehavior)
{
}
template<typename T>
void doValue(T* value)
{
- *value = CharBuffer<T>(&buffer_[pos_]).value();
+ if (endianSwapBehavior_ == EndianSwapBehavior::DoSwap)
+ {
+ *value = swapEndian(CharBuffer<T>(&buffer_[pos_]).value());
+ }
+ else
+ {
+ *value = CharBuffer<T>(&buffer_[pos_]).value();
+ }
pos_ += CharBuffer<T>::ValueSize;
}
void doString(std::string* value)
ArrayRef<const char> buffer_;
bool sourceIsDouble_;
size_t pos_;
+ EndianSwapBehavior endianSwapBehavior_;
};
-InMemoryDeserializer::InMemoryDeserializer(ArrayRef<const char> buffer, bool sourceIsDouble) :
- impl_(new Impl(buffer, sourceIsDouble))
+InMemoryDeserializer::InMemoryDeserializer(ArrayRef<const char> buffer,
+ bool sourceIsDouble,
+ EndianSwapBehavior endianSwapBehavior) :
+ impl_(new Impl(buffer, sourceIsDouble, endianSwapBehavior))
{
}
-InMemoryDeserializer::~InMemoryDeserializer() {}
+InMemoryDeserializer::~InMemoryDeserializer() = default;
bool InMemoryDeserializer::sourceIsDouble() const
{
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for gmx::InMemorySerializer and InMemoryDeserializer.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_utility
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/utility/inmemoryserializer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+union IntAndFloat32 {
+ std::int32_t int32Value_;
+ float floatValue_;
+};
+
+union IntAndFloat64 {
+ std::int64_t int64Value_;
+ double doubleValue_;
+};
+
+//! Constants used for testing endian swap operations
+//! \{
+constexpr std::int16_t c_int16Value = static_cast<std::int16_t>(0x7A2B);
+constexpr std::int16_t c_int16ValueSwapped = static_cast<std::int16_t>(0x2B7A);
+constexpr std::int32_t c_int32Value = static_cast<std::int32_t>(0x78ABCDEF);
+constexpr std::int32_t c_int32ValueSwapped = static_cast<std::int32_t>(0xEFCDAB78);
+constexpr std::int64_t c_int64Value = static_cast<std::int64_t>(0x78ABCDEF12345678);
+constexpr std::int64_t c_int64ValueSwapped = static_cast<std::int64_t>(0x78563412EFCDAB78);
+
+constexpr const IntAndFloat32 c_intAndFloat32{ c_int32Value };
+constexpr const IntAndFloat64 c_intAndFloat64{ c_int64Value };
+
+constexpr const IntAndFloat32 c_intAndFloat32Swapped{ c_int32ValueSwapped };
+constexpr const IntAndFloat64 c_intAndFloat64Swapped{ c_int64ValueSwapped };
+//! \}
+
+//! Return the integer used for testing, depending on the size of int.
+constexpr int integerSizeDependentTestingValue()
+{
+ return sizeof(int) == 4 ? c_int32Value : sizeof(int) == 8 ? c_int64Value : c_int16Value;
+}
+
+//! Return the endianess-swapped integer used for testing, depending on the size of int.
+constexpr int integerSizeDependentTestingValueEndianessSwapped()
+{
+ return sizeof(int) == 4 ? c_int32ValueSwapped
+ : sizeof(int) == 8 ? c_int64ValueSwapped : c_int16ValueSwapped;
+}
+
+class InMemorySerializerTest : public ::testing::Test
+{
+public:
+ struct SerializerValues
+ {
+ bool boolValue_;
+ unsigned char unsignedCharValue_;
+ char charValue_;
+ unsigned short unsignedShortValue_;
+ std::int32_t int32Value_;
+ float floatValue_;
+ std::int64_t int64Value_;
+ double doubleValue_;
+ int intValue_;
+ real realValue_;
+ };
+
+ void serialize(ISerializer* serializer, SerializerValues* values)
+ {
+ EXPECT_FALSE(serializer->reading());
+ doValues(serializer, values);
+ }
+
+ SerializerValues deserialize(ISerializer* serializer)
+ {
+ EXPECT_TRUE(serializer->reading());
+ SerializerValues result;
+ doValues(serializer, &result);
+ return result;
+ }
+
+ void checkSerializerValuesforEquality(const SerializerValues& lhs, const SerializerValues& rhs)
+ {
+ EXPECT_EQ(lhs.boolValue_, rhs.boolValue_);
+ EXPECT_EQ(lhs.unsignedCharValue_, rhs.unsignedCharValue_);
+ EXPECT_EQ(lhs.charValue_, rhs.charValue_);
+ EXPECT_EQ(lhs.intValue_, rhs.intValue_);
+ EXPECT_EQ(lhs.int32Value_, rhs.int32Value_);
+ EXPECT_EQ(lhs.int64Value_, rhs.int64Value_);
+ EXPECT_EQ(lhs.unsignedShortValue_, rhs.unsignedShortValue_);
+ EXPECT_EQ(lhs.realValue_, rhs.realValue_);
+ EXPECT_EQ(lhs.floatValue_, rhs.floatValue_);
+ EXPECT_EQ(lhs.doubleValue_, rhs.doubleValue_);
+ }
+
+private:
+ void doValues(ISerializer* serializer, SerializerValues* values)
+ {
+ serializer->doBool(&values->boolValue_);
+ serializer->doUChar(&values->unsignedCharValue_);
+ serializer->doChar(&values->charValue_);
+ serializer->doInt(&values->intValue_);
+ serializer->doInt32(&values->int32Value_);
+ serializer->doInt64(&values->int64Value_);
+ serializer->doUShort(&values->unsignedShortValue_);
+ serializer->doReal(&values->realValue_);
+ serializer->doFloat(&values->floatValue_);
+ serializer->doDouble(&values->doubleValue_);
+ }
+
+protected:
+ SerializerValues defaultValues_ = { true,
+ 0x78,
+ 0x78,
+ static_cast<unsigned short>(c_int16Value),
+ c_int32Value,
+ c_intAndFloat32.floatValue_,
+ c_int64Value,
+ c_intAndFloat64.doubleValue_,
+ integerSizeDependentTestingValue(),
+ std::is_same<real, double>::value
+ ? static_cast<real>(c_intAndFloat64.doubleValue_)
+ : static_cast<real>(c_intAndFloat32.floatValue_) };
+
+ SerializerValues endianessSwappedValues_ = {
+ true,
+ 0x78,
+ 0x78,
+ static_cast<unsigned short>(c_int16ValueSwapped),
+ c_int32ValueSwapped,
+ c_intAndFloat32Swapped.floatValue_,
+ c_int64ValueSwapped,
+ c_intAndFloat64Swapped.doubleValue_,
+ integerSizeDependentTestingValueEndianessSwapped(),
+ std::is_same<real, float>::value ? static_cast<real>(c_intAndFloat32Swapped.floatValue_)
+ : static_cast<real>(c_intAndFloat64Swapped.doubleValue_)
+ };
+};
+
+TEST_F(InMemorySerializerTest, Roundtrip)
+{
+ InMemorySerializer serializer;
+ SerializerValues values = defaultValues_;
+ serialize(&serializer, &values);
+
+ auto buffer = serializer.finishAndGetBuffer();
+
+ InMemoryDeserializer deserializer(buffer, std::is_same<real, double>::value);
+
+ SerializerValues deserialisedValues = deserialize(&deserializer);
+
+ checkSerializerValuesforEquality(values, deserialisedValues);
+}
+
+TEST_F(InMemorySerializerTest, RoundtripWithEndianessSwap)
+{
+ InMemorySerializer serializerWithSwap(EndianSwapBehavior::DoSwap);
+ SerializerValues values = defaultValues_;
+ serialize(&serializerWithSwap, &values);
+
+ auto buffer = serializerWithSwap.finishAndGetBuffer();
+
+ InMemoryDeserializer deserializerWithSwap(buffer, std::is_same<real, double>::value,
+ EndianSwapBehavior::DoSwap);
+
+ SerializerValues deserialisedValues = deserialize(&deserializerWithSwap);
+
+ checkSerializerValuesforEquality(values, deserialisedValues);
+}
+
+TEST_F(InMemorySerializerTest, SerializerExplicitEndianessSwap)
+{
+ InMemorySerializer serializerWithSwap(EndianSwapBehavior::DoSwap);
+ SerializerValues values = defaultValues_;
+ serialize(&serializerWithSwap, &values);
+
+ auto buffer = serializerWithSwap.finishAndGetBuffer();
+
+ InMemoryDeserializer deserializerWithOutSwap(buffer, std::is_same<real, double>::value);
+
+ SerializerValues deserialisedValues = deserialize(&deserializerWithOutSwap);
+ checkSerializerValuesforEquality(endianessSwappedValues_, deserialisedValues);
+}
+
+TEST_F(InMemorySerializerTest, DeserializerExplicitEndianessSwap)
+{
+ InMemorySerializer serializer;
+ SerializerValues values = defaultValues_;
+ serialize(&serializer, &values);
+
+ auto buffer = serializer.finishAndGetBuffer();
+
+ InMemoryDeserializer deserializerWithSwap(buffer, std::is_same<real, double>::value,
+ EndianSwapBehavior::DoSwap);
+
+ SerializerValues deserialisedValues = deserialize(&deserializerWithSwap);
+ checkSerializerValuesforEquality(endianessSwappedValues_, deserialisedValues);
+}
+
+} // namespace
+} // namespace test
+} // namespace gmx