package org.glassfish.grizzly.compression.zip;
import java.nio.ByteBuffer;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import org.glassfish.grizzly.AbstractTransformer;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.TransformationException;
import org.glassfish.grizzly.TransformationResult;
import org.glassfish.grizzly.attributes.AttributeStorage;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.ByteBufferArray;
import org.glassfish.grizzly.memory.MemoryManager;
public class GZipEncoder extends AbstractTransformer<Buffer, Buffer> {
private static final int GZIP_MAGIC = 0x8b1f;
private static final int TRAILER_SIZE = 8;
private final int bufferSize;
private static final Buffer ;
static {
header = MemoryManager.DEFAULT_MEMORY_MANAGER.allocate(10);
header.put((byte) GZIP_MAGIC);
header.put((byte) (GZIP_MAGIC >> 8));
header.put((byte) Deflater.DEFLATED);
header.put((byte) 0);
header.put((byte) 0);
header.put((byte) 0);
header.put((byte) 0);
header.put((byte) 0);
header.put((byte) 0);
header.put((byte) 0);
header.flip();
}
public GZipEncoder() {
this(512);
}
public GZipEncoder(int bufferSize) {
this.bufferSize = bufferSize;
}
@Override
public String getName() {
return "gzip-encoder";
}
@Override
public boolean hasInputRemaining(AttributeStorage storage, Buffer input) {
return input.hasRemaining();
}
@Override
protected GZipOutputState createStateObject() {
return new GZipOutputState();
}
@Override
protected TransformationResult<Buffer, Buffer> transformImpl(
AttributeStorage storage, Buffer input) throws TransformationException {
final MemoryManager memoryManager = obtainMemoryManager(storage);
final GZipOutputState state = (GZipOutputState) obtainStateObject(storage);
if (!state.isInitialized) {
state.initialize();
}
Buffer encodedBuffer = null;
if (input != null && input.hasRemaining()) {
encodedBuffer = encodeBuffer(input, state, memoryManager);
}
if (encodedBuffer == null) {
return TransformationResult.createIncompletedResult(null);
}
if (!state.isHeaderWritten) {
state.isHeaderWritten = true;
encodedBuffer = Buffers.appendBuffers(memoryManager,
getHeader(), encodedBuffer);
}
return TransformationResult.createCompletedResult(encodedBuffer, null);
}
public Buffer finish(AttributeStorage storage) {
final MemoryManager memoryManager = obtainMemoryManager(storage);
final GZipOutputState state = (GZipOutputState) obtainStateObject(storage);
Buffer resultBuffer = null;
if (state.isInitialized) {
final Deflater deflater = state.deflater;
if (!deflater.finished()) {
deflater.finish();
while (!deflater.finished()) {
resultBuffer = Buffers.appendBuffers(memoryManager,
resultBuffer,
deflate(deflater, memoryManager));
}
if (!state.isHeaderWritten) {
state.isHeaderWritten = true;
resultBuffer = Buffers.appendBuffers(memoryManager,
getHeader(), resultBuffer);
}
final Buffer trailer = memoryManager.allocate(TRAILER_SIZE);
final CRC32 crc32 = state.crc32;
putUInt(trailer, (int) crc32.getValue());
putUInt(trailer, deflater.getTotalIn());
trailer.flip();
resultBuffer = Buffers.appendBuffers(memoryManager,
resultBuffer, trailer);
}
state.reset();
}
return resultBuffer;
}
private Buffer () {
final Buffer headerToWrite = header.duplicate();
headerToWrite.allowBufferDispose(false);
return headerToWrite;
}
private Buffer encodeBuffer(Buffer buffer,
GZipOutputState state, MemoryManager memoryManager) {
final CRC32 crc32 = state.crc32;
final Deflater deflater = state.deflater;
if (deflater.finished()) {
throw new IllegalStateException("write beyond end of stream");
}
int stride = bufferSize;
Buffer resultBuffer = null;
final ByteBufferArray byteBufferArray = buffer.toByteBufferArray();
final ByteBuffer[] buffers = byteBufferArray.getArray();
final int size = byteBufferArray.size();
for (int i = 0; i < size; i++) {
final ByteBuffer byteBuffer = buffers[i];
final int len = byteBuffer.remaining();
if (len > 0) {
final byte[] buf;
final int off;
if (byteBuffer.hasArray()) {
buf = byteBuffer.array();
off = byteBuffer.arrayOffset() + byteBuffer.position();
} else {
buf = new byte[len];
off = 0;
byteBuffer.get(buf);
byteBuffer.position(byteBuffer.position() - len);
}
for (int j = 0; j < len; j += stride) {
deflater.setInput(buf, off + j, Math.min(stride, len - j));
while (!deflater.needsInput()) {
final Buffer deflated = deflate(deflater, memoryManager);
if (deflated != null) {
resultBuffer = Buffers.appendBuffers(
memoryManager, resultBuffer, deflated);
}
}
}
crc32.update(buf, off, len);
}
}
byteBufferArray.restore();
byteBufferArray.recycle();
buffer.position(buffer.limit());
return resultBuffer;
}
protected Buffer deflate(Deflater deflater, MemoryManager memoryManager) {
final Buffer buffer = memoryManager.allocate(bufferSize);
final ByteBuffer byteBuffer = buffer.toByteBuffer();
final byte[] array = byteBuffer.array();
final int offset = byteBuffer.arrayOffset() + byteBuffer.position();
int len = deflater.deflate(array, offset, bufferSize);
if (len <= 0) {
buffer.dispose();
return null;
}
buffer.position(len);
buffer.trim();
return buffer;
}
private static void putUInt(Buffer buffer, int value) {
putUShort(buffer, value & 0xffff);
putUShort(buffer, (value >> 16) & 0xffff);
}
private static void putUShort(Buffer buffer, int value) {
buffer.put((byte) (value & 0xff));
buffer.put((byte) ((value >> 8) & 0xff));
}
protected static final class GZipOutputState
extends LastResultAwareState<Buffer, Buffer> {
private boolean isInitialized;
private boolean ;
private CRC32 crc32;
private Deflater deflater;
private void initialize() {
final Deflater newDeflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
final CRC32 newCrc32 = new CRC32();
newCrc32.reset();
deflater = newDeflater;
crc32 = newCrc32;
isInitialized = true;
}
private void reset() {
isInitialized = false;
isHeaderWritten = false;
deflater.end();
crc32 = null;
deflater = null;
}
}
}