mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
Some changes to make deflate/bzip2 faster.
This commit is contained in:
+8
-6
@@ -63,7 +63,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
var out: [UInt8] = []
|
||||
|
||||
/// Object with input data which supports convenient work with bit shifts.
|
||||
let pointerData = DataWithPointer(data: data, bitOrder: .straight)
|
||||
var pointerData = DataWithPointer(data: data, bitOrder: .straight)
|
||||
|
||||
let magic = pointerData.intFromBits(count: 16)
|
||||
guard magic == 0x425a else { throw BZip2Error.WrongMagic }
|
||||
@@ -84,7 +84,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
let _ = pointerData.intFromBits(count: 32)
|
||||
|
||||
if blockType == 0x314159265359 {
|
||||
try out.append(contentsOf: decode(data: pointerData))
|
||||
try out.append(contentsOf: decode(data: &pointerData))
|
||||
} else if blockType == 0x177245385090 {
|
||||
break
|
||||
} else {
|
||||
@@ -95,7 +95,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
return Data(bytes: out)
|
||||
}
|
||||
|
||||
private static func decode(data: DataWithPointer) throws -> [UInt8] {
|
||||
private static func decode(data: inout DataWithPointer) throws -> [UInt8] {
|
||||
let isRandomized = data.bit()
|
||||
guard isRandomized != 1 else { throw BZip2Error.RandomizedBlock }
|
||||
|
||||
@@ -163,7 +163,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
}
|
||||
lengths.append(length)
|
||||
}
|
||||
let codes = HuffmanTree(lengthsToOrder: lengths)
|
||||
let codes = HuffmanTree(lengthsToOrder: lengths, &data)
|
||||
tables.append(codes)
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public class BZip2: DecompressionAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
let symbol = t?.findNextSymbol(in: data)
|
||||
let symbol = t?.findNextSymbol()
|
||||
guard symbol != nil && symbol != -1 else { throw BZip2Error.SymbolNotFound }
|
||||
|
||||
if symbol == 1 || symbol == 0 {
|
||||
@@ -256,7 +256,9 @@ public class BZip2: DecompressionAlgorithm {
|
||||
while i < nt.count {
|
||||
if (i < nt.count - 4) && (nt[i] == nt[i + 1]) && (nt[i] == nt[i + 2]) && (nt[i] == nt[i + 3]) {
|
||||
let sCount = nt[i + 4].toInt() + 4
|
||||
out.append(contentsOf: Array(repeating: nt[i], count: sCount))
|
||||
for _ in 0..<sCount {
|
||||
out.append(nt[i])
|
||||
}
|
||||
i += 5
|
||||
} else {
|
||||
out.append(nt[i])
|
||||
|
||||
+17
-13
@@ -46,11 +46,11 @@ public class Deflate: DecompressionAlgorithm {
|
||||
*/
|
||||
public static func decompress(compressedData data: Data) throws -> Data {
|
||||
/// Object with input data which supports convenient work with bit shifts.
|
||||
let pointerData = DataWithPointer(data: data, bitOrder: .reversed)
|
||||
return try decompress(pointerData: pointerData)
|
||||
var pointerData = DataWithPointer(data: data, bitOrder: .reversed)
|
||||
return try decompress(pointerData: &pointerData)
|
||||
}
|
||||
|
||||
static func decompress(pointerData: DataWithPointer) throws -> Data {
|
||||
static func decompress(pointerData: inout DataWithPointer) throws -> Data {
|
||||
/// An array for storing output data
|
||||
var out: [UInt8] = []
|
||||
|
||||
@@ -95,8 +95,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 = HuffmanTree(bootstrap: staticHuffmanBootstrap)
|
||||
mainDistances = HuffmanTree(bootstrap: staticHuffmanLengthsBootstrap)
|
||||
mainLiterals = HuffmanTree(bootstrap: staticHuffmanBootstrap, &pointerData)
|
||||
mainDistances = HuffmanTree(bootstrap: staticHuffmanLengthsBootstrap, &pointerData)
|
||||
} 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).
|
||||
@@ -115,14 +115,14 @@ public class Deflate: DecompressionAlgorithm {
|
||||
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 = HuffmanTree(lengthsToOrder: lengthsForOrder)
|
||||
let dynamicCodes = HuffmanTree(lengthsToOrder: lengthsForOrder, &pointerData)
|
||||
|
||||
// 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.
|
||||
let symbol = dynamicCodes.findNextSymbol(in: pointerData)
|
||||
let symbol = dynamicCodes.findNextSymbol()
|
||||
guard symbol != -1 else { throw DeflateError.HuffmanTableError }
|
||||
|
||||
let count: Int
|
||||
@@ -154,15 +154,15 @@ public class Deflate: DecompressionAlgorithm {
|
||||
}
|
||||
// We have read codeLengths for both tables at once.
|
||||
// Now we need to split them and make corresponding tables.
|
||||
mainLiterals = HuffmanTree(lengthsToOrder: Array(codeLengths[0..<literals]))
|
||||
mainDistances = HuffmanTree(lengthsToOrder: Array(codeLengths[literals..<codeLengths.count]))
|
||||
mainLiterals = HuffmanTree(lengthsToOrder: Array(codeLengths[0..<literals]), &pointerData)
|
||||
mainDistances = HuffmanTree(lengthsToOrder: Array(codeLengths[literals..<codeLengths.count]), &pointerData)
|
||||
}
|
||||
|
||||
// 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.
|
||||
let nextSymbol = mainLiterals.findNextSymbol(in: pointerData)
|
||||
let nextSymbol = mainLiterals.findNextSymbol()
|
||||
guard nextSymbol != -1 else { throw DeflateError.HuffmanTableError }
|
||||
|
||||
if nextSymbol >= 0 && nextSymbol <= 255 {
|
||||
@@ -182,7 +182,7 @@ public class Deflate: DecompressionAlgorithm {
|
||||
pointerData.intFromBits(count: extraLength)
|
||||
|
||||
// Then we need to get distance code.
|
||||
let distanceCode = mainDistances.findNextSymbol(in: pointerData)
|
||||
let distanceCode = mainDistances.findNextSymbol()
|
||||
guard distanceCode != -1 else { throw DeflateError.HuffmanTableError }
|
||||
|
||||
if distanceCode >= 0 && distanceCode <= 29 {
|
||||
@@ -202,9 +202,13 @@ public class Deflate: DecompressionAlgorithm {
|
||||
out.append(contentsOf: arrayToRepeat)
|
||||
// Now we deal with the remainings.
|
||||
if length - distance * repeatCount == distance {
|
||||
out.append(contentsOf: out[out.count - distance..<out.count])
|
||||
for i in out.count - distance..<out.count {
|
||||
out.append(out[i])
|
||||
}
|
||||
} else {
|
||||
out.append(contentsOf: out[out.count - distance..<out.count + length - distance * (repeatCount + 1)])
|
||||
for i in out.count - distance..<out.count + length - distance * (repeatCount + 1) {
|
||||
out.append(out[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw DeflateError.HuffmanTableError
|
||||
|
||||
@@ -139,10 +139,10 @@ public class GzipArchive: Archive {
|
||||
*/
|
||||
public static func unarchive(archiveData data: Data) throws -> Data {
|
||||
/// Object with input data which supports convenient work with bit shifts.
|
||||
let pointerData = DataWithPointer(data: data, bitOrder: .reversed)
|
||||
var pointerData = DataWithPointer(data: data, bitOrder: .reversed)
|
||||
|
||||
_ = try serviceInfo(pointerData: pointerData)
|
||||
return try Deflate.decompress(pointerData: pointerData)
|
||||
return try Deflate.decompress(pointerData: &pointerData)
|
||||
// TODO: Add crc check
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ class HuffmanTree: CustomStringConvertible {
|
||||
8193, 12289, 16385, 24577]
|
||||
}
|
||||
|
||||
private var pointerData: DataWithPointer
|
||||
|
||||
var description: String {
|
||||
return self.tree.reduce("HuffmanTree:\n") { $0.appending("\($1)\n") }
|
||||
}
|
||||
@@ -32,7 +34,9 @@ class HuffmanTree: CustomStringConvertible {
|
||||
private var tree: [Int]
|
||||
private let leafCount: Int
|
||||
|
||||
init(bootstrap: [[Int]]) {
|
||||
init(bootstrap: [[Int]], _ pointerData: inout DataWithPointer) {
|
||||
self.pointerData = pointerData
|
||||
|
||||
// Fills the 'lengths' array with numerous HuffmanLengths from a 'bootstrap'.
|
||||
var lengths: [[Int]] = []
|
||||
var start = bootstrap[0][0]
|
||||
@@ -99,15 +103,15 @@ class HuffmanTree: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(lengthsToOrder: [Int]) {
|
||||
convenience init(lengthsToOrder: [Int], _ pointerData: inout DataWithPointer) {
|
||||
var addedLengths = lengthsToOrder
|
||||
addedLengths.append(-1)
|
||||
let lengthsCount = addedLengths.count
|
||||
let range = Array(0...lengthsCount)
|
||||
self.init(bootstrap: (zip(range, addedLengths)).map { [$0, $1] })
|
||||
self.init(bootstrap: (zip(range, addedLengths)).map { [$0, $1] }, &pointerData)
|
||||
}
|
||||
|
||||
func findNextSymbol(in pointerData: DataWithPointer) -> Int {
|
||||
func findNextSymbol() -> Int {
|
||||
var index = 0
|
||||
while true {
|
||||
let bit = pointerData.bit()
|
||||
|
||||
@@ -117,10 +117,10 @@ public class ZlibArchive: Archive {
|
||||
*/
|
||||
public static func unarchive(archiveData data: Data) throws -> Data {
|
||||
/// Object with input data which supports convenient work with bit shifts.
|
||||
let pointerData = DataWithPointer(data: data, bitOrder: .reversed)
|
||||
var pointerData = DataWithPointer(data: data, bitOrder: .reversed)
|
||||
|
||||
_ = try serviceInfo(pointerData: pointerData)
|
||||
return try Deflate.decompress(pointerData: pointerData)
|
||||
return try Deflate.decompress(pointerData: &pointerData)
|
||||
// TODO: Add Adler-32 check
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user