QueueWithSizes.h (4798B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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/. */ 6 7 #ifndef mozilla_dom_QueueWithSizes_h 8 #define mozilla_dom_QueueWithSizes_h 9 10 #include <cmath> 11 12 #include "js/TypeDecls.h" 13 #include "js/Value.h" 14 #include "mozilla/ErrorResult.h" 15 #include "mozilla/UniquePtr.h" 16 #include "nsTArray.h" 17 18 namespace mozilla::dom { 19 20 // Note: Consumers of QueueWithSize need to ensure it is traced. See 21 // ReadableStreamDefaultController.cpp for an example. 22 23 struct ValueWithSize : LinkedListElement<ValueWithSize> { 24 ValueWithSize(JS::Handle<JS::Value> aValue, double aSize) 25 : mValue(aValue), mSize(aSize) {}; 26 27 JS::Heap<JS::Value> mValue; 28 double mSize = 0.0f; 29 }; 30 31 // This type is a little tricky lifetime wise: Despite the fact that we're 32 // talking about linked list of VaueWithSize, what is actually stored in the 33 // list are dynamically allocated ValueWithSize*. As a result, these have to be 34 // deleted. There are two ways this lifetime is managed: 35 // 36 // 1. In DequeueValue we pop the first element into a UniquePtr, so that it is 37 // correctly cleaned up at destruction time. 38 // 2. We use an AutoCleanLinkedList which will delete elements when destroyed 39 // or `clear`ed. 40 using QueueWithSizes = AutoCleanLinkedList<ValueWithSize>; 41 42 // https://streams.spec.whatwg.org/#is-non-negative-number 43 inline bool IsNonNegativeNumber(double v) { 44 // Step 1. Implicit. 45 // Step 2. 46 if (std::isnan(v)) { 47 return false; 48 } 49 50 // Step 3. 51 return !(v < 0); 52 } 53 54 // https://streams.spec.whatwg.org/#enqueue-value-with-size 55 template <class QueueContainingClass> 56 inline void EnqueueValueWithSize(QueueContainingClass aContainer, 57 JS::Handle<JS::Value> aValue, double aSize, 58 ErrorResult& aRv) { 59 // Step 1. Assert: container has [[queue]] and [[queueTotalSize]] internal 60 // slots. (Implicit by template instantiation.) 61 // Step 2. If ! IsNonNegativeNumber(size) is false, throw a RangeError 62 // exception. 63 if (!IsNonNegativeNumber(aSize)) { 64 aRv.ThrowRangeError("invalid size"); 65 return; 66 } 67 68 // Step 3. If size is +∞, throw a RangeError exception. 69 if (std::isinf(aSize)) { 70 aRv.ThrowRangeError("Infinite queue size"); 71 return; 72 } 73 74 // Step 4. Append a new value-with-size with value value and size size to 75 // container.[[queue]]. 76 // (See the comment on QueueWithSizes for the lifetime reasoning around this 77 // allocation.) 78 ValueWithSize* valueWithSize = new ValueWithSize(aValue, aSize); 79 aContainer->Queue().insertBack(valueWithSize); 80 // Step 5. Set container.[[queueTotalSize]] to container.[[queueTotalSize]] + 81 // size. 82 aContainer->SetQueueTotalSize(aContainer->QueueTotalSize() + aSize); 83 } 84 85 // https://streams.spec.whatwg.org/#dequeue-value 86 template <class QueueContainingClass> 87 inline void DequeueValue(QueueContainingClass aContainer, 88 JS::MutableHandle<JS::Value> aResultValue) { 89 // Step 1. Implicit via template instantiation. 90 // Step 2. 91 MOZ_ASSERT(!aContainer->Queue().isEmpty()); 92 93 // Step 3+4 94 // UniquePtr to ensure memory is freed. 95 UniquePtr<ValueWithSize> valueWithSize(aContainer->Queue().popFirst()); 96 97 // Step 5. 98 aContainer->SetQueueTotalSize(aContainer->QueueTotalSize() - 99 valueWithSize->mSize); 100 101 // Step 6. 102 if (aContainer->QueueTotalSize() < 0) { 103 aContainer->SetQueueTotalSize(0); 104 } 105 106 // Step 7. 107 aResultValue.set(valueWithSize->mValue); 108 } 109 110 // https://streams.spec.whatwg.org/#peek-queue-value 111 template <class QueueContainingClass> 112 inline void PeekQueueValue(QueueContainingClass aContainer, 113 JS::MutableHandle<JS::Value> aResultValue) { 114 // Step 1. Assert: container has [[queue]] and [[queueTotalSize]] internal 115 // slots. 116 // Step 2. Assert: container.[[queue]] is not empty. 117 MOZ_ASSERT(!aContainer->Queue().isEmpty()); 118 119 // Step 3. Let valueWithSize be container.[[queue]][0]. 120 ValueWithSize* valueWithSize = aContainer->Queue().getFirst(); 121 122 // Step 4. Return valueWithSize’s value. 123 aResultValue.set(valueWithSize->mValue); 124 } 125 126 // https://streams.spec.whatwg.org/#reset-queue 127 template <class QueueContainingClass> 128 inline void ResetQueue(QueueContainingClass aContainer) { 129 // Step 1. Assert: container has [[queue]] and [[queueTotalSize]] internal 130 // slots. (implicit) 131 132 // Step 2. Set container.[[queue]] to a new empty list. 133 aContainer->Queue().clear(); 134 135 // Step 3. Set container.[[queueTotalSize]] to 0. 136 aContainer->SetQueueTotalSize(0.0); 137 } 138 139 } // namespace mozilla::dom 140 141 #endif