From 04106bfa8a4e1d9561615c499f0e1cd34ffa52e2 Mon Sep 17 00:00:00 2001 From: Timofey Solomko Date: Sat, 24 Dec 2016 18:42:56 +0300 Subject: [PATCH] Some changes to make deflate/bzip2 faster. --- Sources/BZip2.swift | 14 ++++++++------ Sources/Deflate.swift | 30 +++++++++++++++++------------- Sources/GzipArchive.swift | 4 ++-- Sources/HuffmanTree.swift | 12 ++++++++---- Sources/ZlibArchive.swift | 4 ++-- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/Sources/BZip2.swift b/Sources/BZip2.swift index 54cca076..a5825f2b 100644 --- a/Sources/BZip2.swift +++ b/Sources/BZip2.swift @@ -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.. 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..= 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.. 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 } diff --git a/Sources/HuffmanTree.swift b/Sources/HuffmanTree.swift index c3d3d8dc..5a7ce074 100644 --- a/Sources/HuffmanTree.swift +++ b/Sources/HuffmanTree.swift @@ -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() diff --git a/Sources/ZlibArchive.swift b/Sources/ZlibArchive.swift index d37ec133..2f34ff68 100644 --- a/Sources/ZlibArchive.swift +++ b/Sources/ZlibArchive.swift @@ -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 }