Skip to content

Commit

Permalink
Use specific builder functions for encoding.
Browse files Browse the repository at this point in the history
Instead of keeping internal function for decoding we can
reuse much more efficient builders from the bytestring library.
It increases speed and improves memory usage and allows more
code sharing between the libraries.

Specialization pragmas were removed, they didn't provide much
benefit anyway as the code is recursive and optimizer does not
inline it. However internal functions were not removed so the
user of cassava can use them for semi-efficient numeric types
encoding in case if there are no efficient builders available
directly for those types.
  • Loading branch information
qnikst committed Jul 5, 2020
1 parent 2348030 commit a2c416a
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 47 deletions.
23 changes: 12 additions & 11 deletions src/Data/Csv/Conversion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import qualified Control.Monad.Fail as Fail
import Data.Attoparsec.ByteString.Char8 (double)
import qualified Data.Attoparsec.ByteString.Char8 as A8
import qualified Data.ByteString as B
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as L
#if MIN_VERSION_bytestring(0,10,4)
Expand Down Expand Up @@ -885,7 +886,7 @@ instance FromField Int where

-- | Uses decimal encoding with optional sign.
instance ToField Int where
toField = decimal
toField = viaBuilder . Builder.intDec
{-# INLINE toField #-}

-- | Accepts a signed decimal number. Ignores whitespace.
Expand All @@ -895,7 +896,7 @@ instance FromField Integer where

-- | Uses decimal encoding with optional sign.
instance ToField Integer where
toField = decimal
toField = viaBuilder . Builder.integerDec
{-# INLINE toField #-}

-- | Accepts a signed decimal number. Ignores whitespace.
Expand All @@ -905,7 +906,7 @@ instance FromField Int8 where

-- | Uses decimal encoding with optional sign.
instance ToField Int8 where
toField = decimal
toField = viaBuilder . Builder.int8Dec
{-# INLINE toField #-}

-- | Accepts a signed decimal number. Ignores whitespace.
Expand All @@ -915,7 +916,7 @@ instance FromField Int16 where

-- | Uses decimal encoding with optional sign.
instance ToField Int16 where
toField = decimal
toField = viaBuilder . Builder.int16Dec
{-# INLINE toField #-}

-- | Accepts a signed decimal number. Ignores whitespace.
Expand All @@ -925,7 +926,7 @@ instance FromField Int32 where

-- | Uses decimal encoding with optional sign.
instance ToField Int32 where
toField = decimal
toField = viaBuilder . Builder.int32Dec
{-# INLINE toField #-}

-- | Accepts a signed decimal number. Ignores whitespace.
Expand All @@ -935,7 +936,7 @@ instance FromField Int64 where

-- | Uses decimal encoding with optional sign.
instance ToField Int64 where
toField = decimal
toField = viaBuilder . Builder.int64Dec
{-# INLINE toField #-}

-- | Accepts an unsigned decimal number. Ignores whitespace.
Expand All @@ -945,7 +946,7 @@ instance FromField Word where

-- | Uses decimal encoding.
instance ToField Word where
toField = decimal
toField = viaBuilder . Builder.wordDec
{-# INLINE toField #-}

-- | Accepts an unsigned decimal number. Ignores whitespace.
Expand All @@ -969,7 +970,7 @@ instance FromField Word8 where

-- | Uses decimal encoding.
instance ToField Word8 where
toField = decimal
toField = viaBuilder . Builder.word8Dec
{-# INLINE toField #-}

-- | Accepts an unsigned decimal number. Ignores whitespace.
Expand All @@ -979,7 +980,7 @@ instance FromField Word16 where

-- | Uses decimal encoding.
instance ToField Word16 where
toField = decimal
toField = viaBuilder . Builder.word16Dec
{-# INLINE toField #-}

-- | Accepts an unsigned decimal number. Ignores whitespace.
Expand All @@ -989,7 +990,7 @@ instance FromField Word32 where

-- | Uses decimal encoding.
instance ToField Word32 where
toField = decimal
toField = viaBuilder . Builder.word32Dec
{-# INLINE toField #-}

-- | Accepts an unsigned decimal number. Ignores whitespace.
Expand All @@ -999,7 +1000,7 @@ instance FromField Word64 where

-- | Uses decimal encoding.
instance ToField Word64 where
toField = decimal
toField = viaBuilder . Builder.word64Dec
{-# INLINE toField #-}

instance FromField B.ByteString where
Expand Down
43 changes: 7 additions & 36 deletions src/Data/Csv/Conversion/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Data.Csv.Conversion.Internal
( decimal
, scientific
, realFloat
, viaBuilder
, formatBoundedSigned
) where

import Data.ByteString.Builder (Builder, toLazyByteString, word8, char8,
Expand All @@ -12,7 +14,6 @@ import Data.Array.Base (unsafeAt)
import Data.Array.IArray
import qualified Data.ByteString as B
import Data.Char (ord)
import Data.Int
import qualified Data.Monoid as Mon
import Data.Scientific (Scientific)
import Data.Word
Expand All @@ -29,26 +30,6 @@ decimal = toStrict . toLazyByteString . formatDecimal
-- TODO: Add an optimized version for Integer.

formatDecimal :: Integral a => a -> Builder
{-# RULES "formatDecimal/Int" formatDecimal = formatBoundedSigned
:: Int -> Builder #-}
{-# RULES "formatDecimal/Int8" formatDecimal = formatBoundedSigned
:: Int8 -> Builder #-}
{-# RULES "formatDecimal/Int16" formatDecimal = formatBoundedSigned
:: Int16 -> Builder #-}
{-# RULES "formatDecimal/Int32" formatDecimal = formatBoundedSigned
:: Int32 -> Builder #-}
{-# RULES "formatDecimal/Int64" formatDecimal = formatBoundedSigned
:: Int64 -> Builder #-}
{-# RULES "formatDecimal/Word" formatDecimal = formatPositive
:: Word -> Builder #-}
{-# RULES "formatDecimal/Word8" formatDecimal = formatPositive
:: Word8 -> Builder #-}
{-# RULES "formatDecimal/Word16" formatDecimal = formatPositive
:: Word16 -> Builder #-}
{-# RULES "formatDecimal/Word32" formatDecimal = formatPositive
:: Word32 -> Builder #-}
{-# RULES "formatDecimal/Word64" formatDecimal = formatPositive
:: Word64 -> Builder #-}
{-# NOINLINE formatDecimal #-}
formatDecimal i
| i < 0 = minus Mon.<>
Expand All @@ -58,11 +39,6 @@ formatDecimal i
| otherwise = formatPositive i

formatBoundedSigned :: (Integral a, Bounded a) => a -> Builder
{-# SPECIALIZE formatBoundedSigned :: Int -> Builder #-}
{-# SPECIALIZE formatBoundedSigned :: Int8 -> Builder #-}
{-# SPECIALIZE formatBoundedSigned :: Int16 -> Builder #-}
{-# SPECIALIZE formatBoundedSigned :: Int32 -> Builder #-}
{-# SPECIALIZE formatBoundedSigned :: Int64 -> Builder #-}
formatBoundedSigned i
| i < 0 = minus Mon.<>
if i == minBound
Expand All @@ -71,16 +47,6 @@ formatBoundedSigned i
| otherwise = formatPositive i

formatPositive :: Integral a => a -> Builder
{-# SPECIALIZE formatPositive :: Int -> Builder #-}
{-# SPECIALIZE formatPositive :: Int8 -> Builder #-}
{-# SPECIALIZE formatPositive :: Int16 -> Builder #-}
{-# SPECIALIZE formatPositive :: Int32 -> Builder #-}
{-# SPECIALIZE formatPositive :: Int64 -> Builder #-}
{-# SPECIALIZE formatPositive :: Word -> Builder #-}
{-# SPECIALIZE formatPositive :: Word8 -> Builder #-}
{-# SPECIALIZE formatPositive :: Word16 -> Builder #-}
{-# SPECIALIZE formatPositive :: Word32 -> Builder #-}
{-# SPECIALIZE formatPositive :: Word64 -> Builder #-}
formatPositive = go
where go n | n < 10 = digit n
| otherwise = go (n `quot` 10) Mon.<> digit (n `rem` 10)
Expand Down Expand Up @@ -297,3 +263,8 @@ i2d i = fromIntegral (ord '0' + i)
-- | Word8 list rendering
word8s :: [Word8] -> Builder
word8s = BP.primMapListFixed BP.word8

-- | Helper function that build strict bytestring via a provided builder.
viaBuilder :: Builder -> B.ByteString
viaBuilder = toStrict . toLazyByteString
{-# INLINE viaBuilder #-}

0 comments on commit a2c416a

Please sign in to comment.