com.kolibrifx.plovercrest.server.internal.protocol.SizeLimitedByteBufferReader Maven / Gradle / Ivy
Show all versions of plovercrest-server Show documentation
/*
* Copyright (c) 2010-2017, KolibriFX AS. Licensed under the Apache License, version 2.0.
*/
package com.kolibrifx.plovercrest.server.internal.protocol;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
/**
* A helper class for reading a limited amount of data from an underlying channel into a
* {@link ByteBuffer}. The buffer can have a smaller capacity than the total amount of data, but not
* smaller than the size passed to {@link #readSlice(int)}. Slices returned are invalidated by the
* next read* call.
*
* While the reader is in use, the underlying {@link ByteBuffer} should not be accessed directly.
*/
public class SizeLimitedByteBufferReader {
private final ByteBuffer buffer;
private final ReadableByteChannel channel;
private int unreadByteCount;
public SizeLimitedByteBufferReader(final ByteBuffer buffer,
final int totalSize,
final ReadableByteChannel channel) {
this.buffer = buffer;
this.channel = channel;
this.unreadByteCount = totalSize;
// initialize the buffer so that the first read call should trigger readAsMuchAsPossible()
buffer.position(0);
buffer.limit(0);
}
private void readAsMuchAsPossible() throws IOException, EndOfStreamDuringRead {
final int remainingCapacity = buffer.capacity() - buffer.position();
final int bytesToRead = Math.min(remainingCapacity, unreadByteCount);
buffer.limit(buffer.position() + bytesToRead);
while (buffer.hasRemaining()) {
final int readCount = channel.read(buffer);
if (readCount < 0) {
throw new EndOfStreamDuringRead();
}
assert readCount > 0; // blocking channel guarantee
}
buffer.flip();
unreadByteCount -= bytesToRead;
assert unreadByteCount >= 0;
}
private void bufferAtLeast(final int byteCount) throws IOException, EndOfStreamDuringRead {
if (buffer.remaining() >= byteCount) {
return;
}
if (byteCount > remaining()) {
throw new ProtocolError("Cannot buffer up " + byteCount + " bytes, buffer.remaining()=" + buffer.remaining()
+ " remainingToRead=" + unreadByteCount);
}
buffer.compact();
readAsMuchAsPossible();
// The following should only happen if byteCount > buffer.capacity()
if (buffer.remaining() < byteCount) {
throw new ProtocolError("Failed to buffer up " + byteCount + " bytes, remaining after read is "
+ buffer.remaining());
}
}
public int readInt() throws IOException, EndOfStreamDuringRead {
bufferAtLeast(4);
return buffer.getInt();
}
public long readLong() throws IOException, EndOfStreamDuringRead {
bufferAtLeast(8);
return buffer.getLong();
}
public ByteBuffer readSlice(final int size) throws IOException, EndOfStreamDuringRead {
bufferAtLeast(size);
final int oldLimit = buffer.limit();
final int endOfSlice = buffer.position() + size;
buffer.limit(endOfSlice);
final ByteBuffer slice = buffer.slice();
buffer.limit(oldLimit);
buffer.position(endOfSlice);
return slice;
}
public int remaining() {
return unreadByteCount + buffer.remaining();
}
}