mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
369da20c16
I've also removed a most occurrences of inout keyword because DWP (and BitReader) are classes and they are already passed by reference in arguments.
95 lines
3.4 KiB
Swift
95 lines
3.4 KiB
Swift
// Copyright (c) 2017 Timofey Solomko
|
|
// Licensed under MIT License
|
|
//
|
|
// See LICENSE for license information
|
|
|
|
import Foundation
|
|
|
|
/// Represents a Zlib archive's header.
|
|
public struct ZlibHeader {
|
|
|
|
/// Supported compression methods in zlib archive.
|
|
public enum CompressionMethod: Int {
|
|
/// The only one supported compression method (Deflate).
|
|
case deflate = 8
|
|
}
|
|
|
|
/// Levels of compression which can be used to create Zlib archive.
|
|
public enum CompressionLevel: Int {
|
|
/// Fastest algorithm.
|
|
case fastestAlgorithm = 0
|
|
/// Fast algorithm.
|
|
case fastAlgorithm = 1
|
|
/// Default algorithm.
|
|
case defaultAlgorithm = 2
|
|
/// Slowest algorithm but with maximum compression.
|
|
case slowAlgorithm = 3
|
|
}
|
|
|
|
/// Compression method of archive. Currently, always equals to `.deflate`.
|
|
public let compressionMethod: CompressionMethod
|
|
|
|
/// Level of compression used in archive.
|
|
public let compressionLevel: CompressionLevel
|
|
|
|
/// Size of 'window': moving interval of data which was used to make archive.
|
|
public let windowSize: Int
|
|
|
|
/**
|
|
Initializes the structure with the values from Zlib `archive`.
|
|
|
|
If data passed is not actually a Zlib archive, `ZlibError` will be thrown.
|
|
|
|
- Parameter archive: Data archived with zlib.
|
|
|
|
- Throws: `ZlibError`. It may indicate that either archive is damaged or
|
|
it might not be archived with Zlib at all.
|
|
*/
|
|
public init(archive data: Data) throws {
|
|
let bitReader = BitReader(data: data, bitOrder: .reversed)
|
|
try self.init(bitReader)
|
|
}
|
|
|
|
init(_ bitReader: BitReader) throws {
|
|
// First four bits are compression method.
|
|
// Only compression method = 8 (DEFLATE) is supported.
|
|
let compressionMethod = bitReader.intFromBits(count: 4)
|
|
guard compressionMethod == 8 else { throw ZlibError.wrongCompressionMethod }
|
|
|
|
self.compressionMethod = .deflate
|
|
|
|
// Remaining four bits indicate window size.
|
|
// For Deflate it must not be more than 7.
|
|
let compressionInfo = bitReader.intFromBits(count: 4)
|
|
guard compressionInfo <= 7 else { throw ZlibError.wrongCompressionInfo }
|
|
let windowSize = 1 << (compressionInfo + 8)
|
|
|
|
self.windowSize = windowSize
|
|
|
|
// compressionMethod and compressionInfo combined are needed later for integrity check.
|
|
let cmf = compressionInfo << 4 + compressionMethod
|
|
|
|
// Next five bits are fcheck bits which are supposed to be integrity check.
|
|
let fcheck = bitReader.intFromBits(count: 5)
|
|
|
|
// Sixth bit indicate if archive contain Adler-32 checksum of preset dictionary.
|
|
let fdict = bitReader.intFromBits(count: 1)
|
|
|
|
// Remaining bits indicate compression level.
|
|
guard let compressionLevel = ZlibHeader.CompressionLevel(rawValue:
|
|
bitReader.intFromBits(count: 2)) else { throw ZlibError.wrongCompressionLevel }
|
|
|
|
self.compressionLevel = compressionLevel
|
|
|
|
// fcheck, fdict and compresionLevel together make flags byte which is used in integrity check.
|
|
let flags = compressionLevel.rawValue << 6 + fdict << 5 + fcheck
|
|
guard (UInt(cmf) * 256 + UInt(flags)) % 31 == 0 else { throw ZlibError.wrongFcheck }
|
|
|
|
// If preset dictionary is present 4 bytes will be skipped.
|
|
if fdict == 1 {
|
|
bitReader.index += 4
|
|
}
|
|
}
|
|
|
|
}
|