From 775d0b277ba52f3b18de57cb29e4b0eac7d2e2f7 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Wed, 27 Sep 2023 19:35:55 +0200
Subject: [PATCH] ENH: improve OpenFOAM I/O for std::vector container

- input: added support (similar to DynamicList)
- output: redirect through UList output, which enables binary etc

- these changes enable support for broadcast and other parallel IO
  for std::vector

ENH: support SubList of std::vector (entire length)

- allows a 'glue' layer for re-casting std::vector to UList etc
---
 .../Test-parallel-broadcast.C                 |  26 ++-
 src/OpenFOAM/containers/Lists/List/SubList.H  |   3 +
 src/OpenFOAM/containers/Lists/List/SubListI.H |  10 +
 src/OpenFOAM/containers/Lists/List/UList.H    |   6 +-
 .../containers/Lists/List/stdVectorIO.C       | 171 ++++++++++++++++--
 5 files changed, 202 insertions(+), 14 deletions(-)

diff --git a/applications/test/parallel-broadcast/Test-parallel-broadcast.C b/applications/test/parallel-broadcast/Test-parallel-broadcast.C
index 38a68382e35..02b68242d93 100644
--- a/applications/test/parallel-broadcast/Test-parallel-broadcast.C
+++ b/applications/test/parallel-broadcast/Test-parallel-broadcast.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2022 OpenCFD Ltd.
+    Copyright (C) 2022-2023 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -75,6 +75,16 @@ void testBroadcast(List<T>& values)
 }
 
 
+template<class T>
+void testBroadcast(std::vector<T>& values)
+{
+    Info<< nl << "is_contiguous:" << is_contiguous<T>::value << endl;
+    Pout<< "pre-broadcast: " << flatOutput(values) << endl;
+    Pstream::broadcast(values);
+    Pout<< "post-broadcast: " << flatOutput(values) << endl;
+}
+
+
 void testBroadcast(bitSet& values)
 {
     Pout<< "pre-broadcast: "
@@ -135,6 +145,20 @@ int main(int argc, char *argv[])
         testBroadcast(values);
     }
 
+    {
+        std::vector<word> values;
+        if (Pstream::master())
+        {
+            values.resize(UPstream::nProcs());
+
+            for (decltype(values.size()) i=0; i < values.size(); ++i)
+            {
+                values[i] = "vector_" + Foam::name(i);
+            }
+        }
+        testBroadcast(values);
+    }
+
     {
         vector values(vector::uniform(-1));
         if (Pstream::master())
diff --git a/src/OpenFOAM/containers/Lists/List/SubList.H b/src/OpenFOAM/containers/Lists/List/SubList.H
index f1c6aa2b38d..886068bf073 100644
--- a/src/OpenFOAM/containers/Lists/List/SubList.H
+++ b/src/OpenFOAM/containers/Lists/List/SubList.H
@@ -91,6 +91,9 @@ public:
         //- Construct from UList, the entire size
         inline explicit SubList(const UList<T>& list) noexcept;
 
+        //- Construct from std::vector, the entire size
+        inline explicit SubList(const std::vector<T>& list) noexcept;
+
         //- Construct from FixedList, the entire size
         template<unsigned N>
         inline explicit SubList(const FixedList<T, N>& list);
diff --git a/src/OpenFOAM/containers/Lists/List/SubListI.H b/src/OpenFOAM/containers/Lists/List/SubListI.H
index 389e9e85f8f..f9057480d29 100644
--- a/src/OpenFOAM/containers/Lists/List/SubListI.H
+++ b/src/OpenFOAM/containers/Lists/List/SubListI.H
@@ -49,6 +49,16 @@ inline Foam::SubList<T>::SubList
 {}
 
 
+template<class T>
+inline Foam::SubList<T>::SubList
+(
+    const std::vector<T>& list
+) noexcept
+:
+    UList<T>(const_cast<T*>(list.data()), label(list.size()))
+{}
+
+
 template<class T>
 template<unsigned N>
 inline Foam::SubList<T>::SubList
diff --git a/src/OpenFOAM/containers/Lists/List/UList.H b/src/OpenFOAM/containers/Lists/List/UList.H
index 1d70c027032..169e50f35be 100644
--- a/src/OpenFOAM/containers/Lists/List/UList.H
+++ b/src/OpenFOAM/containers/Lists/List/UList.H
@@ -676,7 +676,11 @@ Ostream& operator<<(Ostream& os, const UList<T>& list)
     return list.writeList(os, Detail::ListPolicy::short_length<T>::value);
 }
 
-//- Write std::vector to Ostream. ASCII only, no line-breaks
+//- Read std::vector contents from Istream
+template<class T>
+Istream& operator>>(Istream& is, std::vector<T>& list);
+
+//- Write std::vector to Ostream (via UList)
 template<class T>
 Ostream& operator<<(Ostream& os, const std::vector<T>& list);
 
diff --git a/src/OpenFOAM/containers/Lists/List/stdVectorIO.C b/src/OpenFOAM/containers/Lists/List/stdVectorIO.C
index d3359cd8642..e286a14edfa 100644
--- a/src/OpenFOAM/containers/Lists/List/stdVectorIO.C
+++ b/src/OpenFOAM/containers/Lists/List/stdVectorIO.C
@@ -26,36 +26,183 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "UList.H"
+#include "Istream.H"
 #include "Ostream.H"
