Skip to content

Commit fe9fa08

Browse files
authored
Merge pull request #2 from gllebede/master
MessagePack update
2 parents 6caf299 + 3d1b75b commit fe9fa08

File tree

177 files changed

+11833
-4014
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

177 files changed

+11833
-4014
lines changed

Directory.Build.props

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Visible="false" />
2929
</ItemGroup>
3030
<ItemGroup>
31-
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
32-
<PackageReference Include="Nerdbank.GitVersioning" Version="3.2.7-beta" PrivateAssets="all" />
33-
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.5.124-alpha" PrivateAssets="All" />
34-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
35-
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164" PrivateAssets="all" />
31+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="All" />
32+
<PackageReference Include="Nerdbank.GitVersioning" Version="3.4.244" PrivateAssets="all" />
33+
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.0.64" PrivateAssets="All" />
34+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
35+
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.261" PrivateAssets="all" />
3636
</ItemGroup>
3737

3838
<Target Name="PrepareReleaseNotes" BeforeTargets="GenerateNuspec" DependsOnTargets="GetBuildVersion">

Directory.Build.rsp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#------------------------------------------------------------------------------
2+
# This file contains command-line options that MSBuild will process as part of
3+
# every build, unless the "/noautoresponse" switch is specified.
4+
#
5+
# MSBuild processes the options in this file first, before processing the
6+
# options on the command line. As a result, options on the command line can
7+
# override the options in this file. However, depending on the options being
8+
# set, the overriding can also result in conflicts.
9+
#
10+
# NOTE: The "/noautoresponse" switch cannot be specified in this file, nor in
11+
# any response file that is referenced by this file.
12+
#------------------------------------------------------------------------------
13+
/nr:false
14+
/m
15+
/verbosity:minimal
16+
/clp:Summary;ForceNoAlign

MessagePack.sln

