1 using System;
2 
3 namespace ICSharpCode.SharpZipLib.Zip.Compression
4 {
5 	/// <summary>
6 	/// This class is general purpose class for writing data to a buffer.
7 	///
8 	/// It allows you to write bits as well as bytes
9 	/// Based on DeflaterPending.java
10 	///
11 	/// author of the original java version : Jochen Hoenicke
12 	/// </summary>
13 	public class PendingBuffer
14 	{
15 		readonly
16 		#region Instance Fields
17 		/// <summary>
18 		/// Internal work buffer
19 		/// </summary>
20 		byte[] buffer_;
21 
22 		int start;
23 		int end;
24 
25 		uint bits;
26 		int bitCount;
27 		#endregion
28 
29 		#region Constructors
30 		/// <summary>
31 		/// construct instance using default buffer size of 4096
32 		/// </summary>
PendingBuffer()33 		public PendingBuffer() : this(4096)
34 		{
35 		}
36 
37 		/// <summary>
38 		/// construct instance using specified buffer size
39 		/// </summary>
40 		/// <param name="bufferSize">
41 		/// size to use for internal buffer
42 		/// </param>
PendingBuffer(int bufferSize)43 		public PendingBuffer(int bufferSize)
44 		{
45 			buffer_ = new byte[bufferSize];
46 		}
47 
48 		#endregion
49 
50 		/// <summary>
51 		/// Clear internal state/buffers
52 		/// </summary>
Reset()53 		public void Reset()
54 		{
55 			start = end = bitCount = 0;
56 		}
57 
58 		/// <summary>
59 		/// Write a byte to buffer
60 		/// </summary>
61 		/// <param name="value">
62 		/// The value to write
63 		/// </param>
WriteByte(int value)64 		public void WriteByte(int value)
65 		{
66 #if DebugDeflation
67 			if (DeflaterConstants.DEBUGGING && (start != 0) )
68 			{
69 				throw new SharpZipBaseException("Debug check: start != 0");
70 			}
71 #endif
72 			buffer_[end++] = unchecked((byte)value);
73 		}
74 
75 		/// <summary>
76 		/// Write a short value to buffer LSB first
77 		/// </summary>
78 		/// <param name="value">
79 		/// The value to write.
80 		/// </param>
WriteShort(int value)81 		public void WriteShort(int value)
82 		{
83 #if DebugDeflation
84 			if (DeflaterConstants.DEBUGGING && (start != 0) )
85 			{
86 				throw new SharpZipBaseException("Debug check: start != 0");
87 			}
88 #endif
89 			buffer_[end++] = unchecked((byte)value);
90 			buffer_[end++] = unchecked((byte)(value >> 8));
91 		}
92 
93 		/// <summary>
94 		/// write an integer LSB first
95 		/// </summary>
96 		/// <param name="value">The value to write.</param>
WriteInt(int value)97 		public void WriteInt(int value)
98 		{
99 #if DebugDeflation
100 			if (DeflaterConstants.DEBUGGING && (start != 0) )
101 			{
102 				throw new SharpZipBaseException("Debug check: start != 0");
103 			}
104 #endif
105 			buffer_[end++] = unchecked((byte)value);
106 			buffer_[end++] = unchecked((byte)(value >> 8));
107 			buffer_[end++] = unchecked((byte)(value >> 16));
108 			buffer_[end++] = unchecked((byte)(value >> 24));
109 		}
110 
111 		/// <summary>
112 		/// Write a block of data to buffer
113 		/// </summary>
114 		/// <param name="block">data to write</param>
115 		/// <param name="offset">offset of first byte to write</param>
116 		/// <param name="length">number of bytes to write</param>
WriteBlock(byte[] block, int offset, int length)117 		public void WriteBlock(byte[] block, int offset, int length)
118 		{
119 #if DebugDeflation
120 			if (DeflaterConstants.DEBUGGING && (start != 0) )
121 			{
122 				throw new SharpZipBaseException("Debug check: start != 0");
123 			}
124 #endif
125 			System.Array.Copy(block, offset, buffer_, end, length);
126 			end += length;
127 		}
128 
129 		/// <summary>
130 		/// The number of bits written to the buffer
131 		/// </summary>
132 		public int BitCount {
133 			get {
134 				return bitCount;
135 			}
136 		}
137 
138 		/// <summary>
139 		/// Align internal buffer on a byte boundary
140 		/// </summary>
AlignToByte()141 		public void AlignToByte()
142 		{
143 #if DebugDeflation
144 			if (DeflaterConstants.DEBUGGING && (start != 0) )
145 			{
146 				throw new SharpZipBaseException("Debug check: start != 0");
147 			}
148 #endif
149 			if (bitCount > 0) {
150 				buffer_[end++] = unchecked((byte)bits);
151 				if (bitCount > 8) {
152 					buffer_[end++] = unchecked((byte)(bits >> 8));
153 				}
154 			}
155 			bits = 0;
156 			bitCount = 0;
157 		}
158 
159 		/// <summary>
160 		/// Write bits to internal buffer
161 		/// </summary>
162 		/// <param name="b">source of bits</param>
163 		/// <param name="count">number of bits to write</param>
WriteBits(int b, int count)164 		public void WriteBits(int b, int count)
165 		{
166 #if DebugDeflation
167 			if (DeflaterConstants.DEBUGGING && (start != 0) )
168 			{
169 				throw new SharpZipBaseException("Debug check: start != 0");
170 			}
171 
172 			//			if (DeflaterConstants.DEBUGGING) {
173 			//				//Console.WriteLine("writeBits("+b+","+count+")");
174 			//			}
175 #endif
176 			bits |= (uint)(b << bitCount);
177 			bitCount += count;
178 			if (bitCount >= 16) {
179 				buffer_[end++] = unchecked((byte)bits);
180 				buffer_[end++] = unchecked((byte)(bits >> 8));
181 				bits >>= 16;
182 				bitCount -= 16;
183 			}
184 		}
185 
186 		/// <summary>
187 		/// Write a short value to internal buffer most significant byte first
188 		/// </summary>
189 		/// <param name="s">value to write</param>
WriteShortMSB(int s)190 		public void WriteShortMSB(int s)
191 		{
192 #if DebugDeflation
193 			if (DeflaterConstants.DEBUGGING && (start != 0) )
194 			{
195 				throw new SharpZipBaseException("Debug check: start != 0");
196 			}
197 #endif
198 			buffer_[end++] = unchecked((byte)(s >> 8));
199 			buffer_[end++] = unchecked((byte)s);
200 		}
201 
202 		/// <summary>
203 		/// Indicates if buffer has been flushed
204 		/// </summary>
205 		public bool IsFlushed {
206 			get {
207 				return end == 0;
208 			}
209 		}
210 
211 		/// <summary>
212 		/// Flushes the pending buffer into the given output array.  If the
213 		/// output array is to small, only a partial flush is done.
214 		/// </summary>
215 		/// <param name="output">The output array.</param>
216 		/// <param name="offset">The offset into output array.</param>
217 		/// <param name="length">The maximum number of bytes to store.</param>
218 		/// <returns>The number of bytes flushed.</returns>
Flush(byte[] output, int offset, int length)219 		public int Flush(byte[] output, int offset, int length)
220 		{
221 			if (bitCount >= 8) {
222 				buffer_[end++] = unchecked((byte)bits);
223 				bits >>= 8;
224 				bitCount -= 8;
225 			}
226 
227 			if (length > end - start) {
228 				length = end - start;
229 				System.Array.Copy(buffer_, start, output, offset, length);
230 				start = 0;
231 				end = 0;
232 			} else {
233 				System.Array.Copy(buffer_, start, output, offset, length);
234 				start += length;
235 			}
236 			return length;
237 		}
238 
239 		/// <summary>
240 		/// Convert internal buffer to byte array.
241 		/// Buffer is empty on completion
242 		/// </summary>
243 		/// <returns>
244 		/// The internal buffer contents converted to a byte array.
245 		/// </returns>
ToByteArray()246 		public byte[] ToByteArray()
247 		{
248 			AlignToByte();
249 
250 			byte[] result = new byte[end - start];
251 			System.Array.Copy(buffer_, start, result, 0, result.Length);
252 			start = 0;
253 			end = 0;
254 			return result;
255 		}
256 	}
257 }
258