/**
* Circular chained byte array
*
* @author bbwang8088@126.com
*/
public class CircularChainedBuffer {
enum ACTION {
WAIT, READ, WRITE
}
private ACTION lastAction = ACTION.WAIT;
// current write position
private int wPos = 0;
// current read position
private int rPos = 0;
// byte array
private byte[] buffer = null;
// byte array length
private int length = 0;
public CircularChainedBuffer(int size) {
this.length = size;
init();
}
/**
* initialize method
*/
private void init() {
this.buffer = new byte[this.length];
this.wPos = 0;
this.rPos = 0;
this.lastAction = ACTION.WAIT;
}
/**
* reset method
*/
public void reset() {
init();
}
/**
* Get current writable data size.
*
* @return
*/
public int getWritableSize() {
int ret = 0;
// if initialize, return length of buffer array.
if (this.wPos == 0 && this.rPos == 0) {
return this.length;
}
ret = this.wPos - this.rPos;
// normal case.
if (ret > 0) {
ret = this.length - ret;
}
// same position of read and write.
else if (ret == 0) {
// write to same position,then cann't write any more.
if (this.lastAction == ACTION.WRITE) {
ret = 0;
}
// read to same position,then cann't read any more, array is empty.
else if (this.lastAction == ACTION.READ) {
ret = this.length;
}
}
// if read position before write position, then calculate against.
else {
ret = this.rPos - this.wPos;
}
return ret;
}
/**
* Get current readable data size.
*
* @return
*/
public int getReadableSize() {
return this.length - this.getWritableSize();
}
/**
* Try to write some data, if cann't write all, then write a part, if buffer
* is filled, then return 0.
*
* @param data
* @return return size of wrote data
*/
public synchronized int tryWrite(byte[] data) {
int retValue = 0;
int tmpWritableSize = this.getWritableSize();
// if current writable size is 0, then return 0.
if (tmpWritableSize == 0) {
return 0;
}
int copyLength = 0;
if (data.length > tmpWritableSize) {
copyLength = tmpWritableSize;
} else {
copyLength = data.length;
}
// write position after read position.
if (this.wPos >= this.rPos) {
// all data can write from current write position to end of array,
// no need to write data back to head of buffer array.
if ((this.wPos + copyLength) <= this.length) {
// copy all data of parameter array to buffer array.
System.arraycopy(data, 0, this.buffer, this.wPos, copyLength);
// move write position.
this.wPos += copyLength;
}
// copy part of parameter array full fill buffer array from write
// position,
// then copy left part of parameter array from head of buffer array.
else {
int partA = (this.length - this.wPos);
int partB = copyLength - partA;
// copy part of parameter array full fill buffer array from
// write position
System.arraycopy(data, 0, this.buffer, this.wPos, partA);
// copy left part of parameter array from head of buffer array
System.arraycopy(data, partA, this.buffer, 0, partB);
// move write position
this.wPos = partB;
}
}
// read position after write position
else {
// can only write data between write position and read position
System.arraycopy(data, 0, this.buffer, this.wPos, copyLength);
// move write position
this.wPos += copyLength;
}
retValue = copyLength;
this.lastAction = ACTION.WRITE;
return retValue;
}
/**
* Try to read size of data,return real read data, if no data can read then
* return null.
*
* @param size
* @return return byte array of read data
*/
public synchronized byte[] tryRead(int size) {
byte[] ret = null;
int tmpReadableSize = this.getReadableSize();
if (tmpReadableSize == 0 || size <= 0) {
return new byte[0];
}
int realReadLength = 0;
if (size > tmpReadableSize) {
realReadLength = tmpReadableSize;
} else {
realReadLength = size;
}
ret = new byte[realReadLength];
// write position after read position.
if (this.rPos < this.wPos) {
System.arraycopy(this.buffer, this.rPos, ret, 0, realReadLength);
this.rPos += realReadLength;
}
// read position after write position
else {
if( (this.rPos +realReadLength ) <= this.length){
System.arraycopy(this.buffer, this.rPos, ret, 0, realReadLength);
this.rPos += realReadLength;
}else{
int partA = (this.length - this.rPos);
int partB = realReadLength - partA;
//Log.d(this.getClass().getSimpleName(),"size:"+size+" realReadLength"+realReadLength+" this.length:"+this.length+" this.rPos:"+this.rPos+" this.wPos:"+this.wPos+" partA:"+partA+" partB:"+partB);
// copy part of parameter array full fill buffer array from write
// position
System.arraycopy(this.buffer, this.rPos, ret, 0, partA);
// copy left part of parameter array from head of buffer array
System.arraycopy(this.buffer, 0, ret, partA, partB);
// move write position
this.rPos = partB;
}
}
this.lastAction = ACTION.READ;
return ret;
}
/**
* Attation: this method will not move read position. Try to read size of
* data,return real read data, if no data can read then return null.
*
* @param size
* @return return byte array of read data
*/
public synchronized byte[] pretendRead(int size) {
byte[] ret = null;
int tmpReadableSize = this.getReadableSize();
if (tmpReadableSize == 0) {
return new byte[0];
}
int realReadLength = 0;
if (size > tmpReadableSize) {
realReadLength = tmpReadableSize;
} else {
realReadLength = size;
}
ret = new byte[realReadLength];
// write position after read position.
if (this.rPos < this.wPos) {
System.arraycopy(this.buffer, this.rPos, ret, 0, realReadLength);
// this.rPos += realReadLength;
}
// read position after write position
else {
int partA = (this.length - this.rPos);
int partB = realReadLength - partA;
// copy part of parameter array full fill buffer array from write
// position
System.arraycopy(this.buffer, this.rPos, ret, 0, partA);
// copy left part of parameter array from head of buffer array
System.arraycopy(this.buffer, 0, ret, partA, partB);
// move write position
// this.rPos = partB;
}
// this.lastAction = ACTION.READ;
return ret;
}
public synchronized byte[] pretendReadAll() {
return this.pretendRead(this.getReadableSize());
}
/**
* Attation: this method will not move read position. Try to read size of
* data,return real read data, if no data can read then return null.
*
* @param size
* @return return byte array of read data
*/
public synchronized byte[] pretendRead(int offset, int size) {
byte[] data = new byte[size];
System.arraycopy(pretendReadAll(), offset, data, 0, size);
return data;
}
public synchronized byte[] readAll() {
return this.tryRead(this.getReadableSize());
}
public String toString() {
StringBuffer sb = new StringBuffer();
for (byte b : this.buffer) {
sb.append(" " + Integer.toHexString(b & 0xFF));
}
return sb.toString();
}
}
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Tester for Circular chained byte array
*
* @author bbwang8088@126.com
*/
public class TestCycleByteArray {
@Test
public void TestGetWritableSize() {
int size = 4096;
CircularChainedBuffer buffer = new CircularChainedBuffer(size);
assertEquals(buffer.getWritableSize(), size);
byte[] data1_size10 = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a };
byte[] data2_size10 = { (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa };
// byte[] data3_size10 = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
// (byte) 0xff };
buffer.tryWrite(data1_size10);
assertEquals(size - data1_size10.length, buffer.getWritableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
// System.out.println(buffer.toString());
buffer.tryWrite(data2_size10);
assertEquals(size - data2_size10.length * 2, buffer.getWritableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
// System.out.println(buffer.toString());
byte[] data = buffer.tryRead(data2_size10.length * 2);
// System.out.println(new String(data));
assertEquals(size, buffer.getWritableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
// System.out.println(buffer.toString());
data = buffer.tryRead(data2_size10.length * 2);
assertEquals(0, data.length);
// System.out.println(new String(data));
// System.out.println(buffer.toString());
for (int i = 0; i < (size / data1_size10.length); i++) {
assertEquals(data1_size10.length, buffer.tryWrite(data1_size10));// 1
// System.out.println(buffer.toString());
}
assertEquals(size % data1_size10.length, buffer.getWritableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
data = buffer.tryRead(size*2);
assertEquals(size-size%data1_size10.length, data.length);
assertEquals(size, buffer.getWritableSize());
assertEquals(0, buffer.getReadableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
assertEquals(data1_size10.length, buffer.tryWrite(data1_size10));
assertEquals(data2_size10.length, buffer.tryWrite(data2_size10));
assertEquals(size - data1_size10.length * 2, buffer.getWritableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
data = buffer.tryRead(data1_size10.length*2);
assertEquals(data1_size10.length*2, data.length);
assertEquals(size, buffer.getWritableSize());
assertEquals(0, buffer.getReadableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
for (int i = 0; i < (size / data1_size10.length); i++) {
assertEquals(data1_size10.length, buffer.tryWrite(data1_size10));// 1
}
assertEquals(size % data1_size10.length, buffer.getWritableSize());
assertEquals(size-size % data1_size10.length, buffer.getReadableSize());
assertEquals(size, buffer.getWritableSize() + buffer.getReadableSize());
assertEquals(size % data1_size10.length, buffer.tryWrite(data1_size10));// 1
data = buffer.readAll();
assertEquals(size, buffer.getWritableSize());
assertEquals(0, buffer.getReadableSize());
}
}