io.datakernel.bytebuf.ByteBufQueue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datakernel-bytebuf Show documentation
Show all versions of datakernel-bytebuf Show documentation
Fast and memory-efficient byte buffer, an optimized version of Java's ByteBuffer class.
Useful for fast low-level I/O operations like working with files or transferring data over the internet.
/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed 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 io.datakernel.bytebuf;
import static java.lang.System.arraycopy;
public final class ByteBufQueue {
private static final int DEFAULT_CAPACITY = 8;
private ByteBuf[] bufs;
private int first = 0;
private int last = 0;
// region builders
private ByteBufQueue(int capacity) {
this.bufs = new ByteBuf[capacity];
}
public static ByteBufQueue create() {return new ByteBufQueue(DEFAULT_CAPACITY);}
public ByteBufQueue withCapacity(int capacity) {
return new ByteBufQueue(capacity);
}
// endregion
private int next(int i) {
return ++i >= bufs.length ? 0 : i;
}
private void doPoll() {
bufs[first].recycle();
first = next(first);
}
private void grow() {
ByteBuf[] newBufs = new ByteBuf[bufs.length * 2];
arraycopy(bufs, last, newBufs, 0, bufs.length - last);
arraycopy(bufs, 0, newBufs, bufs.length - last, last);
first = 0;
last = bufs.length;
bufs = newBufs;
}
public void add(ByteBuf buf) {
if (!buf.canRead()) {
buf.recycle();
return;
}
bufs[last] = buf;
last = next(last);
if (last == first) {
grow();
}
}
public void addAll(Iterable byteBufs) {
for (ByteBuf buf : byteBufs) {
add(buf);
}
}
public ByteBuf take() {
assert hasRemaining();
ByteBuf buf = bufs[first];
first = next(first);
return buf;
}
/**
* Creates and returns ByteBufSlice that contains {@code maxSize} bytes from queue's first ByteBuf
* if latter contains enough bytes. Otherwise creates and returns ByteBuf that contains all bytes
* from first ByteBuf in queue.
*
* @param maxSize number of bytes to returning
* @return ByteBuf with result bytes
*/
public ByteBuf takeMaxSize(int maxSize) {
assert hasRemaining();
ByteBuf buf = bufs[first];
if (maxSize >= buf.readRemaining()) {
first = next(first);
return buf;
}
ByteBuf result = buf.slice(maxSize);
buf.moveReadPosition(maxSize);
return result;
}
/**
* Returns ByteBuf that contains {@code exactSize} of bytes if queue has enough bytes.
* Otherwise returns ByteBuf that contains all bytes from queue
*
* @param exactSize amount of bytes to return
* @return ByteBuf with {@code exactSize} or less bytes
*/
public ByteBuf takeExactSize(int exactSize) {
if (!hasRemaining())
return ByteBuf.empty();
ByteBuf buf = bufs[first];
if (buf.readRemaining() == exactSize) {
first = next(first);
return buf;
} else if (exactSize < buf.readRemaining()) {
ByteBuf result = buf.slice(exactSize);
buf.moveReadPosition(exactSize);
return result;
}
ByteBuf result = ByteBufPool.allocate(exactSize);
drainTo(result, exactSize);
return result;
}
/**
* Creates and returns ByteBuf with all remaining bytes from queue
*
* @return ByteBuf with all remaining bytes
*/
public ByteBuf takeRemaining() {
return takeExactSize(remainingBytes());
}
/**
* Returns the first ByteBuf from this queue
*/
public ByteBuf peekBuf() {
return hasRemaining() ? bufs[first] : null;
}
/**
* Returns the ByteBuf with the given index, relatively than head of queue
*
* @param n index of ByteBuf relatively than head of queue
*/
public ByteBuf peekBuf(int n) {
assert n <= remainingBufs();
int i = first + n;
if (i >= bufs.length)
i -= bufs.length;
return bufs[i];
}
/**
* Returns the number of ByteBufs in this queue
*/
public int remainingBufs() {
return last >= first ? last - first : bufs.length + (last - first);
}
/**
* Returns the number of bytes in this queue
*/
public int remainingBytes() {
int result = 0;
for (int i = first; i != last; i = next(i)) {
result += bufs[i].readRemaining();
}
return result;
}
/**
* Tells whether or not this queue is empty.
*
* @return true if, and only if, there is at least one element is remaining in this queue
*/
public boolean isEmpty() {
return !hasRemaining();
}
public boolean hasRemaining() {
return first != last;
}
/**
* Tells whether or not this queue has remaining bytes.
*
* @param remaining number of bytes for checking
* @return true if, and only if, there are remaining bytes.
*/
public boolean hasRemainingBytes(int remaining) {
for (int i = first; i != last; i = next(i)) {
int bufRemaining = bufs[i].readRemaining();
if (bufRemaining >= remaining)
return true;
remaining -= bufRemaining;
}
return false;
}
/**
* Returns the first byte from this queue and removes it.
*/
public byte getByte() {
assert hasRemaining();
ByteBuf buf = bufs[first];
assert buf.canRead();
byte result = buf.get();
if (!buf.canRead()) {
doPoll();
}
return result;
}
/**
* Returns the first byte from this queue without its removing.
*/
public byte peekByte() {
assert hasRemaining();
ByteBuf buf = bufs[first];
return buf.peek();
}
/**
* Returns the byte from this queue with the given index
*
* @param index the index at which the bytes will be returned
*/
public byte peekByte(int index) {
assert hasRemainingBytes(index + 1);
for (int i = first; ; i = next(i)) {
ByteBuf buf = bufs[i];
if (index < buf.readRemaining())
return buf.peek(index);
index -= buf.readRemaining();
}
}
/**
* Removes {@code maxSize} bytes from this queue
*
* @param maxSize number of bytes for removing
* @return number of removed bytes
*/
public int skip(int maxSize) {
int s = maxSize;
while (hasRemaining()) {
ByteBuf buf = bufs[first];
int remaining = buf.readRemaining();
if (s < remaining) {
buf.moveReadPosition(s);
return maxSize;
} else {
buf.readPosition(buf.writePosition());
doPoll();
s -= remaining;
}
}
return maxSize - s;
}
/**
* Adds {@code maxSize} bytes from this queue to dest if queue contains more than {@code maxSize} bytes.
* Otherwise adds all bytes from queue to dest. In both cases advances queue's position to number of drained bytes.
*
* @param dest array to draining
* @param destOffset start position for adding to dest
* @param maxSize number of bytes for adding
* @return number of drained bytes.
*/
public int drainTo(byte[] dest, int destOffset, int maxSize) {
int s = maxSize;
while (hasRemaining()) {
ByteBuf buf = bufs[first];
int remaining = buf.readRemaining();
if (s < remaining) {
arraycopy(buf.array(), buf.readPosition(), dest, destOffset, s);
buf.moveReadPosition(s);
return maxSize;
} else {
arraycopy(buf.array(), buf.readPosition(), dest, destOffset, remaining);
buf.readPosition(buf.writePosition());
doPoll();
s -= remaining;
destOffset += remaining;
}
}
return maxSize - s;
}
/**
* Adds {@code maxSize} bytes from this queue to ByteBuf dest if queue contains more than {@code maxSize} bytes.
* Otherwise adds all bytes from queue to dest. In both cases advances queue's position to number of drained bytes.
*
* @param dest ByteBuf for draining
* @param maxSize number of bytes for adding
* @return number of drained bytes.
*/
public int drainTo(ByteBuf dest, int maxSize) {
int actualSize = drainTo(dest.array(), dest.writePosition(), maxSize);
dest.moveWritePosition(actualSize);
return actualSize;
}
/**
* Adds as much bytes to dest as it can store. If queue doesn't contain enough bytes - adds all byte from queue.
* Advances queue's position to number of drained bytes.
*
* @param dest ByteBuf for draining
* @return number of drained bytes
*/
public int drainTo(ByteBuf dest) {
return drainTo(dest, dest.writeRemaining());
}
/**
* Copies all bytes from this queue to dest, and removes it from this queue.
*
* @param dest ByteBufQueue for draining
* @return number of adding bytes
*/
public int drainTo(ByteBufQueue dest) {
int size = 0;
while (hasRemaining()) {
ByteBuf buf = take();
dest.add(buf);
size += buf.readRemaining();
}
return size;
}
/**
* Adds to ByteBufQueue dest {@code maxSize} bytes from this queue. If this queue doesn't contain enough bytes,
* add all bytes from this queue.
*
* @param dest ByteBufQueue for draining
* @param maxSize number of bytes for adding
* @return number of added elements
*/
public int drainTo(ByteBufQueue dest, int maxSize) {
int s = maxSize;
while (s != 0 && hasRemaining()) {
ByteBuf buf = takeMaxSize(s);
dest.add(buf);
s -= buf.readRemaining();
}
return maxSize - s;
}
/**
* Recycles all ByteBufs from this queue.
*/
public void clear() {
for (int i = first; i != last; i = next(i)) {
bufs[i].recycle();
}
first = last = 0;
}
@Override
public String toString() {
return "bufs:" + remainingBufs() + " bytes:" + remainingBytes();
}
}