Skip to content

Commit 35d896a

Browse files
authored
Enable reading files of indeterminate length on Linux (#739)
1 parent 2d517e8 commit 35d896a

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

Sources/FoundationEssentials/Data/Data+Reading.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,26 @@ internal func readBytesFromFile(path inPath: PathOrURL, reportProgress: Bool, ma
351351
let localProgress = (reportProgress && Progress.current() != nil) ? Progress(totalUnitCount: Int64(fileSize)) : nil
352352

353353
if fileSize == 0 {
354+
#if os(Linux)
355+
// Linux has some files that may report a size of 0 but actually have contents
356+
let chunkSize = 1024 * 4
357+
var buffer = malloc(chunkSize)!
358+
var totalRead = 0
359+
while true {
360+
let length = try readBytesFromFileDescriptor(fd, path: inPath, buffer: buffer.advanced(by: totalRead), length: chunkSize, readUntilLength: false, reportProgress: false)
361+
362+
totalRead += length
363+
if length != chunkSize {
364+
break
365+
}
366+
buffer = realloc(buffer, totalRead + chunkSize)
367+
}
368+
result = ReadBytesResult(bytes: buffer, length: totalRead, deallocator: .free)
369+
#else
370+
result = ReadBytesResult(bytes: nil, length: 0, deallocator: nil)
371+
#endif
354372
localProgress?.totalUnitCount = 1
355373
localProgress?.completedUnitCount = 1
356-
result = ReadBytesResult(bytes: nil, length: 0, deallocator: nil)
357374
} else if shouldMap {
358375
#if !NO_FILESYSTEM
359376
#if os(Android)

Tests/FoundationEssentialsTests/DataIOTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,17 @@ class DataIOTests : XCTestCase {
236236
XCTAssertNoThrow(try Data("Output to STDOUT\n".utf8).write(to: path))
237237
#endif
238238
}
239+
240+
func test_zeroSizeFile() throws {
241+
#if !os(Linux)
242+
throw XCTSkip("This test is only applicable on Linux")
243+
#else
244+
// Some files in /proc report a file size of 0 bytes via a stat call
245+
// Ensure that these files can still be read despite appearing to be empty
246+
let maps = try String(contentsOfFile: "/proc/self/maps", encoding: String._Encoding.utf8)
247+
XCTAssertFalse(maps.isEmpty)
248+
#endif
249+
}
239250

240251
// MARK: - String Path Tests
241252
func testStringDeletingLastPathComponent() {

0 commit comments

Comments
 (0)