#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. Извините за задержку, время действительно летит! Еще раз спасибо