Commit ec48965f authored by Keanu Pang's avatar Keanu Pang

HA-1053 Workaround fix for memory allocation in unzip process

parent 000fb6b0
...@@ -17,7 +17,7 @@ public enum ZipError: Error { ...@@ -17,7 +17,7 @@ public enum ZipError: Error {
case unzipFail case unzipFail
/// Zip fail /// Zip fail
case zipFail case zipFail
/// User readable description /// User readable description
public var description: String { public var description: String {
switch self { switch self {
...@@ -64,56 +64,56 @@ public struct ArchiveFile { ...@@ -64,56 +64,56 @@ public struct ArchiveFile {
/// Zip class /// Zip class
public class Zip { public class Zip {
/** /**
Set of vaild file extensions Set of vaild file extensions
*/ */
internal static var customFileExtensions: Set<String> = [] internal static var customFileExtensions: Set<String> = []
// MARK: Lifecycle // MARK: Lifecycle
/** /**
Init Init
- returns: Zip object - returns: Zip object
*/ */
public init () { public init () {
} }
// MARK: Unzip // MARK: Unzip
/** /**
Unzip file Unzip file
- parameter zipFilePath: Local file path of zipped file. NSURL. - parameter zipFilePath: Local file path of zipped file. NSURL.
- parameter destination: Local file path to unzip to. NSURL. - parameter destination: Local file path to unzip to. NSURL.
- parameter overwrite: Overwrite bool. - parameter overwrite: Overwrite bool.
- parameter password: Optional password if file is protected. - parameter password: Optional password if file is protected.
- parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1. - parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1.
- throws: Error if unzipping fails or if fail is not found. Can be printed with a description variable. - throws: Error if unzipping fails or if fail is not found. Can be printed with a description variable.
- notes: Supports implicit progress composition - notes: Supports implicit progress composition
*/ */
public class func unzipFile(_ zipFilePath: URL, destination: URL, overwrite: Bool, password: String?, progress: ((_ progress: Double) -> ())? = nil, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws { public class func unzipFile(_ zipFilePath: URL, destination: URL, overwrite: Bool, password: String?, progress: ((_ progress: Double) -> ())? = nil, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws {
// File manager // File manager
let fileManager = FileManager.default let fileManager = FileManager.default
// Check whether a zip file exists at path. // Check whether a zip file exists at path.
let path = zipFilePath.path let path = zipFilePath.path
if fileManager.fileExists(atPath: path) == false || fileExtensionIsInvalid(zipFilePath.pathExtension) { if fileManager.fileExists(atPath: path) == false || fileExtensionIsInvalid(zipFilePath.pathExtension) {
throw ZipError.fileNotFound throw ZipError.fileNotFound
} }
// Unzip set up // Unzip set up
var ret: Int32 = 0 var ret: Int32 = 0
var crc_ret: Int32 = 0 var crc_ret: Int32 = 0
let bufferSize: UInt32 = 4096 let bufferSize: UInt32 = 4096
var buffer = Array<CUnsignedChar>(repeating: 0, count: Int(bufferSize)) var buffer = Array<CUnsignedChar>(repeating: 0, count: Int(bufferSize))
// Progress handler set up // Progress handler set up
var totalSize: Double = 0.0 var totalSize: Double = 0.0
var currentPosition: Double = 0.0 var currentPosition: Double = 0.0
...@@ -121,12 +121,12 @@ public class Zip { ...@@ -121,12 +121,12 @@ public class Zip {
if let attributeFileSize = fileAttributes[FileAttributeKey.size] as? Double { if let attributeFileSize = fileAttributes[FileAttributeKey.size] as? Double {
totalSize += attributeFileSize totalSize += attributeFileSize
} }
let progressTracker = Progress(totalUnitCount: Int64(totalSize)) let progressTracker = Progress(totalUnitCount: Int64(totalSize))
progressTracker.isCancellable = false progressTracker.isCancellable = false
progressTracker.isPausable = false progressTracker.isPausable = false
progressTracker.kind = ProgressKind.file progressTracker.kind = ProgressKind.file
// Begin unzipping // Begin unzipping
let zip = unzOpen64(path) let zip = unzOpen64(path)
defer { defer {
...@@ -153,24 +153,25 @@ public class Zip { ...@@ -153,24 +153,25 @@ public class Zip {
throw ZipError.unzipFail throw ZipError.unzipFail
} }
currentPosition += Double(fileInfo.compressed_size) currentPosition += Double(fileInfo.compressed_size)
let fileNameSize = Int(fileInfo.size_filename) + 1 fileInfo.size_filename = 10240
let fileNameSize = 10240 // Int(fileInfo.size_filename) + 1
//let fileName = UnsafeMutablePointer<CChar>(allocatingCapacity: fileNameSize) //let fileName = UnsafeMutablePointer<CChar>(allocatingCapacity: fileNameSize)
let fileName = UnsafeMutablePointer<CChar>.allocate(capacity: fileNameSize) let fileName = UnsafeMutablePointer<CChar>.allocate(capacity: fileNameSize)
unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0) unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0)
fileName[Int(fileInfo.size_filename)] = 0 //fileName[Int(fileInfo.size_filename)] = 0
var pathString = String(cString: fileName) var pathString = String(cString: fileName)
guard pathString.count > 0 else { guard pathString.count > 0 else {
throw ZipError.unzipFail throw ZipError.unzipFail
} }
var isDirectory = false // var isDirectory = false
let fileInfoSizeFileName = Int(fileInfo.size_filename-1) // let fileInfoSizeFileName = Int(fileInfo.size_filename-1)
if (fileName[fileInfoSizeFileName] == "/".cString(using: String.Encoding.utf8)?.first || fileName[fileInfoSizeFileName] == "\\".cString(using: String.Encoding.utf8)?.first) { // if (fileName[fileInfoSizeFileName] == "/".cString(using: String.Encoding.utf8)?.first || fileName[fileInfoSizeFileName] == "\\".cString(using: String.Encoding.utf8)?.first) {
isDirectory = true; // isDirectory = true;
} // }
free(fileName) free(fileName)
if pathString.rangeOfCharacter(from: CharacterSet(charactersIn: "/\\")) != nil { if pathString.rangeOfCharacter(from: CharacterSet(charactersIn: "/\\")) != nil {
pathString = pathString.replacingOccurrences(of: "\\", with: "/") pathString = pathString.replacingOccurrences(of: "\\", with: "/")
...@@ -184,15 +185,15 @@ public class Zip { ...@@ -184,15 +185,15 @@ public class Zip {
FileAttributeKey.modificationDate : creationDate] FileAttributeKey.modificationDate : creationDate]
do { do {
if isDirectory { // if isDirectory {
try fileManager.createDirectory(atPath: fullPath, withIntermediateDirectories: true, attributes: directoryAttributes) // try fileManager.createDirectory(atPath: fullPath, withIntermediateDirectories: true, attributes: directoryAttributes)
} // }
else { // else {
let parentDirectory = (fullPath as NSString).deletingLastPathComponent let parentDirectory = (fullPath as NSString).deletingLastPathComponent
try fileManager.createDirectory(atPath: parentDirectory, withIntermediateDirectories: true, attributes: directoryAttributes) try fileManager.createDirectory(atPath: parentDirectory, withIntermediateDirectories: true, attributes: directoryAttributes)
} // }
} catch {} } catch {}
if fileManager.fileExists(atPath: fullPath) && !isDirectory && !overwrite { if fileManager.fileExists(atPath: fullPath) && !overwrite { // !isDirectory && !overwrite {
unzCloseCurrentFile(zip) unzCloseCurrentFile(zip)
ret = unzGoToNextFile(zip) ret = unzGoToNextFile(zip)
} }
...@@ -237,61 +238,61 @@ public class Zip { ...@@ -237,61 +238,61 @@ public class Zip {
} }
ret = unzGoToNextFile(zip) ret = unzGoToNextFile(zip)
// Update progress handler // Update progress handler
if let progressHandler = progress{ if let progressHandler = progress{
progressHandler((currentPosition/totalSize)) progressHandler((currentPosition/totalSize))
} }
if let fileHandler = fileOutputHandler, if let fileHandler = fileOutputHandler,
let encodedString = fullPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let encodedString = fullPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let fileUrl = URL(string: encodedString) { let fileUrl = URL(string: encodedString) {
fileHandler(fileUrl) fileHandler(fileUrl)
} }
progressTracker.completedUnitCount = Int64(currentPosition) progressTracker.completedUnitCount = Int64(currentPosition)
} while (ret == UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE) } while (ret == UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE)
// Completed. Update progress handler. // Completed. Update progress handler.
if let progressHandler = progress{ if let progressHandler = progress{
progressHandler(1.0) progressHandler(1.0)
} }
progressTracker.completedUnitCount = Int64(totalSize) progressTracker.completedUnitCount = Int64(totalSize)
} }
// MARK: Zip // MARK: Zip
/** /**
Zip files. Zip files.
- parameter paths: Array of NSURL filepaths. - parameter paths: Array of NSURL filepaths.
- parameter zipFilePath: Destination NSURL, should lead to a .zip filepath. - parameter zipFilePath: Destination NSURL, should lead to a .zip filepath.
- parameter password: Password string. Optional. - parameter password: Password string. Optional.
- parameter compression: Compression strategy - parameter compression: Compression strategy
- parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1. - parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1.
- throws: Error if zipping fails. - throws: Error if zipping fails.
- notes: Supports implicit progress composition - notes: Supports implicit progress composition
*/ */
public class func zipFiles(paths: [URL], zipFilePath: URL, password: String?, compression: ZipCompression = .DefaultCompression, progress: ((_ progress: Double) -> ())?) throws { public class func zipFiles(paths: [URL], zipFilePath: URL, password: String?, compression: ZipCompression = .DefaultCompression, progress: ((_ progress: Double) -> ())?) throws {
// File manager // File manager
let fileManager = FileManager.default let fileManager = FileManager.default
// Check whether a zip file exists at path. // Check whether a zip file exists at path.
let destinationPath = zipFilePath.path let destinationPath = zipFilePath.path
// Process zip paths // Process zip paths
let processedPaths = ZipUtilities().processZipPaths(paths) let processedPaths = ZipUtilities().processZipPaths(paths)
// Zip set up // Zip set up
let chunkSize: Int = 16384 let chunkSize: Int = 16384
// Progress handler set up // Progress handler set up
var currentPosition: Double = 0.0 var currentPosition: Double = 0.0
var totalSize: Double = 0.0 var totalSize: Double = 0.0
...@@ -307,12 +308,12 @@ public class Zip { ...@@ -307,12 +308,12 @@ public class Zip {
} }
catch {} catch {}
} }
let progressTracker = Progress(totalUnitCount: Int64(totalSize)) let progressTracker = Progress(totalUnitCount: Int64(totalSize))
progressTracker.isCancellable = false progressTracker.isCancellable = false
progressTracker.isPausable = false progressTracker.isPausable = false
progressTracker.kind = ProgressKind.file progressTracker.kind = ProgressKind.file
// Begin Zipping // Begin Zipping
let zip = zipOpen(destinationPath, APPEND_STATUS_CREATE) let zip = zipOpen(destinationPath, APPEND_STATUS_CREATE)
for path in processedPaths { for path in processedPaths {
...@@ -357,44 +358,44 @@ public class Zip { ...@@ -357,44 +358,44 @@ public class Zip {
length = fread(buffer, 1, chunkSize, input) length = fread(buffer, 1, chunkSize, input)
zipWriteInFileInZip(zip, buffer, UInt32(length)) zipWriteInFileInZip(zip, buffer, UInt32(length))
} }
// Update progress handler // Update progress handler
if let progressHandler = progress{ if let progressHandler = progress{
progressHandler((currentPosition/totalSize)) progressHandler((currentPosition/totalSize))
} }
progressTracker.completedUnitCount = Int64(currentPosition) progressTracker.completedUnitCount = Int64(currentPosition)
zipCloseFileInZip(zip) zipCloseFileInZip(zip)
free(buffer) free(buffer)
fclose(input) fclose(input)
} }
} }
zipClose(zip, nil) zipClose(zip, nil)
// Completed. Update progress handler. // Completed. Update progress handler.
if let progressHandler = progress{ if let progressHandler = progress{
progressHandler(1.0) progressHandler(1.0)
} }
progressTracker.completedUnitCount = Int64(totalSize) progressTracker.completedUnitCount = Int64(totalSize)
} }
/** /**
Zip data in memory. Zip data in memory.
- parameter archiveFiles:Array of Archive Files. - parameter archiveFiles:Array of Archive Files.
- parameter zipFilePath: Destination NSURL, should lead to a .zip filepath. - parameter zipFilePath: Destination NSURL, should lead to a .zip filepath.
- parameter password: Password string. Optional. - parameter password: Password string. Optional.
- parameter compression: Compression strategy - parameter compression: Compression strategy
- parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1. - parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1.
- throws: Error if zipping fails. - throws: Error if zipping fails.
- notes: Supports implicit progress composition - notes: Supports implicit progress composition
*/ */
public class func zipData(archiveFiles:[ArchiveFile], zipFilePath:URL, password: String?, compression: ZipCompression = .DefaultCompression, progress: ((_ progress: Double) -> ())?) throws { public class func zipData(archiveFiles:[ArchiveFile], zipFilePath:URL, password: String?, compression: ZipCompression = .DefaultCompression, progress: ((_ progress: Double) -> ())?) throws {
let destinationPath = zipFilePath.path let destinationPath = zipFilePath.path
// Progress handler set up // Progress handler set up
...@@ -475,51 +476,51 @@ public class Zip { ...@@ -475,51 +476,51 @@ public class Zip {
progressTracker.completedUnitCount = Int64(totalSize) progressTracker.completedUnitCount = Int64(totalSize)
} }
/** /**
Check if file extension is invalid. Check if file extension is invalid.
- parameter fileExtension: A file extension. - parameter fileExtension: A file extension.
- returns: false if the extension is a valid file extension, otherwise true. - returns: false if the extension is a valid file extension, otherwise true.
*/ */
internal class func fileExtensionIsInvalid(_ fileExtension: String?) -> Bool { internal class func fileExtensionIsInvalid(_ fileExtension: String?) -> Bool {
guard let fileExtension = fileExtension else { return true } guard let fileExtension = fileExtension else { return true }
return !isValidFileExtension(fileExtension) return !isValidFileExtension(fileExtension)
} }
/** /**
Add a file extension to the set of custom file extensions Add a file extension to the set of custom file extensions
- parameter fileExtension: A file extension. - parameter fileExtension: A file extension.
*/ */
public class func addCustomFileExtension(_ fileExtension: String) { public class func addCustomFileExtension(_ fileExtension: String) {
customFileExtensions.insert(fileExtension) customFileExtensions.insert(fileExtension)
} }
/** /**
Remove a file extension from the set of custom file extensions Remove a file extension from the set of custom file extensions
- parameter fileExtension: A file extension. - parameter fileExtension: A file extension.
*/ */
public class func removeCustomFileExtension(_ fileExtension: String) { public class func removeCustomFileExtension(_ fileExtension: String) {
customFileExtensions.remove(fileExtension) customFileExtensions.remove(fileExtension)
} }
/** /**
Check if a specific file extension is valid Check if a specific file extension is valid
- parameter fileExtension: A file extension. - parameter fileExtension: A file extension.
- returns: true if the extension valid, otherwise false. - returns: true if the extension valid, otherwise false.
*/ */
public class func isValidFileExtension(_ fileExtension: String) -> Bool { public class func isValidFileExtension(_ fileExtension: String) -> Bool {
let validFileExtensions: Set<String> = customFileExtensions.union(["zip", "cbz"]) let validFileExtensions: Set<String> = customFileExtensions.union(["zip", "cbz"])
return validFileExtensions.contains(fileExtension) return validFileExtensions.contains(fileExtension)
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment