/****************************************************************************** * Compilation: javac BinaryOut.java * Execution: java BinaryOut * Dependencies: none * * Write binary data to an output stream, either one 1-bit boolean, * one 8-bit char, one 32-bit int, one 64-bit double, one 32-bit float, * or one 64-bit long at a time. The output stream can be standard * output, a file, an OutputStream or a Socket. * * The bytes written are not aligned. * ******************************************************************************/ import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; /** * The BinaryOut data type provides a basic capability for * converting primitive type variables ({@code boolean}, {@code byte}, * {@code char}, {@code int}, {@code long}, {@code float}, and {@code double}) * to sequences of bits and writing them to an output stream. * The output stream can be standard output, a file, an OutputStream or a Socket. * Uses big-endian (most-significant byte first). *

* The client must {@code flush()} the output stream when finished writing bits. *

* The client should not intermix calls to {@code BinaryOut} with calls * to {@code Out}; otherwise unexpected behavior will result. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class BinaryOut { private BufferedOutputStream out; // the output stream private int buffer; // 8-bit buffer of bits to write out private int n; // number of bits remaining in buffer /** * Initializes a binary output stream from standard output. */ public BinaryOut() { out = new BufferedOutputStream(System.out); } /** * Initializes a binary output stream from an {@code OutputStream}. * @param os the {@code OutputStream} */ public BinaryOut(OutputStream os) { out = new BufferedOutputStream(os); } /** * Initializes a binary output stream from a file. * @param filename the name of the file */ public BinaryOut(String filename) { try { OutputStream os = new FileOutputStream(filename); out = new BufferedOutputStream(os); } catch (IOException e) { e.printStackTrace(); } } /** * Initializes a binary output stream from a socket. * @param socket the socket */ public BinaryOut(Socket socket) { try { OutputStream os = socket.getOutputStream(); out = new BufferedOutputStream(os); } catch (IOException e) { e.printStackTrace(); } } /** * Writes the specified bit to the binary output stream. * @param x the bit */ private void writeBit(boolean x) { // add bit to buffer buffer <<= 1; if (x) buffer |= 1; // if buffer is full (8 bits), write out as a single byte n++; if (n == 8) clearBuffer(); } /** * Writes the 8-bit byte to the binary output stream. * @param x the byte */ private void writeByte(int x) { assert x >= 0 && x < 256; // optimized if byte-aligned if (n == 0) { try { out.write(x); } catch (IOException e) { e.printStackTrace(); } return; } // otherwise write one bit at a time for (int i = 0; i < 8; i++) { boolean bit = ((x >>> (8 - i - 1)) & 1) == 1; writeBit(bit); } } // write out any remaining bits in buffer to the binary output stream, padding with 0s private void clearBuffer() { if (n == 0) return; if (n > 0) buffer <<= (8 - n); try { out.write(buffer); } catch (IOException e) { e.printStackTrace(); } n = 0; buffer = 0; } /** * Flushes the binary output stream, padding 0s if number of bits written so far * is not a multiple of 8. */ public void flush() { clearBuffer(); try { out.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * Flushes and closes the binary output stream. * Once it is closed, bits can no longer be written. */ public void close() { flush(); try { out.close(); } catch (IOException e) { e.printStackTrace(); } } /** * Writes the specified bit to the binary output stream. * @param x the {@code boolean} to write */ public void write(boolean x) { writeBit(x); } /** * Writes the 8-bit byte to the binary output stream. * @param x the {@code byte} to write. */ public void write(byte x) { writeByte(x & 0xff); } /** * Writes the 32-bit int to the binary output stream. * @param x the {@code int} to write */ public void write(int x) { writeByte((x >>> 24) & 0xff); writeByte((x >>> 16) & 0xff); writeByte((x >>> 8) & 0xff); writeByte((x >>> 0) & 0xff); } /** * Writes the r-bit int to the binary output stream. * * @param x the {@code int} to write * @param r the number of relevant bits in the char * @throws IllegalArgumentException unless {@code r} is between 1 and 32 * @throws IllegalArgumentException unless {@code x} is between 0 and 2r - 1 */ public void write(int x, int r) { if (r == 32) { write(x); return; } if (r < 1 || r > 32) throw new IllegalArgumentException("Illegal value for r = " + r); if (x >= (1 << r)) throw new IllegalArgumentException("Illegal " + r + "-bit char = " + x); for (int i = 0; i < r; i++) { boolean bit = ((x >>> (r - i - 1)) & 1) == 1; writeBit(bit); } } /** * Writes the 64-bit double to the binary output stream. * @param x the {@code double} to write */ public void write(double x) { write(Double.doubleToRawLongBits(x)); } /** * Writes the 64-bit long to the binary output stream. * @param x the {@code long} to write */ public void write(long x) { writeByte((int) ((x >>> 56) & 0xff)); writeByte((int) ((x >>> 48) & 0xff)); writeByte((int) ((x >>> 40) & 0xff)); writeByte((int) ((x >>> 32) & 0xff)); writeByte((int) ((x >>> 24) & 0xff)); writeByte((int) ((x >>> 16) & 0xff)); writeByte((int) ((x >>> 8) & 0xff)); writeByte((int) ((x >>> 0) & 0xff)); } /** * Writes the 32-bit float to the binary output stream. * @param x the {@code float} to write */ public void write(float x) { write(Float.floatToRawIntBits(x)); } /** * Write the 16-bit int to the binary output stream. * @param x the {@code short} to write. */ public void write(short x) { writeByte((x >>> 8) & 0xff); writeByte((x >>> 0) & 0xff); } /** * Writes the 8-bit char to the binary output stream. * * @param x the {@code char} to write * @throws IllegalArgumentException unless {@code x} is between 0 and 255 */ public void write(char x) { if (x >= 256) throw new IllegalArgumentException("Illegal 8-bit char = " + x); writeByte(x); } /** * Writes the r-bit char to the binary output stream. * * @param x the {@code char} to write * @param r the number of relevant bits in the char * @throws IllegalArgumentException unless {@code r} is between 1 and 16 * @throws IllegalArgumentException unless {@code x} is between 0 and 2r - 1 */ public void write(char x, int r) { if (r == 8) { write(x); return; } if (r < 1 || r > 16) throw new IllegalArgumentException("Illegal value for r = " + r); if (x >= (1 << r)) throw new IllegalArgumentException("Illegal " + r + "-bit char = " + x); for (int i = 0; i < r; i++) { boolean bit = ((x >>> (r - i - 1)) & 1) == 1; writeBit(bit); } } /** * Writes the string of 8-bit characters to the binary output stream. * * @param s the {@code String} to write * @throws IllegalArgumentException if any character in the string is not * between 0 and 255 */ public void write(String s) { for (int i = 0; i < s.length(); i++) write(s.charAt(i)); } /** * Writes the string of r-bit characters to the binary output stream. * @param s the {@code String} to write * @param r the number of relevant bits in each character * @throws IllegalArgumentException unless r is between 1 and 16 * @throws IllegalArgumentException if any character in the string is not * between 0 and 2r - 1 */ public void write(String s, int r) { for (int i = 0; i < s.length(); i++) write(s.charAt(i), r); } /** * Test client. Read bits from standard input and write to the file * specified on command line. * * @param args the command-line arguments */ public static void main(String[] args) { // create binary output stream to write to file String filename = args[0]; BinaryOut out = new BinaryOut(filename); BinaryIn in = new BinaryIn(); // read from standard input and write to file while (!in.isEmpty()) { char c = in.readChar(); out.write(c); } out.flush(); } }