@@ -111,6 +111,8 @@ const {
111111 handleErrorFromBinding,
112112 preprocessSymlinkDestination,
113113 Stats,
114+ getReadFileBuffer,
115+ getReadFileBufferByteLengthName,
114116 getStatFsFromBinding,
115117 getStatsFromBinding,
116118 realpathCacheKey,
@@ -123,6 +125,7 @@ const {
123125 validateOffsetLengthWrite,
124126 validatePath,
125127 validatePosition,
128+ validateReadFileBufferOptions,
126129 validateRmOptions,
127130 validateRmOptionsSync,
128131 validateRmdirOptions,
@@ -319,13 +322,7 @@ function readFileAfterStat(err, stats) {
319322 }
320323
321324 try {
322- if ( size === 0 ) {
323- // TODO(BridgeAR): If an encoding is set, use the StringDecoder to concat
324- // the result and reuse the buffer instead of allocating a new one.
325- context . buffers = [ ] ;
326- } else {
327- context . buffer = Buffer . allocUnsafeSlow ( size ) ;
328- }
325+ context . prepare ( ) ;
329326 } catch ( err ) {
330327 return context . close ( err ) ;
331328 }
@@ -358,8 +355,9 @@ function readFile(path, options, callback) {
358355 callback ||= options ;
359356 validateFunction ( callback , 'cb' ) ;
360357 options = getOptions ( options , { flag : 'r' } ) ;
358+ validateReadFileBufferOptions ( options ) ;
361359 ReadFileContext ??= require ( 'internal/fs/read/context' ) ;
362- const context = new ReadFileContext ( callback , options . encoding ) ;
360+ const context = new ReadFileContext ( callback , options ) ;
363361 context . isUserFd = isFd ( path ) ; // File descriptor ownership
364362
365363 if ( options . signal ) {
@@ -405,6 +403,18 @@ function tryCreateBuffer(size, fd, isUserFd) {
405403 return buffer ;
406404}
407405
406+ function tryGetReadFileBuffer ( options , size , fd , isUserFd ) {
407+ let threw = true ;
408+ let buffer ;
409+ try {
410+ buffer = getReadFileBuffer ( options , size ) ;
411+ threw = false ;
412+ } finally {
413+ if ( threw && ! isUserFd ) fs . closeSync ( fd ) ;
414+ }
415+ return buffer ;
416+ }
417+
408418function tryReadSync ( fd , isUserFd , buffer , pos , len ) {
409419 let threw = true ;
410420 let bytesRead ;
@@ -417,6 +427,36 @@ function tryReadSync(fd, isUserFd, buffer, pos, len) {
417427 return bytesRead ;
418428}
419429
430+ function tryReadSyncWithUserBuffer ( fd , isUserFd , buffer , byteLengthName ) {
431+ let pos = 0 ;
432+ let bytesRead = 0 ;
433+
434+ while ( pos < buffer . byteLength ) {
435+ bytesRead = tryReadSync ( fd , isUserFd , buffer , pos , buffer . byteLength - pos ) ;
436+ pos += bytesRead ;
437+
438+ if ( bytesRead === 0 ) {
439+ return pos ;
440+ }
441+ }
442+
443+ const extraBuffer = tryCreateBuffer ( 1 , fd , isUserFd ) ;
444+ bytesRead = tryReadSync ( fd , isUserFd , extraBuffer , 0 , 1 ) ;
445+
446+ if ( bytesRead !== 0 ) {
447+ if ( ! isUserFd ) {
448+ fs . closeSync ( fd ) ;
449+ }
450+ throw new ERR_INVALID_ARG_VALUE (
451+ byteLengthName ,
452+ buffer . byteLength ,
453+ 'is too small to contain the entire file' ,
454+ ) ;
455+ }
456+
457+ return pos ;
458+ }
459+
420460/**
421461 * Synchronously reads the entire contents of a file.
422462 * @param {string | Buffer | URL | number } path
@@ -428,8 +468,12 @@ function tryReadSync(fd, isUserFd, buffer, pos, len) {
428468 */
429469function readFileSync ( path , options ) {
430470 options = getOptions ( options , { flag : 'r' } ) ;
471+ validateReadFileBufferOptions ( options ) ;
472+ const hasUserBuffer =
473+ options . buffer !== undefined || options . getBuffer !== undefined ;
431474
432- if ( options . encoding === 'utf8' || options . encoding === 'utf-8' ) {
475+ if ( ( options . encoding === 'utf8' || options . encoding === 'utf-8' ) &&
476+ ! hasUserBuffer ) {
433477 if ( ! isInt32 ( path ) ) {
434478 path = getValidatedPath ( path ) ;
435479 }
@@ -445,15 +489,31 @@ function readFileSync(path, options) {
445489 let buffer ; // Single buffer with file data
446490 let buffers ; // List for when size is unknown
447491
448- if ( size === 0 ) {
492+ if ( hasUserBuffer ) {
493+ buffer = tryGetReadFileBuffer ( options , size , fd , isUserFd ) ;
494+ } else if ( size === 0 ) {
449495 buffers = [ ] ;
450496 } else {
451497 buffer = tryCreateBuffer ( size , fd , isUserFd ) ;
452498 }
453499
454500 let bytesRead ;
455501
456- if ( size !== 0 ) {
502+ if ( hasUserBuffer ) {
503+ if ( size !== 0 ) {
504+ do {
505+ bytesRead = tryReadSync ( fd , isUserFd , buffer , pos , size - pos ) ;
506+ pos += bytesRead ;
507+ } while ( bytesRead !== 0 && pos < size ) ;
508+ } else {
509+ pos = tryReadSyncWithUserBuffer (
510+ fd ,
511+ isUserFd ,
512+ buffer ,
513+ getReadFileBufferByteLengthName ( options ) ,
514+ ) ;
515+ }
516+ } else if ( size !== 0 ) {
457517 do {
458518 bytesRead = tryReadSync ( fd , isUserFd , buffer , pos , size - pos ) ;
459519 pos += bytesRead ;
@@ -474,7 +534,9 @@ function readFileSync(path, options) {
474534 if ( ! isUserFd )
475535 fs . closeSync ( fd ) ;
476536
477- if ( size === 0 ) {
537+ if ( hasUserBuffer ) {
538+ buffer = buffer . subarray ( 0 , pos ) ;
539+ } else if ( size === 0 ) {
478540 // Data was collected into the buffers list.
479541 buffer = Buffer . concat ( buffers , pos ) ;
480542 } else if ( pos < size ) {
0 commit comments