[=release a read lock|Releases the reader's lock=] on the corresponding stream. After the lock
@@ -1436,7 +1458,7 @@ value: newViewOnSameMemory, done: true } for closed streams. If the strea
@@ -3217,7 +3274,8 @@ The following abstract operations support the implementation of the
1. If |controller|.[=ReadableByteStreamController/[[pendingPullIntos]]=] is not empty,
1. Let |firstPendingPullInto| be
|controller|.[=ReadableByteStreamController/[[pendingPullIntos]]=][0].
- 1. If |firstPendingPullInto|'s [=pull-into descriptor/bytes filled=] > 0,
+ 1. If the remainder after dividing |firstPendingPullInto|'s [=pull-into descriptor/bytes filled=]
+ by |firstPendingPullInto|'s [=pull-into descriptor/element size=] is not 0,
1. Let |e| be a new {{TypeError}} exception.
1. Perform ! [$ReadableByteStreamControllerError$](|controller|, |e|).
1. Throw |e|.
@@ -3234,7 +3292,8 @@ The following abstract operations support the implementation of the
1. Assert: |pullIntoDescriptor|.[=pull-into descriptor/reader type=] is not "`none`".
1. Let |done| be false.
1. If |stream|.[=ReadableStream/[[state]]=] is "`closed`",
- 1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] is 0.
+ 1. Assert: the remainder after dividing |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=]
+ by |pullIntoDescriptor|'s [=pull-into descriptor/element size=] is 0.
1. Set |done| to true.
1. Let |filledView| be !
[$ReadableByteStreamControllerConvertPullIntoDescriptor$](|pullIntoDescriptor|).
@@ -3253,7 +3312,7 @@ The following abstract operations support the implementation of the
1. Let |bytesFilled| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=].
1. Let |elementSize| be |pullIntoDescriptor|'s [=pull-into descriptor/element size=].
1. Assert: |bytesFilled| ≤ |pullIntoDescriptor|'s [=pull-into descriptor/byte length=].
- 1. Assert: |bytesFilled| mod |elementSize| is 0.
+ 1. Assert: the remainder after dividing |bytesFilled| by |elementSize| is 0.
1. Let |buffer| be ! [$TransferArrayBuffer$](|pullIntoDescriptor|'s [=pull-into descriptor/buffer=]).
1. Return ! [$Construct$](|pullIntoDescriptor|'s [=pull-into descriptor/view constructor=], «
|buffer|, |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=],
@@ -3383,21 +3442,25 @@ The following abstract operations support the implementation of the
id="readable-byte-stream-controller-fill-pull-into-descriptor-from-queue">ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(|controller|,
|pullIntoDescriptor|) performs the following steps:
- 1. Let |elementSize| be |pullIntoDescriptor|.\[[elementSize]].
- 1. Let |currentAlignedBytes| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] −
- (|pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] mod |elementSize|).
1. Let |maxBytesToCopy| be min(|controller|.[=ReadableByteStreamController/[[queueTotalSize]]=],
|pullIntoDescriptor|'s [=pull-into descriptor/byte length=] − |pullIntoDescriptor|'s [=pull-into
descriptor/bytes filled=]).
1. Let |maxBytesFilled| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] +
|maxBytesToCopy|.
- 1. Let |maxAlignedBytes| be |maxBytesFilled| − (|maxBytesFilled| mod |elementSize|).
1. Let |totalBytesToCopyRemaining| be |maxBytesToCopy|.
1. Let |ready| be false.
- 1. If |maxAlignedBytes| > |currentAlignedBytes|,
+ 1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] < |pullIntoDescriptor|'s
+ [=pull-into descriptor/minimum fill=].
+ 1. Let |remainderBytes| be the remainder after dividing |maxBytesFilled| by |pullIntoDescriptor|'s
+ [=pull-into descriptor/element size=].
+ 1. Let |maxAlignedBytes| be |maxBytesFilled| − |remainderBytes|.
+ 1. If |maxAlignedBytes| ≥ |pullIntoDescriptor|'s [=pull-into descriptor/minimum fill=],
1. Set |totalBytesToCopyRemaining| to |maxAlignedBytes| − |pullIntoDescriptor|'s [=pull-into
descriptor/bytes filled=].
1. Set |ready| to true.
+
A descriptor for a {{ReadableStreamBYOBReader/read()}} request
+ that is not yet filled up to its minimum length will stay at the head of the queue, so the
+ [=underlying source=] can keep filling it.
1. Let |queue| be |controller|.[=ReadableByteStreamController/[[queue]]=].
1. [=While=] |totalBytesToCopyRemaining| > 0,
1. Let |headOfQueue| be |queue|[0].
@@ -3425,7 +3488,7 @@ The following abstract operations support the implementation of the
1. Assert: |controller|.[=ReadableByteStreamController/[[queueTotalSize]]=] is 0.
1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] > 0.
1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] <
- |pullIntoDescriptor|'s [=pull-into descriptor/element size=].
+ |pullIntoDescriptor|'s [=pull-into descriptor/minimum fill=].
1. Return |ready|.
@@ -3544,7 +3607,7 @@ The following abstract operations support the implementation of the
ReadableByteStreamControllerPullInto(|controller|,
- |view|, |readIntoRequest|) performs the following steps:
+ |view|, |min|, |readIntoRequest|) performs the following steps:
1. Let |stream| be |controller|.[=ReadableByteStreamController/[[stream]]=].
1. Let |elementSize| be 1.
@@ -3554,6 +3617,9 @@ The following abstract operations support the implementation of the
|view|.\[[TypedArrayName]].
1. Set |ctor| to the constructor specified in [=the typed array constructors table=] for
|view|.\[[TypedArrayName]].
+ 1. Let |minimumFill| be |min| × |elementSize|.
+ 1. Assert: |minimumFill| ≥ 0 and |minimumFill| ≤ |view|.\[[ByteLength]].
+ 1. Assert: the remainder after dividing |minimumFill| by |elementSize| is 0.
1. Let |byteOffset| be |view|.\[[ByteOffset]].
1. Let |byteLength| be |view|.\[[ByteLength]].
1. Let |bufferResult| be [$TransferArrayBuffer$](|view|.\[[ViewedArrayBuffer]]).
@@ -3561,12 +3627,35 @@ The following abstract operations support the implementation of the
1. Perform |readIntoRequest|'s [=read-into request/error steps=], given |bufferResult|.\[[Value]].
1. Return.
1. Let |buffer| be |bufferResult|.\[[Value]].
- 1. Let |pullIntoDescriptor| be a new [=pull-into descriptor=] with [=pull-into descriptor/buffer=]
- |buffer|, [=pull-into descriptor/buffer byte length=] |buffer|.\[[ArrayBufferByteLength]],
- [=pull-into descriptor/byte offset=] |byteOffset|, [=pull-into descriptor/byte length=]
- |byteLength|, [=pull-into descriptor/bytes filled=] 0, [=pull-into descriptor/element size=]
- |elementSize|, [=pull-into descriptor/view constructor=] |ctor|, and [=pull-into
- descriptor/reader type=] "`byob`".
+ 1. Let |pullIntoDescriptor| be a new [=pull-into descriptor=] with
+
+ - [=pull-into descriptor/buffer=]
+
- |buffer|
+
+
- [=pull-into descriptor/buffer byte length=]
+
- |buffer|.\[[ArrayBufferByteLength]]
+
+
- [=pull-into descriptor/byte offset=]
+
- |byteOffset|
+
+
- [=pull-into descriptor/byte length=]
+
- |byteLength|
+
+
- [=pull-into descriptor/bytes filled=]
+
- 0
+
+
- [=pull-into descriptor/minimum fill=]
+
- |minimumFill|
+
+
- [=pull-into descriptor/element size=]
+
- |elementSize|
+
+
- [=pull-into descriptor/view constructor=]
+
- |ctor|
+
+
- [=pull-into descriptor/reader type=]
+
- "`byob`"
+
1. If |controller|.[=ReadableByteStreamController/[[pendingPullIntos]]=] is not empty,
1. [=list/Append=] |pullIntoDescriptor| to
|controller|.[=ReadableByteStreamController/[[pendingPullIntos]]=].
@@ -3622,7 +3711,8 @@ The following abstract operations support the implementation of the
id="readable-byte-stream-controller-respond-in-closed-state">ReadableByteStreamControllerRespondInClosedState(|controller|,
|firstDescriptor|) performs the following steps:
- 1. Assert: |firstDescriptor|'s [=pull-into descriptor/bytes filled=] is 0.
+ 1. Assert: the remainder after dividing |firstDescriptor|'s [=pull-into descriptor/bytes filled=]
+ by |firstDescriptor|'s [=pull-into descriptor/element size=] is 0.
1. If |firstDescriptor|'s [=pull-into descriptor/reader type=] is "`none`",
perform ! [$ReadableByteStreamControllerShiftPendingPullInto$](|controller|).
1. Let |stream| be |controller|.[=ReadableByteStreamController/[[stream]]=].
@@ -3649,10 +3739,13 @@ The following abstract operations support the implementation of the
1. Perform ! [$ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue$](|controller|).
1. Return.
1. If |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] < |pullIntoDescriptor|'s
- [=pull-into descriptor/element size=], return.
+ [=pull-into descriptor/minimum fill=], return.
+
A descriptor for a {{ReadableStreamBYOBReader/read()}} request
+ that is not yet filled up to its minimum length will stay at the head of the queue, so the
+ [=underlying source=] can keep filling it.
1. Perform ! [$ReadableByteStreamControllerShiftPendingPullInto$](|controller|).
- 1. Let |remainderSize| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] mod
- |pullIntoDescriptor|'s [=pull-into descriptor/element size=].
+ 1. Let |remainderSize| be the remainder after dividing |pullIntoDescriptor|'s
+ [=pull-into descriptor/bytes filled=] by |pullIntoDescriptor|'s [=pull-into descriptor/element size=].
1. If |remainderSize| > 0,
1. Let |end| be |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=] +
|pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=].
diff --git a/reference-implementation/lib/ReadableByteStreamController-impl.js b/reference-implementation/lib/ReadableByteStreamController-impl.js
index 66cbe2278..eac1a028a 100644
--- a/reference-implementation/lib/ReadableByteStreamController-impl.js
+++ b/reference-implementation/lib/ReadableByteStreamController-impl.js
@@ -87,6 +87,7 @@ exports.implementation = class ReadableByteStreamControllerImpl {
byteOffset: 0,
byteLength: autoAllocateChunkSize,
bytesFilled: 0,
+ minimumFill: 1,
elementSize: 1,
viewConstructor: Uint8Array,
readerType: 'default'
diff --git a/reference-implementation/lib/ReadableStreamBYOBReader-impl.js b/reference-implementation/lib/ReadableStreamBYOBReader-impl.js
index 446846a45..954761162 100644
--- a/reference-implementation/lib/ReadableStreamBYOBReader-impl.js
+++ b/reference-implementation/lib/ReadableStreamBYOBReader-impl.js
@@ -11,7 +11,7 @@ class ReadableStreamBYOBReaderImpl {
aos.SetUpReadableStreamBYOBReader(this, stream);
}
- read(view) {
+ read(view, options) {
if (view.byteLength === 0) {
return promiseRejectedWith(new TypeError('view must have non-zero byteLength'));
}
@@ -22,6 +22,23 @@ class ReadableStreamBYOBReaderImpl {
return promiseRejectedWith(new TypeError('view\'s buffer has been detached'));
}
+ if (options.min === 0) {
+ return promiseRejectedWith(
+ new TypeError('options.min must be greater than 0')
+ );
+ }
+ if (view.constructor !== DataView) {
+ if (options.min > view.length) {
+ return promiseRejectedWith(
+ new RangeError('options.min must be less than or equal to view\'s length')
+ );
+ }
+ } else if (options.min > view.byteLength) {
+ return promiseRejectedWith(
+ new RangeError('options.min must be less than or equal to view\'s byteLength')
+ );
+ }
+
if (this._stream === undefined) {
return promiseRejectedWith(readerLockException('read'));
}
@@ -32,7 +49,7 @@ class ReadableStreamBYOBReaderImpl {
closeSteps: chunk => resolvePromise(promise, { value: chunk, done: true }),
errorSteps: e => rejectPromise(promise, e)
};
- aos.ReadableStreamBYOBReaderRead(this, view, readIntoRequest);
+ aos.ReadableStreamBYOBReaderRead(this, view, options.min, readIntoRequest);
return promise;
}
diff --git a/reference-implementation/lib/ReadableStreamBYOBReader.webidl b/reference-implementation/lib/ReadableStreamBYOBReader.webidl
index dfd3c25d8..6967c4311 100644
--- a/reference-implementation/lib/ReadableStreamBYOBReader.webidl
+++ b/reference-implementation/lib/ReadableStreamBYOBReader.webidl
@@ -2,7 +2,11 @@
interface ReadableStreamBYOBReader {
constructor(ReadableStream stream);
- Promise read(ArrayBufferView view);
+ Promise read(ArrayBufferView view, optional ReadableStreamBYOBReaderReadOptions options = {});
undefined releaseLock();
};
ReadableStreamBYOBReader includes ReadableStreamGenericReader;
+
+dictionary ReadableStreamBYOBReaderReadOptions {
+ [EnforceRange] unsigned long long min = 1;
+};
diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js
index 285ac22e0..5cd4814b3 100644
--- a/reference-implementation/lib/abstract-ops/readable-streams.js
+++ b/reference-implementation/lib/abstract-ops/readable-streams.js
@@ -647,7 +647,7 @@ function ReadableByteStreamTee(stream) {
reading = false;
}
};
- ReadableStreamBYOBReaderRead(reader, view, readIntoRequest);
+ ReadableStreamBYOBReaderRead(reader, view, 1, readIntoRequest);
}
function pull1Algorithm() {
@@ -913,7 +913,7 @@ function ReadableStreamReaderGenericRelease(reader) {
reader._stream = undefined;
}
-function ReadableStreamBYOBReaderRead(reader, view, readIntoRequest) {
+function ReadableStreamBYOBReaderRead(reader, view, min, readIntoRequest) {
const stream = reader._stream;
assert(stream !== undefined);
@@ -923,7 +923,7 @@ function ReadableStreamBYOBReaderRead(reader, view, readIntoRequest) {
if (stream._state === 'errored') {
readIntoRequest.errorSteps(stream._storedError);
} else {
- ReadableByteStreamControllerPullInto(stream._controller, view, readIntoRequest);
+ ReadableByteStreamControllerPullInto(stream._controller, view, min, readIntoRequest);
}
}
@@ -1272,7 +1272,7 @@ function ReadableByteStreamControllerClose(controller) {
if (controller._pendingPullIntos.length > 0) {
const firstPendingPullInto = controller._pendingPullIntos[0];
- if (firstPendingPullInto.bytesFilled > 0) {
+ if (firstPendingPullInto.bytesFilled % firstPendingPullInto.elementSize !== 0) {
const e = new TypeError('Insufficient bytes to fill elements in the given buffer');
ReadableByteStreamControllerError(controller, e);
@@ -1290,7 +1290,7 @@ function ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDe
let done = false;
if (stream._state === 'closed') {
- assert(pullIntoDescriptor.bytesFilled === 0);
+ assert(pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize === 0);
done = true;
}
@@ -1419,18 +1419,18 @@ function ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, size
}
function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) {
- const elementSize = pullIntoDescriptor.elementSize;
-
- const currentAlignedBytes = pullIntoDescriptor.bytesFilled - pullIntoDescriptor.bytesFilled % elementSize;
-
const maxBytesToCopy = Math.min(controller._queueTotalSize,
pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled);
const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy;
- const maxAlignedBytes = maxBytesFilled - maxBytesFilled % elementSize;
let totalBytesToCopyRemaining = maxBytesToCopy;
let ready = false;
- if (maxAlignedBytes > currentAlignedBytes) {
+ assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.minimumFill);
+ const remainderBytes = maxBytesFilled % pullIntoDescriptor.elementSize;
+ const maxAlignedBytes = maxBytesFilled - remainderBytes;
+ // A descriptor for a read() request that is not yet filled up to its minimum length will stay at the head
+ // of the queue, so the underlying source can keep filling it.
+ if (maxAlignedBytes >= pullIntoDescriptor.minimumFill) {
totalBytesToCopyRemaining = maxAlignedBytes - pullIntoDescriptor.bytesFilled;
ready = true;
}
@@ -1461,7 +1461,7 @@ function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
if (ready === false) {
assert(controller._queueTotalSize === 0);
assert(pullIntoDescriptor.bytesFilled > 0);
- assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize);
+ assert(pullIntoDescriptor.bytesFilled < pullIntoDescriptor.minimumFill);
}
return ready;
@@ -1563,7 +1563,7 @@ function ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller) {
}
}
-function ReadableByteStreamControllerPullInto(controller, view, readIntoRequest) {
+function ReadableByteStreamControllerPullInto(controller, view, min, readIntoRequest) {
const stream = controller._stream;
let elementSize = 1;
@@ -1571,6 +1571,10 @@ function ReadableByteStreamControllerPullInto(controller, view, readIntoRequest)
elementSize = view.constructor.BYTES_PER_ELEMENT;
}
+ const minimumFill = min * elementSize;
+ assert(minimumFill >= elementSize && minimumFill <= view.byteLength);
+ assert(minimumFill % elementSize === 0);
+
const ctor = view.constructor;
let buffer;
@@ -1587,6 +1591,7 @@ function ReadableByteStreamControllerPullInto(controller, view, readIntoRequest)
byteOffset: view.byteOffset,
byteLength: view.byteLength,
bytesFilled: 0,
+ minimumFill,
elementSize,
viewConstructor: ctor,
readerType: 'byob'
@@ -1660,7 +1665,7 @@ function ReadableByteStreamControllerRespond(controller, bytesWritten) {
}
function ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor) {
- assert(firstDescriptor.bytesFilled === 0);
+ assert(firstDescriptor.bytesFilled % firstDescriptor.elementSize === 0);
if (firstDescriptor.readerType === 'none') {
ReadableByteStreamControllerShiftPendingPullInto(controller);
@@ -1686,7 +1691,9 @@ function ReadableByteStreamControllerRespondInReadableState(controller, bytesWri
return;
}
- if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) {
+ if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.minimumFill) {
+ // A descriptor for a read() request that is not yet filled up to its minimum length will stay at the head
+ // of the queue, so the underlying source can keep filling it.
return;
}
diff --git a/reference-implementation/web-platform-tests b/reference-implementation/web-platform-tests
index a8872d92b..7eaf605c3 160000
--- a/reference-implementation/web-platform-tests
+++ b/reference-implementation/web-platform-tests
@@ -1 +1 @@
-Subproject commit a8872d92b147fc87200eb0c14fe7a4a9e7cd4f73
+Subproject commit 7eaf605c38d80377c717828376deabad86b702b2