Little-Endian Numbers

It's likely that at some point in time you'll need to read a file full of little-endian data, especially if you're working on Intel hardware or with data written by native code on such a platform. Java has essentially no support for little-endian numbers. The LittleEndianOutputStream class in Example 8-5 and the LittleEndianInputStream class in Example 8-6 provide the support you need to do this. These classes are closely modeled on the java.io.DataInputStream and java.io.DataOutputStream classes. Some of the methods in these classes do exactly the same thing as the same methods in the DataInputStream and DataOutputStream classes. After all, a big-endian byte is no different from a little-endian byte. In fact, these two classes come very close to implementing the java.io.DataInput and java.io.DataOutput interfaces. Actually doing so would have been a bad idea, however, because client programmers expect objects implementing DataInput and DataOutput to use big-endian numbers, and it's best not to go against such common assumptions.

Example 8-5. The LittleEndianOutputStream class

package com.elharo.io;
import java.io.*;
public class LittleEndianOutputStream extends FilterOutputStream {
 protected int written;
 public LittleEndianOutputStream(OutputStream out) {
 super(out);
 }
 public void write(int b) throws IOException {
 out.write(b);
 written++;
 }
 public void write(byte[] data, int offset, int length)
 throws IOException {
 out.write(data, offset, length);
 written += length;
 }
 public void writeBoolean(boolean b) throws IOException {
 if (b) this.write(1);
 else this.write(0);
 }

 public void writeByte(int b) throws IOException {
 out.write(b);
 written++;
 }
 public void writeShort(int s) throws IOException {
 out.write(s & 0xFF);
 out.write((s >>> 8) & 0xFF);
 written += 2;
 }
 public void writeChar(int c) throws IOException {
 out.write(c & 0xFF);
 out.write((c >>> 8) & 0xFF);
 written += 2;
 }
 public void writeInt(int i) throws IOException {
 out.write(i & 0xFF);
 out.write((i >>> 8) & 0xFF);
 out.write((i >>> 16) & 0xFF);
 out.write((i >>> 24) & 0xFF);
 written += 4;
 }
 public void writeLong(long l) throws IOException {
 out.write((int) l & 0xFF);
 out.write((int) (l >>> 8) & 0xFF);
 out.write((int) (l >>> 16) & 0xFF);
 out.write((int) (l >>> 24) & 0xFF);
 out.write((int) (l >>> 32) & 0xFF);
 out.write((int) (l >>> 40) & 0xFF);
 out.write((int) (l >>> 48) & 0xFF);
 out.write((int) (l >>> 56) & 0xFF);
 written += 8;
 }
 public final void writeFloat(float f) throws IOException {
 this.writeInt(Float.floatToIntBits(f));
 }
 public final void writeDouble(double d) throws IOException {
 this.writeLong(Double.doubleToLongBits(d));
 }
 public void writeBytes(String s) throws IOException {
 int length = s.length( );
 for (int i = 0; i < length; i++) {
 out.write((byte) s.charAt(i));
 }
 written += length;
 }
 public void writeChars(String s) throws IOException {
 int length = s.length( );
 for (int i = 0; i < length; i++) {
 int c = s.charAt(i);
 out.write(c & 0xFF);
 out.write((c >>> 8) & 0xFF);
 }
 written += length * 2;
 }
 public void writeUTF(String s) throws IOException {
 int numchars = s.length( );
 int numbytes = 0;
 for (int i = 0 ; i < numchars ; i++) {
 int c = s.charAt(i);
 if ((c >= 0x0001) && (c <= 0x007F)) numbytes++;
 else if (c > 0x07FF) numbytes += 3;
 else numbytes += 2;
 }
 if (numbytes > 65535) throw new UTFDataFormatException( );
 out.write((numbytes >>> 8) & 0xFF);
 out.write(numbytes & 0xFF);
 for (int i = 0 ; i < numchars ; i++) {
 int c = s.charAt(i);
 if ((c >= 0x0001) && (c <= 0x007F)) {
 out.write(c);
 }
 else if (c > 0x07FF) {
 out.write(0xE0 | ((c >> 12) & 0x0F));
 out.write(0x80 | ((c >> 6) & 0x3F));
 out.write(0x80 | (c & 0x3F));
 written += 2;
 }
 else {
 out.write(0xC0 | ((c >> 6) & 0x1F));
 out.write(0x80 | (c & 0x3F));
 written += 1;
 }
 }
 written += numchars + 2;
 }

 public int size( ) {
 return this.written;
 }
}

Notice how all writing is done by passing byte values to the underlying output stream out (set in the constructor and inherited from the superclass, FilterOutputStream). The primary purpose of these methods is to convert the Java data type to bytes and then write them in a little-endian order. In general, the conversions are accomplished by shifting the bits of interest into the low-order eight bits and then masking it off. For example, consider the writeInt( ) method:

