mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
Merge branch 'release-1.2.0' into develop
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
# Changelog
|
||||
v1.2.0
|
||||
----------------
|
||||
- Reimplemented Huffman Coding using a tree-like structure.
|
||||
- Now only DataWithPointer is used during the processing.
|
||||
|
||||
v1.1.2
|
||||
----------------
|
||||
- Fixed memory problem in Deflate.
|
||||
|
||||
@@ -58,7 +58,7 @@ Then run `carthage update`.
|
||||
Finally, drag and drop `SWCompression.framework` from `Carthage/Build` folder into the "Embedded Binaries" section on your targets' "General" tab.
|
||||
|
||||
##### Swift Package Manager
|
||||
Add to you package specification's dependecies `.Package(url: "https://github.com/tsolomko/SWCompression.git")`, for example like this:
|
||||
Add to you package dependecies `.Package(url: "https://github.com/tsolomko/SWCompression.git")`, for example like this:
|
||||
```swift
|
||||
import PackageDescription
|
||||
|
||||
@@ -79,7 +79,7 @@ If you'd like to decompress "deflated" data just use:
|
||||
let data = try! Data(contentsOf: URL(fileURLWithPath: "path/to/file"))
|
||||
let decompressedData = try? Deflate.decompress(compressedData: data)
|
||||
```
|
||||
**Note:** you should properly handle possible errors in loading data from file
|
||||
_Note:_ you should properly handle possible errors in loading data from file
|
||||
and decide yourself if you need any `Data.ReadingOptions`.
|
||||
|
||||
However, it is unlikely that you will encounter deflated data outside of any archive.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "SWCompression"
|
||||
s.version = "1.1.2"
|
||||
s.version = "1.2.0"
|
||||
s.summary = "Framework with implementations in Swift of different (de)compression algorithms"
|
||||
|
||||
s.description = <<-DESC
|
||||
@@ -26,7 +26,7 @@ Pod::Spec.new do |s|
|
||||
|
||||
s.subspec 'Common' do |sp|
|
||||
sp.public_header_files = 'Sources/Service/SWCompression.h'
|
||||
sp.source_files = "Sources/{DataWithPointer.swift,HuffmanTable.swift,HuffmanLength.swift,Protocols.swift,Extensions.swift,Service/*.swift,Service/*.h}"
|
||||
sp.source_files = "Sources/{DataWithPointer.swift,HuffmanTree.swift,HuffmanLength.swift,Protocols.swift,Extensions.swift,Service/*.swift,Service/*.h}"
|
||||
end
|
||||
|
||||
s.subspec 'Deflate' do |sp|
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
063DF1101DE1F07800F38082 /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1071DE1F07800F38082 /* HuffmanLength.swift */; };
|
||||
063DF1111DE1F07800F38082 /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1071DE1F07800F38082 /* HuffmanLength.swift */; };
|
||||
063DF1121DE1F07800F38082 /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1071DE1F07800F38082 /* HuffmanLength.swift */; };
|
||||
063DF1131DE1F07800F38082 /* HuffmanTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTable.swift */; };
|
||||
063DF1141DE1F07800F38082 /* HuffmanTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTable.swift */; };
|
||||
063DF1151DE1F07800F38082 /* HuffmanTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTable.swift */; };
|
||||
063DF1131DE1F07800F38082 /* HuffmanTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTree.swift */; };
|
||||
063DF1141DE1F07800F38082 /* HuffmanTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTree.swift */; };
|
||||
063DF1151DE1F07800F38082 /* HuffmanTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTree.swift */; };
|
||||
063DF1161DE1F07800F38082 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1091DE1F07800F38082 /* Protocols.swift */; };
|
||||
063DF1171DE1F07800F38082 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1091DE1F07800F38082 /* Protocols.swift */; };
|
||||
063DF1181DE1F07800F38082 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1091DE1F07800F38082 /* Protocols.swift */; };
|
||||
063DF1191DE1F2E200F38082 /* DataWithPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1051DE1F07800F38082 /* DataWithPointer.swift */; };
|
||||
063DF11A1DE1F2E200F38082 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1061DE1F07800F38082 /* Extensions.swift */; };
|
||||
063DF11B1DE1F2E200F38082 /* HuffmanLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1071DE1F07800F38082 /* HuffmanLength.swift */; };
|
||||
063DF11C1DE1F2E200F38082 /* HuffmanTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTable.swift */; };
|
||||
063DF11C1DE1F2E200F38082 /* HuffmanTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1081DE1F07800F38082 /* HuffmanTree.swift */; };
|
||||
063DF11D1DE1F2E200F38082 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063DF1091DE1F07800F38082 /* Protocols.swift */; };
|
||||
065569681DC65C2A00A47E66 /* GzipArchive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063364E21DC52979007E313F /* GzipArchive.swift */; };
|
||||
065569691DC65C2A00A47E66 /* ZlibArchive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064492581DC606D400F10981 /* ZlibArchive.swift */; };
|
||||
@@ -101,7 +101,7 @@
|
||||
063DF1051DE1F07800F38082 /* DataWithPointer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataWithPointer.swift; path = Sources/DataWithPointer.swift; sourceTree = SOURCE_ROOT; };
|
||||
063DF1061DE1F07800F38082 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Sources/Extensions.swift; sourceTree = SOURCE_ROOT; };
|
||||
063DF1071DE1F07800F38082 /* HuffmanLength.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HuffmanLength.swift; path = Sources/HuffmanLength.swift; sourceTree = SOURCE_ROOT; };
|
||||
063DF1081DE1F07800F38082 /* HuffmanTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HuffmanTable.swift; path = Sources/HuffmanTable.swift; sourceTree = SOURCE_ROOT; };
|
||||
063DF1081DE1F07800F38082 /* HuffmanTree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HuffmanTree.swift; path = Sources/HuffmanTree.swift; sourceTree = SOURCE_ROOT; };
|
||||
063DF1091DE1F07800F38082 /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Protocols.swift; path = Sources/Protocols.swift; sourceTree = SOURCE_ROOT; };
|
||||
064492581DC606D400F10981 /* ZlibArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZlibArchive.swift; sourceTree = "<group>"; };
|
||||
0655693F1DC659FF00A47E66 /* SWCompression.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SWCompression.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -197,7 +197,7 @@
|
||||
063DF1051DE1F07800F38082 /* DataWithPointer.swift */,
|
||||
063DF1061DE1F07800F38082 /* Extensions.swift */,
|
||||
063DF1071DE1F07800F38082 /* HuffmanLength.swift */,
|
||||
063DF1081DE1F07800F38082 /* HuffmanTable.swift */,
|
||||
063DF1081DE1F07800F38082 /* HuffmanTree.swift */,
|
||||
063DF1091DE1F07800F38082 /* Protocols.swift */,
|
||||
);
|
||||
path = Common;
|
||||
@@ -592,7 +592,7 @@
|
||||
files = (
|
||||
065569691DC65C2A00A47E66 /* ZlibArchive.swift in Sources */,
|
||||
063DF10E1DE1F07800F38082 /* Extensions.swift in Sources */,
|
||||
063DF1141DE1F07800F38082 /* HuffmanTable.swift in Sources */,
|
||||
063DF1141DE1F07800F38082 /* HuffmanTree.swift in Sources */,
|
||||
063DF1171DE1F07800F38082 /* Protocols.swift in Sources */,
|
||||
0655696A1DC65C2A00A47E66 /* Deflate.swift in Sources */,
|
||||
06FED40D1DD7717E0013DFB2 /* BZip2.swift in Sources */,
|
||||
@@ -609,7 +609,7 @@
|
||||
063DF1191DE1F2E200F38082 /* DataWithPointer.swift in Sources */,
|
||||
063DF11A1DE1F2E200F38082 /* Extensions.swift in Sources */,
|
||||
063DF11B1DE1F2E200F38082 /* HuffmanLength.swift in Sources */,
|
||||
063DF11C1DE1F2E200F38082 /* HuffmanTable.swift in Sources */,
|
||||
063DF11C1DE1F2E200F38082 /* HuffmanTree.swift in Sources */,
|
||||
063DF11D1DE1F2E200F38082 /* Protocols.swift in Sources */,
|
||||
065569701DC65C2B00A47E66 /* ZlibArchive.swift in Sources */,
|
||||
065569711DC65C2B00A47E66 /* Deflate.swift in Sources */,
|
||||
@@ -624,7 +624,7 @@
|
||||
files = (
|
||||
065569771DC65C2B00A47E66 /* ZlibArchive.swift in Sources */,
|
||||
063DF10F1DE1F07800F38082 /* Extensions.swift in Sources */,
|
||||
063DF1151DE1F07800F38082 /* HuffmanTable.swift in Sources */,
|
||||
063DF1151DE1F07800F38082 /* HuffmanTree.swift in Sources */,
|
||||
063DF1181DE1F07800F38082 /* Protocols.swift in Sources */,
|
||||
065569781DC65C2B00A47E66 /* Deflate.swift in Sources */,
|
||||
06FED40F1DD7717E0013DFB2 /* BZip2.swift in Sources */,
|
||||
@@ -640,7 +640,7 @@
|
||||
files = (
|
||||
06A393391DE0709300182E12 /* GzipArchive.swift in Sources */,
|
||||
063DF10D1DE1F07800F38082 /* Extensions.swift in Sources */,
|
||||
063DF1131DE1F07800F38082 /* HuffmanTable.swift in Sources */,
|
||||
063DF1131DE1F07800F38082 /* HuffmanTree.swift in Sources */,
|
||||
063DF1161DE1F07800F38082 /* Protocols.swift in Sources */,
|
||||
06A3933A1DE0709300182E12 /* ZlibArchive.swift in Sources */,
|
||||
06A3933B1DE0709300182E12 /* Deflate.swift in Sources */,
|
||||
|
||||
+6
-6
@@ -149,8 +149,8 @@ public class BZip2: DecompressionAlgorithm {
|
||||
let selectorsList = try computeSelectorsList()
|
||||
let symbolsInUse = used.filter { $0 }.count + 2
|
||||
|
||||
func computeTables() throws -> [HuffmanTable] {
|
||||
var tables: [HuffmanTable] = []
|
||||
func computeTables() throws -> [HuffmanTree] {
|
||||
var tables: [HuffmanTree] = []
|
||||
for _ in 0..<huffmanGroups {
|
||||
var length = data.intFromBits(count: 5)
|
||||
var lengths: [Int] = []
|
||||
@@ -161,7 +161,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
}
|
||||
lengths.append(length)
|
||||
}
|
||||
let codes = HuffmanTable(lengthsToOrder: lengths)
|
||||
let codes = HuffmanTree(lengthsToOrder: lengths)
|
||||
tables.append(codes)
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
var repeat_ = 0
|
||||
var repeatPower = 0
|
||||
var buffer: [UInt8] = []
|
||||
var t: HuffmanTable?
|
||||
var t: HuffmanTree?
|
||||
|
||||
while true {
|
||||
decoded -= 1
|
||||
@@ -196,10 +196,10 @@ public class BZip2: DecompressionAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
guard let symbolLength = t?.findNextSymbol(in: data) else {
|
||||
guard let symbol = t?.findNextSymbol(in: data) else {
|
||||
throw BZip2Error.SymbolNotFound
|
||||
}
|
||||
let symbol = symbolLength.code
|
||||
|
||||
if symbol == 1 || symbol == 0 {
|
||||
if repeat_ == 0 {
|
||||
repeatPower = 1
|
||||
|
||||
@@ -110,15 +110,4 @@ class DataWithPointer {
|
||||
self.bitShift = 0
|
||||
}
|
||||
|
||||
func rewind(bitsCount: Int) {
|
||||
let amountOfBytes = (bitsCount - self.bitShift) / 8 + 1
|
||||
self.index -= amountOfBytes
|
||||
self.bitShift = 8 - (bitsCount - self.bitShift) % 8
|
||||
|
||||
if self.bitShift == 8 {
|
||||
self.index += 1
|
||||
self.bitShift = 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-18
@@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// TODO: Rename HuffmanTableError to HuffmanTreeError.
|
||||
/**
|
||||
Error happened during deflate decompression.
|
||||
It may indicate that either the data is damaged or it might not be compressed with DEFLATE at all.
|
||||
@@ -77,9 +78,9 @@ public class Deflate: DecompressionAlgorithm {
|
||||
// There are two alphabets in use and each one needs a Huffman table.
|
||||
|
||||
/// Huffman table for literal bytes.
|
||||
var mainLiterals: HuffmanTable
|
||||
var mainLiterals: HuffmanTree
|
||||
/// Huffman table for bytes alphabet and alphabet of pairs (length, backward distance).
|
||||
var mainDistances: HuffmanTable
|
||||
var mainDistances: HuffmanTree
|
||||
|
||||
if blockType == [0, 1] { // Static Huffman
|
||||
// In this case codes for literals and distances are fixed.
|
||||
@@ -87,8 +88,8 @@ public class Deflate: DecompressionAlgorithm {
|
||||
let staticHuffmanBootstrap = [[0, 8], [144, 9], [256, 7], [280, 8], [288, -1]]
|
||||
let staticHuffmanLengthsBootstrap = [[0, 5], [32, -1]]
|
||||
// Initialize tables from these bootstraps.
|
||||
mainLiterals = HuffmanTable(bootstrap: staticHuffmanBootstrap)
|
||||
mainDistances = HuffmanTable(bootstrap: staticHuffmanLengthsBootstrap)
|
||||
mainLiterals = HuffmanTree(bootstrap: staticHuffmanBootstrap)
|
||||
mainDistances = HuffmanTree(bootstrap: staticHuffmanLengthsBootstrap)
|
||||
} else { // Dynamic Huffman
|
||||
// In this case there are Huffman codes for two alphabets in data right after block header.
|
||||
// Each code defined by a sequence of code lengths (which are compressed themselves with Huffman).
|
||||
@@ -101,23 +102,23 @@ public class Deflate: DecompressionAlgorithm {
|
||||
let codeLengthsLength = pointerData.intFromBits(count: 4) + 4
|
||||
|
||||
// Read code lengths codes.
|
||||
// Moreover, they are stored in a very specific order (defined by HuffmanTable.Constants.codeLengthOrders).
|
||||
// Moreover, they are stored in a very specific order (defined by HuffmanTree.Constants.codeLengthOrders).
|
||||
var lengthsForOrder = Array(repeating: 0, count: 19)
|
||||
for i in 0..<codeLengthsLength {
|
||||
lengthsForOrder[HuffmanTable.Constants.codeLengthOrders[i]] = pointerData.intFromBits(count: 3)
|
||||
lengthsForOrder[HuffmanTree.Constants.codeLengthOrders[i]] = pointerData.intFromBits(count: 3)
|
||||
}
|
||||
/// Huffman table for code lengths. Each code in the main alphabets is coded with this table.
|
||||
let dynamicCodes = HuffmanTable(lengthsToOrder: lengthsForOrder)
|
||||
let dynamicCodes = HuffmanTree(lengthsToOrder: lengthsForOrder)
|
||||
|
||||
// Now we need to read codes (code lengths) for two main alphabets (tables).
|
||||
var codeLengths: [Int] = []
|
||||
var n = 0
|
||||
while n < (literals + distances) {
|
||||
// Finding next Huffman table's symbol in data.
|
||||
guard let dynamicCodeHuffmanLength = dynamicCodes.findNextSymbol(in: pointerData) else {
|
||||
guard let symbol = dynamicCodes.findNextSymbol(in: pointerData) else {
|
||||
throw DeflateError.HuffmanTableError
|
||||
}
|
||||
let symbol = dynamicCodeHuffmanLength.code
|
||||
|
||||
let count: Int
|
||||
let what: Int
|
||||
if symbol >= 0 && symbol <= 15 {
|
||||
@@ -147,18 +148,17 @@ public class Deflate: DecompressionAlgorithm {
|
||||
}
|
||||
// We have read codeLengths for both tables at once.
|
||||
// Now we need to split them and make corresponding tables.
|
||||
mainLiterals = HuffmanTable(lengthsToOrder: Array(codeLengths[0..<literals]))
|
||||
mainDistances = HuffmanTable(lengthsToOrder: Array(codeLengths[literals..<codeLengths.count]))
|
||||
mainLiterals = HuffmanTree(lengthsToOrder: Array(codeLengths[0..<literals]))
|
||||
mainDistances = HuffmanTree(lengthsToOrder: Array(codeLengths[literals..<codeLengths.count]))
|
||||
}
|
||||
|
||||
// Main loop of data decompression.
|
||||
while true {
|
||||
// Read next symbol from data.
|
||||
// It will be either literal symbol or a length of (previous) data we will need to copy.
|
||||
guard let nextSymbolLength = mainLiterals.findNextSymbol(in: pointerData) else {
|
||||
guard let nextSymbol = mainLiterals.findNextSymbol(in: pointerData) else {
|
||||
throw DeflateError.HuffmanTableError
|
||||
}
|
||||
let nextSymbol = nextSymbolLength.code
|
||||
|
||||
if nextSymbol >= 0 && nextSymbol <= 255 {
|
||||
// It is a literal symbol so we add it straight to the output data.
|
||||
@@ -173,21 +173,20 @@ public class Deflate: DecompressionAlgorithm {
|
||||
let extraLength = (257 <= nextSymbol && nextSymbol <= 260) || nextSymbol == 285 ?
|
||||
0 : (((nextSymbol - 257) >> 2) - 1)
|
||||
// Actually, nextSymbol is not a starting value of length but an index for special array of starting values.
|
||||
let length = HuffmanTable.Constants.lengthBase[nextSymbol - 257] +
|
||||
let length = HuffmanTree.Constants.lengthBase[nextSymbol - 257] +
|
||||
pointerData.intFromBits(count: extraLength)
|
||||
|
||||
// Then we need to get distance code.
|
||||
guard let distanceLength = mainDistances.findNextSymbol(in: pointerData) else {
|
||||
guard let distanceCode = mainDistances.findNextSymbol(in: pointerData) else {
|
||||
throw DeflateError.HuffmanTableError
|
||||
}
|
||||
let distanceCode = distanceLength.code
|
||||
|
||||
if distanceCode >= 0 && distanceCode <= 29 {
|
||||
// Again, depending on the distanceCode's value there might be additional bits in data,
|
||||
// which we need to combine with distanceCode to get the actual distance.
|
||||
let extraDistance = distanceCode == 0 || distanceCode == 1 ? 0 : ((distanceCode >> 1) - 1)
|
||||
// And yes, distanceCode is not a first part of distance but rather an index for special array.
|
||||
let distance = HuffmanTable.Constants.distanceBase[distanceCode] +
|
||||
let distance = HuffmanTree.Constants.distanceBase[distanceCode] +
|
||||
pointerData.intFromBits(count: extraDistance)
|
||||
|
||||
// We should repeat last 'distance' amount of data.
|
||||
@@ -215,7 +214,7 @@ public class Deflate: DecompressionAlgorithm {
|
||||
throw DeflateError.UnknownBlockType
|
||||
}
|
||||
|
||||
// End the cycle if it was the last block
|
||||
// End the cycle if it was the last block.
|
||||
if isLastBit == 1 { break }
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,9 @@ import Foundation
|
||||
struct HuffmanLength: Comparable, CustomStringConvertible {
|
||||
let code: Int
|
||||
let bits: Int
|
||||
var symbol: Int? = nil
|
||||
|
||||
var description: String {
|
||||
return "(code: \(code), bits: \(bits), symbol: \(symbol)"
|
||||
return "(code: \(code), bits: \(bits)"
|
||||
}
|
||||
|
||||
static func < (left: HuffmanLength, right: HuffmanLength) -> Bool {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// HuffmanTable.swift
|
||||
// HuffmanTree.swift
|
||||
// SWCompression
|
||||
//
|
||||
// Created by Timofey Solomko on 24.10.16.
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
class HuffmanTable: CustomStringConvertible {
|
||||
class HuffmanTree: CustomStringConvertible {
|
||||
|
||||
struct Constants {
|
||||
static let codeLengthOrders: [Int] =
|
||||
@@ -23,18 +23,17 @@ class HuffmanTable: CustomStringConvertible {
|
||||
[1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||
8193, 12289, 16385, 24577]
|
||||
|
||||
}
|
||||
|
||||
var tree: [HuffmanLength?]
|
||||
let leafCount: Int
|
||||
|
||||
var description: String {
|
||||
return self.tree.reduce("HuffmanTree:\n") { $0.appending("\($1)\n") }
|
||||
}
|
||||
|
||||
init(bootstrap: [Array<Int>]) {
|
||||
// Fills the 'lengths' array with numerous HuffmanLengths from a 'bootstrap'
|
||||
private var tree: [Int?]
|
||||
private let leafCount: Int
|
||||
|
||||
init(bootstrap: [[Int]]) {
|
||||
// Fills the 'lengths' array with numerous HuffmanLengths from a 'bootstrap'.
|
||||
var lengths: [HuffmanLength] = []
|
||||
var start = bootstrap[0][0]
|
||||
var bits = bootstrap[0][1]
|
||||
@@ -43,16 +42,16 @@ class HuffmanTable: CustomStringConvertible {
|
||||
let endbits = pair[1]
|
||||
if bits > 0 {
|
||||
lengths.append(contentsOf:
|
||||
(start..<finish).map { HuffmanLength(code: $0, bits: bits, symbol: nil) })
|
||||
(start..<finish).map { HuffmanLength(code: $0, bits: bits) })
|
||||
}
|
||||
start = finish
|
||||
bits = endbits
|
||||
}
|
||||
// Sort the lengths' array to calculate symbols correctly
|
||||
// Sort the lengths' array to calculate symbols correctly.
|
||||
lengths.sort()
|
||||
|
||||
func reverse(bits: Int, in symbol: Int) -> Int {
|
||||
// Auxiliarly function, which generates reversed order of bits in a number
|
||||
// Auxiliarly function, which generates reversed order of bits in a number.
|
||||
var a = 1 << 0
|
||||
var b = 1 << (bits - 1)
|
||||
var z = 0
|
||||
@@ -65,36 +64,32 @@ class HuffmanTable: CustomStringConvertible {
|
||||
return z
|
||||
}
|
||||
|
||||
// Calculates symbols for each length in 'lengths' array
|
||||
// Calculate maximum amount of leaves possible in a tree.
|
||||
self.leafCount = Int(pow(Double(2), Double(lengths.last!.bits + 1)))
|
||||
// Create a tree (array, actually) with all leaves equal nil.
|
||||
self.tree = Array(repeating: nil, count: leafCount)
|
||||
|
||||
// Calculates symbols for each length in 'lengths' array and put them in the tree.
|
||||
var loopBits = -1
|
||||
var symbol = -1
|
||||
for index in 0..<lengths.count {
|
||||
for length in lengths {
|
||||
symbol += 1
|
||||
let length = lengths[index]
|
||||
// We sometimes need to make symbol to have length.bits bit length
|
||||
// We sometimes need to make symbol to have length.bits bit length.
|
||||
if length.bits != loopBits {
|
||||
symbol <<= (length.bits - loopBits)
|
||||
loopBits = length.bits
|
||||
}
|
||||
lengths[index].symbol = reverse(bits: loopBits, in: symbol)
|
||||
}
|
||||
|
||||
// Calculate maximum amount of leaves possible in a tree
|
||||
self.leafCount = Int(pow(Double(2), Double(lengths.last!.bits + 1)))
|
||||
// Create a tree (array, actually) with all leaves equal nil
|
||||
self.tree = Array(repeating: nil, count: leafCount)
|
||||
|
||||
// Populate necessary leaves with HuffmanLengths
|
||||
for length in lengths {
|
||||
var symbol = length.symbol!
|
||||
// Then we need to reverse bit order of the symbol.
|
||||
var treeCode = reverse(bits: loopBits, in: symbol)
|
||||
// Finally, we put it at its place in the tree.
|
||||
let bits = length.bits
|
||||
var index = 0
|
||||
for _ in 0..<bits {
|
||||
let bit = symbol & 1
|
||||
let bit = treeCode & 1
|
||||
index = bit == 0 ? 2 * index + 1 : 2 * index + 2
|
||||
symbol >>= 1
|
||||
treeCode >>= 1
|
||||
}
|
||||
self.tree[index] = length
|
||||
self.tree[index] = length.code
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,14 +101,14 @@ class HuffmanTable: CustomStringConvertible {
|
||||
self.init(bootstrap: (zip(range, addedLengths)).map { [$0, $1] })
|
||||
}
|
||||
|
||||
func findNextSymbol(in pointerData: DataWithPointer) -> HuffmanLength? {
|
||||
func findNextSymbol(in pointerData: DataWithPointer) -> Int? {
|
||||
var index = 0
|
||||
while true {
|
||||
let bit = pointerData.bit()
|
||||
index = bit == 0 ? 2 * index + 1 : 2 * index + 2
|
||||
guard index < self.leafCount else { return nil }
|
||||
if let length = self.tree[index] {
|
||||
return length
|
||||
if let code = self.tree[index] {
|
||||
return code
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.2</string>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.2</string>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.2</string>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.2</string>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ZlibArchive: Archive {
|
||||
let windowSize: Int
|
||||
let compressionLevel: CompressionLevel
|
||||
|
||||
public static func ==(lhs: ServiceInfo, rhs: ServiceInfo) -> Bool {
|
||||
public static func == (lhs: ServiceInfo, rhs: ServiceInfo) -> Bool {
|
||||
return lhs.compressionMethod == rhs.compressionMethod &&
|
||||
lhs.windowSize == rhs.windowSize &&
|
||||
lhs.compressionLevel == rhs.compressionLevel
|
||||
|
||||
@@ -73,7 +73,6 @@ class DeflateTests: XCTestCase {
|
||||
XCTAssertEqual(decompressedData, answerData, "Decompression was incorrect")
|
||||
}
|
||||
|
||||
|
||||
func testDeflate4() {
|
||||
let testName = "test4"
|
||||
guard let testData = try? Data(contentsOf: Constants.url(forTest: testName, withType: DeflateTests.testType)) else {
|
||||
@@ -94,7 +93,6 @@ class DeflateTests: XCTestCase {
|
||||
XCTAssertEqual(decompressedData, answerData, "Decompression was incorrect")
|
||||
}
|
||||
|
||||
|
||||
func testDeflate5() {
|
||||
let testName = "test5"
|
||||
guard let testData = try? Data(contentsOf: Constants.url(forTest: testName, withType: DeflateTests.testType)) else {
|
||||
@@ -115,7 +113,6 @@ class DeflateTests: XCTestCase {
|
||||
XCTAssertEqual(decompressedData, answerData, "Decompression was incorrect")
|
||||
}
|
||||
|
||||
|
||||
func testDeflate6() {
|
||||
let testName = "test6"
|
||||
guard let testData = try? Data(contentsOf: Constants.url(forTest: testName, withType: DeflateTests.testType)) else {
|
||||
@@ -136,7 +133,6 @@ class DeflateTests: XCTestCase {
|
||||
XCTAssertEqual(decompressedData, answerData, "Decompression was incorrect")
|
||||
}
|
||||
|
||||
|
||||
func testDeflate7() {
|
||||
let testName = "test7"
|
||||
guard let testData = try? Data(contentsOf: Constants.url(forTest: testName, withType: DeflateTests.testType)) else {
|
||||
|
||||
@@ -113,7 +113,6 @@ class GzipTests: XCTestCase {
|
||||
XCTAssertEqual(testServiceInfo, answerServiceInfo, "Incorrect service info")
|
||||
}
|
||||
|
||||
|
||||
func testGzip5() {
|
||||
let testName = "test5"
|
||||
guard let testData = try? Data(contentsOf: Constants.url(forTest: testName, withType: GzipTests.testType)) else {
|
||||
|
||||
Reference in New Issue
Block a user