Битовый сдвиг для набора байт

487
27 июня 2017, 21:16

Есть byte[]

Требуется выполнить для него битовый сдвиг влево или вправо с минимальными затратами.

В поиске решений я наткнулся на BitArray. Один из его конструкторов принимает массив байт. Класс позволяет выполнять различные битовые операции (AND NOT OR XOR), но не позволяет делать сдвиги.

Answer 1

Можно было бы попробовать

return ((new System.Numerics.BigInteger(b)) << k).ToByteArray()

хотя потенциальные 3 копирования массива несколько настораживают.

Впрочем, можно заглянуть в его исходники и как-то использовать соответствующий код:

public static BigInteger operator <<(BigInteger value, int shift) {
    if (shift == 0) return value;
    else if (shift == Int32.MinValue) return ((value >> Int32.MaxValue) >> 1);
    else if (shift < 0) return value >> -shift;
    int digitShift = shift / kcbitUint;
    int smallShift = shift - (digitShift * kcbitUint);
    uint[] xd; int xl; bool negx;
    negx = GetPartsForBitManipulation(ref value, out xd, out xl);
    int zl = xl + digitShift + 1;
    uint[] zd = new uint[zl];
    if (smallShift == 0) {
        for (int i = 0; i < xl; i++) {
            zd[i + digitShift] = xd[i];
        }
    } else {
        int carryShift = kcbitUint - smallShift;
        uint carry = 0;
        int i;
        for (i = 0; i < xl; i++) {
            uint rot = xd[i];
            zd[i + digitShift] = rot << smallShift | carry;
            carry = rot >> carryShift;
        }
        zd[i + digitShift] = carry;
    }
    return new BigInteger(zd, negx);
}
Answer 2
#region сдвиг на 1 бит
byte[] myBytes;
BitArray gamma_value = new BitArray(myBytes);
int len = gamma_value.Length; //длина массива бит
c = gamma_value[0]; //младший бит
//создание массива типа bool 
//такой же длины как gamma_value типа BitArray
bool[] gamma_new = new bool[len]; 
//копируем массив типа BitArray в массив типа bool
gamma_value.CopyTo(gamma_new, 0); 
//копируем массив bool со сдвигом на 1ну позицию в другой массив bool
Array.Copy(gamma_new, 1, gamma_new, 0, len - 1);
//преобразование массива типа bool в массив типа BitArray
gamma_value = new BitArray(gamma_new);
gamma_value.CopyTo(myBytes, 0); //переводим массив бит в массив байт
#endregion
Answer 3

Сдвиг придётся реализовывать алгоритимчески: требуется на основе величины сдвига в цикле определить в какой байт попадут данные из текущего байта. Данные байта делятся на две части: смещённая и смещаемая часть.

Задание решить несложно, но это требует времени. Советую разбить задачу на несколько частей, например: превратить любое отрицательное смещение в положительное; сделать переход по всем байтам в цикле; разбить байт на две части - та что выпала после смещения и вторая; в локальные переменные записать значения новых индексов и смещения в их байтах для данных текущего байта; записать в другой массив такой же длинны по смещённым индексам

