Some changes to make deflate/bzip2 faster.

This commit is contained in:
Timofey Solomko
2016-12-24 18:42:56 +03:00
parent c226b4905b
commit 04106bfa8a
5 changed files with 37 additions and 27 deletions
+8 -6
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
}
+8 -4
View File
@@ -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()
+2 -2
View File
@@ -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
}