-
-
Save pedoc/8f1d2cf4d7e4b70f135f7966f44e227a to your computer and use it in GitHub Desktop.
A bit accessor for `byte`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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