11{-# language LambdaCase #-}
2- module Database.PostgreSQL.Protocol.Codecs.Numeric where
3-
4- -- TODO test it
5- import Data.Tuple
6- import Data.Word
7- import Data.Int
8- import Data.Foldable
9- import Data.Scientific
10- import Data.List (unfoldr )
112
12- integerToDigits :: Integer -> [Word16 ]
13- integerToDigits = (reverse . ) . unfoldr $ \ case
14- 0 -> Nothing
15- n -> let (rest, rem ) = n `divMod` nBase in Just (fromIntegral rem , rest)
3+ module Database.PostgreSQL.Protocol.Codecs.Numeric where
164
17- toNumericSign :: Scientific -> Word16
18- toNumericSign s | s >= 0 = 0x0000
19- | otherwise = 0x4000
5+ import Data.Word (Word16 )
6+ import Data.Int (Int16 )
7+ import Data.Foldable (foldl' )
8+ import Data.Scientific (Scientific , scientific , base10Exponent , coefficient )
9+ import Data.List (unfoldr )
2010
11+ {-# INLINE scientificToNumeric #-}
2112scientificToNumeric :: Scientific -> (Int16 , Word16 , [Word16 ])
2213scientificToNumeric number =
2314 let a = base10Exponent number `mod` nBaseDigits
@@ -28,24 +19,40 @@ scientificToNumeric number =
2819 scale = fromIntegral . negate $ min (base10Exponent number) 0
2920 in (weight, scale, digits)
3021
31- digitsToInteger :: [Word16 ] -> Integer
32- digitsToInteger = foldl' (\ acc n -> acc * nBase + fromIntegral n) 0
22+ {-# INLINE numericToScientific #-}
23+ numericToScientific :: Integer -> Int16 -> [Word16 ] -> Scientific
24+ numericToScientific sign weight digits =
25+ let coef = digitsToInteger digits * sign
26+ exp' = (fromIntegral weight + 1 - length digits) * nBaseDigits
27+ in scientific coef exp'
3328
29+ {-# INLINE toNumericSign #-}
30+ toNumericSign :: Scientific -> Word16
31+ toNumericSign s | s >= 0 = 0x0000
32+ | otherwise = 0x4000
33+
34+ {-# INLINE fromNumericSign #-}
3435fromNumericSign :: (Monad m , Num a ) => Word16 -> m a
3536fromNumericSign 0x0000 = pure 1
3637fromNumericSign 0x4000 = pure $ - 1
3738-- NaN code is 0xC000, it is not supported.
3839fromNumericSign _ = fail " Unknown numeric sign"
3940
40- numericToScientific :: Integer -> Int16 -> [Word16 ] -> Scientific
41- numericToScientific sign weight digits =
42- let coef = digitsToInteger digits * sign
43- exp' = (fromIntegral weight + 1 - length digits) * nBaseDigits
44- in scientific coef exp'
41+ {-# INLINE integerToDigits #-}
42+ integerToDigits :: Integer -> [Word16 ]
43+ integerToDigits = (reverse . ) . unfoldr $ \ case
44+ 0 -> Nothing
45+ n -> let (rest, rem ) = n `divMod` nBase in Just (fromIntegral rem , rest)
46+
47+ {-# INLINE digitsToInteger #-}
48+ digitsToInteger :: [Word16 ] -> Integer
49+ digitsToInteger = foldl' (\ acc n -> acc * nBase + fromIntegral n) 0
4550
51+ {-# INLINE nBase #-}
4652nBase :: Num a => a
4753nBase = 10000
4854
55+ {-# INLINE nBaseDigits #-}
4956nBaseDigits :: Num a => a
5057nBaseDigits = 4
5158
0 commit comments