Bit fields in C#

Viewed 111706

I have a structure which I need to populate and write to disk (several actually).

An example is:

byte-6    
bit0 - original_or_copy  
bit1 - copyright  
bit2 - data_alignment_indicator  
bit3 - PES_priority  
bit4-bit5 - PES_scrambling control.  
bit6-bit7 - reserved  

In C I might do something like the following:

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;
};

Is there any way to do this in C# that would enable me to access the bits using the struct dereferencing dot operator?

For a couple of structures, I can just do bit shifting wrapped in an accessor function.

I have loads of structures to handle in this way, so I'm looking for something that's easier to read and quicker to write.

12 Answers

I'd probably knock together something using attributes, then a conversion class to convert suitably attributed structures to the bitfield primitives. Something like...

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;
        }
    }
}

Which produces the expected ...000101011. Of course, it needs more error checking and a slightly saner typing, but the concept is (I think) sound, reusable, and lets you knock out easily maintained structures by the dozen.

adamw

By using an enum you can do this, but will look awkward.

[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.
}

You want 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; 
}

This is really a union but you can use it as a bitfield--you just have to be conscious of where in the byte the bits for each field are supposed to be. Utility functions and/or constants to AND against can help.

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;
}    

There is also LayoutKind.Sequential which will allow you to do it the C way.

I find myself quite comfortable with these helper functions:

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;
}

then:

uint the_word;

public uint Itemx
{
    get { return ReadBits(the_word, 5, 2); }
    set { the_word = SetBits(the_word, value, 5, 2) }
}

A flags enum can work too, I think, if you make it a byte enum:

[Flags] enum PesHeaders : byte { /* ... */ }

I wrote one this morning with T4. :) Same example as Zbyl, though I threw in a bit of uint sizing fun. This is just a first pass, it could obviously use a little error checking. Also the bitFields spec array would be nicer in a separate file, maybe a .ttinclude, or a json/yaml..

=== BitFields.tt ===

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>

<#
var bitFields = new[]
{
    new
    {
        Name = "rcSpan2", Fields = new[] { ("smin", 13), ("smax", 13), ("area", 6) },
    },  
};

foreach (var bitField in bitFields)
{
    static string getType(int size) =>
        size switch
        {
            > 32 => "ulong",
            > 16 => "uint",
            > 8 => "ushort",
            _ => "byte",
        };

    var bitFieldType = getType(bitField.Fields.Sum(f => f.Item2)); 
#>
public struct <#=bitField.Name#>
{
    <#=bitFieldType#> _bitfield;

<#
var offset = 0;
foreach (var (fieldName, fieldSize) in bitField.Fields)
{
    var fieldType = getType(fieldSize);
    var fieldMask = $"0x{((1UL<<fieldSize)-1):X}U";
#>
    public <#=fieldType#> <#=fieldName#> // : <#=fieldSize#>
    {
        get => (<#=fieldType#>)(<#=offset > 0 ? $"(_bitfield >> {offset})" : "_bitfield"#> & <#=fieldMask#>);
        set => _bitfield = (<#=bitFieldType#>)((_bitfield & ~((<#=bitFieldType#>)<#=fieldMask#> << <#=offset#>)) | ((<#=bitFieldType#>)(value & <#=fieldMask#>) << <#=offset#>));
    }  
<#
    offset += fieldSize;
}
#>
}

<#}#>

=== BitFields.cs === (generated)

public struct rcSpan2
{
    uint _bitfield;

    public ushort smin // : 13
    {
        get => (ushort)(_bitfield & 0x1FFFU);
        set => _bitfield = (uint)((_bitfield & ~((uint)0x1FFFU << 0)) | ((uint)(value & 0x1FFFU) << 0));
    }  
    public ushort smax // : 13
    {
        get => (ushort)((_bitfield >> 13) & 0x1FFFU);
        set => _bitfield = (uint)((_bitfield & ~((uint)0x1FFFU << 13)) | ((uint)(value & 0x1FFFU) << 13));
    }  
    public byte area // : 6
    {
        get => (byte)((_bitfield >> 26) & 0x3FU);
        set => _bitfield = (uint)((_bitfield & ~((uint)0x3FU << 26)) | ((uint)(value & 0x3FU) << 26));
    }  
}
Related