|
Custom Streams
in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
int) by performing a bitwise AND
(e.g., i = b & 0x000000FF)
i = i << 8)
package io;
import java.io.*;
/**
* For reading from binary streams that were written on a
* machine that uses a little-endian scheme.
*
* For example, C under DOS/Windows uses a little-endian scheme
* (i.e., LSB first).
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class LittleEndianInputStream
{
protected byte[] b1, b2, b4, b8;
protected InputStream in;
/**
* Construct a new LittleEndianInputStream
*
* @param in The InputStream to use
*/
public LittleEndianInputStream(InputStream in)
{
this.in = in;
b1 = new byte[1];
b2 = new byte[2];
b4 = new byte[4];
b8 = new byte[8];
}
/**
* Returns the number of bytes available without blocking
*
*/
public int available() throws IOException
{
return in.available();
}
/**
* Closes this input stream
*/
public void close() throws IOException
{
in.close();
}
/**
* Reads an array of bytes
*
* @param b The buffer into which the data are read
* @param off The start offset of the data
* @param len The maximum number of bytes read
*
* @return The number of bytes actually read or -1 if end-of-stream
*/
public int read(byte[] b, int off, int len) throws IOException
{
return in.read(b, off, len);
}
/**
* Reads an array of bytes
*
* @param b The buffer into which the data are read
*
* @return The number of bytes actually read or -1 if end-of-stream
*/
public int read(byte[] b) throws IOException
{
return in.read(b);
}
/**
* Reads an array of bytes
*
* Note: Unlike read(), this method keeps reading until all of the bytes
* are read or an end-of-stream is encountered
*
* @param b The buffer into which the data are read
*/
public void readFully(byte[] b) throws IOException
{
EOFException eofe;
int i, n;
for (i=0; i < b.length; i+=n)
{
n = read(b, i, b.length-i);
if (n < 0) // End of File Encountered
{
eofe = new EOFException("EOF in LittleEndianInputStream");
throw(eofe);
}
}
}
/**
* Reads an int in __big-endian__ format
*/
public int readInt() throws IOException
{
readFully(b4);
return (b4[0]&0xff) << 24 |
(b4[1]&0xff) << 16 |
(b4[2]&0xff) << 8 |
(b4[3]&0xff);
}
/**
* Reads a 1-byte integer (-128 to 127)
*
* @return An int with the appropriate value
*/
public int readInteger1() throws IOException
{
readFully(b1);
return b1[0];
}
/**
* Reads a 2-byte integer (-32,768 to 32,767)
*
* @return An int with the appropriate value
*/
public int readInteger2() throws IOException
{
readFully(b2);
return (int)( (short)( (b2[1] & 0xFF) << 8 |
(b2[0] & 0xFF)) );
}
/**
* Reads a 4-byte integer (-2,147,483,648 to 2,147,483,647)
*
* @return An int with the appropriate value
*/
public int readInteger4() throws IOException
{
readFully(b4);
return (b4[3]&0xff) << 24 |
(b4[2]&0xff) << 16 |
(b4[1]&0xff) << 8 |
(b4[0]&0xff);
}
/**
* Reads an 8-byte integer (-9,223,372,036,854,775,808 to
* 9,223,372,036,854,775,807)
*
* @return A long with the appropriate value
*/
public long readInteger8() throws IOException
{
readFully(b8);
return (long)(b8[7]& 0xFF) << 56 |
(long)(b8[6]& 0xFF) << 48 |
(long)(b8[5]& 0xFF) << 40 |
(long)(b8[4]& 0xFF) << 32 |
(long)(b8[3]& 0xFF) << 24 |
(long)(b8[2]& 0xFF) << 16 |
(long)(b8[1]& 0xFF) << 8 |
(long)(b8[0]& 0xFF);
}
/**
* Reads a 4-byte real number
*
* @return A float with the appropriate value
*/
public float readReal4() throws IOException
{
return Float.intBitsToFloat(readInteger4());
}
/**
* Reads an 8-byte real number
*
* @return A double with the appropriate value
*/
public double readReal8() throws IOException
{
return Double.longBitsToDouble(readInteger8());
}
/**
* Reads a String
*
* @param n The number of characters in the String
*/
public String readString(int n) throws IOException
{
byte[] b;
b = new byte[n];
readFully(b);
return new String(b);
}
/**
* Reads an unsigned 1-byte integer (0 to 255)
*
* @return An int with the appropriate value
*/
public int readUnsignedInteger1() throws IOException
{
readFully(b1);
return (int)( (b1[0] & 0xFF) );
}
/**
* Reads an unsigned 2-byte integer (0 to 65,536)
*
* @return An int with the appropriate value
*/
public int readUnsignedInteger2() throws IOException
{
readFully(b2);
return ((b2[1] & 0xFF) << 8 |
(b2[0] & 0xFF) );
}
/**
* Reads an unsigned 4-byte integer (0 to 4,294,967,296)
*
* @return A long with the appropriate value
*/
public long readUnsignedInteger4() throws IOException
{
readFully(b4);
return ( (b4[3]&0xff) << 24 |
(b4[2]&0xff) << 16 |
(b4[1]&0xff) << 8 |
(b4[0]&0xff) );
}
}
package io;
import java.io.*;
/**
* For writing binary streams to be read on a
* machine that uses a little-endian scheme.
*
* For example, C under DOS/Windows uses a little-endian scheme
* (i.e., LSB first).
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class LittleEndianOutputStream
{
protected byte[] b1, b2, b4;
protected OutputStream out;
/**
* Construct a new LittleEndianOutputStream
*
* @param in The OutputStream to use
*/
public LittleEndianOutputStream(OutputStream out)
{
this.out = out;
b1 = new byte[1];
b2 = new byte[2];
b4 = new byte[4];
}
/**
* Closes this output stream
*/
public void close() throws IOException
{
out.close();
}
/**
* Writes a 1-byte integer
*
* @param i The int to write
*/
public void writeInteger1(int i) throws IOException
{
b1[0] = (byte)i;
out.write(b1,0,1);
}
/**
* Writes a 2-byte integer
*
* @param i The int to write
*/
public void writeInteger2(int i) throws IOException
{
b2[0] = (byte)i; // LSB
b2[1] = (byte)(i >> 8); // MSB
out.write(b2,0,2);
}
/**
* Writes a 4-byte integer
*
* @param i The int to write
*/
public void writeInteger4(int i) throws IOException
{
b4[0] = (byte)i; // LSB
b4[1] = (byte)(i >> 8);
b4[2] = (byte)(i >> 16);
b4[3] = (byte)(i >> 24); // MSB
out.write(b4,0,4);
}
/**
* Writes an 8-byte integer
*
* @param l The long to write
*/
public void writeInteger8(long l) throws IOException
{
b4[0] = (byte)l; // LSB
b4[1] = (byte)(l >> 8);
b4[2] = (byte)(l >> 16);
b4[3] = (byte)(l >> 24);
b4[4] = (byte)(l >> 32);
b4[5] = (byte)(l >> 40);
b4[6] = (byte)(l >> 48);
b4[7] = (byte)(l >> 56); // MSB
out.write(b4,0,4);
}
/**
* Writes a 4-byte real number
*
* @param f The float to write
*/
public void writeReal4(float f) throws IOException
{
writeInteger4(Float.floatToIntBits(f));
}
/**
* Writes an 8-byte real number
*
* @param d The double to write
*/
public void writeReal8(double d) throws IOException
{
writeInteger8(Double.doubleToLongBits(d));
}
}
DataInputStream
import java.io.*;
/**
* An input stream for reading both byte-stream data and
* character-stream that is terminated using the HTTP end-of-line
* characters ('\r\n')
*
* @author Prof. David Bernstein, James Madison University
* @version 0.0
*/
public class HttpInputStream extends DataInputStream
{
private static final int INITIAL_BUFFER_SIZE = 256;
public static final byte CR = (byte)'\r'; //13;
public static final byte LF = (byte)'\n'; //10;
public static final byte NULL = (byte)'\0'; // 0;
private byte[] buffer;
private int currentSize;
/**
* Explicit Value Constructor
*
* @param is The underlying InputStream
*/
public HttpInputStream(InputStream is)
{
super(is);
}
/**
* Add a byte to the current line buffer (increasing the
* size of the line buffer if necessary)
*
* @Param b The byte to add
*/
private void addToLineBuffer(byte b)
{
byte[] temp;
buffer[currentSize] = b;
currentSize++;
if (currentSize == buffer.length)
{
temp = new byte[buffer.length*2];
System.arraycopy(buffer, 0, temp, 0, buffer.length);
buffer = temp;
}
}
/**
* Reads the input stream, one line at a time.
* Lines are terminated either by '\r\n' or by end-of-stream.
*
* @return A String containing the line
*/
public String readHttpLine() throws IOException
{
byte previous;
byte[] b;
int status, stringLength;
String result;
// This method will read a byte at a time. It would be much
// more efficient to read multiple bytes at a time into a buffer
// but this is much simpler.
b = new byte[1];
buffer = new byte[INITIAL_BUFFER_SIZE];
currentSize = 0;
status = -1;
previous = NULL;
while (status < 0)
{
try
{
readFully(b); // Blocks until a byte is available
if ((b[0] == LF) && (previous == CR))
{
status = 1;
}
else
{
previous = b[0];
addToLineBuffer(b[0]);
}
}
catch (EOFException eofe)
{
status = 0;
}
}
if (status == 0) stringLength = currentSize; // Terminated by EOS
else stringLength = currentSize - 1; // Remove the CR
try
{
// Use the HTTP "standard" character set
result = new String(buffer, 0, stringLength, "ISO-8859-1");
}
catch (UnsupportedEncodingException uee)
{
// Use the default character set
result = new String(buffer, 0, stringLength);
}
return result;
}
}
PrintStream
import java.io.*;
/**
* A PrintStream that can be used for writing HTTP requests
* and responses
*
* @author Prof. David Bernstein, James Madison University
* @version 0.0
*/
public class HttpOutputStream extends PrintStream
{
/**
* Explicit Value Constructor
*
* @param os The underlying OutputStream to use
*/
public HttpOutputStream(OutputStream os)
{
super(os, false);
}
/**
* Print an End-Of-Line marker
*/
public void printEOL()
{
print("\r\n");
}
/**
* Print a line (using the HTTP End-Of-Line marker)
*
* @param line The line to print
*/
public void printHttpLine(String line)
{
print(line);
print("\r\n");
}
/**
* Print an appropriately formatted and terminated
* header line
*
* @param name The name
* @param value The corresponding value
*/
public void printHeaderLine(String name, String value)
{
print(name);
print(":");
print(value);
printEOL();
}
}