@@ -47,7 +47,7 @@ const {
4747 toUint8Array,
4848} = require ( 'internal/streams/iter/utils' ) ;
4949
50- // Maximum number of chunks to yield per batch from from(Uint8Array[] ).
50+ // Maximum number of chunks to yield per batch from from()/fromSync( ).
5151// Bounds peak memory when arrays flow through transforms, which must
5252// allocate output for the entire batch at once.
5353const FROM_BATCH_SIZE = 128 ;
@@ -190,33 +190,66 @@ function isUint8ArrayBatch(value) {
190190return true ;
191191}
192192
193+ function * yieldBoundedBatch ( batch ) {
194+ if ( batch . length === 0 ) {
195+ return ;
196+ }
197+ if ( batch . length <= FROM_BATCH_SIZE ) {
198+ yield batch ;
199+ return ;
200+ }
201+ for ( let i = 0 ; i < batch . length ; i += FROM_BATCH_SIZE ) {
202+ yield ArrayPrototypeSlice ( batch , i , i + FROM_BATCH_SIZE ) ;
203+ }
204+ }
205+
193206/**
194207 * Normalize a sync streamable source, yielding batches of Uint8Array.
195208 * @param {Iterable } source
196209 * @yields {Uint8Array[]}
197210 */
198211function * normalizeSyncSource ( source ) {
212+ let batch = [ ] ;
213+
199214for ( const value of source ) {
200215// Fast path 1: value is already a Uint8Array[] batch
201216if ( isUint8ArrayBatch ( value ) ) {
202- if ( value . length > 0 ) {
203- yield value ;
217+ if ( batch . length > 0 ) {
218+ yield batch ;
219+ batch = [ ] ;
204220}
221+ yield * yieldBoundedBatch ( value ) ;
205222continue ;
206223}
207224// Fast path 2: value is a single Uint8Array (very common)
208225if ( isUint8Array ( value ) ) {
209- yield [ value ] ;
226+ ArrayPrototypePush ( batch , value ) ;
227+ if ( batch . length === FROM_BATCH_SIZE ) {
228+ yield batch ;
229+ batch = [ ] ;
230+ }
210231continue ;
211232}
212233// Slow path: normalize the value
213- const batch = [ ] ;
214- for ( const chunk of normalizeSyncValue ( value ) ) {
215- ArrayPrototypePush ( batch , chunk ) ;
216- }
217234if ( batch . length > 0 ) {
218235yield batch ;
236+ batch = [ ] ;
237+ }
238+ let valueBatch = [ ] ;
239+ for ( const chunk of normalizeSyncValue ( value ) ) {
240+ ArrayPrototypePush ( valueBatch , chunk ) ;
241+ if ( valueBatch . length === FROM_BATCH_SIZE ) {
242+ yield valueBatch ;
243+ valueBatch = [ ] ;
244+ }
219245}
246+ if ( valueBatch . length > 0 ) {
247+ yield valueBatch ;
248+ }
249+ }
250+
251+ if ( batch . length > 0 ) {
252+ yield batch ;
220253}
221254}
222255
@@ -329,36 +362,42 @@ async function* normalizeAsyncSource(source) {
329362return ;
330363}
331364
332- // Fall back to sync iteration - batch all sync values together
365+ // Fall back to sync iteration - batch sync values together with a bound.
333366if ( isSyncIterable ( source ) ) {
334- const batch = [ ] ;
367+ let batch = [ ] ;
335368
336369for ( const value of source ) {
337370// Fast path 1: value is already a Uint8Array[] batch
338371if ( isUint8ArrayBatch ( value ) ) {
339372// Flush any accumulated batch first
340373if ( batch . length > 0 ) {
341- yield ArrayPrototypeSlice ( batch ) ;
342- batch . length = 0 ;
343- }
344- if ( value . length > 0 ) {
345- yield value ;
374+ yield batch ;
375+ batch = [ ] ;
346376}
377+ yield * yieldBoundedBatch ( value ) ;
347378continue ;
348379}
349380// Fast path 2: value is a single Uint8Array (very common)
350381if ( isUint8Array ( value ) ) {
351382ArrayPrototypePush ( batch , value ) ;
383+ if ( batch . length === FROM_BATCH_SIZE ) {
384+ yield batch ;
385+ batch = [ ] ;
386+ }
352387continue ;
353388}
354389// Slow path: normalize the value - must flush and yield individually
355390if ( batch . length > 0 ) {
356- yield ArrayPrototypeSlice ( batch ) ;
357- batch . length = 0 ;
391+ yield batch ;
392+ batch = [ ] ;
358393}
359- const asyncBatch = [ ] ;
394+ let asyncBatch = [ ] ;
360395for await ( const chunk of normalizeAsyncValue ( value ) ) {
361396ArrayPrototypePush ( asyncBatch , chunk ) ;
397+ if ( asyncBatch . length === FROM_BATCH_SIZE ) {
398+ yield asyncBatch ;
399+ asyncBatch = [ ] ;
400+ }
362401}
363402if ( asyncBatch . length > 0 ) {
364403yield asyncBatch ;
0 commit comments