Skip to content

Commit

Permalink
nino v2.0.3
Browse files Browse the repository at this point in the history
- [Fix] Fix compilation error for special assembly names
- [Optimisation] Faster deserialisation
  • Loading branch information
JasonXuDeveloper committed Jul 8, 2024
1 parent 19ce0d0 commit 691943d
Show file tree
Hide file tree
Showing 15 changed files with 119 additions and 118 deletions.
16 changes: 8 additions & 8 deletions Docs/Serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,16 @@ public partial struct NotAutoCollectStruct
## 序列化

```csharp
byte[] Nino.Serializer.Serialize(可Nino序列化类型 val);
void Nino.Serializer.Serialize(可Nino序列化类型 val, IBufferWriter<byte> bufferWriter);
byte[] Serializer.Serialize(可Nino序列化类型 val);
void Serializer.Serialize(可Nino序列化类型 val, IBufferWriter<byte> bufferWriter);
```

示范:

```csharp
//懒人写法
ObjClass obj = new ObjClass();
byte[] byteArr = Nino.Serializer.Serialize(obj);
byte[] byteArr = Serializer.Serialize(obj);
//
byteArr = obj.Serialize();

Expand All @@ -135,7 +135,7 @@ byteArr = obj.Serialize();
## 反序列化

```csharp
void Nino.Deserializer.Deserialize(ReadOnlySpan<byte> data, outNino序列化类型 value);
void Deserializer.Deserialize(ReadOnlySpan<byte> data, outNino序列化类型 value);
```

> data不仅可以传```byte[]```,还可以```ArraySegment<byte>``````Span<byte>```
Expand All @@ -144,7 +144,7 @@ void Nino.Deserializer.Deserialize(ReadOnlySpan<byte> data, out 可Nino序列化

```csharp
//假设这里byteArr是byte[]
Nino.Deserializer.Deserialize(byteArr, out ObjClass obj);
Deserializer.Deserialize(byteArr, out ObjClass obj);
...
//高级用法,假设网络层传来了数据(比如Pipeline),我们收到了ReadOnlySequence<byte>
//这样写性能最最最最好
Expand All @@ -153,21 +153,21 @@ ObjClass obj;
if(data.IsSingleSegment)
{
Span<byte> dataSpan = data.FirstSpan;
Nino.Deserializer.Deserialize(dataSpan, out ObjClass obj);
Deserializer.Deserialize(dataSpan, out ObjClass obj);
}
else
{
if(data.Length <= 1024)
{
Span<byte> stackMemory = stackalloc byte[(int)data.Length];
data.CopyTo(stackMemory);
Nino.Deserializer.Deserialize(stackMemory, out ObjClass obj);
Deserializer.Deserialize(stackMemory, out ObjClass obj);
}
else
{
byte[] arr = ArrayPool<byte>.Shared.Rent((int)data.Length);
data.CopyTo(arr);
Nino.Deserializer.Deserialize(arr, out ObjClass obj);
Deserializer.Deserialize(arr, out ObjClass obj);
ArrayPool<byte>.Shared.Return(arr);
}
}
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ Definite useful and high performance serialisation library for C# projects, espe

- [Test10](Nino_Unity/Assets/Nino/Test/Editor/Serialization/Test10.cs) 复杂结构类型序列化反序列化

- [Test11](Nino_Unity/Assets/Nino/Test/Editor/Serialization/Test11.cs) ILRuntime测试(需要搭配使用教程启用ILRuntime)

- [真机测试](Nino_Unity/Assets/Nino/Test/BuildTest.cs) 可以打IL2CPP或Mono包进行测试(对比了Nino Reflection/Code Gen与Protobuf-net/BinaryFormatter/Bson/MsgPack Code Gen的序列化性能、序列化体积、反序列化性能)

> Protobuf-net与Bson在IL2CPP下暂不支持字典序列化
Expand All @@ -83,5 +81,5 @@ Definite useful and high performance serialisation library for C# projects, espe
NuGet里搜```Nino```

```bash
PM> Install-Package Nino -Version 2.0.2
PM> Install-Package Nino -Version 2.0.3
```
1 change: 0 additions & 1 deletion src/Nino.Benchmark/Nino.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12"/>
Expand Down
2 changes: 1 addition & 1 deletion src/Nino.Benchmark/SimpleTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#nullable disable
namespace Nino.Benchmark;

