org.jboss.netty.handler.codec.compression.ZlibDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payment-retries-plugin Show documentation
Show all versions of payment-retries-plugin Show documentation
Kill Bill Payment Retries plugin
The newest version!
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.codec.compression;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
import org.jboss.netty.util.internal.jzlib.JZlib;
import org.jboss.netty.util.internal.jzlib.ZStream;
/**
* Decompresses a {@link ChannelBuffer} using the deflate algorithm.
* @apiviz.landmark
* @apiviz.has org.jboss.netty.handler.codec.compression.ZlibWrapper
*/
public class ZlibDecoder extends OneToOneDecoder {
private final ZStream z = new ZStream();
private byte[] dictionary;
private volatile boolean finished;
/**
* Creates a new instance with the default wrapper ({@link ZlibWrapper#ZLIB}).
*
* @throws CompressionException if failed to initialize zlib
*/
public ZlibDecoder() {
this(ZlibWrapper.ZLIB);
}
/**
* Creates a new instance with the specified wrapper.
*
* @throws CompressionException if failed to initialize zlib
*/
public ZlibDecoder(ZlibWrapper wrapper) {
if (wrapper == null) {
throw new NullPointerException("wrapper");
}
synchronized (z) {
int resultCode = z.inflateInit(ZlibUtil.convertWrapperType(wrapper));
if (resultCode != JZlib.Z_OK) {
ZlibUtil.fail(z, "initialization failure", resultCode);
}
}
}
/**
* Creates a new instance with the specified preset dictionary. The wrapper
* is always {@link ZlibWrapper#ZLIB} because it is the only format that
* supports the preset dictionary.
*
* @throws CompressionException if failed to initialize zlib
*/
public ZlibDecoder(byte[] dictionary) {
if (dictionary == null) {
throw new NullPointerException("dictionary");
}
this.dictionary = dictionary;
synchronized (z) {
int resultCode;
resultCode = z.inflateInit(JZlib.W_ZLIB);
if (resultCode != JZlib.Z_OK) {
ZlibUtil.fail(z, "initialization failure", resultCode);
}
}
}
/**
* Returns {@code true} if and only if the end of the compressed stream
* has been reached.
*/
public boolean isClosed() {
return finished;
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
if (!(msg instanceof ChannelBuffer) || finished) {
return msg;
}
synchronized (z) {
try {
// Configure input.
ChannelBuffer compressed = (ChannelBuffer) msg;
byte[] in = new byte[compressed.readableBytes()];
compressed.readBytes(in);
z.next_in = in;
z.next_in_index = 0;
z.avail_in = in.length;
// Configure output.
byte[] out = new byte[in.length << 1];
ChannelBuffer decompressed = ChannelBuffers.dynamicBuffer(
compressed.order(), out.length,
ctx.getChannel().getConfig().getBufferFactory());
z.next_out = out;
z.next_out_index = 0;
z.avail_out = out.length;
loop: for (;;) {
// Decompress 'in' into 'out'
int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH);
if (z.next_out_index > 0) {
decompressed.writeBytes(out, 0, z.next_out_index);
z.avail_out = out.length;
}
z.next_out_index = 0;
switch (resultCode) {
case JZlib.Z_NEED_DICT:
if (dictionary == null) {
ZlibUtil.fail(z, "decompression failure", resultCode);
} else {
resultCode = z.inflateSetDictionary(dictionary, dictionary.length);
if (resultCode != JZlib.Z_OK) {
ZlibUtil.fail(z, "failed to set the dictionary", resultCode);
}
}
break;
case JZlib.Z_STREAM_END:
finished = true; // Do not decode anymore.
z.inflateEnd();
break loop;
case JZlib.Z_OK:
break;
case JZlib.Z_BUF_ERROR:
if (z.avail_in <= 0) {
break loop;
}
break;
default:
ZlibUtil.fail(z, "decompression failure", resultCode);
}
}
if (decompressed.writerIndex() != 0) { // readerIndex is always 0
return decompressed;
} else {
return null;
}
} finally {
// Deference the external references explicitly to tell the VM that
// the allocated byte arrays are temporary so that the call stack
// can be utilized.
// I'm not sure if the modern VMs do this optimization though.
z.next_in = null;
z.next_out = null;
}
}
}
}