-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Description
Affects: 6.0.2
The methods asInputStream(...) of the interface DataBuffer have a default implementations. Every such method returns an instance of the class DataBufferInputStream.
Violation of backward compatibility
The constructor of the DataBufferInputStream marks the current write position of the underlying data buffer as the end position of the input stream, which the created instance represents after. The further written data into the underlying data buffer are not reflected by the input stream instance. An InputStream instance, returned by the DefaultDataBuffer of previous versions (e.g. 5.*.*) had this reflections.
Violation of design by contract of InputStream
The class DataBufferInputStream supports marking. But the method mark takes the argument, which is used as a certain read position instead of read limit, as the method reset is called. This violates the specification of the marking concept of InputStream.
Suggestion how it should be.
final class DataBufferInputStream extends InputStream {
private final DataBuffer dataBuffer;
private final boolean releaseOnClose;
private boolean closed;
private int markedPosition;
private int readLimit;
public DataBufferInputStream(DataBuffer dataBuffer, boolean releaseOnClose) {
Assert.notNull(dataBuffer, "DataBuffer must not be null");
this.dataBuffer = dataBuffer;
this.releaseOnClose = releaseOnClose;
this.markedPosition = -1;
}
@Override
public int read() throws IOException {
checkClosed();
if (available() == 0) {
return -1;
}
return this.dataBuffer.read() & 0xFF;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
int available = available();
if (available == 0) {
return -1;
}
len = Math.min(available, len);
this.dataBuffer.read(b, off, len);
return len;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public void mark(int readLimit) {
if(readLimit < 1) {
throw new IllegalArgumentException("readLimit must be greater than 0");
}
this.markedPosition = this.dataBuffer.readPosition();
this.readLimit = readLimit
}
@Override
public int available() {
return this.dataBuffer.readableByteCount();
}
@Override
public void reset() {
String errorMessage = null;
int bytesReadSinceLastMark;
if(this.markedPosition == -1) {
errorMessage = "The input stream was never marked";
} else if((bytesReadSinceLastMark = this.dataBuffer.readPosition() - this.markedPosition) > this.readLimit) {
errorMessage = "The read limit %d was exceeded since last mark at %d by %d bytes".formatted(this.readLimit, this.markedPosition, bytesReadSinceLastMark);
} else if(bytesReadSinceLastMark < 0) {
errorMessage = "The read position was set before marked position";
}
if(errorMessage != null) {
throw new IOException(errorMessage);
}
this.dataBuffer.readPosition(this.markedPosition);
}
@Override
public void close() {
if (this.closed) {
return;
}
if (this.releaseOnClose) {
DataBufferUtils.release(this.dataBuffer);
}
this.closed = true;
}
private void checkClosed() throws IOException {
if (this.closed) {
throw new IOException("DataBufferInputStream is closed");
}
}
}