3
3
using System . Collections . Generic ;
4
4
using System . Buffers ;
5
5
using System . Globalization ;
6
+ using System . Linq . Expressions ;
7
+ using System . Buffers . Binary ;
8
+ using System . Numerics ;
6
9
7
10
namespace SciSharp . MySQL . Replication . Types
8
11
{
@@ -12,8 +15,29 @@ namespace SciSharp.MySQL.Replication.Types
12
15
/// <remarks>
13
16
/// Handles the reading and conversion of MySQL DECIMAL values.
14
17
/// </remarks>
15
- class NewDecimalType : IMySQLDataType
18
+ class NewDecimalType : IMySQLDataType , IColumnMetadataLoader
16
19
{
20
+ private static readonly IReadOnlyList < int > DIGITS_PER_INTEGER = new [ ] { 0 , 9 , 19 , 28 , 38 } ;
21
+
22
+ /// <summary>
23
+ /// Loads metadata for the DECIMAL column type.
24
+ /// </summary>
25
+ /// <param name="columnMetadata">the colummn metadata.</param>
26
+ public void LoadMetadataValue ( ColumnMetadata columnMetadata )
27
+ {
28
+ var decimalOptions = new DecimalOptions
29
+ {
30
+ Precision = columnMetadata . MetadataValue & 0xFF ,
31
+ Scale = columnMetadata . MetadataValue >> 8
32
+ } ;
33
+
34
+ // Calculate storage size (MySQL packs 9 digits into 4 bytes)
35
+ decimalOptions . IntegerBytes = ( decimalOptions . Precision - decimalOptions . Scale + 8 ) / 9 * 4 ;
36
+ decimalOptions . FractionBytes = ( decimalOptions . Scale + 8 ) / 9 * 4 ;
37
+
38
+ columnMetadata . Options = decimalOptions ;
39
+ }
40
+
17
41
/// <summary>
18
42
/// Reads a DECIMAL value from the binary log.
19
43
/// </summary>
@@ -22,7 +46,77 @@ class NewDecimalType : IMySQLDataType
22
46
/// <returns>A decimal value representing the MySQL DECIMAL value.</returns>
23
47
public object ReadValue ( ref SequenceReader < byte > reader , ColumnMetadata columnMetadata )
24
48
{
25
- return decimal . Parse ( reader . ReadLengthEncodedString ( ) , CultureInfo . InvariantCulture ) ;
49
+ var options = columnMetadata . Options as DecimalOptions ;
50
+
51
+ reader . TryPeek ( out byte signByte ) ;
52
+ bool negative = ( signByte & 0x80 ) == 0x80 ;
53
+
54
+ // Read integer part
55
+ var intPart = ReadCompactDecimal ( ref reader , ( int ) Math . Min ( options . IntegerBytes , reader . Remaining ) , true ) ;
56
+
57
+ // Read fraction part
58
+ var fracPart = ReadCompactDecimal ( ref reader , ( int ) Math . Min ( options . FractionBytes , reader . Remaining ) , false ) ;
59
+
60
+ // Convert to decimal using direct decimal operations
61
+ decimal intDecimal = ( decimal ) intPart ;
62
+
63
+ // Calculate the fractional part as decimal
64
+ decimal fracDecimal = 0 ;
65
+
66
+ if ( fracPart > 0 )
67
+ {
68
+ // Create the appropriate scaling factor based on the scale
69
+ decimal scaleFactor = ( decimal ) Math . Pow ( 10 , options . Scale ) ;
70
+ fracDecimal = ( decimal ) fracPart / scaleFactor ;
71
+ }
72
+
73
+ var result = intDecimal + fracDecimal ;
74
+
75
+ // Apply sign
76
+ return negative ? - result : result ;
77
+ }
78
+
79
+ private static BigInteger ReadCompactDecimal ( ref SequenceReader < byte > reader , int byteCount , bool isIntegerPart )
80
+ {
81
+ if ( byteCount == 0 )
82
+ return BigInteger . Zero ;
83
+
84
+ Span < byte > bytes = stackalloc byte [ byteCount ] ;
85
+ reader . TryCopyTo ( bytes ) ;
86
+ reader . Advance ( byteCount ) ;
87
+
88
+ // Handle sign bit in the integer part
89
+ if ( isIntegerPart )
90
+ bytes [ 0 ] &= 0x7F ; // Clear the sign bit
91
+
92
+ // Process each 4-byte group
93
+ BigInteger result = BigInteger . Zero ;
94
+
95
+ for ( int i = 0 ; i < byteCount ; i += 4 )
96
+ {
97
+ int groupSize = Math . Min ( 4 , byteCount - i ) ;
98
+ int value = 0 ;
99
+
100
+ // Combine bytes in group (big-endian within the group)
101
+ for ( int j = 0 ; j < groupSize ; j ++ )
102
+ {
103
+ value = ( value << 8 ) | bytes [ i + j ] ;
104
+ }
105
+
106
+ // Each group represents a specific number of decimal digits
107
+ int digitCount = Math . Min ( 9 , DIGITS_PER_INTEGER [ groupSize ] ) ;
108
+ result = result * BigInteger . Pow ( 10 , digitCount ) + value ;
109
+ }
110
+
111
+ return result ;
112
+ }
113
+
114
+ class DecimalOptions
115
+ {
116
+ public int Precision { get ; set ; }
117
+ public int Scale { get ; set ; }
118
+ public int IntegerBytes { get ; set ; }
119
+ public int FractionBytes { get ; set ; }
26
120
}
27
121
}
28
122
}
0 commit comments