Write TPR body as opaque XDR data in big-endian format
authorErik Lindahl <erik@kth.se>
Sun, 29 Dec 2019 13:43:35 +0000 (14:43 +0100)
committerPaul Bauer <paul.bauer.q@gmail.com>
Tue, 7 Jan 2020 11:56:56 +0000 (12:56 +0100)
This implements support for opaque data in our serializers,
as well as a few missing XDR support opaque routines,
including support for data objects larger than 2GB.

By using this for the new serialization of the TPR body
we avoid the unintended 4x growth of the file size, and
avoid introducing extra endian swapping or padding.

We also adjust the endian so the InMemorySerializer
swaps to big endian (i.e., swap if host is little endian)
such that the body of the TPR file is compatible with
the header that is already written as big-endian by
the XDR routines.

Fixes #3276.

Change-Id: I99011dffe190155e671bd656127c2a33459923e2

src/gromacs/fileio/gmxfio_xdr.cpp
src/gromacs/fileio/gmxfio_xdr.h
src/gromacs/fileio/mrcdensitymap.cpp
src/gromacs/fileio/tpxio.cpp
src/gromacs/utility/inmemoryserializer.cpp
src/gromacs/utility/inmemoryserializer.h
src/gromacs/utility/iserializer.h
src/gromacs/utility/tests/inmemoryserializer.cpp
src/gromacs/utility/tests/keyvaluetreeserializer.cpp

index 254dc37fb83f42709d990e6115507fe2005acfe4..2a371921efb3bfc4cdf7e95645fea6de72f0be6a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, 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.
 
 #include "gmxfio_xdr.h"
 
+#include <cstddef>
 #include <cstdio>
 #include <cstring>
 
+#include <limits>
+
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/xdrf.h"
 #include "gromacs/utility/fatalerror.h"
@@ -67,12 +70,13 @@ enum
     eioNRVEC,
     eioIVEC,
     eioSTRING,
+    eioOPAQUE,
     eioNR
 };
 
-static const char* eioNames[eioNR] = { "REAL",   "FLOAT", "DOUBLE", "INT",   "INT32",
-                                       "INT64",  "UCHAR", "CHAR",   "NCHAR", "NUCHAR",
-                                       "USHORT", "RVEC",  "NRVEC",  "IVEC",  "STRING" };
+static const char* eioNames[eioNR] = { "REAL",  "FLOAT", "DOUBLE", "INT",    "INT32",  "INT64",
+                                       "UCHAR", "CHAR",  "NCHAR",  "NUCHAR", "USHORT", "RVEC",
+                                       "NRVEC", "IVEC",  "STRING", "OPAQUE" };
 
 void gmx_fio_setprecision(t_fileio* fio, gmx_bool bDouble)
 {
@@ -101,15 +105,16 @@ XDR* gmx_fio_getxdr(t_fileio* fio)
 }
 
 /* check the number of items given against the type */
-static void gmx_fio_check_nitem(int eio, int nitem, const char* file, int line)
+static void gmx_fio_check_nitem(int eio, std::size_t nitem, const char* file, int line)
 {
-    if ((nitem != 1) && !((eio == eioNRVEC) || (eio == eioNUCHAR) || (eio == eioNCHAR)))
+    if ((nitem != 1)
+        && !((eio == eioNRVEC) || (eio == eioNUCHAR) || (eio == eioNCHAR) || (eio == eioOPAQUE)))
     {
         gmx_fatal(FARGS,
-                  "nitem (%d) may differ from 1 only for %s, %s or %s, not   for %s"
+                  "nitem may differ from 1 only for %s, %s, %s or %s, not for %s"
                   "(%s, %d)",
-                  nitem, eioNames[eioNUCHAR], eioNames[eioNRVEC], eioNames[eioNCHAR], eioNames[eio],
-                  file, line);
+                  eioNames[eioNUCHAR], eioNames[eioNRVEC], eioNames[eioNCHAR], eioNames[eioOPAQUE],
+                  eioNames[eio], file, line);
     }
 }
 
