mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
Gzip related code moved into separate file; added two protocols; extended serviceInfo structure.
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
061FCE311DBCC8BF0052F7BE /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061FCE301DBCC8BF0052F7BE /* Extensions.swift */; };
|
||||
063364DE1DC51D33007E313F /* empty.txt in Resources */ = {isa = PBXBuildFile; fileRef = 063364DC1DC51D33007E313F /* empty.txt */; };
|
||||
063364DF1DC51D33007E313F /* empty.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 063364DD1DC51D33007E313F /* empty.txt.gz */; };
|
||||
063364E11DC52970007E313F /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063364E01DC52970007E313F /* Protocols.swift */; };
|
||||
063364E31DC52979007E313F /* GzipArchive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063364E21DC52979007E313F /* GzipArchive.swift */; };
|
||||
06AC9B0D1DC1027900A5066B /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC9B0C1DC1027900A5066B /* HuffmanLength.swift */; };
|
||||
06BE1AD21DB410F100EE0F59 /* SWCompression.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06BE1AC81DB410F100EE0F59 /* SWCompression.framework */; };
|
||||
06BE1AD71DB410F100EE0F59 /* SWCompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BE1AD61DB410F100EE0F59 /* SWCompressionTests.swift */; };
|
||||
@@ -41,6 +43,8 @@
|
||||
061FCE301DBCC8BF0052F7BE /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||
063364DC1DC51D33007E313F /* empty.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = empty.txt; sourceTree = "<group>"; };
|
||||
063364DD1DC51D33007E313F /* empty.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = empty.txt.gz; sourceTree = "<group>"; };
|
||||
063364E01DC52970007E313F /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; };
|
||||
063364E21DC52979007E313F /* GzipArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GzipArchive.swift; sourceTree = "<group>"; };
|
||||
06AC9B0C1DC1027900A5066B /* HuffmanLength.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HuffmanLength.swift; sourceTree = "<group>"; };
|
||||
06BE1AC81DB410F100EE0F59 /* SWCompression.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWCompression.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
06BE1ACB1DB410F100EE0F59 /* SWCompression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWCompression.h; sourceTree = "<group>"; };
|
||||
@@ -106,12 +110,14 @@
|
||||
06BE1ACA1DB410F100EE0F59 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
06BE1ACB1DB410F100EE0F59 /* SWCompression.h */,
|
||||
063364E01DC52970007E313F /* Protocols.swift */,
|
||||
063364E21DC52979007E313F /* GzipArchive.swift */,
|
||||
061FCE251DBCC4BE0052F7BE /* Deflate.swift */,
|
||||
06E961C91DBD5FA1008C47F2 /* HuffmanTable.swift */,
|
||||
061FCE301DBCC8BF0052F7BE /* Extensions.swift */,
|
||||
06AC9B0C1DC1027900A5066B /* HuffmanLength.swift */,
|
||||
06BE1ACC1DB410F100EE0F59 /* Info.plist */,
|
||||
06BE1ACB1DB410F100EE0F59 /* SWCompression.h */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -246,9 +252,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
063364E11DC52970007E313F /* Protocols.swift in Sources */,
|
||||
06E961CA1DBD5FA1008C47F2 /* HuffmanTable.swift in Sources */,
|
||||
06AC9B0D1DC1027900A5066B /* HuffmanLength.swift in Sources */,
|
||||
061FCE261DBCC4BE0052F7BE /* Deflate.swift in Sources */,
|
||||
063364E31DC52979007E313F /* GzipArchive.swift in Sources */,
|
||||
061FCE311DBCC8BF0052F7BE /* Extensions.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
+3
-78
@@ -9,99 +9,24 @@
|
||||
import Foundation
|
||||
|
||||
public enum DeflateError: Error {
|
||||
case WrongMagic
|
||||
case UnknownCompressionMethod
|
||||
case WrongBlockLengths
|
||||
case HuffmanTableError
|
||||
case UnknownBlockType
|
||||
}
|
||||
|
||||
public class Deflate {
|
||||
|
||||
struct Flags {
|
||||
static let ftext: UInt8 = 0x01
|
||||
static let fhcrc: UInt8 = 0x02
|
||||
static let fextra: UInt8 = 0x04
|
||||
static let fname: UInt8 = 0x08
|
||||
static let fcomment: UInt8 = 0x10
|
||||
}
|
||||
|
||||
struct ServiceInfo {
|
||||
let magic: [UInt8]
|
||||
let method: UInt8
|
||||
let flags: UInt8
|
||||
let mtime: UInt64
|
||||
let extraFlags: UInt8
|
||||
let osType: UInt8
|
||||
}
|
||||
|
||||
public static func decompress(data: Data) throws -> Data {
|
||||
// First two bytes should be correct 'magic' bytes
|
||||
let magic = data.bytes(from: 0..<2)
|
||||
guard magic == [31, 139] else { throw DeflateError.WrongMagic }
|
||||
|
||||
// Third byte is a method of compression. Only type 8 (DEFLATE) compression is supported
|
||||
let method = data[2]
|
||||
guard method == 8 else { throw DeflateError.UnknownCompressionMethod }
|
||||
|
||||
// Next bytes present some service information
|
||||
let serviceInfo = ServiceInfo(magic: magic,
|
||||
method: method,
|
||||
flags: data[3],
|
||||
mtime: Data(data[4...7]).to(type: UInt64.self),
|
||||
extraFlags: data[8],
|
||||
osType: data[9])
|
||||
|
||||
var startPoint = 10 // Index in data of 'actual data'
|
||||
|
||||
// Some archives may contain extra fields
|
||||
if serviceInfo.flags & Flags.fextra != 0 {
|
||||
let xlen = Data(data[startPoint...startPoint + 1]).to(type: UInt16.self).toInt()
|
||||
startPoint += 2 + xlen
|
||||
}
|
||||
|
||||
// Some archives may contain source file name (this part ends with zero byte)
|
||||
if serviceInfo.flags & Flags.fname != 0 {
|
||||
let fnameStart = startPoint
|
||||
while true {
|
||||
let byte = data[startPoint]
|
||||
startPoint += 1
|
||||
guard byte != 0 else { break }
|
||||
}
|
||||
print(String(data: Data(data[fnameStart..<startPoint - 1]), encoding: .utf8) ??
|
||||
"Unable to get file name")
|
||||
}
|
||||
|
||||
// Some archives may contain comment (this part also ends with zero)
|
||||
if serviceInfo.flags & Flags.fcomment != 0 {
|
||||
let fcommentStart = startPoint
|
||||
while true {
|
||||
let byte = data[startPoint]
|
||||
startPoint += 1
|
||||
guard byte != 0 else { break }
|
||||
}
|
||||
print(String(data: Data(data[fcommentStart..<startPoint - 1]), encoding: .utf8) ??
|
||||
"Unable to get comment")
|
||||
}
|
||||
|
||||
// Some archives may contain 2-bytes checksum
|
||||
if serviceInfo.flags & Flags.fhcrc != 0 {
|
||||
let crc = Data(data[startPoint...startPoint + 1]).to(type: UInt16.self)
|
||||
startPoint += 2
|
||||
print("\(crc)")
|
||||
}
|
||||
public class Deflate: DecompressionAlgorithm {
|
||||
|
||||
public static func decompress(compressedData data: Data) throws -> Data {
|
||||
var out: [String] = []
|
||||
|
||||
// Current point of processing in data
|
||||
var index = startPoint
|
||||
var index = 0
|
||||
while true {
|
||||
// Is this a last block?
|
||||
let isLastBit = data[index][0]
|
||||
// Type of the current block
|
||||
let blockType = [UInt8](data[index][1..<3].reversed())
|
||||
var shift = 3
|
||||
print("blockType: \(convertToInt(uint8Array: blockType))")
|
||||
|
||||
if blockType == [0, 0] { // Uncompressed block
|
||||
// Get length of the uncompressed data
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// GzipArchive.swift
|
||||
// SWCompression
|
||||
//
|
||||
// Created by Timofey Solomko on 29.10.16.
|
||||
// Copyright © 2016 tsolomko. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum GzipError: Error {
|
||||
case WrongMagic
|
||||
case UnknownCompressionMethod
|
||||
}
|
||||
|
||||
public class GzipArchive: Archive {
|
||||
|
||||
struct Flags {
|
||||
static let ftext: UInt8 = 0x01
|
||||
static let fhcrc: UInt8 = 0x02
|
||||
static let fextra: UInt8 = 0x04
|
||||
static let fname: UInt8 = 0x08
|
||||
static let fcomment: UInt8 = 0x10
|
||||
}
|
||||
|
||||
struct ServiceInfo {
|
||||
let magic: [UInt8]
|
||||
let method: UInt8
|
||||
let flags: UInt8
|
||||
let mtime: UInt64
|
||||
let extraFlags: UInt8
|
||||
let osType: UInt8
|
||||
// Optional fields
|
||||
var startPoint: Int
|
||||
var fileName: String
|
||||
var comment: String
|
||||
var crc: UInt16
|
||||
}
|
||||
|
||||
static func serviceInfo(archiveData data: Data) throws -> ServiceInfo {
|
||||
// First two bytes should be correct 'magic' bytes
|
||||
let magic = data.bytes(from: 0..<2)
|
||||
guard magic == [31, 139] else { throw GzipError.WrongMagic }
|
||||
|
||||
// Third byte is a method of compression. Only type 8 (DEFLATE) compression is supported
|
||||
let method = data[2]
|
||||
guard method == 8 else { throw GzipError.UnknownCompressionMethod }
|
||||
|
||||
// Next bytes present some service information
|
||||
var serviceInfo = ServiceInfo(magic: magic,
|
||||
method: method,
|
||||
flags: data[3],
|
||||
mtime: Data(data[4...7]).to(type: UInt64.self),
|
||||
extraFlags: data[8],
|
||||
osType: data[9],
|
||||
startPoint: 10,
|
||||
fileName: "",
|
||||
comment: "",
|
||||
crc: 0)
|
||||
|
||||
// Some archives may contain extra fields
|
||||
if serviceInfo.flags & Flags.fextra != 0 {
|
||||
let xlen = Data(data[serviceInfo.startPoint...serviceInfo.startPoint + 1]).to(type: UInt16.self).toInt()
|
||||
serviceInfo.startPoint += 2 + xlen
|
||||
}
|
||||
|
||||
// Some archives may contain source file name (this part ends with zero byte)
|
||||
if serviceInfo.flags & Flags.fname != 0 {
|
||||
let fnameStart = serviceInfo.startPoint
|
||||
while true {
|
||||
let byte = data[serviceInfo.startPoint]
|
||||
serviceInfo.startPoint += 1
|
||||
guard byte != 0 else { break }
|
||||
}
|
||||
serviceInfo.fileName = String(data: Data(data[fnameStart..<serviceInfo.startPoint - 1]),
|
||||
encoding: .utf8) ?? ""
|
||||
}
|
||||
|
||||
// Some archives may contain comment (this part also ends with zero)
|
||||
if serviceInfo.flags & Flags.fcomment != 0 {
|
||||
let fcommentStart = serviceInfo.startPoint
|
||||
while true {
|
||||
let byte = data[serviceInfo.startPoint]
|
||||
serviceInfo.startPoint += 1
|
||||
guard byte != 0 else { break }
|
||||
}
|
||||
serviceInfo.comment = String(data: Data(data[fcommentStart..<serviceInfo.startPoint - 1]),
|
||||
encoding: .utf8) ?? ""
|
||||
}
|
||||
|
||||
// Some archives may contain 2-bytes checksum
|
||||
if serviceInfo.flags & Flags.fhcrc != 0 {
|
||||
serviceInfo.crc = Data(data[serviceInfo.startPoint...serviceInfo.startPoint + 1]).to(type: UInt16.self)
|
||||
serviceInfo.startPoint += 2
|
||||
}
|
||||
|
||||
return serviceInfo
|
||||
}
|
||||
|
||||
public static func unarchive(archiveData data: Data) throws -> Data {
|
||||
let info = try serviceInfo(archiveData: data)
|
||||
return try Deflate.decompress(compressedData: Data(data[info.startPoint..<data.count]))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Protocols.swift
|
||||
// SWCompression
|
||||
//
|
||||
// Created by Timofey Solomko on 29.10.16.
|
||||
// Copyright © 2016 tsolomko. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Archive {
|
||||
|
||||
static func unarchive(archiveData: Data) throws -> Data
|
||||
|
||||
}
|
||||
|
||||
public protocol DecompressionAlgorithm {
|
||||
|
||||
static func decompress(compressedData: Data) throws -> Data
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class SWCompressionTests: XCTestCase {
|
||||
func testHelloWorldFile() {
|
||||
let testData = try? Data(contentsOf: Constants.helloWorldArchivePath)
|
||||
XCTAssertNotNil(testData, "Failed to load test archive")
|
||||
let decompressedData = try? Deflate.decompress(data: testData!)
|
||||
let decompressedData = try? GzipArchive.unarchive(archiveData: testData!)
|
||||
XCTAssertNotNil(decompressedData, "Failed to decompress")
|
||||
guard decompressedData != nil else { return }
|
||||
let decompressedString = String(data: decompressedData!, encoding: .utf8)
|
||||
@@ -25,7 +25,7 @@ class SWCompressionTests: XCTestCase {
|
||||
func testSecondTestFile() {
|
||||
let testData = try? Data(contentsOf: Constants.secondTestArchivePath)
|
||||
XCTAssertNotNil(testData, "Failed to load test archive")
|
||||
let decompressedData = try? Deflate.decompress(data: testData!)
|
||||
let decompressedData = try? GzipArchive.unarchive(archiveData: testData!)
|
||||
XCTAssertNotNil(decompressedData, "Failed to decompress")
|
||||
guard decompressedData != nil else { return }
|
||||
let decompressedString = String(data: decompressedData!, encoding: .utf8)
|
||||
@@ -39,7 +39,7 @@ class SWCompressionTests: XCTestCase {
|
||||
func testEmptyFile() {
|
||||
let testData = try? Data(contentsOf: Constants.emptyFileArchivePath)
|
||||
XCTAssertNotNil(testData, "Failed to load test archive")
|
||||
let decompressedData = try? Deflate.decompress(data: testData!)
|
||||
let decompressedData = try? GzipArchive.unarchive(archiveData: testData!)
|
||||
XCTAssertNotNil(decompressedData, "Failed to decompress")
|
||||
guard decompressedData != nil else { return }
|
||||
let decompressedString = String(data: decompressedData!, encoding: .utf8)
|
||||
|
||||
Reference in New Issue
Block a user