11using System ;
2+ using System . Collections . Generic ;
23using ICSharpCode . SharpZipLib . Zip . Compression . Streams ;
34
45namespace ICSharpCode . SharpZipLib . Zip . Compression
@@ -7,100 +8,148 @@ class InflaterDynHeader
78 {
89 #region Constants
910
10- static readonly int [ ] BL_ORDER =
11- { 16 , 17 , 18 , 0 , 8 , 7 , 9 , 6 , 10 , 5 , 11 , 4 , 12 , 3 , 13 , 2 , 14 , 1 , 15 } ;
11+ // maximum number of literal/length codes
12+ const int LITLEN_MAX = 286 ;
13+
14+ // maximum number of distance codes
15+ const int DIST_MAX = 30 ;
16+
17+ // maximum data code lengths to read
18+ const int CODELEN_MAX = LITLEN_MAX + DIST_MAX ;
19+
20+ // maximum meta code length codes to read
21+ const int META_MAX = 19 ;
22+
23+ static readonly int [ ] MetaCodeLengthIndex =
24+ { 16 , 17 , 18 , 0 , 8 , 7 , 9 , 6 , 10 , 5 , 11 , 4 , 12 , 3 , 13 , 2 , 14 , 1 , 15 } ;
25+
1226 #endregion
1327
14- public bool Decode ( StreamManipulator input )
28+ /// <summary>
29+ /// Continue decoding header from <see cref="input"/> until more bits are needed or decoding has been completed
30+ /// </summary>
31+ /// <returns>Returns whether decoding could be completed</returns>
32+ public bool AttemptRead ( )
33+ => ! state . MoveNext ( ) || state . Current ;
34+
35+ public InflaterDynHeader ( StreamManipulator input )
1536 {
16- try
37+ this . input = input ;
38+ stateMachine = CreateStateMachine ( ) ;
39+ state = stateMachine . GetEnumerator ( ) ;
40+ }
41+
42+ private IEnumerable < bool > CreateStateMachine ( )
43+ {
44+
45+ // Read initial code length counts from header
46+ while ( ! input . TryGetBits ( 5 , ref litLenCodeCount , 257 ) ) yield return false ;
47+ while ( ! input . TryGetBits ( 5 , ref distanceCodeCount , 1 ) ) yield return false ;
48+ while ( ! input . TryGetBits ( 4 , ref metaCodeCount , 4 ) ) yield return false ;
49+ var dataCodeCount = litLenCodeCount + distanceCodeCount ;
50+
51+ if ( litLenCodeCount > LITLEN_MAX ) throw new ValueOutOfRangeException ( nameof ( litLenCodeCount ) ) ;
52+ if ( distanceCodeCount > DIST_MAX ) throw new ValueOutOfRangeException ( nameof ( distanceCodeCount ) ) ;
53+ if ( metaCodeCount > META_MAX ) throw new ValueOutOfRangeException ( nameof ( metaCodeCount ) ) ;
54+
55+ // Load code lengths for the meta tree from the header bits
56+ for ( int i = 0 ; i < metaCodeCount ; i ++ )
57+ {
58+ while ( ! input . TryGetBits ( 3 , ref codeLengths , MetaCodeLengthIndex [ i ] ) ) yield return false ;
59+ }
60+
61+ var metaCodeTree = new InflaterHuffmanTree ( codeLengths ) ;
62+
63+ // Decompress the meta tree symbols into the data table code lengths
64+ int index = 0 ;
65+ while ( index < dataCodeCount )
1766 {
18- lnum = input . GrabBits ( 5 ) + 257 ;
19- dnum = input . GrabBits ( 5 ) + 1 ;
20- blnum = input . GrabBits ( 4 ) + 4 ;
21- num = lnum + dnum ;
67+ byte codeLength ;
68+ int symbol ;
2269
23- lengths = new byte [ 19 ] ;
70+ while ( ( symbol = metaCodeTree . GetSymbol ( input ) ) < 0 ) yield return false ;
2471
25- for ( int i = 0 ; i < blnum ; i ++ )
72+ if ( symbol < 16 )
2673 {
27- lengths [ BL_ORDER [ i ] ] = ( byte ) input . GrabBits ( 3 , true ) ;
74+ // append literal code length
75+ codeLengths [ index ++ ] = ( byte ) symbol ;
2876 }
29- blTree = new InflaterHuffmanTree ( lengths ) ;
30- lengths = new byte [ num ] ;
31-
32- int index = 0 ;
33- while ( index < lnum + dnum )
77+ else
3478 {
35- byte len ;
36-
37- int symbol = blTree . GetSymbol ( input ) ;
38- if ( symbol < 0 )
39- return false ;
40- if ( symbol < 16 )
41- lengths [ index ++ ] = ( byte ) symbol ;
42- else
79+ int repeatCount = 0 ;
80+
81+ if ( symbol == 16 ) // Repeat last code length 3..6 times
82+ {
83+
84+ if ( index == 0 )
85+ throw new StreamDecodingException ( "Cannot repeat previous code length when no other code length has been read" ) ;
86+
87+ codeLength = codeLengths [ index - 1 ] ;
88+
89+ // 2 bits + 3, [3..6]
90+ while ( ! input . TryGetBits ( 2 , ref repeatCount , 3 ) ) yield return false ;
91+ }
92+ else if ( symbol == 17 ) // Repeat zero 3..10 times
4393 {
44- len = 0 ;
45- if ( symbol == 16 )
46- {
47- if ( index == 0 )
48- return false ; // No last length!
49- len = lengths [ index - 1 ] ;
50- symbol = input . GrabBits ( 2 , true ) + 3 ;
51- }
52- else if ( symbol == 17 )
53- {
54- // repeat zero 3..10 times
55- symbol = input . GrabBits ( 3 , true ) + 3 ;
56- }
57- else
58- {
59- // (symbol == 18), repeat zero 11..138 times
60- symbol = input . GrabBits ( 7 , true ) + 11 ;
61- }
62-
63- if ( index + symbol > lnum + dnum )
64- return false ; // too many lengths!
65-
66- // repeat last or zero symbol times
67- while ( symbol -- > 0 )
68- lengths [ index ++ ] = len ;
94+ codeLength = 0 ;
95+
96+ // 3 bits + 3, [3..10]
97+ while ( ! input . TryGetBits ( 3 , ref repeatCount , 3 ) ) yield return false ;
6998 }
70- }
99+ else // (symbol == 18), Repeat zero 11..138 times
100+ {
101+ codeLength = 0 ;
71102
72- if ( lengths [ 256 ] == 0 )
73- return false ; // No end-of-block code!
103+ // 7 bits + 11, [11..138]
104+ while ( ! input . TryGetBits ( 7 , ref repeatCount , 11 ) ) yield return false ;
105+ }
74106
75- return true ;
76- }
77- catch ( Exception x )
78- {
79- return false ;
107+ if ( index + repeatCount > dataCodeCount )
108+ throw new StreamDecodingException ( "Cannot repeat code lengths past total number of data code lengths" ) ;
109+
110+ while ( repeatCount -- > 0 )
111+ codeLengths [ index ++ ] = codeLength ;
112+ }
80113 }
81- }
82114
83- public InflaterHuffmanTree BuildLitLenTree ( )
84- {
85- byte [ ] litlenLens = new byte [ lnum ] ;
86- Array . Copy ( lengths , 0 , litlenLens , 0 , lnum ) ;
87- return new InflaterHuffmanTree ( litlenLens ) ;
88- }
115+ if ( codeLengths [ 256 ] == 0 )
116+ throw new StreamDecodingException ( "Inflater dynamic header end-of-block code missing" ) ;
89117
90- public InflaterHuffmanTree BuildDistTree ( )
91- {
92- byte [ ] distLens = new byte [ dnum ] ;
93- Array . Copy ( lengths , lnum , distLens , 0 , dnum ) ;
94- return new InflaterHuffmanTree ( distLens ) ;
118+ litLenTree = new InflaterHuffmanTree ( new ArraySegment < byte > ( codeLengths , 0 , litLenCodeCount ) ) ;
119+ distTree = new InflaterHuffmanTree ( new ArraySegment < byte > ( codeLengths , litLenCodeCount , distanceCodeCount ) ) ;
120+
121+ yield return true ;
95122 }
96123
124+ /// <summary>
125+ /// Get literal/length huffman tree, must not be used before <see cref="AttemptRead"/> has returned true
126+ /// </summary>
127+ /// <exception cref="StreamDecodingException">If hader has not been successfully read by the state machine</exception>
128+ public InflaterHuffmanTree LiteralLengthTree
129+ => litLenTree ?? throw new StreamDecodingException ( "Header properties were accessed before header had been successfully read" ) ;
130+
131+ /// <summary>
132+ /// Get distance huffman tree, must not be used before <see cref="AttemptRead"/> has returned true
133+ /// </summary>
134+ /// <exception cref="StreamDecodingException">If hader has not been successfully read by the state machine</exception>
135+ public InflaterHuffmanTree DistanceTree
136+ => distTree ?? throw new StreamDecodingException ( "Header properties were accessed before header had been successfully read" ) ;
137+
97138 #region Instance Fields
98- byte [ ] lengths ;
99139
100- InflaterHuffmanTree blTree ;
140+ private readonly StreamManipulator input ;
141+ private readonly IEnumerator < bool > state ;
142+ private readonly IEnumerable < bool > stateMachine ;
143+
144+ private byte [ ] codeLengths = new byte [ CODELEN_MAX ] ;
145+
146+ private InflaterHuffmanTree litLenTree ;
147+ private InflaterHuffmanTree distTree ;
148+
149+ int litLenCodeCount , distanceCodeCount , metaCodeCount ;
101150
102- int lnum , dnum , blnum , num ;
103151 #endregion
104152
105153 }
154+
106155}
0 commit comments