SerialQueue: add const version of the iterators.

Unfortunately you can't template on const-ness in C++ so we have to
duplicate all the iterator code for SerialQueue (that, or introduce very
heavy templating).
diff --git a/src/backend/common/SerialQueue.h b/src/backend/common/SerialQueue.h
index 23f6634..09dcbfc 100644
--- a/src/backend/common/SerialQueue.h
+++ b/src/backend/common/SerialQueue.h
@@ -27,7 +27,8 @@
         private:
             using SerialPair = std::pair<Serial, std::vector<T>>;
             using Storage = std::vector<SerialPair>;
-            using StorageIterator = typename Storage::const_iterator;
+            using StorageIterator = typename Storage::iterator;
+            using ConstStorageIterator = typename Storage::const_iterator;
 
         public:
             class Iterator {
@@ -37,13 +38,27 @@
 
                     bool operator==(const Iterator& other) const;
                     bool operator!=(const Iterator& other) const;
-                    const T& operator*() const;
+                    T& operator*() const;
 
                 private:
                     StorageIterator storageIterator;
                     // Special case the serialIterator when it should be equal to storageIterator.begin()
                     // otherwise we could ask storageIterator.begin() when storageIterator is storage.end()
                     // which is invalid. storageIterator.begin() is tagged with a nullptr.
+                    T* serialIterator;
+            };
+
+            class ConstIterator {
+                public:
+                    ConstIterator(ConstStorageIterator start);
+                    ConstIterator& operator++();
+
+                    bool operator==(const ConstIterator& other) const;
+                    bool operator!=(const ConstIterator& other) const;
+                    const T& operator*() const;
+
+                private:
+                    ConstStorageIterator storageIterator;
                     const T* serialIterator;
             };
 
@@ -59,6 +74,18 @@
                     StorageIterator endIt;
             };
 
+            class ConstBeginEnd {
+                public:
+                    ConstBeginEnd(ConstStorageIterator start, ConstStorageIterator end);
+
+                    ConstIterator begin() const;
+                    ConstIterator end() const;
+
+                private:
+                    ConstStorageIterator startIt;
+                    ConstStorageIterator endIt;
+            };
+
             // The serial must be given in (not strictly) increasing order.
             void Enqueue(const T& value, Serial serial);
             void Enqueue(T&& value, Serial serial);
@@ -70,8 +97,10 @@
             // The UpTo variants of Iterate and Clear affect all values associated to a serial
             // that is smaller OR EQUAL to the given serial. Iterating is done like so:
             //     for (const T& value : queue.IterateAll()) { stuff(T); }
-            BeginEnd IterateAll() const;
-            BeginEnd IterateUpTo(Serial serial) const;
+            ConstBeginEnd IterateAll() const;
+            ConstBeginEnd IterateUpTo(Serial serial) const;
+            BeginEnd IterateAll();
+            BeginEnd IterateUpTo(Serial serial);
 
             void Clear();
             void ClearUpTo(Serial serial);
@@ -80,7 +109,8 @@
 
         private:
             // Returns the first StorageIterator that a serial bigger than serial.
-            StorageIterator FindUpTo(Serial serial) const;
+            ConstStorageIterator FindUpTo(Serial serial) const;
+            StorageIterator FindUpTo(Serial serial);
             Storage storage;
     };
 
@@ -93,7 +123,7 @@
         if (Empty() || storage.back().first < serial) {
             storage.emplace_back(SerialPair(serial, {}));
         }
-        storage.back().second.push_back(value);
+        storage.back().second.emplace_back(value);
     }
 
     template<typename T>
@@ -103,7 +133,7 @@
         if (Empty() || storage.back().first < serial) {
             storage.emplace_back(SerialPair(serial, {}));
         }
-        storage.back().second.push_back(value);
+        storage.back().second.emplace_back(value);
     }
 
     template<typename T>
@@ -126,12 +156,22 @@
     }
 
     template<typename T>
