|
| 1 | +{-# LANGUAGE BangPatterns, CPP, MagicHash, UnboxedTuples #-} |
| 2 | + |
| 3 | +-- Module: Blaze.Text.Int |
| 4 | +-- Copyright: (c) 2011 MailRank, Inc. |
| 5 | +-- License: BSD3 |
| 6 | +-- Maintainer: Bryan O'Sullivan <[email protected]> |
| 7 | +-- Stability: experimental |
| 8 | +-- Portability: portable |
| 9 | +-- |
| 10 | +-- Efficiently serialize an integral value as a lazy 'L.ByteString'. |
| 11 | + |
| 12 | +module Data.Text.Format.Int |
| 13 | + ( |
| 14 | + digit |
| 15 | + , integral |
| 16 | + , minus |
| 17 | + ) where |
| 18 | + |
| 19 | +import Data.Char (chr) |
| 20 | +import Data.Int (Int8, Int16, Int32, Int64) |
| 21 | +import Data.Monoid (mappend, mempty) |
| 22 | +import Data.Text.Lazy.Builder |
| 23 | +import Data.Word (Word, Word8, Word16, Word32, Word64) |
| 24 | +import GHC.Base (quotInt, remInt) |
| 25 | +import GHC.Num (quotRemInteger) |
| 26 | +import GHC.Types (Int(..)) |
| 27 | + |
| 28 | +#ifdef __GLASGOW_HASKELL__ |
| 29 | +# if __GLASGOW_HASKELL__ < 611 |
| 30 | +import GHC.Integer.Internals |
| 31 | +# else |
| 32 | +import GHC.Integer.GMP.Internals |
| 33 | +# endif |
| 34 | +#endif |
| 35 | + |
| 36 | +#ifdef INTEGER_GMP |
| 37 | +# define PAIR(a,b) (# a,b #) |
| 38 | +#else |
| 39 | +# define PAIR(a,b) (a,b) |
| 40 | +#endif |
| 41 | + |
| 42 | +integral :: Integral a => a -> Builder |
| 43 | +{-# SPECIALIZE integral :: Int -> Builder #-} |
| 44 | +{-# SPECIALIZE integral :: Int8 -> Builder #-} |
| 45 | +{-# SPECIALIZE integral :: Int16 -> Builder #-} |
| 46 | +{-# SPECIALIZE integral :: Int32 -> Builder #-} |
| 47 | +{-# SPECIALIZE integral :: Int64 -> Builder #-} |
| 48 | +{-# SPECIALIZE integral :: Word -> Builder #-} |
| 49 | +{-# SPECIALIZE integral :: Word8 -> Builder #-} |
| 50 | +{-# SPECIALIZE integral :: Word16 -> Builder #-} |
| 51 | +{-# SPECIALIZE integral :: Word32 -> Builder #-} |
| 52 | +{-# SPECIALIZE integral :: Word64 -> Builder #-} |
| 53 | +{-# RULES "integral/Integer" integral = integer :: Integer -> Builder #-} |
| 54 | +integral i |
| 55 | + | i < 0 = minus `mappend` go (-i) |
| 56 | + | otherwise = go i |
| 57 | + where |
| 58 | + go n | n < 10 = digit n |
| 59 | + | otherwise = go (n `quot` 10) `mappend` digit (n `rem` 10) |
| 60 | + |
| 61 | +digit :: Integral a => a -> Builder |
| 62 | +digit n = singleton $! chr (fromIntegral n + 48) |
| 63 | +{-# INLINE digit #-} |
| 64 | + |
| 65 | +minus :: Builder |
| 66 | +minus = singleton '-' |
| 67 | + |
| 68 | +int :: Int -> Builder |
| 69 | +int = integral |
| 70 | +{-# INLINE int #-} |
| 71 | + |
| 72 | +integer :: Integer -> Builder |
| 73 | +integer (S# i#) = int (I# i#) |
| 74 | +integer i |
| 75 | + | i < 0 = minus `mappend` go (-i) |
| 76 | + | otherwise = go i |
| 77 | + where |
| 78 | + go n | n < maxInt = int (fromInteger n) |
| 79 | + | otherwise = putH (splitf (maxInt * maxInt) n) |
| 80 | + |
| 81 | + splitf p n |
| 82 | + | p > n = [n] |
| 83 | + | otherwise = splith p (splitf (p*p) n) |
| 84 | + |
| 85 | + splith p (n:ns) = case n `quotRemInteger` p of |
| 86 | + PAIR(q,r) | q > 0 -> q : r : splitb p ns |
| 87 | + | otherwise -> r : splitb p ns |
| 88 | + splith _ _ = error "splith: the impossible happened." |
| 89 | + |
| 90 | + splitb p (n:ns) = case n `quotRemInteger` p of |
| 91 | + PAIR(q,r) -> q : r : splitb p ns |
| 92 | + splitb _ _ = [] |
| 93 | + |
| 94 | +data T = T !Integer !Int |
| 95 | + |
| 96 | +fstT :: T -> Integer |
| 97 | +fstT (T a _) = a |
| 98 | + |
| 99 | +maxInt :: Integer |
| 100 | +maxDigits :: Int |
| 101 | +T maxInt maxDigits = |
| 102 | + until ((>mi) . (*10) . fstT) (\(T n d) -> T (n*10) (d+1)) (T 10 1) |
| 103 | + where mi = fromIntegral (maxBound :: Int) |
| 104 | + |
| 105 | +putH :: [Integer] -> Builder |
| 106 | +putH (n:ns) = case n `quotRemInteger` maxInt of |
| 107 | + PAIR(x,y) |
| 108 | + | q > 0 -> int q `mappend` pblock r `mappend` putB ns |
| 109 | + | otherwise -> int r `mappend` putB ns |
| 110 | + where q = fromInteger x |
| 111 | + r = fromInteger y |
| 112 | +putH _ = error "putH: the impossible happened" |
| 113 | + |
| 114 | +putB :: [Integer] -> Builder |
| 115 | +putB (n:ns) = case n `quotRemInteger` maxInt of |
| 116 | + PAIR(x,y) -> pblock q `mappend` pblock r `mappend` putB ns |
| 117 | + where q = fromInteger x |
| 118 | + r = fromInteger y |
| 119 | +putB _ = mempty |
| 120 | + |
| 121 | +pblock :: Int -> Builder |
| 122 | +pblock = go maxDigits |
| 123 | + where |
| 124 | + go !d !n |
| 125 | + | d == 1 = digit n |
| 126 | + | otherwise = go (d-1) q `mappend` digit r |
| 127 | + where q = n `quotInt` 10 |
| 128 | + r = n `remInt` 10 |
0 commit comments