Преобразование n-байтового массива в целое число с помощью ByteBuffer - PullRequest
1 голос
/ 12 июля 2020

Мне нужно преобразовать массивы байтов произвольного размера в short / int / long. Это означает, что я мог бы получить 3 байта для преобразования в int:

final byte[] bytes = { 0b0000, 0b0000, 0b1111 }; // 15 - big endian
final int value = doConversion(bytes);

Таким образом, я пытаюсь придумать общую функцию c.


* Преобразования 1011 *ByteBuffer отлично работают, когда у вас есть массивы размера, который точно представляет значение short, int, long. Но что, если у меня есть int, представленный как один байт?
final byte[] bytes = { 0b1111 }; // 15

Кажется, преобразование такого байтового массива в int с использованием ByteBuffer требует изменения размера массива и его заполнения. С отрицательным значением все становится еще сложнее, так как заполнение должно выполняться наиболее значимым битом .

final byte[] bytes = { (byte) 0b11110001 }; // -15 stored as two's complement

Есть ли более простой способ выполнить sh эту задачу , или я должен просто использовать собственный код? Примером может быть использование функций расширения в Kotlin:

fun ByteArray.toShort(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Short =
    toInteger(Short.SIZE_BYTES, byteOrder, signed).toShort()

fun ByteArray.toInt(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Int =
    toInteger(Int.SIZE_BYTES, byteOrder, signed).toInt()

fun ByteArray.toLong(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Long =
    toInteger(Long.SIZE_BYTES, byteOrder, signed)

private fun ByteArray.toInteger(
     typeBytes: Int /* 2, 4, 8 */,
     byteOrder: ByteOrder /* little, big */, 
     signed: Boolean,
): Long {
    // Checks omitted...

    // If the byte array is bigger than the type bytes, it needs to be truncated
    val bytes =
        if (size > typeBytes) {
            if (byteOrder == LITTLE) {
                copyOf(typeBytes)
            } else {
                copyOfRange(size - typeBytes, size)
            }
        } else {
            copyOf()
        }

    val negative =
        signed && (this[if (byteOrder == LITTLE) bytes.size - 1 else 0]).toInt() and 0b1000000 != 0

    if (!negative) {
        return bytes.absoluteToLong(byteOrder)
    }

    // The number is stored as two's complement.
    // Thus we invert each byte and then sum 1 to obtain the absolute value
    for (i in bytes.indices) {
        bytes[i] = bytes[i].inv()
    }

    return -(bytes.absoluteToLong(byteOrder) + 1)
}

private fun ByteArray.absoluteToLong(byteOrder: ByteOrder): Long {
    var result = 0L
    var shift = 8 * (size - 1)
    val range =
        if (byteOrder == LITTLE) {
            size - 1 downTo 0
        } else {
            0 until size
        }

    for (i in range) {
        result = (this[i].toInt() and 0b11111111).toLong() shl shift or result
        shift -= 8
    }

    return result
}

1 Ответ

1 голос
/ 12 июля 2020

Класс BigInteger имеет конструктор, удобный для этого варианта использования. Пример:

byte[] bytes = { 0b1111 }; 
int value = new BigInteger(bytes).intValue(); 

Он выполняет «знаковое расширение» для вас: если значение первого байта в массиве отрицательное, конечный результат отрицательный.

Он также имеет методы для получения значение, как и другие типы чисел (короткие, длинные, ...).

...