Skip to content

Commit

Permalink
Add data source (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickael-menu authored Dec 18, 2024
1 parent c9003f4 commit 2223735
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 152 deletions.
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

0 comments on commit 2223735

Please sign in to comment.