Tengo una estructura que necesito llenar y escribir en el disco (varias en realidad).
Un ejemplo es:
byte-6
bit0 - original_or_copy
bit1 - copyright
bit2 - data_alignment_indicator
bit3 - PES_priority
bit4-bit5 - PES_scrambling control.
bit6-bit7 - reserved
En CI podría hacer algo como lo siguiente:
struct PESHeader {
unsigned reserved:2;
unsigned scrambling_control:2;
unsigned priority:1;
unsigned data_alignment_indicator:1;
unsigned copyright:1;
unsigned original_or_copy:1;
};
¿Hay alguna forma de hacer esto en C # que me permita acceder a los bits usando el operador de punto de desreferencia de estructura?
Para un par de estructuras, puedo hacer cambios de bits envueltos en una función de acceso.
Tengo muchas estructuras que manejar de esta manera, así que estoy buscando algo que sea más fácil de leer y más rápido de escribir.
fuente
GetFields
método no devuelve campos en un orden particular, como orden alfabético o de declaración. Su código no debe depender del orden en el que se devuelvan los campos, porque ese orden varía". ¿No causa esto un problema aquí?IBitfield
interfaz de 'marcador' (que no tiene miembros), puede convertir laPrimitiveConversion
clase en métodos de extensión para cualquier estructura que implementeIBitfield
. Por ejemplo:public static long ToLong(this IBitfield obj) {}
. Luego, elToLong()
método aparecerá en Intellisense paraIBitfield
objetos.GetFields
puede sufrir un reordenamiento debido a la memoria caché de reflexión, pero ordenar por 'MetadataToken' debería superar ese problema (que puede reproducir mezclando get single field y obtener todos los campos en algún orden).Al usar una enumeración, puede hacer esto, pero se verá incómodo.
[Flags] public enum PESHeaderFlags { IsCopy = 1, // implied that if not present, then it is an original IsCopyrighted = 2, IsDataAligned = 4, Priority = 8, ScramblingControlType1 = 0, ScramblingControlType2 = 16, ScramblingControlType3 = 32, ScramblingControlType4 = 16+32, ScramblingControlFlags = ScramblingControlType1 | ScramblingControlType2 | ... ype4 etc. }
fuente
Quieres StructLayoutAttribute
[StructLayout(LayoutKind.Explicit, Size=1, CharSet=CharSet.Ansi)] public struct Foo { [FieldOffset(0)]public byte original_or_copy; [FieldOffset(0)]public byte copyright; [FieldOffset(0)]public byte data_alignment_indicator; [FieldOffset(0)]public byte PES_priority; [FieldOffset(0)]public byte PES_scrambling_control; [FieldOffset(0)]public byte reserved; }
Esto es realmente una unión, pero puede usarlo como un campo de bits; solo debe ser consciente de dónde se supone que están los bits de cada campo en el byte. Las funciones de utilidad y / o constantes a AND contra pueden ayudar.
const byte _original_or_copy = 1; const byte _copyright = 2; //bool ooo = foo.original_or_copy(); static bool original_or_copy(this Foo foo) { return (foo.original_or_copy & _original_or_copy) == original_or_copy; }
También existe LayoutKind.Sequential que le permitirá hacerlo de la manera C.
fuente
Como sugirió Christophe Lambrechts, BitVector32 proporciona una solución. El rendimiento nervioso debería ser adecuado, pero no estoy seguro. Aquí está el código que ilustra esta solución:
public struct rcSpan { //C# Spec 10.4.5.1: The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. internal static readonly BitVector32.Section sminSection = BitVector32.CreateSection(0x1FFF); internal static readonly BitVector32.Section smaxSection = BitVector32.CreateSection(0x1FFF, sminSection); internal static readonly BitVector32.Section areaSection = BitVector32.CreateSection(0x3F, smaxSection); internal BitVector32 data; //public uint smin : 13; public uint smin { get { return (uint)data[sminSection]; } set { data[sminSection] = (int)value; } } //public uint smax : 13; public uint smax { get { return (uint)data[smaxSection]; } set { data[smaxSection] = (int)value; } } //public uint area : 6; public uint area { get { return (uint)data[areaSection]; } set { data[areaSection] = (int)value; } } }
Puedes hacer mucho de esta manera. Puede hacerlo aún mejor sin usar BitVector32, proporcionando accesos hechos a mano para cada campo:
public struct rcSpan2 { internal uint data; //public uint smin : 13; public uint smin { get { return data & 0x1FFF; } set { data = (data & ~0x1FFFu ) | (value & 0x1FFF); } } //public uint smax : 13; public uint smax { get { return (data >> 13) & 0x1FFF; } set { data = (data & ~(0x1FFFu << 13)) | (value & 0x1FFF) << 13; } } //public uint area : 6; public uint area { get { return (data >> 26) & 0x3F; } set { data = (data & ~(0x3F << 26)) | (value & 0x3F) << 26; } } }
Sorprendentemente, esta última solución hecha a mano parece ser la más conveniente, la menos complicada y la más corta. Eso es, por supuesto, solo mi preferencia personal.
fuente
Uno más basado en la respuesta de Zbyl. Este es un poco más fácil de cambiar para mí: solo tengo que ajustar sz0, sz1 ... y también asegurarme de que mask # y loc # sean correctos en los bloques Set / Get.
En cuanto al rendimiento, debería ser el mismo que ambos resolvieron en 38 declaraciones de MSIL. (las constantes se resuelven en tiempo de compilación)
public struct MyStruct { internal uint raw; const int sz0 = 4, loc0 = 0, mask0 = ((1 << sz0) - 1) << loc0; const int sz1 = 4, loc1 = loc0 + sz0, mask1 = ((1 << sz1) - 1) << loc1; const int sz2 = 4, loc2 = loc1 + sz1, mask2 = ((1 << sz2) - 1) << loc2; const int sz3 = 4, loc3 = loc2 + sz2, mask3 = ((1 << sz3) - 1) << loc3; public uint Item0 { get { return (uint)(raw & mask0) >> loc0; } set { raw = (uint)(raw & ~mask0 | (value << loc0) & mask0); } } public uint Item1 { get { return (uint)(raw & mask1) >> loc1; } set { raw = (uint)(raw & ~mask1 | (value << loc1) & mask1); } } public uint Item2 { get { return (uint)(raw & mask2) >> loc2; } set { raw = (uint)(raw & ~mask2 | (value << loc2) & mask2); } } public uint Item3 { get { return (uint)((raw & mask3) >> loc3); } set { raw = (uint)(raw & ~mask3 | (value << loc3) & mask3); } } }
fuente
raw=uint.MaxValue
) tuve que cambiar ligeramente el último elemento. O tal vez solo se refiera a la última propiedad. No estoy seguro. Entonces, para el ejemplo anterior, losItemX
captadores de propiedades se ven así:get { return (uint)((Raw & Mask3) >> Loc3); }. The setter look like this:
set {Raw = (uint) (Raw & ~ Mask3 | (value << Loc3) & Mask3); } `Sin ese cambio, el casting falla para la última propiedad.También puede utilizar el
BitVector32
y especialmente elSection struct
. El ejemplo es muy bueno.fuente
Si bien es una clase, usar
BitArray
parece ser la forma de reinventar menos la rueda. A menos que esté realmente presionado por el rendimiento, esta es la opción más simple. (Los índices se pueden referenciar con el[]
operador).fuente
¿Podría ayudar una enumeración con el atributo Flags? Mira aquí:
¿Qué significa el atributo Enum [Flags] en C #?
fuente
Una enumeración de banderas también puede funcionar, creo, si la convierte en una enumeración de bytes:
[Flags] enum PesHeaders : byte { /* ... */ }
fuente
Escribí uno, compártelo, puede ayudar a alguien:
[global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public sealed class BitInfoAttribute : Attribute { byte length; public BitInfoAttribute(byte length) { this.length = length; } public byte Length { get { return length; } } } public abstract class BitField { public void parse<T>(T[] vals) { analysis().parse(this, ArrayConverter.convert<T, uint>(vals)); } public byte[] toArray() { return ArrayConverter.convert<uint, byte>(analysis().toArray(this)); } public T[] toArray<T>() { return ArrayConverter.convert<uint, T>(analysis().toArray(this)); } static Dictionary<Type, BitTypeInfo> bitInfoMap = new Dictionary<Type, BitTypeInfo>(); private BitTypeInfo analysis() { Type type = this.GetType(); if (!bitInfoMap.ContainsKey(type)) { List<BitInfo> infos = new List<BitInfo>(); byte dataIdx = 0, offset = 0; foreach (System.Reflection.FieldInfo f in type.GetFields()) { object[] attrs = f.GetCustomAttributes(typeof(BitInfoAttribute), false); if (attrs.Length == 1) { byte bitLen = ((BitInfoAttribute)attrs[0]).Length; if (offset + bitLen > 32) { dataIdx++; offset = 0; } infos.Add(new BitInfo(f, bitLen, dataIdx, offset)); offset += bitLen; } } bitInfoMap.Add(type, new BitTypeInfo(dataIdx + 1, infos.ToArray())); } return bitInfoMap[type]; } } class BitTypeInfo { public int dataLen { get; private set; } public BitInfo[] bitInfos { get; private set; } public BitTypeInfo(int _dataLen, BitInfo[] _bitInfos) { dataLen = _dataLen; bitInfos = _bitInfos; } public uint[] toArray<T>(T obj) { uint[] datas = new uint[dataLen]; foreach (BitInfo bif in bitInfos) { bif.encode(obj, datas); } return datas; } public void parse<T>(T obj, uint[] vals) { foreach (BitInfo bif in bitInfos) { bif.decode(obj, vals); } } } class BitInfo { private System.Reflection.FieldInfo field; private uint mask; private byte idx, offset, shiftA, shiftB; private bool isUnsigned = false; public BitInfo(System.Reflection.FieldInfo _field, byte _bitLen, byte _idx, byte _offset) { field = _field; mask = (uint)(((1 << _bitLen) - 1) << _offset); idx = _idx; offset = _offset; shiftA = (byte)(32 - _offset - _bitLen); shiftB = (byte)(32 - _bitLen); if (_field.FieldType == typeof(bool) || _field.FieldType == typeof(byte) || _field.FieldType == typeof(char) || _field.FieldType == typeof(uint) || _field.FieldType == typeof(ulong) || _field.FieldType == typeof(ushort)) { isUnsigned = true; } } public void encode(Object obj, uint[] datas) { if (isUnsigned) { uint val = (uint)Convert.ChangeType(field.GetValue(obj), typeof(uint)); datas[idx] |= ((uint)(val << offset) & mask); } else { int val = (int)Convert.ChangeType(field.GetValue(obj), typeof(int)); datas[idx] |= ((uint)(val << offset) & mask); } } public void decode(Object obj, uint[] datas) { if (isUnsigned) { field.SetValue(obj, Convert.ChangeType((((uint)(datas[idx] & mask)) << shiftA) >> shiftB, field.FieldType)); } else { field.SetValue(obj, Convert.ChangeType((((int)(datas[idx] & mask)) << shiftA) >> shiftB, field.FieldType)); } } } public class ArrayConverter { public static T[] convert<T>(uint[] val) { return convert<uint, T>(val); } public static T1[] convert<T0, T1>(T0[] val) { T1[] rt = null; // type is same or length is same // refer to http://stackoverflow.com/questions/25759878/convert-byte-to-sbyte if (typeof(T0) == typeof(T1)) { rt = (T1[])(Array)val; } else { int len = Buffer.ByteLength(val); int w = typeWidth<T1>(); if (w == 1) { // bool rt = new T1[len * 8]; } else if (w == 8) { rt = new T1[len]; } else { // w > 8 int nn = w / 8; int len2 = (len / nn) + ((len % nn) > 0 ? 1 : 0); rt = new T1[len2]; } Buffer.BlockCopy(val, 0, rt, 0, len); } return rt; } public static string toBinary<T>(T[] vals) { StringBuilder sb = new StringBuilder(); int width = typeWidth<T>(); int len = Buffer.ByteLength(vals); for (int i = len-1; i >=0; i--) { sb.Append(Convert.ToString(Buffer.GetByte(vals, i), 2).PadLeft(8, '0')).Append(" "); } return sb.ToString(); } private static int typeWidth<T>() { int rt = 0; if (typeof(T) == typeof(bool)) { // x rt = 1; } else if (typeof(T) == typeof(byte)) { // x rt = 8; } else if (typeof(T) == typeof(sbyte)) { rt = 8; } else if (typeof(T) == typeof(ushort)) { // x rt = 16; } else if (typeof(T) == typeof(short)) { rt = 16; } else if (typeof(T) == typeof(char)) { rt = 16; } else if (typeof(T) == typeof(uint)) { // x rt = 32; } else if (typeof(T) == typeof(int)) { rt = 32; } else if (typeof(T) == typeof(float)) { rt = 32; } else if (typeof(T) == typeof(ulong)) { // x rt = 64; } else if (typeof(T) == typeof(long)) { rt = 64; } else if (typeof(T) == typeof(double)) { rt = 64; } else { throw new Exception("Unsupport type : " + typeof(T).Name); } return rt; } }
y el uso:
class MyTest01 : BitField { [BitInfo(3)] public bool d0; [BitInfo(3)] public short d1; [BitInfo(3)] public int d2; [BitInfo(3)] public int d3; [BitInfo(3)] public int d4; [BitInfo(3)] public int d5; public MyTest01(bool _d0, short _d1, int _d2, int _d3, int _d4, int _d5) { d0 = _d0; d1 = _d1; d2 = _d2; d3 = _d3; d4 = _d4; d5 = _d5; } public MyTest01(byte[] datas) { parse(datas); } public new string ToString() { return string.Format("d0: {0}, d1: {1}, d2: {2}, d3: {3}, d4: {4}, d5: {5} \r\nbinary => {6}", d0, d1, d2, d3, d4, d5, ArrayConverter.toBinary(toArray())); } }; class MyTest02 : BitField { [BitInfo(5)] public bool val0; [BitInfo(5)] public byte val1; [BitInfo(15)] public uint val2; [BitInfo(15)] public float val3; [BitInfo(15)] public int val4; [BitInfo(15)] public int val5; [BitInfo(15)] public int val6; public MyTest02(bool v0, byte v1, uint v2, float v3, int v4, int v5, int v6) { val0 = v0; val1 = v1; val2 = v2; val3 = v3; val4 = v4; val5 = v5; val6 = v6; } public MyTest02(byte[] datas) { parse(datas); } public new string ToString() { return string.Format("val0: {0}, val1: {1}, val2: {2}, val3: {3}, val4: {4}, val5: {5}, val6: {6}\r\nbinary => {7}", val0, val1, val2, val3, val4, val5, val6, ArrayConverter.toBinary(toArray())); } } public class MainClass { public static void Main(string[] args) { MyTest01 p = new MyTest01(false, 1, 2, 3, -1, -2); Debug.Log("P:: " + p.ToString()); MyTest01 p2 = new MyTest01(p.toArray()); Debug.Log("P2:: " + p2.ToString()); MyTest02 t = new MyTest02(true, 1, 12, -1.3f, 4, -5, 100); Debug.Log("t:: " + t.ToString()); MyTest02 t2 = new MyTest02(t.toArray()); Debug.Log("t:: " + t.ToString()); Console.Read(); return; } }
fuente
Me encuentro bastante cómodo con estas funciones de ayuda:
uint SetBits(uint word, uint value, int pos, int size) { uint mask = ((((uint)1) << size) - 1) << pos; word &= ~mask; //resettiamo le posizioni word |= (value << pos) & mask; return word; } uint ReadBits(uint word, int pos, int size) { uint mask = ((((uint)1) << size) - 1) << pos; return (word & mask) >> pos; }
luego:
uint the_word; public uint Itemx { get { return ReadBits(the_word, 5, 2); } set { the_word = SetBits(the_word, value, 5, 2) } }
fuente