/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace AK { template class Queue { public: Queue() = default; ~Queue() { clear(); } size_t size() const { return m_size; } bool is_empty() const { return m_size == 0; } template void enqueue(U&& value) { if (m_segments.is_empty() || m_segments.last()->data.size() >= segment_size) { auto segment = new QueueSegment; m_segments.append(*segment); } m_segments.last()->data.append(forward(value)); ++m_size; } T dequeue() { VERIFY(!is_empty()); auto value = move(m_segments.first()->data[m_index_into_first++]); if (m_index_into_first == segment_size) { delete m_segments.take_first(); m_index_into_first = 0; } --m_size; if (m_size == 0 && !m_segments.is_empty()) { // This is not necessary for correctness but avoids faulting in // all the pages for the underlying Vector in the case where // the caller repeatedly enqueues and then dequeues a single item. m_index_into_first = 0; m_segments.last()->data.clear_with_capacity(); } return value; } T const& head() const { VERIFY(!is_empty()); return m_segments.first()->data[m_index_into_first]; } T& tail() { VERIFY(!is_empty()); return m_segments.last()->data.last(); } void clear() { while (auto* segment = m_segments.take_first()) delete segment; m_index_into_first = 0; m_size = 0; } private: struct QueueSegment { Vector data; IntrusiveListNode node; }; IntrusiveList<&QueueSegment::node> m_segments; size_t m_index_into_first { 0 }; size_t m_size { 0 }; }; } #if USING_AK_GLOBALLY using AK::Queue; #endif