* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / Zip / Compression / Streams / DeflaterOutputStream.cs
1 // DeflaterOutputStream.cs\r
2 //\r
3 // Copyright (C) 2001 Mike Krueger\r
4 //\r
5 // This file was translated from java, it was part of the GNU Classpath\r
6 // Copyright (C) 2001 Free Software Foundation, Inc.\r
7 //\r
8 // This program is free software; you can redistribute it and/or\r
9 // modify it under the terms of the GNU General Public License\r
10 // as published by the Free Software Foundation; either version 2\r
11 // of the License, or (at your option) any later version.\r
12 //\r
13 // This program is distributed in the hope that it will be useful,\r
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16 // GNU General Public License for more details.\r
17 //\r
18 // You should have received a copy of the GNU General Public License\r
19 // along with this program; if not, write to the Free Software\r
20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
21 //\r
22 // Linking this library statically or dynamically with other modules is\r
23 // making a combined work based on this library.  Thus, the terms and\r
24 // conditions of the GNU General Public License cover the whole\r
25 // combination.\r
26 // \r
27 // As a special exception, the copyright holders of this library give you\r
28 // permission to link this library with independent modules to produce an\r
29 // executable, regardless of the license terms of these independent\r
30 // modules, and to copy and distribute the resulting executable under\r
31 // terms of your choice, provided that you also meet, for each linked\r
32 // independent module, the terms and conditions of the license of that\r
33 // module.  An independent module is a module which is not derived from\r
34 // or based on this library.  If you modify this library, you may extend\r
35 // this exception to your version of the library, but you are not\r
36 // obligated to do so.  If you do not wish to do so, delete this\r
37 // exception statement from your version.\r
38 \r
39 using System;\r
40 using System.IO;\r
41 using ICSharpCode.SharpZipLib.Checksums;\r
42 using ICSharpCode.SharpZipLib.Zip.Compression;\r
43 \r
44 namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams \r
45 {\r
46 \r
47         /// <summary>\r
48         /// A special stream deflating or compressing the bytes that are\r
49         /// written to it.  It uses a Deflater to perform actual deflating.<br/>\r
50         /// Authors of the original java version : Tom Tromey, Jochen Hoenicke \r
51         /// </summary>\r
52         public class DeflaterOutputStream : Stream\r
53         {\r
54                 /// <summary>\r
55                 /// This buffer is used temporarily to retrieve the bytes from the\r
56                 /// deflater and write them to the underlying output stream.\r
57                 /// </summary>\r
58                 protected byte[] buf;\r
59                 \r
60                 /// <summary>\r
61                 /// The deflater which is used to deflate the stream.\r
62                 /// </summary>\r
63                 protected Deflater def;\r
64                 \r
65                 /// <summary>\r
66                 /// Base stream the deflater depends on.\r
67                 /// </summary>\r
68                 protected Stream baseOutputStream;\r
69 \r
70                 bool isClosed = false;\r
71                 bool isStreamOwner = true;\r
72                 \r
73                 /// <summary>\r
74                 /// Get/set flag indicating ownership of underlying stream.\r
75                 /// When the flag is true <see cref="Close"></see> will close the underlying stream also.\r
76                 /// </summary>\r
77                 public bool IsStreamOwner\r
78                 {\r
79                         get { return isStreamOwner; }\r
80                         set { isStreamOwner = value; }\r
81                 }\r
82                 \r
83                 ///     <summary>\r
84                 /// Allows client to determine if an entry can be patched after its added\r
85                 /// </summary>\r
86                 public bool CanPatchEntries {\r
87                         get { \r
88                                 return baseOutputStream.CanSeek; \r
89                         }\r
90                 }\r
91                 \r
92                 /// <summary>\r
93                 /// Gets value indicating stream can be read from\r
94                 /// </summary>\r
95                 public override bool CanRead {\r
96                         get {\r
97                                 return baseOutputStream.CanRead;\r
98                         }\r
99                 }\r
100                 \r
101                 /// <summary>\r
102                 /// Gets a value indicating if seeking is supported for this stream\r
103                 /// This property always returns false\r
104                 /// </summary>\r
105                 public override bool CanSeek {\r
106                         get {\r
107                                 return false;\r
108                         }\r
109                 }\r
110                 \r
111                 /// <summary>\r
112                 /// Get value indicating if this stream supports writing\r
113                 /// </summary>\r
114                 public override bool CanWrite {\r
115                         get {\r
116                                 return baseOutputStream.CanWrite;\r
117                         }\r
118                 }\r
119                 \r
120                 /// <summary>\r
121                 /// Get current length of stream\r
122                 /// </summary>\r
123                 public override long Length {\r
124                         get {\r
125                                 return baseOutputStream.Length;\r
126                         }\r
127                 }\r
128                 \r
129                 /// <summary>\r
130                 /// The current position within the stream.\r
131                 /// Always throws a NotSupportedExceptionNotSupportedException\r
132                 /// </summary>\r
133                 /// <exception cref="NotSupportedException">Any attempt to set position</exception>\r
134                 public override long Position {\r
135                         get {\r
136                                 return baseOutputStream.Position;\r
137                         }\r
138                         set {\r
139                                 throw new NotSupportedException("DefalterOutputStream Position not supported");\r
140                         }\r
141                 }\r
142                 \r
143                 /// <summary>\r
144                 /// Sets the current position of this stream to the given value. Not supported by this class!\r
145                 /// </summary>\r
146                 /// <exception cref="NotSupportedException">Any access</exception>\r
147                 public override long Seek(long offset, SeekOrigin origin)\r
148                 {\r
149                         throw new NotSupportedException("DeflaterOutputStream Seek not supported");\r
150                 }\r
151                 \r
152                 /// <summary>\r
153                 /// Sets the length of this stream to the given value. Not supported by this class!\r
154                 /// </summary>\r
155                 /// <exception cref="NotSupportedException">Any access</exception>\r
156                 public override void SetLength(long val)\r
157                 {\r
158                         throw new NotSupportedException("DeflaterOutputStream SetLength not supported");\r
159                 }\r
160                 \r
161                 /// <summary>\r
162                 /// Read a byte from stream advancing position by one\r
163                 /// </summary>\r
164                 /// <exception cref="NotSupportedException">Any access</exception>\r
165                 public override int ReadByte()\r
166                 {\r
167                         throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");\r
168                 }\r
169                 \r
170                 /// <summary>\r
171                 /// Read a block of bytes from stream\r
172                 /// </summary>\r
173                 /// <exception cref="NotSupportedException">Any access</exception>\r
174                 public override int Read(byte[] b, int off, int len)\r
175                 {\r
176                         throw new NotSupportedException("DeflaterOutputStream Read not supported");\r
177                 }\r
178                 \r
179                 /// <summary>\r
180                 /// Asynchronous reads are not supported a NotSupportedException is always thrown\r
181                 /// </summary>\r
182                 /// <param name="buffer"></param>\r
183                 /// <param name="offset"></param>\r
184                 /// <param name="count"></param>\r
185                 /// <param name="callback"></param>\r
186                 /// <param name="state"></param>\r
187                 /// <returns></returns>\r
188                 /// <exception cref="NotSupportedException">Any access</exception>\r
189                 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)\r
190                 {\r
191                         throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported");\r
192                 }\r
193                 \r
194                 /// <summary>\r
195                 /// Asynchronous writes arent supported, a NotSupportedException is always thrown\r
196                 /// </summary>\r
197                 /// <param name="buffer"></param>\r
198                 /// <param name="offset"></param>\r
199                 /// <param name="count"></param>\r
200                 /// <param name="callback"></param>\r
201                 /// <param name="state"></param>\r
202                 /// <returns></returns>\r
203                 /// <exception cref="NotSupportedException">Any access</exception>\r
204                 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)\r
205                 {\r
206                         throw new NotSupportedException("DeflaterOutputStream BeginWrite not currently supported");\r
207                 }\r
208                 \r
209                 /// <summary>\r
210                 /// Deflates everything in the input buffers.  This will call\r
211                 /// <code>def.deflate()</code> until all bytes from the input buffers\r
212                 /// are processed.\r
213                 /// </summary>\r
214                 protected void Deflate()\r
215                 {\r
216                         while (!def.IsNeedingInput) {\r
217                                 int len = def.Deflate(buf, 0, buf.Length);\r
218                                 \r
219                                 if (len <= 0) {\r
220                                         break;\r
221                                 }\r
222                                 \r
223                                 if (this.keys != null) {\r
224                                         this.EncryptBlock(buf, 0, len);\r
225                                 }\r
226                                 \r
227                                 baseOutputStream.Write(buf, 0, len);\r
228                         }\r
229                         \r
230                         if (!def.IsNeedingInput) {\r
231                                 throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");\r
232                         }\r
233                 }\r
234                 \r
235                 /// <summary>\r
236                 /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.\r
237                 /// </summary>\r
238                 /// <param name="baseOutputStream">\r
239                 /// the output stream where deflated output should be written.\r
240                 /// </param>\r
241                 public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512)\r
242                 {\r
243                 }\r
244                 \r
245                 /// <summary>\r
246                 /// Creates a new DeflaterOutputStream with the given Deflater and\r
247                 /// default buffer size.\r
248                 /// </summary>\r
249                 /// <param name="baseOutputStream">\r
250                 /// the output stream where deflated output should be written.\r
251                 /// </param>\r
252                 /// <param name="defl">\r
253                 /// the underlying deflater.\r
254                 /// </param>\r
255                 public DeflaterOutputStream(Stream baseOutputStream, Deflater defl) : this(baseOutputStream, defl, 512)\r
256                 {\r
257                 }\r
258                 \r
259                 /// <summary>\r
260                 /// Creates a new DeflaterOutputStream with the given Deflater and\r
261                 /// buffer size.\r
262                 /// </summary>\r
263                 /// <param name="baseOutputStream">\r
264                 /// The output stream where deflated output is written.\r
265                 /// </param>\r
266                 /// <param name="deflater">\r
267                 /// The underlying deflater to use\r
268                 /// </param>\r
269                 /// <param name="bufsize">\r
270                 /// The buffer size to use when deflating\r
271                 /// </param>\r
272                 /// <exception cref="ArgumentOutOfRangeException">\r
273                 /// bufsize is less than or equal to zero.\r
274                 /// </exception>\r
275                 /// <exception cref="ArgumentException">\r
276                 /// baseOutputStream does not support writing\r
277                 /// </exception>\r
278                 /// <exception cref="ArgumentNullException">\r
279                 /// deflater instance is null\r
280                 /// </exception>\r
281                 public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufsize)\r
282                 {\r
283                         if (baseOutputStream.CanWrite == false) {\r
284                                 throw new ArgumentException("baseOutputStream", "must support writing");\r
285                         }\r
286 \r
287                         if (deflater == null) {\r
288                                 throw new ArgumentNullException("deflater");\r
289                         }\r
290                         \r
291                         if (bufsize <= 0) {\r
292                                 throw new ArgumentOutOfRangeException("bufsize");\r
293                         }\r
294                         \r
295                         this.baseOutputStream = baseOutputStream;\r
296                         buf = new byte[bufsize];\r
297                         def = deflater;\r
298                 }\r
299                 \r
300                 /// <summary>\r
301                 /// Flushes the stream by calling flush() on the deflater and then\r
302                 /// on the underlying stream.  This ensures that all bytes are\r
303                 /// flushed.\r
304                 /// </summary>\r
305                 public override void Flush()\r
306                 {\r
307                         def.Flush();\r
308                         Deflate();\r
309                         baseOutputStream.Flush();\r
310                 }\r
311                 \r
312                 /// <summary>\r
313                 /// Finishes the stream by calling finish() on the deflater. \r
314                 /// </summary>\r
315                 /// <exception cref="SharpZipBaseException">\r
316                 /// Not all input is deflated\r
317                 /// </exception>\r
318                 public virtual void Finish()\r
319                 {\r
320                         def.Finish();\r
321                         while (!def.IsFinished)  {\r
322                                 int len = def.Deflate(buf, 0, buf.Length);\r
323                                 if (len <= 0) {\r
324                                         break;\r
325                                 }\r
326                                 \r
327                                 if (this.keys != null) {\r
328                                         this.EncryptBlock(buf, 0, len);\r
329                                 }\r
330                                 \r
331                                 baseOutputStream.Write(buf, 0, len);\r
332                         }\r
333                         if (!def.IsFinished) {\r
334                                 throw new SharpZipBaseException("Can't deflate all input?");\r
335                         }\r
336                         baseOutputStream.Flush();\r
337                         keys = null;\r
338                 }\r
339                 \r
340                 /// <summary>\r
341                 /// Calls finish() and closes the underlying\r
342                 /// stream when <see cref="IsStreamOwner"></see> is true.\r
343                 /// </summary>\r
344                 public override void Close()\r
345                 {\r
346                         if ( !isClosed ) {\r
347                                 isClosed = true;\r
348                                 Finish();\r
349                                 if ( isStreamOwner ) {\r
350                                         baseOutputStream.Close();\r
351                                 }\r
352                         }\r
353                 }\r
354                 \r
355                 /// <summary>\r
356                 /// Writes a single byte to the compressed output stream.\r
357                 /// </summary>\r
358                 /// <param name="bval">\r
359                 /// The byte value.\r
360                 /// </param>\r
361                 public override void WriteByte(byte bval)\r
362                 {\r
363                         byte[] b = new byte[1];\r
364                         b[0] = bval;\r
365                         Write(b, 0, 1);\r
366                 }\r
367                 \r
368                 /// <summary>\r
369                 /// Writes bytes from an array to the compressed stream.\r
370                 /// </summary>\r
371                 /// <param name="buf">\r
372                 /// The byte array\r
373                 /// </param>\r
374                 /// <param name="off">\r
375                 /// The offset into the byte array where to start.\r
376                 /// </param>\r
377                 /// <param name="len">\r
378                 /// The number of bytes to write.\r
379                 /// </param>\r
380                 public override void Write(byte[] buf, int off, int len)\r
381                 {\r
382                         def.SetInput(buf, off, len);\r
383                         Deflate();\r
384                 }\r
385                 \r
386                 #region Encryption\r
387                 \r
388                 // TODO:  Refactor this code.  The presence of Zip specific code in this low level class is wrong\r
389                 string password = null;\r
390                 uint[] keys     = null;\r
391                 \r
392                 /// <summary>\r
393                 /// Get/set the password used for encryption.  When null no encryption is performed\r
394                 /// </summary>\r
395                 public string Password {\r
396                         get { \r
397                                 return password; \r
398                         }\r
399                         set {\r
400                                 if ( value != null && value.Length == 0 ) {\r
401                                         password = null;\r
402                                 } else {\r
403                                         password = value; \r
404                                 }\r
405                         }\r
406                 }\r
407                 \r
408                 \r
409                 /// <summary>\r
410                 /// Encrypt a single byte \r
411                 /// </summary>\r
412                 /// <returns>\r
413                 /// The encrypted value\r
414                 /// </returns>\r
415                 protected byte EncryptByte()\r
416                 {\r
417                         uint temp = ((keys[2] & 0xFFFF) | 2);\r
418                         return (byte)((temp * (temp ^ 1)) >> 8);\r
419                 }\r
420                 \r
421                 \r
422                 /// <summary>\r
423                 /// Encrypt a block of data\r
424                 /// </summary>\r
425                 /// <param name="buffer">\r
426                 /// Data to encrypt.  NOTE the original contents of the buffer are lost\r
427                 /// </param>\r
428                 /// <param name="offset">\r
429                 /// Offset of first byte in buffer to encrypt\r
430                 /// </param>\r
431                 /// <param name="length">\r
432                 /// Number of bytes in buffer to encrypt\r
433                 /// </param>\r
434                 protected void EncryptBlock(byte[] buffer, int offset, int length)\r
435                 {\r
436                         // TODO: refactor to use crypto transform\r
437                         for (int i = offset; i < offset + length; ++i) {\r
438                                 byte oldbyte = buffer[i];\r
439                                 buffer[i] ^= EncryptByte();\r
440                                 UpdateKeys(oldbyte);\r
441                         }\r
442                 }\r
443                 \r
444                 /// <summary>\r
445                 /// Initializes encryption keys based on given password\r
446                 /// </summary>\r
447                 protected void InitializePassword(string password) {\r
448                         keys = new uint[] {\r
449                                 0x12345678,\r
450                                 0x23456789,\r
451                                 0x34567890\r
452                         };\r
453                         \r
454                         for (int i = 0; i < password.Length; ++i) {\r
455                                 UpdateKeys((byte)password[i]);\r
456                         }\r
457                 }\r
458 \r
459                 /// <summary>\r
460                 /// Update encryption keys \r
461                 /// </summary>          \r
462                 protected void UpdateKeys(byte ch)\r
463                 {\r
464                         keys[0] = Crc32.ComputeCrc32(keys[0], ch);\r
465                         keys[1] = keys[1] + (byte)keys[0];\r
466                         keys[1] = keys[1] * 134775813 + 1;\r
467                         keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));\r
468                 }\r
469                 #endregion\r
470         }\r
471 }\r