Я пытаюсь создать служебный класс, чтобы сделать побитовые манипуляции и преобразования более читабельными.В настоящее время я застрял в создании метода для извлечения битов из байтового массива и формирования из них нового байта [].Излишне говорить, что я не очень хорошо разбираюсь в побитовых операциях.
Я полагаю, что этого можно достичь с помощью BitSet, но было бы слишком много преобразований, и реализация была бы специфичной для Java.Было бы неплохо иметь понятный алгоритм, который позже можно было бы легко перенести на другие языки.
До сих пор я дошел до этого:
public static byte[] toBytes(int offset /*full bytes*/, int bitsOffset /*bytes + bits*/, int bitsCount, byte... bytes) {
int bytesCount = bitsCount / 8;
int paddingBits = bitsCount % 8;
int partialBits = 8 - paddingBits;
if (paddingBits > 0) {
bytesCount++;
}
byte[] data = new byte[bytesCount];
return data;
}
Я прокомментировал вышеи временно заменил его на
public static byte[] toBytes(int offset, int bitsOffset, int bitsCount, byte... bytes) {
int firstBitIndex = (offset * 8) + bitsOffset;
return new BigInteger(new BigInteger(1, bytes).toString(2).substring(firstBitIndex, firstBitIndex + bitsCount), 2).toByteArray();
}
Однако я все же хотел бы иметь правильную реализацию с как можно меньшими накладными расходами и не специфичную для Java (не используются специфичные для Java инструменты, такие как BitSet)
Это подсказка, что я ожидал бы сделать
/**
* [0000 0110 1111 0010] = toBytes(1, 4, 12, [xxxx xxxx xxxx 0110 1111 0010 xxxx xxxx])
* [0000 0110 1111 0010] = toBytes(1, 5, 12, [xxxx xxxx xxxx x011 0111 1001 0xxx xxxx])
* [0000 0110 1111 0010] = toBytes(1, 6, 12, [xxxx xxxx xxxx xx01 1011 1100 10xx xxxx])
*/
А вот несколько юнит-тестов
public class ByteUtilTest {
@Test
public void toBytes_sameByte() {
byte[] result = ByteUtil.toBytes(1, 4, 3,
toByte("11111111"),
toByte("11110111"),
toByte("11111111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000011")), toBinaryString(result));
}
@Test
public void toBytes_sameByte_full() {
byte[] result = ByteUtil.toBytes(1, 0, 8,
toByte("11111111"),
toByte("01110101"),
toByte("11111111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("01110101")), toBinaryString(result));
}
@Test
public void toBytes_sameByte_noneWithoutOffset() {
byte[] result = ByteUtil.toBytes(1, 0, 0,
toByte("11111111"),
toByte("01110101"),
toByte("11111111"),
toByte("11111111"));
assertEquals(0, result.length);
}
@Test
public void toBytes_sameByte_noneWithOffset() {
byte[] result = ByteUtil.toBytes(1, 3, 0,
toByte("11111111"),
toByte("01110101"),
toByte("11111111"),
toByte("11111111"));
assertEquals(0, result.length);
}
@Test
public void toBytes_twoBytes_resultWithTwoBytes() {
byte[] result = ByteUtil.toBytes(1, 2, 11,
toByte("11111111"),
toByte("01110101"),
toByte("10011111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("10110011")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_resultWithOneByte() {
byte[] result = ByteUtil.toBytes(1, 2, 7,
toByte("11111111"),
toByte("01110101"),
toByte("10011111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("01101011")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_firstFull() {
byte[] result = ByteUtil.toBytes(1, 0, 11,
toByte("11111111"),
toByte("01110101"),
toByte("10011111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000011"), toByte("10101100")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_lastFull() {
byte[] result = ByteUtil.toBytes(1, 5, 11,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000101"), toByte("10011101")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_bothFull() {
byte[] result = ByteUtil.toBytes(1, 0, 16,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("01110101"), toByte("10011101")), toBinaryString(result));
}
@Test
public void toBytes_threeBytes() {
byte[] result = ByteUtil.toBytes(1, 2, 19,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("00000110"),
toByte("10110011"),
toByte("10110111")),
toBinaryString(result));
}
@Test
public void toBytes_threeBytes_firstFull() {
byte[] result = ByteUtil.toBytes(1, 0, 19,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("00000011"),
toByte("10101100"),
toByte("11101101")),
toBinaryString(result));
}
@Test
public void toBytes_threeBytes_lastFull() {
byte[] result = ByteUtil.toBytes(1, 2, 22,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("00110101"),
toByte("10011101"),
toByte("10111111")),
toBinaryString(result));
}
@Test
public void toBytes_threeBytes_allFull() {
byte[] result = ByteUtil.toBytes(1, 0, 24,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("01110101"),
toByte("10011101"),
toByte("10111111")),
toBinaryString(result));
}
@Test
public void toBytes_bitsOffset_4() {
byte[] result = ByteUtil.toBytes(1, 4, 12,
toByte("11111111"),
toByte("11110110"),
toByte("11110010"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("11110010")), toBinaryString(result));
}
@Test
public void toBytes_bitsOffset_5() {
byte[] result = ByteUtil.toBytes(1, 5, 12,
toByte("11111111"),
toByte("11111011"),
toByte("01111001"),
toByte("01111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("11110010")), toBinaryString(result));
}
@Test
public void toBytes_bitsOffset_6() {
byte[] result = ByteUtil.toBytes(1, 6, 12,
toByte("11111111"),
toByte("11111101"),
toByte("10111100"),
toByte("10111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("11110010")), toBinaryString(result));
}
private String toBinaryString(byte... data) {
StringBuilder binaryStr = new StringBuilder();
String value = Integer.toBinaryString(data[0]);
if (value.length() > 8) value = value.substring(value.length() - 8);
else if (value.length() < 8) value = String.format("%8s", value).replace(" ", "0");
binaryStr.append(value);
for (int i = 1; i < data.length; i++) {
value = Integer.toBinaryString(data[i]);
if (value.length() > 8) value = value.substring(value.length() - 8);
else if (value.length() < 8) value = String.format("%8s", value).replace(" ", "0");
binaryStr.append(" ").append(value);
}
return binaryStr.toString();
}
private String toString(byte[] data) {
return Arrays.toString(data);
}
private byte toByte(String binary) {
return (byte) Integer.parseInt(binary, 2);
}
}