001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *
019: */
020: package org.apache.mina.filter.compression;
021:
022: import java.io.IOException;
023:
024: import org.apache.mina.common.IoBuffer;
025:
026: import com.jcraft.jzlib.JZlib;
027: import com.jcraft.jzlib.ZStream;
028:
029: /**
030: * A helper class for interfacing with the JZlib library. This class acts both
031: * as a compressor and decompressor, but only as one at a time. The only
032: * flush method supported is <tt>Z_SYNC_FLUSH</tt> also known as <tt>Z_PARTIAL_FLUSH</tt>
033: *
034: * @author The Apache MINA Project (dev@mina.apache.org)
035: * @version $Rev: 581234 $, $Date: 2007-10-02 07:39:48 -0600 (Tue, 02 Oct 2007) $
036: */
037: class Zlib {
038: public static final int COMPRESSION_MAX = JZlib.Z_BEST_COMPRESSION;
039:
040: public static final int COMPRESSION_MIN = JZlib.Z_BEST_SPEED;
041:
042: public static final int COMPRESSION_NONE = JZlib.Z_NO_COMPRESSION;
043:
044: public static final int COMPRESSION_DEFAULT = JZlib.Z_DEFAULT_COMPRESSION;
045:
046: public static final int MODE_DEFLATER = 1;
047:
048: public static final int MODE_INFLATER = 2;
049:
050: private int compressionLevel;
051:
052: private ZStream zStream = null;
053:
054: private int mode = -1;
055:
056: /**
057: * @param compressionLevel the level of compression that should be used
058: * @param mode the mode in which the instance will operate. Can be either
059: * of <tt>MODE_DEFLATER</tt> or <tt>MODE_INFLATER</tt>
060: */
061: public Zlib(int compressionLevel, int mode) {
062: switch (compressionLevel) {
063: case COMPRESSION_MAX:
064: case COMPRESSION_MIN:
065: case COMPRESSION_NONE:
066: case COMPRESSION_DEFAULT:
067: this .compressionLevel = compressionLevel;
068: break;
069: default:
070: throw new IllegalArgumentException(
071: "invalid compression level specified");
072: }
073:
074: // create a new instance of ZStream. This will be done only once.
075: zStream = new ZStream();
076:
077: switch (mode) {
078: case MODE_DEFLATER:
079: zStream.deflateInit(this .compressionLevel);
080: break;
081: case MODE_INFLATER:
082: zStream.inflateInit();
083: break;
084: default:
085: throw new IllegalArgumentException("invalid mode specified");
086: }
087: this .mode = mode;
088: }
089:
090: /**
091: * @param inBuffer the {@link IoBuffer} to be decompressed. The contents
092: * of the buffer are transferred into a local byte array and the buffer is
093: * flipped and returned intact.
094: * @return the decompressed data
095: * @throws IOException if the decompression of the data failed for some reason.
096: */
097: public IoBuffer inflate(IoBuffer inBuffer) throws IOException {
098: if (mode == MODE_DEFLATER) {
099: throw new IllegalStateException(
100: "not initialized as INFLATER");
101: }
102:
103: byte[] inBytes = new byte[inBuffer.limit()];
104: inBuffer.get(inBytes).flip();
105:
106: byte[] outBytes = new byte[inBytes.length * 2];
107: IoBuffer outBuffer = IoBuffer.allocate(outBytes.length);
108: outBuffer.setAutoExpand(true);
109:
110: zStream.next_in = inBytes;
111: zStream.next_in_index = 0;
112: zStream.avail_in = inBytes.length;
113: zStream.next_out = outBytes;
114: zStream.next_out_index = 0;
115: zStream.avail_out = outBytes.length;
116: int retval = 0;
117:
118: do {
119: retval = zStream.inflate(JZlib.Z_SYNC_FLUSH);
120: switch (retval) {
121: case JZlib.Z_OK:
122: // completed decompression, lets copy data and get out
123: case JZlib.Z_BUF_ERROR:
124: // need more space for output. store current output and get more
125: outBuffer.put(outBytes, 0, zStream.next_out_index);
126: zStream.next_out_index = 0;
127: zStream.avail_out = outBytes.length;
128: break;
129: default:
130: // unknown error
131: outBuffer = null;
132: if (zStream.msg == null) {
133: throw new IOException(
134: "Unknown error. Error code : " + retval);
135: } else {
136: throw new IOException(
137: "Unknown error. Error code : " + retval
138: + " and message : " + zStream.msg);
139: }
140: }
141: } while (zStream.avail_in > 0);
142:
143: return outBuffer.flip();
144: }
145:
146: /**
147: * @param inBuffer the buffer to be compressed. The contents are transferred
148: * into a local byte array and the buffer is flipped and returned intact.
149: * @return the buffer with the compressed data
150: * @throws IOException if the compression of teh buffer failed for some reason
151: */
152: public IoBuffer deflate(IoBuffer inBuffer) throws IOException {
153: if (mode == MODE_INFLATER) {
154: throw new IllegalStateException(
155: "not initialized as DEFLATER");
156: }
157:
158: byte[] inBytes = new byte[inBuffer.limit()];
159: inBuffer.get(inBytes).flip();
160:
161: // according to spec, destination buffer should be 0.1% larger
162: // than source length plus 12 bytes. We add a single byte to safeguard
163: // against rounds that round down to the smaller value
164: int outLen = (int) Math.round(inBytes.length * 1.001) + 1 + 12;
165: byte[] outBytes = new byte[outLen];
166:
167: zStream.next_in = inBytes;
168: zStream.next_in_index = 0;
169: zStream.avail_in = inBytes.length;
170: zStream.next_out = outBytes;
171: zStream.next_out_index = 0;
172: zStream.avail_out = outBytes.length;
173:
174: int retval = zStream.deflate(JZlib.Z_SYNC_FLUSH);
175: if (retval != JZlib.Z_OK) {
176: outBytes = null;
177: inBytes = null;
178: throw new IOException(
179: "Compression failed with return value : " + retval);
180: }
181:
182: IoBuffer outBuf = IoBuffer.wrap(outBytes, 0,
183: zStream.next_out_index);
184:
185: return outBuf;
186: }
187:
188: /**
189: * Cleans up the resources used by the compression library.
190: */
191: public void cleanUp() {
192: if (zStream != null) {
193: zStream.free();
194: }
195: }
196: }
|