Permalink
Browse files

ported to new bytestring builder

Slight slowdown 5%.
  • Loading branch information...
1 parent d13767d commit 6a685753223968881a9f41483f6d8a065f567b0f @meiersi committed Oct 2, 2012
Showing with 37 additions and 34 deletions.
  1. +23 −18 Data/Csv/Conversion/Internal.hs
  2. +7 −8 Data/Csv/Encoding.hs
  3. +5 −5 Data/Csv/Parser.hs
  4. +2 −3 cassava.cabal
View
41 Data/Csv/Conversion/Internal.hs
@@ -3,11 +3,12 @@ module Data.Csv.Conversion.Internal
, realFloat
) where
-import Blaze.ByteString.Builder
-import Blaze.ByteString.Builder.Char8
+import Data.ByteString.Builder
+import qualified Data.ByteString.Builder.Prim as BP
import Data.Array.Base (unsafeAt)
import Data.Array.IArray
import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
import Data.Char (ord)
import Data.Int
import Data.Word
@@ -18,7 +19,7 @@ import Data.Csv.Compat.Monoid ((<>))
-- Integers
decimal :: Integral a => a -> B.ByteString
-decimal = toByteString . formatDecimal
+decimal = L.toStrict . toLazyByteString . formatDecimal
{-# INLINE decimal #-}
-- TODO: Add an optimized version for Integer.
@@ -79,13 +80,13 @@ formatPositive = go
| otherwise = go (n `quot` 10) <> digit (n `rem` 10)
minus :: Builder
-minus = fromWord8 45
+minus = word8 45
zero :: Word8
zero = 48
digit :: Integral a => a -> Builder
-digit n = fromWord8 $! i2w (fromIntegral n)
+digit n = word8 $! i2w (fromIntegral n)
{-# INLINE digit #-}
i2w :: Int -> Word8
@@ -98,7 +99,7 @@ i2w i = zero + fromIntegral i
realFloat :: RealFloat a => a -> B.ByteString
{-# SPECIALIZE realFloat :: Float -> B.ByteString #-}
{-# SPECIALIZE realFloat :: Double -> B.ByteString #-}
-realFloat = toByteString . formatRealFloat Generic
+realFloat = L.toStrict . toLazyByteString . formatRealFloat Generic
-- | Control the rendering of floating point numbers.
data FPFormat = Exponent
@@ -114,10 +115,10 @@ formatRealFloat :: RealFloat a => FPFormat -> a -> Builder
{-# SPECIALIZE formatRealFloat :: FPFormat -> Float -> Builder #-}
{-# SPECIALIZE formatRealFloat :: FPFormat -> Double -> Builder #-}
formatRealFloat fmt x
- | isNaN x = fromString "NaN"
+ | isNaN x = string8 "NaN"
| isInfinite x = if x < 0
- then fromString "-Infinity"
- else fromString "Infinity"
+ then string8 "-Infinity"
+ else string8 "Infinity"
| x < 0 || isNegativeZero x = minus <> doFmt fmt (floatToDigits (-x))
| otherwise = doFmt fmt (floatToDigits x)
where
@@ -130,23 +131,23 @@ formatRealFloat fmt x
Exponent ->
let show_e' = formatDecimal (e-1) in
case ds of
- [48] -> fromString "0.0e0"
- [d] -> fromWord8 d <> fromString ".0e" <> show_e'
- (d:ds') -> fromWord8 d <> fromChar '.' <> fromWord8s ds' <>
- fromChar 'e' <> show_e'
+ [48] -> string8 "0.0e0"
+ [d] -> word8 d <> string8 ".0e" <> show_e'
+ (d:ds') -> word8 d <> char8 '.' <> word8s ds' <>
+ char8 'e' <> show_e'
[] -> error "formatRealFloat/doFmt/Exponent: []"
Fixed
- | e <= 0 -> fromString "0." <>
- fromByteString (B.replicate (-e) zero) <>
- fromWord8s ds
+ | e <= 0 -> string8 "0." <>
+ byteString (B.replicate (-e) zero) <>
+ word8s ds
| otherwise ->
let
- f 0 s rs = mk0 (reverse s) <> fromChar '.' <> mk0 rs
+ f 0 s rs = mk0 (reverse s) <> char8 '.' <> mk0 rs
f n s [] = f (n-1) (zero:s) []
f n s (r:rs) = f (n-1) (r:s) rs
in
f e [] ds
- where mk0 ls = case ls of { [] -> fromWord8 zero ; _ -> fromWord8s ls}
+ where mk0 ls = case ls of { [] -> word8 zero ; _ -> word8s ls}
-- Based on "Printing Floating-Point Numbers Quickly and Accurately"
-- by R.G. Burger and R.K. Dybvig in PLDI 96.
@@ -282,3 +283,7 @@ expts10 = array (minExpt,maxExpt10) [(n,10^n) | n <- [minExpt .. maxExpt10]]
{-# INLINE i2d #-}
i2d :: Int -> Word8
i2d i = fromIntegral (ord '0' + i)
+
+-- | Word8 list rendering
+word8s :: [Word8] -> Builder
+word8s = BP.primMapListFixed BP.word8
View
15 Data/Csv/Encoding.hs
@@ -10,7 +10,7 @@
--
-- Encoding and decoding of data types into CSV.
module Data.Csv.Encoding
- (
+ (
-- * Encoding and decoding
decode
, decodeByName
@@ -28,8 +28,7 @@ module Data.Csv.Encoding
, encodeByNameWith
) where
-import Blaze.ByteString.Builder
-import Blaze.ByteString.Builder.Char8
+import Data.ByteString.Builder
import Control.Applicative
import qualified Data.Attoparsec.ByteString.Lazy as AL
import qualified Data.ByteString as B
@@ -132,8 +131,8 @@ encodeWith opts = toLazyByteString
{-# INLINE encodeWith #-}
encodeRecord :: Word8 -> Record -> Builder
-encodeRecord delim = mconcat . intersperse (fromWord8 delim)
- . map fromByteString . V.toList
+encodeRecord delim = mconcat . intersperse (word8 delim)
+ . map byteString . V.toList
{-# INLINE encodeRecord #-}
-- | Like 'encodeByName', but lets you customize how the CSV data is
@@ -142,7 +141,7 @@ encodeByNameWith :: ToNamedRecord a => EncodeOptions -> Header -> V.Vector a
-> L.ByteString
encodeByNameWith opts hdr v =
toLazyByteString ((encodeRecord (encDelimiter opts) hdr) <>
- fromByteString "\r\n" <> records)
+ byteString "\r\n" <> records)
where
records = unlines
. map (encodeRecord (encDelimiter opts)
@@ -166,7 +165,7 @@ moduleError func msg = error $ "Data.Csv.Encoding." ++ func ++ ": " ++ msg
unlines :: [Builder] -> Builder
unlines [] = mempty
-unlines (b:bs) = b <> fromString "\r\n" <> unlines bs
+unlines (b:bs) = b <> byteString "\r\n" <> unlines bs
intersperse :: Builder -> [Builder] -> [Builder]
intersperse _ [] = []
@@ -184,4 +183,4 @@ decodeWithP p to s =
Error msg -> Left $ "conversion error: " ++ msg
AL.Fail left _ msg -> Left $ "parse error (" ++ msg ++ ") at " ++
show (BL8.unpack left)
-{-# INLINE decodeWithP #-}
+{-# INLINE decodeWithP #-}
View
10 Data/Csv/Parser.hs
@@ -24,15 +24,15 @@ module Data.Csv.Parser
, field
) where
-import Blaze.ByteString.Builder (fromByteString, toByteString)
-import Blaze.ByteString.Builder.Char.Utf8 (fromChar)
+import Data.ByteString.Builder (byteString, toLazyByteString, charUtf8)
import Control.Applicative
import Data.Attoparsec.Char8 hiding (Parser, Result, parse)
import qualified Data.Attoparsec as A
import qualified Data.Attoparsec.Lazy as AL
import qualified Data.Attoparsec.Zepto as Z
import qualified Data.ByteString as S
import qualified Data.ByteString.Unsafe as S
+import qualified Data.ByteString.Lazy as L
import qualified Data.HashMap.Strict as HM
import Data.Monoid
import qualified Data.Vector as V
@@ -137,18 +137,18 @@ dquote :: AL.Parser Char
dquote = char '"'
unescape :: Z.Parser S.ByteString
-unescape = toByteString <$> go mempty where
+unescape = (L.toStrict . toLazyByteString) <$> go mempty where
go acc = do
h <- Z.takeWhile (/= doubleQuote)
let rest = do
start <- Z.take 2
if (S.unsafeHead start == doubleQuote &&
S.unsafeIndex start 1 == doubleQuote)
- then go (acc `mappend` fromByteString h `mappend` fromChar '"')
+ then go (acc `mappend` byteString h `mappend` charUtf8 '"')
else fail "invalid CSV escape sequence"
done <- Z.atEnd
if done
- then return (acc `mappend` fromByteString h)
+ then return (acc `mappend` byteString h)
else rest
doubleQuote, newline, cr :: Word8
View
5 cassava.cabal
@@ -32,8 +32,7 @@ Library
Build-depends: array,
attoparsec >= 0.10.2,
base < 5,
- blaze-builder,
- bytestring,
+ bytestring >= 0.10.2,
containers,
text,
unordered-containers,
@@ -44,7 +43,7 @@ Library
if impl(ghc >= 7.2.1)
cpp-options: -DGENERICS
build-depends: ghc-prim >= 0.2
-
+
Test-suite unit-tests
Type: exitcode-stdio-1.0
Main-is: UnitTests.hs

0 comments on commit 6a68575

Please sign in to comment.