我正在开发一个必须符合规范的工具,该规范将大量数据跨字节边界打包成位。示例:2字节编码2个字段,10位值,6位容忍度。其他字段可能跨2-4字节,并被分解成更多的字段。
我认为,另一种选择是在发送/接收数据之前/之后创建通用位打包/解压缩函数,并使用标准类型字节、短、int、long等标准类型处理C#中的所有数据,而不是与C#进行斗争并尝试使用Bitfield(如C++)获取结构。
我对C#并不熟悉,所以我不确定如何最好地解决这个问题。据我所读,不鼓励使用unsafe和指针,但我使用泛型类型的尝试不幸失败了:
private static bool GetBitsFromByte<T,U>(T input, byte count, out U output, byte start = 0) where T:struct where U:struct
{
if (input == default(T))
return false;
if( (start + count) > Marshal.SizeOf(input))
return false;
if(count > Marshal.SizeOf(output))
return false;
// I'd like to setup the correct output container based on the
// number of bits that are needed
if(count <= 8)
output = new byte();
else if (count <= 16)
output = new UInt16();
else if (count <= 32)
output = new UInt32();
else if (count <= 64)
output = new UInt64();
else
return false;
output = 0; // Init output
// Copy bits out in order
for (int i = start; i < count; i++)
{
output |= (input & (1 << i)); // This is not possible with generic types from my understanding
}
return true;
}我会用这样的方法调用这个方法,从data_in中提取10位(从LSB)到data_out,然后从data_in中提取6位到next_data_out。
Uint32 data_in = 0xdeadbeef;
Uint16 data_out;
byte next_data_out;
if(GetBitsFromByte<Uint32,Uint16>(data_in, 10, out data_out, 0))
{
// data_out should now = 0x2EF
if(GetBitsFromByte<Uint32,byte>(data_in, 6, out next_data_out, data_out.Length))
{
// next_data_out should now = 0x2F
}
}我不想为所有可能的byte、ushort、uint、ulong组合编写函数,尽管我认为这是另一种选择。
我已经看过了BitConverter类,但这是用于字节数组,而不是操作位。我也明白,不能做类似于:where T : INumeric或where T : System.ValueType之类的事情,所以我愿意接受建议。
谢谢!
发布于 2013-11-04 21:26:07
正如您所知道的,您不能执行where T : INumeric,所以无论您编写什么,都可能需要一些变体来支持不同的数字类型。
我可能会在必要时使用BitArray和write方法来转换/从其他数据类型转换。然后,您最多需要一种方法,用于每种数字类型,而不是每种类型组合的方法。(在8-ish整数类型中有C#,所以最坏的情况是8+8=16,而不是8*8=64)
如果您不喜欢手动复制/粘贴以及在某些事情发生变化时进行更新的想法,您可能可以使用T4模板来生成8 ish整数类型的方法。
uint data_in = 0xdeadbeef;
ushort data_out;
byte next_data_out;
// pay attention to BitConverter.IsLittleEndian here!
// you might need to write your own conversion methods,
// or do a Reverse() or find a better library
var bits = new BitArray(BitConverter.GetBytes(data_in));
if (bits.TryConvertToUInt16(out data_out, 10))
{
Console.WriteLine(data_out.ToString("X")); // 2EF
if (bits.TryConvertToByte(out next_data_out, 6, 10))
{
Console.WriteLine(next_data_out.ToString("X")); // 2F
}
}
private static bool Validate(BitArray bits, int len, int start, int size)
{
return len < size * 8 && bits.Count > start + len;
}
public static bool TryConvertToUInt16(this BitArray bits, out ushort output, int len, int start = 0)
{
output = 0;
if (!Validate(bits, len, start, sizeof(ushort)))
return false;
for (int i = start; i < len + start; i++)
{
output |= (ushort)(bits[i] ? 1 << (i - start) : 0);
}
return true;
}
public static bool TryConvertToByte(this BitArray bits, out byte output, int len, int start = 0)
{
output = 0;
if (!Validate(bits, len, start, sizeof(byte)))
return false;
for (int i = start; i < len + start; i++)
{
output |= (byte)(bits[i] ? 1 << (i - start) : 0);
}
return true;
}发布于 2013-11-04 21:34:13
这里发生了几件事:
U类型
//不要这样做如果(计数<= 8)输出=新字节();如果(.) //etc /完成此输出=新U();因此,您可以不使用具有硬编码输出(使U成为硬编码类型)的版本,但在我看来,尝试使用out泛型似乎几乎是不可能的。
编辑:想想看,我也不确定你是否能够在一个通用结构上执行按位操作。
发布于 2013-11-05 16:03:59
如果您想让它在任意随机结构上工作,它有点像序列化问题。有关这方面的一些信息,请参阅这篇文章:
下面是上面的概念,并对其进行了一些修改,使其具有通用性:
class GenericSerializer <T>
{
public BitArray ToBitArray(T input, int start, int len)
{
int structSize = Marshal.SizeOf(input);
BitArray ret = new BitArray(len);
int byteStart = start / 8;
int byteEnd = (start + len) / 8 + 1;
byte[] buffer = new byte[byteEnd - byteStart];
IntPtr ptr = Marshal.AllocHGlobal(structSize);
Marshal.StructureToPtr(input, ptr, false);
Marshal.Copy(ptr, buffer, byteStart, buffer.Length);
Marshal.FreeHGlobal(ptr);
int destBit = 0;
int sourceBit = start % 8;
int sourceEnd = sourceBit + len;
while (sourceBit < sourceEnd)
{
ret[destBit] = 0 != (buffer[sourceBit / 8]
& (1 << (sourceBit % 8)));
++sourceBit;
++destBit;
}
return ret;
}
public T FromBytes(byte[] arr)
{
IntPtr ptr = Marshal.AllocHGlobal(arr.Length);
Marshal.Copy(arr, 0, ptr, arr.Length);
T output = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return output;
}
}注意:我只对读做了BitArray,在写时使用了byte []。(这里的一个缺点是,每个操作都要完全复制结构两次,这样就不会很好地执行)
使用BitConverter或从几个已知类型(例如Int32、Int16、Int64等)转到/来自的一组函数可能会运行得更快。
https://stackoverflow.com/questions/19777116
复制相似问题