Source code: org/esau/ptarmigan/util/CountingInputStream.java
1 package org.esau.ptarmigan.util;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.FilterInputStream;
6
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
9
10 /**
11 * Counts the number of bytes that pass through it.
12 * <p>
13 * Note that the counter in this filter is independent of the
14 * mark/reset logic. If you wish to reset the counter when you
15 * reset the mark, you'll have to do so manually with a call
16 * to resetCount().
17 * <p>
18 * Note that this class has its own renamed read() methods. This
19 * is to insulate a buffered parent class that will read-ahead as
20 * necessary.
21 *
22 * @author Reed Esau
23 * @version $Revision: 1.3 $ $Date: 2002/10/02 05:30:04 $
24 */
25 public class CountingInputStream extends FilterInputStream {
26
27 static final int NO_LIMIT = -1;
28
29 private long m_limit;
30 private long m_count;
31
32 public CountingInputStream( InputStream in ) {
33 this(in, NO_LIMIT);
34 }
35 public CountingInputStream( InputStream in, long limit ) {
36 super(in);
37 m_limit = limit;
38 log.debug("ctor: limit=" + m_limit);
39 }
40
41 public int readC() throws IOException {
42
43 //log.debug("readC: m_limit=" + m_limit + " m_count=" + m_count + " remaining=" + getRemaining());
44
45 if (m_limit != NO_LIMIT) {
46 if (getRemaining() < 1)
47 return -1;
48 }
49
50 int by = super.read();
51 if (by != -1)
52 m_count++;
53 return by;
54 }
55
56 public int readC(byte b[]) throws IOException {
57
58 //log.debug("readC-b: blen=" + b.length + " m_limit=" + m_limit + " m_count=" + m_count + " remaining=" + getRemaining());
59
60 int len = b.length;
61
62 if (m_limit != NO_LIMIT) {
63 long remaining = getRemaining();
64 if (len > remaining)
65 len = (int)remaining;
66 }
67
68 int bytes = super.read(b, 0, len);
69 if (bytes != -1)
70 m_count += bytes;
71 return bytes;
72 }
73
74 public int readC(byte b[], int off, int len) throws IOException {
75
76 //log.debug("readC-bol: blen=" + b.length + " off=" + off + " len=" + len + " m_limit=" + m_limit + " m_count=" + m_count + " remaining=" + getRemaining());
77
78 if (m_limit != NO_LIMIT) {
79 long remaining = getRemaining();
80 if (len > remaining)
81 len = (int)remaining;
82 }
83
84 int bytes = super.read(b, off, len);
85 if (bytes != -1)
86 m_count += bytes;
87 return bytes;
88 }
89
90 public long skipC(long n) throws IOException {
91
92 //log.debug("skipC: n=" + n + " m_limit=" + m_limit + " m_count=" + m_count + " remaining=" + getRemaining());
93
94 if (m_limit != NO_LIMIT) {
95 long remaining = getRemaining();
96 if (n > remaining)
97 n = remaining;
98 }
99
100 long skipped = super.skip(n);
101 if (skipped >= 0)
102 m_count += skipped;
103 return skipped;
104 }
105
106 /**
107 * The number of bytes that have passed through this stream.
108 */
109 public long getCount() {
110 return m_count;
111 }
112
113 /**
114 * Start the count at zero
115 */
116 public void resetCount() {
117 //log.debug("resetCount: m_count=" + m_count);
118 m_count = 0L;
119 }
120
121 public long getLimit() {
122 return m_limit;
123 }
124 public void setLimit(long limit) {
125 m_limit = limit;
126 }
127
128 public long getRemaining() {
129 return m_limit - m_count;
130 }
131
132 //
133 // set the file position
134 //
135
136 static final int BLOCK_SIZE = 128;
137
138 /**
139 * a substitute for skip, which doesn't seem to want to work
140 * correctly on buffered streams
141 * <p>
142 * TODO: should be 'long' param?
143 */
144 public int fastForward(int n) throws IOException {
145 if (log.isDebugEnabled())
146 log.debug("fastForward: n=" + n + " counter=" + m_count);
147
148 if (m_limit == NO_LIMIT)
149 return (int)skip(n); // trivial case (no limit imposed)
150
151 if (n < 0)
152 throw new IllegalArgumentException("unexpected value " + n);
153
154 long remaining = getRemaining();
155 if (n > remaining) {
156 log.warn("fastForward: truncating at limit; remaining=" +remaining);
157 n = (int)remaining;
158 }
159
160 if (n == 0)
161 return 0; //trivial case
162
163 int buf_size = n > BLOCK_SIZE ? BLOCK_SIZE : n;
164 byte[] buf = new byte[ buf_size ];
165
166 int bytes_to_read = n;
167
168 while (bytes_to_read > 0) {
169 int bytes_read = readC(buf, 0, (bytes_to_read > BLOCK_SIZE
170 ? BLOCK_SIZE
171 : bytes_to_read));
172 if (bytes_read == -1) {
173 log.warn("fastForward: failure to consume bytes, bytes_to_read="
174 + bytes_to_read);
175 return -1;
176 }
177
178 bytes_to_read -= bytes_read;
179 }
180 if (log.isDebugEnabled() && bytes_to_read > 0)
181 log.debug("fastForward: bytes_to_read=" + bytes_to_read);
182 return(n - bytes_to_read);
183 }
184
185 /**
186 * reposition the stream from the last mark point (in case we
187 * read too far)
188 * <p>
189 * TODO: should be 'long' param?
190 *
191 * @return true if rewind was successful; false if we had read beyond the limit of the mark
192 */
193 public boolean rewind(int n) throws IOException {
194
195 if (markSupported() == false)
196 throw new IOException("mark/reset not supported");
197
198 if (m_limit == NO_LIMIT) {
199 reset(); // throws if read beyond mark
200 m_count = 0;
201 return true;
202 }
203
204 if (log.isDebugEnabled())
205 log.debug("rewind: n=" + n + " counter=" + m_count);
206
207 int diff = (int)(n - m_count);
208 if (diff > 0) {
209 long skipped = fastForward(diff);
210 if (skipped < 0 || skipped != diff) {
211 throw new IOException("unable to advance");
212 }
213 }
214 else if (diff < 0) {
215 log.debug("rewind: RESET");
216
217 reset(); // throws if read beyond mark
218 m_count = 0;
219 long skipped = fastForward(n);
220 if (skipped < 0 || skipped != n) {
221 throw new IOException("unable to advance after rewind");
222 }
223 }
224 //log.debug("rewind: success, counter=" + m_count);
225 return true;
226 }
227
228 /**
229 * logging object
230 */
231 static Log log = LogFactory.getLog(CountingInputStream.class);
232 }
233
234