Question

This is my first post on here, so apologies for the problems it likely has.

I've been working with a custom input stream recently, that uses a byte array to store data in (similar to a ByteArrayInputStream) but with more control over the pointer. The problem is for some reason my implementation of read() starts returning negative numbers after the values get past 127, which causes DataInputStream to assume it's the EOF.

I've condensed things into a small program to demonstrate the problem:

(broken up into pieces, because I can't seem to figure out how to fit it all into a single code block)

The custom input stream class:

class TestByteArrayInputStream extends InputStream {

    byte[] data;

    {
        // fill with some data
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(out);

        try {
            for (int i = 0; i < 256; i++) { // fill array with shorts valued 0-255
                dout.writeShort(i);
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

        data = out.toByteArray();

    }

    int pointer = 0;

    @Override
    public int read() throws IOException {
        if (pointer >= data.length) {
            pointer = 0;
        }
        return data[pointer++]; // I've tried casting this to a char to remove signing, and using Integer.valueOf, but neither solve the problem.
    }
}

And here's the main method:

public class Bugdemo {

    public static void main(String[] args) {
        TestByteArrayInputStream tin = new TestByteArrayInputStream();
        DataInputStream din = new DataInputStream(tin);
        try { // read through normally
            for (int i = 0; i < 256; i++) {
                System.out.println(din.readShort());
            }
        } catch (Throwable t) {
            System.out.println(t.toString()); // avoid logging issues
        }
        tin.pointer = 0; // reset to beginning of data
        try {
            for (int i = 0; i < 256; i++) {
                // readShort code with added debugging
                int ch1 = tin.read();
                int ch2 = tin.read();
                if ((ch1 | ch2) < 0) {
                    System.out.print("readshort \"eof\": ");
                    System.out.printf("data in array is %02X ", tin.data[tin.pointer - 2]);
                    System.out.printf("%02X ", tin.data[tin.pointer - 1]);
                    System.out.printf(" but got %02X ", ch1);
                    System.out.printf("%02X from read()", ch2);
                    System.out.println();
                    //throw new EOFException(); // this is in DataInputStream.readShort after if((ch1 | ch2) < 0)
                } else {
                    System.out.println((short) ((ch1 << 8) + (ch2 << 0)));
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

And here's the output (pasted so this isn't too long): http://paste.ubuntu.com/6642589/ (is there a better way of doing this on here?)

The important bit:

readshort "eof": data in array is 00 80  but got 00 FFFFFF80 from read()

From my debugging I'm pretty sure it's a casting issue from the byte in the array to an int for returning in read(), but shouldn't it cast properly naturally? If not, what's the proper way of doing this?

Was it helpful?

Solution

readShort works as expected, read too.

Integer datatypes in Java are signed, including byte. As read returns a byte and you are outputting this value as it is you get the negative representation. You have to convert it to an int with the unsigned value before printing with ch1 & 0xff.

OTHER TIPS

No suprises here. The Max Value for bytes is (2 to power 7) - 1

http://docs.oracle.com/javase/7/docs/api/java/lang/Byte.html#MAX_VALUE

all types in java are signed so byte can hold values between -128 +127. You are putting two bytes by writing short

  for (int i = 0; i < 256; i++) { // fill array with shorts valued 0-255
            dout.writeShort(i);

but the code reads just one byte:

return data[pointer++];

it should be done this way

DataInputStream din = new DataInputStream(new ByteArrayInputStream(out.toByteArray()));
...

return din.readShort();

It isn't always easy. Just a quick summary for those who are still lost:

simple input stream

The read() of an input stream just returns a value in range [0;255]. However, if no data is available, then it will return a -1.

int value = inputStream.read(); // -1 if no data

If you would just cast this to a byte, then you are creating an overflow and you actually convert the range of [0;255] to a range of [-128;127].

byte signedValue = (byte) value;

data input stream

Now, if you wrap your InputStream in a DataInputStream then additional methods are available, such as the readByte() method. This method will return a value in range [-128;127] because that is the range of the byte type of java. Often you may want to convert it to a positive value.

If there is no data available, then of course a DataInputStream cannot return -1. So instead it will throw an EOFException.

byte value = dataInputStream.readByte(); // throws EOFException
int positiveValue = value & 0xFF;
char character = (char) positiveValue;

PS: The DataInputStream offers some convenient methods that help you to read the values immediately in the correct value range.

int positiveValue = dataInputStream.readUnsignedByte();
int positiveValue = dataInputStream.readUnsignedShort(); 

socket input stream

But it can be more complex. If your initial input stream is actually a SocketInputStream then no matter which method you use or how you wrap it, you will not receive -1 or EOFException. Instead you will receive a SocketTimeoutException.

socket.setSoTimeout(1000);
int value = socketInputStream.read(); // throws SocketTimeoutException
byte signedValue = (byte) value;
char character = (char) value;

There is just a little shortcoming in that last statement: Very rarely the read() method of a SocketInputStream will not return a SocketTimeoutException in case of a timeout. It can actually return a -1 if the input stream is not correctly bound. In that case the connection is broken, and you need to close everything down and reconnect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top