| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Diagnostics; |
| | | 5 | | |
| | | 6 | | namespace System.IO.Compression |
| | | 7 | | { |
| | | 8 | | // Strictly speaking this class is not a HuffmanTree, this class is |
| | | 9 | | // a lookup table combined with a HuffmanTree. The idea is to speed up |
| | | 10 | | // the lookup for short symbols (they should appear more frequently ideally.) |
| | | 11 | | // However we don't want to create a huge table since it might take longer to |
| | | 12 | | // build the table than decoding (Deflate usually generates new tables frequently.) |
| | | 13 | | // |
| | | 14 | | // Jean-loup Gailly and Mark Adler gave a very good explanation about this. |
| | | 15 | | // The full text (algorithm.txt) can be found inside |
| | | 16 | | // ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib.zip. |
| | | 17 | | // |
| | | 18 | | // Following paper explains decoding in details: |
| | | 19 | | // Hirschberg and Lelewer, "Efficient decoding of prefix codes," |
| | | 20 | | // Comm. ACM, 33,4, April 1990, pp. 449-459. |
| | | 21 | | // |
| | | 22 | | |
| | | 23 | | internal sealed class HuffmanTree |
| | | 24 | | { |
| | | 25 | | internal const int MaxLiteralTreeElements = 288; |
| | | 26 | | internal const int MaxDistTreeElements = 32; |
| | | 27 | | internal const int EndOfBlockCode = 256; |
| | | 28 | | internal const int NumberOfCodeLengthTreeElements = 19; |
| | | 29 | | |
| | | 30 | | private readonly int _tableBits; |
| | | 31 | | private readonly short[] _table; |
| | | 32 | | private readonly short[] _left; |
| | | 33 | | private readonly short[] _right; |
| | | 34 | | private readonly byte[] _codeLengthArray; |
| | | 35 | | #if DEBUG |
| | | 36 | | private uint[]? _codeArrayDebug; |
| | | 37 | | #endif |
| | | 38 | | |
| | | 39 | | private readonly int _tableMask; |
| | | 40 | | |
| | | 41 | | // huffman tree for static block |
| | 0 | 42 | | public static HuffmanTree StaticLiteralLengthTree { get; } = new HuffmanTree(GetStaticLiteralTreeLength()); |
| | | 43 | | |
| | 0 | 44 | | public static HuffmanTree StaticDistanceTree { get; } = new HuffmanTree(GetStaticDistanceTreeLength()); |
| | | 45 | | |
| | 0 | 46 | | public HuffmanTree(byte[] codeLengths) |
| | 0 | 47 | | { |
| | 0 | 48 | | Debug.Assert( |
| | 0 | 49 | | codeLengths.Length == MaxLiteralTreeElements || |
| | 0 | 50 | | codeLengths.Length == MaxDistTreeElements || |
| | 0 | 51 | | codeLengths.Length == NumberOfCodeLengthTreeElements, |
| | 0 | 52 | | "we only expect three kinds of Length here"); |
| | 0 | 53 | | _codeLengthArray = codeLengths; |
| | | 54 | | |
| | 0 | 55 | | if (_codeLengthArray.Length == MaxLiteralTreeElements) |
| | 0 | 56 | | { |
| | | 57 | | // bits for Literal/Length tree table |
| | 0 | 58 | | _tableBits = 9; |
| | 0 | 59 | | } |
| | | 60 | | else |
| | 0 | 61 | | { |
| | | 62 | | // bits for distance tree table and code length tree table |
| | 0 | 63 | | _tableBits = 7; |
| | 0 | 64 | | } |
| | 0 | 65 | | _tableMask = (1 << _tableBits) - 1; |
| | | 66 | | |
| | 0 | 67 | | _table = new short[1 << _tableBits]; |
| | | 68 | | |
| | | 69 | | // I need to find proof that left and right array will always be |
| | | 70 | | // enough. I think they are. |
| | 0 | 71 | | _left = new short[2 * _codeLengthArray.Length]; |
| | 0 | 72 | | _right = new short[2 * _codeLengthArray.Length]; |
| | | 73 | | |
| | 0 | 74 | | CreateTable(); |
| | 0 | 75 | | } |
| | | 76 | | |
| | | 77 | | // Generate the array contains huffman codes lengths for static huffman tree. |
| | | 78 | | // The data is in RFC 1951. |
| | | 79 | | private static byte[] GetStaticLiteralTreeLength() |
| | 0 | 80 | | { |
| | 0 | 81 | | byte[] literalTreeLength = new byte[MaxLiteralTreeElements]; |
| | | 82 | | |
| | 0 | 83 | | literalTreeLength.AsSpan(0, 144).Fill(8); |
| | 0 | 84 | | literalTreeLength.AsSpan(144, 112).Fill(9); |
| | 0 | 85 | | literalTreeLength.AsSpan(256, 24).Fill(7); |
| | 0 | 86 | | literalTreeLength.AsSpan(280, 8).Fill(8); |
| | 0 | 87 | | return literalTreeLength; |
| | 0 | 88 | | } |
| | | 89 | | |
| | | 90 | | private static byte[] GetStaticDistanceTreeLength() |
| | 0 | 91 | | { |
| | 0 | 92 | | byte[] staticDistanceTreeLength = new byte[MaxDistTreeElements]; |
| | 0 | 93 | | Array.Fill(staticDistanceTreeLength, (byte)5); |
| | 0 | 94 | | return staticDistanceTreeLength; |
| | 0 | 95 | | } |
| | | 96 | | |
| | | 97 | | // Reverse 'length' of the bits in code |
| | | 98 | | private static uint BitReverse(uint code, int length) |
| | 0 | 99 | | { |
| | 0 | 100 | | uint new_code = 0; |
| | | 101 | | |
| | 0 | 102 | | Debug.Assert(length > 0 && length <= 16, "Invalid len"); |
| | | 103 | | do |
| | 0 | 104 | | { |
| | 0 | 105 | | new_code |= (code & 1); |
| | 0 | 106 | | new_code <<= 1; |
| | 0 | 107 | | code >>= 1; |
| | 0 | 108 | | } while (--length > 0); |
| | | 109 | | |
| | 0 | 110 | | return new_code >> 1; |
| | 0 | 111 | | } |
| | | 112 | | |
| | | 113 | | // Calculate the huffman code for each character based on the code length for each character. |
| | | 114 | | // This algorithm is described in standard RFC 1951 |
| | | 115 | | private unsafe uint[] CalculateHuffmanCode() |
| | 0 | 116 | | { |
| | 0 | 117 | | Span<uint> bitLengthCount = stackalloc uint[17]; |
| | 0 | 118 | | bitLengthCount.Clear(); |
| | 0 | 119 | | foreach (int codeLength in _codeLengthArray) |
| | 0 | 120 | | { |
| | 0 | 121 | | bitLengthCount[codeLength]++; |
| | 0 | 122 | | } |
| | 0 | 123 | | bitLengthCount[0] = 0; // clear count for length 0 |
| | | 124 | | |
| | 0 | 125 | | Span<uint> nextCode = stackalloc uint[17]; |
| | 0 | 126 | | nextCode.Clear(); |
| | 0 | 127 | | uint tempCode = 0; |
| | 0 | 128 | | for (int bits = 1; bits <= 16; bits++) |
| | 0 | 129 | | { |
| | 0 | 130 | | tempCode = (tempCode + bitLengthCount[bits - 1]) << 1; |
| | 0 | 131 | | nextCode[bits] = tempCode; |
| | 0 | 132 | | } |
| | | 133 | | |
| | 0 | 134 | | uint[] code = new uint[MaxLiteralTreeElements]; |
| | 0 | 135 | | for (int i = 0; i < _codeLengthArray.Length; i++) |
| | 0 | 136 | | { |
| | 0 | 137 | | int len = _codeLengthArray[i]; |
| | | 138 | | |
| | 0 | 139 | | if (len > 0) |
| | 0 | 140 | | { |
| | 0 | 141 | | code[i] = BitReverse(nextCode[len], len); |
| | 0 | 142 | | nextCode[len]++; |
| | 0 | 143 | | } |
| | 0 | 144 | | } |
| | 0 | 145 | | return code; |
| | 0 | 146 | | } |
| | | 147 | | |
| | | 148 | | private void CreateTable() |
| | 0 | 149 | | { |
| | 0 | 150 | | uint[] codeArray = CalculateHuffmanCode(); |
| | | 151 | | #if DEBUG |
| | 0 | 152 | | _codeArrayDebug = codeArray; |
| | | 153 | | #endif |
| | | 154 | | |
| | 0 | 155 | | short avail = (short)_codeLengthArray.Length; |
| | | 156 | | |
| | 0 | 157 | | for (int ch = 0; ch < _codeLengthArray.Length; ch++) |
| | 0 | 158 | | { |
| | | 159 | | // length of this code |
| | 0 | 160 | | int len = _codeLengthArray[ch]; |
| | 0 | 161 | | if (len > 0) |
| | 0 | 162 | | { |
| | | 163 | | // start value (bit reversed) |
| | 0 | 164 | | int start = (int)codeArray[ch]; |
| | | 165 | | |
| | 0 | 166 | | if (len <= _tableBits) |
| | 0 | 167 | | { |
| | | 168 | | // If a particular symbol is shorter than nine bits, |
| | | 169 | | // then that symbol's translation is duplicated |
| | | 170 | | // in all those entries that start with that symbol's bits. |
| | | 171 | | // For example, if the symbol is four bits, then it's duplicated |
| | | 172 | | // 32 times in a nine-bit table. If a symbol is nine bits long, |
| | | 173 | | // it appears in the table once. |
| | | 174 | | // |
| | | 175 | | // Make sure that in the loop below, code is always |
| | | 176 | | // less than table_size. |
| | | 177 | | // |
| | | 178 | | // On last iteration we store at array index: |
| | | 179 | | // initial_start_at + (locs-1)*increment |
| | | 180 | | // = initial_start_at + locs*increment - increment |
| | | 181 | | // = initial_start_at + (1 << tableBits) - increment |
| | | 182 | | // = initial_start_at + table_size - increment |
| | | 183 | | // |
| | | 184 | | // Therefore we must ensure: |
| | | 185 | | // initial_start_at + table_size - increment < table_size |
| | | 186 | | // or: initial_start_at < increment |
| | | 187 | | // |
| | 0 | 188 | | int increment = 1 << len; |
| | 0 | 189 | | if (start >= increment) |
| | 0 | 190 | | { |
| | 0 | 191 | | throw new InvalidDataException(SR.InvalidHuffmanData); |
| | | 192 | | } |
| | | 193 | | |
| | | 194 | | // Note the bits in the table are reverted. |
| | 0 | 195 | | int locs = 1 << (_tableBits - len); |
| | 0 | 196 | | for (int j = 0; j < locs; j++) |
| | 0 | 197 | | { |
| | 0 | 198 | | _table[start] = (short)ch; |
| | 0 | 199 | | start += increment; |
| | 0 | 200 | | } |
| | 0 | 201 | | } |
| | | 202 | | else |
| | 0 | 203 | | { |
| | | 204 | | // For any code which has length longer than num_elements, |
| | | 205 | | // build a binary tree. |
| | | 206 | | |
| | 0 | 207 | | int overflowBits = len - _tableBits; // the nodes we need to respent the data. |
| | 0 | 208 | | int codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table) |
| | | 209 | | |
| | | 210 | | // the left, right table is used to repesent the |
| | | 211 | | // the rest bits. When we got the first part (number bits.) and look at |
| | | 212 | | // tbe table, we will need to follow the tree to find the real character. |
| | | 213 | | // This is in place to avoid bloating the table if there are |
| | | 214 | | // a few ones with long code. |
| | 0 | 215 | | int index = start & ((1 << _tableBits) - 1); |
| | 0 | 216 | | short[] array = _table; |
| | | 217 | | |
| | | 218 | | do |
| | 0 | 219 | | { |
| | 0 | 220 | | short value = array[index]; |
| | | 221 | | |
| | 0 | 222 | | if (value == 0) |
| | 0 | 223 | | { |
| | | 224 | | // set up next pointer if this node is not used before. |
| | 0 | 225 | | array[index] = (short)-avail; // use next available slot. |
| | 0 | 226 | | value = (short)-avail; |
| | 0 | 227 | | avail++; |
| | 0 | 228 | | } |
| | | 229 | | |
| | 0 | 230 | | if (value > 0) |
| | 0 | 231 | | { |
| | | 232 | | // prevent an IndexOutOfRangeException from array[index] |
| | 0 | 233 | | throw new InvalidDataException(SR.InvalidHuffmanData); |
| | | 234 | | } |
| | | 235 | | |
| | 0 | 236 | | Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!"); |
| | | 237 | | |
| | 0 | 238 | | if ((start & codeBitMask) == 0) |
| | 0 | 239 | | { |
| | | 240 | | // if current bit is 0, go change the left array |
| | 0 | 241 | | array = _left; |
| | 0 | 242 | | } |
| | | 243 | | else |
| | 0 | 244 | | { |
| | | 245 | | // if current bit is 1, set value in the right array |
| | 0 | 246 | | array = _right; |
| | 0 | 247 | | } |
| | 0 | 248 | | index = -value; // go to next node |
| | | 249 | | |
| | 0 | 250 | | if (index >= array.Length) |
| | 0 | 251 | | { |
| | | 252 | | // prevent an IndexOutOfRangeException from array[index] |
| | 0 | 253 | | throw new InvalidDataException(SR.InvalidHuffmanData); |
| | | 254 | | } |
| | | 255 | | |
| | 0 | 256 | | codeBitMask <<= 1; |
| | 0 | 257 | | overflowBits--; |
| | 0 | 258 | | } while (overflowBits != 0); |
| | | 259 | | |
| | 0 | 260 | | array[index] = (short)ch; |
| | 0 | 261 | | } |
| | 0 | 262 | | } |
| | 0 | 263 | | } |
| | 0 | 264 | | } |
| | | 265 | | |
| | | 266 | | // |
| | | 267 | | // This function will try to get enough bits from input and |
| | | 268 | | // try to decode the bits. |
| | | 269 | | // If there are no enought bits in the input, this function will return -1. |
| | | 270 | | // |
| | | 271 | | public int GetNextSymbol(InputBuffer input) |
| | 0 | 272 | | { |
| | | 273 | | // Try to load 16 bits into input buffer if possible and get the bitBuffer value. |
| | | 274 | | // If there aren't 16 bits available we will return all we have in the |
| | | 275 | | // input buffer. |
| | 0 | 276 | | uint bitBuffer = input.TryLoad16Bits(); |
| | 0 | 277 | | if (input.AvailableBits == 0) |
| | 0 | 278 | | { // running out of input. |
| | 0 | 279 | | return -1; |
| | | 280 | | } |
| | | 281 | | |
| | | 282 | | // decode an element |
| | 0 | 283 | | int symbol = _table[bitBuffer & _tableMask]; |
| | 0 | 284 | | if (symbol < 0) |
| | 0 | 285 | | { // this will be the start of the binary tree |
| | | 286 | | // navigate the tree |
| | 0 | 287 | | uint mask = (uint)1 << _tableBits; |
| | | 288 | | do |
| | 0 | 289 | | { |
| | 0 | 290 | | symbol = -symbol; |
| | 0 | 291 | | if ((bitBuffer & mask) == 0) |
| | 0 | 292 | | symbol = _left[symbol]; |
| | | 293 | | else |
| | 0 | 294 | | symbol = _right[symbol]; |
| | 0 | 295 | | mask <<= 1; |
| | 0 | 296 | | } while (symbol < 0); |
| | 0 | 297 | | } |
| | | 298 | | |
| | 0 | 299 | | int codeLength = _codeLengthArray[symbol]; |
| | | 300 | | |
| | | 301 | | // huffman code lengths must be at least 1 bit long |
| | 0 | 302 | | if (codeLength <= 0) |
| | 0 | 303 | | { |
| | 0 | 304 | | throw new InvalidDataException(SR.InvalidHuffmanData); |
| | | 305 | | } |
| | | 306 | | |
| | | 307 | | // |
| | | 308 | | // If this code is longer than the # bits we had in the bit buffer (i.e. |
| | | 309 | | // we read only part of the code), we can hit the entry in the table or the tree |
| | | 310 | | // for another symbol. However the length of another symbol will not match the |
| | | 311 | | // available bits count. |
| | 0 | 312 | | if (codeLength > input.AvailableBits) |
| | 0 | 313 | | { |
| | | 314 | | // We already tried to load 16 bits and maximum length is 15, |
| | | 315 | | // so this means we are running out of input. |
| | 0 | 316 | | return -1; |
| | | 317 | | } |
| | | 318 | | |
| | 0 | 319 | | input.SkipBits(codeLength); |
| | 0 | 320 | | return symbol; |
| | 0 | 321 | | } |
| | | 322 | | } |
| | | 323 | | } |