bytestring-read 0.2.1 → 0.3.0
raw patch · 6 files changed
+112/−84 lines, 6 filesdep ~criterionPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: criterion
API changes (from Hackage documentation)
+ Data.ByteString.Read: class Source a
+ Data.ByteString.Read.Class: class Source a
+ Data.ByteString.Read.Class: empty :: Source a => a
+ Data.ByteString.Read.Class: head :: Source a => a -> Word8
+ Data.ByteString.Read.Class: instance Source ByteString
+ Data.ByteString.Read.Class: null :: Source a => a -> Bool
+ Data.ByteString.Read.Class: tail :: Source a => a -> a
- Data.ByteString.Read: double :: ByteString -> Maybe (Double, ByteString)
+ Data.ByteString.Read: double :: Source s => s -> Maybe (Double, s)
- Data.ByteString.Read: fractional :: ReadFractional r => ByteString -> Maybe (r, ByteString)
+ Data.ByteString.Read: fractional :: (ReadFractional r, Source s) => s -> Maybe (r, s)
- Data.ByteString.Read: int :: ByteString -> Maybe (Int, ByteString)
+ Data.ByteString.Read: int :: Source s => s -> Maybe (Int, s)
- Data.ByteString.Read: integral :: Num n => ByteString -> Maybe (n, ByteString)
+ Data.ByteString.Read: integral :: (Num n, Source s) => s -> Maybe (n, s)
- Data.ByteString.Read: signed :: Num r => (ByteString -> Maybe (r, ByteString)) -> ByteString -> Maybe (r, ByteString)
+ Data.ByteString.Read: signed :: (Source s, Num r) => (s -> Maybe (r, s)) -> s -> Maybe (r, s)
- Data.ByteString.Read.Fractional: double :: ByteString -> Maybe (Double, ByteString)
+ Data.ByteString.Read.Fractional: double :: Source s => s -> Maybe (Double, s)
- Data.ByteString.Read.Fractional: fractional :: ReadFractional r => ByteString -> Maybe (r, ByteString)
+ Data.ByteString.Read.Fractional: fractional :: (ReadFractional r, Source s) => s -> Maybe (r, s)
- Data.ByteString.Read.Fractional: fractional' :: (Radix b, ReadFractional r) => proxy b -> ByteString -> Maybe (r, ByteString)
+ Data.ByteString.Read.Fractional: fractional' :: (Radix b, ReadFractional r, Source s) => proxy b -> s -> Maybe (r, s)
- Data.ByteString.Read.Fractional: fractional10 :: ReadFractional r => ByteString -> Maybe (r, ByteString)
+ Data.ByteString.Read.Fractional: fractional10 :: (ReadFractional r, Source s) => s -> Maybe (r, s)
- Data.ByteString.Read.Integral: int :: ByteString -> Maybe (Int, ByteString)
+ Data.ByteString.Read.Integral: int :: Source s => s -> Maybe (Int, s)
- Data.ByteString.Read.Integral: integral :: Num n => ByteString -> Maybe (n, ByteString)
+ Data.ByteString.Read.Integral: integral :: (Num n, Source s) => s -> Maybe (n, s)
- Data.ByteString.Read.Integral: integral' :: (Radix b, Num n) => proxy b -> ByteString -> Maybe (n, ByteString)
+ Data.ByteString.Read.Integral: integral' :: (Radix b, Num n, Source s) => proxy b -> s -> Maybe (n, s)
Files
- bench/main.hs +10/−5
- bytestring-read.cabal +2/−2
- src/Data/ByteString/Read.hs +13/−14
- src/Data/ByteString/Read/Class.hs +28/−0
- src/Data/ByteString/Read/Fractional.hs +44/−46
- src/Data/ByteString/Read/Integral.hs +15/−17
bench/main.hs view
@@ -6,6 +6,7 @@ import Data.ByteString(ByteString) import qualified Data.ByteString.Char8 as SC+import qualified Data.ByteString.Lazy as L import qualified Data.Text.Encoding as T @@ -14,13 +15,13 @@ import qualified Data.ByteString.Read as R import qualified Data.Text.Read as T-import qualified Data.ByteString.Lex.Double as L+import qualified Data.ByteString.Lex.Double as Lex import qualified Data.Attoparsec.ByteString.Char8 as A -short :: ByteString+short :: IsString s => s short = "-2342395232123424.3424346343524e3" -long :: ByteString+long :: IsString s => s long = "-234232345678976521345895325678987654321345678987654321345689643213595232123424.34243463435223456789321367899231808534492500740957389523850293482093852039587495203586329850238562834290374029844e3" checkConsumed :: (IsString a, Eq a) => (v, a) -> v@@ -30,11 +31,14 @@ bytestringRead :: ByteString -> Double bytestringRead = maybe (error "parse error") checkConsumed . R.signed R.fractional +lazyBytestringRead :: L.ByteString -> Double+lazyBytestringRead = maybe (error "parse error") checkConsumed . R.signed R.fractional+ text :: ByteString -> Double text = either (const $ error "parse error") checkConsumed . T.signed T.double . T.decodeUtf8 bytestringLexing :: ByteString -> Double-bytestringLexing = maybe (error "parse error") checkConsumed . L.readDouble+bytestringLexing = maybe (error "parse error") checkConsumed . Lex.readDouble attoparsec :: ByteString -> Double attoparsec = either (const $ error "parse error") id . A.parseOnly (fmap toRealFloat A.scientific)@@ -42,7 +46,7 @@ read' :: ByteString -> Double read' = read . SC.unpack -doBench :: String -> (ByteString -> Double) -> Benchmark+doBench :: IsString s => String -> (s -> Double) -> Benchmark doBench n f = bgroup n [ bench "short" $ nf f short , bench "long" $ nf f long@@ -51,6 +55,7 @@ main :: IO () main = defaultMain [ doBench "bytestring-read" bytestringRead+ , doBench "bytestring-read(Lazy)" lazyBytestringRead , doBench "text" text , doBench "bytestring-lexing" bytestringLexing , doBench "attoparsec" attoparsec
bytestring-read.cabal view
@@ -1,5 +1,5 @@ name: bytestring-read-version: 0.2.1+version: 0.3.0 synopsis: fast ByteString to number converting library description: benchmark: <http://philopon.github.io/bytestring-read/bench.html> license: MIT@@ -34,7 +34,7 @@ , bytestring , text , bytestring-lexing >=0.4 && <0.5- , criterion >=1.0 && <1.1+ , criterion >=1.0 && <1.2 , scientific , attoparsec ghc-options: -Wall -O2
src/Data/ByteString/Read.hs view
@@ -1,6 +1,7 @@ module Data.ByteString.Read- ( -- * fractional- ReadFractional+ ( Source+ -- * fractional+ , ReadFractional , fractional , double @@ -17,17 +18,15 @@ import Control.Arrow -import Data.ByteString(ByteString)-import Data.ByteString.Unsafe-import qualified Data.ByteString as S--import Data.ByteString.Read.Class+import Data.ByteString.Read.Class as C import Data.ByteString.Read.Integral import Data.ByteString.Read.Fractional import Data.ByteString.Read.DEPRECATED -- $setup -- >>> :set -XDataKinds -XOverloadedStrings+-- >>> import qualified Data.ByteString as S+-- >>> import qualified Data.ByteString.Lazy as L -- | convert unsigned parser to signed parser. --@@ -35,17 +34,17 @@ -- -- * sign (+, -) (optional) ----- >>> signed double "12.4"+-- >>> signed double ("12.4" :: S.ByteString) -- Just (12.4,"")--- >>> signed double "-3.21e3"+-- >>> signed double ("-3.21e3" :: L.ByteString) -- Just (-3210.0,"")--- >>> signed double "+0x1f.4"+-- >>> signed double ("+0x1f.4" :: S.ByteString) -- Just (31.25,"")-signed :: Num r => (ByteString -> Maybe (r, ByteString)) -> ByteString -> Maybe (r, ByteString)+signed :: (Source s, Num r) => (s -> Maybe (r, s)) -> s -> Maybe (r, s) signed f s- | S.null s = Nothing- | unsafeHead s == minus = first negate `fmap` f (unsafeTail s)- | unsafeHead s == plus = f (unsafeTail s)+ | C.null s = Nothing+ | C.head s == minus = first negate `fmap` f (C.tail s)+ | C.head s == plus = f (C.tail s) | otherwise = f s where minus = 45
src/Data/ByteString/Read/Class.hs view
@@ -8,10 +8,14 @@ module Data.ByteString.Read.Class ( ReadFractional(..) , Radix(..)+ , Source(..) ) where import Data.Word import GHC.TypeLits.Compat+import qualified Data.ByteString as S+import Data.ByteString.Unsafe+import qualified Data.ByteString.Lazy as L class (Fractional a, Num (Fraction a), Ord (Fraction a)) => ReadFractional a where -- | data type to store fractional part of floating@@ -121,3 +125,27 @@ defineRadixOver10(34, 88, 120) defineRadixOver10(35, 89, 121) defineRadixOver10(36, 90, 122)++class Source a where+ null :: a -> Bool+ empty :: a+ head :: a -> Word8+ tail :: a -> a++instance Source S.ByteString where+ null = S.null+ empty = S.empty+ head = unsafeHead+ tail = unsafeTail+ {-# INLINE null #-}+ {-# INLINE head #-}+ {-# INLINE tail #-}++instance Source L.ByteString where+ null = L.null+ empty = L.empty+ head = L.head+ tail = L.tail+ {-# INLINE null #-}+ {-# INLINE head #-}+ {-# INLINE tail #-}
src/Data/ByteString/Read/Fractional.hs view
@@ -18,32 +18,30 @@ , fractional' ) where -import Data.ByteString.Unsafe-import Data.ByteString(ByteString)-import qualified Data.ByteString as S- import GHC.TypeLits.Compat import Data.Proxy.Compat -import Data.ByteString.Read.Class+import Data.ByteString.Read.Class as C -- $setup -- >>> :set -XDataKinds -XOverloadedStrings+-- >>> import qualified Data.ByteString as S+-- >>> import qualified Data.ByteString.Lazy as L -integral :: forall proxy n r. (Radix n, ReadFractional r, Ord (Fraction r), Num (Fraction r))- => proxy n -> ByteString -> (Fraction r, Int, Int, ByteString)+integral :: forall proxy n r s. (Radix n, ReadFractional r, Ord (Fraction r), Num (Fraction r), Source s)+ => proxy n -> s -> (Fraction r, Int, Int, s) integral pn = loop 0 0 0 where pr :: Proxy r pr = Proxy loop !i !d !ad !s- | S.null s = (i, d, ad, s)- | not (isDigit pn (unsafeHead s)) = (i, d, ad, s)- | maybe False (i >=) (maxValue pr) = loop i d (ad + 1) (unsafeTail s)+ | C.null s = (i, d, ad, s)+ | not (isDigit pn (C.head s)) = (i, d, ad, s)+ | maybe False (i >=) (maxValue pr) = loop i d (ad + 1) (C.tail s) | otherwise = loop- (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (unsafeHead s) :: Fraction r))- (d+1) ad (unsafeTail s)+ (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (C.head s) :: Fraction r))+ (d+1) ad (C.tail s) {-# INLINABLE integral #-} toFractional :: (Radix b, ReadFractional r, Fractional r)@@ -59,30 +57,30 @@ -- -- * fractional(0.1, 12224.3543) ----- >>> fractional' (Proxy :: Proxy 36) "12z" :: Maybe (Double, ByteString)+-- >>> fractional' (Proxy :: Proxy 36) "12z" :: Maybe (Double, S.ByteString) -- Just (1403.0,"")--- >>> fractional' (Proxy :: Proxy 2) "1012" :: Maybe (Double, ByteString)+-- >>> fractional' (Proxy :: Proxy 2) "1012" :: Maybe (Double, L.ByteString) -- Just (5.0,"2")--- >>> fractional' (Proxy :: Proxy 10) "a12" :: Maybe (Double, ByteString)+-- >>> fractional' (Proxy :: Proxy 10) "a12" :: Maybe (Double, S.ByteString) -- Nothing-fractional' :: (Radix b, ReadFractional r) => proxy b -> ByteString -> Maybe (r, ByteString)+fractional' :: (Radix b, ReadFractional r, Source s) => proxy b -> s -> Maybe (r, s) fractional' pn s = case integral pn s of (_, 0, _, _) -> Nothing- (q, _, d, "") -> Just (fromFraction q * fromIntegral (natVal pn) ^ d, "") (q, _, d, s1)- | unsafeHead s1 /= dot -> Just (fromFraction q, s1)- | otherwise -> case integral pn (unsafeTail s1) of+ | C.null s1 -> Just (fromFraction q * fromIntegral (natVal pn) ^ d, C.empty)+ | C.head s1 /= dot -> Just (fromFraction q, s1)+ | otherwise -> case integral pn (C.tail s1) of (_, 0, _, _) -> Just (fromFraction q, s1) (r, d', _, s2) -> Just (toFractional pn q r d d', s2) where dot = 46 {-# INLINABLE fractional' #-} -exponential :: ByteString -> (Int, ByteString)+exponential :: forall s. Source s => s -> (Int, s) exponential s0- | S.null s0 = (0, s0)- | isE (unsafeHead s0) = sign (unsafeTail s0)- | otherwise = (0, s0)+ | C.null s0 = (0, s0)+ | isE (C.head s0) = sign (C.tail s0)+ | otherwise = (0, s0) where isE w = w == 101 || w == 69 @@ -90,12 +88,12 @@ plus = 43 sign s1- | S.null s1 = (0, s0)- | unsafeHead s1 == plus = expPart $ unsafeTail s1- | unsafeHead s1 == minus = let (e, s) = expPart $ unsafeTail s1 in (-e, s)- | otherwise = expPart s1+ | C.null s1 = (0, s0)+ | C.head s1 == plus = expPart $ C.tail s1+ | C.head s1 == minus = let (e, s) = expPart $ C.tail s1 in (-e, s)+ | otherwise = expPart s1 - expPart s2 = case integral (Proxy :: Proxy 10) s2 :: (Fraction Double, Int, Int, ByteString) of+ expPart s2 = case integral (Proxy :: Proxy 10) s2 :: (Fraction Double, Int, Int, s) of (_, 0, _, _) -> (0, s0) (e, _, _, s) -> (fromFraction e, s) {-# INLINABLE exponential #-}@@ -115,15 +113,15 @@ -- * fractional(0.1, 12224.3543) -- * exponential (e1, E+2, e-123) (optional) ----- >>> fractional10 "12.5" :: Maybe (Double, ByteString)+-- >>> fractional10 "12.5" :: Maybe (Double, S.ByteString) -- Just (12.5,"")--- >>> fractional10 "124.1e12" :: Maybe (Double, ByteString)+-- >>> fractional10 "124.1e12" :: Maybe (Double, L.ByteString) -- Just (1.241e14,"")--- >>> fractional10 "12.5e-3" :: Maybe (Double, ByteString)+-- >>> fractional10 "12.5e-3" :: Maybe (Double, S.ByteString) -- Just (1.25e-2,"")--- >>> fractional10 "3.11e+3" :: Maybe (Double, ByteString)+-- >>> fractional10 "3.11e+3" :: Maybe (Double, L.ByteString) -- Just (3110.0,"")-fractional10 :: ReadFractional r => ByteString -> Maybe (r, ByteString)+fractional10 :: (ReadFractional r, Source s) => s -> Maybe (r, s) fractional10 s = fractional' (Proxy :: Proxy 10) s >>= \(f, s') -> let (e, s'') = exponential s' in Just (setExpPart e f, s'')@@ -137,33 +135,33 @@ -- * fractional(0.1, 12224.3543) -- * exponential (e1, E+2, e-123) (10-radixed only, optional) ----- >>> fractional "12.4" :: Maybe (Double, ByteString)+-- >>> fractional "12.4" :: Maybe (Double, S.ByteString) -- Just (12.4,"")--- >>> fractional "1.23e12" :: Maybe (Double, ByteString)+-- >>> fractional "1.23e12" :: Maybe (Double, L.ByteString) -- Just (1.23e12,"")--- >>> fractional "0o0.4" :: Maybe (Double, ByteString)+-- >>> fractional "0o0.4" :: Maybe (Double, S.ByteString) -- Just (0.5,"")--- >>> fractional "0x3f.12" :: Maybe (Double, ByteString)+-- >>> fractional "0x3f.12" :: Maybe (Double, L.ByteString) -- Just (63.0703125,"")-fractional :: ReadFractional r => ByteString -> Maybe (r, ByteString)+fractional :: (ReadFractional r, Source s) => s -> Maybe (r, s) fractional s0- | S.null s0 = Nothing- | unsafeHead s0 == zero = radix $ unsafeTail s0- | otherwise = fractional10 s0+ | C.null s0 = Nothing+ | C.head s0 == zero = radix $ C.tail s0+ | otherwise = fractional10 s0 where zero = 48 isX w = w == 120 || w == 88 isO w = w == 111 || w == 79 radix s1- | S.null s1 = Just (0, "")- | isX (unsafeHead s1) = fractional' (Proxy :: Proxy 16) (unsafeTail s1)- | isO (unsafeHead s1) = fractional' (Proxy :: Proxy 8) (unsafeTail s1)- | otherwise = fractional10 s0+ | C.null s1 = Just (0, C.empty)+ | isX (C.head s1) = fractional' (Proxy :: Proxy 16) (C.tail s1)+ | isO (C.head s1) = fractional' (Proxy :: Proxy 8) (C.tail s1)+ | otherwise = fractional10 s0 {-# INLINABLE fractional #-} -- | @ -- double = fractional -- @-double :: ByteString -> Maybe (Double, ByteString)+double :: Source s => s -> Maybe (Double, s) double = fractional
src/Data/ByteString/Read/Integral.hs view
@@ -7,38 +7,36 @@ , int ) where -import Data.ByteString(ByteString)-import qualified Data.ByteString as S-import Data.ByteString.Unsafe- import Data.Proxy.Compat import GHC.TypeLits.Compat -import Data.ByteString.Read.Class+import Data.ByteString.Read.Class as C -- $setup -- >>> :set -XDataKinds -XOverloadedStrings+-- >>> import qualified Data.ByteString as S+-- >>> import qualified Data.ByteString.Lazy as L -integral_ :: (Radix b, Num n) => proxy b -> ByteString -> (n, Int, ByteString)+integral_ :: (Radix b, Num n, Source s) => proxy b -> s -> (n, Int, s) integral_ pn = loop 0 0 where loop !i !d !s- | S.null s = (i, d, s)- | not (isDigit pn (unsafeHead s)) = (i, d, s)- | otherwise = loop- (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (unsafeHead s)))- (d+1) (unsafeTail s)+ | C.null s = (i, d, s)+ | not (isDigit pn (C.head s)) = (i, d, s)+ | otherwise = loop+ (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (C.head s)))+ (d+1) (C.tail s) {-# INLINABLE integral_ #-} -- | convert bytestring into unsigned integral using radix ----- >>> integral' (Proxy :: Proxy 10) "12345" :: Maybe (Int, ByteString)+-- >>> integral' (Proxy :: Proxy 10) "12345" :: Maybe (Int, S.ByteString) -- Just (12345,"")--- >>> integral' (Proxy :: Proxy 2) "10112" :: Maybe (Int, ByteString)+-- >>> integral' (Proxy :: Proxy 2) "10112" :: Maybe (Int, L.ByteString) -- Just (11,"2")--- >>> integral' (Proxy :: Proxy 36) "Z" :: Maybe (Double, ByteString)+-- >>> integral' (Proxy :: Proxy 36) "Z" :: Maybe (Double, S.ByteString) -- Just (35.0,"")-integral' :: (Radix b, Num n) => proxy b -> ByteString -> Maybe (n, ByteString)+integral' :: (Radix b, Num n, Source s) => proxy b -> s -> Maybe (n, s) integral' pn s0 = case integral_ pn s0 of (_, 0, _) -> Nothing (n, _, s) -> Just (n, s)@@ -46,13 +44,13 @@ -- | @ -- integral = 'integral'' (Proxy :: Proxy 10) -- @-integral :: Num n => ByteString -> Maybe (n, ByteString)+integral :: (Num n, Source s) => s -> Maybe (n, s) integral = integral' (Proxy :: Proxy 10) {-# INLINABLE integral #-} -- | @ -- int = integral -- @-int :: ByteString -> Maybe (Int, ByteString)+int :: Source s => s -> Maybe (Int, s) int = integral {-# INLINABLE int #-}