From 438f4de01edfee5d1e6924ee3510f9a20bade1f9 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Fri, 20 Oct 2017 10:26:55 +0200
Subject: [PATCH] ENH: additional low-level raw binary output for Ostream.

- low-level beginRaw(), writeRaw(), endRaw() methods.
  These can be used to directly add '()' decorators for serial output
  or prepare/cleanup parallel buffers.
  Used, for example, when outputting indirect lists in binary to avoid.
---
 .../test/IndirectList/Test-IndirectList.C     | 65 +++++++++++-
 .../containers/Lists/FixedList/FixedListIO.C  | 16 +--
 src/OpenFOAM/containers/Lists/List/ListIO.C   | 16 +--
 .../containers/Lists/PtrList/PtrListIO.C      | 10 +-
 .../Lists/UIndirectList/UIndirectListIO.C     | 67 +++++++------
 src/OpenFOAM/containers/Lists/UList/UListIO.C | 72 +++++++-------
 .../containers/Lists/UPtrList/UPtrListIO.C    | 18 ++--
 src/OpenFOAM/db/IOstreams/IOstreams/Ostream.H | 45 ++++++---
 .../db/IOstreams/Pstreams/UOPstream.C         | 99 +++++++++++++++----
 .../db/IOstreams/Pstreams/UOPstream.H         | 27 ++++-
 src/OpenFOAM/db/IOstreams/Sstreams/OSstream.C | 48 ++++++++-
 src/OpenFOAM/db/IOstreams/Sstreams/OSstream.H | 21 +++-
 12 files changed, 370 insertions(+), 134 deletions(-)

diff --git a/applications/test/IndirectList/Test-IndirectList.C b/applications/test/IndirectList/Test-IndirectList.C
index 4727ed5248..449af8ca7d 100644
--- a/applications/test/IndirectList/Test-IndirectList.C
+++ b/applications/test/IndirectList/Test-IndirectList.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2017 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,15 +27,18 @@ Description
 
 #include "IndirectList.H"
 #include "IOstreams.H"
+#include "Fstream.H"
 #include "ListOps.H"
 #include "labelIndList.H"
+#include "argList.H"
 
 using namespace Foam;
 
 template<class ListType>
 void printInfo(const ListType& lst)
 {
-    Info<< "addr: " << flatOutput(lst.addressing()) << nl
+    Info<< "full: " << flatOutput(lst.completeList()) << nl
+        << "addr: " << flatOutput(lst.addressing()) << nl
         << "list: " << flatOutput(lst) << nl
         << endl;
 }
@@ -61,6 +64,15 @@ void testFind(const T& val, const ListType& lst)
 
 int main(int argc, char *argv[])
 {
+    argList::addOption
+    (
+        "binary",
+        "file",
+        "write lists in binary to specified file"
+    );
+
+    argList args(argc, argv);
+
     List<label> completeList(20);
 
     forAll(completeList, i)
@@ -104,6 +116,55 @@ int main(int argc, char *argv[])
     printInfo(idl2);
     printInfo(idl3);
 
+    fileName binaryOutput;
+    if (args.optionReadIfPresent("binary", binaryOutput))
+    {
+        Info<<"Writing output to " << binaryOutput << endl;
+
+        OFstream os(binaryOutput, IOstream::BINARY);
+
+        os.writeEntry("idl1", idl1);
+        os.writeEntry("idl2", idl2);
+        os.writeEntry("idl3", idl3);
+    }
+
+    if (Pstream::parRun())
+    {
+        if (Pstream::master())
+        {
+            Pout<< "full: " << flatOutput(idl3.completeList()) << nl
+                << "send: " << flatOutput(idl3) << endl;
+
+            for
+            (
+                int slave = Pstream::firstSlave();
+                slave <= Pstream::lastSlave();
+                ++slave
+            )
+            {
+                OPstream toSlave(Pstream::commsTypes::scheduled, slave);
+                toSlave << idl3;
+            }
+        }
+        else
+        {
+            // From master
+            IPstream fromMaster
+            (
+                Pstream::commsTypes::scheduled,
+                Pstream::masterNo()
+            );
+
+            List<label> recv(fromMaster);
+
+            Pout<<"recv: " << flatOutput(recv) << endl;
+        }
+
+        // MPI barrier
+        bool barrier = true;
+        Pstream::scatter(barrier);
+    }
+
     Info<< "End\n" << endl;
 
     return 0;
diff --git a/src/OpenFOAM/containers/Lists/FixedList/FixedListIO.C b/src/OpenFOAM/containers/Lists/FixedList/FixedListIO.C
index 6d1d65b20e..87851fcef4 100644
--- a/src/OpenFOAM/containers/Lists/FixedList/FixedListIO.C
+++ b/src/OpenFOAM/containers/Lists/FixedList/FixedListIO.C
@@ -91,10 +91,10 @@ Foam::Ostream& Foam::FixedList<T, Size>::writeList
             // Write size (so it is valid dictionary entry) and start delimiter
             os << Size << token::BEGIN_BLOCK;
 
-            // Write contents
+            // Contents
             os << L[0];
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_BLOCK;
         }
         else if
