diff --git a/README.md b/README.md
index 362e43fe..8952b805 100644
--- a/README.md
+++ b/README.md
@@ -854,6 +854,22 @@ There are a few restrictions on the types that can be generated. Among the primi
`[GenerateTypeScript]` can only be applied to classes and is currently not supported by struct.
+### Configure import file extension
+
+In default, MemoryPack generates file extension as `.js` like `import { MemoryPackWriter } from "./MemoryPackWriter.js";`. If you want to change other extension or empty, use `MemoryPackGenerator_TypeScriptImportExtension` to configure it.
+
+```xml
+
+
+
+
+
+ $(MSBuildProjectDirectory)\wwwroot\js\memorypack
+
+
+
+```
+
Streaming Serialization
---
`MemoryPack.Streaming` provides additional `MemoryPackStreamingSerializer`, it serialize/deserialize collection data streamingly.
diff --git a/sandbox/Benchmark/Benchmarks/ListFormatterVsDirect.cs b/sandbox/Benchmark/Benchmarks/ListFormatterVsDirect.cs
new file mode 100644
index 00000000..dc715cff
--- /dev/null
+++ b/sandbox/Benchmark/Benchmarks/ListFormatterVsDirect.cs
@@ -0,0 +1,75 @@
+using Benchmark.BenchmarkNetUtilities;
+using Benchmark.Models;
+using MemoryPack;
+using MemoryPack.Formatters;
+using Orleans.Serialization.Buffers;
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Benchmark.Benchmarks;
+
+public class ListFormatterVsDirect
+{
+ List value;
+ byte[] bytes;
+ IMemoryPackFormatter> formatter;
+ ArrayBufferWriter buffer;
+ MemoryPackWriterOptionalState state;
+ MemoryPackReaderOptionalState state2;
+
+ public ListFormatterVsDirect()
+ {
+ value = Enumerable.Range(0, 100)
+ .Select(_ => new MyClass { X = 100, Y = 99999999, Z = 4444, FirstName = "Hoge Huga Tako", LastName = "あいうえおかきくけこ" })
+ .ToList();
+ bytes = MemoryPackSerializer.Serialize(value);
+ formatter = new ListFormatter();
+ buffer = new ArrayBufferWriter(bytes.Length);
+
+ state = MemoryPackWriterOptionalStatePool.Rent(null);
+ state2 = MemoryPackReaderOptionalStatePool.Rent(null);
+ }
+
+ [Benchmark, BenchmarkCategory(Categories.Serialize)]
+ public void SerializeFormatter()
+ {
+ var writer = new MemoryPackWriter>(ref buffer, state);
+ formatter.Serialize(ref writer, ref value!);
+ writer.Flush();
+ buffer.Clear();
+ }
+
+ [Benchmark, BenchmarkCategory(Categories.Serialize)]
+ public void SerializePackable()
+ {
+ var writer = new MemoryPackWriter>(ref buffer, state);
+ MemoryPack.Formatters.ListFormatter.SerializePackable(ref writer, ref value!);
+ writer.Flush();
+ buffer.Clear();
+ }
+
+
+ [Benchmark, BenchmarkCategory(Categories.Deserialize)]
+ public void DeserializeFormatter()
+ {
+ List? list = null;
+ var reader = new MemoryPackReader(bytes, state2);
+ //reader.ReadPackableArray
+ // var a = MemoryPack.Formatters.ListFormatter.DeserializePackable<(ref reader);
+ formatter.Deserialize(ref reader, ref list);
+ reader.Dispose();
+ }
+
+ [Benchmark, BenchmarkCategory(Categories.Deserialize)]
+ public void DeserializePackable()
+ {
+ List? list = null;
+ var reader = new MemoryPackReader(bytes, state2);
+ ListFormatter.DeserializePackable(ref reader, ref list!);
+ reader.Dispose();
+ }
+}
diff --git a/sandbox/Benchmark/Program.cs b/sandbox/Benchmark/Program.cs
index 7d6db548..4592d552 100644
--- a/sandbox/Benchmark/Program.cs
+++ b/sandbox/Benchmark/Program.cs
@@ -45,7 +45,7 @@
// BenchmarkRunner.Run(config, args);
-
+BenchmarkRunner.Run(config, args);
//BenchmarkRunner.Run(config, args);
@@ -55,7 +55,7 @@
// BenchmarkRunner.Run>(config, args);
-BenchmarkRunner.Run(config, args);
+//BenchmarkRunner.Run(config, args);
//BenchmarkRunner.Run>(config, args);
//BenchmarkRunner.Run>(config, args);
//BenchmarkRunner.Run>(config, args);
diff --git a/sandbox/SandboxConsoleApp/Models.cs b/sandbox/SandboxConsoleApp/Models.cs
index 7507a6c1..209fc2f7 100644
--- a/sandbox/SandboxConsoleApp/Models.cs
+++ b/sandbox/SandboxConsoleApp/Models.cs
@@ -1,6 +1,7 @@
using MemoryPack;
using MemoryPack.Formatters;
using MemoryPack.Internal;
+using Newtonsoft.Json.Linq;
using System;
using System.Buffers;
using System.Collections.Generic;
@@ -13,6 +14,15 @@
namespace SandboxConsoleApp;
+[MemoryPackable]
+public partial class Mop
+{
+ public NoGen? MyProperty { get; set; }
+ public LisList? MyLisList { get; set; }
+ public List? SuageMan { get; set; }
+}
+
+
[MemoryPackable]
public partial class NotSample
{
@@ -53,3 +63,16 @@ public partial class Suage
// this.Prop2 = prop2;
//}
}
+
+
+
+[MemoryPackable(GenerateType.NoGenerate)]
+public partial class NoGen
+{
+}
+
+[MemoryPackable(GenerateType.Collection)]
+public partial class LisList : List
+{
+
+}
diff --git a/sandbox/SandboxWebApp/SandboxWebApp.csproj b/sandbox/SandboxWebApp/SandboxWebApp.csproj
index 3dcbcb47..d6103652 100644
--- a/sandbox/SandboxWebApp/SandboxWebApp.csproj
+++ b/sandbox/SandboxWebApp/SandboxWebApp.csproj
@@ -16,9 +16,11 @@
+
$(MSBuildProjectDirectory)\wwwroot\js\memorypack
+ .js
diff --git a/src/MemoryPack.Core/Formatters/CollectionFormatters.cs b/src/MemoryPack.Core/Formatters/CollectionFormatters.cs
index 256474b5..f0863471 100644
--- a/src/MemoryPack.Core/Formatters/CollectionFormatters.cs
+++ b/src/MemoryPack.Core/Formatters/CollectionFormatters.cs
@@ -3,7 +3,6 @@
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
-using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -49,6 +48,87 @@ public static partial class MemoryPackFormatterProvider
namespace MemoryPack.Formatters
{
+ [Preserve]
+ public static class ListFormatter
+ {
+ [Preserve]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void SerializePackable(ref MemoryPackWriter writer, scoped ref List? value)
+ where T : IMemoryPackable
+#if NET7_0_OR_GREATER
+ where TBufferWriter : IBufferWriter
+#else
+ where TBufferWriter : class, IBufferWriter
+#endif
+ {
+ if (value == null)
+ {
+ writer.WriteNullCollectionHeader();
+ return;
+ }
+
+#if NET7_0_OR_GREATER
+ writer.WritePackableSpan(CollectionsMarshal.AsSpan(value));
+#else
+ var formatter = writer.GetFormatter();
+ writer.WriteCollectionHeader(value.Count);
+ foreach (var item in value)
+ {
+ var v = item;
+ formatter.Serialize(ref writer, ref v);
+ }
+#endif
+ }
+
+ [Preserve]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static List? DeserializePackable(ref MemoryPackReader reader)
+ where T : IMemoryPackable
+ {
+ List? value = default;
+ DeserializePackable(ref reader, ref value);
+ return value;
+ }
+
+ [Preserve]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void DeserializePackable(ref MemoryPackReader reader, scoped ref List? value)
+ where T : IMemoryPackable
+ {
+ if (!reader.TryReadCollectionHeader(out var length))
+ {
+ value = null;
+ return;
+ }
+
+ if (value == null)
+ {
+ value = new List(length);
+ }
+#if NET7_0_OR_GREATER
+ else if (value.Count == length)
+ {
+ value.Clear();
+ }
+
+ var span = CollectionsMarshalEx.CreateSpan(value, length);
+ reader.ReadPackableSpanWithoutReadLengthHeader(length, ref span);
+#else
+ else
+ {
+ value.Clear();
+ }
+ var formatter = reader.GetFormatter();
+ for (var i = 0; i < length; i++)
+ {
+ T? v = default;
+ formatter.Deserialize(ref reader, ref v);
+ value.Add(v);
+ }
+#endif
+ }
+ }
+
[Preserve]
public sealed class ListFormatter : MemoryPackFormatter>
{
diff --git a/src/MemoryPack.Core/MemoryPackReader.cs b/src/MemoryPack.Core/MemoryPackReader.cs
index fe4bee71..54871947 100644
--- a/src/MemoryPack.Core/MemoryPackReader.cs
+++ b/src/MemoryPack.Core/MemoryPackReader.cs
@@ -420,6 +420,7 @@ public void ReadValueWithFormatter(TFormatter formatter, scoped r
#region ReadArray/Span
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T?[]? ReadArray()
{
T?[]? value = default;
@@ -427,6 +428,7 @@ public void ReadValueWithFormatter(TFormatter formatter, scoped r
return value;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadArray(scoped ref T?[]? value)
{
if (!RuntimeHelpers.IsReferenceOrContainsReferences())
@@ -460,6 +462,7 @@ public void ReadArray(scoped ref T?[]? value)
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadSpan(scoped ref Span value)
{
if (!RuntimeHelpers.IsReferenceOrContainsReferences())
@@ -492,22 +495,111 @@ public void ReadSpan(scoped ref Span value)
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T?[]? ReadPackableArray()
+ where T : IMemoryPackable
+ {
+ T?[]? value = default;
+ ReadPackableArray(ref value);
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReadPackableArray(scoped ref T?[]? value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ ReadArray(ref value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousReadUnmanagedArray(ref value);
+ return;
+ }
+
+ if (!TryReadCollectionHeader(out var length))
+ {
+ value = null;
+ return;
+ }
+
+ if (length == 0)
+ {
+ value = Array.Empty();
+ return;
+ }
+
+ // T[] support overwrite
+ if (value == null || value.Length != length)
+ {
+ value = new T[length];
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ T.Deserialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReadPackableSpan(scoped ref Span value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ ReadSpan(ref value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousReadUnmanagedSpan(ref value);
+ return;
+ }
+
+ if (!TryReadCollectionHeader(out var length))
+ {
+ value = default;
+ return;
+ }
+
+ if (length == 0)
+ {
+ value = Array.Empty();
+ return;
+ }
+
+ if (value.Length != length)
+ {
+ value = new T[length];
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ T.Deserialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
#endregion
#region UnmanagedArray/Span
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[]? ReadUnmanagedArray()
where T : unmanaged
{
return DangerousReadUnmanagedArray();
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadUnmanagedArray(scoped ref T[]? value)
where T : unmanaged
{
DangerousReadUnmanagedArray(ref value);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadUnmanagedSpan(scoped ref Span value)
where T : unmanaged
{
@@ -515,6 +607,7 @@ public void ReadUnmanagedSpan(scoped ref Span value)
}
// T: should be unamanged type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T[]? DangerousReadUnmanagedArray()
{
if (!TryReadCollectionHeader(out var length))
@@ -533,6 +626,7 @@ public void ReadUnmanagedSpan(scoped ref Span value)
return dest;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void DangerousReadUnmanagedArray(scoped ref T[]? value)
{
if (!TryReadCollectionHeader(out var length))
@@ -561,6 +655,7 @@ public unsafe void DangerousReadUnmanagedArray(scoped ref T[]? value)
Advance(byteCount);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void DangerousReadUnmanagedSpan(scoped ref Span value)
{
if (!TryReadCollectionHeader(out var length))
@@ -591,6 +686,7 @@ public unsafe void DangerousReadUnmanagedSpan(scoped ref Span value)
#endregion
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadSpanWithoutReadLengthHeader(int length, scoped ref Span value)
{
if (length == 0)
@@ -627,4 +723,47 @@ public void ReadSpanWithoutReadLengthHeader(int length, scoped ref Span v
}
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReadPackableSpanWithoutReadLengthHeader(int length, scoped ref Span value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ ReadSpanWithoutReadLengthHeader(length, ref value);
+ return;
+#else
+ if (length == 0)
+ {
+ value = Array.Empty();
+ return;
+ }
+
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ if (value.Length != length)
+ {
+ value = AllocateUninitializedArray(length);
+ }
+
+ var byteCount = length * Unsafe.SizeOf();
+ ref var src = ref GetSpanReference(byteCount);
+ ref var dest = ref Unsafe.As(ref MemoryMarshal.GetReference(value)!);
+ Unsafe.CopyBlockUnaligned(ref dest, ref src, (uint)byteCount);
+
+ Advance(byteCount);
+ }
+ else
+ {
+ if (value.Length != length)
+ {
+ value = new T[length];
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ T.Deserialize(ref this, ref value[i]);
+ }
+ }
+#endif
+ }
}
diff --git a/src/MemoryPack.Core/MemoryPackWriter.cs b/src/MemoryPack.Core/MemoryPackWriter.cs
index b58226be..98a2a3c4 100644
--- a/src/MemoryPack.Core/MemoryPackWriter.cs
+++ b/src/MemoryPack.Core/MemoryPackWriter.cs
@@ -496,9 +496,82 @@ public void WriteSpan(scoped ReadOnlySpan value)
}
}
- #endregion
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WritePackableArray(T?[]? value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ WriteArray(value);
+ return;
+#else
+
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousWriteUnmanagedArray(value);
+ return;
+ }
+
+ if (value == null)
+ {
+ WriteNullCollectionHeader();
+ return;
+ }
+
+ WriteCollectionHeader(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ T.Serialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WritePackableSpan(scoped Span value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ WriteSpan(value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousWriteUnmanagedSpan(value);
+ return;
+ }
+
+ WriteCollectionHeader(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ T.Serialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WritePackableSpan(scoped ReadOnlySpan value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ WriteSpan(value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousWriteUnmanagedSpan(value);
+ return;
+ }
+
+ WriteCollectionHeader(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ T.Serialize(ref this, ref Unsafe.AsRef(value[i]));
+ }
+#endif
+ }
+
+#endregion
- #region WriteUnmanagedArray/Span
+#region WriteUnmanagedArray/Span
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUnmanagedArray(T[]? value)
@@ -589,7 +662,7 @@ public void DangerousWriteUnmanagedSpan(scoped ReadOnlySpan value)
Advance(allocSize);
}
- #endregion
+#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MemoryPack.Generator/Extensions.cs b/src/MemoryPack.Generator/Extensions.cs
index 18bde486..a017f607 100644
--- a/src/MemoryPack.Generator/Extensions.cs
+++ b/src/MemoryPack.Generator/Extensions.cs
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace MemoryPack.Generator;
@@ -61,10 +62,46 @@ public static IEnumerable GetAllMembers(this INamedTypeSymbol symbol, b
}
}
- public static bool IsWillImplementIMemoryPackable(this ITypeSymbol symbol, ReferenceSymbols references)
+ public static bool TryGetMemoryPackableType(this ITypeSymbol symbol, ReferenceSymbols references, out GenerateType generateType, out SerializeLayout serializeLayout)
{
- // [MemoryPackable] and not interface/abstract, generator will implmement IMemoryPackable
- return !symbol.IsAbstract && symbol.ContainsAttribute(references.MemoryPackableAttribute);
+ var packableCtorArgs = symbol.GetAttribute(references.MemoryPackableAttribute)?.ConstructorArguments;
+ generateType = GenerateType.Object;
+ serializeLayout = SerializeLayout.Sequential;
+ if (packableCtorArgs == null)
+ {
+ generateType = GenerateType.NoGenerate;
+ serializeLayout = SerializeLayout.Sequential;
+ return false;
+ }
+ else if (packableCtorArgs.Value.Length != 0)
+ {
+ // MemoryPackable has two attribtue
+ if (packableCtorArgs.Value.Length == 1)
+ {
+ // (SerializeLayout serializeLayout)
+ var ctorValue = packableCtorArgs.Value[0];
+ serializeLayout = (SerializeLayout)(ctorValue.Value ?? SerializeLayout.Sequential);
+ generateType = GenerateType.Object;
+ }
+ else
+ {
+ // (GenerateType generateType = GenerateType.Object, SerializeLayout serializeLayout = SerializeLayout.Sequential)
+ generateType = (GenerateType)(packableCtorArgs.Value[0].Value ?? GenerateType.Object);
+ serializeLayout = (SerializeLayout)(packableCtorArgs.Value[1].Value ?? SerializeLayout.Sequential);
+ if (generateType is GenerateType.VersionTolerant or GenerateType.CircularReference)
+ {
+ serializeLayout = SerializeLayout.Explicit; // version-torelant, always explicit.
+ }
+ }
+ }
+
+ if (symbol.IsStatic || symbol.IsAbstract)
+ {
+ // static or abstract class is Union
+ return false;
+ }
+
+ return true;
}
public static bool IsWillImplementMemoryPackUnion(this ITypeSymbol symbol, ReferenceSymbols references)
diff --git a/src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs b/src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs
index dcafa415..76b7730b 100644
--- a/src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs
+++ b/src/MemoryPack.Generator/MemoryPackGenerator.Emitter.cs
@@ -948,7 +948,7 @@ string EmitUnionSerializeBody()
var writeBody = UnionTags
.Select(x =>
{
- var method = x.Type.IsWillImplementIMemoryPackable(reference)
+ var method = (x.Type.TryGetMemoryPackableType(reference, out var genType, out _) && genType is GenerateType.Object or GenerateType.VersionTolerant or GenerateType.CircularReference)
? "WritePackable"
: "WriteValue";
return $" case {x.Tag}: writer.{method}(System.Runtime.CompilerServices.Unsafe.As<{TypeName}?, {ToUnionTagTypeFullyQualifiedToString(x.Type)}>(ref value)); break;";
@@ -985,7 +985,7 @@ string EmitUnionDeserializeBody()
{
var readBody = UnionTags.Select(x =>
{
- var method = x.Type.IsWillImplementIMemoryPackable(reference)
+ var method = (x.Type.TryGetMemoryPackableType(reference, out var genType, out _) && genType is GenerateType.Object or GenerateType.VersionTolerant or GenerateType.CircularReference)
? "ReadPackable"
: "ReadValue";
return $$"""
@@ -1099,6 +1099,10 @@ public string EmitSerialize(string writer)
return $"{writer}.WriteString(value.{Name});";
case MemberKind.UnmanagedArray:
return $"{writer}.WriteUnmanagedArray(value.{Name});";
+ case MemberKind.MemoryPackableArray:
+ return $"{writer}.WritePackableArray(value.{Name});";
+ case MemberKind.MemoryPackableList:
+ return $"MemoryPack.Formatters.ListFormatter.SerializePackable(ref {writer}, ref System.Runtime.CompilerServices.Unsafe.AsRef(value.{Name}));";
case MemberKind.Array:
return $"{writer}.WriteArray(value.{Name});";
case MemberKind.Blank:
@@ -1147,6 +1151,10 @@ public string EmitReadToDeserialize(int i, bool requireDeltaCheck)
return $"{pre}__{Name} = reader.ReadString();";
case MemberKind.UnmanagedArray:
return $"{pre}__{Name} = reader.ReadUnmanagedArray<{(MemberType as IArrayTypeSymbol)!.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>();";
+ case MemberKind.MemoryPackableArray:
+ return $"{pre}__{Name} = reader.ReadPackableArray<{(MemberType as IArrayTypeSymbol)!.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>();";
+ case MemberKind.MemoryPackableList:
+ return $"{pre}__{Name} = MemoryPack.Formatters.ListFormatter.DeserializePackable<{(MemberType as INamedTypeSymbol)!.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>(ref reader);";
case MemberKind.Array:
return $"{pre}__{Name} = reader.ReadArray<{(MemberType as IArrayTypeSymbol)!.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>();";
case MemberKind.Blank:
@@ -1177,6 +1185,10 @@ public string EmitReadRefDeserialize(int i, bool requireDeltaCheck)
return $"{pre}__{Name} = reader.ReadString();";
case MemberKind.UnmanagedArray:
return $"{pre}reader.ReadUnmanagedArray(ref __{Name});";
+ case MemberKind.MemoryPackableArray:
+ return $"{pre}reader.ReadPackableArray(ref __{Name});";
+ case MemberKind.MemoryPackableList:
+ return $"{pre}MemoryPack.Formatters.ListFormatter.DeserializePackable(ref reader, ref __{Name});";
case MemberKind.Array:
return $"{pre}reader.ReadArray(ref __{Name});";
case MemberKind.Blank:
diff --git a/src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs b/src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs
index a8246495..db1af342 100644
--- a/src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs
+++ b/src/MemoryPack.Generator/MemoryPackGenerator.Parser.cs
@@ -21,6 +21,10 @@ public enum MemberKind
String,
Array,
UnmanagedArray,
+ MemoryPackableArray, // T[] where T: IMemoryPackable
+ MemoryPackableList, // List where T: IMemoryPackable
+ MemoryPackableCollection, // GenerateType.Collection
+ MemoryPackableNoGenerate, // GenerateType.NoGenerate
Enum,
// from attribute
@@ -62,34 +66,9 @@ public TypeMeta(INamedTypeSymbol symbol, ReferenceSymbols reference)
this.reference = reference;
this.Symbol = symbol;
- var packableCtorArgs = symbol.GetAttribute(reference.MemoryPackableAttribute)?.ConstructorArguments;
- this.GenerateType = GenerateType.Object;
- if (packableCtorArgs == null)
- {
- this.GenerateType = GenerateType.NoGenerate;
- this.SerializeLayout = SerializeLayout.Sequential;
- }
- else if (packableCtorArgs.Value.Length != 0)
- {
- // MemoryPackable has two attribtue
- if (packableCtorArgs.Value.Length == 1)
- {
- // (SerializeLayout serializeLayout)
- var ctorValue = packableCtorArgs.Value[0];
- this.SerializeLayout = (SerializeLayout)(ctorValue.Value ?? SerializeLayout.Sequential);
- this.GenerateType = GenerateType.Object;
- }
- else
- {
- // (GenerateType generateType = GenerateType.Object, SerializeLayout serializeLayout = SerializeLayout.Sequential)
- this.GenerateType = (GenerateType)(packableCtorArgs.Value[0].Value ?? GenerateType.Object);
- this.SerializeLayout = (SerializeLayout)(packableCtorArgs.Value[1].Value ?? SerializeLayout.Sequential);
- if (this.GenerateType is GenerateType.VersionTolerant or GenerateType.CircularReference)
- {
- this.SerializeLayout = SerializeLayout.Explicit; // version-torelant, always explicit.
- }
- }
- }
+ symbol.TryGetMemoryPackableType(reference, out var generateType, out var serializeLayout);
+ this.GenerateType = generateType;
+ this.SerializeLayout = serializeLayout;
this.TypeName = symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
this.Constructor = ChooseConstructor(symbol, reference);
@@ -616,9 +595,20 @@ static MemberKind ParseMemberKind(ISymbol? memberSymbol, ITypeSymbol memberType,
{
return MemberKind.MemoryPackable;
}
- else if (memberType.IsWillImplementIMemoryPackable(references))
+ else if (memberType.TryGetMemoryPackableType(references, out var generateType, out var serializeLayout))
{
- return MemberKind.MemoryPackable;
+ switch (generateType)
+ {
+ case GenerateType.Object:
+ case GenerateType.VersionTolerant:
+ case GenerateType.CircularReference:
+ return MemberKind.MemoryPackable;
+ case GenerateType.Collection:
+ return MemberKind.MemoryPackableCollection;
+ case GenerateType.NoGenerate:
+ default:
+ return MemberKind.MemoryPackableNoGenerate;
+ }
}
else if (memberType.IsWillImplementMemoryPackUnion(references))
{
@@ -645,6 +635,11 @@ static MemberKind ParseMemberKind(ISymbol? memberSymbol, ITypeSymbol memberType,
}
else
{
+ if (elemType.TryGetMemoryPackableType(references, out var elemGenerateType, out _) && elemGenerateType is GenerateType.Object or GenerateType.VersionTolerant or GenerateType.CircularReference)
+ {
+ return MemberKind.MemoryPackableArray;
+ }
+
return MemberKind.Array;
}
}
@@ -677,6 +672,15 @@ static MemberKind ParseMemberKind(ISymbol? memberSymbol, ITypeSymbol memberType,
{
return MemberKind.Nullable;
}
+
+ if (nts.EqualsUnconstructedGenericType(references.KnownTypes.System_Collections_Generic_List_T))
+ {
+ if (nts.TypeArguments[0].TryGetMemoryPackableType(references, out var elemGenerateType, out _) && elemGenerateType is GenerateType.Object or GenerateType.VersionTolerant or GenerateType.CircularReference)
+ {
+ return MemberKind.MemoryPackableList;
+ }
+ return MemberKind.KnownType;
+ }
}
if (references.KnownTypes.Contains(memberType))
diff --git a/src/MemoryPack.Generator/MemoryPackGenerator.TypeScript.cs b/src/MemoryPack.Generator/MemoryPackGenerator.TypeScript.cs
index 4df399cb..ebd341c4 100644
--- a/src/MemoryPack.Generator/MemoryPackGenerator.TypeScript.cs
+++ b/src/MemoryPack.Generator/MemoryPackGenerator.TypeScript.cs
@@ -11,7 +11,7 @@ namespace MemoryPack.Generator;
partial class MemoryPackGenerator
{
- static TypeMeta? GenerateTypeScript(TypeDeclarationSyntax syntax, Compilation compilation, string typeScriptOutputDirectoryPath, in SourceProductionContext context,
+ static TypeMeta? GenerateTypeScript(TypeDeclarationSyntax syntax, Compilation compilation, string typeScriptOutputDirectoryPath,string importExt, in SourceProductionContext context,
ReferenceSymbols reference, IReadOnlyDictionary unionMap)
{
var semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree);
@@ -44,9 +44,9 @@ partial class MemoryPackGenerator
var sb = new StringBuilder();
- sb.AppendLine("""
-import { MemoryPackWriter } from "./MemoryPackWriter.js";
-import { MemoryPackReader } from "./MemoryPackReader.js";
+ sb.AppendLine($$"""
+import { MemoryPackWriter } from "./MemoryPackWriter{{importExt}}";
+import { MemoryPackReader } from "./MemoryPackReader{{importExt}}";
""");
var collector = new TypeCollector();
@@ -68,17 +68,17 @@ partial class MemoryPackGenerator
// add import(enum, union, memorypackable)
foreach (var item in collector.GetEnums())
{
- sb.AppendLine($"import {{ {item.Name} }} from \"./{item.Name}.js\"; ");
+ sb.AppendLine($"import {{ {item.Name} }} from \"./{item.Name}{importExt}\"; ");
}
foreach (var item in collector.GetMemoryPackableTypes(reference).Where(x => !SymbolEqualityComparer.Default.Equals(x, typeSymbol)))
{
- sb.AppendLine($"import {{ {item.Name} }} from \"./{item.Name}.js\"; ");
+ sb.AppendLine($"import {{ {item.Name} }} from \"./{item.Name}{importExt}\"; ");
}
sb.AppendLine();
try
{
- typeMeta.EmitTypescript(sb, unionMap);
+ typeMeta.EmitTypescript(sb, unionMap, importExt);
}
catch (NotSupportedTypeException ex)
{
@@ -165,11 +165,11 @@ static bool Validate(TypeMeta type, TypeDeclarationSyntax syntax, in SourceProdu
public partial class TypeMeta
{
- public void EmitTypescript(StringBuilder sb, IReadOnlyDictionary unionMap)
+ public void EmitTypescript(StringBuilder sb, IReadOnlyDictionary unionMap, string importExt)
{
if (IsUnion)
{
- EmitTypeScriptUnion(sb);
+ EmitTypeScriptUnion(sb, importExt);
return;
}
@@ -249,7 +249,7 @@ static deserializeArrayCore(reader: MemoryPackReader): ({{TypeName}} | null)[] |
sb.AppendLine(code);
}
- public void EmitTypeScriptUnion(StringBuilder sb)
+ public void EmitTypeScriptUnion(StringBuilder sb, string importExt)
{
string EmitUnionSerialize()
{
@@ -282,7 +282,7 @@ string EmitUnionDeserialize()
foreach (var item in UnionTags)
{
- sb.AppendLine($"import {{ {item.Type.Name} }} from \"./{item.Type.Name}.js\"; ");
+ sb.AppendLine($"import {{ {item.Type.Name} }} from \"./{item.Type.Name}{importExt}\"; ");
}
sb.AppendLine();
diff --git a/src/MemoryPack.Generator/MemoryPackGenerator.cs b/src/MemoryPack.Generator/MemoryPackGenerator.cs
index 492d0210..2ba55e58 100644
--- a/src/MemoryPack.Generator/MemoryPackGenerator.cs
+++ b/src/MemoryPack.Generator/MemoryPackGenerator.cs
@@ -100,12 +100,18 @@ void RegisterTypeScript(IncrementalGeneratorInitializationContext context)
var typeScriptEnabled = context.AnalyzerConfigOptionsProvider
.Select((configOptions, token) =>
{
- if (configOptions.GlobalOptions.TryGetValue("build_property.MemoryPackGenerator_TypeScriptOutputDirectory", out var path))
+ string? path;
+ if (!configOptions.GlobalOptions.TryGetValue("build_property.MemoryPackGenerator_TypeScriptOutputDirectory", out path))
{
- return path;
+ path = null;
+ }
+ string ext;
+ if (!configOptions.GlobalOptions.TryGetValue("build_property.MemoryPackGenerator_TypeScriptImportExtension", out ext!))
+ {
+ ext = ".js";
}
- return (string?)null;
+ return (path, ext);
});
var typeScriptDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName(
@@ -125,7 +131,7 @@ or RecordDeclarationSyntax
.Combine(context.CompilationProvider)
.WithComparer(Comparer.Instance)
.Combine(typeScriptEnabled)
- .Where(x => x.Right != null) // filter, exists TypeScriptOutputDirectory
+ .Where(x => x.Right.path != null) // filter, exists TypeScriptOutputDirectory
.Collect();
context.RegisterSourceOutput(typeScriptGenerateSource, static (context, source) =>
@@ -166,14 +172,15 @@ or RecordDeclarationSyntax
{
var typeDeclaration = item.Left.Item1;
var compilation = item.Left.Item2;
- var path = generatePath = item.Right!;
+ var path = generatePath = item.Right.path!;
+ var importExt = item.Right.ext;
if (reference == null)
{
reference = new ReferenceSymbols(compilation);
}
- var meta = GenerateTypeScript(typeDeclaration, compilation, path, context, reference, unionMap);
+ var meta = GenerateTypeScript(typeDeclaration, compilation, path, importExt, context, reference, unionMap);
if (meta != null)
{
collector.Visit(meta, false);
diff --git a/src/MemoryPack.Generator/ReferenceSymbols.cs b/src/MemoryPack.Generator/ReferenceSymbols.cs
index 88867b01..7abc8aad 100644
--- a/src/MemoryPack.Generator/ReferenceSymbols.cs
+++ b/src/MemoryPack.Generator/ReferenceSymbols.cs
@@ -65,6 +65,7 @@ public class WellKnownTypes
public INamedTypeSymbol System_Collections_Generic_ICollection_T { get; }
public INamedTypeSymbol System_Collections_Generic_ISet_T { get; }
public INamedTypeSymbol System_Collections_Generic_IDictionary_T { get; }
+ public INamedTypeSymbol System_Collections_Generic_List_T { get; }
public INamedTypeSymbol System_Guid { get; }
public INamedTypeSymbol System_Version { get; }
@@ -177,6 +178,7 @@ public WellKnownTypes(ReferenceSymbols parent)
System_Collections_Generic_ICollection_T = GetTypeByMetadataName("System.Collections.Generic.ICollection`1").ConstructUnboundGenericType();
System_Collections_Generic_ISet_T = GetTypeByMetadataName("System.Collections.Generic.ISet`1").ConstructUnboundGenericType();
System_Collections_Generic_IDictionary_T = GetTypeByMetadataName("System.Collections.Generic.IDictionary`2").ConstructUnboundGenericType();
+ System_Collections_Generic_List_T = GetTypeByMetadataName("System.Collections.Generic.List`1").ConstructUnboundGenericType();
System_Guid = GetTypeByMetadataName("System.Guid");
System_Version = GetTypeByMetadataName("System.Version");
System_Uri = GetTypeByMetadataName("System.Uri");
diff --git a/src/MemoryPack.Generator/TypeScriptMember.cs b/src/MemoryPack.Generator/TypeScriptMember.cs
index 281ea5af..d3a9b072 100644
--- a/src/MemoryPack.Generator/TypeScriptMember.cs
+++ b/src/MemoryPack.Generator/TypeScriptMember.cs
@@ -164,7 +164,7 @@ TypeScriptType ConvertToTypeScriptType(ITypeSymbol symbol, ReferenceSymbols refe
break;
}
- if (symbol.IsWillImplementIMemoryPackable(references) || symbol.IsWillImplementMemoryPackUnion(references))
+ if (symbol.TryGetMemoryPackableType(references, out _, out _) || symbol.IsWillImplementMemoryPackUnion(references))
{
return new TypeScriptType
{
diff --git a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/Formatters/CollectionFormatters.cs b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/Formatters/CollectionFormatters.cs
index 5f915891..111988ec 100644
--- a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/Formatters/CollectionFormatters.cs
+++ b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/Formatters/CollectionFormatters.cs
@@ -12,7 +12,6 @@
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
-using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -58,6 +57,87 @@ public static partial class MemoryPackFormatterProvider
namespace MemoryPack.Formatters
{
+ [Preserve]
+ public static class ListFormatter
+ {
+ [Preserve]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void SerializePackable(ref MemoryPackWriter writer, ref List? value)
+ where T : IMemoryPackable
+#if NET7_0_OR_GREATER
+
+#else
+
+#endif
+ {
+ if (value == null)
+ {
+ writer.WriteNullCollectionHeader();
+ return;
+ }
+
+#if NET7_0_OR_GREATER
+ writer.WritePackableSpan(CollectionsMarshal.AsSpan(value));
+#else
+ var formatter = writer.GetFormatter();
+ writer.WriteCollectionHeader(value.Count);
+ foreach (var item in value)
+ {
+ var v = item;
+ formatter.Serialize(ref writer, ref v);
+ }
+#endif
+ }
+
+ [Preserve]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static List? DeserializePackable(ref MemoryPackReader reader)
+ where T : IMemoryPackable
+ {
+ List? value = default;
+ DeserializePackable(ref reader, ref value);
+ return value;
+ }
+
+ [Preserve]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void DeserializePackable(ref MemoryPackReader reader, ref List? value)
+ where T : IMemoryPackable
+ {
+ if (!reader.TryReadCollectionHeader(out var length))
+ {
+ value = null;
+ return;
+ }
+
+ if (value == null)
+ {
+ value = new List(length);
+ }
+#if NET7_0_OR_GREATER
+ else if (value.Count == length)
+ {
+ value.Clear();
+ }
+
+ var span = CollectionsMarshalEx.CreateSpan(value, length);
+ reader.ReadPackableSpanWithoutReadLengthHeader(length, ref span);
+#else
+ else
+ {
+ value.Clear();
+ }
+ var formatter = reader.GetFormatter();
+ for (var i = 0; i < length; i++)
+ {
+ T? v = default;
+ formatter.Deserialize(ref reader, ref v);
+ value.Add(v);
+ }
+#endif
+ }
+ }
+
[Preserve]
public sealed class ListFormatter : MemoryPackFormatter>
{
diff --git a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackReader.cs b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackReader.cs
index 3da20b34..6a533b3b 100644
--- a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackReader.cs
+++ b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackReader.cs
@@ -429,6 +429,7 @@ public void ReadValueWithFormatter(TFormatter formatter, ref T? v
#region ReadArray/Span
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T?[]? ReadArray()
{
T?[]? value = default;
@@ -436,6 +437,7 @@ public void ReadValueWithFormatter(TFormatter formatter, ref T? v
return value;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadArray(ref T?[]? value)
{
if (!RuntimeHelpers.IsReferenceOrContainsReferences())
@@ -469,6 +471,7 @@ public void ReadArray(ref T?[]? value)
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadSpan(ref Span value)
{
if (!RuntimeHelpers.IsReferenceOrContainsReferences())
@@ -501,22 +504,111 @@ public void ReadSpan(ref Span value)
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T?[]? ReadPackableArray()
+ where T : IMemoryPackable
+ {
+ T?[]? value = default;
+ ReadPackableArray(ref value);
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReadPackableArray(ref T?[]? value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ ReadArray(ref value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousReadUnmanagedArray(ref value);
+ return;
+ }
+
+ if (!TryReadCollectionHeader(out var length))
+ {
+ value = null;
+ return;
+ }
+
+ if (length == 0)
+ {
+ value = Array.Empty();
+ return;
+ }
+
+ // T[] support overwrite
+ if (value == null || value.Length != length)
+ {
+ value = new T[length];
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ T.Deserialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReadPackableSpan(ref Span value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ ReadSpan(ref value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousReadUnmanagedSpan(ref value);
+ return;
+ }
+
+ if (!TryReadCollectionHeader(out var length))
+ {
+ value = default;
+ return;
+ }
+
+ if (length == 0)
+ {
+ value = Array.Empty();
+ return;
+ }
+
+ if (value.Length != length)
+ {
+ value = new T[length];
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ T.Deserialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
#endregion
#region UnmanagedArray/Span
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[]? ReadUnmanagedArray()
where T : unmanaged
{
return DangerousReadUnmanagedArray();
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadUnmanagedArray(ref T[]? value)
where T : unmanaged
{
DangerousReadUnmanagedArray(ref value);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadUnmanagedSpan(ref Span value)
where T : unmanaged
{
@@ -524,6 +616,7 @@ public void ReadUnmanagedSpan(ref Span value)
}
// T: should be unamanged type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T[]? DangerousReadUnmanagedArray()
{
if (!TryReadCollectionHeader(out var length))
@@ -542,6 +635,7 @@ public void ReadUnmanagedSpan(ref Span value)
return dest;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void DangerousReadUnmanagedArray(ref T[]? value)
{
if (!TryReadCollectionHeader(out var length))
@@ -570,6 +664,7 @@ public unsafe void DangerousReadUnmanagedArray(ref T[]? value)
Advance(byteCount);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void DangerousReadUnmanagedSpan(ref Span value)
{
if (!TryReadCollectionHeader(out var length))
@@ -600,6 +695,7 @@ public unsafe void DangerousReadUnmanagedSpan(ref Span value)
#endregion
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadSpanWithoutReadLengthHeader(int length, ref Span value)
{
if (length == 0)
@@ -636,6 +732,49 @@ public void ReadSpanWithoutReadLengthHeader(int length, ref Span value)
}
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReadPackableSpanWithoutReadLengthHeader(int length, ref Span value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ ReadSpanWithoutReadLengthHeader(length, ref value);
+ return;
+#else
+ if (length == 0)
+ {
+ value = Array.Empty();
+ return;
+ }
+
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ if (value.Length != length)
+ {
+ value = AllocateUninitializedArray(length);
+ }
+
+ var byteCount = length * Unsafe.SizeOf();
+ ref var src = ref GetSpanReference(byteCount);
+ ref var dest = ref Unsafe.As(ref MemoryMarshal.GetReference(value)!);
+ Unsafe.CopyBlockUnaligned(ref dest, ref src, (uint)byteCount);
+
+ Advance(byteCount);
+ }
+ else
+ {
+ if (value.Length != length)
+ {
+ value = new T[length];
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ T.Deserialize(ref this, ref value[i]);
+ }
+ }
+#endif
+ }
}
}
\ No newline at end of file
diff --git a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackWriter.cs b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackWriter.cs
index e59d2366..3c629e7e 100644
--- a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackWriter.cs
+++ b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Core/MemoryPackWriter.cs
@@ -505,9 +505,82 @@ public void WriteSpan(ReadOnlySpan value)
}
}
- #endregion
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WritePackableArray(T?[]? value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ WriteArray(value);
+ return;
+#else
+
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousWriteUnmanagedArray(value);
+ return;
+ }
+
+ if (value == null)
+ {
+ WriteNullCollectionHeader();
+ return;
+ }
+
+ WriteCollectionHeader(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ T.Serialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WritePackableSpan(Span value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ WriteSpan(value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousWriteUnmanagedSpan(value);
+ return;
+ }
+
+ WriteCollectionHeader(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ T.Serialize(ref this, ref value[i]);
+ }
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WritePackableSpan(ReadOnlySpan value)
+ where T : IMemoryPackable
+ {
+#if !NET7_0_OR_GREATER
+ WriteSpan(value);
+ return;
+#else
+ if (!RuntimeHelpers.IsReferenceOrContainsReferences())
+ {
+ DangerousWriteUnmanagedSpan(value);
+ return;
+ }
+
+ WriteCollectionHeader(value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ T.Serialize(ref this, ref Unsafe.AsRef(value[i]));
+ }
+#endif
+ }
+
+#endregion
- #region WriteUnmanagedArray/Span
+#region WriteUnmanagedArray/Span
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUnmanagedArray(T[]? value)
@@ -598,7 +671,7 @@ public void DangerousWriteUnmanagedSpan(ReadOnlySpan value)
Advance(allocSize);
}
- #endregion
+#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Generator/MemoryPack.Generator.Roslyn3.dll b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Generator/MemoryPack.Generator.Roslyn3.dll
index 32fb1ca1..c7bcdf31 100644
Binary files a/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Generator/MemoryPack.Generator.Roslyn3.dll and b/src/MemoryPack.Unity/Assets/Plugins/MemoryPack/Runtime/MemoryPack.Generator/MemoryPack.Generator.Roslyn3.dll differ
diff --git a/tests/MemoryPack.Tests/ArrayTest.cs b/tests/MemoryPack.Tests/ArrayTest.cs
index f3b26ccc..d6b13e08 100644
--- a/tests/MemoryPack.Tests/ArrayTest.cs
+++ b/tests/MemoryPack.Tests/ArrayTest.cs
@@ -28,4 +28,27 @@ public void Check()
v2!.Array3.Should().Equal(checker.Array3);
v2!.Array4.Should().Equal(checker.Array4);
}
+
+ [Fact]
+ public void Check2()
+ {
+ var checker = new ArrayOptimizeCheck()
+ {
+ Array1 = new[] { new StandardTypeTwo { One = 9, Two = 2 }, new StandardTypeTwo { One = 999, Two = 444 } },
+ List1 = new List { new StandardTypeTwo { One = 93, Two = 12 }, new StandardTypeTwo { One = 9499, Two = 45344 } }
+ };
+
+ var bin = MemoryPackSerializer.Serialize(checker);
+ var v2 = MemoryPackSerializer.Deserialize(bin);
+#pragma warning disable CS8602
+ v2!.Array1[0].One.Should().Be(checker.Array1[0].One);
+ v2!.Array1[0].Two.Should().Be(checker.Array1[0].Two);
+ v2!.Array1[1].One.Should().Be(checker.Array1[1].One);
+ v2!.Array1[1].Two.Should().Be(checker.Array1[1].Two);
+
+ v2!.List1[0].One.Should().Be(checker.List1[0].One);
+ v2!.List1[0].Two.Should().Be(checker.List1[0].Two);
+ v2!.List1[1].One.Should().Be(checker.List1[1].One);
+ v2!.List1[1].Two.Should().Be(checker.List1[1].Two);
+ }
}
diff --git a/tests/MemoryPack.Tests/Models/Arrays.cs b/tests/MemoryPack.Tests/Models/Arrays.cs
index 322451e4..c2d4eb5f 100644
--- a/tests/MemoryPack.Tests/Models/Arrays.cs
+++ b/tests/MemoryPack.Tests/Models/Arrays.cs
@@ -15,3 +15,11 @@ public partial class ArrayCheck
public string[]? Array3 { get; set; }
public string?[]? Array4 { get; set; }
}
+
+
+[MemoryPackable]
+public partial class ArrayOptimizeCheck
+{
+ public StandardTypeTwo?[]? Array1 { get; set; }
+ public List? List1 { get; set; }
+}