Skip to content

Commit ec6c57c

Browse files
perf(collections): reuse backing storage and extend tests
- move List/Dictionary/HashSet/Queue/Stack view types into Nino.Core.Internal so core and generators share the same layouts - expose fast span helpers on Reader/Writer to copy unmanaged buffers and remove TypeCollector references - rewrite queue/stack serializers to reuse backing arrays, pre-size instances, and simplify .NET vs Unity directives - add Unity editor coverage for collection and concurrent collection serialization including ref overloads - update Unity package versions and project version to match the new test environment Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 3924539 commit ec6c57c

17 files changed

Lines changed: 1163 additions & 197 deletions
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Collections.Generic;
2+
using System.Diagnostics.CodeAnalysis;
3+
4+
namespace Nino.Core.Internal
5+
{
6+
[SuppressMessage("ReSharper", "InconsistentNaming")]
7+
public class DictionaryView<TKey, TValue>
8+
{
9+
public int[] _buckets; // Do not rename (binary serialization)
10+
public Entry[] _entries; // Do not rename (binary serialization)
11+
public int _count; // Do not rename (binary serialization)
12+
public int _version; // Do not rename (binary serialization)
13+
public int _freeList; // Do not rename (binary serialization)
14+
public int _freeCount; // Do not rename (binary serialization)
15+
public IEqualityComparer<TKey> _comparer; // Do not rename (binary serialization)
16+
public Dictionary<TKey, TValue>.KeyCollection _keys; // Do not rename (binary serialization)
17+
public Dictionary<TKey, TValue>.ValueCollection _values; // Do not rename (binary serialization)
18+
public object _syncRoot; // Do not rename (binary serialization)
19+
20+
[SuppressMessage("ReSharper", "InconsistentNaming")]
21+
public struct Entry
22+
{
23+
public int hashCode; // Do not rename (binary serialization)
24+
public int next; // Do not rename (binary serialization)
25+
public TKey key; // Do not rename (binary serialization)
26+
public TValue value; // Do not rename (binary serialization)
27+
}
28+
}
29+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Collections.Generic;
2+
using System.Diagnostics.CodeAnalysis;
3+
4+
namespace Nino.Core.Internal
5+
{
6+
[SuppressMessage("ReSharper", "InconsistentNaming")]
7+
public class HashSetView<T>
8+
{
9+
public int[] _buckets; // Do not rename (binary serialization)
10+
public Entry[] _entries; // Do not rename (binary serialization)
11+
public int _count; // Do not rename (binary serialization)
12+
public int _version; // Do not rename (binary serialization)
13+
public int _freeList; // Do not rename (binary serialization)
14+
public int _freeCount; // Do not rename (binary serialization)
15+
public IEqualityComparer<T> _comparer; // Do not rename (binary serialization)
16+
17+
[SuppressMessage("ReSharper", "InconsistentNaming")]
18+
public struct Entry
19+
{
20+
public int hashCode; // Do not rename (binary serialization)
21+
public int next; // Do not rename (binary serialization)
22+
public T value; // Do not rename (binary serialization)
23+
}
24+
}
25+
}

src/Nino.Core/Internal/ListView.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace Nino.Core.Internal
4+
{
5+
[SuppressMessage("ReSharper", "InconsistentNaming")]
6+
public class ListView<T>
7+
{
8+
public T[] _items; // Do not rename (binary serialization)
9+
public int _size; // Do not rename (binary serialization)
10+
}
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace Nino.Core.Internal
4+
{
5+
[SuppressMessage("ReSharper", "InconsistentNaming")]
6+
public class QueueView<T>
7+
{
8+
public T[] _array; // Do not rename (binary serialization)
9+
public int _head; // Do not rename (binary serialization)
10+
public int _tail; // Do not rename (binary serialization)
11+
public int _size; // Do not rename (binary serialization)
12+
}
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace Nino.Core.Internal
4+
{
5+
[SuppressMessage("ReSharper", "InconsistentNaming")]
6+
public class StackView<T>
7+
{
8+
public T[] _array; // Do not rename (binary serialization)
9+
public int _size; // Do not rename (binary serialization)
10+
}
11+
}

src/Nino.Core/Reader.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Runtime.InteropServices;
66
using System.Runtime.CompilerServices;
7+
using Nino.Core.Internal;
78

89
namespace Nino.Core
910
{
@@ -226,7 +227,7 @@ public void Read<T>(out List<T> ret) where T : unmanaged
226227
}
227228

228229
ret = new List<T>();
229-
ref var lst = ref Unsafe.As<List<T>, TypeCollector.ListView<T>>(ref ret);
230+
ref var lst = ref Unsafe.As<List<T>, ListView<T>>(ref ret);
230231
lst._size = arr.Length;
231232
lst._items = arr;
232233
#endif
@@ -377,7 +378,7 @@ ref MemoryMarshal.GetReference(utf16Bytes))),
377378
}
378379

