001package stdlib; 002import java.io.BufferedInputStream; 003import java.io.File; 004import java.io.FileInputStream; 005import java.io.IOException; 006import java.io.InputStream; 007import java.net.Socket; 008import java.net.URL; 009import java.net.URLConnection; 010 011/* *********************************************************************** 012 * Compilation: javac BinaryIn.java 013 * Execution: java BinaryIn input output 014 * 015 * This library is for reading binary data from an input stream. 016 * 017 * % java BinaryIn http://introcs.cs.princeton.edu/cover.jpg output.jpg 018 * 019 *************************************************************************/ 020 021/** 022 * <i>Binary input</i>. This class provides methods for reading 023 * in bits from a binary input stream, either 024 * one bit at a time (as a {@code boolean}), 025 * 8 bits at a time (as a {@code byte} or {@code char}), 026 * 16 bits at a time (as a {@code short}), 027 * 32 bits at a time (as an {@code int} or {@code float}), or 028 * 64 bits at a time (as a {@code double} or {@code long}). 029 * <p> 030 * The binary input stream can be from standard input, a filename, 031 * a URL name, a Socket, or an InputStream. 032 * <p> 033 * All primitive types are assumed to be represented using their 034 * standard Java representations, in big-endian (most significant 035 * byte first) order. 036 * <p> 037 * The client should not intermix calls to {@code BinaryIn} with calls 038 * to {@code In}; otherwise unexpected behavior will result. 039 */ 040public final class BinaryIn { 041 private static final int EOF = -1; // end of file 042 043 private BufferedInputStream in; // the input stream 044 private int buffer; // one character buffer 045 private int N; // number of bits left in buffer 046 047 /** 048 * Create a binary input stream from standard input. 049 */ 050 public BinaryIn() { 051 in = new BufferedInputStream(System.in); 052 fillBuffer(); 053 } 054 055 /** 056 * Create a binary input stream from an InputStream. 057 */ 058 public BinaryIn(InputStream is) { 059 in = new BufferedInputStream(is); 060 fillBuffer(); 061 } 062 063 /** 064 * Create a binary input stream from a socket. 065 */ 066 public BinaryIn(Socket socket) { 067 try { 068 InputStream is = socket.getInputStream(); 069 in = new BufferedInputStream(is); 070 fillBuffer(); 071 } 072 catch (IOException ioe) { 073 System.err.println("Could not open " + socket); 074 } 075 } 076 077 /** 078 * Create a binary input stream from a URL. 079 */ 080 public BinaryIn(URL url) { 081 try { 082 URLConnection site = url.openConnection(); 083 InputStream is = site.getInputStream(); 084 in = new BufferedInputStream(is); 085 fillBuffer(); 086 } 087 catch (IOException ioe) { 088 System.err.println("Could not open " + url); 089 } 090 } 091 092 /** 093 * Create a binary input stream from a filename or URL name. 094 */ 095 public BinaryIn(String s) { 096 097 try { 098 // first try to read file from local file system 099 File file = new File(s); 100 if (file.exists()) { 101 FileInputStream fis = new FileInputStream(file); 102 in = new BufferedInputStream(fis); 103 fillBuffer(); 104 return; 105 } 106 107 // next try for files included in jar 108 URL url = getClass().getResource(s); 109 110 // or URL from web 111 if (url == null) { url = new URL(s); } 112 113 URLConnection site = url.openConnection(); 114 InputStream is = site.getInputStream(); 115 in = new BufferedInputStream(is); 116 fillBuffer(); 117 } 118 catch (IOException ioe) { 119 System.err.println("Could not open " + s); 120 } 121 } 122 123 private void fillBuffer() { 124 try { buffer = in.read(); N = 8; } 125 catch (IOException e) { System.err.println("EOF"); buffer = EOF; N = -1; } 126 } 127 128 /** 129 * Does the binary input stream exist? 130 */ 131 public boolean exists() { 132 return in != null; 133 } 134 135 /** 136 * Returns true if the binary input stream is empty. 137 * @return true if and only if the binary input stream is empty 138 */ 139 public boolean isEmpty() { 140 return buffer == EOF; 141 } 142 143 /** 144 * Read the next bit of data from the binary input stream and return as a boolean. 145 * @return the next bit of data from the binary input stream as a {@code boolean} 146 * @throws RuntimeException if the input stream is empty 147 */ 148 public boolean readBoolean() { 149 if (isEmpty()) throw new RuntimeException("Reading from empty input stream"); 150 N--; 151 boolean bit = ((buffer >> N) & 1) == 1; 152 if (N == 0) fillBuffer(); 153 return bit; 154 } 155 156 /** 157 * Read the next 8 bits from the binary input stream and return as an 8-bit char. 158 * @return the next 8 bits of data from the binary input stream as a {@code char} 159 * @throws RuntimeException if there are fewer than 8 bits available 160 */ 161 public char readChar() { 162 if (isEmpty()) throw new RuntimeException("Reading from empty input stream"); 163 164 // special case when aligned byte 165 if (N == 8) { 166 int x = buffer; 167 fillBuffer(); 168 return (char) (x & 0xff); 169 } 170 171 // combine last N bits of current buffer with first 8-N bits of new buffer 172 int x = buffer; 173 x <<= (8-N); 174 int oldN = N; 175 fillBuffer(); 176 if (isEmpty()) throw new RuntimeException("Reading from empty input stream"); 177 N = oldN; 178 x |= (buffer >>> N); 179 return (char) (x & 0xff); 180 // the above code doesn't quite work for the last character if N = 8 181 // because buffer will be -1 182 } 183 184 185 /** 186 * Read the next r bits from the binary input stream and return as an r-bit character. 187 * @param r number of bits to read. 188 * @return the next r bits of data from the binary input streamt as a {@code char} 189 * @throws RuntimeException if there are fewer than r bits available 190 */ 191 public char readChar(int r) { 192 if (r < 1 || r > 16) throw new RuntimeException("Illegal value of r = " + r); 193 194 // optimize r = 8 case 195 if (r == 8) return readChar(); 196 197 char x = 0; 198 for (int i = 0; i < r; i++) { 199 x <<= 1; 200 boolean bit = readBoolean(); 201 if (bit) x |= 1; 202 } 203 return x; 204 } 205 206 207 /** 208 * Read the remaining bytes of data from the binary input stream and return as a string. 209 * @return the remaining bytes of data from the binary input stream as a {@code String} 210 * @throws RuntimeException if the input stream is empty or if the number of bits 211 * available is not a multiple of 8 (byte-aligned) 212 */ 213 public String readString() { 214 if (isEmpty()) throw new RuntimeException("Reading from empty input stream"); 215 216 StringBuilder sb = new StringBuilder(); 217 while (!isEmpty()) { 218 char c = readChar(); 219 sb.append(c); 220 } 221 return sb.toString(); 222 } 223 224 225 /** 226 * Read the next 16 bits from the binary input stream and return as a 16-bit short. 227 * @return the next 16 bits of data from the binary standard input as a {@code short} 228 * @throws RuntimeException if there are fewer than 16 bits available 229 */ 230 public short readShort() { 231 short x = 0; 232 for (int i = 0; i < 2; i++) { 233 char c = readChar(); 234 x <<= 8; 235 x |= c; 236 } 237 return x; 238 } 239 240 /** 241 * Read the next 32 bits from the binary input stream and return as a 32-bit int. 242 * @return the next 32 bits of data from the binary input stream as a {@code int} 243 * @throws RuntimeException if there are fewer than 32 bits available 244 */ 245 public int readInt() { 246 int x = 0; 247 for (int i = 0; i < 4; i++) { 248 char c = readChar(); 249 x <<= 8; 250 x |= c; 251 } 252 return x; 253 } 254 255 /** 256 * Read the next r bits from the binary input stream return as an r-bit int. 257 * @param r number of bits to read. 258 * @return the next r bits of data from the binary input stream as a {@code int} 259 * @throws RuntimeException if there are fewer than r bits available on standard input 260 */ 261 public int readInt(int r) { 262 if (r < 1 || r > 32) throw new RuntimeException("Illegal value of r = " + r); 263 264 // optimize r = 32 case 265 if (r == 32) return readInt(); 266 267 int x = 0; 268 for (int i = 0; i < r; i++) { 269 x <<= 1; 270 boolean bit = readBoolean(); 271 if (bit) x |= 1; 272 } 273 return x; 274 } 275 276 /** 277 * Read the next 64 bits from the binary input stream and return as a 64-bit long. 278 * @return the next 64 bits of data from the binary input stream as a {@code long} 279 * @throws RuntimeException if there are fewer than 64 bits available 280 */ 281 public long readLong() { 282 long x = 0; 283 for (int i = 0; i < 8; i++) { 284 char c = readChar(); 285 x <<= 8; 286 x |= c; 287 } 288 return x; 289 } 290 291 /** 292 * Read the next 64 bits from the binary input stream and return as a 64-bit double. 293 * @return the next 64 bits of data from the binary input stream as a {@code double} 294 * @throws RuntimeException if there are fewer than 64 bits available 295 */ 296 public double readDouble() { 297 return Double.longBitsToDouble(readLong()); 298 } 299 300 /** 301 * Read the next 32 bits from standard input and return as a 32-bit float. 302 * @return the next 32 bits of data from standard input as a {@code float} 303 * @throws RuntimeException if there are fewer than 32 bits available on standard input 304 */ 305 public float readFloat() { 306 return Float.intBitsToFloat(readInt()); 307 } 308 309 310 /** 311 * Read the next 8 bits from the binary input stream and return as an 8-bit byte. 312 * @return the next 8 bits of data from the binary input stream as a {@code byte} 313 * @throws RuntimeException if there are fewer than 8 bits available 314 */ 315 public byte readByte() { 316 char c = readChar(); 317 byte x = (byte) (c & 0xff); 318 return x; 319 } 320 321 /** 322 * Test client. Reads in the name of a file or url (first command-line 323 * argument) and writes it to a file (second command-line argument). 324 */ 325 public static void main(String[] args) { 326 BinaryIn in = new BinaryIn(args[0]); 327 BinaryOut out = new BinaryOut(args[1]); 328 329 // read one 8-bit char at a time 330 while (!in.isEmpty()) { 331 char c = in.readChar(); 332 out.write(c); 333 } 334 out.flush(); 335 } 336}