public void writeInt(int i) throws IOException {
 out.write(i & 0xFF);
 out.write((i >>> 8) & 0xFF);
 out.write((i >>> 16) & 0xFF);
 out.write((i >>> 24) & 0xFF);
 written += 4;
 }

A Java int is composed of four bytes in big-endian order. Thus, the low-order byte is in the last eight bits. This byte needs to be written first in a little-endian scheme. The mask 0xFF has one bit in the low-order eight bits and zero bits everywhere else. By bitwise ANDing 0xFF with i, we select the low-order eight bits of i. The second-lowest order bytethat is, bits 16 to 23is selected by first shifting the bits right without sign extension into the low-order bits. That's the purpose of (i >>> 8). Then this byte can be retrieved with the same 0xFF mask used before. The same is done for the second-to-lowest-order byte and the highest-order byte. Here, however, it's necessary to shift by 16 and 24 bits, respectively.

floats and doubles are written by first converting them to ints and longs using Float.floatToIntBits( ) and Double.doubleTolongBits( ) and then invoking writeInt( ) or writeLong( ) to write those bits in little-endian order.

Each method increments the protected field written by the number of bytes actually written. This tracks the total number of bytes written onto the output stream at any one time.

Example 8-6 shows the corresponding LittleEndianInputStream class, based on the DataInputStream class.

Example 8-6. The LittleEndianInputStream class

package com.elharo.io;
import java.io.*;
public class LittleEndianInputStream extends FilterInputStream {

 public LittleEndianInputStream(InputStream in) {
 super(in);
 }
 public boolean readBoolean( ) throws IOException {
 int bool = in.read( );
 if (bool == -1) throw new EOFException( );
 return (bool != 0);
 }
 public byte readByte(int b) throws IOException {
 int temp = in.read( );
 if (temp == -1) throw new EOFException( );
 return (byte) temp;
 }
 public int readUnsignedByte( ) throws IOException {
 int temp = in.read( );
 if (temp == -1) throw new EOFException( );
 return temp;
 }
 public short readShort( ) 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 << 24) >>> 16) + (byte1 << 24) >>> 24);
 }
 public int readUnsignedShort( ) throws IOException {
 int byte1 = in.read( );
 int byte2 = in.read( );
 if (byte2 == -1) throw new EOFException( );
 return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
 }
 public char readChar( ) throws IOException {
 int byte1 = in.read( );
 int byte2 = in.read( );
 if (byte2 == -1) throw new EOFException( );
 return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
 }
 public int readInt( ) throws IOException {
 int byte1 = in.read( );
 int byte2 = in.read( );
 int byte3 = in.read( );
 int byte4 = in.read( );
 if (byte4 == -1) {
 throw new EOFException( );
 }
 return (byte4 << 24)
 + ((byte3 << 24) >>> 8)
 + ((byte2 << 24) >>> 16)
 + ((byte1 << 24) >>> 24);
 }
 public long readLong( ) 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 << 56) >>> 8)
 + ((byte6 << 56) >>> 16)
 + ((byte5 << 56) >>> 24)
 + ((byte4 << 56) >>> 32)
 + ((byte3 << 56) >>> 40)
 + ((byte2 << 56) >>> 48)
 + ((byte1 << 56) >>> 56);
 }
 public String readUTF( ) 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( );
 // The first 4 bits of c1 determine how many bytes are in this char
 int test = c1 >> 4;
 if (test < 8) { // one byte
 numread++;
 result[numchars++] = (char) c1;
 }
 else if (test == 12 || test == 13) { // 2 bytes
 numread += 2;
 if (numread > numbytes) throw new UTFDataFormatException( );
 int 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( );
 int c2 = readUnsignedByte( );
 int 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);
 }
 public final double readDouble( ) throws IOException {
 return Double.longBitsToDouble(this.readLong( ));
 }
 public final float readFloat( ) throws IOException {
 return Float.intBitsToFloat(this.readInt( ));
 }
 public final int skipBytes(int n) throws IOException {
 for (int i = 0; i < n; i += (int) skip(n - i));
 return n;
 }
}

This class is used later in this chapter to view files containing little-endian numbers.

Basic I/O

Introducing I/O

Output Streams

Input Streams

Data Sources

File Streams

Network Streams

Filter Streams

Filter Streams

Print Streams

Data Streams

Streams in Memory

Compressing Streams

JAR Archives

Cryptographic Streams

Object Serialization

New I/O

Buffers

Channels

Nonblocking I/O

The File System

Working with Files

File Dialogs and Choosers

Text

Character Sets and Unicode

Readers and Writers

Formatted I/O with java.text

Devices

The Java Communications API

USB

The J2ME Generic Connection Framework

Bluetooth

Character Sets



Java I/O
Java I/O
ISBN: 0596527500
EAN: 2147483647
Year: 2004
Pages: 244

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net