Lines changed: 7 additions & 115 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 116 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Releases](https://img.shields.io/github/release/neuecc/MessagePack-CSharp.svg)][Releases]
66

77
[![Join the chat at https://gitter.im/MessagePack-CSharp/Lobby](https://badges.gitter.im/MessagePack-CSharp/Lobby.svg)](https://gitter.im/MessagePack-CSharp/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8-
[![Build Status](https://dev.azure.com/ils0086/MessagePack-CSharp/_apis/build/status/MessagePack-CSharp-CI)](https://dev.azure.com/ils0086/MessagePack-CSharp/_build/latest?definitionId=2)
8+
[![Build Status](https://dev.azure.com/ils0086/MessagePack-CSharp/_apis/build/status/MessagePack-CSharp-CI?branchName=master)](https://dev.azure.com/ils0086/MessagePack-CSharp/_build/latest?definitionId=2&branchName=master)
99

1010
The extremely fast [MessagePack](http://msgpack.org/) serializer for C#.
1111
It is 10x faster than [MsgPack-Cli](https://github.com/msgpack/msgpack-cli) and outperforms other C# serializers. MessagePack for C# also ships with built-in support for LZ4 compression - an extremely fast compression algorithm. Performance is important, particularly in applications like games, distributed computing, microservices, or data caches.
@@ -34,6 +34,7 @@ MessagePack has a compact binary size and a full set of general purpose expressi
3434
- [Security](#security)
3535
- [Performance](#performance)
3636
- [Deserialization Performance for different options](#deserialization-performance-for-different-options)
37+
- [String interning](#string-interning)
3738
- [LZ4 Compression](#lz4-compression)
3839
- [Attributions](#attributions)
3940
- [Comparison with protobuf, JSON, ZeroFormatter](#comparison-with-protobuf-json-zeroformatter)
@@ -184,7 +185,7 @@ These types can serialize by default:
184185
* Primitives (`int`, `string`, etc...), `Enum`s, `Nullable<>`, `Lazy<>`
185186
* `TimeSpan`, `DateTime`, `DateTimeOffset`
186187
* `Guid`, `Uri`, `Version`, `StringBuilder`
187-
* `BigInteger`, `Complex`
188+
* `BigInteger`, `Complex`, `Half`
188189
* `Array[]`, `Array[,]`, `Array[,,]`, `Array[,,,]`, `ArraySegment<>`, `BitArray`
189190
* `KeyValuePair<,>`, `Tuple<,...>`, `ValueTuple<,...>`
190191
* `ArrayList`, `Hashtable`
@@ -404,6 +405,8 @@ public struct Point
404405
}
405406
```
406407

408+
### C# 9 `record` types
409+
407410
C# 9.0 record with primary constructor is similar immutable object, also supports serialize/deserialize.
408411

409412
```csharp
@@ -412,8 +415,26 @@ C# 9.0 record with primary constructor is similar immutable object, also support
412415

413416
// use property: to set KeyAttribute
414417
[MessagePackObject] public record Point([property:Key(0)] int X, [property: Key(1)] int Y);
418+
419+
// Or use explicit properties
420+
[MessagePackObject]
421+
public record Person
422+
{
423+
[Key(0)]
424+
public string FirstName { get; init; }
425+
426+
[Key(1)]
427+
public string LastName { get; init; }
428+
}
415429
```
416430

431+
### C# 9 `init` property setter limitations
432+
433+
When using `init` property setters in _generic_ classes, [a CLR bug](https://github.com/neuecc/MessagePack-CSharp/issues/1134) prevents our most efficient code generation from invoking the property setter.
434+
As a result, you should avoid using `init` on property setters in generic classes when using the public-only `DynamicObjectResolver`/`StandardResolver`.
435+
436+
When using the `DynamicObjectResolverAllowPrivate`/`StandardResolverAllowPrivate` resolver the bug does not apply and you may use `init` without restriction.
437+
417438
## Serialization Callback
418439

419440
Objects implementing the `IMessagePackSerializationCallbackReceiver` interface will received `OnBeforeSerialize` and `OnAfterDeserialize` calls during serialization/deserialization.
@@ -679,7 +700,7 @@ Benchmarks comparing MessagePack For C# to other serializers were run on `Window
679700
* Avoid string key decoding for lookup maps (string key and use automata based name lookup with inlined IL code generation, see: [AutomataDictionary](https://github.com/neuecc/MessagePack-CSharp/blob/bcedbce3fd98cb294210d6b4a22bdc4c75ccd916/src/MessagePack/Internal/AutomataDictionary.cs)
680701
* To encode string keys, use pre-generated member name bytes and fixed sized byte array copies in IL, see: [UnsafeMemory.cs](https://github.com/neuecc/MessagePack-CSharp/blob/f17ddc5d107d3a2f66f60398b214ef87919ff892/src/MessagePack/Internal/UnsafeMemory.cs)
681702

682-
Before creating this library, I implemented a fast fast serializer with [ZeroFormatter#Performance](https://github.com/neuecc/ZeroFormatter#performance). This is a further evolved implementation. MessagePack for C# is always fast and optimized for all types (primitive, small struct, large object, any collections).
703+
Before creating this library, I implemented a fast serializer with [ZeroFormatter#Performance](https://github.com/neuecc/ZeroFormatter#performance). This is a further evolved implementation. MessagePack for C# is always fast and optimized for all types (primitive, small struct, large object, any collections).
683704

684705
### <a name="deserialize-performance"></a>Deserialization Performance for different options
685706

@@ -738,6 +759,49 @@ Extra note, this is serialization benchmark result.
738759

739760
Of course, `IntKey` is fastest but `StringKey` also performs reasonably well.
740761

762+
### <a name="string-interning"></a>String interning
763+
764+
The msgpack format does not provide for reusing strings in the data stream.
765+
This naturally leads the deserializer to create a new `string` object for every string encountered,
766+
even if it is equal to another string previously encountered.
767+
768+
When deserializing data that may contain the same strings repeatedly it can be worthwhile
769+
to have the deserializer take a little extra time to check whether it has seen a given string before
770+
and reuse it if it has.
771+
772+
To enable string interning on *all* string values, use a resolver that specifies `StringInterningFormatter`
773+
before any of the standard ones, like this:
774+
775+
```cs
776+
var options = MessagePackSerializerOptions.Standard.WithResolver(
777+
CompositeResolver.Create(
778+
new IMessagePackFormatter[] { new StringInterningFormatter() },
779+
new IFormatterResolver[] { StandardResolver.Instance }));
780+
781+
MessagePackSerializer.Deserialize<ClassOfStrings>(data, options);
782+
```
783+
784+
If you know which fields of a particular type are likely to contain duplicate strings,
785+
you can apply the string interning formatter to just those fields so the deserializer only pays
786+
for the interned string check where it matters most.
787+
Note that this technique requires a `[MessagePackObject]` or `[DataContract]` class.
788+
789+
```cs
790+
[MessagePackObject]
791+
public class ClassOfStrings
792+
{
793+
[Key(0)]
794+
[MessagePackFormatter(typeof(StringInterningFormatter))]
795+
public string InternedString { get; set; }
796+
797+
[Key(1)]
798+
public string OrdinaryString { get; set; }
799+
}
800+
```
801+
802+
If you are writing your own formatter for some type that contains strings,
803+
you can call on the `StringInterningFormatter` directly from your formatter as well for the strings.
804+
741805
## LZ4 Compression
742806

743807
MessagePack is a fast and *compact* format but it is not compression. [LZ4](https://github.com/lz4/lz4) is an extremely fast compression algorithm, and using it MessagePack for C# can achieve extremely fast performance as well as extremely compact binary sizes!
@@ -1068,7 +1132,7 @@ Here is an example of such a custom formatter implementation. Note its use of th
10681132

10691133
```csharp
10701134
/// <summary>Serializes a <see cref="FileInfo" /> by its full path as a string.</summary>
1071-
public class FileInfoFormatter<T> : IMessagePackFormatter<FileInfo>
1135+
public class FileInfoFormatter : IMessagePackFormatter<FileInfo>
10721136
{
10731137
public void Serialize(
10741138
ref MessagePackWriter writer, FileInfo value, MessagePackSerializerOptions options)
@@ -1111,7 +1175,7 @@ you must precede it with a map or array header. You must read the entire map/arr
11111175
For example:
11121176

11131177
```csharp
1114-
public class MySpecialObjectFormatter<T> : IMessagePackFormatter<MySpecialObject>
1178+
public class MySpecialObjectFormatter : IMessagePackFormatter<MySpecialObject>
11151179
{
11161180
public void Serialize(
11171181
ref MessagePackWriter writer, MySpecialObject value, MessagePackSerializerOptions options)
@@ -1149,15 +1213,18 @@ public class MySpecialObjectFormatter<T> : IMessagePackFormatter<MySpecialObject
11491213
int count = reader.ReadArrayHeader();
11501214
for (int i = 0; i < count; i++)
11511215
{
1152-
case 0:
1153-
fullName = reader.ReadString();
1154-
break;
1155-
case 1:
1156-
age = reader.ReadInt32();
1157-
break;
1158-
default:
1159-
reader.Skip();
1160-
break;
1216+
switch (i)
1217+
{
1218+
case 0:
1219+
fullName = reader.ReadString();
1220+
break;
1221+
case 1:
1222+
age = reader.ReadInt32();
1223+
break;
1224+
default:
1225+
reader.Skip();
1226+
break;
1227+
}
11611228
}
11621229

11631230
reader.Depth--;
@@ -1342,12 +1409,6 @@ internal static class SampleCustomResolverGetFormatterHelper
13421409
return formatter;
13431410
}
13441411

1345-
// If target type is generics, use MakeGenericType.
1346-
if (t.IsGenericParameter && t.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
1347-
{
1348-
return Activator.CreateInstance(typeof(ValueTupleFormatter<,>).MakeGenericType(t.GenericTypeArguments));
1349-
}
1350-
13511412
// If type can not get, must return null for fallback mechanism.
13521413
return null;
13531414
}
@@ -1435,30 +1496,44 @@ var resolver = MessagePack.Resolvers.CompositeResolver.Create(
14351496

14361497
## Reserved Extension Types
14371498

1438-
MessagePack for C# already used some MessagePack extension type codes, be careful to use same ext code.
1499+
MessagePack for C# already used some MessagePack extension type codes, be careful to avoid using the same ext code for other purposes.
1500+
1501+
Range | Reserved for
1502+
--|--
1503+
\[-128, -1\] | Reserved by the msgpack spec for predefined types
1504+
\[30, 120) | Reserved for this library's use to support common types in .NET
1505+
1506+
This leaves the following ranges for your use:
1507+
1508+
- \[0, 30)
1509+
- \[120, 127]
1510+
1511+
Within the *reserved* ranges, this library defines or implements extensions that use these type codes:
14391512

14401513
| Code | Type | Use by |
1441-
| --- | --- | --- |
1442-
| -1 | DateTime | MessagePack-spec reserved for timestamp |
1443-
| 30 | Vector2[] | for Unity, UnsafeBlitFormatter |
1444-
| 31 | Vector3[] | for Unity, UnsafeBlitFormatter |
1445-
| 32 | Vector4[] | for Unity, UnsafeBlitFormatter |
1446-
| 33 | Quaternion[] | for Unity, UnsafeBlitFormatter |
1447-
| 34 | Color[] | for Unity, UnsafeBlitFormatter |
1448-
| 35 | Bounds[] | for Unity, UnsafeBlitFormatter |
1449-
| 36 | Rect[] | for Unity, UnsafeBlitFormatter |
1450-
| 37 | Int[] | for Unity, UnsafeBlitFormatter |
1451-
| 38 | Float[] | for Unity, UnsafeBlitFormatter |
1452-
| 39 | Double[] | for Unity, UnsafeBlitFormatter |
1453-
| 98 | All | MessagePackCompression.Lz4BlockArray |
1454-
| 99 | All | MessagePackCompression.Lz4Block |
1455-
| 100 | object | TypelessFormatter |
1514+
| ---- | ---- | --- |
1515+
| -1 | DateTime | MessagePack-spec reserved for timestamp |
1516+
| 30 | Vector2[] | for Unity, UnsafeBlitFormatter |
1517+
| 31 | Vector3[] | for Unity, UnsafeBlitFormatter |
1518+
| 32 | Vector4[] | for Unity, UnsafeBlitFormatter |
1519+
| 33 | Quaternion[] | for Unity, UnsafeBlitFormatter |
1520+
| 34 | Color[] | for Unity, UnsafeBlitFormatter |
1521+
| 35 | Bounds[] | for Unity, UnsafeBlitFormatter |
1522+
| 36 | Rect[] | for Unity, UnsafeBlitFormatter |
1523+
| 37 | Int[] | for Unity, UnsafeBlitFormatter |
1524+
| 38 | Float[] | for Unity, UnsafeBlitFormatter |
1525+
| 39 | Double[] | for Unity, UnsafeBlitFormatter |
1526+
| 98 | All | MessagePackCompression.Lz4BlockArray |
1527+
| 99 | All | MessagePackCompression.Lz4Block |
1528+
| 100 | object | TypelessFormatter |
14561529

14571530
## Unity support
14581531

14591532
Unity lowest supported version is `2018.3`, API Compatibility Level supports both `.NET 4.x` and `.NET Standard 2.0`.
14601533

1461-
You can install the `unitypackage` from the [releases][Releases] page. If your build targets PC, you can use it as is, but if your build targets IL2CPP, you can not use `Dynamic***Resolver`, so it is required to use pre-code generation. Please see [pre-code generation section](#aot).
1534+
You can install the `unitypackage` from the [releases][Releases] page.
1535+
If your build targets .NET Framework 4.x and runs on mono, you can use it as is.
1536+
But if your build targets IL2CPP, you can not use `Dynamic***Resolver`, so it is required to use pre-code generation. Please see [pre-code generation section](#aot).
14621537

14631538
MessagePack for C# includes some additional `System.*.dll` libraries that originally provides in NuGet. They are located under `Plugins`. If other packages use these libraries (e.g. Unity Collections package using `System.Runtime.CompilerServices.Unsafe.dll`), to avoid conflicts, please delete the DLL under `Plugins`.
14641539

@@ -1504,7 +1579,8 @@ If you want to share a class between Unity and a server, you can use `SharedProj
15041579
By default, MessagePack for C# serializes custom objects by [generating IL](https://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator.aspx) on the fly at runtime to create custom, highly tuned formatters for each type. This code generation has a minor upfront performance cost.
15051580
Because strict-AOT environments such as Xamarin and Unity IL2CPP forbid runtime code generation, MessagePack provides a way for you to run a code generator ahead of time as well.
15061581

1507-
> Note: When Unity targets the PC it allows dynamic code generation, so AOT is not required.
1582+
> Note: When using Unity, dynamic code generation only works when targeting .NET Framework 4.x + mono runtime.
1583+
For all other Unity targets, AOT is required.
15081584

15091585
If you want to avoid the upfront dynamic generation cost or you need to run on Xamarin or Unity, you need AOT code generation. `mpc` (MessagePackCompiler) is the code generator of MessagePack for C#. mpc uses [Roslyn](https://github.com/dotnet/roslyn) to analyze source code.
15101586

@@ -1526,7 +1602,7 @@ Check in your `.config\dotnet-tools.json` file. On another machine you can "rest
15261602
Once you have the tool installed, simply invoke using `dotnet mpc` within your repo:
15271603

15281604
```
1529-
dotnet mpc -h
1605+
dotnet mpc --help
15301606
```
15311607

15321608
Alternatively, you can download mpc from the [releases][Releases] page, that includes platform native binaries (that don't require a separate dotnet runtime).
@@ -1535,7 +1611,7 @@ Alternatively, you can download mpc from the [releases][Releases] page, that inc
15351611
Usage: mpc [options...]
15361612
15371613
Options:
1538-
-i, -input <String> Input path of analyze csproj or directory, if input multiple csproj split with ','. (Required)
1614+
-i, -input <String> Input path to MSBuild project file or the directory containing Unity source files. (Required)
15391615
-o, -output <String> Output file path(.cs) or directory(multiple generate file). (Required)
15401616
-c, -conditionalSymbol <String> Conditional compiler symbols, split with ','. (Default: null)
15411617
-r, -resolverName <String> Set resolver name. (Default: GeneratedResolver)

azure-pipelines.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ stages:
1919
jobs:
2020
- job: Windows
2121
pool:
22-
vmImage: windows-2019
22+
vmImage: windows-2022
2323
steps:
2424
- checkout: self
2525
clean: true
@@ -33,7 +33,9 @@ stages:
3333
- job: Unity
3434
pool:
3535
name: CustomAgents
36-
demands: UNITYHUB_EDITORS_FOLDER_LOCATION
36+
demands:
37+
- UNITYHUB_EDITORS_FOLDER_LOCATION
38+
- UNITYVERSION -equals 2019.1
3739
steps:
3840
- checkout: self
3941
clean: true
@@ -42,7 +44,7 @@ stages:
4244

4345
- job: Linux
4446
pool:
45-
vmImage: Ubuntu 16.04
47+
vmImage: ubuntu-20.04
4648
steps:
4749
- checkout: self
4850
clean: true
@@ -51,7 +53,7 @@ stages:
5153

5254
- job: macOS
5355
pool:
54-
vmImage: macOS-10.15
56+
vmImage: macOS-11
5557
steps:
5658
- checkout: self
5759
clean: true
@@ -62,7 +64,7 @@ stages:
6264
# It also helps exercise mpc so bugs don't go unnoticed.
6365
- job: codegen_diff
6466
pool:
65-
vmImage: ubuntu-latest
67+
vmImage: ubuntu-20.04
6668
steps:
6769
- checkout: self
6870
clean: true
@@ -82,7 +84,7 @@ stages:
8284
jobs:
8385
- job: push
8486
pool:
85-
vmImage: ubuntu-latest
87+
vmImage: ubuntu-20.04
8688
steps:
8789
- download: current
8890
artifact: nuget

azure-pipelines/Get-TempToolsPath.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
if ($env:AGENT_TOOLSDIRECTORY) {
2-
$path = "$env:AGENT_TOOLSDIRECTORY\vs-platform\tools"
1+
if ($env:AGENT_TEMPDIRECTORY) {
2+
$path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID"
33
} elseif ($env:localappdata) {
4-
$path = "$env:localappdata\vs-platform\tools"
4+
$path = "$env:localappdata\gitrepos\tools"
55
} else {
66
$path = "$PSScriptRoot\..\obj\tools"
77
}

azure-pipelines/Get-nbgv.ps1

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ if ($existingTool) {
1010
return $existingTool.Path
1111
}
1212

13-
if ($env:AGENT_TEMPDIRECTORY) {
14-
$toolInstallDir = "$env:AGENT_TEMPDIRECTORY/$env:BUILD_BUILDID"
15-
} else {
16-
$toolInstallDir = "$PSScriptRoot/../obj/tools"
17-
}
13+
$toolInstallDir = & "$PSScriptRoot/Get-TempToolsPath.ps1"
1814

1915
$toolPath = "$toolInstallDir/nbgv"
2016
if (!(Test-Path $toolInstallDir)) { New-Item -Path $toolInstallDir -ItemType Directory | Out-Null }

azure-pipelines/build.yml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
steps:
2-
# Use VSBuild to pack because `dotnet pack` can't build VSIX projects.
3-
- task: VSBuild@1
2+
- task: DotNetCoreCLI@2
43
inputs:
5-
vsVersion: 16.0
6-
solution: MessagePack.sln
7-
msbuildArgs: /t:build,pack /m /v:m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog"
8-
platform: $(BuildPlatform)
9-
configuration: $(BuildConfiguration)
4+
command: build
5+
arguments: --configuration $(BuildConfiguration) /t:build,pack /m /v:m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog"
106
displayName: Build MessagePack.sln
117

128
- task: DotNetCoreCLI@2

0 commit comments

Comments
 (0)