[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은 바퀴를 최소한으로 재발 명하는 방법처럼 보입니다. 실제로 성능을 요구하지 않는 한 이것은 가장 간단한 옵션입니다. ( []연산자로 인덱스를 참조 할 수 있습니다 .)