package org.yamcs.parameterarchive;
import java.nio.ByteBuffer;
/**
* BitBuffer allows to write individual bits or group of bits into a buffer backed by an long[] array.
*
* All the writings/readings are performed to/from a temporary field which is stored/retrieve from the backing array when it is "full"
*
* @author nm
*
*/
public class BitBuffer {
private int bitShift; //bit offset from the right inside the current int
private long[] a;
//we put all the bits in the b, when it is full we save it in the array a and increase the offset
private long b; //current element
private int offset; //the offset of the current element in the array
/**
* Constructs a buffer of size 8*n backed by an long[n] array. You need to allocate one bit more than required to store the data
*
* @param n
*/
BitBuffer(int n) {
a = new long[n];
offset = 0;
bitShift = 64;
b = 0;
}
public BitBuffer(byte[] b) {
int n = b.length/8;
a = new long[n];
ByteBuffer bb= ByteBuffer.wrap(b);
for(int i=0;i<n;i++) {
a[i] = bb.getLong();
}
}
/**
* write the least significant numBits of x into the BitBuffer
*
* Note that there is no check that the bits will actually fit into the allocated buffer, they will be stored in the temporary field.
* A buffer overflow exception will happen when the temporary field is full and flushed to the array (so the buffer is exceeded by 64 bits)
*
* @param x
* @param numBits
*/
public void write(int x, int numBits) {
int k = numBits-bitShift;
if(k<0) {
doWrite(x, numBits);
} else {
doWrite(x>>k, bitShift);
bitShift = 64;
a[offset] = b;
b = 0;
offset++;
doWrite(x, k);
}
}
//here we know that numBits<bitShift
private void doWrite(long x, int numBits) {
bitShift-=numBits;
long mask = (1L<<numBits) -1;
b |= ((x&mask) << bitShift);
}
public long readLong(int numBits) {
int k = numBits-bitShift;
if(k<0) {
return doRead(numBits);
} else {
long x= doRead(bitShift)<<k;
bitShift = 64;
offset++;
b = a[offset];
return x|doRead(k);
}
}
public int read(int numBits) {
return (int)readLong(numBits);
}
private long doRead(int numBits) {
bitShift-=numBits;
long mask = (1L<<numBits) -1;
return (b>>bitShift)&mask;
}
/**
* flush the temporary field to the array and return the backing array
* @return the backing array
*/
public long[] getArray() {
if(offset<a.length) {
a[offset] = b;
}
return a;
}
/**
* get the size of the backing array containing data
* @return the size of the backing array
*
* */
public int getSize() {
int r = offset;
if(bitShift<64) r++;
return r;
}
public void rewind() {
if(offset<a.length) {
a[offset] = b;
}
offset = 0;
bitShift = 64;
b = a[0];
}
/**
* flush the temporary field to the backing array and return a byte[] copy of the backing long array
* @return a byte[] copy of the backing long array
*/
public byte[] toByteArray() {
byte[] b= new byte[offset*8+8];
ByteBuffer bb = ByteBuffer.wrap(b);
for(int i=0; i<offset+1; i++) {
bb.putLong(a[i]);
}
return b;
}
}