Why BufferedInputStream(InputStream in, int bufSize) is not reading data in chunks of bufSize from the inputStream while using mark()?

Viewed 34

Here is my code:

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class MarkAndResetDemo {

    public static void main(String[] args) {
        String s = "Today is Sunday!";
        byte buf[] = s.getBytes();
        
        System.out.println("s: "+s);
        ByteArrayInputStream in = new ByteArrayInputStream(buf);
        int c, count = 0;
        
        try(BufferedInputStream f = new BufferedInputStream(in, 4)) {//Line 1
            while((c=f.read()) != -1) {
                System.out.print((char)c);
                count++;
                
                if(count==3) f.mark(2); //Line 2
                
                if(count==8) f.reset(); //Line 3
                
            }
        }
        catch(IOException e) {
            e.printStackTrace();
        }

    }

}

When the count is set as 8 in Line 3, output is:

s: Today is Sunday!

Today isjava.io.IOException: Resetting to invalid mark

When the count is set as 7 in Line 3, output is:

s: Today is Sunday!

Today iay is Sunday!

So my question is why "invalid mark" exception is not appearing when count is 7 in Line 3 since more than 2 bytes have been read since the mark(int i) was last called? It seems that internal buffer of BufferedInputStream is being filled one byte at a time unless mark(int i) is invalidated.

I know that BufferedInputStream will read chunk of data of size equal to its internal buffer from the inputstream (and here the buffer size is 4). So after 4 iterations of while loop the internal buffer of BufferedInputStream should contain the next 4 bytes from the byte array(buf[] which means the characters 'y', ' ', 'i', 's' in byte encoded form). Also the mark(2) in Line 2 will place a hook on the current position(the next byte to be read in the inputstream) while mark(int i) is called.

2 Answers

When the mark is set, the buffer contains 'Toda' and the marked character is 'a'. When we need to refill the buffer to print 'y', the first 3 characters are discarded (because they aren't needed for the mark), three new characters are read, and the buffer now contains 'ay i'. After printing 7 characters 'ay ' we haven't needed to refill the buffer, so reset can still reset to the 'a'.

If you want to understand things like this, put some breakpoints in the JDK classes.

Thanks, tgdavies for your explanation and your advice to use the breakpoints. I could see the protected fields of the BufferedInputStream like markpos, marlimit, pos and count. After playing with the above code by setting different values for marklimit and internal buffer size I came to these conclusions:

For internal buffer of length n >= marklimit:

If markpos = k where k is the index of the internal buffer(buf) and reset is not called even after (n-k) bytes have been read from the buf since mark() was last called then on reading the next input from the input stream:

Bytes from index position k to n-1 of buf are written at index position 0 to n-k-1 and at the index ranging from n-k to n-1 new data is written and the value of pos(the index position of the next byte to be read in buf) becomes n-k+1 since by now one byte from the newly added data has been read.

For internal buffer of length n < marklimit:

A new buffer of length marklimit will be created after n+k bytes have been read and (n+k+1)th byte is about to be read.

I know it is a long explanation but thought this might be useful for someone else if they come across this mark() and reset().

Related