Skip to content

Abstract ZIP data access #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 33 additions & 52 deletions Sources/ZIPFoundation/Archive+BackingConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ import Foundation
extension Archive {

struct BackingConfiguration {
let file: FILEPointer
let dataSource: DataSource
let endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
let zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
#if swift(>=5.0)
let memoryFile: MemoryFile?

init(file: FILEPointer,
init(dataSource: DataSource,
endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord,
zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory? = nil,
memoryFile: MemoryFile? = nil) {
self.file = file
self.dataSource = dataSource
self.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
self.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
self.memoryFile = memoryFile
}
#else

init(file: FILEPointer,
init(dataSource: DataSource,
endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord,
zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?) {
self.file = file
self.dataSource = dataSource
self.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
self.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
}
Expand All @@ -42,19 +42,10 @@ extension Archive {

static func makeBackingConfiguration(for url: URL, mode: AccessMode) throws
-> BackingConfiguration {
let fileManager = FileManager()
let dataSource: DataSource
switch mode {
case .read:
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
guard let archiveFile = fopen(fileSystemRepresentation, "rb") else {
throw POSIXError(errno, path: url.path)
}
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
throw ArchiveError.missingEndOfCentralDirectoryRecord
}
return BackingConfiguration(file: archiveFile,
endOfCentralDirectoryRecord: eocdRecord,
zip64EndOfCentralDirectory: zip64EOCD)
dataSource = try FileDataSource(url: url, mode: .read)
case .create:
let endOfCentralDirectoryRecord = EndOfCentralDirectoryRecord(numberOfDisk: 0, numberOfDiskStart: 0,
totalNumberOfEntriesOnDisk: 0,
Expand All @@ -66,18 +57,19 @@ extension Archive {
try endOfCentralDirectoryRecord.data.write(to: url, options: .withoutOverwriting)
fallthrough
case .update:
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
guard let archiveFile = fopen(fileSystemRepresentation, "rb+") else {
throw POSIXError(errno, path: url.path)
}
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
throw ArchiveError.missingEndOfCentralDirectoryRecord
}
fseeko(archiveFile, 0, SEEK_SET)
return BackingConfiguration(file: archiveFile,
endOfCentralDirectoryRecord: eocdRecord,
zip64EndOfCentralDirectory: zip64EOCD)
dataSource = try FileDataSource(url: url, mode: .write)
}

guard let (eocdRecord, zip64EOCD) = try Archive.scanForEndOfCentralDirectoryRecord(in: dataSource) else {
throw ArchiveError.missingEndOfCentralDirectoryRecord
}
try dataSource.seek(to: 0)

return BackingConfiguration(
dataSource: dataSource,
endOfCentralDirectoryRecord: eocdRecord,
zip64EndOfCentralDirectory: zip64EOCD
)
}

#if swift(>=5.0)
Expand All @@ -93,40 +85,29 @@ extension Archive {
guard let archiveFile = memoryFile.open(mode: posixMode) else {
throw ArchiveError.unreadableArchive
}

let dataSource = FileDataSource(file: archiveFile)

switch mode {
case .read:
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
throw ArchiveError.missingEndOfCentralDirectoryRecord
}

return BackingConfiguration(file: archiveFile,
endOfCentralDirectoryRecord: eocdRecord,
zip64EndOfCentralDirectory: zip64EOCD,
memoryFile: memoryFile)
case .create:
if mode == .create {
let endOfCentralDirectoryRecord = EndOfCentralDirectoryRecord(numberOfDisk: 0, numberOfDiskStart: 0,
totalNumberOfEntriesOnDisk: 0,
totalNumberOfEntriesInCentralDirectory: 0,
sizeOfCentralDirectory: 0,
offsetToStartOfCentralDirectory: 0,
zipFileCommentLength: 0,
zipFileCommentData: Data())
_ = endOfCentralDirectoryRecord.data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
fwrite(buffer.baseAddress, buffer.count, 1, archiveFile) // Errors handled during read
}
fallthrough
case .update:
guard let (eocdRecord, zip64EOCD) = Archive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else {
throw ArchiveError.missingEndOfCentralDirectoryRecord
}

fseeko(archiveFile, 0, SEEK_SET)
return BackingConfiguration(file: archiveFile,
endOfCentralDirectoryRecord: eocdRecord,
zip64EndOfCentralDirectory: zip64EOCD,
memoryFile: memoryFile)
try dataSource.write(endOfCentralDirectoryRecord.data)
}

guard let (eocdRecord, zip64EOCD) = try Archive.scanForEndOfCentralDirectoryRecord(in: dataSource) else {
throw ArchiveError.missingEndOfCentralDirectoryRecord
}

try dataSource.seek(to: 0)
return BackingConfiguration(dataSource: dataSource,
endOfCentralDirectoryRecord: eocdRecord,
zip64EndOfCentralDirectory: zip64EOCD,
memoryFile: memoryFile)
}
#endif
}
24 changes: 14 additions & 10 deletions Sources/ZIPFoundation/Archive+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension Archive {
guard size <= .max else { throw ArchiveError.invalidEntrySize }
return try Data.consumePart(of: Int64(size), chunkSize: bufferSize, skipCRC32: skipCRC32,
provider: { (_, chunkSize) -> Data in
return try Data.readChunk(of: chunkSize, from: self.archiveFile)
return try self.dataSource.read(length: chunkSize)
}, consumer: { (data) in
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
try consumer(data)
Expand All @@ -34,7 +34,7 @@ extension Archive {
guard size <= .max else { throw ArchiveError.invalidEntrySize }
return try Data.decompress(size: Int64(size), bufferSize: bufferSize, skipCRC32: skipCRC32,
provider: { (_, chunkSize) -> Data in
return try Data.readChunk(of: chunkSize, from: self.archiveFile)
return try self.dataSource.read(length: chunkSize)
}, consumer: { (data) in
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
try consumer(data)
Expand Down Expand Up @@ -110,7 +110,7 @@ extension Archive {
fileNameLength: UInt16(fileNameData.count),
extraFieldLength: extraFieldLength, fileNameData: fileNameData,
extraFieldData: zip64ExtendedInformation?.data ?? Data())
_ = try Data.write(chunk: localFileHeader.data, to: self.archiveFile)
try writableDataSource.write(localFileHeader.data)
return localFileHeader
}

Expand Down Expand Up @@ -151,7 +151,7 @@ extension Archive {
relativeOffset: relativeOffsetOfCD,
extraField: (extraFieldLength,
zip64ExtendedInformation?.data ?? Data()))
_ = try Data.write(chunk: centralDirectory.data, to: self.archiveFile)
try writableDataSource.write(centralDirectory.data)
return centralDirectory
}

Expand Down Expand Up @@ -202,7 +202,7 @@ extension Archive {
numberOfEntriesInCentralDirectory: numberOfTotalEntriesForEOCD,
updatedSizeOfCentralDirectory: sizeOfCDForEOCD,
startOfCentralDirectory: offsetOfCDForEOCD)
_ = try Data.write(chunk: record.data, to: self.archiveFile)
try writableDataSource.write(record.data)
return (record, zip64EOCD)
}

Expand All @@ -216,7 +216,8 @@ extension Archive {
let readSize = (size - position) >= bufferSize ? bufferSize : Int(size - position)
let entryChunk = try provider(position, readSize)
checksum = entryChunk.crc32(checksum: checksum)
sizeWritten += Int64(try Data.write(chunk: entryChunk, to: self.archiveFile))
try writableDataSource.write(entryChunk)
sizeWritten += Int64(entryChunk.count)
position += Int64(bufferSize)
progress?.completedUnitCount = sizeWritten
}
Expand All @@ -226,7 +227,10 @@ extension Archive {
func writeCompressed(size: Int64, bufferSize: Int, progress: Progress? = nil,
provider: Provider) throws -> (sizeWritten: Int64, checksum: CRC32) {
var sizeWritten: Int64 = 0
let consumer: Consumer = { data in sizeWritten += Int64(try Data.write(chunk: data, to: self.archiveFile)) }
let consumer: Consumer = { data in
try self.writableDataSource.write(data)
sizeWritten += Int64(data.count)
}
let checksum = try Data.compress(size: size, bufferSize: bufferSize,
provider: { (position, size) -> Data in
if progress?.isCancelled == true { throw ArchiveError.cancelledOperation }
Expand All @@ -241,8 +245,8 @@ extension Archive {
// The reported size of a symlink is the number of characters in the path it points to.
let linkData = try provider(0, size)
let checksum = linkData.crc32(checksum: 0)
let sizeWritten = try Data.write(chunk: linkData, to: self.archiveFile)
return (sizeWritten, checksum)
try writableDataSource.write(linkData)
return (linkData.count, checksum)
}

func writeZIP64EOCD(totalNumberOfEntries: UInt64,
Expand Down Expand Up @@ -274,7 +278,7 @@ extension Archive {
let updatedLocator = ZIP64EndOfCentralDirectoryLocator(locator: zip64EOCD.locator,
offsetOfZIP64EOCDRecord: offsetOfEndOfCentralDirectory)
zip64EOCD = ZIP64EndOfCentralDirectory(record: updatedRecord, locator: updatedLocator)
_ = try Data.write(chunk: zip64EOCD.data, to: self.archiveFile)
try writableDataSource.write(zip64EOCD.data)
return zip64EOCD
}
}
10 changes: 5 additions & 5 deletions Sources/ZIPFoundation/Archive+Reading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ extension Archive {
var checksum = CRC32(0)
let localFileHeader = entry.localFileHeader
guard entry.dataOffset <= .max else { throw ArchiveError.invalidLocalHeaderDataOffset }
fseeko(self.archiveFile, off_t(entry.dataOffset), SEEK_SET)
try dataSource.seek(to: entry.dataOffset)
progress?.totalUnitCount = self.totalUnitCountForReading(entry)
switch entry.type {
case .file:
Expand All @@ -110,7 +110,7 @@ extension Archive {
case .symlink:
let localFileHeader = entry.localFileHeader
let size = Int(localFileHeader.compressedSize)
let data = try Data.readChunk(of: size, from: self.archiveFile)
let data = try dataSource.read(length: size)
checksum = data.crc32(checksum: 0)
try consumer(data)
progress?.completedUnitCount = self.totalUnitCountForReading(entry)
Expand Down Expand Up @@ -167,14 +167,14 @@ extension Archive {
bufferSize: Int,
consumer: Consumer
) throws {
fseeko(archiveFile, off_t(entry.dataOffset + range.lowerBound), SEEK_SET)
try dataSource.seek(to: entry.dataOffset + range.lowerBound)

_ = try Data.consumePart(
of: Int64(range.count),
chunkSize: bufferSize,
skipCRC32: true,
provider: { pos, chunkSize -> Data in
try Data.readChunk(of: chunkSize, from: self.archiveFile)
try dataSource.read(length: chunkSize)
},
consumer: consumer
)
Expand All @@ -191,7 +191,7 @@ extension Archive {
var bytesRead: UInt64 = 0

do {
fseeko(archiveFile, off_t(entry.dataOffset), SEEK_SET)
try dataSource.seek(to: entry.dataOffset)

_ = try readCompressed(
entry: entry,
Expand Down
Loading
Loading