C# 从字节数组中提取位范围
C# extract bit ranges from byte array
我需要从一个 16 字节的值中提取一些位范围,例如:
bit 0 = first thing
next 54 bits = second thing
next 52 bits = third thing
last 21 bits = fourth thing
.net 没有 UInt128
结构,它有 BigInteger
class,但我不确定这是否适合这份工作,也许是?
我找到了可以从流中读取位的第三方库,但是当尝试使用 BitConverter
将它们转换回 UInt64
时,它会失败,因为 54 位对于 UInt64
来说不够长,但对于 UInt32
来说太长了
我的直接想法是位移是执行此操作的方法,但现在我不太确定如何继续,因为我想不出处理原始 16 字节的好方法。
如有任何建议或意见,我们将不胜感激。
这是一些未经测试的代码。我敢肯定它里面有错误(每当我写这样的代码时,我都会弄错班次,掩码等)。但是,它应该足以让您入门。如果你让它工作并且只有几个问题,请在评论中告诉我,我会解决问题。如果你不能让它工作,也让我知道,我会删除答案。如果它需要重大重写,post 你的工作代码作为答案并告诉我。
另一个需要担心的事情(因为你提到它来自文件)是 endian-ness。并非所有的计算机体系结构都以相同的方式表示值。我会把任何字节 swizzling(如果需要的话)留给你。
首先,C++ 中的结构与类 基本相同(尽管人们认为它们不同)。在 C# 中,它们非常不同。 C# 中的结构是 值类型 。当你进行值类型赋值时,编译器会复制结构的值,而不是仅仅复制对象的引用(就像 类 那样)。值类型有一个隐式默认构造函数,它将所有成员初始化为其默认(零或空)值。
用 [StructLayout(LayoutKind.Sequential)]
标记结构告诉编译器以指定的顺序排列成员(编译器通常不需要这样做)。如果需要,这允许您将对其中之一的引用(通过 P/Invoke)传递给 C 程序。
所以,我的结构是这样开始的:
[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 & 0x01) == 1;
set
{
//set or clear the 0 bit
if (value)
{
_bottom64bits |= 1ul;
}
else
{
_bottom64bits &= (~1ul);
}
}
}
Getting/setting第二点和第四点很相似。在这两种情况下,为了获得值,您需要屏蔽除重要位以外的所有位,然后移动结果。要设置该值,您需要 属性 值,将其移动到正确的位置,将存储在结构中的适当(顶部或底部)值中的位清零,并在新位(您设置的通过移动)
//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 & SecondThingMask) >> 1;
set
{
var shifted = (value << 1) & SecondThingMask;
_bottom64bits = (_bottom64bits & (~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 & FourthThingMask) >> FourthThingShift);
set
{
var shifted = ((ulong)value << FourthThingShift) & FourthThingMask;
_top64bits = (_top64bits & (~FourthThingMask)) | shifted;
}
}
棘手的是第三件事。要获得该值,您需要从最高值和最低值中屏蔽正确的位,将它们移动到正确的位置,然后 return 或运算结果。
要设置该值,您需要获取 属性 值,将其分为上部和下部,然后执行与第二项和第四项相同的魔术 ORing:
//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 & ThirdThingBottomMask) >> ThirdThingBottomShift;
var top = (_top64bits & ThirdThingTopMask) << 9;
return top | bottom;
}
set
{
var masked = value & ThirdThingOverallMask;
var bottom = (masked & ThirdThingBottomSetMask) << ThirdThingBottomShift;
_bottom64bits = (_bottom64bits & (~ThirdThingBottomSetMask)) | bottom;
var top = (masked & ThirdThingTopSetMask) >> 9;
_top64bits = (_top64bits & (~ThirdThingTopSetMask)) | top;
}
}
我希望这是有用的。让我知道。
我需要从一个 16 字节的值中提取一些位范围,例如:
bit 0 = first thing
next 54 bits = second thing
next 52 bits = third thing
last 21 bits = fourth thing
.net 没有 UInt128
结构,它有 BigInteger
class,但我不确定这是否适合这份工作,也许是?
我找到了可以从流中读取位的第三方库,但是当尝试使用 BitConverter
将它们转换回 UInt64
时,它会失败,因为 54 位对于 UInt64
来说不够长,但对于 UInt32
我的直接想法是位移是执行此操作的方法,但现在我不太确定如何继续,因为我想不出处理原始 16 字节的好方法。
如有任何建议或意见,我们将不胜感激。
这是一些未经测试的代码。我敢肯定它里面有错误(每当我写这样的代码时,我都会弄错班次,掩码等)。但是,它应该足以让您入门。如果你让它工作并且只有几个问题,请在评论中告诉我,我会解决问题。如果你不能让它工作,也让我知道,我会删除答案。如果它需要重大重写,post 你的工作代码作为答案并告诉我。
另一个需要担心的事情(因为你提到它来自文件)是 endian-ness。并非所有的计算机体系结构都以相同的方式表示值。我会把任何字节 swizzling(如果需要的话)留给你。
首先,C++ 中的结构与类 基本相同(尽管人们认为它们不同)。在 C# 中,它们非常不同。 C# 中的结构是 值类型 。当你进行值类型赋值时,编译器会复制结构的值,而不是仅仅复制对象的引用(就像 类 那样)。值类型有一个隐式默认构造函数,它将所有成员初始化为其默认(零或空)值。
用 [StructLayout(LayoutKind.Sequential)]
标记结构告诉编译器以指定的顺序排列成员(编译器通常不需要这样做)。如果需要,这允许您将对其中之一的引用(通过 P/Invoke)传递给 C 程序。
所以,我的结构是这样开始的:
[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 & 0x01) == 1;
set
{
//set or clear the 0 bit
if (value)
{
_bottom64bits |= 1ul;
}
else
{
_bottom64bits &= (~1ul);
}
}
}
Getting/setting第二点和第四点很相似。在这两种情况下,为了获得值,您需要屏蔽除重要位以外的所有位,然后移动结果。要设置该值,您需要 属性 值,将其移动到正确的位置,将存储在结构中的适当(顶部或底部)值中的位清零,并在新位(您设置的通过移动)
//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 & SecondThingMask) >> 1;
set
{
var shifted = (value << 1) & SecondThingMask;
_bottom64bits = (_bottom64bits & (~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 & FourthThingMask) >> FourthThingShift);
set
{
var shifted = ((ulong)value << FourthThingShift) & FourthThingMask;
_top64bits = (_top64bits & (~FourthThingMask)) | shifted;
}
}
棘手的是第三件事。要获得该值,您需要从最高值和最低值中屏蔽正确的位,将它们移动到正确的位置,然后 return 或运算结果。
要设置该值,您需要获取 属性 值,将其分为上部和下部,然后执行与第二项和第四项相同的魔术 ORing:
//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 & ThirdThingBottomMask) >> ThirdThingBottomShift;
var top = (_top64bits & ThirdThingTopMask) << 9;
return top | bottom;
}
set
{
var masked = value & ThirdThingOverallMask;
var bottom = (masked & ThirdThingBottomSetMask) << ThirdThingBottomShift;
_bottom64bits = (_bottom64bits & (~ThirdThingBottomSetMask)) | bottom;
var top = (masked & ThirdThingTopSetMask) >> 9;
_top64bits = (_top64bits & (~ThirdThingTopSetMask)) | top;
}
}
我希望这是有用的。让我知道。