==================
[Android Glide 3.7.0 source code analysis (VII), detailed graphics transformation and decoding](
)The role of RecyclableBufferedInputStream in mark(int marklimit) and reset() methods has been mentioned. This paper discusses the specific implementation ideas
mark(int marklimit) is used to create a repeatable read area in the stream whose starting point is markPos and length is markLimit. When the reset() method is called, the read position of the stream will return to markPos to realize that markLimit can be read repeatedly
be careful:
- When the read position reaches readPos_2, the reset() method will fail because it has exceeded the length range of markLimit
- The starting point markPos of mark(int marklimit) mark is the read position of the stream during method call (when mark in the above figure, the read position is readPos_0, so markPos == readPos_0)
We all know that a Stream can only be read once
2, Realization idea
The above diagram is just a black box operation route view. Let's analyze the specific implementation diagram Since InputStream can only be read once and consumed, we must use an external tool to repeat reading. Here is buf a byte [] array with an initial size of 64K. The elements in the above figure are described below
- On the right is unread data that still exists in the InputStream
- On the left is the data that has been read from the process and stored in buf
- count represents the number of valid data in the buf (if you read to the end of the stream, the buf may be dissatisfied with reading)
- markpos represents the position of the mark()
- The blue bar is the part that needs to be read repeatedly. marklimit indicates its length
- pos represents the location currently being read
Now that we have buf as the cache, we can cache the stream data of mark in buf, so that we can read it repeatedly
Note: the buf size is not constant. When marklimit > buf.length and some other conditions are met, the buf will be expanded twice
3, Source code analysis
Look at the variables of RecyclableBufferedInputStream (in fact, I talked about them when I introduced the idea)
public class RecyclableBufferedInputStream extends FilterInputStream { // buf caches the data read from the stream private volatile byte[] buf; // buf length of valid data private int count; // mark marks the length of the data private int marklimit; // Mark is the location of the mark. The default value of - 1 indicates that there is no mark private int markpos = -1; // Current read buf location private int pos; }
mark() and reset()
Let's look at the mark and reset methods
public synchronized void mark(int readlimit) { marklimit = Math.max(marklimit, readlimit); markpos = pos; } public synchronized void reset() throws IOException { if (buf == null) { throw new IOException("Stream is closed"); } if (-1 == markpos) { throw new InvalidMarkException("Mark has been invalidated"); } pos = markpos; }
mark did two things: assignment + assignment
- Mark limit is compared with the old one to get a maximum value
- Set markpos to the current reading position
reset did one thing
- The current position pos is reset to markpos
read()
Let's see what the read method does
public synchronized int read() throws IOException { // Use local refs since buf and in may be invalidated by an // unsynchronized close() byte[] localBuf = buf; InputStream localIn = in; if (localBuf == null || localIn == null) { throw streamClosed(); } // Are there buffered bytes available? if (pos >= count && fillbuf(localIn, localBuf) == -1) { // no, fill buffer return -1; } // localBuf may have been invalidated by fillbuf if (localBuf != buf) { localBuf = buf; if (localBuf == null) { throw streamClosed(); } } // Did filling the buffer fail with -1 (EOF)? if (count - pos > 0) { return localBuf[pos++] & 0xFF; } return -1; }
This read() method is used to read a single character. It is read-only and returns 1 byte at a time
- POS > = count means that the buf has been read. Call fillbuf(localIn, localBuf) to refill the buf with data
- Count - POS > 0 means buf data is enough. Read directly and return localbuf [POS + +] & 0xff
return here is an int??? The good byte is that the returned stream data has an unsigned byte range of 0 ~ 255, while the byte in java has a signed range of - 128 ~ 127 by default. The data range is not enough and can only be rounded up with int. note that this line of code: localbuf [POS + +] & 0xff clears all the upper 24 bits to achieve the effect of returning 0 ~ 255
It seems that there is no mention of mark in the middle. I guess it can only be implemented in the fillbuf function. Follow in and have a look
fillbuf()
The code is long. Be patient
private int fillbuf(InputStream localIn, byte[] localBuf) throws IOException { // If there is no tag or the read area exceeds the range of the tag, read the data directly from the stream to the buf if (markpos == -1 || pos - markpos >= marklimit) { // Mark position not set or exceeded readlimit int result = localIn.read(localBuf); if (result > 0) { markpos = -1; pos = 0; count = result; } return result; } // [valid flag] buf.length here is not enough. Expand the capacity by 2 times if (markpos == 0 && marklimit > localBuf.length && count == localBuf.length) { } // [valid flag] buf.length here is not enough. Expand the capacity by 2 times if (markpos == 0 && marklimit > localBuf.length && count == localBuf.length) {