/* Copyright 2017 Google Inc. All Rights Reserved.
|
|
Distributed under MIT license.
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
*/
|
|
package org.brotli.wrapper.enc;
|
|
import java.io.IOException;
|
import java.nio.ByteBuffer;
|
|
/**
|
* JNI wrapper for brotli encoder.
|
*/
|
class EncoderJNI {
|
private static native ByteBuffer nativeCreate(long[] context);
|
private static native void nativePush(long[] context, int length);
|
private static native ByteBuffer nativePull(long[] context);
|
private static native void nativeDestroy(long[] context);
|
|
enum Operation {
|
PROCESS,
|
FLUSH,
|
FINISH
|
}
|
|
static class Wrapper {
|
protected final long[] context = new long[5];
|
private final ByteBuffer inputBuffer;
|
private boolean fresh = true;
|
|
Wrapper(int inputBufferSize, int quality, int lgwin)
|
throws IOException {
|
if (inputBufferSize <= 0) {
|
throw new IOException("buffer size must be positive");
|
}
|
this.context[1] = inputBufferSize;
|
this.context[2] = quality;
|
this.context[3] = lgwin;
|
this.inputBuffer = nativeCreate(this.context);
|
if (this.context[0] == 0) {
|
throw new IOException("failed to initialize native brotli encoder");
|
}
|
this.context[1] = 1;
|
this.context[2] = 0;
|
this.context[3] = 0;
|
}
|
|
void push(Operation op, int length) {
|
if (length < 0) {
|
throw new IllegalArgumentException("negative block length");
|
}
|
if (context[0] == 0) {
|
throw new IllegalStateException("brotli encoder is already destroyed");
|
}
|
if (!isSuccess() || hasMoreOutput()) {
|
throw new IllegalStateException("pushing input to encoder in unexpected state");
|
}
|
if (hasRemainingInput() && length != 0) {
|
throw new IllegalStateException("pushing input to encoder over previous input");
|
}
|
context[1] = op.ordinal();
|
fresh = false;
|
nativePush(context, length);
|
}
|
|
boolean isSuccess() {
|
return context[1] != 0;
|
}
|
|
boolean hasMoreOutput() {
|
return context[2] != 0;
|
}
|
|
boolean hasRemainingInput() {
|
return context[3] != 0;
|
}
|
|
boolean isFinished() {
|
return context[4] != 0;
|
}
|
|
ByteBuffer getInputBuffer() {
|
return inputBuffer;
|
}
|
|
ByteBuffer pull() {
|
if (context[0] == 0) {
|
throw new IllegalStateException("brotli encoder is already destroyed");
|
}
|
if (!isSuccess() || !hasMoreOutput()) {
|
throw new IllegalStateException("pulling while data is not ready");
|
}
|
fresh = false;
|
return nativePull(context);
|
}
|
|
/**
|
* Releases native resources.
|
*/
|
void destroy() {
|
if (context[0] == 0) {
|
throw new IllegalStateException("brotli encoder is already destroyed");
|
}
|
nativeDestroy(context);
|
context[0] = 0;
|
}
|
|
@Override
|
protected void finalize() throws Throwable {
|
if (context[0] != 0) {
|
/* TODO: log resource leak? */
|
destroy();
|
}
|
super.finalize();
|
}
|
}
|
}
|