diff --git a/Sources/ZIPFoundation/Data+Serialization.swift b/Sources/ZIPFoundation/Data+Serialization.swift index bbe25ed7..3574275e 100644 --- a/Sources/ZIPFoundation/Data+Serialization.swift +++ b/Sources/ZIPFoundation/Data+Serialization.swift @@ -44,9 +44,14 @@ extension Data { static func consumePart(of size: Int, chunkSize: Int, skipCRC32: Bool = false, provider: Provider, consumer: Consumer) throws -> CRC32 { + var checksum = CRC32(0) + guard size > 0 else { + try consumer(Data()) + return checksum + } + let readInOneChunk = (size < chunkSize) var chunkSize = readInOneChunk ? size : chunkSize - var checksum = CRC32(0) var bytesRead = 0 while bytesRead < size { let remainingSize = size - bytesRead diff --git a/Tests/ZIPFoundationTests/Resources/testExtractUncompressedEmptyFile.zip b/Tests/ZIPFoundationTests/Resources/testExtractUncompressedEmptyFile.zip new file mode 100644 index 00000000..a5b74a1e Binary files /dev/null and b/Tests/ZIPFoundationTests/Resources/testExtractUncompressedEmptyFile.zip differ diff --git a/Tests/ZIPFoundationTests/ZIPFoundationReadingTests.swift b/Tests/ZIPFoundationTests/ZIPFoundationReadingTests.swift index 3111028e..f9ac3fe7 100644 --- a/Tests/ZIPFoundationTests/ZIPFoundationReadingTests.swift +++ b/Tests/ZIPFoundationTests/ZIPFoundationReadingTests.swift @@ -227,6 +227,24 @@ extension ZIPFoundationTests { XCTAssert(entriesRead == 0) } + func testExtractUncompressedEmptyFile() { + // We had a logic error, where completion handlers for empty entries were not called + // Ensure that this edge case works + var didCallCompletion = false + let archive = self.archive(for: #function, mode: .read) + guard let entry = archive["empty.txt"] else { XCTFail("Failed to extract entry."); return } + + do { + _ = try archive.extract(entry) { (data) in + XCTAssertEqual(data.count, 0) + didCallCompletion = true + } + } catch { + XCTFail("Unexpected error while trying to extract empty file of uncompressed archive.") + } + XCTAssert(didCallCompletion) + } + func testExtractUncompressedEntryCancelation() { let archive = self.archive(for: #function, mode: .read) guard let entry = archive["original"] else { XCTFail("Failed to extract entry."); return } diff --git a/Tests/ZIPFoundationTests/ZIPFoundationTests.swift b/Tests/ZIPFoundationTests/ZIPFoundationTests.swift index 094f4a2d..963fed24 100644 --- a/Tests/ZIPFoundationTests/ZIPFoundationTests.swift +++ b/Tests/ZIPFoundationTests/ZIPFoundationTests.swift @@ -286,6 +286,7 @@ extension ZIPFoundationTests { ("testExtractMSDOSArchive", testExtractMSDOSArchive), ("testExtractUncompressedDataDescriptorArchive", testExtractUncompressedDataDescriptorArchive), ("testExtractUncompressedFolderEntries", testExtractUncompressedFolderEntries), + ("testExtractUncompressedEmptyFile", testExtractUncompressedEmptyFile), ("testExtractZIP64ArchiveErrorConditions", testExtractZIP64ArchiveErrorConditions), ("testFileAttributeHelperMethods", testFileAttributeHelperMethods), ("testFilePermissionHelperMethods", testFilePermissionHelperMethods),