@@ -22,6 +22,7 @@ const {
22
22
SymbolAsyncIterator,
23
23
SymbolDispose,
24
24
SymbolToStringTag,
25
+ TypedArrayPrototypeGetLength,
25
26
Uint8Array,
26
27
} = primordials ;
27
28
@@ -33,6 +34,7 @@ const {
33
34
ERR_INVALID_ARG_TYPE ,
34
35
ERR_INVALID_STATE ,
35
36
ERR_INVALID_THIS ,
37
+ ERR_OUT_OF_RANGE ,
36
38
} ,
37
39
} = require ( 'internal/errors' ) ;
38
40
@@ -58,8 +60,8 @@ const {
58
60
validateAbortSignal,
59
61
validateBuffer,
60
62
validateObject,
61
- kValidateObjectAllowNullable ,
62
- kValidateObjectAllowFunction ,
63
+ kValidateObjectAllowObjects ,
64
+ kValidateObjectAllowObjectsAndNull ,
63
65
} = require ( 'internal/validators' ) ;
64
66
65
67
const {
@@ -246,10 +248,10 @@ class ReadableStream {
246
248
* @param {UnderlyingSource } [source]
247
249
* @param {QueuingStrategy } [strategy]
248
250
*/
249
- constructor ( source = { } , strategy = kEmptyObject ) {
251
+ constructor ( source = kEmptyObject , strategy = kEmptyObject ) {
250
252
markTransferMode ( this , false , true ) ;
251
- if ( source === null )
252
- throw new ERR_INVALID_ARG_VALUE ( 'source' , 'Object ' , source ) ;
253
+ validateObject ( source , 'source' , kValidateObjectAllowObjects ) ;
254
+ validateObject ( strategy , 'strategy ' , kValidateObjectAllowObjectsAndNull ) ;
253
255
this [ kState ] = createReadableStreamState ( ) ;
254
256
255
257
this [ kIsClosedPromise ] = createDeferredPromise ( ) ;
@@ -332,7 +334,7 @@ class ReadableStream {
332
334
getReader ( options = kEmptyObject ) {
333
335
if ( ! isReadableStream ( this ) )
334
336
throw new ERR_INVALID_THIS ( 'ReadableStream' ) ;
335
- validateObject ( options , 'options' , kValidateObjectAllowNullable | kValidateObjectAllowFunction ) ;
337
+ validateObject ( options , 'options' , kValidateObjectAllowObjectsAndNull ) ;
336
338
const mode = options ?. mode ;
337
339
338
340
if ( mode === undefined )
@@ -370,6 +372,7 @@ class ReadableStream {
370
372
371
373
// The web platform tests require that these be handled one at a
372
374
// time and in a specific order. options can be null or undefined.
375
+ validateObject ( options , 'options' , kValidateObjectAllowObjectsAndNull ) ;
373
376
const preventAbort = options ?. preventAbort ;
374
377
const preventCancel = options ?. preventCancel ;
375
378
const preventClose = options ?. preventClose ;
@@ -412,6 +415,7 @@ class ReadableStream {
412
415
destination ) ;
413
416
}
414
417
418
+ validateObject ( options , 'options' , kValidateObjectAllowObjectsAndNull ) ;
415
419
const preventAbort = options ?. preventAbort ;
416
420
const preventCancel = options ?. preventCancel ;
417
421
const preventClose = options ?. preventClose ;
@@ -456,10 +460,8 @@ class ReadableStream {
456
460
values ( options = kEmptyObject ) {
457
461
if ( ! isReadableStream ( this ) )
458
462
throw new ERR_INVALID_THIS ( 'ReadableStream' ) ;
459
- validateObject ( options , 'options' ) ;
460
- const {
461
- preventCancel = false ,
462
- } = options ;
463
+ validateObject ( options , 'options' , kValidateObjectAllowObjectsAndNull ) ;
464
+ const preventCancel = ! ! ( options ?. preventCancel ) ;
463
465
464
466
// eslint-disable-next-line no-use-before-define
465
467
const reader = new ReadableStreamDefaultReader ( this ) ;
@@ -931,47 +933,62 @@ class ReadableStreamBYOBReader {
931
933
932
934
/**
933
935
* @param {ArrayBufferView } view
936
+ * @param {{
937
+ * min? : number
938
+ * }} [options]
934
939
* @returns {Promise<{
935
- * view : ArrayBufferView,
940
+ * value : ArrayBufferView,
936
941
* done : boolean,
937
942
* }>}
938
943
*/
939
- read ( view ) {
944
+ async read ( view , options = kEmptyObject ) {
940
945
if ( ! isReadableStreamBYOBReader ( this ) )
941
- return PromiseReject ( new ERR_INVALID_THIS ( 'ReadableStreamBYOBReader' ) ) ;
946
+ throw new ERR_INVALID_THIS ( 'ReadableStreamBYOBReader' ) ;
942
947
if ( ! isArrayBufferView ( view ) ) {
943
- return PromiseReject (
944
- new ERR_INVALID_ARG_TYPE (
945
- 'view' ,
946
- [
947
- 'Buffer ',
948
- 'TypedArray ',
949
- 'DataView' ,
950
- ] ,
951
- view ) ) ;
948
+ throw new ERR_INVALID_ARG_TYPE (
949
+ 'view' ,
950
+ [
951
+ 'Buffer' ,
952
+ 'TypedArray ',
953
+ 'DataView ',
954
+ ] ,
955
+ view ,
956
+ ) ;
952
957
}
958
+ validateObject ( options , 'options' , kValidateObjectAllowObjectsAndNull ) ;
953
959
954
960
const viewByteLength = ArrayBufferViewGetByteLength ( view ) ;
955
961
const viewBuffer = ArrayBufferViewGetBuffer ( view ) ;
956
962
const viewBufferByteLength = ArrayBufferPrototypeGetByteLength ( viewBuffer ) ;
957
963
958
964
if ( viewByteLength === 0 || viewBufferByteLength === 0 ) {
959
- return PromiseReject (
960
- new ERR_INVALID_STATE . TypeError (
961
- 'View or Viewed ArrayBuffer is zero-length or detached' ,
962
- ) ,
963
- ) ;
965
+ throw new ERR_INVALID_STATE . TypeError (
966
+ 'View or Viewed ArrayBuffer is zero-length or detached' ) ;
964
967
}
965
968
966
969
// Supposed to assert here that the view's buffer is not
967
970
// detached, but there's no API available to use to check that.
971
+
972
+ const min = options ?. min ?? 1 ;
973
+ if ( typeof min !== 'number' )
974
+ throw new ERR_INVALID_ARG_TYPE ( 'options.min' , 'number' , min ) ;
975
+ if ( ! NumberIsInteger ( min ) )
976
+ throw new ERR_INVALID_ARG_VALUE ( 'options.min' , min , 'must be an integer' ) ;
977
+ if ( min <= 0 )
978
+ throw new ERR_INVALID_ARG_VALUE ( 'options.min' , min , 'must be greater than 0' ) ;
979
+ if ( ! isDataView ( view ) ) {
980
+ if ( min > TypedArrayPrototypeGetLength ( view ) ) {
981
+ throw new ERR_OUT_OF_RANGE ( 'options.min' , '<= view.length' , min ) ;
982
+ }
983
+ } else if ( min > viewByteLength ) {
984
+ throw new ERR_OUT_OF_RANGE ( 'options.min' , '<= view.byteLength' , min ) ;
985
+ }
986
+
968
987
if ( this [ kState ] . stream === undefined ) {
969
- return PromiseReject (
970
- new ERR_INVALID_STATE . TypeError (
971
- 'The reader is not attached to a stream' ) ) ;
988
+ throw new ERR_INVALID_STATE . TypeError ( 'The reader is not attached to a stream' ) ;
972
989
}
973
990
const readIntoRequest = new ReadIntoRequest ( ) ;
974
- readableStreamBYOBReaderRead ( this , view , readIntoRequest ) ;
991
+ readableStreamBYOBReaderRead ( this , view , min , readIntoRequest ) ;
975
992
return readIntoRequest . promise ;
976
993
}
977
994
@@ -1885,7 +1902,7 @@ function readableByteStreamTee(stream) {
1885
1902
reading = false ;
1886
1903
} ,
1887
1904
} ;
1888
- readableStreamBYOBReaderRead ( reader , view , readIntoRequest ) ;
1905
+ readableStreamBYOBReaderRead ( reader , view , 1 , readIntoRequest ) ;
1889
1906
}
1890
1907
1891
1908
function pull1Algorithm ( ) {
@@ -2212,7 +2229,7 @@ function readableStreamReaderGenericRelease(reader) {
2212
2229
reader [ kState ] . stream = undefined ;
2213
2230
}
2214
2231
2215
- function readableStreamBYOBReaderRead ( reader , view , readIntoRequest ) {
2232
+ function readableStreamBYOBReaderRead ( reader , view , min , readIntoRequest ) {
2216
2233
const {
2217
2234
stream,
2218
2235
} = reader [ kState ] ;
@@ -2225,6 +2242,7 @@ function readableStreamBYOBReaderRead(reader, view, readIntoRequest) {
2225
2242
readableByteStreamControllerPullInto (
2226
2243
stream [ kState ] . controller ,
2227
2244
view ,
2245
+ min ,
2228
2246
readIntoRequest ) ;
2229
2247
}
2230
2248
@@ -2497,7 +2515,7 @@ function readableByteStreamControllerClose(controller) {
2497
2515
2498
2516
if ( pendingPullIntos . length ) {
2499
2517
const firstPendingPullInto = pendingPullIntos [ 0 ] ;
2500
- if ( firstPendingPullInto . bytesFilled > 0 ) {
2518
+ if ( firstPendingPullInto . bytesFilled % firstPendingPullInto . elementSize !== 0 ) {
2501
2519
const error = new ERR_INVALID_STATE . TypeError ( 'Partial read' ) ;
2502
2520
readableByteStreamControllerError ( controller , error ) ;
2503
2521
throw error ;
@@ -2514,7 +2532,7 @@ function readableByteStreamControllerCommitPullIntoDescriptor(stream, desc) {
2514
2532
2515
2533
let done = false ;
2516
2534
if ( stream [ kState ] . state === 'closed' ) {
2517
- desc . bytesFilled = 0 ;
2535
+ assert ( desc . bytesFilled % desc . elementSize === 0 ) ;
2518
2536
done = true ;
2519
2537
}
2520
2538
@@ -2603,6 +2621,7 @@ function readableByteStreamControllerHandleQueueDrain(controller) {
2603
2621
function readableByteStreamControllerPullInto (
2604
2622
controller ,
2605
2623
view ,
2624
+ min ,
2606
2625
readIntoRequest ) {
2607
2626
const {
2608
2627
closeRequested,
@@ -2615,6 +2634,11 @@ function readableByteStreamControllerPullInto(
2615
2634
elementSize = view . constructor . BYTES_PER_ELEMENT ;
2616
2635
ctor = view . constructor ;
2617
2636
}
2637
+
2638
+ const minimumFill = min * elementSize ;
2639
+ assert ( minimumFill >= elementSize && minimumFill <= view . byteLength ) ;
2640
+ assert ( minimumFill % elementSize === 0 ) ;
2641
+
2618
2642
const buffer = ArrayBufferViewGetBuffer ( view ) ;
2619
2643
const byteOffset = ArrayBufferViewGetByteOffset ( view ) ;
2620
2644
const byteLength = ArrayBufferViewGetByteLength ( view ) ;
@@ -2633,6 +2657,7 @@ function readableByteStreamControllerPullInto(
2633
2657
byteOffset,
2634
2658
byteLength,
2635
2659
bytesFilled : 0 ,
2660
+ minimumFill,
2636
2661
elementSize,
2637
2662
ctor,
2638
2663
type : 'byob' ,
@@ -2720,7 +2745,7 @@ function readableByteStreamControllerRespond(controller, bytesWritten) {
2720
2745
}
2721
2746
2722
2747
function readableByteStreamControllerRespondInClosedState ( controller , desc ) {
2723
- assert ( ! desc . bytesFilled ) ;
2748
+ assert ( desc . bytesFilled % desc . elementSize === 0 ) ;
2724
2749
if ( desc . type === 'none' ) {
2725
2750
readableByteStreamControllerShiftPendingPullInto ( controller ) ;
2726
2751
}
@@ -2897,17 +2922,18 @@ function readableByteStreamControllerFillPullIntoDescriptorFromQueue(
2897
2922
byteLength,
2898
2923
byteOffset,
2899
2924
bytesFilled,
2925
+ minimumFill,
2900
2926
elementSize,
2901
2927
} = desc ;
2902
- const currentAlignedBytes = bytesFilled - ( bytesFilled % elementSize ) ;
2903
2928
const maxBytesToCopy = MathMin (
2904
2929
controller [ kState ] . queueTotalSize ,
2905
2930
byteLength - bytesFilled ) ;
2906
2931
const maxBytesFilled = bytesFilled + maxBytesToCopy ;
2907
2932
const maxAlignedBytes = maxBytesFilled - ( maxBytesFilled % elementSize ) ;
2908
2933
let totalBytesToCopyRemaining = maxBytesToCopy ;
2909
2934
let ready = false ;
2910
- if ( maxAlignedBytes > currentAlignedBytes ) {
2935
+ assert ( bytesFilled < minimumFill ) ;
2936
+ if ( maxAlignedBytes >= minimumFill ) {
2911
2937
totalBytesToCopyRemaining = maxAlignedBytes - bytesFilled ;
2912
2938
ready = true ;
2913
2939
}
@@ -2950,7 +2976,7 @@ function readableByteStreamControllerFillPullIntoDescriptorFromQueue(
2950
2976
if ( ! ready ) {
2951
2977
assert ( ! controller [ kState ] . queueTotalSize ) ;
2952
2978
assert ( desc . bytesFilled > 0 ) ;
2953
- assert ( desc . bytesFilled < elementSize ) ;
2979
+ assert ( desc . bytesFilled < minimumFill ) ;
2954
2980
}
2955
2981
return ready ;
2956
2982
}
@@ -3006,7 +3032,7 @@ function readableByteStreamControllerRespondInReadableState(
3006
3032
return ;
3007
3033
}
3008
3034
3009
- if ( desc . bytesFilled < desc . elementSize )
3035
+ if ( desc . bytesFilled < desc . minimumFill )
3010
3036
return ;
3011
3037
3012
3038
readableByteStreamControllerShiftPendingPullInto ( controller ) ;
@@ -3191,6 +3217,7 @@ function readableByteStreamControllerPullSteps(controller, readRequest) {
3191
3217
byteOffset : 0 ,
3192
3218
byteLength : autoAllocateChunkSize ,
3193
3219
bytesFilled : 0 ,
3220
+ minimumFill : 1 ,
3194
3221
elementSize : 1 ,
3195
3222
ctor : Uint8Array ,
3196
3223
type : 'default' ,
0 commit comments