33using System . Collections . Generic ;
44using System . Buffers ;
55using System . Globalization ;
6+ using System . Linq . Expressions ;
7+ using System . Buffers . Binary ;
8+ using System . Numerics ;
69
710namespace SciSharp . MySQL . Replication . Types
811{
@@ -12,8 +15,29 @@ namespace SciSharp.MySQL.Replication.Types
1215 /// <remarks>
1316 /// Handles the reading and conversion of MySQL DECIMAL values.
1417 /// </remarks>
15- class NewDecimalType : IMySQLDataType
18+ class NewDecimalType : IMySQLDataType , IColumnMetadataLoader
1619 {
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+
1741 /// <summary>
1842 /// Reads a DECIMAL value from the binary log.
1943 /// </summary>
@@ -22,7 +46,77 @@ class NewDecimalType : IMySQLDataType
2246 /// <returns>A decimal value representing the MySQL DECIMAL value.</returns>
2347 public object ReadValue ( ref SequenceReader < byte > reader , ColumnMetadata columnMetadata )
2448 {
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 ; }
26120 }
27121 }
28122}
0 commit comments