+#include "contiguous.H"
 #include "token.H"
 #include <vector>
 
 // * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
 
 template<class T>
-Foam::Ostream& Foam::operator<<(Ostream& os, const std::vector<T>& list)
+Foam::Istream& Foam::operator>>(Istream& is, std::vector<T>& list)
 {
-    auto iter = list.cbegin();
-    const auto last = list.cend();
+    is.fatalCheck(FUNCTION_NAME);
+
+    token tok(is);
+
+    is.fatalCheck("Istream >> std::vector<T> : reading first token");
+
+    if (tok.isCompound())
+    {
+        // No compound handling ...
+
+        list.clear();  // Clear old contents
+        FatalIOErrorInFunction(is)
+            << "Support for compoundToken - not implemented" << nl
+            << exit(FatalIOError);
+    }
+    else if (tok.isLabel())
+    {
+        // Label: could be int(..), int{...} or just a plain '0'
+
+        const label len = tok.labelToken();
+
+        // Resize to length required
+        list.resize(len);
+
+        if (is.format() == IOstreamOption::BINARY && is_contiguous<T>::value)
+        {
+            // Binary and contiguous
+
+            if (len)
+            {
+                Detail::readContiguous<T>
+                (
+                    is,
+                    reinterpret_cast<char*>(list.data()),   // data_bytes()
+                    std::streamsize(list.size())*sizeof(T)  // size_bytes()
+                );
+
+                is.fatalCheck
+                (
+                    "Istream >> std::vector<T> : "
+                    "reading binary block"
+                );
+            }
+        }
+        else if (std::is_same<char, T>::value)
+        {
+            // Special treatment for char data (binary I/O only)
+            const auto oldFmt = is.format(IOstreamOption::BINARY);
+
+            if (len)
+            {
+                // read(...) includes surrounding start/end delimiters
+                is.read
+                (
+                    reinterpret_cast<char*>(list.data()),   // data_bytes()
+                    std::streamsize(list.size())*sizeof(T)  // size_bytes()
+                );
+
+                is.fatalCheck
+                (
+                    "Istream >> std::vector<char> : "
+                    "reading binary block"
+                );
+            }
+
+            is.format(oldFmt);
+        }
+        else
+        {
+            // Begin of contents marker
+            const char delimiter = is.readBeginList("List");
 
-    // Write ascii list contents, no line breaks
+            if (len)
+            {
+                if (delimiter == token::BEGIN_LIST)
+                {
+                    auto iter = list.begin();
+                    const auto last = list.end();
 
-    os << label(list.size()) << token::BEGIN_LIST;
+                    // Contents
+                    for (/*nil*/; (iter != last); (void)++iter)
+                    {
+                        is >> *iter;
 
-    // Contents
-    if (iter != last)
+                        is.fatalCheck
+                        (
+                            "Istream >> std::vector<char> : "
+                            "reading entry"
+                        );
+                    }
+                }
+                else
+                {
+                    // Uniform content (delimiter == token::BEGIN_BLOCK)
+
+                    T elem;
+                    is >> elem;
+
+                    is.fatalCheck
+                    (
+                        "Istream >> std::vector<char> : "
+                        "reading the single entry"
+                    );
+
+                    // Fill with the value
+                    list.assign(list.size(), elem);
+                }
+            }
+
+            // End of contents marker
+            is.readEndList("List");
+        }
+    }
+    else if (tok.isPunctuation(token::BEGIN_LIST))
     {
-        os << *iter;
+        // "(...)" : read as bracketed list
 
-        for (++iter; (iter != last); (void)++iter)
+        // Slightly sub-optimal since it has intermediate resizing,
+        // however don't expect this as input very often.
+
+        list.clear();  // Clear addressing, leave storage intact (probably)
+
+        is >> tok;
+        is.fatalCheck(FUNCTION_NAME);
+
+        while (!tok.isPunctuation(token::END_LIST))
         {
-            os << token::SPACE << *iter;
+            is.putBack(tok);
+
+            // C++17
+            // is >> list.emplace_back();
+
+            // C++11
+            list.emplace_back();
+            is >> list.back();
+
+            is.fatalCheck
+            (
+                "Istream >> std::vector<char> : "
+                "reading entry"
+            );
+
+            is >> tok;
+            is.fatalCheck(FUNCTION_NAME);
         }
     }
+    else
+    {
+        list.clear();  // Clear old contents
+
+        FatalIOErrorInFunction(is)
+            << "incorrect first token, expected <int> or '(', found "
+            << tok.info() << nl
+            << exit(FatalIOError);
+    }
+
+    return is;
+}
 
-    os << token::END_LIST;
 
-    os.check(FUNCTION_NAME);
+template<class T>
+Foam::Ostream& Foam::operator<<(Ostream& os, const std::vector<T>& list)
+{
+    // Use UList for output
+    UList<T> proxy(const_cast<T*>(list.data()), label(list.size()));
+    os  << proxy;
     return os;
 }
 
-- 
GitLab