using System.Text;
static class StreamExtensions
{
///
/// Read a signed 8-bit integer from the stream.
///
///
///
public static sbyte ReadInt8(this Stream s) => unchecked((sbyte)s.ReadUInt8());
///
/// Read an unsigned 8-bit integer from the stream.
///
///
///
public static byte ReadUInt8(this Stream s)
{
byte ret;
byte[] tmp = new byte[1];
s.Read(tmp, 0, 1);
ret = tmp[0];
return ret;
}
///
/// Read an unsigned 16-bit little-endian integer from the stream.
///
///
///
public static ushort ReadUInt16LE(this Stream s) => unchecked((ushort)s.ReadInt16LE());
///
/// Read a signed 16-bit little-endian integer from the stream.
///
///
///
public static short ReadInt16LE(this Stream s)
{
int ret;
byte[] tmp = new byte[2];
s.Read(tmp, 0, 2);
ret = tmp[0] & 0x00FF;
ret |= (tmp[1] << 8) & 0xFF00;
return (short)ret;
}
public static void WriteInt16LE(this Stream s, short i)
{
s.WriteUInt16LE(unchecked((ushort)i));
}
public static void WriteUInt16LE(this Stream s, ushort i)
{
byte[] tmp = new byte[2];
tmp[0] = (byte)(i & 0xFF);
tmp[1] = (byte)((i >> 8) & 0xFF);
s.Write(tmp, 0, 2);
}
public static void WriteUInt16BE(this Stream s, ushort i)
{
byte[] tmp = new byte[2];
tmp[0] = (byte)((i >> 8) & 0xFF);
tmp[1] = (byte)(i & 0xFF);
s.Write(tmp, 0, 2);
}
///
/// Read an unsigned 16-bit Big-endian integer from the stream.
///
///
///
public static ushort ReadUInt16BE(this Stream s) => unchecked((ushort)s.ReadInt16BE());
///
/// Read a signed 16-bit Big-endian integer from the stream.
///
///
///
public static short ReadInt16BE(this Stream s)
{
int ret;
byte[] tmp = new byte[2];
s.Read(tmp, 0, 2);
ret = (tmp[0] << 8) & 0xFF00;
ret |= tmp[1] & 0x00FF;
return (short)ret;
}
public static void WriteUInt24LE(this Stream s, uint i)
{
byte[] tmp = new byte[3];
tmp[0] = (byte)(i & 0xFF);
tmp[1] = (byte)((i >> 8) & 0xFF);
tmp[2] = (byte)((i >> 16) & 0xFF);
s.Write(tmp, 0, 3);
}
public static void WriteUInt24BE(this Stream s, uint i)
{
byte[] tmp = new byte[3];
tmp[2] = (byte)(i & 0xFF);
tmp[1] = (byte)((i >> 8) & 0xFF);
tmp[0] = (byte)((i >> 16) & 0xFF);
s.Write(tmp, 0, 3);
}
///
/// Read an unsigned 24-bit little-endian integer from the stream.
///
///
///
public static uint ReadUInt24LE(this Stream s)
{
int ret;
byte[] tmp = new byte[3];
s.Read(tmp, 0, 3);
ret = tmp[0] & 0x0000FF;
ret |= (tmp[1] << 8) & 0x00FF00;
ret |= (tmp[2] << 16) & 0xFF0000;
return unchecked((uint)ret);
}
///
/// Read a signed 24-bit little-endian integer from the stream.
///
///
///
public static int ReadInt24LE(this Stream s)
{
int ret;
byte[] tmp = new byte[3];
s.Read(tmp, 0, 3);
ret = tmp[0] & 0x0000FF;
ret |= (tmp[1] << 8) & 0x00FF00;
ret |= (tmp[2] << 16) & 0xFF0000;
if ((tmp[2] & 0x80) == 0x80)
{
ret |= 0xFF << 24;
}
return ret;
}
///
/// Read an unsigned 24-bit Big-endian integer from the stream.
///
///
///
public static uint ReadUInt24BE(this Stream s)
{
int ret;
byte[] tmp = new byte[3];
s.Read(tmp, 0, 3);
ret = tmp[2] & 0x0000FF;
ret |= (tmp[1] << 8) & 0x00FF00;
ret |= (tmp[0] << 16) & 0xFF0000;
return (uint)ret;
}
///
/// Read a signed 24-bit Big-endian integer from the stream.
///
///
///
public static int ReadInt24BE(this Stream s)
{
int ret;
byte[] tmp = new byte[3];
s.Read(tmp, 0, 3);
ret = tmp[2] & 0x0000FF;
ret |= (tmp[1] << 8) & 0x00FF00;
ret |= (tmp[0] << 16) & 0xFF0000;
if ((tmp[0] & 0x80) == 0x80)
{
ret |= 0xFF << 24; // sign-extend
}
return ret;
}
///
/// Read an unsigned 32-bit little-endian integer from the stream.
///
///
///
public static uint ReadUInt32LE(this Stream s) => unchecked((uint)s.ReadInt32LE());
///
/// Read a signed 32-bit little-endian integer from the stream.
///
///
///
public static int ReadInt32LE(this Stream s)
{
int ret;
byte[] tmp = new byte[4];
s.Read(tmp, 0, 4);
ret = tmp[0] & 0x000000FF;
ret |= (tmp[1] << 8) & 0x0000FF00;
ret |= (tmp[2] << 16) & 0x00FF0000;
ret |= (tmp[3] << 24);
return ret;
}
public static void WriteInt32LE(this Stream s, int i)
{
s.WriteUInt32LE(unchecked((uint)i));
}
public static void WriteUInt32LE(this Stream s, uint i)
{
byte[] tmp = new byte[4];
tmp[0] = (byte)(i & 0xFF);
tmp[1] = (byte)((i >> 8) & 0xFF);
tmp[2] = (byte)((i >> 16) & 0xFF);
tmp[3] = (byte)((i >> 24) & 0xFF);
s.Write(tmp, 0, 4);
}
public static void WriteUInt32BE(this Stream s, uint i)
{
byte[] tmp = new byte[4];
tmp[3] = (byte)(i & 0xFF);
tmp[2] = (byte)((i >> 8) & 0xFF);
tmp[1] = (byte)((i >> 16) & 0xFF);
tmp[0] = (byte)((i >> 24) & 0xFF);
s.Write(tmp, 0, 4);
}
///
/// Read an unsigned 32-bit Big-endian integer from the stream.
///
///
///
public static uint ReadUInt32BE(this Stream s) => unchecked((uint)s.ReadInt32BE());
///
/// Read a signed 32-bit Big-endian integer from the stream.
///
///
///
public static int ReadInt32BE(this Stream s)
{
int ret;
byte[] tmp = new byte[4];
s.Read(tmp, 0, 4);
ret = (tmp[0] << 24);
ret |= (tmp[1] << 16) & 0x00FF0000;
ret |= (tmp[2] << 8) & 0x0000FF00;
ret |= tmp[3] & 0x000000FF;
return ret;
}
///
/// Read an unsigned 64-bit little-endian integer from the stream.
///
///
///
public static ulong ReadUInt64LE(this Stream s) => unchecked((ulong)s.ReadInt64LE());
///
/// Read a signed 64-bit little-endian integer from the stream.
///
///
///
public static long ReadInt64LE(this Stream s)
{
long ret;
byte[] tmp = new byte[8];
s.Read(tmp, 0, 8);
ret = tmp[4] & 0x000000FFL;
ret |= (tmp[5] << 8) & 0x0000FF00L;
ret |= (tmp[6] << 16) & 0x00FF0000L;
ret |= (tmp[7] << 24) & 0xFF000000L;
ret <<= 32;
ret |= tmp[0] & 0x000000FFL;
ret |= (tmp[1] << 8) & 0x0000FF00L;
ret |= (tmp[2] << 16) & 0x00FF0000L;
ret |= (tmp[3] << 24) & 0xFF000000L;
return ret;
}
public static void WriteInt64LE(this Stream s, long i)
{
s.WriteUInt64LE(unchecked((ulong)i));
}
public static void WriteUInt64LE(this Stream s, ulong i)
{
byte[] tmp = new byte[8];
tmp[0] = (byte)(i & 0xFF);
tmp[1] = (byte)((i >> 8) & 0xFF);
tmp[2] = (byte)((i >> 16) & 0xFF);
tmp[3] = (byte)((i >> 24) & 0xFF);
i >>= 32;
tmp[4] = (byte)(i & 0xFF);
tmp[5] = (byte)((i >> 8) & 0xFF);
tmp[6] = (byte)((i >> 16) & 0xFF);
tmp[7] = (byte)((i >> 24) & 0xFF);
s.Write(tmp, 0, 8);
}
///
/// Read an unsigned 64-bit big-endian integer from the stream.
///
///
///
public static ulong ReadUInt64BE(this Stream s) => unchecked((ulong)s.ReadInt64BE());
///
/// Read a signed 64-bit big-endian integer from the stream.
///
///
///
public static long ReadInt64BE(this Stream s)
{
long ret;
byte[] tmp = new byte[8];
s.Read(tmp, 0, 8);
ret = tmp[3] & 0x000000FFL;
ret |= (tmp[2] << 8) & 0x0000FF00L;
ret |= (tmp[1] << 16) & 0x00FF0000L;
ret |= (tmp[0] << 24) & 0xFF000000L;
ret <<= 32;
ret |= tmp[7] & 0x000000FFL;
ret |= (tmp[6] << 8) & 0x0000FF00L;
ret |= (tmp[5] << 16) & 0x00FF0000L;
ret |= (tmp[4] << 24) & 0xFF000000L;
return ret;
}
///
/// Reads a multibyte value of the specified length from the stream.
///
/// The stream
/// Must be less than or equal to 8
///
public static long ReadMultibyteBE(this Stream s, byte bytes)
{
if (bytes > 8) return 0;
long ret = 0;
var b = s.ReadBytes(bytes);
for (uint i = 0; i < b.Length; i++)
{
ret <<= 8;
ret |= b[i];
}
return ret;
}
///
/// Read a single-precision (4-byte) floating-point value from the stream.
///
///
///
public static float ReadFloat(this Stream s)
{
byte[] tmp = new byte[4];
s.Read(tmp, 0, 4);
return BitConverter.ToSingle(tmp, 0);
}
///
/// Read a half-precision (2-byte) floating point value from the stream.
/// Return value is aliased to a single precision float because C# does not support half floats.
///
///
///
public static float ReadHalfFloat(this Stream s) => ParseHalfFloat(s.ReadUInt16LE());
unsafe public static float ParseHalfFloat(int half)
{
int sign = half >> 15;
int exponent = ((half >> 10) & 0x1F);
int mantissa = half & 0x03FF;
int single;
if (exponent == 0)
{
// Subnormal
if (mantissa == 0) return 0;
int exp = -15;
int mask = 0x3FF;
// Find the first leading 1.
while (mantissa == (mantissa & mask))
{
mask >>= 1;
exp--;
}
// AND the mantissa with the mask because the SP float is *not* subnormal and has an implied "1."
single = (sign << 31) | (((128 + exp) & 0xFF) << 23) | ((mantissa & mask) << (30 + exp));
}
else
{
single = exponent == 31 ?
// Infinity
(mantissa == 0 ? (sign << 31) | (0xFF << 23)
// NaN
: (sign << 31) | (0xFF << 23) | 1)
// Normal
: (sign << 31) | (((exponent + 112) & 0xFF) << 23) | (mantissa << 13);
}
// TODO: Any other option besides unsafe code or allocating an unnecessary byte array?
return *(float*)(&single);
/* Eek, unsafe, but BitConverter.ToSingle is also unsafe according to reference source,
and it requires allocating another byte array and multiple method calls... */
// return BitConverter.ToSingle(BitConverter.GetBytes(single), 0);
}
///
/// Read a null-terminated ASCII string from the given stream.
///
///
///
public static string ReadASCIINullTerminated(this Stream s, int limit = -1)
{
StringBuilder sb = new StringBuilder(255);
char cur;
while ((limit == -1 || sb.Length < limit) && (cur = (char)s.ReadByte()) != 0)
{
sb.Append(cur);
}
return sb.ToString();
}
///
/// Read a null-terminated UTF8 string from the given stream.
///
///
///
public static string ReadFixedLengthNullTerminatedString(this Stream s, int length)
{
var stringBytes = s.ReadBytes(length);
var endIdx = 0;
for (int i = 0; i < stringBytes.Length; i++)
{
endIdx = i;
if (stringBytes[i] == 0)
break;
}
return Encoding.UTF8.GetString(stringBytes, 0, endIdx);
}
public static string ReadFixedLengthString(this Stream s, int length)
{
var stringBytes = s.ReadBytes(length);
return Encoding.UTF8.GetString(stringBytes, 0, stringBytes.Length);
}
///
/// Read a length-prefixed string of the specified encoding type from the file.
/// The length is a 32-bit little endian integer.
///
///
/// The encoding to use to decode the string.
///
public static string ReadLengthPrefixedString(this Stream s, Encoding e, bool bigEdn = false)
{
int length = bigEdn ? s.ReadInt32BE() : s.ReadInt32LE();
byte[] chars = new byte[length];
s.Read(chars, 0, length);
return e.GetString(chars);
}
///
/// Read a length-prefixed UTF-8 string from the given stream.
///
///
///
public static string ReadLengthUTF8(this Stream s, bool bigEdn = false)
{
return s.ReadLengthPrefixedString(Encoding.UTF8, bigEdn);
}
///
/// Read a given number of bytes from a stream into a new byte array.
///
///
/// Number of bytes to read (maximum)
/// New byte array of size <=count.
public static byte[] ReadBytes(this Stream s, int count)
{
// Size of returned array at most count, at least difference between position and length.
int realCount = (int)((s.Position + count > s.Length) ? (s.Length - s.Position) : count);
byte[] ret = new byte[realCount];
s.Read(ret, 0, realCount);
return ret;
}
///
/// Read a variable-length integral value as found in MIDI messages.
///
///
///
public static uint ReadMidiMultiByte(this Stream s)
{
int ret = 0;
byte b = (byte)(s.ReadByte());
ret += b & 0x7f;
if (0x80 == (b & 0x80))
{
ret <<= 7;
b = (byte)(s.ReadByte());
ret += b & 0x7f;
if (0x80 == (b & 0x80))
{
ret <<= 7;
b = (byte)(s.ReadByte());
ret += b & 0x7f;
if (0x80 == (b & 0x80))
{
ret <<= 7;
b = (byte)(s.ReadByte());
ret += b & 0x7f;
if (0x80 == (b & 0x80))
throw new InvalidDataException("Variable-length MIDI number > 4 bytes");
}
}
}
return (uint)ret;
}
public static void WriteMidiMultiByte(this Stream s, uint i)
{
if (i > 0x7FU)
{
int max = 7;
while ((i >> max) > 0x7FU) max += 7;
while (max > 0)
{
s.WriteByte((byte)(((i >> max) & 0x7FU) | 0x80));
max -= 7;
}
}
s.WriteByte((byte)(i & 0x7FU));
}
public static void WriteLE(this Stream s, ushort i) => s.WriteUInt16LE(i);
public static void WriteLE(this Stream s, uint i) => s.WriteUInt32LE(i);
public static void WriteLE(this Stream s, ulong i) => s.WriteUInt64LE(i);
public static void WriteLE(this Stream s, short i) => s.WriteInt16LE(i);
public static void WriteLE(this Stream s, int i) => s.WriteInt32LE(i);
public static void WriteLE(this Stream s, long i) => s.WriteInt64LE(i);
public static ushort FlipEndian(this ushort i) => (ushort)((i & 0xFFU) << 8 | (i & 0xFF00U) >> 8);
public static uint FlipEndian(this uint i) => (i & 0x000000FFU) << 24 | (i & 0x0000FF00U) << 8 |
(i & 0x00FF0000U) >> 8 | (i & 0xFF000000U) >> 24;
public static ulong FlipEndian(this ulong i) => (i & 0x00000000000000FFUL) << 56 | (i & 0x000000000000FF00UL) << 40 |
(i & 0x0000000000FF0000UL) << 24 | (i & 0x00000000FF000000UL) << 8 |
(i & 0x000000FF00000000UL) >> 8 | (i & 0x0000FF0000000000UL) >> 24 |
(i & 0x00FF000000000000UL) >> 40 | (i & 0xFF00000000000000UL) >> 56;
public static short FlipEndian(this short i) => unchecked((short)((ushort)i).FlipEndian());
public static int FlipEndian(this int i) => unchecked((int)((uint)i).FlipEndian());
public static long FlipEndian(this long i) => unchecked((long)((ulong)i).FlipEndian());
public static void WriteBE(this Stream s, ushort i) => s.WriteUInt16LE(i.FlipEndian());
public static void WriteBE(this Stream s, uint i) => s.WriteUInt32LE(i.FlipEndian());
public static void WriteBE(this Stream s, ulong i) => s.WriteUInt64LE(i.FlipEndian());
public static void WriteBE(this Stream s, short i) => s.WriteInt16LE(i.FlipEndian());
public static void WriteBE(this Stream s, int i) => s.WriteInt32LE(i.FlipEndian());
public static void WriteBE(this Stream s, long i) => s.WriteInt64LE(i.FlipEndian());
///
/// Write a signed 8-bit integer to the stream.
///
///
/// The integer to write.
public static void WriteInt8(this Stream s, sbyte int8) => s.WriteUInt8((byte)int8);
///
/// Write an unsigned 8-bit integer to the stream.
///
///
/// The integer to write.
public static void WriteUInt8(this Stream s, byte uint8)
{
byte[] tmp = new byte[1] { uint8 };
s.Write(tmp, 0, 1);
}
///
/// Write a length-prefixed string of the specified encoding type to the file.
/// The length is a 32-bit little endian integer.
///
///
/// The encoding to use to decode the string.
/// The string to write.
public static void WriteLengthPrefixedString(this Stream s, Encoding e, string str, bool bigEdn = false)
{
int byteCount = e.GetByteCount(str);
if (bigEdn)
s.WriteUInt32BE((uint)byteCount);
else
s.WriteInt32LE(byteCount);
byte[] chars = e.GetBytes(str);
s.Write(chars, 0, byteCount);
}
///
/// Write a length-prefixed UTF-8 string to the given stream.
///
///
/// The string to write.
public static void WriteLengthUTF8(this Stream s, string str, bool bigEdn = false)
{
s.WriteLengthPrefixedString(Encoding.UTF8, str, bigEdn);
}
///
/// Write a single-precision (4-byte) floating-point value to the stream.
///
///
/// The floating point value to write.
public static void WriteFloat(this Stream s, float flt)
{
byte[] tmp = BitConverter.GetBytes(flt);
s.Write(tmp, 0, 4);
}
}