[CategoriesColumn, MinColumn, MaxColumn, PayloadColumn]
[MinColumn, MaxColumn, PayloadColumn]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[SimpleJob(RuntimeMoniker.Net80, warmupCount: 3, iterationCount: 10)]
[MarkdownExporter, AsciiDocExporter, HtmlExporter, CsvExporter, RPlotExporter]
Expand Down
8 changes: 4 additions & 4 deletions src/Nino.Core/Nino.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
<PropertyGroup>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworks>net5.0;net6.0;netstandard2.1</TargetFrameworks>
<LangVersion>9</LangVersion>
<PackageId>Nino.Serialization</PackageId>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.0.2</Version>
<Version>2.0.3</Version>
<Title>Nino.Serialization</Title>
<Authors>JasonXuDeveloper</Authors>
<Description>High performance and low size binary serialization solution, especially for Unity.</Description>
<Copyright>JasonXuDeveloper</Copyright>
<RepositoryUrl>https://github.com/JasonXuDeveloper/Nino</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>Nino;Serialization;Binary</PackageTags>
<PackageReleaseNotes>Nino.Serialization v2.0.2
- [Optimisation] More efficient code generation for unmanaged types and arrays</PackageReleaseNotes>
<PackageReleaseNotes>Nino.Serialization v2.0.3
- [Fix] Fix complilation error for special assembly names
- [Optimisation] Faster deserialisation</PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>

