001/** 002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC 003 * and GZip files. (http://jwat.org/) 004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/) 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.jwat.common; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.PushbackInputStream; 023 024/** 025 * Basic <code>PushBackInputStream</code> that also keeps track of the number 026 * of consumed bytes at any given time. 027 * 028 * @author nicl 029 */ 030public class ByteCountingPushBackInputStream extends PushbackInputStream { 031 032 /** Read line initial size. */ 033 public static final int READLINE_INITIAL_SIZE = 128; 034 035 /** Pushback buffer size. */ 036 protected int pushback_size; 037 038 /** Offset relative to beginning of stream. */ 039 protected long consumed = 0; 040 041 /** Byte counter which can also be changed. */ 042 protected long counter = 0; 043 044 /** 045 * Given an <code>InputStream</code> and a push back buffer size returns 046 * a wrapped input stream with push back capabilities. 047 * @param in <code>InputStream</code> to wrap 048 * @param size push back buffer size 049 */ 050 public ByteCountingPushBackInputStream(InputStream in, int size) { 051 super(in, size); 052 pushback_size = size; 053 } 054 055 /** 056 * Get the pushback buffer size. 057 * @return pushback buffer size 058 */ 059 public int getPushbackSize() { 060 return pushback_size; 061 } 062 063 /** 064 * Retrieve the number of bytes consumed by this stream. 065 * @return current byte offset in this stream 066 */ 067 public long getConsumed() { 068 return consumed; 069 } 070 071 /** 072 * Change the counter value. 073 * Useful for reading zero indexed relative data. 074 * @param bytes new counter value 075 */ 076 public void setCounter(long bytes) { 077 counter = bytes; 078 } 079 080 /** 081 * Retrieve the current counter value. 082 * @return current counter value 083 */ 084 public long getCounter() { 085 return counter; 086 } 087 088 @Override 089 public boolean markSupported() { 090 return false; 091 } 092 093 @Override 094 public synchronized void mark(int readlimit) { 095 } 096 097 @Override 098 public synchronized void reset() throws IOException { 099 throw new UnsupportedOperationException(); 100 } 101 102 @Override 103 public int read() throws IOException { 104 int b = super.read(); 105 if (b != -1) { 106 ++consumed; 107 ++counter; 108 } 109 return b; 110 } 111 112 /* 113 * The super method did this anyway causing a double amount of 114 * consumed bytes. 115 * @see java.io.FilterInputStream#read(byte[]) 116 */ 117 @Override 118 public int read(byte[] b) throws IOException { 119 return read(b, 0, b.length); 120 } 121 122 @Override 123 public int read(byte[] b, int off, int len) throws IOException { 124 int bytesRead = super.read(b, off, len); 125 if (bytesRead > 0) { 126 consumed += bytesRead; 127 counter += bytesRead; 128 } 129 return bytesRead; 130 } 131 132 @Override 133 public long skip(long n) throws IOException { 134 long bytesSkipped = super.skip(n); 135 consumed += bytesSkipped; 136 counter += bytesSkipped; 137 return bytesSkipped; 138 } 139 140 @Override 141 public void unread(int b) throws IOException { 142 super.unread(b); 143 --consumed; 144 --counter; 145 } 146 147 /* 148 * The super method did this anyway causing a double amount of 149 * un-consumed bytes. 150 * @see java.io.PushbackInputStream#unread(byte[]) 151 */ 152 @Override 153 public void unread(byte[] b) throws IOException { 154 unread(b, 0, b.length); 155 } 156 157 @Override 158 public void unread(byte[] b, int off, int len) throws IOException { 159 super.unread(b, off, len); 160 consumed -= len; 161 counter -= len; 162 } 163 164 /** 165 * Read a single line into a string. 166 * @return single string line 167 * @throws IOException if an I/O error occurs while reading line 168 */ 169 public String readLine() throws IOException { 170 StringBuffer sb = new StringBuffer(READLINE_INITIAL_SIZE); 171 int b; 172 while (true) { 173 b = read(); 174 if (b == -1) { 175 return null; //Unexpected EOF 176 } 177 if (b == '\n'){ 178 break; 179 } 180 if (b != '\r') { 181 sb.append((char) b); 182 } 183 } 184 return sb.toString(); 185 } 186 187 /** 188 * Guaranteed to read the exact number of bytes that are in the array, 189 * if not, the bytes are pushed back into the stream before returning. 190 * @param buffer byte buffer to read bytes into 191 * @return the number of bytes read into array 192 * @throws IOException if an I/O error occurs while reading array 193 */ 194 public int readFully(byte[] buffer) throws IOException { 195 int readOffset = 0; 196 int readRemaining = buffer.length; 197 int readLast = 0; 198 while (readRemaining > 0 && readLast != -1) { 199 readRemaining -= readLast; 200 readOffset += readLast; 201 readLast = read(buffer, readOffset, readRemaining); 202 } 203 if (readRemaining > 0) { 204 unread(buffer, 0, readOffset); 205 readOffset = 0; 206 } 207 return readOffset; 208 } 209 210 /** 211 * Peek into the stream by filling as much of the supplied array as 212 * possible before unreading the stream and returning. 213 * @param buffer byte buffer to read bytes into 214 * @return the number of bytes read into array 215 * @throws IOException if an I/O error occurs while reading array 216 */ 217 public int peek(byte[] buffer) throws IOException { 218 int readOffset = 0; 219 int readRemaining = buffer.length; 220 int readLast = 0; 221 while (readRemaining > 0 && readLast != -1) { 222 readRemaining -= readLast; 223 readOffset += readLast; 224 readLast = read(buffer, readOffset, readRemaining); 225 } 226 if (readOffset > 0) { 227 unread(buffer, 0, readOffset); 228 } 229 return readOffset; 230 } 231 232}