Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A 64bit Bit-Field in C#

0.00/5 (No votes)
5 Nov 2014 1  
Making BitField with C# alternative to BitField made with union and struct of C++

Introduction

We could easily make a useful and various bit-field by combining 'union' and 'struct' in C++. But, C# does not have union but has FieldOffset, BitVector32 and BitArray. I tried to make 64Bit-field with FieldOffset, BitVector32 and BitArray. BitArray cannot be used with FieldOffset, so I passed FieldOffset over. However, I did manage to make 64Bit-field with FieldOffset and BitVector32, Using the index of BitVector32 was still inconvenient. So, I have made it conveniently with 'this' and some bit-wise operator.

Background

I made a 64Bit-Field a long long time ago by referring to books and searching the web. I don't remember exactly but, I found BitVector32 source code suddenly. And then I could make BitVector64. I'd like to take this opportunity to thank programmers that contribute too.

Using the Code

I don't like to talk a lot so I hope to talk about the core instead. For more details, please refer to the comment of source code. The 'struct' for 64Bit-Field is like this:

    /// Homepage: http://www.idle-natura-system.com
    /// Author: WonYoung(Brad) Jeong
    /// 
    /// 64bit Integer is expressed in even any type beside bit since FieldOffse's unit is byte.
    /// So, you must create your own bit express.
    ///
    /// 1. Size
    /// This field must be equal or greater than the total size, in bytes, 
    /// of the members of the class or structure.
    ///
    /// 2. Pack 
    /// This field determines how many bytes will be used(or, n-byte unit).
    ///  ex) Pack n 
    ///     struct MyStruct
    ///     {
    ///        byte B0;
    ///        byte B1;
    ///        byte B2;
    ///        byte B3;
    ///     }
    ///
    ///   B0 will still begin at offset 0 (byte 0).
    ///   B1 will begin at offset n (byte n).
    ///   B2 will begin at offset n*2 (byte 2n).
    ///   B3 will begin at offset n*3 (byte 3n).
    ///
    [StructLayout(LayoutKind.Explicit, Size=8, Pack=1, CharSet=CharSet.Ansi)]
    public struct Any64
    {
        ///
        ///  express 64bits to 64bits integer.
        ///
        #region Int64
        [FieldOffset(0)]        // 'FieldOffset' offsets by byte.
        public Int64 INT64;
        #endregion //Int64

        ///
        ///  express 64bits to 64bits unsigned integer.
        ///
        #region UInt64
        [FieldOffset(0)]        // 'FieldOffset' offsets by byte.
        public UInt64 UINT64;
        #endregion //UInt64

        ///
        ///  express 64bits to double.
        ///
        #region double
        [FieldOffset(0)]        // 'FieldOffset' offsets by byte.
        public double DOUBLE;
        #endregion //double

        ///
        ///  express 64bits to 32bits float.
        ///
        #region float
        [FieldOffset(0)]        // 'FieldOffset' offsets by byte.
        public float FLOAT_0;
        [FieldOffset(4)]        // float or Single is 4 bytes. So, it is FieldOffset(4).
        public float FLOAT_1;
        #endregion //float

        ///
        /// express 64bits to 32bits unsigned integer.
        ///
        #region Uint32
        [FieldOffset(0)]
        public uint UINT32_0;
        [FieldOffset(4)]       //uint is 4 bytes. So, it is FieldOffset(4).
        public uint UINT32_1;
        #endregion //Uint32
 
        ///
        /// express 64bits to 16bits unsigned integer.
        ///
        #region UInt16
        [FieldOffset(0)]
        public ushort UINT16_0;
        [FieldOffset(2)]       //ushort is 2 bytes. So, it is FieldOffset(2).
        public ushort UINT16_1;
        [FieldOffset(4)]
        public ushort UINT16_2;
        [FieldOffset(6)]
        public ushort UINT16_3;
        #endregion //UInt16
 
        ///
        /// express 64bits to 8bits unsigned integer.
        ///
        #region UInt8
        [FieldOffset(0)]
        public byte UINT8_0;
        [FieldOffset(1)]       //byte is 1 byte. So, it is FieldOffset(1). increase one by one.
        public byte UINT8_1;
        [FieldOffset(2)]
        public byte UINT8_2;
        [FieldOffset(3)]
        public byte UINT8_3;
        [FieldOffset(4)]
        public byte UINT8_4;
        [FieldOffset(5)]
        public byte UINT8_5;
        [FieldOffset(6)]
        public byte UINT8_6;
        [FieldOffset(7)]
        public byte UINT8_7;
        #endregion //Int8
 
        ///
        /// express 64bits to 32bits integer.
        ///
        #region int32
        [FieldOffset(0)]
        public int INT32_0;
        [FieldOffset(4)]       //int is 4 bytes. So, it is FieldOffset(4).
        public int INT32_1;
        #endregion //int32
 
        ///
        /// express 64bits to 16bits integer.
        ///
        #region Int16
        [FieldOffset(0)]
        public short INT16_0;
        [FieldOffset(2)]      //short is 2 bytes. So, it is FieldOffset(2).
        public short INT16_1;
        [FieldOffset(4)]
        public short INT16_2;
        [FieldOffset(6)]
        public short INT16_3;
        #endregion //Int16
 
        ///
        /// express 64bits to 8bits integer.
        ///
        #region Int8
        [FieldOffset(0)]
        public sbyte INT8_0;
        [FieldOffset(1)]      //sbyte is 1 byte. So, it is FieldOffset(1). increase one by one.
        public sbyte INT8_1;
        [FieldOffset(2)]
        public sbyte INT8_2;
        [FieldOffset(3)]
        public sbyte INT8_3;
        [FieldOffset(4)]
        public sbyte INT8_4;
        [FieldOffset(5)]
        public sbyte INT8_5;
        [FieldOffset(6)]
        public sbyte INT8_6;
        [FieldOffset(7)]
        public sbyte INT8_7;
        #endregion //Int8
 
        ///
        /// express 64bits to high bits and low bits.
        /// It was made with 'private' for accessing to the index with 'this'.
        ///
        #region Bit            /// 'FieldOffset' is byte unit. So, we need  BitVector unavoidably.
        [FieldOffset(0)]
        private BitVector32 LowBits;
        [FieldOffset(4)]       //BitVector32 is 4 bytes, So, it is FieldOffset(4).
        private BitVector32 HighBits;
 
        ///
        /// express 64bits to bits.
        /// It was made with 'private' for accessing to the index with 'this'.
        ///
        [FieldOffset(0)]
        private BitVector64 Bits;
        #endregion //Bit
 
      // the index of BitVector64 is not convenient.
        // this make access to index convenient.
        public bool this[int index]
        {
            get{ return Bits[(long)(1 << index)];}
            set{ Bits[(long)(1 << index)] = value;}
        }
    }