379380
[MethodImpl(MethodImplOptions.AggressiveInlining)]
380-
private void GetBytes(int length, out ReadOnlySpan<byte> bytes)
381+
public void GetBytes(int length, out ReadOnlySpan<byte> bytes)
381382
{
382383
bytes = _data.Slice(0, length);
383384
_data = _data.Slice(length);
@@ -443,7 +444,7 @@ public void ReadRef<T>(ref List<T> value) where T : unmanaged
443444
Span<byte> dst = MemoryMarshal.AsBytes(span);
444445
bytes.CopyTo(dst);
445446
#else
446-
ref var lst = ref Unsafe.As<List<T>, TypeCollector.ListView<T>>(ref value);
447+
ref var lst = ref Unsafe.As<List<T>, ListView<T>>(ref value);
447448
lst._size = length;
448449
#if !NET5_0_OR_GREATER
449450
Array.Resize(ref lst._items, length);

src/Nino.Core/TypeCollector.cs

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Diagnostics.CodeAnalysis;
42
using System.Runtime.CompilerServices;
53

64
#pragma warning disable CS0649
@@ -15,7 +13,7 @@ public static class TypeCollector
1513
public const byte NullCollection = 0;
1614
public const uint EmptyCollectionHeader = 128;
1715
public static readonly bool Is64Bit = IntPtr.Size == 8;
18-
16+
1917
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2018
public static uint GetCollectionHeader(int size)
2119
{
@@ -28,56 +26,5 @@ public static uint GetCollectionHeader(int size)
2826
return System.Buffers.Binary.BinaryPrimitives.ReverseEndianness(ret);
2927
#endif
3028
}
31-
32-
[SuppressMessage("ReSharper", "InconsistentNaming")]
33-
public class ListView<T>
34-
{
35-
public T[] _items; // Do not rename (binary serialization)
36-
public int _size; // Do not rename (binary serialization)
37-
}
38-
39-
[SuppressMessage("ReSharper", "InconsistentNaming")]
40-
public class DictionaryView<TKey, TValue>
41-
{
42-
public int[] _buckets; // Do not rename (binary serialization)
43-
public Entry[] _entries; // Do not rename (binary serialization)
44-
public int _count; // Do not rename (binary serialization)
45-
public int _version; // Do not rename (binary serialization)
46-
public int _freeList; // Do not rename (binary serialization)
47-
public int _freeCount; // Do not rename (binary serialization)
48-
public IEqualityComparer<TKey> _comparer; // Do not rename (binary serialization)
49-
public Dictionary<TKey, TValue>.KeyCollection _keys; // Do not rename (binary serialization)
50-
public Dictionary<TKey, TValue>.ValueCollection _values; // Do not rename (binary serialization)
51-
public object _syncRoot; // Do not rename (binary serialization)
52-
53-
[SuppressMessage("ReSharper", "InconsistentNaming")]
54-
public struct Entry
55-
{
56-
public int hashCode; // Do not rename (binary serialization)
57-
public int next; // Do not rename (binary serialization)
58-
public TKey key; // Do not rename (binary serialization)
59-
public TValue value; // Do not rename (binary serialization)
60-
}
61-
}
62-
63-
[SuppressMessage("ReSharper", "InconsistentNaming")]
64-
public class HashSetView<T>
65-
{
66-
public int[] _buckets; // Do not rename (binary serialization)
67-
public Entry[] _entries; // Do not rename (binary serialization)
68-
public int _count; // Do not rename (binary serialization)
69-
public int _version; // Do not rename (binary serialization)
70-
public int _freeList; // Do not rename (binary serialization)
71-
public int _freeCount; // Do not rename (binary serialization)
72-
public IEqualityComparer<T> _comparer; // Do not rename (binary serialization)
73-
74-
[SuppressMessage("ReSharper", "InconsistentNaming")]
75-
public struct Entry
76-
{
77-
public int hashCode; // Do not rename (binary serialization)
78-
public int next; // Do not rename (binary serialization)
79-
public T value; // Do not rename (binary serialization)
80-
}
81-
}
8229
}
8330
}

src/Nino.Core/Writer.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Runtime.InteropServices;
66
using System.Runtime.CompilerServices;
7+
using Nino.Core.Internal;
78

89
namespace Nino.Core
910
{
@@ -212,6 +213,20 @@ public void Write<T>(Span<T> value) where T : unmanaged
212213
_bufferWriter.Advance(size);
213214
}
214215

216+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
217+
public void WriteSpanWithoutHeader<T>(Span<T> value) where T : unmanaged
218+
{
219+
if (value.IsEmpty)
220+
{
221+
return;
222+
}
223+
224+
var valueSpan = MemoryMarshal.AsBytes(value);
225+
var span = _bufferWriter.GetSpan(valueSpan.Length);
226+
valueSpan.CopyTo(span);
227+
_bufferWriter.Advance(valueSpan.Length);
228+
}
229+
215230
[MethodImpl(MethodImplOptions.AggressiveInlining)]
216231
public void Write<T>(Span<T?> value) where T : unmanaged
217232
{
@@ -240,7 +255,7 @@ public void Write<T>(List<T> value) where T : unmanaged
240255
#if NET6_0_OR_GREATER
241256
Write(CollectionsMarshal.AsSpan(value));
242257
#else
243-
ref var lst = ref Unsafe.As<List<T>, TypeCollector.ListView<T>>(ref value);
258+
ref var lst = ref Unsafe.As<List<T>, ListView<T>>(ref value);
244259
Write(lst._items.AsSpan(0, lst._size));
245260
#endif
246261
}
@@ -257,7 +272,7 @@ public void Write<T>(List<T?> value) where T : unmanaged
257272
#if NET6_0_OR_GREATER
258273
Write(CollectionsMarshal.AsSpan(value));
259274
#else
260-
ref var lst = ref Unsafe.As<List<T?>, TypeCollector.ListView<T?>>(ref value);
275+
ref var lst = ref Unsafe.As<List<T?>, ListView<T?>>(ref value);
261276
Write(lst._items.AsSpan(0, lst._size));
262277
#endif
263278
}

src/Nino.Generator/BuiltInType/DictionaryGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ protected override void GenerateSerializer(ITypeSymbol typeSymbol, Writer writer
102102

103103
if (isDictionary)
104104
{
105-
var dictViewTypeName = $"TypeCollector.DictionaryView<{keyType.GetDisplayString()}, {valueType.GetDisplayString()}>";
105+
var dictViewTypeName = $"Nino.Core.Internal.DictionaryView<{keyType.GetDisplayString()}, {valueType.GetDisplayString()}>";
106106
writer.Append(" ref var dict = ref System.Runtime.CompilerServices.Unsafe.As<");
107107
writer.Append(typeName);
108108
writer.Append(", ");

src/Nino.Generator/BuiltInType/HashSetGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ protected override void GenerateSerializer(ITypeSymbol typeSymbol, Writer writer
9494

9595
if (isHashSet)
9696
{
97-
var hashSetViewTypeName = $"TypeCollector.HashSetView<{elementType.GetDisplayString()}>";
97+
var hashSetViewTypeName = $"Nino.Core.Internal.HashSetView<{elementType.GetDisplayString()}>";
9898
writer.Append(" ref var hashSet = ref System.Runtime.CompilerServices.Unsafe.As<");
9999
writer.Append(typeName);
100100
writer.Append(", ");

0 commit comments

Comments
 (0)