diff --git a/utils/src/main/java/mc/utils/array/AbstractBitBufferArray.java b/utils/src/main/java/mc/utils/array/AbstractBitBufferArray.java new file mode 100644 index 0000000..85900fc --- /dev/null +++ b/utils/src/main/java/mc/utils/array/AbstractBitBufferArray.java @@ -0,0 +1,51 @@ +package mc.utils.array; + +import java.nio.ByteBuffer; + +public abstract class AbstractBitBufferArray implements BitArray { + + protected int bitPerEntity; + protected int arraySize; + protected long maxEntityValue; + protected ByteBuffer byteBuffer; + + protected int writePosition = 0; + + protected AbstractBitBufferArray(int bitPerEntity, int arraySize, boolean direct) { + this.bitPerEntity = bitPerEntity; + this.arraySize = arraySize; + this.maxEntityValue = (1L << bitPerEntity) - 1L; + + int capaticy = calculateCapacity(); + if (direct) { + this.byteBuffer = ByteBuffer.allocateDirect(capaticy); + } else { + this.byteBuffer = ByteBuffer.allocate(capaticy); + } + } + + @Override + public void put(int value) { + put(writePosition, value); + writePosition++; + } + + @Override + public ByteBuffer byteBuffer() { + return this.byteBuffer; + } + + protected void validateIndex(int index) { + if (index < 0 || index > arraySize - 1) { + throw new IllegalArgumentException("Index " + index + " out of range [0;" + arraySize + "]"); + } + } + + protected void validateValue(int value) { + if ((value | maxEntityValue) > maxEntityValue) { + throw new IllegalArgumentException("Invalide value " + value + ". Value bits: " + Integer.bitCount(value) + ", maximum bits: " + bitPerEntity); + } + } + + protected abstract int calculateCapacity(); +} diff --git a/utils/src/main/java/mc/utils/array/BitArray.java b/utils/src/main/java/mc/utils/array/BitArray.java new file mode 100644 index 0000000..d784f7d --- /dev/null +++ b/utils/src/main/java/mc/utils/array/BitArray.java @@ -0,0 +1,13 @@ +package mc.utils.array; + +import java.nio.ByteBuffer; + +public interface BitArray { + + void put(int value); + void put(int index, int value); + + int get(int index); + + ByteBuffer byteBuffer(); +} diff --git a/utils/src/main/java/mc/utils/array/BitByteArray.java b/utils/src/main/java/mc/utils/array/BitByteArray.java new file mode 100644 index 0000000..29b4987 --- /dev/null +++ b/utils/src/main/java/mc/utils/array/BitByteArray.java @@ -0,0 +1,62 @@ +package mc.utils.array; + +public class BitByteArray extends AbstractBitBufferArray { + + public BitByteArray(int bitPerEntity, int arraySize, boolean direct) { + super(bitPerEntity, arraySize, direct); + } + + public BitByteArray(int bitPerEntity, int arraySize) { + this(bitPerEntity, arraySize, true); + } + + @Override + public void put(int index, int value) { + validateIndex(index); + validateValue(value); + + //@formatter:off + int headValueIndex = ((index + 1) * bitPerEntity - 1) / Byte.SIZE; + int var1 = index * bitPerEntity; + int tailValueIndex = var1 / Byte.SIZE; + int offsetValue = var1 % Byte.SIZE; + //@formatter:on + + byteBuffer.put(tailValueIndex, + (byte) (byteBuffer.get(tailValueIndex) & ~(maxEntityValue << offsetValue) + | ((byte) value & maxEntityValue) << offsetValue)); + + if (headValueIndex != tailValueIndex) { + int shift1 = Byte.SIZE - offsetValue; + int shift2 = bitPerEntity - shift1; + + byteBuffer.put(headValueIndex, + (byte) (byteBuffer.get(headValueIndex) >>> shift2 << shift2 + | ((byte) value & maxEntityValue) >> shift1)); + } + } + + @Override + public int get(int index) { + validateIndex(index); + + //@formatter:off + int headValueIndex = ((index + 1) * bitPerEntity - 1) / Byte.SIZE; + int var1 = index * bitPerEntity; + int tailValueIndex = var1 / Byte.SIZE; + int offsetValue = var1 % Byte.SIZE; + //@formatter:on + + if (headValueIndex == tailValueIndex) { + return (int) (byteBuffer.get(tailValueIndex) >>> offsetValue & maxEntityValue); + } else { + return (int) ((byteBuffer.get(tailValueIndex) >>> offsetValue + | byteBuffer.get(headValueIndex) << (Byte.SIZE - offsetValue)) & maxEntityValue); + } + } + + @Override + protected int calculateCapacity() { + return (arraySize * bitPerEntity / Byte.SIZE + 1) * Byte.BYTES; + } +} diff --git a/utils/src/main/java/mc/utils/array/BitLongArray.java b/utils/src/main/java/mc/utils/array/BitLongArray.java new file mode 100644 index 0000000..ac75ca8 --- /dev/null +++ b/utils/src/main/java/mc/utils/array/BitLongArray.java @@ -0,0 +1,67 @@ +package mc.utils.array; + +import java.nio.LongBuffer; + +public class BitLongArray extends AbstractBitBufferArray { + + private final LongBuffer longBuffer; + + public BitLongArray(int bitPerEntity, int arraySize, boolean direct) { + super(bitPerEntity, arraySize, direct); + this.longBuffer = this.byteBuffer.asLongBuffer(); + } + + public BitLongArray(int bitPerEntity, int arraySize) { + this(bitPerEntity, arraySize, true); + } + + @Override + public void put(int index, int value) { + validateIndex(index); + validateValue(value); + + //@formatter:off + int headValueIndex = ((index + 1) * bitPerEntity - 1) / Long.SIZE; + int var1 = index * bitPerEntity; + int tailValueIndex = var1 / Long.SIZE; + int offsetValue = var1 % Long.SIZE; + //@formatter:on + + longBuffer.put(tailValueIndex, + longBuffer.get(tailValueIndex) & ~(maxEntityValue << offsetValue) + | ((long) value & maxEntityValue) << offsetValue); + + if (headValueIndex != tailValueIndex) { + int shift1 = Long.SIZE - offsetValue; + int shift2 = bitPerEntity - shift1; + + longBuffer.put(headValueIndex, + longBuffer.get(headValueIndex) >>> shift2 << shift2 + | ((long) value & maxEntityValue) >> shift1); + } + } + + @Override + public int get(int index) { + validateIndex(index); + + //@formatter:off + int headValueIndex = ((index + 1) * bitPerEntity - 1) / Long.SIZE; + int var1 = index * bitPerEntity; + int tailValueIndex = var1 / Long.SIZE; + int offsetValue = var1 % Long.SIZE; + //@formatter:on + + if (headValueIndex == tailValueIndex) { + return (int) (longBuffer.get(tailValueIndex) >>> offsetValue & maxEntityValue); + } else { + return (int) ((longBuffer.get(tailValueIndex) >>> offsetValue + | longBuffer.get(headValueIndex) << (Long.SIZE - offsetValue)) & maxEntityValue); + } + } + + @Override + protected int calculateCapacity() { + return (arraySize * bitPerEntity / Long.SIZE + 1) * Long.BYTES; + } +} diff --git a/utils/src/test/java/mc/utils/array/BitByteArrayTest.java b/utils/src/test/java/mc/utils/array/BitByteArrayTest.java new file mode 100644 index 0000000..2474d81 --- /dev/null +++ b/utils/src/test/java/mc/utils/array/BitByteArrayTest.java @@ -0,0 +1,80 @@ +package mc.utils.array; + +import org.junit.jupiter.api.Test; + +import java.nio.ByteBuffer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BitByteArrayTest { + + @Test + void initTest() { + BitArray bitArray = new BitByteArray(4, 1, false); + ByteBuffer byteBuffer = bitArray.byteBuffer(); + + assertEquals(1, byteBuffer.capacity()); + assertEquals(1, byteBuffer.limit()); + assertEquals(0, byteBuffer.position()); + assertEquals(1, byteBuffer.array().length); + + bitArray = new BitByteArray(4, 2); + byteBuffer = bitArray.byteBuffer(); + + assertEquals(2, byteBuffer.capacity()); + assertEquals(2, byteBuffer.limit()); + } + + @Test + void writeTest1() { + int value = 0b00001001; + int expected = 0b10011001; + + BitArray bitArray = new BitByteArray(4, 2); + bitArray.put(value); + bitArray.put(value); + + ByteBuffer byteBuffer = bitArray.byteBuffer(); + assertEquals(expected, byteBuffer.get(0) & 0xFF); //Unsigned Byte + } + + @Test + void writeTest2() { + int value1 = 0b00001001; + int value2 = 0b00001111; + int expected1 = 0b10011001; + int expected2 = 0b11111111; + + BitArray bitArray = new BitByteArray(4, 4); + bitArray.put(value1); + bitArray.put(value1); + bitArray.put(value2); + bitArray.put(value2); + + ByteBuffer byteBuffer = bitArray.byteBuffer(); + assertEquals(expected1, byteBuffer.get(0) & 0xFF); //Unsigned Byte + assertEquals(expected2, byteBuffer.get(1) & 0xFF); //Unsigned Byte + } + + @Test + void writeOffsetTest() { + int value = 0b00001001; + int expected = 0b10010000; + + BitArray bitArray = new BitByteArray(4, 2); + bitArray.put(1, value); + + ByteBuffer byteBuffer = bitArray.byteBuffer(); + assertEquals(expected, byteBuffer.get(0) & 0xFF); //Unsigned Byte + } + + @Test + void readTest() { + int excepted = 0b00000110; + + BitArray bitArray = new BitByteArray(4, 2); + bitArray.put(1, excepted); + + assertEquals(excepted, bitArray.get(1)); + } +} \ No newline at end of file diff --git a/utils/src/test/java/mc/utils/array/BitLongArrayTest.java b/utils/src/test/java/mc/utils/array/BitLongArrayTest.java new file mode 100644 index 0000000..e1a3aea --- /dev/null +++ b/utils/src/test/java/mc/utils/array/BitLongArrayTest.java @@ -0,0 +1,81 @@ +package mc.utils.array; + +import org.junit.jupiter.api.Test; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import static org.junit.jupiter.api.Assertions.*; + +class BitLongArrayTest { + + @Test + void initTest() { + BitArray bitArray = new BitLongArray(13, 1, false); + ByteBuffer byteBuffer = bitArray.byteBuffer(); + + assertEquals(8, byteBuffer.capacity()); + assertEquals(8, byteBuffer.limit()); + assertEquals(0, byteBuffer.position()); + assertEquals(8, byteBuffer.array().length); + + bitArray = new BitLongArray(13, 5); + byteBuffer = bitArray.byteBuffer(); + + assertEquals(16, byteBuffer.capacity()); + assertEquals(16, byteBuffer.limit()); + } + + @Test + void writeTest1() { + int value = 0b00010000_00000011; + long expected = 0b00000000_00000000_00000000_00000000_00000010_00000000_01110000_00000011L; + + BitArray bitArray = new BitLongArray(13, 2); + bitArray.put(value); + bitArray.put(value); + + LongBuffer longBuffer = bitArray.byteBuffer().asLongBuffer(); + assertEquals(expected, longBuffer.get(0)); + } + + @Test + void writeTest2() { + int value = 0b00010000_00000011; + long expected1 = 0b00000000_00111000_00000001_11000000_00001110_00000000_01110000_00000011L; + long expected2 = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001L; + + BitArray bitArray = new BitLongArray(13, 5); + bitArray.put(value); + bitArray.put(value); + bitArray.put(value); + bitArray.put(value); + bitArray.put(value); + + LongBuffer longBuffer = bitArray.byteBuffer().asLongBuffer(); + assertEquals(expected1, longBuffer.get(0)); + assertEquals(expected2, longBuffer.get(1)); + } + + @Test + void writeOffsetTest() { + int value = 0b00010000_00000011; + long expected = 0b00000000_00000000_00000000_00000000_00000010_00000000_01100000_00000000; + + BitArray bitArray = new BitLongArray(13, 2); + bitArray.put(1, value); + + LongBuffer longBuffer = bitArray.byteBuffer().asLongBuffer(); + assertEquals(expected, longBuffer.get(0)); + } + + @Test + void readTest() { + int excepted = 0b00010000_00000011; + + BitArray bitArray = new BitLongArray(13, 2); + bitArray.put(1, excepted); + + assertEquals(excepted, bitArray.get(1)); + } +} \ No newline at end of file