Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

ported to new bytestring builder

Slight slowdown 5%.
  • Loading branch information...
commit 6a685753223968881a9f41483f6d8a065f567b0f 1 parent d13767d
Simon Meier authored
41 Data/Csv/Conversion/Internal.hs
@@ -3,11 +3,12 @@ module Data.Csv.Conversion.Internal
3 3 , realFloat
4 4 ) where
5 5
6   -import Blaze.ByteString.Builder
7   -import Blaze.ByteString.Builder.Char8
  6 +import Data.ByteString.Builder
  7 +import qualified Data.ByteString.Builder.Prim as BP
8 8 import Data.Array.Base (unsafeAt)
9 9 import Data.Array.IArray
10 10 import qualified Data.ByteString as B
  11 +import qualified Data.ByteString.Lazy as L
11 12 import Data.Char (ord)
12 13 import Data.Int
13 14 import Data.Word
@@ -18,7 +19,7 @@ import Data.Csv.Compat.Monoid ((<>))
18 19 -- Integers
19 20
20 21 decimal :: Integral a => a -> B.ByteString
21   -decimal = toByteString . formatDecimal
  22 +decimal = L.toStrict . toLazyByteString . formatDecimal
22 23 {-# INLINE decimal #-}
23 24
24 25 -- TODO: Add an optimized version for Integer.
@@ -79,13 +80,13 @@ formatPositive = go
79 80 | otherwise = go (n `quot` 10) <> digit (n `rem` 10)
80 81
81 82 minus :: Builder
82   -minus = fromWord8 45
  83 +minus = word8 45
83 84
84 85 zero :: Word8
85 86 zero = 48
86 87
87 88 digit :: Integral a => a -> Builder
88   -digit n = fromWord8 $! i2w (fromIntegral n)
  89 +digit n = word8 $! i2w (fromIntegral n)
89 90 {-# INLINE digit #-}
90 91
91 92 i2w :: Int -> Word8
@@ -98,7 +99,7 @@ i2w i = zero + fromIntegral i
98 99 realFloat :: RealFloat a => a -> B.ByteString
99 100 {-# SPECIALIZE realFloat :: Float -> B.ByteString #-}
100 101 {-# SPECIALIZE realFloat :: Double -> B.ByteString #-}
101   -realFloat = toByteString . formatRealFloat Generic
  102 +realFloat = L.toStrict . toLazyByteString . formatRealFloat Generic
102 103
103 104 -- | Control the rendering of floating point numbers.
104 105 data FPFormat = Exponent
@@ -114,10 +115,10 @@ formatRealFloat :: RealFloat a => FPFormat -> a -> Builder
114 115 {-# SPECIALIZE formatRealFloat :: FPFormat -> Float -> Builder #-}
115 116 {-# SPECIALIZE formatRealFloat :: FPFormat -> Double -> Builder #-}
116 117 formatRealFloat fmt x
117   - | isNaN x = fromString "NaN"
  118 + | isNaN x = string8 "NaN"
118 119 | isInfinite x = if x < 0
119   - then fromString "-Infinity"
120   - else fromString "Infinity"
  120 + then string8 "-Infinity"
  121 + else string8 "Infinity"
121 122 | x < 0 || isNegativeZero x = minus <> doFmt fmt (floatToDigits (-x))
122 123 | otherwise = doFmt fmt (floatToDigits x)
123 124 where
@@ -130,23 +131,23 @@ formatRealFloat fmt x
130 131 Exponent ->
131 132 let show_e' = formatDecimal (e-1) in
132 133 case ds of
133   - [48] -> fromString "0.0e0"
134   - [d] -> fromWord8 d <> fromString ".0e" <> show_e'
135   - (d:ds') -> fromWord8 d <> fromChar '.' <> fromWord8s ds' <>
136   - fromChar 'e' <> show_e'
  134 + [48] -> string8 "0.0e0"
  135 + [d] -> word8 d <> string8 ".0e" <> show_e'
  136 + (d:ds') -> word8 d <> char8 '.' <> word8s ds' <>
  137 + char8 'e' <> show_e'
137 138 [] -> error "formatRealFloat/doFmt/Exponent: []"
138 139 Fixed
139   - | e <= 0 -> fromString "0." <>
140   - fromByteString (B.replicate (-e) zero) <>
141   - fromWord8s ds
  140 + | e <= 0 -> string8 "0." <>
  141 + byteString (B.replicate (-e) zero) <>
  142 + word8s ds
142 143 | otherwise ->
143 144 let
144   - f 0 s rs = mk0 (reverse s) <> fromChar '.' <> mk0 rs
  145 + f 0 s rs = mk0 (reverse s) <> char8 '.' <> mk0 rs
145 146 f n s [] = f (n-1) (zero:s) []
146 147 f n s (r:rs) = f (n-1) (r:s) rs
147 148 in
148 149 f e [] ds
149   - where mk0 ls = case ls of { [] -> fromWord8 zero ; _ -> fromWord8s ls}
  150 + where mk0 ls = case ls of { [] -> word8 zero ; _ -> word8s ls}
150 151
151 152 -- Based on "Printing Floating-Point Numbers Quickly and Accurately"
152 153 -- 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]]
282 283 {-# INLINE i2d #-}
283 284 i2d :: Int -> Word8
284 285 i2d i = fromIntegral (ord '0' + i)
  286 +
  287 +-- | Word8 list rendering
  288 +word8s :: [Word8] -> Builder
  289 +word8s = BP.primMapListFixed BP.word8
15 Data/Csv/Encoding.hs
@@ -10,7 +10,7 @@
10 10 --
11 11 -- Encoding and decoding of data types into CSV.
12 12 module Data.Csv.Encoding
13   - (
  13 + (
14 14 -- * Encoding and decoding
15 15 decode
16 16 , decodeByName
@@ -28,8 +28,7 @@ module Data.Csv.Encoding
28 28 , encodeByNameWith
29 29 ) where
30 30
31   -import Blaze.ByteString.Builder
32   -import Blaze.ByteString.Builder.Char8
  31 +import Data.ByteString.Builder
33 32 import Control.Applicative
34 33 import qualified Data.Attoparsec.ByteString.Lazy as AL
35 34 import qualified Data.ByteString as B
@@ -132,8 +131,8 @@ encodeWith opts = toLazyByteString
132 131 {-# INLINE encodeWith #-}
133 132
134 133 encodeRecord :: Word8 -> Record -> Builder
135   -encodeRecord delim = mconcat . intersperse (fromWord8 delim)
136   - . map fromByteString . V.toList
  134 +encodeRecord delim = mconcat . intersperse (word8 delim)
  135 + . map byteString . V.toList
137 136 {-# INLINE encodeRecord #-}
138 137
139 138 -- | Like 'encodeByName', but lets you customize how the CSV data is
@@ -142,7 +141,7 @@ encodeByNameWith :: ToNamedRecord a => EncodeOptions -> Header -> V.Vector a
142 141 -> L.ByteString
143 142 encodeByNameWith opts hdr v =
144 143 toLazyByteString ((encodeRecord (encDelimiter opts) hdr) <>
145   - fromByteString "\r\n" <> records)
  144 + byteString "\r\n" <> records)
146 145 where
147 146 records = unlines
148 147 . map (encodeRecord (encDelimiter opts)
@@ -166,7 +165,7 @@ moduleError func msg = error $ "Data.Csv.Encoding." ++ func ++ ": " ++ msg
166 165
167 166 unlines :: [Builder] -> Builder
168 167 unlines [] = mempty
169   -unlines (b:bs) = b <> fromString "\r\n" <> unlines bs
  168 +unlines (b:bs) = b <> byteString "\r\n" <> unlines bs
170 169
171 170 intersperse :: Builder -> [Builder] -> [Builder]
172 171 intersperse _ [] = []
@@ -184,4 +183,4 @@ decodeWithP p to s =
184 183 Error msg -> Left $ "conversion error: " ++ msg
185 184 AL.Fail left _ msg -> Left $ "parse error (" ++ msg ++ ") at " ++
186 185 show (BL8.unpack left)
187   -{-# INLINE decodeWithP #-}
  186 +{-# INLINE decodeWithP #-}
10 Data/Csv/Parser.hs
@@ -24,8 +24,7 @@ module Data.Csv.Parser
24 24 , field
25 25 ) where
26 26
27   -import Blaze.ByteString.Builder (fromByteString, toByteString)
28   -import Blaze.ByteString.Builder.Char.Utf8 (fromChar)
  27 +import Data.ByteString.Builder (byteString, toLazyByteString, charUtf8)
29 28 import Control.Applicative
30 29 import Data.Attoparsec.Char8 hiding (Parser, Result, parse)
31 30 import qualified Data.Attoparsec as A
@@ -33,6 +32,7 @@ import qualified Data.Attoparsec.Lazy as AL
33 32 import qualified Data.Attoparsec.Zepto as Z
34 33 import qualified Data.ByteString as S
35 34 import qualified Data.ByteString.Unsafe as S
  35 +import qualified Data.ByteString.Lazy as L
36 36 import qualified Data.HashMap.Strict as HM
37 37 import Data.Monoid
38 38 import qualified Data.Vector as V
@@ -137,18 +137,18 @@ dquote :: AL.Parser Char
137 137 dquote = char '"'
138 138
139 139 unescape :: Z.Parser S.ByteString
140   -unescape = toByteString <$> go mempty where
  140 +unescape = (L.toStrict . toLazyByteString) <$> go mempty where
141 141 go acc = do
142 142 h <- Z.takeWhile (/= doubleQuote)
143 143 let rest = do
144 144 start <- Z.take 2
145 145 if (S.unsafeHead start == doubleQuote &&
146 146 S.unsafeIndex start 1 == doubleQuote)
147   - then go (acc `mappend` fromByteString h `mappend` fromChar '"')
  147 + then go (acc `mappend` byteString h `mappend` charUtf8 '"')
148 148 else fail "invalid CSV escape sequence"
149 149 done <- Z.atEnd
150 150 if done
151   - then return (acc `mappend` fromByteString h)
  151 + then return (acc `mappend` byteString h)
152 152 else rest
153 153
154 154 doubleQuote, newline, cr :: Word8
5 cassava.cabal
@@ -32,8 +32,7 @@ Library
32 32 Build-depends: array,
33 33 attoparsec >= 0.10.2,
34 34 base < 5,
35   - blaze-builder,
36   - bytestring,
  35 + bytestring >= 0.10.2,
37 36 containers,
38 37 text,
39 38 unordered-containers,
@@ -44,7 +43,7 @@ Library
44 43 if impl(ghc >= 7.2.1)
45 44 cpp-options: -DGENERICS
46 45 build-depends: ghc-prim >= 0.2
47   -
  46 +
48 47 Test-suite unit-tests
49 48 Type: exitcode-stdio-1.0
50 49 Main-is: UnitTests.hs

0 comments on commit 6a68575

Please sign in to comment.
Something went wrong with that request. Please try again.