no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / xpcom / ds / nsDeque.h
blob4f0bf27037fecacab2979444d874376953c8570d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /**
8 * MODULE NOTES:
10 * The Deque is a very small, very efficient container object
11 * than can hold items of type T*, offering the following features:
12 * - Its interface supports pushing, popping, and peeking of items at the back
13 * or front, and retrieval from any position.
14 * - It can iterate over items via a ForEach method, range-for, or an iterator
15 * class.
16 * - When full, it can efficiently resize dynamically.
18 * NOTE: The only bit of trickery here is that this deque is
19 * built upon a ring-buffer. Like all ring buffers, the first
20 * item may not be at index[0]. The mOrigin member determines
21 * where the first child is. This point is quietly hidden from
22 * customers of this class.
25 #ifndef _NSDEQUE
26 #define _NSDEQUE
27 #include <cstddef>
29 #include "mozilla/AlreadyAddRefed.h"
30 #include "mozilla/Assertions.h"
31 #include "mozilla/fallible.h"
32 #include "mozilla/MemoryReporting.h"
33 #include "mozilla/RefPtr.h"
34 #include "nsCOMPtr.h"
35 #include "nsDebug.h"
36 #include "nsISupports.h"
38 namespace mozilla {
40 namespace detail {
41 /**
42 * The deque (double-ended queue) class is a common container type,
43 * whose behavior mimics a line in your favorite checkout stand.
44 * Classic CS describes the common behavior of a queue as FIFO.
45 * A deque allows insertion and removal at both ends of
46 * the container.
48 * nsDequeBase implements the untyped deque data structure while
49 * nsDeque provides the typed implementation and iterators.
51 class nsDequeBase {
52 public:
53 /**
54 * Returns the number of items currently stored in
55 * this deque.
57 * @return number of items currently in the deque
59 inline size_t GetSize() const { return mSize; }
61 protected:
62 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
64 /**
65 * Constructs an empty deque.
67 * @param aDeallocator Optional deallocator functor that will be called from
68 * Erase() and the destructor on any remaining item.
69 * The deallocator is owned by the deque and will be
70 * deleted at destruction time.
72 explicit nsDequeBase();
74 /**
75 * Deque destructor. Erases all items, deletes the deallocator.
77 ~nsDequeBase();
79 /**
80 * Appends new member at the end of the deque.
82 * @param aItem item to store in deque
83 * @return true if succeeded, false if failed to resize deque as needed
85 [[nodiscard]] bool Push(void* aItem, const fallible_t&);
87 /**
88 * Inserts new member at the front of the deque.
90 * @param aItem item to store in deque
91 * @return true if succeeded, false if failed to resize deque as needed
93 [[nodiscard]] bool PushFront(void* aItem, const fallible_t&);
95 /**
96 * Remove and return the last item in the container.
98 * @return the item that was the last item in container
100 void* Pop();
103 * Remove and return the first item in the container.
105 * @return the item that was first item in container
107 void* PopFront();
110 * Retrieve the last item without removing it.
112 * @return the last item in container
114 void* Peek() const;
117 * Retrieve the first item without removing it.
119 * @return the first item in container
121 void* PeekFront() const;
124 * Retrieve a member from the deque without removing it.
126 * @param index of desired item
127 * @return item in list, or nullptr if index is outside the deque
129 void* ObjectAt(size_t aIndex) const;
131 bool GrowCapacity();
134 * Remove all items from container without destroying them.
136 void Empty();
138 size_t mSize;
139 size_t mCapacity;
140 size_t mOrigin;
141 void* mBuffer[8];
142 void** mData;
144 nsDequeBase& operator=(const nsDequeBase& aOther) = delete;
145 nsDequeBase(const nsDequeBase& aOther) = delete;
148 // This iterator assumes that the deque itself is const, i.e., it cannot be
149 // modified while the iterator is used.
150 // Also it is a 'const' iterator in that it provides copies of the deque's
151 // elements, and therefore it is not possible to modify the deque's contents
152 // by assigning to a dereference of this iterator.
153 template <typename Deque>
154 class ConstDequeIterator {
155 public:
156 ConstDequeIterator(const Deque& aDeque, size_t aIndex)
157 : mDeque(aDeque), mIndex(aIndex) {}
158 ConstDequeIterator& operator++() {
159 ++mIndex;
160 return *this;
162 bool operator==(const ConstDequeIterator& aOther) const {
163 return mIndex == aOther.mIndex;
165 bool operator!=(const ConstDequeIterator& aOther) const {
166 return mIndex != aOther.mIndex;
168 typename Deque::PointerType operator*() const {
169 // Don't allow out-of-deque dereferences.
170 MOZ_RELEASE_ASSERT(mIndex < mDeque.GetSize());
171 return mDeque.ObjectAt(mIndex);
174 private:
175 const Deque& mDeque;
176 size_t mIndex;
179 // It is a 'const' iterator in that it provides copies of the deque's
180 // elements, and therefore it is not possible to modify the deque's contents
181 // by assigning to a dereference of this iterator.
182 // If the deque is modified in other ways, this iterator will stay at the same
183 // index, and will handle past-the-end comparisons, but not dereferencing.
184 template <typename Deque>
185 class ConstIterator {
186 public:
187 // Special index for the end iterator, to track the possibly-shifting
188 // deque size.
189 static const size_t EndIteratorIndex = size_t(-1);
191 ConstIterator(const Deque& aDeque, size_t aIndex)
192 : mDeque(aDeque), mIndex(aIndex) {}
193 ConstIterator& operator++() {
194 // End-iterator shouldn't be modified.
195 MOZ_ASSERT(mIndex != EndIteratorIndex);
196 ++mIndex;
197 return *this;
199 bool operator==(const ConstIterator& aOther) const {
200 return EffectiveIndex() == aOther.EffectiveIndex();
202 bool operator!=(const ConstIterator& aOther) const {
203 return EffectiveIndex() != aOther.EffectiveIndex();
205 typename Deque::PointerType operator*() const {
206 // Don't allow out-of-deque dereferences.
207 MOZ_RELEASE_ASSERT(mIndex < mDeque.GetSize());
208 return mDeque.ObjectAt(mIndex);
211 private:
212 // 0 <= index < deque.GetSize() inside the deque, deque.GetSize() otherwise.
213 // Only used when comparing indices, not to actually access items.
214 size_t EffectiveIndex() const {
215 return (mIndex < mDeque.GetSize()) ? mIndex : mDeque.GetSize();
218 const Deque& mDeque;
219 size_t mIndex; // May point outside the deque!
222 } // namespace detail
223 } // namespace mozilla
226 * The nsDequeFunctor class is used when you want to create
227 * callbacks between the deque and your generic code.
228 * Use these objects in a call to ForEach(), and as custom deallocators.
230 template <typename T>
231 class nsDequeFunctor {
232 public:
233 virtual void operator()(T* aObject) = 0;
234 virtual ~nsDequeFunctor() = default;
237 /******************************************************
238 * Here comes the nsDeque class itself...
239 ******************************************************/
242 * The deque (double-ended queue) class is a common container type,
243 * whose behavior mimics a line in your favorite checkout stand.
244 * Classic CS describes the common behavior of a queue as FIFO.
245 * A deque allows insertion and removal at both ends of
246 * the container.
248 * The deque stores pointers to items.
250 template <typename T>
251 class nsDeque : public mozilla::detail::nsDequeBase {
252 typedef mozilla::fallible_t fallible_t;
254 public:
255 using PointerType = T*;
256 using ConstDequeIterator = mozilla::detail::ConstDequeIterator<nsDeque<T>>;
257 using ConstIterator = mozilla::detail::ConstIterator<nsDeque<T>>;
260 * Constructs an empty deque.
262 * @param aDeallocator Optional deallocator functor that will be called from
263 * Erase() and the destructor on any remaining item.
264 * The deallocator is owned by the deque and will be
265 * deleted at destruction time.
267 explicit nsDeque(nsDequeFunctor<T>* aDeallocator = nullptr) {
268 MOZ_COUNT_CTOR(nsDeque);
269 mDeallocator = aDeallocator;
273 * Deque destructor. Erases all items, deletes the deallocator.
275 ~nsDeque() {
276 MOZ_COUNT_DTOR(nsDeque);
278 Erase();
279 SetDeallocator(nullptr);
283 * Appends new member at the end of the deque.
285 * @param aItem item to store in deque
287 inline void Push(T* aItem) {
288 if (!nsDequeBase::Push(aItem, mozilla::fallible)) {
289 NS_ABORT_OOM(mSize * sizeof(T*));
294 * Appends new member at the end of the deque.
296 * @param aItem item to store in deque
297 * @return true if succeeded, false if failed to resize deque as needed
299 [[nodiscard]] inline bool Push(T* aItem, const fallible_t& aFaillible) {
300 return nsDequeBase::Push(aItem, aFaillible);
304 * Inserts new member at the front of the deque.
306 * @param aItem item to store in deque
308 inline void PushFront(T* aItem) {
309 if (!nsDequeBase::PushFront(aItem, mozilla::fallible)) {
310 NS_ABORT_OOM(mSize * sizeof(T*));
315 * Inserts new member at the front of the deque.
317 * @param aItem item to store in deque
318 * @return true if succeeded, false if failed to resize deque as needed
320 [[nodiscard]] bool PushFront(T* aItem, const fallible_t& aFallible) {
321 return nsDequeBase::PushFront(aItem, aFallible);
325 * Remove and return the last item in the container.
327 * @return the item that was the last item in container
329 inline T* Pop() { return static_cast<T*>(nsDequeBase::Pop()); }
332 * Remove and return the first item in the container.
334 * @return the item that was first item in container
336 inline T* PopFront() { return static_cast<T*>(nsDequeBase::PopFront()); }
339 * Retrieve the last item without removing it.
341 * @return the last item in container
343 inline T* Peek() const { return static_cast<T*>(nsDequeBase::Peek()); }
346 * Retrieve the first item without removing it.
348 * @return the first item in container
350 inline T* PeekFront() const {
351 return static_cast<T*>(nsDequeBase::PeekFront());
355 * Retrieve a member from the deque without removing it.
357 * @param index of desired item
358 * @return item in list, or nullptr if index is outside the deque
360 inline T* ObjectAt(size_t aIndex) const {
361 if (NS_WARN_IF(aIndex >= GetSize())) {
362 return nullptr;
364 return static_cast<T*>(nsDequeBase::ObjectAt(aIndex));
368 * Remove and delete all items from container.
369 * Deletes are handled by the deallocator nsDequeFunctor
370 * which is specified at deque construction.
372 void Erase() {
373 if (mDeallocator && mSize) {
374 ForEach(*mDeallocator);
376 Empty();
380 * Call this method when you want to iterate through all
381 * items in the container, passing a functor along
382 * to call your code.
383 * If the deque is modified during ForEach, iteration will continue based on
384 * item indices; meaning that front operations may effectively skip over
385 * items or visit some items multiple times.
387 * @param aFunctor object to call for each member
389 void ForEach(nsDequeFunctor<T>& aFunctor) const {
390 for (size_t i = 0; i < mSize; ++i) {
391 aFunctor(ObjectAt(i));
395 // If this deque is const, we can provide ConstDequeIterator's.
396 ConstDequeIterator begin() const { return ConstDequeIterator(*this, 0); }
397 ConstDequeIterator end() const { return ConstDequeIterator(*this, mSize); }
399 // If this deque is *not* const, we provide ConstIterator's that can handle
400 // deque size changes.
401 ConstIterator begin() { return ConstIterator(*this, 0); }
402 ConstIterator end() {
403 return ConstIterator(*this, ConstIterator::EndIteratorIndex);
406 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
407 size_t size = nsDequeBase::SizeOfExcludingThis(aMallocSizeOf);
408 if (mDeallocator) {
409 size += aMallocSizeOf(mDeallocator);
411 return size;
414 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
415 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
418 protected:
419 nsDequeFunctor<T>* mDeallocator;
421 private:
423 * Copy constructor (deleted)
425 * @param aOther another deque
427 nsDeque(const nsDeque& aOther) = delete;
430 * Deque assignment operator (deleted)
432 * @param aOther another deque
433 * @return *this
435 nsDeque& operator=(const nsDeque& aOther) = delete;
437 void SetDeallocator(nsDequeFunctor<T>* aDeallocator) {
438 delete mDeallocator;
439 mDeallocator = aDeallocator;
444 * Specialization of nsDeque for reference counted pointers.
446 * Provides builtin reference handling as part of Push/Peek/Pop
448 template <typename T>
449 class nsRefPtrDeque : private nsDeque<T> {
450 typedef mozilla::fallible_t fallible_t;
452 class RefPtrDeallocator : public nsDequeFunctor<T> {
453 public:
454 virtual void operator()(T* aObject) override {
455 RefPtr<T> releaseMe = dont_AddRef(aObject);
459 public:
460 using PointerType = RefPtr<T>;
461 using ConstDequeIterator =
462 mozilla::detail::ConstDequeIterator<nsRefPtrDeque<T>>;
463 using ConstIterator = mozilla::detail::ConstIterator<nsRefPtrDeque<T>>;
465 explicit nsRefPtrDeque() : nsDeque<T>(new RefPtrDeallocator()) {}
467 inline void PushFront(already_AddRefed<T> aItem) {
468 T* item = aItem.take();
469 nsDeque<T>::PushFront(item);
472 inline void PushFront(T* aItem) { PushFront(do_AddRef(aItem)); }
474 inline void Push(T* aItem) { Push(do_AddRef(aItem)); }
476 inline void Push(already_AddRefed<T> aItem) {
477 T* item = aItem.take();
478 nsDeque<T>::Push(item);
481 inline already_AddRefed<T> PopFront() {
482 return dont_AddRef(nsDeque<T>::PopFront());
485 inline already_AddRefed<T> Pop() { return dont_AddRef(nsDeque<T>::Pop()); }
487 inline T* PeekFront() const { return nsDeque<T>::PeekFront(); }
489 inline T* Peek() const { return nsDeque<T>::Peek(); }
491 inline T* ObjectAt(size_t aIndex) const {
492 return nsDeque<T>::ObjectAt(aIndex);
495 inline void Erase() { nsDeque<T>::Erase(); }
497 // If this deque is const, we can provide ConstDequeIterator's.
498 ConstDequeIterator begin() const { return ConstDequeIterator(*this, 0); }
499 ConstDequeIterator end() const {
500 return ConstDequeIterator(*this, GetSize());
503 // If this deque is *not* const, we provide ConstIterator's that can handle
504 // deque size changes.
505 ConstIterator begin() { return ConstIterator(*this, 0); }
506 ConstIterator end() {
507 return ConstIterator(*this, ConstIterator::EndIteratorIndex);
510 inline size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
511 return nsDeque<T>::SizeOfExcludingThis(aMallocSizeOf);
514 inline size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
515 return nsDeque<T>::SizeOfIncludingThis(aMallocSizeOf);
518 inline size_t GetSize() const { return nsDeque<T>::GetSize(); }
521 * Call this method when you want to iterate through all
522 * items in the container, passing a functor along
523 * to call your code.
524 * If the deque is modified during ForEach, iteration will continue based on
525 * item indices; meaning that front operations may effectively skip over
526 * items or visit some items multiple times.
528 * @param aFunctor object to call for each member
530 void ForEach(nsDequeFunctor<T>& aFunctor) const {
531 size_t size = GetSize();
532 for (size_t i = 0; i < size; ++i) {
533 aFunctor(ObjectAt(i));
538 #endif