@@ -18,10 +18,58 @@ mod bigdecimal {
1818 use pg:: data_types:: PgNumeric ;
1919 use types:: { self , FromSql , ToSql , IsNull } ;
2020
21+ type Digits = Vec < i16 > ;
22+
23+ fn bigdec_add_integer_part ( digits : & mut Digits , absolute : & BigDecimal ) -> i16 {
24+ let mut weight = 0 ;
25+ let ten_k = BigInt :: from ( 10000 ) ;
26+
27+ let mut integer_part = absolute. to_bigint ( ) . expect ( "Can always take integer part of BigDecimal" ) ;
28+
29+ while ten_k < integer_part {
30+ weight += 1 ;
31+ // digit is integer_part REM 10_000
32+ let ( div, digit) = integer_part. div_rem ( & ten_k) ;
33+ digits. push ( digit. to_u16 ( ) . expect ( "digit < 10000, but cannot fit in i16" ) as i16 ) ;
34+ integer_part = div;
35+ }
36+ digits. push ( integer_part. to_string ( ) . parse :: < i16 > ( ) . expect ( "digit < 10000, but cannot fit in i16" ) ) ;
37+
38+ digits. reverse ( ) ;
39+
40+ weight
41+ }
42+
43+ fn bigdec_add_decimal_part ( digits : & mut Digits , absolute : & BigDecimal ) -> u16 {
44+ use std:: str:: FromStr ;
45+
46+ let ten_k = BigDecimal :: from_str ( "10000" ) . expect ( "Could not parse into BigDecimal" ) ;
47+
48+ let decimal_part = absolute;
49+ let mut decimal_part = decimal_part - absolute. with_scale ( 0 ) ;
50+ // scale is the amount of digits to print. to_string() includes a "0.",
51+ // that's why the -2 is there.
52+ let scale = if decimal_part == Zero :: zero ( ) {
53+ 0
54+ } else {
55+ decimal_part. to_string ( ) . len ( ) as u16 - 2
56+ } ;
57+
58+ while decimal_part != BigDecimal :: zero ( ) {
59+ decimal_part *= & ten_k;
60+ let digit = decimal_part. to_bigint ( ) . expect ( "Can always take integer part of BigDecimal" ) ;
61+
62+ // This can be simplified when github.com/akubera/bigdecimal-rs/issues/13 gets
63+ // solved; decimal_part -= &digit; should suffice by then.
64+ decimal_part -= BigDecimal :: new ( digit. clone ( ) , 0 ) ;
65+ digits. push ( digit. to_u16 ( ) . expect ( "digit < 10000, but cannot fit in i16" ) as i16 ) ;
66+ }
67+
68+ scale
69+ }
70+
2171 impl ToSql < types:: Numeric , Pg > for BigDecimal {
2272 fn to_sql < W : Write > ( & self , out : & mut W ) -> Result < IsNull , Box < Error + Send + Sync > > {
23- use std:: str:: FromStr ;
24-
2573 // The encoding of the BigDecimal type for PostgreSQL is a bit complicated:
2674 // PostgreSQL expects the data in base-10000 (so two bytes per 10k),
2775 // and the decimal point should lie on a boundary (as per definition of "base-10000").
@@ -33,43 +81,13 @@ mod bigdecimal {
3381 // the sign, the (integer) part before the decimal, and the part after the decimal.
3482
3583 let absolute = self . abs ( ) ;
36-
3784 let mut digits = vec ! [ ] ;
38- let ten_k = BigInt :: from ( 10000 ) ;
39- let mut integer_part = absolute. to_bigint ( ) . expect ( "Can always take integer part of BigDecimal" ) ;
40- let decimal_part = & absolute;
41- let mut decimal_part = decimal_part - absolute. with_scale ( 0 ) ;
42- // scale is the amount of digits to print. to_string() includes a "0.",
43- // that's why the -2 is there.
44- let scale = if decimal_part == Zero :: zero ( ) {
45- 0
46- } else {
47- decimal_part. to_string ( ) . len ( ) as u16 - 2
48- } ;
49- let mut weight = 0 ;
5085
5186 // Encode the integer part
52- while ten_k < integer_part {
53- weight += 1 ;
54- // digit is integer_part REM 10_000
55- let ( div, digit) = integer_part. div_rem ( & ten_k) ;
56- digits. push ( digit. to_u16 ( ) . expect ( "digit < 10000, but cannot fit in i16" ) as i16 ) ;
57- integer_part = div;
58- }
59- digits. push ( integer_part. to_string ( ) . parse :: < i16 > ( ) . expect ( "digit < 10000, but cannot fit in i16" ) ) ;
60-
61- digits. reverse ( ) ;
87+ let weight = bigdec_add_integer_part ( & mut digits, & absolute) ;
6288
6389 // Encode the decimal part
64- let ten_k = BigDecimal :: from_str ( "10000" ) . expect ( "Could not parse into BigDecimal" ) ;
65- while decimal_part != BigDecimal :: zero ( ) {
66- decimal_part *= & ten_k;
67- let digit = decimal_part. to_bigint ( ) . expect ( "Can always take integer part of BigDecimal" ) ;
68- // This can be simplified when github.com/akubera/bigdecimal-rs/issues/13 gets
69- // solved; decimal_part -= &digit; should suffice by then.
70- decimal_part -= BigDecimal :: new ( digit. clone ( ) , 0 ) ;
71- digits. push ( digit. to_u16 ( ) . expect ( "digit < 10000, but cannot fit in i16" ) as i16 ) ;
72- }
90+ let scale = bigdec_add_decimal_part ( & mut digits, & absolute) ;
7391
7492 let numeric = match self . sign ( ) {
7593 Sign :: Plus => PgNumeric :: Positive {
0 commit comments