• 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     private boolean fresh = true;
34 
Wrapper(int inputBufferSize)35     public Wrapper(int inputBufferSize) throws IOException {
36       this.context[1] = inputBufferSize;
37       this.inputBuffer = nativeCreate(this.context);
38       if (this.context[0] == 0) {
39         throw new IOException("failed to initialize native brotli decoder");
40       }
41     }
42 
push(int length)43     public void push(int length) {
44       if (length < 0) {
45         throw new IllegalArgumentException("negative block length");
46       }
47       if (context[0] == 0) {
48         throw new IllegalStateException("brotli decoder is already destroyed");
49       }
50       if (lastStatus != Status.NEEDS_MORE_INPUT && lastStatus != Status.OK) {
51         throw new IllegalStateException("pushing input to decoder in " + lastStatus + " state");
52       }
53       if (lastStatus == Status.OK && length != 0) {
54         throw new IllegalStateException("pushing input to decoder in OK state");
55       }
56       fresh = false;
57       nativePush(context, length);
58       parseStatus();
59     }
60 
parseStatus()61     private void parseStatus() {
62       long status = context[1];
63       if (status == 1) {
64         lastStatus = Status.DONE;
65       } else if (status == 2) {
66         lastStatus = Status.NEEDS_MORE_INPUT;
67       } else if (status == 3) {
68         lastStatus = Status.NEEDS_MORE_OUTPUT;
69       } else if (status == 4) {
70         lastStatus = Status.OK;
71       } else {
72         lastStatus = Status.ERROR;
73       }
74     }
75 
getStatus()76     public Status getStatus() {
77       return lastStatus;
78     }
79 
getInputBuffer()80     public ByteBuffer getInputBuffer() {
81       return inputBuffer;
82     }
83 
hasOutput()84     public boolean hasOutput() {
85       return context[2] != 0;
86     }
87 
pull()88     public ByteBuffer pull() {
89       if (context[0] == 0) {
90         throw new IllegalStateException("brotli decoder is already destroyed");
91       }
92       if (lastStatus != Status.NEEDS_MORE_OUTPUT && !hasOutput()) {
93         throw new IllegalStateException("pulling output from decoder in " + lastStatus + " state");
94       }
95       fresh = false;
96       ByteBuffer result = nativePull(context);
97       parseStatus();
98       return result;
99     }
100 
101     /**
102      * Releases native resources.
103      */
destroy()104     public void destroy() {
105       if (context[0] == 0) {
106         throw new IllegalStateException("brotli decoder is already destroyed");
107       }
108       nativeDestroy(context);
109       context[0] = 0;
110     }
111 
112     @Override
finalize()113     protected void finalize() throws Throwable {
114       if (context[0] != 0) {
115         /* TODO: log resource leak? */
116         destroy();
117       }
118       super.finalize();
119     }
120   }
121 }
122