@@ -103,31 +103,31 @@ Foam::Ostream& Foam::FixedList<T, Size>::writeList
          || (Size <= unsigned(shortListLen) && contiguous<T>())
         )
         {
-            // Write start delimiter
+            // Start delimiter
             os << token::BEGIN_LIST;
 
-            // Write contents
+            // Contents
             forAll(L, i)
             {
                 if (i) os << token::SPACE;
                 os << L[i];
             }
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_LIST;
         }
         else
         {
-            // Write start delimiter
+            // Start delimiter
             os << nl << token::BEGIN_LIST << nl;
 
-            // Write contents
+            // Contents
             forAll(L, i)
             {
                 os << L[i] << nl;
             }
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_LIST << nl;
         }
     }
diff --git a/src/OpenFOAM/containers/Lists/List/ListIO.C b/src/OpenFOAM/containers/Lists/List/ListIO.C
index b21b6a8664..b29418c24a 100644
--- a/src/OpenFOAM/containers/Lists/List/ListIO.C
+++ b/src/OpenFOAM/containers/Lists/List/ListIO.C
@@ -64,10 +64,10 @@ Foam::Istream& Foam::operator>>(Istream& is, List<T>& L)
     }
     else if (firstToken.isLabel())
     {
-        const label s = firstToken.labelToken();
+        const label sz = firstToken.labelToken();
 
         // Set list length to that read
-        L.setSize(s);
+        L.setSize(sz);
 
         // Read list contents depending on data format
 
@@ -76,11 +76,11 @@ Foam::Istream& Foam::operator>>(Istream& is, List<T>& L)
             // Read beginning of contents
             const char delimiter = is.readBeginList("List");
 
-            if (s)
+            if (sz)
             {
                 if (delimiter == token::BEGIN_LIST)
                 {
-                    for (label i=0; i<s; ++i)
+                    for (label i=0; i<sz; ++i)
                     {
                         is >> L[i];
 
@@ -103,7 +103,7 @@ Foam::Istream& Foam::operator>>(Istream& is, List<T>& L)
                         "reading the single entry"
                     );
 
-                    for (label i=0; i<s; ++i)
+                    for (label i=0; i<sz; ++i)
                     {
                         L[i] = element;
                     }
@@ -115,11 +115,11 @@ Foam::Istream& Foam::operator>>(Istream& is, List<T>& L)
         }
         else
         {
-            // contents are binary and contiguous
+            // Contents are binary and contiguous
 
-            if (s)
+            if (sz)
             {
-                is.read(reinterpret_cast<char*>(L.data()), s*sizeof(T));
+                is.read(reinterpret_cast<char*>(L.data()), sz*sizeof(T));
 
                 is.fatalCheck
                 (
diff --git a/src/OpenFOAM/containers/Lists/PtrList/PtrListIO.C b/src/OpenFOAM/containers/Lists/PtrList/PtrListIO.C
index 6aff9ed81e..594aa01e4d 100644
--- a/src/OpenFOAM/containers/Lists/PtrList/PtrListIO.C
+++ b/src/OpenFOAM/containers/Lists/PtrList/PtrListIO.C
@@ -48,19 +48,19 @@ void Foam::PtrList<T>::read(Istream& is, const INew& inewt)
     if (firstToken.isLabel())
     {
         // Read size of list
-        const label s = firstToken.labelToken();
+        const label sz = firstToken.labelToken();
 
         // Set list length to that read
-        setSize(s);
+        setSize(sz);
 
         // Read beginning of contents
         const char delimiter = is.readBeginList("PtrList");
 
-        if (s)
+        if (sz)
         {
             if (delimiter == token::BEGIN_LIST)
             {
-                forAll(*this, i)
+                for (label i=0; i<sz; ++i)
                 {
                     set(i, inewt(is));
 
@@ -82,7 +82,7 @@ void Foam::PtrList<T>::read(Istream& is, const INew& inewt)
                     "reading the single entry"
                 );
 
-                for (label i=1; i<s; ++i)
+                for (label i=1; i<sz; ++i)
                 {
                     set(i, tPtr->clone());
                 }
diff --git a/src/OpenFOAM/containers/Lists/UIndirectList/UIndirectListIO.C b/src/OpenFOAM/containers/Lists/UIndirectList/UIndirectListIO.C
index de49fc74a0..b990952e2d 100644
--- a/src/OpenFOAM/containers/Lists/UIndirectList/UIndirectListIO.C
+++ b/src/OpenFOAM/containers/Lists/UIndirectList/UIndirectListIO.C
@@ -39,14 +39,16 @@ Foam::Ostream& Foam::UIndirectList<T>::writeList
 {
     const UIndirectList<T>& L = *this;
 
+    const label sz = L.size();
+
     // Write list contents depending on data format
     if (os.format() == IOstream::ASCII || !contiguous<T>())
     {
         // Can the contents be considered 'uniform' (ie, identical)?
-        bool uniform = (L.size() > 1 && contiguous<T>());
+        bool uniform = (sz > 1 && contiguous<T>());
         if (uniform)
         {
-            forAll(L, i)
+            for (label i=1; i < sz; ++i)
             {
                 if (L[i] != L[0])
                 {
@@ -58,65 +60,72 @@ Foam::Ostream& Foam::UIndirectList<T>::writeList
 
         if (uniform)
         {
-            // Write size and start delimiter
-            os << L.size() << token::BEGIN_BLOCK;
+            // Size and start delimiter
+            os << sz << token::BEGIN_BLOCK;
 
-            // Write contents
+            // Contents
             os << L[0];
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_BLOCK;
         }
         else if
         (
-            L.size() <= 1 || !shortListLen
-         || (L.size() <= shortListLen && contiguous<T>())
+            sz <= 1 || !shortListLen
+         || (sz <= shortListLen && contiguous<T>())
         )
         {
-            // Write size and start delimiter
-            os << L.size() << token::BEGIN_LIST;
+            // Size and start delimiter
+            os << sz << token::BEGIN_LIST;
 
-            // Write contents
-            forAll(L, i)
+            // Contents
+            for (label i=0; i < sz; ++i)
             {
                 if (i) os << token::SPACE;
                 os << L[i];
             }
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_LIST;
         }
         else
         {
-            // Write size and start delimiter
-            os << nl << L.size() << nl << token::BEGIN_LIST << nl;
+            // Size and start delimiter
+            os << nl << sz << nl << token::BEGIN_LIST << nl;
 
-            // Write contents
-            forAll(L, i)
+            // Contents
+            for (label i=0; i < sz; ++i)
             {
                 os << L[i] << nl;
             }
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_LIST << nl;
         }
     }
     else
     {
         // Contents are binary and contiguous
-        os << nl << L.size() << nl;
+        os << nl << sz << nl;
 
-        if (L.size())
+        if (sz)
         {
-            // This is annoying, and wasteful, but currently no alternative
-            List<T> lst = L();
-
-            // write(...) includes surrounding start/end delimiters
-            os.write
-            (
-                reinterpret_cast<const char*>(lst.cdata()),
-                lst.byteSize()
-            );
+            // The TOTAL number of bytes to be written.
+            // - possibly add start delimiter
+            os.beginRaw(sz*sizeof(T));
+
+            // Contents
+            for (label i=0; i < sz; ++i)
+            {
+                os.writeRaw
+                (
+                    reinterpret_cast<const char*>(&(L[i])),
+                    sizeof(T)
+                );
+            }
+
+            // End delimiter and/or cleanup.
+            os.endRaw();
         }
     }
 
diff --git a/src/OpenFOAM/containers/Lists/UList/UListIO.C b/src/OpenFOAM/containers/Lists/UList/UListIO.C
index efb2ad3ffc..08d1856917 100644
--- a/src/OpenFOAM/containers/Lists/UList/UListIO.C
+++ b/src/OpenFOAM/containers/Lists/UList/UListIO.C
@@ -76,14 +76,16 @@ Foam::Ostream& Foam::UList<T>::writeList
 {
     const UList<T>& L = *this;
 
+    const label sz = L.size();
+
     // Write list contents depending on data format
     if (os.format() == IOstream::ASCII || !contiguous<T>())
     {
         // Can the contents be considered 'uniform' (ie, identical)?
-        bool uniform = (L.size() > 1 && contiguous<T>());
+        bool uniform = (sz > 1 && contiguous<T>());
         if (uniform)
         {
-            forAll(L, i)
+            for (label i=1; i < sz; ++i)
             {
                 if (L[i] != L[0])
                 {
@@ -95,58 +97,62 @@ Foam::Ostream& Foam::UList<T>::writeList
 
         if (uniform)
         {
-            // Write size and start delimiter
-            os << L.size() << token::BEGIN_BLOCK;
+            // Size and start delimiter
+            os << sz << token::BEGIN_BLOCK;
 
-            // Write contents
+            // Contents
             os << L[0];
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_BLOCK;
         }
         else if
         (
-            L.size() <= 1 || !shortListLen
-         || (L.size() <= shortListLen && contiguous<T>())
+            sz <= 1 || !shortListLen
+         || (sz <= shortListLen && contiguous<T>())
         )
         {
-            // Write size and start delimiter
-            os << L.size() << token::BEGIN_LIST;
+            // Size and start delimiter
+            os << sz << token::BEGIN_LIST;
 
-            // Write contents
-            forAll(L, i)
+            // Contents
+            for (label i=0; i < sz; ++i)
             {
                 if (i) os << token::SPACE;
                 os << L[i];
             }
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_LIST;
         }
         else
         {
-            // Write size and start delimiter
-            os << nl << L.size() << nl << token::BEGIN_LIST << nl;
+            // Size and start delimiter
+            os << nl << sz << nl << token::BEGIN_LIST << nl;
 
-            // Write contents
-            forAll(L, i)
+            // Contents
+            for (label i=0; i < sz; ++i)
             {
                 os << L[i] << nl;
             }
 
-            // Write end delimiter
+            // End delimiter
             os << token::END_LIST << nl;
         }
     }
     else
     {
         // Contents are binary and contiguous
-        os << nl << L.size() << nl;
+        os << nl << sz << nl;
 
-        if (L.size())
+        if (sz)
         {
             // write(...) includes surrounding start/end delimiters
-            os.write(reinterpret_cast<const char*>(L.cdata()), L.byteSize());
+            os.write
+            (
+                reinterpret_cast<const char*>(L.cdata()),
+                L.byteSize()
+            );
         }
     }
 
@@ -184,29 +190,29 @@ Foam::Istream& Foam::operator>>(Istream& is, UList<T>& L)
             )
         );
         // Check list length
-        const label s = elems.size();
+        const label sz = elems.size();
 
-        if (s != L.size())
+        if (sz != L.size())
         {
             FatalIOErrorInFunction(is)
-                << "incorrect length for UList. Read " << s
+                << "incorrect length for UList. Read " << sz
                 << " expected " << L.size()
                 << exit(FatalIOError);
         }
-        for (label i=0; i<s; ++i)
+        for (label i=0; i<sz; ++i)
         {
             L[i] = elems[i];
         }
     }
     else if (firstToken.isLabel())
     {
-        const label s = firstToken.labelToken();
+        const label sz = firstToken.labelToken();
 
         // Set list length to that read
-        if (s != L.size())
+        if (sz != L.size())
         {
             FatalIOErrorInFunction(is)
-                << "incorrect length for UList. Read " << s
+                << "incorrect length for UList. Read " << sz
                 << " expected " << L.size()
                 << exit(FatalIOError);
         }
@@ -218,11 +224,11 @@ Foam::Istream& Foam::operator>>(Istream& is, UList<T>& L)
             // Read beginning of contents
             const char delimiter = is.readBeginList("List");
 
-            if (s)
+            if (sz)
             {
                 if (delimiter == token::BEGIN_LIST)
                 {
-                    for (label i=0; i<s; ++i)
+                    for (label i=0; i<sz; ++i)
                     {
                         is >> L[i];
 
@@ -245,7 +251,7 @@ Foam::Istream& Foam::operator>>(Istream& is, UList<T>& L)
                         "reading the single entry"
                     );
 
-                    for (label i=0; i<s; ++i)
+                    for (label i=0; i<sz; ++i)
                     {
                         L[i] = element;
                     }
@@ -259,9 +265,9 @@ Foam::Istream& Foam::operator>>(Istream& is, UList<T>& L)
         {
             // contents are binary and contiguous
 
-            if (s)
+            if (sz)
             {
-                is.read(reinterpret_cast<char*>(L.data()), s*sizeof(T));
+                is.read(reinterpret_cast<char*>(L.data()), sz*sizeof(T));
 
                 is.fatalCheck
                 (
diff --git a/src/OpenFOAM/containers/Lists/UPtrList/UPtrListIO.C b/src/OpenFOAM/containers/Lists/UPtrList/UPtrListIO.C
index 2852654dda..33ff3d9878 100644
--- a/src/OpenFOAM/containers/Lists/UPtrList/UPtrListIO.C
+++ b/src/OpenFOAM/containers/Lists/UPtrList/UPtrListIO.C
@@ -31,18 +31,20 @@ License
 template<class T>
 Foam::Ostream& Foam::operator<<(Ostream& os, const UPtrList<T>& L)
 {
-    // Write size and start delimiter
-    os  << nl << indent << L.size() << nl
-        << indent << token::BEGIN_LIST << incrIndent;
+    const label sz = L.size();
 
-    // Write contents
-    forAll(L, i)
+    // Size and start delimiter
+    os  << nl << indent << sz << nl
+        << indent << token::BEGIN_LIST << incrIndent << nl;
+
+    // Contents
+    for (label i=0; i < sz; ++i)
     {
-        os << nl << L[i];
+        os << L[i] << nl;
     }
 
-    // Write end delimiter
-    os << nl << decrIndent << indent << token::END_LIST << nl;
+    // End delimiter
+    os << decrIndent << indent << token::END_LIST << nl;
 
     os.check(FUNCTION_NAME);
     return os;
diff --git a/src/OpenFOAM/db/IOstreams/IOstreams/Ostream.H b/src/OpenFOAM/db/IOstreams/IOstreams/Ostream.H
index 3604a22d0b..916bae2278 100644
--- a/src/OpenFOAM/db/IOstreams/IOstreams/Ostream.H
+++ b/src/OpenFOAM/db/IOstreams/IOstreams/Ostream.H
@@ -97,47 +97,62 @@ public:
         // Write functions
 
             //- Write next token to stream
-            virtual Ostream& write(const token&) = 0;
+            virtual Ostream& write(const token& t) = 0;
 
             //- Write character
-            virtual Ostream& write(const char) = 0;
+            virtual Ostream& write(const char c) = 0;
 
             //- Write character string
-            virtual Ostream& write(const char*) = 0;
+            virtual Ostream& write(const char* str) = 0;
 
             //- Write word
-            virtual Ostream& write(const word&) = 0;
+            virtual Ostream& write(const word& str) = 0;
 
             //- Write keyType
             //  A plain word is written unquoted.
             //  A regular expression is written as a quoted string.
-            virtual Ostream& write(const keyType&);
+            virtual Ostream& write(const keyType& kw);
 
             //- Write string
-            virtual Ostream& write(const string&) = 0;
+            virtual Ostream& write(const string& str) = 0;
 
             //- Write std::string surrounded by quotes.
             //  Optional write without quotes.
             virtual Ostream& writeQuoted
             (
-                const std::string&,
+                const std::string& str,
                 const bool quoted=true
             ) = 0;
 
             //- Write int32_t
-            virtual Ostream& write(const int32_t) = 0;
+            virtual Ostream& write(const int32_t val) = 0;
 
             //- Write int64_t
-            virtual Ostream& write(const int64_t) = 0;
+            virtual Ostream& write(const int64_t val) = 0;
 
             //- Write floatScalar
-            virtual Ostream& write(const floatScalar) = 0;
+            virtual Ostream& write(const floatScalar val) = 0;
 
             //- Write doubleScalar
-            virtual Ostream& write(const doubleScalar) = 0;
+            virtual Ostream& write(const doubleScalar val) = 0;
 
-            //- Write binary block
-            virtual Ostream& write(const char*, std::streamsize) = 0;
+            //- Write binary block.
+            virtual Ostream& write(const char* data, std::streamsize count) = 0;
+
+            //- Emit begin marker for low-level raw binary output.
+            //  The count should indicate the number of bytes for subsequent
+            //  writeRaw calls.
+            virtual Ostream& beginRaw(std::streamsize count) = 0;
+
+            //- Low-level raw binary output.
+            virtual Ostream& writeRaw
+            (
+                const char* data,
+                std::streamsize count
+            ) = 0;
+
+            //- Emit end marker for low-level raw binary output.
+            virtual Ostream& endRaw() = 0;
 
             //- Add indentation characters
             virtual void indent() = 0;
@@ -164,11 +179,11 @@ public:
             void decrIndent();
 
             //- Write the keyword followed by an appropriate indentation
-            virtual Ostream& writeKeyword(const keyType&);
+            virtual Ostream& writeKeyword(const keyType& kw);
 
             //- Write begin block group with the given name
             //  Increments indentation, adds newline.
-            virtual Ostream& beginBlock(const keyType&);
+            virtual Ostream& beginBlock(const keyType& keyword);
 
             //- Write begin block group without a name
             //  Increments indentation, adds newline.
diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.C b/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.C
index 6667389b74..eb54b5ea61 100644
--- a/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.C
+++ b/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2017 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2016-2017 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,15 +26,42 @@ License
 #include "UOPstream.H"
 #include "int.H"
 #include "token.H"
-
 #include <cctype>
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
+inline void Foam::UOPstream::prepareBuffer
+(
+    const size_t count,
+    const size_t align
+)
+{
+    if (!count)
+    {
+        return;
+    }
+
+    // The current output position
+    label pos = sendBuf_.size();
+
+    if (align > 1)
+    {
+        // Align output position. Pads sendBuf_.size() - oldPos characters.
+        pos = align + ((pos - 1) & ~(align - 1));
+    }
+
+    // Extend buffer (as required)
+    sendBuf_.reserve(max(1000, label(pos + count)));
+
+    // Move to the aligned output position
+    sendBuf_.setSize(pos);
+}
+
+
 template<class T>
-inline void Foam::UOPstream::writeToBuffer(const T& t)
+inline void Foam::UOPstream::writeToBuffer(const T& val)
 {
-    writeToBuffer(&t, sizeof(T), sizeof(T));
+    writeToBuffer(&val, sizeof(T), sizeof(T));
 }
 
 
@@ -55,25 +82,26 @@ inline void Foam::UOPstream::writeToBuffer
     const size_t align
 )
 {
-    if (!sendBuf_.capacity())
+    if (!count)
     {
-        sendBuf_.setCapacity(1000);
+        return;
     }
 
-    label alignedPos = sendBuf_.size();
+    prepareBuffer(count, align);
 
-    if (align > 1)
-    {
-        // Align bufPosition. Pads sendBuf_.size() - oldPos characters.
-        alignedPos = align + ((sendBuf_.size() - 1) & ~(align - 1));
-    }
+    // The aligned output position
+    const label pos = sendBuf_.size();
+
+    // Extend the addressable range for direct pointer access
+    sendBuf_.setSize(pos + count);
 
-    // Extend if necessary
-    sendBuf_.setSize(alignedPos + count);
+    char* const __restrict__ buf = (sendBuf_.begin() + pos);
+    const char* const __restrict__ input = reinterpret_cast<const char*>(data);
 
-    const char* dataPtr = reinterpret_cast<const char*>(data);
-    size_t i = count;
-    while (i--) sendBuf_[alignedPos++] = *dataPtr++;
+    for (size_t i = 0; i < count; ++i)
+    {
+        buf[i] = input[i];
+    }
 }
 
 
@@ -135,7 +163,7 @@ Foam::UOPstream::~UOPstream()
     {
         if
         (
-           !UOPstream::write
+            !UOPstream::write
             (
                 commsType_,
                 toProcNo_,
@@ -298,6 +326,41 @@ Foam::Ostream& Foam::UOPstream::write
 }
 
 
+Foam::Ostream& Foam::UOPstream::beginRaw
+(
+    const std::streamsize count
+)
+{
+    if (format() != BINARY)
+    {
+        FatalErrorInFunction
+            << "stream format not binary"
+            << Foam::abort(FatalError);
+    }
+
+    // Alignment = 8, as per write(const char*, streamsize)
+    prepareBuffer(count, 8);
+
+    return *this;
+}
+
+
+Foam::Ostream& Foam::UOPstream::writeRaw
+(
+    const char* data,
+    const std::streamsize count
+)
+{
+    // No check for format() == BINARY since this is either done in the
+    // beginRaw() method, or the caller knows what they are doing.
+
+    // Previously aligned and sizes reserved via beginRaw()
+    writeToBuffer(data, count, 1);
+
+    return *this;
+}
+
+
 void Foam::UOPstream::print(Ostream& os) const
 {
     os  << "Writing from processor " << toProcNo_
diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.H b/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.H
index 6ee40e0378..364b10ccdd 100644
--- a/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.H
+++ b/src/OpenFOAM/db/IOstreams/Pstreams/UOPstream.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2014 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2017 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -72,9 +72,12 @@ class UOPstream
 
     // Private Member Functions
 
-        //- Write a T to the transfer buffer
+        //- Prepare buffer for count bytes of output at specified alignment.
+        inline void prepareBuffer(const size_t count, const size_t align);
+
+        //- Write data to the transfer buffer
         template<class T>
-        inline void writeToBuffer(const T& t);
+        inline void writeToBuffer(const T& val);
 
         //- Write a char to the transfer buffer
         inline void writeToBuffer(const char& c);
@@ -182,6 +185,24 @@ public:
             //- Write binary block with 8-byte alignment.
             Ostream& write(const char* data, const std::streamsize count);
 
+            //- Begin marker for low-level raw binary output.
+            //  The count should indicate the number of bytes for subsequent
+            //  writeRaw calls.
+            Ostream& beginRaw(const std::streamsize count);
+
+            //- Low-level raw binary output.
+            Ostream& writeRaw
+            (
+                const char* data,
+                const std::streamsize count
+            );
+
+            //- End marker for low-level raw binary output.
+            Ostream& endRaw()
+            {
+                return *this;
+            }
+
             //- Add indentation characters
             void indent()
             {}
diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.C b/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.C
index 5dc8a3e6bd..5c6f14103e 100644
--- a/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.C
+++ b/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.C
@@ -210,7 +210,24 @@ Foam::Ostream& Foam::OSstream::write(const doubleScalar val)
 }
 
 
-Foam::Ostream& Foam::OSstream::write(const char* buf, std::streamsize count)
+Foam::Ostream& Foam::OSstream::write
+(
+    const char* data,
+    const std::streamsize count
+)
+{
+    beginRaw(count);
+    writeRaw(data, count);
+    endRaw();
+
+    return *this;
+}
+
+
+Foam::Ostream& Foam::OSstream::beginRaw
+(
+    const std::streamsize count
+)
 {
     if (format() != BINARY)
     {
@@ -220,8 +237,6 @@ Foam::Ostream& Foam::OSstream::write(const char* buf, std::streamsize count)
     }
 
     os_ << token::BEGIN_LIST;
-    os_.write(buf, count);
-    os_ << token::END_LIST;
 
     setState(os_.rdstate());
 
@@ -229,9 +244,34 @@ Foam::Ostream& Foam::OSstream::write(const char* buf, std::streamsize count)
 }
 
 
+Foam::Ostream& Foam::OSstream::writeRaw
+(
+    const char* data,
+    std::streamsize count
+)
+{
+    // No check for format() == BINARY since this is either done in the
+    // beginRaw() method, or the caller knows what they are doing.
+
+    os_.write(data, count);
+    setState(os_.rdstate());
+
+    return *this;
+}
+
+
+Foam::Ostream& Foam::OSstream::endRaw()
+{
+    os_ << token::END_LIST;
+    setState(os_.rdstate());
+
+    return *this;
+}
+
+
 void Foam::OSstream::indent()
 {
-    for (unsigned short i = 0; i < indentLevel_*indentSize_; i++)
+    for (unsigned short i = 0; i < indentLevel_*indentSize_; ++i)
     {
         os_ << ' ';
     }
diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.H b/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.H
index 4c6e97fd59..158f7123ec 100644
--- a/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.H
+++ b/src/OpenFOAM/db/IOstreams/Sstreams/OSstream.H
@@ -143,7 +143,26 @@ public:
             virtual Ostream& write(const doubleScalar val);
 
             //- Write binary block
-            virtual Ostream& write(const char* buf, std::streamsize count);
+            virtual Ostream& write
+            (
+                const char* data,
+                const std::streamsize count
+            );
+
+            //- Begin marker for low-level raw binary output.
+            //  The count should indicate the number of bytes for subsequent
+            //  writeRaw calls.
+            virtual Ostream& beginRaw(const std::streamsize count);
+
+            //- Low-level raw binary output.
+            virtual Ostream& writeRaw
+            (
+                const char* data,
+                const std::streamsize count
+            );
+
+            //- End marker for low-level raw binary output.
+            virtual Ostream& endRaw();
 
             //- Add indentation characters
             virtual void indent();
-- 
GitLab