/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the PickaPack library.
* <p>
* PickaPack is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* PickaPack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with PickaPack. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.util.buffer;
import java.nio.ByteBuffer;
/**
* Circular byte buffer.
*
* @author Min Cai
*/
public class CircularByteBuffer {
private ByteBuffer buffer;
private int readIndex = 0;
private int writeIndex = 0;
/**
* Create a circular byte buffer.
*
* @param capacity the capacity
*/
public CircularByteBuffer(int capacity) {
buffer = ByteBuffer.allocate(capacity);
}
/**
* Reset the circular byte buffer.
*/
public void reset() {
readIndex = 0;
writeIndex = 0;
}
/**
* Read data from the circular byte buffer into the specified destination byte array.
*
* @param dest the destination byte array
* @param offset the offset
* @param length the length
* @return the number of data read from the circular byte buffer
*/
public int read(byte[] dest, int offset, int length) {
assert length < buffer.capacity() : "The requested read is bigger than the buffer";
if (writeIndex == readIndex) {
return 0;
}
buffer.position(readIndex);
if (writeIndex < readIndex) {
int remainder = buffer.remaining();
if (remainder < length) {
buffer.get(dest, offset, remainder);
offset += remainder;
length -= remainder;
readIndex = 0;
buffer.position(readIndex);
int space = writeIndex - readIndex;
if (space <= length) {
length = space;
}
buffer.get(dest, offset, length);
readIndex += length;
return remainder + length;
} else {
buffer.get(dest, offset, remainder);
readIndex += remainder;
return remainder;
}
} else {
int space = writeIndex - readIndex;
if (space <= length) {
length = space;
}
buffer.get(dest, offset, length);
readIndex += length;
return length;
}
}
/**
* Write data from the specified byte array into the specified circular byte buffer.
*
* @param src the source byte array
* @param offset the offset
* @param length the length of the data written
* @return a boolean value indicating whether the specified number of bytes has been written successfully or not
*/
public boolean write(byte[] src, int offset, int length) {
assert length < buffer.capacity() : "The requested write is bigger than the buffer";
buffer.position(writeIndex);
if ((readIndex <= writeIndex && writeIndex + length < buffer.capacity()) ||
(writeIndex < readIndex && length < readIndex - writeIndex)) {
// source fits in the remainder of the buffer
buffer.put(src, offset, length);
writeIndex += length;
return true;
} else {
// the source don't fit in the buffer without wrapping
int remainder = buffer.remaining();
if (readIndex < writeIndex && length > readIndex + remainder) {
return false;
}
if (writeIndex < readIndex && length > readIndex - writeIndex) {
return false;
}
buffer.put(src, offset, remainder);
offset += remainder;
length -= remainder;
writeIndex = 0;
buffer.position(writeIndex);
assert length < readIndex : "There is not enough room for this write operation";
buffer.put(src, offset, length);
writeIndex += length;
return true;
}
}
/**
* Get a boolean value indicating whether the circular byte buffer is empty or not.
*
* @return a boolean value indicating whether the circular byte buffer is empty or not
*/
public boolean isEmpty() {
return writeIndex == readIndex;
}
/**
* Get a boolean value indicating whether the circular byte buffer is full or not
*
* @return a boolean value indicating whether the circular byte buffer is full or not
*/
public boolean isFull() {
return writeIndex + 1 <= buffer.capacity() && writeIndex + 1 == readIndex || writeIndex == buffer.capacity() - 1 && readIndex == 0;
}
/**
* Get the supporting byte buffer.
*
* @return the supporting byte buffer
*/
public ByteBuffer getBuffer() {
return buffer;
}
/**
* Get the read index.
*
* @return the read index
*/
public int getReadIndex() {
return readIndex;
}
/**
* Get the write index.
*
* @return the write index
*/
public int getWriteIndex() {
return writeIndex;
}
}