using System; using static Sandbox.IByteParsable; namespace TestSystem; public class MyDataClass : IByteParsable { public string DataContents; public static object ReadObject( ref ByteStream stream, ByteParseOptions o ) => Read( ref stream, o ); public static void WriteObject( ref ByteStream stream, object value, ByteParseOptions o ) => Write( ref stream, value as MyDataClass, o ); public static MyDataClass Read( ref ByteStream stream, ByteParseOptions o ) { if ( stream.Read() == 0 ) return default; var value = new MyDataClass(); value.DataContents = stream.Read(); return value; } public static void Write( ref ByteStream stream, MyDataClass value, ByteParseOptions o ) { if ( value is null ) { stream.Write( (byte)0 ); return; } stream.Write( (byte)1 ); stream.Write( value.DataContents ); } } public class MyDataClassChild : MyDataClass { } public struct MyPlainOldDataClass : IByteParsable { public Vector3 Position; public static object ReadObject( ref ByteStream stream, ByteParseOptions o ) => Read( ref stream, o ); public static void WriteObject( ref ByteStream stream, object value, ByteParseOptions o ) => Write( ref stream, (MyPlainOldDataClass)value, o ); public static MyPlainOldDataClass Read( ref ByteStream stream, ByteParseOptions o ) { var value = new MyPlainOldDataClass(); value.Position = stream.Read(); return value; } public static void Write( ref ByteStream stream, MyPlainOldDataClass value, ByteParseOptions o ) { stream.Write( value.Position ); } } [TestClass] public class ByteStreamTest { [TestMethod] public void WriterBasic() { var s = ByteStream.Create( 512 ); s.Write( (int)32 ); Assert.AreEqual( 4, s.Position ); s.Write( (uint)32 ); Assert.AreEqual( 8, s.Position ); s.Write( (short)32 ); Assert.AreEqual( 10, s.Position ); s.Write( (ushort)32 ); Assert.AreEqual( 12, s.Position ); s.Write( (long)32 ); Assert.AreEqual( 20, s.Position ); s.Write( (ulong)32 ); Assert.AreEqual( 28, s.Position ); s.Write( Vector2.One ); Assert.AreEqual( 36, s.Position ); s.Write( Vector3.One ); Assert.AreEqual( 48, s.Position ); s.Write( Vector4.One ); Assert.AreEqual( 64, s.Position ); s.Write( Transform.Zero ); s.Write( "hello there this is a big string" ); Console.WriteLine( $"Written {s.Position}b" ); } [TestMethod] public void ReadWriteInt() { var s = ByteStream.Create( 512 ); s.Write( 32 ); s.Write( 64 ); s.Write( 512 ); var data = s.ToArray(); s.Dispose(); s = ByteStream.CreateReader( data ); Assert.AreEqual( 32, s.Read() ); Assert.AreEqual( 64, s.Read() ); Assert.AreEqual( 512, s.Read() ); } [TestMethod] public void ReadWriteFloat() { var s = ByteStream.Create( 512 ); s.Write( 32.0f ); s.Write( 64.0f ); s.Write( 512.0f ); var data = s.ToArray(); s.Dispose(); s = ByteStream.CreateReader( data ); Assert.AreEqual( 32.0f, s.Read() ); Assert.AreEqual( 64.0f, s.Read() ); Assert.AreEqual( 512.0f, s.Read() ); } [TestMethod] public void ReadWriteVector3() { var s = ByteStream.Create( 512 ); s.Write( new Vector3( 4, 5, 1 ) ); s.Write( new Vector3( 44, 1235, 5121 ) ); s.Write( new Vector3( 0, 0, 0 ) ); var data = s.ToArray(); s.Dispose(); s = ByteStream.CreateReader( data ); Assert.AreEqual( new Vector3( 4, 5, 1 ), s.Read() ); Assert.AreEqual( new Vector3( 44, 1235, 5121 ), s.Read() ); Assert.AreEqual( new Vector3( 0, 0, 0 ), s.Read() ); } [TestMethod] public void ReadWriteByteParsable() { var s = ByteStream.Create( 512 ); var a = new MyDataClass(); a.DataContents = "Hello There Happy Birthday Superman"; s.Write( a ); var data = s.ToArray(); Assert.AreNotEqual( 0, data.Length ); s.Dispose(); s = ByteStream.CreateReader( data ); var b = s.Read(); Assert.AreEqual( b.DataContents, a.DataContents ); } [TestMethod] public void ReadWriteByteParsableStruct() { var s = ByteStream.Create( 512 ); var a = new MyPlainOldDataClass(); a.Position = Vector3.Up * 1000.0f; s.Write( a ); var data = s.ToArray(); Assert.AreNotEqual( 0, data.Length ); s.Dispose(); s = ByteStream.CreateReader( data ); var b = s.Read(); Assert.AreEqual( b.Position, a.Position ); } [TestMethod] public void Growing() { var s = ByteStream.Create( 2 ); s.Write( 32 ); s.Write( 64 ); s.Write( 512 ); var data = s.ToArray(); s.Dispose(); s = ByteStream.CreateReader( data ); Assert.AreEqual( 32, s.Read() ); Assert.AreEqual( 64, s.Read() ); Assert.AreEqual( 512, s.Read() ); } [TestMethod] public void WriteStreamToStream() { using var s = ByteStream.Create( 2 ); s.Write( 64 ); s.Write( "Hello" ); s.Write( 512 ); using var t = ByteStream.Create( 2 ); t.Write( 32 ); t.Write( s ); t.Write( 109 ); Assert.AreEqual( s.Length + 4 + 4, t.Length ); var data = t.ToArray(); var r = ByteStream.CreateReader( data ); Assert.AreEqual( 32, r.Read() ); Assert.AreEqual( 64, r.Read() ); Assert.AreEqual( "Hello", r.Read() ); Assert.AreEqual( 512, r.Read() ); Assert.AreEqual( 109, r.Read() ); } [TestMethod] public void No_Negative_Buffers() { Assert.ThrowsException( () => ByteStream.Create( -1 ) ); } [TestMethod] public void ReadBuffer() { var data = Enumerable.Range( 0, 256 ).Select( x => (byte)x ).ToArray(); var r = ByteStream.CreateReader( data ); var buffer = new byte[256]; r.Read(); Assert.AreEqual( 128, r.Read( buffer, 128, 128 ) ); Assert.AreEqual( 4, buffer[128] ); Assert.AreEqual( 5, buffer[129] ); Assert.AreEqual( 127 + 4, buffer[128 + 127] ); } [TestMethod] public void ReadBufferUntilStreamEnd() { var data = Enumerable.Range( 0, 16 ).Select( x => (byte)x ).ToArray(); var r = ByteStream.CreateReader( data ); var buffer = new byte[256]; r.Read(); Assert.AreEqual( 12, r.Read( buffer, 128, 128 ) ); Assert.AreEqual( 4, buffer[128] ); Assert.AreEqual( 5, buffer[129] ); Assert.AreEqual( 15, buffer[128 + 11] ); Assert.AreEqual( 0, r.Read( buffer, 0, 256 ) ); } [TestMethod] public void ReadBufferOutOfRange() { var buffer = new byte[256]; Assert.ThrowsException( () => { var r = ByteStream.CreateReader( new byte[128] ); return r.Read( buffer, -1, 16 ); } ); Assert.ThrowsException( () => { var r = ByteStream.CreateReader( new byte[128] ); return r.Read( buffer, 384, 16 ); } ); Assert.ThrowsException( () => { var r = ByteStream.CreateReader( new byte[128] ); return r.Read( buffer, 128, 256 ); } ); } [TestMethod] public void LeakExploit() { const int chunkSize = 0x50000000 - 128; byte[] chunk1 = new byte[chunkSize]; const int kLeakSize = 8; Assert.ThrowsException( () => { ByteStream leakStream = ByteStream.Create( 128 ); leakStream.Write( chunk1, (int)(0x80000000 - kLeakSize), kLeakSize ); leakStream.Position = 0; ulong arrPtr = leakStream.Read(); } ); } /// /// We were getting rare access violations when decompressing a . /// [TestMethod] public void DecompressStressTest() { var payloadBytes = CreateRandomBytes( 0x1eb78437, 1024 ); using var payload = new ByteStream( payloadBytes ); using var compressed = payload.Compress(); var compressedBytes = compressed.ToArray(); for ( var i = 0; i < 10; ++i ) { using var temp = new ByteStream( compressedBytes ); GC.Collect(); using var decompressed = temp.Decompress(); Assert.AreEqual( payloadBytes.Length, decompressed.Length ); foreach ( var b in payloadBytes ) { Assert.AreEqual( b, decompressed.Read() ); } } } [TestMethod] public void WriteOffset() { using var stream = new ByteStream( 10 ); var data = new byte[100]; data[50] = 123; data[59] = 231; stream.Write( data, 50, 10 ); var written = stream.ToArray(); Assert.AreEqual( 123, written[0] ); Assert.AreEqual( 231, written[9] ); } private static byte[] CreateRandomBytes( int seed, int length ) { var random = new Random( seed ); var bytes = new byte[length]; random.NextBytes( bytes ); return bytes; } // This isn't : unmanaged public readonly record struct TypeWrapper( Type Value ); interface IConstrainetBreaker { void Write( TFrom value ); void Read(); } public class ConstraintBreaker : IConstrainetBreaker where TFrom : unmanaged { public void Write( TFrom value ) { var byteStream = ByteStream.Create( IntPtr.Size ); byteStream.Write( value ); } public void Read() { var byteStream = ByteStream.Create( IntPtr.Size ); var x = byteStream.Read(); } } [TestMethod] public void CannotWriteNonPod() { var breakerType = typeof( ConstraintBreaker<> ).MakeGenericType( [typeof( TypeWrapper )] ); var instance = (IConstrainetBreaker)Activator.CreateInstance( breakerType ); Assert.ThrowsException( () => { instance.Write( new TypeWrapper( typeof( int ) ) ); } ); } [TestMethod] public void CannotReadNonPod() { var breakerType = typeof( ConstraintBreaker<> ).MakeGenericType( [typeof( TypeWrapper )] ); var instance = (IConstrainetBreaker)Activator.CreateInstance( breakerType ); Assert.ThrowsException( () => { instance.Read(); } ); } }