1+ // Copyright (c) Six Labors and contributors.
2+ // Licensed under the Apache License, Version 2.0.
3+
4+ using System . Runtime . CompilerServices ;
5+ using SixLabors . ImageSharp . IO ;
6+
7+ namespace SixLabors . ImageSharp . Formats . Jpeg . Components . Decoder
8+ {
9+ /// <summary>
10+ /// Used to buffer and track the bits read from the Huffman entropy encoded data.
11+ /// </summary>
12+ internal struct HuffmanScanBuffer
13+ {
14+ private readonly DoubleBufferedStreamReader stream ;
15+
16+ // The entropy encoded code buffer.
17+ private ulong data ;
18+
19+ // The number of valid bits left to read in the buffer.
20+ private int remain ;
21+
22+ // Whether there is more data to pull from the stream for the current mcu.
23+ private bool noMore ;
24+
25+ public HuffmanScanBuffer ( DoubleBufferedStreamReader stream )
26+ {
27+ this . stream = stream ;
28+ this . data = 0ul ;
29+ this . remain = 0 ;
30+ this . Marker = JpegConstants . Markers . XFF ;
31+ this . MarkerPosition = 0 ;
32+ this . BadMarker = false ;
33+ this . noMore = false ;
34+ this . Eof = false ;
35+ }
36+
37+ /// <summary>
38+ /// Gets or sets the current, if any, marker in the input stream.
39+ /// </summary>
40+ public byte Marker { get ; set ; }
41+
42+ /// <summary>
43+ /// Gets or sets the opening position of an identified marker.
44+ /// </summary>
45+ public long MarkerPosition { get ; set ; }
46+
47+ /// <summary>
48+ /// Gets or sets a value indicating whether we have a bad marker, I.E. One that is not between RST0 and RST7
49+ /// </summary>
50+ public bool BadMarker { get ; set ; }
51+
52+ /// <summary>
53+ /// Gets or sets a value indicating whether we have prematurely reached the end of the file.
54+ /// </summary>
55+ public bool Eof { get ; set ; }
56+
57+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
58+ public void CheckBits ( )
59+ {
60+ if ( this . remain < 16 )
61+ {
62+ this . FillBuffer ( ) ;
63+ }
64+ }
65+
66+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
67+ public void Reset ( )
68+ {
69+ this . data = 0ul ;
70+ this . remain = 0 ;
71+ this . Marker = JpegConstants . Markers . XFF ;
72+ this . MarkerPosition = 0 ;
73+ this . BadMarker = false ;
74+ this . noMore = false ;
75+ this . Eof = false ;
76+ }
77+
78+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
79+ public bool HasRestart ( )
80+ {
81+ byte m = this . Marker ;
82+ return m >= JpegConstants . Markers . RST0 && m <= JpegConstants . Markers . RST7 ;
83+ }
84+
85+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
86+ public void FillBuffer ( )
87+ {
88+ // Attempt to load at least the minimum number of required bits into the buffer.
89+ // We fail to do so only if we hit a marker or reach the end of the input stream.
90+ this . remain += 48 ;
91+ this . data = ( this . data << 48 ) | this . GetBytes ( ) ;
92+ }
93+
94+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
95+ public unsafe int DecodeHuffman ( ref HuffmanTable h )
96+ {
97+ this . CheckBits ( ) ;
98+ int v = this . PeekBits ( JpegConstants . Huffman . LookupBits ) ;
99+ int symbol = h . LookaheadValue [ v ] ;
100+ int size = h . LookaheadSize [ v ] ;
101+
102+ if ( size == JpegConstants . Huffman . SlowBits )
103+ {
104+ ulong x = this . data << ( JpegConstants . Huffman . RegisterSize - this . remain ) ;
105+ while ( x > h . MaxCode [ size ] )
106+ {
107+ size ++ ;
108+ }
109+
110+ v = ( int ) ( x >> ( JpegConstants . Huffman . RegisterSize - size ) ) ;
111+ symbol = h . Values [ h . ValOffset [ size ] + v ] ;
112+ }
113+
114+ this . remain -= size ;
115+
116+ return symbol ;
117+ }
118+
119+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
120+ public int Receive ( int nbits )
121+ {
122+ this . CheckBits ( ) ;
123+ return Extend ( this . GetBits ( nbits ) , nbits ) ;
124+ }
125+
126+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
127+ public int GetBits ( int nbits ) => ( int ) ExtractBits ( this . data , this . remain -= nbits , nbits ) ;
128+
129+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
130+ public int PeekBits ( int nbits ) => ( int ) ExtractBits ( this . data , this . remain - nbits , nbits ) ;
131+
132+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
133+ private static ulong ExtractBits ( ulong value , int offset , int size ) => ( value >> offset ) & ( ulong ) ( ( 1 << size ) - 1 ) ;
134+
135+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
136+ private static int Extend ( int v , int nbits ) => v - ( ( ( ( v + v ) >> nbits ) - 1 ) & ( ( 1 << nbits ) - 1 ) ) ;
137+
138+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
139+ private ulong GetBytes ( )
140+ {
141+ ulong temp = 0 ;
142+ for ( int i = 0 ; i < 6 ; i ++ )
143+ {
144+ int b = this . noMore ? 0 : this . stream . ReadByte ( ) ;
145+
146+ if ( b == - 1 )
147+ {
148+ // We've encountered the end of the file stream which means there's no EOI marker in the image
149+ // or the SOS marker has the wrong dimensions set.
150+ this . Eof = true ;
151+ b = 0 ;
152+ }
153+
154+ // Found a marker.
155+ if ( b == JpegConstants . Markers . XFF )
156+ {
157+ this . MarkerPosition = this . stream . Position - 1 ;
158+ int c = this . stream . ReadByte ( ) ;
159+ while ( c == JpegConstants . Markers . XFF )
160+ {
161+ c = this . stream . ReadByte ( ) ;
162+
163+ if ( c == - 1 )
164+ {
165+ this . Eof = true ;
166+ c = 0 ;
167+ break ;
168+ }
169+ }
170+
171+ if ( c != 0 )
172+ {
173+ this . Marker = ( byte ) c ;
174+ this . noMore = true ;
175+ if ( ! this . HasRestart ( ) )
176+ {
177+ this . BadMarker = true ;
178+ }
179+ }
180+ }
181+
182+ temp = ( temp << 8 ) | ( ulong ) ( long ) b ;
183+ }
184+
185+ return temp ;
186+ }
187+ }
188+ }
0 commit comments