1*09537850SAkhilesh Sanikop /* 2*09537850SAkhilesh Sanikop * Copyright 2019 The libgav1 Authors 3*09537850SAkhilesh Sanikop * 4*09537850SAkhilesh Sanikop * Licensed under the Apache License, Version 2.0 (the "License"); 5*09537850SAkhilesh Sanikop * you may not use this file except in compliance with the License. 6*09537850SAkhilesh Sanikop * You may obtain a copy of the License at 7*09537850SAkhilesh Sanikop * 8*09537850SAkhilesh Sanikop * http://www.apache.org/licenses/LICENSE-2.0 9*09537850SAkhilesh Sanikop * 10*09537850SAkhilesh Sanikop * Unless required by applicable law or agreed to in writing, software 11*09537850SAkhilesh Sanikop * distributed under the License is distributed on an "AS IS" BASIS, 12*09537850SAkhilesh Sanikop * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*09537850SAkhilesh Sanikop * See the License for the specific language governing permissions and 14*09537850SAkhilesh Sanikop * limitations under the License. 15*09537850SAkhilesh Sanikop */ 16*09537850SAkhilesh Sanikop 17*09537850SAkhilesh Sanikop #ifndef LIBGAV1_SRC_UTILS_UNBOUNDED_QUEUE_H_ 18*09537850SAkhilesh Sanikop #define LIBGAV1_SRC_UTILS_UNBOUNDED_QUEUE_H_ 19*09537850SAkhilesh Sanikop 20*09537850SAkhilesh Sanikop #include <cassert> 21*09537850SAkhilesh Sanikop #include <cstddef> 22*09537850SAkhilesh Sanikop #include <memory> 23*09537850SAkhilesh Sanikop #include <new> 24*09537850SAkhilesh Sanikop #include <utility> 25*09537850SAkhilesh Sanikop 26*09537850SAkhilesh Sanikop #include "src/utils/compiler_attributes.h" 27*09537850SAkhilesh Sanikop #include "src/utils/memory.h" 28*09537850SAkhilesh Sanikop 29*09537850SAkhilesh Sanikop namespace libgav1 { 30*09537850SAkhilesh Sanikop 31*09537850SAkhilesh Sanikop // A FIFO queue of an unbounded capacity. 32*09537850SAkhilesh Sanikop // 33*09537850SAkhilesh Sanikop // This implementation uses the general approach used in std::deque 34*09537850SAkhilesh Sanikop // implementations. See, for example, 35*09537850SAkhilesh Sanikop // https://stackoverflow.com/questions/6292332/what-really-is-a-deque-in-stl 36*09537850SAkhilesh Sanikop // 37*09537850SAkhilesh Sanikop // It is much simpler because it just needs to support the queue interface. 38*09537850SAkhilesh Sanikop // The blocks are chained into a circular list, not managed by a "map". It 39*09537850SAkhilesh Sanikop // does not shrink the internal buffer. 40*09537850SAkhilesh Sanikop // 41*09537850SAkhilesh Sanikop // An alternative implementation approach is a resizable circular array. See, 42*09537850SAkhilesh Sanikop // for example, ResizingArrayQueue.java in https://algs4.cs.princeton.edu/code/ 43*09537850SAkhilesh Sanikop // and base::circular_deque in Chromium's base/containers library. 44*09537850SAkhilesh Sanikop template <typename T> 45*09537850SAkhilesh Sanikop class UnboundedQueue { 46*09537850SAkhilesh Sanikop public: 47*09537850SAkhilesh Sanikop UnboundedQueue() = default; 48*09537850SAkhilesh Sanikop 49*09537850SAkhilesh Sanikop // Move only. UnboundedQueue(UnboundedQueue && other)50*09537850SAkhilesh Sanikop UnboundedQueue(UnboundedQueue&& other) 51*09537850SAkhilesh Sanikop : first_block_(other.first_block_), 52*09537850SAkhilesh Sanikop front_(other.front_), 53*09537850SAkhilesh Sanikop last_block_(other.last_block_), 54*09537850SAkhilesh Sanikop back_(other.back_) { 55*09537850SAkhilesh Sanikop other.first_block_ = nullptr; 56*09537850SAkhilesh Sanikop other.front_ = 0; 57*09537850SAkhilesh Sanikop other.last_block_ = nullptr; 58*09537850SAkhilesh Sanikop other.back_ = 0; 59*09537850SAkhilesh Sanikop } 60*09537850SAkhilesh Sanikop UnboundedQueue& operator=(UnboundedQueue&& other) { 61*09537850SAkhilesh Sanikop if (this != &other) { 62*09537850SAkhilesh Sanikop Destroy(); 63*09537850SAkhilesh Sanikop first_block_ = other.first_block_; 64*09537850SAkhilesh Sanikop front_ = other.front_; 65*09537850SAkhilesh Sanikop last_block_ = other.last_block_; 66*09537850SAkhilesh Sanikop back_ = other.back_; 67*09537850SAkhilesh Sanikop other.first_block_ = nullptr; 68*09537850SAkhilesh Sanikop other.front_ = 0; 69*09537850SAkhilesh Sanikop other.last_block_ = nullptr; 70*09537850SAkhilesh Sanikop other.back_ = 0; 71*09537850SAkhilesh Sanikop } 72*09537850SAkhilesh Sanikop return *this; 73*09537850SAkhilesh Sanikop } 74*09537850SAkhilesh Sanikop ~UnboundedQueue()75*09537850SAkhilesh Sanikop ~UnboundedQueue() { Destroy(); } 76*09537850SAkhilesh Sanikop 77*09537850SAkhilesh Sanikop // Allocates two Blocks upfront because most access patterns require at 78*09537850SAkhilesh Sanikop // least two Blocks. Returns false if the allocation of the Blocks failed. Init()79*09537850SAkhilesh Sanikop LIBGAV1_MUST_USE_RESULT bool Init() { 80*09537850SAkhilesh Sanikop std::unique_ptr<Block> new_block0(new (std::nothrow) Block); 81*09537850SAkhilesh Sanikop std::unique_ptr<Block> new_block1(new (std::nothrow) Block); 82*09537850SAkhilesh Sanikop if (new_block0 == nullptr || new_block1 == nullptr) return false; 83*09537850SAkhilesh Sanikop first_block_ = last_block_ = new_block0.release(); 84*09537850SAkhilesh Sanikop new_block1->next = first_block_; 85*09537850SAkhilesh Sanikop last_block_->next = new_block1.release(); 86*09537850SAkhilesh Sanikop return true; 87*09537850SAkhilesh Sanikop } 88*09537850SAkhilesh Sanikop 89*09537850SAkhilesh Sanikop // Checks if the queue has room for a new element. If the queue is full, 90*09537850SAkhilesh Sanikop // tries to grow it. Returns false if the queue is full and the attempt to 91*09537850SAkhilesh Sanikop // grow it failed. 92*09537850SAkhilesh Sanikop // 93*09537850SAkhilesh Sanikop // NOTE: GrowIfNeeded() must be called before each call to Push(). This 94*09537850SAkhilesh Sanikop // inconvenient design is necessary to guarantee a successful Push() call. 95*09537850SAkhilesh Sanikop // 96*09537850SAkhilesh Sanikop // Push(T&& value) is often called with the argument std::move(value). The 97*09537850SAkhilesh Sanikop // moved-from object |value| won't be usable afterwards, so it would be 98*09537850SAkhilesh Sanikop // problematic if Push(T&& value) failed and we lost access to the original 99*09537850SAkhilesh Sanikop // |value| object. GrowIfNeeded()100*09537850SAkhilesh Sanikop LIBGAV1_MUST_USE_RESULT bool GrowIfNeeded() { 101*09537850SAkhilesh Sanikop assert(last_block_ != nullptr); 102*09537850SAkhilesh Sanikop if (back_ == kBlockCapacity) { 103*09537850SAkhilesh Sanikop if (last_block_->next == first_block_) { 104*09537850SAkhilesh Sanikop // All Blocks are in use. 105*09537850SAkhilesh Sanikop std::unique_ptr<Block> new_block(new (std::nothrow) Block); 106*09537850SAkhilesh Sanikop if (new_block == nullptr) return false; 107*09537850SAkhilesh Sanikop new_block->next = first_block_; 108*09537850SAkhilesh Sanikop last_block_->next = new_block.release(); 109*09537850SAkhilesh Sanikop } 110*09537850SAkhilesh Sanikop last_block_ = last_block_->next; 111*09537850SAkhilesh Sanikop back_ = 0; 112*09537850SAkhilesh Sanikop } 113*09537850SAkhilesh Sanikop return true; 114*09537850SAkhilesh Sanikop } 115*09537850SAkhilesh Sanikop 116*09537850SAkhilesh Sanikop // Pushes the element |value| to the end of the queue. It is an error to call 117*09537850SAkhilesh Sanikop // Push() when the queue is full. Push(const T & value)118*09537850SAkhilesh Sanikop void Push(const T& value) { 119*09537850SAkhilesh Sanikop assert(last_block_ != nullptr); 120*09537850SAkhilesh Sanikop assert(back_ < kBlockCapacity); 121*09537850SAkhilesh Sanikop T* elements = reinterpret_cast<T*>(last_block_->buffer); 122*09537850SAkhilesh Sanikop new (&elements[back_++]) T(value); 123*09537850SAkhilesh Sanikop } 124*09537850SAkhilesh Sanikop Push(T && value)125*09537850SAkhilesh Sanikop void Push(T&& value) { 126*09537850SAkhilesh Sanikop assert(last_block_ != nullptr); 127*09537850SAkhilesh Sanikop assert(back_ < kBlockCapacity); 128*09537850SAkhilesh Sanikop T* elements = reinterpret_cast<T*>(last_block_->buffer); 129*09537850SAkhilesh Sanikop new (&elements[back_++]) T(std::move(value)); 130*09537850SAkhilesh Sanikop } 131*09537850SAkhilesh Sanikop 132*09537850SAkhilesh Sanikop // Returns the element at the front of the queue. It is an error to call 133*09537850SAkhilesh Sanikop // Front() when the queue is empty. Front()134*09537850SAkhilesh Sanikop T& Front() { 135*09537850SAkhilesh Sanikop assert(!Empty()); 136*09537850SAkhilesh Sanikop T* elements = reinterpret_cast<T*>(first_block_->buffer); 137*09537850SAkhilesh Sanikop return elements[front_]; 138*09537850SAkhilesh Sanikop } 139*09537850SAkhilesh Sanikop Front()140*09537850SAkhilesh Sanikop const T& Front() const { 141*09537850SAkhilesh Sanikop assert(!Empty()); 142*09537850SAkhilesh Sanikop T* elements = reinterpret_cast<T*>(first_block_->buffer); 143*09537850SAkhilesh Sanikop return elements[front_]; 144*09537850SAkhilesh Sanikop } 145*09537850SAkhilesh Sanikop 146*09537850SAkhilesh Sanikop // Removes the element at the front of the queue from the queue. It is an 147*09537850SAkhilesh Sanikop // error to call Pop() when the queue is empty. Pop()148*09537850SAkhilesh Sanikop void Pop() { 149*09537850SAkhilesh Sanikop assert(!Empty()); 150*09537850SAkhilesh Sanikop T* elements = reinterpret_cast<T*>(first_block_->buffer); 151*09537850SAkhilesh Sanikop elements[front_++].~T(); 152*09537850SAkhilesh Sanikop if (front_ == kBlockCapacity) { 153*09537850SAkhilesh Sanikop // The first block has become empty. 154*09537850SAkhilesh Sanikop front_ = 0; 155*09537850SAkhilesh Sanikop if (first_block_ == last_block_) { 156*09537850SAkhilesh Sanikop // Only one Block is in use. Simply reset back_. 157*09537850SAkhilesh Sanikop back_ = 0; 158*09537850SAkhilesh Sanikop } else { 159*09537850SAkhilesh Sanikop first_block_ = first_block_->next; 160*09537850SAkhilesh Sanikop } 161*09537850SAkhilesh Sanikop } 162*09537850SAkhilesh Sanikop } 163*09537850SAkhilesh Sanikop 164*09537850SAkhilesh Sanikop // Returns true if the queue is empty. Empty()165*09537850SAkhilesh Sanikop bool Empty() const { return first_block_ == last_block_ && front_ == back_; } 166*09537850SAkhilesh Sanikop 167*09537850SAkhilesh Sanikop private: 168*09537850SAkhilesh Sanikop // kBlockCapacity is the maximum number of elements each Block can hold. 169*09537850SAkhilesh Sanikop // sizeof(void*) is subtracted from 2048 to account for the |next| pointer in 170*09537850SAkhilesh Sanikop // the Block struct. 171*09537850SAkhilesh Sanikop // 172*09537850SAkhilesh Sanikop // In Linux x86_64, sizeof(std::function<void()>) is 32, so each Block can 173*09537850SAkhilesh Sanikop // hold 63 std::function<void()> objects. 174*09537850SAkhilesh Sanikop // 175*09537850SAkhilesh Sanikop // NOTE: The corresponding value in <deque> in libc++ revision 176*09537850SAkhilesh Sanikop // 245b5ba3448b9d3f6de5962066557e253a6bc9a4 is: 177*09537850SAkhilesh Sanikop // template <class _ValueType, class _DiffType> 178*09537850SAkhilesh Sanikop // struct __deque_block_size { 179*09537850SAkhilesh Sanikop // static const _DiffType value = 180*09537850SAkhilesh Sanikop // sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16; 181*09537850SAkhilesh Sanikop // }; 182*09537850SAkhilesh Sanikop // 183*09537850SAkhilesh Sanikop // Note that 4096 / 256 = 16, so apparently this expression is intended to 184*09537850SAkhilesh Sanikop // ensure the block size is at least 4096 bytes and each block can hold at 185*09537850SAkhilesh Sanikop // least 16 elements. 186*09537850SAkhilesh Sanikop static constexpr size_t kBlockCapacity = 187*09537850SAkhilesh Sanikop (sizeof(T) < 128) ? (2048 - sizeof(void*)) / sizeof(T) : 16; 188*09537850SAkhilesh Sanikop 189*09537850SAkhilesh Sanikop struct Block : public Allocable { 190*09537850SAkhilesh Sanikop alignas(T) char buffer[kBlockCapacity * sizeof(T)]; 191*09537850SAkhilesh Sanikop Block* next; 192*09537850SAkhilesh Sanikop }; 193*09537850SAkhilesh Sanikop Destroy()194*09537850SAkhilesh Sanikop void Destroy() { 195*09537850SAkhilesh Sanikop if (first_block_ == nullptr) return; // An uninitialized queue. 196*09537850SAkhilesh Sanikop 197*09537850SAkhilesh Sanikop // First free the unused blocks, which are located after last_block and 198*09537850SAkhilesh Sanikop // before first_block_. 199*09537850SAkhilesh Sanikop Block* block = last_block_->next; 200*09537850SAkhilesh Sanikop // Cut the circular list open after last_block_. 201*09537850SAkhilesh Sanikop last_block_->next = nullptr; 202*09537850SAkhilesh Sanikop while (block != first_block_) { 203*09537850SAkhilesh Sanikop Block* next = block->next; 204*09537850SAkhilesh Sanikop delete block; 205*09537850SAkhilesh Sanikop block = next; 206*09537850SAkhilesh Sanikop } 207*09537850SAkhilesh Sanikop 208*09537850SAkhilesh Sanikop // Then free the used blocks. Destruct the elements in the used blocks. 209*09537850SAkhilesh Sanikop while (block != nullptr) { 210*09537850SAkhilesh Sanikop const size_t begin = (block == first_block_) ? front_ : 0; 211*09537850SAkhilesh Sanikop const size_t end = (block == last_block_) ? back_ : kBlockCapacity; 212*09537850SAkhilesh Sanikop T* elements = reinterpret_cast<T*>(block->buffer); 213*09537850SAkhilesh Sanikop for (size_t i = begin; i < end; ++i) { 214*09537850SAkhilesh Sanikop elements[i].~T(); 215*09537850SAkhilesh Sanikop } 216*09537850SAkhilesh Sanikop Block* next = block->next; 217*09537850SAkhilesh Sanikop delete block; 218*09537850SAkhilesh Sanikop block = next; 219*09537850SAkhilesh Sanikop } 220*09537850SAkhilesh Sanikop } 221*09537850SAkhilesh Sanikop 222*09537850SAkhilesh Sanikop // Blocks are chained in a circular singly-linked list. If the list of Blocks 223*09537850SAkhilesh Sanikop // is empty, both first_block_ and last_block_ are null pointers. If the list 224*09537850SAkhilesh Sanikop // is nonempty, first_block_ points to the first used Block and last_block_ 225*09537850SAkhilesh Sanikop // points to the last used Block. 226*09537850SAkhilesh Sanikop // 227*09537850SAkhilesh Sanikop // Invariant: If Init() is called and succeeds, the queue is always nonempty. 228*09537850SAkhilesh Sanikop // This allows all methods (except the destructor) to avoid null pointer 229*09537850SAkhilesh Sanikop // checks for first_block_ and last_block_. 230*09537850SAkhilesh Sanikop Block* first_block_ = nullptr; 231*09537850SAkhilesh Sanikop // The index of the element in first_block_ to be removed by Pop(). 232*09537850SAkhilesh Sanikop size_t front_ = 0; 233*09537850SAkhilesh Sanikop Block* last_block_ = nullptr; 234*09537850SAkhilesh Sanikop // The index in last_block_ where the new element is inserted by Push(). 235*09537850SAkhilesh Sanikop size_t back_ = 0; 236*09537850SAkhilesh Sanikop }; 237*09537850SAkhilesh Sanikop 238*09537850SAkhilesh Sanikop #if !LIBGAV1_CXX17 239*09537850SAkhilesh Sanikop template <typename T> 240*09537850SAkhilesh Sanikop constexpr size_t UnboundedQueue<T>::kBlockCapacity; 241*09537850SAkhilesh Sanikop #endif 242*09537850SAkhilesh Sanikop 243*09537850SAkhilesh Sanikop } // namespace libgav1 244*09537850SAkhilesh Sanikop 245*09537850SAkhilesh Sanikop #endif // LIBGAV1_SRC_UTILS_UNBOUNDED_QUEUE_H_ 246