packages feed

flatparse 0.5.0.1 → 0.5.0.2

raw patch · 5 files changed

+209/−32 lines, 5 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- FlatParse.Common.GHCExts: type ZeroBitRep = 'TupleRep ('[] :: [RuntimeRep])
- FlatParse.Common.GHCExts: type ZeroBitType = TYPE ZeroBitRep
+ FlatParse.Basic: embedParserST :: forall e a. (forall s. ParserST s e a) -> Parser e a
+ FlatParse.Basic: isolateToNextNull :: ParserT st e a -> ParserT st e a
+ FlatParse.Common.Assorted: zbytel :: (FiniteBits a, Num a) => a -> Int
+ FlatParse.Common.Assorted: zbytel'intermediate :: (FiniteBits a, Num a) => a -> a
+ FlatParse.Common.Assorted: zbytel'toIdx :: (FiniteBits a, Num a) => a -> Int
+ FlatParse.Common.Assorted: zbyter :: (FiniteBits a, Num a) => a -> Int
+ FlatParse.Common.Assorted: zbyter'intermediate :: (FiniteBits a, Num a) => a -> a
+ FlatParse.Common.Assorted: zbyter'toIdx :: (FiniteBits a, Num a) => a -> Int
+ FlatParse.Stateful: embedParserST :: forall r e a. (forall s. ParserST s r e a) -> Parser r e a
+ FlatParse.Stateful: isolateToNextNull :: ParserT st r e a -> ParserT st r e a

Files

