c# - Bit fields in C#

this is a thread that is inspired by the thoght and discusson from the stackoverflow. and hte original post is here:  Bit fields in C#.

 

 

As a background/foundamental knowledge,or the first question to ask: what is bit fields?

 

 

Bit Fields is a extremely space saving technique which is quit commonly used in C++ proramms

 

 

here is one example of the BitField in C++:

 

 

 

class BitFields { 
  public:
    int mode : 2;
	int full_flag : 1;
	int empty_flag : 1;

  };

 

 and you can use it this way:

 

 

enum { On = 0, Off = 1 } ;
enum { Yes = 0, No = 1 };

int main() { 
    BitFields bitFields;
	bitFields.mode |= On;
	bitFields.full_flag &= ~Yes;
}

 

 

 

 

Why do we need the Bit Fields classes, it is space effecient, and it is easy to manipulate. But they are not natively built inside the C# language. 

 

 

 

 

Given an example.

 

 

suppose that we are going to model a flag as this: 

 

 

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

 

 

 

and you can do this in C

 

 

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

 

 

 

Let's first examine some solution that has been discussed.

 

The StructLayoutAttribute solution

 

 

 

static class StructLayoutResolution
  {
    // the Fool structure is using a union approach.
    // or you can change the Explicit location so that they can aligh with each other sequentially
    // or you can try the LayoutKind.Sequential
    public void DemoStructLayoutResolution()
    {
      
      Foo foo = new Foo();
      Console.WriteLine("foo.original_or_copy = {0}", foo.original_or_copy);

    }

    const byte _original_or_copy = 1;
    const byte _copyright = 2;

    static bool original_or_copy(this Foo foo)
    {
      return (foo.original_or_copy & _original_or_copy) == _original_or_copy;
    }
  }

  // you can use the StructlayoutAttribute 
  //   http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute%28VS.71%29.aspx
  [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_prioirty;
    [FieldOffset(0)]
    public byte reserved;
  }
 

 

The code shown above is the union approach, but you can use the bit fields approach, by changing the Layoutkind from LayoutKind.Explicit to LayoutKind.Sequential.

 

 

The custom attributes solution

 

 

 

// this is using a custmo attribute , where you can decorate the fields just as you can do with the StructLayoutAttribute
  class CustomAttributeResoltuion
  {
    public static void DemoCustomAttributeTechnique()
    {
      PSEHeader p = new PSEHeader();
      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.WriteLine(((l & (1L << i)) > 0) ? "1" : "0");
      }
      Console.WriteLine();
      return;
    }
  }

  [AttributeUsage(AttributeTargets.Field, AllowMultiple=false)]
  sealed class BitfieldLengthAttribute : Attribute
  {
    uint length;

    public BitfieldLengthAttribute(uint length)
    {
      this.length = length;
    }

    public uint Length { get { return this.length; } }
  }

  // this is a helper class that helps you to convert a structure that is decorated with BitfieldLengthAttribute 
  // to a long value
  static class PrimitiveConversion
  {
    public static long ToLong<T>(T t) where T : struct
    {
      long r = 0;
      int offset = 0;

      foreach (var f in t.GetType().GetFields())
      {
        var attrs = f.GetCustomAttributes(typeof(BitfieldLengthAttribute), false);
        if (attrs.Length == 1)
        {
          uint fieldlength = ((BitfieldLengthAttribute)attrs[0]).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 PSEHeader
  {
    [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;
  }

 

 

 

However, be cautions that this approach uses the Type.GetFields() call, which, according to the MSDN, does not guarantee to return the fields in the order that they are declared.

 

 

So you have to use it with caution.

 

 

BitVector approach

This is probably the most native one, the bit vector is a managed class and it is natural to use. 

 

public static class BitVectorMethod
  {
    public static void BitVectorDemo()
    {
      rcSpan rcspan = new rcSpan();
      rcspan.smin = 13;

      rcSpan2 rcspan2 = new rcSpan2();
      rcspan2.smin = 13;
    }
  }


  public struct rcSpan
  {
    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);

    // the internal storage class object.
    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; } 
    }

    


  }

  // or you can do some handmade access this way

  public struct rcSpan2
  {
    // andyou can do more
    // such as provide the handmade accessors for every fields

    internal uint data;

    // pulbic uint smin: 13
    public uint smin
    {
      get { return (uint)data & 0x1FFFF; }
      set { data = (data & ~0x1FFFFu) | (value & 0x1FFFF); }
    }

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

As you can see, that in the code above, we are not only show the native bitvector approach, we are actually using some custom handmade bitvector acesss, but the priciple is the same.

 

 

 

 

你可能感兴趣的:(C#)