C # извлекает диапазоны битов из массива байтов

#c# #.net #bit-shift

#c# #.net #битовый сдвиг

Вопрос:

Мне нужно извлечь некоторые диапазоны битов из 16-байтового значения, например:

 bit 0 = first thing
next 54 bits = second thing
next 52 bits = third thing
last 21 bits = fourth thing
  

у .net нет UInt128 структуры, ну, у нее есть BigInteger класс, но я не уверен, что это подходит для работы, может быть, это так?

Я нашел стороннюю библиотеку, которая может считывать биты из потока, но при попытке преобразовать их обратно в UInt64 с помощью BitConverter , это не удастся, так как 54 бита недостаточно для UInt64 , но это слишком долго для UInt32

Моей первой мыслью было, что для этого нужно было сделать сдвиг битов, но теперь я не уверен, как поступить, поскольку я не могу придумать хороший способ обработки исходных 16 байтов.

Любые предложения или комментарии будут оценены.

Комментарии:

1. Для этого нужно создать структуру нужного размера методом перебора, а затем использовать маски (с amp; оператором) и сдвиги (с >> и << операторами) для доступа к свойствам. Откуда вы берете это 16-битное значение?

2. 16 байт берутся из файла. Да, это нормально, для начала нужно просто перевести данные в состояние, которое можно изменять.

3. Используйте [StructLayout(LayoutKind.Sequential)] в структуре с двумя UInt64 s. Затем создайте свойства для объектов 1 .. 4 (и напишите код для маскирования / сдвига в получателях / установщиках свойств). Первый, второй и четвертый просты. Для третьего вам нужно будет извлечь значение из двух длин и / или объединить их вместе соответствующим образом (установщики обратят этот процесс вспять). Это будет утомительно писать, но довольно эффективно, как только вы соберете все это вместе.

4. Спасибо за информацию. Я никогда не делал ничего подобного, что вы предложили, я использую C # только шесть месяцев или около того. Если у вас есть время, не могли бы вы создать небольшой объем кода, просто чтобы проиллюстрировать свою точку зрения? Только если у вас есть время, конечно, спасибо.

5. Кстати, выполнение этого с массивом байтов (а не со структурой, состоящей из байтов или двух длин) заставит вас рвать на себе волосы. Структуры являются «изменяемыми», массивов не так много

Ответ №1:

Вот несколько непроверенных кодов. Я уверен, что в нем есть ошибки (всякий раз, когда я пишу подобный код, я получаю неправильные сдвиги, маски и т.д.). Однако этого должно быть достаточно, чтобы вы начали. Если у вас все получится и возникнет всего несколько проблем, дайте мне знать в комментариях, и я все исправлю. Если вы не можете заставить его работать, дайте мне знать, и я удалю ответ. Если это требует серьезной перезаписи, опубликуйте свой рабочий код в качестве ответа и дайте мне знать.

Другая вещь, о которой следует беспокоиться при этом (поскольку вы упомянули, что это происходит из файла), — это порядковый номер. Не все компьютерные архитектуры представляют значения одинаковым образом. Я оставлю вам любой байт, разбирающий (при необходимости).

Во-первых, структуры в C в основном такие же, как классы (хотя люди думают, что они разные). В C # они сильно отличаются. Структура в C # — это тип значения. Когда вы выполняете присвоение типа значения, компилятор создает копию значения структуры, а не просто копирует ссылку на объект (как это делается с классами). Типы значений имеют неявный конструктор по умолчанию, который инициализирует все элементы их значениями по умолчанию (ноль или null).

Пометка структуры с помощью [StructLayout(LayoutKind.Sequential)] указывает компилятору расположить элементы в указанном порядке (обычно компилятор не обязан этого делать). Это позволяет вам передать ссылку на один из них (через P / Invoke) в программу на языке Си, если вы хотите.

Итак, моя структура начинается таким образом:

 [StructLayout(LayoutKind.Sequential)]
public struct Struct128
{
    //not using auto-properties with private setters on purpose.
    //This should look like a single 128-bit value (in part, because of LayoutKind.Sequential)
    private ulong _bottom64bits;
    private ulong _top64bits;
}
  

Теперь я собираюсь добавить элементы в эту структуру. Поскольку вы получаете 128 бит из файла, не пытайтесь считывать данные в единую 128-битную структуру (если вы можете выяснить, как (посмотрите сериализацию), вы можете, но …). Вместо этого считывайте 64 бита за раз и используйте конструктор, подобный этому:

  public Struct128(ulong bottom64, ulong top64)
 {
     _top64bits = top64;
     _bottom64bits = bottom64;
 }
  

