[c#] 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을 생성합니다. 물론 더 많은 오류 검사와 약간 더 건전한 타이핑이 필요하지만 개념은 (내 생각에) 소리이고 재사용 가능하며 쉽게 유지 관리되는 구조를 수십 개까지 제거 할 수 있다는 것입니다.
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
은 바퀴를 최소한으로 재발 명하는 방법처럼 보입니다. 실제로 성능을 요구하지 않는 한 이것은 가장 간단한 옵션입니다. ( []
연산자로 인덱스를 참조 할 수 있습니다 .)