@@ -122,14 +127,15 @@ static void gmx_fio_check_nitem(int eio, int nitem, const char* file, int line)
 
 /* This is the part that reads xdr files.  */
 
-static gmx_bool do_xdr(t_fileio* fio, void* item, int nitem, int eio, const char* desc, const char* srcfile, int line)
+static gmx_bool
+do_xdr(t_fileio* fio, void* item, std::size_t nitem, int eio, const char* desc, const char* srcfile, int line)
 {
     unsigned char  ucdum, *ucptr;
     char           cdum, *cptr;
     bool_t         res = 0;
     float          fvec[DIM];
     double         dvec[DIM];
-    int            j, m, *iptr, idum;
+    int            m, *iptr, idum;
     int32_t        s32dum;
     int64_t        s64dum;
     real*          ptr;
@@ -246,12 +252,17 @@ static gmx_bool do_xdr(t_fileio* fio, void* item, int nitem, int eio, const char
             break;
         case eioNCHAR:
             cptr = static_cast<char*>(item);
-            res  = xdr_vector(fio->xdr, cptr, nitem, static_cast<unsigned int>(sizeof(char)),
+            GMX_RELEASE_ASSERT(nitem < static_cast<std::size_t>(std::numeric_limits<int>::max()),
+                               "The XDR interface cannot handle array lengths > 2^31");
+            res = xdr_vector(fio->xdr, cptr, static_cast<int>(nitem),
+                             static_cast<unsigned int>(sizeof(char)),
                              reinterpret_cast<xdrproc_t>(xdr_char));
             break;
         case eioNUCHAR:
             ucptr = static_cast<unsigned char*>(item);
-            res   = xdr_vector(fio->xdr, reinterpret_cast<char*>(ucptr), nitem,
+            GMX_RELEASE_ASSERT(nitem < static_cast<std::size_t>(std::numeric_limits<int>::max()),
+                               "The XDR interface cannot handle array lengths > 2^31");
+            res = xdr_vector(fio->xdr, reinterpret_cast<char*>(ucptr), static_cast<int>(nitem),
                              static_cast<unsigned int>(sizeof(unsigned char)),
                              reinterpret_cast<xdrproc_t>(xdr_u_char));
             break;
@@ -311,7 +322,7 @@ static gmx_bool do_xdr(t_fileio* fio, void* item, int nitem, int eio, const char
         case eioNRVEC:
             ptr = nullptr;
             res = 1;
-            for (j = 0; (j < nitem) && res; j++)
+            for (std::size_t j = 0; j < nitem && res; j++)
             {
                 if (item)
                 {
@@ -386,6 +397,38 @@ static gmx_bool do_xdr(t_fileio* fio, void* item, int nitem, int eio, const char
             }
             break;
         }
+        case eioOPAQUE:
+        {
+            if (item == nullptr && nitem > 0)
+            {
+                gmx_fatal(FARGS, "Null pointer provided for non-zero length XDR opaque data.");
+            }
+
+            if (nitem > 0)
+            {
+                // We need to support very large opaque data objects although the default
+                // XDR interface only uses integers for the size field, since gromacs-2020
+                // e.g. embeds the entire TPR body as a single such object, which would break all
+                // TPR files larger than 2GB unless we handle it as a special case.
+                // To avoid inserting extra padding, we calculate the chunk size as:
+                // - The max value of a signed integer + 1
+                // - Subtract 4 (the XDR object size) to get a size within the range of the signed int.
+                const std::size_t maxChunk =
+                        static_cast<std::size_t>(std::numeric_limits<int>::max()) + 1 - 4;
+
+                for (res = 1; res > 0 && nitem > 0;)
+                {
+                    std::size_t thisChunk = std::min(maxChunk, nitem);
+                    res = xdr_opaque(fio->xdr, reinterpret_cast<char*>(item), thisChunk);
+                    nitem -= thisChunk;
+                }
+            }
+            else
+            {
+                res = 1;
+            }
+            break;
+        }
         default: gmx_fio_fe(fio, eio, desc, srcfile, line);
     }
 
@@ -537,6 +580,14 @@ gmx_bool gmx_fio_doe_string(t_fileio* fio, char* item, const char* desc, const c
     return ret;
 }
 
+gmx_bool gmx_fio_doe_opaque(t_fileio* fio, char* data, std::size_t size, const char* desc, const char* srcfile, int line)
+{
+    gmx_bool ret;
+    gmx_fio_lock(fio);
+    ret = do_xdr(fio, data, size, eioOPAQUE, desc, srcfile, line);
+    gmx_fio_unlock(fio);
+    return ret;
+}
 
 /* Array reading & writing */
 
@@ -809,4 +860,9 @@ void FileIOXdrSerializer::doString(std::string* value)
     }
 }
 
+void FileIOXdrSerializer::doOpaque(char* data, std::size_t size)
+{
+    gmx_fio_do_opaque(fio_, data, size);
+}
+
 } // namespace gmx
index b70011e686d20cf25fa56d77d08797bdd2d05510..1d8fb38de41044f4abddba02556c37ac82fdd8fd 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, 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.
@@ -37,6 +37,8 @@
 #ifndef GMX_FILEIO_GMXFIO_XDR_H
 #define GMX_FILEIO_GMXFIO_XDR_H
 
+#include <cstddef>
+
 #include <string>
 
 #include "gromacs/fileio/xdrf.h"
@@ -75,6 +77,12 @@ gmx_bool gmx_fio_doe_ushort(struct t_fileio* fio,
 gmx_bool gmx_fio_doe_rvec(struct t_fileio* fio, rvec* item, const char* desc, const char* srcfile, int line);
 gmx_bool gmx_fio_doe_ivec(struct t_fileio* fio, ivec* item, const char* desc, const char* srcfile, int line);
 gmx_bool gmx_fio_doe_string(struct t_fileio* fio, char* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_opaque(struct t_fileio* fio,
+                            char*            item,
+                            std::size_t      size,
+                            const char*      desc,
+                            const char*      srcfile,
+                            int              line);
 
 /* array reading & writing */
 gmx_bool gmx_fio_ndoe_real(struct t_fileio* fio, real* item, int n, const char* desc, const char* srcfile, int line);
@@ -125,6 +133,8 @@ gmx_bool gmx_fio_ndoe_string(struct t_fileio* fio, char* item[], int n, const ch
 #define gmx_fio_do_rvec(fio, item) gmx_fio_doe_rvec(fio, &(item), (#item), __FILE__, __LINE__)
 #define gmx_fio_do_ivec(fio, item) gmx_fio_doe_ivec(fio, &(item), (#item), __FILE__, __LINE__)
 #define gmx_fio_do_string(fio, item) gmx_fio_doe_string(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_opaque(fio, item, size) \
+    gmx_fio_doe_opaque(fio, item, size, (#item), __FILE__, __LINE__)
 
 
 #define gmx_fio_ndo_real(fio, item, n) gmx_fio_ndoe_real(fio, item, n, (#item), __FILE__, __LINE__)
@@ -188,6 +198,8 @@ public:
     void doRvec(rvec* value) override;
     //! Handle I/O if string.
     void doString(std::string* value) override;
+    //! Handle opaque data.
+    void doOpaque(char* data, std::size_t size) override;
     //! Special case for handling I/O of a vector of characters.
     void doCharArray(char* values, int elements) override;
     //! Special case for handling I/O of a vector of unsigned characters.
index ff866adfde4a91c514c13215ea874a43b8438cd7..de5dad28041eb79c895f8d5f712bc2d3bc53ae99 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, 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.
@@ -180,8 +180,8 @@ MrcDensityMapOfFloatFromFileReader::Impl::Impl(const std::string& filename) :
 {
     if (!mrcHeaderIsSane(reader_->header()))
     {
-        serializer_ = std::make_unique<InMemoryDeserializer>(buffer_, false, EndianSwapBehavior::DoSwap);
-        reader_ = std::make_unique<MrcDensityMapOfFloatReader>(serializer_.get());
+        serializer_ = std::make_unique<InMemoryDeserializer>(buffer_, false, EndianSwapBehavior::Swap);
+        reader_     = std::make_unique<MrcDensityMapOfFloatReader>(serializer_.get());
         if (!mrcHeaderIsSane(reader_->header()))
         {
             GMX_THROW(FileIOError(
index 801174e5e55f312f5f50c5bb489a3336350ef257..308c16e35bec2707e7fbab1801e9a58e7f918814 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, 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.
@@ -3129,7 +3129,7 @@ static TpxFileHeader populateTpxHeader(const t_state& state, const t_inputrec* i
 }
 
 /*! \brief
- * Process the body of a TPR file as char buffer.
+ * Process the body of a TPR file as an opaque data buffer.
  *
  * Reads/writes the information in \p buffer from/to the \p serializer
  * provided to the function. Does not interact with the actual
@@ -3143,7 +3143,7 @@ static TpxFileHeader populateTpxHeader(const t_state& state, const t_inputrec* i
  */
 static void doTpxBodyBuffer(gmx::ISerializer* serializer, gmx::ArrayRef<char> buffer)
 {
-    serializer->doCharArray(buffer.data(), buffer.size());
+    serializer->doOpaque(buffer.data(), buffer.size());
 }
 
 /*! \brief
@@ -3199,7 +3199,12 @@ static PartialDeserializedTprFile readTpxBody(TpxFileHeader*    tpx,
     // we prepare a new char buffer with the information we have already read
     // in on master.
     partialDeserializedTpr.header = populateTpxHeader(*state, ir, mtop);
-    gmx::InMemorySerializer tprBodySerializer;
+    // Long-term we should move to use little endian in files to avoid extra byte swapping,
+    // but since we just used the default XDR format (which is big endian) for the TPR
+    // header it would cause third-party libraries reading our raw data to tear their hair
+    // if we swap the endian in the middle of the file, so we stick to big endian in the
+    // TPR file for now - and thus we ask the serializer to swap if this host is little endian.
+    gmx::InMemorySerializer tprBodySerializer(gmx::EndianSwapBehavior::SwapIfHostIsLittleEndian);
     do_tpx_body(&tprBodySerializer, &partialDeserializedTpr.header, ir, mtop);
     partialDeserializedTpr.body = tprBodySerializer.finishAndGetBuffer();
 
@@ -3236,8 +3241,12 @@ void write_tpx_state(const char* fn, const t_inputrec* ir, const t_state* state,
     t_fileio* fio;
 
     TpxFileHeader tpx = populateTpxHeader(*state, ir, mtop);
-
-    gmx::InMemorySerializer tprBodySerializer;
+    // Long-term we should move to use little endian in files to avoid extra byte swapping,
+    // but since we just used the default XDR format (which is big endian) for the TPR
+    // header it would cause third-party libraries reading our raw data to tear their hair
+    // if we swap the endian in the middle of the file, so we stick to big endian in the
+    // TPR file for now - and thus we ask the serializer to swap if this host is little endian.
+    gmx::InMemorySerializer tprBodySerializer(gmx::EndianSwapBehavior::SwapIfHostIsLittleEndian);
 
     do_tpx_body(&tprBodySerializer, &tpx, const_cast<t_inputrec*>(ir), const_cast<t_state*>(state),
                 nullptr, nullptr, const_cast<gmx_mtop_t*>(mtop));
@@ -3260,8 +3269,14 @@ int completeTprDeserialization(PartialDeserializedTprFile* partialDeserializedTp
                                rvec*                       v,
                                gmx_mtop_t*                 mtop)
 {
+    // Long-term we should move to use little endian in files to avoid extra byte swapping,
+    // but since we just used the default XDR format (which is big endian) for the TPR
+    // header it would cause third-party libraries reading our raw data to tear their hair
+    // if we swap the endian in the middle of the file, so we stick to big endian in the
+    // TPR file for now - and thus we ask the serializer to swap if this host is little endian.
     gmx::InMemoryDeserializer tprBodyDeserializer(partialDeserializedTpr->body,
-                                                  partialDeserializedTpr->header.isDouble);
+                                                  partialDeserializedTpr->header.isDouble,
+                                                  gmx::EndianSwapBehavior::SwapIfHostIsLittleEndian);
     return do_tpx_body(&tprBodyDeserializer, &partialDeserializedTpr->header, ir, state, x, v, mtop);
 }
 
index eb012c36fccb4a9fe44d3fa47a848ed5510a72ab..e24befae3f47af7ca253e5f1dcfa722bf8167a88 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, 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.
@@ -36,6 +36,8 @@
 
 #include "inmemoryserializer.h"
 
+#include "config.h"
+
 #include <algorithm>
 #include <vector>
 
@@ -88,6 +90,27 @@ T swapEndian(const T& value)
     return endianessSwappedValue.value_;
 }
 
+//! \brief Change the host-dependent endian settings to either Swap or DoNotSwap.
+//
+// \param endianSwapBehavior input swap behavior, might depend on host.
+//
+// \return Host-independent setting, either Swap or DoNotSwap.
+EndianSwapBehavior setEndianSwapBehaviorFromHost(EndianSwapBehavior endianSwapBehavior)
+{
+    if (endianSwapBehavior == EndianSwapBehavior::SwapIfHostIsBigEndian)
+    {
+        return GMX_INTEGER_BIG_ENDIAN ? EndianSwapBehavior::Swap : EndianSwapBehavior::DoNotSwap;
+    }
+    else if (endianSwapBehavior == EndianSwapBehavior::SwapIfHostIsLittleEndian)
+    {
+        return GMX_INTEGER_BIG_ENDIAN ? EndianSwapBehavior::DoNotSwap : EndianSwapBehavior::Swap;
+    }
+    else
+    {
+        return endianSwapBehavior;
+    }
+}
+
 } // namespace
 
 /********************************************************************
@@ -97,11 +120,15 @@ T swapEndian(const T& value)
 class InMemorySerializer::Impl
 {
 public:
-    Impl(EndianSwapBehavior endianSwapBehavior) : endianSwapBehavior_(endianSwapBehavior) {}
+    Impl(EndianSwapBehavior endianSwapBehavior) :
+        endianSwapBehavior_(setEndianSwapBehaviorFromHost(endianSwapBehavior))
+    {
+    }
+
     template<typename T>
     void doValue(T value)
     {
-        if (endianSwapBehavior_ == EndianSwapBehavior::DoSwap)
+        if (endianSwapBehavior_ == EndianSwapBehavior::Swap)
         {
             CharBuffer<T>(swapEndian(value)).appendTo(&buffer_);
         }
@@ -115,6 +142,10 @@ public:
         doValue<uint64_t>(value.size());
         buffer_.insert(buffer_.end(), value.begin(), value.end());
     }
+    void doOpaque(const char* data, std::size_t size)
+    {
+        buffer_.insert(buffer_.end(), data, data + size);
+    }
 
     std::vector<char>  buffer_;
     EndianSwapBehavior endianSwapBehavior_;
@@ -203,6 +234,11 @@ void InMemorySerializer::doString(std::string* value)
     impl_->doString(*value);
 }
 
+void InMemorySerializer::doOpaque(char* data, std::size_t size)
+{
+    impl_->doOpaque(data, size);
+}
+
 /********************************************************************
  * InMemoryDeserializer
  */
@@ -214,14 +250,14 @@ public:
         buffer_(buffer),
         sourceIsDouble_(sourceIsDouble),
         pos_(0),
-        endianSwapBehavior_(endianSwapBehavior)
+        endianSwapBehavior_(setEndianSwapBehaviorFromHost(endianSwapBehavior))
     {
     }
 
     template<typename T>
     void doValue(T* value)
     {
-        if (endianSwapBehavior_ == EndianSwapBehavior::DoSwap)
+        if (endianSwapBehavior_ == EndianSwapBehavior::Swap)
         {
             *value = swapEndian(CharBuffer<T>(&buffer_[pos_]).value());
         }
@@ -238,6 +274,11 @@ public:
         *value = std::string(&buffer_[pos_], size);
         pos_ += size;
     }
+    void doOpaque(char* data, std::size_t size)
+    {
+        std::copy(&buffer_[pos_], &buffer_[pos_ + size], data);
+        pos_ += size;
+    }
 
     ArrayRef<const char> buffer_;
     bool                 sourceIsDouble_;
@@ -341,4 +382,9 @@ void InMemoryDeserializer::doString(std::string* value)
     impl_->doString(value);
 }
 
+void InMemoryDeserializer::doOpaque(char* data, std::size_t size)
+{
+    impl_->doOpaque(data, size);
+}
+
 } // namespace gmx
index 8b6ac98a70f1ba35430753dc3f2fe3ada0cf4afa..252eeae886a9760b032e30ca82050b6efca97c17 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, 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.
@@ -43,6 +43,8 @@
 #ifndef GMX_UTILITY_INMEMORYSERIALIZER_H
 #define GMX_UTILITY_INMEMORYSERIALIZER_H
 
+#include <cstddef>
+
 #include <vector>
 
 #include "gromacs/utility/arrayref.h"
 namespace gmx
 {
 
-//! Specify endian swapping behvaior
+//! Specify endian swapping behavoir.
+//
+// The host-dependent choices avoid the calling file having to
+// depend on config.h.
+//
 enum class EndianSwapBehavior : int
 {
-    DoSwap,    //!< Swap the bytes
-    DoNotSwap, //!< Do not swap the bytes
-    Count      //!< Number of possible behaviors
+    DoNotSwap,                //!< Don't touch anything
+    Swap,                     //!< User-enforced swapping
+    SwapIfHostIsBigEndian,    //!< Only swap if machine we execute on is big-endian
+    SwapIfHostIsLittleEndian, //!< Only swap if machine we execute on is little-endian
+    Count                     //!< Number of possible behaviors
 };
 
 class InMemorySerializer : public ISerializer
@@ -83,6 +91,7 @@ public:
     void doIvec(ivec* value) override;
     void doRvec(rvec* value) override;
     void doString(std::string* value) override;
+    void doOpaque(char* data, std::size_t size) override;
 
 private:
     class Impl;
@@ -116,6 +125,7 @@ public:
     void doIvec(ivec* value) override;
     void doRvec(rvec* value) override;
     void doString(std::string* value) override;
+    void doOpaque(char* data, std::size_t size) override;
 
 private:
     class Impl;
index 44ad11e3ebc1c8f468d832d6b9449fcf8f6e3380..aa68dea4f7d637c5a5e69806bf42f23f9048f05f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, 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.
@@ -46,6 +46,8 @@
 #ifndef GMX_UTILITY_ISERIALIZER_H
 #define GMX_UTILITY_ISERIALIZER_H
 
+#include <cstddef>
+
 #include <string>
 
 #include "gromacs/math/vectypes.h"
@@ -72,19 +74,20 @@ public:
     virtual bool reading() const = 0;
     //! \brief Serialize values of different types.
     ///@{
-    virtual void doBool(bool* value)             = 0;
-    virtual void doUChar(unsigned char* value)   = 0;
-    virtual void doChar(char* value)             = 0;
-    virtual void doUShort(unsigned short* value) = 0;
-    virtual void doInt(int* value)               = 0;
-    virtual void doInt32(int32_t* value)         = 0;
-    virtual void doInt64(int64_t* value)         = 0;
-    virtual void doFloat(float* value)           = 0;
-    virtual void doDouble(double* value)         = 0;
-    virtual void doReal(real* value)             = 0;
-    virtual void doIvec(ivec* value)             = 0;
-    virtual void doRvec(rvec* value)             = 0;
-    virtual void doString(std::string* value)    = 0;
+    virtual void doBool(bool* value)                    = 0;
+    virtual void doUChar(unsigned char* value)          = 0;
+    virtual void doChar(char* value)                    = 0;
+    virtual void doUShort(unsigned short* value)        = 0;
+    virtual void doInt(int* value)                      = 0;
+    virtual void doInt32(int32_t* value)                = 0;
+    virtual void doInt64(int64_t* value)                = 0;
+    virtual void doFloat(float* value)                  = 0;
+    virtual void doDouble(double* value)                = 0;
+    virtual void doReal(real* value)                    = 0;
+    virtual void doIvec(ivec* value)                    = 0;
+    virtual void doRvec(rvec* value)                    = 0;
+    virtual void doString(std::string* value)           = 0;
+    virtual void doOpaque(char* data, std::size_t size) = 0;
     ///@}
 
     //! \brief Serialize arrays of values of different types.
index a3612eb5afe7198f556e04f45284bd1117d36600..d11e6c91697dafe448431750ea50ecd196437a7f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, 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.
@@ -198,14 +198,14 @@ TEST_F(InMemorySerializerTest, Roundtrip)
 
 TEST_F(InMemorySerializerTest, RoundtripWithEndianessSwap)
 {
-    InMemorySerializer serializerWithSwap(EndianSwapBehavior::DoSwap);
+    InMemorySerializer serializerWithSwap(EndianSwapBehavior::Swap);
     SerializerValues   values = defaultValues_;
     serialize(&serializerWithSwap, &values);
 
     auto buffer = serializerWithSwap.finishAndGetBuffer();
 
     InMemoryDeserializer deserializerWithSwap(buffer, std::is_same<real, double>::value,
-                                              EndianSwapBehavior::DoSwap);
+                                              EndianSwapBehavior::Swap);
 
     SerializerValues deserialisedValues = deserialize(&deserializerWithSwap);
 
@@ -214,7 +214,7 @@ TEST_F(InMemorySerializerTest, RoundtripWithEndianessSwap)
 
 TEST_F(InMemorySerializerTest, SerializerExplicitEndianessSwap)
 {
-    InMemorySerializer serializerWithSwap(EndianSwapBehavior::DoSwap);
+    InMemorySerializer serializerWithSwap(EndianSwapBehavior::Swap);
     SerializerValues   values = defaultValues_;
     serialize(&serializerWithSwap, &values);
 
@@ -235,7 +235,7 @@ TEST_F(InMemorySerializerTest, DeserializerExplicitEndianessSwap)
     auto buffer = serializer.finishAndGetBuffer();
 
     InMemoryDeserializer deserializerWithSwap(buffer, std::is_same<real, double>::value,
-                                              EndianSwapBehavior::DoSwap);
+                                              EndianSwapBehavior::Swap);
 
     SerializerValues deserialisedValues = deserialize(&deserializerWithSwap);
     checkSerializerValuesforEquality(endianessSwappedValues_, deserialisedValues);
index c273d1c9b56987cf3990937bddccf2db10101034..dea592d38f52bd2fe25e5234eaaadcab44273fe9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020, 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.
@@ -36,6 +36,8 @@
 
 #include "gromacs/utility/keyvaluetreeserializer.h"
 
+#include <cstddef>
+
 #include <gtest/gtest.h>
 
 #include "gromacs/utility/inmemoryserializer.h"
@@ -73,6 +75,7 @@ public:
     void doFloat(float* value) override { checker_.checkFloat(*value, nullptr); }
     void doDouble(double* value) override { checker_.checkDouble(*value, nullptr); }
     void doString(std::string* value) override { checker_.checkString(*value, nullptr); }
+    void doOpaque(char* /* value */, std::size_t /* size */) override { raiseAssert(); }
     void doReal(real* /* value */) override { raiseAssert(); }
     void doIvec(ivec* /* value */) override { raiseAssert(); }
     void doRvec(rvec* /* value */) override { raiseAssert(); }