├── src └── SpanDex │ ├── Properties │ └── AssemblyInfo.cs │ ├── SpanDex.csproj │ ├── SpanDex.xml │ ├── SpanReader.cs │ ├── SpanWriter.cs │ ├── SpanWriting.cs │ ├── SpanReading.cs │ └── SpanExtensions.cs ├── .travis.yml ├── example └── SpanDex.Example │ ├── SpanDex.Example.csproj │ └── Program.cs ├── SpanDex.Benchmarking ├── SpanDex.Benchmarking.csproj └── Program.cs ├── CONTRIBUTING.md ├── tests └── SpanDex.Tests │ ├── SpanDex.Tests.csproj │ ├── SpanReaderTests.cs │ ├── SpanWriterTests.cs │ └── SpanExtensionTests.cs ├── LICENSE ├── SpanDex.sln ├── README.md └── .gitignore /src/SpanDex/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [assembly: CLSCompliant(true)] -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: none 3 | dist: bionic 4 | dotnet: 2.2.105 5 | script: 6 | - dotnet restore 7 | - dotnet build 8 | - dotnet test tests/SpanDex.Tests/SpanDex.Tests.csproj -------------------------------------------------------------------------------- /example/SpanDex.Example/SpanDex.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /SpanDex.Benchmarking/SpanDex.Benchmarking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to SpanDex 2 | 3 | Thanks for thinking about contributing! :+1: 4 | 5 | If you have encountered a bug (or just have a question on how to use the library), feel free to open an issue immediately. If reporting a bug, please include the version of SpanDex you are using, your runtime platform and version, and code to reproduce the problem. 6 | 7 | If you have an idea for code, or would like to contribute some code, open an issue for discussion.. If it works well with the library then create a PR and I'll review it. 8 | 9 | That's it! Thanks for considering contributing! :+1: -------------------------------------------------------------------------------- /tests/SpanDex.Tests/SpanDex.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/SpanDex.Tests/SpanReaderTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace SpanDex.Tests { 7 | [TestClass] 8 | public class SpanReaderTests { 9 | [TestMethod] 10 | public void ImplicitConstructors_LengthIsCorrect() { 11 | var byteArray = new byte[10]; 12 | Span span = new byte[20]; 13 | ReadOnlySpan readOnlySpan = new byte[30]; 14 | 15 | SpanReader spanReader = byteArray; 16 | Assert.AreEqual(byteArray.Length, spanReader.Length); 17 | spanReader = span; 18 | Assert.AreEqual(span.Length, spanReader.Length); 19 | spanReader = readOnlySpan; 20 | Assert.AreEqual(readOnlySpan.Length, spanReader.Length); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SpanDex/SpanDex.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | 1.0.2 7 | Jono Rogers 8 | 9 | Utilities to read/write primitives from/to spans easily 10 | MIT 11 | span spanreader spanwriter binaryprimitives 12 | https://github.com/jonorogers/SpanDex 13 | https://github.com/jonorogers/SpanDex 14 | 7.3 15 | 1.0.2.0 16 | 17 | 18 | 19 | C:\code\SpanDex\src\SpanDex\SpanDex.xml 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jono Rogers 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 | -------------------------------------------------------------------------------- /tests/SpanDex.Tests/SpanWriterTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace SpanDex.Tests { 7 | [TestClass] 8 | public class SpanWriterTests { 9 | [TestMethod] 10 | public void NullMemory_ThrowsArgumentNullException() { 11 | Assert.ThrowsException(() => { 12 | var spanWriter = new SpanWriter(null); 13 | }); 14 | } 15 | 16 | [TestMethod] 17 | public void ImplicitConstructors_LengthIsCorrect() { 18 | var byteArray = new byte[10]; 19 | Span span = new byte[20]; 20 | 21 | SpanWriter spanWriter = byteArray; 22 | Assert.AreEqual(byteArray.Length, spanWriter.Length); 23 | spanWriter = span; 24 | Assert.AreEqual(span.Length, spanWriter.Length); 25 | } 26 | 27 | [TestMethod] 28 | public void CreateFromSpan_WithCursor_InitializesCorrectly() { 29 | SpanWriter writer = new SpanWriter(new byte[20], 10); 30 | Assert.AreEqual(10, writer.Cursor); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SpanDex.Benchmarking/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Running; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace SpanDex.Benchmarking { 7 | [RankColumn] 8 | [MemoryDiagnoser] 9 | [MarkdownExporterAttribute.Default] 10 | [MarkdownExporterAttribute.GitHub] 11 | public class SpanReaderVsBitConverter { 12 | private byte[] data; 13 | 14 | [Params(1000, 10000)] 15 | public int N; 16 | 17 | [GlobalSetup] 18 | public void Setup() { 19 | data = new byte[N * sizeof(Int16)]; 20 | } 21 | 22 | [Benchmark] 23 | public void SpanReader() { 24 | SpanReader reader = data; 25 | 26 | for (int i = 0; i < N; i += 2) { 27 | var v = reader.ReadInt16LittleEndian(); 28 | } 29 | } 30 | 31 | [Benchmark] 32 | public void BitConvert() { 33 | for (int i = 0; i < N; i += 2) { 34 | var v = BitConverter.ToInt16(data, i); 35 | } 36 | } 37 | 38 | [Benchmark] 39 | public void Manual() { 40 | for (int i = 0; i < N; i += 2) { 41 | var v = (short)((data[i] << 8) | data[i + 1]); 42 | } 43 | } 44 | } 45 | 46 | public class Program { 47 | public static void Main() { 48 | var summary = BenchmarkRunner.Run(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /example/SpanDex.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SpanDex.Extensions; 3 | 4 | namespace SpanDex.Example { 5 | internal static class Program { 6 | private static void Main() { 7 | // Create a buffer 8 | var bytes = new byte[] { 0xA0, 0x1A, 0x35, 0x91, 0xE9, 0x51, 0x0C, 0x79, 0x04, 0xD1, 0x57, 0x81, 0x1D, 0x27 }; 9 | var span = new ReadOnlySpan(bytes); 10 | // Read from it 11 | int cursor = 0; 12 | ushort one = span.ReadUInt16BigEndian(ref cursor); 13 | uint two = span.ReadUInt32BigEndian(ref cursor); 14 | ulong three = span.ReadUInt64BigEndian(ref cursor); 15 | 16 | Console.WriteLine($"One: {one}"); 17 | Console.WriteLine($"Two: {two}"); 18 | Console.WriteLine($"Three: {three}"); 19 | 20 | const string testString = "this is a test string!"; 21 | 22 | var writer = new SpanWriter(36); 23 | writer.WriteInt16BigEndian(-31_971); 24 | writer.WriteInt32BigEndian(-1_598_468_598); 25 | writer.WriteInt64BigEndian(-7_223_372_036_854_775_808); 26 | writer.WriteAsciiString(testString); 27 | 28 | var reader = new SpanReader(writer.Span); 29 | Console.WriteLine(reader.ReadInt16BigEndian()); 30 | Console.WriteLine(reader.ReadInt32BigEndian()); 31 | Console.WriteLine(reader.ReadInt64BigEndian()); 32 | Console.WriteLine(reader.ReadAsciiString(testString.Length)); 33 | 34 | Console.ReadKey(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SpanDex.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29020.237 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpanDex", "src\SpanDex\SpanDex.csproj", "{DF644125-142B-4ED6-9F9A-16F1DDE6C983}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpanDex.Example", "example\SpanDex.Example\SpanDex.Example.csproj", "{83A8BDA3-F2AD-4F77-87B9-D6E235952010}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpanDex.Tests", "tests\SpanDex.Tests\SpanDex.Tests.csproj", "{C5398393-4686-40AD-834D-07AEE06617C0}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpanDex.Benchmarking", "SpanDex.Benchmarking\SpanDex.Benchmarking.csproj", "{D9900867-305A-496A-B848-FBD4A01850BE}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {DF644125-142B-4ED6-9F9A-16F1DDE6C983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {DF644125-142B-4ED6-9F9A-16F1DDE6C983}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {DF644125-142B-4ED6-9F9A-16F1DDE6C983}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {DF644125-142B-4ED6-9F9A-16F1DDE6C983}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {83A8BDA3-F2AD-4F77-87B9-D6E235952010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {83A8BDA3-F2AD-4F77-87B9-D6E235952010}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {83A8BDA3-F2AD-4F77-87B9-D6E235952010}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {83A8BDA3-F2AD-4F77-87B9-D6E235952010}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {C5398393-4686-40AD-834D-07AEE06617C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {C5398393-4686-40AD-834D-07AEE06617C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {C5398393-4686-40AD-834D-07AEE06617C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {C5398393-4686-40AD-834D-07AEE06617C0}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {D9900867-305A-496A-B848-FBD4A01850BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {D9900867-305A-496A-B848-FBD4A01850BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {D9900867-305A-496A-B848-FBD4A01850BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {D9900867-305A-496A-B848-FBD4A01850BE}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {B9224358-D078-45C4-9578-6126A31BCF01} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpanDex 2 | 3 | [![Build Status](https://travis-ci.org/jonorogers/SpanDex.svg?branch=master)](https://travis-ci.org/jonorogers/SpanDex) [![nuget](https://img.shields.io/nuget/v/SpanDex.svg)](https://www.nuget.org/packages/SpanDex/) [![netstandard 2.0](https://img.shields.io/badge/netstandard-2.0-brightgreen.svg)](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) 4 | 5 | Utilities to read/write primitives from/to spans easily. Uses the C# Span struct. 6 | 7 | ## Getting Started 8 | 9 | Install the [Nuget Package](https://www.nuget.org/packages/SpanDex) 10 | 11 | ### Motivation 12 | 13 | Reading/writing spans can be a chore, keeping track of where you're up to reading a span. This lib lets you simplify this: 14 | 15 | ```cs 16 | var one = BinaryPrimitives.ReadUInt16LittleEndian(span.Slice(3, 2)); 17 | var two = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(5, 4)); 18 | var three = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(9, 2)); 19 | ``` 20 | 21 | to this: 22 | 23 | ```cs 24 | int cursor = 0; 25 | var one = span.ReadUInt16LittleEndian(ref cursor); 26 | var two = span.ReadUInt32LittleEndian(ref cursor); 27 | var three = span.ReadUInt16BigEndian(ref cursor); 28 | ``` 29 | 30 | or: 31 | 32 | ```cs 33 | var spanReader = new SpanReader(span); 34 | var one = spanReader.ReadUInt16LittleEndian(); 35 | var two = spanReader.ReadUInt32LittleEndian(); 36 | var three = spanReader.ReadUInt16BigEndian(); 37 | ``` 38 | 39 | This lib is based around the `System.Buffers.Binary.BinaryPrimitives` class, with some other methods sprinkled in for some spice. 40 | 41 | ### Included types 42 | 43 | - `SpanReader` for reading a span 44 | - `SpanWriter` for creating and writing to a span; initialize with `size` or `Memory` 45 | - A tonne of extension methods, applicable to `Span` and `ReadOnlySpan` - all ripped straight from `System.Buffers.Binary.BinaryPrimitives`, as well as: 46 | - `span.ReadSpan()` - same as slice, but keeps track of where you're up to 47 | - `span.ReadASCIIString()` - does what it says on the packet 48 | - `span.ReadUTF8String()` - I think you get the drift 49 | 50 | ### Benchmark Results 51 | 52 | ``` ini 53 | 54 | BenchmarkDotNet=v0.11.5, OS=Windows 10.0.19041 55 | Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores 56 | .NET Core SDK=5.0.104 57 | [Host] : .NET Core 2.2.8 (CoreCLR 4.6.28207.03, CoreFX 4.6.28208.02), 64bit RyuJIT 58 | DefaultJob : .NET Core 2.2.8 (CoreCLR 4.6.28207.03, CoreFX 4.6.28208.02), 64bit RyuJIT 59 | 60 | 61 | ``` 62 | | Method | N | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated | 63 | |----------- |------ |------------:|----------:|----------:|-----:|------:|------:|------:|----------:| 64 | | **SpanReader** | **1000** | **1,201.3 ns** | **22.18 ns** | **20.75 ns** | **3** | **-** | **-** | **-** | **-** | 65 | | BitConvert | 1000 | 947.9 ns | 18.67 ns | 27.37 ns | 2 | - | - | - | - | 66 | | Manual | 1000 | 553.7 ns | 10.96 ns | 10.77 ns | 1 | - | - | - | - | 67 | | **SpanReader** | **10000** | **13,152.5 ns** | **262.35 ns** | **376.25 ns** | **6** | **-** | **-** | **-** | **-** | 68 | | BitConvert | 10000 | 9,786.4 ns | 168.66 ns | 187.47 ns | 5 | - | - | - | - | 69 | | Manual | 10000 | 5,379.4 ns | 107.46 ns | 182.47 ns | 4 | - | - | - | - | 70 | 71 | -------------------------------------------------------------------------------- /src/SpanDex/SpanDex.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SpanDex 5 | 6 | 7 | 8 | 9 | Provides an easy way to read from a Span 10 | 11 | 12 | 13 | 14 | Initializes a SpanReader using the given memory 15 | 16 | The memory to use 17 | Optional initial cursor position (defaults to 0) 18 | 19 | 20 | 21 | The current cursor position 22 | 23 | 24 | 25 | 26 | The space remaining in the memory 27 | 28 | 29 | 30 | 31 | The length of the memory 32 | 33 | 34 | 35 | 36 | Manually advances the cursor by the given length 37 | 38 | The amount (in bytes) to move the cursor 39 | 40 | 41 | 42 | Provides an easy way to write to a Span 43 | 44 | 45 | 46 | 47 | The underlying span 48 | 49 | 50 | 51 | 52 | Initializes a new SpanWriter with the provided size in bytes. 53 | 54 | The size (in bytes) of the underlying memory 55 | 56 | 57 | 58 | Initializes a new SpanWriter using an existing Span of memory 59 | 60 | The memory to use 61 | Optional initial cursor position to use (defaults to 0) 62 | 63 | 64 | 65 | The current cursor position 66 | 67 | 68 | 69 | 70 | The space remaining in the memory 71 | 72 | 73 | 74 | 75 | The length of the memory 76 | 77 | 78 | 79 | 80 | Returns a byte[] of the memory 81 | 82 | 83 | 84 | 85 | Returns a read only span of the memory 86 | 87 | 88 | 89 | 90 | Manually advances the cursor by the given length 91 | 92 | The amount (in bytes) to move the cursor 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/SpanDex/SpanReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SpanDex.Extensions; 3 | 4 | namespace SpanDex { 5 | /// 6 | /// Provides an easy way to read from a Span 7 | /// 8 | public ref struct SpanReader { 9 | private readonly ReadOnlySpan span; 10 | private int cursor; 11 | 12 | /// 13 | /// Initializes a SpanReader using the given memory 14 | /// 15 | /// The memory to use 16 | /// Optional initial cursor position (defaults to 0) 17 | public SpanReader(ReadOnlySpan span, int cursor = 0) { 18 | this.span = span; 19 | this.cursor = cursor; 20 | } 21 | 22 | public static implicit operator SpanReader(byte[] array) => new SpanReader(array); 23 | public static implicit operator SpanReader(Span span) => new SpanReader(span); 24 | public static implicit operator SpanReader(ReadOnlySpan span) => new SpanReader(span); 25 | public static implicit operator SpanReader(ArraySegment segment) => new SpanReader(segment); 26 | 27 | /// 28 | /// The current cursor position 29 | /// 30 | public int Cursor => cursor; 31 | 32 | /// 33 | /// The space remaining in the memory 34 | /// 35 | public int Remaining => span.Length - cursor; 36 | 37 | /// 38 | /// The length of the memory 39 | /// 40 | public int Length => span.Length; 41 | 42 | /// 43 | /// Manually advances the cursor by the given length 44 | /// 45 | /// The amount (in bytes) to move the cursor 46 | public void Advance(int length) { 47 | if (length <= 0) 48 | throw new ArgumentException("Length must be a non-negative integer"); 49 | 50 | cursor = checked(cursor + length); 51 | } 52 | 53 | public short ReadInt16BigEndian() => span.ReadInt16BigEndian(ref cursor); 54 | 55 | public short ReadInt16LittleEndian() => span.ReadInt16LittleEndian(ref cursor); 56 | 57 | public int ReadInt32BigEndian() => span.ReadInt32BigEndian(ref cursor); 58 | 59 | public int ReadInt32LittleEndian() => span.ReadInt32LittleEndian(ref cursor); 60 | 61 | public long ReadInt64BigEndian() => span.ReadInt64BigEndian(ref cursor); 62 | 63 | public long ReadInt64LittleEndian() => span.ReadInt64LittleEndian(ref cursor); 64 | 65 | [CLSCompliant(false)] 66 | public ushort ReadUInt16BigEndian() => span.ReadUInt16BigEndian(ref cursor); 67 | 68 | [CLSCompliant(false)] 69 | public ushort ReadUInt16LittleEndian() => span.ReadUInt16LittleEndian(ref cursor); 70 | 71 | [CLSCompliant(false)] 72 | public uint ReadUInt32BigEndian() => span.ReadUInt32BigEndian(ref cursor); 73 | 74 | [CLSCompliant(false)] 75 | public uint ReadUInt32LittleEndian() => span.ReadUInt32LittleEndian(ref cursor); 76 | 77 | [CLSCompliant(false)] 78 | public ulong ReadUInt64BigEndian() => span.ReadUInt64BigEndian(ref cursor); 79 | 80 | [CLSCompliant(false)] 81 | public ulong ReadUInt64LittleEndian() => span.ReadUInt64LittleEndian(ref cursor); 82 | 83 | public ReadOnlySpan ReadSpan(int size) => span.ReadSpan(size, ref cursor); 84 | 85 | public string ReadAsciiString(int size) => span.ReadAsciiString(size, ref cursor); 86 | 87 | public string ReadUtf8string(int size) => span.ReadUtf8String(size, ref cursor); 88 | 89 | public byte ReadByte() => span.ReadByte(ref cursor); 90 | 91 | public bool TryReadInt16BigEndian(out short value) => span.TryReadInt16BigEndian(out value, ref cursor); 92 | 93 | public bool TryReadInt16LittleEndian(out short value) => span.TryReadInt16LittleEndian(out value, ref cursor); 94 | 95 | public bool TryReadInt32BigEndian(out int value) => span.TryReadInt32BigEndian(out value, ref cursor); 96 | 97 | public bool TryReadInt32LittleEndian(out int value) => span.TryReadInt32LittleEndian(out value, ref cursor); 98 | 99 | public bool TryReadInt64BigEndian(out long value) => span.TryReadInt64BigEndian(out value, ref cursor); 100 | 101 | public bool TryReadInt64LittleEndian(out long value) => span.TryReadInt64LittleEndian(out value, ref cursor); 102 | 103 | [CLSCompliant(false)] 104 | public bool TryReadUInt16BigEndian(out ushort value) => span.TryReadUInt16BigEndian(out value, ref cursor); 105 | 106 | [CLSCompliant(false)] 107 | public bool TryReadUInt16LittleEndian(out ushort value) => span.TryReadUInt16LittleEndian(out value, ref cursor); 108 | 109 | [CLSCompliant(false)] 110 | public bool TryReadUInt32BigEndian(out uint value) => span.TryReadUInt32BigEndian(out value, ref cursor); 111 | 112 | [CLSCompliant(false)] 113 | public bool TryReadUInt32LittleEndian(out uint value) => span.TryReadUInt32LittleEndian(out value, ref cursor); 114 | 115 | [CLSCompliant(false)] 116 | public bool TryReadUInt64BigEndian(out ulong value) => span.TryReadUInt64BigEndian(out value, ref cursor); 117 | 118 | [CLSCompliant(false)] 119 | public bool TryReadUInt64LittleEndian(out ulong value) => span.TryReadUInt64LittleEndian(out value, ref cursor); 120 | 121 | public bool TryReadSpan(out ReadOnlySpan value, int size) => span.TryReadSpan(out value, size, ref cursor); 122 | 123 | public bool TryReadAsciiString(out string value, int size) => span.TryReadAsciiString(out value, size, ref cursor); 124 | 125 | public bool TryReadUtf8string(out string value, int size) => span.TryReadUtf8String(out value, size, ref cursor); 126 | 127 | public bool TryReadByte(out byte value) => span.TryReadByte(out value, ref cursor); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/SpanDex/SpanWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SpanDex.Extensions; 3 | 4 | namespace SpanDex { 5 | /// 6 | /// Provides an easy way to write to a Span 7 | /// 8 | public ref struct SpanWriter { 9 | /// 10 | /// The underlying span 11 | /// 12 | public readonly Span Span; 13 | private int cursor; 14 | 15 | /// 16 | /// Initializes a new SpanWriter with the provided size in bytes. 17 | /// 18 | /// The size (in bytes) of the underlying memory 19 | public SpanWriter(int size) { 20 | if (size <= 0) 21 | throw new ArgumentException("Size must be a non-negative integer"); 22 | 23 | this.Span = new byte[size]; 24 | this.cursor = 0; 25 | } 26 | 27 | /// 28 | /// Initializes a new SpanWriter using an existing Span of memory 29 | /// 30 | /// The memory to use 31 | /// Optional initial cursor position to use (defaults to 0) 32 | public SpanWriter(Span span, int cursor = 0) { 33 | if (span.Length == 0) 34 | throw new ArgumentNullException(nameof(span)); 35 | 36 | this.Span = span; 37 | this.cursor = cursor; 38 | } 39 | 40 | public static implicit operator SpanWriter(byte[] array) => new SpanWriter(array); 41 | public static implicit operator SpanWriter(Span span) => new SpanWriter(span); 42 | public static implicit operator SpanWriter(ArraySegment segment) => new SpanWriter(segment); 43 | 44 | /// 45 | /// The current cursor position 46 | /// 47 | public int Cursor => cursor; 48 | 49 | /// 50 | /// The space remaining in the memory 51 | /// 52 | public int Remaining => Span.Length - cursor; 53 | 54 | /// 55 | /// The length of the memory 56 | /// 57 | public int Length => Span.Length; 58 | 59 | /// 60 | /// Returns a byte[] of the memory 61 | /// 62 | public byte[] ToArray() => Span.ToArray(); 63 | 64 | /// 65 | /// Returns a read only span of the memory 66 | /// 67 | public ReadOnlySpan ToReadOnlySpan() => new ReadOnlySpan(ToArray()); 68 | 69 | /// 70 | /// Manually advances the cursor by the given length 71 | /// 72 | /// The amount (in bytes) to move the cursor 73 | public void Advance(int length) { 74 | if (length <= 0) 75 | throw new ArgumentException("Length must be a non-negative integer"); 76 | 77 | cursor = checked(cursor + length); 78 | } 79 | 80 | public bool TryWriteInt16BigEndian(short value) => Span.TryWriteInt16BigEndian(value, ref cursor); 81 | 82 | public bool TryWriteInt16LittleEndian(short value) => Span.TryWriteInt16LittleEndian(value, ref cursor); 83 | 84 | public bool TryWriteInt32BigEndian(int value) => Span.TryWriteInt32BigEndian(value, ref cursor); 85 | 86 | public bool TryWriteInt32LittleEndian(int value) => Span.TryWriteInt32LittleEndian(value, ref cursor); 87 | 88 | public bool TryWriteInt64BigEndian(long value) => Span.TryWriteInt64BigEndian(value, ref cursor); 89 | 90 | public bool TryWriteInt64LittleEndian(long value) => Span.TryWriteInt64LittleEndian(value, ref cursor); 91 | 92 | [CLSCompliant(false)] 93 | public bool TryWriteUInt16BigEndian(ushort value) => Span.TryWriteUInt16BigEndian(value, ref cursor); 94 | 95 | [CLSCompliant(false)] 96 | public bool TryWriteUInt16LittleEndian(ushort value) => Span.TryWriteUInt16LittleEndian(value, ref cursor); 97 | 98 | [CLSCompliant(false)] 99 | public bool TryWriteUInt32BigEndian(uint value) => Span.TryWriteUInt32BigEndian(value, ref cursor); 100 | 101 | [CLSCompliant(false)] 102 | public bool TryWriteUInt32LittleEndian(uint value) => Span.TryWriteUInt32LittleEndian(value, ref cursor); 103 | 104 | [CLSCompliant(false)] 105 | public bool TryWriteUInt64BigEndian(ulong value) => Span.TryWriteUInt64BigEndian(value, ref cursor); 106 | 107 | [CLSCompliant(false)] 108 | public bool TryWriteUInt64LittleEndian(ulong value) => Span.TryWriteUInt64LittleEndian(value, ref cursor); 109 | 110 | public bool TryWriteSpan(ReadOnlySpan value) => Span.TryWriteSpan(value, ref cursor); 111 | 112 | public bool TryWriteAsciiString(string value) => Span.TryWriteAsciiString(value, ref cursor); 113 | 114 | public bool TryWriteUtf8String(string value) => Span.TryWriteUtf8String(value, ref cursor); 115 | 116 | public bool TryWriteByte(byte value) => Span.TryWriteByte(value, ref cursor); 117 | 118 | public void WriteInt16BigEndian(short value) => Span.WriteInt16BigEndian(value, ref cursor); 119 | 120 | public void WriteInt16LittleEndian(short value) => Span.WriteInt16LittleEndian(value, ref cursor); 121 | 122 | public void WriteInt32BigEndian(int value) => Span.WriteInt32BigEndian(value, ref cursor); 123 | 124 | public void WriteInt32LittleEndian(int value) => Span.WriteInt32LittleEndian(value, ref cursor); 125 | 126 | public void WriteInt64BigEndian(long value) => Span.WriteInt64BigEndian(value, ref cursor); 127 | 128 | public void WriteInt64LittleEndian(long value) => Span.WriteInt64LittleEndian(value, ref cursor); 129 | 130 | [CLSCompliant(false)] 131 | public void WriteUInt16BigEndian(ushort value) => Span.WriteUInt16BigEndian(value, ref cursor); 132 | 133 | [CLSCompliant(false)] 134 | public void WriteUInt16LittleEndian(ushort value) => Span.WriteUInt16LittleEndian(value, ref cursor); 135 | 136 | [CLSCompliant(false)] 137 | public void WriteUInt32BigEndian(uint value) => Span.WriteUInt32BigEndian(value, ref cursor); 138 | 139 | [CLSCompliant(false)] 140 | public void WriteUInt32LittleEndian(uint value) => Span.WriteUInt32LittleEndian(value, ref cursor); 141 | 142 | [CLSCompliant(false)] 143 | public void WriteUInt64BigEndian(ulong value) => Span.WriteUInt64BigEndian(value, ref cursor); 144 | 145 | [CLSCompliant(false)] 146 | public void WriteUInt64LittleEndian(ulong value) => Span.WriteUInt64LittleEndian(value, ref cursor); 147 | 148 | public void WriteSpan(ReadOnlySpan value) => Span.WriteSpan(value, ref cursor); 149 | 150 | public void WriteAsciiString(string value) => Span.WriteAsciiString(value, ref cursor); 151 | 152 | public void WriteUtf8String(string value) => Span.WriteUtf8String(value, ref cursor); 153 | 154 | public void WriteByte(byte value) => Span.WriteByte(value, ref cursor); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /.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 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /src/SpanDex/SpanWriting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Text; 4 | 5 | namespace SpanDex { 6 | internal static class SpanWriting { 7 | internal static bool TryWriteInt16BigEndian(Span destination, short value, ref int cursor) { 8 | if (!ValidateTryWrite(destination, cursor, 2)) 9 | return false; 10 | 11 | return TryAdvance(BinaryPrimitives.TryWriteInt16BigEndian(destination.Slice(cursor, 2), value), ref cursor, 2); 12 | } 13 | 14 | internal static bool TryWriteInt16LittleEndian(Span destination, short value, ref int cursor) { 15 | if (!ValidateTryWrite(destination, cursor, 2)) 16 | return false; 17 | 18 | return TryAdvance(BinaryPrimitives.TryWriteInt16LittleEndian(destination.Slice(cursor, 2), value), ref cursor, 2); 19 | } 20 | 21 | internal static bool TryWriteInt32BigEndian(Span destination, int value, ref int cursor) { 22 | if (!ValidateTryWrite(destination, cursor, 4)) 23 | return false; 24 | 25 | return TryAdvance(BinaryPrimitives.TryWriteInt32BigEndian(destination.Slice(cursor, 4), value), ref cursor, 4); 26 | } 27 | 28 | internal static bool TryWriteInt32LittleEndian(Span destination, int value, ref int cursor) { 29 | if (!ValidateTryWrite(destination, cursor, 4)) 30 | return false; 31 | 32 | return TryAdvance(BinaryPrimitives.TryWriteInt32LittleEndian(destination.Slice(cursor, 4), value), ref cursor, 4); 33 | } 34 | 35 | internal static bool TryWriteInt64BigEndian(Span destination, long value, ref int cursor) { 36 | if (!ValidateTryWrite(destination, cursor, 8)) 37 | return false; 38 | 39 | return TryAdvance(BinaryPrimitives.TryWriteInt64BigEndian(destination.Slice(cursor, 8), value), ref cursor, 8); 40 | } 41 | 42 | internal static bool TryWriteInt64LittleEndian(Span destination, long value, ref int cursor) { 43 | if (!ValidateTryWrite(destination, cursor, 8)) 44 | return false; 45 | 46 | return TryAdvance(BinaryPrimitives.TryWriteInt64LittleEndian(destination.Slice(cursor, 8), value), ref cursor, 8); 47 | } 48 | 49 | internal static bool TryWriteUInt16BigEndian(Span destination, ushort value, ref int cursor) { 50 | if (!ValidateTryWrite(destination, cursor, 2)) 51 | return false; 52 | 53 | return TryAdvance(BinaryPrimitives.TryWriteUInt16BigEndian(destination.Slice(cursor, 2), value), ref cursor, 2); 54 | } 55 | 56 | internal static bool TryWriteUInt16LittleEndian(Span destination, ushort value, ref int cursor) { 57 | if (!ValidateTryWrite(destination, cursor, 2)) 58 | return false; 59 | 60 | return TryAdvance(BinaryPrimitives.TryWriteUInt16LittleEndian(destination.Slice(cursor, 2), value), ref cursor, 2); 61 | } 62 | 63 | internal static bool TryWriteUInt32BigEndian(Span destination, uint value, ref int cursor) { 64 | if (!ValidateTryWrite(destination, cursor, 4)) 65 | return false; 66 | 67 | return TryAdvance(BinaryPrimitives.TryWriteUInt32BigEndian(destination.Slice(cursor, 4), value), ref cursor, 4); 68 | } 69 | 70 | internal static bool TryWriteUInt32LittleEndian(Span destination, uint value, ref int cursor) { 71 | if (!ValidateTryWrite(destination, cursor, 4)) 72 | return false; 73 | 74 | return TryAdvance(BinaryPrimitives.TryWriteUInt32LittleEndian(destination.Slice(cursor, 4), value), ref cursor, 4); 75 | } 76 | 77 | internal static bool TryWriteUInt64BigEndian(Span destination, ulong value, ref int cursor) { 78 | if (!ValidateTryWrite(destination, cursor, 8)) 79 | return false; 80 | 81 | return TryAdvance(BinaryPrimitives.TryWriteUInt64BigEndian(destination.Slice(cursor, 8), value), ref cursor, 8); 82 | } 83 | 84 | internal static bool TryWriteUInt64LittleEndian(Span destination, ulong value, ref int cursor) { 85 | if (!ValidateTryWrite(destination, cursor, 8)) 86 | return false; 87 | 88 | return TryAdvance(BinaryPrimitives.TryWriteUInt64LittleEndian(destination.Slice(cursor, 8), value), ref cursor, 8); 89 | } 90 | 91 | internal static bool TryWriteSpan(Span destination, ReadOnlySpan value, ref int cursor) { 92 | ValidateCursor(cursor); 93 | if (value == default) 94 | throw new ArgumentNullException(nameof(value)); 95 | 96 | if (destination.HasSpace(cursor + value.Length)) { 97 | WriteSpan(destination, value, ref cursor); 98 | return true; 99 | } 100 | return false; 101 | } 102 | 103 | internal static bool TryWriteAsciiString(Span destination, string value, ref int cursor) { 104 | ValidateCursor(cursor); 105 | if (value == null) 106 | throw new ArgumentNullException(nameof(value)); 107 | 108 | return TryWriteSpan(destination, Encoding.ASCII.GetBytes(value), ref cursor); 109 | } 110 | 111 | internal static bool TryWriteUtf8String(Span destination, string value, ref int cursor) { 112 | ValidateCursor(cursor); 113 | if (value == null) 114 | throw new ArgumentNullException(nameof(value)); 115 | 116 | return TryWriteSpan(destination, Encoding.UTF8.GetBytes(value), ref cursor); 117 | } 118 | 119 | internal static bool TryWriteByte(Span destination, byte value, ref int cursor) { 120 | if(destination.HasSpace(cursor + 1)) { 121 | WriteByte(destination, value, ref cursor); 122 | return true; 123 | } 124 | return false; 125 | } 126 | 127 | internal static void WriteInt16BigEndian(Span destination, short value, ref int cursor) { 128 | ValidateCursor(cursor); 129 | BinaryPrimitives.WriteInt16BigEndian(destination.SliceAndAdvance(ref cursor, 2), value); 130 | } 131 | 132 | internal static void WriteInt16LittleEndian(Span destination, short value, ref int cursor) { 133 | ValidateCursor(cursor); 134 | BinaryPrimitives.WriteInt16LittleEndian(destination.SliceAndAdvance(ref cursor, 2), value); 135 | } 136 | 137 | internal static void WriteInt32BigEndian(Span destination, int value, ref int cursor) { 138 | ValidateCursor(cursor); 139 | BinaryPrimitives.WriteInt32BigEndian(destination.SliceAndAdvance(ref cursor, 4), value); 140 | } 141 | 142 | internal static void WriteInt32LittleEndian(Span destination, int value, ref int cursor) { 143 | ValidateCursor(cursor); 144 | BinaryPrimitives.WriteInt32LittleEndian(destination.SliceAndAdvance(ref cursor, 4), value); 145 | } 146 | 147 | internal static void WriteInt64BigEndian(Span destination, long value, ref int cursor) { 148 | ValidateCursor(cursor); 149 | BinaryPrimitives.WriteInt64BigEndian(destination.SliceAndAdvance(ref cursor, 8), value); 150 | } 151 | 152 | internal static void WriteInt64LittleEndian(Span destination, long value, ref int cursor) { 153 | ValidateCursor(cursor); 154 | BinaryPrimitives.WriteInt64LittleEndian(destination.SliceAndAdvance(ref cursor, 8), value); 155 | } 156 | 157 | internal static void WriteUInt16BigEndian(Span destination, ushort value, ref int cursor) { 158 | ValidateCursor(cursor); 159 | BinaryPrimitives.WriteUInt16BigEndian(destination.SliceAndAdvance(ref cursor, 2), value); 160 | } 161 | 162 | internal static void WriteUInt16LittleEndian(Span destination, ushort value, ref int cursor) { 163 | ValidateCursor(cursor); 164 | BinaryPrimitives.WriteUInt16LittleEndian(destination.SliceAndAdvance(ref cursor, 2), value); 165 | } 166 | 167 | internal static void WriteUInt32BigEndian(Span destination, uint value, ref int cursor) { 168 | ValidateCursor(cursor); 169 | BinaryPrimitives.WriteUInt32BigEndian(destination.SliceAndAdvance(ref cursor, 4), value); 170 | } 171 | 172 | internal static void WriteUInt32LittleEndian(Span destination, uint value, ref int cursor) { 173 | ValidateCursor(cursor); 174 | BinaryPrimitives.WriteUInt32LittleEndian(destination.SliceAndAdvance(ref cursor, 4), value); 175 | } 176 | 177 | internal static void WriteUInt64BigEndian(Span destination, ulong value, ref int cursor) { 178 | ValidateCursor(cursor); 179 | BinaryPrimitives.WriteUInt64BigEndian(destination.SliceAndAdvance(ref cursor, 8), value); 180 | } 181 | 182 | internal static void WriteUInt64LittleEndian(Span destination, ulong value, ref int cursor) { 183 | ValidateCursor(cursor); 184 | BinaryPrimitives.WriteUInt64LittleEndian(destination.SliceAndAdvance(ref cursor, 8), value); 185 | } 186 | 187 | internal static void WriteSpan(Span destination, ReadOnlySpan value, ref int cursor) { 188 | ValidateCursor(cursor); 189 | if (value == default) 190 | throw new ArgumentNullException(nameof(value)); 191 | 192 | value.CopyTo(destination.Slice(cursor, value.Length)); 193 | IncrementCursor(ref cursor, value.Length); 194 | } 195 | 196 | internal static void WriteAsciiString(Span destination, string value, ref int cursor) { 197 | ValidateCursor(cursor); 198 | if (value == null) 199 | throw new ArgumentNullException(nameof(value)); 200 | 201 | WriteSpan(destination, Encoding.ASCII.GetBytes(value), ref cursor); 202 | } 203 | 204 | internal static void WriteUtf8String(Span destination, string value, ref int cursor) { 205 | ValidateCursor(cursor); 206 | if (value == null) 207 | throw new ArgumentNullException(nameof(value)); 208 | 209 | WriteSpan(destination, Encoding.UTF8.GetBytes(value), ref cursor); 210 | } 211 | 212 | internal static void WriteByte(Span destination, byte value, ref int cursor) { 213 | ValidateCursor(cursor); 214 | destination[cursor] = value; 215 | IncrementCursor(ref cursor, 1); 216 | } 217 | 218 | private static bool ValidateTryWrite(Span source, int cursor, int size) { 219 | ValidateCursor(cursor); 220 | return HasSpace(source, cursor + size); 221 | } 222 | 223 | private static Span SliceAndAdvance(this Span source, ref int cursor, int size) { 224 | Span slice = source.Slice(cursor, size); 225 | IncrementCursor(ref cursor, size); 226 | return slice; 227 | } 228 | 229 | private static bool TryAdvance(bool succeeded, ref int cursor, int size) { 230 | if (succeeded) 231 | IncrementCursor(ref cursor, size); 232 | return succeeded; 233 | } 234 | 235 | private static void IncrementCursor(ref int cursor, int size) => cursor = checked(cursor + size); 236 | 237 | private static bool HasSpace(this Span source, int space) => source.Length >= space; 238 | 239 | private static void ValidateCursor(int cursor) { 240 | if (cursor < 0) 241 | throw new ArgumentException("Cursor cannot be less than 0"); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/SpanDex/SpanReading.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Diagnostics.Contracts; 4 | using System.Text; 5 | 6 | namespace SpanDex { 7 | internal static class SpanReading { 8 | internal static short ReadInt16BigEndian(ReadOnlySpan source, ref int cursor) { 9 | ValidateCursor(cursor); 10 | return BinaryPrimitives.ReadInt16BigEndian(source.SliceAndAdvance(ref cursor, 2)); 11 | } 12 | 13 | internal static short ReadInt16LittleEndian(ReadOnlySpan source, ref int cursor) { 14 | ValidateCursor(cursor); 15 | return BinaryPrimitives.ReadInt16LittleEndian(source.SliceAndAdvance(ref cursor, 2)); 16 | } 17 | 18 | internal static int ReadInt32BigEndian(ReadOnlySpan source, ref int cursor) { 19 | ValidateCursor(cursor); 20 | return BinaryPrimitives.ReadInt32BigEndian(source.SliceAndAdvance(ref cursor, 4)); 21 | } 22 | 23 | internal static int ReadInt32LittleEndian(ReadOnlySpan source, ref int cursor) { 24 | ValidateCursor(cursor); 25 | return BinaryPrimitives.ReadInt32LittleEndian(source.SliceAndAdvance(ref cursor, 4)); 26 | } 27 | 28 | internal static long ReadInt64BigEndian(ReadOnlySpan source, ref int cursor) { 29 | ValidateCursor(cursor); 30 | return BinaryPrimitives.ReadInt64BigEndian(source.SliceAndAdvance(ref cursor, 8)); 31 | } 32 | 33 | internal static long ReadInt64LittleEndian(ReadOnlySpan source, ref int cursor) { 34 | ValidateCursor(cursor); 35 | return BinaryPrimitives.ReadInt64LittleEndian(source.SliceAndAdvance(ref cursor, 8)); 36 | } 37 | 38 | internal static ushort ReadUInt16BigEndian(ReadOnlySpan source, ref int cursor) { 39 | ValidateCursor(cursor); 40 | return BinaryPrimitives.ReadUInt16BigEndian(source.SliceAndAdvance(ref cursor, 2)); 41 | } 42 | 43 | internal static ushort ReadUInt16LittleEndian(ReadOnlySpan source, ref int cursor) { 44 | ValidateCursor(cursor); 45 | return BinaryPrimitives.ReadUInt16LittleEndian(source.SliceAndAdvance(ref cursor, 2)); 46 | } 47 | 48 | internal static uint ReadUInt32BigEndian(ReadOnlySpan source, ref int cursor) { 49 | ValidateCursor(cursor); 50 | return BinaryPrimitives.ReadUInt32BigEndian(source.SliceAndAdvance(ref cursor, 4)); 51 | } 52 | 53 | internal static uint ReadUInt32LittleEndian(ReadOnlySpan source, ref int cursor) { 54 | ValidateCursor(cursor); 55 | return BinaryPrimitives.ReadUInt32LittleEndian(source.SliceAndAdvance(ref cursor, 4)); 56 | } 57 | 58 | internal static ulong ReadUInt64BigEndian(ReadOnlySpan source, ref int cursor) { 59 | ValidateCursor(cursor); 60 | return BinaryPrimitives.ReadUInt64BigEndian(source.SliceAndAdvance(ref cursor, 8)); 61 | } 62 | 63 | internal static ulong ReadUInt64LittleEndian(ReadOnlySpan source, ref int cursor) { 64 | ValidateCursor(cursor); 65 | return BinaryPrimitives.ReadUInt64LittleEndian(source.SliceAndAdvance(ref cursor, 8)); 66 | } 67 | 68 | internal static ReadOnlySpan ReadSpan(ReadOnlySpan source, int size, ref int cursor) { 69 | ValidateCursor(cursor); 70 | return source.SliceAndAdvance(ref cursor, size); 71 | } 72 | 73 | internal static string ReadAsciiString(ReadOnlySpan source, int size, ref int cursor) { 74 | ValidateCursor(cursor); 75 | var slice = ReadSpan(source, size, ref cursor); 76 | return Encoding.ASCII.GetString(slice.ToArray()); 77 | } 78 | 79 | internal static string ReadUtf8String(ReadOnlySpan source, int size, ref int cursor) { 80 | ValidateCursor(cursor); 81 | var slice = ReadSpan(source, size, ref cursor); 82 | return Encoding.UTF8.GetString(slice.ToArray()); 83 | } 84 | 85 | internal static byte ReadByte(ReadOnlySpan source, ref int cursor) { 86 | ValidateCursor(cursor); 87 | var bite = source[cursor]; 88 | IncrementCursor(ref cursor, 1); 89 | return bite; 90 | } 91 | 92 | internal static bool TryReadInt16BigEndian(ReadOnlySpan source, out short value, ref int cursor) { 93 | if (!ValidateTryRead(source, out value, cursor, 2)) 94 | return false; 95 | 96 | return TryAdvance(BinaryPrimitives.TryReadInt16BigEndian(source.Slice(cursor, 2), out value), ref cursor, 2); 97 | } 98 | 99 | internal static bool TryReadInt16LittleEndian(ReadOnlySpan source, out short value, ref int cursor) { 100 | if (!ValidateTryRead(source, out value, cursor, 2)) 101 | return false; 102 | 103 | return TryAdvance(BinaryPrimitives.TryReadInt16LittleEndian(source.Slice(cursor, 2), out value), ref cursor, 2); 104 | } 105 | 106 | internal static bool TryReadInt32BigEndian(ReadOnlySpan source, out int value, ref int cursor) { 107 | if (!ValidateTryRead(source, out value, cursor, 4)) 108 | return false; 109 | 110 | return TryAdvance(BinaryPrimitives.TryReadInt32BigEndian(source.Slice(cursor, 4), out value), ref cursor, 4); 111 | } 112 | 113 | internal static bool TryReadInt32LittleEndian(ReadOnlySpan source, out int value, ref int cursor) { 114 | if (!ValidateTryRead(source, out value, cursor, 4)) 115 | return false; 116 | 117 | return TryAdvance(BinaryPrimitives.TryReadInt32LittleEndian(source.Slice(cursor, 4), out value), ref cursor, 4); 118 | } 119 | 120 | internal static bool TryReadInt64BigEndian(ReadOnlySpan source, out long value, ref int cursor) { 121 | if (!ValidateTryRead(source, out value, cursor, 8)) 122 | return false; 123 | 124 | return TryAdvance(BinaryPrimitives.TryReadInt64BigEndian(source.Slice(cursor, 8), out value), ref cursor, 8); 125 | } 126 | 127 | internal static bool TryReadInt64LittleEndian(ReadOnlySpan source, out long value, ref int cursor) { 128 | if (!ValidateTryRead(source, out value, cursor, 8)) 129 | return false; 130 | 131 | return TryAdvance(BinaryPrimitives.TryReadInt64LittleEndian(source.Slice(cursor, 8), out value), ref cursor, 8); 132 | } 133 | 134 | internal static bool TryReadUInt16BigEndian(ReadOnlySpan source, out ushort value, ref int cursor) { 135 | if (!ValidateTryRead(source, out value, cursor, 2)) 136 | return false; 137 | 138 | return TryAdvance(BinaryPrimitives.TryReadUInt16BigEndian(source.Slice(cursor, 2), out value), ref cursor, 2); 139 | } 140 | 141 | internal static bool TryReadUInt16LittleEndian(ReadOnlySpan source, out ushort value, ref int cursor) { 142 | if (!ValidateTryRead(source, out value, cursor, 2)) 143 | return false; 144 | 145 | return TryAdvance(BinaryPrimitives.TryReadUInt16LittleEndian(source.Slice(cursor, 2), out value), ref cursor, 2); 146 | } 147 | 148 | internal static bool TryReadUInt32BigEndian(ReadOnlySpan source, out uint value, ref int cursor) { 149 | if (!ValidateTryRead(source, out value, cursor, 4)) 150 | return false; 151 | 152 | return TryAdvance(BinaryPrimitives.TryReadUInt32BigEndian(source.Slice(cursor, 4), out value), ref cursor, 4); 153 | } 154 | 155 | internal static bool TryReadUInt32LittleEndian(ReadOnlySpan source, out uint value, ref int cursor) { 156 | if (!ValidateTryRead(source, out value, cursor, 4)) 157 | return false; 158 | 159 | return TryAdvance(BinaryPrimitives.TryReadUInt32LittleEndian(source.Slice(cursor, 4), out value), ref cursor, 4); 160 | } 161 | 162 | internal static bool TryReadUInt64BigEndian(ReadOnlySpan source, out ulong value, ref int cursor) { 163 | if (!ValidateTryRead(source, out value, cursor, 8)) 164 | return false; 165 | 166 | return TryAdvance(BinaryPrimitives.TryReadUInt64BigEndian(source.Slice(cursor, 8), out value), ref cursor, 8); 167 | } 168 | 169 | internal static bool TryReadUInt64LittleEndian(ReadOnlySpan source, out ulong value, ref int cursor) { 170 | if (!ValidateTryRead(source, out value, cursor, 8)) 171 | return false; 172 | 173 | return TryAdvance(BinaryPrimitives.TryReadUInt64LittleEndian(source.Slice(cursor, 8), out value), ref cursor, 8); 174 | } 175 | 176 | internal static bool TryReadSpan(ReadOnlySpan source, out ReadOnlySpan value, int size, ref int cursor) { 177 | ValidateCursor(cursor); 178 | value = default; 179 | if (source.HasSpace(cursor + size)) { 180 | value = ReadSpan(source, size, ref cursor); 181 | return true; 182 | } 183 | return false; 184 | } 185 | 186 | internal static bool TryReadAsciiString(ReadOnlySpan source, out string value, int size, ref int cursor) { 187 | ValidateCursor(cursor); 188 | value = default; 189 | if (source.HasSpace(cursor + size)) { 190 | value = ReadAsciiString(source, size, ref cursor); 191 | return true; 192 | } 193 | return false; 194 | } 195 | 196 | internal static bool TryReadUtf8String(ReadOnlySpan source, out string value, int size, ref int cursor) { 197 | ValidateCursor(cursor); 198 | value = default; 199 | if (source.HasSpace(cursor + size)) { 200 | value = ReadUtf8String(source, size, ref cursor); 201 | return true; 202 | } 203 | return false; 204 | } 205 | 206 | internal static bool TryReadByte(ReadOnlySpan source, out byte value, ref int cursor) { 207 | ValidateCursor(cursor); 208 | 209 | value = default; 210 | if (source.HasSpace(cursor + 1)) { 211 | value = ReadByte(source, ref cursor); 212 | return true; 213 | } 214 | return false; 215 | } 216 | 217 | private static bool ValidateTryRead(ReadOnlySpan source, out T value, int cursor, int size) { 218 | ValidateCursor(cursor); 219 | 220 | value = default; 221 | return HasSpace(source, cursor + size); 222 | } 223 | 224 | private static ReadOnlySpan SliceAndAdvance(this ReadOnlySpan source, ref int cursor, int size) { 225 | if (size <= 0) 226 | throw new ArgumentException("Size must be a positive integer"); 227 | 228 | ReadOnlySpan slice = source.Slice(cursor, size); 229 | IncrementCursor(ref cursor, size); 230 | return slice; 231 | } 232 | 233 | private static bool TryAdvance(bool succeeded, ref int cursor, int size) { 234 | if (succeeded) 235 | IncrementCursor(ref cursor, size); 236 | return succeeded; 237 | } 238 | 239 | private static void IncrementCursor(ref int cursor, int size) => cursor = checked(cursor + size); 240 | 241 | private static bool HasSpace(this ReadOnlySpan source, int space) => source.Length >= space; 242 | 243 | private static void ValidateCursor(int cursor) { 244 | if (cursor < 0) 245 | throw new ArgumentException("Cursor cannot be less than 0"); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/SpanDex/SpanExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SpanDex.Extensions { 4 | public static class SpanExtensions { 5 | public static short ReadInt16BigEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadInt16BigEndian(source, ref cursor); 6 | public static short ReadInt16LittleEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadInt16LittleEndian(source, ref cursor); 7 | public static int ReadInt32BigEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadInt32BigEndian(source, ref cursor); 8 | public static int ReadInt32LittleEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadInt32LittleEndian(source, ref cursor); 9 | public static long ReadInt64BigEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadInt64BigEndian(source, ref cursor); 10 | public static long ReadInt64LittleEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadInt64LittleEndian(source, ref cursor); 11 | 12 | [CLSCompliant(false)] 13 | public static ushort ReadUInt16BigEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadUInt16BigEndian(source, ref cursor); 14 | 15 | [CLSCompliant(false)] 16 | public static ushort ReadUInt16LittleEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadUInt16LittleEndian(source, ref cursor); 17 | 18 | [CLSCompliant(false)] 19 | public static uint ReadUInt32BigEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadUInt32BigEndian(source, ref cursor); 20 | 21 | [CLSCompliant(false)] 22 | public static uint ReadUInt32LittleEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadUInt32LittleEndian(source, ref cursor); 23 | 24 | [CLSCompliant(false)] 25 | public static ulong ReadUInt64BigEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadUInt64BigEndian(source, ref cursor); 26 | 27 | [CLSCompliant(false)] 28 | public static ulong ReadUInt64LittleEndian(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadUInt64LittleEndian(source, ref cursor); 29 | 30 | public static ReadOnlySpan ReadSpan(this ReadOnlySpan source, int size, ref int cursor) => SpanReading.ReadSpan(source, size, ref cursor); 31 | public static string ReadAsciiString(this ReadOnlySpan source, int size, ref int cursor) => SpanReading.ReadAsciiString(source, size, ref cursor); 32 | public static string ReadUtf8String(this ReadOnlySpan source, int size, ref int cursor) => SpanReading.ReadUtf8String(source, size, ref cursor); 33 | public static byte ReadByte(this ReadOnlySpan source, ref int cursor) => SpanReading.ReadByte(source, ref cursor); 34 | public static bool TryReadInt16BigEndian(this ReadOnlySpan source, out short value, ref int cursor) => SpanReading.TryReadInt16BigEndian(source, out value, ref cursor); 35 | public static bool TryReadInt16LittleEndian(this ReadOnlySpan source, out short value, ref int cursor) => SpanReading.TryReadInt16LittleEndian(source, out value, ref cursor); 36 | public static bool TryReadInt32BigEndian(this ReadOnlySpan source, out int value, ref int cursor) => SpanReading.TryReadInt32BigEndian(source, out value, ref cursor); 37 | public static bool TryReadInt32LittleEndian(this ReadOnlySpan source, out int value, ref int cursor) => SpanReading.TryReadInt32LittleEndian(source, out value, ref cursor); 38 | public static bool TryReadInt64BigEndian(this ReadOnlySpan source, out long value, ref int cursor) => SpanReading.TryReadInt64BigEndian(source, out value, ref cursor); 39 | public static bool TryReadInt64LittleEndian(this ReadOnlySpan source, out long value, ref int cursor) => SpanReading.TryReadInt64LittleEndian(source, out value, ref cursor); 40 | 41 | [CLSCompliant(false)] 42 | public static bool TryReadUInt16BigEndian(this ReadOnlySpan source, out ushort value, ref int cursor) => SpanReading.TryReadUInt16BigEndian(source, out value, ref cursor); 43 | 44 | [CLSCompliant(false)] 45 | public static bool TryReadUInt16LittleEndian(this ReadOnlySpan source, out ushort value, ref int cursor) => SpanReading.TryReadUInt16LittleEndian(source, out value, ref cursor); 46 | 47 | [CLSCompliant(false)] 48 | public static bool TryReadUInt32BigEndian(this ReadOnlySpan source, out uint value, ref int cursor) => SpanReading.TryReadUInt32BigEndian(source, out value, ref cursor); 49 | 50 | [CLSCompliant(false)] 51 | public static bool TryReadUInt32LittleEndian(this ReadOnlySpan source, out uint value, ref int cursor) => SpanReading.TryReadUInt32LittleEndian(source, out value, ref cursor); 52 | 53 | [CLSCompliant(false)] 54 | public static bool TryReadUInt64BigEndian(this ReadOnlySpan source, out ulong value, ref int cursor) => SpanReading.TryReadUInt64BigEndian(source, out value, ref cursor); 55 | 56 | [CLSCompliant(false)] 57 | public static bool TryReadUInt64LittleEndian(this ReadOnlySpan source, out ulong value, ref int cursor) => SpanReading.TryReadUInt64LittleEndian(source, out value, ref cursor); 58 | 59 | public static bool TryReadSpan(this ReadOnlySpan source, out ReadOnlySpan value, int size, ref int cursor) => SpanReading.TryReadSpan(source, out value, size, ref cursor); 60 | public static bool TryReadAsciiString(this ReadOnlySpan source, out string value, int size, ref int cursor) => SpanReading.TryReadAsciiString(source, out value, size, ref cursor); 61 | public static bool TryReadUtf8String(this ReadOnlySpan source, out string value, int size, ref int cursor) => SpanReading.TryReadUtf8String(source, out value, size, ref cursor); 62 | public static bool TryReadByte(this ReadOnlySpan source, out byte value, ref int cursor) => SpanReading.TryReadByte(source, out value, ref cursor); 63 | public static short ReadInt16BigEndian(this Span source, ref int cursor) => SpanReading.ReadInt16BigEndian(source, ref cursor); 64 | public static short ReadInt16LittleEndian(this Span source, ref int cursor) => SpanReading.ReadInt16LittleEndian(source, ref cursor); 65 | public static int ReadInt32BigEndian(this Span source, ref int cursor) => SpanReading.ReadInt32BigEndian(source, ref cursor); 66 | public static int ReadInt32LittleEndian(this Span source, ref int cursor) => SpanReading.ReadInt32LittleEndian(source, ref cursor); 67 | public static long ReadInt64BigEndian(this Span source, ref int cursor) => SpanReading.ReadInt64BigEndian(source, ref cursor); 68 | public static long ReadInt64LittleEndian(this Span source, ref int cursor) => SpanReading.ReadInt64LittleEndian(source, ref cursor); 69 | 70 | [CLSCompliant(false)] 71 | public static ushort ReadUInt16BigEndian(this Span source, ref int cursor) => SpanReading.ReadUInt16BigEndian(source, ref cursor); 72 | 73 | [CLSCompliant(false)] 74 | public static ushort ReadUInt16LittleEndian(this Span source, ref int cursor) => SpanReading.ReadUInt16LittleEndian(source, ref cursor); 75 | 76 | [CLSCompliant(false)] 77 | public static uint ReadUInt32BigEndian(this Span source, ref int cursor) => SpanReading.ReadUInt32BigEndian(source, ref cursor); 78 | 79 | [CLSCompliant(false)] 80 | public static uint ReadUInt32LittleEndian(this Span source, ref int cursor) => SpanReading.ReadUInt32LittleEndian(source, ref cursor); 81 | 82 | [CLSCompliant(false)] 83 | public static ulong ReadUInt64BigEndian(this Span source, ref int cursor) => SpanReading.ReadUInt64BigEndian(source, ref cursor); 84 | 85 | [CLSCompliant(false)] 86 | public static ulong ReadUInt64LittleEndian(this Span source, ref int cursor) => SpanReading.ReadUInt64LittleEndian(source, ref cursor); 87 | 88 | public static ReadOnlySpan ReadSpan(this Span source, int size, ref int cursor) => SpanReading.ReadSpan(source, size, ref cursor); 89 | public static string ReadAsciiString(this Span source, int size, ref int cursor) => SpanReading.ReadAsciiString(source, size, ref cursor); 90 | public static string ReadUtf8String(this Span source, int size, ref int cursor) => SpanReading.ReadUtf8String(source, size, ref cursor); 91 | public static byte ReadByte(this Span source, ref int cursor) => SpanReading.ReadByte(source, ref cursor); 92 | public static bool TryReadInt16BigEndian(this Span source, out short value, ref int cursor) => SpanReading.TryReadInt16BigEndian(source, out value, ref cursor); 93 | public static bool TryReadInt16LittleEndian(this Span source, out short value, ref int cursor) => SpanReading.TryReadInt16LittleEndian(source, out value, ref cursor); 94 | public static bool TryReadInt32BigEndian(this Span source, out int value, ref int cursor) => SpanReading.TryReadInt32BigEndian(source, out value, ref cursor); 95 | public static bool TryReadInt32LittleEndian(this Span source, out int value, ref int cursor) => SpanReading.TryReadInt32LittleEndian(source, out value, ref cursor); 96 | public static bool TryReadInt64BigEndian(this Span source, out long value, ref int cursor) => SpanReading.TryReadInt64BigEndian(source, out value, ref cursor); 97 | public static bool TryReadInt64LittleEndian(this Span source, out long value, ref int cursor) => SpanReading.TryReadInt64LittleEndian(source, out value, ref cursor); 98 | 99 | [CLSCompliant(false)] 100 | public static bool TryReadUInt16BigEndian(this Span source, out ushort value, ref int cursor) => SpanReading.TryReadUInt16BigEndian(source, out value, ref cursor); 101 | 102 | [CLSCompliant(false)] 103 | public static bool TryReadUInt16LittleEndian(this Span source, out ushort value, ref int cursor) => SpanReading.TryReadUInt16LittleEndian(source, out value, ref cursor); 104 | 105 | [CLSCompliant(false)] 106 | public static bool TryReadUInt32BigEndian(this Span source, out uint value, ref int cursor) => SpanReading.TryReadUInt32BigEndian(source, out value, ref cursor); 107 | 108 | [CLSCompliant(false)] 109 | public static bool TryReadUInt32LittleEndian(this Span source, out uint value, ref int cursor) => SpanReading.TryReadUInt32LittleEndian(source, out value, ref cursor); 110 | 111 | [CLSCompliant(false)] 112 | public static bool TryReadUInt64BigEndian(this Span source, out ulong value, ref int cursor) => SpanReading.TryReadUInt64BigEndian(source, out value, ref cursor); 113 | 114 | [CLSCompliant(false)] 115 | public static bool TryReadUInt64LittleEndian(this Span source, out ulong value, ref int cursor) => SpanReading.TryReadUInt64LittleEndian(source, out value, ref cursor); 116 | 117 | public static bool TryReadSpan(this Span source, out ReadOnlySpan value, int size, ref int cursor) => SpanReading.TryReadSpan(source, out value, size, ref cursor); 118 | public static bool TryReadAsciiString(this Span source, out string value, int size, ref int cursor) => SpanReading.TryReadAsciiString(source, out value, size, ref cursor); 119 | public static bool TryReadUtf8String(this Span source, out string value, int size, ref int cursor) => SpanReading.TryReadUtf8String(source, out value, size, ref cursor); 120 | public static bool TryReadByte(this Span source, out byte value, ref int cursor) => SpanReading.TryReadByte(source, out value, ref cursor); 121 | 122 | public static bool TryWriteInt16BigEndian(this Span destination, short value, ref int cursor) => SpanWriting.TryWriteInt16BigEndian(destination, value, ref cursor); 123 | public static bool TryWriteInt16LittleEndian(this Span destination, short value, ref int cursor) => SpanWriting.TryWriteInt16LittleEndian(destination, value, ref cursor); 124 | public static bool TryWriteInt32BigEndian(this Span destination, int value, ref int cursor) => SpanWriting.TryWriteInt32BigEndian(destination, value, ref cursor); 125 | public static bool TryWriteInt32LittleEndian(this Span destination, int value, ref int cursor) => SpanWriting.TryWriteInt32LittleEndian(destination, value, ref cursor); 126 | public static bool TryWriteInt64BigEndian(this Span destination, long value, ref int cursor) => SpanWriting.TryWriteInt64BigEndian(destination, value, ref cursor); 127 | public static bool TryWriteInt64LittleEndian(this Span destination, long value, ref int cursor) => SpanWriting.TryWriteInt64LittleEndian(destination, value, ref cursor); 128 | 129 | [CLSCompliant(false)] 130 | public static bool TryWriteUInt16BigEndian(this Span destination, ushort value, ref int cursor) => SpanWriting.TryWriteUInt16BigEndian(destination, value, ref cursor); 131 | 132 | [CLSCompliant(false)] 133 | public static bool TryWriteUInt16LittleEndian(this Span destination, ushort value, ref int cursor) => SpanWriting.TryWriteUInt16LittleEndian(destination, value, ref cursor); 134 | 135 | [CLSCompliant(false)] 136 | public static bool TryWriteUInt32BigEndian(this Span destination, uint value, ref int cursor) => SpanWriting.TryWriteUInt32BigEndian(destination, value, ref cursor); 137 | 138 | [CLSCompliant(false)] 139 | public static bool TryWriteUInt32LittleEndian(this Span destination, uint value, ref int cursor) => SpanWriting.TryWriteUInt32LittleEndian(destination, value, ref cursor); 140 | 141 | [CLSCompliant(false)] 142 | public static bool TryWriteUInt64BigEndian(this Span destination, ulong value, ref int cursor) => SpanWriting.TryWriteUInt64BigEndian(destination, value, ref cursor); 143 | 144 | [CLSCompliant(false)] 145 | public static bool TryWriteUInt64LittleEndian(this Span destination, ulong value, ref int cursor) => SpanWriting.TryWriteUInt64LittleEndian(destination, value, ref cursor); 146 | 147 | public static bool TryWriteSpan(this Span destination, ReadOnlySpan value, ref int cursor) => SpanWriting.TryWriteSpan(destination, value, ref cursor); 148 | public static bool TryWriteAsciiString(this Span destination, string value, ref int cursor) => SpanWriting.TryWriteAsciiString(destination, value, ref cursor); 149 | public static bool TryWriteUtf8String(this Span destination, string value, ref int cursor) => SpanWriting.TryWriteUtf8String(destination, value, ref cursor); 150 | public static bool TryWriteByte(this Span destination, byte value, ref int cursor) => SpanWriting.TryWriteByte(destination, value, ref cursor); 151 | public static void WriteInt16BigEndian(this Span destination, short value, ref int cursor) => SpanWriting.WriteInt16BigEndian(destination, value, ref cursor); 152 | public static void WriteInt16LittleEndian(this Span destination, short value, ref int cursor) => SpanWriting.WriteInt16LittleEndian(destination, value, ref cursor); 153 | public static void WriteInt32BigEndian(this Span destination, int value, ref int cursor) => SpanWriting.WriteInt32BigEndian(destination, value, ref cursor); 154 | public static void WriteInt32LittleEndian(this Span destination, int value, ref int cursor) => SpanWriting.WriteInt32LittleEndian(destination, value, ref cursor); 155 | public static void WriteInt64BigEndian(this Span destination, long value, ref int cursor) => SpanWriting.WriteInt64BigEndian(destination, value, ref cursor); 156 | public static void WriteInt64LittleEndian(this Span destination, long value, ref int cursor) => SpanWriting.WriteInt64LittleEndian(destination, value, ref cursor); 157 | 158 | [CLSCompliant(false)] 159 | public static void WriteUInt16BigEndian(this Span destination, ushort value, ref int cursor) => SpanWriting.WriteUInt16BigEndian(destination, value, ref cursor); 160 | 161 | [CLSCompliant(false)] 162 | public static void WriteUInt16LittleEndian(this Span destination, ushort value, ref int cursor) => SpanWriting.WriteUInt16LittleEndian(destination, value, ref cursor); 163 | 164 | [CLSCompliant(false)] 165 | public static void WriteUInt32BigEndian(this Span destination, uint value, ref int cursor) => SpanWriting.WriteUInt32BigEndian(destination, value, ref cursor); 166 | 167 | [CLSCompliant(false)] 168 | public static void WriteUInt32LittleEndian(this Span destination, uint value, ref int cursor) => SpanWriting.WriteUInt32LittleEndian(destination, value, ref cursor); 169 | 170 | [CLSCompliant(false)] 171 | public static void WriteUInt64BigEndian(this Span destination, ulong value, ref int cursor) => SpanWriting.WriteUInt64BigEndian(destination, value, ref cursor); 172 | 173 | [CLSCompliant(false)] 174 | public static void WriteUInt64LittleEndian(this Span destination, ulong value, ref int cursor) => SpanWriting.WriteUInt64LittleEndian(destination, value, ref cursor); 175 | 176 | public static void WriteSpan(this Span destination, ReadOnlySpan value, ref int cursor) => SpanWriting.WriteSpan(destination, value, ref cursor); 177 | public static void WriteAsciiString(this Span destination, string value, ref int cursor) => SpanWriting.WriteAsciiString(destination, value, ref cursor); 178 | public static void WriteUtf8String(this Span destination, string value, ref int cursor) => SpanWriting.WriteUtf8String(destination, value, ref cursor); 179 | public static void WriteByte(this Span destination, byte value, ref int cursor) => SpanWriting.WriteByte(destination, value, ref cursor); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /tests/SpanDex.Tests/SpanExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using SpanDex.Extensions; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Buffers.Binary; 7 | 8 | namespace SpanDex.Tests { 9 | internal enum Endianness { 10 | Little, 11 | Big 12 | } 13 | 14 | [TestClass] 15 | public class SpanExtensionTests { 16 | private int cursor = 0; 17 | 18 | [TestMethod] 19 | public void Read_NegativeSize_ThrowsArgumentException() { 20 | Assert.ThrowsException(() => { 21 | ReadOnlySpan span = new byte[1]; 22 | span.ReadSpan(-1, ref cursor); 23 | }); 24 | } 25 | 26 | [TestMethod] 27 | public void ReadInt16BigEndian_ReadsCorrectly() { 28 | var span = GetSpan((short)-31_971, Endianness.Big); 29 | var value = span.ReadInt16BigEndian(ref cursor); 30 | Assert.AreEqual(-31_971, value); 31 | } 32 | 33 | [TestMethod] 34 | public void ReadInt16LittleEndian_ReadsCorrectly() { 35 | var span = GetSpan(-31_971, Endianness.Little); 36 | var value = span.ReadInt16LittleEndian(ref cursor); 37 | Assert.AreEqual(-31_971, value); 38 | } 39 | 40 | [TestMethod] 41 | public void ReadInt32BigEndian_ReadsCorrectly() { 42 | var span = GetSpan(-1_598_468_598, Endianness.Big); 43 | var value = span.ReadInt32BigEndian(ref cursor); 44 | Assert.AreEqual(-1_598_468_598, value); 45 | } 46 | 47 | [TestMethod] 48 | public void ReadInt32LittleEndian_ReadsCorrectly() { 49 | var span = GetSpan(-1_598_468_598, Endianness.Little); 50 | var value = span.ReadInt32LittleEndian(ref cursor); 51 | Assert.AreEqual(-1_598_468_598, value); 52 | } 53 | 54 | [TestMethod] 55 | public void ReadInt64BigEndian_ReadsCorrectly() { 56 | var span = GetSpan(-7_223_372_036_854_775_808, Endianness.Big); 57 | var value = span.ReadInt64BigEndian(ref cursor); 58 | Assert.AreEqual(-7_223_372_036_854_775_808, value); 59 | } 60 | 61 | [TestMethod] 62 | public void ReadInt64LittleEndian_ReadsCorrectly() { 63 | var span = GetSpan(-7_223_372_036_854_775_808, Endianness.Little); 64 | var value = span.ReadInt64LittleEndian(ref cursor); 65 | Assert.AreEqual(-7_223_372_036_854_775_808, value); 66 | } 67 | 68 | [TestMethod] 69 | public void ReadUInt16BigEndian_ReadsCorrectly() { 70 | var span = GetSpan((ushort)26_598, Endianness.Big); 71 | var value = span.ReadUInt16BigEndian(ref cursor); 72 | Assert.AreEqual(26_598, value); 73 | } 74 | 75 | [TestMethod] 76 | public void ReadUInt16LittleEndian_ReadsCorrectly() { 77 | var span = GetSpan(26_598, Endianness.Little); 78 | var value = span.ReadUInt16LittleEndian(ref cursor); 79 | Assert.AreEqual(26_598, value); 80 | } 81 | 82 | [TestMethod] 83 | public void ReadUInt32BigEndian_ReadsCorrectly() { 84 | var span = GetSpan(4_293_568_955, Endianness.Big); 85 | var value = span.ReadUInt32BigEndian(ref cursor); 86 | Assert.AreEqual(4_293_568_955, value); 87 | } 88 | 89 | [TestMethod] 90 | public void ReadUInt32LittleEndian_ReadsCorrectly() { 91 | var span = GetSpan(4_293_568_955, Endianness.Little); 92 | var value = span.ReadUInt32LittleEndian(ref cursor); 93 | Assert.AreEqual(4_293_568_955, value); 94 | } 95 | 96 | [TestMethod] 97 | public void ReadUInt64BigEndian_ReadsCorrectly() { 98 | var span = GetSpan(18_378_564_784_564_235_456, Endianness.Big); 99 | var value = span.ReadUInt64BigEndian(ref cursor); 100 | Assert.AreEqual(18_378_564_784_564_235_456, value); 101 | } 102 | 103 | [TestMethod] 104 | public void ReadUInt64LittleEndian_ReadsCorrectly() { 105 | var span = GetSpan(18_378_564_784_564_235_456, Endianness.Little); 106 | var value = span.ReadUInt64LittleEndian(ref cursor); 107 | Assert.AreEqual(18_378_564_784_564_235_456, value); 108 | } 109 | 110 | [TestMethod] 111 | public void ReadSpan_ReadsCorrectly() { 112 | ReadOnlySpan span = new byte[10]; 113 | var value = span.ReadSpan(10, ref cursor); 114 | 115 | Assert.AreEqual(span.Length, cursor); 116 | CollectionAssert.AreEqual(span.ToArray(), value.ToArray()); 117 | } 118 | 119 | [TestMethod] 120 | public void ReadAsciiString_ReadsCorrectly() { 121 | const string testString = "A little more than one hundred days into the fortieth year of her confinement, Dajeil Gelian was visited in her lonely tower overlooking the sea by an avatar of the great ship that was her home."; 122 | var bytes = Encoding.ASCII.GetBytes(testString); 123 | ReadOnlySpan span = bytes; 124 | var value = span.ReadAsciiString(testString.Length, ref cursor); 125 | 126 | Assert.AreEqual(testString, value); 127 | Assert.AreEqual(testString.Length, cursor); 128 | } 129 | 130 | [TestMethod] 131 | public void ReadUtf8String_ReadsCorrectly() { 132 | const string testString = "A little more than one hundred days into the fortieth year of her confinement, Dajeil Gelian was visited in her lonely tower overlooking the sea by an avatar of the great ship that was her home."; 133 | var bytes = Encoding.UTF8.GetBytes(testString); 134 | ReadOnlySpan span = bytes; 135 | var value = span.ReadUtf8String(testString.Length, ref cursor); 136 | 137 | Assert.AreEqual(testString, value); 138 | Assert.AreEqual(testString.Length, cursor); 139 | } 140 | 141 | [TestMethod] 142 | public void ReadByte_ReadsCorrectly() { 143 | ReadOnlySpan span = new byte[1] { 253 }; 144 | var value = span.ReadByte(ref cursor); 145 | 146 | Assert.AreEqual(253, value); 147 | } 148 | 149 | [TestMethod] 150 | 151 | public void TryReadInt16BigEndian_ReadsCorrectly() { 152 | var span = GetSpan((short)-31_971, Endianness.Big); 153 | if (span.TryReadInt16BigEndian(out short value, ref cursor)) { 154 | Assert.AreEqual(-31_971, value); 155 | } else { 156 | Assert.Fail(); 157 | } 158 | } 159 | 160 | [TestMethod] 161 | public void TryReadInt16LittleEndian_ReadsCorrectly() { 162 | var span = GetSpan((short)-31_971, Endianness.Little); 163 | if (span.TryReadInt16LittleEndian(out short value, ref cursor)) { 164 | Assert.AreEqual(-31_971, value); 165 | } else { 166 | Assert.Fail(); 167 | } 168 | } 169 | 170 | [TestMethod] 171 | public void TryReadInt32BigEndian_ReadsCorrectly() { 172 | var span = GetSpan(-1_598_468_598, Endianness.Big); 173 | if (span.TryReadInt32BigEndian(out int value, ref cursor)) { 174 | Assert.AreEqual(-1_598_468_598, value); 175 | } else { 176 | Assert.Fail(); 177 | } 178 | } 179 | 180 | [TestMethod] 181 | public void TryReadInt32LittleEndian_ReadsCorrectly() { 182 | var span = GetSpan(-1_598_468_598, Endianness.Little); 183 | if (span.TryReadInt32LittleEndian(out int value, ref cursor)) { 184 | Assert.AreEqual(-1_598_468_598, value); 185 | } else { 186 | Assert.Fail(); 187 | } 188 | } 189 | 190 | [TestMethod] 191 | public void TryReadInt64BigEndian_ReadsCorrectly() { 192 | var span = GetSpan(-7_223_372_036_854_775_808, Endianness.Big); 193 | if (span.TryReadInt64BigEndian(out long value, ref cursor)) { 194 | Assert.AreEqual(-7_223_372_036_854_775_808, value); 195 | } else { 196 | Assert.Fail(); 197 | } 198 | } 199 | 200 | [TestMethod] 201 | public void TryReadInt64LittleEndian_ReadsCorrectly() { 202 | var span = GetSpan(-7_223_372_036_854_775_808, Endianness.Little); 203 | if (span.TryReadInt64LittleEndian(out long value, ref cursor)) { 204 | Assert.AreEqual(-7_223_372_036_854_775_808, value); 205 | } else { 206 | Assert.Fail(); 207 | } 208 | } 209 | 210 | [TestMethod] 211 | public void TryReadUInt16BigEndian_ReadsCorrectly() { 212 | var span = GetSpan((ushort)26_598, Endianness.Big); 213 | if (span.TryReadUInt16BigEndian(out ushort value, ref cursor)) { 214 | Assert.AreEqual(26_598, value); 215 | } else { 216 | Assert.Fail(); 217 | } 218 | } 219 | 220 | [TestMethod] 221 | public void TryReadUInt16LittleEndian_ReadsCorrectly() { 222 | var span = GetSpan((ushort)26_598, Endianness.Little); 223 | if (span.TryReadUInt16LittleEndian(out ushort value, ref cursor)) { 224 | Assert.AreEqual(26_598, value); 225 | } else { 226 | Assert.Fail(); 227 | } 228 | } 229 | 230 | [TestMethod] 231 | public void TryReadUInt32BigEndian_ReadsCorrectly() { 232 | var span = GetSpan(4_293_568_955, Endianness.Big); 233 | if (span.TryReadUInt32BigEndian(out uint value, ref cursor)) { 234 | Assert.AreEqual(4_293_568_955, value); 235 | } else { 236 | Assert.Fail(); 237 | } 238 | } 239 | 240 | [TestMethod] 241 | public void TryReadUInt32LittleEndian_ReadsCorrectly() { 242 | var span = GetSpan(4_293_568_955, Endianness.Little); 243 | if (span.TryReadUInt32LittleEndian(out uint value, ref cursor)) { 244 | Assert.AreEqual(4_293_568_955, value); 245 | } else { 246 | Assert.Fail(); 247 | } 248 | } 249 | 250 | [TestMethod] 251 | public void TryReadUInt64BigEndian_ReadsCorrectly() { 252 | var span = GetSpan(18_378_564_784_564_235_456, Endianness.Big); 253 | if (span.TryReadUInt64BigEndian(out ulong value, ref cursor)) { 254 | Assert.AreEqual(18_378_564_784_564_235_456, value); 255 | } else { 256 | Assert.Fail(); 257 | } 258 | } 259 | 260 | [TestMethod] 261 | public void TryReadUInt64LittleEndian_ReadsCorrectly() { 262 | var span = GetSpan(18_378_564_784_564_235_456, Endianness.Little); 263 | if (span.TryReadUInt64LittleEndian(out ulong value, ref cursor)) { 264 | Assert.AreEqual(18_378_564_784_564_235_456, value); 265 | } else { 266 | Assert.Fail(); 267 | } 268 | } 269 | 270 | [TestMethod] 271 | public void TryReadUInt64LittleEndian_NegativeCursor_ThrowsArgumentException() { 272 | Assert.ThrowsException(() => { 273 | var span = GetSpan(18_378_564_784_564_235_456, Endianness.Little); 274 | cursor = -1; 275 | span.TryReadUInt64LittleEndian(out ulong value, ref cursor); 276 | }); 277 | } 278 | 279 | [TestMethod] 280 | public void TryReadSpan_ReadsCorrectly() { 281 | ReadOnlySpan span = new byte[10]; 282 | if(span.TryReadSpan(out var value, 10, ref cursor)) { 283 | Assert.AreEqual(span.Length, cursor); 284 | CollectionAssert.AreEqual(span.ToArray(), value.ToArray()); 285 | } else { 286 | Assert.Fail(); 287 | } 288 | } 289 | 290 | [TestMethod] 291 | public void TryReadSpan_IncorrectLength_Fails() { 292 | ReadOnlySpan span = new byte[10]; 293 | if (span.TryReadSpan(out var _, 20, ref cursor)) { 294 | Assert.Fail(); 295 | } else { 296 | Assert.AreEqual(0, cursor); 297 | } 298 | } 299 | 300 | [TestMethod] 301 | public void TryReadAsciiString_ReadsCorrectly() { 302 | const string testString = "A little more than one hundred days into the fortieth year of her confinement, Dajeil Gelian was visited in her lonely tower overlooking the sea by an avatar of the great ship that was her home."; 303 | var bytes = Encoding.ASCII.GetBytes(testString); 304 | ReadOnlySpan span = bytes; 305 | 306 | if(span.TryReadAsciiString(out var value, testString.Length, ref cursor)) { 307 | Assert.AreEqual(testString, value); 308 | Assert.AreEqual(testString.Length, cursor); 309 | } else { 310 | Assert.Fail(); 311 | } 312 | } 313 | 314 | [TestMethod] 315 | public void TryReadUtf8String_ReadsCorrectly() { 316 | const string testString = "A little more than one hundred days into the fortieth year of her confinement, Dajeil Gelian was visited in her lonely tower overlooking the sea by an avatar of the great ship that was her home."; 317 | var bytes = Encoding.UTF8.GetBytes(testString); 318 | ReadOnlySpan span = bytes; 319 | if(span.TryReadUtf8String(out var value, testString.Length, ref cursor)) { 320 | Assert.AreEqual(testString, value); 321 | Assert.AreEqual(testString.Length, cursor); 322 | } else { 323 | Assert.Fail(); 324 | } 325 | } 326 | 327 | [TestMethod] 328 | public void TryReadByte_ReadsCorrectly() { 329 | ReadOnlySpan span = new byte[1] { 253 }; 330 | if(span.TryReadByte(out var value, ref cursor)) { 331 | Assert.AreEqual(253, value); 332 | Assert.AreEqual(1, cursor); 333 | } else { 334 | Assert.Fail(); 335 | } 336 | } 337 | 338 | [TestMethod] 339 | public void TryWriteInt16BigEndian_WritesCorrectly() { 340 | var bitConverterSpan = GetSpan((short)-31_971, Endianness.Big); 341 | Span span = new byte[sizeof(short)]; 342 | var succeeded = span.TryWriteInt16BigEndian(-31_971, ref cursor); 343 | 344 | Assert.IsTrue(succeeded); 345 | Assert.AreEqual(sizeof(short), cursor); 346 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 347 | } 348 | 349 | [TestMethod] 350 | public void TryWriteInt16LittleEndian_WritesCorrectly() { 351 | var bitConverterSpan = GetSpan((short)-31_971, Endianness.Little); 352 | Span span = new byte[sizeof(short)]; 353 | var succeeded = span.TryWriteInt16LittleEndian(-31_971, ref cursor); 354 | 355 | Assert.IsTrue(succeeded); 356 | Assert.AreEqual(sizeof(short), cursor); 357 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 358 | } 359 | 360 | [TestMethod] 361 | public void TryWriteInt32BigEndian_WritesCorrectly() { 362 | var bitConverterSpan = GetSpan(-1_598_468_598, Endianness.Big); 363 | Span span = new byte[sizeof(int)]; 364 | var succeeded = span.TryWriteInt32BigEndian(-1_598_468_598, ref cursor); 365 | 366 | Assert.IsTrue(succeeded); 367 | Assert.AreEqual(sizeof(int), cursor); 368 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 369 | } 370 | 371 | [TestMethod] 372 | public void TryWriteInt32LittleEndian_WritesCorrectly() { 373 | var bitConverterSpan = GetSpan(-1_598_468_598, Endianness.Little); 374 | Span span = new byte[sizeof(int)]; 375 | var succeeded = span.TryWriteInt32LittleEndian(-1_598_468_598, ref cursor); 376 | 377 | Assert.IsTrue(succeeded); 378 | Assert.AreEqual(sizeof(int), cursor); 379 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 380 | } 381 | 382 | [TestMethod] 383 | public void TryWriteInt64BigEndian_WritesCorrectly() { 384 | var bitConverterSpan = GetSpan(-7_223_372_036_854_775_808, Endianness.Big); 385 | Span span = new byte[sizeof(long)]; 386 | var succeeded = span.TryWriteInt64BigEndian(-7_223_372_036_854_775_808, ref cursor); 387 | 388 | Assert.IsTrue(succeeded); 389 | Assert.AreEqual(sizeof(long), cursor); 390 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 391 | } 392 | 393 | [TestMethod] 394 | public void TryWriteInt64LittleEndian_WritesCorrectly() { 395 | var bitConverterSpan = GetSpan(-7_223_372_036_854_775_808, Endianness.Little); 396 | Span span = new byte[sizeof(long)]; 397 | var succeeded = span.TryWriteInt64LittleEndian(-7_223_372_036_854_775_808, ref cursor); 398 | 399 | Assert.IsTrue(succeeded); 400 | Assert.AreEqual(sizeof(long), cursor); 401 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 402 | } 403 | 404 | [TestMethod] 405 | public void TryWriteUInt16BigEndian_WritesCorrectly() { 406 | var bitConverterSpan = GetSpan((ushort)26_598, Endianness.Big); 407 | Span span = new byte[sizeof(ushort)]; 408 | var succeeded = span.TryWriteUInt16BigEndian(26_598, ref cursor); 409 | 410 | Assert.IsTrue(succeeded); 411 | Assert.AreEqual(sizeof(ushort), cursor); 412 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 413 | } 414 | 415 | [TestMethod] 416 | public void TryWriteUInt16LittleEndian_WritesCorrectly() { 417 | var bitConverterSpan = GetSpan((ushort)26_598, Endianness.Little); 418 | Span span = new byte[sizeof(ushort)]; 419 | var succeeded = span.TryWriteUInt16LittleEndian(26_598, ref cursor); 420 | 421 | Assert.IsTrue(succeeded); 422 | Assert.AreEqual(sizeof(ushort), cursor); 423 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 424 | } 425 | 426 | [TestMethod] 427 | public void TryWriteUInt32BigEndian_WritesCorrectly() { 428 | var bitConverterSpan = GetSpan(4_293_568_955, Endianness.Big); 429 | Span span = new byte[sizeof(uint)]; 430 | var succeeded = span.TryWriteUInt32BigEndian(4_293_568_955, ref cursor); 431 | 432 | Assert.IsTrue(succeeded); 433 | Assert.AreEqual(sizeof(uint), cursor); 434 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 435 | } 436 | 437 | [TestMethod] 438 | public void TryWriteUInt32LittleEndian_WritesCorrectly() { 439 | var bitConverterSpan = GetSpan(4_293_568_955, Endianness.Little); 440 | Span span = new byte[sizeof(uint)]; 441 | var succeeded = span.TryWriteUInt32LittleEndian(4_293_568_955, ref cursor); 442 | 443 | Assert.IsTrue(succeeded); 444 | Assert.AreEqual(sizeof(uint), cursor); 445 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 446 | } 447 | 448 | [TestMethod] 449 | public void TryWriteUInt64BigEndian_WritesCorrectly() { 450 | var bitConverterSpan = GetSpan(18_378_564_784_564_235_456, Endianness.Big); 451 | Span span = new byte[sizeof(ulong)]; 452 | var succeeded = span.TryWriteUInt64BigEndian(18_378_564_784_564_235_456, ref cursor); 453 | 454 | Assert.IsTrue(succeeded); 455 | Assert.AreEqual(sizeof(ulong), cursor); 456 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 457 | } 458 | 459 | [TestMethod] 460 | public void TryWriteUInt64LittleEndian_WritesCorrectly() { 461 | var bitConverterSpan = GetSpan(18_378_564_784_564_235_456, Endianness.Little); 462 | Span span = new byte[sizeof(ulong)]; 463 | var succeeded = span.TryWriteUInt64LittleEndian(18_378_564_784_564_235_456, ref cursor); 464 | 465 | Assert.IsTrue(succeeded); 466 | Assert.AreEqual(sizeof(ulong), cursor); 467 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 468 | } 469 | 470 | [TestMethod] 471 | public void TryWriteSpan_WritesCorrectly() { 472 | Span span = new byte[10]; 473 | var toWrite = new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 474 | var succeeded = span.TryWriteSpan(toWrite, ref cursor); 475 | 476 | Assert.IsTrue(succeeded); 477 | Assert.AreEqual(toWrite.Length, cursor); 478 | CollectionAssert.AreEqual(toWrite.ToArray(), span.ToArray()); 479 | } 480 | 481 | [TestMethod] 482 | public void TryWriteSpan_IncorrectSize_Fails() { 483 | Span span = new byte[1]; 484 | var toWrite = new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 485 | var succeeded = span.TryWriteSpan(toWrite, ref cursor); 486 | 487 | Assert.IsFalse(succeeded); 488 | } 489 | 490 | [TestMethod] 491 | public void TryWriteAsciiString_WritesCorrectly() { 492 | Span span = new byte[11]; 493 | const string toWrite = "test string"; 494 | var succeeded = span.TryWriteAsciiString(toWrite, ref cursor); 495 | 496 | var encodingArray = Encoding.ASCII.GetBytes(toWrite); 497 | 498 | Assert.IsTrue(succeeded); 499 | Assert.AreEqual(toWrite.Length, cursor); 500 | CollectionAssert.AreEqual(encodingArray, span.ToArray()); 501 | } 502 | 503 | [TestMethod] 504 | public void TryWriteUtf8String_WritesCorrectly() { 505 | Span span = new byte[11]; 506 | const string toWrite = "test string"; 507 | var succeeded = span.TryWriteUtf8String(toWrite, ref cursor); 508 | var encodingArray = Encoding.UTF8.GetBytes(toWrite); 509 | 510 | Assert.IsTrue(succeeded); 511 | Assert.AreEqual(toWrite.Length, cursor); 512 | CollectionAssert.AreEqual(encodingArray, span.ToArray()); 513 | } 514 | 515 | [TestMethod] 516 | public void TryWriteByte_WritesCorrectly() { 517 | Span span = new byte[1]; 518 | const byte toWrite = 234; 519 | var succeeded = span.TryWriteByte(toWrite, ref cursor); 520 | 521 | Assert.IsTrue(succeeded); 522 | Assert.AreEqual(1, cursor); 523 | Assert.AreEqual(toWrite, span[0]); 524 | } 525 | 526 | [TestMethod] 527 | public void WriteInt16BigEndian_WritesCorrectly() { 528 | var bitConverterSpan = GetSpan((short)-31_971, Endianness.Big); 529 | Span span = new byte[sizeof(short)]; 530 | span.WriteInt16BigEndian(-31_971, ref cursor); 531 | 532 | Assert.AreEqual(sizeof(short), cursor); 533 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 534 | } 535 | 536 | [TestMethod] 537 | public void WriteInt16LittleEndian_WritesCorrectly() { 538 | var bitConverterSpan = GetSpan((short)-31_971, Endianness.Little); 539 | Span span = new byte[sizeof(short)]; 540 | span.WriteInt16LittleEndian(-31_971, ref cursor); 541 | 542 | Assert.AreEqual(sizeof(short), cursor); 543 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 544 | } 545 | 546 | [TestMethod] 547 | public void WriteInt32BigEndian_WritesCorrectly() { 548 | var bitConverterSpan = GetSpan(-1_598_468_598, Endianness.Big); 549 | Span span = new byte[sizeof(int)]; 550 | span.WriteInt32BigEndian(-1_598_468_598, ref cursor); 551 | 552 | Assert.AreEqual(sizeof(int), cursor); 553 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 554 | } 555 | 556 | [TestMethod] 557 | public void WriteInt32LittleEndian_WritesCorrectly() { 558 | var bitConverterSpan = GetSpan(-1_598_468_598, Endianness.Little); 559 | Span span = new byte[sizeof(int)]; 560 | span.WriteInt32LittleEndian(-1_598_468_598, ref cursor); 561 | 562 | Assert.AreEqual(sizeof(int), cursor); 563 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 564 | } 565 | 566 | [TestMethod] 567 | public void WriteInt64BigEndian_WritesCorrectly() { 568 | var bitConverterSpan = GetSpan(-7_223_372_036_854_775_808, Endianness.Big); 569 | Span span = new byte[sizeof(long)]; 570 | span.WriteInt64BigEndian(-7_223_372_036_854_775_808, ref cursor); 571 | 572 | Assert.AreEqual(sizeof(long), cursor); 573 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 574 | } 575 | 576 | [TestMethod] 577 | public void WriteInt64LittleEndian_WritesCorrectly() { 578 | var bitConverterSpan = GetSpan(-7_223_372_036_854_775_808, Endianness.Little); 579 | Span span = new byte[sizeof(long)]; 580 | span.WriteInt64LittleEndian(-7_223_372_036_854_775_808, ref cursor); 581 | 582 | Assert.AreEqual(sizeof(long), cursor); 583 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 584 | } 585 | 586 | [TestMethod] 587 | public void WriteUInt16BigEndian_WritesCorrectly() { 588 | var bitConverterSpan = GetSpan((ushort)26_598, Endianness.Big); 589 | Span span = new byte[sizeof(ushort)]; 590 | span.WriteUInt16BigEndian(26_598, ref cursor); 591 | 592 | Assert.AreEqual(sizeof(ushort), cursor); 593 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 594 | } 595 | 596 | [TestMethod] 597 | public void WriteUInt16LittleEndian_WritesCorrectly() { 598 | var bitConverterSpan = GetSpan((ushort)26_598, Endianness.Little); 599 | Span span = new byte[sizeof(ushort)]; 600 | span.WriteUInt16LittleEndian(26_598, ref cursor); 601 | 602 | Assert.AreEqual(sizeof(ushort), cursor); 603 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 604 | } 605 | 606 | [TestMethod] 607 | public void WriteUInt32BigEndian_WritesCorrectly() { 608 | var bitConverterSpan = GetSpan(4_293_568_955, Endianness.Big); 609 | Span span = new byte[sizeof(uint)]; 610 | span.WriteUInt32BigEndian(4_293_568_955, ref cursor); 611 | 612 | Assert.AreEqual(sizeof(uint), cursor); 613 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 614 | } 615 | 616 | [TestMethod] 617 | public void WriteUInt32LittleEndian_WritesCorrectly() { 618 | var bitConverterSpan = GetSpan(4_293_568_955, Endianness.Little); 619 | Span span = new byte[sizeof(uint)]; 620 | span.WriteUInt32LittleEndian(4_293_568_955, ref cursor); 621 | 622 | Assert.AreEqual(sizeof(uint), cursor); 623 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 624 | } 625 | 626 | [TestMethod] 627 | public void WriteUInt64BigEndian_WritesCorrectly() { 628 | var bitConverterSpan = GetSpan(18_378_564_784_564_235_456, Endianness.Big); 629 | Span span = new byte[sizeof(ulong)]; 630 | span.WriteUInt64BigEndian(18_378_564_784_564_235_456, ref cursor); 631 | 632 | Assert.AreEqual(sizeof(ulong), cursor); 633 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 634 | } 635 | 636 | [TestMethod] 637 | public void WriteUInt64LittleEndian_WritesCorrectly() { 638 | var bitConverterSpan = GetSpan(18_378_564_784_564_235_456, Endianness.Little); 639 | Span span = new byte[sizeof(ulong)]; 640 | span.WriteUInt64LittleEndian(18_378_564_784_564_235_456, ref cursor); 641 | 642 | Assert.AreEqual(sizeof(ulong), cursor); 643 | CollectionAssert.AreEqual(bitConverterSpan.ToArray(), span.ToArray()); 644 | } 645 | 646 | [TestMethod] 647 | public void WriteSpan_WritesCorrectly() { 648 | Span span = new byte[10]; 649 | var toWrite = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 650 | 651 | span.WriteSpan(toWrite, ref cursor); 652 | 653 | Assert.AreEqual(toWrite.Length, cursor); 654 | CollectionAssert.AreEqual(toWrite, span.ToArray()); 655 | } 656 | 657 | [TestMethod] 658 | public void WriteSpan_Null_ThrowsArgumentNullException() { 659 | Assert.ThrowsException(() => { 660 | Span span = new byte[10]; 661 | byte[] toWrite = null; 662 | 663 | span.WriteSpan(toWrite, ref cursor); 664 | }); 665 | } 666 | 667 | [TestMethod] 668 | public void WriteAsciiString_WritesCorrectly() { 669 | Span span = new byte[11]; 670 | const string toWrite = "test string"; 671 | var encodingArray = Encoding.ASCII.GetBytes(toWrite); 672 | 673 | span.WriteAsciiString(toWrite, ref cursor); 674 | 675 | Assert.AreEqual(toWrite.Length, cursor); 676 | CollectionAssert.AreEqual(encodingArray, span.ToArray()); 677 | } 678 | 679 | [TestMethod] 680 | public void WriteAsciiString_Null_ThrowsArgumentNullException() { 681 | Assert.ThrowsException(() => { 682 | Span span = new byte[11]; 683 | const string toWrite = null; 684 | 685 | span.WriteAsciiString(toWrite, ref cursor); 686 | }); 687 | } 688 | 689 | [TestMethod] 690 | public void WriteAsciiString_Empty_DoesntThrow() { 691 | Span span = new byte[11]; 692 | var toWrite = string.Empty; 693 | 694 | span.WriteAsciiString(toWrite, ref cursor); 695 | 696 | Assert.AreEqual(toWrite.Length, cursor); 697 | } 698 | 699 | [TestMethod] 700 | public void WriteUT8String_WritesCorrectly() { 701 | Span span = new byte[11]; 702 | const string toWrite = "test string"; 703 | var encodingArray = Encoding.UTF8.GetBytes(toWrite); 704 | 705 | span.WriteUtf8String(toWrite, ref cursor); 706 | 707 | Assert.AreEqual(toWrite.Length, cursor); 708 | CollectionAssert.AreEqual(encodingArray, span.ToArray()); 709 | } 710 | 711 | [TestMethod] 712 | public void WriteByte_WritesCorrectly() { 713 | Span span = new byte[1]; 714 | const byte toWrite = 234; 715 | span.WriteByte(toWrite, ref cursor); 716 | 717 | Assert.AreEqual(1, cursor); 718 | Assert.AreEqual(toWrite, span[0]); 719 | } 720 | 721 | private ReadOnlySpan GetSpan(short value, Endianness endian) => CreateSpan(BitConverter.GetBytes(value), endian); 722 | private ReadOnlySpan GetSpan(ushort value, Endianness endian) => CreateSpan(BitConverter.GetBytes(value), endian); 723 | private ReadOnlySpan GetSpan(int value, Endianness endian) => CreateSpan(BitConverter.GetBytes(value), endian); 724 | private ReadOnlySpan GetSpan(uint value, Endianness endian) => CreateSpan(BitConverter.GetBytes(value), endian); 725 | private ReadOnlySpan GetSpan(long value, Endianness endian) => CreateSpan(BitConverter.GetBytes(value), endian); 726 | private ReadOnlySpan GetSpan(ulong value, Endianness endian) => CreateSpan(BitConverter.GetBytes(value), endian); 727 | 728 | private ReadOnlySpan CreateSpan(byte[] bytes, Endianness endian) { 729 | if (BitConverter.IsLittleEndian) { 730 | if (endian == Endianness.Big) { 731 | bytes = bytes.Reverse().ToArray(); 732 | } 733 | } else { 734 | if (endian == Endianness.Little) { 735 | bytes = bytes.Reverse().ToArray(); 736 | } 737 | } 738 | return new ReadOnlySpan(bytes); 739 | } 740 | } 741 | } 742 | --------------------------------------------------------------------------------