Skip to content

Commit 3a75aa2

Browse files
authored
Optimize Proto decimal conversion (#2495)
1 parent 5f4b207 commit 3a75aa2

File tree

1 file changed

+41
-47
lines changed

1 file changed

+41
-47
lines changed

src/Confluent.SchemaRegistry.Serdes.Protobuf/DecimalExtensions.cs

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
// Refer to LICENSE for more information.
1616

1717
using System;
18+
using System.Buffers.Binary;
1819
using System.Numerics;
20+
using System.Runtime.CompilerServices;
1921
using Google.Protobuf;
2022
using Decimal = Confluent.SchemaRegistry.Serdes.Protobuf.Decimal;
2123

@@ -33,24 +35,35 @@ public static class DecimalExtensions
3335
/// <returns>Protobuf decimal value</returns>
3436
public static Decimal ToProtobufDecimal(this decimal value)
3537
{
36-
var bytes = GetBytesFromDecimal(value);
38+
Span<byte> bytes = stackalloc byte[16];
39+
WriteBytesFromDecimal(value, bytes);
3740

3841
// Copy the 12 bytes into an array of size 13 so that the last byte is 0,
3942
// which will ensure that the unscaled value is positive.
40-
var unscaledValueBytes = new byte[13];
41-
Array.Copy(bytes, unscaledValueBytes, 12);
43+
Span<byte> unscaledValueBytes = stackalloc byte[13];
44+
bytes.Slice(0, 12).CopyTo(unscaledValueBytes);
4245

46+
#if NET6_0_OR_GREATER
4347
var unscaledValue = new BigInteger(unscaledValueBytes);
44-
var scale = bytes[14];
45-
48+
#else
49+
var unscaledValue = new BigInteger(unscaledValueBytes.ToArray());
50+
#endif
51+
4652
if (bytes[15] == 128)
4753
{
4854
unscaledValue *= BigInteger.MinusOne;
4955
}
50-
56+
var scale = bytes[14];
57+
58+
#if NET6_0_OR_GREATER
59+
Span<byte> buffer = stackalloc byte[16];
60+
unscaledValue.TryWriteBytes(buffer, out var bytesWritten, isBigEndian: true);
61+
buffer = buffer.Slice(0, bytesWritten);
62+
#else
5163
var buffer = unscaledValue.ToByteArray();
5264
Array.Reverse(buffer);
53-
65+
#endif
66+
5467
return new Decimal {
5568
Value = ByteString.CopyFrom(buffer),
5669
Scale = scale,
@@ -64,61 +77,42 @@ public static Decimal ToProtobufDecimal(this decimal value)
6477
/// <returns>Decimal value</returns>
6578
public static decimal ToSystemDecimal(this Decimal value)
6679
{
80+
#if NET6_0_OR_GREATER
81+
var unscaledValue = new BigInteger(value.Value.Span, isBigEndian: true);
82+
#else
6783
var buffer = value.Value.ToByteArray();
6884
Array.Reverse(buffer);
69-
7085
var unscaledValue = new BigInteger(buffer);
86+
#endif
7187

7288
var scaleDivisor = BigInteger.Pow(new BigInteger(10), value.Scale);
73-
var remainder = BigInteger.Remainder(unscaledValue, scaleDivisor);
74-
var scaledValue = BigInteger.Divide(unscaledValue, scaleDivisor);
89+
var quotient = BigInteger.DivRem(unscaledValue, scaleDivisor, out var remainder);
7590

76-
if (scaledValue > new BigInteger(decimal.MaxValue))
91+
if (quotient > new BigInteger(decimal.MaxValue))
7792
{
7893
throw new OverflowException($"The value {unscaledValue} cannot fit into decimal.");
7994
}
8095

81-
var leftOfDecimal = (decimal)scaledValue;
82-
var rightOfDecimal = ((decimal)remainder) / ((decimal)scaleDivisor);
96+
var leftOfDecimal = (decimal)quotient;
97+
var rightOfDecimal = (decimal)remainder / (decimal)scaleDivisor;
8398

8499
return leftOfDecimal + rightOfDecimal;
85100
}
86101

87-
/// <summary>
88-
/// Gets the bytes from decimal.
89-
/// </summary>
90-
/// <param name="d">The <see cref="decimal" />.</param>
91-
/// <returns>
92-
/// A byte array.
93-
/// </returns>
94-
private static byte[] GetBytesFromDecimal(decimal d)
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
private static void WriteBytesFromDecimal(decimal value, Span<byte> destination)
95104
{
96-
byte[] bytes = new byte[16];
97-
98-
int[] bits = decimal.GetBits(d);
99-
int lo = bits[0];
100-
int mid = bits[1];
101-
int hi = bits[2];
102-
int flags = bits[3];
103-
104-
bytes[0] = (byte)lo;
105-
bytes[1] = (byte)(lo >> 8);
106-
bytes[2] = (byte)(lo >> 0x10);
107-
bytes[3] = (byte)(lo >> 0x18);
108-
bytes[4] = (byte)mid;
109-
bytes[5] = (byte)(mid >> 8);
110-
bytes[6] = (byte)(mid >> 0x10);
111-
bytes[7] = (byte)(mid >> 0x18);
112-
bytes[8] = (byte)hi;
113-
bytes[9] = (byte)(hi >> 8);
114-
bytes[10] = (byte)(hi >> 0x10);
115-
bytes[11] = (byte)(hi >> 0x18);
116-
bytes[12] = (byte)flags;
117-
bytes[13] = (byte)(flags >> 8);
118-
bytes[14] = (byte)(flags >> 0x10);
119-
bytes[15] = (byte)(flags >> 0x18);
120-
121-
return bytes;
105+
#if NET6_0_OR_GREATER
106+
Span<int> bits = stackalloc int[4];
107+
_ = decimal.GetBits(value, bits);
108+
#else
109+
var bits = decimal.GetBits(value);
110+
#endif
111+
112+
BinaryPrimitives.WriteInt32LittleEndian(destination, bits[0]);
113+
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4), bits[1]);
114+
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(8), bits[2]);
115+
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(12), bits[3]);
122116
}
123117
}
124118
}

0 commit comments

Comments
 (0)