sarath kumar
sarath kumar

Reputation: 219

what is the use of mark read limit in bufferedinputstream

I'm new to Java streams, I would like to read a specific files content then need to read it from the beginning. I have created a BufferedInputStream and i'm confused about the documentation of BufferedInputStream.mark(int markLimit)

Documentation says:

public void mark(int readlimit)

This method marks a position in the input to which the stream can be "reset" by calling the reset() method. The parameter readlimit is the number of bytes that can be read from the stream after setting the mark before the mark becomes invalid. For example, if mark() is called with a read limit of 10, then when 11 bytes of data are read from the stream before the reset() method is called, then the mark is invalid and the stream object instance is not required to remember the mark.

Note that the number of bytes that can be remembered by this method can be greater than the size of the internal read buffer. It is also not dependent on the subordinate stream supporting mark/reset functionality.

Overrides: mark in class FilterInputStream

Parameters: readlimit - The number of bytes that can be read before the mark becomes invalid**

My code is:

public class Test {
    public static void main(String[] args) throws IOException {

        File resource = new File("beforeFix.txt");          
        FileInputStream fileInputStream = new FileInputStream(resource);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
        int i = bufferedInputStream.read();
        bufferedInputStream.mark(1);
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        bufferedInputStream.reset(); 
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        i = bufferedInputStream.read();
        bufferedInputStream.reset();  
    }
}

In the above code I have set the marklimit as 1 but the mark is not goes into invalid as per the documentation.

Can anyone clearly explain me what is the actual purpose of setting this with small example?

Thanks in advance

Upvotes: 6

Views: 8901

Answers (5)

axel
axel

Reputation: 11

As the Oracle documentation says about reset() method of InputStream (which is override by FilterInputStream and further by BufferedInputStream).

The general contract of reset is:

  • The general contract of reset is:

    -If the method markSupported returns true, then:

       -If the method mark has not been called since the stream was created, or 
        the number of bytes read from the stream since mark was last called is 
        larger than the argument to mark at that last call, then an IOException 
        **might be** thrown.
        (note that IOException might be thrown)
    
       -If such an IOException is not thrown, then the stream is reset to a state 
        such that all the bytes read since the most recent call to mark (or since 
        the start of the file, if mark has not been called) will be resupplied to 
        subsequent callers of the read method, followed by any bytes that 
        otherwise would have been the next input data as of the time of the call 
        to reset.
    

I hope, your question is solved and help this for future programmers.

Upvotes: 0

Ryan
Ryan

Reputation: 322

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Main {
    
    static final String fileAbsolutePath = "file.txt";
    
    public static void main(String[] args) {
        // file contains 3 chars 'a', 'b', and 'c'
        try (FileInputStream fileInputStream = new FileInputStream(fileAbsolutePath);
             BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {
            
            System.out.println((char) bufferedInputStream.read()); // prints a
            bufferedInputStream.mark(2);   // place mark at second byte
            System.out.println((char) bufferedInputStream.read()); // prints b
            System.out.println((char) bufferedInputStream.read()); // prints c
            System.out.println(bufferedInputStream.read()); // prints -1
            
            bufferedInputStream.reset();    // meaning start again from where I placed the mark
            System.out.println((char) bufferedInputStream.read()); // prints b
            System.out.println((char) bufferedInputStream.read()); // prints c
            System.out.println( bufferedInputStream.read()); // prints -1
    
        } catch (IOException ie) { ie.printStackTrace();}
    }
}

Upvotes: 0

Shirshendu Deb Roy
Shirshendu Deb Roy

Reputation: 21

Please read the below documentation to better understand it. I had the same doubt as you and then decided to read about it in detail.

  1. If the method mark has not been called since the stream was created, or the number of bytes read from the stream since the mark was last called is larger than the argument to mark at that last call, then an IOException might be thrown.
  2. If such an IOException is not thrown, then the stream is reset to a state such that all the bytes read since the most recent call to mark (or since the start of the file, if the mark has not been called) will be resupplied to subsequent callers of the read method, followed by any bytes that otherwise would have been the next input data as of the time of the call to reset.

From the first point, it is now very much clear that an IOException is not guaranteed to be thrown. So, if you are reading more than the allowed number of bytes (the specified argument of mark method) after calling the mark method, then it's a risky operation and not recommended.

From the second point, you can understand what happens if IOException is not thrown.

The link to the documentation: https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html#reset--

Upvotes: 0

Holger
Holger

Reputation: 298579

By calling mark with a specified limit, you are requesting the capability to support resetting after reading up to the specified limit, you are not denying a capability beyond that. The specification clearly says:

However, the stream is not required to remember any data at all if more than readlimit bytes are read from the stream before reset is called.

“is not required to” does not imply “is not allowed to”. The specification simply states what you can not expect to always work, it doesn’t state what you can expect to always fail.

In the case of BufferedInputStream, it’s easy to explain what happens under the hood. Each BufferedInputStream has a capacity, the default is 8192, and it always can reset past as many bytes as its current buffer capacity. By specifying a higher limit, you will cause it to allocate a larger buffer when needed, to fulfill the guaranty.

Since you can’t query a stream for its current buffer capacity, you can only rely on the guaranty that reset works as long as you don’t read more bytes than the specified limit.

It’s easy to change your example to make it fail reproducible:

File resource = new File("beforeFix.txt");          
FileInputStream fileInputStream = new FileInputStream(resource);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 1);
int i = bufferedInputStream.read();
bufferedInputStream.mark(1);
i = bufferedInputStream.read();
i = bufferedInputStream.read();
bufferedInputStream.reset(); // will fail

Upvotes: 2

john16384
john16384

Reputation: 8074

In order for reset to work and go back to the position you marked, the data that was read after you marked needs to be buffered in memory. The value you specify when marking is the amount of memory that should be reserved for this.

So if you intend to read 100 bytes before calling reset, then your buffer needs to be atleast 100 bytes, and so that is what you have to call mark with.

bufferedInputStream.mark(200);

... read no more than 200 bytes ...

bufferedInputStream.reset();  // reset back to marked position

Update

It looks like the documentation for mark is not matching the actual behaviour. The documentation states:

the maximum limit of bytes that can be read before the mark position becomes invalid

However, it looks like it should be the minimum limit, or at the very least the underlying implementations are not required to discard the mark as soon as the read limit is exceeded if they can still support resetting to the marked position.

Upvotes: 1

Related Questions