< Summary

Information
Class: System.IO.Compression.HuffmanTree
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateManaged\HuffmanTree.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 159
Coverable lines: 159
Total lines: 322
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 50
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)0%660%
GetStaticLiteralTreeLength()100%110%
GetStaticDistanceTreeLength()100%110%
BitReverse(...)0%440%
CalculateHuffmanCode()0%880%
CreateTable()0%20200%
GetNextSymbol(...)0%12120%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateManaged\HuffmanTree.cs

#LineLine coverage
 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
 4using System.Diagnostics;
 5
 6namespace 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
 042        public static HuffmanTree StaticLiteralLengthTree { get; } = new HuffmanTree(GetStaticLiteralTreeLength());
 43
 044        public static HuffmanTree StaticDistanceTree { get; } = new HuffmanTree(GetStaticDistanceTreeLength());
 45
 046        public HuffmanTree(byte[] codeLengths)
 047        {
 048            Debug.Assert(
 049                codeLengths.Length == MaxLiteralTreeElements ||
 050                codeLengths.Length == MaxDistTreeElements ||
 051                codeLengths.Length == NumberOfCodeLengthTreeElements,
 052                "we only expect three kinds of Length here");
 053            _codeLengthArray = codeLengths;
 54
 055            if (_codeLengthArray.Length == MaxLiteralTreeElements)
 056            {
 57                // bits for Literal/Length tree table
 058                _tableBits = 9;
 059            }
 60            else
 061            {
 62                // bits for distance tree table and code length tree table
 063                _tableBits = 7;
 064            }
 065            _tableMask = (1 << _tableBits) - 1;
 66
 067            _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.
 071            _left = new short[2 * _codeLengthArray.Length];
 072            _right = new short[2 * _codeLengthArray.Length];
 73
 074            CreateTable();
 075        }
 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()
 080        {
 081            byte[] literalTreeLength = new byte[MaxLiteralTreeElements];
 82
 083            literalTreeLength.AsSpan(0, 144).Fill(8);
 084            literalTreeLength.AsSpan(144, 112).Fill(9);
 085            literalTreeLength.AsSpan(256, 24).Fill(7);
 086            literalTreeLength.AsSpan(280, 8).Fill(8);
 087            return literalTreeLength;
 088        }
 89
 90        private static byte[] GetStaticDistanceTreeLength()
 091        {
 092            byte[] staticDistanceTreeLength = Array.CreateFilled<byte>(MaxDistTreeElements, 5);
 093            return staticDistanceTreeLength;
 094        }
 95
 96        // Reverse 'length' of the bits in code
 97        private static uint BitReverse(uint code, int length)
 098        {
 099            uint new_code = 0;
 100
 0101            Debug.Assert(length > 0 && length <= 16, "Invalid len");
 102            do
 0103            {
 0104                new_code |= (code & 1);
 0105                new_code <<= 1;
 0106                code >>= 1;
 0107            } while (--length > 0);
 108
 0109            return new_code >> 1;
 0110        }
 111
 112        // Calculate the huffman code for each character based on the code length for each character.
 113        // This algorithm is described in standard RFC 1951
 114        private unsafe uint[] CalculateHuffmanCode()
 0115        {
 0116            Span<uint> bitLengthCount = stackalloc uint[17];
 0117            bitLengthCount.Clear();
 0118            foreach (int codeLength in _codeLengthArray)
 0119            {
 0120                bitLengthCount[codeLength]++;
 0121            }
 0122            bitLengthCount[0] = 0;  // clear count for length 0
 123
 0124            Span<uint> nextCode = stackalloc uint[17];
 0125            nextCode.Clear();
 0126            uint tempCode = 0;
 0127            for (int bits = 1; bits <= 16; bits++)
 0128            {
 0129                tempCode = (tempCode + bitLengthCount[bits - 1]) << 1;
 0130                nextCode[bits] = tempCode;
 0131            }
 132
 0133            uint[] code = new uint[MaxLiteralTreeElements];
 0134            for (int i = 0; i < _codeLengthArray.Length; i++)
 0135            {
 0136                int len = _codeLengthArray[i];
 137
 0138                if (len > 0)
 0139                {
 0140                    code[i] = BitReverse(nextCode[len], len);
 0141                    nextCode[len]++;
 0142                }
 0143            }
 0144            return code;
 0145        }
 146
 147        private void CreateTable()
 0148        {
 0149            uint[] codeArray = CalculateHuffmanCode();
 150#if DEBUG
 0151            _codeArrayDebug = codeArray;
 152#endif
 153
 0154            short avail = (short)_codeLengthArray.Length;
 155
 0156            for (int ch = 0; ch < _codeLengthArray.Length; ch++)
 0157            {
 158                // length of this code
 0159                int len = _codeLengthArray[ch];
 0160                if (len > 0)
 0161                {
 162                    // start value (bit reversed)
 0163                    int start = (int)codeArray[ch];
 164
 0165                    if (len <= _tableBits)
 0166                    {
 167                        // If a particular symbol is shorter than nine bits,
 168                        // then that symbol's translation is duplicated
 169                        // in all those entries that start with that symbol's bits.
 170                        // For example, if the symbol is four bits, then it's duplicated
 171                        // 32 times in a nine-bit table. If a symbol is nine bits long,
 172                        // it appears in the table once.
 173                        //
 174                        // Make sure that in the loop below, code is always
 175                        // less than table_size.
 176                        //
 177                        // On last iteration we store at array index:
 178                        //    initial_start_at + (locs-1)*increment
 179                        //  = initial_start_at + locs*increment - increment
 180                        //  = initial_start_at + (1 << tableBits) - increment
 181                        //  = initial_start_at + table_size - increment
 182                        //
 183                        // Therefore we must ensure:
 184                        //     initial_start_at + table_size - increment < table_size
 185                        // or: initial_start_at < increment
 186                        //
 0187                        int increment = 1 << len;
 0188                        if (start >= increment)
 0189                        {
 0190                            throw new InvalidDataException(SR.InvalidHuffmanData);
 191                        }
 192
 193                        // Note the bits in the table are reverted.
 0194                        int locs = 1 << (_tableBits - len);
 0195                        for (int j = 0; j < locs; j++)
 0196                        {
 0197                            _table[start] = (short)ch;
 0198                            start += increment;
 0199                        }
 0200                    }
 201                    else
 0202                    {
 203                        // For any code which has length longer than num_elements,
 204                        // build a binary tree.
 205
 0206                        int overflowBits = len - _tableBits; // the nodes we need to respent the data.
 0207                        int codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table)
 208
 209                        // the left, right table is used to repesent the
 210                        // the rest bits. When we got the first part (number bits.) and look at
 211                        // tbe table, we will need to follow the tree to find the real character.
 212                        // This is in place to avoid bloating the table if there are
 213                        // a few ones with long code.
 0214                        int index = start & ((1 << _tableBits) - 1);
 0215                        short[] array = _table;
 216
 217                        do
 0218                        {
 0219                            short value = array[index];
 220
 0221                            if (value == 0)
 0222                            {
 223                                // set up next pointer if this node is not used before.
 0224                                array[index] = (short)-avail; // use next available slot.
 0225                                value = (short)-avail;
 0226                                avail++;
 0227                            }
 228
 0229                            if (value > 0)
 0230                            {
 231                                // prevent an IndexOutOfRangeException from array[index]
 0232                                throw new InvalidDataException(SR.InvalidHuffmanData);
 233                            }
 234
 0235                            Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!");
 236
 0237                            if ((start & codeBitMask) == 0)
 0238                            {
 239                                // if current bit is 0, go change the left array
 0240                                array = _left;
 0241                            }
 242                            else
 0243                            {
 244                                // if current bit is 1, set value in the right array
 0245                                array = _right;
 0246                            }
 0247                            index = -value; // go to next node
 248
 0249                            if (index >= array.Length)
 0250                            {
 251                                // prevent an IndexOutOfRangeException from array[index]
 0252                                throw new InvalidDataException(SR.InvalidHuffmanData);
 253                            }
 254
 0255                            codeBitMask <<= 1;
 0256                            overflowBits--;
 0257                        } while (overflowBits != 0);
 258
 0259                        array[index] = (short)ch;
 0260                    }
 0261                }
 0262            }
 0263        }
 264
 265        //
 266        // This function will try to get enough bits from input and
 267        // try to decode the bits.
 268        // If there are no enought bits in the input, this function will return -1.
 269        //
 270        public int GetNextSymbol(InputBuffer input)
 0271        {
 272            // Try to load 16 bits into input buffer if possible and get the bitBuffer value.
 273            // If there aren't 16 bits available we will return all we have in the
 274            // input buffer.
 0275            uint bitBuffer = input.TryLoad16Bits();
 0276            if (input.AvailableBits == 0)
 0277            {    // running out of input.
 0278                return -1;
 279            }
 280
 281            // decode an element
 0282            int symbol = _table[bitBuffer & _tableMask];
 0283            if (symbol < 0)
 0284            {       //  this will be the start of the binary tree
 285                // navigate the tree
 0286                uint mask = (uint)1 << _tableBits;
 287                do
 0288                {
 0289                    symbol = -symbol;
 0290                    if ((bitBuffer & mask) == 0)
 0291                        symbol = _left[symbol];
 292                    else
 0293                        symbol = _right[symbol];
 0294                    mask <<= 1;
 0295                } while (symbol < 0);
 0296            }
 297
 0298            int codeLength = _codeLengthArray[symbol];
 299
 300            // huffman code lengths must be at least 1 bit long
 0301            if (codeLength <= 0)
 0302            {
 0303                throw new InvalidDataException(SR.InvalidHuffmanData);
 304            }
 305
 306            //
 307            // If this code is longer than the # bits we had in the bit buffer (i.e.
 308            // we read only part of the code), we can hit the entry in the table or the tree
 309            // for another symbol. However the length of another symbol will not match the
 310            // available bits count.
 0311            if (codeLength > input.AvailableBits)
 0312            {
 313                // We already tried to load 16 bits and maximum length is 15,
 314                // so this means we are running out of input.
 0315                return -1;
 316            }
 317
 0318            input.SkipBits(codeLength);
 0319            return symbol;
 0320        }
 321    }
 322}