@@ -42,27 +42,46 @@ namespace RabbitMQ.Client.Impl
42
42
{
43
43
internal static class WireFormatting
44
44
{
45
+ // * DESCRIPTION TAKEN FROM MS REFERENCE SOURCE *
46
+ // https://github.com/microsoft/referencesource/blob/master/mscorlib/system/decimal.cs
47
+ // The lo, mid, hi, and flags fields contain the representation of the
48
+ // Decimal value. The lo, mid, and hi fields contain the 96-bit integer
49
+ // part of the Decimal. Bits 0-15 (the lower word) of the flags field are
50
+ // unused and must be zero; bits 16-23 contain must contain a value between
51
+ // 0 and 28, indicating the power of 10 to divide the 96-bit integer part
52
+ // by to produce the Decimal value; bits 24-30 are unused and must be zero;
53
+ // and finally bit 31 indicates the sign of the Decimal value, 0 meaning
54
+ // positive and 1 meaning negative.
55
+ readonly struct DecimalData
56
+ {
57
+ public readonly uint Flags ;
58
+ public readonly uint Hi ;
59
+ public readonly uint Lo ;
60
+ public readonly uint Mid ;
61
+
62
+ internal DecimalData ( uint flags , uint hi , uint lo , uint mid )
63
+ {
64
+ Flags = flags ;
65
+ Hi = hi ;
66
+ Lo = lo ;
67
+ Mid = mid ;
68
+ }
69
+ }
70
+
45
71
private static UTF8Encoding UTF8 = new UTF8Encoding ( ) ;
46
72
73
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
47
74
public static decimal ReadDecimal ( ReadOnlySpan < byte > span )
48
75
{
49
76
byte scale = span [ 0 ] ;
50
77
if ( scale > 28 )
51
78
{
52
- throw new SyntaxErrorException ( $ "Unrepresentable AMQP decimal table field: scale= { scale } " ) ;
79
+ ThrowInvalidDecimalScale ( scale ) ;
53
80
}
54
81
55
82
uint unsignedMantissa = NetworkOrderDeserializer . ReadUInt32 ( span . Slice ( 1 ) ) ;
56
- return new decimal (
57
- // The low 32 bits of a 96-bit integer
58
- lo : ( int ) ( unsignedMantissa & 0x7FFFFFFF ) ,
59
- // The middle 32 bits of a 96-bit integer.
60
- mid : 0 ,
61
- // The high 32 bits of a 96-bit integer.
62
- hi : 0 ,
63
- isNegative : ( unsignedMantissa & 0x80000000 ) != 0 ,
64
- // A power of 10 ranging from 0 to 28.
65
- scale : scale ) ;
83
+ var data = new DecimalData ( ( ( uint ) ( scale << 16 ) ) | unsignedMantissa & 0x80000000 , 0 , unsignedMantissa & 0x7FFFFFFF , 0 ) ;
84
+ return Unsafe . As < DecimalData , decimal > ( ref data ) ;
66
85
}
67
86
68
87
public static IList ReadArray ( ReadOnlySpan < byte > span , out int bytesRead )
@@ -360,15 +379,11 @@ public static int GetArrayByteCount(IList val)
360
379
public static int GetByteCount ( string val ) => string . IsNullOrEmpty ( val ) ? 0 : UTF8 . GetByteCount ( val ) ;
361
380
#endif
362
381
382
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
363
383
public static int WriteDecimal ( Span < byte > span , decimal value )
364
384
{
365
- DecimalToAmqp ( value , out byte scale , out int mantissa ) ;
366
- span [ 0 ] = scale ;
367
- return 1 + WriteLong ( span . Slice ( 1 ) , ( uint ) mantissa ) ;
368
- }
369
-
370
- private static void DecimalToAmqp ( decimal value , out byte scale , out int mantissa )
371
- {
385
+ // Cast the decimal to our struct to avoid the decimal.GetBits allocations.
386
+ DecimalData data = Unsafe . As < decimal , DecimalData > ( ref value ) ;
372
387
// According to the documentation :-
373
388
// - word 0: low-order "mantissa"
374
389
// - word 1, word 2: medium- and high-order "mantissa"
@@ -378,16 +393,16 @@ private static void DecimalToAmqp(decimal value, out byte scale, out int mantiss
378
393
// We need to be careful about the range of word 0, too: we can
379
394
// only take 31 bits worth of it, since the sign bit needs to
380
395
// fit in there too.
381
- int [ ] bitRepresentation = decimal . GetBits ( value ) ;
382
- if ( bitRepresentation [ 1 ] != 0 || // mantissa extends into middle word
383
- bitRepresentation [ 2 ] != 0 || // mantissa extends into top word
384
- bitRepresentation [ 0 ] < 0 ) // mantissa extends beyond 31 bits
396
+ if ( data . Mid != 0 || // mantissa extends into middle word
397
+ data . Hi != 0 || // mantissa extends into top word
398
+ data . Lo < 0 ) // mantissa extends beyond 31 bits
385
399
{
386
- throw new WireFormattingException ( "Decimal overflow in AMQP encoding" , value ) ;
400
+ return ThrowWireFormattingException ( value ) ;
387
401
}
388
- scale = ( byte ) ( ( ( ( uint ) bitRepresentation [ 3 ] ) >> 16 ) & 0xFF ) ;
389
- mantissa = ( int ) ( ( ( ( uint ) bitRepresentation [ 3 ] ) & 0x80000000 ) |
390
- ( ( ( uint ) bitRepresentation [ 0 ] ) & 0x7FFFFFFF ) ) ;
402
+
403
+ span [ 0 ] = ( byte ) ( ( data . Flags >> 16 ) & 0xFF ) ;
404
+ WriteLong ( span . Slice ( 1 ) , ( data . Flags & 0b1000_0000_0000_0000_0000_0000_0000_0000 ) | ( data . Lo & 0b0111_1111_1111_1111_1111_1111_1111_1111 ) ) ;
405
+ return 5 ;
391
406
}
392
407
393
408
public static int WriteFieldValue ( Span < byte > span , object value )
@@ -908,5 +923,15 @@ public static int ThrowSyntaxErrorException(uint byteCount)
908
923
{
909
924
throw new SyntaxErrorException ( $ "Long string too long; byte length={ byteCount } , max={ int . MaxValue } ") ;
910
925
}
926
+
927
+ private static int ThrowWireFormattingException ( decimal value )
928
+ {
929
+ throw new WireFormattingException ( "Decimal overflow in AMQP encoding" , value ) ;
930
+ }
931
+
932
+ private static decimal ThrowInvalidDecimalScale ( int scale )
933
+ {
934
+ throw new SyntaxErrorException ( $ "Unrepresentable AMQP decimal table field: scale={ scale } ") ;
935
+ }
911
936
}
912
937
}
0 commit comments