Если вам нужно записать данные из одного из них обратно в файл, используйте 64-разрядные данные за раз, используя свойства, доступные только для чтения, подобные этому:

 //read access to the raw storage
public ulong Top64 => _top64bits;
public ulong Bottom64 => _bottom64bits;
  

Теперь нам нужно получить и установить различные битовые значения из нашей структуры. Получить (и настроить) первое, что просто:

 public bool FirstThing
{
    get => (_bottom64bits amp; 0x01) == 1;
    set
    {
        //set or clear the 0 bit
        if (value)
        {
            _bottom64bits |= 1ul;
        }
        else
        {
            _bottom64bits amp;= (~1ul);
        }
    }
}
  

Получение / настройка второй и четвертой вещей очень похожи. В обоих случаях, чтобы получить значение, вы маскируете все, кроме важных битов, а затем сдвигаете результат. Чтобы установить значение, вы берете значение свойства, сдвигаете его в нужное место, обнуляете биты в соответствующем (верхнем или нижнем) значении, хранящемся в структуре, и / или в новых битах (которые вы настраиваете путем сдвига)

 //bits 1 through 55
private const ulong SecondThingMask = 0b111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1110;

public ulong SecondThing
{
    get => (_bottom64bits amp; SecondThingMask) >> 1;
    set
    {
        var shifted = (value << 1) amp; SecondThingMask;
        _bottom64bits = (_bottom64bits amp; (~SecondThingMask)) | shifted;
    }
}
  

и

  //top 21 bits
 private const ulong FourthThingMask = 0b1111_1111_1111_1111_1111_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
 //to shift the top 21 bits down to the bottom 21 bits, need to shift 64-21
 private const int FourthThingShift = 64 - 21;

 public uint FourthThing
 {
     get => (uint)((_top64bits amp; FourthThingMask) >> FourthThingShift);
     set
     {
         var shifted = ((ulong)value << FourthThingShift) amp; FourthThingMask;
         _top64bits = (_top64bits amp; (~FourthThingMask)) | shifted;
     }
 }
  

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

Чтобы установить значение, вам нужно взять значение свойства, разделить его на верхнюю и нижнюю части, а затем выполнить то же волшебное действие, которое было выполнено для второго и четвертого элементов:

  //the third thing is the hard part.  
 //The bottom 55 bits of the _bottom64bits are dedicate to the 1st and 2nd things, so the next 9 are the bottom 9 of the 3rd thing
 //The other 52-9 (=43) bits come-from/go-to the _top64bits

 //top 9 bits
 private const ulong ThirdThingBottomMask = 0b1111_1111_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
 //bottom 43 bits
 private const ulong ThirdThingTopMask = 0b111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111;
 private const int ThirdThingBottomShift = 64 - 9;

 //bottom 9 bits
 private const ulong ThirdThingBottomSetMask = 0b1_1111_1111;
 //all but the bottom 9 bits
 private const ulong ThirdThingTopSetMask = 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1110_0000_0000;
 //52 bits total
 private const ulong ThirdThingOverallMask = 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111;

 public ulong ThirdThing
 {
     get
     {
         var bottom = (_bottom64bits amp; ThirdThingBottomMask) >> ThirdThingBottomShift;
         var top = (_top64bits amp; ThirdThingTopMask) << 9;
         return top | bottom;
     }
     set
     {
         var masked = value amp; ThirdThingOverallMask;
         var bottom = (masked amp; ThirdThingBottomSetMask) << ThirdThingBottomShift;
         _bottom64bits = (_bottom64bits amp; (~ThirdThingBottomSetMask)) | bottom;
         var top = (masked amp; ThirdThingTopSetMask) >> 9;
         _top64bits = (_top64bits amp; (~ThirdThingTopSetMask)) | top;
     }
 }
  

Я надеюсь, что это полезно. Дайте мне знать.

Комментарии:

1. Большое спасибо за код, я посмотрю, как только смогу, и отчитаюсь. Ценю усилия!

2. @Tony: Ты сказал «Спасибо» два с половиной года назад. Было ли это полезно? Если бы это было так, почему за это не проголосовали или не приняли? Кто-то, наконец, проголосовал за это на этой неделе. Все еще жду обратной связи от OP. Как я уже упоминал, если вы обнаружите какие-либо проблемы, дайте мне знать, и я исправлю ответ

3. Извините за задержку, время действительно летит! Еще раз спасибо