org.apache.flink.runtime.state.gemini.engine.fs.GeminiBufferedInputStream Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flink.runtime.state.gemini.engine.fs;
import org.apache.flink.core.fs.FSDataInputStream;
import java.io.IOException;
/**
* refer to {@link java.io.BufferedInputStream}.
*/
public class GeminiBufferedInputStream extends FSDataInputStream {
private static final int DEFAULT_BUFFER_SIZE = 8192;
/**
* The internal buffer array where the data is stored. When necessary,
* it may be replaced by another array of
* a different size.
*/
protected volatile byte[] buffer;
/**
* The index one greater than the index of the last valid byte in
* the buffer.
* This value is always
* in the range 0
through buffer.length
;
* elements buf[0]
through buffer[count-1]
*
contain buffered input data obtained
* from the underlying input stream.
*/
protected int count;
/**
* The current position in the buffer. This is the index of the next
* character to be read from the buffer
array.
*
* This value is always in the range 0
* through count
. If it is less
* than count
, then buffer[pos]
* is the next byte to be supplied as input;
* if it is equal to count
, then
* the next read
or skip
* operation will require more bytes to be
* read from the contained input stream.
*/
protected int pos;
private final FSDataInputStream in;
/**
* Creates a GeminiBufferedInputStream
* and saves its argument, the input stream
* in
, for later use. An internal
* buffer array is created and stored in buf
.
*
* @param in the underlying input stream.
*/
public GeminiBufferedInputStream(FSDataInputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
/**
* Creates a GeminiBufferedInputStream
* with the specified buffer size,
* and saves its argument, the input stream
* in
, for later use. An internal
* buffer array of length size
* is created and stored in buf
.
*
* @param in the underlying input stream.
* @param size the buffer size.
* @exception IllegalArgumentException if {@code size <= 0}.
*/
public GeminiBufferedInputStream(FSDataInputStream in, int size) {
this.in = in;
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
this.buffer = new byte[size];
}
/**
* Fills the buffer with more data. This method assumes that all data
* has already been read in, hence pos > count.
*/
private void fill() throws IOException {
count = 0;
pos = 0;
int n = in.read(buffer, pos, buffer.length);
if (n > 0) {
count = n + pos;
}
}
@Override
public int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count) {
return -1;
}
}
return buffer[pos++] & 0xff;
}
/**
* Read characters into a portion of an array, reading from the underlying
* stream at most once if necessary.
*/
private int read1(byte[] b, int off, int len) throws IOException {
int avail = count - pos;
if (avail <= 0) {
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, do not bother to copy the
bytes into the local buffer. In this way buffered streams will
cascade harmlessly. */
if (len >= buffer.length) {
return in.read(b, off, len);
}
fill();
avail = count - pos;
if (avail <= 0) {
return -1;
}
}
int cnt = (avail < len) ? avail : len;
System.arraycopy(buffer, pos, b, off, cnt);
pos += cnt;
return cnt;
}
/**
* Reads bytes from this byte-input stream into the specified byte array,
* starting at the given offset.
*
*
This method implements the general contract of the corresponding
* {@link FSDataInputStream#read(byte[], int, int) read}
method of
* the {@link FSDataInputStream}
class. As an additional
* convenience, it attempts to read as many bytes as possible by repeatedly
* invoking the read
method of the underlying stream. This
* iterated read
continues until one of the following
* conditions becomes true:
*
* - The specified number of bytes have been read,
*
*
- The
read
method of the underlying stream returns
* -1
, indicating end-of-file, or
*
* - The
available
method of the underlying stream
* returns zero, indicating that further input requests would block.
*
*
If the first read
on the underlying stream returns
* -1
to indicate end-of-file then this method returns
* -1
. Otherwise this method returns the number of bytes
* actually read.
*
* Subclasses of this class are encouraged, but not required, to
* attempt to read as many bytes as possible in the same fashion.
*
* @param b destination buffer.
* @param off offset at which to start storing bytes.
* @param len maximum number of bytes to read.
* @return the number of bytes read, or -1
if the end of
* the stream has been reached.
* @exception IOException if this input stream has been closed by
* invoking its {@link #close()} method,
* or an I/O error occurs.
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = 0;
for ( ; ; ) {
int nread = read1(b, off + n, len - n);
if (nread <= 0) {
return (n == 0) ? nread : n;
}
n += nread;
if (n >= len || in.available() <= 0) {
return n;
}
}
}
/**
* See the general contract of the skip
* method of FSDataInputStream
.
*
* @exception IOException if the stream does not support seek,
* or if this input stream has been closed by
* invoking its {@link #close()} method, or an
* I/O error occurs.
*/
@Override
public long skip(long n) throws IOException {
if (n <= 0) {
return 0;
}
long avail = count - pos;
if (avail <= 0) {
return in.skip(n);
}
long skipped = (avail < n) ? avail : n;
pos += skipped;
return skipped;
}
@Override
public int available() throws IOException {
int n = count - pos;
int avail = in.available();
return n > (Integer.MAX_VALUE - avail)
? Integer.MAX_VALUE
: n + avail;
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
* Once the stream has been closed, further read(), available(), reset(),
* or skip() invocations will throw an IOException.
* Closing a previously closed stream has no effect.
*
* @exception IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
in.close();
buffer = null;
}
@Override
public void seek(long desired) throws IOException {
int avail = count - pos;
if (avail <= 0) {
in.seek(desired);
return;
}
long upperPos = in.getPos();
long lowerPos = upperPos - count;
if (desired > upperPos || desired < lowerPos) {
pos = count;
in.seek(desired);
return;
}
pos = (int) (desired - lowerPos);
}
@Override
public long getPos() throws IOException {
long streamPos = in.getPos();
int n = count - pos;
return streamPos - n;
}
public FSDataInputStream getWrappedInputStream() {
return in;
}
}