34
34
using System . Collections . Generic ;
35
35
using System . Runtime . CompilerServices ;
36
36
using System . Text ;
37
+
37
38
using RabbitMQ . Client . Exceptions ;
38
39
using RabbitMQ . Util ;
39
40
40
41
namespace RabbitMQ . Client . Impl
41
42
{
43
+ // * DESCRIPTION TAKEN FROM MS REFERENCE SOURCE *
44
+ // https://github.com/microsoft/referencesource/blob/master/mscorlib/system/decimal.cs
45
+ // The lo, mid, hi, and flags fields contain the representation of the
46
+ // Decimal value. The lo, mid, and hi fields contain the 96-bit integer
47
+ // part of the Decimal. Bits 0-15 (the lower word) of the flags field are
48
+ // unused and must be zero; bits 16-23 contain must contain a value between
49
+ // 0 and 28, indicating the power of 10 to divide the 96-bit integer part
50
+ // by to produce the Decimal value; bits 24-30 are unused and must be zero;
51
+ // and finally bit 31 indicates the sign of the Decimal value, 0 meaning
52
+ // positive and 1 meaning negative.
53
+ readonly struct DecimalData
54
+ {
55
+ public readonly uint Flags ;
56
+ public readonly uint Hi ;
57
+ public readonly uint Lo ;
58
+ public readonly uint Mid ;
59
+
60
+ internal DecimalData ( uint flags , uint hi , uint lo , uint mid )
61
+ {
62
+ Flags = flags ;
63
+ Hi = hi ;
64
+ Lo = lo ;
65
+ Mid = mid ;
66
+ }
67
+ }
68
+
42
69
internal static class WireFormatting
43
70
{
71
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
44
72
public static decimal ReadDecimal ( ReadOnlySpan < byte > span )
45
73
{
46
74
byte scale = span [ 0 ] ;
75
+ ValidateDecimalScale ( scale ) ;
76
+ uint unsignedMantissa = NetworkOrderDeserializer . ReadUInt32 ( span . Slice ( 1 ) ) ;
77
+ var data = new DecimalData ( ( ( uint ) ( scale << 16 ) ) | unsignedMantissa & 0x80000000 , 0 , unsignedMantissa & 0x7FFFFFFF , 0 ) ;
78
+ return Unsafe . As < DecimalData , decimal > ( ref data ) ;
79
+ }
80
+
81
+ private static void ValidateDecimalScale ( byte scale )
82
+ {
47
83
if ( scale > 28 )
48
84
{
49
85
throw new SyntaxErrorException ( $ "Unrepresentable AMQP decimal table field: scale={ scale } ") ;
50
86
}
51
-
52
- uint unsignedMantissa = NetworkOrderDeserializer . ReadUInt32 ( span . Slice ( 1 ) ) ;
53
- return new decimal (
54
- // The low 32 bits of a 96-bit integer
55
- lo : ( int ) ( unsignedMantissa & 0x7FFFFFFF ) ,
56
- // The middle 32 bits of a 96-bit integer.
57
- mid : 0 ,
58
- // The high 32 bits of a 96-bit integer.
59
- hi : 0 ,
60
- isNegative : ( unsignedMantissa & 0x80000000 ) != 0 ,
61
- // A power of 10 ranging from 0 to 28.
62
- scale : scale ) ;
63
87
}
64
88
65
89
public static IList ReadArray ( ReadOnlySpan < byte > span , out int bytesRead )
@@ -229,16 +253,16 @@ public static int ReadBits(ReadOnlySpan<byte> span,
229
253
out bool val11 , out bool val12 , out bool val13 , out bool val14 )
230
254
{
231
255
byte bits = span [ 0 ] ;
232
- val1 = ( bits & 0b1000_0000 ) != 0 ;
233
- val2 = ( bits & 0b0100_0000 ) != 0 ;
234
- val3 = ( bits & 0b0010_0000 ) != 0 ;
235
- val4 = ( bits & 0b0001_0000 ) != 0 ;
236
- val5 = ( bits & 0b0000_1000 ) != 0 ;
237
- val6 = ( bits & 0b0000_0100 ) != 0 ;
238
- val7 = ( bits & 0b0000_0010 ) != 0 ;
239
- val8 = ( bits & 0b0000_0001 ) != 0 ;
256
+ val1 = ( bits & 0b1000_0000 ) != 0 ;
257
+ val2 = ( bits & 0b0100_0000 ) != 0 ;
258
+ val3 = ( bits & 0b0010_0000 ) != 0 ;
259
+ val4 = ( bits & 0b0001_0000 ) != 0 ;
260
+ val5 = ( bits & 0b0000_1000 ) != 0 ;
261
+ val6 = ( bits & 0b0000_0100 ) != 0 ;
262
+ val7 = ( bits & 0b0000_0010 ) != 0 ;
263
+ val8 = ( bits & 0b0000_0001 ) != 0 ;
240
264
bits = span [ 1 ] ;
241
- val9 = ( bits & 0b1000_0000 ) != 0 ;
265
+ val9 = ( bits & 0b1000_0000 ) != 0 ;
242
266
val10 = ( bits & 0b0100_0000 ) != 0 ;
243
267
val11 = ( bits & 0b0010_0000 ) != 0 ;
244
268
val12 = ( bits & 0b0001_0000 ) != 0 ;
@@ -342,15 +366,11 @@ public static int GetArrayByteCount(IList val)
342
366
return byteCount ;
343
367
}
344
368
369
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
345
370
public static int WriteDecimal ( Span < byte > span , decimal value )
346
371
{
347
- DecimalToAmqp ( value , out byte scale , out int mantissa ) ;
348
- span [ 0 ] = scale ;
349
- return 1 + WriteLong ( span . Slice ( 1 ) , ( uint ) mantissa ) ;
350
- }
351
-
352
- private static void DecimalToAmqp ( decimal value , out byte scale , out int mantissa )
353
- {
372
+ // Cast the decimal to our struct to avoid the decimal.GetBits allocations.
373
+ DecimalData data = Unsafe . As < decimal , DecimalData > ( ref value ) ;
354
374
// According to the documentation :-
355
375
// - word 0: low-order "mantissa"
356
376
// - word 1, word 2: medium- and high-order "mantissa"
@@ -360,16 +380,16 @@ private static void DecimalToAmqp(decimal value, out byte scale, out int mantiss
360
380
// We need to be careful about the range of word 0, too: we can
361
381
// only take 31 bits worth of it, since the sign bit needs to
362
382
// fit in there too.
363
- int [ ] bitRepresentation = decimal . GetBits ( value ) ;
364
- if ( bitRepresentation [ 1 ] != 0 || // mantissa extends into middle word
365
- bitRepresentation [ 2 ] != 0 || // mantissa extends into top word
366
- bitRepresentation [ 0 ] < 0 ) // mantissa extends beyond 31 bits
383
+ if ( data . Mid != 0 || // mantissa extends into middle word
384
+ data . Hi != 0 || // mantissa extends into top word
385
+ data . Lo < 0 ) // mantissa extends beyond 31 bits
367
386
{
368
- throw new WireFormattingException ( "Decimal overflow in AMQP encoding" , value ) ;
387
+ return ThrowWireFormattingException ( value ) ;
369
388
}
370
- scale = ( byte ) ( ( ( ( uint ) bitRepresentation [ 3 ] ) >> 16 ) & 0xFF ) ;
371
- mantissa = ( int ) ( ( ( ( uint ) bitRepresentation [ 3 ] ) & 0x80000000 ) |
372
- ( ( ( uint ) bitRepresentation [ 0 ] ) & 0x7FFFFFFF ) ) ;
389
+
390
+ span [ 0 ] = ( byte ) ( ( data . Flags >> 16 ) & 0xFF ) ;
391
+ WriteLong ( span . Slice ( 1 ) , ( data . Flags & 0b1000_0000_0000_0000_0000_0000_0000_0000 ) | ( data . Lo & 0b0111_1111_1111_1111_1111_1111_1111_1111 ) ) ;
392
+ return 5 ;
373
393
}
374
394
375
395
public static int WriteFieldValue ( Span < byte > span , object value )
@@ -499,7 +519,7 @@ public static int WriteLonglong(Span<byte> span, ulong val)
499
519
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
500
520
public static int WriteBits ( Span < byte > span , bool val )
501
521
{
502
- span [ 0 ] = val ? ( byte ) 1 : ( byte ) 0 ;
522
+ span [ 0 ] = val ? ( byte ) 1 : ( byte ) 0 ;
503
523
return 1 ;
504
524
}
505
525
@@ -855,5 +875,10 @@ public static int ThrowSyntaxErrorException(uint byteCount)
855
875
{
856
876
throw new SyntaxErrorException ( $ "Long string too long; byte length={ byteCount } , max={ int . MaxValue } ") ;
857
877
}
878
+
879
+ private static int ThrowWireFormattingException ( decimal value )
880
+ {
881
+ throw new WireFormattingException ( "Decimal overflow in AMQP encoding" , value ) ;
882
+ }
858
883
}
859
884
}
0 commit comments