diff --git a/NetSerializer.UnitTests/HalfTest.cs b/NetSerializer.UnitTests/HalfTest.cs
new file mode 100644
index 0000000..572557d
--- /dev/null
+++ b/NetSerializer.UnitTests/HalfTest.cs
@@ -0,0 +1,49 @@
+#if NET5_0
+using System;
+using System.IO;
+using NUnit.Framework;
+
+namespace NetSerializer.UnitTests
+{
+ [TestFixture]
+ [TestOf(typeof(Primitives))]
+ [Parallelizable(ParallelScope.All)]
+ public class HalfTest
+ {
+ [Test]
+ public void Test()
+ {
+ var serializer = new Serializer(new[] {typeof(SerializationType)});
+
+ var stream = new MemoryStream();
+ var obj = new SerializationType
+ {
+ R = (Half) 0,
+ G = (Half) 12.34,
+ B = (Half) 0.1,
+ A = Half.PositiveInfinity,
+ };
+
+ serializer.Serialize(stream, obj);
+
+ stream.Position = 0;
+
+ var read = (SerializationType) serializer.Deserialize(stream);
+
+ Assert.That(read.R, Is.EqualTo(obj.R));
+ Assert.That(read.G, Is.EqualTo(obj.G));
+ Assert.That(read.B, Is.EqualTo(obj.B));
+ Assert.That(read.A, Is.EqualTo(obj.A));
+ }
+
+ [Serializable]
+ private class SerializationType
+ {
+ public Half R;
+ public Half G;
+ public Half B;
+ public Half A;
+ }
+ }
+}
+#endif
diff --git a/NetSerializer.UnitTests/NetSerializer.UnitTests.csproj b/NetSerializer.UnitTests/NetSerializer.UnitTests.csproj
new file mode 100644
index 0000000..03ef9b8
--- /dev/null
+++ b/NetSerializer.UnitTests/NetSerializer.UnitTests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net45;netcoreapp3.1;net5.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
diff --git a/NetSerializer.UnitTests/PrimitivesTest.cs b/NetSerializer.UnitTests/PrimitivesTest.cs
new file mode 100644
index 0000000..5bfdd2d
--- /dev/null
+++ b/NetSerializer.UnitTests/PrimitivesTest.cs
@@ -0,0 +1,130 @@
+using System;
+using System.IO;
+using NUnit.Framework;
+
+namespace NetSerializer.UnitTests
+{
+ [TestFixture]
+ [TestOf(typeof(Primitives))]
+ [Parallelizable(ParallelScope.All)]
+ public class PrimitivesTest
+ {
+#if NET5_0
+#if NO_UNSAFE
+ [Ignore("Float and half tests are inacurrate due to rounding when NO_UNSAFE is enabled.")]
+#endif
+ [Test]
+ [TestCase(0)]
+ [TestCase(1)]
+ [TestCase(123.4)]
+ [TestCase(0.01)]
+ [TestCase(double.PositiveInfinity)]
+ [TestCase(double.NegativeInfinity)]
+ [TestCase(double.NaN)]
+ public void TestHalf(double val)
+ {
+ // Can't stick Half values in attributes so have to do this.
+ var half = (Half) val;
+
+ var stream = new MemoryStream();
+ Primitives.WritePrimitive(stream, half);
+
+ stream.Position = 0;
+
+ Primitives.ReadPrimitive(new ByteStream(stream), out Half read);
+ Assert.That(read, Is.EqualTo(half));
+ }
+#endif
+
+#if NO_UNSAFE
+ [Ignore("Float tests are inacurrate due to rounding when NO_UNSAFE is enabled.")]
+#endif
+ [Test]
+ [TestCase(0)]
+ [TestCase(1)]
+ [TestCase(123.4f)]
+ [TestCase(0.01f)]
+ [TestCase(float.PositiveInfinity)]
+ [TestCase(float.NegativeInfinity)]
+ [TestCase(float.NaN)]
+ public void TestSingle(float val)
+ {
+ var stream = new MemoryStream();
+ Primitives.WritePrimitive(stream, val);
+
+ stream.Position = 0;
+
+ Primitives.ReadPrimitive(new ByteStream(stream), out float read);
+ Assert.That(read, Is.EqualTo(val));
+ }
+
+ [Test]
+ [TestCase(0)]
+ [TestCase(1)]
+ [TestCase(123.4)]
+ [TestCase(0.01)]
+ [TestCase(float.PositiveInfinity)]
+ [TestCase(float.NegativeInfinity)]
+ [TestCase(float.NaN)]
+ public void TestDouble(double val)
+ {
+ var stream = new MemoryStream();
+ Primitives.WritePrimitive(stream, val);
+
+ stream.Position = 0;
+
+ Primitives.ReadPrimitive(new ByteStream(stream), out double read);
+ Assert.That(read, Is.EqualTo(val));
+ }
+
+ // Stream wrapper that only reads one byte at a time to test the reading code.
+ private sealed class ByteStream : Stream
+ {
+ private readonly Stream _parent;
+
+ public ByteStream(Stream parent)
+ {
+ _parent = parent;
+ }
+
+ public override void Flush()
+ {
+ _parent.Flush();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return _parent.Seek(offset, origin);
+ }
+
+ public override void SetLength(long value)
+ {
+ _parent.SetLength(value);
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return _parent.Read(buffer, offset, 1);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ _parent.Write(buffer, offset, count);
+ }
+
+ public override bool CanRead => _parent.CanRead;
+
+ public override bool CanSeek => _parent.CanSeek;
+
+ public override bool CanWrite => _parent.CanWrite;
+
+ public override long Length => _parent.Length;
+
+ public override long Position
+ {
+ get => _parent.Position;
+ set => _parent.Position = value;
+ }
+ }
+ }
+}
diff --git a/NetSerializer.sln b/NetSerializer.sln
index 8be0ec5..d1139da 100644
--- a/NetSerializer.sln
+++ b/NetSerializer.sln
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrimitiveTest", "PrimitiveTest\PrimitiveTest.csproj", "{CBA6D818-4B6A-4A80-95D5-7F5EC3FBB3C4}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetSerializer.UnitTests", "NetSerializer.UnitTests\NetSerializer.UnitTests.csproj", "{17DC557B-DB9E-464C-A311-5AB83E5684AC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
{CBA6D818-4B6A-4A80-95D5-7F5EC3FBB3C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBA6D818-4B6A-4A80-95D5-7F5EC3FBB3C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBA6D818-4B6A-4A80-95D5-7F5EC3FBB3C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {17DC557B-DB9E-464C-A311-5AB83E5684AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {17DC557B-DB9E-464C-A311-5AB83E5684AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {17DC557B-DB9E-464C-A311-5AB83E5684AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {17DC557B-DB9E-464C-A311-5AB83E5684AC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/NetSerializer/NetSerializer.csproj b/NetSerializer/NetSerializer.csproj
index e933d9a..bcee252 100644
--- a/NetSerializer/NetSerializer.csproj
+++ b/NetSerializer/NetSerializer.csproj
@@ -1,6 +1,6 @@
- netstandard2.1;net45;netcoreapp3.0
+ netstandard2.1;net45;netcoreapp3.1;net5.0
A very fast and minimal serializer
Tomi Valkeinen
Copyright © 2012-2019 Tomi Valkeinen
diff --git a/NetSerializer/Primitives.cs b/NetSerializer/Primitives.cs
index 566ce82..d9d9e45 100644
--- a/NetSerializer/Primitives.cs
+++ b/NetSerializer/Primitives.cs
@@ -12,7 +12,9 @@
using System.Text;
using System.Diagnostics;
#if NETCOREAPP
+using System.Runtime.CompilerServices;
using System.Text.Unicode;
+using System.Buffers.Binary;
using System.Buffers;
#endif
@@ -219,26 +221,41 @@ public static void ReadPrimitive(Stream stream, out long value)
public static unsafe void WritePrimitive(Stream stream, float value)
{
uint v = *(uint*)(&value);
- WriteVarint32(stream, v);
+ WriteUInt32(stream, v);
}
public static unsafe void ReadPrimitive(Stream stream, out float value)
{
- uint v = ReadVarint32(stream);
+ uint v = ReadUInt32(stream);
value = *(float*)(&v);
}
public static unsafe void WritePrimitive(Stream stream, double value)
{
ulong v = *(ulong*)(&value);
- WriteVarint64(stream, v);
+ WriteUInt64(stream, v);
}
public static unsafe void ReadPrimitive(Stream stream, out double value)
{
- ulong v = ReadVarint64(stream);
+ ulong v = ReadUInt64(stream);
value = *(double*)(&v);
}
+
+#if NET5_0
+ public static void WritePrimitive(Stream stream, Half value)
+ {
+ ushort v = Unsafe.As(ref value);
+ WriteUInt16(stream, v);
+ }
+
+ public static void ReadPrimitive(Stream stream, out Half value)
+ {
+ var v = ReadUInt16(stream);
+ value = Unsafe.As(ref v);
+ }
+#endif
+
#else
public static void WritePrimitive(Stream stream, float value)
{
@@ -255,14 +272,159 @@ public static void ReadPrimitive(Stream stream, out float value)
public static void WritePrimitive(Stream stream, double value)
{
ulong v = (ulong)BitConverter.DoubleToInt64Bits(value);
- WriteVarint64(stream, v);
+ WriteUInt64(stream, v);
}
public static void ReadPrimitive(Stream stream, out double value)
{
- ulong v = ReadVarint64(stream);
+ ulong v = ReadUInt64(stream);
value = BitConverter.Int64BitsToDouble((long)v);
}
+
+#if NET5_0
+ public static void WritePrimitive(Stream stream, Half value)
+ {
+ WritePrimitive(stream, (double)value);
+ }
+
+ public static void ReadPrimitive(Stream stream, out Half value)
+ {
+ double v;
+ ReadPrimitive(stream, out v);
+ value = (Half)v;
+ }
+#endif
+
+#endif
+
+ private static void WriteUInt16(Stream stream, ushort value)
+ {
+ stream.WriteByte((byte) value);
+ stream.WriteByte((byte) (value >> 8));
+ }
+
+ private static ushort ReadUInt16(Stream stream)
+ {
+ ushort a = 0;
+
+ for (var i = 0; i < 16; i += 8)
+ {
+ var val = stream.ReadByte();
+ if (val == -1)
+ throw new EndOfStreamException();
+
+ a |= (ushort) (val << i);
+ }
+
+ return a;
+ }
+
+ // 32 and 64 bit variants use stackalloc when everything is available since it's faster.
+
+#if !NETCOREAPP
+ private static void WriteUInt32(Stream stream, uint value)
+ {
+ stream.WriteByte((byte) value);
+ stream.WriteByte((byte) (value >> 8));
+ stream.WriteByte((byte) (value >> 16));
+ stream.WriteByte((byte) (value >> 24));
+ }
+
+ private static void WriteUInt64(Stream stream, ulong value)
+ {
+ stream.WriteByte((byte) value);
+ stream.WriteByte((byte) (value >> 8));
+ stream.WriteByte((byte) (value >> 16));
+ stream.WriteByte((byte) (value >> 24));
+ stream.WriteByte((byte) (value >> 32));
+ stream.WriteByte((byte) (value >> 40));
+ stream.WriteByte((byte) (value >> 48));
+ stream.WriteByte((byte) (value >> 56));
+ }
+
+ private static uint ReadUInt32(Stream stream)
+ {
+ uint a = 0;
+
+ for (var i = 0; i < 32; i += 8)
+ {
+ var val = stream.ReadByte();
+ if (val < 0)
+ throw new EndOfStreamException();
+
+ a |= (uint)val << i;
+ }
+
+ return a;
+ }
+
+ private static ulong ReadUInt64(Stream stream)
+ {
+ ulong a = 0;
+
+ for (var i = 0; i < 64; i += 8)
+ {
+ var val = stream.ReadByte();
+ if (val < 0)
+ throw new EndOfStreamException();
+
+ a |= (ulong)val << i;
+ }
+
+ return a;
+ }
+#else
+ private static void WriteUInt32(Stream stream, uint value)
+ {
+ Span buf = stackalloc byte[4];
+ BinaryPrimitives.WriteUInt32LittleEndian(buf, value);
+
+ stream.Write(buf);
+ }
+
+ private static void WriteUInt64(Stream stream, ulong value)
+ {
+ Span buf = stackalloc byte[8];
+ BinaryPrimitives.WriteUInt64LittleEndian(buf, value);
+
+ stream.Write(buf);
+ }
+
+ private static uint ReadUInt32(Stream stream)
+ {
+ Span buf = stackalloc byte[4];
+ var wSpan = buf;
+
+ while (true)
+ {
+ var read = stream.Read(wSpan);
+ if (read == 0)
+ throw new EndOfStreamException();
+ if (read == wSpan.Length)
+ break;
+ wSpan = wSpan[read..];
+ }
+
+ return BinaryPrimitives.ReadUInt32LittleEndian(buf);
+ }
+
+ private static ulong ReadUInt64(Stream stream)
+ {
+ Span buf = stackalloc byte[8];
+ var wSpan = buf;
+
+ while (true)
+ {
+ var read = stream.Read(wSpan);
+ if (read == 0)
+ throw new EndOfStreamException();
+ if (read == wSpan.Length)
+ break;
+ wSpan = wSpan[read..];
+ }
+
+ return BinaryPrimitives.ReadUInt64LittleEndian(buf);
+ }
#endif
public static void WritePrimitive(Stream stream, DateTime value)
diff --git a/NetSerializer/TypeSerializers/PrimitivesSerializer.cs b/NetSerializer/TypeSerializers/PrimitivesSerializer.cs
index 26c6918..aad5954 100644
--- a/NetSerializer/TypeSerializers/PrimitivesSerializer.cs
+++ b/NetSerializer/TypeSerializers/PrimitivesSerializer.cs
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Tomi Valkeinen
- *
+ *
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -29,6 +29,9 @@ sealed class PrimitivesSerializer : IStaticTypeSerializer
typeof(DateTime),
typeof(byte[]),
typeof(Decimal),
+#if NET5_0
+ typeof(Half),
+#endif
};
public bool Handles(Type type)
diff --git a/PrimitiveTest/PrimitiveTest.csproj b/PrimitiveTest/PrimitiveTest.csproj
index 144e502..9d9edae 100644
--- a/PrimitiveTest/PrimitiveTest.csproj
+++ b/PrimitiveTest/PrimitiveTest.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.0;net45
+ netcoreapp3.1;net45;net5.0
Exe
diff --git a/Test/Test.csproj b/Test/Test.csproj
index 418c1cb..ee49692 100644
--- a/Test/Test.csproj
+++ b/Test/Test.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.0;net45
+ netcoreapp3.1;net45;net5.0
Exe