* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / GZip / GzipInputStream.cs
1 // GzipInputStream.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 \r
42 using ICSharpCode.SharpZipLib.Checksums;\r
43 using ICSharpCode.SharpZipLib.Zip.Compression;\r
44 using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
45 \r
46 namespace ICSharpCode.SharpZipLib.GZip \r
47 {\r
48         \r
49         /// <summary>\r
50         /// This filter stream is used to decompress a "GZIP" format stream.\r
51         /// The "GZIP" format is described baseInputStream RFC 1952.\r
52         /// \r
53         /// author of the original java version : John Leuner\r
54         /// </summary>\r
55         /// <example> This sample shows how to unzip a gzipped file\r
56         /// <code>\r
57         /// using System;\r
58         /// using System.IO;\r
59         /// \r
60         /// using ICSharpCode.SharpZipLib.GZip;\r
61         /// \r
62         /// class MainClass\r
63         /// {\r
64         ///     public static void Main(string[] args)\r
65         ///     {\r
66         ///             Stream s = new GZipInputStream(File.OpenRead(args[0]));\r
67         ///             FileStream fs = File.Create(Path.GetFileNameWithoutExtension(args[0]));\r
68         ///             int size = 2048;\r
69         ///             byte[] writeData = new byte[2048];\r
70         ///             while (true) {\r
71         ///                     size = s.Read(writeData, 0, size);\r
72         ///                     if (size > 0) {\r
73         ///                             fs.Write(writeData, 0, size);\r
74         ///                     } else {\r
75         ///                             break;\r
76         ///                     }\r
77         ///             }\r
78         ///             s.Close();\r
79         ///     }\r
80         /// }   \r
81         /// </code>\r
82         /// </example>\r
83         public class GZipInputStream : InflaterInputStream \r
84         {\r
85                 /// <summary>\r
86                 /// CRC-32 value for uncompressed data\r
87                 /// </summary>\r
88                 protected Crc32 crc = new Crc32();\r
89                 \r
90                 /// <summary>\r
91                 /// Indicates end of stream\r
92                 /// </summary>\r
93                 protected bool eos;\r
94                 \r
95                 // Have we read the GZIP header yet?\r
96                 bool readGZIPHeader;\r
97                 \r
98                 /// <summary>\r
99                 /// Creates a GzipInputStream with the default buffer size\r
100                 /// </summary>\r
101                 /// <param name="baseInputStream">\r
102                 /// The stream to read compressed data from (baseInputStream GZIP format)\r
103                 /// </param>\r
104                 public GZipInputStream(Stream baseInputStream) : this(baseInputStream, 4096)\r
105                 {\r
106                 }\r
107                 \r
108                 /// <summary>\r
109                 /// Creates a GZIPInputStream with the specified buffer size\r
110                 /// </summary>\r
111                 /// <param name="baseInputStream">\r
112                 /// The stream to read compressed data from (baseInputStream GZIP format)\r
113                 /// </param>\r
114                 /// <param name="size">\r
115                 /// Size of the buffer to use\r
116                 /// </param>\r
117                 public GZipInputStream(Stream baseInputStream, int size) : base(baseInputStream, new Inflater(true), size)\r
118                 {\r
119                 }\r
120                 \r
121                 /// <summary>\r
122                 /// Reads uncompressed data into an array of bytes\r
123                 /// </summary>\r
124                 /// <param name="buf">\r
125                 /// The buffer to read uncompressed data into\r
126                 /// </param>\r
127                 /// <param name="offset">\r
128                 /// The offset indicating where the data should be placed\r
129                 /// </param>\r
130                 /// <param name="len">\r
131                 /// The number of uncompressed bytes to be read\r
132                 /// </param>\r
133                 public override int Read(byte[] buf, int offset, int len) \r
134                 {\r
135                         // We first have to read the GZIP header, then we feed all the\r
136                         // rest of the data to the base class.\r
137                         //\r
138                         // As we do that we continually update the CRC32. Once the data is\r
139                         // finished, we check the CRC32\r
140                         //\r
141                         // This means we don't need our own buffer, as everything is done\r
142                         // baseInputStream the superclass.\r
143                         if (!readGZIPHeader) {\r
144                                 ReadHeader();\r
145                         }\r
146                         \r
147                         if (eos) {\r
148                                 return 0;\r
149                         }\r
150                         \r
151                         // We don't have to read the header, so we just grab data from the superclass\r
152                         int numRead = base.Read(buf, offset, len);\r
153                         if (numRead > 0) {\r
154                                 crc.Update(buf, offset, numRead);\r
155                         }\r
156                         \r
157                         if (inf.IsFinished) {\r
158                                 ReadFooter();\r
159                         }\r
160                         return numRead;\r
161                 }\r
162                 \r
163                 void ReadHeader() \r
164                 {\r
165                         /* 1. Check the two magic bytes */\r
166                         Crc32 headCRC = new Crc32();\r
167                         int magic = baseInputStream.ReadByte();\r
168                         if (magic < 0) {\r
169                                 eos = true;\r
170                                 return;\r
171                         }\r
172                         headCRC.Update(magic);\r
173                         if (magic != (GZipConstants.GZIP_MAGIC >> 8)) {\r
174                                 throw new GZipException("Error baseInputStream GZIP header, first byte doesn't match");\r
175                         }\r
176                                 \r
177                         magic = baseInputStream.ReadByte();\r
178                         if (magic != (GZipConstants.GZIP_MAGIC & 0xFF)) {\r
179                                 throw new GZipException("Error baseInputStream GZIP header,  second byte doesn't match");\r
180                         }\r
181                         headCRC.Update(magic);\r
182                         \r
183                         /* 2. Check the compression type (must be 8) */\r
184                         int CM = baseInputStream.ReadByte();\r
185                         if (CM != 8) {\r
186                                 throw new GZipException("Error baseInputStream GZIP header, data not baseInputStream deflate format");\r
187                         }\r
188                         headCRC.Update(CM);\r
189                         \r
190                         /* 3. Check the flags */\r
191                         int flags = baseInputStream.ReadByte();\r
192                         if (flags < 0) {\r
193                                 throw new GZipException("Early EOF baseInputStream GZIP header");\r
194                         }\r
195                         headCRC.Update(flags);\r
196                         \r
197                         /*    This flag byte is divided into individual bits as follows:\r
198                                 \r
199                                 bit 0   FTEXT\r
200                                 bit 1   FHCRC\r
201                                 bit 2   FEXTRA\r
202                                 bit 3   FNAME\r
203                                 bit 4   FCOMMENT\r
204                                 bit 5   reserved\r
205                                 bit 6   reserved\r
206                                 bit 7   reserved\r
207                                 */\r
208                                 \r
209                         /* 3.1 Check the reserved bits are zero */\r
210                         \r
211                         if ((flags & 0xd0) != 0) {\r
212                                 throw new GZipException("Reserved flag bits baseInputStream GZIP header != 0");\r
213                         }\r
214                         \r
215                         /* 4.-6. Skip the modification time, extra flags, and OS type */\r
216                         for (int i=0; i< 6; i++) {\r
217                                 int readByte = baseInputStream.ReadByte();\r
218                                 if (readByte < 0) {\r
219                                         throw new GZipException("Early EOF baseInputStream GZIP header");\r
220                                 }\r
221                                 headCRC.Update(readByte);\r
222                         }\r
223                         \r
224                         /* 7. Read extra field */\r
225                         if ((flags & GZipConstants.FEXTRA) != 0) {\r
226                                 /* Skip subfield id */\r
227                                 for (int i=0; i< 2; i++) {\r
228                                         int readByte = baseInputStream.ReadByte();\r
229                                         if (readByte < 0) {\r
230                                                 throw new GZipException("Early EOF baseInputStream GZIP header");\r
231                                         }\r
232                                         headCRC.Update(readByte);\r
233                                 }\r
234                                 if (baseInputStream.ReadByte() < 0 || baseInputStream.ReadByte() < 0) {\r
235                                         throw new GZipException("Early EOF baseInputStream GZIP header");\r
236                                 }\r
237                                 \r
238                                 int len1, len2, extraLen;\r
239                                 len1 = baseInputStream.ReadByte();\r
240                                 len2 = baseInputStream.ReadByte();\r
241                                 if ((len1 < 0) || (len2 < 0)) {\r
242                                         throw new GZipException("Early EOF baseInputStream GZIP header");\r
243                                 }\r
244                                 headCRC.Update(len1);\r
245                                 headCRC.Update(len2);\r
246                                 \r
247                                 extraLen = (len1 << 8) | len2;\r
248                                 for (int i = 0; i < extraLen;i++) {\r
249                                         int readByte = baseInputStream.ReadByte();\r
250                                         if (readByte < 0) \r
251                                         {\r
252                                                 throw new GZipException("Early EOF baseInputStream GZIP header");\r
253                                         }\r
254                                         headCRC.Update(readByte);\r
255                                 }\r
256                         }\r
257                         \r
258                         /* 8. Read file name */\r
259                         if ((flags & GZipConstants.FNAME) != 0) {\r
260                                 int readByte;\r
261                                 while ( (readByte = baseInputStream.ReadByte()) > 0) {\r
262                                         headCRC.Update(readByte);\r
263                                 }\r
264                                 if (readByte < 0) {\r
265                                         throw new GZipException("Early EOF baseInputStream GZIP file name");\r
266                                 }\r
267                                 headCRC.Update(readByte);\r
268                         }\r
269                         \r
270                         /* 9. Read comment */\r
271                         if ((flags & GZipConstants.FCOMMENT) != 0) {\r
272                                 int readByte;\r
273                                 while ( (readByte = baseInputStream.ReadByte()) > 0) {\r
274                                         headCRC.Update(readByte);\r
275                                 }\r
276                                 \r
277                                 if (readByte < 0) {\r
278                                         throw new GZipException("Early EOF baseInputStream GZIP comment");\r
279                                 }\r
280                                 headCRC.Update(readByte);\r
281                         }\r
282                         \r
283                         /* 10. Read header CRC */\r
284                         if ((flags & GZipConstants.FHCRC) != 0) {\r
285                                 int tempByte;\r
286                                 int crcval = baseInputStream.ReadByte();\r
287                                 if (crcval < 0) {\r
288                                         throw new GZipException("Early EOF baseInputStream GZIP header");\r
289                                 }\r
290                                 \r
291                                 tempByte = baseInputStream.ReadByte();\r
292                                 if (tempByte < 0) {\r
293                                         throw new GZipException("Early EOF baseInputStream GZIP header");\r
294                                 }\r
295                                 \r
296                                 crcval = (crcval << 8) | tempByte;\r
297                                 if (crcval != ((int) headCRC.Value & 0xffff)) {\r
298                                         throw new GZipException("Header CRC value mismatch");\r
299                                 }\r
300                         }\r
301                         \r
302                         readGZIPHeader = true;\r
303                 }\r
304                 \r
305                 void ReadFooter() \r
306                 {\r
307                         byte[] footer = new byte[8];\r
308                         int avail = inf.RemainingInput;\r
309                         \r
310                         if (avail > 8) {\r
311                                 avail = 8;\r
312                         }\r
313                         \r
314          System.Array.Copy(inputBuffer.RawData, inputBuffer.RawLength - inf.RemainingInput, footer, 0, avail);\r
315          int needed = 8 - avail;\r
316                         \r
317                         while (needed > 0) {\r
318                                 int count = baseInputStream.Read(footer, 8 - needed, needed);\r
319                                 if (count <= 0) {\r
320                                         throw new GZipException("Early EOF baseInputStream GZIP footer");\r
321                                 }\r
322                                 needed -= count; // Jewel Jan 16\r
323                         }\r
324                         int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24);\r
325                         if (crcval != (int) crc.Value) {\r
326                                 throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int) crc.Value);\r
327                         }\r
328                         \r
329                         int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8) | ((footer[6] & 0xff) << 16) | (footer[7] << 24);\r
330                         if (total != inf.TotalOut) {\r
331                                 throw new GZipException("Number of bytes mismatch in footer");\r
332                         }\r
333                         \r
334                         /* XXX Should we support multiple members.\r
335                         * Difficult, since there may be some bytes still baseInputStream dataBuffer\r
336                         */\r
337                         eos = true;\r
338                 }\r
339         }\r
340 }\r