Remove manual alignment from InMemorySerializer
authorTeemu Murtola <teemu.murtola@gmail.com>
Sat, 11 Mar 2017 17:00:54 +0000 (19:00 +0200)
committerDavid van der Spoel <davidvanderspoel@gmail.com>
Mon, 13 Mar 2017 08:45:16 +0000 (09:45 +0100)
This makes the serialized form more compact, and removes one source of
uncertainty on whether the code works in all possible scenarios.

Also, fix a case that seems to crash on Windows debug builds if a string
is serialized last, indexing the buffer beyond the end even though the
pointer is never dereferenced.

Change-Id: I95ffaefc2296dda1b4f9302003c90fac9ace2a9e

src/gromacs/utility/inmemoryserializer.cpp

index a741b6760773068b43d2287ab939378e0c4043f8..2249a2279229cc84b90adda85878db5160f88b07 100644 (file)
@@ -47,11 +47,34 @@ namespace gmx
 namespace
 {
 
-//! Returns offset to add to `pos` to get it aligned at `alignment` bytes.
-size_t alignedOffset(size_t pos, size_t alignment)
+template <typename T>
+class CharBuffer
 {
-    return (alignment - pos % alignment) % alignment;
-}
+    public:
+        static const size_t ValueSize = sizeof(T);
+
+        explicit CharBuffer(T value)
+        {
+            u.v = value;
+        }
+        explicit CharBuffer(const char buffer[])
+        {
+            std::copy(buffer, buffer + ValueSize, u.c);
+        }
+
+        T value() const { return u.v; }
+
+        void appendTo(std::vector<char> *buffer)
+        {
+            buffer->insert(buffer->end(), u.c, u.c + ValueSize);
+        }
+
+    private:
+        union {
+            char c[ValueSize];
+            T    v;
+        } u;
+};
 
 }   // namespace
 
@@ -65,19 +88,12 @@ class InMemorySerializer::Impl
         template <typename T>
         void doValue(T value)
         {
-            // Here, we assume that the vector memory buffer start is aligned,
-            // similar to what malloc() guarantees.
-            const size_t size = buffer_.size();
-            const size_t pos  = size + alignedOffset(size, alignof(T));
-            buffer_.resize(pos + sizeof(T));
-            *reinterpret_cast<T *>(&buffer_[pos]) = value;
+            CharBuffer<T>(value).appendTo(&buffer_);
         }
         void doString(const std::string &value)
         {
             doValue<size_t>(value.size());
-            const size_t pos = buffer_.size();
-            buffer_.resize(pos + value.size());
-            std::copy(value.begin(), value.end(), &buffer_[pos]);
+            buffer_.insert(buffer_.end(), value.begin(), value.end());
         }
 
         std::vector<char> buffer_;
@@ -142,15 +158,14 @@ class InMemoryDeserializer::Impl
         template <typename T>
         void doValue(T *value)
         {
-            pos_  += alignedOffset(pos_, alignof(T));
-            *value = *reinterpret_cast<const T *>(&buffer_[pos_]);
-            pos_  += sizeof(T);
+            *value = CharBuffer<T>(&buffer_[pos_]).value();
+            pos_  += CharBuffer<T>::ValueSize;
         }
         void doString(std::string *value)
         {
             size_t size;
             doValue<size_t>(&size);
-            *value = std::string(&buffer_[pos_], &buffer_[pos_ + size]);
+            *value = std::string(&buffer_[pos_], size);
             pos_  += size;
         }