C #의 비트 필드
디스크를 채우고 써야하는 구조가 있습니다 (실제로는 여러 개).
예 :
byte-6
bit0 - original_or_copy
bit1 - copyright
bit2 - data_alignment_indicator
bit3 - PES_priority
bit4-bit5 - PES_scrambling control.
bit6-bit7 - reserved
CI에서 다음과 같은 작업을 수행 할 수 있습니다.
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;
};
구조체 역 참조 도트 연산자를 사용하여 비트에 액세스 할 수 있도록 C #에서이 작업을 수행하는 방법이 있습니까?
몇 가지 구조의 경우 접근 자 함수에 래핑 된 비트 시프 팅을 수행 할 수 있습니다.
이런 식으로 처리 할 수있는 많은 구조가 있으므로 읽기 쉽고 쓰기가 더 빠른 것을 찾고 있습니다.
속성을 사용하여 무언가를 결합한 다음 적절하게 속성을 부여한 구조를 비트 필드 프리미티브로 변환하는 변환 클래스를 사용합니다. 뭔가 ...
using System;
namespace BitfieldTest
{
[global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
sealed class BitfieldLengthAttribute : Attribute
{
uint length;
public BitfieldLengthAttribute(uint length)
{
this.length = length;
}
public uint Length { get { return length; } }
}
static class PrimitiveConversion
{
public static long ToLong<T>(T t) where T : struct
{
long r = 0;
int offset = 0;
// For every field suitably attributed with a BitfieldLength
foreach (System.Reflection.FieldInfo f in t.GetType().GetFields())
{
object[] attrs = f.GetCustomAttributes(typeof(BitfieldLengthAttribute), false);
if (attrs.Length == 1)
{
uint fieldLength = ((BitfieldLengthAttribute)attrs[0]).Length;
// Calculate a bitmask of the desired length
long mask = 0;
for (int i = 0; i < fieldLength; i++)
mask |= 1 << i;
r |= ((UInt32)f.GetValue(t) & mask) << offset;
offset += (int)fieldLength;
}
}
return r;
}
}
struct PESHeader
{
[BitfieldLength(2)]
public uint reserved;
[BitfieldLength(2)]
public uint scrambling_control;
[BitfieldLength(1)]
public uint priority;
[BitfieldLength(1)]
public uint data_alignment_indicator;
[BitfieldLength(1)]
public uint copyright;
[BitfieldLength(1)]
public uint original_or_copy;
};
public class MainClass
{
public static void Main(string[] args)
{
PESHeader p = new PESHeader();
p.reserved = 3;
p.scrambling_control = 2;
p.data_alignment_indicator = 1;
long l = PrimitiveConversion.ToLong(p);
for (int i = 63; i >= 0; i--)
{
Console.Write( ((l & (1l << i)) > 0) ? "1" : "0");
}
Console.WriteLine();
return;
}
}
}
예상되는 ... 000101011을 생성합니다. 물론 더 많은 오류 검사와 약간 더 건전한 타이핑이 필요하지만 개념은 (내 생각에) 소리이고 재사용 가능하며 쉽게 유지 관리되는 구조를 12 개까지 제거 할 수 있다는 것입니다.
Adamw
열거 형을 사용하면 이렇게 할 수 있지만 어색해 보일 것입니다.
[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.
}
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;
}
이것은 실제로 유니온이지만 비트 필드로 사용할 수 있습니다. 바이트에서 각 필드의 비트가 있어야하는 위치를 인식하면됩니다. AND에 대한 유틸리티 함수 및 / 또는 상수가 도움이 될 수 있습니다.
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;
}
C 방식으로 할 수있는 LayoutKind.Sequential도 있습니다.
Christophe Lambrechts가 제안했듯이 BitVector32가 솔루션을 제공합니다. Jitted 성능은 적절해야하지만 확실하지는 않습니다. 이 솔루션을 설명하는 코드는 다음과 같습니다.
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; }
}
}
이런 식으로 많은 일을 할 수 있습니다. 모든 필드에 수제 접근자를 제공하면 BitVector32를 사용하지 않고도 더 잘할 수 있습니다.
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; }
}
}
놀랍게도이 마지막 핸드 메이드 솔루션은 가장 편리하고 복잡하지 않으며 가장 짧은 솔루션 인 것 같습니다. 물론 그것은 내 개인적인 취향 일뿐입니다.
Zbyl의 대답을 기반으로 한 또 하나. 이것은 나를 위해 조금 더 쉽게 변경할 수 있습니다. sz0, sz1 ...을 조정하고 Set / Get 블록에서 mask # 및 loc #이 올바른지 확인해야합니다.
성능면에서 둘 다 38 개의 MSIL 문으로 확인 된 것과 같아야합니다. (상수는 컴파일 타임에 해결됨)
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); }
}
}
당신은 또한 사용할 수 있습니다 BitVector32
, 특히를 Section struct
. 예는 매우 좋습니다.
클래스이지만 사용 BitArray
은 바퀴를 최소한으로 재발 명하는 방법처럼 보입니다. 실제로 성능을 요구하지 않는 한 이것은 가장 간단한 옵션입니다. ( []
연산자로 인덱스를 참조 할 수 있습니다 .)
플래그 속성이있는 열거 형이 도움이 될 수 있습니까? 여기를 보아라:
C #에서 [Flags] Enum 특성은 무엇을 의미합니까?
플래그 열거 형도 작동 할 수 있습니다. 바이트 열거 형으로 만들면 다음과 같습니다.
[Flags] enum PesHeaders : byte { /* ... */ }
나는 하나를 작성하고 공유하고 누군가를 도울 수 있습니다.
[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;
}
}
및 사용법 :
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;
}
}
나는 이러한 도우미 기능에 상당히 익숙하다.
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;
}
그때:
uint the_word;
public uint Itemx
{
get { return ReadBits(the_word, 5, 2); }
set { the_word = SetBits(the_word, value, 5, 2) }
}
참고 URL : https://stackoverflow.com/questions/14464/bit-fields-in-c-sharp
'program story' 카테고리의 다른 글
node.js를 사용하여 파일 이름 바꾸기 (0) | 2020.10.24 |
---|---|
nginx : [emerg] "server"지시문은 여기에서 허용되지 않습니다. (0) | 2020.10.24 |
Eclipse 용 선호하는 Ruby 플러그인? (0) | 2020.10.24 |
파생 형식에서 클래스의 정적 멤버를 사용하고 있습니까? (0) | 2020.10.24 |
C # 람다 식 역방향 <= (0) | 2020.10.24 |