├── .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