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