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 }