what is the use of mark read limit in bufferedinputstream
Asked Answered
N

5

6

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

Nunhood answered 22/2, 2017 at 10:55 Comment(2)
when I'm calling the reset(), it does not thrown any exception to me. As per documentation, mark should be invalid after I have read the second byte since I have set the mark limit to 1 but it is not. So what is the purpose of this mark()?Nunhood
Use quote formatting for text that is quoted. And please lay off the boldface. It hurts our eyes.Shimberg
B
2

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
Brewage answered 23/2, 2017 at 9:2 Comment(3)
The docs also says this: the maximum limit of bytes that can be read before the mark position becomes invalid, but it doesn't become invalid always which is the OP's issue. It doesn't read "could become invalid".Karren
@john16384: conceptually, the mark is invalid as you can’t rely on it. A different question is, which consequences arise. The documentation of reset() clearly says: “If … 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.” You see, “might be”, not “is guaranteed to be”…Brewage
Ok, that's a nice find.Karren
K
1

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.

Karren answered 22/2, 2017 at 12:47 Comment(4)
In the above example (which I have provided), I have set the mark limit to 1 and reading the bytes more than one. As per documentation, mark should be invalid after I have read the second byte since I have set the mark limit to 1 but it is not. So what is the purpose of this mark()?Nunhood
I think you found a bug in the documentation here, even though the docs seem to hint that the reset should fail as soon as the limit is exceeded, this is not happening in practice. I think they intended to say minimum limit instead of maximum limit.Karren
A “minimum limit” doesn’t make sense. Of course, you are not required to read as much bytes before calling reset().Brewage
@Brewage I think it makes more sense... a maximum limit indicates that it could also be less than the maximum; a minimum limit guarantees at least that many bytes or more apparently if the underlying implementation allows for itKarren
S
0

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--

Smallclothes answered 11/7, 2020 at 21:21 Comment(0)
L
0
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();}
    }
}
Loot answered 12/1, 2021 at 18:59 Comment(0)
O
0

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.

Od answered 20/1, 2023 at 13:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.