繁体   English   中英

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类,但我不确定这是否适合这项工作,也许是?

我找到了一个可以从流中读取位的第三方库,但是当尝试使用BitConverter将它们转换回UInt64 ,它会失败,因为 54 位对于UInt64来说不够长,但它太长了对于UInt32

我的直接想法是位移位是这样做的方法,但现在我不太确定如何继续,因为我想不出处理原始 16 字节的好方法。

任何建议或意见将不胜感激。

这是一些未经测试的代码。 我确信其中存在错误(每当我编写这样的代码时,我都会得到移位、掩码等错误)。 但是,它应该足以让您入门。 如果你让它工作并且只有几个问题,请在评论中告诉我,我会解决问题。 如果你不能让它工作,也让我知道,我会删除答案。 如果它需要重大重写,请将您的工作代码作为答案发布并告诉我。

另一个需要担心的事情(因为你提到这来自一个文件)是endian-ness 并非所有计算机体系结构都以相同的方式表示值。 我会把任何字节混淆(如果需要)留给你。

首先,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);
        }
    }
}

获取/设置第二和第四件事非常相似。 在这两种情况下,要获得该值,您需要屏蔽除重要位以外的所有位,然后移动结果。 要设置该值,您需要获取属性值,将其移动到正确的位置,将存储在结构中的适当(顶部或底部)值中的位清零,并将新位中的 OR (您通过移位设置)

//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;
     }
 }

这是第三件棘手的事情。 要获得该值,您需要从顶部和底部值中屏蔽正确的位,将它们移到正确的位置并返回 ORed 结果。

要设置该值,您需要获取属性值,将其拆分为上下两部分,然后执行与为第二和第四件事所做的相同类型的魔术 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;
     }
 }

我希望这是有用的。 让我知道。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM