blob: 83c9fb464b30e1065157f538f0a8346f1129e9fc [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2021 MariaDB Corporation Ab
package org.mariadb.jdbc.client.socket.impl;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Permit to buf socket data, reading not only asked bytes, but available number of bytes when
* possible.
*/
public class ReadAheadBufferedStream extends FilterInputStream {
private static final int BUF_SIZE = 16384;
private final byte[] buf;
private int end;
private int pos;
/**
* Constructor
*
* @param in socket input stream
*/
public ReadAheadBufferedStream(InputStream in) {
super(in);
buf = new byte[BUF_SIZE];
end = 0;
pos = 0;
}
/**
* Returning byte array, from cache of reading socket if needed.
*
* @param externalBuf buf to fill
* @param off offset
* @param len length to read
* @return number of added bytes
* @throws IOException if exception during socket reading
*/
public synchronized int read(byte[] externalBuf, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
int totalReads = 0;
while (true) {
// read
if (end - pos <= 0) {
if (len - totalReads >= buf.length) {
// buf length is less than asked byte and buf is empty
// => filling directly into external buf
int reads = super.read(externalBuf, off + totalReads, len - totalReads);
if (reads <= 0) {
return (totalReads == 0) ? -1 : totalReads;
}
return totalReads + reads;
} else {
// filling internal buf
fillingBuffer(len - totalReads);
if (end <= 0) {
return (totalReads == 0) ? -1 : totalReads;
}
}
}
// copy internal value to buf.
int copyLength = Math.min(len - totalReads, end - pos);
System.arraycopy(buf, pos, externalBuf, off + totalReads, copyLength);
pos += copyLength;
totalReads += copyLength;
if (totalReads >= len || super.available() <= 0) {
return totalReads;
}
}
}
/**
* Fill buf with required length, or available bytes.
*
* @param minNeededBytes asked number of bytes
* @throws IOException in case of failing reading stream.
*/
private void fillingBuffer(int minNeededBytes) throws IOException {
int lengthToReallyRead = Math.min(BUF_SIZE, Math.max(super.available(), minNeededBytes));
end = super.read(buf, 0, lengthToReallyRead);
pos = 0;
}
public boolean markSupported() {
return false;
}
public void close() throws IOException {
super.close();
end = 0;
pos = 0;
}
public synchronized int available() throws IOException {
return end - pos + super.available();
}
public int read() throws IOException {
throw new IOException("read() from socket not implemented");
}
public long skip(long n) throws IOException {
throw new IOException("Skip from socket not implemented");
}
public void reset() throws IOException {
throw new IOException("reset from socket not implemented");
}
}