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 package org.archive.io;
26
27 import it.unimi.dsi.fastutil.io.RepositionableStream;
28
29 import java.io.BufferedInputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32
33 /***
34 * Wrapper around an {@link InputStream} to make a primitive Repositionable
35 * stream. Uses a {@link BufferedInputStream}. Calls mark on every read so
36 * we'll remember at least the last thing read (You can only backup on the
37 * last thing read -- not last 2 or 3 things read). Used by
38 * {@link GzippedInputStream} when reading streams over a network. Wraps a
39 * HTTP, etc., stream so we can back it up if needs be after the
40 * GZIP inflater has done a fill of its full buffer though it only needed
41 * the first few bytes to finish decompressing the current GZIP member.
42 *
43 * <p>TODO: More robust implementation. Tried to use the it.unimi.dsi.io
44 * FastBufferdInputStream but relies on FileChannel ByteBuffers and if not
45 * present -- as would be the case reading from a network stream, the main
46 * application for this instance -- then it expects the underlying stream
47 * implements RepositionableStream interface so chicken or egg problem.
48 * @author stack
49 */
50 public class RepositionableInputStream extends BufferedInputStream implements
51 RepositionableStream {
52 private long position = 0;
53 private long markPosition = -1;
54
55 public RepositionableInputStream(InputStream in) {
56 super(in);
57 }
58
59 public RepositionableInputStream(InputStream in, int size) {
60 super(in, size);
61 }
62
63 public int read(byte[] b) throws IOException {
64 int read = super.read(b);
65 if (read != -1) {
66 position += read;
67 }
68 return read;
69 }
70
71 public synchronized int read(byte[] b, int offset, int ct)
72 throws IOException {
73
74
75
76
77
78 if (!isMarked()) {
79 super.mark((ct > offset)? ct - offset: ct);
80 }
81 int read = super.read(b, offset, ct);
82 if (read != -1) {
83 position += read;
84 }
85 return read;
86 }
87
88 public int read() throws IOException {
89
90
91
92
93
94 if (!isMarked()) {
95 super.mark(1);
96 }
97 int c = super.read();
98 if (c != -1) {
99 position++;
100 }
101 return c;
102 }
103
104 public void position(final long offset) {
105 if (this.position == offset) {
106 return;
107 }
108 int diff = (int)(offset - this.position);
109 long lowerBound = this.position - this.pos;
110 long upperBound = lowerBound + this.count;
111 if (offset < lowerBound || offset >= upperBound) {
112 throw new IllegalAccessError("Offset goes outside " +
113 "current this.buf (TODO: Do buffer fills if positive)");
114 }
115 this.position = offset;
116 this.pos += diff;
117
118 this.markPosition = -1;
119 }
120
121 public void mark(int readlimit) {
122 this.markPosition = this.position;
123 super.mark(readlimit);
124 }
125
126 public void reset() throws IOException {
127 super.reset();
128 this.position = this.markPosition;
129 this.markPosition = -1;
130 }
131
132 protected boolean isMarked() {
133 return this.markPosition != -1;
134 }
135
136 public long position() {
137 return this.position;
138 }
139 }