Skip to content

Commit 6e3ef19

Browse files
committed
Bug 1864406 - Add ReadableStreamBYOBReader.prototype.read(view, { min }). r=saschanaz,webidl,smaug
Implements whatwg/streams#1145 Differential Revision: https://phabricator.services.mozilla.com/D226225 UltraBlame original commit: df7878660191f4991ce6a5898629c963b38a6e50
1 parent 7139724 commit 6e3ef19

7 files changed

+113
-250
lines changed

dom/streams/ReadableByteStreamController.cpp

+61-26
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,15 @@ struct PullIntoDescriptor final
115115

116116
PullIntoDescriptor(JS::Handle<JSObject*> aBuffer, uint64_t aBufferByteLength,
117117
uint64_t aByteOffset, uint64_t aByteLength,
118-
uint64_t aBytesFilled, uint64_t aElementSize,
119-
Constructor aViewConstructor, ReaderType aReaderType)
118+
uint64_t aBytesFilled, uint64_t aMinimumFill,
119+
uint64_t aElementSize, Constructor aViewConstructor,
120+
ReaderType aReaderType)
120121
: mBuffer(aBuffer),
121122
mBufferByteLength(aBufferByteLength),
122123
mByteOffset(aByteOffset),
123124
mByteLength(aByteLength),
124125
mBytesFilled(aBytesFilled),
126+
mMinimumFill(aMinimumFill),
125127
mElementSize(aElementSize),
126128
mViewConstructor(aViewConstructor),
127129
mReaderType(aReaderType) {
@@ -147,6 +149,8 @@ struct PullIntoDescriptor final
147149
mBytesFilled = aBytesFilled;
148150
}
149151

