66 MathMin,
77 ObjectDefineProperties,
88 ObjectDefineProperty,
9- PromiseResolve,
109 PromiseReject,
11- SafePromisePrototypeFinally ,
10+ PromiseResolve ,
1211 ReflectConstruct,
1312 RegExpPrototypeExec,
1413 RegExpPrototypeSymbolReplace,
@@ -22,7 +21,8 @@ const {
2221
2322const {
2423 createBlob : _createBlob ,
25- FixedSizeBlobCopyJob,
24+ createBlobFromFileHandle : _createBlobFromFileHandle ,
25+ concat,
2626 getDataObject,
2727} = internalBinding ( 'blob' ) ;
2828
@@ -52,13 +52,13 @@ const {
5252const { inspect } = require ( 'internal/util/inspect' ) ;
5353
5454const {
55- AbortError,
5655 codes : {
5756 ERR_INVALID_ARG_TYPE ,
5857 ERR_INVALID_ARG_VALUE ,
5958 ERR_INVALID_THIS ,
6059 ERR_BUFFER_TOO_LARGE ,
61- }
60+ } ,
61+ errnoException,
6262} = require ( 'internal/errors' ) ;
6363
6464const {
@@ -67,13 +67,8 @@ const {
6767} = require ( 'internal/validators' ) ;
6868
6969const kHandle = Symbol ( 'kHandle' ) ;
70- const kState = Symbol ( 'kState' ) ;
71- const kIndex = Symbol ( 'kIndex' ) ;
7270const kType = Symbol ( 'kType' ) ;
7371const kLength = Symbol ( 'kLength' ) ;
74- const kArrayBufferPromise = Symbol ( 'kArrayBufferPromise' ) ;
75-
76- const kMaxChunkSize = 65536 ;
7772
7873const disallowedTypeCharacters = / [ ^ \u{0020} - \u{007E} ] / u;
7974
@@ -266,40 +261,28 @@ class Blob {
266261 if ( ! isBlob ( this ) )
267262 return PromiseReject ( new ERR_INVALID_THIS ( 'Blob' ) ) ;
268263
269- // If there's already a promise in flight for the content,
270- // reuse it, but only while it's in flight. After the cached
271- // promise resolves it will be cleared, allowing it to be
272- // garbage collected as soon as possible.
273- if ( this [ kArrayBufferPromise ] )
274- return this [ kArrayBufferPromise ] ;
275-
276- const job = new FixedSizeBlobCopyJob ( this [ kHandle ] ) ;
277-
278- const ret = job . run ( ) ;
279-
280- // If the job returns a value immediately, the ArrayBuffer
281- // was generated synchronously and should just be returned
282- // directly.
283- if ( ret !== undefined )
284- return PromiseResolve ( ret ) ;
264+ if ( this . size === 0 ) {
265+ return PromiseResolve ( new ArrayBuffer ( 0 ) ) ;
266+ }
285267
286- const {
287- promise,
288- resolve,
289- reject,
290- } = createDeferredPromise ( ) ;
291-
292- job . ondone = ( err , ab ) => {
293- if ( err !== undefined )
294- return reject ( new AbortError ( undefined , { cause : err } ) ) ;
295- resolve ( ab ) ;
268+ const { promise, resolve } = createDeferredPromise ( ) ;
269+ const reader = this [ kHandle ] . getReader ( ) ;
270+ const buffers = [ ] ;
271+ const readNext = ( ) => {
272+ reader . pull ( ( status , buffer ) => {
273+ if ( status === - 1 ) {
274+ // EOS, concat & resolve
275+ // buffer should be undefined here
276+ resolve ( concat ( buffers ) ) ;
277+ return ;
278+ }
279+ if ( buffer !== undefined )
280+ buffers . push ( buffer ) ;
281+ readNext ( ) ;
282+ } ) ;
296283 } ;
297- this [ kArrayBufferPromise ] =
298- SafePromisePrototypeFinally (
299- promise ,
300- ( ) => this [ kArrayBufferPromise ] = undefined ) ;
301-
302- return this [ kArrayBufferPromise ] ;
284+ readNext ( ) ;
285+ return promise ;
303286 }
304287
305288 /**
@@ -321,24 +304,57 @@ class Blob {
321304 if ( ! isBlob ( this ) )
322305 throw new ERR_INVALID_THIS ( 'Blob' ) ;
323306
324- const self = this ;
307+ if ( this . size === 0 ) {
308+ return new lazyReadableStream ( {
309+ start ( c ) { c . close ( ) ; }
310+ } ) ;
311+ }
312+
313+ const reader = this [ kHandle ] . getReader ( ) ;
325314 return new lazyReadableStream ( {
326- async start ( ) {
327- this [ kState ] = await self . arrayBuffer ( ) ;
328- this [ kIndex ] = 0 ;
315+ start ( c ) {
316+ // There really should only be one read at a time so using an
317+ // array here is purely defensive.
318+ this . pendingPulls = [ ] ;
329319 } ,
330-
331- pull ( controller ) {
332- if ( this [ kState ] . byteLength - this [ kIndex ] <= kMaxChunkSize ) {
333- controller . enqueue ( new Uint8Array ( this [ kState ] , this [ kIndex ] ) ) ;
334- controller . close ( ) ;
335- this [ kState ] = undefined ;
336- } else {
337- controller . enqueue ( new Uint8Array ( this [ kState ] , this [ kIndex ] , kMaxChunkSize ) ) ;
338- this [ kIndex ] += kMaxChunkSize ;
320+ pull ( c ) {
321+ const { promise, resolve, reject } = createDeferredPromise ( ) ;
322+ this . pendingPulls . push ( { resolve, reject} ) ;
323+ reader . pull ( ( status , buffer ) => {
324+ // If pendingPulls is empty here, the stream had to have
325+ // been canceled, and we don't really care about the result.
326+ // we can simply exit.
327+ if ( this . pendingPulls . length === 0 ) {
328+ return ;
329+ }
330+ const pending = this . pendingPulls . shift ( ) ;
331+ if ( status === - 1 || ( status === 0 && buffer === undefined ) ) {
332+ // EOS
333+ c . close ( ) ;
334+ pending . resolve ( ) ;
335+ return ;
336+ } else if ( status < 0 ) {
337+ const error = errnoException ( status , 'read' ) ;
338+ c . error ( error ) ;
339+ pending . reject ( error ) ;
340+ return ;
341+ }
342+ c . enqueue ( new Uint8Array ( buffer ) ) ;
343+ pending . resolve ( ) ;
344+ } ) ;
345+ return promise ;
346+ } ,
347+ cancel ( reason ) {
348+ // Reject any currently pending pulls here.
349+ for ( const pending of this . pendingPulls ) {
350+ pending . reject ( reason ) ;
339351 }
352+ this . pendingPulls = [ ] ;
340353 }
341- } ) ;
354+ // We set the highWaterMark to 0 because we do not want the stream to
355+ // start reading immediately on creation. We want it to wait until read
356+ // is called.
357+ } , new CountQueuingStrategy ( { highWaterMark : 0 } ) ) ;
342358 }
343359}
344360
@@ -406,10 +422,16 @@ function resolveObjectURL(url) {
406422 }
407423}
408424
425+ function createBlobFromFileHandle ( handle ) {
426+ const [ blob , length ] = _createBlobFromFileHandle ( handle ) ;
427+ return createBlob ( blob , length ) ;
428+ }
429+
409430module . exports = {
410431 Blob,
411432 ClonedBlob,
412433 createBlob,
434+ createBlobFromFileHandle,
413435 isBlob,
414436 kHandle,
415437 resolveObjectURL,
0 commit comments