I made 'BitVector64' just with remodeling 'BitVector32' source code. Please allow me to leave out the explanation. Once again, I'd like to take this opportunity to thank programmers that contribute too. The 'struct' of 'BitVector64' is like this:

//
// System.Collections.Specialized.BitVector32.cs
//
// Author:
//   Miguel de Icaza 
//   Lawrence Pit 
//   Andrew Birkett 
//   Andreas Nahr 
//
//   WonYoung(Brad) Jeong : converted 32bits to 64bits
//
//
// (C) Ximian, Inc.  <a href="http://www.ximian.com">http://www.ximian.com
// Copyright (C) 2005 Novell, Inc (<a href="http://www.novell.com">http://www.novell.com</a>)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;

namespace INS.BaseLib
{
    public struct BitVector64
    {
        long data;
 
        #region Section
        ///
        /// Section
        ///
        public struct Section
        {
            private short mask;
            private short offset;
 
            internal Section(short mask, short offset)
            {
                this.mask = mask;
                this.offset = offset;
            }
 
            public short Mask
            {
                get { return mask; }
            }
 
            public short Offset
            {
                get { return offset; }
            }
#if NET_2_0
            public static bool operator == (Section v1, Section v2)
            {
                return v1.mask == v2.mask &&
                       v1.offset == v2.offset;
            }
 
            public static bool operator != (Section v1, Section v2)
            {
                return v1.mask != v2.mask &&
                       v1.offset != v2.offset;
            }
 
            public bool Equals (Section obj)
            {
                return this.mask == obj.mask &&
                       this.offset == obj.offset;
            }
#endif
            public override bool Equals(object o)
            {
                if (!(o is Section))
                    return false;
 
                Section section = (Section)o;
                return this.mask == section.mask &&
                       this.offset == section.offset;
            }
 
            public override int GetHashCode()
            {
                return (((Int16)mask).GetHashCode() << 16) +
                       ((Int16)offset).GetHashCode();
            }
 
            public override string ToString()
            {
                return "Section{0x" + Convert.ToString(mask, 16) +
                       ", 0x" + Convert.ToString(offset, 16) + "}";
            }
 
