mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
d63d2977fb
And it is necessary only for Deflate. This will allow us to remove bitOrder argument of BitWriter.write, which has very unclear semantics
83 lines
2.8 KiB
Swift
83 lines
2.8 KiB
Swift
// Copyright (c) 2017 Timofey Solomko
|
|
// Licensed under MIT License
|
|
//
|
|
// See LICENSE for license information
|
|
|
|
import Foundation
|
|
|
|
class EncodingHuffmanTree {
|
|
|
|
private var bitWriter: BitWriter
|
|
|
|
private var codingIndices: [[Int]]
|
|
|
|
/// `lengths` don't have to be properly sorted, but there must not be any 0 code lengths.
|
|
/// If `reverseCodes` is true, then bit order of tree codes will be reversed. Necessary for Deflate.
|
|
init(lengths: [HuffmanLength], _ bitWriter: BitWriter, reverseCodes: Bool = false) {
|
|
self.bitWriter = bitWriter
|
|
|
|
// Sort `lengths` array to calculate canonical Huffman code.
|
|
let sortedLengths = lengths.sorted()
|
|
|
|
func reverse(bits: Int, in symbol: Int) -> Int {
|
|
// Auxiliarly function, which generates reversed order of bits in a number.
|
|
var a = 1 << 0
|
|
var b = 1 << (bits - 1)
|
|
var z = 0
|
|
for i in stride(from: bits - 1, to: -1, by: -2) {
|
|
z |= (symbol >> i) & a
|
|
z |= (symbol << i) & b
|
|
a <<= 1
|
|
b >>= 1
|
|
}
|
|
return z
|
|
}
|
|
|
|
self.codingIndices = Array(repeating: [-1, -1], count: sortedLengths.count)
|
|
|
|
// Calculates symbols for each length in 'sortedLengths' array and put them in the tree.
|
|
var loopBits = -1
|
|
var symbol = -1
|
|
for length in sortedLengths {
|
|
precondition(length.codeLength > 0, "Code length must not be 0 during HuffmanTree initialisation.")
|
|
symbol += 1
|
|
// We sometimes need to make symbol to have length.bits bit length.
|
|
let bits = length.codeLength
|
|
if bits != loopBits {
|
|
symbol <<= (bits - loopBits)
|
|
loopBits = bits
|
|
}
|
|
// Then we reverse bit order of the symbol, if necessary.
|
|
let treeCode = reverseCodes ? reverse(bits: loopBits, in: symbol) : symbol
|
|
self.codingIndices[length.symbol] = [treeCode, bits]
|
|
}
|
|
}
|
|
|
|
func code(symbol: Int) {
|
|
guard symbol < self.codingIndices.count
|
|
else { fatalError("Symbol is not found.") }
|
|
|
|
let codingIndex = self.codingIndices[symbol]
|
|
|
|
guard codingIndex[0] > -1
|
|
else { fatalError("Symbol is not found.") }
|
|
|
|
self.bitWriter.write(number: codingIndex[0], bitsCount: codingIndex[1])
|
|
}
|
|
|
|
func bitSize(for stats: [Int]) -> Int {
|
|
var totalSize = 0
|
|
for (symbol, count) in stats.enumerated() where count > 0 {
|
|
guard symbol < self.codingIndices.count
|
|
else { fatalError("Symbol is not found.") }
|
|
let codingIndex = self.codingIndices[symbol]
|
|
guard codingIndex[0] > -1
|
|
else { fatalError("Symbol is not found.") }
|
|
|
|
totalSize += count * codingIndex[1]
|
|
}
|
|
return totalSize
|
|
}
|
|
|
|
}
|