public static class BinaryReaderExtensions
{
public static ushort ReadUInt16LE(this BinaryReader reader)
{
ArgumentNullException.ThrowIfNull(reader);
return reader.ReadUInt16();
}
public static uint ReadUInt32LE(this BinaryReader reader)
{
ArgumentNullException.ThrowIfNull(reader);
return reader.ReadUInt32();
}
public static ulong ReadUInt64LE(this BinaryReader reader)
{
ArgumentNullException.ThrowIfNull(reader);
return reader.ReadUInt64();
}
public static byte[] ReadBytesExact(this BinaryReader reader, int count)
{
ArgumentNullException.ThrowIfNull(reader);
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), "Byte count cannot be negative.");
}
var bytes = reader.ReadBytes(count);
if (bytes.Length != count)
{
throw new EndOfStreamException($"Expected {count} bytes but got {bytes.Length}.");
}
return bytes;
}
public static string ReadNullTerminatedAscii(this BinaryReader reader, int maxBytes)
{
ArgumentNullException.ThrowIfNull(reader);
if (maxBytes <= 0)
{
return string.Empty;
}
var stream = reader.BaseStream;
if (!stream.CanRead)
{
throw new IOException("The underlying stream is not readable.");
}
var buffer = new byte[maxBytes];
var index = 0;
while (index < maxBytes && stream.Position < stream.Length)
{
var b = reader.ReadByte();
if (b == 0)
{
break;
}
buffer[index++] = b;
}
return index == 0
? string.Empty
: Encoding.ASCII.GetString(buffer, 0, index);
}
public static string ReadFixedAscii(this BinaryReader reader, int count)
{
ArgumentNullException.ThrowIfNull(reader);
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), "Character count cannot be negative.");
}
if (count == 0)
{
return string.Empty;
}
var bytes = reader.ReadBytesExact(count);
var zero = Array.IndexOf(bytes, (byte)0);
return zero >= 0
? Encoding.ASCII.GetString(bytes, 0, zero)
: Encoding.ASCII.GetString(bytes);
}
public static string ReadUnicodeStringAt(byte[] data, int offset, int maxChars)
{
ArgumentNullException.ThrowIfNull(data);
if (offset < 0 || offset > data.Length)
{
return string.Empty;
}
if (maxChars <= 0 || offset == data.Length)
{
return string.Empty;
}
var availableBytes = data.Length - offset;
var readableChars = Math.Min(maxChars, availableBytes / 2);
if (readableChars <= 0)
{
return string.Empty;
}
var sb = new StringBuilder(readableChars);
for (var i = 0; i < readableChars; i++)
{
var currentOffset = offset + (i * 2);
BoundsChecker.EnsureRange(currentOffset, 2, data.Length, "unicode string read");
var ch = BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(currentOffset, 2));
if (ch == 0)
{
break;
}
sb.Append((char)ch);
}
return sb.ToString();
}
public static string ReadAsciiStringAt(byte[] data, int offset, int maxBytes)
{
ArgumentNullException.ThrowIfNull(data);
if (offset < 0 || offset >= data.Length)
{
return string.Empty;
}
if (maxBytes <= 0)
{
return string.Empty;
}
var availableBytes = data.Length - offset;
var readableBytes = Math.Min(maxBytes, availableBytes);
if (readableBytes <= 0)
{
return string.Empty;
}
var end = offset;
var limit = offset + readableBytes;
while (end < limit && data[end] != 0)
{
end++;
}
return end == offset
? string.Empty
: Encoding.ASCII.GetString(data, offset, end - offset);
}
}