            public static string ToString(Section value)
            {
                StringBuilder b = new StringBuilder();
                b.Append("Section{0x");
                b.Append(Convert.ToString(value.Mask, 16));
                b.Append(", 0x");
                b.Append(Convert.ToString(value.Offset, 16));
                b.Append("}");
 
                return b.ToString();
            }
        }
        #endregion //Section
 
        #region Constructors
        public BitVector64(BitVector64 source)
        {
            this.data = source.data;
        }
        public BitVector64(BitVector32 source)
        {
            this.data = source.Data;
        }
 
        public BitVector64(long source)
        {
            this.data = source;
        }
        public BitVector64(int init)
        {
            this.data = init;
        }
        #endregion // Constructors
 
        #region Properties
        public long Data
        {
            get { return this.data; }
        }
 
        public long this[BitVector64.Section section]
        {
            get
            {
                return ((data >> section.Offset) & section.Mask);
            }
 
            set
            {
                if (value < 0)
                    throw new ArgumentException("Section can't hold negative values");
                if (value > section.Mask)
                    throw new ArgumentException("Value too large to fit in section");
                this.data &= ~(section.Mask << section.Offset);
                this.data |= (value << section.Offset);
            }
        }
 
        public bool this[long mask]
        {
            get
            {
#if NET_2_0
                return (this.data & mask) == mask;
#else
                long tmp = /*(uint)*/this.data;
                return (tmp & (long)mask) == (long)mask;
#endif
            }
 
            set
            {
                if (value)
                    this.data |= mask;
                else
                    this.data &= ~mask;
            }
        }
        #endregion //Properties
 
        // Methods     
        public static long CreateMask()
        {
            return CreateMask(0);   // 1;
        }
 
        public static long CreateMask(long prev)
        {
            if (prev == 0)
                return 1;
            if (prev == Int64.MinValue)
                throw new InvalidOperationException("all bits set");
            return prev << 1;
        }
 
        public static Section CreateSection(int maxValue)
        {
            return CreateSection(maxValue, new Section(0, 0));
        }
 
        public static Section CreateSection(int maxValue, BitVector64.Section previous)
        {
            if (maxValue < 1)
                throw new ArgumentException("maxValue");
 
            int bit = HighestSetBit(maxValue) + 1;
            int mask = (1 << bit) - 1;
            int offset = previous.Offset + NumberOfSetBits(previous.Mask);
 
            if (offset > 64)
            {
                throw new ArgumentException("Sections cannot exceed 64 bits in total");
            }
 
            return new Section((short)mask, (short)offset);
        }
 
        public override bool Equals(object o)
        {
            if (!(o is BitVector64))
                return false;
 
            return data == ((BitVector64)o).data;
        }
 
        public override int GetHashCode()
        {
            return data.GetHashCode();
        }
 
        public override string ToString()
        {
            return ToString(this);
        }
 
        public static string ToString(BitVector64 value)
        {
            StringBuilder sb = new StringBuilder(0x2d);
            sb.Append("BitVector64{");
            ulong data = (ulong)value.Data;
            for (int i = 0; i < 0x40; i++)
            {
                sb.Append(((data & 0x8000000000000000) == 0) ? '0' : '1');
                data = data << 1;
            }
 
            sb.Append("}");
            return sb.ToString();
 
            //StringBuilder b = new StringBuilder();
            //b.Append("BitVector64{");
            //ulong mask = (ulong)Convert.ToInt64(0x8000000000000000);
            //while (mask > 0)
            //{
            //    b.Append((((ulong)value.Data & mask) == 0) ? '0' : '1');
            //    mask >>= 1;
            //}
            //b.Append('}');
            //return b.ToString();
        }
 
        // Private utilities
        private static int NumberOfSetBits(int i)
        {
            int count = 0;
            for (int bit = 0; bit < 64; bit++)
            {
                int mask = 1 << bit;
                if ((i & mask) != 0)
                    count++;
            }
            return count;
        }
 
        private static int HighestSetBit(int i)
        {
            for (int bit = 63; bit >= 0; bit--)
            {
                int mask = 1 << bit;
                if ((mask & i) != 0)
                {
                    return bit;
                }
            }
            return -1;
        }
    }
}

How To Use

You can find that the value of any other type also got surprisingly changed if a value is set to 'INT64', UINT8_5 and so on.

INS.BaseLib.Any64 bitField64 = new INS.BaseLib.Any64();

bitField64.INT64 = 255; 
bitField64.UINT8_5 = 17;
bitField64[5] = true;
bool bValues = bitField64[63];

Points of Interest

You can get the value of wanted type from the value of any type. Even double or float. So, you don't need the complex calculation to get double value from integer values.

If you have any questions and suggestions about this tip, please don't hesitate to let me know.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here