* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Compat.ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / GZip / GZipInputStream.cs
1 // GzipInputStream.cs\r
2 // Copyright (C) 2001 Mike Krueger\r
3 //\r
4 // This file was translated from java, it was part of the GNU Classpath\r
5 // Copyright (C) 2001 Free Software Foundation, Inc.\r
6 //\r
7 // This program is free software; you can redistribute it and/or\r
8 // modify it under the terms of the GNU General Public License\r
9 // as published by the Free Software Foundation; either version 2\r
10 // of the License, or (at your option) any later version.\r
11 //\r
12 // This program is distributed in the hope that it will be useful,\r
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 // GNU General Public License for more details.\r
16 //\r
17 // You should have received a copy of the GNU General Public License\r
18 // along with this program; if not, write to the Free Software\r
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
20 //\r
21 // Linking this library statically or dynamically with other modules is\r
22 // making a combined work based on this library.  Thus, the terms and\r
23 // conditions of the GNU General Public License cover the whole\r
24 // combination.\r
25 // \r
26 // As a special exception, the copyright holders of this library give you\r
27 // permission to link this library with independent modules to produce an\r
28 // executable, regardless of the license terms of these independent\r
29 // modules, and to copy and distribute the resulting executable under\r
30 // terms of your choice, provided that you also meet, for each linked\r
31 // independent module, the terms and conditions of the license of that\r
32 // module.  An independent module is a module which is not derived from\r
33 // or based on this library.  If you modify this library, you may extend\r
34 // this exception to your version of the library, but you are not\r
35 // obligated to do so.  If you do not wish to do so, delete this\r
36 // exception statement from your version.\r
37 \r
38 using System;\r
39 using System.IO;\r
40 \r
41 using ICSharpCode.SharpZipLib.Checksums;\r
42 using ICSharpCode.SharpZipLib.Zip.Compression;\r
43 using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
44 \r
45 namespace ICSharpCode.SharpZipLib.GZip \r
46 {\r
47         \r
48         /// <summary>\r
49         /// This filter stream is used to decompress a "GZIP" format stream.\r
50         /// The "GZIP" format is described baseInputStream RFC 1952.\r
51         /// \r
52         /// author of the original java version : John Leuner\r
53         /// </summary>\r
54         /// <example> This sample shows how to unzip a gzipped file\r
55         /// <code>\r
56         /// using System;\r
57         /// using System.IO;\r
58         /// \r
59         /// using NZlib.GZip;\r
60         /// \r
61         /// class MainClass\r
62         /// {\r
63         ///     public static void Main(string[] args)\r
64         ///     {\r
65         ///             Stream s = new GZipInputStream(File.OpenRead(args[0]));\r
66         ///             FileStream fs = File.Create(Path.GetFileNameWithoutExtension(args[0]));\r
67         ///             int size = 2048;\r
68         ///             byte[] writeData = new byte[2048];\r
69         ///             while (true) {\r
70         ///                     size = s.Read(writeData, 0, size);\r
71         ///                     if (size > 0) {\r
72         ///                             fs.Write(writeData, 0, size);\r
73         ///                     } else {\r
74         ///                             break;\r
75         ///                     }\r
76         ///             }\r
77         ///             s.Close();\r
78         ///     }\r
79         /// }   \r
80         /// </code>\r
81         /// </example>\r
82         public class GZipInputStream : InflaterInputStream \r
83         {\r
84                 /// <summary>\r
85                 /// CRC-32 value for uncompressed data\r
86                 /// </summary>\r
87                 protected Crc32 crc = new Crc32();\r
88                 \r
89                 /// <summary>\r
90                 /// Indicates end of stream\r
91                 /// </summary>\r
92                 protected bool eos;\r
93                 \r
94                 // Have we read the GZIP header yet?\r
95                 bool readGZIPHeader;\r
96                 \r
97                 /// <summary>\r
98                 /// Creates a GzipInputStream with the default buffer size\r
99                 /// </summary>\r
100                 /// <param name="baseInputStream">\r
101                 /// The stream to read compressed data from (baseInputStream GZIP format)\r
102                 /// </param>\r
103                 public GZipInputStream(Stream baseInputStream) : this(baseInputStream, 4096)\r
104                 {\r
105                 }\r
106                 \r
107                 /// <summary>\r
108                 /// Creates a GZIPInputStream with the specified buffer size\r
109                 /// </summary>\r
110                 /// <param name="baseInputStream">\r
111                 /// The stream to read compressed data from (baseInputStream GZIP format)\r
112                 /// </param>\r
113                 /// <param name="size">\r
114                 /// Size of the buffer to use\r
115                 /// </param>\r
116                 public GZipInputStream(Stream baseInputStream, int size) : base(baseInputStream, new Inflater(true), size)\r
117                 {\r
118                 }\r
119                 \r
120                 /// <summary>\r
121                 /// Reads uncompressed data into an array of bytes\r
122                 /// </summary>\r
123                 /// <param name="buf">\r
124                 /// the buffer to read uncompressed data into\r
125                 /// </param>\r
126                 /// <param name="offset">\r
127                 /// the offset indicating where the data should be placed\r
128                 /// </param>\r
129                 /// <param name="len">\r
130                 /// the number of uncompressed bytes to be read\r
131                 /// </param>\r
132                 public override int Read(byte[] buf, int offset, int len) \r
133                 {\r
134                         // We first have to slurp baseInputStream the GZIP header, then we feed all the\r
135                         // rest of the data to the superclass.\r
136                         //\r
137                         // As we do that we continually update the CRC32. Once the data is\r
138                         // finished, we check the CRC32\r
139                         //\r
140                         // This means we don't need our own buffer, as everything is done\r
141                         // baseInputStream the superclass.\r
142                         if (!readGZIPHeader) {\r
143                                 ReadHeader();\r
144                         }\r
145                         \r
146                         if (eos) {\r
147                                 return 0;\r
148                         }\r
149                         \r
150                         //    System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len);\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                 private 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 IOException("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 IOException("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 IOException("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 Exception("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 IOException("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 Exception("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 Exception("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 Exception("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 Exception("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 Exception("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 Exception("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 Exception("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 Exception("Early EOF baseInputStream GZIP header");\r
289                                 }\r
290                                 \r
291                                 tempByte = baseInputStream.ReadByte();\r
292                                 if (tempByte < 0) {\r
293                                         throw new Exception("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 IOException("Header CRC value mismatch");\r
299                                 }\r
300                         }\r
301                         \r
302                         readGZIPHeader = true;\r
303                         //System.err.println("Read GZIP header");\r
304                 }\r
305                 \r
306                 private void ReadFooter() \r
307                 {\r
308                         byte[] footer = new byte[8];\r
309                         int avail = inf.RemainingInput;\r
310                         if (avail > 8) {\r
311                                 avail = 8;\r
312                         }\r
313                         System.Array.Copy(buf, len - inf.RemainingInput, footer, 0, avail);\r
314                         int needed = 8 - avail;\r
315                         while (needed > 0) {\r
316                                 int count = baseInputStream.Read(footer, 8-needed, needed);\r
317                                 if (count <= 0) {\r
318                                         throw new Exception("Early EOF baseInputStream GZIP footer");\r
319                                 }\r
320                                 needed -= count; //Jewel Jan 16\r
321                         }\r
322                         int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24);\r
323                         if (crcval != (int) crc.Value) {\r
324                                 throw new IOException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int) crc.Value);\r
325                         }\r
326                         int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8) | ((footer[6] & 0xff) << 16) | (footer[7] << 24);\r
327                         if (total != inf.TotalOut) {\r
328                                 throw new IOException("Number of bytes mismatch");\r
329                         }\r
330                         /* XXX Should we support multiple members.\r
331                         * Difficult, since there may be some bytes still baseInputStream buf\r
332                         */\r
333                         eos = true;\r
334                 }\r
335         }\r
336 }\r