private void compress(FrameEntry entry, boolean first)
{
// Get a chunk of the payload to avoid to blow
// the heap if the payload is a huge mapped file.
Frame frame = entry.frame;
ByteBuffer data = frame.getPayload();
int remaining = data.remaining();
int inputLength = Math.min(remaining, 32 * 1024);
LOG.debug("Compressing {}: {} bytes in {} bytes chunk", entry, remaining, inputLength);
// Avoid to copy the bytes if the ByteBuffer
// is backed by an array.
int inputOffset;
byte[] input;
if (data.hasArray())
{
input = data.array();
int position = data.position();
inputOffset = position + data.arrayOffset();
data.position(position + inputLength);
}
else
{
input = new byte[inputLength];
inputOffset = 0;
data.get(input, 0, inputLength);
}
finished = inputLength == remaining;
compressor.setInput(input, inputOffset, inputLength);
// Use an additional space in case the content is not compressible.
byte[] output = new byte[inputLength + 64];
int outputOffset = 0;
int outputLength = 0;
while (true)
{
int space = output.length - outputOffset;
int compressed = compressor.deflate(output, outputOffset, space, Deflater.SYNC_FLUSH);
outputLength += compressed;
if (compressed < space)
{
// Everything was compressed.
break;
}
else
{
// The compressed output is bigger than the uncompressed input.
byte[] newOutput = new byte[output.length * 2];
System.arraycopy(output, 0, newOutput, 0, output.length);
outputOffset += output.length;
output = newOutput;
}
}
// Skip the last tail bytes bytes generated by SYNC_FLUSH.
payload = ByteBuffer.wrap(output, 0, outputLength - TAIL_BYTES.length);
LOG.debug("Compressed {}: {}->{} chunk bytes", entry, inputLength, outputLength);
boolean continuation = frame.getType().isContinuation() || !first;
DataFrame chunk = new DataFrame(frame, continuation);
chunk.setRsv1(true);
chunk.setPayload(payload);
boolean fin = frame.isFin() && finished;
chunk.setFin(fin);
nextOutgoingFrame(chunk, this, entry.batchMode);
}