• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 Google Inc. All Rights Reserved.
2 
3    Distributed under MIT license.
4    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5 */
6 
7 package org.brotli.wrapper.dec;
8 
9 import java.io.IOException;
10 import java.nio.ByteBuffer;
11 
12 /**
13  * JNI wrapper for brotli decoder.
14  */
15 public class DecoderJNI {
nativeCreate(long[] context)16   private static native ByteBuffer nativeCreate(long[] context);
nativePush(long[] context, int length)17   private static native void nativePush(long[] context, int length);
nativePull(long[] context)18   private static native ByteBuffer nativePull(long[] context);
nativeDestroy(long[] context)19   private static native void nativeDestroy(long[] context);
20 
21   public enum Status {
22     ERROR,
23     DONE,
24     NEEDS_MORE_INPUT,
25     NEEDS_MORE_OUTPUT,
26     OK
27   };
28 
29   public static class Wrapper {
30     private final long[] context = new long[3];
31     private final ByteBuffer inputBuffer;
32     private Status lastStatus = Status.NEEDS_MORE_INPUT;
33 
Wrapper(int inputBufferSize)34     public Wrapper(int inputBufferSize) throws IOException {
35       this.context[1] = inputBufferSize;
36       this.inputBuffer = nativeCreate(this.context);
37       if (this.context[0] == 0) {
38         throw new IOException("failed to initialize native brotli decoder");
39       }
40     }
41 
push(int length)42     public void push(int length) {
43       if (length < 0) {
44         throw new IllegalArgumentException("negative block length");
45       }
46       if (context[0] == 0) {
47         throw new IllegalStateException("brotli decoder is already destroyed");
48       }
49       if (lastStatus != Status.NEEDS_MORE_INPUT && lastStatus != Status.OK) {
50         throw new IllegalStateException("pushing input to decoder in " + lastStatus + " state");
51       }
52       if (lastStatus == Status.OK && length != 0) {
53         throw new IllegalStateException("pushing input to decoder in OK state");
54       }
55       nativePush(context, length);
56       parseStatus();
57     }
58 
parseStatus()59     private void parseStatus() {
60       long status = context[1];
61       if (status == 1) {
62         lastStatus = Status.DONE;
63       } else if (status == 2) {
64         lastStatus = Status.NEEDS_MORE_INPUT;
65       } else if (status == 3) {
66         lastStatus = Status.NEEDS_MORE_OUTPUT;
67       } else if (status == 4) {
68         lastStatus = Status.OK;
69       } else {
70         lastStatus = Status.ERROR;
71       }
72     }
73 
getStatus()74     public Status getStatus() {
75       return lastStatus;
76     }
77 
getInputBuffer()78     public ByteBuffer getInputBuffer() {
79       return inputBuffer;
80     }
81 
hasOutput()82     public boolean hasOutput() {
83       return context[2] != 0;
84     }
85 
pull()86     public ByteBuffer pull() {
87       if (context[0] == 0) {
88         throw new IllegalStateException("brotli decoder is already destroyed");
89       }
90       if (lastStatus != Status.NEEDS_MORE_OUTPUT && !hasOutput()) {
91         throw new IllegalStateException("pulling output from decoder in " + lastStatus + " state");
92       }
93       ByteBuffer result = nativePull(context);
94       parseStatus();
95       return result;
96     }
97 
98     /**
99      * Releases native resources.
100      */
destroy()101     public void destroy() {
102       if (context[0] == 0) {
103         throw new IllegalStateException("brotli decoder is already destroyed");
104       }
105       nativeDestroy(context);
106       context[0] = 0;
107     }
108 
109     @Override
finalize()110     protected void finalize() throws Throwable {
111       if (context[0] != 0) {
112         /* TODO: log resource leak? */
113         destroy();
114       }
115       super.finalize();
116     }
117   }
118 }
119