Last active
July 8, 2024 10:39
-
-
Save timyhac/085963dcd6217528f0b4e99bd1f56fb7 to your computer and use it in GitHub Desktop.
A helper class for parsing binary structures that are Big-Endian encoded (as is common for network protocols).
This file contains hidden or 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 static System.Buffers.Binary.BinaryPrimitives; | |
namespace BinaryStruct | |
{ | |
public ref struct Reader | |
{ | |
public enum Endian | |
{ | |
Little, | |
Big | |
} | |
private readonly ReadOnlySpan<byte> _buffer; | |
public Reader(ReadOnlySpan<byte> buffer) | |
{ | |
_buffer = buffer; | |
Cursor = 0; | |
} | |
public int Cursor { get; set; } | |
public int Length => _buffer.Length; | |
public Endian Endianess { get; set; } = Endian.Big; | |
public static implicit operator Reader(byte[] buffer) => new Reader(buffer); | |
public void Skip(int n) | |
{ | |
Cursor += n; | |
} | |
public bool Take(int n, out ReadOnlySpan<byte> value, int? cursor = default) | |
{ | |
value = default; | |
Cursor = (cursor ?? Cursor) + n; | |
if (Cursor > Length) | |
return false; | |
value = _buffer.Slice(Cursor - n, n); | |
return true; | |
} | |
public bool U8(out byte value, int? cursor = default) | |
{ | |
value = default; | |
Cursor = (cursor ?? Cursor) + 1; | |
if (Cursor > Length) | |
return false; | |
value = _buffer[Cursor - 1]; | |
return true; | |
} | |
public bool U16(out ushort value, int? cursor = default, Endian? endianness = default) | |
{ | |
value = default; | |
Cursor = (cursor ?? Cursor) + 2; | |
if (Cursor > Length) | |
return false; | |
var slice = _buffer.Slice(Cursor - 2, 2); | |
value = (endianness ?? Endianess) switch | |
{ | |
Endian.Little => ReadUInt16LittleEndian(slice), | |
Endian.Big => ReadUInt16BigEndian(slice), | |
}; | |
return true; | |
} | |
public bool U32(out uint value, int? cursor = default, Endian? endianness = default) | |
{ | |
value = default; | |
Cursor = (cursor ?? Cursor) + 4; | |
if (Cursor > Length) | |
return false; | |
var slice = _buffer.Slice(Cursor - 4, 4); | |
value = (endianness ?? Endianess) switch | |
{ | |
Endian.Little => ReadUInt32LittleEndian(slice), | |
Endian.Big => ReadUInt32BigEndian(slice), | |
}; | |
return true; | |
} | |
} | |
} | |
namespace BinaryStruct.Tests | |
{ | |
public class ReaderTests | |
{ | |
[Fact] | |
public void Test1() | |
{ | |
var reader = new Reader([0x00, 0x01, 0x02, 0x03]); | |
reader.U8(out var zero); | |
reader.U8(out var one); | |
reader.U8(out var two); | |
reader.U8(out var three); | |
Assert.Equal(0, zero); | |
Assert.Equal(1, one); | |
Assert.Equal(2, two); | |
Assert.Equal(3, three); | |
} | |
[Fact] | |
public void Test2() | |
{ | |
var reader = new Reader([0x00, 0x01, 0x02, 0x03]); | |
reader.U16(out var x0001); | |
reader.U16(out var x0203); | |
Assert.Equal(0x00_01, x0001); | |
Assert.Equal(0x02_03, x0203); | |
} | |
[Fact] | |
public void Test3() | |
{ | |
var reader = new Reader([0x00, 0x01, 0x02, 0x03]); | |
reader.U32(out var x00010203); | |
Assert.Equal((uint)0x00_01_02_03, x00010203); | |
} | |
[Fact] | |
public void Test4() | |
{ | |
var reader = new Reader([0x00, 0x01, 0x02, 0x03]); | |
reader.Skip(1); | |
reader.Take(2, out var slice); | |
byte[] expected = [0x01, 0x02]; | |
Assert.Equal(expected, slice.ToArray()); | |
} | |
[Fact] | |
public void Test5() | |
{ | |
var reader = new Reader([0x00, 0x01, 0x02, 0x03]); | |
reader.Cursor += 4; | |
var success = reader.U8(out _); | |
Assert.False(success); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment