aboutsummaryrefslogtreecommitdiff
path: root/java/org/brotli/wrapper/dec/BrotliInputStream.java
blob: 26f7a82154cff90e8a70e351b56393aa92c042aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* 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.dec;

import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;

/**
 * InputStream that wraps native brotli decoder.
 */
public class BrotliInputStream extends InputStream {
  /** The default internal buffer size used by the decoder. */
  private static final int DEFAULT_BUFFER_SIZE = 16384;

  private final Decoder decoder;

  /**
   * Creates a BrotliInputStream.
   *
   * @param source underlying source
   * @param bufferSize intermediate buffer size
   */
  public BrotliInputStream(InputStream source, int bufferSize)
      throws IOException {
    this.decoder = new Decoder(Channels.newChannel(source), bufferSize);
  }

  public BrotliInputStream(InputStream source) throws IOException {
    this(source, DEFAULT_BUFFER_SIZE);
  }

  public void setEager(boolean eager) {
    decoder.setEager(eager);
  }

  @Override
  public void close() throws IOException {
    decoder.close();
  }

  @Override
  public int available() {
    return (decoder.buffer != null) ? decoder.buffer.remaining() : 0;
  }

  @Override
  public int read() throws IOException {
    if (decoder.closed) {
      throw new IOException("read after close");
    }
    int decoded;
    // Iterate until at leat one byte is decoded, or EOF reached.
    while (true) {
      decoded = decoder.decode();
      if (decoded != 0) {
        break;
      }
    }

    if (decoded == -1) {
      return -1;
    }
    return decoder.buffer.get() & 0xFF;
  }

  @Override
  public int read(byte[] b) throws IOException {
    return read(b, 0, b.length);
  }

  @Override
  public int read(byte[] b, int off, int len) throws IOException {
    if (decoder.closed) {
      throw new IOException("read after close");
    }
    if (decoder.decode() == -1) {
      return -1;
    }
    int result = 0;
    while (len > 0) {
      int limit = Math.min(len, decoder.buffer.remaining());
      decoder.buffer.get(b, off, limit);
      off += limit;
      len -= limit;
      result += limit;
      if (decoder.decode() == -1) {
        break;
      }
    }
    return result;
  }

  @Override
  public long skip(long n) throws IOException {
    if (decoder.closed) {
      throw new IOException("read after close");
    }
    long result = 0;
    while (n > 0) {
      if (decoder.decode() == -1) {
        break;
      }
      int limit = (int) Math.min(n, (long) decoder.buffer.remaining());
      decoder.discard(limit);
      result += limit;
      n -= limit;
    }
    return result;
  }
}