1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package java.util.zip;
19
20import java.io.IOException;
21import java.io.OutputStream;
22
23/**
24 * The {@code GZIPOutputStream} class is used to write data to a stream in the
25 * GZIP storage format.
26 *
27 * <h3>Example</h3>
28 * <p>Using {@code GZIPOutputStream} is a little easier than {@link ZipOutputStream}
29 * because GZIP is only for compression, and is not a container for multiple files.
30 * This code creates a GZIP stream, similar to the {@code gzip(1)} utility.
31 * <pre>
32 * OutputStream os = ...
33 * byte[] bytes = ...
34 * GZIPOutputStream zos = new GZIPOutputStream(new BufferedOutputStream(os));
35 * try {
36 *     zos.write(bytes);
37 * } finally {
38 *     zos.close();
39 * }
40 * </pre>
41 */
42public class GZIPOutputStream extends DeflaterOutputStream {
43
44    /**
45     * The checksum algorithm used when treating uncompressed data.
46     */
47    protected CRC32 crc = new CRC32();
48
49    /**
50     * Construct a new {@code GZIPOutputStream} to write data in GZIP format to
51     * the underlying stream.
52     *
53     * @param os
54     *            the {@code OutputStream} to write data to.
55     * @throws IOException
56     *             if an {@code IOException} occurs.
57     */
58    public GZIPOutputStream(OutputStream os) throws IOException {
59        this(os, BUF_SIZE);
60    }
61
62    /**
63     * Construct a new {@code GZIPOutputStream} to write data in GZIP format to
64     * the underlying stream. Set the internal compression buffer to size
65     * {@code size}.
66     *
67     * @param os
68     *            the {@code OutputStream} to write to.
69     * @param size
70     *            the internal buffer size.
71     * @throws IOException
72     *             if an {@code IOException} occurs.
73     */
74    public GZIPOutputStream(OutputStream os, int size) throws IOException {
75        super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size);
76        writeShort(GZIPInputStream.GZIP_MAGIC);
77        out.write(Deflater.DEFLATED);
78        out.write(0); // flags
79        writeLong(0); // mod time
80        out.write(0); // extra flags
81        out.write(0); // operating system
82    }
83
84    /**
85     * Indicates to the stream that all data has been written out, and any GZIP
86     * terminal data can now be written.
87     *
88     * @throws IOException
89     *             if an {@code IOException} occurs.
90     */
91    @Override
92    public void finish() throws IOException {
93        super.finish();
94        writeLong(crc.getValue());
95        writeLong(crc.tbytes);
96    }
97
98    /**
99     * Write up to nbytes of data from the given buffer, starting at offset off,
100     * to the underlying stream in GZIP format.
101     */
102    @Override
103    public void write(byte[] buffer, int off, int nbytes) throws IOException {
104        super.write(buffer, off, nbytes);
105        crc.update(buffer, off, nbytes);
106    }
107
108    private long writeLong(long i) throws IOException {
109        // Write out the long value as an unsigned int
110        int unsigned = (int) i;
111        out.write(unsigned & 0xFF);
112        out.write((unsigned >> 8) & 0xFF);
113        out.write((unsigned >> 16) & 0xFF);
114        out.write((unsigned >> 24) & 0xFF);
115        return i;
116    }
117
118    private int writeShort(int i) throws IOException {
119        out.write(i & 0xFF);
120        out.write((i >> 8) & 0xFF);
121        return i;
122    }
123}
124