152+
uint64_t MinimumFill() const { return mMinimumFill; }
153+
150154
uint64_t ElementSize() const { return mElementSize; }
151155
void SetElementSize(const uint64_t aElementSize) {
152156
mElementSize = aElementSize;
@@ -166,6 +170,7 @@ struct PullIntoDescriptor final
166170
uint64_t mByteOffset = 0;
167171
uint64_t mByteLength = 0;
168172
uint64_t mBytesFilled = 0;
173+
uint64_t mMinimumFill = 0;
169174
uint64_t mElementSize = 0;
170175
Constructor mViewConstructor;
171176
ReaderType mReaderType;
@@ -397,8 +402,11 @@ void ReadableByteStreamControllerClose(
397402

398403
PullIntoDescriptor* firstPendingPullInto =
399404
aController->PendingPullIntos().getFirst();
405+
406+
400407

401-
if (firstPendingPullInto->BytesFilled() > 0) {
408+
if ((firstPendingPullInto->BytesFilled() %
409+
firstPendingPullInto->ElementSize()) != 0) {
402410

403411
ErrorResult rv;
404412
rv.ThrowTypeError("Leftover Bytes");
@@ -704,7 +712,9 @@ void ReadableByteStreamControllerCommitPullIntoDescriptor(
704712

705713
if (aStream->State() == ReadableStream::ReaderState::Closed) {
706714

707-
MOZ_ASSERT(pullIntoDescriptor->BytesFilled() == 0);
715+
716+
MOZ_ASSERT((pullIntoDescriptor->BytesFilled() %
717+
pullIntoDescriptor->ElementSize()) == 0);
708718

709719

710720
done = true;
@@ -1170,9 +1180,11 @@ void ReadableByteStreamController::PullSteps(JSContext* aCx,
11701180

11711181
if (autoAllocateChunkSize) {
11721182

1183+
11731184
aRv.MightThrowJSException();
11741185
JS::Rooted<JSObject*> buffer(
11751186
aCx, JS::NewArrayBuffer(aCx, *autoAllocateChunkSize));
1187+
11761188

11771189
if (!buffer) {
11781190

@@ -1194,8 +1206,19 @@ void ReadableByteStreamController::PullSteps(JSContext* aCx,
11941206
}
11951207

11961208

1209+
1210+
1211+
1212+
1213+
1214+
1215+
1216+
1217+
11971218
RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
1198-
buffer, *autoAllocateChunkSize, 0, *autoAllocateChunkSize, 0, 1,
1219+
buffer, *autoAllocateChunkSize,
1220+
0, *autoAllocateChunkSize,
1221+
0, 1, 1,
11991222
PullIntoDescriptor::Constructor::Uint8, ReaderType::Default);
12001223

12011224

@@ -1281,6 +1304,7 @@ JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
12811304
MOZ_ASSERT(bytesFilled <= pullIntoDescriptor->ByteLength());
12821305

12831306

1307+
12841308
MOZ_ASSERT(bytesFilled % elementSize == 0);
12851309

12861310

@@ -1311,7 +1335,9 @@ static void ReadableByteStreamControllerRespondInClosedState(
13111335
JSContext* aCx, ReadableByteStreamController* aController,
13121336
RefPtr<PullIntoDescriptor>& aFirstDescriptor, ErrorResult& aRv) {
13131337

1314-
MOZ_ASSERT(aFirstDescriptor->BytesFilled() == 0);
1338+
1339+
MOZ_ASSERT(
1340+
(aFirstDescriptor->BytesFilled() % aFirstDescriptor->ElementSize()) == 0);
13151341

13161342

13171343

@@ -1398,7 +1424,7 @@ static void ReadableByteStreamControllerRespondInReadableState(
13981424

13991425

14001426

1401-
if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->ElementSize()) {
1427+
if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->MinimumFill()) {
14021428
return;
14031429
}
14041430

@@ -1648,15 +1674,6 @@ bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
16481674
JSContext* aCx, ReadableByteStreamController* aController,
16491675
PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
16501676

1651-
size_t elementSize = aPullIntoDescriptor->ElementSize();
1652-
1653-
1654-
1655-
size_t currentAlignedBytes =
1656-
aPullIntoDescriptor->BytesFilled() -
1657-
(aPullIntoDescriptor->BytesFilled() % elementSize);
1658-
1659-
16601677

16611678
size_t maxBytesToCopy =
16621679
std::min(static_cast<size_t>(aController->QueueTotalSize()),
@@ -1668,17 +1685,25 @@ bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
16681685
size_t maxBytesFilled = aPullIntoDescriptor->BytesFilled() + maxBytesToCopy;
16691686

16701687

1688+
size_t totalBytesToCopyRemaining = maxBytesToCopy;
1689+
16711690

1672-
size_t maxAlignedBytes = maxBytesFilled - (maxBytesFilled % elementSize);
1691+
bool ready = false;
16731692

16741693

1675-
size_t totalBytesToCopyRemaining = maxBytesToCopy;
1694+
1695+
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
1696+
aPullIntoDescriptor->MinimumFill());
16761697

16771698

1678-
bool ready = false;
1699+
1700+
size_t remainderBytes = maxBytesFilled % aPullIntoDescriptor->ElementSize();
16791701

16801702

1681-
if (maxAlignedBytes > currentAlignedBytes) {
1703+
size_t maxAlignedBytes = maxBytesFilled - remainderBytes;
1704+
1705+
1706+
if (maxAlignedBytes >= aPullIntoDescriptor->MinimumFill()) {
16821707

16831708

16841709
totalBytesToCopyRemaining =
@@ -1759,9 +1784,8 @@ bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
17591784

17601785

17611786

1762-
17631787
MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
1764-
aPullIntoDescriptor->ElementSize());
1788+
aPullIntoDescriptor->MinimumFill());
17651789
}
17661790

17671791

@@ -1771,8 +1795,8 @@ bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
17711795

17721796
void ReadableByteStreamControllerPullInto(
17731797
JSContext* aCx, ReadableByteStreamController* aController,
1774-
JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
1775-
ErrorResult& aRv) {
1798+
JS::Handle<JSObject*> aView, uint64_t aMin,
1799+
ReadIntoRequest* aReadIntoRequest, ErrorResult& aRv) {
17761800
aRv.MightThrowJSException();
17771801

17781802

@@ -1799,6 +1823,17 @@ void ReadableByteStreamControllerPullInto(
17991823
}
18001824

18011825

1826+
uint64_t minimumFill = aMin * elementSize;
1827+
1828+
1829+
MOZ_ASSERT(minimumFill >= 0 &&
1830+
minimumFill <= JS_GetArrayBufferViewByteLength(aView));
1831+
1832+
1833+
1834+
MOZ_ASSERT((minimumFill % elementSize) == 0);
1835+
1836+
18021837
size_t byteOffset = JS_GetArrayBufferViewByteOffset(aView);
18031838

18041839

@@ -1851,9 +1886,10 @@ void ReadableByteStreamControllerPullInto(
18511886

18521887

18531888

1889+
18541890
RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
18551891
buffer, JS::GetArrayBufferByteLength(buffer), byteOffset, byteLength, 0,
1856-
elementSize, ctor, ReaderType::BYOB);
1892+
minimumFill, elementSize, ctor, ReaderType::BYOB);
18571893

18581894

18591895
if (!aController->PendingPullIntos().isEmpty()) {
@@ -1953,7 +1989,6 @@ void ReadableByteStreamControllerPullInto(
19531989
ReadableStreamAddReadIntoRequest(stream, aReadIntoRequest);
19541990

19551991

1956-
19571992
ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
19581993
}
19591994

dom/streams/ReadableByteStreamController.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerRespondWithNewView(
187187

188188
MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerPullInto(
189189
JSContext* aCx, ReadableByteStreamController* aController,
190-
JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
191-
ErrorResult& aRv);
190+
JS::Handle<JSObject*> aView, uint64_t aMin,
191+
ReadIntoRequest* aReadIntoRequest, ErrorResult& aRv);
192192

193193
void ReadableByteStreamControllerError(
194194
ReadableByteStreamController* aController, JS::Handle<JS::Value> aValue,

dom/streams/ReadableStreamBYOBReader.cpp

+38-5
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ namespace streams_abstract {
173173

174174
void ReadableStreamBYOBReaderRead(JSContext* aCx,
175175
ReadableStreamBYOBReader* aReader,
176-
JS::Handle<JSObject*> aView,
176+
JS::Handle<JSObject*> aView, uint64_t aMin,
177177
ReadIntoRequest* aReadIntoRequest,
178178
ErrorResult& aRv) {
179179

@@ -200,14 +200,15 @@ void ReadableStreamBYOBReaderRead(JSContext* aCx,
200200
MOZ_ASSERT(stream->Controller()->IsByte());
201201
RefPtr<ReadableByteStreamController> controller(
202202
stream->Controller()->AsByte());
203-
ReadableByteStreamControllerPullInto(aCx, controller, aView, aReadIntoRequest,
204-
aRv);
203+
ReadableByteStreamControllerPullInto(aCx, controller, aView, aMin,
204+
aReadIntoRequest, aRv);
205205
}
206206
}
207207

208208

209209
already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
210-
const ArrayBufferView& aArray, ErrorResult& aRv) {
210+
const ArrayBufferView& aArray,
211+
const ReadableStreamBYOBReaderReadOptions& aOptions, ErrorResult& aRv) {
211212
AutoJSAPI jsapi;
212213
if (!jsapi.Init(GetParentObject())) {
213214
aRv.ThrowUnknownError("Internal error");
@@ -249,6 +250,37 @@ already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
249250

250251

251252

253+
if (aOptions.mMin == 0) {
254+
aRv.ThrowTypeError(
255+
"Zero is not a valid value for 'min' member of "
256+
"ReadableStreamBYOBReaderReadOptions.");
257+
return nullptr;
258+
}
259+
260+
261+
if (JS_IsTypedArrayObject(view)) {
262+
263+
264+
if (aOptions.mMin > JS_GetTypedArrayLength(view)) {
265+
aRv.ThrowRangeError(
266+
"Array length exceeded by 'min' member of "
267+
"ReadableStreamBYOBReaderReadOptions.");
268+
return nullptr;
269+
}
270+
} else {
271+
272+
273+
274+
if (aOptions.mMin > JS_GetArrayBufferViewByteLength(view)) {
275+
aRv.ThrowRangeError(
276+
"byteLength exceeded by 'min' member of "
277+
"ReadableStreamBYOBReaderReadOptions.");
278+
return nullptr;
279+
}
280+
}
281+
282+
283+
252284
if (!GetStream()) {
253285
aRv.ThrowTypeError("Reader has undefined stream");
254286
return nullptr;
@@ -263,7 +295,8 @@ already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
263295

264296

265297

266-
ReadableStreamBYOBReaderRead(cx, this, view, readIntoRequest, aRv);
298+
ReadableStreamBYOBReaderRead(cx, this, view, aOptions.mMin, readIntoRequest,
299+
aRv);
267300
if (aRv.Failed()) {
268301
return nullptr;
269302
}

dom/streams/ReadableStreamBYOBReader.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace mozilla::dom {
2222
class Promise;
2323
struct ReadIntoRequest;
2424
class ReadableStream;
25+
struct ReadableStreamBYOBReaderReadOptions;
2526

2627
}
2728

@@ -52,7 +53,8 @@ class ReadableStreamBYOBReader final : public ReadableStreamGenericReader,
5253
const GlobalObject& global, ReadableStream& stream, ErrorResult& rv);
5354

5455
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Read(
55-
const ArrayBufferView& aArray, ErrorResult& rv);
56+
const ArrayBufferView& aArray,
57+
const ReadableStreamBYOBReaderReadOptions& aOptions, ErrorResult& rv);
5658

5759
void ReleaseLock(ErrorResult& rv);
5860

@@ -73,8 +75,8 @@ already_AddRefed<ReadableStreamBYOBReader> AcquireReadableStreamBYOBReader(
7375

7476
MOZ_CAN_RUN_SCRIPT void ReadableStreamBYOBReaderRead(
7577
JSContext* aCx, ReadableStreamBYOBReader* aReader,
76-
JS::Handle<JSObject*> aView, ReadIntoRequest* aReadIntoRequest,
77-
ErrorResult& aRv);
78+
JS::Handle<JSObject*> aView, uint64_t aMin,
79+
ReadIntoRequest* aReadIntoRequest, ErrorResult& aRv);
7880

7981
void ReadableStreamBYOBReaderErrorReadIntoRequests(
8082
JSContext* aCx, ReadableStreamBYOBReader* aReader,

dom/streams/ReadableStreamTee.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -912,9 +912,10 @@ void PullWithBYOBReader(JSContext* aCx, TeeState* aTeeState,
912912
new PullWithBYOBReader_ReadIntoRequest(aTeeState, aForBranch);
913913

914914

915+
915916
RefPtr<ReadableStreamBYOBReader> byobReader =
916917
aTeeState->GetReader()->AsBYOB();
917-
ReadableStreamBYOBReaderRead(aCx, byobReader, aView, readIntoRequest, aRv);
918+
ReadableStreamBYOBReaderRead(aCx, byobReader, aView, 1, readIntoRequest, aRv);
918919
}
919920

920921

dom/webidl/ReadableStreamBYOBReader.webidl

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ interface ReadableStreamBYOBReader {
1313
constructor(ReadableStream stream);
1414

1515
[NewObject]
16-
Promise<ReadableStreamReadResult> read(ArrayBufferView view);
16+
Promise<ReadableStreamReadResult> read(ArrayBufferView view, optional ReadableStreamBYOBReaderReadOptions options = {});
1717

1818
[Throws]
1919
undefined releaseLock();
2020
};
2121
ReadableStreamBYOBReader includes ReadableStreamGenericReader;
22+
23+
dictionary ReadableStreamBYOBReaderReadOptions {
24+
[EnforceRange] unsigned long long min = 1;
25+
};

0 commit comments

Comments
 (0)