org.eclipse.jetty.compression.brotli.internal.BrotliEncoderSink Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jetty-compression-brotli Show documentation
Show all versions of jetty-compression-brotli Show documentation
Jetty Core Compression :: Brotli Support
The newest version!
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.compression.brotli.internal;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicReference;
import com.aayushatharva.brotli4j.encoder.Encoder;
import com.aayushatharva.brotli4j.encoder.EncoderJNI;
import org.eclipse.jetty.compression.EncoderSink;
import org.eclipse.jetty.compression.brotli.BrotliEncoderConfig;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
public class BrotliEncoderSink extends EncoderSink
{
enum State
{
/**
* Taking the input and encoding.
*/
PROCESSING,
/**
* Done taking input, flushing what's left in encoder.
*/
FLUSHING,
/**
* Done flushing, performing finish operation.
*/
FINISHING,
/**
* Finish operation completed.
*/
FINISHED
}
private final EncoderJNI.Wrapper encoder;
private final ByteBuffer inputBuffer;
private final AtomicReference state = new AtomicReference<>(State.PROCESSING);
public BrotliEncoderSink(Content.Sink sink, BrotliEncoderConfig config)
{
super(sink);
try
{
Encoder.Mode mode = Encoder.Mode.of(config.getStrategy());
this.encoder = new EncoderJNI.Wrapper(config.getBufferSize(), config.getCompressionLevel(), config.getLgWindow(), mode);
this.inputBuffer = encoder.getInputBuffer();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
protected WriteRecord encode(boolean last, ByteBuffer content)
{
if (encoder.isFinished())
return null;
while (true)
{
switch (state.get())
{
case PROCESSING ->
{
try
{
while (content.hasRemaining())
{
// only encode if inputBuffer is full.
if (!inputBuffer.hasRemaining())
{
ByteBuffer output = encode(EncoderJNI.Operation.PROCESS);
if (output != null)
return new WriteRecord(false, output, Callback.NOOP);
}
// the only place the input buffer gets set.
BufferUtil.put(content, inputBuffer);
// do not flip input buffer, that's not what Brotli4j expects/wants.
}
// content is fully consumed.
if (!last)
return null;
}
finally
{
if (last)
state.compareAndSet(State.PROCESSING, State.FLUSHING);
}
}
case FLUSHING ->
{
inputBuffer.limit(inputBuffer.position());
ByteBuffer output = encode(EncoderJNI.Operation.FLUSH);
state.compareAndSet(State.FLUSHING, State.FINISHING);
if (output != null)
return new WriteRecord(false, output, Callback.NOOP);
}
case FINISHING ->
{
inputBuffer.limit(inputBuffer.position());
ByteBuffer output = encode(EncoderJNI.Operation.FINISH);
state.compareAndSet(State.FINISHING, State.FINISHED);
return new WriteRecord(true, output != null ? output : BufferUtil.EMPTY_BUFFER, Callback.NOOP);
}
case FINISHED ->
{
return null;
}
}
}
}
protected ByteBuffer encode(EncoderJNI.Operation op)
{
try
{
boolean inputPushed = false;
ByteBuffer output = null;
while (true)
{
if (!encoder.isSuccess())
{
throw new IOException("Brotli Encoder failure");
}
// process previous output before new input
else if (encoder.hasMoreOutput())
{
output = encoder.pull();
}
else if (encoder.hasRemainingInput())
{
encoder.push(op, 0);
}
else if (!inputPushed)
{
encoder.push(op, inputBuffer.limit());
inputPushed = true;
}
else
{
inputBuffer.clear();
return output;
}
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
protected void release()
{
this.encoder.destroy();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy