Last active
August 5, 2022 13:51
-
-
Save nathan130200/77b6b462e264bd656dbe81754fa00ae4 to your computer and use it in GitHub Desktop.
T4 templates for stream I/O + Minecraft stream I/O utilities.
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.Runtime.CompilerServices; | |
using System.Text; | |
namespace CraftDedicatedServer; | |
public static class Util | |
{ | |
const int SEGMENT_BITS = 0x7f; | |
const int CONTINUE_BIT = 0x80; | |
const int MAX_STRING_LEN = short.MaxValue - 3; | |
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | |
public static void Rsh<S, U>(this ref S value, U bits) | |
where S : struct | |
where U : struct | |
{ | |
dynamic _value = value; | |
dynamic _bits = (S)((dynamic)bits); | |
value = ((S)((U)_value >> _bits)); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | |
public static void Lsh<S, U>(this ref S value, U bits) | |
where S : struct | |
where U : struct | |
{ | |
dynamic _value = value; | |
dynamic _bits = (S)((dynamic)bits); | |
value = ((S)((U)_value << _bits)); | |
} | |
public static bool TryReadByte(this Stream s, out byte result) | |
{ | |
result = default; | |
int current = s.ReadByte(); | |
if (current != -1) | |
result = (byte)current; | |
return current != -1; | |
} | |
public static int ReadVarInt(this Stream stream) | |
{ | |
int value = 0; | |
int position = 0; | |
while (true) | |
{ | |
if (!stream.TryReadByte(out byte currentByte)) | |
throw new IOException("End of stream."); | |
value |= (currentByte & SEGMENT_BITS) << position; | |
if ((currentByte & CONTINUE_BIT) == 0) | |
break; | |
position += 7; | |
if (position >= 32) | |
throw new IOException("VarInt is too big."); | |
} | |
return value; | |
} | |
public static void WriteVarInt(this Stream stream, int value) | |
{ | |
while (true) | |
{ | |
if ((value & ~SEGMENT_BITS) == 0) | |
{ | |
stream.WriteByte((byte)value); | |
return; | |
} | |
stream.WriteByte((byte)((value & SEGMENT_BITS) | CONTINUE_BIT)); | |
value.Rsh(7U); | |
} | |
} | |
public static long ReadVarLong(this Stream stream) | |
{ | |
long value = 0; | |
int position = 0; | |
while (true) | |
{ | |
if (!stream.TryReadByte(out byte currentByte)) | |
throw new IOException("End of stream."); | |
value |= (long)(currentByte & SEGMENT_BITS) << position; | |
if ((currentByte & CONTINUE_BIT) == 0) | |
break; | |
position += 7; | |
if (position >= 64) | |
throw new IOException("VarLong is too big."); | |
} | |
return value; | |
} | |
public static void WriteVarLong(this Stream stream, long value) | |
{ | |
while (true) | |
{ | |
if ((value & ~((long)SEGMENT_BITS)) == 0) | |
{ | |
stream.WriteByte((byte)value); | |
return; | |
} | |
stream.WriteByte((byte)((value & SEGMENT_BITS) | CONTINUE_BIT)); | |
value.Rsh(7UL); | |
} | |
} | |
public static string ReadPrefixedString(this Stream stream) | |
{ | |
int size = stream.ReadVarInt(); | |
var buff = new byte[size]; | |
if (stream.Read(buff) != size) | |
throw new IOException("Cannot read varsized string from stream."); | |
return Encoding.UTF8.GetString(buff, 0, size); | |
} | |
public static void WritePrefixedString(this Stream stream, string text) | |
{ | |
if (text.Length > MAX_STRING_LEN) | |
text = text.Substring(0, MAX_STRING_LEN); | |
stream.WriteVarInt(text.Length); | |
stream.Write(Encoding.UTF8.GetBytes(text)); | |
} | |
// ------------------------------------------------------------------------------------------------ | |
// Async versions | |
// ------------------------------------------------------------------------------------------------ | |
public static Task<int> ReadVarIntAsync(this Stream stream, CancellationToken token = default) | |
=> Task.Run(() => stream.ReadVarInt()); | |
public static Task WriteVarIntAsync(this Stream stream, int value, CancellationToken token = default) | |
=> Task.Run(() => stream.WriteVarInt(value), token); | |
public static Task<long> ReadVarLongAsync(this Stream stream, CancellationToken token = default) | |
=> Task.Run(() => stream.ReadVarLong(), token); | |
public static Task WriteVarLongAsync(this Stream stream, long value, CancellationToken token = default) | |
=> Task.Run(() => stream.ReadVarLong(), token); | |
public static Task<string> ReadPrefixedStringAsync(this Stream stream, CancellationToken token = default) | |
=> Task.Run(() => stream.ReadPrefixedString(), token); | |
public static void WritePrefixedStringAsync(this Stream stream, string text, CancellationToken token = default) | |
=> Task.Run(() => stream.WritePrefixedString(text), token); | |
} |
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
#pragma warning disable | |
using System; | |
namespace System.IO; | |
public static class StreamUtil | |
{ | |
public static short ReadInt16(this Stream stream) | |
{ | |
const int size = 2; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read 'System.Int16' from stream."); | |
return BitConverter.ToInt16(buff, 0); | |
} | |
public static async Task<short> ReadInt16Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = 2; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read 'System.Int16' from stream."); | |
return BitConverter.ToInt16(buff, 0); | |
} | |
public static void WriteInt16(this Stream stream, short value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task WriteInt16Async(this Stream stream, short value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
public static int ReadInt32(this Stream stream) | |
{ | |
const int size = 4; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read 'System.Int32' from stream."); | |
return BitConverter.ToInt32(buff, 0); | |
} | |
public static async Task<int> ReadInt32Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = 4; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read 'System.Int32' from stream."); | |
return BitConverter.ToInt32(buff, 0); | |
} | |
public static void WriteInt32(this Stream stream, int value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task WriteInt32Async(this Stream stream, int value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
public static long ReadInt64(this Stream stream) | |
{ | |
const int size = 8; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read 'System.Int64' from stream."); | |
return BitConverter.ToInt64(buff, 0); | |
} | |
public static async Task<long> ReadInt64Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = 8; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read 'System.Int64' from stream."); | |
return BitConverter.ToInt64(buff, 0); | |
} | |
public static void WriteInt64(this Stream stream, long value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task WriteInt64Async(this Stream stream, long value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
public static ushort ReadUInt16(this Stream stream) | |
{ | |
const int size = 2; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read 'System.UInt16' from stream."); | |
return BitConverter.ToUInt16(buff, 0); | |
} | |
public static async Task<ushort> ReadUInt16Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = 2; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read 'System.UInt16' from stream."); | |
return BitConverter.ToUInt16(buff, 0); | |
} | |
public static void WriteUInt16(this Stream stream, ushort value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task WriteUInt16Async(this Stream stream, ushort value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
public static uint ReadUInt32(this Stream stream) | |
{ | |
const int size = 4; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read 'System.UInt32' from stream."); | |
return BitConverter.ToUInt32(buff, 0); | |
} | |
public static async Task<uint> ReadUInt32Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = 4; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read 'System.UInt32' from stream."); | |
return BitConverter.ToUInt32(buff, 0); | |
} | |
public static void WriteUInt32(this Stream stream, uint value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task WriteUInt32Async(this Stream stream, uint value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
public static ulong ReadUInt64(this Stream stream) | |
{ | |
const int size = 8; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read 'System.UInt64' from stream."); | |
return BitConverter.ToUInt64(buff, 0); | |
} | |
public static async Task<ulong> ReadUInt64Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = 8; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read 'System.UInt64' from stream."); | |
return BitConverter.ToUInt64(buff, 0); | |
} | |
public static void WriteUInt64(this Stream stream, ulong value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task WriteUInt64Async(this Stream stream, ulong value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
} | |
#pragma warning restore |
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
<#@ template language="C#" #> | |
<#@ output extension=".cs" #> | |
<#@ import namespace="System" #> | |
#pragma warning disable | |
using System; | |
namespace System.IO; | |
public static class StreamUtil | |
{ | |
<# | |
var Types = new(string alias, Type type, int len)[] | |
{ | |
new("short", typeof(Int16), 2), | |
new("int", typeof(Int32), 4), | |
new("long", typeof(Int64), 8), | |
new("ushort", typeof(UInt16), 2), | |
new("uint", typeof(UInt32), 4), | |
new("ulong", typeof(UInt64), 8), | |
}; | |
foreach(var t in Types){ #> | |
public static <#= t.alias #> Read<#= t.type.Name #>(this Stream stream) | |
{ | |
const int size = <#= t.len #>; | |
var buff = new byte[size]; | |
if(stream.Read(buff) != size) | |
throw new IOException("Cannot read '<#= t.type.FullName #>' from stream."); | |
return BitConverter.To<#= t.type.Name #>(buff, 0); | |
} | |
public static async Task<<#= t.alias #>> Read<#= t.type.Name #>Async(this Stream stream, CancellationToken token = default) | |
{ | |
const int size = <#= t.len #>; | |
var buff = new byte[size]; | |
if(await stream.ReadAsync(buff, token) != size) | |
throw new IOException("Cannot read '<#= t.type.FullName #>' from stream."); | |
return BitConverter.To<#= t.type.Name #>(buff, 0); | |
} | |
public static void Write<#= t.type.Name #>(this Stream stream, <#= t.alias #> value) | |
=> stream.Write(BitConverter.GetBytes(value)); | |
public static async Task Write<#= t.type.Name #>Async(this Stream stream, <#= t.alias #> value, CancellationToken token = default) | |
=> await stream.WriteAsync(BitConverter.GetBytes(value), token); | |
<# | |
} | |
#> | |
} | |
#pragma warning restore |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment