Mam strukturę, którą muszę wypełnić i zapisać na dysku (a właściwie kilka).
Przykładem jest:
byte-6
bit0 - original_or_copy
bit1 - copyright
bit2 - data_alignment_indicator
bit3 - PES_priority
bit4-bit5 - PES_scrambling control.
bit6-bit7 - reserved
W CI może zrobić coś takiego:
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;
};
Czy istnieje sposób, aby to zrobić w C #, który umożliwiłby mi dostęp do bitów za pomocą operatora kropki dereferencing struktury?
W przypadku kilku struktur mogę po prostu przesunąć bit w funkcji akcesorium.
Mam mnóstwo struktur do obsłużenia w ten sposób, więc szukam czegoś, co jest łatwiejsze do odczytania i szybsze do napisania.
źródło
GetFields
Metoda nie zwraca pól w określonej kolejności, na przykład kolejności alfabetycznej lub kolejności deklaracji. Kod nie może zależeć od kolejności zwracania pól, ponieważ ta kolejność jest różna”. Czy to nie powoduje tutaj problemu?IBitfield
interfejs „znacznika” (nie mający elementów członkowskich), możesz przekonwertowaćPrimitiveConversion
klasę na metody rozszerzające dla dowolnej implementowanej strukturyIBitfield
. Na przykład:public static long ToLong(this IBitfield obj) {}
. NastępnieToLong()
metoda pojawi się w Intellisense dlaIBitfield
obiektów.GetFields
może ucierpieć z powodu zmiany kolejności z powodu pamięci podręcznej odbić, ale sortowanie według 'MetadataToken' powinno rozwiązać ten problem (który można odtworzyć, mieszając get single field i pobierz wszystkie pola w jakiejś kolejności).Używając wyliczenia, możesz to zrobić, ale będzie to wyglądać niezręcznie.
[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. }
źródło
Chcesz 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; }
To naprawdę jest suma, ale możesz jej używać jako pola bitowego - po prostu musisz być świadomy tego, gdzie w bajcie powinny znajdować się bity dla każdego pola. Pomocne mogą być funkcje narzędziowe i / lub stałe do AND przeciw.
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; }
Jest też LayoutKind.Sequential, który pozwoli ci to zrobić w C.
źródło
Jak zasugerował Christophe Lambrechts, BitVector32 zapewnia rozwiązanie. Wydajność w ruchu powinna być odpowiednia, ale nie wiem na pewno. Oto kod ilustrujący to rozwiązanie:
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; } } }
W ten sposób możesz wiele zrobić. Możesz działać jeszcze lepiej bez korzystania z BitVector32, dostarczając ręcznie robione akcesoria dla każdego pola:
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; } } }
O dziwo to ostatnie, ręcznie robione rozwiązanie wydaje się najwygodniejsze, najmniej zawiłe i najkrótsze. To oczywiście tylko moje osobiste preferencje.
źródło
Jeszcze jedno oparte na odpowiedzi Zbyla. To jest dla mnie trochę łatwiejsze do zmiany - muszę tylko dostosować sz0, sz1 ... a także upewnić się, że maski # i loc # są poprawne w blokach Set / Get.
Jeśli chodzi o wydajność, powinno być takie samo, ponieważ oba rozwiązały się do 38 instrukcji MSIL. (stałe są rozwiązywane w czasie kompilacji)
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); } } }
źródło
raw=uint.MaxValue
), musiałem nieznacznie zmienić ostatnią pozycję. A może dotyczy tylko ostatniej nieruchomości. Niepewny. Zatem w powyższym przykładzie metodyItemX
pobierające właściwości wyglądają następująco:get { return (uint)((Raw & Mask3) >> Loc3); }. The setter look like this:
set {Raw = (uint) (Raw & ~ Mask3 | (wartość << Loc3) & Mask3); } `Bez tej zmiany rzutowanie kończy się niepowodzeniem dla ostatniej właściwości.Możesz także użyć,
BitVector32
a zwłaszczaSection struct
. Przykład jest bardzo dobry.źródło
Chociaż jest to klasa, używanie
BitArray
wydaje się sposobem na najmniejsze odkrycie koła. O ile nie jesteś naprawdę pod presją wydajności, jest to najprostsza opcja. (Do indeksów można się odwoływać za pomocą[]
operatora).źródło
Czy wyliczenie z atrybutem flagi może pomóc? Spójrz tutaj:
Co oznacza atrybut [Flags] Enum w języku C #?
źródło
Myślę, że wyliczenie flag również może działać, jeśli zrobisz z niego wyliczenie bajtowe:
[Flags] enum PesHeaders : byte { /* ... */ }
źródło
Napisałem jedną, podziel się nią, może komuś pomóc:
[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; } }
i zastosowanie:
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; } }
źródło
Te funkcje pomocnicze są dla mnie całkiem wygodne:
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; }
następnie:
uint the_word; public uint Itemx { get { return ReadBits(the_word, 5, 2); } set { the_word = SetBits(the_word, value, 5, 2) } }
źródło