@@ -20,6 +20,8 @@ import SystemPackage
2020
2121/// A class responsible for writing archives in various formats.
2222public final class ArchiveWriter {
23+ private static let chunkSize = 4 * 1024 * 1024
24+
2325 var underlying : OpaquePointer !
2426
2527 /// Initialize a new `ArchiveWriter` with the given configuration.
@@ -166,9 +168,16 @@ extension ArchiveWriter {
166168 fileprivate func writeData( data: UnsafeRawBufferPointer ) throws {
167169 guard let underlying = self . underlying else { throw ArchiveError . noUnderlyingArchive }
168170
169- let result = archive_write_data ( underlying, data. baseAddress, data. count)
170- guard result >= 0 else {
171- throw ArchiveError . unableToWriteData ( result)
171+ var offset = 0
172+ while offset < data. count {
173+ guard let baseAddress = data. baseAddress? . advanced ( by: offset) else {
174+ throw ArchiveError . invalidBaseAddressArchiveWrite
175+ }
176+ let result = archive_write_data ( underlying, baseAddress, data. count - offset)
177+ guard result > 0 else {
178+ throw ArchiveError . unableToWriteData ( result)
179+ }
180+ offset += Int ( result)
172181 }
173182 }
174183}
@@ -188,7 +197,7 @@ extension ArchiveWriter {
188197 var rootStat = stat ( )
189198 guard lstat ( dirPath. string, & rootStat) == 0 else {
190199 let err = POSIXErrorCode ( rawValue: errno) ?? . EINVAL
191- throw ArchiveError . failedToExtractArchive ( " lstat failed for ' \( dirPath) ': \( POSIXError ( err) ) " )
200+ throw ArchiveError . failedToCreateArchive ( " lstat failed for ' \( dirPath) ': \( POSIXError ( err) ) " )
192201 }
193202 let rootEntry = WriteEntry ( )
194203 rootEntry. path = " ./ "
@@ -215,7 +224,7 @@ extension ArchiveWriter {
215224 guard lstat ( fullPath. string, & statInfo) == 0 else {
216225 let errNo = errno
217226 let err = POSIXErrorCode ( rawValue: errNo) ?? . EINVAL
218- throw ArchiveError . failedToExtractArchive ( " lstat failed for ' \( fullPath) ': \( POSIXError ( err) ) " )
227+ throw ArchiveError . failedToCreateArchive ( " lstat failed for ' \( fullPath) ': \( POSIXError ( err) ) " )
219228 }
220229
221230 let mode = statInfo. st_mode
@@ -267,8 +276,28 @@ extension ArchiveWriter {
267276 entry. owner = uid
268277 entry. permissions = mode
269278 if type == . regular {
270- let data = try Data ( contentsOf: URL ( fileURLWithPath: fullPath. string) , options: . uncached)
271- try self . writeEntry ( entry: entry, data: data)
279+ let buf = UnsafeMutableRawBufferPointer . allocate ( byteCount: Self . chunkSize, alignment: 1 )
280+ guard let baseAddress = buf. baseAddress else {
281+ throw ArchiveError . failedToCreateArchive ( " cannot create temporary buffer of size \( Self . chunkSize) " )
282+ }
283+ defer { buf. deallocate ( ) }
284+ let fd = Foundation . open ( fullPath. string, O_RDONLY)
285+ guard fd >= 0 else {
286+ let err = POSIXErrorCode ( rawValue: errno) ?? . EINVAL
287+ throw ArchiveError . failedToCreateArchive ( " cannot open file \( fullPath. string) for reading: \( err) " )
288+ }
289+ defer { close ( fd) }
290+ try self . writeHeader ( entry: entry)
291+ while true {
292+ let n = read ( fd, baseAddress, Self . chunkSize)
293+ if n == 0 { break }
294+ if n < 0 {
295+ let err = POSIXErrorCode ( rawValue: errno) ?? . EIO
296+ throw ArchiveError . failedToCreateArchive ( " failed to read from file \( fullPath. string) : \( err) " )
297+ }
298+ try self . writeData ( data: UnsafeRawBufferPointer ( start: baseAddress, count: n) )
299+ }
300+ try self . finishEntry ( )
272301 } else {
273302 try self . writeEntry ( entry: entry, data: nil )
274303 }
0 commit comments