A 64bit Bit-Field in C#






4.88/5 (10 votes)
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.