FlyRanger FlyRanger - 1 day ago 4
Java Question

How InputStream's read() method is implemented?

For the specific task I am trying to override

read()
method for my custom class that extends InputStream.

So far my implementation is:

private ArrayList<byte[]> inputBuffer = new ArrayList<>();
...
@Override
public int read(@NonNull byte[] b) throws IOException {
if (inputBuffer.size() > 0) {
b = inputBuffer.get(0);
inputBuffer.remove(0);
} else
return -1;

return b.length;
}


And I am adding data to my
InputStream
like this:

boolean writeDataToInputStream(byte[] data) {
int arrSize = inputBuffer.size();
if (data.length > 0) {
inputBuffer.add(data);
}
return arrSize < inputBuffer.size();
}


I've read documentation, I know how this method works by default. But I need somehow to pass
ArrayList
element to input parameter
byte[] b
.

I've been coding in java for few years and yet I've never put attention to how this method is actually implemented. How can I pass data to an incoming parameter and return the number of bytes from my ArrayList's element written?

I have to use custom Socket specifically for BLE w/ Input and Output stream due to my architecture, where I use WiFi socket, BT socket.

Please uncover this mystery for me.

Answer

When you create your own InputStream, the only method that you have to implement since it is an abstract method is read() which is also much less error prone than implementing read(byte[] b) and/or read(byte b[], int off, int len). Moreover please note that the default implementation of read(byte b[], int off, int len) already checks the arguments for you so unless you want to revalidate the arguments yourself, you should implement read() only.

So in your case this method could be:

// Current index in the last byte array read
private int index;
private List<byte[]> inputBuffer = new ArrayList<>();
...
@Override
public int read() throws IOException {
   if (inputBuffer.isEmpty()) {
        return -1;
    }
    // Get first element of the List
    byte[] bytes = inputBuffer.get(0);
    // Get the byte corresponding to the index and post increment the current index
    byte result = bytes[index++];
    if (index >= bytes.length) {
        // It was the last index of the byte array so we remove it from the list
        // and reset the current index
        inputBuffer.remove(0);
        index = 0;
    }
    return result;
}

However if you really want to implement read(byte b[], int off, int len), here is how it could look like:

@Override
public int read(byte b[], int off, int len) throws IOException {
    // Check parameters
    if (b == null) {
        throw new NullPointerException();
    } else if (off < 0 || len < 0 || len > b.length - off) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return 0;
    }
    if (inputBuffer.isEmpty()) {
        return -1;
    }
    int read = 0;
    // Iterate as long as don't get the expected bytes amount and the list is not empty
    do {
        byte[] bytes = inputBuffer.get(0);
        int lg = Math.min(bytes.length - index, len);
        // Copy the bytes from "bytes" to "b"
        System.arraycopy(bytes, index, b, off, lg);
        // Update all counters
        read += lg;
        off += lg;
        index += lg;
        len -= lg;
        if (index >= bytes.length) {
            // It was the last index of the byte array so we remove it from the list
            // and reset the current index
            inputBuffer.remove(0);
            index = 0;
        }
    } while (read < len && !inputBuffer.isEmpty());

    return read;
}
Comments