Skip to content

Instantly share code, notes, and snippets.

@ufcpp
Last active November 15, 2022 18:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ufcpp/64244e996bfad975c2c8e56cad788d03 to your computer and use it in GitHub Desktop.
Save ufcpp/64244e996bfad975c2c8e56cad788d03 to your computer and use it in GitHub Desktop.
A bit accessor for `byte`
// required packages:
// System.Runtime.CompilerServices.Unsafe
// System.Numerics.Vectors
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
struct _16Bytes
{
public ulong LowWord;
public ulong HighWord;
}
interface CBitOperator<T>
{
int Size { get; }
bool GetBit(ref T x, int index);
void SetBit(ref T x, int index, bool value);
T RightShift(T x);
}
struct ByteBitOperator : CBitOperator<byte>
{
public int Size => 8;
public bool GetBit(ref byte x, int index)
{
if (index < 0 || index >= 8) throw new IndexOutOfRangeException();
return (x & (1 << index)) != 0;
}
public void SetBit(ref byte x, int index, bool value)
{
if (value) x |= (byte)(1 << index);
else x &= (byte)~(1 << index);
}
public byte RightShift(byte x) => (byte)(x >> 1);
}
struct BitOperator16Bytes : CBitOperator<_16Bytes>
{
public int Size => 128;
public bool GetBit(ref _16Bytes x, int index)
{
if (index < 0 || index >= 128) throw new IndexOutOfRangeException();
if (index < 64) return (x.LowWord & (1UL << index)) != 0;
else return (x.HighWord & (1UL << (index - 64))) != 0;
}
public void SetBit(ref _16Bytes x, int index, bool value)
{
if(index < 64)
{
if (value) x.LowWord |= (1UL << index);
else x.LowWord &= ~(1UL << index);
}
else
{
index -= 64;
if (value) x.HighWord |= (1UL << index);
else x.HighWord &= ~(1UL << index);
}
}
public _16Bytes RightShift(_16Bytes x)
{
var lowestHigh = (x.HighWord & 1);
x.HighWord >>= 1;
x.LowWord >>= 1;
if (lowestHigh == 1) x.LowWord |= 0x8000_0000_0000_0000;
return x;
}
}
unsafe struct Bits<T, TOperator> : IEnumerable<bool>
where T : struct // should be `blittable` in the future
where TOperator : CBitOperator<T>
{
// In the future, we'll be able to implement this in safe context with https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/ByReference.cs
void* _ptr;
public Bits(ref T x) => _ptr = Unsafe.AsPointer(ref x);
public bool this[int index]
{
get => default(TOperator).GetBit(ref Unsafe.AsRef<T>(_ptr), index);
set => default(TOperator).SetBit(ref Unsafe.AsRef<T>(_ptr), index, value);
}
public int Length => default(TOperator).Size;
Enumerator GetEnumerator() => new Enumerator(Unsafe.AsRef<T>(_ptr));
IEnumerator<bool> IEnumerable<bool>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<bool>
{
int _i;
T _x;
public Enumerator(T x) => (_x, _i) = (x, 0);
public bool Current => default(TOperator).GetBit(ref _x, 0);
object IEnumerator.Current => Current;
void IDisposable.Dispose() { }
public bool MoveNext()
{
if (_i == 0)
{
++_i;
return true;
}
if (_i >= default(TOperator).Size) return false;
++_i;
_x = default(TOperator).RightShift(_x);
return true;
}
public void Reset() => throw new NotImplementedException();
}
}
static class Bits
{
public static Bits<byte, ByteBitOperator> New(ref byte x) => new Bits<byte, ByteBitOperator>(ref x);
public static Bits<_16Bytes, BitOperator16Bytes> New(ref _16Bytes x) => new Bits<_16Bytes, BitOperator16Bytes>(ref x);
}
class Program
{
static void Main()
{
// System.Numerics.Vector<byte> is a 16-byte struct.
var vector = new Vector<byte>();
// cast Vector<byte> to _16Bytes
ref _16Bytes bytes = ref Unsafe.As<Vector<byte>, _16Bytes>(ref vector);
var bits = Bits.New(ref bytes);
for (int i = 0; i < 128; i++) bits[i] = i % 2 == 1;
Console.WriteLine(vector);
Console.WriteLine(string.Join(", ", bits));
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
unsafe struct ByteBits : IEnumerable<bool>
{
// In the future, we'll be able to implement this in safe context with https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/ByReference.cs
void* _ptr;
public ByteBits(ref byte x) => _ptr = Unsafe.AsPointer(ref x);
public bool this[int index]
{
//todo: range check
get => (Unsafe.AsRef<byte>(_ptr) & (1 << index)) != 0;
set
{
if(value) Unsafe.AsRef<byte>(_ptr) |= (byte)(1 << index);
else Unsafe.AsRef<byte>(_ptr) &= (byte)~(1 << index);
}
}
Enumerator GetEnumerator() => new Enumerator(Unsafe.AsRef<byte>(_ptr));
IEnumerator<bool> IEnumerable<bool>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<bool>
{
int _i;
byte _x;
public Enumerator(byte x) => (_x, _i) = (x, 0);
public bool Current => (_x & 1) != 0;
object IEnumerator.Current => Current;
void IDisposable.Dispose() { }
public bool MoveNext()
{
if (_i == 0)
{
++_i;
return true;
}
if (_i >= 8) return false;
++_i;
_x >>= 1;
return true;
}
public void Reset() => throw new NotImplementedException();
}
}
class Program
{
static void Main()
{
byte b = 0;
var bits = new ByteBits(ref b);
Console.WriteLine(b); // 0
Console.WriteLine(string.Join(", ", bits));
b = 0b1100_1010;
Console.WriteLine(b); // 202
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = i % 2 == 1;
Console.WriteLine(b); // 170
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = true;
Console.WriteLine(b); // 255
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = i % 2 == 0;
Console.WriteLine(b); // 85
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = false;
Console.WriteLine(b); // 0
Console.WriteLine(string.Join(", ", bits));
}
}
// required packages:
// System.Runtime.CompilerServices.Unsafe
// System.ValueTuple
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace GenericBits
{
public unsafe struct Bits<T> : IEnumerable<bool>, IReadOnlyList<bool>
where T : struct
{
private static readonly int NumBits = Unsafe.SizeOf<T>() * 8;
private byte* _ptr;
public Bits(ref T x) => _ptr = (byte*)Unsafe.AsPointer(ref x);
const int Shift = 3;
const int Mask = 0b111;
public bool this[int index]
{
get
{
if (index >= NumBits) throw new IndexOutOfRangeException();
return (_ptr[index >> Shift] & (1 << (index & Mask))) != 0;
}
set
{
if (index >= NumBits) throw new IndexOutOfRangeException();
if (value) _ptr[index >> Shift] |= (byte)(1UL << (index & Mask));
else _ptr[index >> Shift] &= (byte)~(1 << (index & Mask));
}
}
public int Count => NumBits;
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<bool> IEnumerable<bool>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<bool>
{
private int _i;
private Bits<T> _bits;
public Enumerator(Bits<T> bits) => (_bits, _i) = (bits, -1);
public bool Current => _bits[_i];
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext() => ++_i < _bits.Count;
public void Reset() => _i = -1;
}
}
struct Sample1
{
public byte A;
public byte B;
public ushort C;
public uint D;
public Sample1(byte a, byte b, ushort c, uint d) => (A, B, C, D) = (a, b, c, d);
public override string ToString() => (A, B, C, D).ToString();
}
struct Sample2
{
public short A;
public int B;
public byte C;
public long D;
public Sample2(short a, int b, byte c, long d) => (A, B, C, D) = (a, b, c, d);
public override string ToString() => (A, B, C, D).ToString();
}
class Program
{
static void Main(string[] args)
{
Write((byte)0x12);
Write((short)0x1234);
Write(0x1234_5678);
Write(0x12345678abcdef0L);
Write(new Sample1(0x12, 0x34, 0x5678, 0x9abcdef0));
Write(new Sample2(0x1234, 0x5678_9abc, 0xde, 0x1234_5678_9abc_def0L));
}
static void Write<T>(T initialValue)
where T : struct
{
Console.WriteLine($"---- write bits for {typeof(T).Name} (size = {Unsafe.SizeOf<T>()}) ----");
var x = initialValue;
var bits = new Bits<T>(ref x);
Console.WriteLine($"bits: {bits.Count}");
Console.WriteLine(x);
WriteBits(bits);
for (int i = 0; i < bits.Count; i++) bits[i] = (i % 2) == 1;
Console.WriteLine(x);
WriteBits(bits);
for (int i = 0; i < bits.Count; i++) bits[i] = (i % 3) == 1;
Console.WriteLine(x);
WriteBits(bits);
}
static void WriteBits<T>(Bits<T> bits)
where T : struct
{
for (int i = 0; i < bits.Count; i++)
{
if ((i % 4 == 0 && i != 0)) Console.Write('_');
Console.Write(bits[i] ? '1' : '0');
}
Console.WriteLine();
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
interface CBitOperator<T>
{
int Size { get; }
bool GetBit(ref T x, int index);
void SetBit(ref T x, int index, bool value);
T RightShift(T x);
}
struct ByteBitOperator : CBitOperator<byte>
{
public int Size => 8;
public bool GetBit(ref byte x, int index)
{
if (index < 0 || index >= 8) throw new IndexOutOfRangeException();
return (x & (1 << index)) != 0;
}
public void SetBit(ref byte x, int index, bool value)
{
if (value) x |= (byte)(1 << index);
else x &= (byte)~(1 << index);
}
public byte RightShift(byte x) => (byte)(x >> 1);
}
unsafe struct Bits<T, TOperator> : IEnumerable<bool>
where T : struct // should be `blittable` in the future
where TOperator : CBitOperator<T>
{
// In the future, we'll be able to implement this in safe context with https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/ByReference.cs
void* _ptr;
public Bits(ref T x) => _ptr = Unsafe.AsPointer(ref x);
public bool this[int index]
{
get => default(TOperator).GetBit(ref Unsafe.AsRef<T>(_ptr), index);
set => default(TOperator).SetBit(ref Unsafe.AsRef<T>(_ptr), index, value);
}
public int Length => default(TOperator).Size;
Enumerator GetEnumerator() => new Enumerator(Unsafe.AsRef<T>(_ptr));
IEnumerator<bool> IEnumerable<bool>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<bool>
{
int _i;
T _x;
public Enumerator(T x) => (_x, _i) = (x, 0);
public bool Current => default(TOperator).GetBit(ref _x, 0);
object IEnumerator.Current => Current;
void IDisposable.Dispose() { }
public bool MoveNext()
{
if (_i == 0)
{
++_i;
return true;
}
if (_i >= 8) return false;
++_i;
_x = default(TOperator).RightShift(_x);
return true;
}
public void Reset() => throw new NotImplementedException();
}
}
static class Bits
{
public static Bits<byte, ByteBitOperator> New(ref byte x) => new Bits<byte, ByteBitOperator>(ref x);
}
class Program
{
static void Main()
{
byte b = 0;
var bits = Bits.New(ref b);
Console.WriteLine(b); // 0
Console.WriteLine(string.Join(", ", bits));
b = 0b1100_1010;
Console.WriteLine(b); // 202
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = i % 2 == 1;
Console.WriteLine(b); // 170
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = true;
Console.WriteLine(b); // 255
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = i % 2 == 0;
Console.WriteLine(b); // 85
Console.WriteLine(string.Join(", ", bits));
for (int i = 0; i < 8; i++) bits[i] = false;
Console.WriteLine(b); // 0
Console.WriteLine(string.Join(", ", bits));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment