/*
* MixedEndianDataInputStream.java
*
* Created on January 31, 2007, 9:54 PM
*
*/
package edu.oregonstate.cartography.geometryimport;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UTFDataFormatException;
/**
* MixedEndianDataInputStream can be used to read little endian and big endian
* data from one single stream. Based on O'Reilly's "Java I/O" second edition,
* chapter 8, page 137, LittleEndianInputStream class.
*
* @author Bernhard Jenny, Institute of Cartography, ETH Zurich
*/
public class MixedEndianDataInputStream extends DataInputStream {
/**
* Creates a new instance of MixedEndianDataInputStream
*
* @param i
*/
public MixedEndianDataInputStream(InputStream i) {
super(i);
}
/**
* Reads a two byte signed <code>short</code> from the underlying input
* stream in little endian order, low byte first.
*
* @return the <code>short</code> read.
* @exception EOFException if the end of the underlying input stream has
* been reached
* @exception IOException if the underlying stream throws an IOException.
*/
public short readLittleEndianShort() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
// only need to test last byte read
// if byte1 is -1 so is byte2
if (byte2 == -1) {
throw new EOFException();
}
return (short) ((byte2 << 8) + byte1);
}
/**
* Reads a two byte unsigned <code>short</code> from the underlying input
* stream in little endian order, low byte first.
*
* @return the int value of the unsigned short read.
* @exception EOFException if the end of the underlying input stream has
* been reached
* @exception IOException if the underlying stream throws an IOException.
*/
public int readLittleEndianUnsignedShort() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 == -1) {
throw new EOFException();
}
return (byte2 << 8) + byte1;
}
/**
* Reads a two byte Unicode <code>char</code> from the underlying input
* stream in little endian order, low byte first.
*
* @return the int value of the unsigned short read.
* @exception EOFException if the end of the underlying input stream has
* been reached
* @exception IOException if the underlying stream throws an IOException.
*/
public char readLittleEndianChar() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 == -1) {
throw new EOFException();
}
return (char) ((byte2 << 8) + byte1);
}
/**
* Reads a four byte signed <code>int</code> from the underlying input
* stream in little endian order, low byte first.
*
* @return the <code>int</code> read.
* @exception EOFException if the end of the underlying input stream has
* been reached
* @exception IOException if the underlying stream throws an IOException.
*/
public int readLittleEndianInt() throws IOException {
int byte1, byte2, byte3, byte4;
byte1 = in.read();
byte2 = in.read();
byte3 = in.read();
byte4 = in.read();
if (byte4 == -1) {
throw new EOFException();
}
return (byte4 << 24) + (byte3 << 16) + (byte2 << 8) + byte1;
}
/**
* Reads an eight byte signed <code>int</code> from the underlying input
* stream in little endian order, low byte first.
*
* @return the <code>int</code> read.
* @exception EOFException if the end of the underlying input stream has
* been reached
* @exception IOException if the underlying stream throws an IOException.
*/
public long readLittleEndianLong() throws IOException {
long byte1 = in.read();
long byte2 = in.read();
long byte3 = in.read();
long byte4 = in.read();
long byte5 = in.read();
long byte6 = in.read();
long byte7 = in.read();
long byte8 = in.read();
if (byte8 == -1) {
throw new EOFException();
}
return (byte8 << 56) + (byte7 << 48) + (byte6 << 40) + (byte5 << 32)
+ (byte4 << 24) + (byte3 << 16) + (byte2 << 8) + byte1;
}
/**
* Reads a string of no more than 65,535 characters from the underlying
* input stream using UTF-8 encoding. This method first reads a two byte
* short in <b>big</b> endian order as required by the UTF-8 specification.
* This gives the number of bytes in the UTF-8 encoded version of the
* string. Next this many bytes are read and decoded as UTF-8 encoded
* characters.
*
* @return the decoded string
* @exception UTFDataFormatException if the string cannot be decoded
* @exception IOException if the underlying stream throws an IOException.
*/
public String readLittleEndianUTF() throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if (byte2 == -1) {
throw new EOFException();
}
int numbytes = (byte1 << 8) + byte2;
char result[] = new char[numbytes];
int numread = 0;
int numchars = 0;
while (numread < numbytes) {
int c1 = readUnsignedByte();
int c2, c3;
// look at the first four bits of c1 to determine how many
// bytes in this char
int test = c1 >> 4;
if (test < 8) { // one byte
numread++;
result[numchars++] = (char) c1;
} else if (test == 12 || test == 13) { // two bytes
numread += 2;
if (numread > numbytes) {
throw new UTFDataFormatException();
}
c2 = readUnsignedByte();
if ((c2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
} else if (test == 14) { // three bytes
numread += 3;
if (numread > numbytes) {
throw new UTFDataFormatException();
}
c2 = readUnsignedByte();
c3 = readUnsignedByte();
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
throw new UTFDataFormatException();
}
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
} else { // malformed
throw new UTFDataFormatException();
}
} // end while
return new String(result, 0, numchars);
}
/**
*
* @return the next eight bytes of this input stream, interpreted as a
* little endian <code>double</code>.
* @exception EOFException if end of stream occurs before eight bytes have
* been read.
* @exception IOException if an I/O error occurs.
*/
public final double readLittleEndianDouble() throws IOException {
return Double.longBitsToDouble(this.readLittleEndianLong());
}
/**
*
* @return the next four bytes of this input stream, interpreted as a little
* endian <code>int</code>.
* @exception EOFException if end of stream occurs before four bytes have
* been read.
* @exception IOException if an I/O error occurs.
*/
public final float readLittleEndianFloat() throws IOException {
return Float.intBitsToFloat(this.readLittleEndianInt());
}
}