// Copyright 2017 The NXT Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef BACKEND_COMMON_SERIALQUEUE_H_ #define BACKEND_COMMON_SERIALQUEUE_H_ #include "Forward.h" #include #include namespace backend { template class SerialQueue { private: using SerialPair = std::pair>; using Storage = std::vector; using StorageIterator = typename Storage::const_iterator; public: class Iterator { public: Iterator(StorageIterator start); Iterator& operator++(); bool operator==(const Iterator& other) const; bool operator!=(const Iterator& other) const; 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. const T* serialIterator; }; class BeginEnd { public: BeginEnd(StorageIterator start, StorageIterator end); Iterator begin() const; Iterator end() const; private: StorageIterator startIt; StorageIterator endIt; }; // The serial must be given in (not strictly) increasing order. void Enqueue(const T& value, Serial serial); void Enqueue(T&& value, Serial serial); void Enqueue(const std::vector& values, Serial serial); void Enqueue(std::vector&& values, Serial serial); bool Empty() const; // 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; void Clear(); void ClearUpTo(Serial serial); Serial FirstSerial() const; private: // Returns the first StorageIterator that a serial bigger than serial. StorageIterator FindUpTo(Serial serial) const; Storage storage; }; // SerialQueue template void SerialQueue::Enqueue(const T& value, Serial serial) { ASSERT(Empty() || storage.back().first <= serial); if (Empty() || storage.back().first < serial) { storage.emplace_back(SerialPair(serial, {})); } storage.back().second.push_back(value); } template void SerialQueue::Enqueue(T&& value, Serial serial) { ASSERT(Empty() || storage.back().first <= serial); if (Empty() || storage.back().first < serial) { storage.emplace_back(SerialPair(serial, {})); } storage.back().second.push_back(value); } template void SerialQueue::Enqueue(const std::vector& values, Serial serial) { ASSERT(values.size() > 0); ASSERT(Empty() || storage.back().first <= serial); storage.emplace_back(SerialPair(serial, {values})); } template void SerialQueue::Enqueue(std::vector&& values, Serial serial) { ASSERT(values.size() > 0); ASSERT(Empty() || storage.back().first <= serial); storage.emplace_back(SerialPair(serial, {values})); } template bool SerialQueue::Empty() const { return storage.empty(); } template typename SerialQueue::BeginEnd SerialQueue::IterateAll() const { return {storage.begin(), storage.end()}; } template typename SerialQueue::BeginEnd SerialQueue::IterateUpTo(Serial serial) const { return {storage.begin(), FindUpTo(serial)}; } template void SerialQueue::Clear() { storage.clear(); } template void SerialQueue::ClearUpTo(Serial serial) { storage.erase(storage.begin(), FindUpTo(serial)); } template Serial SerialQueue::FirstSerial() const { ASSERT(!Empty()); return storage.front().first; } template typename SerialQueue::StorageIterator SerialQueue::FindUpTo(Serial serial) const { auto it = storage.begin(); while (it != storage.end() && it->first <= serial) { it ++; } return it; } // SerialQueue::BeginEnd template SerialQueue::BeginEnd::BeginEnd(typename SerialQueue::StorageIterator start, typename SerialQueue::StorageIterator end) : startIt(start), endIt(end) { } template typename SerialQueue::Iterator SerialQueue::BeginEnd::begin() const { return {startIt}; } template typename SerialQueue::Iterator SerialQueue::BeginEnd::end() const { return {endIt}; } // SerialQueue::Iterator template SerialQueue::Iterator::Iterator(typename SerialQueue::StorageIterator start) : storageIterator(start), serialIterator(nullptr) { } template typename SerialQueue::Iterator& SerialQueue::Iterator::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 bool SerialQueue::Iterator::operator==(const typename SerialQueue::Iterator& other) const { return other.storageIterator == storageIterator && other.serialIterator == serialIterator; } template bool SerialQueue::Iterator::operator!=(const typename SerialQueue::Iterator& other) const { return !(*this == other); } template const T& SerialQueue::Iterator::operator*() const { if (serialIterator == nullptr) { return *storageIterator->second.begin(); } return *serialIterator; } } #endif // BACKEND_COMMON_SERIALQUEUE_H_