Skip to content

Optimized cross-platform binary (de)serialization #729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions projects/client/RabbitMQ.Client/RabbitMQ.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>

<ItemGroup Condition=" '$(Configuration)' == 'SignedRelease' ">
Expand Down
90 changes: 17 additions & 73 deletions projects/client/RabbitMQ.Client/src/util/NetworkBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------

using System;
using System.Buffers.Binary;
using System.IO;
using System.Text;

Expand All @@ -54,8 +56,6 @@ namespace RabbitMQ.Util
/// </remarks>
public class NetworkBinaryReader : BinaryReader
{
private static readonly Encoding s_encoding = new UTF8Encoding();

// Not particularly efficient. To be more efficient, we could
// reuse BinaryReader's implementation details: m_buffer and
// FillBuffer, if they weren't private
Expand All @@ -66,82 +66,41 @@ public class NetworkBinaryReader : BinaryReader
/// <summary>
/// Construct a NetworkBinaryReader over the given input stream.
/// </summary>
public NetworkBinaryReader(Stream input) : base(input, s_encoding)
{
}

/// <summary>
/// Construct a NetworkBinaryReader over the given input
/// stream, reading strings using the given encoding.
/// </summary>
public NetworkBinaryReader(Stream input, Encoding encoding) : base(input, encoding)
public NetworkBinaryReader(Stream input) : base(input, Encoding.UTF8)
{
}

///<summary>Helper method for constructing a temporary
///BinaryReader over a byte[].</summary>
public static BinaryReader TemporaryBinaryReader(byte[] bytes)
{
return new BinaryReader(new MemoryStream(bytes));
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override double ReadDouble()
{
byte[] bytes = ReadBytes(8);
byte temp = bytes[0];
bytes[0] = bytes[7];
bytes[7] = temp;
temp = bytes[1];
bytes[1] = bytes[6];
bytes[6] = temp;
temp = bytes[2];
bytes[2] = bytes[5];
bytes[5] = temp;
temp = bytes[3];
bytes[3] = bytes[4];
bytes[4] = temp;
return TemporaryBinaryReader(bytes).ReadDouble();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's SO NICE to see this sort of code disappear 🎉

long val = BinaryPrimitives.ReadInt64BigEndian(ReadBytes(8));
return BitConverter.Int64BitsToDouble(val);
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override short ReadInt16()
{
uint i = base.ReadUInt16();
return (short)(((i & 0xFF00) >> 8) |
((i & 0x00FF) << 8));
return BinaryPrimitives.ReadInt16BigEndian(ReadBytes(2));
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override int ReadInt32()
{
uint i = base.ReadUInt32();
return (int)(((i & 0xFF000000) >> 24) |
((i & 0x00FF0000) >> 8) |
((i & 0x0000FF00) << 8) |
((i & 0x000000FF) << 24));
return BinaryPrimitives.ReadInt32BigEndian(ReadBytes(4));
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override long ReadInt64()
{
ulong i = base.ReadUInt64();
return (long)(((i & 0xFF00000000000000) >> 56) |
((i & 0x00FF000000000000) >> 40) |
((i & 0x0000FF0000000000) >> 24) |
((i & 0x000000FF00000000) >> 8) |
((i & 0x00000000FF000000) << 8) |
((i & 0x0000000000FF0000) << 24) |
((i & 0x000000000000FF00) << 40) |
((i & 0x00000000000000FF) << 56));
return BinaryPrimitives.ReadInt64BigEndian(ReadBytes(8));
}

/// <summary>
Expand All @@ -150,51 +109,36 @@ public override long ReadInt64()
public override float ReadSingle()
{
byte[] bytes = ReadBytes(4);
byte temp = bytes[0];
bytes[0] = bytes[3];
bytes[3] = temp;
temp = bytes[1];
bytes[1] = bytes[2];
bytes[2] = temp;
return TemporaryBinaryReader(bytes).ReadSingle();
if (BitConverter.IsLittleEndian)
{
bytes.AsSpan().Reverse();
}

return BitConverter.ToSingle(bytes, 0);
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override ushort ReadUInt16()
{
uint i = base.ReadUInt16();
return (ushort)(((i & 0xFF00) >> 8) |
((i & 0x00FF) << 8));
return BinaryPrimitives.ReadUInt16BigEndian(ReadBytes(2));
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override uint ReadUInt32()
{
uint i = base.ReadUInt32();
return ((i & 0xFF000000) >> 24) |
((i & 0x00FF0000) >> 8) |
((i & 0x0000FF00) << 8) |
((i & 0x000000FF) << 24);
return BinaryPrimitives.ReadUInt32BigEndian(ReadBytes(4));
}

/// <summary>
/// Override BinaryReader's method for network-order.
/// </summary>
public override ulong ReadUInt64()
{
ulong i = base.ReadUInt64();
return ((i & 0xFF00000000000000) >> 56) |
((i & 0x00FF000000000000) >> 40) |
((i & 0x0000FF0000000000) >> 24) |
((i & 0x000000FF00000000) >> 8) |
((i & 0x00000000FF000000) << 8) |
((i & 0x0000000000FF0000) << 24) |
((i & 0x000000000000FF00) << 40) |
((i & 0x00000000000000FF) << 56);
return BinaryPrimitives.ReadUInt64BigEndian(ReadBytes(8));
}
}
}
118 changes: 80 additions & 38 deletions projects/client/RabbitMQ.Client/src/util/NetworkBinaryWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------

using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Text;

Expand Down Expand Up @@ -94,93 +97,132 @@ public static byte[] TemporaryContents(BinaryWriter w)
/// </summary>
public override void Write(short i)
{
Write((byte)((i & 0xFF00) >> 8));
Write((byte)(i & 0x00FF));
byte[] bytes = ArrayPool<byte>.Shared.Rent(2);
try
{
BinaryPrimitives.WriteInt16BigEndian(bytes, i);
Write(bytes, 0, 2);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(ushort i)
{
Write((byte)((i & 0xFF00) >> 8));
Write((byte)(i & 0x00FF));
byte[] bytes = ArrayPool<byte>.Shared.Rent(2);
try
{
BinaryPrimitives.WriteUInt16BigEndian(bytes, i);
Write(bytes, 0, 2);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(int i)
{
Write((byte)((i & 0xFF000000) >> 24));
Write((byte)((i & 0x00FF0000) >> 16));
Write((byte)((i & 0x0000FF00) >> 8));
Write((byte)(i & 0x000000FF));
byte[] bytes = ArrayPool<byte>.Shared.Rent(4);
try
{
BinaryPrimitives.WriteInt32BigEndian(bytes, i);
Write(bytes, 0, 4);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(uint i)
{
Write((byte)((i & 0xFF000000) >> 24));
Write((byte)((i & 0x00FF0000) >> 16));
Write((byte)((i & 0x0000FF00) >> 8));
Write((byte)(i & 0x000000FF));
byte[] bytes = ArrayPool<byte>.Shared.Rent(4);
try
{
BinaryPrimitives.WriteUInt32BigEndian(bytes, i);
Write(bytes, 0, 4);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(long i)
{
uint i1 = (uint)(i >> 32);
uint i2 = (uint)i;
Write(i1);
Write(i2);
byte[] bytes = ArrayPool<byte>.Shared.Rent(8);
try
{
BinaryPrimitives.WriteInt64BigEndian(bytes, i);
Write(bytes, 0, 8);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(ulong i)
{
uint i1 = (uint)(i >> 32);
uint i2 = (uint)i;
Write(i1);
Write(i2);
byte[] bytes = ArrayPool<byte>.Shared.Rent(8);
try
{
BinaryPrimitives.WriteUInt64BigEndian(bytes, i);
Write(bytes, 0, 8);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(float f)
{
BinaryWriter w = TemporaryBinaryWriter(4);
w.Write(f);
byte[] wrongBytes = TemporaryContents(w);
Write(wrongBytes[3]);
Write(wrongBytes[2]);
Write(wrongBytes[1]);
Write(wrongBytes[0]);
byte[] bytes = BitConverter.GetBytes(f);
if (BitConverter.IsLittleEndian)
{
bytes.AsSpan().Reverse();
}

Write(bytes);
}

/// <summary>
/// Override BinaryWriter's method for network-order.
/// </summary>
public override void Write(double d)
{
BinaryWriter w = TemporaryBinaryWriter(8);
w.Write(d);
byte[] wrongBytes = TemporaryContents(w);
Write(wrongBytes[7]);
Write(wrongBytes[6]);
Write(wrongBytes[5]);
Write(wrongBytes[4]);
Write(wrongBytes[3]);
Write(wrongBytes[2]);
Write(wrongBytes[1]);
Write(wrongBytes[0]);
byte[] bytes = ArrayPool<byte>.Shared.Rent(8);
try
{
BinaryPrimitives.WriteInt64BigEndian(bytes, BitConverter.DoubleToInt64Bits(d));
Write(bytes, 0, 8);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2427,7 +2427,6 @@ namespace RabbitMQ.Util
public class NetworkBinaryReader : System.IO.BinaryReader
{
public NetworkBinaryReader(System.IO.Stream input) { }
public NetworkBinaryReader(System.IO.Stream input, System.Text.Encoding encoding) { }
public override double ReadDouble() { }
public override short ReadInt16() { }
public override int ReadInt32() { }
Expand All @@ -2436,7 +2435,6 @@ namespace RabbitMQ.Util
public override ushort ReadUInt16() { }
public override uint ReadUInt32() { }
public override ulong ReadUInt64() { }
public static System.IO.BinaryReader TemporaryBinaryReader(byte[] bytes) { }
}
public class NetworkBinaryWriter : System.IO.BinaryWriter
{
Expand Down