flatparse.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack  name:           flatparse-version:        0.5.0.1+version:        0.5.0.2 synopsis:       High-performance parsing from strict bytestrings description:    @Flatparse@ is a high-performance parsing library for strict bytestring input. See the README for more information:                 <https://github.com/AndrasKovacs/flatparse>.
src/FlatParse/Basic.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}  {-| Parser supporting custom error types and embeddable `IO` or `ST` actions, but@@ -22,6 +23,7 @@   , runParserUtf8   , runParserIO   , runParserST+  , embedParserST    -- ** Primitive result types   , type FP.Parser.Res#@@ -73,6 +75,7 @@   , FP.Base.withEnsure1   , FP.Base.withEnsure#   , FP.Base.isolate+  , isolateToNextNull   , FP.Base.isolate#   , FP.Base.isolateUnsafe#   , FP.Switch.switch@@ -259,6 +262,11 @@   (# st, a #) -> OK# st a s {-# inline liftST #-} +-- | Run a `ParserST` inside a pure parser.+embedParserST :: forall e a. (forall s. ParserST s e a) -> Parser e a+embedParserST f = unsafeCoerce# (f :: ParserST RealWorld e a)+{-# inline embedParserST #-}+ --------------------------------------------------------------------------------  -- | Parse a given 'B.ByteString'.@@ -437,26 +445,81 @@  -------------------------------------------------------------------------------- --- | Read a null-terminated bytestring (a C-style string).+-- | Isolate the given parser up to (excluding) the next null byte. ----- Consumes the null terminator.-anyCString :: ParserT st e B.ByteString-anyCString = ParserT go'+-- Like 'isolate', all isolated bytes must be consumed. The null byte is+-- consumed afterwards.+--+-- Useful for defining parsers for null-terminated data.+isolateToNextNull :: ParserT st e a -> ParserT st e a+isolateToNextNull (ParserT p) = ParserT \fp eob s st -> go fp eob s st s   where-    go' fp eob s0 st = go 0# s0-      where-        go n# s = case eqAddr# eob s of-          1# -> Fail# st+    goP fp sNull s0 st =+        case p fp sNull s0 st of+          OK# st' a s' ->+            case eqAddr# s' sNull of+              1# -> -- consumed up to null: skip null, return+                    OK#   st' a (sNull `plusAddr#` 1#)+              _  -> Fail# st' -- didn't consume fully up to null: fail+          x -> x++    go8 fp eob s0 st s =+        case eqAddr# eob s of+          1# -> Fail# st -- end of input, no null: fail           _  ->-            let s' = plusAddr# s 1#+            let s' = s `plusAddr#` 1# in #if MIN_VERSION_base(4,16,0)-            -- TODO below is a candidate for improving with ExtendedLiterals!-            in  case eqWord8# (indexWord8OffAddr# s 0#) (wordToWord8# 0##) of+            -- below may be made clearer with ExtendedLiterals (GHC 9.8)+            case eqWord8# (indexWord8OffAddr# s 0#) (wordToWord8# 0##) of #else-            in  case eqWord# (indexWord8OffAddr# s 0#) 0## of+            case eqWord# (indexWord8OffAddr# s 0#) 0## of #endif-                  1# -> OK# st (B.PS (ForeignPtr s0 fp) 0 (I# n#)) s'-                  _  -> go (n# +# 1#) s'+              1# -> goP fp s   s0 st    --     0x00: isolate, execute parser+              _  -> go8 fp eob s0 st s' -- not 0x00: next please!++    {- The "find first null byte" algorithms used here are adapted from+       Hacker's Delight (2012) ch.6.++    We read a word (8 bytes) at a time for efficiency. The internal algorithm+    does byte indexing, thus endianness matters. We switch between indexing+    algorithms depending on compile-time native endianness. (The code+    surrounding the indexing is endian-independent, so we do this inline).+    -}+    go fp eob s0 st s =+        let sWord = s `plusAddr#` 8# in+        case gtAddr# sWord eob of+          1# -> -- <  8 bytes of input: revert to scanning byte by byte+            -- we _could_ operate on a word and simply ensure not to use the+            -- out-of-bounds data, which would be faster, but the act of+            -- reading could probably segfault+            go8 fp eob s0 st s+          _  -> -- >= 8 bytes of input: use efficient 8-byte scanning+#ifdef WORDS_BIGENDIAN+            -- big-endian ("L->R"): find leftmost null byte+            let !x@(I# x#) = Common.zbytel'intermediate (I# (indexIntOffAddr# s 0#)) in+#else+            -- little-endian ("R->L"): find rightmost null byte+            let !x@(I# x#) = Common.zbyter'intermediate (I# (indexIntOffAddr# s 0#)) in+#endif+            case x# ==# 0# of+              1# -> go fp eob s0 st sWord -- no 0x00 in next word+              _  -> -- 0x00 somewhere in next word+#ifdef WORDS_BIGENDIAN+                let !(I# nullIdx#) = Common.zbytel'toIdx x in+#else+                let !(I# nullIdx#) = Common.zbyter'toIdx x in+                -- TO TEST BE ON LE: change above CPP to zbytel, uncomment below+                -- let !(I# nullIdx#) = Common.zbytel'toIdx (I# (word2Int# (byteSwap# (int2Word# x#)))) in+#endif+                let sNull = s `plusAddr#` nullIdx# in+                goP fp sNull s0 st+{-# inline isolateToNextNull #-}++-- | Read a null-terminated bytestring (a C-style string).+--+-- Consumes the null terminator.+anyCString :: ParserT st e B.ByteString+anyCString = isolateToNextNull takeRest {-# inline anyCString #-}  -- | Read a null-terminated bytestring (a C-style string), where the bytestring
src/FlatParse/Basic/Integers.hs view
@@ -71,8 +71,7 @@ withAnySizedUnsafe#     :: Int# -> (Addr# -> Int# -> a) -> (a -> ParserT st e r) -> ParserT st e r withAnySizedUnsafe# size# indexOffAddr p = ParserT \fp eob buf st ->--- TODO force? i.e. @let !a, !buf'@ ?-  let a    = indexOffAddr buf 0#+  let !a   = indexOffAddr buf 0#       buf' = plusAddr# buf size#   in  runParserT# (p a) fp eob buf' st {-# inline withAnySizedUnsafe# #-}
src/FlatParse/Common/Assorted.hs view
@@ -24,6 +24,10 @@    -- * Helpers   , withPosInt#, withIntUnwrap#++  -- * Bit manipulation+  , zbytel, zbytel'intermediate, zbytel'toIdx+  , zbyter, zbyter'intermediate, zbyter'toIdx   ) where  import Data.Bits@@ -166,3 +170,51 @@ utf8ToStr :: B.ByteString -> String utf8ToStr = UTF8.toString {-# inline utf8ToStr #-}++--------------------------------------------------------------------------------++-- | Index of leftmost null byte, or (number of bytes in type) if not present.+--+-- Adapted from Hacker's Delight 6-1. Useful in big-endian environments.+zbytel :: (FiniteBits a, Num a) => a -> Int+zbytel = zbytel'toIdx . zbytel'intermediate+{-# inline zbytel #-}++-- | bit mangling, returns 0 for inputs without a null byte+--+-- Separating allows us to skip some index calculation if there was no null byte.+zbytel'intermediate :: (FiniteBits a, Num a) => a -> a+zbytel'intermediate a =+    let a' = (a .&. mask) + mask+    in  complement (a' .|. a .|. mask)+  where+    mask = 0x7F7F7F7F7F7F7F7F+{-# inline zbytel'intermediate #-}++-- | bit mangling, turns intermediate value into an index+--+-- Separating allows us to skip some index calculation if there was no null byte.+zbytel'toIdx :: (FiniteBits a, Num a) => a -> Int+zbytel'toIdx a = countLeadingZeros a `unsafeShiftR` 3+{-# inline zbytel'toIdx #-}++-- | Index of rightmost null byte, or (number of bytes in type) if not present+--+-- Adapted from Hacker's Delight 6-1. Useful in little-endian environments.+zbyter :: (FiniteBits a, Num a) => a -> Int+zbyter = zbyter'toIdx . zbyter'intermediate+{-# inline zbyter #-}++-- | bit mangling, returns 0 for inputs without a null byte+--+-- Separating allows us to skip some index calculation if there was no null byte.+zbyter'intermediate :: (FiniteBits a, Num a) => a -> a+zbyter'intermediate a = (a - 0x0101010101010101) .&. (complement a) .&. 0x8080808080808080+{-# inline zbyter'intermediate #-}++-- | bit mangling, turns intermediate value into an index+--+-- Separating allows us to skip some index calculation if there was no null byte.+zbyter'toIdx :: (FiniteBits a, Num a) => a -> Int+zbyter'toIdx a = countTrailingZeros a `unsafeShiftR` 3+{-# inline zbyter'toIdx #-}
src/FlatParse/Stateful.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}  {-| Parser supporting a custom reader environment, custom error types and an 'Int'@@ -22,6 +23,7 @@   , runParserUtf8   , runParserIO   , runParserST+  , embedParserST    -- ** Primitive result types   , type FP.Parser.Res#@@ -82,6 +84,7 @@   , FP.Base.withEnsure1   , FP.Base.withEnsure#   , FP.Base.isolate+  , isolateToNextNull   , FP.Base.isolate#   , FP.Base.isolateUnsafe#   , FP.Switch.switch@@ -261,6 +264,11 @@       Fail# rw'  ->  (# rw', Fail #) {-# inlinable runParserIO #-} +-- | Run a `ParserST` inside a pure parser.+embedParserST :: forall r e a. (forall s. ParserST s r e a) -> Parser r e a+embedParserST f = unsafeCoerce# (f :: ParserST RealWorld r e a)+{-# inline embedParserST #-}+ --------------------------------------------------------------------------------  -- | Run an `ST` action in a `ParserST`.@@ -415,26 +423,81 @@  -------------------------------------------------------------------------------- --- | Read a null-terminated bytestring (a C-style string).+-- | Isolate the given parser up to (excluding) the next null byte. ----- Consumes the null terminator.-anyCString :: ParserT st r e B.ByteString-anyCString = ParserT \fp !r eob s n st -> go' fp eob s n st+-- Like 'isolate', all isolated bytes must be consumed. The null byte is+-- consumed afterwards.+--+-- Useful for defining parsers for null-terminated data.+isolateToNextNull :: ParserT st r e a -> ParserT st r e a+isolateToNextNull (ParserT p) = ParserT \fp !r eob s n st -> go fp r n eob s st s   where-    go' fp eob s0 n st = go 0# s0 n-      where-        go n# s n = case eqAddr# eob s of-          1# -> Fail# st+    goP fp r n sNull s0 st =+        case p fp r sNull s0 n st of+          OK# st' a s' n' ->+            case eqAddr# s' sNull of+              1# -> -- consumed up to null: skip null, return+                    OK#   st' a (sNull `plusAddr#` 1#) n'+              _  -> Fail# st' -- didn't consume fully up to null: fail+          x -> x++    go8 fp r n eob s0 st s =+        case eqAddr# eob s of+          1# -> Fail# st -- end of input, no null: fail           _  ->-            let s' = plusAddr# s 1#+            let s' = s `plusAddr#` 1# in #if MIN_VERSION_base(4,16,0)-            -- TODO below is a candidate for improving with ExtendedLiterals!-            in  case eqWord8# (indexWord8OffAddr# s 0#) (wordToWord8# 0##) of+            -- below may be made clearer with ExtendedLiterals (GHC 9.8)+            case eqWord8# (indexWord8OffAddr# s 0#) (wordToWord8# 0##) of #else-            in  case eqWord# (indexWord8OffAddr# s 0#) 0## of+            case eqWord# (indexWord8OffAddr# s 0#) 0## of #endif-                  1# -> OK# st (B.PS (ForeignPtr s0 fp) 0 (I# n#)) s' n-                  _  -> go (n# +# 1#) s' n+              1# -> goP fp r n s   s0 st    --     0x00: isolate, execute parser+              _  -> go8 fp r n eob s0 st s' -- not 0x00: next please!++    {- The "find first null byte" algorithms used here are adapted from+       Hacker's Delight (2012) ch.6.++    We read a word (8 bytes) at a time for efficiency. The internal algorithm+    does byte indexing, thus endianness matters. We switch between indexing+    algorithms depending on compile-time native endianness. (The code+    surrounding the indexing is endian-independent, so we do this inline).+    -}+    go fp r n eob s0 st s =+        let sWord = s `plusAddr#` 8# in+        case gtAddr# sWord eob of+          1# -> -- <  8 bytes of input: revert to scanning byte by byte+            -- we _could_ operate on a word and simply ensure not to use the+            -- out-of-bounds data, which would be faster, but the act of+            -- reading could probably segfault+            go8 fp r n eob s0 st s+          _  -> -- >= 8 bytes of input: use efficient 8-byte scanning+#ifdef WORDS_BIGENDIAN+            -- big-endian ("L->R"): find leftmost null byte+            let !x@(I# x#) = Common.zbytel'intermediate (I# (indexIntOffAddr# s 0#)) in+#else+            -- little-endian ("R->L"): find rightmost null byte+            let !x@(I# x#) = Common.zbyter'intermediate (I# (indexIntOffAddr# s 0#)) in+#endif+            case x# ==# 0# of+              1# -> go fp r n eob s0 st sWord -- no 0x00 in next word+              _  -> -- 0x00 somewhere in next word+#ifdef WORDS_BIGENDIAN+                let !(I# nullIdx#) = Common.zbytel'toIdx x in+#else+                let !(I# nullIdx#) = Common.zbyter'toIdx x in+                -- TO TEST BE ON LE: change above CPP to zbytel, uncomment below+                -- let !(I# nullIdx#) = Common.zbytel'toIdx (I# (word2Int# (byteSwap# (int2Word# x#)))) in+#endif+                let sNull = s `plusAddr#` nullIdx# in+                goP fp r n sNull s0 st+{-# inline isolateToNextNull #-}++-- | Read a null-terminated bytestring (a C-style string).+--+-- Consumes the null terminator.+anyCString :: ParserT st r e B.ByteString+anyCString = isolateToNextNull takeRest {-# inline anyCString #-}  -- | Read a null-terminated bytestring (a C-style string), where the bytestring