// Copyright (c) 2022 Timofey Solomko // Licensed under MIT License // // See LICENSE for license information import Foundation import BitByteData /// Provides functions for compression and decompression for Deflate algorithm. public class Deflate: DecompressionAlgorithm { /** Decompresses `data` using Deflate algortihm. - Note: This function is specification compliant. - Parameter data: Data compressed with Deflate. - Throws: `DeflateError` if unexpected byte (bit) sequence was encountered in `data`. It may indicate that either data is damaged or it might not be compressed with Deflate at all. - Returns: Decompressed data. */ public static func decompress(data: Data) throws -> Data { /// Object with input data which supports convenient work with bit shifts. let bitReader = LsbBitReader(data: data) return try decompress(bitReader) } static func decompress(_ bitReader: LsbBitReader) throws -> Data { /// An array for storing output data var out: [UInt8] = [] // The smallest possible deflate block consists of 10 bits: `isLastBit`, `blockType` (2 bits), and the // end-of-block Huffman-encoded symbol (7 bits). guard bitReader.bitsLeft >= 10 else { throw DeflateError.wrongBlockType } while true { /// Is this a last block? let isLastBit = bitReader.bit() /// Type of the current block. let blockType = bitReader.int(fromBits: 2) if blockType == 0 { // Uncompressed block. bitReader.align() // The uncompressed block consists at the very least of 32 bits or 4 bytes since they are byte-aligned. guard bitReader.bytesLeft >= 4 else { throw DeflateError.wrongUncompressedBlockLengths } /// Length of the uncompressed data. let length = bitReader.uint16() /// 1-complement of the length. let nlength = bitReader.uint16() // Check if lengths are OK (nlength should be a 1-complement of length). guard length & nlength == 0 else { throw DeflateError.wrongUncompressedBlockLengths } guard bitReader.bytesLeft >= length else { throw DeflateError.wrongUncompressedBlockLengths } // Process uncompressed data into the output for _ in 0..= 14 else { throw DeflateError.symbolNotFound } /// Number of literals codes. let literals = bitReader.int(fromBits: 5) + 257 /// Number of distances codes. let distances = bitReader.int(fromBits: 5) + 1 /// Number of code lengths codes. let codeLengthsCount = bitReader.int(fromBits: 4) + 4 guard bitReader.bitsLeft >= 3 * codeLengthsCount else { throw DeflateError.symbolNotFound } var orderedCodeLengths = Array(repeating: 0, count: 19) for i in 0..= 0 && symbol <= 15 { // It is a raw code length. count = 1 what = symbol } else if symbol == 16 { // Copy previous code length 3 to 6 times. // Next two bits show how many times we need to copy. guard bitReader.bitsLeft >= 2 else { throw DeflateError.symbolNotFound } count = bitReader.int(fromBits: 2) + 3 what = codeLengths.last! } else if symbol == 17 { // Repeat code length 0 from 3 to 10 times. // Next three bits show how many times we need to copy. guard bitReader.bitsLeft >= 3 else { throw DeflateError.symbolNotFound } count = bitReader.int(fromBits: 3) + 3 what = 0 } else if symbol == 18 { // Repeat code length 0 from 11 to 138 times. // Next seven bits show how many times we need to do this. guard bitReader.bitsLeft >= 7 else { throw DeflateError.symbolNotFound } count = bitReader.int(fromBits: 7) + 11 what = 0 } else { throw DeflateError.wrongSymbol } for _ in 0..= 0 && nextSymbol <= 255 { // It is a literal symbol so we add it straight to the output data. out.append(nextSymbol.toUInt8()) } else if nextSymbol == 256 { // It is a symbol indicating the end of data. break } else if nextSymbol >= 257 && nextSymbol <= 285 { // It is a length symbol. // Depending on the value of nextSymbol there might be additional bits in data, // which we need to add to nextSymbol to get the full length. 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. guard bitReader.bitsLeft >= extraLength else { throw DeflateError.symbolNotFound } let length = Constants.lengthBase[nextSymbol - 257] + bitReader.int(fromBits: extraLength) // Then we need to get distance code. let distanceCode = mainDistances.findNextSymbol() guard distanceCode != -1 else { throw DeflateError.symbolNotFound } guard distanceCode >= 0 && distanceCode <= 29 else { throw DeflateError.wrongSymbol } // 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. guard bitReader.bitsLeft >= extraDistance else { throw DeflateError.symbolNotFound } let distance = Constants.distanceBase[distanceCode] + bitReader.int(fromBits: extraDistance) // We should repeat last 'distance' amount of data. // The amount of times we do this is round(length / distance). // length actually indicates the amount of data we get from this nextSymbol. let repeatCount: Int = length / distance let count = out.count for _ in 0..