< 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
98%
Covered lines: 157
Uncovered lines: 2
Coverable lines: 159
Total lines: 322
Line coverage: 98.7%
Branch coverage
96%
Covered branches: 48
Total branches: 50
Branch coverage: 96%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%66100%
GetStaticLiteralTreeLength()100%11100%
GetStaticDistanceTreeLength()100%11100%
BitReverse(...)75%44100%
CalculateHuffmanCode()100%88100%
CreateTable()95%202096.61%
GetNextSymbol(...)100%1212100%

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
 513142        public static HuffmanTree StaticLiteralLengthTree { get; } = new HuffmanTree(GetStaticLiteralTreeLength());
 43
 513144        public static HuffmanTree StaticDistanceTree { get; } = new HuffmanTree(GetStaticDistanceTreeLength());
 45
 845846        public HuffmanTree(byte[] codeLengths)
 845847        {
 845848            Debug.Assert(
 845849                codeLengths.Length == MaxLiteralTreeElements ||
 845850                codeLengths.Length == MaxDistTreeElements ||
 845851                codeLengths.Length == NumberOfCodeLengthTreeElements,
 845852                "we only expect three kinds of Length here");
 845853            _codeLengthArray = codeLengths;
 54
 845855            if (_codeLengthArray.Length == MaxLiteralTreeElements)
 220156            {
 57                // bits for Literal/Length tree table
 220158                _tableBits = 9;
 220159            }
 60            else
 625761            {
 62                // bits for distance tree table and code length tree table
 625763                _tableBits = 7;
 625764            }
 845865            _tableMask = (1 << _tableBits) - 1;
 66
 845867            _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.
 845871            _left = new short[2 * _codeLengthArray.Length];
 845872            _right = new short[2 * _codeLengthArray.Length];
 73
 845874            CreateTable();
 807075        }
 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()
 180        {
 181            byte[] literalTreeLength = new byte[MaxLiteralTreeElements];
 82
 183            literalTreeLength.AsSpan(0, 144).Fill(8);
 184            literalTreeLength.AsSpan(144, 112).Fill(9);
 185            literalTreeLength.AsSpan(256, 24).Fill(7);
 186            literalTreeLength.AsSpan(280, 8).Fill(8);
 187            return literalTreeLength;
 188        }
 89
 90        private static byte[] GetStaticDistanceTreeLength()
 191        {
 192            byte[] staticDistanceTreeLength = Array.CreateFilled<byte>(MaxDistTreeElements, 5);
 193            return staticDistanceTreeLength;
 194        }
 95
 96        // Reverse 'length' of the bits in code
 97        private static uint BitReverse(uint code, int length)
 35125298        {
 35125299            uint new_code = 0;
 100
 351252101            Debug.Assert(length > 0 && length <= 16, "Invalid len");
 102            do
 2796242103            {
 2796242104                new_code |= (code & 1);
 2796242105                new_code <<= 1;
 2796242106                code >>= 1;
 5592484107            } while (--length > 0);
 108
 351252109            return new_code >> 1;
 351252110        }
 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()
 8458115        {
 8458116            Span<uint> bitLengthCount = stackalloc uint[17];
 8458117            bitLengthCount.Clear();
 1579978118            foreach (int codeLength in _codeLengthArray)
 777302119            {
 777302120                bitLengthCount[codeLength]++;
 777302121            }
 8458122            bitLengthCount[0] = 0;  // clear count for length 0
 123
 8458124            Span<uint> nextCode = stackalloc uint[17];
 8458125            nextCode.Clear();
 8458126            uint tempCode = 0;
 287572127            for (int bits = 1; bits <= 16; bits++)
 135328128            {
 135328129                tempCode = (tempCode + bitLengthCount[bits - 1]) << 1;
 135328130                nextCode[bits] = tempCode;
 135328131            }
 132
 8458133            uint[] code = new uint[MaxLiteralTreeElements];
 1571520134            for (int i = 0; i < _codeLengthArray.Length; i++)
 777302135            {
 777302136                int len = _codeLengthArray[i];
 137
 777302138                if (len > 0)
 351252139                {
 351252140                    code[i] = BitReverse(nextCode[len], len);
 351252141                    nextCode[len]++;
 351252142                }
 777302143            }
 8458144            return code;
 8458145        }
 146
 147        private void CreateTable()
 8458148        {
 8458149            uint[] codeArray = CalculateHuffmanCode();
 150#if DEBUG
 8458151            _codeArrayDebug = codeArray;
 152#endif
 153
 8458154            short avail = (short)_codeLengthArray.Length;
 155
 1470260156            for (int ch = 0; ch < _codeLengthArray.Length; ch++)
 727060157            {
 158                // length of this code
 727060159                int len = _codeLengthArray[ch];
 727060160                if (len > 0)
 310758161                {
 162                    // start value (bit reversed)
 310758163                    int start = (int)codeArray[ch];
 164
 310758165                    if (len <= _tableBits)
 225724166                    {
 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                        //
 225724187                        int increment = 1 << len;
 225724188                        if (start >= increment)
 0189                        {
 0190                            throw new InvalidDataException(SR.InvalidHuffmanData);
 191                        }
 192
 193                        // Note the bits in the table are reverted.
 225724194                        int locs = 1 << (_tableBits - len);
 4347992195                        for (int j = 0; j < locs; j++)
 1948272196                        {
 1948272197                            _table[start] = (short)ch;
 1948272198                            start += increment;
 1948272199                        }
 225724200                    }
 201                    else
 85034202                    {
 203                        // For any code which has length longer than num_elements,
 204                        // build a binary tree.
 205
 85034206                        int overflowBits = len - _tableBits; // the nodes we need to respent the data.
 85034207                        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.
 85034214                        int index = start & ((1 << _tableBits) - 1);
 85034215                        short[] array = _table;
 216
 217                        do
 263356218                        {
 263356219                            short value = array[index];
 220
 263356221                            if (value == 0)
 68692222                            {
 223                                // set up next pointer if this node is not used before.
 68692224                                array[index] = (short)-avail; // use next available slot.
 68692225                                value = (short)-avail;
 68692226                                avail++;
 68692227                            }
 228
 263356229                            if (value > 0)
 340230                            {
 231                                // prevent an IndexOutOfRangeException from array[index]
 340232                                throw new InvalidDataException(SR.InvalidHuffmanData);
 233                            }
 234
 263016235                            Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!");
 236
 263016237                            if ((start & codeBitMask) == 0)
 133244238                            {
 239                                // if current bit is 0, go change the left array
 133244240                                array = _left;
 133244241                            }
 242                            else
 129772243                            {
 244                                // if current bit is 1, set value in the right array
 129772245                                array = _right;
 129772246                            }
 263016247                            index = -value; // go to next node
 248
 263016249                            if (index >= array.Length)
 48250                            {
 251                                // prevent an IndexOutOfRangeException from array[index]
 48252                                throw new InvalidDataException(SR.InvalidHuffmanData);
 253                            }
 254
 262968255                            codeBitMask <<= 1;
 262968256                            overflowBits--;
 525936257                        } while (overflowBits != 0);
 258
 84646259                        array[index] = (short)ch;
 84646260                    }
 310370261                }
 726672262            }
 8070263        }
 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)
 1157202271        {
 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.
 1157202275            uint bitBuffer = input.TryLoad16Bits();
 1157202276            if (input.AvailableBits == 0)
 562277            {    // running out of input.
 562278                return -1;
 279            }
 280
 281            // decode an element
 1156640282            int symbol = _table[bitBuffer & _tableMask];
 1156640283            if (symbol < 0)
 10054284            {       //  this will be the start of the binary tree
 285                // navigate the tree
 10054286                uint mask = (uint)1 << _tableBits;
 287                do
 14776288                {
 14776289                    symbol = -symbol;
 14776290                    if ((bitBuffer & mask) == 0)
 9908291                        symbol = _left[symbol];
 292                    else
 4868293                        symbol = _right[symbol];
 14776294                    mask <<= 1;
 29552295                } while (symbol < 0);
 10054296            }
 297
 1156640298            int codeLength = _codeLengthArray[symbol];
 299
 300            // huffman code lengths must be at least 1 bit long
 1156640301            if (codeLength <= 0)
 318302            {
 318303                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.
 1156322311            if (codeLength > input.AvailableBits)
 2650312            {
 313                // We already tried to load 16 bits and maximum length is 15,
 314                // so this means we are running out of input.
 2650315                return -1;
 316            }
 317
 1153672318            input.SkipBits(codeLength);
 1153672319            return symbol;
 1156884320        }
 321    }
 322}