Answer 4
/// <summary>
/// Rotates the bits in an array of bytes to the left.
/// </summary>
/// <param name="bytes">The byte array to rotate.</param>
public static void RotateLeft(byte[] bytes)
{
    bool carryFlag = ShiftLeft(bytes);
    if (carryFlag == true)
    {
        bytes[bytes.Length - 1] = (byte)(bytes[bytes.Length - 1] | 0x01);
    }
}
/// <summary>
/// Rotates the bits in an array of bytes to the right.
/// </summary>
/// <param name="bytes">The byte array to rotate.</param>
public static void RotateRight(byte[] bytes)
{
    bool carryFlag = ShiftRight(bytes);
    if (carryFlag == true)
    {
        bytes[0] = (byte)(bytes[0] | 0x80);
    }
}
/// <summary>
/// Shifts the bits in an array of bytes to the left.
/// </summary>
/// <param name="bytes">The byte array to shift.</param>
public static bool ShiftLeft(byte[] bytes)
{
    bool leftMostCarryFlag = false;
    // Iterate through the elements of the array from left to right.
    for (int index = 0; index < bytes.Length; index++)
    {
        // If the leftmost bit of the current byte is 1 then we have a carry.
        bool carryFlag = (bytes[index] & 0x80) > 0;
        if (index > 0)
        {
            if (carryFlag == true)
            {
                // Apply the carry to the rightmost bit of the current bytes neighbor to the left.
                bytes[index - 1] = (byte)(bytes[index - 1] | 0x01);
            }
        }
        else
        {
            leftMostCarryFlag = carryFlag;
        }
        bytes[index] = (byte)(bytes[index] << 1);
    }
    return leftMostCarryFlag;
}
/// <summary>
/// Shifts the bits in an array of bytes to the right.
/// </summary>
/// <param name="bytes">The byte array to shift.</param>
public static bool ShiftRight(byte[] bytes) 
{
    bool rightMostCarryFlag = false;
    int rightEnd = bytes.Length - 1;
    // Iterate through the elements of the array right to left.
    for (int index = rightEnd; index >= 0; index--)
    {
        // If the rightmost bit of the current byte is 1 then we have a carry.
        bool carryFlag = (bytes[index] & 0x01) > 0;
        if (index < rightEnd)
        {
            if (carryFlag == true)
            {
                // Apply the carry to the leftmost bit of the current bytes neighbor to the right.
                bytes[index + 1] = (byte)(bytes[index + 1] | 0x80);
            }
        }
        else
        {
            rightMostCarryFlag = carryFlag;
        }
        bytes[index] = (byte)(bytes[index] >> 1);
    }
    return rightMostCarryFlag;
} 
Answer 5

Идея состоит в ручном копировании битов. То есть если делаем сдвиг влево на 4 бита, то копируем в 0-й бит 4-й бит, в 1-й - 5-й и т.д. Соответственно, в, например, 15-й бит (2-й байт) копируется 19-й бит (3-й байт).

Для сдвига вправо - аналогично.

private static readonly byte[] bits = new byte[] { 0x80, 0x40, 0x20, 0x10, 8, 4, 2, 1 };
public static void ShiftLeft(byte[] array, int shift)
{
    var totalLength = array.Length * 8;
    for (var toIndex = 0; toIndex < totalLength; toIndex++)
    {
        var fromIndex = toIndex + shift;
        SetBit(fromIndex, toIndex, array);
    }
}
public static void ShiftRight(byte[] array, int shift)
{
    var totalLength = array.Length * 8;
    for (var toIndex = totalLength - 1; toIndex >= 0; toIndex--)
    {
        var fromIndex = toIndex - shift;
        SetBit(fromIndex, toIndex, array);
    }
}
private static void SetBit(int fromIndex, int toIndex, byte[] array)
{
    var toBit = bits[toIndex % 8];
    int toByte = array[toIndex / 8];
    toByte &= ~toBit;
    if (fromIndex >= 0 && fromIndex < array.Length * 8)
    {
        var fromByte = array[fromIndex / 8];
        var fromBit = bits[fromIndex % 8];
        if ((fromByte & fromBit) != 0)
        {
            toByte += toBit;
        }
    }
    array[toIndex / 8] = (byte)toByte;
}
READ ALSO
Произведение матриц [требует правки]

Произведение матриц [требует правки]

Не могу решить умножение матриц через форму, то есть надо задать размер двух матриц так же через форму, умножить и вывести на экран

189
Использование KeepAlive опции для потоковых TCP сокетов

Использование KeepAlive опции для потоковых TCP сокетов

Для установки KeepAlive опции я использую метод socketIOControl:

233
Не завершается процесс C#

Не завершается процесс C#

Имеется код, который должен завершать процесс, который был запущен из указанной папки:

236
Некорректно парсится дата

Некорректно парсится дата

У меня проблема с парсингом даты из строки в Datetime формат, когда я использую, напр01

264