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/. */
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
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.
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"
36 #include "nsISupports.h"
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
48 * nsDequeBase implements the untyped deque data structure while
49 * nsDeque provides the typed implementation and iterators.
54 * Returns the number of items currently stored in
57 * @return number of items currently in the deque
59 inline size_t GetSize() const { return mSize
; }
62 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const;
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();
75 * Deque destructor. Erases all items, deletes the deallocator.
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
&);
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
&);
96 * Remove and return the last item in the container.
98 * @return the item that was the last item in container
103 * Remove and return the first item in the container.
105 * @return the item that was first item in container
110 * Retrieve the last item without removing it.
112 * @return the last item in container
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;
134 * Remove all items from container without destroying them.
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
{
156 ConstDequeIterator(const Deque
& aDeque
, size_t aIndex
)
157 : mDeque(aDeque
), mIndex(aIndex
) {}
158 ConstDequeIterator
& operator++() {
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
);
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
{
187 // Special index for the end iterator, to track the possibly-shifting
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
);
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
);
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();
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
{
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
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
;
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.
276 MOZ_COUNT_DTOR(nsDeque
);
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())) {
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.
373 if (mDeallocator
&& mSize
) {
374 ForEach(*mDeallocator
);
380 * Call this method when you want to iterate through all
381 * items in the container, passing a functor along
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
);
409 size
+= aMallocSizeOf(mDeallocator
);
414 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const {
415 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
419 nsDequeFunctor
<T
>* mDeallocator
;
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
435 nsDeque
& operator=(const nsDeque
& aOther
) = delete;
437 void SetDeallocator(nsDequeFunctor
<T
>* aDeallocator
) {
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
> {
454 virtual void operator()(T
* aObject
) override
{
455 RefPtr
<T
> releaseMe
= dont_AddRef(aObject
);
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
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
));