< 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: 160
Coverable lines: 160
Total lines: 323
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 = new byte[MaxDistTreeElements];
 093            Array.Fill(staticDistanceTreeLength, (byte)5);
 094            return staticDistanceTreeLength;
 095        }
 96
 97        // Reverse 'length' of the bits in code
 98        private static uint BitReverse(uint code, int length)
 099        {
 0100            uint new_code = 0;
 101
 0102            Debug.Assert(length > 0 && length <= 16, "Invalid len");
 103            do
 0104            {
 0105                new_code |= (code & 1);
 0106                new_code <<= 1;
 0107                code >>= 1;
 0108            } while (--length > 0);
 109
 0110            return new_code >> 1;
 0111        }
 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()
 0116        {
 0117            Span<uint> bitLengthCount = stackalloc uint[17];
 0118            bitLengthCount.Clear();
 0119            foreach (int codeLength in _codeLengthArray)
 0120            {
 0121                bitLengthCount[codeLength]++;
 0122            }
 0123            bitLengthCount[0] = 0;  // clear count for length 0
 124
 0125            Span<uint> nextCode = stackalloc uint[17];
 0126            nextCode.Clear();
 0127            uint tempCode = 0;
 0128            for (int bits = 1; bits <= 16; bits++)
 0129            {
 0130                tempCode = (tempCode + bitLengthCount[bits - 1]) << 1;
 0131                nextCode[bits] = tempCode;
 0132            }
 133
 0134            uint[] code = new uint[MaxLiteralTreeElements];
 0135            for (int i = 0; i < _codeLengthArray.Length; i++)
 0136            {
 0137                int len = _codeLengthArray[i];
 138
 0139                if (len > 0)
 0140                {
 0141                    code[i] = BitReverse(nextCode[len], len);
 0142                    nextCode[len]++;
 0143                }
 0144            }
 0145            return code;
 0146        }
 147
 148        private void CreateTable()
 0149        {
 0150            uint[] codeArray = CalculateHuffmanCode();
 151#if DEBUG
 0152            _codeArrayDebug = codeArray;
 153#endif
 154
 0155            short avail = (short)_codeLengthArray.Length;
 156
 0157            for (int ch = 0; ch < _codeLengthArray.Length; ch++)
 0158            {
 159                // length of this code
 0160                int len = _codeLengthArray[ch];
 0161                if (len > 0)
 0162                {
 163                    // start value (bit reversed)
 0164                    int start = (int)codeArray[ch];
 165
 0166                    if (len <= _tableBits)
 0167                    {
 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                        //
 0188                        int increment = 1 << len;
 0189                        if (start >= increment)
 0190                        {
 0191                            throw new InvalidDataException(SR.InvalidHuffmanData);
 192                        }
 193
 194                        // Note the bits in the table are reverted.
 0195                        int locs = 1 << (_tableBits - len);
 0196                        for (int j = 0; j < locs; j++)
 0197                        {
 0198                            _table[start] = (short)ch;
 0199                            start += increment;
 0200                        }
 0201                    }
 202                    else
 0203                    {
 204                        // For any code which has length longer than num_elements,
 205                        // build a binary tree.
 206
 0207                        int overflowBits = len - _tableBits; // the nodes we need to respent the data.
 0208                        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.
 0215                        int index = start & ((1 << _tableBits) - 1);
 0216                        short[] array = _table;
 217
 218                        do
 0219                        {
 0220                            short value = array[index];
 221
 0222                            if (value == 0)
 0223                            {
 224                                // set up next pointer if this node is not used before.
 0225                                array[index] = (short)-avail; // use next available slot.
 0226                                value = (short)-avail;
 0227                                avail++;
 0228                            }
 229
 0230                            if (value > 0)
 0231                            {
 232                                // prevent an IndexOutOfRangeException from array[index]
 0233                                throw new InvalidDataException(SR.InvalidHuffmanData);
 234                            }
 235
 0236                            Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!");
 237
 0238                            if ((start & codeBitMask) == 0)
 0239                            {
 240                                // if current bit is 0, go change the left array
 0241                                array = _left;
 0242                            }
 243                            else
 0244                            {
 245                                // if current bit is 1, set value in the right array
 0246                                array = _right;
 0247                            }
 0248                            index = -value; // go to next node
 249
 0250                            if (index >= array.Length)
 0251                            {
 252                                // prevent an IndexOutOfRangeException from array[index]
 0253                                throw new InvalidDataException(SR.InvalidHuffmanData);
 254                            }
 255
 0256                            codeBitMask <<= 1;
 0257                            overflowBits--;
 0258                        } while (overflowBits != 0);
 259
 0260                        array[index] = (short)ch;
 0261                    }
 0262                }
 0263            }
 0264        }
 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)
 0272        {
 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.
 0276            uint bitBuffer = input.TryLoad16Bits();
 0277            if (input.AvailableBits == 0)
 0278            {    // running out of input.
 0279                return -1;
 280            }
 281
 282            // decode an element
 0283            int symbol = _table[bitBuffer & _tableMask];
 0284            if (symbol < 0)
 0285            {       //  this will be the start of the binary tree
 286                // navigate the tree
 0287                uint mask = (uint)1 << _tableBits;
 288                do
 0289                {
 0290                    symbol = -symbol;
 0291                    if ((bitBuffer & mask) == 0)
 0292                        symbol = _left[symbol];
 293                    else
 0294                        symbol = _right[symbol];
 0295                    mask <<= 1;
 0296                } while (symbol < 0);
 0297            }
 298
 0299            int codeLength = _codeLengthArray[symbol];
 300
 301            // huffman code lengths must be at least 1 bit long
 0302            if (codeLength <= 0)
 0303            {
 0304                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.
 0312            if (codeLength > input.AvailableBits)
 0313            {
 314                // We already tried to load 16 bits and maximum length is 15,
 315                // so this means we are running out of input.
 0316                return -1;
 317            }
 318
 0319            input.SkipBits(codeLength);
 0320            return symbol;
 0321        }
 322    }
 323}