-    typename SerialQueue<T>::BeginEnd SerialQueue<T>::IterateAll() const {
+    typename SerialQueue<T>::ConstBeginEnd SerialQueue<T>::IterateAll() const {
         return {storage.begin(), storage.end()};
     }
 
     template<typename T>
-    typename SerialQueue<T>::BeginEnd SerialQueue<T>::IterateUpTo(Serial serial) const {
+    typename SerialQueue<T>::ConstBeginEnd SerialQueue<T>::IterateUpTo(Serial serial) const {
+        return {storage.begin(), FindUpTo(serial)};
+    }
+
+    template<typename T>
+    typename SerialQueue<T>::BeginEnd SerialQueue<T>::IterateAll() {
+        return {storage.begin(), storage.end()};
+    }
+
+    template<typename T>
+    typename SerialQueue<T>::BeginEnd SerialQueue<T>::IterateUpTo(Serial serial) {
         return {storage.begin(), FindUpTo(serial)};
     }
 
@@ -152,7 +192,16 @@
     }
 
     template<typename T>
-    typename SerialQueue<T>::StorageIterator SerialQueue<T>::FindUpTo(Serial serial) const {
+    typename SerialQueue<T>::ConstStorageIterator SerialQueue<T>::FindUpTo(Serial serial) const {
+        auto it = storage.begin();
+        while (it != storage.end() && it->first <= serial) {
+            it ++;
+        }
+        return it;
+    }
+
+    template<typename T>
+    typename SerialQueue<T>::StorageIterator SerialQueue<T>::FindUpTo(Serial serial) {
         auto it = storage.begin();
         while (it != storage.end() && it->first <= serial) {
             it ++;
@@ -186,7 +235,7 @@
 
     template<typename T>
     typename SerialQueue<T>::Iterator& SerialQueue<T>::Iterator::operator++() {
-        const T* vectorData = storageIterator->second.data();
+        T* vectorData = storageIterator->second.data();
 
         if (serialIterator == nullptr) {
             serialIterator = vectorData + 1;
@@ -213,13 +262,72 @@
     }
 
     template<typename T>
-    const T& SerialQueue<T>::Iterator::operator*() const {
+    T& SerialQueue<T>::Iterator::operator*() const {
         if (serialIterator == nullptr) {
             return *storageIterator->second.begin();
         }
         return *serialIterator;
     }
 
+    // SerialQueue::ConstBeginEnd
+
+    template<typename T>
+    SerialQueue<T>::ConstBeginEnd::ConstBeginEnd(typename SerialQueue<T>::ConstStorageIterator start, typename SerialQueue<T>::ConstStorageIterator end)
+        : startIt(start), endIt(end) {
+    }
+
+    template<typename T>
+    typename SerialQueue<T>::ConstIterator SerialQueue<T>::ConstBeginEnd::begin() const {
+        return {startIt};
+    }
+
+    template<typename T>
+    typename SerialQueue<T>::ConstIterator SerialQueue<T>::ConstBeginEnd::end() const {
+        return {endIt};
+    }
+
+    // SerialQueue::ConstIterator
+
+    template<typename T>
+    SerialQueue<T>::ConstIterator::ConstIterator(typename SerialQueue<T>::ConstStorageIterator start)
+        : storageIterator(start), serialIterator(nullptr) {
+    }
+
+    template<typename T>
+    typename SerialQueue<T>::ConstIterator& SerialQueue<T>::ConstIterator::operator++() {
+        const T* vectorData = storageIterator->second.data();
+
+        if (serialIterator == nullptr) {
+            serialIterator = vectorData + 1;
+        } else {
+            serialIterator ++;
+        }
+
+        if (serialIterator >= vectorData + storageIterator->second.size()) {
+            serialIterator = nullptr;
+            storageIterator ++;
+        }
+
+        return *this;
+    }
+
+    template<typename T>
+    bool SerialQueue<T>::ConstIterator::operator==(const typename SerialQueue<T>::ConstIterator& other) const {
+        return other.storageIterator == storageIterator && other.serialIterator == serialIterator;
+    }
+
+    template<typename T>
+    bool SerialQueue<T>::ConstIterator::operator!=(const typename SerialQueue<T>::ConstIterator& other) const {
+        return !(*this == other);
+    }
+
+    template<typename T>
+    const T& SerialQueue<T>::ConstIterator::operator*() const {
+        if (serialIterator == nullptr) {
+            return *storageIterator->second.begin();
+        }
+        return *serialIterator;
+    }
 }
 
 #endif // BACKEND_COMMON_SERIALQUEUE_H_