From c4aab57c622bcb222be5ca72dcbec90764501384 Mon Sep 17 00:00:00 2001
From: Stephen Henry <stephenry@gmail.com>
Date: Thu, 28 May 2020 22:59:18 +0100
Subject: [PATCH] Internals: Refactor to introduce VerilatedFdList. (#2363)

---
 include/verilated.cpp   | 14 ++------
 include/verilated_imp.h | 77 +++++++++++++++++++++++++++++------------
 2 files changed, 57 insertions(+), 34 deletions(-)

diff --git a/include/verilated.cpp b/include/verilated.cpp
index 5204b3665..d99918312 100644
--- a/include/verilated.cpp
+++ b/include/verilated.cpp
@@ -1190,11 +1190,8 @@ done:
 // File I/O
 
 FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
-    // Expected non-MCD case; returns ONLY the first file descriptor seen in lhs (which
-    // in the MCD case can result in descriptors being ignored).
-    FILE* fp[1] = {NULL};
-    VerilatedImp::fdToFp(lhs, fp, 1);
-    return fp[0];
+    // Expected non-MCD case; returns null on MCD descriptors.
+    return VerilatedImp::fdToFp(lhs);
 }
 
 void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE {
@@ -1378,12 +1375,7 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
     _vl_vsformat(output, formatp, ap);
     va_end(ap);
 
-    FILE* fp[30];
-    const std::size_t n = VerilatedImp::fdToFp(fpi, fp, 30);
-    for (std::size_t i = 0; i < n; ++i) {
-        if (VL_UNLIKELY(!fp[i])) continue;
-        fwrite(output.c_str(), 1, output.size(), fp[i]);
-    }
+    VerilatedImp::fdWrite(fpi, output);
 }
 
 IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
diff --git a/include/verilated_imp.h b/include/verilated_imp.h
index b337aee1c..724d10433 100644
--- a/include/verilated_imp.h
+++ b/include/verilated_imp.h
@@ -169,6 +169,24 @@ public:
 };
 #endif  // VL_THREADED
 
+// FILE* list constructed from a file-descriptor
+class VerilatedFpList {
+    FILE* m_fp[31];
+    std::size_t m_sz;
+
+public:
+    typedef FILE* const* const_iterator;
+    explicit VerilatedFpList()
+        : m_sz(0) {}
+    const_iterator begin() const { return m_fp; }
+    const_iterator end() const { return m_fp + m_sz; }
+    std::size_t size() const { return m_sz; }
+    std::size_t capacity() const { return 31; }
+    void push_back(FILE* fd) {
+        if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
+    }
+};
+
 //======================================================================
 // VerilatedImp
 
@@ -484,24 +502,32 @@ public:  // But only for verilated*.cpp
         return (idx | (1UL << 31));  // bit 31 indicates not MCD
     }
     static void fdFlush(IData fdi) VL_MT_SAFE {
-        FILE* fp[30];
-        const int n = fdToFp(fdi, fp, 30);
         VerilatedLockGuard lock(s_s.m_fdMutex);
-        for (int i = 0; i < n; i++) fflush(fp[i]);
+        const VerilatedFpList fdlist = fdToFpList(fdi);
+        for (VerilatedFpList::const_iterator it = fdlist.begin(); it != fdlist.end(); ++it) {
+            fflush(*it);
+        }
     }
     static IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE {
-        FILE* fp;
-        const int n = fdToFp(fdi, &fp);
         VerilatedLockGuard lock(s_s.m_fdMutex);
-        if (VL_UNLIKELY(!fp || (n != 1))) return 0;
-        return static_cast<IData>(fseek(fp, static_cast<long>(offset), static_cast<int>(origin)));
+        const VerilatedFpList fdlist = fdToFpList(fdi);
+        if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
+        return static_cast<IData>(
+            fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
     }
     static IData fdTell(IData fdi) VL_MT_SAFE {
-        FILE* fp;
-        const int n = fdToFp(fdi, &fp);
         VerilatedLockGuard lock(s_s.m_fdMutex);
-        if (VL_UNLIKELY(!fp || (n != 1))) return 0;
-        return static_cast<IData>(ftell(fp));
+        const VerilatedFpList fdlist = fdToFpList(fdi);
+        if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
+        return static_cast<IData>(ftell(*fdlist.begin()));
+    }
+    static void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE {
+        VerilatedLockGuard lock(s_s.m_fdMutex);
+        const VerilatedFpList fdlist = fdToFpList(fdi);
+        for (VerilatedFpList::const_iterator it = fdlist.begin(); it != fdlist.end(); ++it) {
+            if (VL_UNLIKELY(!*it)) continue;
+            fwrite(output.c_str(), 1, output.size(), *it);
+        }
     }
     static void fdClose(IData fdi) VL_MT_SAFE {
         VerilatedLockGuard lock(s_s.m_fdMutex);
@@ -524,29 +550,34 @@ public:  // But only for verilated*.cpp
             }
         }
     }
-    static inline int fdToFp(IData fdi, FILE** fp, std::size_t max = 1) VL_MT_SAFE {
-        if (VL_UNLIKELY(!fp || (max == 0))) return 0;
+    static inline FILE* fdToFp(IData fdi) VL_MT_SAFE {
         VerilatedLockGuard lock(s_s.m_fdMutex);
-        int out = 0;
+        const VerilatedFpList fdlist = fdToFpList(fdi);
+        if (VL_UNLIKELY(fdlist.size() != 1)) return NULL;
+        return *fdlist.begin();
+    }
+
+private:
+    static inline VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(s_s.m_fdMutex) {
+        VerilatedFpList fp;
         if ((fdi & (1 << 31)) != 0) {
             // Non-MCD case
-            IData idx = fdi & VL_MASK_I(31);
+            const IData idx = fdi & VL_MASK_I(31);
             switch (idx) {
-            case 0: fp[out++] = stdin; break;
-            case 1: fp[out++] = stdout; break;
-            case 2: fp[out++] = stderr; break;
+            case 0: fp.push_back(stdin); break;
+            case 1: fp.push_back(stdout); break;
+            case 2: fp.push_back(stderr); break;
             default:
-                if (VL_LIKELY(idx < s_s.m_fdps.size())) fp[out++] = s_s.m_fdps[idx];
+                if (VL_LIKELY(idx < s_s.m_fdps.size())) fp.push_back(s_s.m_fdps[idx]);
                 break;
             }
         } else {
             // MCD Case
-            for (int i = 0; (fdi != 0) && (out < static_cast<int>(max)) && (i < 31);
-                 ++i, fdi >>= 1) {
-                if (fdi & VL_MASK_I(1)) fp[out++] = s_s.m_fdps[i];
+            for (int i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
+                if (fdi & VL_MASK_I(1)) fp.push_back(s_s.m_fdps[i]);
             }
         }
-        return out;
+        return fp;
     }
 };