├── ByteStream ├── ByteStream │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── SerializationMode.cs │ ├── Interfaces │ │ ├── IReader.cs │ │ ├── IWriter.cs │ │ ├── IBuffer.cs │ │ └── IByteStream.cs │ ├── Utils │ │ ├── MathUtils.cs │ │ ├── ExceptionHelper.cs │ │ ├── ArrayExtensions.cs │ │ └── Memory.cs │ ├── ByteStream.csproj │ ├── BinaryHelper.cs │ ├── Mananged │ │ ├── ByteReader.cs │ │ ├── ByteWriter.cs │ │ └── ManagedStream.cs │ ├── Unmanaged │ │ ├── PtrReader.cs │ │ ├── PtrWriter.cs │ │ └── UnmanagedStream.cs │ └── StringHelper.cs ├── ByteStreamTest │ ├── Unmanaged │ │ ├── ByteStreamTest.cs │ │ ├── PtrReaderTest.cs │ │ ├── PtrReadWriteTest.cs │ │ └── PtrWriterTest.cs │ ├── Data │ │ └── BlittableStruct.cs │ ├── ByteStreamTest.csproj │ ├── Utils │ │ └── ArrayExtensionTest.cs │ ├── Managed │ │ ├── ByteReaderTest.cs │ │ ├── ByteStreamTests.cs │ │ ├── ByteWriterTest.cs │ │ └── ByteReadWriteTest.cs │ ├── StringHelperTest.cs │ └── BinaryHelperTest.cs ├── ByteStreamBenchmark │ ├── Program.cs │ ├── ByteStreamBenchmark.csproj │ ├── Example │ │ └── SteamSample.cs │ ├── Comparison │ │ ├── WriteCompare.cs │ │ └── ReadCompare.cs │ └── Helper │ │ └── ByteConverter.cs └── ByteStream.sln ├── LICENSE ├── .gitattributes ├── .gitignore └── README.md /ByteStream/ByteStream/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ByteStreamTest")] -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Unmanaged/ByteStreamTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStreamTest.Unmanaged 6 | { 7 | class ByteStreamTest 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/SerializationMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStream 6 | { 7 | public enum SerializationMode : byte 8 | { 9 | None = 0, 10 | Reading = 1, 11 | Writing = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Interfaces/IReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStream.Interfaces 6 | { 7 | public interface IReader : IBuffer 8 | { 9 | T Read() where T : unmanaged; 10 | bool TryRead(out T value) where T : unmanaged; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Interfaces/IWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStream.Interfaces 6 | { 7 | public interface IWriter : IBuffer 8 | { 9 | void Write(T value) where T : unmanaged; 10 | bool TryWrite(T value) where T : unmanaged; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Interfaces/IBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStream.Interfaces 6 | { 7 | public interface IBuffer 8 | { 9 | int Length { get; } 10 | int Offset { get; } 11 | 12 | void SkipBytes(int amount); 13 | void Clear(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamBenchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Running; 3 | using ByteStreamBenchmark.Comparison; 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace ByteStreamBenchmark 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | BenchmarkRunner.Run(); 14 | Console.ReadLine(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Interfaces/IByteStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStream.Interfaces 6 | { 7 | public interface IByteStream 8 | { 9 | int Offset { get; } 10 | int Length { get; } 11 | 12 | SerializationMode Mode { get; } 13 | 14 | void Serialize(ref T value) where T : unmanaged; 15 | void SerializeString(ref string value, Encoding encoding); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Data/BlittableStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace ByteStreamTest.Data 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct BlittableStruct 10 | { 11 | public double ValOne; 12 | public byte ValTwo; 13 | public ushort ValThree; 14 | 15 | public bool IsEqual(BlittableStruct other) 16 | { 17 | return ValOne == other.ValOne && ValTwo == other.ValTwo && ValThree == other.ValThree; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Utils/MathUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ByteStream.Utils 6 | { 7 | internal class MathUtils 8 | { 9 | /// 10 | /// Gets the next power of two of the current number. 11 | /// 12 | public static int NextPowerOfTwo(int num) 13 | { 14 | num--; 15 | num |= num >> 1; 16 | num |= num >> 2; 17 | num |= num >> 4; 18 | num |= num >> 8; 19 | num |= num >> 16; 20 | num++; 21 | return num; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/ByteStreamTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 7.3 9 | 10 | 11 | 12 | true 13 | 14 | 15 | 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Utils/ExceptionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Text; 5 | 6 | namespace ByteStream.Utils 7 | { 8 | internal static class ExceptionHelper 9 | { 10 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 11 | internal static void ThrowReadBufferExceeded() 12 | { 13 | throw new InvalidOperationException("Read operation exceeds buffer size."); 14 | } 15 | 16 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 17 | internal static void ThrowFixedBufferExceeded() 18 | { 19 | throw new InvalidOperationException("Buffer is set to a fixed size and cannot resize automatically."); 20 | } 21 | 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | internal static void ThrowWriteBufferExceeded() 24 | { 25 | throw new InvalidOperationException("Write operation exceeds buffer size."); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamBenchmark/ByteStreamBenchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7.3 7 | 8 | 9 | 10 | true 11 | 12 | 13 | 14 | false 15 | true 16 | 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 DennisCorvers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamBenchmark/Example/SteamSample.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Numerics; 5 | using System.Text; 6 | 7 | namespace ByteStreamBenchmark.Example 8 | { 9 | public class SteamSample 10 | { 11 | 12 | } 13 | 14 | 15 | 16 | public class PlayerData 17 | { 18 | public string Name; 19 | public int Health; 20 | public float Speed; 21 | public PlayerInventory Inventory; 22 | 23 | public void Serialize(IByteStream stream) 24 | { 25 | stream.SerializeString(ref Name, Encoding.ASCII); 26 | stream.Serialize(ref Health); 27 | stream.Serialize(ref Speed); 28 | 29 | Inventory.Serialize(stream); 30 | } 31 | } 32 | 33 | public class PlayerInventory 34 | { 35 | public void Serialize(IByteStream stream) { } 36 | } 37 | 38 | internal static class Extension 39 | { 40 | public static void Serialize(this IByteStream stream, ref Vector3 vector) 41 | { 42 | stream.Serialize(ref vector.X); 43 | stream.Serialize(ref vector.Y); 44 | stream.Serialize(ref vector.Z); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Utils/ArrayExtensionTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using ByteStream.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace ByteStreamTest.Utils 8 | { 9 | [TestFixture] 10 | public class ArrayExtensionTest 11 | { 12 | private int[] m_source; 13 | private int[] m_dest; 14 | 15 | [SetUp] 16 | public void Init() 17 | { 18 | m_source = new int[5] { 1, 2, 3, 4, 5 }; 19 | m_dest = new int[3]; 20 | } 21 | 22 | [Test] 23 | public void CopyToUnsafe() 24 | { 25 | int srcIndex = 1; 26 | int dstIndex = 1; 27 | int len = 2; 28 | 29 | m_source.CopyToUnsafe(srcIndex, m_dest, dstIndex, len); 30 | for (int i = 0; i < len; i++) 31 | { Assert.AreEqual(m_dest[dstIndex + i], m_source[srcIndex + i]); } 32 | 33 | } 34 | 35 | [Test] 36 | public void CloneUnsafe() 37 | { 38 | int[] cpy = m_source.CloneUnsafe(); 39 | for (int i = 0; i < m_source.Length; i++) 40 | { Assert.AreEqual(cpy[i], m_source[i]); } 41 | } 42 | 43 | [Test] 44 | public void ResizeUnsafe() 45 | { 46 | int len = 3; 47 | ArrayExtensions.ResizeUnsafe(ref m_source, len); 48 | 49 | Assert.AreEqual(m_source.Length, len); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/ByteStream.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 7.3 6 | Dennis Corvers 7 | 8 | en 9 | Bytestream is a small library that enables blazing fast serialization of a collection of types to raw bytes. Either in the form of a byte array byte[] or unmanaged memory IntPtr. The library performs no memory allocation on its own, so you are free to use your own memory allocator! 10 | MIT 11 | Copyright (c) 2021 DennisCorvers 12 | https://github.com/DennisCorvers/ByteStream 13 | serialization, fast, unmanaged, pointer, no allocations 14 | false 15 | false 16 | 17 | 1.1.0 18 | 19 | 20 | 21 | true 22 | AnyCPU 23 | 24 | 25 | 26 | true 27 | AnyCPU 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ByteStream/ByteStream.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ByteStream", "ByteStream\ByteStream.csproj", "{6E4C66A0-2B3F-468C-95EA-85FA1C394865}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ByteStreamTest", "ByteStreamTest\ByteStreamTest.csproj", "{F3FC0B38-AE0B-4CF5-A259-D09BA8FE41C3}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ByteStreamBenchmark", "ByteStreamBenchmark\ByteStreamBenchmark.csproj", "{81CD8C01-2262-41FD-BF41-C5BF19AD05E1}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {6E4C66A0-2B3F-468C-95EA-85FA1C394865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {6E4C66A0-2B3F-468C-95EA-85FA1C394865}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {6E4C66A0-2B3F-468C-95EA-85FA1C394865}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {6E4C66A0-2B3F-468C-95EA-85FA1C394865}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {F3FC0B38-AE0B-4CF5-A259-D09BA8FE41C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F3FC0B38-AE0B-4CF5-A259-D09BA8FE41C3}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F3FC0B38-AE0B-4CF5-A259-D09BA8FE41C3}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F3FC0B38-AE0B-4CF5-A259-D09BA8FE41C3}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {81CD8C01-2262-41FD-BF41-C5BF19AD05E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {81CD8C01-2262-41FD-BF41-C5BF19AD05E1}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {81CD8C01-2262-41FD-BF41-C5BF19AD05E1}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {81CD8C01-2262-41FD-BF41-C5BF19AD05E1}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {D5FCEEBC-43C6-471C-BAD6-2B991FEFB8DC} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Managed/ByteReaderTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Mananged; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace ByteStreamTest.Managed 8 | { 9 | [TestFixture] 10 | public class ByteReaderTest 11 | { 12 | [Test] 13 | public void CTorTest() 14 | { 15 | byte[] buf = new byte[30]; 16 | 17 | Assert.Catch(typeof(ArgumentNullException), () => 18 | { var br = new ByteReader(null); }); 19 | 20 | ByteReader reader = new ByteReader(buf); 21 | Assert.AreEqual(30, reader.Length); 22 | Assert.AreEqual(0, reader.Offset); 23 | Assert.AreEqual(true, reader.IsFixedSize); 24 | } 25 | 26 | [Test] 27 | public void CTorFaultTest() 28 | { 29 | byte[] buf = new byte[30]; 30 | 31 | ByteReader br; 32 | Assert.Catch(typeof(ArgumentNullException), () => 33 | { br = new ByteReader(null); }); 34 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 35 | { br = new ByteReader(buf, -1, -1); }); 36 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 37 | { br = new ByteReader(buf, 0, -1); }); 38 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 39 | { br = new ByteReader(buf, 25, 6); }); 40 | } 41 | 42 | [Test] 43 | public void CTorOffsetTest() 44 | { 45 | byte[] buf = new byte[30]; 46 | ByteReader br = new ByteReader(buf, 10, 15); 47 | Assert.AreEqual(10, br.Offset); 48 | Assert.AreEqual(15, br.Length); 49 | } 50 | 51 | [Test] 52 | public void SkipBytesTest() 53 | { 54 | var br = new ByteReader(new byte[10]); 55 | Assert.Catch(typeof(InvalidOperationException), () => { br.SkipBytes(11); }); 56 | br.SkipBytes(10); 57 | Assert.AreEqual(10, br.Offset); 58 | } 59 | 60 | [Test] 61 | public void ClearTest() 62 | { 63 | var br = new ByteReader(new byte[10], 5, 5); 64 | br.Clear(); 65 | Assert.AreEqual(0, br.Offset); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Utils/ArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace ByteStream.Utils 7 | { 8 | internal static class ArrayExtensions 9 | { 10 | /// 11 | /// Copies the source array to another. DOES NOT PERFORM BOUNDARY OR NULL CHECKS! 12 | /// 13 | /// The source index. 14 | /// The destination array. 15 | /// The destination index. 16 | /// The amount of elements to copy. 17 | public unsafe static void CopyToUnsafe(this T[] source, int sourceIndex, T[] destination, int destinationIndex, int length) 18 | where T : unmanaged 19 | { 20 | length *= sizeof(T); 21 | 22 | fixed (T* src = &source[sourceIndex]) 23 | fixed (T* dst = &destination[destinationIndex]) 24 | { 25 | Buffer.MemoryCopy(src, dst, length, length); 26 | } 27 | } 28 | 29 | /// 30 | /// Duplicates an array. 31 | /// 32 | public unsafe static T[] CloneUnsafe(this T[] source) 33 | where T : unmanaged 34 | { 35 | T[] val = new T[source.Length]; 36 | CopyToUnsafe(source, 0, val, 0, source.Length); 37 | return val; 38 | } 39 | 40 | /// 41 | /// Resizes an array. 42 | /// 43 | /// The array to resize. 44 | /// The new size of the array. 45 | public static void ResizeUnsafe(ref T[] source, int newSize) where T : unmanaged 46 | { 47 | T[] lArray = source; 48 | if (lArray == null) 49 | { 50 | source = new T[newSize]; 51 | return; 52 | } 53 | 54 | if (lArray.Length != newSize) 55 | { 56 | T[] newArray = new T[newSize]; 57 | newSize = Math.Min(lArray.Length, newSize); 58 | CopyToUnsafe(lArray, 0, newArray, 0, newSize); 59 | 60 | source = newArray; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Unmanaged/PtrReaderTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Mananged; 2 | using ByteStream.Unmanaged; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace ByteStreamTest.Unmanaged 10 | { 11 | [TestFixture] 12 | public class PtrReaderTest 13 | { 14 | private IntPtr m_buffer; 15 | 16 | [SetUp] 17 | public void Init() 18 | { 19 | m_buffer = Marshal.AllocHGlobal(64); 20 | } 21 | [TearDown] 22 | public void TearDown() 23 | { 24 | Marshal.FreeHGlobal(m_buffer); 25 | } 26 | 27 | [Test] 28 | public void CTorTest() 29 | { 30 | byte[] buf = new byte[30]; 31 | 32 | Assert.Catch(typeof(ArgumentNullException), () => 33 | { var pr = new PtrReader(IntPtr.Zero, 64); }); 34 | 35 | PtrReader reader = new PtrReader(m_buffer, 64); 36 | Assert.AreEqual(64, reader.Length); 37 | Assert.AreEqual(0, reader.Offset); 38 | } 39 | 40 | [Test] 41 | public void CTorFaultTest() 42 | { 43 | PtrWriter pw; 44 | Assert.Catch(typeof(ArgumentNullException), () => 45 | { pw = new PtrWriter(IntPtr.Zero, 64); }); 46 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 47 | { pw = new PtrWriter(m_buffer, -1, -1); }); 48 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 49 | { pw = new PtrWriter(m_buffer, 0, -1); }); 50 | Assert.Catch(typeof(ArgumentException), () => 51 | { pw = new PtrWriter(m_buffer, 25, 6); }); 52 | } 53 | 54 | [Test] 55 | public void CTorOffsetTest() 56 | { 57 | PtrReader pr = new PtrReader(m_buffer, 10, 15); 58 | Assert.AreEqual(10, pr.Offset); 59 | Assert.AreEqual(15, pr.Length); 60 | } 61 | 62 | [Test] 63 | public void SkipBytesTest() 64 | { 65 | var pr = new PtrReader(m_buffer, 10); 66 | Assert.Catch(typeof(InvalidOperationException), () => { pr.SkipBytes(11); }); 67 | pr.SkipBytes(10); 68 | Assert.AreEqual(10, pr.Offset); 69 | } 70 | 71 | [Test] 72 | public void ClearTest() 73 | { 74 | var pr = new PtrReader(m_buffer, 5, 5); 75 | pr.Clear(); 76 | Assert.AreEqual(0, pr.Offset); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Utils/Memory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace ByteStream.Utils.Unsafe 6 | { 7 | internal unsafe static class Memory 8 | { 9 | /// 10 | /// Copies memory between pointers. DOES NOT CHECK BOUNDARIES OR NULL! 11 | /// 12 | /// The source memory. 13 | /// The source index. 14 | /// The destination memory. 15 | /// The destination index. 16 | /// The amount of bytes to copy. 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static void CopyMemory(void* source, void* destination, int length) 19 | { 20 | Buffer.MemoryCopy(source, destination, length, length); 21 | } 22 | 23 | /// 24 | /// Copies memory between pointers. DOES NOT CHECK BOUNDARIES OR NULL! 25 | /// 26 | /// The source memory. 27 | /// The source index. 28 | /// The destination memory. 29 | /// The destination index. 30 | /// The amount of bytes to copy. 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public static void CopyMemory(byte[] source, int sourceIndex, IntPtr destination, int destinationIndex, int length) 33 | { 34 | fixed (byte* src = &source[sourceIndex]) 35 | { 36 | Buffer.MemoryCopy(src, (byte*)destination + destinationIndex, length, length); 37 | } 38 | } 39 | 40 | /// 41 | /// Copies memory between pointers. DOES NOT CHECK BOUNDARIES OR NULL! 42 | /// 43 | /// The source memory. 44 | /// The source index. 45 | /// The destination memory. 46 | /// The destination index. 47 | /// The amount of bytes to copy. 48 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 49 | public static void CopyMemory(IntPtr source, int sourceIndex, byte[] destination, int destinationIndex, int length) 50 | { 51 | fixed (byte* dst = &destination[destinationIndex]) 52 | { 53 | Buffer.MemoryCopy((byte*)source + sourceIndex, dst, length, length); 54 | } 55 | } 56 | 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static void ClearMemory(IntPtr source, int length) 59 | { 60 | ClearMemory((void*)source, length); 61 | } 62 | 63 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 64 | public static void ClearMemory(void* source, int length) 65 | { 66 | long c = length >> 3; // longs 67 | 68 | int i = 0; 69 | for (; i < c; i++) 70 | *((ulong*)source + i) = 0; 71 | 72 | i = i << 3; 73 | for (; i < length; i++) 74 | *((byte*)source + i) = 0; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Unmanaged/PtrReadWriteTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using ByteStream.Mananged; 6 | using ByteStream; 7 | using System.Runtime.InteropServices; 8 | using ByteStream.Unmanaged; 9 | 10 | namespace ByteStreamTest.Unmanaged 11 | { 12 | public class ByteStreamTests 13 | { 14 | private const int BUFSIZE = 32; 15 | private IntPtr m_buffer; 16 | 17 | [SetUp] 18 | public void Init() 19 | { 20 | m_buffer = Marshal.AllocHGlobal(BUFSIZE); 21 | } 22 | [TearDown] 23 | public void TearDown() 24 | { 25 | Marshal.FreeHGlobal(m_buffer); 26 | } 27 | 28 | [Test] 29 | public void ReadWriteTest() 30 | { 31 | UnmanagedStream bs = new UnmanagedStream(); 32 | 33 | bs.ResetWrite(m_buffer, BUFSIZE); 34 | 35 | bs.Write(123); 36 | 37 | bs.ResetRead(); 38 | 39 | int res = 0; 40 | bs.Read(ref res); 41 | Assert.AreEqual(123, res); 42 | } 43 | 44 | [Test] 45 | public void ResetReadFault() 46 | { 47 | UnmanagedStream bs = new UnmanagedStream(); 48 | 49 | Assert.Catch(() => { bs.ResetRead(); }); 50 | } 51 | 52 | [Test] 53 | public void InvalidateStreamer() 54 | { 55 | UnmanagedStream bs = new UnmanagedStream(); 56 | bs.ResetWrite(m_buffer, 4); 57 | 58 | bs.Write(123); 59 | 60 | Assert.Catch(() => 61 | { 62 | bs.Write(321); 63 | }); 64 | } 65 | 66 | [Test] 67 | public void InvalidateStream() 68 | { 69 | UnmanagedStream bs = new UnmanagedStream(); 70 | bs.ResetWrite(m_buffer, 2); 71 | 72 | Assert.Catch(() => 73 | { 74 | bs.Write(321); 75 | }); 76 | 77 | Assert.AreEqual(2, bs.Length); 78 | } 79 | 80 | [Test] 81 | public void ResetStreamer() 82 | { 83 | UnmanagedStream bs = new UnmanagedStream(); 84 | bs.ResetWrite(m_buffer, 8); 85 | 86 | bs.Write(333); 87 | 88 | Assert.AreEqual(4, bs.Offset); 89 | Assert.IsTrue(bs.IsWriting); 90 | 91 | bs.ResetRead(); 92 | 93 | Assert.AreEqual(0, bs.Offset); 94 | Assert.IsTrue(bs.IsReading); 95 | } 96 | 97 | [Test] 98 | public void LargeStringWrite() 99 | { 100 | UnmanagedStream ms = new UnmanagedStream(); 101 | ms.ResetWrite(Marshal.AllocHGlobal(128), 128); 102 | 103 | StringBuilder sb = new StringBuilder(50); 104 | for (int i = 0; i < 50; i++) 105 | sb.Append(i); 106 | 107 | var str = sb.ToString(); 108 | 109 | ms.WriteString(str, Encoding.Default); 110 | 111 | Assert.IsTrue(ms.Length >= 64); 112 | 113 | ms.ResetRead(); 114 | 115 | Assert.AreEqual(str, ms.ReadString(Encoding.Default)); 116 | 117 | Marshal.FreeHGlobal(ms.Buffer); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamBenchmark/Comparison/WriteCompare.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using ByteStream.Mananged; 3 | using ByteStream.Unmanaged; 4 | using ByteStreamBenchmark.Helper; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | 10 | namespace ByteStreamBenchmark.Comparison 11 | { 12 | [MemoryDiagnoser] 13 | public class WriteCompare 14 | { 15 | private const int AMOUNT = 1024; 16 | private readonly byte[] m_byteBuf; 17 | private readonly IntPtr m_ptrBuf; 18 | private readonly ManagedStream managedStream; 19 | private readonly UnmanagedStream unmanagedStream; 20 | 21 | public WriteCompare() 22 | { 23 | int size = sizeof(int) * AMOUNT; 24 | m_byteBuf = new byte[size]; 25 | m_ptrBuf = Marshal.AllocHGlobal(size); 26 | managedStream = new ManagedStream(); 27 | unmanagedStream = new UnmanagedStream(); 28 | } 29 | ~WriteCompare() 30 | { 31 | Marshal.FreeHGlobal(m_ptrBuf); 32 | } 33 | 34 | [Benchmark] 35 | public void ByteWrite() 36 | { 37 | ByteWriter writer = new ByteWriter(m_byteBuf); 38 | for (int i = 0; i < AMOUNT; i++) 39 | { writer.Write(i); } 40 | } 41 | 42 | [Benchmark] 43 | public void PtrWrite() 44 | { 45 | PtrWriter writer = new PtrWriter(m_ptrBuf, AMOUNT * sizeof(int)); 46 | for (int i = 0; i < AMOUNT; i++) 47 | { writer.Write(i); } 48 | } 49 | 50 | [Benchmark] 51 | public void ManagedStream() 52 | { 53 | managedStream.ResetWrite(m_byteBuf); 54 | 55 | for (int i = 0; i < AMOUNT; i++) 56 | managedStream.Write(i); 57 | } 58 | 59 | [Benchmark] 60 | public void UnmanagedStream() 61 | { 62 | unmanagedStream.ResetWrite(m_ptrBuf, AMOUNT * sizeof(int)); 63 | 64 | for (int i = 0; i < AMOUNT; i++) 65 | unmanagedStream.Write(i); 66 | } 67 | 68 | [Benchmark(Baseline = true)] 69 | public void ArraySegment() 70 | { 71 | ArraySegment writer = new ArraySegment(m_byteBuf); 72 | int offset = 0; 73 | 74 | for (int i = 0; i < AMOUNT; i++) 75 | { 76 | for (int j = 0; j < sizeof(int); j++) 77 | { 78 | writer[offset] = (byte)(i << j * 8); 79 | offset++; 80 | } 81 | } 82 | } 83 | 84 | [Benchmark] 85 | public void BitConvert() 86 | { 87 | int offset = 0; 88 | for (int i = 0; i < AMOUNT; i++) 89 | { 90 | var dat = BitConverter.GetBytes(i); 91 | Array.Copy(dat, 0, m_byteBuf, offset, dat.Length); 92 | offset += sizeof(int); 93 | } 94 | } 95 | 96 | [Benchmark] 97 | public void Union() 98 | { 99 | int offset = 0; 100 | for (int i = 0; i < AMOUNT; i++) 101 | { 102 | ByteConverter converter = new ByteConverter(); 103 | converter.int32 = i; 104 | for (int j = 0; j < sizeof(int); j++) 105 | { 106 | m_byteBuf[offset] = converter[j]; 107 | offset++; 108 | } 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Unmanaged/PtrWriterTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream; 2 | using ByteStream.Mananged; 3 | using ByteStream.Unmanaged; 4 | using ByteStream.Utils; 5 | using NUnit.Framework; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Runtime.InteropServices; 9 | using System.Text; 10 | 11 | namespace ByteStreamTest.Unmanaged 12 | { 13 | [TestFixture] 14 | public class PtrWriterTest 15 | { 16 | private IntPtr m_buffer; 17 | 18 | [SetUp] 19 | public void Init() 20 | { 21 | m_buffer = Marshal.AllocHGlobal(64); 22 | } 23 | [TearDown] 24 | public void TearDown() 25 | { 26 | Marshal.FreeHGlobal(m_buffer); 27 | } 28 | 29 | [Test] 30 | public void CTorFaultTest() 31 | { 32 | PtrWriter pw; 33 | Assert.Catch(typeof(ArgumentNullException), () => 34 | { pw = new PtrWriter(IntPtr.Zero, 64); }); 35 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 36 | { pw = new PtrWriter(m_buffer, -1, -1); }); 37 | Assert.Catch(typeof(ArgumentOutOfRangeException), () => 38 | { pw = new PtrWriter(m_buffer, 0, -1); }); 39 | Assert.Catch(typeof(ArgumentException), () => 40 | { pw = new PtrWriter(m_buffer, 25, 6); }); 41 | } 42 | 43 | [Test] 44 | public void CTorTest() 45 | { 46 | PtrWriter writer = new PtrWriter(m_buffer, 64); 47 | Assert.AreEqual(64, writer.Length); 48 | Assert.AreEqual(0, writer.Offset); 49 | } 50 | 51 | [Test] 52 | public void SkipBytesTest() 53 | { 54 | var pw = new PtrWriter(m_buffer, 10); 55 | Assert.Catch(typeof(InvalidOperationException), () => { pw.SkipBytes(11); }); 56 | pw.SkipBytes(10); 57 | Assert.AreEqual(10, pw.Offset); 58 | } 59 | 60 | [Test] 61 | public void ClearTest() 62 | { 63 | var pw = new PtrWriter(m_buffer, 5, 64); 64 | pw.Clear(); 65 | Assert.AreEqual(0, pw.Offset); 66 | } 67 | 68 | [TestCase(280851)] 69 | public void CopyToBytesTest(int value) 70 | { 71 | byte[] buf = new byte[8]; 72 | 73 | BinaryHelper.Write(m_buffer, 4, value); 74 | var pw = new PtrWriter(m_buffer, 64); 75 | pw.CopyTo(buf, 0, 8); 76 | 77 | Assert.AreEqual(value, BinaryHelper.Read(buf, 4)); 78 | } 79 | 80 | [TestCase(1337666)] 81 | public void CopyToPtrTest(int value) 82 | { 83 | BinaryHelper.Write(m_buffer, 4, value); 84 | 85 | var pw = new PtrWriter(m_buffer, 64); 86 | var newBuf = Marshal.AllocHGlobal(8); 87 | pw.CopyTo(newBuf, 0, 8); 88 | 89 | Assert.AreEqual(value, BinaryHelper.Read(newBuf, 4)); 90 | Marshal.FreeHGlobal(newBuf); 91 | } 92 | 93 | [Test] 94 | public void SizePrefixTest() 95 | { 96 | PtrWriter bw = new PtrWriter(m_buffer, 16); 97 | bw.ReserveSizePrefix(); 98 | bw.Write(1); 99 | bw.Write(2); 100 | bw.Write(3); 101 | 102 | Assert.AreEqual(16, bw.Offset); 103 | Assert.AreEqual(16, bw.Length); 104 | 105 | Assert.AreEqual(16, bw.PrefixSize()); 106 | 107 | PtrReader br = new PtrReader(bw.Buffer, 16); 108 | Assert.AreEqual(16, br.Read()); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Managed/ByteStreamTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using ByteStream.Mananged; 6 | using ByteStream; 7 | 8 | namespace ByteStreamTest.Managed 9 | { 10 | public class ByteStreamTests 11 | { 12 | private const int BUFSIZE = 32; 13 | private ManagedStream bs; 14 | 15 | private byte[] m_buffer; 16 | 17 | [SetUp] 18 | public void Init() 19 | { 20 | bs = new ManagedStream(); 21 | m_buffer = new byte[BUFSIZE]; 22 | } 23 | 24 | [Test] 25 | public void ReadWriteTest() 26 | { 27 | bs.ResetWrite(m_buffer); 28 | 29 | bs.Write(123); 30 | 31 | bs.ResetRead(); 32 | 33 | int res = 0; 34 | bs.Read(ref res); 35 | Assert.AreEqual(123, res); 36 | } 37 | 38 | [Test] 39 | public void ResetReadFault() 40 | { 41 | ManagedStream bs = new ManagedStream(); 42 | 43 | Assert.Catch(() => { bs.ResetRead(); }); 44 | } 45 | 46 | [Test] 47 | public void ResetWriteDefault() 48 | { 49 | ManagedStream bs = new ManagedStream(); 50 | 51 | bs.ResetWrite(); 52 | 53 | Assert.AreEqual(SerializationMode.Writing, bs.Mode); 54 | Assert.IsTrue(bs.IsWriting); 55 | 56 | Assert.AreEqual(ManagedStream.DefaultBufferSize, bs.Length); 57 | } 58 | 59 | [Test] 60 | public void InvalidateStreamer() 61 | { 62 | ManagedStream bs = new ManagedStream(); 63 | bs.ResetWrite(new byte[4], true); 64 | 65 | bs.Write(123); 66 | 67 | Assert.Catch(() => 68 | { 69 | bs.Write(321); 70 | }); 71 | } 72 | 73 | [Test] 74 | public void InvalidateStream() 75 | { 76 | ManagedStream bs = new ManagedStream(); 77 | bs.ResetWrite(new byte[2], true); 78 | 79 | Assert.Catch(() => 80 | { 81 | bs.Write(321); 82 | }); 83 | 84 | Assert.AreEqual(2, bs.Length); 85 | } 86 | 87 | [Test] 88 | public void ResetStreamer() 89 | { 90 | ManagedStream bs = new ManagedStream(); 91 | bs.ResetWrite(new byte[8], true); 92 | 93 | bs.Write(333); 94 | 95 | Assert.AreEqual(4, bs.Offset); 96 | Assert.IsTrue(bs.IsWriting); 97 | 98 | bs.ResetRead(); 99 | 100 | Assert.AreEqual(0, bs.Offset); 101 | Assert.IsTrue(bs.IsReading); 102 | } 103 | 104 | [Test] 105 | public void ExpandStreamTest() 106 | { 107 | ManagedStream ms = new ManagedStream(); 108 | ms.ResetWrite(2); 109 | 110 | ms.Write(123); 111 | 112 | // Resizes to the next power of two. 113 | // Which is 2 -> 4 114 | Assert.AreEqual(4, ms.Length); 115 | } 116 | 117 | [Test] 118 | public void LargeStringWrite() 119 | { 120 | ManagedStream ms = new ManagedStream(); 121 | ms.ResetWrite(2); 122 | 123 | StringBuilder sb = new StringBuilder(50); 124 | for (int i = 0; i < 50; i++) 125 | sb.Append(i); 126 | 127 | var str = sb.ToString(); 128 | 129 | ms.WriteString(str, Encoding.Default); 130 | 131 | Assert.IsTrue(ms.Length >= 64); 132 | 133 | ms.ResetRead(); 134 | 135 | Assert.AreEqual(str, ms.ReadString(Encoding.Default)); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamBenchmark/Comparison/ReadCompare.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using ByteStream; 3 | using ByteStream.Mananged; 4 | using ByteStream.Unmanaged; 5 | using ByteStreamBenchmark.Helper; 6 | using System; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace ByteStreamBenchmark.Comparison 10 | { 11 | [MemoryDiagnoser] 12 | public class ReadCompare 13 | { 14 | private const int AMOUNT = 1024; 15 | private readonly byte[] m_byteBuf; 16 | private readonly IntPtr m_ptrBuf; 17 | private readonly ManagedStream managedStream; 18 | private readonly UnmanagedStream unmanagedStream; 19 | 20 | public ReadCompare() 21 | { 22 | managedStream = new ManagedStream(); 23 | unmanagedStream = new UnmanagedStream(); 24 | int size = sizeof(int) * AMOUNT; 25 | m_byteBuf = new byte[size]; 26 | m_ptrBuf = Marshal.AllocHGlobal(size); 27 | 28 | for (int i = 0; i < size; i += 4) 29 | { 30 | int num = i / 4; 31 | BinaryHelper.Write(m_byteBuf, i, num); 32 | BinaryHelper.Write(m_ptrBuf, i, num); 33 | } 34 | } 35 | ~ReadCompare() 36 | { 37 | Marshal.FreeHGlobal(m_ptrBuf); 38 | } 39 | 40 | [Benchmark] 41 | public void ByteRead() 42 | { 43 | ByteReader reader = new ByteReader(m_byteBuf); 44 | for (int i = 0; i < AMOUNT; i++) 45 | { int result = reader.Read(); } 46 | } 47 | 48 | [Benchmark] 49 | public void PtrRead() 50 | { 51 | PtrReader reader = new PtrReader(m_ptrBuf, AMOUNT * sizeof(int)); 52 | for (int i = 0; i < AMOUNT; i++) 53 | { int result = reader.Read(); } 54 | } 55 | 56 | [Benchmark] 57 | public void ManagedStream() 58 | { 59 | managedStream.ResetRead(m_byteBuf); 60 | 61 | for (int i = 0; i < AMOUNT; i++) 62 | { 63 | int result = managedStream.Read(); 64 | } 65 | } 66 | 67 | [Benchmark] 68 | public void UnmanagedStream() 69 | { 70 | unmanagedStream.ResetRead(m_ptrBuf, AMOUNT * sizeof(int)); 71 | 72 | for (int i = 0; i < AMOUNT; i++) 73 | { 74 | int result = unmanagedStream.Read(); 75 | } 76 | } 77 | 78 | [Benchmark(Baseline = true)] 79 | public void ArraySegment() 80 | { 81 | ArraySegment writer = new ArraySegment(m_byteBuf); 82 | int offset = 0; 83 | 84 | for (int i = 0; i < AMOUNT; i++) 85 | { 86 | int result = 0; 87 | for (int j = 0; j < sizeof(int); j++) 88 | { 89 | result |= writer[offset] << j * 8; 90 | offset++; 91 | } 92 | } 93 | } 94 | 95 | [Benchmark] 96 | public void BitConvert() 97 | { 98 | int offset = 0; 99 | for (int i = 0; i < AMOUNT; i++) 100 | { 101 | int result = BitConverter.ToInt32(m_byteBuf, offset); 102 | offset += sizeof(int); 103 | } 104 | } 105 | 106 | [Benchmark] 107 | public void Union() 108 | { 109 | int offset = 0; 110 | for (int i = 0; i < AMOUNT; i++) 111 | { 112 | ByteConverter converter = new ByteConverter(); 113 | for (int j = 0; j < sizeof(int); j++) 114 | { 115 | converter[j] = m_byteBuf[offset]; 116 | offset++; 117 | } 118 | int result = converter.int32; 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Managed/ByteWriterTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream; 2 | using ByteStream.Mananged; 3 | using ByteStream.Utils; 4 | using NUnit.Framework; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | 10 | namespace ByteStreamTest.Managed 11 | { 12 | [TestFixture] 13 | public class ByteWriterTest 14 | { 15 | [Test] 16 | public void CTorTest() 17 | { 18 | byte[] buf = new byte[30]; 19 | 20 | Assert.Catch(typeof(ArgumentNullException), () => 21 | { var br = new ByteWriter(null); }); 22 | 23 | ByteWriter writer = new ByteWriter(buf); 24 | Assert.AreEqual(30, writer.Length); 25 | Assert.AreEqual(0, writer.Offset); 26 | Assert.AreEqual(true, writer.IsFixedSize); 27 | } 28 | 29 | [Test] 30 | public void CTorFaultTest() 31 | { 32 | byte[] buf = new byte[30]; 33 | 34 | ByteWriter bw; 35 | Assert.Catch(typeof(ArgumentNullException), () => 36 | { bw = new ByteWriter(null); }); 37 | Assert.Catch(typeof(ArgumentException), () => 38 | { bw = new ByteWriter(buf, 40); }); 39 | } 40 | 41 | [Test] 42 | public void CTorNewTest() 43 | { 44 | ByteWriter bw = new ByteWriter(30, true); 45 | Assert.AreEqual(30, bw.Length); 46 | } 47 | 48 | [Test] 49 | public void CTorOffsetTest() 50 | { 51 | byte[] buf = new byte[30]; 52 | ByteWriter bw = new ByteWriter(buf, 10); 53 | Assert.AreEqual(10, bw.Offset); 54 | Assert.AreEqual(30, bw.Length); 55 | } 56 | 57 | [Test] 58 | public void SkipBytesTest() 59 | { 60 | var bw = new ByteWriter(new byte[10]); 61 | Assert.Catch(typeof(InvalidOperationException), () => { bw.SkipBytes(11); }); 62 | bw.SkipBytes(10); 63 | Assert.AreEqual(10, bw.Offset); 64 | } 65 | 66 | [Test] 67 | public void ClearTest() 68 | { 69 | var bw = new ByteWriter(new byte[10], 5); 70 | bw.Clear(); 71 | Assert.AreEqual(0, bw.Offset); 72 | } 73 | 74 | [Test] 75 | public void ExpandTest() 76 | { 77 | ByteWriter bw = new ByteWriter(10, false); 78 | bw.SkipBytes(11); 79 | Assert.AreEqual(16, bw.Length); 80 | Assert.AreEqual(11, bw.Offset); 81 | 82 | bw.SkipBytes(100); 83 | Assert.AreEqual(MathUtils.NextPowerOfTwo(116), bw.Length); 84 | } 85 | 86 | [Test] 87 | public void TrimTest() 88 | { 89 | ByteWriter bw = new ByteWriter(new byte[16], 10); 90 | bw.Trim(); 91 | Assert.AreEqual(10, bw.Length); 92 | Assert.AreEqual(bw.Offset, bw.Length); 93 | } 94 | 95 | [TestCase(280851)] 96 | public void CopyToBytesTest(int value) 97 | { 98 | byte[] buf = new byte[8]; 99 | BinaryHelper.Write(buf, 0, value); 100 | 101 | var writer = new ByteWriter(buf); 102 | var newBuf = new byte[4]; 103 | writer.CopyTo(newBuf, 0, 4); 104 | 105 | Assert.AreEqual(value, BinaryHelper.Read(newBuf, 0)); 106 | } 107 | 108 | [Test] 109 | public void SizePrefixTest() 110 | { 111 | ByteWriter bw = new ByteWriter(16, false); 112 | bw.ReserveSizePrefix(); 113 | bw.Write(1); 114 | bw.Write(2); 115 | bw.Write(3); 116 | 117 | Assert.AreEqual(16, bw.Offset); 118 | Assert.AreEqual(16, bw.Length); 119 | 120 | Assert.AreEqual(16, bw.PrefixSize()); 121 | 122 | ByteReader br = new ByteReader(bw.Buffer); 123 | Assert.AreEqual(16, br.Read()); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/Managed/ByteReadWriteTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream; 2 | using ByteStream.Mananged; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace ByteStreamTest.Managed 10 | { 11 | [TestFixture] 12 | public class ByteReadWriteTest 13 | { 14 | //Do not change this value! 15 | private const int BUFSIZE = 32; 16 | 17 | private byte[] m_buffer; 18 | 19 | [SetUp] 20 | public void Init() 21 | { 22 | m_buffer = new byte[BUFSIZE]; 23 | } 24 | 25 | [TestCase(123, 1.2f, 1.3d)] 26 | public void ValueTest(int iVal, float fVal, double dVal) 27 | { 28 | var bw = new ByteWriter(m_buffer); 29 | bw.Write(iVal); bw.Write(fVal); bw.Write(dVal); 30 | 31 | var br = new ByteReader(m_buffer); 32 | Assert.AreEqual(iVal, br.Read()); 33 | Assert.AreEqual(fVal, br.Read()); 34 | Assert.AreEqual(dVal, br.Read()); 35 | 36 | Assert.AreEqual(16, bw.Offset); 37 | Assert.AreEqual(br.Offset, bw.Offset); 38 | } 39 | 40 | [Test] 41 | public void TryWrite() 42 | { 43 | var bw = new ByteWriter(m_buffer); 44 | Assert.AreEqual(true, bw.TryWrite(123)); 45 | 46 | bw.SkipBytes(28); 47 | Assert.AreEqual(false, bw.TryWrite(123)); 48 | } 49 | 50 | [Test] 51 | public void TryRead() 52 | { 53 | var br = new ByteReader(m_buffer); 54 | Assert.AreEqual(true, br.TryRead(out int value)); 55 | 56 | br.SkipBytes(28); 57 | Assert.AreEqual(false, br.TryRead(out int valu2)); 58 | } 59 | 60 | [Test] 61 | public void BytesTest() 62 | { 63 | byte[] val = new byte[4] { 1, 2, 3, 4 }; 64 | var bw = new ByteWriter(m_buffer); 65 | bw.WriteBytes(val); 66 | 67 | var br = new ByteReader(m_buffer); 68 | var returnVal = br.ReadBytes(4); 69 | 70 | Assert.AreEqual(val, returnVal); 71 | Assert.AreEqual(4, br.Offset); 72 | Assert.AreEqual(bw.Offset, br.Offset); 73 | } 74 | 75 | [TestCase("手機瀏覽")] 76 | public void UTF16Test(string value) 77 | { 78 | var bw = new ByteWriter(m_buffer); 79 | bw.WriteUTF16(value, true); 80 | 81 | var br = new ByteReader(m_buffer); 82 | Assert.AreEqual(value, br.ReadUTF16()); 83 | Assert.AreEqual(br.Offset, bw.Offset); 84 | } 85 | 86 | [TestCase("手機瀏覽")] 87 | public void UTF16TestLen(string value) 88 | { 89 | var bw = new ByteWriter(m_buffer); 90 | bw.WriteUTF16(value); 91 | 92 | var br = new ByteReader(m_buffer); 93 | Assert.AreEqual(value, br.ReadUTF16(value.Length)); 94 | Assert.AreEqual(br.Offset, bw.Offset); 95 | Assert.AreEqual(value.Length * sizeof(char), bw.Offset); 96 | } 97 | 98 | [TestCase("Test.")] 99 | public void ANSITestLen(string value) 100 | { 101 | var bw = new ByteWriter(m_buffer); 102 | bw.WriteANSI(value); 103 | 104 | var br = new ByteReader(m_buffer); 105 | Assert.AreEqual(value, br.ReadANSI(value.Length)); 106 | Assert.AreEqual(br.Offset, bw.Offset); 107 | Assert.AreEqual(value.Length, bw.Offset); 108 | } 109 | 110 | [TestCase("手機瀏覽")] 111 | [TestCase("Test.")] 112 | public void StringTest(string value) 113 | { 114 | var bw = new ByteWriter(m_buffer); 115 | bw.WriteString(value, Encoding.UTF32); 116 | 117 | var br = new ByteReader(m_buffer); 118 | 119 | Assert.AreEqual(value, br.ReadString(Encoding.UTF32)); 120 | Assert.AreEqual(bw.Offset, br.Offset); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/StringHelperTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace ByteStreamTest 9 | { 10 | [TestFixture] 11 | public class StringHelperTest 12 | { 13 | private class IntPtrTest 14 | { 15 | private readonly IntPtr m_buffer; 16 | 17 | public IntPtrTest() 18 | { 19 | m_buffer = Marshal.AllocHGlobal(64); 20 | } 21 | ~IntPtrTest() 22 | { 23 | Marshal.FreeHGlobal(m_buffer); 24 | } 25 | 26 | [TestCase("手機瀏覽")] 27 | public void UTF16Test(string value) 28 | { 29 | int length = value.Length * sizeof(char); 30 | 31 | for (int i = 0; i < 4 * length; i += length) 32 | { StringHelper.WriteUTF16(m_buffer, i, value); } 33 | 34 | for (int i = 0; i < 4 * length; i += length) 35 | { Assert.AreEqual(value, StringHelper.ReadUTF16(m_buffer, i, value.Length)); } 36 | } 37 | 38 | [TestCase("Test.")] 39 | public void ANSITest(string value) 40 | { 41 | int length = value.Length; 42 | 43 | for (int i = 0; i < 4 * length; i += length) 44 | { StringHelper.WriteANSI(m_buffer, i, value); } 45 | 46 | for (int i = 0; i < 4 * length; i += length) 47 | { Assert.AreEqual(value, StringHelper.ReadANSI(m_buffer, i, value.Length)); } 48 | } 49 | 50 | [TestCase("手機瀏覽")] 51 | [TestCase("")] 52 | public void EncodingTest(string value) 53 | { 54 | var encoding = Encoding.UTF7; 55 | 56 | int byteCount = StringHelper.WriteString(m_buffer, 0, 64, value, encoding); 57 | 58 | Assert.AreEqual(value, StringHelper.ReadString(m_buffer, 0, byteCount, encoding)); 59 | } 60 | } 61 | 62 | private class ByteArrTest 63 | { 64 | private readonly byte[] m_buffer; 65 | 66 | public ByteArrTest() 67 | { 68 | m_buffer = new byte[64]; 69 | } 70 | 71 | [TestCase("手機瀏覽")] 72 | public void UTF16Test(string value) 73 | { 74 | int length = value.Length * sizeof(char); 75 | 76 | for (int i = 0; i < 4 * length; i += length) 77 | { StringHelper.WriteUTF16(m_buffer, i, value); } 78 | 79 | for (int i = 0; i < 4 * length; i += length) 80 | { Assert.AreEqual(value, StringHelper.ReadUTF16(m_buffer, i, value.Length)); } 81 | } 82 | 83 | [TestCase("Test.")] 84 | public void ANSITest(string value) 85 | { 86 | int length = value.Length; 87 | 88 | for (int i = 0; i < 4 * length; i += length) 89 | { StringHelper.WriteANSI(m_buffer, i, value); } 90 | 91 | for (int i = 0; i < 4 * length; i += length) 92 | { Assert.AreEqual(value, StringHelper.ReadANSI(m_buffer, i, value.Length)); } 93 | } 94 | 95 | [TestCase("手機瀏覽")] 96 | [TestCase("")] 97 | public void EncodingTest(string value) 98 | { 99 | var encoding = Encoding.UTF7; 100 | 101 | int byteCount = StringHelper.WriteString(m_buffer, 0, value, encoding); 102 | 103 | Assert.AreEqual(value, StringHelper.ReadString(m_buffer, 0, byteCount, encoding)); 104 | } 105 | 106 | [Test] 107 | public void EncodingFaultTest() 108 | { 109 | var value = @""; 110 | 111 | // Use a buffer that is too small for the string. 112 | var buffer = new byte[8]; 113 | var encoding = Encoding.UTF7; 114 | 115 | Assert.Catch(() => 116 | { 117 | StringHelper.WriteString(buffer, 0, value, encoding); 118 | }); 119 | 120 | int byteCount = encoding.GetByteCount(value); 121 | 122 | Assert.Catch(() => 123 | { 124 | Assert.AreEqual(value, StringHelper.ReadString(buffer, 0, byteCount, encoding)); 125 | }); 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamTest/BinaryHelperTest.cs: -------------------------------------------------------------------------------- 1 | using ByteStream; 2 | using ByteStreamTest.Data; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace ByteStreamTest 10 | { 11 | [TestFixture] 12 | public class BinaryHelperTest 13 | { 14 | private class IntPtrTest 15 | { 16 | private IntPtr m_buffer; 17 | 18 | [SetUp] 19 | public void Setup() 20 | { 21 | m_buffer = Marshal.AllocHGlobal(64); 22 | } 23 | [TearDown] 24 | public void Teardown() 25 | { 26 | if (m_buffer != IntPtr.Zero) 27 | Marshal.FreeHGlobal(m_buffer); 28 | } 29 | 30 | [TestCase(108971523)] 31 | public void IntTest(int value) 32 | { 33 | int length = sizeof(int); 34 | 35 | for (int i = 0; i < 4 * 4; i += length) 36 | { BinaryHelper.Write(m_buffer, i, value); } 37 | 38 | for (int i = 0; i < 4 * length; i += length) 39 | { Assert.AreEqual(value, BinaryHelper.Read(m_buffer, i)); } 40 | } 41 | 42 | [Test] 43 | public void BytesTest() 44 | { 45 | Assert.Catch(() => { BinaryHelper.WriteBytes(m_buffer, 0, null); }); 46 | 47 | byte[] value = new byte[4] { 1, 2, 3, 5 }; 48 | int length = 4; 49 | 50 | for (int i = 0; i < 4 * length; i += length) 51 | { BinaryHelper.WriteBytes(m_buffer, i, value); } 52 | 53 | for (int i = 0; i < 4 * length; i += length) 54 | { Assert.AreEqual(value, BinaryHelper.ReadBytes(m_buffer, i, 4)); } 55 | 56 | Assert.AreEqual(0, BinaryHelper.ReadBytes(m_buffer, 0, 0).Length); 57 | } 58 | 59 | [Test] 60 | public void StructTest() 61 | { 62 | BlittableStruct dat = new BlittableStruct() { ValOne = int.MaxValue, ValTwo = 123, ValThree = 321 }; 63 | int length = 16; 64 | 65 | for (int i = 0; i < 4 * length; i += length) 66 | { BinaryHelper.Write(m_buffer, i, dat); } 67 | 68 | for (int i = 0; i < 4 * length; i += length) 69 | { 70 | var other = BinaryHelper.Read(m_buffer, i); 71 | Assert.True(other.IsEqual(dat)); 72 | } 73 | } 74 | } 75 | private class ByteArrTest 76 | { 77 | private byte[] m_buffer; 78 | 79 | [SetUp] 80 | public void Setup() 81 | { 82 | m_buffer = new byte[64]; 83 | } 84 | [TearDown] 85 | public void Teardown() 86 | { 87 | 88 | } 89 | 90 | [Test] 91 | public void BytesTest() 92 | { 93 | byte[] value = new byte[4] { 1, 2, 3, 5 }; 94 | int length = 4; 95 | 96 | for (int i = 0; i < 4 * length; i += length) 97 | { BinaryHelper.WriteBytes(m_buffer, i, value); } 98 | 99 | for (int i = 0; i < 4 * length; i += length) 100 | { Assert.AreEqual(value, BinaryHelper.ReadBytes(m_buffer, i, 4)); } 101 | 102 | Assert.AreEqual(0, BinaryHelper.ReadBytes(m_buffer, 0, 0).Length); 103 | } 104 | 105 | [TestCase(108971523)] 106 | public void IntTest(int value) 107 | { 108 | int length = sizeof(int); 109 | 110 | for (int i = 0; i < 4 * 4; i += length) 111 | { BinaryHelper.Write(m_buffer, i, value); } 112 | 113 | for (int i = 0; i < 4 * length; i += length) 114 | { Assert.AreEqual(value, BinaryHelper.Read(m_buffer, i)); } 115 | } 116 | 117 | [Test] 118 | public void StructTest() 119 | { 120 | BlittableStruct dat = new BlittableStruct() { ValOne = int.MaxValue, ValTwo = 123, ValThree = 321 }; 121 | int length = 16; 122 | 123 | for (int i = 0; i < 4 * length; i += length) 124 | { BinaryHelper.Write(m_buffer, i, dat); } 125 | 126 | for (int i = 0; i < 4 * length; i += length) 127 | { 128 | var other = BinaryHelper.Read(m_buffer, i); 129 | Assert.True(other.IsEqual(dat)); 130 | } 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/BinaryHelper.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Utils; 2 | using ByteStream.Utils.Unsafe; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace ByteStream 10 | { 11 | public unsafe static class BinaryHelper 12 | { 13 | /// 14 | /// Writes a byte array to the specified array. 15 | /// 16 | /// The destination byte array. 17 | /// The current write offset. 18 | /// The value to write to the destination. 19 | public static void WriteBytes(byte[] dest, int offset, byte[] value) 20 | { 21 | value.CopyToUnsafe(0, dest, offset, value.Length); 22 | } 23 | 24 | /// 25 | /// Writes a byte array to the specified memory. 26 | /// 27 | /// The destination memory. 28 | /// The current write offset. 29 | /// The value to write to the destination. 30 | public static void WriteBytes(IntPtr dest, int offset, byte[] value) 31 | { 32 | if (value == null) 33 | throw new ArgumentNullException(nameof(value)); 34 | 35 | fixed (byte* src = value) 36 | { 37 | Buffer.MemoryCopy(src, (void*)(dest + offset), value.Length, value.Length); 38 | } 39 | } 40 | 41 | /// 42 | /// Writes a blittable value type to the specified byte array. 43 | /// 44 | /// The destination byte array. 45 | /// The current write offset. 46 | /// The value to write to the destination. 47 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 48 | public static void Write(byte[] dest, int offset, T value) where T : unmanaged 49 | { 50 | fixed (byte* ptr = &dest[offset]) 51 | { 52 | *(T*)ptr = value; 53 | } 54 | } 55 | 56 | /// 57 | /// Writes a blittable value type to the specified memory. 58 | /// 59 | /// The destination memory. 60 | /// The current write offset. 61 | /// The value to write to the destination. 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | public static void Write(IntPtr dest, int offset, T value) where T : unmanaged 64 | { 65 | *(T*)(dest + offset) = value; 66 | } 67 | 68 | /// 69 | /// Writes a blittable value type to the specified memory. 70 | /// 71 | /// The destination memory. 72 | /// The current write offset. 73 | /// The value to write to the destination. 74 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 75 | public static void Write(void* dest, int offset, T value) where T : unmanaged 76 | { 77 | *(T*)((byte*)dest + offset) = value; 78 | } 79 | 80 | /// 81 | /// Reads a byte-memory from the source memory. 82 | /// 83 | /// The source memory. 84 | /// The offset at which to read. 85 | /// The total length of the byte buffer to read as an int32 86 | public static byte[] ReadBytes(IntPtr data, int offset, int length) 87 | { 88 | if (length < 1) 89 | return new byte[0]; 90 | 91 | byte[] result = new byte[length]; 92 | 93 | fixed (byte* ptr = result) 94 | { 95 | Buffer.MemoryCopy((byte*)data + offset, ptr, length, length); 96 | } 97 | 98 | return result; 99 | } 100 | 101 | /// 102 | /// Reads a byte-array from the source array. 103 | /// 104 | /// The source array. 105 | /// The current read offset. 106 | /// The total length of the byte buffer to read as an int32 107 | public static byte[] ReadBytes(byte[] bin, int offset, int length) 108 | { 109 | if (length < 1) 110 | return new byte[0]; 111 | 112 | byte[] result = new byte[length]; 113 | 114 | bin.CopyToUnsafe(offset, result, 0, length); 115 | return result; 116 | } 117 | 118 | /// 119 | /// Reads a blittable value type from the source array. 120 | /// 121 | /// The source array. 122 | /// The current read offset. 123 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 124 | public static T Read(byte[] bin, int offset) where T : unmanaged 125 | { 126 | fixed (byte* ptr = &bin[offset]) 127 | { 128 | return *(T*)ptr; 129 | } 130 | } 131 | 132 | /// 133 | /// Reads a blittable value type from the source memory. 134 | /// 135 | /// The source memory. 136 | /// The current read offset. 137 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 138 | public static T Read(IntPtr data, int offset) where T : unmanaged 139 | { 140 | return *(T*)(data + offset); 141 | } 142 | 143 | /// 144 | /// Reads a blittable value type from the source memory. 145 | /// 146 | /// The source memory. 147 | /// The current read offset. 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | public static T Read(void* data, int offset) where T : unmanaged 150 | { 151 | return *(T*)((byte*)data + offset); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | Test/ 27 | Results/ 28 | 29 | # Visual Studio 2015/2017 cache/options directory 30 | .vs/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # Visual Studio 2017 auto generated files 35 | Generated\ Files/ 36 | 37 | # MSTest test Results 38 | [Tt]est[Rr]esult*/ 39 | [Bb]uild[Ll]og.* 40 | 41 | # NUNIT 42 | *.VisualState.xml 43 | TestResult.xml 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # Benchmark Results 51 | BenchmarkDotNet.Artifacts/ 52 | 53 | # .NET Core 54 | project.lock.json 55 | project.fragment.lock.json 56 | artifacts/ 57 | **/Properties/launchSettings.json 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_i.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *.log 83 | *.vspscc 84 | *.vssscc 85 | .builds 86 | *.pidb 87 | *.svclog 88 | *.scc 89 | 90 | # Chutzpah Test files 91 | _Chutzpah* 92 | 93 | # Visual C++ cache files 94 | ipch/ 95 | *.aps 96 | *.ncb 97 | *.opendb 98 | *.opensdf 99 | *.sdf 100 | *.cachefile 101 | *.VC.db 102 | *.VC.VC.opendb 103 | 104 | # Visual Studio profiler 105 | *.psess 106 | *.vsp 107 | *.vspx 108 | *.sap 109 | 110 | # Visual Studio Trace Files 111 | *.e2e 112 | 113 | # TFS 2012 Local Workspace 114 | $tf/ 115 | 116 | # Guidance Automation Toolkit 117 | *.gpState 118 | 119 | # ReSharper is a .NET coding add-in 120 | _ReSharper*/ 121 | *.[Rr]e[Ss]harper 122 | *.DotSettings.user 123 | 124 | # JustCode is a .NET coding add-in 125 | .JustCode 126 | 127 | # TeamCity is a build add-in 128 | _TeamCity* 129 | 130 | # DotCover is a Code Coverage Tool 131 | *.dotCover 132 | 133 | # AxoCover is a Code Coverage Tool 134 | .axoCover/* 135 | !.axoCover/settings.json 136 | 137 | # Visual Studio code coverage results 138 | *.coverage 139 | *.coveragexml 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | nCrunchTemp_* 145 | 146 | # MightyMoose 147 | *.mm.* 148 | AutoTest.Net/ 149 | 150 | # Web workbench (sass) 151 | .sass-cache/ 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # Note: Comment the next line if you want to checkin your web deploy settings, 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/[Pp]ackages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/[Pp]ackages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/[Pp]ackages/repositories.config 190 | # NuGet v3's project.json files produces more ignorable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Windows Store app package directories and files 203 | AppPackages/ 204 | BundleArtifacts/ 205 | Package.StoreAssociation.xml 206 | _pkginfo.txt 207 | *.appx 208 | 209 | # Visual Studio cache files 210 | # files ending in .cache can be ignored 211 | *.[Cc]ache 212 | # but keep track of directories ending in .cache 213 | !*.[Cc]ache/ 214 | 215 | # Others 216 | ClientBin/ 217 | ~$* 218 | *~ 219 | *.dbmdl 220 | *.dbproj.schemaview 221 | *.jfm 222 | *.pfx 223 | *.publishsettings 224 | orleans.codegen.cs 225 | 226 | # Including strong name files can present a security risk 227 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 228 | #*.snk 229 | 230 | # Since there are multiple workflows, uncomment next line to ignore bower_components 231 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 232 | #bower_components/ 233 | 234 | # RIA/Silverlight projects 235 | Generated_Code/ 236 | 237 | # Backup & report files from converting an old project file 238 | # to a newer Visual Studio version. Backup files are not needed, 239 | # because we have git ;-) 240 | _UpgradeReport_Files/ 241 | Backup*/ 242 | UpgradeLog*.XML 243 | UpgradeLog*.htm 244 | ServiceFabricBackup/ 245 | *.rptproj.bak 246 | 247 | # SQL Server files 248 | *.mdf 249 | *.ldf 250 | *.ndf 251 | 252 | # Business Intelligence projects 253 | *.rdl.data 254 | *.bim.layout 255 | *.bim_*.settings 256 | *.rptproj.rsuser 257 | 258 | # Microsoft Fakes 259 | FakesAssemblies/ 260 | 261 | # GhostDoc plugin setting file 262 | *.GhostDoc.xml 263 | 264 | # Node.js Tools for Visual Studio 265 | .ntvs_analysis.dat 266 | node_modules/ 267 | 268 | # Visual Studio 6 build log 269 | *.plg 270 | 271 | # Visual Studio 6 workspace options file 272 | *.opt 273 | 274 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 275 | *.vbw 276 | 277 | # Visual Studio LightSwitch build output 278 | **/*.HTMLClient/GeneratedArtifacts 279 | **/*.DesktopClient/GeneratedArtifacts 280 | **/*.DesktopClient/ModelManifest.xml 281 | **/*.Server/GeneratedArtifacts 282 | **/*.Server/ModelManifest.xml 283 | _Pvt_Extensions 284 | 285 | # Paket dependency manager 286 | .paket/paket.exe 287 | paket-files/ 288 | 289 | # FAKE - F# Make 290 | .fake/ 291 | 292 | # JetBrains Rider 293 | .idea/ 294 | *.sln.iml 295 | 296 | # CodeRush 297 | .cr/ 298 | 299 | # Python Tools for Visual Studio (PTVS) 300 | __pycache__/ 301 | *.pyc 302 | 303 | # Cake - Uncomment if you are using it 304 | # tools/** 305 | # !tools/packages.config 306 | 307 | # Tabs Studio 308 | *.tss 309 | 310 | # Telerik's JustMock configuration file 311 | *.jmconfig 312 | 313 | # BizTalk build output 314 | *.btp.cs 315 | *.btm.cs 316 | *.odx.cs 317 | *.xsd.cs 318 | 319 | # OpenCover UI analysis results 320 | OpenCover/ 321 | 322 | # Azure Stream Analytics local run output 323 | ASALocalRun/ 324 | 325 | # MSBuild Binary and Structured Log 326 | *.binlog 327 | 328 | # NVidia Nsight GPU debugger configuration file 329 | *.nvuser 330 | 331 | # MFractors (Xamarin productivity tool) working folder 332 | .mfractor/ 333 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Mananged/ByteReader.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using ByteStream.Utils; 3 | using System; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | 7 | namespace ByteStream.Mananged 8 | { 9 | public struct ByteReader : IReader 10 | { 11 | #pragma warning disable IDE0032 12 | private readonly byte[] m_buffer; 13 | private readonly int m_length; 14 | private int m_offset; 15 | #pragma warning restore IDE0032 16 | 17 | /// 18 | /// The length of this . 19 | /// 20 | public int Length 21 | => m_length; 22 | /// 23 | /// The current read offset. 24 | /// 25 | public int Offset 26 | => m_offset; 27 | /// 28 | /// Determines if the has a fixed size. 29 | /// 30 | public bool IsFixedSize 31 | => true; 32 | /// 33 | /// Gets the internal buffer used by this . 34 | /// Do not modify the buffer while performing read operations. 35 | /// 36 | public byte[] Buffer 37 | => m_buffer; 38 | 39 | 40 | /// 41 | /// Creates a new instance of . 42 | /// 43 | /// The data to read. 44 | public ByteReader(byte[] data) 45 | { 46 | m_buffer = data ?? throw new ArgumentNullException("data"); 47 | 48 | m_offset = 0; 49 | m_length = data.Length; 50 | } 51 | 52 | /// 53 | /// Creates a new instance of . 54 | /// 55 | /// The data to read. 56 | /// The read offset. 57 | /// The total amount of bytes available for reading. 58 | public ByteReader(byte[] data, int offset, int length) 59 | { 60 | m_buffer = data ?? throw new ArgumentNullException(nameof(data)); 61 | 62 | if ((uint)(offset + length) > data.Length) 63 | throw new ArgumentOutOfRangeException("Offset + Length must be smaller than array length."); 64 | 65 | m_offset = offset; 66 | m_length = length; 67 | } 68 | 69 | /// 70 | /// Increases the read offset by some amount. 71 | /// 72 | /// The amounts of bytes to skip. 73 | public void SkipBytes(int amount) 74 | { 75 | if (amount < 1) 76 | throw new ArgumentOutOfRangeException(nameof(amount)); 77 | 78 | EnsureCapacity(amount); 79 | m_offset += amount; 80 | } 81 | 82 | /// 83 | /// Resets the offset to its original position. 84 | /// 85 | public void Clear() 86 | { 87 | m_offset = 0; 88 | } 89 | 90 | /// 91 | /// Reads a blittable struct or primitive value from the . 92 | /// 93 | /// The type of the blittable struct/primitive. 94 | public T Read() where T : unmanaged 95 | { 96 | unsafe 97 | { 98 | int size = sizeof(T); 99 | EnsureCapacity(size); 100 | T val = BinaryHelper.Read(m_buffer, m_offset); 101 | 102 | m_offset += size; 103 | return val; 104 | } 105 | } 106 | /// 107 | /// Tries to read a blittable struct or primitive value from the . 108 | /// 109 | /// The type of the blittable struct/primitive. 110 | /// Returns false if the value couldn't be read. 111 | public bool TryRead(out T value) where T : unmanaged 112 | { 113 | value = default; 114 | 115 | unsafe 116 | { 117 | int size = sizeof(T); 118 | if (m_offset + size > Length) 119 | return false; 120 | 121 | value = BinaryHelper.Read(m_buffer, m_offset); 122 | m_offset += size; 123 | } 124 | 125 | return true; 126 | } 127 | 128 | /// 129 | /// Reads a byte-array from the . Length is automatically read as an uint16. 130 | /// 131 | public byte[] ReadBytes() 132 | { 133 | ushort length = Read(); 134 | return ReadBytes(length); 135 | } 136 | /// 137 | /// Reads a byte-array from the . 138 | /// Does NOT automatically read length. 139 | /// 140 | /// The length of the byte-array. 141 | /// 142 | public byte[] ReadBytes(int length) 143 | { 144 | EnsureCapacity(length); 145 | byte[] val = BinaryHelper.ReadBytes(m_buffer, m_offset, length); 146 | 147 | m_offset += length; 148 | return val; 149 | } 150 | 151 | /// 152 | /// Reads a string as a double-byte character set. Length is automatically retrieved as an uint16. 153 | /// 154 | public string ReadUTF16() 155 | { 156 | ushort length = Read(); 157 | return ReadUTF16(length); 158 | } 159 | /// 160 | /// Reads a string as a double-byte character set. Each character requires 2 bytes. 161 | /// Does NOT automatically read length. 162 | /// 163 | public string ReadUTF16(int stringLength) 164 | { 165 | EnsureCapacity(stringLength * sizeof(char)); 166 | string val = StringHelper.ReadUTF16(m_buffer, m_offset, stringLength); 167 | 168 | m_offset += stringLength * sizeof(char); 169 | return val; 170 | } 171 | 172 | /// 173 | /// Reads a string in ANSI encoding. Length is automatically retrieved as an uint16. 174 | /// 175 | public string ReadANSI() 176 | { 177 | ushort length = Read(); 178 | return ReadANSI(length); 179 | } 180 | /// 181 | /// Reads a string in ANSI encoding. Each character requires 1 byte. 182 | /// Does NOT automatically read length. 183 | /// 184 | public string ReadANSI(int stringLength) 185 | { 186 | EnsureCapacity(stringLength); 187 | string val = StringHelper.ReadANSI(m_buffer, m_offset, stringLength); 188 | 189 | m_offset += stringLength; 190 | return val; 191 | } 192 | 193 | /// 194 | /// Reads a string from the . 195 | /// Length is automatically retrieved. 196 | /// 197 | /// 198 | public string ReadString(Encoding encoding) 199 | { 200 | int byteCount = Read(); 201 | 202 | EnsureCapacity(byteCount); 203 | return StringHelper.ReadString(m_buffer, m_offset, byteCount, encoding); 204 | } 205 | 206 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 207 | private void EnsureCapacity(int bytesToRead) 208 | { 209 | if (bytesToRead + m_offset > m_length) 210 | ExceptionHelper.ThrowReadBufferExceeded(); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Unmanaged/PtrReader.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using ByteStream.Utils; 3 | using System; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | 7 | namespace ByteStream.Unmanaged 8 | { 9 | public struct PtrReader : IReader 10 | { 11 | #pragma warning disable IDE0032 12 | private readonly IntPtr m_buffer; 13 | private readonly int m_length; 14 | private int m_offset; 15 | #pragma warning restore IDE0032 16 | 17 | /// 18 | /// The length of this . 19 | /// 20 | public int Length 21 | => m_length; 22 | /// 23 | /// The current read offset. 24 | /// 25 | public int Offset 26 | => m_offset; 27 | /// 28 | /// Gets the internal buffer used by the . 29 | /// Do not modify the buffer while performing read operations. 30 | /// 31 | public IntPtr Buffer 32 | => m_buffer; 33 | 34 | /// 35 | /// Creates a new . 36 | /// 37 | /// The data to be read by the . 38 | /// The amount of bytes that can be read. 39 | public PtrReader(IntPtr buffer, int length) 40 | : this(buffer, 0, length) 41 | { } 42 | 43 | /// 44 | /// Creates a new . 45 | /// 46 | /// The data to be read by the . 47 | /// The amount of bytes that can be read. 48 | /// The read offset. 49 | public PtrReader(IntPtr buffer, int offset, int length) 50 | { 51 | if (buffer == IntPtr.Zero) 52 | throw new ArgumentNullException(nameof(buffer)); 53 | 54 | if (length < 0) 55 | throw new ArgumentOutOfRangeException(nameof(length)); 56 | 57 | if ((uint)offset > length) 58 | { throw new ArgumentOutOfRangeException("Out of bounds."); } 59 | 60 | m_offset = offset; 61 | m_length = length; 62 | m_buffer = buffer; 63 | } 64 | 65 | 66 | /// 67 | /// Increases the read offset by some amount. 68 | /// 69 | /// The amounts of bytes to skip. 70 | public void SkipBytes(int amount) 71 | { 72 | if (amount < 1) 73 | throw new ArgumentOutOfRangeException(nameof(amount)); 74 | 75 | EnsureCapacity(amount); 76 | m_offset += amount; 77 | } 78 | 79 | /// 80 | /// Resets the offset to zero. 81 | /// 82 | public void Clear() 83 | { 84 | m_offset = 0; 85 | } 86 | 87 | 88 | /// 89 | /// Reads a byte-array from the . Length is automatically read as an uint16. 90 | /// 91 | public byte[] ReadBytes() 92 | { 93 | ushort length = Read(); 94 | return ReadBytes(length); 95 | } 96 | 97 | /// 98 | /// Reads a byte-array from the . 99 | /// Does NOT automatically read length. 100 | /// 101 | /// The length of the byte-array. 102 | /// 103 | public byte[] ReadBytes(int length) 104 | { 105 | EnsureCapacity(length); 106 | byte[] val = BinaryHelper.ReadBytes(m_buffer, m_offset, length); 107 | 108 | m_offset += length; 109 | return val; 110 | } 111 | 112 | 113 | /// 114 | /// Reads a string as a double-byte character set. Length is automatically retrieved as an uint16. 115 | /// 116 | public string ReadUTF16() 117 | { 118 | ushort length = Read(); 119 | return ReadUTF16(length); 120 | } 121 | 122 | /// 123 | /// Reads a string as a double-byte character set. Each character requires 2 bytes. 124 | /// Does NOT automatically read length. 125 | /// 126 | /// 127 | public string ReadUTF16(int stringLength) 128 | { 129 | EnsureCapacity(stringLength * sizeof(char)); 130 | string val = StringHelper.ReadUTF16(m_buffer, m_offset, stringLength); 131 | 132 | m_offset += stringLength * sizeof(char); 133 | return val; 134 | } 135 | 136 | 137 | /// 138 | /// Reads a string in ANSI encoding. Length is automatically retrieved as an uint16. 139 | /// 140 | public string ReadANSI() 141 | { 142 | ushort length = Read(); 143 | return ReadANSI(length); 144 | } 145 | 146 | /// 147 | /// Reads a string in ANSI encoding. Each character requires 1 byte. 148 | /// Does NOT automatically read length. 149 | /// 150 | public string ReadANSI(int stringLength) 151 | { 152 | EnsureCapacity(stringLength); 153 | string val = StringHelper.ReadANSI(m_buffer, m_offset, stringLength); 154 | 155 | m_offset += stringLength; 156 | return val; 157 | } 158 | 159 | /// 160 | /// Reads a string from the . 161 | /// Length is automatically retrieved. 162 | /// 163 | /// 164 | public string ReadString(Encoding encoding) 165 | { 166 | int byteCount = Read(); 167 | 168 | EnsureCapacity(byteCount); 169 | return StringHelper.ReadString(m_buffer, m_offset, byteCount, encoding); 170 | } 171 | 172 | /// 173 | /// Reads a blittable struct or primitive value from the . 174 | /// 175 | /// The type of the blittable struct/primitive. 176 | public T Read() where T : unmanaged 177 | { 178 | return ReadValueInternal(); 179 | } 180 | 181 | /// 182 | /// Tries to read a blittable struct or primitive value from the . 183 | /// 184 | /// The type of the blittable struct/primitive. 185 | /// Returns false if the value couldn't be read. 186 | public bool TryRead(out T value) where T : unmanaged 187 | { 188 | value = default; 189 | 190 | unsafe 191 | { 192 | int size = sizeof(T); 193 | if (m_offset + size > m_length) { return false; } 194 | value = BinaryHelper.Read(m_buffer, m_offset); 195 | m_offset += size; 196 | } 197 | return true; 198 | } 199 | 200 | 201 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 202 | private unsafe T ReadValueInternal() where T : unmanaged 203 | { 204 | int size = sizeof(T); 205 | EnsureCapacity(size); 206 | T val = BinaryHelper.Read(m_buffer, m_offset); 207 | 208 | m_offset += size; 209 | return val; 210 | } 211 | 212 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 213 | private void EnsureCapacity(int bytesToRead) 214 | { 215 | if (bytesToRead + m_offset > m_length) 216 | ExceptionHelper.ThrowReadBufferExceeded(); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ByteStream 2 | A blazing fast byte (de)serializer for C#. 3 | 4 | Available on NuGet via ```Install-Package ByteStream``` 5 | 6 | ## What is ByteStream? 7 | Bytestream is a small library that enables blazing fast serialization of a collection of types to raw bytes. Either in the form of a byte array `byte[]` or unmanaged memory `IntPtr`. 8 | The library performs no memory allocation on its own, so you are free to use your own memory allocator! 9 | 10 | ByteStream is written in NetStandard2.0 making it compatible with .Net, .Net Core and Unity3D among things! 11 | 12 | *__Carefully read the Usage section__ for a brief introduction on how to use the library.* 13 | 14 | ## What does ByteStream offer? 15 | Bytestream offers 2 sets of serializers and deserializers, called writers and readers. They are solely used for manual serialization and deserialization of the supported types. 16 | - Easy to understand, simple syntax. 17 | - Lightweight, no memory allocation. 18 | - Extremely fast, using pointer conversion to read and write data. 19 | - Uses memory copy to quickly copy large blocks of memory to and from the buffers. 20 | - Comes in a managed and unmanaged variant. 21 | - Auto-resizable, managed buffer (optional). 22 | - Automatically keeps track of offsets and buffer boundaries. 23 | - Prefixes strings and memory blocks with a 2-byte length (optional). 24 | 25 | ## Supported types: 26 | - All types that adhere to the [unmanaged constraint](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types) can be written to, and read from the buffers. 27 | - Byte arrays 28 | - ANSI strings (1-byte per character) 29 | - UTF16 strings (2-byte per character, default in C#) 30 | - Any string that's supported by .NET's Encoding class. 31 | 32 | *User-defined structs may change their layout when used on another system. Don't serialize user-defined structs unless you are absolutely certain the layout will be the same everywhere it is read back.* 33 | 34 | ## Drawbacks 35 | - The ByteWriter and ByteReader makes heavy use of memory pinning. This can make the unmanaged variant preferable when possible. 36 | - The writers and readers always make use of pointer conversion. In the case of writing single-byte values to a `byte[]` buffer, consider directly writing these values to the buffer instead. 37 | 38 | # Technical specifications: 39 | 40 | ## Comparison to other methods: 41 | Writing of 1024 integer (4-byte) values to a buffer. 42 | 43 | | Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | 44 | |---------------- |----------:|----------:|----------:|------:|-------:|----------:| 45 | | ByteWrite | 2.199 us | 0.0048 us | 0.0043 us | 0.57 | - | - | 46 | | PtrWrite | 1.651 us | 0.0074 us | 0.0069 us | 0.43 | - | - | 47 | | ManagedStream | 1.705 us | 0.0012 us | 0.0010 us | 0.44 | - | - | 48 | | UnmanagedStream | 1.351 us | 0.0058 us | 0.0055 us | 0.35 | - | - | 49 | | ArraySegment | 3.875 us | 0.0050 us | 0.0042 us | 1.00 | - | - | 50 | | BitConvert | 10.762 us | 0.0227 us | 0.0201 us | 2.78 | 5.1880 | 32768 B | 51 | | Union | 9.001 us | 0.0438 us | 0.0410 us | 2.32 | - | - | 52 | 53 | 54 | Reading of 1024 integer (4-byte) values from a buffer. 55 | 56 | | Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | 57 | |---------------- |---------:|----------:|----------:|------:|------:|----------:| 58 | | ByteRead | 1.311 us | 0.0045 us | 0.0042 us | 0.35 | - | - | 59 | | PtrRead | 1.239 us | 0.0055 us | 0.0052 us | 0.33 | - | - | 60 | | ManagedStream | 1.172 us | 0.0012 us | 0.0012 us | 0.32 | - | - | 61 | | UnmanagedStream | 1.192 us | 0.0048 us | 0.0045 us | 0.32 | - | - | 62 | | ArraySegment | 3.712 us | 0.0085 us | 0.0080 us | 1.00 | - | - | 63 | | BitConvert | 1.542 us | 0.0046 us | 0.0043 us | 0.42 | - | - | 64 | | Union | 9.421 us | 0.0336 us | 0.0314 us | 2.54 | - | - | 65 | 66 | When using ArraySegment, BitConverter and Unioning structs the user needs to keep track of the offsets themselves. Manual bitshifting may also be required to read and write values. 67 | 68 | # Usage 69 | 70 | Because the API is almost identical, the managed and unmanaged readers and writers function nearly identical. Be warned that when using the unmanaged reader or writer, the provided length must be no longer than the actual length of the memory block provided. 71 | 72 | It is important that you write and read back the values in the same order to keep data consistent (as shown in the write and read example). 73 | 74 | Because the readers and writers are value types, they must be passed along using the `ref` keyword. 75 | 76 | **The readers and writers are not thread safe! Beware of accessing buffers concurrently from multiple threads and modifying the supplied buffers during operation!** 77 | 78 | 79 | ### Writing 80 | 81 | Writing of values is really easy. Just wrap a buffer and you can start writing! 82 | 83 | ```C# 84 | //Creating a simple buffer that resizes when the limit it met. 85 | ByteWriter writer = new ByteWriter(64, false); 86 | 87 | //Wrapping a pre-existing buffer 88 | byte[] buffer = new byte[128]; 89 | ByteWriter writer = new ByteWriter(buffer); 90 | 91 | //Writing an ANSI string with a prefixed length. 92 | writer.WriteANSI("Writing an integer value...", true); 93 | //Writing an integer value. 94 | writer.Write(591823); 95 | 96 | //Copying the internal buffer to a new byte array. 97 | byte[] copy = new byte[32]; 98 | writer.CopyTo(copy); 99 | 100 | //Gets the original buffer (is equal to the above defined "buffer") 101 | byte[] originalBuffer = writer.Buffer; 102 | 103 | ``` 104 | 105 | ### Reading 106 | 107 | Reading values is just as easy as writing them! Again, just wrap a buffer and you can start reading. 108 | 109 | ```C# 110 | byte[] buffer; //Some earlier defined buffer that holds data. 111 | ByteReader reader = new ByteReader(buffer); 112 | 113 | //Read an ANSI string (automatically grabs the prefixed size). 114 | string stringValue = reader.ReadANSI(); 115 | //Read an integer value. 116 | int intValue = reader.Read(); 117 | ``` 118 | 119 | ### Defining custom types 120 | 121 | We can create extension methods to allow serializing and deserializing of user-defined types (even classes!). 122 | __Be sure to add the `ref` keyword to ensure the offset gets incremented!__ 123 | 124 | ```C# 125 | public static void WritePoint(ref this ByteWriter writer, Point point) 126 | { 127 | writer.Write(point.X); writer.Write(point.Y); 128 | } 129 | 130 | public static Point ReadPoint(ref this ByteReader reader) 131 | { 132 | return new Point(reader.Read(), reader.Read()); 133 | } 134 | 135 | //We can then use these extension methods from anywhere else 136 | ByteWriter writer = new ByteWriter(buffer); 137 | writer.WritePoint(new Point(1, 2)); 138 | 139 | ByteReader reader = new ByteReader(buffer); 140 | var point = reader.ReadPoint(); 141 | ``` 142 | 143 | ## Streaming classes 144 | 145 | The ManagedSteam and the UnmanagedStream make serialization of objects even easier. Below is an example of how to serialize a class using the IByteStream interface that comes with both of the aforementioned Streams. 146 | 147 | The Stream can simply be passed to the Serialize method. Depending if the Stream is in Write or Read mode, the object is serialized or deserialized automatically. 148 | 149 | ```C# 150 | public class PlayerData 151 | { 152 | public string Name; 153 | public int Health; 154 | public float Speed; 155 | public PlayerInventory Inventory; 156 | 157 | public void Serialize(IByteStream stream) 158 | { 159 | stream.SerializeString(ref Name, Encoding.ASCII); 160 | stream.Serialize(ref Health); 161 | stream.Serialize(ref Speed); 162 | 163 | Inventory.Serialize(stream); 164 | } 165 | } 166 | ``` 167 | 168 | Extending either of the Stream classes (or the IByteStream Interface) is equally as easy as using them to serialize. Below is an example of a Serialize method for a Vector3 by extending IByteStream. 169 | 170 | ```C# 171 | internal static class Extension 172 | { 173 | public static void Serialize(this IByteStream stream, ref Vector3 vector) 174 | { 175 | stream.Serialize(ref vector.X); 176 | stream.Serialize(ref vector.Y); 177 | stream.Serialize(ref vector.Z); 178 | } 179 | } 180 | ``` 181 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/StringHelper.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Utils.Unsafe; 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace ByteStream 8 | { 9 | public unsafe static class StringHelper 10 | { 11 | private const int StrBufSize = 1024; 12 | 13 | /// 14 | /// Reads a string as a double-byte character set. Each character requires 2 bytes. 15 | /// 16 | /// The source array. 17 | /// The offset at which to read. 18 | /// The total length of the string to read. 19 | public static string ReadUTF16(byte[] data, int offset, int stringLength) 20 | { 21 | if (stringLength < 1) 22 | return string.Empty; 23 | 24 | if (data == null) 25 | throw new ArgumentNullException(nameof(data)); 26 | 27 | fixed (byte* ptr = &data[offset]) 28 | { 29 | return new string((char*)ptr, 0, stringLength); 30 | } 31 | } 32 | 33 | /// 34 | /// Reads a string as a double-byte character set. Each character requires 2 bytes. 35 | /// 36 | /// The source memory. 37 | /// The offset at which to read. 38 | /// The total length of the string to read. 39 | public static string ReadUTF16(IntPtr data, int offset, int stringLength) 40 | { 41 | if (stringLength < 1) 42 | return string.Empty; 43 | 44 | if (data == IntPtr.Zero) 45 | throw new ArgumentNullException(nameof(data)); 46 | 47 | return new string((char*)data, offset, stringLength); 48 | } 49 | 50 | 51 | /// 52 | /// Reads a string in ANSI encoding. Each character requires 1 byte. 53 | /// 54 | /// The source array. 55 | /// The offset at which to read. 56 | /// The total length of the string to read. 57 | public static string ReadANSI(byte[] data, int offset, int stringLength) 58 | { 59 | if (stringLength < 1) 60 | return string.Empty; 61 | 62 | if (data == null) 63 | throw new ArgumentNullException(nameof(data)); 64 | 65 | fixed (byte* ptr = data) 66 | { 67 | return new string((sbyte*)ptr, offset, stringLength); 68 | } 69 | } 70 | 71 | /// 72 | /// Reads a string in ANSI encoding. Each character requires 1 byte. 73 | /// 74 | /// The source memory. 75 | /// The offset at which to read. 76 | /// The total length of the string to read. 77 | public static string ReadANSI(IntPtr data, int offset, int stringLength) 78 | { 79 | if (stringLength < 1) 80 | return string.Empty; 81 | 82 | if (data == IntPtr.Zero) 83 | throw new ArgumentNullException(nameof(data)); 84 | 85 | return new string((sbyte*)data, offset, stringLength); 86 | } 87 | 88 | 89 | /// 90 | /// Writes a string as a double-byte character set. Each character requires 2 bytes. 91 | /// 92 | /// The destination byte array. 93 | /// The current write offset. 94 | /// The value to write to the destination. 95 | public static int WriteUTF16(byte[] dest, int offset, string value) 96 | { 97 | if (dest == null) 98 | throw new ArgumentNullException(nameof(dest)); 99 | 100 | int length = value.Length * sizeof(char); 101 | 102 | fixed (byte* ptr = &dest[offset]) 103 | fixed (char* str = value) 104 | { 105 | Buffer.MemoryCopy(str, ptr, length, length); 106 | } 107 | 108 | return length; 109 | } 110 | 111 | /// 112 | /// Writes a string as a double-byte character set. Each character requires 2 bytes. 113 | /// Does NOT include the length. 114 | /// 115 | /// The destination memory. 116 | /// The current write offset. 117 | /// The value to write to the destination. 118 | public static int WriteUTF16(IntPtr dest, int offset, string value) 119 | { 120 | if (dest == IntPtr.Zero) 121 | throw new ArgumentNullException(nameof(dest)); 122 | 123 | for (int i = 0; i < value.Length; i++) 124 | *((char*)dest + offset + i) = value[i]; 125 | 126 | return value.Length * sizeof(char); 127 | } 128 | 129 | 130 | /// 131 | /// Writes a string in ANSI encoding. Each character requires 1 byte. 132 | /// 133 | /// The destination byte array. 134 | /// The current write offset. 135 | /// The value to write to the destination. 136 | public static int WriteANSI(byte[] dest, int offset, string value) 137 | { 138 | if (dest == null) 139 | throw new ArgumentNullException(nameof(dest)); 140 | 141 | for (int i = 0; i < value.Length; i++) 142 | dest[offset + i] = (byte)value[i]; 143 | 144 | return value.Length; 145 | } 146 | 147 | /// 148 | /// Writes a string in ANSI encoding. Each character requires 1 byte. 149 | /// Does NOT include the length. 150 | /// 151 | /// The destination memory. 152 | /// The current write offset. 153 | /// The value to write to the destination. 154 | public static int WriteANSI(IntPtr dest, int offset, string value) 155 | { 156 | if (dest == IntPtr.Zero) 157 | throw new ArgumentNullException(nameof(dest)); 158 | 159 | for (int i = 0; i < value.Length; i++) 160 | *((byte*)dest + offset + i) = (byte)value[i]; 161 | 162 | return value.Length; 163 | } 164 | 165 | 166 | /// 167 | /// Writes a string to the supplied buffer. 168 | /// 169 | public static int WriteString(byte[] data, int offset, string value, Encoding encoding) 170 | { 171 | if (data == null) 172 | throw new ArgumentNullException(nameof(data)); 173 | 174 | if ((uint)offset > data.Length) 175 | throw new ArgumentOutOfRangeException(nameof(offset)); 176 | 177 | return encoding.GetBytes(value, 0, value.Length, data, offset); 178 | } 179 | 180 | /// 181 | /// Writes a string to the supplied buffer. 182 | /// 183 | /// The length of the supplied buffer. 184 | public static int WriteString(IntPtr data, int offset, int dataLength, string value, Encoding encoding) 185 | { 186 | if (data == IntPtr.Zero) 187 | throw new ArgumentNullException(nameof(data)); 188 | 189 | if ((uint)offset > dataLength) 190 | throw new ArgumentOutOfRangeException("Offset must be smaller than dataLength"); 191 | 192 | fixed (char* str = value) 193 | { 194 | return encoding.GetBytes(str, value.Length, ((byte*)data) + offset, dataLength); 195 | } 196 | } 197 | 198 | /// 199 | /// Reads a string from the supplied buffer. 200 | /// 201 | /// The total length in bytes of the endcoded string. 202 | public static string ReadString(byte[] data, int offset, int byteCount, Encoding encoding) 203 | { 204 | if (byteCount <= 0) 205 | return string.Empty; 206 | 207 | if ((uint)offset + byteCount > data.Length) 208 | throw new ArgumentOutOfRangeException("ByteCount + Offset is larger than data length."); 209 | 210 | fixed (byte* ptr = data) 211 | { 212 | return ReadString((IntPtr)ptr, offset, byteCount, encoding); 213 | } 214 | } 215 | 216 | /// 217 | /// Reads a string from the supplied buffer. 218 | /// 219 | /// The total length in bytes of the endcoded string. 220 | public static string ReadString(IntPtr data, int offset, int byteCount, Encoding encoding) 221 | { 222 | return new string((sbyte*)data, offset, byteCount, encoding); 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /ByteStream/ByteStreamBenchmark/Helper/ByteConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * 5 | * Copyright (c) 2012-2013 Fredrik Holmstrom (fredrik.johan.holmstrom@gmail.com) 6 | * Extended 2018-2019 Davin Carten [emotitron] (davincarten@gmail.com) 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | using System; 27 | using System.Runtime.InteropServices; 28 | 29 | namespace ByteStreamBenchmark.Helper 30 | { 31 | 32 | #pragma warning disable IDE0034 33 | /// 34 | /// Temporariliy using the Utilities namespace to avoid collisions with the DLL still in use by NST 35 | /// 36 | 37 | [StructLayout(LayoutKind.Explicit)] 38 | public struct ByteConverter 39 | { 40 | 41 | [FieldOffset(0)] 42 | public Single float32; 43 | [FieldOffset(0)] 44 | public Double float64; 45 | [FieldOffset(0)] 46 | public SByte int8; 47 | [FieldOffset(0)] 48 | public Int16 int16; 49 | [FieldOffset(0)] 50 | public UInt16 uint16; 51 | [FieldOffset(0)] 52 | public Char character; 53 | [FieldOffset(0)] 54 | public Int32 int32; 55 | [FieldOffset(0)] 56 | public UInt32 uint32; 57 | [FieldOffset(0)] 58 | public Int64 int64; 59 | [FieldOffset(0)] 60 | public UInt64 uint64; 61 | 62 | [FieldOffset(0)] 63 | public Byte byte0; 64 | [FieldOffset(1)] 65 | public Byte byte1; 66 | [FieldOffset(2)] 67 | public Byte byte2; 68 | [FieldOffset(3)] 69 | public Byte byte3; 70 | [FieldOffset(4)] 71 | public Byte byte4; 72 | [FieldOffset(5)] 73 | public Byte byte5; 74 | [FieldOffset(6)] 75 | public Byte byte6; 76 | [FieldOffset(7)] 77 | public Byte byte7; 78 | 79 | /// 80 | /// The upper 4 bytes of this 8 byte struct, returned as a uint. 81 | /// 82 | [FieldOffset(4)] 83 | public uint uint16_B; 84 | 85 | /// 86 | /// A Byte indexer. 87 | /// 88 | /// Byte index. 89 | /// Value of the byte at given index. 90 | public Byte this[int index] 91 | { 92 | get 93 | { 94 | switch (index) 95 | { 96 | case 0: return byte0; 97 | case 1: return byte1; 98 | case 2: return byte2; 99 | case 3: return byte3; 100 | case 4: return byte4; 101 | case 5: return byte5; 102 | case 6: return byte6; 103 | case 7: return byte7; 104 | default: 105 | System.Diagnostics.Debug.Assert((index >= 8), "Index value of " + index + " is invalid. ByteConverter this[] indexer must be a value between 0 and 7."); 106 | return 0; 107 | } 108 | } 109 | 110 | set 111 | { 112 | switch (index) 113 | { 114 | case 0: byte0 = value; return; 115 | case 1: byte1 = value; return; 116 | case 2: byte2 = value; return; 117 | case 3: byte3 = value; return; 118 | case 4: byte4 = value; return; 119 | case 5: byte5 = value; return; 120 | case 6: byte6 = value; return; 121 | case 7: byte7 = value; return; 122 | default: 123 | System.Diagnostics.Debug.Assert((index >= 8), "Index value of " + index + " is invalid. ByteConverter this[] indexer must be a value between 0 and 7."); 124 | return; 125 | } 126 | } 127 | } 128 | 129 | //public static UdpByteConverter BytesFromObject(object obj) 130 | //{ 131 | // byte[] incAsBytes = obj as byte[]; 132 | // UdpByteConverter bytes = default(UdpByteConverter); 133 | 134 | // bytes.Byte0 = incAsBytes[0]; 135 | // bytes.Byte1 = incAsBytes[1]; 136 | // bytes.Byte2 = incAsBytes[2]; 137 | // bytes.Byte3 = incAsBytes[3]; 138 | 139 | // return bytes; 140 | //} 141 | 142 | #region Implicit To ByteConverter 143 | 144 | public static implicit operator ByteConverter(byte[] bytes) 145 | { 146 | ByteConverter bc = default(ByteConverter); 147 | 148 | int len = bytes.Length; 149 | 150 | bc.byte0 = bytes[0]; 151 | if (len > 0) bc.byte1 = bytes[1]; 152 | if (len > 1) bc.byte2 = bytes[2]; 153 | if (len > 2) bc.byte3 = bytes[3]; 154 | if (len > 3) bc.byte4 = bytes[4]; 155 | if (len > 4) bc.byte5 = bytes[5]; 156 | if (len > 5) bc.byte6 = bytes[3]; 157 | if (len > 6) bc.byte7 = bytes[7]; 158 | 159 | return bc; 160 | } 161 | 162 | public static implicit operator ByteConverter(Byte val) 163 | { 164 | ByteConverter bc = default(ByteConverter); 165 | bc.byte0 = val; 166 | return bc; 167 | } 168 | 169 | public static implicit operator ByteConverter(SByte val) 170 | { 171 | ByteConverter bc = default(ByteConverter); 172 | bc.int8 = val; 173 | return bc; 174 | } 175 | 176 | public static implicit operator ByteConverter(Char val) 177 | { 178 | ByteConverter bc = default(ByteConverter); 179 | bc.character = val; 180 | return bc; 181 | } 182 | 183 | public static implicit operator ByteConverter(UInt32 val) 184 | { 185 | ByteConverter bc = default(ByteConverter); 186 | bc.uint32 = val; 187 | return bc; 188 | } 189 | 190 | public static implicit operator ByteConverter(Int32 val) 191 | { 192 | ByteConverter bc = default(ByteConverter); 193 | bc.int32 = val; 194 | return bc; 195 | } 196 | 197 | public static implicit operator ByteConverter(UInt64 val) 198 | { 199 | ByteConverter bc = default(ByteConverter); 200 | bc.uint64 = val; 201 | return bc; 202 | } 203 | 204 | public static implicit operator ByteConverter(Int64 val) 205 | { 206 | ByteConverter bc = default(ByteConverter); 207 | bc.int64 = val; 208 | return bc; 209 | } 210 | 211 | public static implicit operator ByteConverter(Single val) 212 | { 213 | ByteConverter bc = default(ByteConverter); 214 | bc.float32 = val; 215 | return bc; 216 | } 217 | 218 | public static implicit operator ByteConverter(Double val) 219 | { 220 | ByteConverter bc = default(ByteConverter); 221 | bc.float64 = val; 222 | return bc; 223 | } 224 | 225 | public static implicit operator ByteConverter(Boolean val) 226 | { 227 | ByteConverter bc = default(ByteConverter); 228 | bc.int32 = val ? 1 : 0; 229 | return bc; 230 | } 231 | 232 | public void ExtractByteArray(byte[] targetArray) 233 | { 234 | int len = targetArray.Length; 235 | 236 | targetArray[0] = byte0; 237 | if (len > 0) targetArray[1] = byte1; 238 | if (len > 1) targetArray[2] = byte2; 239 | if (len > 2) targetArray[3] = byte3; 240 | if (len > 3) targetArray[4] = byte4; 241 | if (len > 4) targetArray[5] = byte5; 242 | if (len > 5) targetArray[6] = byte6; 243 | if (len > 6) targetArray[7] = byte7; 244 | } 245 | 246 | #endregion 247 | 248 | #region Implicit From ByteConverter 249 | 250 | public static implicit operator Byte(ByteConverter bc) { return bc.byte0; } 251 | public static implicit operator SByte(ByteConverter bc) { return bc.int8; } 252 | public static implicit operator Char(ByteConverter bc) { return bc.character; } 253 | public static implicit operator UInt16(ByteConverter bc) { return bc.uint16; } 254 | public static implicit operator Int16(ByteConverter bc) { return bc.int16; } 255 | public static implicit operator UInt32(ByteConverter bc) { return bc.uint32; } 256 | public static implicit operator Int32(ByteConverter bc) { return bc.int32; } 257 | public static implicit operator UInt64(ByteConverter bc) { return bc.uint64; } 258 | public static implicit operator Int64(ByteConverter bc) { return bc.int64; } 259 | public static implicit operator Single(ByteConverter bc) { return bc.float32; } 260 | public static implicit operator Double(ByteConverter bc) { return bc.float64; } 261 | public static implicit operator Boolean(ByteConverter bc) { return bc.int32 != 0; } 262 | 263 | #endregion 264 | } 265 | } 266 | 267 | 268 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Unmanaged/PtrWriter.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using ByteStream.Utils; 3 | using ByteStream.Utils.Unsafe; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.CompilerServices; 7 | using System.Text; 8 | 9 | namespace ByteStream.Unmanaged 10 | { 11 | public unsafe struct PtrWriter : IWriter 12 | { 13 | #pragma warning disable IDE0032 14 | private readonly IntPtr m_buffer; 15 | private readonly int m_length; 16 | private int m_offset; 17 | #pragma warning restore IDE0032 18 | 19 | /// 20 | /// The length of the . 21 | /// 22 | public int Length 23 | => m_length; 24 | /// 25 | /// The current write offset. 26 | /// 27 | public int Offset 28 | => m_offset; 29 | /// 30 | /// Gets the internal buffer used by the . 31 | /// Do not modify the buffer while performing write operations. 32 | /// 33 | public IntPtr Buffer 34 | => m_buffer; 35 | 36 | /// 37 | /// Creates a new . 38 | /// 39 | /// The buffer to use with this . 40 | /// The amount of bytes that can be written. 41 | public PtrWriter(IntPtr buffer, int length) 42 | : this(buffer, 0, length) 43 | { } 44 | 45 | /// 46 | /// Creates a new . 47 | /// 48 | /// 49 | /// 50 | /// 51 | public PtrWriter(IntPtr buffer, int offset, int length) 52 | { 53 | if (buffer == IntPtr.Zero) 54 | throw new ArgumentNullException(nameof(buffer)); 55 | 56 | if (length < 0) 57 | throw new ArgumentOutOfRangeException(nameof(length)); 58 | 59 | if ((uint)offset > length) 60 | throw new ArgumentOutOfRangeException("Offset exceeds buffer length."); 61 | 62 | m_buffer = buffer; 63 | m_offset = offset; 64 | m_length = length; 65 | } 66 | 67 | /// 68 | /// Increases the write offset by some amount. 69 | /// 70 | /// The amounts of bytes to skip. 71 | public void SkipBytes(int amount) 72 | { 73 | EnsureCapacity(amount); 74 | m_offset += amount; 75 | } 76 | 77 | /// 78 | /// Resets the offset to zero. 79 | /// 80 | public void Clear() 81 | { 82 | m_offset = 0; 83 | } 84 | 85 | 86 | /// 87 | /// Reserves 4-bytes of space for a size value at the start of the . 88 | /// 89 | public void ReserveSizePrefix() 90 | { 91 | SkipBytes(sizeof(int)); 92 | } 93 | 94 | /// 95 | /// Writes the total size at the start of the as an . 96 | /// 97 | public int PrefixSize() 98 | { 99 | BinaryHelper.Write(m_buffer, 0, m_offset); 100 | return m_offset; 101 | } 102 | 103 | 104 | /// 105 | /// Writes a blittable struct or primitive value to the . 106 | /// 107 | /// The type of the blittable struct/primitive. 108 | public void Write(T value) where T : unmanaged 109 | { 110 | unsafe 111 | { 112 | int size = sizeof(T); 113 | EnsureCapacity(size); 114 | BinaryHelper.Write(m_buffer, m_offset, value); 115 | m_offset += size; 116 | } 117 | } 118 | 119 | /// 120 | /// Tries to write a blittable struct or primitive value to the . 121 | /// 122 | /// The type of the blittable struct/primitive. 123 | /// Returns false if the value couldn't be written. 124 | public bool TryWrite(T value) where T : unmanaged 125 | { 126 | unsafe 127 | { 128 | int size = sizeof(T); 129 | if (m_offset + size > m_length) 130 | return false; 131 | 132 | BinaryHelper.Write(m_buffer, m_offset, value); 133 | m_offset += size; 134 | } 135 | return true; 136 | } 137 | 138 | /// 139 | /// Writes a byte array. 140 | /// 141 | /// TRUE to include the size as an uint16 142 | public void WriteBytes(byte[] value, bool includeSize = false) 143 | { 144 | if (includeSize) 145 | { 146 | if (value.Length > ushort.MaxValue) 147 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 148 | Write((ushort)value.Length); 149 | } 150 | 151 | EnsureCapacity(value.Length); 152 | BinaryHelper.WriteBytes(m_buffer, m_offset, value); 153 | m_offset += value.Length; 154 | } 155 | 156 | /// 157 | /// Writes a string as a double-byte character set. Each character requires 2 bytes. 158 | /// 159 | /// TRUE to include the size as an uint16 160 | public void WriteUTF16(string value, bool includeSize = false) 161 | { 162 | if (includeSize) 163 | { 164 | if (value.Length > ushort.MaxValue) 165 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 166 | Write((ushort)value.Length); 167 | } 168 | 169 | EnsureCapacity(value.Length * sizeof(char)); 170 | StringHelper.WriteUTF16(m_buffer, m_offset, value); 171 | m_offset += value.Length * sizeof(char); 172 | } 173 | 174 | /// 175 | /// Writes a string in ANSI encoding. Each character requires 1 byte. 176 | /// 177 | /// 178 | public void WriteANSI(string value, bool includeSize = false) 179 | { 180 | if (includeSize) 181 | { 182 | if (value.Length > ushort.MaxValue) 183 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 184 | Write((ushort)value.Length); 185 | } 186 | 187 | EnsureCapacity(value.Length); 188 | StringHelper.WriteANSI(m_buffer, m_offset, value); 189 | m_offset += value.Length; 190 | } 191 | 192 | /// 193 | /// Writes a string to the . 194 | /// Includes the bytesize as a uint16. 195 | /// 196 | public void WriteString(string value, Encoding encoding) 197 | { 198 | int byteCount = encoding.GetByteCount(value); 199 | 200 | if (byteCount > ushort.MaxValue) 201 | throw new ArgumentOutOfRangeException(nameof(value), "String is too large to be written."); 202 | 203 | EnsureCapacity(byteCount + sizeof(ushort)); 204 | 205 | Write((ushort)byteCount); 206 | StringHelper.WriteString(m_buffer, m_offset, m_length, value, encoding); 207 | } 208 | 209 | /// 210 | /// Copies the inner buffer to a supplied buffer. 211 | /// 212 | /// The destination for the data. 213 | public void CopyTo(byte[] buffer) 214 | { 215 | CopyTo(buffer, 0, m_offset); 216 | } 217 | 218 | /// 219 | /// Copies the inner buffer to a supplied buffer. 220 | /// 221 | /// The destination for the data. 222 | public void CopyTo(byte[] buffer, int destinationIndex) 223 | { 224 | CopyTo(buffer, destinationIndex, m_offset); 225 | } 226 | 227 | /// 228 | /// Copies the inner buffer to a supplied buffer. 229 | /// 230 | /// The destination for the data. 231 | /// The total length to copy (starting from 0) 232 | public void CopyTo(byte[] buffer, int destinationIndex, int length) 233 | { 234 | if (buffer == null) 235 | throw new ArgumentNullException(nameof(buffer)); 236 | 237 | if ((uint)(destinationIndex + length) > buffer.Length) 238 | throw new ArgumentOutOfRangeException("Copy action exceeds the supplied buffer!"); 239 | 240 | if ((uint)length > Length) 241 | throw new ArgumentOutOfRangeException(nameof(length)); 242 | 243 | Memory.CopyMemory(m_buffer, 0, buffer, destinationIndex, length); 244 | } 245 | 246 | /// 247 | /// Copies the inner buffer to a supplied buffer. 248 | /// 249 | /// The destination for the data. 250 | public void CopyTo(IntPtr ptr) 251 | { 252 | CopyTo(ptr, 0, m_offset); 253 | } 254 | 255 | /// 256 | /// Copies the inner buffer to a supplied buffer. 257 | /// 258 | /// The destination for the data. 259 | public void CopyTo(IntPtr ptr, int destinationIndex) 260 | { 261 | CopyTo(ptr, destinationIndex, m_offset); 262 | } 263 | 264 | /// 265 | /// Copies the inner buffer to a supplied buffer. 266 | /// 267 | /// The destination for the data. 268 | /// The total length to copy (starting from 0) 269 | public void CopyTo(IntPtr ptr, int destinationIndex, int length) 270 | { 271 | if (ptr == IntPtr.Zero) 272 | throw new ArgumentNullException(nameof(ptr)); 273 | 274 | if (destinationIndex < 0) 275 | throw new ArgumentOutOfRangeException(nameof(destinationIndex)); 276 | 277 | if ((uint)length > Length) 278 | throw new ArgumentOutOfRangeException(nameof(length)); 279 | 280 | Memory.CopyMemory((void*)m_buffer, (byte*)ptr + destinationIndex, length); 281 | } 282 | 283 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 284 | private void EnsureCapacity(int bytesToAdd) 285 | { 286 | if (m_length < m_offset + (uint)bytesToAdd) 287 | ExceptionHelper.ThrowWriteBufferExceeded(); 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Mananged/ByteWriter.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using ByteStream.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | 8 | namespace ByteStream.Mananged 9 | { 10 | public struct ByteWriter : IWriter 11 | { 12 | private const int DEFAULTSIZE = 64; 13 | 14 | #pragma warning disable IDE0032 15 | private byte[] m_buffer; 16 | private int m_offset; 17 | private readonly bool m_isFixedSize; 18 | #pragma warning restore IDE0032 19 | 20 | /// 21 | /// The length of the . 22 | /// 23 | public int Length 24 | => m_buffer.Length; 25 | /// 26 | /// The current write offset. 27 | /// 28 | public int Offset 29 | => m_offset; 30 | /// 31 | /// Determines if the has a fixed size. 32 | /// 33 | public bool IsFixedSize 34 | => m_isFixedSize; 35 | /// 36 | /// Gets the internal buffer used by the . 37 | /// Do not modify the buffer while performing write operations. 38 | /// 39 | public byte[] Buffer 40 | => m_buffer; 41 | 42 | /// 43 | /// Creates a new instance of with an empty byffer. 44 | /// 45 | /// The initial size of the buffer. 46 | /// Determines if the buffer is allowed to increase its size automatically. 47 | public ByteWriter(int initialSize, bool isFixedSize) 48 | { 49 | if (initialSize < 1) 50 | throw new ArgumentException(nameof(initialSize)); 51 | 52 | m_buffer = new byte[initialSize]; 53 | m_isFixedSize = isFixedSize; 54 | m_offset = 0; 55 | } 56 | 57 | /// 58 | /// Creates a new instance of from an existing buffer. 59 | /// 60 | /// The buffer to use with this writer. 61 | public ByteWriter(byte[] data) 62 | : this(data, true) 63 | { } 64 | 65 | /// 66 | /// Creates a new instance of from an existing buffer. 67 | /// 68 | /// The buffer to use with this writer. 69 | /// Determines if the buffer is allowed to increase its size automatically. 70 | public ByteWriter(byte[] data, bool isFixedSize) 71 | { 72 | m_buffer = data ?? throw new ArgumentNullException(nameof(data)); 73 | m_isFixedSize = isFixedSize; 74 | m_offset = 0; 75 | } 76 | 77 | /// 78 | /// Creates a new instance of from an existing buffer. 79 | /// 80 | /// The byte array to wrap. 81 | /// The write offset. 82 | public ByteWriter(byte[] data, int offset) 83 | { 84 | m_buffer = data ?? throw new ArgumentNullException(nameof(data)); 85 | 86 | if ((uint)offset >= data.Length) 87 | throw new ArgumentOutOfRangeException(nameof(offset), "Offset is larger than array length."); 88 | 89 | m_isFixedSize = true; 90 | m_offset = offset; 91 | } 92 | 93 | /// 94 | /// Increases the write offset by some amount. 95 | /// 96 | /// The amounts of bytes to skip. 97 | public void SkipBytes(int amount) 98 | { 99 | if (amount < 1) 100 | throw new ArgumentOutOfRangeException(nameof(amount), "Amount needs to be at least 1."); 101 | 102 | EnsureCapacity(amount); 103 | m_offset += amount; 104 | } 105 | 106 | /// 107 | /// Resizes the current buffer to the current write offset. 108 | /// Performs an array copy. 109 | /// 110 | public void Trim() 111 | { 112 | ArrayExtensions.ResizeUnsafe(ref m_buffer, m_offset); 113 | } 114 | 115 | /// 116 | /// Resets the offset to zero. 117 | /// 118 | public void Clear() 119 | { 120 | m_offset = 0; 121 | } 122 | 123 | 124 | /// 125 | /// Reserves 4-bytes of space for a size value at the start of the . 126 | /// 127 | public void ReserveSizePrefix() 128 | { 129 | SkipBytes(sizeof(int)); 130 | } 131 | 132 | /// 133 | /// Writes the total size at the start of the as an . 134 | /// 135 | public int PrefixSize() 136 | { 137 | BinaryHelper.Write(m_buffer, 0, m_offset); 138 | return m_offset; 139 | } 140 | 141 | 142 | 143 | /// 144 | /// Writes a blittable struct or primitive value to the . 145 | /// 146 | /// The type of the blittable struct/primitive. 147 | public void Write(T value) where T : unmanaged 148 | { 149 | unsafe 150 | { 151 | int size = sizeof(T); 152 | EnsureCapacity(size); 153 | BinaryHelper.Write(m_buffer, m_offset, value); 154 | m_offset += size; 155 | } 156 | } 157 | 158 | /// 159 | /// Tries to write a blittable struct or primitive value to the . 160 | /// 161 | /// The type of the blittable struct/primitive. 162 | /// Returns false if the value couldn't be written. 163 | public bool TryWrite(T value) where T : unmanaged 164 | { 165 | unsafe 166 | { 167 | int size = sizeof(T); 168 | if (m_offset + size > Length) 169 | return false; 170 | 171 | BinaryHelper.Write(m_buffer, m_offset, value); 172 | m_offset += size; 173 | } 174 | return true; 175 | } 176 | 177 | 178 | /// 179 | /// Writes a byte array to the . 180 | /// 181 | /// TRUE to include the size as an uint16 182 | public void WriteBytes(byte[] value, bool includeSize = false) 183 | { 184 | if (includeSize) 185 | { 186 | if (value.Length > ushort.MaxValue) 187 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 188 | 189 | Write((ushort)value.Length); 190 | } 191 | 192 | EnsureCapacity(value.Length); 193 | BinaryHelper.WriteBytes(m_buffer, m_offset, value); 194 | m_offset += value.Length; 195 | } 196 | 197 | /// 198 | /// Writes a string as a double-byte character set. Each character requires 2 bytes. 199 | /// 200 | /// TRUE to include the size as an uint16 201 | public void WriteUTF16(string value, bool includeSize = false) 202 | { 203 | if (includeSize) 204 | { 205 | if (value.Length > ushort.MaxValue) 206 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 207 | 208 | Write((ushort)value.Length); 209 | } 210 | 211 | EnsureCapacity(value.Length * sizeof(char)); 212 | StringHelper.WriteUTF16(m_buffer, m_offset, value); 213 | m_offset += value.Length * sizeof(char); 214 | } 215 | 216 | /// 217 | /// Writes a string in ANSI encoding. Each character requires 1 byte. 218 | /// 219 | /// TRUE to include the size as an uint16 220 | public void WriteANSI(string value, bool includeSize = false) 221 | { 222 | if (includeSize) 223 | { 224 | if (value.Length > ushort.MaxValue) 225 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 226 | 227 | Write((ushort)value.Length); 228 | } 229 | 230 | EnsureCapacity(value.Length); 231 | StringHelper.WriteANSI(m_buffer, m_offset, value); 232 | m_offset += value.Length; 233 | } 234 | 235 | /// 236 | /// Writes a string to the . 237 | /// Includes the bytesize as a uint16. 238 | /// 239 | public void WriteString(string value, Encoding encoding) 240 | { 241 | int byteCount = encoding.GetByteCount(value); 242 | 243 | if (byteCount > ushort.MaxValue) 244 | throw new ArgumentOutOfRangeException(nameof(value), "String is too large to be written."); 245 | 246 | EnsureCapacity(byteCount + sizeof(ushort)); 247 | 248 | Write((ushort)byteCount); 249 | StringHelper.WriteString(m_buffer, m_offset, value, encoding); 250 | } 251 | 252 | /// 253 | /// Copies the inner buffer to a supplied buffer. 254 | /// 255 | /// The destination for the data. 256 | public void CopyTo(byte[] buffer) 257 | { 258 | CopyTo(buffer, 0, m_offset); 259 | } 260 | 261 | /// 262 | /// Copies the inner buffer to a supplied buffer. 263 | /// 264 | /// The destination for the data. 265 | public void CopyTo(byte[] buffer, int destinationIndex) 266 | { 267 | CopyTo(buffer, destinationIndex, m_offset); 268 | } 269 | 270 | /// 271 | /// Copies the inner buffer to a supplied buffer. 272 | /// 273 | /// The destination for the data. 274 | /// The total length to copy (starting from 0) 275 | public void CopyTo(byte[] buffer, int destinationIndex, int length) 276 | { 277 | if (buffer == null) 278 | throw new ArgumentNullException(nameof(buffer)); 279 | 280 | if ((uint)(destinationIndex + length) > buffer.Length) 281 | throw new ArgumentOutOfRangeException("Copy action exceeds the supplied buffer!"); 282 | 283 | if ((uint)length > Length) 284 | throw new ArgumentOutOfRangeException(nameof(length)); 285 | 286 | m_buffer.CopyToUnsafe(0, buffer, destinationIndex, length); 287 | } 288 | 289 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 290 | private void EnsureCapacity(int bytesToAdd) 291 | { 292 | int newAmount = m_offset + bytesToAdd; 293 | 294 | if (newAmount > Length) 295 | ResizeInternal(newAmount); 296 | } 297 | private void ResizeInternal(int resizeTo) 298 | { 299 | if (m_isFixedSize) 300 | ExceptionHelper.ThrowFixedBufferExceeded(); 301 | 302 | ArrayExtensions.ResizeUnsafe(ref m_buffer, MathUtils.NextPowerOfTwo(resizeTo)); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Unmanaged/UnmanagedStream.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using ByteStream.Utils; 3 | using ByteStream.Utils.Unsafe; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.CompilerServices; 7 | using System.Text; 8 | 9 | namespace ByteStream.Unmanaged 10 | { 11 | public unsafe class UnmanagedStream : IWriter, IReader, IByteStream 12 | { 13 | #pragma warning disable IDE0032 14 | private IntPtr m_buffer; 15 | private int m_offset; 16 | private int m_length; 17 | private SerializationMode m_mode; 18 | #pragma warning restore 19 | 20 | /// 21 | /// The current offset in. 22 | /// 23 | public int Offset 24 | => m_offset; 25 | /// 26 | /// The total length in bytes. 27 | /// 28 | public int Length 29 | => m_length; 30 | 31 | /// 32 | /// The current streaming mode. 33 | /// 34 | public SerializationMode Mode 35 | => m_mode; 36 | /// 37 | /// Determines if the is writing. 38 | /// 39 | public bool IsWriting 40 | => m_mode == SerializationMode.Writing; 41 | /// 42 | /// Determines if the is reading. 43 | /// 44 | public bool IsReading 45 | => m_mode == SerializationMode.Reading; 46 | /// 47 | /// The inner buffer used by the . 48 | /// 49 | public IntPtr Buffer 50 | => m_buffer; 51 | 52 | 53 | public UnmanagedStream() { } 54 | 55 | 56 | /// 57 | /// Resets the for reading. 58 | /// 59 | public void ResetRead() 60 | { 61 | if (m_buffer == IntPtr.Zero) 62 | throw new InvalidOperationException("Stream has no buffer assigned."); 63 | 64 | Reset(0, SerializationMode.Reading); 65 | } 66 | 67 | /// 68 | /// Resets the for reading. 69 | /// 70 | /// The buffer to read from. 71 | public void ResetRead(IntPtr data, int length) 72 | { 73 | m_buffer = data; 74 | m_length = length; 75 | 76 | ResetRead(data, 0, length); 77 | } 78 | 79 | /// 80 | /// Resets the for reading. 81 | /// 82 | /// The data to read. 83 | /// The read offset. 84 | /// The total amount of bytes available for reading. 85 | public void ResetRead(IntPtr data, int offset, int length) 86 | { 87 | if (data == IntPtr.Zero) 88 | throw new ArgumentNullException(nameof(data)); 89 | 90 | if (length < 1) 91 | throw new ArgumentOutOfRangeException(nameof(length)); 92 | 93 | if ((uint)offset > length) 94 | throw new ArgumentOutOfRangeException("Offset must be smaller than length."); 95 | 96 | m_buffer = data; 97 | m_length = length; 98 | 99 | Reset(offset, SerializationMode.Reading); 100 | } 101 | 102 | /// 103 | /// Resets the for writing. 104 | /// 105 | /// The byte array to wrap. 106 | /// The total amount of bytes available for writing. 107 | public void ResetWrite(IntPtr data, int length) 108 | { 109 | if (data == IntPtr.Zero) 110 | throw new ArgumentNullException(nameof(data)); 111 | 112 | if (length < 1) 113 | throw new ArgumentOutOfRangeException(nameof(length)); 114 | 115 | m_buffer = data; 116 | m_length = length; 117 | 118 | Reset(0, SerializationMode.Writing); 119 | } 120 | 121 | /// 122 | /// Resets the for writing. 123 | /// 124 | /// The byte array to wrap. 125 | /// The write offset. 126 | public void ResetWrite(IntPtr data, int offset, int length) 127 | { 128 | if (data == IntPtr.Zero) 129 | throw new ArgumentNullException(nameof(data)); 130 | 131 | if (length < 1) 132 | throw new ArgumentOutOfRangeException(nameof(length)); 133 | 134 | if ((uint)offset > length) 135 | throw new ArgumentOutOfRangeException("Offset must be smaller than length."); 136 | 137 | m_buffer = data; 138 | m_length = length; 139 | 140 | Reset(offset, SerializationMode.Writing); 141 | } 142 | 143 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 144 | private void Reset(int offset, SerializationMode mode) 145 | { 146 | m_offset = offset; 147 | m_mode = mode; 148 | } 149 | 150 | 151 | /// 152 | /// Increases the write offset by some amount. 153 | /// 154 | /// The amounts of bytes to skip. 155 | public void SkipBytes(int amount) 156 | { 157 | if (amount < 1) 158 | throw new ArgumentOutOfRangeException(nameof(amount), "Amount needs to be at least 1."); 159 | 160 | EnsureCapacity(amount); 161 | m_offset += amount; 162 | } 163 | 164 | /// 165 | /// Reserves 4-bytes of space for a size value at the start of the . 166 | /// 167 | public void ReserveSizePrefix() 168 | { 169 | SkipBytes(sizeof(int)); 170 | } 171 | 172 | /// 173 | /// Writes the total size at the start of the as an . 174 | /// 175 | public int PrefixSize() 176 | { 177 | BinaryHelper.Write(m_buffer, 0, m_offset); 178 | return m_offset; 179 | } 180 | 181 | 182 | /// 183 | /// Reads a blittable struct or primitive value from the . 184 | /// 185 | /// The type of the blittable struct/primitive. 186 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 187 | public UnmanagedStream Peek(ref T value) where T : unmanaged 188 | { 189 | int size = sizeof(T); 190 | EnsureCapacity(size); 191 | value = BinaryHelper.Read(m_buffer, m_offset); 192 | 193 | return this; 194 | } 195 | 196 | /// 197 | /// Reads a blittable struct or primitive value from the . 198 | /// 199 | /// The type of the blittable struct/primitive. 200 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 201 | public UnmanagedStream Read(ref T value) where T : unmanaged 202 | { 203 | int size = sizeof(T); 204 | EnsureCapacity(size); 205 | 206 | value = BinaryHelper.Read(m_buffer, m_offset); 207 | m_offset += size; 208 | 209 | return this; 210 | } 211 | 212 | /// 213 | /// Reads a value from the . Can overwrite value with default. 214 | /// 215 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 216 | public T Read() where T : unmanaged 217 | { 218 | int size = sizeof(T); 219 | EnsureCapacity(size); 220 | 221 | var value = BinaryHelper.Read(m_buffer, m_offset); 222 | m_offset += size; 223 | 224 | return value; 225 | } 226 | 227 | /// 228 | /// Writes a blittable struct or primitive value to the . 229 | /// 230 | /// The type of the blittable struct/primitive. 231 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 232 | public UnmanagedStream Write(T value) where T : unmanaged 233 | { 234 | int size = sizeof(T); 235 | EnsureCapacity(size); 236 | BinaryHelper.Write(m_buffer, m_offset, value); 237 | m_offset += size; 238 | 239 | return this; 240 | } 241 | 242 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 243 | public UnmanagedStream Serialize(ref T value) where T : unmanaged 244 | { 245 | if (m_mode == SerializationMode.Writing) Write(value); 246 | else Read(ref value); 247 | 248 | return this; 249 | } 250 | 251 | 252 | /// 253 | /// Writes a byte array to the . 254 | /// 255 | /// TRUE to include the size as an uint16 256 | public UnmanagedStream WriteBytes(byte[] value, bool includeSize = true) 257 | { 258 | if (includeSize) 259 | { 260 | if (value.Length > ushort.MaxValue) 261 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 262 | 263 | Write((ushort)value.Length); 264 | } 265 | 266 | EnsureCapacity(value.Length); 267 | BinaryHelper.WriteBytes(m_buffer, m_offset, value); 268 | m_offset += value.Length; 269 | 270 | return this; 271 | } 272 | 273 | /// 274 | /// Writes a string as a double-byte character set. Each character requires 2 bytes. 275 | /// 276 | /// TRUE to include the size as an uint16 277 | public UnmanagedStream WriteUTF16(string value, bool includeSize = true) 278 | { 279 | if (includeSize) 280 | { 281 | if (value.Length > ushort.MaxValue) 282 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 283 | 284 | Write((ushort)value.Length); 285 | } 286 | 287 | EnsureCapacity(value.Length * sizeof(char)); 288 | StringHelper.WriteANSI(m_buffer, m_offset, value); 289 | m_offset += value.Length * sizeof(char); 290 | 291 | return this; 292 | } 293 | 294 | /// 295 | /// Writes a string in ANSI encoding. Each character requires 1 byte. 296 | /// 297 | /// TRUE to include the size as an uint16 298 | public UnmanagedStream WriteANSI(string value, bool includeSize = true) 299 | { 300 | if (includeSize) 301 | { 302 | if (value.Length > ushort.MaxValue) 303 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 304 | 305 | Write((ushort)value.Length); 306 | } 307 | 308 | EnsureCapacity(value.Length); 309 | StringHelper.WriteANSI(m_buffer, m_offset, value); 310 | m_offset += value.Length; 311 | 312 | return this; 313 | } 314 | 315 | /// 316 | /// Writes a string to the . 317 | /// Includes the bytesize as a uint16. 318 | /// 319 | public UnmanagedStream WriteString(string value, Encoding encoding) 320 | { 321 | int byteCount = encoding.GetByteCount(value); 322 | 323 | if (byteCount > ushort.MaxValue) 324 | throw new ArgumentOutOfRangeException(nameof(value), "String is too large to be written."); 325 | 326 | EnsureCapacity(byteCount + sizeof(ushort)); 327 | 328 | Write((ushort)byteCount); 329 | StringHelper.WriteString(m_buffer, m_offset, m_length, value, encoding); 330 | 331 | m_offset += byteCount; 332 | 333 | return this; 334 | } 335 | 336 | /// 337 | /// Reads a byte-array from the . Length is automatically read as an uint16. 338 | /// 339 | public byte[] ReadBytes() 340 | { 341 | ushort length = Read(); 342 | 343 | return ReadBytes(length); 344 | } 345 | /// 346 | /// Reads a byte-array from the . 347 | /// Does NOT automatically read length. 348 | /// 349 | /// The length of the byte-array. 350 | /// 351 | public byte[] ReadBytes(int length) 352 | { 353 | EnsureCapacity(length); 354 | 355 | var value = BinaryHelper.ReadBytes(m_buffer, m_offset, length); 356 | 357 | m_offset += length; 358 | 359 | return value; 360 | } 361 | 362 | /// 363 | /// Reads a string as a double-byte character set. Length is automatically retrieved as an uint16. 364 | /// 365 | public string ReadUTF16() 366 | { 367 | ushort length = Read(); 368 | 369 | return ReadUTF16(length); 370 | } 371 | /// 372 | /// Reads a string as a double-byte character set. Each character requires 2 bytes. 373 | /// Does NOT automatically read length. 374 | /// 375 | public string ReadUTF16(int stringLength) 376 | { 377 | EnsureCapacity(stringLength * sizeof(char)); 378 | 379 | var value = StringHelper.ReadUTF16(m_buffer, m_offset, stringLength); 380 | 381 | m_offset += stringLength * sizeof(char); 382 | 383 | return value; 384 | } 385 | 386 | /// 387 | /// Reads a string in ANSI encoding. Length is automatically retrieved as an uint16. 388 | /// 389 | public string ReadANSI() 390 | { 391 | ushort length = Read(); 392 | 393 | return ReadANSI(length); 394 | } 395 | 396 | /// 397 | /// Reads a string in ANSI encoding. Each character requires 1 byte. 398 | /// Does NOT automatically read length. 399 | /// 400 | public string ReadANSI(int stringLength) 401 | { 402 | EnsureCapacity(stringLength); 403 | var value = StringHelper.ReadANSI(m_buffer, m_offset, stringLength); 404 | 405 | m_offset += stringLength; 406 | 407 | return value; 408 | } 409 | 410 | /// 411 | /// Reads a string from the . 412 | /// Length is automatically retrieved. 413 | /// 414 | /// 415 | public string ReadString(Encoding encoding) 416 | { 417 | ushort byteCount = 0; 418 | Read(ref byteCount); 419 | 420 | EnsureCapacity(byteCount); 421 | var value = StringHelper.ReadString((IntPtr)m_buffer, m_offset, byteCount, encoding); 422 | m_offset += byteCount; 423 | 424 | return value; 425 | } 426 | 427 | public void SerializeString(ref string value, Encoding encoding) 428 | { 429 | if (m_mode == SerializationMode.Reading) 430 | value = ReadString(encoding); 431 | else 432 | WriteString(value, encoding); 433 | } 434 | 435 | /// 436 | /// Copies the inner buffer to a supplied buffer. 437 | /// 438 | /// The destination for the data. 439 | public void CopyTo(byte[] buffer) 440 | { 441 | CopyTo(buffer, 0, m_offset); 442 | } 443 | 444 | /// 445 | /// Copies the inner buffer to a supplied buffer. 446 | /// 447 | /// The destination for the data. 448 | public void CopyTo(byte[] buffer, int destinationIndex) 449 | { 450 | CopyTo(buffer, destinationIndex, m_offset); 451 | } 452 | 453 | /// 454 | /// Copies the inner buffer to a supplied buffer. 455 | /// 456 | /// The destination for the data. 457 | /// The total length to copy (starting from 0) 458 | public void CopyTo(byte[] buffer, int destinationIndex, int length) 459 | { 460 | if (buffer == null) 461 | throw new ArgumentNullException(nameof(buffer)); 462 | 463 | if ((uint)(destinationIndex + length) > buffer.Length) 464 | throw new ArgumentOutOfRangeException("Copy action exceeds the supplied buffer!"); 465 | 466 | if ((uint)length > Length) 467 | throw new ArgumentOutOfRangeException(nameof(length)); 468 | 469 | Memory.CopyMemory(m_buffer, 0, buffer, destinationIndex, length); 470 | } 471 | 472 | /// 473 | /// Copies the inner buffer to a supplied buffer. 474 | /// 475 | /// The destination for the data. 476 | public void CopyTo(IntPtr ptr) 477 | { 478 | CopyTo(ptr, 0, m_offset); 479 | } 480 | 481 | /// 482 | /// Copies the inner buffer to a supplied buffer. 483 | /// 484 | /// The destination for the data. 485 | public void CopyTo(IntPtr ptr, int destinationIndex) 486 | { 487 | CopyTo(ptr, destinationIndex, m_offset); 488 | } 489 | 490 | /// 491 | /// Copies the inner buffer to a supplied buffer. 492 | /// 493 | /// The destination for the data. 494 | /// The total length to copy (starting from 0) 495 | public void CopyTo(IntPtr ptr, int destinationIndex, int length) 496 | { 497 | if (ptr == IntPtr.Zero) 498 | throw new ArgumentNullException(nameof(ptr)); 499 | 500 | if (destinationIndex < 0) 501 | throw new ArgumentOutOfRangeException(nameof(destinationIndex)); 502 | 503 | if ((uint)length > Length) 504 | throw new ArgumentOutOfRangeException(nameof(length)); 505 | 506 | Memory.CopyMemory((void*)m_buffer, (byte*)ptr + destinationIndex, length); 507 | } 508 | 509 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 510 | private void EnsureCapacity(int size) 511 | { 512 | if (m_offset + size > m_length) 513 | ExceptionHelper.ThrowFixedBufferExceeded(); 514 | } 515 | 516 | /// 517 | /// Tries to write a value to the . Returns FALSE if the value couldn't be written. 518 | /// 519 | public bool TryWrite(T value) where T : unmanaged 520 | { 521 | int size = sizeof(T); 522 | if (m_offset + size > m_length) 523 | { 524 | return false; 525 | } 526 | 527 | BinaryHelper.Write(m_buffer, m_offset, value); 528 | m_offset += size; 529 | 530 | return true; 531 | } 532 | 533 | /// 534 | /// Tries to read a value from the . Returns FALSE if the value couldn't be read. 535 | /// 536 | public bool TryRead(out T value) where T : unmanaged 537 | { 538 | int size = sizeof(T); 539 | if (m_offset + size > m_length) 540 | { 541 | value = default; 542 | return false; 543 | } 544 | 545 | value = BinaryHelper.Read(m_buffer, m_offset); 546 | m_offset += size; 547 | 548 | return true; 549 | } 550 | 551 | void IBuffer.Clear() 552 | { 553 | Reset(0, SerializationMode.None); 554 | } 555 | 556 | void IWriter.Write(T value) 557 | { 558 | Write(value); 559 | } 560 | 561 | void IByteStream.Serialize(ref T value) 562 | { 563 | Serialize(ref value); 564 | } 565 | } 566 | } 567 | -------------------------------------------------------------------------------- /ByteStream/ByteStream/Mananged/ManagedStream.cs: -------------------------------------------------------------------------------- 1 | using ByteStream.Interfaces; 2 | using ByteStream.Utils; 3 | using ByteStream.Utils.Unsafe; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | 10 | namespace ByteStream.Mananged 11 | { 12 | public unsafe class ManagedStream : IWriter, IReader, IByteStream 13 | { 14 | public const int DefaultBufferSize = 1024; 15 | 16 | #pragma warning disable IDE0032 17 | private byte[] m_buffer; 18 | private int m_offset; 19 | private SerializationMode m_mode; 20 | 21 | private bool m_isFixedSize; 22 | #pragma warning restore 23 | 24 | /// 25 | /// The current offset in. 26 | /// 27 | public int Offset 28 | => m_offset; 29 | /// 30 | /// The total length in bytes. 31 | /// 32 | public int Length 33 | => m_buffer.Length; 34 | 35 | /// 36 | /// The current streaming mode. 37 | /// 38 | public SerializationMode Mode 39 | => m_mode; 40 | /// 41 | /// Determines if the is writing. 42 | /// 43 | public bool IsWriting 44 | => m_mode == SerializationMode.Writing; 45 | /// 46 | /// Determines if the is reading. 47 | /// 48 | public bool IsReading 49 | => m_mode == SerializationMode.Reading; 50 | /// 51 | /// The inner buffer used by the . 52 | /// 53 | public byte[] Buffer 54 | => m_buffer; 55 | /// 56 | /// A fixed-size buffer will not resize itself. 57 | /// 58 | public bool IsFixedSize 59 | => m_isFixedSize; 60 | 61 | 62 | public ManagedStream() { } 63 | 64 | 65 | /// 66 | /// Resets the for reading. 67 | /// 68 | public void ResetRead() 69 | { 70 | if (m_buffer == null) 71 | throw new InvalidOperationException("Stream has no buffer assigned."); 72 | 73 | Reset(0, SerializationMode.Reading); 74 | } 75 | 76 | /// 77 | /// Resets the for reading. 78 | /// 79 | /// The buffer to read from. 80 | public void ResetRead(byte[] data) 81 | { 82 | m_buffer = data; 83 | Reset(0, SerializationMode.Reading); 84 | } 85 | 86 | /// 87 | /// Resets the for reading. 88 | /// 89 | /// The data to read. 90 | /// The read offset. 91 | /// The total amount of bytes available for reading. 92 | public void ResetRead(byte[] data, int offset, int length) 93 | { 94 | m_buffer = data ?? throw new ArgumentNullException(nameof(data)); 95 | 96 | if ((uint)(offset + length) > data.Length) 97 | throw new ArgumentOutOfRangeException("Offset + Length must be smaller than array length."); 98 | 99 | Reset(offset, SerializationMode.Reading); 100 | } 101 | 102 | 103 | /// 104 | /// Resets the for writing. 105 | /// 106 | public void ResetWrite() 107 | { 108 | m_buffer = new byte[DefaultBufferSize]; 109 | m_isFixedSize = false; 110 | Reset(0, SerializationMode.Writing); 111 | } 112 | 113 | /// 114 | /// Resets the for writing. 115 | /// 116 | /// The starting size of the buffer. 117 | /// FALSE if the buffer can automatically resize. 118 | public void ResetWrite(int initialSize, bool isFixedSize = false) 119 | { 120 | if (initialSize < 1) 121 | throw new ArgumentException(nameof(initialSize)); 122 | 123 | m_buffer = new byte[initialSize]; 124 | m_isFixedSize = isFixedSize; 125 | 126 | Reset(0, SerializationMode.Writing); 127 | } 128 | 129 | /// 130 | /// Resets the for writing. 131 | /// 132 | /// The byte array to wrap. 133 | /// FALSE if the buffer can automatically resize. 134 | public void ResetWrite(byte[] data, bool isFixedSize = false) 135 | { 136 | m_buffer = data ?? throw new ArgumentNullException(nameof(data)); 137 | m_isFixedSize = isFixedSize; 138 | 139 | Reset(0, SerializationMode.Writing); 140 | } 141 | 142 | /// 143 | /// Resets the for writing. 144 | /// 145 | /// The byte array to wrap. 146 | /// The write offset. 147 | public void ResetWrite(byte[] data, int offset) 148 | { 149 | m_buffer = data ?? throw new ArgumentNullException(nameof(data)); 150 | m_isFixedSize = true; 151 | 152 | Reset(0, SerializationMode.Writing); 153 | } 154 | 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | private void Reset(int offset, SerializationMode mode) 157 | { 158 | m_offset = offset; 159 | m_mode = mode; 160 | } 161 | 162 | 163 | /// 164 | /// Increases the write offset by some amount. 165 | /// 166 | /// The amounts of bytes to skip. 167 | public void SkipBytes(int amount) 168 | { 169 | if (amount < 1) 170 | throw new ArgumentOutOfRangeException(nameof(amount), "Amount needs to be at least 1."); 171 | 172 | EnsureWriteCapacity(amount); 173 | m_offset += amount; 174 | } 175 | 176 | /// 177 | /// Reserves 4-bytes of space for a size value at the start of the . 178 | /// 179 | public void ReserveSizePrefix() 180 | { 181 | SkipBytes(sizeof(int)); 182 | } 183 | 184 | /// 185 | /// Writes the total size at the start of the as an . 186 | /// 187 | public int PrefixSize() 188 | { 189 | BinaryHelper.Write(m_buffer, 0, m_offset); 190 | return m_offset; 191 | } 192 | 193 | 194 | /// 195 | /// Reads a blittable struct or primitive value from the . 196 | /// 197 | /// The type of the blittable struct/primitive. 198 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 199 | public ManagedStream Peek(ref T value) where T : unmanaged 200 | { 201 | int size = sizeof(T); 202 | EnsureReadCapacity(size); 203 | value = BinaryHelper.Read(m_buffer, m_offset); 204 | 205 | return this; 206 | } 207 | 208 | /// 209 | /// Reads a blittable struct or primitive value from the . 210 | /// 211 | /// The type of the blittable struct/primitive. 212 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 213 | public ManagedStream Read(ref T value) where T : unmanaged 214 | { 215 | int size = sizeof(T); 216 | EnsureReadCapacity(size); 217 | 218 | value = BinaryHelper.Read(m_buffer, m_offset); 219 | m_offset += size; 220 | 221 | return this; 222 | } 223 | 224 | /// 225 | /// Reads a value from the . Can overwrite value with default. 226 | /// 227 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 228 | public T Read() where T : unmanaged 229 | { 230 | int size = sizeof(T); 231 | EnsureReadCapacity(size); 232 | 233 | var value = BinaryHelper.Read(m_buffer, m_offset); 234 | m_offset += size; 235 | 236 | return value; 237 | } 238 | 239 | /// 240 | /// Writes a blittable struct or primitive value to the . 241 | /// 242 | /// The type of the blittable struct/primitive. 243 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 244 | public ManagedStream Write(T value) where T : unmanaged 245 | { 246 | int size = sizeof(T); 247 | EnsureWriteCapacity(size); 248 | BinaryHelper.Write(m_buffer, m_offset, value); 249 | m_offset += size; 250 | 251 | return this; 252 | } 253 | 254 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 255 | public ManagedStream Serialize(ref T value) where T : unmanaged 256 | { 257 | if (m_mode == SerializationMode.Writing) Write(value); 258 | else Read(ref value); 259 | 260 | return this; 261 | } 262 | 263 | 264 | /// 265 | /// Writes a byte array to the . 266 | /// 267 | /// TRUE to include the size as an uint16 268 | public ManagedStream WriteBytes(byte[] value, bool includeSize = true) 269 | { 270 | if (includeSize) 271 | { 272 | if (value.Length > ushort.MaxValue) 273 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 274 | 275 | Write((ushort)value.Length); 276 | } 277 | 278 | EnsureReadCapacity(value.Length); 279 | BinaryHelper.WriteBytes(m_buffer, m_offset, value); 280 | m_offset += value.Length; 281 | 282 | return this; 283 | } 284 | 285 | /// 286 | /// Writes a string as a double-byte character set. Each character requires 2 bytes. 287 | /// 288 | /// TRUE to include the size as an uint16 289 | public ManagedStream WriteUTF16(string value, bool includeSize = true) 290 | { 291 | if (includeSize) 292 | { 293 | if (value.Length > ushort.MaxValue) 294 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 295 | 296 | Write((ushort)value.Length); 297 | } 298 | 299 | EnsureWriteCapacity(value.Length * sizeof(char)); 300 | StringHelper.WriteANSI(m_buffer, m_offset, value); 301 | m_offset += value.Length * sizeof(char); 302 | 303 | return this; 304 | } 305 | 306 | /// 307 | /// Writes a string in ANSI encoding. Each character requires 1 byte. 308 | /// 309 | /// TRUE to include the size as an uint16 310 | public ManagedStream WriteANSI(string value, bool includeSize = true) 311 | { 312 | if (includeSize) 313 | { 314 | if (value.Length > ushort.MaxValue) 315 | throw new ArgumentOutOfRangeException(nameof(value), "Maximum size of 65.535 exceeded."); 316 | 317 | Write((ushort)value.Length); 318 | } 319 | 320 | EnsureWriteCapacity(value.Length); 321 | StringHelper.WriteANSI(m_buffer, m_offset, value); 322 | m_offset += value.Length; 323 | 324 | return this; 325 | } 326 | 327 | /// 328 | /// Writes a string to the . 329 | /// Includes the bytesize as a uint16. 330 | /// 331 | public ManagedStream WriteString(string value, Encoding encoding) 332 | { 333 | int byteCount = encoding.GetByteCount(value); 334 | 335 | if (byteCount > ushort.MaxValue) 336 | throw new ArgumentOutOfRangeException(nameof(value), "String is too large to be written."); 337 | 338 | EnsureWriteCapacity(byteCount + sizeof(ushort)); 339 | 340 | Write((ushort)byteCount); 341 | StringHelper.WriteString(m_buffer, m_offset, value, encoding); 342 | 343 | m_offset += byteCount; 344 | 345 | return this; 346 | } 347 | 348 | /// 349 | /// Reads a byte-array from the . Length is automatically read as an uint16. 350 | /// 351 | public byte[] ReadBytes() 352 | { 353 | ushort length = Read(); 354 | 355 | return ReadBytes(length); 356 | } 357 | /// 358 | /// Reads a byte-array from the . 359 | /// Does NOT automatically read length. 360 | /// 361 | /// The length of the byte-array. 362 | /// 363 | public byte[] ReadBytes(int length) 364 | { 365 | EnsureReadCapacity(length); 366 | 367 | var value = BinaryHelper.ReadBytes(m_buffer, m_offset, length); 368 | 369 | m_offset += length; 370 | 371 | return value; 372 | } 373 | 374 | /// 375 | /// Reads a string as a double-byte character set. Length is automatically retrieved as an uint16. 376 | /// 377 | public string ReadUTF16() 378 | { 379 | ushort length = Read(); 380 | 381 | return ReadUTF16(length); 382 | } 383 | /// 384 | /// Reads a string as a double-byte character set. Each character requires 2 bytes. 385 | /// Does NOT automatically read length. 386 | /// 387 | public string ReadUTF16(int stringLength) 388 | { 389 | EnsureReadCapacity(stringLength * sizeof(char)); 390 | 391 | var value = StringHelper.ReadUTF16(m_buffer, m_offset, stringLength); 392 | 393 | m_offset += stringLength * sizeof(char); 394 | 395 | return value; 396 | } 397 | 398 | /// 399 | /// Reads a string in ANSI encoding. Length is automatically retrieved as an uint16. 400 | /// 401 | public string ReadANSI() 402 | { 403 | ushort length = Read(); 404 | 405 | return ReadANSI(length); 406 | } 407 | 408 | /// 409 | /// Reads a string in ANSI encoding. Each character requires 1 byte. 410 | /// Does NOT automatically read length. 411 | /// 412 | public string ReadANSI(int stringLength) 413 | { 414 | EnsureReadCapacity(stringLength); 415 | var value = StringHelper.ReadANSI(m_buffer, m_offset, stringLength); 416 | 417 | m_offset += stringLength; 418 | 419 | return value; 420 | } 421 | 422 | /// 423 | /// Reads a string from the . 424 | /// Length is automatically retrieved. 425 | /// 426 | /// 427 | public string ReadString(Encoding encoding) 428 | { 429 | ushort byteCount = 0; 430 | Read(ref byteCount); 431 | 432 | EnsureReadCapacity(byteCount); 433 | var value = StringHelper.ReadString(m_buffer, m_offset, byteCount, encoding); 434 | m_offset += byteCount; 435 | 436 | return value; 437 | } 438 | 439 | public void SerializeString(ref string value, Encoding encoding) 440 | { 441 | if (m_mode == SerializationMode.Reading) 442 | value = ReadString(encoding); 443 | else 444 | WriteString(value, encoding); 445 | } 446 | 447 | /// 448 | /// Copies the inner buffer to a supplied buffer. 449 | /// 450 | /// The destination for the data. 451 | public void CopyTo(byte[] buffer) 452 | { 453 | CopyTo(buffer, 0, m_offset); 454 | } 455 | 456 | /// 457 | /// Copies the inner buffer to a supplied buffer. 458 | /// 459 | /// The destination for the data. 460 | public void CopyTo(byte[] buffer, int destinationIndex) 461 | { 462 | CopyTo(buffer, destinationIndex, m_offset); 463 | } 464 | 465 | /// 466 | /// Copies the inner buffer to a supplied buffer. 467 | /// 468 | /// The destination for the data. 469 | /// The total length to copy (starting from 0) 470 | public void CopyTo(byte[] buffer, int destinationIndex, int length) 471 | { 472 | if (buffer == null) 473 | throw new ArgumentNullException(nameof(buffer)); 474 | 475 | if ((uint)(destinationIndex + length) > buffer.Length) 476 | throw new ArgumentOutOfRangeException("Copy action exceeds the supplied buffer!"); 477 | 478 | if ((uint)length > Length) 479 | throw new ArgumentOutOfRangeException(nameof(length)); 480 | 481 | m_buffer.CopyToUnsafe(0, buffer, destinationIndex, length); 482 | } 483 | 484 | /// 485 | /// Copies the inner buffer to a supplied buffer. 486 | /// 487 | /// The destination for the data. 488 | public void CopyTo(IntPtr ptr) 489 | { 490 | CopyTo(ptr, 0, m_offset); 491 | } 492 | 493 | /// 494 | /// Copies the inner buffer to a supplied buffer. 495 | /// 496 | /// The destination for the data. 497 | public void CopyTo(IntPtr ptr, int destinationIndex) 498 | { 499 | CopyTo(ptr, destinationIndex, m_offset); 500 | } 501 | 502 | /// 503 | /// Copies the inner buffer to a supplied buffer. 504 | /// 505 | /// The destination for the data. 506 | /// The total length to copy (starting from 0) 507 | public void CopyTo(IntPtr ptr, int destinationIndex, int length) 508 | { 509 | if (ptr == IntPtr.Zero) 510 | throw new ArgumentNullException(nameof(ptr)); 511 | 512 | if (destinationIndex < 0) 513 | throw new ArgumentOutOfRangeException(nameof(destinationIndex)); 514 | 515 | if ((uint)length > Length) 516 | throw new ArgumentOutOfRangeException(nameof(length)); 517 | 518 | Memory.CopyMemory(m_buffer, 0, ptr, destinationIndex, length); 519 | } 520 | 521 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 522 | private void EnsureWriteCapacity(int size) 523 | { 524 | if (m_offset + size > m_buffer.Length) 525 | { 526 | if (m_isFixedSize) 527 | { 528 | ExceptionHelper.ThrowFixedBufferExceeded(); 529 | } 530 | 531 | ArrayExtensions.ResizeUnsafe(ref m_buffer, MathUtils.NextPowerOfTwo(m_offset + size)); 532 | } 533 | } 534 | 535 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 536 | private void EnsureReadCapacity(int size) 537 | { 538 | if (m_offset + size > m_buffer.Length) 539 | ExceptionHelper.ThrowFixedBufferExceeded(); 540 | } 541 | 542 | /// 543 | /// Tries to write a value to the . Returns FALSE if the value couldn't be written. 544 | /// 545 | public bool TryWrite(T value) where T : unmanaged 546 | { 547 | int size = sizeof(T); 548 | if (m_offset + size > Length) 549 | { 550 | return false; 551 | } 552 | 553 | BinaryHelper.Write(m_buffer, m_offset, value); 554 | m_offset += size; 555 | 556 | return true; 557 | } 558 | 559 | /// 560 | /// Tries to read a value from the . Returns FALSE if the value couldn't be read. 561 | /// 562 | public bool TryRead(out T value) where T : unmanaged 563 | { 564 | int size = sizeof(T); 565 | if (m_offset + size > Length) 566 | { 567 | value = default; 568 | return false; 569 | } 570 | 571 | value = BinaryHelper.Read(m_buffer, m_offset); 572 | m_offset += size; 573 | 574 | return true; 575 | } 576 | 577 | void IBuffer.Clear() 578 | { 579 | Reset(0, SerializationMode.None); 580 | } 581 | 582 | void IWriter.Write(T value) 583 | { 584 | Write(value); 585 | } 586 | 587 | void IByteStream.Serialize(ref T value) 588 | { 589 | Serialize(ref value); 590 | } 591 | } 592 | } 593 | --------------------------------------------------------------------------------