Expand Down
4 changes: 2 additions & 2 deletions src/Nino.Core/Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Nino.Core
{
public unsafe ref struct Reader
public ref struct Reader
{
private SpanBufferReader _bufferReader;

Expand Down Expand Up @@ -49,7 +49,7 @@ public void Read<T>(out T[] ret) where T : unmanaged
return;
case TypeCollector.CollectionTypeId:
Read(out int length);
_bufferReader.GetBytes(length * sizeof(T), out var bytes);
_bufferReader.GetBytes(length * Unsafe.SizeOf<T>(), out var bytes);
#if NET5_0_OR_GREATER
ret = GC.AllocateUninitializedArray<T>(length);
Unsafe.CopyBlockUnaligned(ref Unsafe.As<T, byte>(ref ret[0]), ref MemoryMarshal.GetReference(bytes),
Expand Down
4 changes: 2 additions & 2 deletions src/Nino.Core/SpanBufferReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Nino.Core
{
internal unsafe ref struct SpanBufferReader
internal ref struct SpanBufferReader
{
private ReadOnlySpan<byte> data;

Expand All @@ -21,7 +21,7 @@ public SpanBufferReader(ReadOnlySpan<byte> data)
public void Get<T>(out T value) where T : unmanaged
{
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(data));
data = data.Slice(sizeof(T));
data = data.Slice(Unsafe.SizeOf<T>());
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
10 changes: 4 additions & 6 deletions src/Nino.Core/Writer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void Write(bool value)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Write(string value)
public void Write(string value)
{
switch (value)
{
Expand All @@ -176,11 +176,9 @@ public unsafe void Write(string value)
var span = _bufferWriter.GetSpan(sizeof(ushort) + sizeof(int) + byteLength);
Unsafe.WriteUnaligned(ref span[0], TypeCollector.StringTypeId);
Unsafe.WriteUnaligned(ref span[2], value.Length);
fixed (char* p = value)
{
Unsafe.CopyBlockUnaligned(ref span[6], ref Unsafe.AsRef<byte>(p), (uint)byteLength);
}

ref var valueRef = ref MemoryMarshal.GetReference(value.AsSpan());
ref byte valueByte = ref Unsafe.As<char, byte>(ref valueRef);
Unsafe.CopyBlockUnaligned(ref span[6], ref valueByte, (uint)byteLength);
_bufferWriter.Advance(sizeof(ushort) + sizeof(int) + byteLength);
break;
}
Expand Down
39 changes: 20 additions & 19 deletions src/Nino.Generator/DeserializerGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ int GetId(string typeFullName)

continue;
}

var typeSymbol = compilation.GetTypeByMetadataName(typeFullName);
if (typeSymbol == null)
{
Expand Down Expand Up @@ -148,22 +148,9 @@ int GetId(string typeFullName)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Deserialize(out {{typeFullName}} value, ref Reader reader)
{
value = default;
reader.Read(out ushort typeId);
""");
// only applicable for reference types
bool isReferenceType = model is ClassDeclarationSyntax;
if (isReferenceType)
{
sb.AppendLine("""
if (typeId == TypeCollector.NullTypeId)
{
return;
}
""");
}

void WriteMembers(List<MemberDeclarationSyntax> members, string valName)
{
Expand Down Expand Up @@ -205,6 +192,17 @@ void WriteMembers(List<MemberDeclarationSyntax> members, string valName)
sb.AppendLine($" switch (typeId)");
sb.AppendLine(" {");

// only applicable for reference types
bool isReferenceType = model is ClassDeclarationSyntax;
if (isReferenceType)
{
sb.AppendLine("""
case TypeCollector.NullTypeId:
value = null;
return;
""");
}

foreach (var subType in lst)
{
subTypes.AppendLine(
Expand All @@ -224,14 +222,14 @@ void WriteMembers(List<MemberDeclarationSyntax> members, string valName)
members = members.Distinct().ToList();
WriteMembers(members, valName);
sb.AppendLine($" value = {valName};");
sb.AppendLine(" break;");
sb.AppendLine(" return;");
}

sb.AppendLine($" case {GetId(typeFullName)}:");
sb.AppendLine($" value = new {typeFullName}();");
var defaultMembers = model.GetNinoTypeMembers(null);
WriteMembers(defaultMembers, "value");
sb.AppendLine(" break;");
sb.AppendLine(" return;");

sb.AppendLine(" default:");
sb.AppendLine(
Expand All @@ -248,11 +246,14 @@ void WriteMembers(List<MemberDeclarationSyntax> members, string valName)
}

var curNamespace = $"{compilation.AssemblyName!}";
if(!string.IsNullOrEmpty(curNamespace))
curNamespace = $"{curNamespace}_Nino";
if (!string.IsNullOrEmpty(curNamespace))
curNamespace = $"{curNamespace}_";
if (!char.IsLetter(curNamespace[0]))
curNamespace = $"_{curNamespace}";
//replace special characters with _
curNamespace = new string(curNamespace.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray());

curNamespace += "Nino";

// generate code
var code = $$"""
// <auto-generated/>
Expand Down
87 changes: 45 additions & 42 deletions src/Nino.Generator/EmbedTypeDeserializerGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,14 @@ bool IsTypeParameter(ITypeSymbol ts)
}

var curNamespace = $"{compilation.AssemblyName!}";
if(!string.IsNullOrEmpty(curNamespace))
curNamespace = $"{curNamespace}_Nino";
if (!string.IsNullOrEmpty(curNamespace))
curNamespace = $"{curNamespace}_";
if (!char.IsLetter(curNamespace[0]))
curNamespace = $"_{curNamespace}";
//replace special characters with _
curNamespace = new string(curNamespace.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray());

curNamespace += "Nino";

// generate code
var code = $$"""
// <auto-generated/>
Expand Down Expand Up @@ -314,23 +317,23 @@ private static string GenerateArrayCollectionSerialization(string elemType, stri
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Deserialize(out {{elemType}}[] value, ref Reader reader)
{
value = null;
reader.Read(out ushort typeId);
if (typeId == TypeCollector.NullTypeId)
{
return;
}
if (typeId != TypeCollector.CollectionTypeId)
{
throw new InvalidOperationException($"Invalid type id {typeId}");
}
reader.Read(out int length);
value = new {{creationDecl}};
for (int i = 0; i < length; i++)
switch (typeId)
{
Deserialize(out value[i], ref reader);
case TypeCollector.NullTypeId:
value = null;
return;
case TypeCollector.CollectionTypeId:
reader.Read(out int length);
value = new {{creationDecl}};
for (int i = 0; i < length; i++)
{
Deserialize(out value[i], ref reader);
}
break;
default:
throw new InvalidOperationException($"Invalid type id {typeId}");
}
}
Expand All @@ -347,24 +350,24 @@ private static string GenerateDictionarySerialization(string type1, string type2
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Deserialize(out {{typeFullName}} value, ref Reader reader)
{
value = null;
reader.Read(out ushort typeId);
if (typeId == TypeCollector.NullTypeId)
{
return;
}
if (typeId != TypeCollector.CollectionTypeId)
{
throw new InvalidOperationException($"Invalid type id {typeId}");
}
reader.Read(out int length);
value = new Dictionary<{{type1}}, {{type2}}>(length);
for (int i = 0; i < length; i++)
switch (typeId)
{
Deserialize(out KeyValuePair<{{type1}}, {{type2}}> kvp, ref reader);
value.Add(kvp.Key, kvp.Value);
case TypeCollector.NullTypeId:
value = null;
return;
case TypeCollector.CollectionTypeId:
reader.Read(out int length);
value = new Dictionary<{{type1}}, {{type2}}>(length);
for (int i = 0; i < length; i++)
{
Deserialize(out KeyValuePair<{{type1}}, {{type2}}> kvp, ref reader);
value.Add(kvp.Key, kvp.Value);
}
return;
default:
throw new InvalidOperationException($"Invalid type id {typeId}");
}
}
Expand Down Expand Up @@ -405,19 +408,19 @@ private static void GenerateNullableStructMethods(StringBuilder sb, string typeF
private static void Deserialize(out {{typeFullName}}? value, ref Reader reader)
{
reader.Read(out ushort typeId);
if (typeId == TypeCollector.NullTypeId)
{
value = null;
return;
}
if (typeId != TypeCollector.NullableTypeId)
switch (typeId)
{
throw new InvalidOperationException($"Invalid type id {typeId}");
case TypeCollector.NullTypeId:
value = null;
return;
case TypeCollector.NullableTypeId:
Deserialize(out {{typeFullName}} ret, ref reader);
value = ret;
return;
default:
throw new InvalidOperationException($"Invalid type id {typeId}");
}
Deserialize(out {{typeFullName}} ret, ref reader);
value = ret;
}
""");
Expand Down
Loading

0 comments on commit 691943d

Please sign in to comment.