├── .gitignore ├── NativeBuffering.sln ├── demo ├── App │ ├── AntiVirusFriendlyConfig.cs │ ├── App.csproj │ ├── Class1.cs │ ├── Entity.cs │ ├── Foobar.cs │ ├── MyBenchmark.cs │ ├── Pointer.cs │ └── Program.cs ├── App2 │ ├── App2.csproj │ ├── Entity.cs │ └── Program.cs └── App3 │ ├── AntiVirusFriendlyConfig.cs │ ├── App3.csproj │ ├── Entity.cs │ ├── MyBenchmark.cs │ ├── Person.cs │ ├── Program.cs │ └── Record.cs ├── pack.bat ├── src ├── NativeBuffering.Generator │ ├── BufferedMessageClassGenerator.cs │ ├── BufferedMessageMemberKind.cs │ ├── BufferedMessageSourceClassGenerator.cs │ ├── BufferedObjectMetadata.cs │ ├── BufferedObjectMetadataException.cs │ ├── BufferedObjectMetadataResolver.cs │ ├── BufferedObjectProperty.cs │ ├── CodeGenerationContext.cs │ ├── Generator.cs │ ├── NamedTypeSymbolExtensions.cs │ └── NativeBuffering.Generator.csproj └── NativeBuffering │ ├── BufferPool │ ├── Bucket.cs │ ├── BufferOwner.cs │ ├── BufferPool.cs │ └── InternalBufferPool.cs │ ├── BufferedBinary.cs │ ├── BufferedMessage.cs │ ├── BufferedMessageSourceAttribute.cs │ ├── BufferedObjectWriteContext.cs │ ├── BufferedObjectWriteContextScope.cs │ ├── BufferedString.cs │ ├── Collections │ ├── ReadOnlyNonNullableBufferedObjectList.cs │ ├── ReadOnlyNonNullableUnmanagedList.cs │ ├── ReadOnlyNullableBufferedObjectList.cs │ └── ReadOnlyNullableUnmanagedList.cs │ ├── Dictionaries │ ├── BufferedKeyValuePair.cs │ ├── BufferedUnamanagedUnamanagedDictionartyEntry.cs │ ├── DictionaryUtilities.cs │ ├── ReadOnlyStringNonNullableBufferedObjectDictionary.cs │ ├── ReadOnlyStringNonNullableUnmanagedDictionary.cs │ ├── ReadOnlyStringNullableBufferedObjectDictionary.cs │ ├── ReadOnlyStringNullableUnmanagedDictionary.cs │ ├── ReadOnlyUnmanagedNonNullableBufferedObjectDictionary.cs │ ├── ReadOnlyUnmanagedNonNullableUnmanagedDictionary.cs │ ├── ReadOnlyUnmanagedNullableBufferedObjectDictionary.cs │ └── ReadOnlyUnmanagedNullableUnmanagedDictionary.cs │ ├── IBufferedObjectSource.cs │ ├── IReadOnlyBufferedObject.cs │ ├── NativeBuffer.cs │ ├── NativeBuffering.csproj │ ├── PooledList.cs │ ├── extensions │ ├── AlignmentCalculator.cs │ ├── BufferedMessageWriteContextCollectionExtensions.cs │ ├── BufferedMessageWriteContextDictionaryExtensions.cs │ ├── BufferedMessageWriteContextScopeCollectionExtensions.cs │ ├── BufferedMessageWriteContextScopeDictionaryExtensions.cs │ ├── BufferedObjectSourceExtensions.cs │ ├── BufferedObjectWriteContextScopeExtensions.cs │ └── NativeBufferReadFieldExtensions.cs │ └── internals │ ├── Utilities.cs │ ├── Utilities1.cs │ └── Utilities2.cs └── test └── NativeBuffering.Test ├── BinaryCollectionFixture.cs ├── BufferPoolFixture.cs ├── NativeBufferFixture.cs ├── NativeBuffering.Test.csproj ├── NonNullableBufferedObjectCollectionFixture.cs ├── NonNullableScalarBufferedMessageFixture.cs ├── NonNullableUnmanagedCollectionFixture.cs ├── NullableBufferedObjectCollectionFixture.cs ├── NullableScalarBufferedMessageFixture.cs ├── NullableUnmanagedCollectionFixture.cs ├── StringBufferedNonNullableObjectDictionaryFixture.cs ├── StringBufferedNullableObjectDictionaryFixture_Class.cs ├── StringBufferedNullableObjectDictionaryFixture_Structure.cs ├── StringCollectionFixture.cs ├── StringNonNullableUnmanagedDictionaryFixture.cs ├── StringNullableUnmanagedDictionaryFixture.cs ├── StringStringDictionaryFixture.cs ├── UnmanagedBinaryDictionaryFixture.cs ├── UnmanagedNonNullableBufferedObjectDictionaryFixture.cs ├── UnmanagedNonNullableUnmanagedDictionaryFixture.cs ├── UnmanagedNullableUnmanagedDictionaryFixture.cs ├── UnmanagedStringDictionaryFixture.cs └── Usings.cs /NativeBuffering.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32414.318 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{719A819A-EA98-4245-AF15-6C67418E7BE2}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{664C69F5-F80F-4B16-ADFD-C751BD185CBE}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{01A496EA-47C0-40FD-BB37-E085EAAF4BB5}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeBuffering", "src\NativeBuffering\NativeBuffering.csproj", "{CD23D393-8865-4C4D-BD32-299DBE659627}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeBuffering.Test", "test\NativeBuffering.Test\NativeBuffering.Test.csproj", "{1CCA4ED7-EE2E-42F5-9BDF-7C6F656072F0}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeBuffering.Generator", "src\NativeBuffering.Generator\NativeBuffering.Generator.csproj", "{0FB015B8-FE0E-4ED3-B4DA-2D0ABDE66307}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "demo\App\App.csproj", "{02F4F312-62B1-4621-9AB9-E07FFDFCEFB2}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App2", "demo\App2\App2.csproj", "{25B33461-2E59-4082-B1DF-A98342A22A77}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App3", "demo\App3\App3.csproj", "{1310BD63-416B-4176-9B2C-EB8D6663C835}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {CD23D393-8865-4C4D-BD32-299DBE659627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {CD23D393-8865-4C4D-BD32-299DBE659627}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {CD23D393-8865-4C4D-BD32-299DBE659627}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {CD23D393-8865-4C4D-BD32-299DBE659627}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {1CCA4ED7-EE2E-42F5-9BDF-7C6F656072F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {1CCA4ED7-EE2E-42F5-9BDF-7C6F656072F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {1CCA4ED7-EE2E-42F5-9BDF-7C6F656072F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {1CCA4ED7-EE2E-42F5-9BDF-7C6F656072F0}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {0FB015B8-FE0E-4ED3-B4DA-2D0ABDE66307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {0FB015B8-FE0E-4ED3-B4DA-2D0ABDE66307}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {0FB015B8-FE0E-4ED3-B4DA-2D0ABDE66307}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {0FB015B8-FE0E-4ED3-B4DA-2D0ABDE66307}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {02F4F312-62B1-4621-9AB9-E07FFDFCEFB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {02F4F312-62B1-4621-9AB9-E07FFDFCEFB2}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {02F4F312-62B1-4621-9AB9-E07FFDFCEFB2}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {02F4F312-62B1-4621-9AB9-E07FFDFCEFB2}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {25B33461-2E59-4082-B1DF-A98342A22A77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {25B33461-2E59-4082-B1DF-A98342A22A77}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {25B33461-2E59-4082-B1DF-A98342A22A77}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {25B33461-2E59-4082-B1DF-A98342A22A77}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {1310BD63-416B-4176-9B2C-EB8D6663C835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {1310BD63-416B-4176-9B2C-EB8D6663C835}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {1310BD63-416B-4176-9B2C-EB8D6663C835}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {1310BD63-416B-4176-9B2C-EB8D6663C835}.Release|Any CPU.Build.0 = Release|Any CPU 54 | EndGlobalSection 55 | GlobalSection(SolutionProperties) = preSolution 56 | HideSolutionNode = FALSE 57 | EndGlobalSection 58 | GlobalSection(NestedProjects) = preSolution 59 | {CD23D393-8865-4C4D-BD32-299DBE659627} = {719A819A-EA98-4245-AF15-6C67418E7BE2} 60 | {1CCA4ED7-EE2E-42F5-9BDF-7C6F656072F0} = {01A496EA-47C0-40FD-BB37-E085EAAF4BB5} 61 | {0FB015B8-FE0E-4ED3-B4DA-2D0ABDE66307} = {719A819A-EA98-4245-AF15-6C67418E7BE2} 62 | {02F4F312-62B1-4621-9AB9-E07FFDFCEFB2} = {664C69F5-F80F-4B16-ADFD-C751BD185CBE} 63 | {25B33461-2E59-4082-B1DF-A98342A22A77} = {664C69F5-F80F-4B16-ADFD-C751BD185CBE} 64 | {1310BD63-416B-4176-9B2C-EB8D6663C835} = {664C69F5-F80F-4B16-ADFD-C751BD185CBE} 65 | EndGlobalSection 66 | GlobalSection(ExtensibilityGlobals) = postSolution 67 | SolutionGuid = {EFD31D86-8218-46EB-9D3C-C50BEC41FD98} 68 | EndGlobalSection 69 | EndGlobal 70 | -------------------------------------------------------------------------------- /demo/App/AntiVirusFriendlyConfig.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Configs; 2 | using BenchmarkDotNet.Jobs; 3 | using BenchmarkDotNet.Toolchains.InProcess.NoEmit; 4 | 5 | namespace App 6 | { 7 | public class AntiVirusFriendlyConfig : ManualConfig 8 | { 9 | public AntiVirusFriendlyConfig() 10 | { 11 | AddJob(Job.MediumRun 12 | .WithToolchain(InProcessNoEmitToolchain.Instance)); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /demo/App/App.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | true 9 | True 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/App/Class1.cs: -------------------------------------------------------------------------------- 1 | //#nullable enable 2 | //using System; 3 | //using System.Runtime.CompilerServices; 4 | //using System.Collections.Generic; 5 | //using NativeBuffering; 6 | //using NativeBuffering.Dictionaries; 7 | //using NativeBuffering.Collections; 8 | //public unsafe readonly struct EntityBufferedMessage : IReadOnlyBufferedObject 9 | //{ 10 | // public NativeBuffer Buffer { get; } 11 | // public EntityBufferedMessage(NativeBuffer buffer) => Buffer = buffer; 12 | // public static EntityBufferedMessage Parse(NativeBuffer buffer) => new EntityBufferedMessage(buffer); 13 | // public System.Int64 Foo => Buffer.ReadUnmanagedField(0); 14 | // public ref readonly UnmanangedStruct Bar => ref Buffer.ReadUnmanagedFieldAsRef(1); 15 | // public BufferedBinary Baz => Buffer.ReadNonNullableBufferedObjectField(2); 16 | // public BufferedString Qux => Buffer.ReadNonNullableBufferedObjectField(3); 17 | //} 18 | //#nullable disable 19 | -------------------------------------------------------------------------------- /demo/App/Entity.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | 3 | namespace App 4 | { 5 | [BufferedMessageSource] 6 | public partial class Entity 7 | { 8 | public int Pritimive { get; set; } 9 | public Pointer Unmanaged { get; set; } 10 | public string String { get; set; } = default!; 11 | public byte[] Binary { get; set; } = default!; 12 | public Foobar BufferedMessage { get; set; } = default!; 13 | 14 | public Pointer[] UnmanagedCollection { get; set; } = default!; 15 | public string[] StringCollection { get; set; } = default!; 16 | public byte[][] BinaryCollection { get; set; } = default!; 17 | public Foobar[] BufferedMessageCollection { get; set; } = default!; 18 | 19 | //public Dictionary UnmanagedUnmanagedDictionary { get; set; } = default!; 20 | //public Dictionary UnmanagedStringDictionary { get; set; } = default!; 21 | //public Dictionary UnmanagedBinaryDictionary { get; set; } = default!; 22 | //public Dictionary UnmanagedBufferedMessageDictionary { get; set; } = default!; 23 | 24 | //public Dictionary StringUnmanagedDictionary { get; set; } = default!; 25 | //public Dictionary StringStringDictionary { get; set; } = default!; 26 | //public Dictionary StringBinaryDictionary { get; set; } = default!; 27 | //public Dictionary StringBufferedMessageDictionary { get; set; } = default!; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demo/App/Foobar.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace App 9 | { 10 | [BufferedMessageSource] 11 | public partial class Foobar 12 | { 13 | public Foobar(int foo, string bar) 14 | { 15 | Foo = foo; 16 | Bar = bar; 17 | } 18 | 19 | public int Foo { get; } 20 | public string Bar { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/App/Pointer.cs: -------------------------------------------------------------------------------- 1 | namespace App 2 | { 3 | public readonly record struct Pointer(double X, double Y); 4 | } 5 | -------------------------------------------------------------------------------- /demo/App/Program.cs: -------------------------------------------------------------------------------- 1 | using App; 2 | using BenchmarkDotNet.Running; 3 | 4 | //var b = new MyBenchmark(); 5 | //b.Setup(); 6 | //b.UseEntityBufferedMessage(); 7 | var summary = BenchmarkRunner.Run(typeof(MyBenchmark)); 8 | 9 | //using App; 10 | //using NativeBuffering; 11 | //using System.Diagnostics; 12 | //using System.Text; 13 | //using System.Text.Json; 14 | //using System.Buffers; 15 | 16 | //var entity = new Entity 17 | //{ 18 | // Pritimive = 123, 19 | // Unmanaged = new Pointer(1, 2), 20 | // String = "Hello World!", 21 | // Binary = new byte[] { 1, 2, 3 }, 22 | // BufferedMessage = new Foobar(123, "Hello World!"), 23 | 24 | // UnmanagedCollection = new Pointer[] { new Pointer(1, 2), new Pointer(3, 4) }, 25 | // StringCollection = new string[] { "Hello", "World" }, 26 | // BinaryCollection = new byte[][] { new byte[] { 1, 2, 3 }, new byte[] { 4, 5, 6 } }, 27 | // BufferedMessageCollection = new Foobar[] { new Foobar(1, "a"), new Foobar(2, "b") }, 28 | 29 | // UnmanagedUnmanagedDictionary = new Dictionary { { 1, new Pointer(1, 2) }, { 2, new Pointer(3, 4) } }, 30 | // UnmanagedStringDictionary = new Dictionary { { 1, "Hello" }, { 2, "World" } }, 31 | // UnmanagedBinaryDictionary = new Dictionary { { 1, new byte[] { 1, 2, 3 } }, { 2, new byte[] { 4, 5, 6 } } }, 32 | // UnmanagedBufferedMessageDictionary = new Dictionary { { 1, new Foobar(1, "a") }, { 2, new Foobar(2, "b") } }, 33 | 34 | // StringUnmanagedDictionary = new Dictionary { { "a", new Pointer(1, 2) }, { "b", new Pointer(3, 4) } }, 35 | // StringStringDictionary = new Dictionary { { "a", "Hello" }, { "b", "World" } }, 36 | // StringBinaryDictionary = new Dictionary { { "a", new byte[] { 1, 2, 3 } }, { "b", new byte[] { 4, 5, 6 } } }, 37 | // StringBufferedMessageDictionary = new Dictionary { { "a", new Foobar(1, "a") }, { "b", new Foobar(2, "b") } }, 38 | //}; 39 | 40 | //var json = JsonSerializer.Serialize(entity); 41 | //var jsonBytes = Encoding.UTF8.GetBytes(json); 42 | 43 | 44 | 45 | //var bytes = new byte[entity.CalculateSize()]; 46 | //var context = new BufferedObjectWriteContext(bytes); 47 | //entity.Write(context); 48 | 49 | //var messsage = EntityBufferedMessage.Parse(new NativeBuffer(bytes, 0)); 50 | 51 | //using (var owner = BufferedMessage.Create(bytes)) 52 | //{ 53 | // messsage = owner.BufferedMessage; 54 | 55 | // Debug.Assert(messsage.Pritimive == 123); 56 | // Debug.Assert(messsage.Unmanaged == new Pointer(1, 2)); 57 | // Debug.Assert(messsage.String == "Hello World!"); 58 | // Debug.Assert(messsage.Binary.AsSpan().SequenceEqual(new byte[] { 1, 2, 3 })); 59 | // Debug.Assert(messsage.BufferedMessage.Foo == 123); 60 | // Debug.Assert(messsage.BufferedMessage.Bar == "Hello World!"); 61 | 62 | // Debug.Assert(messsage.UnmanagedCollection.Count == 2); 63 | // Debug.Assert(messsage.UnmanagedCollection[0] == new Pointer(1, 2)); 64 | // Debug.Assert(messsage.UnmanagedCollection[1] == new Pointer(3, 4)); 65 | 66 | // Debug.Assert(messsage.StringCollection.Count == 2); 67 | // Debug.Assert(messsage.StringCollection[0] == "Hello"); 68 | // Debug.Assert(messsage.StringCollection[1] == "World"); 69 | 70 | // Debug.Assert(messsage.BinaryCollection.Count == 2); 71 | // Debug.Assert(messsage.BinaryCollection[0].AsSpan().SequenceEqual(new byte[] { 1, 2, 3 })); 72 | // Debug.Assert(messsage.BinaryCollection[1].AsSpan().SequenceEqual(new byte[] { 4, 5, 6 })); 73 | 74 | // Debug.Assert(messsage.BufferedMessageCollection.Count == 2); 75 | // Debug.Assert(messsage.BufferedMessageCollection[0].Foo == 1); 76 | // Debug.Assert(messsage.BufferedMessageCollection[0].Bar == "a"); 77 | // Debug.Assert(messsage.BufferedMessageCollection[1].Foo == 2); 78 | // Debug.Assert(messsage.BufferedMessageCollection[1].Bar == "b"); 79 | 80 | // Debug.Assert(messsage.UnmanagedUnmanagedDictionary.Count == 2); 81 | // Debug.Assert(messsage.UnmanagedUnmanagedDictionary[1] == new Pointer(1, 2)); 82 | // Debug.Assert(messsage.UnmanagedUnmanagedDictionary[2] == new Pointer(3, 4)); 83 | 84 | // Debug.Assert(messsage.UnmanagedStringDictionary.Count == 2); 85 | // Debug.Assert(messsage.UnmanagedStringDictionary[1] == "Hello"); 86 | // Debug.Assert(messsage.UnmanagedStringDictionary[2] == "World"); 87 | 88 | // Debug.Assert(messsage.UnmanagedBinaryDictionary.Count == 2); 89 | // Debug.Assert(messsage.UnmanagedBinaryDictionary[1].AsSpan().SequenceEqual(new byte[] { 1, 2, 3 })); 90 | // Debug.Assert(messsage.UnmanagedBinaryDictionary[2].AsSpan().SequenceEqual(new byte[] { 4, 5, 6 })); 91 | 92 | // Debug.Assert(messsage.UnmanagedBufferedMessageDictionary.Count == 2); 93 | // Debug.Assert(messsage.UnmanagedBufferedMessageDictionary[1].Foo == 1); 94 | // Debug.Assert(messsage.UnmanagedBufferedMessageDictionary[1].Bar == "a"); 95 | 96 | // Debug.Assert(messsage.StringUnmanagedDictionary.Count == 2); 97 | // Debug.Assert(messsage.StringUnmanagedDictionary["a"] == new Pointer(1, 2)); 98 | // Debug.Assert(messsage.StringUnmanagedDictionary["b"] == new Pointer(3, 4)); 99 | 100 | // Debug.Assert(messsage.StringStringDictionary.Count == 2); 101 | // Debug.Assert(messsage.StringStringDictionary["a"] == "Hello"); 102 | // Debug.Assert(messsage.StringStringDictionary["b"] == "World"); 103 | 104 | // Debug.Assert(messsage.StringBinaryDictionary.Count == 2); 105 | // Debug.Assert(messsage.StringBinaryDictionary["a"].AsSpan().SequenceEqual(new byte[] { 1, 2, 3 })); 106 | // Debug.Assert(messsage.StringBinaryDictionary["b"].AsSpan().SequenceEqual(new byte[] { 4, 5, 6 })); 107 | 108 | // Debug.Assert(messsage.StringBufferedMessageDictionary.Count == 2); 109 | // Debug.Assert(messsage.StringBufferedMessageDictionary["a"].Foo == 1); 110 | // Debug.Assert(messsage.StringBufferedMessageDictionary["a"].Bar == "a"); 111 | //} 112 | 113 | //using (var owner = BufferedMessage.Create(bytes)) 114 | //{ } 115 | -------------------------------------------------------------------------------- /demo/App2/App2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | true 9 | True 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/App2/Entity.cs: -------------------------------------------------------------------------------- 1 | #nullable disable 2 | using NativeBuffering; 3 | 4 | [BufferedMessageSource] 5 | public partial class Entity 6 | { 7 | public int Primitive { get; set; } 8 | public Foobar Unmanaged { get; set; } 9 | public byte[] Bytes { get; set; } 10 | public string String { get; set; } 11 | public Foobarbaz BufferedObject { get; set; } 12 | 13 | public IEnumerable PrimitiveList { get; set; } 14 | public IEnumerable UnmanagedList { get; set; } 15 | public IEnumerable StringList { get; set; } 16 | public IEnumerable BufferedObjectList { get; set; } 17 | } 18 | 19 | [BufferedMessageSource] 20 | public partial class Foobarbaz 21 | { 22 | public Foobarbaz(Foobar foobar, string baz) 23 | { 24 | Foobar = foobar; 25 | Baz = baz; 26 | } 27 | public Foobar Foobar { get; } 28 | public string Baz { get; } 29 | } 30 | public readonly record struct Foobar(int Foo, long Bar); 31 | 32 | #nullable restore 33 | -------------------------------------------------------------------------------- /demo/App2/Program.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | using System.Diagnostics; 3 | 4 | var entity = new Entity 5 | { 6 | Primitive = 123, 7 | Unmanaged = new Foobar(123, 789), 8 | Bytes = Enumerable.Range(1, 128).Select(_ => byte.MaxValue).ToArray(), 9 | String = "abc", 10 | BufferedObject = new Foobarbaz(new Foobar(111, 222), "xyz"), 11 | 12 | PrimitiveList = new int[] { 1, 2, 3 }, 13 | UnmanagedList = new Foobar[] { new Foobar(1, 2), new Foobar(3, 4) }, 14 | StringList = new string[] { "a", "b", "c" }, 15 | BufferedObjectList = new Foobarbaz[] { new Foobarbaz(new Foobar(1, 2), "a"), new Foobarbaz(new Foobar(3, 4), "b") } 16 | }; 17 | 18 | await entity.WriteToAsync("entity.bin"); 19 | 20 | 21 | using (var pooledMessage = await BufferedMessage.LoadAsync("entity.bin")) 22 | { 23 | var bufferedMessage = pooledMessage.BufferedMessage; 24 | Debug.Assert(bufferedMessage.PrimitiveList.Count == 3); 25 | Debug.Assert(bufferedMessage.PrimitiveList[0] == 1); 26 | Debug.Assert(bufferedMessage.PrimitiveList[1] == 2); 27 | Debug.Assert(bufferedMessage.PrimitiveList[2] == 3); 28 | 29 | Debug.Assert(bufferedMessage.UnmanagedList.Count == 2); 30 | Debug.Assert(bufferedMessage.UnmanagedList[0].Foo == 1); 31 | Debug.Assert(bufferedMessage.UnmanagedList[0].Bar == 2); 32 | Debug.Assert(bufferedMessage.UnmanagedList[1].Foo == 3); 33 | Debug.Assert(bufferedMessage.UnmanagedList[1].Bar == 4); 34 | 35 | Debug.Assert(bufferedMessage.StringList.Count == 3); 36 | Debug.Assert(bufferedMessage.StringList[0] == "a"); 37 | Debug.Assert(bufferedMessage.StringList[1] == "b"); 38 | Debug.Assert(bufferedMessage.StringList[2] == "c"); 39 | 40 | Debug.Assert(bufferedMessage.BufferedObjectList.Count == 2); 41 | Debug.Assert(bufferedMessage.BufferedObjectList[0]!.Value.Foobar.Foo == 1); 42 | Debug.Assert(bufferedMessage.BufferedObjectList[0]!.Value.Foobar.Bar == 2); 43 | Debug.Assert(bufferedMessage.BufferedObjectList[0]!.Value.Baz == "a"); 44 | Debug.Assert(bufferedMessage.BufferedObjectList[1]!.Value.Foobar.Foo == 3); 45 | Debug.Assert(bufferedMessage.BufferedObjectList[1]!.Value.Foobar.Bar == 4); 46 | Debug.Assert(bufferedMessage.BufferedObjectList[1]!.Value.Baz == "b"); 47 | } -------------------------------------------------------------------------------- /demo/App3/AntiVirusFriendlyConfig.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Configs; 2 | using BenchmarkDotNet.Jobs; 3 | using BenchmarkDotNet.Toolchains.InProcess.NoEmit; 4 | 5 | namespace App3 6 | { 7 | public class AntiVirusFriendlyConfig : ManualConfig 8 | { 9 | public AntiVirusFriendlyConfig() 10 | { 11 | AddJob(Job.MediumRun 12 | .WithToolchain(InProcessNoEmitToolchain.Instance)); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /demo/App3/App3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | true 9 | True 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/App3/Entity.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | 3 | namespace App3 4 | { 5 | [BufferedMessageSource] 6 | public partial class Entity 7 | { 8 | // Primitive 9 | public byte ByteValue { get; set; } 10 | public sbyte SByteValue { get; set; } 11 | public short ShortValue { get; set; } 12 | public ushort UShortValue { get; set; } 13 | public int IntValue { get; set; } 14 | public uint UIntValue { get; set; } 15 | public long LongValue { get; set; } 16 | public ulong ULongValue { get; set; } 17 | public float FloatValue { get; set; } 18 | public double DoubleValue { get; set; } 19 | public decimal DecimalValue { get; set; } 20 | public char CharValue { get; set; } 21 | public bool BoolValue { get; set; } 22 | public DateTimeKind EnumValue { get; set; } 23 | 24 | // Unmanaged struct 25 | public TimeSpan TimeSpanValue { get; set; } 26 | 27 | // String 28 | public string StringValue { get; set; } = default!; 29 | 30 | // Binary 31 | public byte[] ByteArrayValue { get; set; } = default!; 32 | //public Memory MemoryOfByteValue { get; set; } 33 | 34 | // Collection 35 | public IEnumerable CollectionValue { get; set; } = default!; 36 | 37 | //Dictionary 38 | public IDictionary DictionaryValue { get; set; } = default!; 39 | } 40 | 41 | [BufferedMessageSource] 42 | public partial class Foobar 43 | { 44 | public int Foo { get; set; } 45 | public long Bar { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo/App3/MyBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using NativeBuffering; 3 | using System.Buffers; 4 | using System.Text; 5 | using System.Text.Json; 6 | 7 | namespace App3 8 | { 9 | [MemoryDiagnoser] 10 | [Config(typeof(AntiVirusFriendlyConfig))] 11 | public class MyBenchmark 12 | { 13 | private static readonly Func _bufferFactory = ArrayPool.Shared.Rent; 14 | 15 | [Benchmark] 16 | public string SerializeAsJson() => JsonSerializer.Serialize(Person.Instance); 17 | 18 | [Benchmark] 19 | public void SerializeNativeBuffering() 20 | { 21 | var arraySegment = Person.Instance.WriteTo(_bufferFactory); 22 | ArrayPool.Shared.Return(arraySegment.Array!); 23 | } 24 | 25 | //private Entity _entity = default!; 26 | 27 | //[GlobalSetup] 28 | //public void Setup() 29 | //{ 30 | // var foobar1 = new Foobar { Foo = 1, Bar = 2 }; 31 | // var foobar2 = new Foobar { Foo = 3, Bar = 4 }; 32 | // var foobar3 = new Foobar { Foo = 5, Bar = 6 }; 33 | 34 | // _entity = new Entity 35 | // { 36 | // BoolValue = true, 37 | // CharValue = 'a', 38 | // DecimalValue = 1.2m, 39 | // DoubleValue = 1.2, 40 | // EnumValue = DateTimeKind.Utc, 41 | // FloatValue = 1.2f, 42 | // IntValue = 1, 43 | // LongValue = 1, 44 | // SByteValue = 1, 45 | // ShortValue = 1, 46 | // TimeSpanValue = TimeSpan.FromDays(1), 47 | // ULongValue = 1, 48 | // UIntValue = 1, 49 | // UShortValue = 1, 50 | // ByteArrayValue = new byte[] { 1, 2, 3 }, 51 | // ByteValue = 1, 52 | // //MemoryOfByteValue = new byte[] { 1, 2, 3 }, 53 | // StringValue = "Hello World", 54 | // CollectionValue = new[] { foobar1, foobar2, foobar3 }, 55 | // //DictionaryValue = new Dictionary { { 1, foobar1 }, { 2, foobar2 }, { 3, foobar3 } }, 56 | // }; 57 | //} 58 | 59 | //private Func _bufferFactory = ArrayPool.Shared.Rent; 60 | //[Benchmark] 61 | //public byte[] SerializeAsJson()=> Encoding.UTF8.GetBytes(JsonSerializer.Serialize(_entity!)); 62 | 63 | //[Benchmark] 64 | //public void SerializeNativeBuffering() 65 | //{ 66 | // var arraySegment = _entity.WriteTo(_bufferFactory); 67 | // ArrayPool.Shared.Return(arraySegment.Array!); 68 | 69 | // //var size = _entity.CalculateSize(); 70 | // //var bytes = ArrayPool.Shared.Rent(size); 71 | // //var context = BufferedObjectWriteContext.Acquire(bytes); 72 | // //_entity.Write(context); 73 | // //BufferedObjectWriteContext.Release(context); 74 | // //ArrayPool.Shared.Return(bytes); 75 | //} 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /demo/App3/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using NativeBuffering; 8 | 9 | namespace App3 10 | { 11 | [BufferedMessageSource] 12 | public partial class Person 13 | { 14 | public string Name { get; set; } 15 | public int Age { get; set; } 16 | public string[] Hobbies { get; set; } 17 | public string Address { get; set; } 18 | public string PhoneNumber { get; set; } 19 | public string Email { get; set; } 20 | public string Gender { get; set; } 21 | public string Nationality { get; set; } 22 | public string Occupation { get; set; } 23 | public string EducationLevel { get; set; } 24 | public string MaritalStatus { get; set; } 25 | public string SpouseName { get; set; } 26 | public int NumberOfChildren { get; set; } 27 | public string[] ChildrenNames { get; set; } 28 | public string[] LanguagesSpoken { get; set; } 29 | public bool HasPets { get; set; } 30 | public string[] PetNames { get; set; } 31 | 32 | public static Person Instance = new Person 33 | { 34 | Name = "Bill", 35 | Age = 30, 36 | Hobbies = new string[] { "Reading", "Writing", "Coding" }, 37 | Address = "123 Main St.", 38 | PhoneNumber = "555-555-5555", 39 | Email = "bill@gmail.com", 40 | Gender = "M", 41 | Nationality = "China", 42 | Occupation = "Software Engineer", 43 | EducationLevel = "Bachelor's", 44 | MaritalStatus = "Married", 45 | SpouseName = "Jane", 46 | NumberOfChildren = 2, 47 | ChildrenNames = new string[] { "John", "Jill" }, 48 | LanguagesSpoken = new string[] { "English", "Chinese" }, 49 | HasPets = true, 50 | PetNames = new string[] { "Fido", "Spot" } 51 | }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /demo/App3/Program.cs: -------------------------------------------------------------------------------- 1 | using App3; 2 | using BenchmarkDotNet.Running; 3 | using NativeBuffering; 4 | using System.Buffers; 5 | using System.Diagnostics; 6 | 7 | //var arraySegment = Record.Instance.WriteTo(ArrayPool.Shared.Rent); 8 | //ArrayPool.Shared.Return(arraySegment.Array!); 9 | 10 | var summary = BenchmarkRunner.Run(typeof(MyBenchmark)); 11 | 12 | //var foobar1 = new Foobar { Foo = 1, Bar = 2 }; 13 | //var foobar2 = new Foobar { Foo = 3, Bar = 4 }; 14 | //var foobar3 = new Foobar { Foo = 5, Bar = 6 }; 15 | 16 | //var entity = new Entity 17 | //{ 18 | // BoolValue = true, 19 | // CharValue = 'a', 20 | // DecimalValue = 1.2m, 21 | // DoubleValue = 1.2, 22 | // EnumValue = DateTimeKind.Utc, 23 | // FloatValue = 1.2f, 24 | // IntValue = 1, 25 | // LongValue = 1, 26 | // SByteValue = 1, 27 | // ShortValue = 1, 28 | // TimeSpanValue = TimeSpan.FromDays(1), 29 | // ULongValue = 1, 30 | // UIntValue = 1, 31 | // UShortValue = 1, 32 | // ByteArrayValue = new byte[] { 1, 2, 3 }, 33 | // ByteValue = 1, 34 | // MemoryOfByteValue = new byte[] { 1, 2, 3 }, 35 | // StringValue = "Hello World", 36 | // CollectionValue = new[] { foobar1, foobar2, foobar3 }, 37 | // DictionaryValue = new Dictionary { { 1, foobar1 }, { 2, foobar2 }, { 3, foobar3 } }, 38 | //}; 39 | 40 | //await entity.WriteToAsync("entity.bin"); 41 | //using (var pooledMessage = await BufferedMessage.LoadAsync("entity.bin")) 42 | //{ 43 | // var message = pooledMessage.BufferedMessage; 44 | // Debug.Assert(entity.BoolValue == message.BoolValue); 45 | // Debug.Assert(entity.CharValue == message.CharValue); 46 | // Debug.Assert(entity.DecimalValue == message.DecimalValue); 47 | // Debug.Assert(entity.DoubleValue == message.DoubleValue); 48 | // Debug.Assert(entity.EnumValue == message.EnumValue); 49 | // Debug.Assert(entity.FloatValue == message.FloatValue); 50 | // Debug.Assert(entity.IntValue == message.IntValue); 51 | // Debug.Assert(entity.LongValue == message.LongValue); 52 | // Debug.Assert(entity.SByteValue == message.SByteValue); 53 | // Debug.Assert(entity.ShortValue == message.ShortValue); 54 | // Debug.Assert(entity.TimeSpanValue == message.TimeSpanValue); 55 | // Debug.Assert(entity.ULongValue == message.ULongValue); 56 | // Debug.Assert(entity.UIntValue == message.UIntValue); 57 | // Debug.Assert(entity.UShortValue == message.UShortValue); 58 | // Debug.Assert(entity.ByteValue == message.ByteValue); 59 | // Debug.Assert(message.ByteArrayValue.AsSpan().SequenceEqual(entity.ByteArrayValue)); 60 | // Debug.Assert(entity.MemoryOfByteValue.Span.SequenceEqual(message.MemoryOfByteValue.AsSpan())); 61 | // Debug.Assert(entity.StringValue == message.StringValue); 62 | 63 | // foreach (var fooabr in entity.CollectionValue) 64 | // { 65 | // Debug.Assert(message.CollectionValue.Any(it => it!.Value.Foo == fooabr.Foo && it.Value.Bar == fooabr.Bar)); 66 | // } 67 | // foreach (var (key, value) in entity.DictionaryValue) 68 | // { 69 | // Debug.Assert(message.DictionaryValue.Any(it => it.Key == key && it.Value!.Foo == value.Foo && it.Value.Bar == value.Bar)); 70 | // } 71 | //} -------------------------------------------------------------------------------- /demo/App3/Record.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | 3 | [BufferedMessageSource] 4 | public partial class Record 5 | { 6 | public Foobarbazqux[] Data { get; set; } = default!; 7 | public static Record Instance => new Record { Data = Enumerable.Range(1, 100).Select(_ => new Foobarbazqux(new Foobarbaz(new Foobar(111, 222), 1.234f), 3.14d)).ToArray() }; 8 | } 9 | 10 | public readonly record struct Foobar(int Foo, long Bar); 11 | public readonly record struct Foobarbaz(Foobar Foobar, float Baz); 12 | public readonly record struct Foobarbazqux(Foobarbaz Foobarbaz, double Qux); -------------------------------------------------------------------------------- /pack.bat: -------------------------------------------------------------------------------- 1 | dotnet clean 2 | dotnet restore 3 | cd ./src/NativeBuffering 4 | dotnet pack -c Release -o ../../packages 5 | cd ../../ 6 | cd ./src/NativeBuffering.Generator 7 | dotnet pack -c Release -o ../../packages 8 | pause -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/BufferedMessageMemberKind.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Generator 2 | { 3 | public enum BufferedMessageMemberKind 4 | { 5 | Primitive, 6 | Unmanaged, 7 | String, 8 | Message, 9 | Binary, 10 | 11 | UnmanagedUnmanagedDictionary, 12 | UnmanagedStringDictionary, 13 | UnmanagedMessageDictionary, 14 | UnmanagedBinaryDictionary, 15 | 16 | StringUnmanagedDictionary, 17 | StringStringDictionary, 18 | StringMessageDictionary, 19 | StringBinaryDictionary, 20 | 21 | UnmanagedCollection, 22 | StringCollection, 23 | MessageCollection, 24 | BinaryCollection 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/BufferedMessageSourceClassGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace NativeBuffering.Generator 4 | { 5 | internal class BufferedMessageSourceClassGenerator 6 | { 7 | public void Generate(BufferedObjectMetadata metadata, CodeGenerationContext context) 8 | { 9 | if(metadata.TypeSymbol.IsValueType) 10 | { 11 | context.WriteLines($"public partial struct {metadata.TypeSymbol.Name} : IBufferedObjectSource"); 12 | } 13 | else 14 | { 15 | context.WriteLines($"public partial class {metadata.TypeSymbol.Name} : IBufferedObjectSource"); 16 | } 17 | using (context.CodeBlock()) 18 | { 19 | GenerateCalculateSizeMethod(metadata, context); 20 | GenerateWriteMethod(metadata, context); 21 | GenerateConstructor(metadata, context); 22 | } 23 | } 24 | 25 | private void GenerateCalculateSizeMethod(BufferedObjectMetadata metadata, CodeGenerationContext context) 26 | { 27 | context.WriteLines("public int CalculateSize()"); 28 | using (context.CodeBlock()) 29 | { 30 | //context.WriteLines("var context = BufferedObjectWriteContext.CreateForSizeCalculation();"); 31 | context.WriteLines("var context = BufferedObjectWriteContext.AcquireForSizeCalculation();"); 32 | context.WriteLines("Write(context);"); 33 | context.WriteLines("var result = context.Position;"); 34 | context.WriteLines("BufferedObjectWriteContext.Release(context);"); 35 | context.WriteLines("return result;"); 36 | } 37 | } 38 | 39 | private void GenerateWriteMethod(BufferedObjectMetadata metadata, CodeGenerationContext context) 40 | { 41 | var properties = metadata.Properties; 42 | context.WriteLines("public void Write(BufferedObjectWriteContext context)"); 43 | using (context.CodeBlock()) 44 | { 45 | //context.WriteLines("using var scope = new BufferedObjectWriteContextScope(context);"); 46 | //context.WriteLines($"var scope = new BufferedObjectWriteContextScope(context, {metadata.Properties.Length});"); 47 | context.WriteLines($"var scope = context.GetWriteContextScope({metadata.Properties.Length});"); 48 | for (int index = 0; index < properties.Length; index++) 49 | { 50 | var property = properties[index]; 51 | var propertyName = property.PropertySymbol.Name; 52 | var propertyType = property.PropertySymbol.Type; 53 | 54 | _ = property.Kind switch 55 | { 56 | BufferedMessageMemberKind.Primitive => context.WriteLines($"scope.WriteUnmanagedField({propertyName});"), 57 | BufferedMessageMemberKind.Unmanaged => context.WriteLines($"scope.WriteUnmanagedField({propertyName});"), 58 | BufferedMessageMemberKind.String => context.WriteLines($"scope.WriteStringField({propertyName});"), 59 | BufferedMessageMemberKind.Binary => context.WriteLines($"scope.WriteBinaryField({propertyName});"), 60 | BufferedMessageMemberKind.Message => context.WriteLines($"scope.WriteBufferedObjectField({propertyName});"), 61 | 62 | BufferedMessageMemberKind.UnmanagedUnmanagedDictionary => property.ValueType!.IsNullable(out _) 63 | ? context.WriteLines($"scope.WriteUnmanagedNullableUnmanagedDictionaryField({propertyName});") 64 | : context.WriteLines($"scope.WriteUnmanagedNonNullableUnmanagedDictionaryField({propertyName});"), 65 | BufferedMessageMemberKind.UnmanagedMessageDictionary => context.WriteLines($"scope.WriteUnmanagedBufferedObjectDictionaryField({propertyName});"), 66 | BufferedMessageMemberKind.UnmanagedStringDictionary => context.WriteLines($"scope.WriteUnmanagedStringDictionaryField({propertyName});"), 67 | BufferedMessageMemberKind.UnmanagedBinaryDictionary => context.WriteLines($"scope.WriteUnmanagedBinaryDictionaryField({propertyName});"), 68 | 69 | BufferedMessageMemberKind.StringUnmanagedDictionary => property.ValueType!.IsNullable(out _) 70 | ? context.WriteLines($"scope.WriteStringNullableUnmanagedDictionaryField({propertyName});") 71 | : context.WriteLines($"scope.WriteStringNonNullableUnmanagedDictionaryField({propertyName});"), 72 | BufferedMessageMemberKind.StringStringDictionary => context.WriteLines($"scope.WriteStringStringDictionaryField({propertyName});"), 73 | BufferedMessageMemberKind.StringMessageDictionary => context.WriteLines($"scope.WriteStringBufferedObjectDictionaryField({propertyName});"), 74 | BufferedMessageMemberKind.StringBinaryDictionary => context.WriteLines($"scope.WriteStringBinaryDictionaryField({propertyName});"), 75 | 76 | BufferedMessageMemberKind.UnmanagedCollection => context.WriteLines($"scope.WriteUnmanagedCollectionField({propertyName});"), 77 | BufferedMessageMemberKind.StringCollection => context.WriteLines($"scope.WriteStringCollectionField({propertyName});"), 78 | BufferedMessageMemberKind.MessageCollection => context.WriteLines($"scope.WriteBufferedObjectCollectionField({propertyName});"), 79 | BufferedMessageMemberKind.BinaryCollection => context.WriteLines($"scope.WriteBinaryCollectionField({propertyName});"), 80 | 81 | _ => throw new NotSupportedException("Will never hit here!") 82 | }; ; 83 | } 84 | context.WriteLines("context.ReleaseWriteContextScope(scope);"); 85 | } 86 | } 87 | 88 | private void GenerateConstructor(BufferedObjectMetadata metadata, CodeGenerationContext context) 89 | { 90 | if (metadata.TypeSymbol is INamedTypeSymbol namedTypeSymbol) 91 | { 92 | if (!namedTypeSymbol.Constructors.Any(it=>it.Parameters.Length ==0 && !it.IsStatic && it.DeclaredAccessibility == Accessibility.Public)) 93 | { 94 | context.WriteLines($"public {metadata.TypeSymbol.Name}() {{ }}"); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/BufferedObjectMetadata.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace NativeBuffering.Generator 4 | { 5 | public class BufferedObjectMetadata 6 | { 7 | public BufferedObjectMetadata(ITypeSymbol typeSymbol, string bufferedMessageClassName, BufferedObjectProperty[] properties) 8 | { 9 | TypeSymbol = typeSymbol; 10 | BufferedMessageClassName = bufferedMessageClassName; 11 | Properties = properties; 12 | var fullName = typeSymbol.GetFullName(); 13 | if (fullName == typeSymbol.Name) 14 | { 15 | BufferedMessageClassFullName = bufferedMessageClassName; 16 | } 17 | else 18 | { 19 | BufferedMessageClassFullName = $"{fullName.Substring(0, fullName.Length - typeSymbol.Name.Length)}{bufferedMessageClassName}"; 20 | } 21 | } 22 | 23 | public ITypeSymbol TypeSymbol { get; } 24 | public string BufferedMessageClassName { get; } 25 | public BufferedObjectProperty[] Properties { get; } 26 | public string BufferedMessageClassFullName { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/BufferedObjectMetadataException.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Generator 2 | { 3 | 4 | [Serializable] 5 | public class BufferedObjectMetadataException : Exception 6 | { 7 | public BufferedObjectMetadataException() { } 8 | public BufferedObjectMetadataException(string message) : base(message) { } 9 | public BufferedObjectMetadataException(string message, Exception inner) : base(message, inner) { } 10 | protected BufferedObjectMetadataException( 11 | System.Runtime.Serialization.SerializationInfo info, 12 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/BufferedObjectProperty.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.IO.Pipes; 3 | 4 | namespace NativeBuffering.Generator 5 | { 6 | public class BufferedObjectProperty 7 | { 8 | public BufferedObjectProperty(IPropertySymbol propertySymbol, BufferedMessageMemberKind kind) 9 | { 10 | PropertySymbol = propertySymbol; 11 | Kind = kind; 12 | IsNullable = propertySymbol.Type.IsNullable(out var underlyingTypeSymbol); 13 | NullableUnderlyingType = underlyingTypeSymbol; 14 | } 15 | 16 | public IPropertySymbol PropertySymbol { get; } 17 | public BufferedMessageMemberKind Kind { get; } 18 | 19 | public ITypeSymbol? ElementType { get; set; } 20 | public ITypeSymbol? KeyType { get; set; } 21 | public ITypeSymbol? ValueType { get; set; } 22 | 23 | public string? BufferedMessageTypeName { get; set; } 24 | public bool IsNullable { get; } 25 | public ITypeSymbol? NullableUnderlyingType { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/CodeGenerationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace NativeBuffering.Generator 7 | { 8 | public sealed class CodeGenerationContext 9 | { 10 | private readonly StringBuilder _writer = new(); 11 | public ISet References { get; } = new HashSet(); 12 | public int IndentLevel { get; private set; } 13 | public string SourceCode => _writer.ToString(); 14 | public string? ErrorMessage { get; set; } 15 | public bool IsValid=> string.IsNullOrEmpty(ErrorMessage); 16 | public CodeGenerationContext WriteLines(params string[] lines) 17 | { 18 | if (lines.Length == 0) 19 | { 20 | _writer.AppendLine(); 21 | } 22 | lines = lines.SelectMany(it => it.Split('\n')).ToArray(); 23 | foreach (var line in lines) 24 | { 25 | _writer.AppendLine($"{new string(' ', 4 * IndentLevel)}{line}"); 26 | } 27 | return this; 28 | } 29 | public IDisposable CodeBlock(string? start = null, string? end = null) => new CodeBlockScope(this, start ?? "{", end ?? "}"); 30 | public IDisposable Indent() => new IndentScope(this); 31 | private class CodeBlockScope : IDisposable 32 | { 33 | private readonly CodeGenerationContext _context; 34 | private readonly string _end; 35 | 36 | public CodeBlockScope(CodeGenerationContext context, string start, string end) 37 | { 38 | _end = end; 39 | _context = context; 40 | _context.WriteLines(start); 41 | _context.IndentLevel++; 42 | } 43 | 44 | public void Dispose() 45 | { 46 | _context.IndentLevel--; 47 | _context.WriteLines(_end); 48 | } 49 | } 50 | private class IndentScope : IDisposable 51 | { 52 | private readonly CodeGenerationContext _context; 53 | 54 | public IndentScope(CodeGenerationContext context) 55 | { 56 | _context = context; 57 | _context.IndentLevel++; 58 | } 59 | 60 | public void Dispose() => _context.IndentLevel--; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/Generator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using Microsoft.CodeAnalysis.Text; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Text; 8 | 9 | namespace NativeBuffering.Generator 10 | { 11 | [Generator] 12 | public class Generator : ISourceGenerator 13 | { 14 | public void Execute(GeneratorExecutionContext context) 15 | { 16 | if (context.SyntaxReceiver is not SyntaxReceiver receiver) 17 | { 18 | return; 19 | } 20 | 21 | foreach (var node in receiver.Candidates) 22 | { 23 | var model = context.Compilation.GetSemanticModel(node.SyntaxTree); 24 | if (model.GetDeclaredSymbol(node, context.CancellationToken) is not ITypeSymbol typeSymbol) 25 | { 26 | continue; 27 | } 28 | if (!typeSymbol.GetAttributes().Any(it => it.AttributeClass?.Name == "BufferedMessageSourceAttribute")) 29 | { 30 | continue; 31 | } 32 | 33 | var metadata = BufferedObjectMetadataResolver.Resolve(typeSymbol); 34 | var generationContext = new CodeGenerationContext(); 35 | Generate(typeSymbol, generationContext, (_, ctx) => new BufferedMessageSourceClassGenerator().Generate(metadata, ctx)); 36 | context.AddSource($"{typeSymbol.GetFullName()}.g.cs", SourceText.From(generationContext.SourceCode!, Encoding.UTF8)); 37 | 38 | generationContext = new CodeGenerationContext(); 39 | Generate(typeSymbol, generationContext, (_, ctx) => new BufferedMessageClassGenerator().Generate(metadata, ctx)); 40 | context.AddSource($"{metadata.BufferedMessageClassFullName}.g.cs", SourceText.From(generationContext.SourceCode!, Encoding.UTF8)); 41 | } 42 | } 43 | 44 | public void Initialize(GeneratorInitializationContext context) 45 | { 46 | //Debugger.Launch(); 47 | //Debugger.Break(); 48 | context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); 49 | } 50 | 51 | public void Generate(ITypeSymbol typeSymbol, CodeGenerationContext context, Action generator) 52 | { 53 | context.WriteLines("#nullable enable"); 54 | context.WriteLines("using System;"); 55 | context.WriteLines("using System.Runtime.CompilerServices;"); 56 | context.WriteLines("using System.Collections.Generic;"); 57 | context.WriteLines("using NativeBuffering;", "using NativeBuffering.Dictionaries;", "using NativeBuffering.Collections;"); 58 | 59 | var stack = new Stack(); 60 | var namespaceOrTypes = typeSymbol.GetContainingNamespaceAndTypes(); 61 | foreach (var item in namespaceOrTypes) 62 | { 63 | stack.Push(item); 64 | if (item is INamespaceSymbol) 65 | { 66 | break; 67 | } 68 | } 69 | WriteClass(); 70 | context.WriteLines("#nullable disable"); 71 | 72 | void WriteClass() 73 | { 74 | while (stack.Any()) 75 | { 76 | var symbol = stack.Pop(); 77 | if (symbol is INamespaceSymbol @namespace && !string.IsNullOrWhiteSpace(@namespace.Name)) 78 | { 79 | context.WriteLines($"namespace {@namespace}"); 80 | using (context.CodeBlock()) 81 | { 82 | WriteClass(); 83 | } 84 | } 85 | 86 | if (symbol is INamedTypeSymbol typeSymbol) 87 | { 88 | if (stack.Any()) // outer class 89 | { 90 | context.WriteLines($"{typeSymbol.DeclaredAccessibility.ToString().ToLower()} partial class {typeSymbol.Name}"); 91 | using (context.CodeBlock()) 92 | { 93 | WriteClass(); 94 | } 95 | } 96 | else 97 | { 98 | generator(typeSymbol, context); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | private class SyntaxReceiver : ISyntaxReceiver 106 | { 107 | public List Candidates { get; } = new List(); 108 | 109 | public void OnVisitSyntaxNode(SyntaxNode syntaxNode) 110 | { 111 | if (syntaxNode is ClassDeclarationSyntax || syntaxNode is RecordDeclarationSyntax || syntaxNode is StructDeclarationSyntax) 112 | { 113 | Candidates.Add(syntaxNode); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/NativeBuffering.Generator/NativeBuffering.Generator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | enable 6 | enable 7 | latest 8 | true 9 | 0.1.6 10 | 0.1.6 11 | True 12 | Fix source class metata resolving issues. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferPool/Bucket.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace NativeBuffering 5 | { 6 | internal sealed class Bucket 7 | { 8 | private readonly ConcurrentBag _pool = new(); 9 | public void Add(byte[] array) => _pool.Add(array); 10 | public bool TryTake([MaybeNullWhen(false)]out byte[] array) => _pool.TryTake(out array); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferPool/BufferOwner.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public sealed class BufferOwner: IDisposable 4 | { 5 | private readonly byte[] _bytes; 6 | private readonly Bucket? _bucket; 7 | private int _isReleased; 8 | 9 | internal BufferOwner(byte[] bytes, Bucket? bucket) 10 | { 11 | _bytes = bytes; 12 | _bucket = bucket; 13 | } 14 | 15 | public byte[] Bytes 16 | { 17 | get 18 | { 19 | if (_isReleased == 1) 20 | { 21 | throw new ObjectDisposedException("The ByteArrayOwner has been released."); 22 | } 23 | return _bytes; 24 | } 25 | } 26 | 27 | 28 | public void Dispose() 29 | { 30 | if (Interlocked.CompareExchange(ref _isReleased, 1, 0) == 0) 31 | { 32 | _bucket?.Add(_bytes); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferPool/BufferPool.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public static class BufferPool 4 | { 5 | private static int _maxRequestedLength = 1024 * 1024; 6 | private static InternalBufferPool? _pool; 7 | 8 | public static void Configure(int maxRequestedLength) 9 | { 10 | if (_pool is not null) 11 | { 12 | throw new InvalidOperationException("BufferPool can only be configure before the 1st usage."); 13 | } 14 | _maxRequestedLength = maxRequestedLength; 15 | } 16 | 17 | public static BufferOwner Rent(int minimumLength) 18 | { 19 | if (_pool is null) 20 | { 21 | Interlocked.CompareExchange(ref _pool, new InternalBufferPool(_maxRequestedLength), null); 22 | } 23 | return _pool.Rent(minimumLength); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferPool/InternalBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace NativeBuffering 5 | { 6 | internal sealed class InternalBufferPool 7 | { 8 | private readonly Bucket[] buckets; 9 | private readonly int _maxRequestedLength; 10 | public static InternalBufferPool Create(int maxLength) => new(maxLength); 11 | 12 | public InternalBufferPool(int maxLength) 13 | { 14 | var bucketCount = SelectBucketIndex(maxLength) + 1; 15 | buckets = new Bucket[bucketCount]; 16 | _maxRequestedLength = GetMaxSizeForBucket(bucketCount - 1); 17 | for (int index = 0; index < bucketCount; index++) 18 | { 19 | buckets[index] = new Bucket(); 20 | } 21 | } 22 | 23 | public BufferOwner Rent(int minimumLength) 24 | { 25 | if (minimumLength < 0) 26 | { 27 | throw new ArgumentOutOfRangeException(nameof(minimumLength)); 28 | } 29 | if (minimumLength > _maxRequestedLength) 30 | { 31 | return new BufferOwner(GC.AllocateUninitializedArray(minimumLength, pinned: false), null); 32 | } 33 | 34 | var bucketIndex = SelectBucketIndex(minimumLength); 35 | for (int index = bucketIndex; index < buckets.Length; index++) 36 | { 37 | var bucket = buckets[index]; 38 | if (bucket.TryTake(out var array)) 39 | { 40 | return new BufferOwner(array, bucket); 41 | } 42 | } 43 | 44 | return new BufferOwner(GC.AllocateUninitializedArray(GetMaxSizeForBucket(bucketIndex), pinned: true), buckets[bucketIndex]); 45 | } 46 | 47 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 48 | internal static int GetMaxSizeForBucket(int binIndex)=> 16 << binIndex; 49 | 50 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 51 | internal static int SelectBucketIndex(int bufferSize)=> BitOperations.Log2((uint)(bufferSize - 1) | 0xFu) - 3; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferedBinary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace NativeBuffering 5 | { 6 | public unsafe readonly struct BufferedBinary : IReadOnlyBufferedObject 7 | { 8 | public static BufferedBinary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 9 | public BufferedBinary(NativeBuffer buffer) => Buffer = buffer; 10 | public NativeBuffer Buffer { get; } 11 | public int Length 12 | { 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | get => Unsafe.Read(Buffer.Start); 15 | } 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public ReadOnlySpan AsSpan() => Length == 0? Array.Empty().AsSpan(): new (Buffer.GetPointerByOffset(sizeof(int)), Length); 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public static BufferedBinary Parse(NativeBuffer buffer) => new(buffer); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public static class BufferedMessage 4 | { 5 | private static T Create(ref BufferOwner byteArrayOwner, int offset = 0) where T : IReadOnlyBufferedObject 6 | => T.Parse(new NativeBuffer(byteArrayOwner.Bytes, offset)); 7 | 8 | public static PooledBufferedMessage AsBufferedMessage(this IBufferedObjectSource source) where T : IReadOnlyBufferedObject 9 | { 10 | var size = (source ?? throw new ArgumentNullException(nameof(source))).CalculateSize(); 11 | var owner = BufferPool.Rent(size); 12 | var context = BufferedObjectWriteContext.Create(owner.Bytes); 13 | source.Write(context); 14 | return new PooledBufferedMessage(owner, Create(ref owner)); 15 | } 16 | 17 | public static PooledBufferedMessage Load(Stream stream) where T : IReadOnlyBufferedObject 18 | { 19 | var size = (int)stream.Length; 20 | var owner = BufferPool.Rent(size); 21 | stream.Read(owner.Bytes, 0, size); 22 | return new PooledBufferedMessage(owner, Create(ref owner)); 23 | } 24 | 25 | public static async Task> LoadAsync(Stream stream, bool closeStream) where T : IReadOnlyBufferedObject 26 | { 27 | var size = (int)stream.Length; 28 | var owner = BufferPool.Rent(size); 29 | await stream.ReadAsync(owner.Bytes.AsMemory(0, size)); 30 | if (closeStream) stream.Close(); 31 | return new PooledBufferedMessage(owner, Create(ref owner)); 32 | } 33 | 34 | public static Task> LoadAsync(string filePath) where T : IReadOnlyBufferedObject 35 | => LoadAsync(new FileStream(filePath, FileMode.Open, FileAccess.Read), true); 36 | } 37 | 38 | public sealed class PooledBufferedMessage(BufferOwner bufferOwner, T bufferedMessage) : IDisposable where T : IReadOnlyBufferedObject 39 | { 40 | private readonly BufferOwner _bufferOwner = bufferOwner; 41 | private readonly T _bufferedMessage = bufferedMessage; 42 | 43 | public T BufferedMessage => _bufferedMessage; 44 | public void Dispose() => _bufferOwner.Dispose(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferedMessageSourceAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | [AttributeUsage(AttributeTargets.Class| AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] 4 | public class BufferedMessageSourceAttribute : Attribute 5 | { 6 | public string? BufferedMessageClassName { get; set; } 7 | public string[] ExcludedProperties { get; } 8 | public BufferedMessageSourceAttribute(params string[] excludedProperties) 9 | { 10 | ExcludedProperties = excludedProperties; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferedObjectWriteContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.ObjectPool; 2 | using System.Runtime.CompilerServices; 3 | using System.Text; 4 | 5 | namespace NativeBuffering 6 | { 7 | public unsafe sealed class BufferedObjectWriteContext 8 | { 9 | private static readonly ObjectPool _pool = new DefaultObjectPoolProvider().Create(new BufferedObjectWriteContextPooledObjectPolicy()); 10 | private static readonly IntPtr _stringTypeHandle = typeof(string).TypeHandle.Value; 11 | 12 | private byte[] _bytes = null!; 13 | private int _position = 0; 14 | public bool IsSizeCalculateMode { get; private set; } 15 | public byte[] Bytes => _bytes; 16 | 17 | public int Position => _position; 18 | public void* PositionAsPointer => Unsafe.AsPointer(ref Bytes[_position]); 19 | private BufferedObjectWriteContext() { } 20 | public BufferedObjectWriteContext Initialize(byte[] buffer, bool isSizeCalculateMode) 21 | { 22 | _bytes = buffer; 23 | IsSizeCalculateMode = isSizeCalculateMode; 24 | _position = 0; 25 | return this; 26 | } 27 | 28 | public void Release()=> _bytes = null!; 29 | //public static BufferedObjectWriteContext CreateForSizeCalculation() => new(Array.Empty(), true); 30 | public void Advance(int step) => _position += step; 31 | public unsafe void WriteUnmanaged(T value) where T : unmanaged 32 | { 33 | if (!IsSizeCalculateMode) 34 | { 35 | Unsafe.Write(Unsafe.AsPointer(ref Bytes[_position]), value); 36 | } 37 | _position += sizeof(T); 38 | } 39 | 40 | //private readonly int _sizeByteCount = sizeof(int) + (IntPtr.Size - sizeof(int)); 41 | public void WriteString(string value) 42 | { 43 | if(value is null) throw new ArgumentNullException(nameof(value)); 44 | var size = BufferedString.CalculateStringSize(value); 45 | if (!IsSizeCalculateMode) 46 | { 47 | //Size + Padding 48 | Unsafe.Write(Unsafe.AsPointer(ref Bytes[_position]), size); 49 | _position += IntPtr.Size; 50 | 51 | var address = *(nint*)Unsafe.AsPointer(ref value) - nint.Size; 52 | var pointer = address.ToPointer(); 53 | Unsafe.CopyBlock(PositionAsPointer, pointer, (uint)size); 54 | _position += size - IntPtr.Size; 55 | return; 56 | } 57 | _position += size; 58 | } 59 | public void WriteBytes(Span bytes) 60 | { 61 | if (!IsSizeCalculateMode) 62 | { 63 | Unsafe.Write(Unsafe.AsPointer(ref Bytes[_position]), bytes.Length); 64 | } 65 | _position += sizeof(int); 66 | if (!IsSizeCalculateMode && bytes.Length > 0) 67 | { 68 | Unsafe.CopyBlock(ref Bytes[_position], ref bytes[0], (uint)bytes.Length); 69 | } 70 | _position += bytes.Length; 71 | } 72 | public int AddPaddingBytes(int alignment) 73 | { 74 | var paddingBytes = _position % alignment; 75 | if (paddingBytes != 0) 76 | { 77 | paddingBytes = alignment - paddingBytes; 78 | _position += paddingBytes; 79 | } 80 | return _position; 81 | } 82 | public void EnsureAlignment(int alignment) 83 | { 84 | if(_position % alignment != 0) throw new InvalidOperationException($"Position is not aligned to {alignment} bytes"); 85 | } 86 | public static BufferedObjectWriteContext Acquire(byte[] buffer) => _pool.Get().Initialize(buffer, false); 87 | public static BufferedObjectWriteContext AcquireForSizeCalculation() => _pool.Get().Initialize(null!, true); 88 | public static void Release(BufferedObjectWriteContext writeContext)=> _pool.Return(writeContext); 89 | public static BufferedObjectWriteContext Create(byte[] buffer)=> new BufferedObjectWriteContext().Initialize(buffer, false); 90 | private sealed class BufferedObjectWriteContextPooledObjectPolicy : PooledObjectPolicy 91 | { 92 | public override BufferedObjectWriteContext Create() => new (); 93 | public override bool Return(BufferedObjectWriteContext obj) 94 | { 95 | obj.Release(); 96 | return true; 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/NativeBuffering/BufferedObjectWriteContextScope.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace NativeBuffering 4 | { 5 | public unsafe sealed class BufferedObjectWriteContextScope 6 | { 7 | private BufferedObjectWriteContext _writeContext = default!; 8 | private int _fieldSlot; 9 | private bool _isSizeCalculateMode; 10 | public BufferedObjectWriteContextScope(BufferedObjectWriteContext context, int fieldCount) 11 | { 12 | _writeContext = context ?? throw new ArgumentNullException(nameof(context)); 13 | _fieldSlot = _writeContext.Position; 14 | _writeContext.Advance(sizeof(int) * fieldCount); 15 | } 16 | 17 | public BufferedObjectWriteContextScope() { } 18 | public void Initialize(BufferedObjectWriteContext writeContext) 19 | { 20 | _writeContext = writeContext; 21 | _fieldSlot = writeContext.Position; 22 | _isSizeCalculateMode = writeContext.IsSizeCalculateMode; 23 | } 24 | public void Release()=> _writeContext = null!; 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public void WriteUnmanagedField(T value) where T : unmanaged => WriteField(value, (c, v) => c.WriteUnmanaged(v), AlignmentCalculator.AlignmentOf()); 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | public void WriteUnmanagedField(T? value) where T : unmanaged => WriteField(value, (c, v) => c.WriteUnmanaged(v!.Value), AlignmentCalculator.AlignmentOf(), value is null); 31 | 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | public void WriteStringField(string value) => WriteField(value, (c, v) => c.WriteString(v), IntPtr.Size, string.IsNullOrEmpty(value)); 34 | 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public void WriteBinaryField(byte[] value) => WriteField(value, (c, v) => c.WriteBytes(v), IntPtr.Size, value is null || value.Length == 0); 37 | 38 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 39 | public void WriteBinaryField(Memory value) => WriteField(value, (c, v) => c.WriteBytes(v.Span), IntPtr.Size, value.Length == 0); 40 | 41 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 42 | public void WriteBufferedObjectField(T value) where T : IBufferedObjectSource => WriteField(value, (c, v) => v.Write(c), IntPtr.Size, value is null); 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | internal void WriteField(T value, Action writeValue, int alignment, bool isDefault = false) 46 | { 47 | if (isDefault || value is null) 48 | { 49 | if (!_isSizeCalculateMode) 50 | { 51 | Unsafe.Write(Unsafe.AsPointer(ref _writeContext.Bytes[_fieldSlot]), -1); 52 | } 53 | } 54 | else 55 | { 56 | _writeContext.AddPaddingBytes(alignment); 57 | if (!_writeContext.IsSizeCalculateMode) 58 | { 59 | Unsafe.Write(Unsafe.AsPointer(ref _writeContext.Bytes[_fieldSlot]), _writeContext.Position); 60 | } 61 | writeValue(_writeContext, value); 62 | } 63 | _fieldSlot += sizeof(int); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/NativeBuffering/BufferedString.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Text; 3 | 4 | namespace NativeBuffering 5 | { 6 | public unsafe readonly struct BufferedString : IReadOnlyBufferedObject 7 | { 8 | private readonly void* _start; 9 | public static BufferedString DefaultValue { get; } 10 | static BufferedString() 11 | { 12 | var size = CalculateStringSize(string.Empty); 13 | var bytes = new byte[size]; 14 | 15 | var context = BufferedObjectWriteContext.Create(bytes); 16 | context.WriteString(string.Empty); 17 | DefaultValue = new BufferedString(new NativeBuffer(bytes)); 18 | } 19 | public BufferedString(NativeBuffer buffer) => _start = buffer.Start; 20 | public BufferedString(void* start) => _start = start; 21 | 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | public static BufferedString Parse(NativeBuffer buffer) => new(buffer); 24 | 25 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 26 | public static BufferedString Parse(void* start) => new(start); 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public static int CalculateSize(void* start) => Unsafe.Read(start); 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public string AsString() 33 | { 34 | string v = default!; 35 | Unsafe.Write(Unsafe.AsPointer(ref v), new IntPtr(Unsafe.Add(_start, IntPtr.Size * 2))); 36 | return v; 37 | } 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | public static implicit operator string(BufferedString value) => value.AsString(); 41 | 42 | public override string ToString() => AsString(); 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public static int CalculateStringSize(string? value) 46 | { 47 | var byteCount = value is null ? 0 : Encoding.Unicode.GetByteCount(value); 48 | var size = _headerByteCount + byteCount; 49 | return Math.Max(IntPtr.Size * 3 + sizeof(int), size); 50 | } 51 | 52 | private static readonly int _headerByteCount = sizeof(nint) + sizeof(nint) + sizeof(nint) + sizeof(int); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/NativeBuffering/Collections/ReadOnlyNonNullableBufferedObjectList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace NativeBuffering.Collections 5 | { 6 | public unsafe readonly struct ReadOnlyNonNullableBufferedObjectList : IReadOnlyList, IReadOnlyBufferedObject> 7 | where T : struct, IReadOnlyBufferedObject 8 | { 9 | public NativeBuffer Buffer { get; } 10 | public static ReadOnlyNonNullableBufferedObjectList DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 11 | public T this[int index] 12 | { 13 | get 14 | { 15 | if (index < 0 || index >= Count) throw new IndexOutOfRangeException(nameof(index)); 16 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 1))); 17 | return position == -1 ? T.DefaultValue : T.Parse(Buffer.CreateByIndex(position)); 18 | } 19 | } 20 | 21 | public int Count => Unsafe.Read(Buffer.Start); 22 | public ReadOnlyNonNullableBufferedObjectList(NativeBuffer buffer) => Buffer = buffer; 23 | public Enumerator GetEnumerator() => new(this); 24 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 25 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 26 | public static ReadOnlyNonNullableBufferedObjectList Parse(NativeBuffer buffer) => new(buffer); 27 | public struct Enumerator : IEnumerator 28 | { 29 | private readonly NativeBuffer _buffer; 30 | private readonly int _count; 31 | private int _index = -1; 32 | public Enumerator(ReadOnlyNonNullableBufferedObjectList collection) 33 | { 34 | _buffer = collection.Buffer; 35 | _count = collection.Count; 36 | } 37 | 38 | public T Current 39 | { 40 | get 41 | { 42 | var position = Unsafe.Read(_buffer.GetPointerByOffset(sizeof(int) * (_index + 1))); 43 | return position == -1 ? T.DefaultValue: T.Parse(_buffer.CreateByIndex(position)); 44 | } 45 | } 46 | 47 | object IEnumerator.Current => Current; 48 | public readonly void Dispose() { } 49 | public bool MoveNext() => ++_index < _count; 50 | public void Reset() => _index = -1; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NativeBuffering/Collections/ReadOnlyNonNullableUnmanagedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace NativeBuffering.Collections 5 | { 6 | public readonly unsafe struct ReadOnlyNonNullableUnmanagedList : IReadOnlyList, IReadOnlyBufferedObject> 7 | where T : unmanaged 8 | { 9 | private static readonly int _elementSize = Unsafe.SizeOf() % AlignmentCalculator.AlignmentOf() == 0 ? Unsafe.SizeOf() : Unsafe.SizeOf() + AlignmentCalculator.AlignmentOf() - Unsafe.SizeOf() % AlignmentCalculator.AlignmentOf(); 10 | public NativeBuffer Buffer { get; } 11 | public static ReadOnlyNonNullableUnmanagedList DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 12 | public readonly int Count => Unsafe.Read(Buffer.Start); 13 | public T this[int index] 14 | { 15 | get 16 | { 17 | if (index < 0 || index >= Count) throw new IndexOutOfRangeException(nameof(index)); 18 | return Unsafe.Read(Buffer.GetPointerByOffset(IntPtr.Size + index * _elementSize)); 19 | } 20 | } 21 | 22 | public readonly ref T AsRef(int index) 23 | { 24 | if (index < 0 || index >= Count) throw new IndexOutOfRangeException(nameof(index)); 25 | return ref Unsafe.AsRef(Buffer.GetPointerByOffset(IntPtr.Size + index * _elementSize)); 26 | } 27 | 28 | public ReadOnlyNonNullableUnmanagedList(NativeBuffer buffer) => Buffer = buffer; 29 | public Enumerator GetEnumerator()=> new Enumerator(this); 30 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 31 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 32 | public static ReadOnlyNonNullableUnmanagedList Parse(NativeBuffer buffer) => new(buffer); 33 | 34 | public struct Enumerator : IEnumerator 35 | { 36 | private readonly void* _start; 37 | private readonly int _count; 38 | private int _index = -1; 39 | 40 | public Enumerator(ReadOnlyNonNullableUnmanagedList list) 41 | { 42 | _start = list.Buffer.Start; 43 | _count = list.Count; 44 | } 45 | 46 | public readonly T Current => Unsafe.Read(Unsafe.Add(_start, IntPtr.Size + _elementSize * _index)); 47 | readonly object IEnumerator.Current => Current; 48 | public readonly void Dispose() { } 49 | public bool MoveNext() => ++_index < _count; 50 | public void Reset() => _index = -1; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NativeBuffering/Collections/ReadOnlyNullableBufferedObjectList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace NativeBuffering.Collections 5 | { 6 | public unsafe readonly struct ReadOnlyNullableBufferedObjectList : IReadOnlyList, IReadOnlyBufferedObject> 7 | where T : struct, IReadOnlyBufferedObject 8 | { 9 | public NativeBuffer Buffer { get; } 10 | 11 | public static ReadOnlyNullableBufferedObjectList DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 12 | public T? this[int index] 13 | { 14 | get 15 | { 16 | if (index < 0 || index >= Count) throw new IndexOutOfRangeException(nameof(index)); 17 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 1))); 18 | return position == -1 ? null: T.Parse(Buffer.CreateByIndex(position)); 19 | } 20 | } 21 | 22 | public int Count => Unsafe.Read(Buffer.Start); 23 | public ReadOnlyNullableBufferedObjectList(NativeBuffer buffer) => Buffer = buffer; 24 | public Enumerator GetEnumerator() => new Enumerator(this); 25 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 26 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 27 | public static ReadOnlyNullableBufferedObjectList Parse(NativeBuffer buffer)=> new(buffer); 28 | public struct Enumerator : IEnumerator 29 | { 30 | private readonly NativeBuffer _buffer; 31 | private readonly int _count; 32 | private int _index = -1; 33 | public Enumerator(ReadOnlyNullableBufferedObjectList collection) 34 | { 35 | _buffer = collection.Buffer; 36 | _count = collection.Count; 37 | } 38 | 39 | public T? Current 40 | { 41 | get 42 | { 43 | var position = Unsafe.Read(_buffer.GetPointerByOffset(sizeof(int) * (_index + 1))); 44 | return position == -1 ? null: T.Parse(_buffer.CreateByIndex(position)); 45 | } 46 | } 47 | 48 | object? IEnumerator.Current => Current; 49 | public readonly void Dispose() { } 50 | public bool MoveNext() => ++_index < _count; 51 | public void Reset() => _index = -1; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/NativeBuffering/Collections/ReadOnlyNullableUnmanagedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace NativeBuffering.Collections 5 | { 6 | public unsafe readonly struct ReadOnlyNullableUnmanagedList : IReadOnlyList, IReadOnlyBufferedObject> 7 | where T : unmanaged 8 | { 9 | public NativeBuffer Buffer { get; } 10 | public static ReadOnlyNullableUnmanagedList DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 11 | public T? this[int index] 12 | { 13 | get 14 | { 15 | if (index < 0 || index >= Count) throw new IndexOutOfRangeException(nameof(index)); 16 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 1))); 17 | return position == -1 ? null : Unsafe.Read(Buffer.GetPointerByIndex(position)); 18 | } 19 | } 20 | 21 | public int Count => Unsafe.Read(Buffer.Start); 22 | public ReadOnlyNullableUnmanagedList(NativeBuffer buffer) => Buffer = buffer; 23 | public Enumerator GetEnumerator() => new Enumerator(this); 24 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 25 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 26 | public static ReadOnlyNullableUnmanagedList Parse(NativeBuffer buffer) => new(buffer); 27 | public struct Enumerator : IEnumerator 28 | { 29 | private readonly NativeBuffer _buffer; 30 | private readonly int _count; 31 | private int _index = -1; 32 | public Enumerator(ReadOnlyNullableUnmanagedList collection) 33 | { 34 | _buffer = collection.Buffer; 35 | _count = collection.Count; 36 | } 37 | 38 | public T? Current 39 | { 40 | get 41 | { 42 | var position = Unsafe.Read(_buffer.GetPointerByOffset(sizeof(int) * (_index + 1))); 43 | return position == -1 ? null : Unsafe.Read(_buffer.GetPointerByIndex(position)); 44 | } 45 | } 46 | 47 | object? IEnumerator.Current => Current; 48 | public readonly void Dispose() { } 49 | public bool MoveNext() => ++_index < _count; 50 | public void Reset() => _index = -1; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/BufferedUnamanagedUnamanagedDictionartyEntry.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.NewDictionaries; 2 | using System; 3 | using System.Collections; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace NativeBuffering.Dictionaries 7 | { 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// Count(4)+Padding(4)+Key + KeyPadding + Value+ ValuePadding+... 14 | internal unsafe readonly struct BufferedUnamanagedUnamanagedDictionartyEntry : IReadOnlyBufferedObject>, IEnumerable> 15 | where TKey : unmanaged, IEquatable 16 | where TValue : unmanaged 17 | { 18 | public static BufferedUnamanagedUnamanagedDictionartyEntry DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 19 | public BufferedUnamanagedUnamanagedDictionartyEntry(NativeBuffer buffer) => Buffer = buffer; 20 | public NativeBuffer Buffer { get; } 21 | public int Count { 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | get => Unsafe.Read(Buffer.Start); 24 | } 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public static BufferedUnamanagedUnamanagedDictionartyEntry Parse(NativeBuffer buffer) => new(buffer); 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | public Enumerator GetEnumerator() => new(this); 31 | IEnumerator> IEnumerable>.GetEnumerator()=> GetEnumerator(); 32 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 33 | public bool TryGetKV(TKey key, out UnmanagedNonNullableUnmanagedPair kvPair) 34 | { 35 | if (Count == 0) 36 | { 37 | kvPair = default; 38 | return false; 39 | } 40 | for (int index = 0; index < Count; index++) 41 | { 42 | var offset = IntPtr.Size + sizeof(UnmanangedKV) * index; 43 | var kv = UnmanagedNonNullableUnmanagedPair.Parse(Buffer.CreateByOffset(offset)); 44 | if (kv.Key.Equals(key)) 45 | { 46 | kvPair = kv; 47 | return true; 48 | } 49 | } 50 | kvPair = default; 51 | return false; 52 | } 53 | 54 | internal struct Enumerator : IEnumerator> 55 | { 56 | private readonly int _count; 57 | private readonly BufferedUnamanagedUnamanagedDictionartyEntry _entry; 58 | private int _index = -1; 59 | 60 | public Enumerator(BufferedUnamanagedUnamanagedDictionartyEntry entry) 61 | { 62 | _entry = entry; 63 | _count = entry.Count; 64 | } 65 | 66 | public readonly KeyValuePair Current 67 | { 68 | get 69 | { 70 | var offset = IntPtr.Size + sizeof(UnmanangedKV) * _index; 71 | var kv = UnmanagedNonNullableUnmanagedPair.Parse(_entry.Buffer.CreateByOffset(offset)); 72 | return new KeyValuePair(kv.Key, kv.Value); 73 | } 74 | } 75 | 76 | readonly object IEnumerator.Current => Current!; 77 | public readonly void Dispose() { } 78 | public bool MoveNext() => ++_index < _count; 79 | public void Reset() => _index = -1; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/DictionaryUtilities.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace NativeBuffering 4 | { 5 | internal unsafe static class DictionaryUtilities 6 | { 7 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 8 | public static int GetEntrySlotCount(IDictionary dictionary) => Utilities.GetDictionaryEntryCount(dictionary.Count); 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | public static int GetCount(NativeBuffer buffer) => Unsafe.Read(buffer.Start); 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | public static int GetEntrySlotCount(NativeBuffer buffer) => Unsafe.Read(buffer.GetPointerByOffset(sizeof(int))); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyStringNonNullableBufferedObjectDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyStringNonNullableBufferedObjectDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TValue : struct, IReadOnlyBufferedObject 11 | { 12 | public static ReadOnlyStringNonNullableBufferedObjectDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 13 | public ReadOnlyStringNonNullableBufferedObjectDictionary(NativeBuffer buffer) => Buffer = buffer; 14 | public TValue this[string key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 15 | public NativeBuffer Buffer { get; } 16 | public IEnumerable Keys 17 | { 18 | get 19 | { 20 | if (Count == 0) 21 | { 22 | return Enumerable.Empty(); 23 | } 24 | var keys = new List(Count); 25 | for (int index = 0; index < EntrySlotCount; index++) 26 | { 27 | var kv = GetDictionaryEntry(index); 28 | keys.AddRange(kv.Select(it => it.Key)); 29 | } 30 | return keys; 31 | } 32 | } 33 | public IEnumerable Values 34 | { 35 | get 36 | { 37 | if (Count == 0) 38 | { 39 | return Enumerable.Empty(); 40 | } 41 | var values = new List(Count); 42 | for (int index = 0; index < EntrySlotCount; index++) 43 | { 44 | var kv = GetDictionaryEntry(index); 45 | values.AddRange(kv.Select(it => it.Value)); 46 | } 47 | return values; 48 | } 49 | } 50 | public int Count 51 | { 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | get => DictionaryUtilities.GetCount(Buffer); 54 | } 55 | public int EntrySlotCount 56 | { 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 59 | } 60 | public static ReadOnlyStringNonNullableBufferedObjectDictionary Parse(NativeBuffer buffer) => new(buffer); 61 | public bool ContainsKey(string key) => TryGetValue(key, out _); 62 | public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue value) 63 | { 64 | var index = key.GetDictionaryEntryIndex(EntrySlotCount); 65 | var kvs = GetDictionaryEntry(index); 66 | if (ReadOnlyStringNonNullableBufferedObjectDictionary.TryGetKV(kvs, key, out var kv)) 67 | { 68 | value = kv.Value; 69 | return true; 70 | } 71 | value = default; 72 | return false; 73 | } 74 | public Enumerator GetEnumerator() => new(this); 75 | IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); 76 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 77 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 78 | { 79 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 80 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 81 | } 82 | 83 | private static bool TryGetKV(ReadOnlyNonNullableBufferedObjectList> entry, string key, [MaybeNullWhen(false)] out StringNonNullableBufferedObjectPair kv) 84 | { 85 | for (var index = 0; index < entry.Count; index++) 86 | { 87 | var item = entry[index]; 88 | if (item.Key.Equals(key)) 89 | { 90 | kv = item; 91 | return true; 92 | } 93 | } 94 | 95 | kv = default; 96 | return false; 97 | } 98 | 99 | public struct Enumerator : IEnumerator> 100 | { 101 | private readonly ReadOnlyStringNonNullableBufferedObjectDictionary _dictionary; 102 | private KeyValuePair _current; 103 | private int _entryIndex = 0; 104 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 105 | 106 | public Enumerator(ReadOnlyStringNonNullableBufferedObjectDictionary dictionary) 107 | { 108 | _dictionary = dictionary; 109 | Reset(); 110 | } 111 | 112 | public readonly KeyValuePair Current => _current; 113 | 114 | readonly object IEnumerator.Current => Current; 115 | 116 | public readonly void Dispose() { } 117 | 118 | public bool MoveNext() 119 | { 120 | while (true) 121 | { 122 | if (_entryEnerator.MoveNext()) 123 | { 124 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 125 | return true; 126 | } 127 | if (++_entryIndex >= _dictionary.EntrySlotCount) 128 | { 129 | return false; 130 | } 131 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 132 | } 133 | } 134 | 135 | public void Reset() 136 | { 137 | _entryIndex = 0; 138 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyStringNonNullableUnmanagedDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyStringNonNullableUnmanagedDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TValue : unmanaged 11 | { 12 | public static ReadOnlyStringNonNullableUnmanagedDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 13 | public ReadOnlyStringNonNullableUnmanagedDictionary(NativeBuffer buffer) => Buffer = buffer; 14 | public TValue this[string key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 15 | 16 | public ref TValue AsRef(string key) 17 | { 18 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 19 | if (ReadOnlyStringNonNullableUnmanagedDictionary.TryGetKV(kvs, key, out var kv)) 20 | { 21 | return ref kv.Value; 22 | } 23 | throw new KeyNotFoundException(); 24 | } 25 | public NativeBuffer Buffer { get; } 26 | public IEnumerable Keys 27 | { 28 | get 29 | { 30 | if(Count == 0) 31 | { 32 | return Enumerable.Empty(); 33 | } 34 | var keys = new List(Count); 35 | for (int index = 0; index < EntrySlotCount; index++) 36 | { 37 | var kv = GetDictionaryEntry(index); 38 | keys.AddRange(kv.Select(it=>it.Key)); 39 | } 40 | return keys; 41 | } 42 | } 43 | public IEnumerable Values 44 | { 45 | get 46 | { 47 | if(Count == 0) 48 | { 49 | return Enumerable.Empty(); 50 | } 51 | var values = new List(Count); 52 | for (int index = 0; index < EntrySlotCount; index++) 53 | { 54 | var kv = GetDictionaryEntry(index); 55 | values.AddRange(kv.Select(it=>it.Value)); 56 | } 57 | return values; 58 | } 59 | } 60 | public int Count 61 | { 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | get => DictionaryUtilities.GetCount(Buffer); 64 | } 65 | public int EntrySlotCount 66 | { 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 69 | } 70 | public static ReadOnlyStringNonNullableUnmanagedDictionary Parse(NativeBuffer buffer) => new(buffer); 71 | public bool ContainsKey(string key) => TryGetValue(key, out _); 72 | 73 | 74 | public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue value) 75 | { 76 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 77 | if (ReadOnlyStringNonNullableUnmanagedDictionary.TryGetKV(kvs, key, out var kv)) 78 | { 79 | value = kv.Value; 80 | return true; 81 | } 82 | value = default; 83 | return false; 84 | } 85 | public Enumerator GetEnumerator() => new(this); 86 | IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); 87 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 88 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 89 | { 90 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 91 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 92 | } 93 | 94 | private static bool TryGetKV(ReadOnlyNonNullableBufferedObjectList> entry, string key, [MaybeNullWhen(false)] out StringNonNullableUnmanagedPair kv) 95 | { 96 | for (var index = 0; index < entry.Count; index++) 97 | { 98 | var item = entry[index]; 99 | if (item.Key.Equals(key)) 100 | { 101 | kv = item; 102 | return true; 103 | } 104 | } 105 | 106 | kv = default; 107 | return false; 108 | } 109 | 110 | public struct Enumerator : IEnumerator> 111 | { 112 | private readonly ReadOnlyStringNonNullableUnmanagedDictionary _dictionary; 113 | private KeyValuePair _current; 114 | private int _entryIndex = 0; 115 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 116 | 117 | public Enumerator(ReadOnlyStringNonNullableUnmanagedDictionary dictionary) 118 | { 119 | _dictionary = dictionary; 120 | Reset(); 121 | } 122 | 123 | public readonly KeyValuePair Current => _current; 124 | 125 | readonly object IEnumerator.Current => Current; 126 | 127 | public readonly void Dispose() { } 128 | 129 | public bool MoveNext() 130 | { 131 | while (true) 132 | { 133 | if (_entryEnerator.MoveNext()) 134 | { 135 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 136 | return true; 137 | } 138 | if (++_entryIndex >= _dictionary.EntrySlotCount) 139 | { 140 | return false; 141 | } 142 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 143 | } 144 | } 145 | 146 | public void Reset() 147 | { 148 | _entryIndex = 0; 149 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyStringNullableBufferedObjectDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyStringNullableBufferedObjectDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TValue : struct, IReadOnlyBufferedObject 11 | { 12 | public static ReadOnlyStringNullableBufferedObjectDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 13 | public ReadOnlyStringNullableBufferedObjectDictionary(NativeBuffer buffer) => Buffer = buffer; 14 | public TValue? this[string key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 15 | public NativeBuffer Buffer { get; } 16 | public IEnumerable Keys 17 | { 18 | get 19 | { 20 | if (Count == 0) 21 | { 22 | return Enumerable.Empty(); 23 | } 24 | var keys = new List(Count); 25 | for (int index = 0; index < EntrySlotCount; index++) 26 | { 27 | var kv = GetDictionaryEntry(index); 28 | keys.AddRange(kv.Select(it => it.Key)); 29 | } 30 | return keys; 31 | } 32 | } 33 | public IEnumerable Values 34 | { 35 | get 36 | { 37 | if (Count == 0) 38 | { 39 | return Enumerable.Empty(); 40 | } 41 | var values = new List(Count); 42 | for (int index = 0; index < EntrySlotCount; index++) 43 | { 44 | var kv = GetDictionaryEntry(index); 45 | values.AddRange(kv.Select(it => it.Value)); 46 | } 47 | return values; 48 | } 49 | } 50 | public int Count 51 | { 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | get => DictionaryUtilities.GetCount(Buffer); 54 | } 55 | public int EntrySlotCount 56 | { 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 59 | } 60 | public static ReadOnlyStringNullableBufferedObjectDictionary Parse(NativeBuffer buffer) => new(buffer); 61 | public bool ContainsKey(string key) => TryGetValue(key, out _); 62 | public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue? value) 63 | { 64 | var index = key.GetDictionaryEntryIndex(EntrySlotCount); 65 | var kvs = GetDictionaryEntry(index); 66 | if (ReadOnlyStringNullableBufferedObjectDictionary.TryGetKV(kvs, key, out var kv)) 67 | { 68 | value = kv.Value; 69 | return true; 70 | } 71 | value = default; 72 | return false; 73 | } 74 | public Enumerator GetEnumerator() => new(this); 75 | IEnumerator> IEnumerable>.GetEnumerator() => GetEnumerator(); 76 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 77 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 78 | { 79 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 80 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 81 | } 82 | 83 | private static bool TryGetKV(ReadOnlyNonNullableBufferedObjectList> entry, string key, [MaybeNullWhen(false)] out StringNullableBufferedObjectPair kv) 84 | { 85 | for (var index = 0; index < entry.Count; index++) 86 | { 87 | var item = entry[index]; 88 | if (item.Key.Equals(key)) 89 | { 90 | kv = item; 91 | return true; 92 | } 93 | } 94 | 95 | kv = default; 96 | return false; 97 | } 98 | 99 | public struct Enumerator : IEnumerator> 100 | { 101 | private readonly ReadOnlyStringNullableBufferedObjectDictionary _dictionary; 102 | private KeyValuePair _current; 103 | private int _entryIndex = 0; 104 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 105 | 106 | public Enumerator(ReadOnlyStringNullableBufferedObjectDictionary dictionary) 107 | { 108 | _dictionary = dictionary; 109 | Reset(); 110 | } 111 | 112 | public readonly KeyValuePair Current => _current; 113 | 114 | readonly object IEnumerator.Current => Current; 115 | 116 | public readonly void Dispose() { } 117 | 118 | public bool MoveNext() 119 | { 120 | while (true) 121 | { 122 | if (_entryEnerator.MoveNext()) 123 | { 124 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 125 | return true; 126 | } 127 | if (++_entryIndex >= _dictionary.EntrySlotCount) 128 | { 129 | return false; 130 | } 131 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 132 | } 133 | } 134 | 135 | public void Reset() 136 | { 137 | _entryIndex = 0; 138 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyStringNullableUnmanagedDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyStringNullableUnmanagedDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TValue : unmanaged 11 | { 12 | public static ReadOnlyStringNullableUnmanagedDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 13 | public ReadOnlyStringNullableUnmanagedDictionary(NativeBuffer buffer) => Buffer = buffer; 14 | public TValue? this[string key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 15 | 16 | public TValue? AsRef(string key) 17 | { 18 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 19 | if (ReadOnlyStringNullableUnmanagedDictionary.TryGetKV(kvs, key, out var kv)) 20 | { 21 | return kv.Value; 22 | } 23 | throw new KeyNotFoundException(); 24 | } 25 | public NativeBuffer Buffer { get; } 26 | public IEnumerable Keys 27 | { 28 | get 29 | { 30 | if(Count == 0) 31 | { 32 | return Enumerable.Empty(); 33 | } 34 | var keys = new List(Count); 35 | for (int index = 0; index < EntrySlotCount; index++) 36 | { 37 | var kv = GetDictionaryEntry(index); 38 | keys.AddRange(kv.Select(it=>it.Key)); 39 | } 40 | return keys; 41 | } 42 | } 43 | public IEnumerable Values 44 | { 45 | get 46 | { 47 | if(Count == 0) 48 | { 49 | return Enumerable.Empty(); 50 | } 51 | var values = new List(Count); 52 | for (int index = 0; index < EntrySlotCount; index++) 53 | { 54 | var kv = GetDictionaryEntry(index); 55 | values.AddRange(kv.Select(it=>it.Value)); 56 | } 57 | return values; 58 | } 59 | } 60 | public int Count 61 | { 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | get => DictionaryUtilities.GetCount(Buffer); 64 | } 65 | public int EntrySlotCount 66 | { 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 69 | } 70 | public static ReadOnlyStringNullableUnmanagedDictionary Parse(NativeBuffer buffer) => new(buffer); 71 | public bool ContainsKey(string key) => TryGetValue(key, out _); 72 | 73 | public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue? value) 74 | { 75 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 76 | if (ReadOnlyStringNullableUnmanagedDictionary.TryGetKV(kvs, key, out var kv)) 77 | { 78 | value = kv.Value; 79 | return true; 80 | } 81 | value = default; 82 | return false; 83 | } 84 | public Enumerator GetEnumerator() => new(this); 85 | IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); 86 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 87 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 88 | { 89 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 90 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 91 | } 92 | 93 | private static bool TryGetKV(ReadOnlyNonNullableBufferedObjectList> entry, string key, [MaybeNullWhen(false)] out StringNullableUnmanagedPair kv) 94 | { 95 | for (var index = 0; index < entry.Count; index++) 96 | { 97 | var item = entry[index]; 98 | if (item.Key.Equals(key)) 99 | { 100 | kv = item; 101 | return true; 102 | } 103 | } 104 | 105 | kv = default; 106 | return false; 107 | } 108 | 109 | public struct Enumerator : IEnumerator> 110 | { 111 | private readonly ReadOnlyStringNullableUnmanagedDictionary _dictionary; 112 | private KeyValuePair _current; 113 | private int _entryIndex = 0; 114 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 115 | 116 | public Enumerator(ReadOnlyStringNullableUnmanagedDictionary dictionary) 117 | { 118 | _dictionary = dictionary; 119 | Reset(); 120 | } 121 | 122 | public readonly KeyValuePair Current => _current; 123 | 124 | readonly object IEnumerator.Current => Current; 125 | 126 | public readonly void Dispose() { } 127 | 128 | public bool MoveNext() 129 | { 130 | while (true) 131 | { 132 | if (_entryEnerator.MoveNext()) 133 | { 134 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 135 | return true; 136 | } 137 | if (++_entryIndex >= _dictionary.EntrySlotCount) 138 | { 139 | return false; 140 | } 141 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 142 | } 143 | } 144 | 145 | public void Reset() 146 | { 147 | _entryIndex = 0; 148 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 149 | } 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyUnmanagedNonNullableBufferedObjectDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyUnmanagedNonNullableBufferedObjectDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TKey : unmanaged, IEquatable 11 | where TValue :struct, IReadOnlyBufferedObject 12 | { 13 | public static ReadOnlyUnmanagedNonNullableBufferedObjectDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 14 | public ReadOnlyUnmanagedNonNullableBufferedObjectDictionary(NativeBuffer buffer) => Buffer = buffer; 15 | public TValue this[TKey key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 16 | public NativeBuffer Buffer { get; } 17 | public IEnumerable Keys 18 | { 19 | get 20 | { 21 | if (Count == 0) 22 | { 23 | return Enumerable.Empty(); 24 | } 25 | var keys = new List(Count); 26 | for (int index = 0; index < EntrySlotCount; index++) 27 | { 28 | var kv = GetDictionaryEntry(index); 29 | keys.AddRange(kv.Select(it => it.Key)); 30 | } 31 | return keys; 32 | } 33 | } 34 | public IEnumerable Values 35 | { 36 | get 37 | { 38 | if (Count == 0) 39 | { 40 | return Enumerable.Empty(); 41 | } 42 | var values = new List(Count); 43 | for (int index = 0; index < EntrySlotCount; index++) 44 | { 45 | var kv = GetDictionaryEntry(index); 46 | values.AddRange(kv.Select(it => it.Value)); 47 | } 48 | return values; 49 | } 50 | } 51 | public int Count 52 | { 53 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 54 | get => DictionaryUtilities.GetCount(Buffer); 55 | } 56 | public int EntrySlotCount 57 | { 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 60 | } 61 | public static ReadOnlyUnmanagedNonNullableBufferedObjectDictionary Parse(NativeBuffer buffer) => new(buffer); 62 | public bool ContainsKey(TKey key) => TryGetValue(key, out _); 63 | public Enumerator GetEnumerator() => new Enumerator(this); 64 | IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); 65 | public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) 66 | { 67 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 68 | if (ReadOnlyUnmanagedNonNullableBufferedObjectDictionary.TryGetKV(kvs, key, out var kv)) 69 | { 70 | value = kv.Value; 71 | return true; 72 | } 73 | value = default; 74 | return false; 75 | } 76 | 77 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 78 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 79 | { 80 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 81 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 82 | } 83 | 84 | private static bool TryGetKV( 85 | ReadOnlyNonNullableBufferedObjectList> entry, 86 | TKey key, 87 | [MaybeNullWhen(false)] out UnmanagedNonNullableBufferedObjectPair kv) 88 | { 89 | for (var index = 0; index < entry.Count; index++) 90 | { 91 | var item = entry[index]; 92 | if (item.Key.Equals(key)) 93 | { 94 | kv = item; 95 | return true; 96 | } 97 | } 98 | 99 | kv = default; 100 | return false; 101 | } 102 | 103 | public struct Enumerator : IEnumerator> 104 | { 105 | private readonly ReadOnlyUnmanagedNonNullableBufferedObjectDictionary _dictionary; 106 | private KeyValuePair _current; 107 | private int _entryIndex = 0; 108 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 109 | 110 | public Enumerator(ReadOnlyUnmanagedNonNullableBufferedObjectDictionary dictionary) 111 | { 112 | _dictionary = dictionary; 113 | Reset(); 114 | } 115 | 116 | public readonly KeyValuePair Current => _current; 117 | 118 | readonly object IEnumerator.Current => Current; 119 | 120 | public readonly void Dispose() { } 121 | 122 | public bool MoveNext() 123 | { 124 | while (true) 125 | { 126 | if (_entryEnerator.MoveNext()) 127 | { 128 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 129 | return true; 130 | } 131 | if (++_entryIndex >= _dictionary.EntrySlotCount) 132 | { 133 | return false; 134 | } 135 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 136 | } 137 | } 138 | 139 | public void Reset() 140 | { 141 | _entryIndex = 0; 142 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 143 | } 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyUnmanagedNonNullableUnmanagedDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using System.Collections; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace NativeBuffering.Dictionaries 7 | { 8 | public unsafe readonly struct ReadOnlyUnmanagedNonNullableUnmanagedDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 9 | where TKey : unmanaged, IEquatable 10 | where TValue : unmanaged 11 | { 12 | public static ReadOnlyUnmanagedNonNullableUnmanagedDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 13 | public ReadOnlyUnmanagedNonNullableUnmanagedDictionary(NativeBuffer buffer) => Buffer = buffer; 14 | public TValue this[TKey key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 15 | public ref TValue AsRef(TKey key) 16 | { 17 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 18 | if (kvs.TryGetKV(key, out var kv)) 19 | { 20 | return ref kv.Value; 21 | } 22 | throw new KeyNotFoundException(); 23 | } 24 | public NativeBuffer Buffer { get; } 25 | 26 | public IEnumerable Keys 27 | { 28 | get 29 | { 30 | if (Count == 0) 31 | { 32 | return Enumerable.Empty(); 33 | } 34 | var keys = new List(Count); 35 | for (int index = 0; index < EntrySlotCount; index++) 36 | { 37 | var kv = GetDictionaryEntry(index); 38 | keys.AddRange(kv.Select(it => it.Key)); 39 | } 40 | return keys; 41 | } 42 | } 43 | public IEnumerable Values 44 | { 45 | get 46 | { 47 | if (Count == 0) 48 | { 49 | return Enumerable.Empty(); 50 | } 51 | var values = new List(Count); 52 | for (int index = 0; index < EntrySlotCount; index++) 53 | { 54 | var kv = GetDictionaryEntry(index); 55 | values.AddRange(kv.Select(it => it.Value)); 56 | } 57 | return values; 58 | } 59 | } 60 | public int Count 61 | { 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | get => DictionaryUtilities.GetCount(Buffer); 64 | } 65 | public int EntrySlotCount 66 | { 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 69 | } 70 | public static ReadOnlyUnmanagedNonNullableUnmanagedDictionary Parse(NativeBuffer buffer) => new(buffer); 71 | public bool ContainsKey(TKey key) => TryGetValue(key, out _); 72 | 73 | public Enumerator GetEnumerator() => new(this); 74 | IEnumerator> IEnumerable>.GetEnumerator() => GetEnumerator(); 75 | public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) 76 | { 77 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 78 | if (kvs.TryGetKV(key, out var kv)) 79 | { 80 | value = kv.Value; 81 | return true; 82 | } 83 | value = default; 84 | return false; 85 | } 86 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 87 | private BufferedUnamanagedUnamanagedDictionartyEntry GetDictionaryEntry(int index) 88 | { 89 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 90 | return position == -1 ? BufferedUnamanagedUnamanagedDictionartyEntry.DefaultValue : BufferedUnamanagedUnamanagedDictionartyEntry.Parse(Buffer.CreateByIndex(position)); 91 | } 92 | 93 | public struct Enumerator : IEnumerator> 94 | { 95 | private readonly ReadOnlyUnmanagedNonNullableUnmanagedDictionary _dictionary; 96 | private KeyValuePair _current; 97 | private int _entryIndex = 0; 98 | private BufferedUnamanagedUnamanagedDictionartyEntry.Enumerator _entryEnerator; 99 | public Enumerator(ReadOnlyUnmanagedNonNullableUnmanagedDictionary dictionary) 100 | { 101 | _dictionary = dictionary; 102 | Reset(); 103 | } 104 | public readonly KeyValuePair Current => _current; 105 | readonly object IEnumerator.Current => Current; 106 | public readonly void Dispose() { } 107 | public bool MoveNext() 108 | { 109 | while (true) 110 | { 111 | if (_entryEnerator.MoveNext()) 112 | { 113 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 114 | return true; 115 | } 116 | if (++_entryIndex >= _dictionary.EntrySlotCount) 117 | { 118 | return false; 119 | } 120 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 121 | } 122 | } 123 | 124 | public void Reset() 125 | { 126 | _entryIndex = 0; 127 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 128 | } 129 | } 130 | } 131 | internal readonly record struct UnmanangedKV(TKey Key, TValue Value) where TKey : unmanaged where TValue : unmanaged { } 132 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyUnmanagedNullableBufferedObjectDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyUnmanagedNullableBufferedObjectDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TKey : unmanaged, IEquatable 11 | where TValue :struct, IReadOnlyBufferedObject 12 | { 13 | public static ReadOnlyUnmanagedNullableBufferedObjectDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 14 | public ReadOnlyUnmanagedNullableBufferedObjectDictionary(NativeBuffer buffer) => Buffer = buffer; 15 | public TValue? this[TKey key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 16 | public NativeBuffer Buffer { get; } 17 | public IEnumerable Keys 18 | { 19 | get 20 | { 21 | if (Count == 0) 22 | { 23 | return Enumerable.Empty(); 24 | } 25 | var keys = new List(Count); 26 | for (int index = 0; index < EntrySlotCount; index++) 27 | { 28 | var kv = GetDictionaryEntry(index); 29 | keys.AddRange(kv.Select(it => it.Key)); 30 | } 31 | return keys; 32 | } 33 | } 34 | public IEnumerable Values 35 | { 36 | get 37 | { 38 | if (Count == 0) 39 | { 40 | return Enumerable.Empty(); 41 | } 42 | var values = new List(Count); 43 | for (int index = 0; index < EntrySlotCount; index++) 44 | { 45 | var kv = GetDictionaryEntry(index); 46 | values.AddRange(kv.Select(it => it.Value)); 47 | } 48 | return values; 49 | } 50 | } 51 | public int Count 52 | { 53 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 54 | get => DictionaryUtilities.GetCount(Buffer); 55 | } 56 | public int EntrySlotCount 57 | { 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 60 | } 61 | public static ReadOnlyUnmanagedNullableBufferedObjectDictionary Parse(NativeBuffer buffer) => new(buffer); 62 | public bool ContainsKey(TKey key) => TryGetValue(key, out _); 63 | public Enumerator GetEnumerator() => new Enumerator(this); 64 | IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); 65 | public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue? value) 66 | { 67 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 68 | if (ReadOnlyUnmanagedNullableBufferedObjectDictionary.TryGetKV(kvs, key, out var kv)) 69 | { 70 | value = kv.Value; 71 | return true; 72 | } 73 | value = default; 74 | return false; 75 | } 76 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 77 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 78 | { 79 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 80 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 81 | } 82 | 83 | private static bool TryGetKV( 84 | ReadOnlyNonNullableBufferedObjectList> entry, 85 | TKey key, 86 | [MaybeNullWhen(false)] out UnmanagedNullableBufferedObjectPair kv) 87 | { 88 | for (var index = 0; index < entry.Count; index++) 89 | { 90 | var item = entry[index]; 91 | if (item.Key.Equals(key)) 92 | { 93 | kv = item; 94 | return true; 95 | } 96 | } 97 | 98 | kv = default; 99 | return false; 100 | } 101 | 102 | public struct Enumerator : IEnumerator> 103 | { 104 | private readonly ReadOnlyUnmanagedNullableBufferedObjectDictionary _dictionary; 105 | private KeyValuePair _current; 106 | private int _entryIndex = 0; 107 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 108 | public Enumerator(ReadOnlyUnmanagedNullableBufferedObjectDictionary dictionary) 109 | { 110 | _dictionary = dictionary; 111 | Reset(); 112 | } 113 | 114 | public readonly KeyValuePair Current => _current; 115 | 116 | readonly object IEnumerator.Current => Current; 117 | 118 | public readonly void Dispose() { } 119 | 120 | public bool MoveNext() 121 | { 122 | while (true) 123 | { 124 | if (_entryEnerator.MoveNext()) 125 | { 126 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 127 | return true; 128 | } 129 | if (++_entryIndex >= _dictionary.EntrySlotCount) 130 | { 131 | return false; 132 | } 133 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 134 | } 135 | } 136 | 137 | public void Reset() 138 | { 139 | _entryIndex = 0; 140 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 141 | } 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /src/NativeBuffering/Dictionaries/ReadOnlyUnmanagedNullableUnmanagedDictionary.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.NewDictionaries; 3 | using System.Collections; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace NativeBuffering.Dictionaries 8 | { 9 | public unsafe readonly struct ReadOnlyUnmanagedNullableUnmanagedDictionary : IReadOnlyDictionary, IReadOnlyBufferedObject> 10 | where TKey : unmanaged, IEquatable 11 | where TValue : unmanaged 12 | { 13 | public static ReadOnlyUnmanagedNullableUnmanagedDictionary DefaultValue { get; } = new(new NativeBuffer(new byte[4])); 14 | public ReadOnlyUnmanagedNullableUnmanagedDictionary(NativeBuffer buffer) => Buffer = buffer; 15 | public TValue? this[TKey key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException(); 16 | public NativeBuffer Buffer { get; } 17 | public IEnumerable Keys 18 | { 19 | get 20 | { 21 | if (Count == 0) 22 | { 23 | return Enumerable.Empty(); 24 | } 25 | var keys = new List(Count); 26 | for (int index = 0; index < EntrySlotCount; index++) 27 | { 28 | var kv = GetDictionaryEntry(index); 29 | keys.AddRange(kv.Select(it => it.Key)); 30 | } 31 | return keys; 32 | } 33 | } 34 | public IEnumerable Values 35 | { 36 | get 37 | { 38 | if (Count == 0) 39 | { 40 | return Enumerable.Empty(); 41 | } 42 | var values = new List(Count); 43 | for (int index = 0; index < EntrySlotCount; index++) 44 | { 45 | var kv = GetDictionaryEntry(index); 46 | values.AddRange(kv.Select(it => it.Value)); 47 | } 48 | return values; 49 | } 50 | } 51 | public int Count 52 | { 53 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 54 | get => DictionaryUtilities.GetCount(Buffer); 55 | } 56 | public int EntrySlotCount 57 | { 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | get => DictionaryUtilities.GetEntrySlotCount(Buffer); 60 | } 61 | public static ReadOnlyUnmanagedNullableUnmanagedDictionary Parse(NativeBuffer buffer) => new(buffer); 62 | public bool ContainsKey(TKey key) => TryGetValue(key, out _); 63 | public Enumerator GetEnumerator() => new Enumerator(this); 64 | IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); 65 | public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue? value) 66 | { 67 | var kvs = GetDictionaryEntry(key.GetDictionaryEntryIndex(EntrySlotCount)); 68 | if (ReadOnlyUnmanagedNullableUnmanagedDictionary.TryGetKV(kvs, key, out var kv)) 69 | { 70 | value = kv.Value; 71 | return true; 72 | } 73 | value = default; 74 | return false; 75 | } 76 | 77 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 78 | private ReadOnlyNonNullableBufferedObjectList> GetDictionaryEntry(int index) 79 | { 80 | var position = Unsafe.Read(Buffer.GetPointerByOffset(sizeof(int) * (index + 2))); 81 | return position == -1 82 | ? ReadOnlyNonNullableBufferedObjectList>.DefaultValue 83 | : ReadOnlyNonNullableBufferedObjectList>.Parse(Buffer.CreateByIndex(position)); 84 | } 85 | 86 | private static bool TryGetKV( 87 | ReadOnlyNonNullableBufferedObjectList> entry, 88 | TKey key, 89 | [MaybeNullWhen(false)] out UnmanagedNullableUnmanagedPair kv) 90 | { 91 | for (var index = 0; index < entry.Count; index++) 92 | { 93 | var item = entry[index]; 94 | if (item.Key.Equals(key)) 95 | { 96 | kv = item; 97 | return true; 98 | } 99 | } 100 | 101 | kv = default; 102 | return false; 103 | } 104 | 105 | public struct Enumerator : IEnumerator> 106 | { 107 | private readonly ReadOnlyUnmanagedNullableUnmanagedDictionary _dictionary; 108 | private KeyValuePair _current; 109 | private int _entryIndex = 0; 110 | private ReadOnlyNonNullableBufferedObjectList>.Enumerator _entryEnerator; 111 | 112 | public Enumerator(ReadOnlyUnmanagedNullableUnmanagedDictionary dictionary) 113 | { 114 | _dictionary = dictionary; 115 | Reset(); 116 | } 117 | 118 | public readonly KeyValuePair Current => _current; 119 | 120 | readonly object IEnumerator.Current => Current; 121 | 122 | public readonly void Dispose() { } 123 | 124 | public bool MoveNext() 125 | { 126 | while (true) 127 | { 128 | if (_entryEnerator.MoveNext()) 129 | { 130 | _current = new KeyValuePair(_entryEnerator.Current.Key, _entryEnerator.Current.Value); 131 | return true; 132 | } 133 | if (++_entryIndex >= _dictionary.EntrySlotCount) 134 | { 135 | return false; 136 | } 137 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 138 | } 139 | } 140 | 141 | public void Reset() 142 | { 143 | _entryIndex = 0; 144 | _entryEnerator = _dictionary.GetDictionaryEntry(_entryIndex).GetEnumerator(); 145 | } 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /src/NativeBuffering/IBufferedObjectSource.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public interface IBufferedObjectSource 4 | { 5 | int CalculateSize(); 6 | void Write(BufferedObjectWriteContext context); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/NativeBuffering/IReadOnlyBufferedObject.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public interface IReadOnlyBufferedObject where T : IReadOnlyBufferedObject 4 | { 5 | static abstract T Parse(NativeBuffer buffer); 6 | static abstract T DefaultValue { get; } 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/NativeBuffering/NativeBuffer.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace NativeBuffering 4 | { 5 | public unsafe readonly struct NativeBuffer 6 | { 7 | public byte[] Bytes { get; } 8 | private readonly int _index; 9 | public void* Start 10 | { 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | get => Unsafe.AsPointer(ref Bytes[_index]); 13 | } 14 | 15 | public NativeBuffer(byte[] bytes, int index = 0) 16 | { 17 | Bytes = bytes ?? throw new ArgumentNullException(nameof(bytes)); 18 | _index = index; 19 | } 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public void* GetPointerByIndex(int index) => Unsafe.AsPointer(ref Bytes[index]); 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public void* GetPointerByOffset(int offset) => Unsafe.Add(Start, offset); 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public NativeBuffer CreateByIndex(int index) => new(Bytes, index); 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | public NativeBuffer CreateByOffset(int offset) => new(Bytes, _index + offset); 31 | 32 | public byte[] ToByteArray() 33 | { 34 | var end = new nint( Unsafe.AsPointer(ref Bytes[Bytes.Length -1])); 35 | var count = end - new nint(Start) + 1; 36 | var result = new byte[count]; 37 | Unsafe.CopyBlockUnaligned(ref result[0], ref Bytes[Bytes.Length - count], (uint)count); 38 | return result; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/NativeBuffering/NativeBuffering.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | preview 8 | true 9 | True 10 | 0.1.5 11 | True 12 | 0.1.5 13 | Improve string based serialization performance. 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/NativeBuffering/PooledList.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.ObjectPool; 2 | using System.Buffers; 3 | 4 | namespace NativeBuffering 5 | { 6 | internal sealed class PooledList 7 | { 8 | private static readonly ObjectPool> _pool = new DefaultObjectPoolProvider().Create>(new PooledListPolicy()); 9 | private T[] _array = null!; 10 | private int _index = 0; 11 | 12 | public int Count => _index; 13 | private PooledList() { } 14 | 15 | public void Initialize(int count) 16 | { 17 | _array = ArrayPool.Shared.Rent(count); 18 | _index = 0; 19 | } 20 | 21 | public void Release() 22 | { 23 | _index = 0; 24 | if (_array is not null) 25 | { 26 | ArrayPool.Shared.Return(_array); 27 | } 28 | } 29 | 30 | public void Add(T item) 31 | { 32 | if (_index < _array.Length) 33 | { 34 | _array[_index++] = item; 35 | return; 36 | } 37 | 38 | var array = ArrayPool.Shared.Rent(_array.Length * 2); 39 | _array.CopyTo(array, 0); 40 | ArrayPool.Shared.Return(_array, clearArray: true); 41 | _array = array; 42 | _array[_index++] = item; 43 | } 44 | 45 | public ArraySegment ToArraySegment() => new(_array, 0, _index); 46 | 47 | public static PooledList Rent(int capacity) 48 | { 49 | var list = _pool.Get(); 50 | list.Initialize(capacity); 51 | return list; 52 | } 53 | 54 | public void Return() => _pool.Return(this); 55 | 56 | private sealed class PooledListPolicy: IPooledObjectPolicy> 57 | { 58 | public PooledList Create() => new(); 59 | public bool Return(PooledList obj) { obj.Release(); return true; } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/NativeBuffering/extensions/AlignmentCalculator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Reflection; 3 | 4 | namespace NativeBuffering 5 | { 6 | internal static class AlignmentCalculator 7 | { 8 | private static readonly ConcurrentDictionary _alignmentCache = new(); 9 | public static int AlignmentOf() where T : unmanaged => CalculateAlignment(typeof(T)); 10 | private static int CalculateAlignment(Type type)=> _alignmentCache.GetOrAdd(type, CalculateAlignmentCore); 11 | private static int CalculateAlignmentCore(Type type) 12 | { 13 | if (type.IsPrimitive) 14 | { 15 | return type == typeof(bool) ? 1 : type == typeof(double) || type == typeof(long) || type == typeof(ulong) ? 8 : 4; 16 | } 17 | else 18 | { 19 | var maxAlignment = 0; 20 | foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) 21 | { 22 | var fieldAlignment = CalculateAlignment(field.FieldType); 23 | if (fieldAlignment > maxAlignment) 24 | { 25 | maxAlignment = fieldAlignment; 26 | } 27 | } 28 | return maxAlignment; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/NativeBuffering/extensions/BufferedMessageWriteContextScopeCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public static class BufferedMessageWriteContextScopeCollectionExtensions 4 | { 5 | #region WriteUnmanagedCollectionField 6 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, IEnumerable value) where T : unmanaged 7 | => scope.WriteField(value, (c, v) => c.WriteNonNullableUnmanagedCollection(v), IntPtr.Size); 8 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, IList value) where T : unmanaged 9 | => scope.WriteField(value, (c, v) => c.WriteNonNullableUnmanagedCollection(v), IntPtr.Size); 10 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, ICollection value) where T : unmanaged 11 | => scope.WriteField(value, (c, v) => c.WriteNonNullableUnmanagedCollection(v), IntPtr.Size); 12 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, T[] value) where T : unmanaged 13 | => scope.WriteField(value, (c, v) => c.WriteNonNullableUnmanagedCollection(v), IntPtr.Size); 14 | #endregion 15 | 16 | #region WriteUnmanagedCollectionField 17 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, IEnumerable value) where T : unmanaged 18 | => scope.WriteField(value, (c, v) => c.WriteNullableUnmanagedCollection(v), IntPtr.Size); 19 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, IList value) where T : unmanaged 20 | => scope.WriteField(value, (c, v) => c.WriteNullableUnmanagedCollection(v), IntPtr.Size); 21 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, ICollection value) where T : unmanaged 22 | => scope.WriteField(value, (c, v) => c.WriteNullableUnmanagedCollection(v), IntPtr.Size); 23 | public static void WriteUnmanagedCollectionField(this BufferedObjectWriteContextScope scope, T?[] value) where T : unmanaged 24 | => scope.WriteField(value, (c, v) => c.WriteNullableUnmanagedCollection(v), IntPtr.Size); 25 | #endregion 26 | 27 | #region WriteStringCollectionField 28 | public static void WriteStringCollectionField(this BufferedObjectWriteContextScope scope, IEnumerable value) 29 | => scope.WriteField(value, (c, v) => c.WriteStringCollection(v), IntPtr.Size); 30 | public static void WriteStringCollectionField(this BufferedObjectWriteContextScope scope, IList value) 31 | => scope.WriteField(value, (c, v) => c.WriteStringCollection(v), IntPtr.Size); 32 | public static void WriteStringCollectionField(this BufferedObjectWriteContextScope scope, ICollection value) 33 | => scope.WriteField(value, (c, v) => c.WriteStringCollection(v), IntPtr.Size); 34 | public static void WriteStringCollectionField(this BufferedObjectWriteContextScope scope, string[] value) 35 | => scope.WriteField(value, (c, v) => c.WriteStringCollection(v), IntPtr.Size); 36 | #endregion 37 | 38 | #region WriteBinaryCollectionField 39 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, IEnumerable value) 40 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 41 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, IList value) 42 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 43 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, ICollection value) 44 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 45 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, byte[][] value) 46 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 47 | 48 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, IEnumerable> value) 49 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 50 | 51 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, IList> value) 52 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 53 | 54 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, ICollection> value) 55 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 56 | 57 | public static void WriteBinaryCollectionField(this BufferedObjectWriteContextScope scope, Memory[] value) 58 | => scope.WriteField(value, (c, v) => c.WriteBinaryCollection(v), IntPtr.Size); 59 | #endregion 60 | 61 | #region WriteBufferedObjectCollectionField 62 | public static void WriteBufferedObjectCollectionField(this BufferedObjectWriteContextScope scope, IEnumerable value) where T : IBufferedObjectSource 63 | => scope.WriteField(value, (c, v) => c.WriteBufferedObjectCollection(v), IntPtr.Size); 64 | public static void WriteBufferedObjectCollectionField(this BufferedObjectWriteContextScope scope, IList value) where T : IBufferedObjectSource 65 | => scope.WriteField(value, (c, v) => c.WriteBufferedObjectCollection(v), IntPtr.Size); 66 | public static void WriteBufferedObjectCollectionField(this BufferedObjectWriteContextScope scope, ICollection value) where T : IBufferedObjectSource 67 | => scope.WriteField(value, (c, v) => c.WriteBufferedObjectCollection(v), IntPtr.Size); 68 | public static void WriteBufferedObjectCollectionField(this BufferedObjectWriteContextScope scope, T[] value) where T : IBufferedObjectSource 69 | => scope.WriteField(value, (c, v) => c.WriteBufferedObjectCollection(v), IntPtr.Size); 70 | #endregion 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/NativeBuffering/extensions/BufferedMessageWriteContextScopeDictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering 2 | { 3 | public static class BufferedMessageWriteContextScopeDictionaryExtensions 4 | { 5 | #region IDictionary 6 | public static void WriteUnmanagedNonNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 7 | where TKey : unmanaged, IComparable 8 | where TValue : unmanaged 9 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedNonNullableUnmanagedDictionary(v), IntPtr.Size); 10 | 11 | public static void WriteUnmanagedNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 12 | where TKey : unmanaged, IComparable 13 | where TValue : unmanaged 14 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedNullableUnmanagedDictionary(v), IntPtr.Size); 15 | 16 | public static void WriteUnmanagedStringDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 17 | where TKey : unmanaged, IComparable 18 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedStringDictionary(v), IntPtr.Size); 19 | 20 | public static void WriteUnmanagedBinaryDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 21 | where TKey : unmanaged, IComparable 22 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedBinaryDictionary(v), IntPtr.Size); 23 | 24 | public static void WriteUnmanagedBinaryDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary> dictionary) 25 | where TKey : unmanaged, IComparable 26 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedBinaryDictionary(v), IntPtr.Size); 27 | 28 | public static void WriteUnmanagedBufferedObjectDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 29 | where TKey : unmanaged, IComparable 30 | where TValue : IBufferedObjectSource 31 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedBufferedObjectDictionary(v), IntPtr.Size); 32 | 33 | public static void WriteStringNonNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 34 | where TValue : unmanaged 35 | => scope.WriteField(dictionary, (c, v) => c.WriteStringNonNullableUnmanagedDictionary(v), IntPtr.Size); 36 | 37 | public static void WriteStringNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 38 | where TValue : unmanaged 39 | => scope.WriteField(dictionary, (c, v) => c.WriteStringNullableUnmanagedDictionary(v), IntPtr.Size); 40 | 41 | public static void WriteStringStringDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 42 | => scope.WriteField(dictionary, (c, v) => c.WriteStringStringDictionary(v), IntPtr.Size); 43 | 44 | public static void WriteStringBinaryDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 45 | => scope.WriteField(dictionary, (c, v) => c.WriteStringBinaryDictionary(v), IntPtr.Size); 46 | 47 | public static void WriteStringBinaryDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary> dictionary) 48 | => scope.WriteField(dictionary, (c, v) => c.WriteStringBinaryDictionary(v), IntPtr.Size); 49 | 50 | public static void WriteStringBufferedObjectDictionaryField(this BufferedObjectWriteContextScope scope, IDictionary dictionary) 51 | where TValue : IBufferedObjectSource 52 | => scope.WriteField(dictionary, (c, v) => c.WriteStringBufferedObjectDictionary(v), IntPtr.Size); 53 | #endregion 54 | 55 | #region Dictionary 56 | public static void WriteUnmanagedNonNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 57 | where TKey : unmanaged, IComparable 58 | where TValue : unmanaged 59 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedNonNullableUnmanagedDictionary(v), IntPtr.Size); 60 | 61 | public static void WriteUnmanagedNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 62 | where TKey : unmanaged, IComparable 63 | where TValue : unmanaged 64 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedNullableUnmanagedDictionary(v), IntPtr.Size); 65 | 66 | public static void WriteUnmanagedStringDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 67 | where TKey : unmanaged, IComparable 68 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedStringDictionary(v), IntPtr.Size); 69 | 70 | public static void WriteUnmanagedBinaryDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 71 | where TKey : unmanaged, IComparable 72 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedBinaryDictionary(v), IntPtr.Size); 73 | 74 | public static void WriteUnmanagedBinaryDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary> dictionary) 75 | where TKey : unmanaged, IComparable 76 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedBinaryDictionary(v), IntPtr.Size); 77 | 78 | public static void WriteUnmanagedBufferedObjectDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 79 | where TKey : unmanaged, IComparable 80 | where TValue : IBufferedObjectSource 81 | => scope.WriteField(dictionary, (c, v) => c.WriteUnmanagedBufferedObjectDictionary(v), IntPtr.Size); 82 | 83 | public static void WriteStringNonNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 84 | where TValue : unmanaged 85 | => scope.WriteField(dictionary, (c, v) => c.WriteStringNonNullableUnmanagedDictionary(v), IntPtr.Size); 86 | 87 | public static void WriteStringNullableUnmanagedDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 88 | where TValue : unmanaged 89 | => scope.WriteField(dictionary, (c, v) => c.WriteStringNullableUnmanagedDictionary(v), IntPtr.Size); 90 | 91 | public static void WriteStringStringDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 92 | => scope.WriteField(dictionary, (c, v) => c.WriteStringStringDictionary(v), IntPtr.Size); 93 | 94 | public static void WriteStringBinaryDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 95 | => scope.WriteField(dictionary, (c, v) => c.WriteStringBinaryDictionary(v), IntPtr.Size); 96 | 97 | public static void WriteStringBinaryDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary> dictionary) 98 | => scope.WriteField(dictionary, (c, v) => c.WriteStringBinaryDictionary(v), IntPtr.Size); 99 | 100 | public static void WriteStringBufferedObjectDictionaryField(this BufferedObjectWriteContextScope scope, Dictionary dictionary) 101 | where TValue : IBufferedObjectSource 102 | => scope.WriteField(dictionary, (c, v) => c.WriteStringBufferedObjectDictionary(v), IntPtr.Size); 103 | #endregion 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/NativeBuffering/extensions/BufferedObjectSourceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | 3 | namespace NativeBuffering 4 | { 5 | public static class BufferedObjectSourceExtensions 6 | { 7 | public static ArraySegment WriteTo(this IBufferedObjectSource bufferedObjectSource, Func bufferProvider) 8 | { 9 | var size = bufferedObjectSource.CalculateSize(); 10 | var buffer = bufferProvider(size); 11 | var context = BufferedObjectWriteContext.Acquire(buffer); 12 | bufferedObjectSource.Write(context); 13 | var result = new ArraySegment(buffer, 0, size); 14 | BufferedObjectWriteContext.Release(context); 15 | return result; 16 | } 17 | 18 | public static async Task WriteToAsync(this IBufferedObjectSource bufferedObjectSource, Stream stream, bool closeStream = false) 19 | { 20 | var size = bufferedObjectSource.CalculateSize(); 21 | var bytes = ArrayPool.Shared.Rent(size); 22 | var context = BufferedObjectWriteContext.Acquire(bytes); 23 | try 24 | { 25 | bufferedObjectSource.Write(context); 26 | await stream.WriteAsync(bytes.AsMemory(0, size)); 27 | } 28 | finally 29 | { 30 | BufferedObjectWriteContext.Release(context); 31 | ArrayPool.Shared.Return(bytes); 32 | if (closeStream) stream.Close(); 33 | } 34 | } 35 | 36 | public static Task WriteToAsync(this IBufferedObjectSource bufferedObjectSource, string filePath) 37 | => bufferedObjectSource.WriteToAsync(new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write), true); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/NativeBuffering/extensions/BufferedObjectWriteContextScopeExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.ObjectPool; 2 | 3 | namespace NativeBuffering 4 | { 5 | public static class BufferedObjectWriteContextScopeExtensions 6 | { 7 | private static readonly ObjectPool _pool = new DefaultObjectPoolProvider().Create(); 8 | public static BufferedObjectWriteContextScope GetWriteContextScope(this BufferedObjectWriteContext writeContext, int fieldCount) 9 | { 10 | var scope = _pool.Get(); 11 | scope.Initialize(writeContext); 12 | writeContext.Advance(sizeof(int) * fieldCount); 13 | return scope; 14 | } 15 | 16 | public static void ReleaseWriteContextScope(this BufferedObjectWriteContext writeContext, BufferedObjectWriteContextScope writeContextScope) 17 | { 18 | writeContextScope.Release(); 19 | _pool.Return(writeContextScope); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/NativeBuffering/extensions/NativeBufferReadFieldExtensions.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Collections; 2 | using NativeBuffering.Dictionaries; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace NativeBuffering 6 | { 7 | public unsafe static class NativeBufferReadFieldExtensions 8 | { 9 | #region Unmanaged 10 | public static T ReadUnmanagedField(this NativeBuffer buffer, int index) where T : unmanaged 11 | { 12 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 13 | return Unsafe.Read(buffer.GetPointerByIndex(position)); 14 | } 15 | 16 | public static T? ReadNullableUnmanagedField(this NativeBuffer buffer, int index) where T : unmanaged 17 | { 18 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 19 | return position == -1 ? null : Unsafe.Read(buffer.GetPointerByIndex(position)); 20 | } 21 | 22 | public static ref T ReadUnmanagedFieldAsRef(this NativeBuffer buffer, int index) where T : unmanaged 23 | { 24 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 25 | return ref Unsafe.AsRef(buffer.GetPointerByIndex(position)); 26 | } 27 | #endregion 28 | 29 | #region BufferedObject 30 | public static T ReadNonNullableBufferedObjectField(this NativeBuffer buffer, int index) where T : struct, IReadOnlyBufferedObject 31 | { 32 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 33 | return position == -1 ? T.DefaultValue: T.Parse(buffer.CreateByIndex(position)); 34 | } 35 | 36 | public static T? ReadNullableBufferedObjectField(this NativeBuffer buffer, int index) where T : struct, IReadOnlyBufferedObject 37 | { 38 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 39 | return position == -1 ? null : T.Parse(buffer.CreateByIndex(position)); 40 | } 41 | #endregion 42 | 43 | #region Collection 44 | public static ReadOnlyNonNullableUnmanagedList ReadNonNullableUnmanagedCollectionField(this NativeBuffer buffer, int index) where T : unmanaged 45 | { 46 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 47 | return new ReadOnlyNonNullableUnmanagedList(buffer.CreateByIndex(position)); 48 | } 49 | 50 | public static ReadOnlyNullableUnmanagedList ReadNullableUnmanagedCollectionField(this NativeBuffer buffer, int index) where T : unmanaged 51 | { 52 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 53 | return new ReadOnlyNullableUnmanagedList(buffer.CreateByIndex(position)); 54 | } 55 | 56 | public static ReadOnlyNonNullableBufferedObjectList ReadNonNullableBufferedObjectCollectionField(this NativeBuffer buffer, int index) where T : struct, IReadOnlyBufferedObject 57 | { 58 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 59 | return position == -1 ? ReadOnlyNonNullableBufferedObjectList.DefaultValue : new ReadOnlyNonNullableBufferedObjectList(buffer.CreateByIndex(position)); 60 | } 61 | 62 | public static ReadOnlyNullableBufferedObjectList ReadNullableBufferedObjectCollectionField(this NativeBuffer buffer, int index) where T : struct, IReadOnlyBufferedObject 63 | { 64 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 65 | return position == -1 ? ReadOnlyNullableBufferedObjectList.DefaultValue : new ReadOnlyNullableBufferedObjectList(buffer.CreateByIndex(position)); 66 | } 67 | #endregion\ 68 | 69 | #region Dictionary 70 | public static ReadOnlyUnmanagedNonNullableUnmanagedDictionary ReadUnmanagedNonNullableUnmanagedDictionaryField(this NativeBuffer buffer, int index) 71 | where TKey : unmanaged, IEquatable 72 | where TValue : unmanaged 73 | { 74 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 75 | return new ReadOnlyUnmanagedNonNullableUnmanagedDictionary(buffer.CreateByIndex(position)); 76 | } 77 | 78 | public static ReadOnlyUnmanagedNullableUnmanagedDictionary ReadUnmanagedNullableUnmanagedDictionaryField(this NativeBuffer buffer, int index) 79 | where TKey : unmanaged, IEquatable 80 | where TValue : unmanaged 81 | { 82 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 83 | return new ReadOnlyUnmanagedNullableUnmanagedDictionary(buffer.CreateByIndex(position)); 84 | } 85 | 86 | public static ReadOnlyUnmanagedNonNullableBufferedObjectDictionary ReadUnmanagedNonNullableBufferedObjectDictionaryField(this NativeBuffer buffer, int index) 87 | where TKey : unmanaged, IEquatable 88 | where TValue :struct, IReadOnlyBufferedObject 89 | { 90 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 91 | return new ReadOnlyUnmanagedNonNullableBufferedObjectDictionary(buffer.CreateByIndex(position)); 92 | } 93 | 94 | public static ReadOnlyUnmanagedNullableBufferedObjectDictionary ReadUnmanagedNullableBufferedObjectDictionaryField(this NativeBuffer buffer, int index) 95 | where TKey : unmanaged, IEquatable 96 | where TValue : struct, IReadOnlyBufferedObject 97 | { 98 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 99 | return new ReadOnlyUnmanagedNullableBufferedObjectDictionary(buffer.CreateByIndex(position)); 100 | } 101 | 102 | public static ReadOnlyStringNonNullableUnmanagedDictionary ReadStringNonNullableUnmanagedDictionaryField(this NativeBuffer buffer, int index) 103 | where TValue : unmanaged 104 | { 105 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 106 | return new ReadOnlyStringNonNullableUnmanagedDictionary(buffer.CreateByIndex(position)); 107 | } 108 | 109 | public static ReadOnlyStringNullableUnmanagedDictionary ReadStringNullableUnmanagedDictionaryField(this NativeBuffer buffer, int index) 110 | where TValue : unmanaged 111 | { 112 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 113 | return new ReadOnlyStringNullableUnmanagedDictionary(buffer.CreateByIndex(position)); 114 | } 115 | 116 | public static ReadOnlyStringNonNullableBufferedObjectDictionary ReadStringNonNullableBufferedObjectDictionaryField(this NativeBuffer buffer, int index) 117 | where TValue : struct, IReadOnlyBufferedObject 118 | { 119 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 120 | return new ReadOnlyStringNonNullableBufferedObjectDictionary(buffer.CreateByIndex(position)); 121 | } 122 | public static ReadOnlyStringNullableBufferedObjectDictionary ReadStringNullableBufferedObjectDictionaryField(this NativeBuffer buffer, int index) 123 | where TValue : struct, IReadOnlyBufferedObject 124 | { 125 | var position = Unsafe.Read(buffer.GetPointerByOffset(sizeof(int) * index)); 126 | return new ReadOnlyStringNullableBufferedObjectDictionary(buffer.CreateByIndex(position)); 127 | } 128 | #endregion 129 | } 130 | } -------------------------------------------------------------------------------- /src/NativeBuffering/internals/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace NativeBuffering 4 | { 5 | public unsafe static partial class Utilities 6 | { 7 | 8 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 9 | internal static int GetDictionaryEntryIndex(this T value, int entrySlotCount)=>(int)((uint)value!.GetHashCode() % entrySlotCount); 10 | 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | internal static int GetDictionaryEntryCount(int count) 13 | { 14 | if (count < 0) 15 | { 16 | throw new ArgumentException(message: "Value must be greater than or equal to 0.", paramName: nameof(count)); 17 | } 18 | int[] array = _primes; 19 | foreach (int num in array) 20 | { 21 | if (num >= count) 22 | { 23 | return num; 24 | } 25 | } 26 | for (int j = count | 1; j < int.MaxValue; j += 2) 27 | { 28 | if (IsPrime(j) && (j - 1) % 101 != 0) 29 | { 30 | return j; 31 | } 32 | } 33 | return count; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | private static bool IsPrime(int candidate) 38 | { 39 | if (((uint)candidate & (true ? 1u : 0u)) != 0) 40 | { 41 | int num = (int)Math.Sqrt(candidate); 42 | for (int i = 3; i <= num; i += 2) 43 | { 44 | if (candidate % i == 0) 45 | { 46 | return false; 47 | } 48 | } 49 | return true; 50 | } 51 | return candidate == 2; 52 | } 53 | 54 | private static readonly int[] _primes = new int[72] 55 | { 56 | 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 57 | 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 58 | 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 59 | 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023, 60 | 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 61 | 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 62 | 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 63 | 5999471, 7199369 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/NativeBuffering/internals/Utilities1.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Text; 4 | 5 | namespace NativeBuffering 6 | { 7 | public unsafe static partial class Utilities 8 | { 9 | public static bool IsUnmanaged() => IsUnmanaged(typeof(T)); 10 | public static bool IsUnmanaged(this Type type) 11 | { 12 | if(!type.IsValueType) return false; 13 | if (type.IsPrimitive || type.IsPointer || type.IsEnum) return true; 14 | if (type.IsGenericType || !type.IsValueType) return true; 15 | return type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).All(x => x.FieldType.IsUnmanaged()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/NativeBuffering/internals/Utilities2.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Runtime.CompilerServices; 3 | using System.Text; 4 | 5 | namespace NativeBuffering 6 | { 7 | public unsafe static partial class Utilities 8 | { 9 | 10 | #region Scalar 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | public static int CalculateUnmanagedFieldSize(T _) where T : unmanaged => CalculateUnmanagedSize() + sizeof(int); 13 | public static int CalculateUnmanagedSize() where T : unmanaged => sizeof(T); 14 | 15 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 16 | public static int CalculateStringFieldSize(string? value) => CalculateStringSize(value) + sizeof(int); 17 | public static int CalculateStringSize(string? value) 18 | { 19 | var byteCount = value is null?0: Encoding.Unicode.GetByteCount(value); 20 | 21 | var size = sizeof(int) 22 | + sizeof(nint) 23 | + sizeof(nint) 24 | + sizeof(int) 25 | + byteCount; 26 | return Math.Max(IntPtr.Size * 3 + sizeof(int), size); 27 | } 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | public static int CalculateBinaryFieldSize(IEnumerable? value) => CalculateBinarySize(value) + sizeof(int); 31 | public static int CalculateBinarySize(IEnumerable? value) => sizeof(int) + value?.Count() ?? 0; 32 | 33 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 34 | public static int CalculateBufferedObjectFieldSize(T value) where T : IBufferedObjectSource => value.CalculateSize() + sizeof(int); 35 | #endregion 36 | 37 | #region Collection 38 | public static int CalculateCollectionFieldSize(IEnumerable? value)=> CalculateCollectionSize(value) + sizeof(int); 39 | public static int CalculateCollectionSize(IEnumerable? value) 40 | { 41 | value = value ?? Array.Empty(); 42 | var count = value.Count(); 43 | var lengthBytes = sizeof(int); 44 | var referenceBytes = sizeof(int) * count; 45 | 46 | if (typeof(T).IsUnmanaged()) 47 | { 48 | return lengthBytes + value.Sum(_ => Unsafe.SizeOf()); 49 | } 50 | if (typeof(T) == typeof(string)) 51 | { 52 | return lengthBytes + referenceBytes + value.Sum(it => CalculateStringSize(it as string)); 53 | } 54 | if (typeof(IEnumerable).IsAssignableFrom(typeof(T))) 55 | { 56 | return lengthBytes + referenceBytes + value.Sum(it => CalculateBinarySize(it as IEnumerable)); 57 | } 58 | if (typeof(IBufferedObjectSource).IsAssignableFrom(typeof(T))) 59 | { 60 | if (value.Any(it => it is null)) 61 | { 62 | throw new ArgumentException($"Specified collection contains null values.", nameof(value)); 63 | } 64 | return lengthBytes + referenceBytes + value.Sum(it => ((IBufferedObjectSource)it!).CalculateSize()); 65 | } 66 | throw new ArgumentException($"Unsupported collection element type {typeof(T).Name}.", nameof(value)); 67 | } 68 | #endregion 69 | 70 | #region Dictionary 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public static int CalculateDictionaryFieldSize(IDictionary value) => CalculateDictionarySize(value) + sizeof(int); 73 | public static int CalculateDictionarySize(IDictionary value) 74 | { 75 | if(value is null) throw new ArgumentNullException(nameof(value)); 76 | var count = value.Count(); 77 | var lengthBytes = sizeof(int); 78 | var referenceBytes = sizeof(int) * count; 79 | 80 | if (typeof(TKey).IsUnmanaged()) 81 | { 82 | if (typeof(TValue).IsUnmanaged()) 83 | { 84 | return lengthBytes + value.Sum(kv => Unsafe.SizeOf() + Unsafe.SizeOf()); 85 | } 86 | if (typeof(TValue) == typeof(string)) 87 | { 88 | return lengthBytes + referenceBytes + value.Sum(kv => Unsafe.SizeOf() + CalculateStringSize(kv.Value as string)); 89 | } 90 | if (typeof(IEnumerable).IsAssignableFrom(typeof(TValue))) 91 | { 92 | return lengthBytes + referenceBytes + value.Sum(kv => Unsafe.SizeOf() + CalculateBinarySize(kv.Value as IEnumerable)); 93 | } 94 | if (typeof(IBufferedObjectSource).IsAssignableFrom(typeof(TValue))) 95 | { 96 | if (value.Any(kv => kv.Value is null)) 97 | { 98 | throw new ArgumentException($"Specified dictionary contains null values.", nameof(value)); 99 | } 100 | return lengthBytes + referenceBytes + value.Sum(kv => Unsafe.SizeOf() + ((IBufferedObjectSource)kv.Value!).CalculateSize()); 101 | } 102 | throw new ArgumentException($"Unsupported dictionary value type {typeof(TValue).Name}.", nameof(value)); 103 | } 104 | 105 | if (typeof(TKey) == typeof(string)) 106 | { 107 | if (typeof(TValue).IsUnmanaged()) 108 | { 109 | return lengthBytes + referenceBytes + value.Sum(kv => CalculateStringSize(kv.Key as string) + Unsafe.SizeOf()); 110 | } 111 | if (typeof(TValue) == typeof(string)) 112 | { 113 | return lengthBytes + referenceBytes + value.Sum(kv => CalculateStringSize(kv.Key as string) + CalculateStringSize(kv.Value as string)); 114 | } 115 | if (typeof(IEnumerable).IsAssignableFrom(typeof(TValue))) 116 | { 117 | return lengthBytes + referenceBytes + value.Sum(kv => CalculateStringSize(kv.Key as string) + CalculateBinarySize(kv.Value as IEnumerable)); 118 | } 119 | if (typeof(IBufferedObjectSource).IsAssignableFrom(typeof(TValue))) 120 | { 121 | if (value.Any(kv => kv.Value is null)) 122 | { 123 | throw new ArgumentException($"Specified dictionary contains null values.", nameof(value)); 124 | } 125 | return lengthBytes + referenceBytes + value.Sum(kv => CalculateStringSize(kv.Key as string) + ((IBufferedObjectSource)kv.Value!).CalculateSize()); 126 | } 127 | throw new ArgumentException($"Unsupported type {typeof(TValue).Name}.", nameof(value)); 128 | } 129 | 130 | throw new ArgumentException($"Unsupported dictionary key type {typeof(TKey).Name}.", nameof(value)); 131 | } 132 | #endregion 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/BinaryCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class BinaryCollectionFixture 4 | { 5 | [Fact] 6 | public void GetValue() 7 | { 8 | var source = new Source(new List { null, new byte[] { 2, 2, 2 }, Array.Empty()}); 9 | var buffer = new byte[source.CalculateSize()]; 10 | var context = BufferedObjectWriteContext.Acquire(buffer); 11 | source.Write(context); 12 | using var pooledMessage = source.AsBufferedMessage(); 13 | var message = pooledMessage.BufferedMessage; 14 | Assert.Equal(3, message.Value.Count); 15 | 16 | Assert.True(message.Value[0]!.Length == 0); 17 | Assert.True(message.Value[0]!.AsSpan().Length == 0); 18 | Assert.True(message.Value[1]!.AsSpan().ToArray().All(it => it == 2)); 19 | Assert.True(message.Value[2]!.Length == 0); 20 | Assert.True(message.Value[2]!.AsSpan().Length == 0); 21 | BufferedObjectWriteContext.Release(context); 22 | } 23 | 24 | [BufferedMessageSource] 25 | public partial class Source 26 | { 27 | public Source(List value) 28 | { 29 | Value = value; 30 | } 31 | 32 | public IList Value { get; } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/BufferPoolFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public class BufferPoolFixture 4 | { 5 | [Fact] 6 | public void Rent() 7 | { 8 | var owner = BufferPool.Rent(100); 9 | Assert.True(owner.Bytes.Length >= 100); 10 | 11 | var bytes1 = owner.Bytes; 12 | owner.Dispose(); 13 | 14 | var bytes2 = BufferPool.Rent(101).Bytes; 15 | Assert.Same(bytes1, bytes2); 16 | 17 | var bytes3 = BufferPool.Rent(101).Bytes; 18 | Assert.NotSame(bytes1, bytes3); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NativeBufferFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NativeBuffering.Test 8 | { 9 | public class NativeBufferFixture 10 | { 11 | [Fact] 12 | public void ToByteArray() 13 | { 14 | var bytes = new byte[byte.MaxValue + 1]; 15 | for (int i = byte.MinValue; i <= byte.MaxValue; i++) 16 | { 17 | bytes[i] = (byte)i; 18 | } 19 | var buffer = new NativeBuffer(bytes,10); 20 | var array = buffer.ToByteArray(); 21 | Assert.Equal(246, array.Length); 22 | for (int index = 0; index < array.Length; index++) 23 | { 24 | Assert.Equal(index + 10, array[index]); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NativeBuffering.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | true 11 | True 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | all 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NonNullableBufferedObjectCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class NonNullableBufferedObjectCollectionFixture 4 | { 5 | [Fact] 6 | public void GetValue() 7 | { 8 | var source = new Source(new Foobar[] { new Foobar(1, 1), new Foobar(2, 2), new Foobar(3, 3) }); 9 | using (var pooledMessage = source.AsBufferedMessage()) 10 | { 11 | var message = pooledMessage.BufferedMessage; 12 | 13 | Assert.Equal(3, message.Value.Count); 14 | Assert.Equal(3, message.Value.Count()); 15 | Assert.Equal(1, message.Value[0]!.Foo); 16 | Assert.Equal(2, message.Value[1]!.Foo); 17 | Assert.Equal(3, message.Value[2]!.Foo); 18 | Assert.Equal(1, message.Value[0]!.Bar); 19 | Assert.Equal(2, message.Value[1]!.Bar); 20 | Assert.Equal(3, message.Value[2]!.Bar); 21 | 22 | var index = 0; 23 | for (; index < 3; index++) 24 | { 25 | Assert.Equal(index + 1, message.Value[index]!.Foo); 26 | Assert.Equal(index + 1, message.Value[index]!.Bar); 27 | } 28 | 29 | index = 0; 30 | foreach (var item in message.Value) 31 | { 32 | index++; 33 | Assert.Equal(index, item!.Foo); 34 | Assert.Equal(index, item!.Bar); 35 | } 36 | } 37 | } 38 | 39 | 40 | [BufferedMessageSource] 41 | public partial class Source 42 | { 43 | public Source(Foobar[] value) 44 | { 45 | Value = value; 46 | } 47 | 48 | public Foobar[] Value { get; } 49 | } 50 | 51 | [BufferedMessageSource] 52 | public partial struct Foobar 53 | { 54 | public Foobar(int foo, long bar) 55 | { 56 | Foo = foo; 57 | Bar = bar; 58 | } 59 | 60 | public int Foo { get; } 61 | public long Bar { get; } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NonNullableScalarBufferedMessageFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class NonNullableScalarBufferedMessageFixture 4 | { 5 | [Fact] 6 | public void GetValue() 7 | { 8 | var source = new Source(1, new Foobar(3, 4), "foobar", new byte[] { 1, 2, 3 }, new Point(1,3)); 9 | using var pooledMessage = source.AsBufferedMessage(); 10 | var message = pooledMessage.BufferedMessage; 11 | 12 | Assert.Equal(1, message.Primitive); 13 | Assert.Equal(3, message.Unmanaged.Foo); 14 | Assert.Equal(4, message.Unmanaged.Bar); 15 | Assert.Equal("foobar", message.String); 16 | 17 | var bytes = message.Bytes.AsSpan(); 18 | Assert.Equal(3, bytes.Length); 19 | Assert.Equal(1, bytes[0]); 20 | Assert.Equal(2, bytes[1]); 21 | Assert.Equal(3, bytes[2]); 22 | 23 | var point = message.Point!.Value; 24 | Assert.Equal(1, point.X); 25 | Assert.Equal(3, point.Y); 26 | } 27 | 28 | public readonly record struct Foobar(int Foo, long Bar); 29 | 30 | [BufferedMessageSource] 31 | public partial class Source 32 | { 33 | public Source(int primitive, Foobar foobar, string @string, byte[] bytes, Point point) 34 | { 35 | Primitive = primitive; 36 | Unmanaged = foobar; 37 | String = @string; 38 | Bytes = bytes; 39 | Point = point; 40 | } 41 | 42 | public int Primitive { get; } 43 | public Foobar Unmanaged { get; } 44 | public string String { get; } 45 | public byte[] Bytes { get; } 46 | public Point Point { get; } 47 | } 48 | 49 | [BufferedMessageSource] 50 | public partial class Point 51 | { 52 | public Point(int x, int y) 53 | { 54 | X = x; 55 | Y = y; 56 | } 57 | 58 | public int X { get; } 59 | public int Y { get; } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NonNullableUnmanagedCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | using NativeBuffering.Collections; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace NativeBuffering.Test 6 | { 7 | public partial class NonNullableUnmanagedCollectionFixture 8 | { 9 | [Fact] 10 | public void GetValue() 11 | { 12 | var source = new Source(new long[] { 1, 2, 3 }); 13 | using var pooledMessage = source.AsBufferedMessage(); 14 | var message = pooledMessage.BufferedMessage; 15 | Assert.Equal(3, message.Value.Count); 16 | Assert.Equal(1, message.Value[0]); 17 | Assert.Equal(2, message.Value[1]); 18 | Assert.Equal(3, message.Value[2]); 19 | 20 | Assert.Contains(message.Value, it => it == 1); 21 | Assert.Contains(message.Value, it => it == 2); 22 | Assert.Contains(message.Value, it => it == 3); 23 | } 24 | 25 | [BufferedMessageSource] 26 | public partial class Source 27 | { 28 | public Source(long[] value) 29 | { 30 | Value = value; 31 | } 32 | 33 | public long[] Value { get; } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NullableBufferedObjectCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class NullableBufferedObjectCollectionFixture 4 | { 5 | [Fact] 6 | public void GetValue() 7 | { 8 | var source = new Source(new Foobar1?[] { new Foobar1(1, 1), null, new Foobar1(3, 3) } 9 | , new Foobar2?[] { new Foobar2(1, 1), null, new Foobar2(3, 3) }); 10 | using (var pooledMessage = source.AsBufferedMessage()) 11 | { 12 | var message = pooledMessage.BufferedMessage; 13 | 14 | Assert.Equal(3, message.Value1.Count); 15 | Assert.Equal(3, message.Value1.Count()); 16 | 17 | Assert.Equal(1, message.Value1[0]!.Value.Foo); 18 | Assert.Equal(3, message.Value1[2]!.Value.Foo); 19 | Assert.Equal(1, message.Value1[0]!.Value.Bar); 20 | Assert.Equal(3, message.Value1[2]!.Value.Bar); 21 | 22 | Assert.Null(message.Value1[1]); 23 | 24 | var index = 0; 25 | for (; index < 3; index++) 26 | { 27 | if (index == 1) 28 | { 29 | Assert.Null(message.Value2[index]); 30 | } 31 | else 32 | { 33 | Assert.Equal(index + 1, message.Value1[index]!.Value.Foo); 34 | Assert.Equal(index + 1, message.Value1[index]!.Value.Bar); 35 | } 36 | } 37 | 38 | index = 0; 39 | foreach (var item in message.Value1) 40 | { 41 | index++; 42 | if (index == 2) 43 | { 44 | Assert.Null(item); 45 | } 46 | else 47 | { 48 | Assert.Equal(index, item!.Value.Foo); 49 | Assert.Equal(index, item!.Value.Bar); 50 | } 51 | } 52 | 53 | Assert.Equal(3, message.Value2.Count); 54 | Assert.Equal(3, message.Value2.Count()); 55 | Assert.Equal(1, message.Value2[0]!.Value.Foo); 56 | Assert.Equal(3, message.Value2[2]!.Value.Foo); 57 | Assert.Equal(1, message.Value2[0]!.Value.Bar); 58 | Assert.Equal(3, message.Value2[2]!.Value.Bar); 59 | 60 | Assert.Null(message.Value2[1]); 61 | 62 | index = 0; 63 | for (; index < 3; index++) 64 | { 65 | if (index == 1) 66 | { 67 | Assert.Null(message.Value2[index]); 68 | } 69 | else 70 | { 71 | Assert.Equal(index + 1, message.Value2[index]!.Value.Foo); 72 | Assert.Equal(index + 1, message.Value2[index]!.Value.Bar); 73 | } 74 | } 75 | 76 | index = 0; 77 | foreach (var item in message.Value2) 78 | { 79 | index++; 80 | if (index == 2) 81 | { 82 | Assert.Null(item); 83 | } 84 | else 85 | { 86 | Assert.Equal(index, item!.Value.Foo); 87 | Assert.Equal(index, item!.Value.Bar); 88 | } 89 | } 90 | } 91 | } 92 | 93 | 94 | [BufferedMessageSource] 95 | public partial class Source 96 | { 97 | public Source(Foobar1?[] value1, Foobar2?[] value2) 98 | { 99 | Value1 = value1; 100 | Value2 = value2; 101 | } 102 | 103 | public Foobar1?[] Value1 { get; } 104 | public Foobar2?[] Value2 { get; } 105 | } 106 | 107 | [BufferedMessageSource] 108 | public partial struct Foobar1 109 | { 110 | public Foobar1(int foo, long bar) 111 | { 112 | Foo = foo; 113 | Bar = bar; 114 | } 115 | 116 | public int Foo { get; } 117 | public long Bar { get; } 118 | } 119 | 120 | [BufferedMessageSource] 121 | public partial class Foobar2 122 | { 123 | public Foobar2(int foo, long bar) 124 | { 125 | Foo = foo; 126 | Bar = bar; 127 | } 128 | 129 | public int Foo { get; } 130 | public long Bar { get; } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NullableScalarBufferedMessageFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class NullableScalarBufferedMessageFixture 4 | { 5 | [Fact] 6 | public void GetValue() 7 | { 8 | var source = new Source(); 9 | using var pooledMessage = source.AsBufferedMessage(); 10 | var message = pooledMessage.BufferedMessage; 11 | 12 | Assert.Null(message.Primitive); 13 | Assert.Null(message.Unmanaged); 14 | Assert.True(message.String == string.Empty); 15 | Assert.True(message.Bytes.AsSpan().Length == 0); 16 | Assert.Null(message.Point); 17 | } 18 | 19 | public readonly record struct Foobar(int Foo, long Bar); 20 | [BufferedMessageSource] 21 | public partial class Source 22 | { 23 | public int? Primitive { get; } 24 | public Foobar? Unmanaged { get; } 25 | public string? String { get; } 26 | public byte[]? Bytes { get; } 27 | public Point? Point { get; } 28 | } 29 | 30 | [BufferedMessageSource] 31 | public partial struct Point 32 | { 33 | public Point(int x, int y) 34 | { 35 | X = x; 36 | Y = y; 37 | } 38 | 39 | public int X { get; } 40 | public int Y { get; } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/NullableUnmanagedCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering; 2 | using NativeBuffering.Collections; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace NativeBuffering.Test 6 | { 7 | public partial class NullableUnmanagedCollectionFixture 8 | { 9 | [Fact] 10 | public void GetValue() 11 | { 12 | var source = new Source(new long?[] { 1, null, 3 }); 13 | using var pooledMessage = source.AsBufferedMessage(); 14 | var message = pooledMessage.BufferedMessage; 15 | Assert.Equal(3, message.Value.Count); 16 | Assert.Equal(1, message.Value[0]); 17 | Assert.Null(message.Value[1]); 18 | Assert.Equal(3, message.Value[2]); 19 | 20 | Assert.Contains(message.Value, it => it == 1); 21 | Assert.Contains(message.Value, it => it is null); 22 | Assert.Contains(message.Value, it => it == 3); 23 | } 24 | 25 | [BufferedMessageSource] 26 | public partial class Source 27 | { 28 | public Source(long?[] value) 29 | { 30 | Value = value; 31 | } 32 | 33 | public long?[] Value { get; } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringBufferedNonNullableObjectDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | 3 | namespace NativeBuffering.Test 4 | { 5 | public partial class StringBufferedNonNullableObjectDictionaryFixture 6 | { 7 | [Fact] 8 | public void GetValues() 9 | { 10 | var source = new Source(new Dictionary { { "1", new Foobar(1,1) } , { "2", new Foobar(2,2) }, { "3", new Foobar(3,3) } 11 | }); 12 | using var pooledMessage = source.AsBufferedMessage(); 13 | var message = pooledMessage.BufferedMessage; 14 | Assert.Equal(3, message.Value.Count); 15 | Assert.Equal(1, message.Value["1"].Foo); 16 | Assert.Equal(2, message.Value["2"].Foo); 17 | Assert.Equal(3, message.Value["3"].Foo); 18 | Assert.Equal(1, message.Value["1"].Bar); 19 | Assert.Equal(2, message.Value["2"].Bar); 20 | Assert.Equal(3, message.Value["3"].Bar); 21 | } 22 | 23 | [BufferedMessageSource] 24 | public partial struct Source 25 | { 26 | public Source(Dictionary value) 27 | { 28 | Value = value; 29 | } 30 | 31 | public Dictionary Value { get; } 32 | } 33 | 34 | [BufferedMessageSource] 35 | public partial struct Foobar 36 | { 37 | public Foobar(int foo, long bar) 38 | { 39 | Foo = foo; 40 | Bar = bar; 41 | } 42 | 43 | public int Foo { get; } 44 | public long Bar { get; } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringBufferedNullableObjectDictionaryFixture_Class.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | 3 | namespace NativeBuffering.Test 4 | { 5 | public partial class StringBufferedNullableObjectDictionaryFixture_Class 6 | { 7 | [Fact] 8 | public void GetValues() 9 | { 10 | var source = new Source(new Dictionary { { "1", new Foobar(1,1) } ,{ "2", null}, { "3", new Foobar(3,3) }}); 11 | using var pooledMessage = source.AsBufferedMessage(); 12 | var message = pooledMessage.BufferedMessage; 13 | Assert.Equal(3, message.Value.Count); 14 | Assert.Equal(1, message.Value["1"]!.Value.Foo); 15 | Assert.Equal(3, message.Value["3"]!.Value.Foo); 16 | Assert.Equal(1, message.Value["1"]!.Value.Bar); 17 | Assert.Equal(3, message.Value["3"]!.Value.Bar); 18 | Assert.Null(message.Value["2"]); 19 | } 20 | 21 | [BufferedMessageSource] 22 | public partial struct Source 23 | { 24 | public Source(Dictionary value) 25 | { 26 | Value = value; 27 | } 28 | 29 | public Dictionary Value { get; } 30 | } 31 | 32 | [BufferedMessageSource] 33 | public partial class Foobar 34 | { 35 | public Foobar(int foo, long bar) 36 | { 37 | Foo = foo; 38 | Bar = bar; 39 | } 40 | 41 | public int Foo { get; } 42 | public long Bar { get; } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringBufferedNullableObjectDictionaryFixture_Structure.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | 3 | namespace NativeBuffering.Test 4 | { 5 | public partial class StringBufferedNullableObjectDictionaryFixture_Structure 6 | { 7 | [Fact] 8 | public void GetValues() 9 | { 10 | var source = new Source(new Dictionary { { "1", new Foobar(1,1) } ,{ "2", null}, { "3", new Foobar(3,3) }}); 11 | using var pooledMessage = source.AsBufferedMessage(); 12 | var message = pooledMessage.BufferedMessage; 13 | Assert.Equal(3, message.Value.Count); 14 | Assert.Equal(1, message.Value["1"]!.Value.Foo); 15 | Assert.Equal(3, message.Value["3"]!.Value.Foo); 16 | Assert.Equal(1, message.Value["1"]!.Value.Bar); 17 | Assert.Equal(3, message.Value["3"]!.Value.Bar); 18 | Assert.Null(message.Value["2"]); 19 | } 20 | 21 | [BufferedMessageSource] 22 | public partial struct Source 23 | { 24 | public Source(Dictionary value) 25 | { 26 | Value = value; 27 | } 28 | 29 | public Dictionary Value { get; } 30 | } 31 | 32 | [BufferedMessageSource] 33 | public partial struct Foobar 34 | { 35 | public Foobar(int foo, long bar) 36 | { 37 | Foo = foo; 38 | Bar = bar; 39 | } 40 | 41 | public int Foo { get; } 42 | public long Bar { get; } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringCollectionFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class StringCollectionFixture 4 | { 5 | [Fact] 6 | public void GetValue() 7 | { 8 | var source = new Source(new string?[] { "1", null, "" }); 9 | using var pooledMessage = source.AsBufferedMessage(); 10 | var message = pooledMessage.BufferedMessage; 11 | Assert.Equal(3, message.Value.Count); 12 | Assert.Equal("1", message.Value[0]); 13 | Assert.Equal("", message.Value[1]); 14 | Assert.Equal("", message.Value[2]); 15 | 16 | Assert.Contains(message.Value, it => it == "1"); 17 | Assert.Contains(message.Value, it => it == ""); 18 | } 19 | 20 | [BufferedMessageSource] 21 | public partial class Source 22 | { 23 | public Source(string?[] value) 24 | { 25 | Value = value; 26 | } 27 | 28 | public string?[] Value { get; } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringNonNullableUnmanagedDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | 3 | namespace NativeBuffering.Test 4 | { 5 | public partial class StringNonNullableUnmanagedDictionaryFixture 6 | { 7 | [Fact] 8 | public void GetValues() 9 | { 10 | var random = new Random(); 11 | var numbers = Enumerable.Range(0, random.Next(10, 30)).Select(_ => random.Next(10, 5000)).Distinct().ToArray(); 12 | 13 | var source = new Source(numbers.ToDictionary(it => it.ToString(), it => (long)it)); 14 | using var pooledMessage = source.AsBufferedMessage(); 15 | var message = pooledMessage.BufferedMessage; 16 | 17 | Assert.Equal(numbers.Length, message.Value.Count); 18 | foreach (var number in numbers) 19 | { 20 | Assert.Equal(number, message.Value[number.ToString()]); 21 | } 22 | 23 | var keys = message.Value.Keys; 24 | Assert.Equal(numbers.Length, keys.Count()); 25 | foreach (var number in numbers) 26 | { 27 | Assert.Contains(number.ToString(), keys); 28 | } 29 | 30 | var values = message.Value.Values; 31 | Assert.Equal(numbers.Length, keys.Count()); 32 | foreach (var number in numbers) 33 | { 34 | Assert.Contains(number, values); 35 | } 36 | 37 | var keySet = new HashSet(message.Value.Keys); 38 | var valueSet = new HashSet(message.Value.Values); 39 | foreach (var kv in message.Value) 40 | { 41 | keySet.Remove(kv.Key); 42 | valueSet.Remove(kv.Value); 43 | } 44 | Assert.Empty(keySet); 45 | Assert.Empty(valueSet); 46 | } 47 | 48 | [BufferedMessageSource] 49 | public partial class Source 50 | { 51 | public Source(Dictionary value) 52 | { 53 | Value = value; 54 | } 55 | 56 | public Dictionary Value { get; } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringNullableUnmanagedDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | using static System.Runtime.InteropServices.JavaScript.JSType; 3 | 4 | namespace NativeBuffering.Test 5 | { 6 | public partial class StringNullableUnmanagedDictionaryFixture 7 | { 8 | [Fact] 9 | public void GetValues() 10 | { 11 | var random = new Random(); 12 | var numbers = Enumerable.Range(0, random.Next(10, 30)).Select(_ => random.Next(10, 5000)).Distinct().ToArray(); 13 | 14 | var source = new Source(numbers.ToDictionary(it => it.ToString(), it => (long?)it)); 15 | source.Value.Add("1", null); 16 | using var pooledMessage = source.AsBufferedMessage(); 17 | var message = pooledMessage.BufferedMessage; 18 | 19 | Assert.Equal(numbers.Length + 1, message.Value.Count); 20 | foreach (var number in numbers) 21 | { 22 | Assert.Equal(number, message.Value[number.ToString()]!.Value); 23 | } 24 | Assert.Null(message.Value["1"]); 25 | 26 | var keys = message.Value.Keys; 27 | Assert.Equal(numbers.Length + 1, keys.Count()); 28 | foreach (var number in numbers) 29 | { 30 | Assert.Contains(number.ToString(), keys); 31 | } 32 | Assert.Contains("1", keys); 33 | 34 | var values = message.Value.Values; 35 | Assert.Equal(numbers.Length+1, keys.Count()); 36 | foreach (var number in numbers) 37 | { 38 | Assert.Contains(number, values); 39 | } 40 | 41 | var keySet = new HashSet(message.Value.Keys); 42 | var valueSet = new HashSet(message.Value.Values); 43 | foreach (var kv in message.Value) 44 | { 45 | keySet.Remove(kv.Key); 46 | valueSet.Remove(kv.Value); 47 | } 48 | keySet.Remove("1"); 49 | valueSet.Remove(null); 50 | Assert.Empty(keySet); 51 | Assert.Empty(valueSet); 52 | } 53 | 54 | [BufferedMessageSource] 55 | public partial class Source 56 | { 57 | public Source(Dictionary value) 58 | { 59 | Value = value; 60 | } 61 | 62 | public Dictionary Value { get; } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/StringStringDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | 3 | namespace NativeBuffering.Test 4 | { 5 | public partial class StringStringDictionaryFixture 6 | { 7 | [Fact] 8 | public void GetValues() 9 | { 10 | var random = new Random(); 11 | var numbers = Enumerable.Range(0, random.Next(10, 30)).Select(_ => random.Next(10, 5000)).Distinct().ToArray(); 12 | 13 | var source = new Source(numbers.ToDictionary(it => it.ToString(), it => it.ToString())); 14 | using var pooledMessage = source.AsBufferedMessage(); 15 | var message = pooledMessage.BufferedMessage; 16 | 17 | Assert.Equal(numbers.Length, message.Value.Count); 18 | foreach (var number in numbers) 19 | { 20 | Assert.Equal(number.ToString(), message.Value[number.ToString()]); 21 | } 22 | 23 | var keys = message.Value.Keys; 24 | Assert.Equal(numbers.Length, keys.Count()); 25 | foreach (var number in numbers) 26 | { 27 | Assert.Contains(number.ToString(), keys); 28 | } 29 | 30 | var values = message.Value.Values.Select(it=>(string)it); 31 | Assert.Equal(numbers.Length, keys.Count()); 32 | foreach (var number in numbers) 33 | { 34 | Assert.Contains(number.ToString(), values); 35 | } 36 | 37 | var keySet = new HashSet(message.Value.Keys); 38 | var valueSet = new HashSet(message.Value.Values.Select(it => (string)it)); 39 | foreach (var kv in message.Value) 40 | { 41 | keySet.Remove(kv.Key); 42 | valueSet.Remove(kv.Value); 43 | } 44 | Assert.Empty(keySet); 45 | Assert.Empty(valueSet); 46 | } 47 | 48 | [BufferedMessageSource] 49 | public partial class Source 50 | { 51 | public Source(Dictionary value) 52 | { 53 | Value = value; 54 | } 55 | 56 | public Dictionary Value { get; } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/UnmanagedBinaryDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace NativeBuffering.Test 9 | { 10 | public partial class UnmanagedBinaryDictionaryFixture 11 | { 12 | [Fact] 13 | public void GetValues() 14 | { 15 | var source = new Source(new Dictionary { { 1, new byte[] { 1, 1, 1 } }, { 2, null }, { 3, new byte[0] } }); 16 | using var pooledMessage = source.AsBufferedMessage(); 17 | var message = pooledMessage.BufferedMessage; 18 | Assert.Equal(3, message.Value.Count); 19 | Assert.True(message.Value[1].AsSpan().SequenceEqual(new byte[] { 1,1,1 })); 20 | Assert.True(message.Value[2].AsSpan().Length == 0); 21 | Assert.True(message.Value[3].AsSpan().Length == 0); 22 | } 23 | 24 | [BufferedMessageSource] 25 | public partial class Source 26 | { 27 | public Source(Dictionary value) 28 | { 29 | Value = value; 30 | } 31 | 32 | public Dictionary Value { get; } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/UnmanagedNonNullableBufferedObjectDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace NativeBuffering.Test 9 | { 10 | public partial class UnmanagedNonNullableBufferedObjectDictionaryFixture 11 | { 12 | [Fact] 13 | public void GetValues() 14 | { 15 | var source = new Source(new Dictionary { { 1, new Foobar(1,1) }, { 2, new Foobar(2,2) }, { 3, new Foobar(3,3)} }); 16 | using var pooledMessage = source.AsBufferedMessage(); 17 | var message = pooledMessage.BufferedMessage; 18 | Assert.Equal(3, message.Value.Count); 19 | Assert.Equal(1, message.Value[1].Foo); 20 | Assert.Equal(2, message.Value[2].Foo); 21 | Assert.Equal(3, message.Value[3].Foo); 22 | Assert.Equal(1, message.Value[1].Bar); 23 | Assert.Equal(2, message.Value[2].Bar); 24 | Assert.Equal(3, message.Value[3].Bar); 25 | } 26 | 27 | [BufferedMessageSource] 28 | public partial class Source 29 | { 30 | public Source(Dictionary value) 31 | { 32 | Value = value; 33 | } 34 | 35 | public Dictionary Value { get; } 36 | } 37 | 38 | [BufferedMessageSource] 39 | public partial struct Foobar 40 | { 41 | public Foobar(int foo, long bar) 42 | { 43 | Foo = foo; 44 | Bar = bar; 45 | } 46 | 47 | public int Foo { get; } 48 | public long Bar { get; } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/UnmanagedNonNullableUnmanagedDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | 3 | namespace NativeBuffering.Test 4 | { 5 | public partial class UnmanagedNonNullableUnmanagedDictionaryFixture 6 | { 7 | [Fact] 8 | public void GetValues() 9 | { 10 | var random = new Random(); 11 | var numbers = Enumerable.Range(0, random.Next(10,30)).Select(_=>random.Next(10,5000)).Distinct().ToArray(); 12 | 13 | var source = new Source(numbers.ToDictionary(it=>(long)it, it=>(long)it)); 14 | using var pooledMessage = source.AsBufferedMessage(); 15 | var message = pooledMessage.BufferedMessage; 16 | 17 | Assert.Equal(numbers.Length, message.Value.Count); 18 | foreach(var number in numbers) 19 | { 20 | Assert.Equal(number, message.Value[number]); 21 | } 22 | 23 | var keys = message.Value.Keys; 24 | Assert.Equal(numbers.Length, keys.Count()); 25 | foreach (var number in numbers) 26 | { 27 | Assert.Contains(number, keys); 28 | } 29 | 30 | var values = message.Value.Values; 31 | Assert.Equal(numbers.Length, keys.Count()); 32 | foreach (var number in numbers) 33 | { 34 | Assert.Contains(number, values); 35 | } 36 | 37 | var keySet = new HashSet( message.Value.Keys); 38 | var valueSet = new HashSet( message.Value.Values); 39 | foreach (var kv in message.Value) 40 | { 41 | keySet.Remove(kv.Key); 42 | valueSet.Remove(kv.Value); 43 | } 44 | Assert.Empty(keySet); 45 | Assert.Empty(valueSet); 46 | } 47 | 48 | [BufferedMessageSource] 49 | public partial class Source 50 | { 51 | public Source(Dictionary value) 52 | { 53 | Value = value; 54 | } 55 | 56 | public Dictionary Value { get; } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/UnmanagedNullableUnmanagedDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | namespace NativeBuffering.Test 2 | { 3 | public partial class UnmanagedNullableUnmanagedDictionaryFixture 4 | { 5 | [Fact] 6 | public void GetValues() 7 | { 8 | var random = new Random(); 9 | var numbers = Enumerable.Range(0, random.Next(10, 30)).Select(_ => random.Next(10, 5000)).Distinct().ToList(); 10 | var source = new Source(numbers.ToDictionary(it => (long)it, it => (long?)it)); 11 | source.Value.Add(1, null); 12 | using var pooledMessage = source.AsBufferedMessage(); 13 | var message = pooledMessage.BufferedMessage; 14 | 15 | Assert.Equal(numbers.Count + 1, message.Value.Count); 16 | foreach (var number in numbers) 17 | { 18 | Assert.Equal(number, message.Value[number]!.Value); 19 | } 20 | Assert.Null(message.Value[1]); 21 | 22 | var keys = message.Value.Keys; 23 | Assert.Equal(numbers.Count + 1, keys.Count()); 24 | foreach (var number in numbers) 25 | { 26 | Assert.Contains(number, keys); 27 | } 28 | 29 | var values = message.Value.Values; 30 | Assert.Equal(numbers.Count + 1, keys.Count()); 31 | foreach (var number in numbers) 32 | { 33 | Assert.Contains(number, values); 34 | } 35 | 36 | var keySet = new HashSet(message.Value.Keys); 37 | var valueSet = new HashSet(message.Value.Values); 38 | foreach (var kv in message.Value) 39 | { 40 | keySet.Remove(kv.Key); 41 | valueSet.Remove(kv.Value); 42 | } 43 | keySet.Remove(1); 44 | valueSet.Remove(null); 45 | Assert.Empty(keySet); 46 | Assert.Empty(valueSet); 47 | } 48 | 49 | [BufferedMessageSource] 50 | public partial class Source 51 | { 52 | public Source(Dictionary value) 53 | { 54 | Value = value; 55 | } 56 | 57 | public Dictionary Value { get; } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/UnmanagedStringDictionaryFixture.cs: -------------------------------------------------------------------------------- 1 | using NativeBuffering.Dictionaries; 2 | using static System.Runtime.InteropServices.JavaScript.JSType; 3 | 4 | namespace NativeBuffering.Test 5 | { 6 | public partial class UnmanagedStringDictionaryFixture 7 | { 8 | [Fact] 9 | public void GetValues() 10 | { 11 | var random = new Random(); 12 | var numbers = Enumerable.Range(0, random.Next(10, 30)).Select(_ => random.Next(10, 5000)).Distinct().ToArray(); 13 | 14 | var source = new Source(numbers.ToDictionary(it => (long)it, it => (string?)it.ToString())); 15 | source.Value.Add(1, null); 16 | source.Value.Add(2, ""); 17 | using var pooledMessage = source.AsBufferedMessage(); 18 | var message = pooledMessage.BufferedMessage; 19 | 20 | Assert.Equal(numbers.Length + 2, message.Value.Count); 21 | foreach (var number in numbers) 22 | { 23 | Assert.Equal(number.ToString(), message.Value[number]); 24 | } 25 | Assert.Equal(string.Empty, message.Value[1]); 26 | Assert.Equal(string.Empty, message.Value[2]); 27 | 28 | var keys = message.Value.Keys; 29 | Assert.Equal(numbers.Length + 2, keys.Count()); 30 | foreach (var number in numbers) 31 | { 32 | Assert.Contains(number, keys); 33 | } 34 | 35 | var values = message.Value.Values.Select(it => (string)it); 36 | Assert.Equal(numbers.Length + 2, keys.Count()); 37 | foreach (var number in numbers) 38 | { 39 | Assert.Contains(number.ToString(), values); 40 | } 41 | 42 | var keySet = new HashSet(message.Value.Keys); 43 | var valueSet = new HashSet(message.Value.Values.Select(it => (string)it)); 44 | foreach (var kv in message.Value) 45 | { 46 | keySet.Remove(kv.Key); 47 | valueSet.Remove(kv.Value); 48 | } 49 | 50 | keySet.Remove(1); 51 | keySet.Remove(2); 52 | valueSet.Remove(""); 53 | Assert.Empty(keySet); 54 | Assert.Empty(valueSet); 55 | } 56 | 57 | [BufferedMessageSource] 58 | public partial class Source 59 | { 60 | public Source(Dictionary value) 61 | { 62 | Value = value; 63 | } 64 | 65 | public Dictionary Value { get; } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/NativeBuffering.Test/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; --------------------------------------------------------------------------------