summaryrefslogtreecommitdiffstats
path: root/dom/streams/QueueWithSizes.h
blob: 5a5bb4903642187d6ee4ae350761d74c182fb77a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_QueueWithSizes_h
#define mozilla_dom_QueueWithSizes_h

#include <cmath>
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"

namespace mozilla::dom {

// Note: Consumers of QueueWithSize need to ensure it is traced. See
// ReadableStreamDefaultController.cpp for an example.

struct ValueWithSize : LinkedListElement<ValueWithSize> {
  ValueWithSize(JS::Handle<JS::Value> aValue, double aSize)
      : mValue(aValue), mSize(aSize){};

  JS::Heap<JS::Value> mValue;
  double mSize = 0.0f;
};

// This type is a little tricky lifetime wise: Despite the fact that we're
// talking about linked list of VaueWithSize, what is actually stored in the
// list are dynamically allocated ValueWithSize*. As a result, these have to be
// deleted. There are two ways this lifetime is managed:
//
// 1. In DequeueValue we pop the first element into a UniquePtr, so that it is
//    correctly cleaned up at destruction time.
// 2. We use an AutoCleanLinkedList which will delete elements when destroyed
//    or `clear`ed.
using QueueWithSizes = AutoCleanLinkedList<ValueWithSize>;

// https://streams.spec.whatwg.org/#is-non-negative-number
inline bool IsNonNegativeNumber(double v) {
  // Step 1. Implicit.
  // Step 2.
  if (std::isnan(v)) {
    return false;
  }

  // Step 3.
  return !(v < 0);
}

// https://streams.spec.whatwg.org/#enqueue-value-with-size
template <class QueueContainingClass>
inline void EnqueueValueWithSize(QueueContainingClass aContainer,
                                 JS::Handle<JS::Value> aValue, double aSize,
                                 ErrorResult& aRv) {
  // Step 1. Assert: container has [[queue]] and [[queueTotalSize]] internal
  // slots. (Implicit by template instantiation.)
  // Step 2. If ! IsNonNegativeNumber(size) is false, throw a RangeError
  // exception.
  if (!IsNonNegativeNumber(aSize)) {
    aRv.ThrowRangeError("invalid size");
    return;
  }

  // Step 3. If size is +∞, throw a RangeError exception.
  if (std::isinf(aSize)) {
    aRv.ThrowRangeError("Infinite queue size");
    return;
  }

  // Step 4. Append a new value-with-size with value value and size size to
  // container.[[queue]].
  // (See the comment on QueueWithSizes for the lifetime reasoning around this
  // allocation.)
  ValueWithSize* valueWithSize = new ValueWithSize(aValue, aSize);
  aContainer->Queue().insertBack(valueWithSize);
  // Step 5. Set container.[[queueTotalSize]] to container.[[queueTotalSize]] +
  // size.
  aContainer->SetQueueTotalSize(aContainer->QueueTotalSize() + aSize);
}

// https://streams.spec.whatwg.org/#dequeue-value
template <class QueueContainingClass>
inline void DequeueValue(QueueContainingClass aContainer,
                         JS::MutableHandle<JS::Value> aResultValue) {
  // Step 1. Implicit via template instantiation.
  // Step 2.
  MOZ_ASSERT(!aContainer->Queue().isEmpty());

  // Step 3+4
  // UniquePtr to ensure memory is freed.
  UniquePtr<ValueWithSize> valueWithSize(aContainer->Queue().popFirst());

  // Step 5.
  aContainer->SetQueueTotalSize(aContainer->QueueTotalSize() -
                                valueWithSize->mSize);

  // Step 6.
  if (aContainer->QueueTotalSize() < 0) {
    aContainer->SetQueueTotalSize(0);
  }

  // Step 7.
  aResultValue.set(valueWithSize->mValue);
}

// https://streams.spec.whatwg.org/#peek-queue-value
template <class QueueContainingClass>
inline void PeekQueueValue(QueueContainingClass aContainer,
                           JS::MutableHandle<JS::Value> aResultValue) {
  // Step 1. Assert: container has [[queue]] and [[queueTotalSize]] internal
  // slots.
  // Step 2. Assert: container.[[queue]] is not empty.
  MOZ_ASSERT(!aContainer->Queue().isEmpty());

  // Step 3. Let valueWithSize be container.[[queue]][0].
  ValueWithSize* valueWithSize = aContainer->Queue().getFirst();

  // Step 4. Return valueWithSize’s value.
  aResultValue.set(valueWithSize->mValue);
}

// https://streams.spec.whatwg.org/#reset-queue
template <class QueueContainingClass>
inline void ResetQueue(QueueContainingClass aContainer) {
  // Step 1. Assert: container has [[queue]] and [[queueTotalSize]] internal
  // slots. (implicit)

  // Step 2. Set container.[[queue]] to a new empty list.
  aContainer->Queue().clear();

  // Step 3. Set container.[[queueTotalSize]] to 0.
  aContainer->SetQueueTotalSize(0.0);
}

}  // namespace mozilla::dom

#endif