@@ -193,33 +193,33 @@ struct ReadBytesResult {
193193}
194194
195195#if os(Windows)
196+ @lifetime ( pBuffer: copy pBuffer)
196197private func read( from hFile: HANDLE , at path: PathOrURL ,
197- into pBuffer: UnsafeMutableRawPointer , length dwLength : Int ,
198+ into pBuffer: inout OutputRawSpan ,
198199 chunkSize dwChunk: Int = 4096 , progress bProgress: Bool )
199- throws -> Int {
200- var pBuffer = pBuffer
201- let progress = bProgress && Progress . current ( ) != nil ? Progress ( totalUnitCount: Int64 ( dwLength) ) : nil
200+ throws {
201+ let progress = bProgress && Progress . current ( ) != nil ? Progress ( totalUnitCount: Int64 ( pBuffer. freeCapacity) ) : nil
202202
203- var dwBytesRemaining : DWORD = DWORD ( dwLength)
204- while dwBytesRemaining > 0 {
203+ while !pBuffer. isFull {
205204 if let progress, progress. isCancelled {
206205 throw CocoaError ( . userCancelled)
207206 }
208207
209208 let dwBytesToRead : DWORD =
210- DWORD ( clamping: DWORD ( min ( DWORD ( dwChunk) , dwBytesRemaining) ) )
209+ DWORD ( clamping: min ( dwChunk, pBuffer. freeCapacity) )
210+
211211 var dwBytesRead : DWORD = 0
212- if !ReadFile( hFile, pBuffer, dwBytesToRead, & dwBytesRead, nil ) {
213- throw CocoaError . errorWithFilePath ( path, win32: GetLastError ( ) , reading: true )
212+ try pBuffer. withUnsafeMutableBytes { bytes, initializedCount in
213+ if !ReadFile( hFile, bytes. baseAddress!. advanced ( by: initializedCount) , dwBytesToRead, & dwBytesRead, nil ) {
214+ throw CocoaError . errorWithFilePath ( path, win32: GetLastError ( ) , reading: true )
215+ }
216+ initializedCount += Int ( dwBytesRead)
214217 }
215- dwBytesRemaining -= DWORD ( clamping: dwBytesRead)
216- progress? . completedUnitCount = Int64 ( dwLength - Int( dwBytesRemaining) )
218+ progress? . completedUnitCount += Int64 ( dwBytesRead)
217219 if dwBytesRead < dwBytesToRead {
218220 break
219221 }
220- pBuffer = pBuffer. advanced ( by: Int ( dwBytesRead) )
221222 }
222- return dwLength - Int( dwBytesRemaining)
223223}
224224#endif
225225
@@ -283,18 +283,20 @@ internal func readBytesFromFile(path inPath: PathOrURL, reportProgress: Bool, ma
283283 }
284284 } ) )
285285 } else {
286- guard let pBuffer : UnsafeMutableRawPointer = malloc ( Int ( szFileSize) ) else {
286+ guard let ptr : UnsafeMutableRawPointer = malloc ( Int ( szFileSize) ) else {
287287 throw CocoaError . errorWithFilePath ( inPath, errno: ENOMEM, reading: true )
288288 }
289+ let buffer = UnsafeMutableRawBufferPointer ( start: ptr, count: Int ( szFileSize) )
290+ var outputSpan = OutputRawSpan ( buffer: buffer, initializedCount: 0 )
289291
290- localProgress? . becomeCurrent ( withPendingUnitCount: Int64 ( szFileSize ) )
292+ localProgress? . becomeCurrent ( withPendingUnitCount: Int64 ( outputSpan . freeCapacity ) )
291293 do {
292- let dwLength = try read ( from: hFile, at: inPath, into: pBuffer , length : Int ( szFileSize ) , progress: reportProgress)
294+ try read ( from: hFile, at: inPath, into: & outputSpan , progress: reportProgress)
293295 localProgress? . resignCurrent ( )
294- return ReadBytesResult ( bytes: pBuffer , length: dwLength , deallocator: . free)
296+ return ReadBytesResult ( bytes: ptr , length: outputSpan . finalize ( for : buffer ) , deallocator: . free)
295297 } catch {
296298 localProgress? . resignCurrent ( )
297- free ( pBuffer )
299+ free ( ptr )
298300 throw error
299301 }
300302 }
@@ -367,18 +369,21 @@ internal func readBytesFromFile(path inPath: PathOrURL, reportProgress: Bool, ma
367369 #if os(Linux) || os(Android)
368370 // Linux has some files that may report a size of 0 but actually have contents
369371 let chunkSize = 1024 * 4
370- var buffer = malloc ( chunkSize) !
372+ var ptr = malloc ( chunkSize) !
371373 var totalRead = 0
372374 while true {
373- let length = try readBytesFromFileDescriptor ( fd, path: inPath, buffer: buffer. advanced ( by: totalRead) , length: chunkSize, readUntilLength: false , reportProgress: false )
375+ let buffer = UnsafeMutableRawBufferPointer ( start: ptr, count: totalRead + chunkSize)
376+ var outputSpan = OutputRawSpan ( buffer: buffer, initializedCount: totalRead)
377+ try readBytesFromFileDescriptor ( fd, path: inPath, buffer: & outputSpan, readUntilLength: false , reportProgress: false )
374378
379+ let length = outputSpan. finalize ( for: buffer)
375380 totalRead += length
376381 if length != chunkSize {
377382 break
378383 }
379- buffer = realloc ( buffer , totalRead + chunkSize)
384+ ptr = realloc ( ptr , totalRead + chunkSize)
380385 }
381- result = ReadBytesResult ( bytes: buffer , length: totalRead, deallocator: . free)
386+ result = ReadBytesResult ( bytes: ptr , length: totalRead, deallocator: . free)
382387 #else
383388 result = ReadBytesResult ( bytes: nil , length: 0 , deallocator: nil )
384389 #endif
@@ -415,13 +420,15 @@ internal func readBytesFromFile(path inPath: PathOrURL, reportProgress: Bool, ma
415420 guard let bytes = malloc ( Int ( fileSize) ) else {
416421 throw CocoaError . errorWithFilePath ( inPath, errno: ENOMEM, reading: true )
417422 }
423+ let buffer = UnsafeMutableRawBufferPointer ( start: bytes, count: Int ( fileSize) )
424+ var outputSpan = OutputRawSpan ( buffer: buffer, initializedCount: 0 )
418425
419426 localProgress? . becomeCurrent ( withPendingUnitCount: Int64 ( fileSize) )
420427 do {
421- let length = try readBytesFromFileDescriptor ( fd, path: inPath, buffer: bytes , length : fileSize , reportProgress: reportProgress)
428+ try readBytesFromFileDescriptor ( fd, path: inPath, buffer: & outputSpan , reportProgress: reportProgress)
422429 localProgress? . resignCurrent ( )
423430
424- result = ReadBytesResult ( bytes: bytes, length: length , deallocator: . free)
431+ result = ReadBytesResult ( bytes: bytes, length: outputSpan . finalize ( for : buffer ) , deallocator: . free)
425432 } catch {
426433 localProgress? . resignCurrent ( )
427434 free ( bytes)
@@ -440,25 +447,24 @@ internal func readBytesFromFile(path inPath: PathOrURL, reportProgress: Bool, ma
440447/// Read data from a file descriptor.
441448/// Takes an `Int` size and returns an `Int` to match `Data`'s count. If we are going to read more than Int.max, throws - because we won't be able to store it in `Data`.
442449/// If `readUntilLength` is `false`, then we will end the read if we receive less than `length` bytes. This can be used to read from something like a socket, where the `length` simply represents the maximum size you can read at once.
443- private func readBytesFromFileDescriptor( _ fd: Int32 , path: PathOrURL , buffer inBuffer: UnsafeMutableRawPointer , length: Int , readUntilLength: Bool = true , reportProgress: Bool ) throws -> Int {
444- var buffer = inBuffer
450+ private func readBytesFromFileDescriptor( _ fd: Int32 , path: PathOrURL , buffer inBuffer: inout OutputRawSpan , readUntilLength: Bool = true , reportProgress: Bool ) throws {
445451 // If chunkSize (8-byte value) is more than blksize_t.max (4 byte value), then use the 4 byte max and chunk
446452
447453 let preferredChunkSize : size_t
448454 let localProgress : Progress ?
455+ let length = inBuffer. freeCapacity
449456
450457 if Progress . current ( ) != nil && reportProgress {
451- localProgress = Progress ( totalUnitCount: Int64 ( length ) )
458+ localProgress = Progress ( totalUnitCount: Int64 ( inBuffer . freeCapacity ) )
452459 // To report progress, we have to try reading in smaller chunks than the whole file. Aim for about 1% increments.
453- preferredChunkSize = max ( length / 100 , 1024 * 4 )
460+ preferredChunkSize = max ( inBuffer . freeCapacity / 100 , 1024 * 4 )
454461 } else {
455462 localProgress = nil
456463 // Get it all in one go, if possible
457- preferredChunkSize = length
464+ preferredChunkSize = inBuffer . freeCapacity
458465 }
459466
460- var numBytesRemaining = length
461- while numBytesRemaining > 0 {
467+ while !inBuffer. isFull {
462468 if let localProgress, localProgress. isCancelled {
463469 throw CocoaError ( . userCancelled)
464470 }
@@ -467,22 +473,27 @@ private func readBytesFromFileDescriptor(_ fd: Int32, path: PathOrURL, buffer in
467473 var numBytesRequested = CUnsignedInt ( clamping: min ( preferredChunkSize, Int ( CInt . max) ) )
468474
469475 // Furthermore, don't request more than the number of bytes remaining
470- if numBytesRequested > numBytesRemaining {
471- numBytesRequested = CUnsignedInt ( clamping: min ( numBytesRemaining , Int ( CInt . max) ) )
476+ if numBytesRequested > inBuffer . freeCapacity {
477+ numBytesRequested = CUnsignedInt ( clamping: min ( inBuffer . freeCapacity , Int ( CInt . max) ) )
472478 }
473479
474- var numBytesRead : CInt
480+ var numBytesRead : CInt = 0
475481 repeat {
476482 if let localProgress, localProgress. isCancelled {
477483 throw CocoaError ( . userCancelled)
478484 }
479485
480486 // read takes an Int-sized argument, which will always be at least the size of Int32.
487+ inBuffer. withUnsafeMutableBytes { buffer, initializedCount in
481488#if os(Windows)
482- numBytesRead = _read ( fd, buffer, numBytesRequested)
489+ numBytesRead = _read ( fd, buffer. baseAddress! . advanced ( by : initializedCount ) , numBytesRequested)
483490#else
484- numBytesRead = CInt ( read ( fd, buffer, Int ( numBytesRequested) ) )
491+ numBytesRead = CInt ( read ( fd, buffer. baseAddress! . advanced ( by : initializedCount ) , Int ( numBytesRequested) ) )
485492#endif
493+ if numBytesRead >= 0 {
494+ initializedCount += Int ( clamping: numBytesRead)
495+ }
496+ }
486497 } while numBytesRead < 0 && errno == EINTR
487498
488499 if numBytesRead < 0 {
@@ -495,23 +506,14 @@ private func readBytesFromFileDescriptor(_ fd: Int32, path: PathOrURL, buffer in
495506 break
496507 } else {
497508 // Partial read
498- numBytesRemaining -= Int ( clamping: numBytesRead)
499- if numBytesRemaining < 0 {
500- // Just in case; we do not want to have a negative amount of bytes remaining. We will just assume that is the end.
501- numBytesRemaining = 0
502- }
503- localProgress? . completedUnitCount = Int64 ( length - numBytesRemaining)
509+ localProgress? . completedUnitCount = Int64 ( length - inBuffer. freeCapacity)
504510
505511 // The `readUntilLength` argument controls if we should end early when `read` returns less than the amount requested, or if we should continue to loop until we have reached `length` bytes.
506512 if !readUntilLength && numBytesRead < numBytesRequested {
507513 break
508514 }
509-
510- buffer = buffer. advanced ( by: numericCast ( numBytesRead) )
511515 }
512516 }
513-
514- return length - numBytesRemaining
515517}
516518
517519@available ( macOS 10 . 10 , iOS 8 . 0 , watchOS 2 . 0 , tvOS 9 . 0 , * )
0 commit comments