001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Nam Nguyen
028: */
029:
030: package com.caucho.quercus.lib.zlib;
031:
032: import com.caucho.quercus.QuercusModuleException;
033: import com.caucho.quercus.lib.file.AbstractBinaryOutput;
034:
035: import java.io.IOException;
036: import java.io.OutputStream;
037: import java.util.zip.CRC32;
038: import java.util.zip.Deflater;
039: import java.util.zip.DeflaterOutputStream;
040:
041: /**
042: * As opposed to java's GZIPOutputStream, this class allows for more control on
043: * what is written to the underlying OutputStream.
044: *
045: * @see java.util.zip.GZIPOutputStream
046: */
047: public class ZlibOutputStream extends AbstractBinaryOutput {
048: private OutputStream _os;
049: private DeflaterOutputStream _out;
050: private CRC32 _crc32;
051:
052: private byte[] _header = { (byte) 0x1f, (byte) 0x8b, // gzip file identifier (ID1, ID2)
053: 8, // Deflate compression method (CM)
054: 0, // optional flags (FLG)
055: 0, 0, 0, 0, // modification time (MTIME)
056: 0, // extra optional flags (XFL)
057: 0x3 // operating system (OS)
058: };
059:
060: private int _encodingMode;
061: private boolean _isGzip;
062: private long _inputSize;
063:
064: /**
065: * Writes gzip header to OutputStream upon construction.
066: * XXX: set operating system (file architecure) header.
067: *
068: * @param out
069: * @param def
070: */
071: private ZlibOutputStream(OutputStream os, Deflater def)
072: throws IOException {
073: _os = os;
074: _out = new DeflaterOutputStream(_os, def);
075:
076: _os.write(_header, 0, _header.length);
077: }
078:
079: /**
080: * @param out
081: * @param compressionLevel
082: * @param strategy Deflate compression strategy
083: * @param encodingMode FORCE_GZIP to write gzwrite compatible output;
084: * FORCE_DEFLATE to write gzip header and zlib header, but do not
085: * write crc32 trailer
086: */
087: public ZlibOutputStream(OutputStream os, int compressionLevel,
088: int strategy, int encodingMode) throws IOException {
089: this (os, createDeflater(compressionLevel, strategy,
090: encodingMode));
091:
092: _isGzip = (encodingMode == ZlibModule.FORCE_GZIP);
093:
094: if (_isGzip)
095: _crc32 = new CRC32();
096: }
097:
098: /**
099: * @param out
100: * @param compressionLevel
101: * @param strategy Deflate compression strategy
102: */
103: public ZlibOutputStream(OutputStream os, int compressionLevel,
104: int strategy) throws IOException {
105: this (os, compressionLevel, strategy, ZlibModule.FORCE_GZIP);
106: }
107:
108: /**
109: * @param out
110: */
111: public ZlibOutputStream(OutputStream os) throws IOException {
112: this (os, Deflater.DEFAULT_COMPRESSION,
113: Deflater.DEFAULT_STRATEGY, ZlibModule.FORCE_GZIP);
114: }
115:
116: /**
117: * Creates a deflater based on the Zlib arguments.
118: */
119: private static Deflater createDeflater(int compressionLevel,
120: int strategy, int encodingMode) {
121: Deflater def;
122:
123: if (encodingMode == ZlibModule.FORCE_GZIP)
124: def = new Deflater(compressionLevel, true);
125: else
126: def = new Deflater(compressionLevel, false);
127:
128: def.setStrategy(strategy);
129:
130: return def;
131: }
132:
133: /**
134: * Writes a byte.
135: *
136: * @param input
137: */
138: public void write(int v) throws IOException {
139: _out.write(v);
140:
141: _inputSize++;
142:
143: if (_isGzip)
144: _crc32.update(v);
145: }
146:
147: /**
148: * @param input
149: * @param offset
150: * @param length
151: */
152: public void write(byte[] buffer, int offset, int length)
153: throws IOException {
154: _out.write(buffer, offset, length);
155:
156: _inputSize += length;
157:
158: if (_isGzip)
159: _crc32.update(buffer, offset, length);
160: }
161:
162: private void finish(DeflaterOutputStream out) throws IOException {
163: out.finish();
164:
165: OutputStream os = _os;
166:
167: if (_isGzip) {
168: long crcValue = _crc32.getValue();
169:
170: byte[] trailerCRC = new byte[4];
171:
172: trailerCRC[0] = (byte) crcValue;
173: trailerCRC[1] = (byte) (crcValue >> 8);
174: trailerCRC[2] = (byte) (crcValue >> 16);
175: trailerCRC[3] = (byte) (crcValue >> 24);
176:
177: _os.write(trailerCRC, 0, trailerCRC.length);
178: }
179:
180: _os.write((byte) _inputSize);
181: _os.write((byte) (_inputSize >> 8));
182: _os.write((byte) (_inputSize >> 16));
183: _os.write((byte) (_inputSize >> 24));
184:
185: _os.flush();
186: }
187:
188: public void flush() {
189: }
190:
191: public void closeWrite() {
192: close();
193: }
194:
195: public void close() {
196: try {
197: DeflaterOutputStream out = _out;
198: _out = null;
199:
200: if (out != null) {
201: finish(out);
202:
203: out.close();
204: }
205:
206: _os.close();
207: } catch (IOException e) {
208: throw new RuntimeException(e);
209: }
210: }
211:
212: /**
213: * Returns false always for an write stream.
214: */
215: public boolean isEOF() {
216: return false;
217: }
218:
219: /**
220: * Tells the position in the gzip stream
221: */
222: public long getPosition() {
223: return _inputSize;
224: }
225:
226: /**
227: * Sets the position.
228: */
229: public boolean setPosition(long offset) {
230: if (offset < _inputSize)
231: return false;
232:
233: offset -= _inputSize;
234: byte[] buffer = new byte[128];
235:
236: try {
237: while (offset > 0) {
238: int sublen = (int) Math.min(offset, buffer.length);
239: write(buffer, 0, sublen);
240: offset -= sublen;
241: }
242:
243: return true;
244: } catch (IOException e) {
245: throw new QuercusModuleException(e);
246: }
247: }
248:
249: public String toString() {
250: return "ZlibOutputStream[]";
251: }
252: }
|