bytesmith 0.3.11.0 → 0.3.11.1
raw patch · 17 files changed
+3814/−3270 lines, 17 filesdep −run-stdep ~byteslicedep ~bytestringsetup-changednew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies removed: run-st
Dependency ranges changed: byteslice, bytestring
API changes (from Hackage documentation)
Files
- CHANGELOG.md +5/−0
- Setup.hs +0/−2
- bench/Main.hs +14/−10
- bytesmith.cabal +52/−43
- src/Data/Bytes/Parser.hs +525/−427
- src/Data/Bytes/Parser/Ascii.hs +174/−156
- src/Data/Bytes/Parser/Base128.hs +4/−4
- src/Data/Bytes/Parser/BigEndian.hs +112/−93
- src/Data/Bytes/Parser/Internal.hs +195/−182
- src/Data/Bytes/Parser/Latin.hs +1524/−1302
- src/Data/Bytes/Parser/Leb128.hs +46/−36
- src/Data/Bytes/Parser/LittleEndian.hs +120/−96
- src/Data/Bytes/Parser/Rebindable.hs +427/−333
- src/Data/Bytes/Parser/Types.hs +29/−27
- src/Data/Bytes/Parser/Unsafe.hs +40/−44
- src/Data/Bytes/Parser/Utf8.hs +62/−55
- test/Main.hs +485/−460
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for bytesmith +## 0.3.11.1 -- 2024-02-28++* Update package metadata.+* Remove dependency on `run-st` library.+ ## 0.3.11.0 -- 2024-01-05 * Add `Data.Bytes.Parser.Latin.hexWord32`.
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
bench/Main.hs view
@@ -4,24 +4,28 @@ import Data.Char (ord) import Data.Primitive (ByteArray) import Data.Word (Word8)-import Gauge.Main (defaultMain,bench,whnf)+import Gauge.Main (bench, defaultMain, whnf) import qualified Data.Bytes.Parser as P import qualified Data.Bytes.Parser.Latin as Latin import qualified GHC.Exts as Exts main :: IO ()-main = defaultMain- [ bench "decPositiveInteger" $ whnf- (\x -> P.parseByteArray (Latin.decUnsignedInteger ()) x)- encodedBigNumber- ]+main =+ defaultMain+ [ bench "decPositiveInteger" $+ whnf+ (\x -> P.parseByteArray (Latin.decUnsignedInteger ()) x)+ encodedBigNumber+ ] encodedBigNumber :: ByteArray-encodedBigNumber = stringToByteArray $ show $ id @Integer $- 246246357264327645234627753190240202405243024304504230544- *- 732345623640035232405249305932503920593209520932095234651+encodedBigNumber =+ stringToByteArray $+ show $+ id @Integer $+ 246246357264327645234627753190240202405243024304504230544+ * 732345623640035232405249305932503920593209520932095234651 stringToByteArray :: String -> ByteArray stringToByteArray =
bytesmith.cabal view
@@ -1,79 +1,88 @@-cabal-version: 2.2-name: bytesmith-version: 0.3.11.0-synopsis: Nonresumable byte parser+cabal-version: 3.0+name: bytesmith+version: 0.3.11.1+synopsis: Nonresumable byte parser description: Parse bytes as fast as possible. This is a nonresumable parser that aggresively uses `UnboxedSums` to avoid performing any allocations.-homepage: https://github.com/andrewthad/bytesmith-bug-reports: https://github.com/andrewthad/bytesmith/issues-license: BSD-3-Clause-license-file: LICENSE-author: Andrew Martin-maintainer: andrew.thaddeus@gmail.com-copyright: 2019 Andrew Martin-category: Data-extra-source-files: CHANGELOG.md-tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.5 || == 9.4.3 +homepage: https://github.com/byteverse/bytesmith+bug-reports: https://github.com/byteverse/bytesmith/issues+license: BSD-3-Clause+license-file: LICENSE+author: Andrew Martin+maintainer: amartin@layer3com.com+copyright: 2019 Andrew Martin+category: Data+extra-doc-files: CHANGELOG.md+tested-with: GHC ==9.4.8 || ==9.6.3 || ==9.8.1++common build-settings+ default-language: Haskell2010+ ghc-options: -Wall -Wunused-packages+ library+ import: build-settings exposed-modules: Data.Bytes.Parser- Data.Bytes.Parser.BigEndian- Data.Bytes.Parser.LittleEndian Data.Bytes.Parser.Ascii Data.Bytes.Parser.Base128+ Data.Bytes.Parser.BigEndian Data.Bytes.Parser.Latin Data.Bytes.Parser.Leb128+ Data.Bytes.Parser.LittleEndian Data.Bytes.Parser.Rebindable Data.Bytes.Parser.Unsafe Data.Bytes.Parser.Utf8+ other-modules: Data.Bytes.Parser.Internal Data.Bytes.Parser.Types+ build-depends:- , base >=4.12 && <5- , byteslice >=0.2.6 && <0.3- , bytestring >=0.10.8 && <=0.12- , contiguous >= 0.6 && < 0.7- , natural-arithmetic >=0.1.3- , primitive >=0.7 && <0.10- , run-st >=0.1 && <0.2- , text-short >=0.1.3 && <0.2- , wide-word >=0.1.0.9 && <0.2- hs-source-dirs: src- ghc-options: -O2 -Wall- default-language: Haskell2010+ , base >=4.12 && <5+ , byteslice >=0.2.6 && <0.3+ , bytestring >=0.10.8 && <0.13+ , contiguous >=0.6 && <0.7+ , natural-arithmetic >=0.1.3+ , primitive >=0.7 && <0.10+ , text-short >=0.1.3 && <0.2+ , wide-word >=0.1.0.9 && <0.2 + hs-source-dirs: src+ ghc-options: -O2+ test-suite test- default-language: Haskell2010- type: exitcode-stdio-1.0+ import: build-settings+ type: exitcode-stdio-1.0 hs-source-dirs: test- main-is: Main.hs- ghc-options: -Wall -O2+ main-is: Main.hs build-depends:- , base >=4.12.0.0 && <5+ , base >=4.12.0.0 && <5+ , byte-order+ , byteslice , bytesmith , primitive- , byteslice- , tasty-hunit , tasty+ , tasty-hunit , tasty-quickcheck- , byte-order , text-short , wide-word benchmark bench- type: exitcode-stdio-1.0+ import: build-settings+ type: exitcode-stdio-1.0 build-depends: , base- , byteslice- , bytestring+ , bytesmith , gauge , primitive- , bytesmith- ghc-options: -Wall -O2- default-language: Haskell2010++ ghc-options: -O2 hs-source-dirs: bench- main-is: Main.hs+ main-is: Main.hs++source-repository head+ type: git+ location: git://github.com/byteverse/bytesmith.git
src/Data/Bytes/Parser.hs view
@@ -1,90 +1,104 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}-{-# language CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedTuples #-} --- | Parse non-resumable sequence of bytes. To parse a byte sequence--- as text, use the @Ascii@, @Latin@, and @Utf8@ modules instead.--- Functions for parsing decimal-encoded numbers are found in those--- modules.+{- | Parse non-resumable sequence of bytes. To parse a byte sequence+as text, use the @Ascii@, @Latin@, and @Utf8@ modules instead.+Functions for parsing decimal-encoded numbers are found in those+modules.+-} module Data.Bytes.Parser ( -- * Types Parser- , Result(..)- , Slice(..)+ , Result (..)+ , Slice (..)+ -- * Run Parsers+ -- ** Result , parseByteArray , parseBytes , parseBytesEffectfully , parseBytesEither , parseBytesMaybe+ -- * One Byte , any+ -- * Many Bytes , take , takeN , takeUpTo , takeWhile , takeTrailedBy+ -- * Skip , skipWhile , skipTrailedBy , skipTrailedBy2 , skipTrailedBy2# , skipTrailedBy3#+ -- * Match , byteArray , bytes , satisfy , satisfyWith , cstring+ -- * End of Input , endOfInput , isEndOfInput , remaining , peekRemaining+ -- * Scanning , scan+ -- * Lookahead , peek , peek'+ -- * Control Flow , fail , orElse , annotate , (<?>) , mapErrorEffectfully+ -- * Repetition , replicate+ -- * Subparsing , delimit , measure , measure_ , measure_#+ -- * Lift Effects , effect+ -- * Box Result , boxWord32 , boxIntPair+ -- * Unbox Result , unboxWord32 , unboxIntPair+ -- * Specialized Bind+ -- | Sometimes, GHC ends up building join points in a way that -- boxes arguments unnecessarily. In this situation, special variants -- of monadic @>>=@ can be helpful. If @C#@, @I#@, etc. never@@ -96,26 +110,26 @@ , bindFromCharToIntPair , bindFromMaybeCharToIntPair , bindFromMaybeCharToLifted+ -- * Specialized Pure , pureIntPair+ -- * Specialized Fail , failIntPair ) where -import Prelude hiding (length,any,fail,takeWhile,take,replicate)+import Prelude hiding (any, fail, length, replicate, take, takeWhile) -import Data.Bytes.Parser.Internal (Parser(..),ST#,unboxBytes)-import Data.Bytes.Parser.Internal (boxBytes,Result#,uneffectful,fail)-import Data.Bytes.Parser.Internal (uneffectful#,uneffectfulInt#)-import Data.Bytes.Parser.Types (Result(Failure,Success),Slice(Slice))-import Data.Bytes.Parser.Unsafe (unconsume,expose,cursor)-import Data.Bytes.Types (Bytes(..),BytesN(BytesN))-import Data.Primitive (ByteArray(..))-import Data.Primitive.Contiguous (Contiguous,Element)+import Data.Bytes.Parser.Internal (Parser (..), Result#, ST#, boxBytes, fail, unboxBytes, uneffectful, uneffectful#, uneffectfulInt#)+import Data.Bytes.Parser.Types (Result (Failure, Success), Slice (Slice))+import Data.Bytes.Parser.Unsafe (cursor, expose, unconsume)+import Data.Bytes.Types (Bytes (..), BytesN (BytesN))+import Data.Primitive (ByteArray (..))+import Data.Primitive.Contiguous (Contiguous, Element) import Foreign.C.String (CString)-import GHC.Exts (Int(I#),Word#,Int#,Char#,runRW#,(+#),(-#),(>=#))-import GHC.ST (ST(..))-import GHC.Word (Word32(W32#),Word8)+import GHC.Exts (Char#, Int (I#), Int#, Word#, runRW#, (+#), (-#), (>=#))+import GHC.ST (ST (..))+import GHC.Word (Word32 (W32#), Word8) import qualified Arithmetic.Nat as Nat import qualified Arithmetic.Types as Arithmetic@@ -125,49 +139,52 @@ import qualified Data.Primitive.Contiguous as C import qualified GHC.Exts as Exts --- | Parse a byte sequence. This can succeed even if the--- entire slice was not consumed by the parser.+{- | Parse a byte sequence. This can succeed even if the+entire slice was not consumed by the parser.+-} parseBytes :: forall e a. (forall s. Parser e s a) -> Bytes -> Result e a-{-# inline parseBytes #-}+{-# INLINE parseBytes #-} parseBytes p !b = runResultST action- where+ where action :: forall s. ST# s (Result# e a) action s0 = case p @s of Parser f -> f (unboxBytes b) s0 --- | Variant of 'parseBytesEither' that discards the error message on failure.--- Just like 'parseBytesEither', this does not impose any checks on the length--- of the remaining input.+{- | Variant of 'parseBytesEither' that discards the error message on failure.+Just like 'parseBytesEither', this does not impose any checks on the length+of the remaining input.+-} parseBytesMaybe :: forall e a. (forall s. Parser e s a) -> Bytes -> Maybe a-{-# inline parseBytesMaybe #-}+{-# INLINE parseBytesMaybe #-} parseBytesMaybe p !b = runMaybeST action- where+ where action :: forall s. ST# s (Result# e a) action s0 = case p @s of Parser f -> f (unboxBytes b) s0 --- | Variant of 'parseBytes' that discards the new offset and the--- remaining length. This does not, however, require the remaining--- length to be zero. Use 'endOfInput' to accomplish that.+{- | Variant of 'parseBytes' that discards the new offset and the+remaining length. This does not, however, require the remaining+length to be zero. Use 'endOfInput' to accomplish that.+-} parseBytesEither :: forall e a. (forall s. Parser e s a) -> Bytes -> Either e a-{-# inline parseBytesEither #-}+{-# INLINE parseBytesEither #-} parseBytesEither p !b = runEitherST action- where+ where action :: forall s. ST# s (Result# e a) action s0 = case p @s of Parser f -> f (unboxBytes b) s0 -- Similar to runResultST runMaybeST :: (forall s. ST# s (Result# e x)) -> Maybe x-{-# inline runMaybeST #-}-runMaybeST f = case (runRW# (\s0 -> case f s0 of { (# _, r #) -> r })) of+{-# INLINE runMaybeST #-}+runMaybeST f = case (runRW# (\s0 -> case f s0 of (# _, r #) -> r)) of (# _ | #) -> Nothing (# | (# x, _, _ #) #) -> Just x -- Similar to runResultST runEitherST :: (forall s. ST# s (Result# e x)) -> Either e x-{-# inline runEitherST #-}-runEitherST f = case (runRW# (\s0 -> case f s0 of { (# _, r #) -> r })) of+{-# INLINE runEitherST #-}+runEitherST f = case (runRW# (\s0 -> case f s0 of (# _, r #) -> r)) of (# e | #) -> Left e (# | (# x, _, _ #) #) -> Right x @@ -177,49 +194,56 @@ -- it avoids the additional boxing that the Success data -- constructor would normally cause. runResultST :: (forall s. ST# s (Result# e x)) -> Result e x-{-# inline runResultST #-}-runResultST f = case (runRW# (\s0 -> case f s0 of { (# _, r #) -> r })) of+{-# INLINE runResultST #-}+runResultST f = case (runRW# (\s0 -> case f s0 of (# _, r #) -> r)) of (# e | #) -> Failure e (# | (# x, off, len #) #) -> Success (Slice (I# off) (I# len) x) -- | Variant of 'parseBytes' that accepts an unsliced 'ByteArray'. parseByteArray :: (forall s. Parser e s a) -> ByteArray -> Result e a-{-# inline parseByteArray #-}+{-# INLINE parseByteArray #-} parseByteArray p b = parseBytes p (Bytes b 0 (PM.sizeofByteArray b)) --- | Variant of 'parseBytes' that allows the parser to be run--- as part of an existing effectful context.+{- | Variant of 'parseBytes' that allows the parser to be run+as part of an existing effectful context.+-} parseBytesEffectfully :: Parser e s a -> Bytes -> ST s (Result e a)-{-# inline parseBytesEffectfully #-}-parseBytesEffectfully (Parser f) !b = ST- (\s0 -> case f (unboxBytes b) s0 of- (# s1, r #) -> (# s1, boxPublicResult r #)- )+{-# INLINE parseBytesEffectfully #-}+parseBytesEffectfully (Parser f) !b =+ ST+ ( \s0 -> case f (unboxBytes b) s0 of+ (# s1, r #) -> (# s1, boxPublicResult r #)+ ) -- | Lift an effectful computation into a parser. effect :: ST s a -> Parser e s a-{-# inline effect #-}-effect (ST f) = Parser- ( \(# _, off, len #) s0 -> case f s0 of- (# s1, a #) -> (# s1, (# | (# a, off, len #) #) #)- )+{-# INLINE effect #-}+effect (ST f) =+ Parser+ ( \(# _, off, len #) s0 -> case f s0 of+ (# s1, a #) -> (# s1, (# | (# a, off, len #) #) #)+ ) byteArray :: e -> ByteArray -> Parser e s ()-{-# inline byteArray #-}+{-# INLINE byteArray #-} byteArray e !expected = bytes e (B.fromByteArray expected) -- | Consume input matching the byte sequence. bytes :: e -> Bytes -> Parser e s ()-bytes e !expected = Parser- ( \actual@(# _, off, len #) s ->- let r = if B.isPrefixOf expected (boxBytes actual)- then let !(I# movement) = length expected in- (# | (# (), off +# movement, len -# movement #) #)- else (# e | #)- in (# s, r #)- )+bytes e !expected =+ Parser+ ( \actual@(# _, off, len #) s ->+ let r =+ if B.isPrefixOf expected (boxBytes actual)+ then+ let !(I# movement) = length expected+ in (# | (# (), off +# movement, len -# movement #) #)+ else (# e | #)+ in (# s, r #)+ ) +{- FOURMOLU_DISABLE -} -- | Consume input matching the @NUL@-terminated C String. cstring :: e -> CString -> Parser e s () cstring e (Exts.Ptr ptr0) = Parser@@ -241,6 +265,7 @@ _ -> (# s, (# e | #) #) in go ptr0 off0 len0 )+{- FOURMOLU_ENABLE -} infix 0 <?> @@ -248,57 +273,65 @@ (<?>) :: Parser x s a -> e -> Parser e s a (<?>) = annotate --- | Annotate a parser. If the parser fails, the error will--- be returned.+{- | Annotate a parser. If the parser fails, the error will+ be returned.+-} annotate :: Parser x s a -> e -> Parser e s a annotate p e = p `orElse` fail e --- | Consumes and returns the next byte in the input.--- Fails if no characters are left.+{- | Consumes and returns the next byte in the input.+Fails if no characters are left.+-} any :: e -> Parser e s Word8-{-# inline any #-}-any e = uneffectful $ \chunk -> if length chunk > 0- then- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8- in Internal.Success w (offset chunk + 1) (length chunk - 1)- else Internal.Failure e+{-# INLINE any #-}+any e = uneffectful $ \chunk ->+ if length chunk > 0+ then+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in Internal.Success w (offset chunk + 1) (length chunk - 1)+ else Internal.Failure e --- | Match any byte, to perform lookahead. Returns 'Nothing' if--- end of input has been reached. Does not consume any input.------ /Note/: Because this parser does not fail, do not use it--- with combinators such as 'many', because such as 'many',--- because such parsers loop until a failure occurs. Careless--- use will thus result in an infinite loop.+{- | Match any byte, to perform lookahead. Returns 'Nothing' if+ end of input has been reached. Does not consume any input.++ /Note/: Because this parser does not fail, do not use it+ with combinators such as 'many', because such as 'many',+ because such parsers loop until a failure occurs. Careless+ use will thus result in an infinite loop.+-} peek :: Parser e s (Maybe Word8)-{-# inline peek #-}+{-# INLINE peek #-} peek = uneffectful $ \chunk ->- let v = if length chunk > 0- then Just (B.unsafeIndex chunk 0)- else Nothing- in Internal.Success v (offset chunk) (length chunk)+ let v =+ if length chunk > 0+ then Just (B.unsafeIndex chunk 0)+ else Nothing+ in Internal.Success v (offset chunk) (length chunk) --- | Match any byte, to perform lookahead. Does not consume any--- input, but will fail if end of input has been reached.+{- | Match any byte, to perform lookahead. Does not consume any+ input, but will fail if end of input has been reached.+-} peek' :: e -> Parser e s Word8-{-# inline peek' #-}-peek' e = uneffectful $ \chunk -> if length chunk > 0- then Internal.Success (B.unsafeIndex chunk 0) (offset chunk) (length chunk)- else Internal.Failure e+{-# INLINE peek' #-}+peek' e = uneffectful $ \chunk ->+ if length chunk > 0+ then Internal.Success (B.unsafeIndex chunk 0) (offset chunk) (length chunk)+ else Internal.Failure e --- | A stateful scanner. The predicate consumes and transforms a--- state argument, and each transformed state is passed to--- successive invocations of the predicate on each byte of the input--- until one returns 'Nothing' or the input ends.------ This parser does not fail. It will return the initial state--- if the predicate returns 'Nothing' on the first byte of input.------ /Note/: Because this parser does not fail, do not use it with--- combinators such a 'many', because such parsers loop until a--- failure occurs. Careless use will thus result in an infinite loop.+{- | A stateful scanner. The predicate consumes and transforms a+ state argument, and each transformed state is passed to+ successive invocations of the predicate on each byte of the input+ until one returns 'Nothing' or the input ends.++ This parser does not fail. It will return the initial state+ if the predicate returns 'Nothing' on the first byte of input.++ /Note/: Because this parser does not fail, do not use it with+ combinators such a 'many', because such parsers loop until a+ failure occurs. Careless use will thus result in an infinite loop.+-} scan :: state -> (state -> Word8 -> Maybe state) -> Parser e s state-{-# inline scan #-}+{-# INLINE scan #-} scan s0 t = do let go s = do mw <- peek@@ -313,26 +346,28 @@ -- Does not check to see if any characters are left. This -- is not exported. anyUnsafe :: Parser e s Word8-{-# inline anyUnsafe #-}+{-# INLINE anyUnsafe #-} anyUnsafe = uneffectful $ \chunk -> let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in Internal.Success w (offset chunk + 1) (length chunk - 1) --- | Take while the predicate is matched. This is always inlined. This--- always succeeds.+{- | Take while the predicate is matched. This is always inlined. This+always succeeds.+-} takeWhile :: (Word8 -> Bool) -> Parser e s Bytes-{-# inline takeWhile #-}+{-# INLINE takeWhile #-} takeWhile f = uneffectful $ \chunk -> case B.takeWhile f chunk of bs -> Internal.Success bs (offset chunk + length bs) (length chunk - length bs) --- | Take bytes until the specified byte is encountered. Consumes--- the matched byte as well. Fails if the byte is not present.--- Visually, the cursor advancement and resulting @Bytes@ for--- @takeTrailedBy 0x19@ look like this:------ > 0x10 0x13 0x08 0x15 0x19 0x23 0x17 | input--- > |---->---->---->---->----| | cursor--- > {----*----*----*----} | result bytes+{- | Take bytes until the specified byte is encountered. Consumes+the matched byte as well. Fails if the byte is not present.+Visually, the cursor advancement and resulting @Bytes@ for+@takeTrailedBy 0x19@ look like this:++> 0x10 0x13 0x08 0x15 0x19 0x23 0x17 | input+> |---->---->---->---->----| | cursor+> {\----*----*----*----\} | result bytes+-} takeTrailedBy :: e -> Word8 -> Parser e s Bytes takeTrailedBy e !w = do !start <- cursor@@ -341,107 +376,129 @@ !arr <- expose pure (Bytes arr start (end - (start + 1))) --- | Skip all characters until the character from the is encountered--- and then consume the matching byte as well.+{- | Skip all characters until the character from the is encountered+and then consume the matching byte as well.+-} skipTrailedBy :: e -> Word8 -> Parser e s ()-{-# inline skipTrailedBy #-}+{-# INLINE skipTrailedBy #-} skipTrailedBy e !w = uneffectful# (\c -> skipUntilConsumeByteLoop e w c) skipUntilConsumeByteLoop ::- e -- Error message- -> Word8 -- byte to match- -> Bytes -- Chunk- -> Result# e ()-skipUntilConsumeByteLoop e !w !c = if length c > 0- then if PM.indexByteArray (array c) (offset c) /= (w :: Word8)- then skipUntilConsumeByteLoop e w (B.unsafeDrop 1 c)- else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)- else (# e | #)+ e -> -- Error message+ Word8 -> -- byte to match+ Bytes -> -- Chunk+ Result# e ()+skipUntilConsumeByteLoop e !w !c =+ if length c > 0+ then+ if PM.indexByteArray (array c) (offset c) /= (w :: Word8)+ then skipUntilConsumeByteLoop e w (B.unsafeDrop 1 c)+ else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)+ else (# e | #) --- | Skip all bytes until either of the bytes in encountered. Then,--- consume the matched byte. @True@ indicates that the first argument--- byte was encountered. @False@ indicates that the second argument--- byte was encountered.+{- | Skip all bytes until either of the bytes in encountered. Then,+consume the matched byte. @True@ indicates that the first argument+byte was encountered. @False@ indicates that the second argument+byte was encountered.+-} skipTrailedBy2 ::- e -- ^ Error message- -> Word8 -- ^ First trailer, @False@ indicates that this was encountered- -> Word8 -- ^ Second trailer, @True@ indicates that this was encountered- -> Parser e s Bool-{-# inline skipTrailedBy2 #-}+ -- | Error message+ e ->+ -- | First trailer, @False@ indicates that this was encountered+ Word8 ->+ -- | Second trailer, @True@ indicates that this was encountered+ Word8 ->+ Parser e s Bool+{-# INLINE skipTrailedBy2 #-} skipTrailedBy2 e !wa !wb = boxBool (skipTrailedBy2# e wa wb) skipTrailedBy2# ::- e -- ^ Error message- -> Word8 -- ^ First trailer, 0 indicates that this was encountered- -> Word8 -- ^ Second trailer, 1 indicates that this was encountered- -> Parser e s Int#-{-# inline skipTrailedBy2# #-}+ -- | Error message+ e ->+ -- | First trailer, 0 indicates that this was encountered+ Word8 ->+ -- | Second trailer, 1 indicates that this was encountered+ Word8 ->+ Parser e s Int#+{-# INLINE skipTrailedBy2# #-} skipTrailedBy2# e !wa !wb = uneffectfulInt# (\c -> skipUntilConsumeByteEitherLoop e wa wb c) skipTrailedBy3# ::- e -- ^ Error message- -> Word8 -- ^ First trailer, 0 indicates that this was encountered- -> Word8 -- ^ Second trailer, 1 indicates that this was encountered- -> Word8 -- ^ Third trailer, 2 indicates that this was encountered- -> Parser e s Int#-{-# inline skipTrailedBy3# #-}+ -- | Error message+ e ->+ -- | First trailer, 0 indicates that this was encountered+ Word8 ->+ -- | Second trailer, 1 indicates that this was encountered+ Word8 ->+ -- | Third trailer, 2 indicates that this was encountered+ Word8 ->+ Parser e s Int#+{-# INLINE skipTrailedBy3# #-} skipTrailedBy3# e !wa !wb !wc = uneffectfulInt# (\c -> skipUntilConsumeByte3Loop e wa wb wc c) skipUntilConsumeByteEitherLoop ::- e -- Error message- -> Word8 -- first trailer- -> Word8 -- second trailer- -> Bytes -- Chunk- -> Result# e Int#-skipUntilConsumeByteEitherLoop e !wa !wb !c = if length c > 0- then let byte = PM.indexByteArray (array c) (offset c) in- if | byte == wa -> (# | (# 0#, unI (offset c + 1), unI (length c - 1) #) #)- | byte == wb -> (# | (# 1#, unI (offset c + 1), unI (length c - 1) #) #)- | otherwise -> skipUntilConsumeByteEitherLoop e wa wb (B.unsafeDrop 1 c)- else (# e | #)+ e -> -- Error message+ Word8 -> -- first trailer+ Word8 -> -- second trailer+ Bytes -> -- Chunk+ Result# e Int#+skipUntilConsumeByteEitherLoop e !wa !wb !c =+ if length c > 0+ then+ let byte = PM.indexByteArray (array c) (offset c)+ in if+ | byte == wa -> (# | (# 0#, unI (offset c + 1), unI (length c - 1) #) #)+ | byte == wb -> (# | (# 1#, unI (offset c + 1), unI (length c - 1) #) #)+ | otherwise -> skipUntilConsumeByteEitherLoop e wa wb (B.unsafeDrop 1 c)+ else (# e | #) skipUntilConsumeByte3Loop ::- e -- Error message- -> Word8 -- first trailer- -> Word8 -- second trailer- -> Word8 -- third trailer- -> Bytes -- Chunk- -> Result# e Int#-skipUntilConsumeByte3Loop e !wa !wb !wc !c = if length c > 0- then let byte = PM.indexByteArray (array c) (offset c) in- if | byte == wa -> (# | (# 0#, unI (offset c + 1), unI (length c - 1) #) #)- | byte == wb -> (# | (# 1#, unI (offset c + 1), unI (length c - 1) #) #)- | byte == wc -> (# | (# 2#, unI (offset c + 1), unI (length c - 1) #) #)- | otherwise -> skipUntilConsumeByte3Loop e wa wb wc (B.unsafeDrop 1 c)- else (# e | #)+ e -> -- Error message+ Word8 -> -- first trailer+ Word8 -> -- second trailer+ Word8 -> -- third trailer+ Bytes -> -- Chunk+ Result# e Int#+skipUntilConsumeByte3Loop e !wa !wb !wc !c =+ if length c > 0+ then+ let byte = PM.indexByteArray (array c) (offset c)+ in if+ | byte == wa -> (# | (# 0#, unI (offset c + 1), unI (length c - 1) #) #)+ | byte == wb -> (# | (# 1#, unI (offset c + 1), unI (length c - 1) #) #)+ | byte == wc -> (# | (# 2#, unI (offset c + 1), unI (length c - 1) #) #)+ | otherwise -> skipUntilConsumeByte3Loop e wa wb wc (B.unsafeDrop 1 c)+ else (# e | #) --- | Take the given number of bytes. Fails if there is not enough--- remaining input.+{- | Take the given number of bytes. Fails if there is not enough+ remaining input.+-} take :: e -> Int -> Parser e s Bytes-{-# inline take #-}-take e n = uneffectful $ \chunk -> if n <= B.length chunk- then case B.unsafeTake n chunk of- bs -> Internal.Success bs (offset chunk + n) (length chunk - n)- else Internal.Failure e+{-# INLINE take #-}+take e n = uneffectful $ \chunk ->+ if n <= B.length chunk+ then case B.unsafeTake n chunk of+ bs -> Internal.Success bs (offset chunk + n) (length chunk - n)+ else Internal.Failure e -- | Variant of 'take' that tracks the length of the result in the result type. takeN :: e -> Arithmetic.Nat n -> Parser e s (BytesN n)-takeN e n0 = uneffectful $ \chunk -> if n <= B.length chunk- then case B.unsafeTake n chunk of- Bytes theChunk theOff _ -> Internal.Success (BytesN theChunk theOff) (offset chunk + n) (length chunk - n)- else Internal.Failure e- where+takeN e n0 = uneffectful $ \chunk ->+ if n <= B.length chunk+ then case B.unsafeTake n chunk of+ Bytes theChunk theOff _ -> Internal.Success (BytesN theChunk theOff) (offset chunk + n) (length chunk - n)+ else Internal.Failure e+ where !n = Nat.demote n0 ----- | Take at most the given number of bytes. This is greedy. It will--- consume as many bytes as there are available until it has consumed--- @n@ bytes. This never fails.+{- | Take at most the given number of bytes. This is greedy. It will+ consume as many bytes as there are available until it has consumed+ @n@ bytes. This never fails.+-} takeUpTo :: Int -> Parser e s Bytes-{-# inline takeUpTo #-}+{-# INLINE takeUpTo #-} takeUpTo n = uneffectful $ \chunk -> let m = min n (B.length chunk) in case B.unsafeTake m chunk of@@ -449,68 +506,76 @@ -- | Consume all remaining bytes in the input. remaining :: Parser e s Bytes-{-# inline remaining #-}+{-# INLINE remaining #-} remaining = uneffectful $ \chunk -> Internal.Success chunk (offset chunk + length chunk) 0 -- | Return all remaining bytes in the input without consuming them. peekRemaining :: Parser e s Bytes-{-# inline peekRemaining #-}+{-# INLINE peekRemaining #-} peekRemaining = uneffectful $ \b@(Bytes _ off len) -> Internal.Success b off len -- | Skip while the predicate is matched. This is always inlined. skipWhile :: (Word8 -> Bool) -> Parser e s ()-{-# inline skipWhile #-}-skipWhile f = go where- go = isEndOfInput >>= \case- True -> pure ()- False -> do- w <- anyUnsafe- if f w- then go- else unconsume 1+{-# INLINE skipWhile #-}+skipWhile f = go+ where+ go =+ isEndOfInput >>= \case+ True -> pure ()+ False -> do+ w <- anyUnsafe+ if f w+ then go+ else unconsume 1 --- | The parser @satisfy p@ succeeds for any byte for which the--- predicate @p@ returns 'True'. Returns the byte that is--- actually parsed.+{- | The parser @satisfy p@ succeeds for any byte for which the+ predicate @p@ returns 'True'. Returns the byte that is+ actually parsed.+-} satisfy :: e -> (Word8 -> Bool) -> Parser e s Word8 satisfy e p = satisfyWith e id p-{-# inline satisfy #-}+{-# INLINE satisfy #-} --- | The parser @satisfyWith f p@ transforms a byte, and succeeds--- if the predicate @p@ returns 'True' on the transformed value.--- The parser returns the transformed byte that was parsed.+{- | The parser @satisfyWith f p@ transforms a byte, and succeeds+ if the predicate @p@ returns 'True' on the transformed value.+ The parser returns the transformed byte that was parsed.+-} satisfyWith :: e -> (Word8 -> a) -> (a -> Bool) -> Parser e s a-{-# inline satisfyWith #-}-satisfyWith e f p = uneffectful $ \chunk -> if length chunk > 0- then case B.unsafeIndex chunk 0 of- w ->- let v = f w- in if p v- then Internal.Success v (offset chunk + 1) (length chunk - 1)- else Internal.Failure e- else Internal.Failure e+{-# INLINE satisfyWith #-}+satisfyWith e f p = uneffectful $ \chunk ->+ if length chunk > 0+ then case B.unsafeIndex chunk 0 of+ w ->+ let v = f w+ in if p v+ then Internal.Success v (offset chunk + 1) (length chunk - 1)+ else Internal.Failure e+ else Internal.Failure e -- | Fails if there is still more input remaining. endOfInput :: e -> Parser e s ()-{-# inline endOfInput #-}-endOfInput e = uneffectful $ \chunk -> if length chunk == 0- then Internal.Success () (offset chunk) 0- else Internal.Failure e+{-# INLINE endOfInput #-}+endOfInput e = uneffectful $ \chunk ->+ if length chunk == 0+ then Internal.Success () (offset chunk) 0+ else Internal.Failure e --- | Returns true if there are no more bytes in the input. Returns--- false otherwise. Always succeeds.+{- | Returns true if there are no more bytes in the input. Returns+false otherwise. Always succeeds.+-} isEndOfInput :: Parser e s Bool-{-# inline isEndOfInput #-}+{-# INLINE isEndOfInput #-} isEndOfInput = uneffectful $ \chunk -> Internal.Success (length chunk == 0) (offset chunk) (length chunk) boxPublicResult :: Result# e a -> Result e a-{-# inline boxPublicResult #-}+{-# INLINE boxPublicResult #-} boxPublicResult (# | (# a, b, c #) #) = Success (Slice (I# b) (I# c) a) boxPublicResult (# e | #) = Failure e +{- FOURMOLU_DISABLE -} -- | Convert a 'Word32' parser to a 'Word#' parser. unboxWord32 :: Parser e s Word32 -> Parser e s Word# {-# inline unboxWord32 #-}@@ -524,17 +589,20 @@ #endif a, b, c #) #) #) )+{- FOURMOLU_ENABLE -} -- | Convert a @(Int,Int)@ parser to a @(# Int#, Int# #)@ parser.-unboxIntPair :: Parser e s (Int,Int) -> Parser e s (# Int#, Int# #)-{-# inline unboxIntPair #-}-unboxIntPair (Parser f) = Parser- (\x s0 -> case f x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# (I# y, I# z), b, c #) #) -> (# s1, (# | (# (# y, z #), b, c #) #) #)- )+unboxIntPair :: Parser e s (Int, Int) -> Parser e s (# Int#, Int# #)+{-# INLINE unboxIntPair #-}+unboxIntPair (Parser f) =+ Parser+ ( \x s0 -> case f x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# (I# y, I# z), b, c #) #) -> (# s1, (# | (# (# y, z #), b, c #) #) #)+ ) +{- FOURMOLU_DISABLE -} -- | Convert a 'Word#' parser to a 'Word32' parser. Precondition: -- the argument parser only returns words less than 4294967296. boxWord32 :: Parser e s Word# -> Parser e s Word32@@ -549,233 +617,263 @@ #endif a), b, c #) #) #) )+{- FOURMOLU_ENABLE -} -- | Convert a @(# Int#, Int# #)@ parser to a @(Int,Int)@ parser. boxInt :: Parser e s Int# -> Parser e s Int-{-# inline boxInt #-}-boxInt (Parser f) = Parser- (\x s0 -> case f x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) -> (# s1, (# | (# I# y, b, c #) #) #)- )+{-# INLINE boxInt #-}+boxInt (Parser f) =+ Parser+ ( \x s0 -> case f x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) -> (# s1, (# | (# I# y, b, c #) #) #)+ ) -- | Convert a @(# Int#, Int# #)@ parser to a @(Int,Int)@ parser. boxBool :: Parser e s Int# -> Parser e s Bool-{-# inline boxBool #-}-boxBool (Parser f) = Parser- (\x s0 -> case f x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) -> (# s1, (# | (# case y of {1# -> True; _ -> False}, b, c #) #) #)- )+{-# INLINE boxBool #-}+boxBool (Parser f) =+ Parser+ ( \x s0 -> case f x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) -> (# s1, (# | (# case y of 1# -> True; _ -> False, b, c #) #) #)+ ) -- | Convert a @(# Int#, Int# #)@ parser to a @(Int,Int)@ parser.-boxIntPair :: Parser e s (# Int#, Int# #) -> Parser e s (Int,Int)-{-# inline boxIntPair #-}-boxIntPair (Parser f) = Parser- (\x s0 -> case f x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# (# y, z #), b, c #) #) -> (# s1, (# | (# (I# y, I# z), b, c #) #) #)- )-+boxIntPair :: Parser e s (# Int#, Int# #) -> Parser e s (Int, Int)+{-# INLINE boxIntPair #-}+boxIntPair (Parser f) =+ Parser+ ( \x s0 -> case f x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# (# y, z #), b, c #) #) -> (# s1, (# | (# (I# y, I# z), b, c #) #) #)+ ) --- | There is a law-abiding instance of 'Alternative' for 'Parser'.--- However, it is not terribly useful since error messages seldom--- have a 'Monoid' instance. This function is a variant of @\<|\>@--- that is right-biased in its treatment of error messages.--- Consequently, @orElse@ lacks an identity.--- See <https://github.com/bos/attoparsec/issues/122 attoparsec issue #122>--- for more discussion of this topic.+{- | There is a law-abiding instance of 'Alternative' for 'Parser'.+However, it is not terribly useful since error messages seldom+have a 'Monoid' instance. This function is a variant of @\<|\>@+that is right-biased in its treatment of error messages.+Consequently, @orElse@ lacks an identity.+See <https://github.com/bos/attoparsec/issues/122 attoparsec issue #122>+for more discussion of this topic.+-} infixl 3 `orElse`+ orElse :: Parser x s a -> Parser e s a -> Parser e s a-{-# inline orElse #-}-orElse (Parser f) (Parser g) = Parser- (\x s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# _ | #) -> g x s1- (# | r #) -> (# s1, (# | r #) #)- )+{-# INLINE orElse #-}+orElse (Parser f) (Parser g) =+ Parser+ ( \x s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# _ | #) -> g x s1+ (# | r #) -> (# s1, (# | r #) #)+ ) -- | Effectfully adjusts the error message if an error occurs. mapErrorEffectfully :: (e1 -> ST s e2) -> Parser e1 s a -> Parser e2 s a-{-# inline mapErrorEffectfully #-}-mapErrorEffectfully f (Parser g) = Parser- (\x s0 -> case g x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> case f e of- ST h -> case h s1 of- (# s2, e' #) -> (# s2, (# e' | #) #)- (# | r #) -> (# s1, (# | r #) #)- )+{-# INLINE mapErrorEffectfully #-}+mapErrorEffectfully f (Parser g) =+ Parser+ ( \x s0 -> case g x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> case f e of+ ST h -> case h s1 of+ (# s2, e' #) -> (# s2, (# e' | #) #)+ (# | r #) -> (# s1, (# | r #) #)+ ) bindFromCharToLifted :: Parser s e Char# -> (Char# -> Parser s e a) -> Parser s e a-{-# inline bindFromCharToLifted #-}-bindFromCharToLifted (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindFromCharToLifted #-}+bindFromCharToLifted (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) bindFromCharToIntPair :: Parser s e Char# -> (Char# -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)-{-# inline bindFromCharToIntPair #-}-bindFromCharToIntPair (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindFromCharToIntPair #-}+bindFromCharToIntPair (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) bindFromLiftedToInt :: Parser s e a -> (a -> Parser s e Int#) -> Parser s e Int#-{-# inline bindFromLiftedToInt #-}-bindFromLiftedToInt (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindFromLiftedToInt #-}+bindFromLiftedToInt (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) bindFromLiftedToIntPair :: Parser s e a -> (a -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)-{-# inline bindFromLiftedToIntPair #-}-bindFromLiftedToIntPair (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindFromLiftedToIntPair #-}+bindFromLiftedToIntPair (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) bindFromIntToIntPair :: Parser s e Int# -> (Int# -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)-{-# inline bindFromIntToIntPair #-}-bindFromIntToIntPair (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindFromIntToIntPair #-}+bindFromIntToIntPair (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) bindFromMaybeCharToIntPair ::- Parser s e (# (# #) | Char# #)- -> ((# (# #) | Char# #) -> Parser s e (# Int#, Int# #))- -> Parser s e (# Int#, Int# #)-{-# inline bindFromMaybeCharToIntPair #-}-bindFromMaybeCharToIntPair (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ Parser s e (# (# #) | Char# #) ->+ ((# (# #) | Char# #) -> Parser s e (# Int#, Int# #)) ->+ Parser s e (# Int#, Int# #)+{-# INLINE bindFromMaybeCharToIntPair #-}+bindFromMaybeCharToIntPair (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) bindFromMaybeCharToLifted ::- Parser s e (# (# #) | Char# #)- -> ((# (# #) | Char# #) -> Parser s e a)- -> Parser s e a-{-# inline bindFromMaybeCharToLifted #-}-bindFromMaybeCharToLifted (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ Parser s e (# (# #) | Char# #) ->+ ((# (# #) | Char# #) -> Parser s e a) ->+ Parser s e a+{-# INLINE bindFromMaybeCharToLifted #-}+bindFromMaybeCharToLifted (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) pureIntPair ::- (# Int#, Int# #)- -> Parser s e (# Int#, Int# #)-{-# inline pureIntPair #-}-pureIntPair a = Parser- (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+ (# Int#, Int# #) ->+ Parser s e (# Int#, Int# #)+{-# INLINE pureIntPair #-}+pureIntPair a =+ Parser+ (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #)) failIntPair :: e -> Parser e s (# Int#, Int# #)-{-# inline failIntPair #-}-failIntPair e = Parser- (\(# _, _, _ #) s -> (# s, (# e | #) #))+{-# INLINE failIntPair #-}+failIntPair e =+ Parser+ (\(# _, _, _ #) s -> (# s, (# e | #) #)) --- | Augment a parser with the number of bytes that were consume while--- it executed.-measure :: Parser e s a -> Parser e s (Int,a)-{-# inline measure #-}-measure (Parser f) = Parser- (\x@(# _, pre, _ #) s0 -> case f x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, post, c #) #) -> (# s1, (# | (# (I# (post -# pre), y),post,c #) #) #)- )+{- | Augment a parser with the number of bytes that were consume while+it executed.+-}+measure :: Parser e s a -> Parser e s (Int, a)+{-# INLINE measure #-}+measure (Parser f) =+ Parser+ ( \x@(# _, pre, _ #) s0 -> case f x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, post, c #) #) -> (# s1, (# | (# (I# (post -# pre), y), post, c #) #) #)+ ) --- | Run a parser and discard the result, returning instead the number--- of bytes that the parser consumed.+{- | Run a parser and discard the result, returning instead the number+of bytes that the parser consumed.+-} measure_ :: Parser e s a -> Parser e s Int-{-# inline measure_ #-}+{-# INLINE measure_ #-} measure_ p = boxInt (measure_# p) -- | Variant of 'measure_' with an unboxed result. measure_# :: Parser e s a -> Parser e s Int#-{-# inline measure_# #-}-measure_# (Parser f) = Parser- (\x@(# _, pre, _ #) s0 -> case f x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, post, c #) #) -> (# s1, (# | (# post -# pre,post,c #) #) #)- )-+{-# INLINE measure_# #-}+measure_# (Parser f) =+ Parser+ ( \x@(# _, pre, _ #) s0 -> case f x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, post, c #) #) -> (# s1, (# | (# post -# pre, post, c #) #) #)+ ) +{- | Run a parser in a delimited context, failing if the requested number+of bytes are not available or if the delimited parser does not+consume all input. This combinator can be understood as a composition+of 'take', 'effect', 'parseBytesEffectfully', and 'endOfInput'. It is+provided as a single combinator because for convenience and because it is+easy to make mistakes when manually assembling the aforementioned parsers.+The pattern of prefixing an encoding with its length is common.+This is discussed more in+<https://github.com/bos/attoparsec/issues/129 attoparsec issue #129>. --- | Run a parser in a delimited context, failing if the requested number--- of bytes are not available or if the delimited parser does not--- consume all input. This combinator can be understood as a composition--- of 'take', 'effect', 'parseBytesEffectfully', and 'endOfInput'. It is--- provided as a single combinator because for convenience and because it is--- easy to make mistakes when manually assembling the aforementioned parsers.--- The pattern of prefixing an encoding with its length is common.--- This is discussed more in--- <https://github.com/bos/attoparsec/issues/129 attoparsec issue #129>.------ > delimit e1 e2 n remaining === take e1 n+> delimit e1 e2 n remaining === take e1 n+-} delimit ::- e -- ^ Error message when not enough bytes are present- -> e -- ^ Error message when delimited parser does not consume all input- -> Int -- ^ Exact number of bytes delimited parser is expected to consume- -> Parser e s a -- ^ Parser to execute in delimited context- -> Parser e s a-{-# inline delimit #-}-delimit esz eleftovers (I# n) (Parser f) = Parser- ( \(# arr, off, len #) s0 -> case len >=# n of- 1# -> case f (# arr, off, n #) s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# a, newOff, leftovers #) #) -> case leftovers of- 0# -> (# s1, (# | (# a, newOff, len -# n #) #) #)- _ -> (# s1, (# eleftovers | #) #)- _ -> (# s0, (# esz | #) #)- )+ -- | Error message when not enough bytes are present+ e ->+ -- | Error message when delimited parser does not consume all input+ e ->+ -- | Exact number of bytes delimited parser is expected to consume+ Int ->+ -- | Parser to execute in delimited context+ Parser e s a ->+ Parser e s a+{-# INLINE delimit #-}+delimit esz eleftovers (I# n) (Parser f) =+ Parser+ ( \(# arr, off, len #) s0 -> case len >=# n of+ 1# -> case f (# arr, off, n #) s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# a, newOff, leftovers #) #) -> case leftovers of+ 0# -> (# s1, (# | (# a, newOff, len -# n #) #) #)+ _ -> (# s1, (# eleftovers | #) #)+ _ -> (# s0, (# esz | #) #)+ ) --- | Replicate a parser @n@ times, writing the results into--- an array of length @n@. For @Array@ and @SmallArray@, this--- is lazy in the elements, so be sure the they result of the--- parser is evaluated appropriately to avoid unwanted thunks.-replicate :: forall arr e s a. (Contiguous arr, Element arr a)- => Int -- ^ Number of times to run the parser- -> Parser e s a -- ^ Parser- -> Parser e s (arr a)-{-# inline replicate #-}+{- | Replicate a parser @n@ times, writing the results into+an array of length @n@. For @Array@ and @SmallArray@, this+is lazy in the elements, so be sure the they result of the+parser is evaluated appropriately to avoid unwanted thunks.+-}+replicate ::+ forall arr e s a.+ (Contiguous arr, Element arr a) =>+ -- | Number of times to run the parser+ Int ->+ -- | Parser+ Parser e s a ->+ Parser e s (arr a)+{-# INLINE replicate #-} replicate !len p = do marr <- effect (C.new len) let go :: Int -> Parser e s (arr a)- go !ix = if ix < len- then do- a <- p- effect (C.write marr ix a)- go (ix + 1)- else effect (C.unsafeFreeze marr)+ go !ix =+ if ix < len+ then do+ a <- p+ effect (C.write marr ix a)+ go (ix + 1)+ else effect (C.unsafeFreeze marr) go 0 unI :: Int -> Int#-{-# inline unI #-}+{-# INLINE unI #-} unI (I# w) = w
src/Data/Bytes/Parser/Ascii.hs view
@@ -1,41 +1,40 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedTuples #-} --- | Parse input as ASCII-encoded text. Some parsers in this module,--- like 'any' and 'peek', fail if they encounter a byte above @0x7F@.--- Others, like numeric parsers and skipping parsers, leave the cursor--- at the position of the offending byte without failing.+{- | Parse input as ASCII-encoded text. Some parsers in this module,+like 'any' and 'peek', fail if they encounter a byte above @0x7F@.+Others, like numeric parsers and skipping parsers, leave the cursor+at the position of the offending byte without failing.+-} module Data.Bytes.Parser.Ascii ( -- * Matching Latin.char , Latin.char2 , Latin.char3 , Latin.char4+ -- * Case-Insensitive Matching , charInsensitive+ -- * Get Character , any , any# , peek , opt+ -- * Match Many , shortTrailedBy , takeShortWhile+ -- * Skip , Latin.skipDigits , Latin.skipDigits1@@ -45,6 +44,7 @@ , skipAlpha1 , skipTrailedBy , skipWhile+ -- * Numbers , Latin.decWord , Latin.decWord8@@ -52,47 +52,49 @@ , Latin.decWord32 ) where -import Prelude hiding (length,any,fail,takeWhile)+import Prelude hiding (any, fail, length, takeWhile) +import Control.Monad.ST (runST) import Data.Bits (clearBit)-import Data.Bytes.Types (Bytes(..))-import Data.Bytes.Parser.Internal (Parser(..),uneffectful,Result#,uneffectful#)-import Data.Bytes.Parser.Internal (Result(..),indexLatinCharArray,upcastUnitSuccess)+import Data.Bytes.Parser.Internal (Parser (..), Result (..), Result#, indexLatinCharArray, uneffectful, uneffectful#, upcastUnitSuccess)+import Data.Bytes.Types (Bytes (..)) import Data.Char (ord)-import Data.Word (Word8) import Data.Text.Short (ShortText)-import Control.Monad.ST.Run (runByteArrayST)-import GHC.Exts (Int(I#),Char(C#),Int#,Char#,(-#),(+#),(<#),ord#,indexCharArray#,chr#)-import GHC.Exts (gtChar#)+import Data.Word (Word8)+import GHC.Exts (Char (C#), Char#, Int (I#), Int#, chr#, gtChar#, indexCharArray#, ord#, (+#), (-#), (<#)) import qualified Data.ByteString.Short.Internal as BSS-import qualified Data.Text.Short.Unsafe as TS import qualified Data.Bytes as Bytes import qualified Data.Bytes.Parser.Latin as Latin import qualified Data.Bytes.Parser.Unsafe as Unsafe import qualified Data.Primitive as PM+import qualified Data.Text.Short.Unsafe as TS --- | Consume the next character, failing if it does not match the expected--- value or if there is no more input. This check for equality is case--- insensitive.------ Precondition: The argument must be a letter (@[a-zA-Z]@). Behavior is--- undefined if it is not.+{- | Consume the next character, failing if it does not match the expected+value or if there is no more input. This check for equality is case+insensitive.++Precondition: The argument must be a letter (@[a-zA-Z]@). Behavior is+undefined if it is not.+-} charInsensitive :: e -> Char -> Parser e s ()-{-# inline charInsensitive #-}-charInsensitive e !c = uneffectful $ \chunk -> if length chunk > 0- then if clearBit (PM.indexByteArray (array chunk) (offset chunk) :: Word8) 5 == w- then Success () (offset chunk + 1) (length chunk - 1)+{-# INLINE charInsensitive #-}+charInsensitive e !c = uneffectful $ \chunk ->+ if length chunk > 0+ then+ if clearBit (PM.indexByteArray (array chunk) (offset chunk) :: Word8) 5 == w+ then Success () (offset chunk + 1) (length chunk - 1)+ else Failure e else Failure e- else Failure e- where+ where w = clearBit (fromIntegral @Int @Word8 (ord c)) 5 --- | Consume input until the trailer is found. Then, consume--- the trailer as well. This fails if the trailer is not--- found or if any non-ASCII characters are encountered.+{- | Consume input until the trailer is found. Then, consume+the trailer as well. This fails if the trailer is not+found or if any non-ASCII characters are encountered.+-} skipTrailedBy :: e -> Char -> Parser e s ()-{-# inline skipTrailedBy #-}+{-# INLINE skipTrailedBy #-} skipTrailedBy e !c = do let go = do !d <- any e@@ -101,30 +103,32 @@ else go go --- | Consume characters matching the predicate. The stops when it--- encounters a non-matching character or when it encounters a byte--- above @0x7F@. This never fails.+{- | Consume characters matching the predicate. The stops when it+encounters a non-matching character or when it encounters a byte+above @0x7F@. This never fails.+-} takeShortWhile :: (Char -> Bool) -> Parser e s ShortText-{-# inline takeShortWhile #-}+{-# INLINE takeShortWhile #-} takeShortWhile p = do !start <- Unsafe.cursor skipWhile p end <- Unsafe.cursor src <- Unsafe.expose let len = end - start- !r = runByteArrayST $ do+ !r = runST $ do marr <- PM.newByteArray len PM.copyByteArray marr 0 src start len PM.unsafeFreezeByteArray marr- pure- $ TS.fromShortByteStringUnsafe- $ byteArrayToShortByteString- $ r+ pure $+ TS.fromShortByteStringUnsafe $+ byteArrayToShortByteString $+ r --- | Consume input through the next occurrence of the target--- character and return the consumed input, excluding the--- target character, as a 'ShortText'. This fails if it--- encounters any bytes above @0x7F@.+{- | Consume input through the next occurrence of the target+character and return the consumed input, excluding the+target character, as a 'ShortText'. This fails if it+encounters any bytes above @0x7F@.+-} shortTrailedBy :: e -> Char -> Parser e s ShortText shortTrailedBy e !c = do !start <- Unsafe.cursor@@ -132,133 +136,147 @@ end <- Unsafe.cursor src <- Unsafe.expose let len = end - start - 1- !r = runByteArrayST $ do+ !r = runST $ do marr <- PM.newByteArray len PM.copyByteArray marr 0 src start len PM.unsafeFreezeByteArray marr- pure- $ TS.fromShortByteStringUnsafe- $ byteArrayToShortByteString- $ r-+ pure $+ TS.fromShortByteStringUnsafe $+ byteArrayToShortByteString $+ r -- | Consumes and returns the next character in the input. any :: e -> Parser e s Char-{-# inline any #-}-any e = uneffectful $ \chunk -> if length chunk > 0- then- let c = indexLatinCharArray (array chunk) (offset chunk)- in if c < '\128'- then Success c (offset chunk + 1) (length chunk - 1)- else Failure e- else Failure e+{-# INLINE any #-}+any e = uneffectful $ \chunk ->+ if length chunk > 0+ then+ let c = indexLatinCharArray (array chunk) (offset chunk)+ in if c < '\128'+ then Success c (offset chunk + 1) (length chunk - 1)+ else Failure e+ else Failure e -- | Variant of 'any' with unboxed result. any# :: e -> Parser e s Char#-{-# inline any# #-}-any# e = Parser- (\(# arr, off, len #) s0 -> case len of- 0# -> (# s0, (# e | #) #)- _ ->- let !w = indexCharArray# arr off- in case ord# w <# 128# of- 1# -> (# s0, (# | (# w, off +# 1#, len -# 1# #) #) #)- _ -> (# s0, (# e | #) #)- )+{-# INLINE any# #-}+any# e =+ Parser+ ( \(# arr, off, len #) s0 -> case len of+ 0# -> (# s0, (# e | #) #)+ _ ->+ let !w = indexCharArray# arr off+ in case ord# w <# 128# of+ 1# -> (# s0, (# | (# w, off +# 1#, len -# 1# #) #) #)+ _ -> (# s0, (# e | #) #)+ ) unI :: Int -> Int#-{-# inline unI #-}+{-# INLINE unI #-} unI (I# w) = w --- | Examine the next byte without consuming it, interpret it as an--- ASCII-encoded character. This fails if the byte is above @0x7F@ or--- if the end of input has been reached.+{- | Examine the next byte without consuming it, interpret it as an+ASCII-encoded character. This fails if the byte is above @0x7F@ or+if the end of input has been reached.+-} peek :: e -> Parser e s Char-{-# inline peek #-}-peek e = uneffectful $ \chunk -> if length chunk > 0- then- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8- in if w < 128- then Success- (C# (chr# (unI (fromIntegral w))))- (offset chunk)- (length chunk)- else Failure e- else Failure e+{-# INLINE peek #-}+peek e = uneffectful $ \chunk ->+ if length chunk > 0+ then+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in if w < 128+ then+ Success+ (C# (chr# (unI (fromIntegral w))))+ (offset chunk)+ (length chunk)+ else Failure e+ else Failure e --- | Consume the next byte, interpreting it as an ASCII-encoded character.--- Fails if the byte is above @0x7F@. Returns @Nothing@ if the--- end of the input has been reached.+{- | Consume the next byte, interpreting it as an ASCII-encoded character.+Fails if the byte is above @0x7F@. Returns @Nothing@ if the+end of the input has been reached.+-} opt :: e -> Parser e s (Maybe Char)-{-# inline opt #-}-opt e = uneffectful $ \chunk -> if length chunk > 0- then- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8- in if w < 128- then Success- (Just (C# (chr# (unI (fromIntegral w)))))- (offset chunk + 1)- (length chunk - 1)- else Failure e- else Success Nothing (offset chunk) (length chunk)+{-# INLINE opt #-}+opt e = uneffectful $ \chunk ->+ if length chunk > 0+ then+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in if w < 128+ then+ Success+ (Just (C# (chr# (unI (fromIntegral w)))))+ (offset chunk + 1)+ (length chunk - 1)+ else Failure e+ else Success Nothing (offset chunk) (length chunk) --- | Consume characters matching the predicate. The stops when it--- encounters a non-matching character or when it encounters a byte--- above @0x7F@. This never fails.+{- | Consume characters matching the predicate. The stops when it+encounters a non-matching character or when it encounters a byte+above @0x7F@. This never fails.+-} skipWhile :: (Char -> Bool) -> Parser e s ()-{-# inline skipWhile #-}-skipWhile p = Parser- ( \(# arr, off0, len0 #) s0 ->- let go off len = case len of- 0# -> (# (), off, 0# #)- _ -> let c = indexCharArray# arr off in- case p (C# c) of- True -> case gtChar# c '\x7F'# of- 1# -> (# (), off, len #)- _ -> go (off +# 1# ) (len -# 1# )- False -> (# (), off, len #)- in (# s0, (# | go off0 len0 #) #)- )+{-# INLINE skipWhile #-}+skipWhile p =+ Parser+ ( \(# arr, off0, len0 #) s0 ->+ let go off len = case len of+ 0# -> (# (), off, 0# #)+ _ ->+ let c = indexCharArray# arr off+ in case p (C# c) of+ True -> case gtChar# c '\x7F'# of+ 1# -> (# (), off, len #)+ _ -> go (off +# 1#) (len -# 1#)+ False -> (# (), off, len #)+ in (# s0, (# | go off0 len0 #) #)+ ) --- | Skip uppercase and lowercase letters until a non-alpha--- character is encountered.+{- | Skip uppercase and lowercase letters until a non-alpha+character is encountered.+-} skipAlpha :: Parser e s ()-{-# inline skipAlpha #-}+{-# INLINE skipAlpha #-} skipAlpha = uneffectful# $ \c -> upcastUnitSuccess (skipAlphaAsciiLoop c) --- | Skip uppercase and lowercase letters until a non-alpha--- character is encountered.+{- | Skip uppercase and lowercase letters until a non-alpha+character is encountered.+-} skipAlpha1 :: e -> Parser e s ()-{-# inline skipAlpha1 #-}+{-# INLINE skipAlpha1 #-} skipAlpha1 e = uneffectful# $ \c -> skipAlphaAsciiLoop1Start e c skipAlphaAsciiLoop ::- Bytes -- Chunk- -> (# Int#, Int# #)-{-# inline skipAlphaAsciiLoop #-}-skipAlphaAsciiLoop !c = if length c > 0- then- let w = indexLatinCharArray (array c) (offset c)- in if (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z')- then skipAlphaAsciiLoop (Bytes.unsafeDrop 1 c)- else (# unI (offset c), unI (length c) #)- else (# unI (offset c), unI (length c) #)+ Bytes -> -- Chunk+ (# Int#, Int# #)+{-# INLINE skipAlphaAsciiLoop #-}+skipAlphaAsciiLoop !c =+ if length c > 0+ then+ let w = indexLatinCharArray (array c) (offset c)+ in if (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z')+ then skipAlphaAsciiLoop (Bytes.unsafeDrop 1 c)+ else (# unI (offset c), unI (length c) #)+ else (# unI (offset c), unI (length c) #) skipAlphaAsciiLoop1Start ::- e- -> Bytes -- chunk- -> Result# e ()-{-# inline skipAlphaAsciiLoop1Start #-}-skipAlphaAsciiLoop1Start e !c = if length c > 0- then - let w = indexLatinCharArray (array c) (offset c)- in if (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z')- then upcastUnitSuccess (skipAlphaAsciiLoop (Bytes.unsafeDrop 1 c))- else (# e | #)- else (# e | #)+ e ->+ Bytes -> -- chunk+ Result# e ()+{-# INLINE skipAlphaAsciiLoop1Start #-}+skipAlphaAsciiLoop1Start e !c =+ if length c > 0+ then+ let w = indexLatinCharArray (array c) (offset c)+ in if (w >= 'a' && w <= 'z') || (w >= 'A' && w <= 'Z')+ then upcastUnitSuccess (skipAlphaAsciiLoop (Bytes.unsafeDrop 1 c))+ else (# e | #)+ else (# e | #) byteArrayToShortByteString :: PM.ByteArray -> BSS.ShortByteString-{-# inline byteArrayToShortByteString #-}+{-# INLINE byteArrayToShortByteString #-} byteArrayToShortByteString (PM.ByteArray x) = BSS.SBS x
src/Data/Bytes/Parser/Base128.hs view
@@ -1,5 +1,5 @@-{-# language BangPatterns #-}-{-# language TypeApplications #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeApplications #-} module Data.Bytes.Parser.Base128 ( -- * Unsigned@@ -9,9 +9,9 @@ ) where import Control.Monad (when)-import Data.Bits (testBit,unsafeShiftL,(.|.),bit,clearBit)+import Data.Bits (bit, clearBit, testBit, unsafeShiftL, (.|.)) import Data.Bytes.Parser (Parser)-import Data.Word (Word8,Word16,Word32,Word64)+import Data.Word (Word16, Word32, Word64, Word8) import qualified Data.Bytes.Parser as P
src/Data/Bytes/Parser/BigEndian.hs view
@@ -1,20 +1,14 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedSums #-} -- | Big-endian fixed-width numbers. module Data.Bytes.Parser.BigEndian@@ -25,12 +19,15 @@ , word64 , word128 , word256+ -- * Signed , int8 , int16 , int32 , int64+ -- * Many+ -- ** Unsigned , word16Array , word32Array@@ -39,20 +36,20 @@ , word256Array ) where -import Prelude hiding (length,any,fail,takeWhile)+import Prelude hiding (any, fail, length, takeWhile) +#if MIN_VERSION_base(4,18,0)+#else import Control.Applicative (liftA2)-import Data.Bits ((.|.),unsafeShiftL)-import Data.Bytes.Types (Bytes(..))-import Data.Bytes.Parser.Internal (Parser,uneffectful)-import Data.Bytes.Parser.Internal (Result(..))-import Data.Bytes.Parser.Internal (swapArray16,swapArray32,swapArray64,swapArray256)-import Data.Bytes.Parser.Internal (swapArray128)-import Data.Word (Word8,Word16,Word32,Word64)-import Data.Int (Int8,Int16,Int32,Int64)-import Data.Primitive (ByteArray(..),PrimArray(..))-import Data.WideWord (Word128(Word128),Word256(Word256))-import GHC.ByteOrder (ByteOrder(LittleEndian,BigEndian),targetByteOrder)+#endif+import Data.Bits (unsafeShiftL, (.|.))+import Data.Bytes.Parser.Internal (Parser, Result (..), swapArray128, swapArray16, swapArray256, swapArray32, swapArray64, uneffectful)+import Data.Bytes.Types (Bytes (..))+import Data.Int (Int16, Int32, Int64, Int8)+import Data.Primitive (ByteArray (..), PrimArray (..))+import Data.WideWord (Word128 (Word128), Word256 (Word256))+import Data.Word (Word16, Word32, Word64, Word8)+import GHC.ByteOrder (ByteOrder (BigEndian, LittleEndian), targetByteOrder) import qualified Data.Bytes as Bytes import qualified Data.Bytes.Parser as P@@ -62,14 +59,18 @@ word8 :: e -> Parser e s Word8 word8 = P.any --- | Parse an array of big-endian unsigned 16-bit words. If the host is--- big-endian, the implementation is optimized to simply @memcpy@ bytes--- into the result array. The result array always has elements in--- native-endian byte order.+{- | Parse an array of big-endian unsigned 16-bit words. If the host is+big-endian, the implementation is optimized to simply @memcpy@ bytes+into the result array. The result array always has elements in+native-endian byte order.+-} word16Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of big-endian 16-bit words to expect- -> Parser e s (PrimArray Word16) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of big-endian 16-bit words to expect+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word16) word16Array e !n = case targetByteOrder of BigEndian -> fmap (asWord16s . Bytes.toByteArrayClone) (P.take e (n * 2)) LittleEndian -> do@@ -79,9 +80,12 @@ -- | Parse an array of big-endian unsigned 32-bit words. word32Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of big-endian 32-bit words to expect- -> Parser e s (PrimArray Word32) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of big-endian 32-bit words to expect+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word32) word32Array e !n = case targetByteOrder of BigEndian -> fmap (asWord32s . Bytes.toByteArrayClone) (P.take e (n * 4)) LittleEndian -> do@@ -91,9 +95,12 @@ -- | Parse an array of big-endian unsigned 64-bit words. word64Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of big-endian 64-bit words to consume- -> Parser e s (PrimArray Word64) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of big-endian 64-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word64) word64Array e !n = case targetByteOrder of BigEndian -> fmap (asWord64s . Bytes.toByteArrayClone) (P.take e (n * 8)) LittleEndian -> do@@ -103,9 +110,12 @@ -- | Parse an array of big-endian unsigned 256-bit words. word256Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of big-endian 256-bit words to consume- -> Parser e s (PrimArray Word256) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of big-endian 256-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word256) word256Array e !n = case targetByteOrder of BigEndian -> fmap (asWord256s . Bytes.toByteArrayClone) (P.take e (n * 32)) LittleEndian -> do@@ -115,9 +125,12 @@ -- | Parse an array of big-endian unsigned 128-bit words. word128Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of big-endian 128-bit words to consume- -> Parser e s (PrimArray Word128) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of big-endian 128-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word128) word128Array e !n = case targetByteOrder of BigEndian -> fmap (asWord128s . Bytes.toByteArrayClone) (P.take e (n * 16)) LittleEndian -> do@@ -142,58 +155,64 @@ -- | Unsigned 16-bit word. word16 :: e -> Parser e s Word16-word16 e = uneffectful $ \chunk -> if length chunk >= 2- then- let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8- wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8- in Success- (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wa) 8 .|. fromIntegral wb))- (offset chunk + 2) (length chunk - 2)- else Failure e+word16 e = uneffectful $ \chunk ->+ if length chunk >= 2+ then+ let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8+ in Success+ (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wa) 8 .|. fromIntegral wb))+ (offset chunk + 2)+ (length chunk - 2)+ else Failure e -- | Unsigned 32-bit word. word32 :: e -> Parser e s Word32-word32 e = uneffectful $ \chunk -> if length chunk >= 4- then- let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8- wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8- wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8- wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8- in Success- (fromIntegral @Word @Word32- ( unsafeShiftL (fromIntegral wa) 24 .|.- unsafeShiftL (fromIntegral wb) 16 .|.- unsafeShiftL (fromIntegral wc) 8 .|.- fromIntegral wd+word32 e = uneffectful $ \chunk ->+ if length chunk >= 4+ then+ let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8+ wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8+ wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8+ in Success+ ( fromIntegral @Word @Word32+ ( unsafeShiftL (fromIntegral wa) 24+ .|. unsafeShiftL (fromIntegral wb) 16+ .|. unsafeShiftL (fromIntegral wc) 8+ .|. fromIntegral wd+ ) )- )- (offset chunk + 4) (length chunk - 4)- else Failure e+ (offset chunk + 4)+ (length chunk - 4)+ else Failure e -- | Unsigned 64-bit word. word64 :: e -> Parser e s Word64-word64 e = uneffectful $ \chunk -> if length chunk >= 8- then- let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8- wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8- wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8- wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8- we = PM.indexByteArray (array chunk) (offset chunk + 4) :: Word8- wf = PM.indexByteArray (array chunk) (offset chunk + 5) :: Word8- wg = PM.indexByteArray (array chunk) (offset chunk + 6) :: Word8- wh = PM.indexByteArray (array chunk) (offset chunk + 7) :: Word8- in Success- ( unsafeShiftL (fromIntegral wa) 56 .|.- unsafeShiftL (fromIntegral wb) 48 .|.- unsafeShiftL (fromIntegral wc) 40 .|.- unsafeShiftL (fromIntegral wd) 32 .|.- unsafeShiftL (fromIntegral we) 24 .|.- unsafeShiftL (fromIntegral wf) 16 .|.- unsafeShiftL (fromIntegral wg) 8 .|.- fromIntegral wh- )- (offset chunk + 8) (length chunk - 8)- else Failure e+word64 e = uneffectful $ \chunk ->+ if length chunk >= 8+ then+ let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8+ wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8+ wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8+ we = PM.indexByteArray (array chunk) (offset chunk + 4) :: Word8+ wf = PM.indexByteArray (array chunk) (offset chunk + 5) :: Word8+ wg = PM.indexByteArray (array chunk) (offset chunk + 6) :: Word8+ wh = PM.indexByteArray (array chunk) (offset chunk + 7) :: Word8+ in Success+ ( unsafeShiftL (fromIntegral wa) 56+ .|. unsafeShiftL (fromIntegral wb) 48+ .|. unsafeShiftL (fromIntegral wc) 40+ .|. unsafeShiftL (fromIntegral wd) 32+ .|. unsafeShiftL (fromIntegral we) 24+ .|. unsafeShiftL (fromIntegral wf) 16+ .|. unsafeShiftL (fromIntegral wg) 8+ .|. fromIntegral wh+ )+ (offset chunk + 8)+ (length chunk - 8)+ else Failure e -- | Unsigned 128-bit word. word128 :: e -> Parser e s Word128
src/Data/Bytes/Parser/Internal.hs view
@@ -1,26 +1,19 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language NamedFieldPuns #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnboxedTuples #-} module Data.Bytes.Parser.Internal- ( Parser(..)- , Result(..)- , InternalStep(..)+ ( Parser (..)+ , Result (..)+ , InternalStep (..) , Bytes# , ST# , Result#@@ -34,7 +27,7 @@ , fail , indexLatinCharArray , upcastUnitSuccess- -- Swapping+ -- Swapping , swapArray16 , swapArray32 , swapArray64@@ -42,15 +35,15 @@ , swapArray256 ) where -import Prelude hiding (length,any,fail,takeWhile)+import Prelude hiding (any, fail, length, takeWhile) import Control.Applicative (Alternative)-import Control.Monad.ST.Run (runByteArrayST)-import Data.Primitive (ByteArray(ByteArray))-import Data.Bytes.Types (Bytes(..))+import Control.Monad.ST (runST)+import Data.Bytes.Types (Bytes (..)) import Data.Kind (Type)+import Data.Primitive (ByteArray (ByteArray)) import Data.Word (Word8)-import GHC.Exts (TYPE,RuntimeRep,Int(I#),Int#,State#,ByteArray#,Char(C#))+import GHC.Exts (ByteArray#, Char (C#), Int (I#), Int#, RuntimeRep, State#, TYPE) import qualified Control.Applicative import qualified Control.Monad@@ -59,71 +52,81 @@ -- | A non-resumable parser. newtype Parser :: forall (r :: RuntimeRep). Type -> Type -> TYPE r -> Type where- Parser :: forall (r :: RuntimeRep) (e :: Type) (s :: Type) (a :: TYPE r).- { runParser :: (# ByteArray#, Int#, Int# #) -> ST# s (Result# e a) } -> Parser e s a+ Parser ::+ forall (r :: RuntimeRep) (e :: Type) (s :: Type) (a :: TYPE r).+ {runParser :: (# ByteArray#, Int#, Int# #) -> ST# s (Result# e a)} ->+ Parser e s a -- The result of running a parser. Used internally. data Result e a = Failure e- -- An error message indicating what went wrong.- | Success !a !Int !Int- -- The parsed value, the offset after the last consumed byte, and the- -- number of bytes remaining in parsed slice.+ | -- An error message indicating what went wrong.+ Success !a !Int !Int +-- The parsed value, the offset after the last consumed byte, and the+-- number of bytes remaining in parsed slice.+ data InternalStep a = InternalStep !a !Int !Int uneffectful :: (Bytes -> Result e a) -> Parser e s a-{-# inline uneffectful #-}-uneffectful f = Parser- ( \b s0 -> (# s0, unboxResult (f (boxBytes b)) #) )+{-# INLINE uneffectful #-}+uneffectful f =+ Parser+ (\b s0 -> (# s0, unboxResult (f (boxBytes b)) #)) -- This is like uneffectful but for parsers that always succeed. -- These combinators typically have names that begin with @try@. unfailing :: (Bytes -> InternalStep a) -> Parser e s a-{-# inline unfailing #-}-unfailing f = Parser- ( \b s0 -> (# s0, case f (boxBytes b) of { InternalStep a (I# off) (I# len) -> (# | (# a, off, len #) #) } #) )+{-# INLINE unfailing #-}+unfailing f =+ Parser+ (\b s0 -> (# s0, case f (boxBytes b) of InternalStep a (I# off) (I# len) -> (# | (# a, off, len #) #) #)) boxBytes :: Bytes# -> Bytes-{-# inline boxBytes #-}+{-# INLINE boxBytes #-} boxBytes (# a, b, c #) = Bytes (ByteArray a) (I# b) (I# c) unboxBytes :: Bytes -> Bytes#-{-# inline unboxBytes #-}-unboxBytes (Bytes (ByteArray a) (I# b) (I# c)) = (# a,b,c #)+{-# INLINE unboxBytes #-}+unboxBytes (Bytes (ByteArray a) (I# b) (I# c)) = (# a, b, c #) type Bytes# = (# ByteArray#, Int#, Int# #) type ST# s (a :: TYPE r) = State# s -> (# State# s, a #) type Result# e (a :: TYPE r) =- (# e- | (# a, Int#, Int# #) #) -- ints are offset and length+ (#+ e |+ (# a, Int#, Int# #) -- ints are offset and length+ #) unboxResult :: Result e a -> Result# e a-{-# inline unboxResult #-}+{-# INLINE unboxResult #-} unboxResult (Success a (I# b) (I# c)) = (# | (# a, b, c #) #) unboxResult (Failure e) = (# e | #) --- | Combines the error messages using '<>' when both--- parsers fail.-instance Monoid e => Alternative (Parser e s) where- {-# inline empty #-}- {-# inline (<|>) #-}+{- | Combines the error messages using '<>' when both+parsers fail.+-}+instance (Monoid e) => Alternative (Parser e s) where+ {-# INLINE empty #-}+ {-# INLINE (<|>) #-} empty = fail mempty- Parser f <|> Parser g = Parser- (\x s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# eRight | #) -> case g x s1 of- (# s2, r1 #) -> case r1 of- (# eLeft | #) -> (# s2, (# eRight <> eLeft | #) #)- (# | r #) -> (# s2, (# | r #) #)- (# | r #) -> (# s1, (# | r #) #)- )+ Parser f <|> Parser g =+ Parser+ ( \x s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# eRight | #) -> case g x s1 of+ (# s2, r1 #) -> case r1 of+ (# eLeft | #) -> (# s2, (# eRight <> eLeft | #) #)+ (# | r #) -> (# s2, (# | r #) #)+ (# | r #) -> (# s1, (# | r #) #)+ ) -- | Fail with the provided error message. fail ::- e -- ^ Error message- -> Parser e s a-{-# inline fail #-}+ -- | Error message+ e ->+ Parser e s a+{-# INLINE fail #-} fail e = uneffectful $ \_ -> Failure e instance Applicative (Parser e s) where@@ -131,165 +134,175 @@ (<*>) = Control.Monad.ap instance Monad (Parser e s) where- {-# inline (>>=) #-}+ {-# INLINE (>>=) #-} (>>=) = bindParser instance Functor (Parser e s) where- {-# inline fmap #-}- fmap f (Parser g) = Parser- (\x s0 -> case g x s0 of- (# s1, r #) -> case r of- (# e | #) -> (# s1, (# e | #) #)- (# | (# a, b, c #) #) -> (# s1, (# | (# f a, b, c #) #) #)- )+ {-# INLINE fmap #-}+ fmap f (Parser g) =+ Parser+ ( \x s0 -> case g x s0 of+ (# s1, r #) -> case r of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# a, b, c #) #) -> (# s1, (# | (# f a, b, c #) #) #)+ ) indexLatinCharArray :: ByteArray -> Int -> Char-{-# inline indexLatinCharArray #-}+{-# INLINE indexLatinCharArray #-} indexLatinCharArray (ByteArray arr) (I# off) = C# (Exts.indexCharArray# arr off) uneffectful# :: (Bytes -> Result# e a) -> Parser e s a-{-# inline uneffectful# #-}-uneffectful# f = Parser- ( \b s0 -> (# s0, (f (boxBytes b)) #) )+{-# INLINE uneffectful# #-}+uneffectful# f =+ Parser+ (\b s0 -> (# s0, (f (boxBytes b)) #)) -uneffectfulInt# :: (Bytes -> Result# e Int# ) -> Parser e s Int#-{-# inline uneffectfulInt# #-}-uneffectfulInt# f = Parser- ( \b s0 -> (# s0, (f (boxBytes b)) #) )+uneffectfulInt# :: (Bytes -> Result# e Int#) -> Parser e s Int#+{-# INLINE uneffectfulInt# #-}+uneffectfulInt# f =+ Parser+ (\b s0 -> (# s0, (f (boxBytes b)) #)) upcastUnitSuccess :: (# Int#, Int# #) -> Result# e ()-{-# inline upcastUnitSuccess #-}+{-# INLINE upcastUnitSuccess #-} upcastUnitSuccess (# b, c #) = (# | (# (), b, c #) #) swapArray16 :: Bytes -> ByteArray-swapArray16 (Bytes{array,offset,length}) = runByteArrayST $ do+swapArray16 (Bytes {array, offset, length}) = runST $ do dst <- PM.newByteArray length- let go !ixSrc !ixDst !len = if len > 0- then do- let v0 = PM.indexByteArray array ixSrc :: Word8- v1 = PM.indexByteArray array (ixSrc + 1) :: Word8- PM.writeByteArray dst ixDst v1- PM.writeByteArray dst (ixDst + 1) v0- go (ixSrc + 2) (ixDst + 2) (len - 2)- else pure ()+ let go !ixSrc !ixDst !len =+ if len > 0+ then do+ let v0 = PM.indexByteArray array ixSrc :: Word8+ v1 = PM.indexByteArray array (ixSrc + 1) :: Word8+ PM.writeByteArray dst ixDst v1+ PM.writeByteArray dst (ixDst + 1) v0+ go (ixSrc + 2) (ixDst + 2) (len - 2)+ else pure () go offset 0 length PM.unsafeFreezeByteArray dst swapArray32 :: Bytes -> ByteArray-swapArray32 (Bytes{array,offset,length}) = runByteArrayST $ do+swapArray32 (Bytes {array, offset, length}) = runST $ do dst <- PM.newByteArray length- let go !ixSrc !ixDst !len = if len > 0- then do- let v0 = PM.indexByteArray array ixSrc :: Word8- v1 = PM.indexByteArray array (ixSrc + 1) :: Word8- v2 = PM.indexByteArray array (ixSrc + 2) :: Word8- v3 = PM.indexByteArray array (ixSrc + 3) :: Word8- PM.writeByteArray dst ixDst v3- PM.writeByteArray dst (ixDst + 1) v2- PM.writeByteArray dst (ixDst + 2) v1- PM.writeByteArray dst (ixDst + 3) v0- go (ixSrc + 4) (ixDst + 4) (len - 4)- else pure ()+ let go !ixSrc !ixDst !len =+ if len > 0+ then do+ let v0 = PM.indexByteArray array ixSrc :: Word8+ v1 = PM.indexByteArray array (ixSrc + 1) :: Word8+ v2 = PM.indexByteArray array (ixSrc + 2) :: Word8+ v3 = PM.indexByteArray array (ixSrc + 3) :: Word8+ PM.writeByteArray dst ixDst v3+ PM.writeByteArray dst (ixDst + 1) v2+ PM.writeByteArray dst (ixDst + 2) v1+ PM.writeByteArray dst (ixDst + 3) v0+ go (ixSrc + 4) (ixDst + 4) (len - 4)+ else pure () go offset 0 length PM.unsafeFreezeByteArray dst swapArray64 :: Bytes -> ByteArray-swapArray64 (Bytes{array,offset,length}) = runByteArrayST $ do+swapArray64 (Bytes {array, offset, length}) = runST $ do dst <- PM.newByteArray length- let go !ixSrc !ixDst !len = if len > 0- then do- let v0 = PM.indexByteArray array ixSrc :: Word8- v1 = PM.indexByteArray array (ixSrc + 1) :: Word8- v2 = PM.indexByteArray array (ixSrc + 2) :: Word8- v3 = PM.indexByteArray array (ixSrc + 3) :: Word8- v4 = PM.indexByteArray array (ixSrc + 4) :: Word8- v5 = PM.indexByteArray array (ixSrc + 5) :: Word8- v6 = PM.indexByteArray array (ixSrc + 6) :: Word8- v7 = PM.indexByteArray array (ixSrc + 7) :: Word8- PM.writeByteArray dst ixDst v7- PM.writeByteArray dst (ixDst + 1) v6- PM.writeByteArray dst (ixDst + 2) v5- PM.writeByteArray dst (ixDst + 3) v4- PM.writeByteArray dst (ixDst + 4) v3- PM.writeByteArray dst (ixDst + 5) v2- PM.writeByteArray dst (ixDst + 6) v1- PM.writeByteArray dst (ixDst + 7) v0- go (ixSrc + 8) (ixDst + 8) (len - 8)- else pure ()+ let go !ixSrc !ixDst !len =+ if len > 0+ then do+ let v0 = PM.indexByteArray array ixSrc :: Word8+ v1 = PM.indexByteArray array (ixSrc + 1) :: Word8+ v2 = PM.indexByteArray array (ixSrc + 2) :: Word8+ v3 = PM.indexByteArray array (ixSrc + 3) :: Word8+ v4 = PM.indexByteArray array (ixSrc + 4) :: Word8+ v5 = PM.indexByteArray array (ixSrc + 5) :: Word8+ v6 = PM.indexByteArray array (ixSrc + 6) :: Word8+ v7 = PM.indexByteArray array (ixSrc + 7) :: Word8+ PM.writeByteArray dst ixDst v7+ PM.writeByteArray dst (ixDst + 1) v6+ PM.writeByteArray dst (ixDst + 2) v5+ PM.writeByteArray dst (ixDst + 3) v4+ PM.writeByteArray dst (ixDst + 4) v3+ PM.writeByteArray dst (ixDst + 5) v2+ PM.writeByteArray dst (ixDst + 6) v1+ PM.writeByteArray dst (ixDst + 7) v0+ go (ixSrc + 8) (ixDst + 8) (len - 8)+ else pure () go offset 0 length PM.unsafeFreezeByteArray dst swapArray128 :: Bytes -> ByteArray-swapArray128 (Bytes{array,offset,length}) = runByteArrayST $ do+swapArray128 (Bytes {array, offset, length}) = runST $ do dst <- PM.newByteArray length- let go !ixSrc !ixDst !len = if len > 0- then do- let v0 = PM.indexByteArray array ixSrc :: Word8- v1 = PM.indexByteArray array (ixSrc + 1) :: Word8- v2 = PM.indexByteArray array (ixSrc + 2) :: Word8- v3 = PM.indexByteArray array (ixSrc + 3) :: Word8- v4 = PM.indexByteArray array (ixSrc + 4) :: Word8- v5 = PM.indexByteArray array (ixSrc + 5) :: Word8- v6 = PM.indexByteArray array (ixSrc + 6) :: Word8- v7 = PM.indexByteArray array (ixSrc + 7) :: Word8- v8 = PM.indexByteArray array (ixSrc + 8) :: Word8- v9 = PM.indexByteArray array (ixSrc + 9) :: Word8- v10 = PM.indexByteArray array (ixSrc + 10) :: Word8- v11 = PM.indexByteArray array (ixSrc + 11) :: Word8- v12 = PM.indexByteArray array (ixSrc + 12) :: Word8- v13 = PM.indexByteArray array (ixSrc + 13) :: Word8- v14 = PM.indexByteArray array (ixSrc + 14) :: Word8- v15 = PM.indexByteArray array (ixSrc + 15) :: Word8- PM.writeByteArray dst ixDst v15- PM.writeByteArray dst (ixDst + 1) v14- PM.writeByteArray dst (ixDst + 2) v13- PM.writeByteArray dst (ixDst + 3) v12- PM.writeByteArray dst (ixDst + 4) v11- PM.writeByteArray dst (ixDst + 5) v10- PM.writeByteArray dst (ixDst + 6) v9- PM.writeByteArray dst (ixDst + 7) v8- PM.writeByteArray dst (ixDst + 8) v7- PM.writeByteArray dst (ixDst + 9) v6- PM.writeByteArray dst (ixDst + 10) v5- PM.writeByteArray dst (ixDst + 11) v4- PM.writeByteArray dst (ixDst + 12) v3- PM.writeByteArray dst (ixDst + 13) v2- PM.writeByteArray dst (ixDst + 14) v1- PM.writeByteArray dst (ixDst + 15) v0- go (ixSrc + 16) (ixDst + 16) (len - 16)- else pure ()+ let go !ixSrc !ixDst !len =+ if len > 0+ then do+ let v0 = PM.indexByteArray array ixSrc :: Word8+ v1 = PM.indexByteArray array (ixSrc + 1) :: Word8+ v2 = PM.indexByteArray array (ixSrc + 2) :: Word8+ v3 = PM.indexByteArray array (ixSrc + 3) :: Word8+ v4 = PM.indexByteArray array (ixSrc + 4) :: Word8+ v5 = PM.indexByteArray array (ixSrc + 5) :: Word8+ v6 = PM.indexByteArray array (ixSrc + 6) :: Word8+ v7 = PM.indexByteArray array (ixSrc + 7) :: Word8+ v8 = PM.indexByteArray array (ixSrc + 8) :: Word8+ v9 = PM.indexByteArray array (ixSrc + 9) :: Word8+ v10 = PM.indexByteArray array (ixSrc + 10) :: Word8+ v11 = PM.indexByteArray array (ixSrc + 11) :: Word8+ v12 = PM.indexByteArray array (ixSrc + 12) :: Word8+ v13 = PM.indexByteArray array (ixSrc + 13) :: Word8+ v14 = PM.indexByteArray array (ixSrc + 14) :: Word8+ v15 = PM.indexByteArray array (ixSrc + 15) :: Word8+ PM.writeByteArray dst ixDst v15+ PM.writeByteArray dst (ixDst + 1) v14+ PM.writeByteArray dst (ixDst + 2) v13+ PM.writeByteArray dst (ixDst + 3) v12+ PM.writeByteArray dst (ixDst + 4) v11+ PM.writeByteArray dst (ixDst + 5) v10+ PM.writeByteArray dst (ixDst + 6) v9+ PM.writeByteArray dst (ixDst + 7) v8+ PM.writeByteArray dst (ixDst + 8) v7+ PM.writeByteArray dst (ixDst + 9) v6+ PM.writeByteArray dst (ixDst + 10) v5+ PM.writeByteArray dst (ixDst + 11) v4+ PM.writeByteArray dst (ixDst + 12) v3+ PM.writeByteArray dst (ixDst + 13) v2+ PM.writeByteArray dst (ixDst + 14) v1+ PM.writeByteArray dst (ixDst + 15) v0+ go (ixSrc + 16) (ixDst + 16) (len - 16)+ else pure () go offset 0 length PM.unsafeFreezeByteArray dst swapArray256 :: Bytes -> ByteArray-swapArray256 (Bytes{array,offset,length}) = runByteArrayST $ do+swapArray256 (Bytes {array, offset, length}) = runST $ do dst <- PM.newByteArray length- let go !ixSrc !ixDst !len = if len > 0- then do- let loop !i- | i < 32 = do- let v = PM.indexByteArray array (ixSrc + i) :: Word8- PM.writeByteArray dst (ixDst + (31 - i)) v- loop (i + 1)- | otherwise = pure ()- loop 0- go (ixSrc + 32) (ixDst + 32) (len - 32)- else pure ()+ let go !ixSrc !ixDst !len =+ if len > 0+ then do+ let loop !i+ | i < 32 = do+ let v = PM.indexByteArray array (ixSrc + i) :: Word8+ PM.writeByteArray dst (ixDst + (31 - i)) v+ loop (i + 1)+ | otherwise = pure ()+ loop 0+ go (ixSrc + 32) (ixDst + 32) (len - 32)+ else pure () go offset 0 length PM.unsafeFreezeByteArray dst pureParser :: a -> Parser e s a-{-# inline pureParser #-}-pureParser a = Parser- (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+{-# INLINE pureParser #-}+pureParser a =+ Parser+ (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #)) bindParser :: Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindParser #-}-bindParser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindParser #-}+bindParser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ )
src/Data/Bytes/Parser/Latin.hs view
@@ -1,1302 +1,1524 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}-{-# language CPP #-}---- | Parse input as though it were text encoded by--- ISO 8859-1 (Latin-1). All byte sequences are valid--- text under ISO 8859-1.-module Data.Bytes.Parser.Latin- ( -- * Matching- -- ** Required- char- , char2- , char3- , char4- , char5- , char6- , char7- , char8- , char9- , char10- , char11- , char12- -- ** Try- , trySatisfy- , trySatisfyThen- -- * One Character- , any- , opt- , opt#- -- * Many Characters- , takeTrailedBy- -- * Lookahead- , peek- , peek'- -- * Skip- , skipDigits- , skipDigits1- , skipChar- , skipChar1- , skipTrailedBy- , skipUntil- , skipWhile- -- * End of Input- , endOfInput- , isEndOfInput- -- * Numbers- -- ** Decimal- -- *** Unsigned- , decWord- , decWord8- , decWord16- , decWord32- , decWord64- -- *** Signed- , decUnsignedInt- , decUnsignedInt#- , decSignedInt- , decStandardInt- , decTrailingInt- , decTrailingInt#- , decSignedInteger- , decUnsignedInteger- , decTrailingInteger- -- ** Hexadecimal- -- *** Variable Length- , hexWord8- , hexWord16- , hexWord32- -- *** Fixed Length- , hexFixedWord8- , hexFixedWord16- , hexFixedWord32- , hexFixedWord64- , hexFixedWord128- , hexFixedWord256- -- *** Digit- , hexNibbleLower- , tryHexNibbleLower- , hexNibble- , tryHexNibble- ) where--import Prelude hiding (length,any,fail,takeWhile)--import Data.Bits ((.|.))-import Data.Bytes.Types (Bytes(..))-import Data.Bytes.Parser.Internal (InternalStep(..),unfailing)-import Data.Bytes.Parser (bindFromLiftedToInt,isEndOfInput,endOfInput)-import Data.Bytes.Parser.Internal (Parser(..),ST#,uneffectful,Result#,uneffectful#)-import Data.Bytes.Parser.Internal (Result(..),indexLatinCharArray,upcastUnitSuccess)-import Data.Bytes.Parser.Internal (boxBytes)-import Data.Bytes.Parser.Unsafe (expose,cursor,unconsume)-import Data.Char (ord)-import Data.Kind (Type)-import Data.WideWord (Word256(Word256),Word128(Word128))-import Data.Word (Word8)-import GHC.Exts (Int(I#),Char(C#),Word#,Int#,Char#,(+#),(-#),indexCharArray#)-import GHC.Exts (TYPE,RuntimeRep,int2Word#,or#)-import GHC.Exts (ltWord#,gtWord#,notI#)-import GHC.Word (Word(W#),Word8(W8#),Word16(W16#),Word32(W32#),Word64(W64#))--import qualified GHC.Exts as Exts-import qualified Data.Bytes as Bytes-import qualified Data.Primitive as PM---- | Runs the predicate on the next character in the input. If the--- predicate is matched, this consumes the character. Otherwise,--- the character is not consumed. This returns @False@ if the end--- of the input has been reached. This never fails.-trySatisfy :: (Char -> Bool) -> Parser e s Bool-trySatisfy f = uneffectful $ \chunk -> case length chunk of- 0 -> Success False (offset chunk) (length chunk)- _ -> case f (indexLatinCharArray (array chunk) (offset chunk)) of- True -> Success True (offset chunk + 1) (length chunk - 1)- False -> Success False (offset chunk) (length chunk)---- | Runs the function on the next character in the input. If the--- function returns @Just@, this consumes the character and then--- runs the parser on the remaining input. If the function returns--- @Nothing@, this does not consume the tested character, and it--- runs the default parser on the input (which includes the tested--- character). If there is no input remaining, this also runs the--- default parser. This combinator never fails.-trySatisfyThen :: forall (r :: RuntimeRep) (e :: Type) (s :: Type) (a :: TYPE r).- Parser e s a -- ^ Default parser. Runs on @Nothing@ or end of input.- -> (Char -> Maybe (Parser e s a)) -- ^ Parser-selecting predicate- -> Parser e s a-{-# inline trySatisfyThen #-}-trySatisfyThen (Parser g) f = Parser- (\input@(# arr,off0,len0 #) s0 -> case len0 of- 0# -> g input s0- _ -> case f (C# (indexCharArray# arr off0)) of- Nothing -> g input s0- Just (Parser p) -> p (# arr, off0 +# 1#, len0 -# 1# #) s0- )---- | Consume the next character, failing if it does not--- match the expected value or if there is no more input.-char :: e -> Char -> Parser e s ()-{-# inline char #-}-char e !c = uneffectful $ \chunk -> if length chunk > 0- then if indexLatinCharArray (array chunk) (offset chunk) == c- then Success () (offset chunk + 1) (length chunk - 1)- else Failure e- else Failure e---- | Consume the next two characters, failing if they do--- not match the expected values.------ > char2 e a b === char e a *> char e b-char2 :: e -> Char -> Char -> Parser e s ()-{-# inline char2 #-}-char2 e !c0 !c1 = uneffectful $ \chunk ->- if | length chunk > 1- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- -> Success () (offset chunk + 2) (length chunk - 2)- | otherwise -> Failure e---- | Consume three characters, failing if they do--- not match the expected values.------ > char3 e a b c === char e a *> char e b *> char e c-char3 :: e -> Char -> Char -> Char -> Parser e s ()-{-# inline char3 #-}-char3 e !c0 !c1 !c2 = uneffectful $ \chunk ->- if | length chunk > 2- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- -> Success () (offset chunk + 3) (length chunk - 3)- | otherwise -> Failure e---- | Consume four characters, failing if they do--- not match the expected values.------ > char4 e a b c d === char e a *> char e b *> char e c *> char e d-char4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char4 #-}-char4 e !c0 !c1 !c2 !c3 = uneffectful $ \chunk ->- if | length chunk > 3- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- -> Success () (offset chunk + 4) (length chunk - 4)- | otherwise -> Failure e---- | Consume five characters, failing if they do--- not match the expected values.-char5 :: e -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char5 #-}-char5 e !c0 !c1 !c2 !c3 !c4 = uneffectful $ \chunk ->- if | length chunk > 4- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- -> Success () (offset chunk + 5) (length chunk - 5)- | otherwise -> Failure e---- | Consume six characters, failing if they do--- not match the expected values.-char6 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char6 #-}-char6 e !c0 !c1 !c2 !c3 !c4 !c5 = uneffectful $ \chunk ->- if | length chunk > 5- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- -> Success () (offset chunk + 6) (length chunk - 6)- | otherwise -> Failure e---- | Consume seven characters, failing if they do--- not match the expected values.-char7 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char7 #-}-char7 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 = uneffectful $ \chunk ->- if | length chunk > 6- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- , indexLatinCharArray (array chunk) (offset chunk + 6) == c6- -> Success () (offset chunk + 7) (length chunk - 7)- | otherwise -> Failure e---- | Consume eight characters, failing if they do--- not match the expected values.-char8 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char8 #-}-char8 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 = uneffectful $ \chunk ->- if | length chunk > 7- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- , indexLatinCharArray (array chunk) (offset chunk + 6) == c6- , indexLatinCharArray (array chunk) (offset chunk + 7) == c7- -> Success () (offset chunk + 8) (length chunk - 8)- | otherwise -> Failure e---- | Consume nine characters, failing if they do--- not match the expected values.-char9 :: e -> Char -> Char -> Char -> Char- -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char9 #-}-char9 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 = uneffectful $ \chunk ->- if | length chunk > 8- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- , indexLatinCharArray (array chunk) (offset chunk + 6) == c6- , indexLatinCharArray (array chunk) (offset chunk + 7) == c7- , indexLatinCharArray (array chunk) (offset chunk + 8) == c8- -> Success () (offset chunk + 9) (length chunk - 9)- | otherwise -> Failure e---- | Consume ten characters, failing if they do--- not match the expected values.-char10 :: e -> Char -> Char -> Char -> Char -> Char- -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char10 #-}-char10 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 = uneffectful $ \chunk ->- if | length chunk > 9- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- , indexLatinCharArray (array chunk) (offset chunk + 6) == c6- , indexLatinCharArray (array chunk) (offset chunk + 7) == c7- , indexLatinCharArray (array chunk) (offset chunk + 8) == c8- , indexLatinCharArray (array chunk) (offset chunk + 9) == c9- -> Success () (offset chunk + 10) (length chunk - 10)- | otherwise -> Failure e---- | Consume eleven characters, failing if they do--- not match the expected values.-char11 :: e -> Char -> Char -> Char -> Char -> Char -> Char- -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char11 #-}-char11 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 = uneffectful $ \chunk ->- if | length chunk > 10- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- , indexLatinCharArray (array chunk) (offset chunk + 6) == c6- , indexLatinCharArray (array chunk) (offset chunk + 7) == c7- , indexLatinCharArray (array chunk) (offset chunk + 8) == c8- , indexLatinCharArray (array chunk) (offset chunk + 9) == c9- , indexLatinCharArray (array chunk) (offset chunk + 10) == c10- -> Success () (offset chunk + 11) (length chunk - 11)- | otherwise -> Failure e---- | Consume twelve characters, failing if they do--- not match the expected values.-char12 :: e -> Char -> Char -> Char -> Char -> Char -> Char- -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()-{-# inline char12 #-}-char12 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 = uneffectful $ \chunk ->- if | length chunk > 11- , indexLatinCharArray (array chunk) (offset chunk) == c0- , indexLatinCharArray (array chunk) (offset chunk + 1) == c1- , indexLatinCharArray (array chunk) (offset chunk + 2) == c2- , indexLatinCharArray (array chunk) (offset chunk + 3) == c3- , indexLatinCharArray (array chunk) (offset chunk + 4) == c4- , indexLatinCharArray (array chunk) (offset chunk + 5) == c5- , indexLatinCharArray (array chunk) (offset chunk + 6) == c6- , indexLatinCharArray (array chunk) (offset chunk + 7) == c7- , indexLatinCharArray (array chunk) (offset chunk + 8) == c8- , indexLatinCharArray (array chunk) (offset chunk + 9) == c9- , indexLatinCharArray (array chunk) (offset chunk + 10) == c10- , indexLatinCharArray (array chunk) (offset chunk + 11) == c11- -> Success () (offset chunk + 12) (length chunk - 12)- | otherwise -> Failure e---- | Consumes and returns the next character in the input.-any :: e -> Parser e s Char-{-# inline any #-}-any e = uneffectful $ \chunk -> if length chunk > 0- then- let c = indexLatinCharArray (array chunk) (offset chunk)- in Success c (offset chunk + 1) (length chunk - 1)- else Failure e---- | Consume a character from the input or return @Nothing@ if--- end of the stream has been reached. Since ISO 8859-1 maps every--- bytes to a character, this parser never fails.-opt :: Parser e s (Maybe Char)-{-# inline opt #-}-opt = uneffectful $ \chunk -> case length chunk of- 0 -> Success Nothing (offset chunk) (length chunk)- _ -> Success- (Just (indexLatinCharArray (array chunk) (offset chunk)))- (offset chunk + 1) (length chunk - 1)---- | Variant of @opt@ with unboxed result.-opt# :: Parser e s (# (# #) | Char# #)-{-# inline opt# #-}-opt# = Parser- (\(# arr, off, len #) s0 -> case len of- 0# -> (# s0, (# | (# (# (# #) | #), off, len #) #) #)- _ -> (# s0, (# | (# (# | indexCharArray# arr off #), off +# 1#, len -# 1# #) #) #)- )--skipDigitsAsciiLoop ::- Bytes -- Chunk- -> (# Int#, Int# #)-skipDigitsAsciiLoop !c = if length c > 0- then- let w = indexLatinCharArray (array c) (offset c)- in if w >= '0' && w <= '9'- then skipDigitsAsciiLoop (Bytes.unsafeDrop 1 c)- else (# unI (offset c), unI (length c) #)- else (# unI (offset c), unI (length c) #)--skipDigitsAscii1LoopStart ::- e- -> Bytes -- chunk- -> Result# e ()-skipDigitsAscii1LoopStart e !c = if length c > 0- then- let w = indexLatinCharArray (array c) (offset c)- in if w >= '0' && w <= '9'- then upcastUnitSuccess (skipDigitsAsciiLoop (Bytes.unsafeDrop 1 c))- else (# e | #)- else (# e | #)---- | Variant of 'skipDigits' that requires at least one digit--- to be present.-skipDigits1 :: e -> Parser e s ()-{-# inline skipDigits1 #-}-skipDigits1 e = uneffectful# $ \c ->- skipDigitsAscii1LoopStart e c---- | Skip the characters @0-9@ until a non-digit is encountered.--- This parser does not fail.-skipDigits :: Parser e s ()-skipDigits = uneffectful# $ \c ->- upcastUnitSuccess (skipDigitsAsciiLoop c)--unI :: Int -> Int#-{-# inline unI #-}-unI (I# w) = w---- | Skip the character any number of times. This succeeds--- even if the character was not present.-skipChar :: Char -> Parser e s ()-{-# inline skipChar #-}-skipChar !w = uneffectful# $ \c ->- upcastUnitSuccess (skipLoop w c)---- | Skip the character any number of times. It must occur--- at least once or else this will fail.-skipChar1 :: e -> Char -> Parser e s ()-{-# inline skipChar1 #-}-skipChar1 e !w = uneffectful# $ \c ->- skipLoop1Start e w c--skipLoop ::- Char -- byte to match- -> Bytes -- Chunk- -> (# Int#, Int# #)-skipLoop !w !c = if length c > 0- then if indexLatinCharArray (array c) (offset c) == w- then skipLoop w (Bytes.unsafeDrop 1 c)- else (# unI (offset c), unI (length c) #)- else (# unI (offset c), unI (length c) #)--skipLoop1Start ::- e- -> Char -- byte to match- -> Bytes -- chunk- -> Result# e ()-skipLoop1Start e !w !chunk0 = if length chunk0 > 0- then if indexLatinCharArray (array chunk0) (offset chunk0) == w- then upcastUnitSuccess (skipLoop w (Bytes.unsafeDrop 1 chunk0))- else (# e | #)- else (# e | #)---- | Parse a decimal-encoded 8-bit word. If the number is larger--- than 255, this parser fails.-decWord8 :: e -> Parser e s Word8-decWord8 e = Parser- (\chunk0 s0 -> case decSmallWordStart e 256 (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord8Result r #)- )---- | Parse a hexadecimal-encoded 8-bit word. If the number is larger--- than 255, this parser fails. This allows leading zeroes and is--- insensitive to case. For example, @00A@, @0a@ and @A@ would all--- be accepted as the same number.-hexWord8 :: e -> Parser e s Word8-hexWord8 e = Parser- (\chunk0 s0 -> case hexSmallWordStart e 256 (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord8Result r #)- )---- | Parse a hexadecimal-encoded 16-bit word. If the number is larger--- than 65535, this parser fails. This allows leading zeroes and is--- insensitive to case. For example, @0100a@ and @100A@ would both--- be accepted as the same number.-hexWord16 :: e -> Parser e s Word16-hexWord16 e = Parser- (\chunk0 s0 -> case hexSmallWordStart e 65536 (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord16Result r #)- )--hexWord32 :: e -> Parser e s Word32-hexWord32 e = Parser- (\chunk0 s0 -> case hexSmallWordStart e 4294967296 (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord32Result r #)- )---- | Parse a decimal-encoded 16-bit word. If the number is larger--- than 65535, this parser fails.-decWord16 :: e -> Parser e s Word16-decWord16 e = Parser- (\chunk0 s0 -> case decSmallWordStart e 65536 (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord16Result r #)- )---- | Parse a decimal-encoded 32-bit word. If the number is larger--- than 4294967295, this parser fails.-decWord32 :: e -> Parser e s Word32--- This will not work on 32-bit platforms.-decWord32 e = Parser- (\chunk0 s0 -> case decSmallWordStart e 4294967296 (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord32Result r #)- )---- | Parse a decimal-encoded number. If the number is too large to be--- represented by a machine word, this fails with the provided--- error message. This accepts any number of leading zeroes.-decWord :: e -> Parser e s Word-decWord e = Parser- (\chunk0 s0 -> case decWordStart e (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWordResult r #)- )---- | Parse a decimal-encoded unsigned number. If the number is--- too large to be represented by a 64-bit word, this fails with--- the provided error message. This accepts any number of leading--- zeroes.-decWord64 :: e -> Parser e s Word64-decWord64 e = Parser- (\chunk0 s0 -> case decWordStart e (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastWord64Result r #)- )--hexSmallWordStart ::- e -- Error message- -> Word -- Upper Bound- -> Bytes -- Chunk- -> ST# s (Result# e Word# )-hexSmallWordStart e !limit !chunk0 s0 = if length chunk0 > 0- then case oneHexMaybe (PM.indexByteArray (array chunk0) (offset chunk0)) of- Nothing -> (# s0, (# e | #) #)- Just w -> (# s0, hexSmallWordMore e w limit (Bytes.unsafeDrop 1 chunk0) #)- else (# s0, (# e | #) #)--decSmallWordStart ::- e -- Error message- -> Word -- Upper Bound- -> Bytes -- Chunk- -> ST# s (Result# e Word# )-decSmallWordStart e !limit !chunk0 s0 = if length chunk0 > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then (# s0, decSmallWordMore e w limit (Bytes.unsafeDrop 1 chunk0) #)- else (# s0, (# e | #) #)- else (# s0, (# e | #) #)---- This will not inline since it is recursive, but worker--- wrapper will still happen.-decWordMore ::- e -- Error message- -> Word -- Accumulator- -> Bytes -- Chunk- -> Result# e Word#-decWordMore e !acc !chunk0 = case len of- 0 -> (# | (# unW (fromIntegral acc), unI (offset chunk0), 0# #) #)- _ ->- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then- let (overflow,acc') = unsignedPushBase10 acc w- in if overflow- then (# e | #)- else decWordMore e acc' (Bytes.unsafeDrop 1 chunk0)- else (# | (# unW (fromIntegral acc), unI (offset chunk0), len# #) #)- where- !len@(I# len# ) = length chunk0--upcastWordResult :: Result# e Word# -> Result# e Word-{-# inline upcastWordResult #-}-upcastWordResult (# e | #) = (# e | #)-upcastWordResult (# | (# a, b, c #) #) = (# | (# W# a, b, c #) #)---- This only works on 64-bit platforms.-upcastWord64Result :: Result# e Word# -> Result# e Word64-{-# inline upcastWord64Result #-}-upcastWord64Result (# e | #) = (# e | #)-upcastWord64Result (# | (# a, b, c #) #) = (# | (# W64# (-#if MIN_VERSION_base(4,17,0)- Exts.wordToWord64# a-#else- a-#endif- ), b, c #) #)--hexSmallWordMore ::- e -- Error message- -> Word -- Accumulator- -> Word -- Upper Bound- -> Bytes -- Chunk- -> Result# e Word#-hexSmallWordMore e !acc !limit !chunk0 = if length chunk0 > 0- then case oneHexMaybe (PM.indexByteArray (array chunk0) (offset chunk0)) of- Nothing -> (# | (# unW acc, unI (offset chunk0), unI (length chunk0) #) #)- Just w -> let w' = acc * 16 + w in- if w' < limit- then hexSmallWordMore e w' limit (Bytes.unsafeDrop 1 chunk0)- else (# e | #)- else (# | (# unW acc, unI (offset chunk0), 0# #) #)--decSmallWordMore ::- e -- Error message- -> Word -- Accumulator- -> Word -- Upper Bound- -> Bytes -- Chunk- -> Result# e Word#-decSmallWordMore e !acc !limit !chunk0 = if length chunk0 > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then- let w' = acc * 10 + w- in if w' < limit- then decSmallWordMore e w' limit (Bytes.unsafeDrop 1 chunk0)- else (# e | #)- else (# | (# unW acc, unI (offset chunk0), unI (length chunk0) #) #)- else (# | (# unW acc, unI (offset chunk0), 0# #) #)--unW :: Word -> Word#-{-# inline unW #-}-unW (W# w) = w--decWordStart ::- e -- Error message- -> Bytes -- Chunk- -> ST# s (Result# e Word# )-decWordStart e !chunk0 s0 = if length chunk0 > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then (# s0, decWordMore e w (Bytes.unsafeDrop 1 chunk0) #)- else (# s0, (# e | #) #)- else (# s0, (# e | #) #)---- Precondition: the word is small enough-upcastWord16Result :: Result# e Word# -> Result# e Word16-{-# inline upcastWord16Result #-}-upcastWord16Result (# e | #) = (# e | #)-upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# (-#if MIN_VERSION_base(4,16,0)- Exts.wordToWord16#-#endif- a), b, c #) #)---- Precondition: the word is small enough-upcastWord32Result :: Result# e Word# -> Result# e Word32-{-# inline upcastWord32Result #-}-upcastWord32Result (# e | #) = (# e | #)-upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# (-#if MIN_VERSION_base(4,16,0)- Exts.wordToWord32#-#endif- a), b, c #) #)---- Precondition: the word is small enough-upcastWord8Result :: Result# e Word# -> Result# e Word8-{-# inline upcastWord8Result #-}-upcastWord8Result (# e | #) = (# e | #)-upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# (-#if MIN_VERSION_base(4,16,0)- Exts.wordToWord8#-#endif- a), b, c #) #)---- | Parse a decimal-encoded number. If the number is too large to be--- represented by a machine integer, this fails with the provided--- error message. This rejects input with that is preceeded by plus--- or minus. Consequently, it does not parse negative numbers. Use--- 'decStandardInt' or 'decSignedInt' for that purpose. On a 64-bit--- platform 'decWord' will successfully parse 9223372036854775808--- (i.e. @2 ^ 63@), but 'decUnsignedInt' will fail. This parser allows--- leading zeroes.-decUnsignedInt :: e -> Parser e s Int-decUnsignedInt e = Parser- (\chunk0 s0 -> case decPosIntStart e (boxBytes chunk0) s0 of- (# s1, r #) -> (# s1, upcastIntResult r #)- )---- | Variant of 'decUnsignedInt' with an unboxed result.-decUnsignedInt# :: e -> Parser e s Int#-decUnsignedInt# e = Parser- (\chunk0 s0 -> decPosIntStart e (boxBytes chunk0) s0)---- | Parse a decimal-encoded number. If the number is too large to be--- represented by a machine integer, this fails with the provided--- error message. This allows the number to optionally be prefixed--- by plus or minus. If the sign prefix is not present, the number--- is interpreted as positive. This allows leading zeroes.-decSignedInt :: e -> Parser e s Int-decSignedInt e = Parser- (\chunk0 s0 -> case runParser (decSignedInt# e) chunk0 s0 of- (# s1, r #) -> (# s1, upcastIntResult r #)- )---- | Variant of 'decUnsignedInt' that lets the caller supply a leading--- digit. This is useful when parsing formats like JSON where integers with--- leading zeroes are considered invalid. The calling context must--- consume the first digit before calling this parser. Results are--- always positive numbers.-decTrailingInt ::- e -- ^ Error message- -> Int -- ^ Leading digit, should be between @0@ and @9@.- -> Parser e s Int-decTrailingInt e (I# w) = Parser- (\chunk0 s0 -> case runParser (decTrailingInt# e w) chunk0 s0 of- (# s1, r #) -> (# s1, upcastIntResult r #)- )--decTrailingInt# ::- e -- Error message- -> Int# -- Leading digit, should be between @0@ and @9@.- -> Parser e s Int#-decTrailingInt# e !w =- Parser (\chunk0 s0 -> (# s0, decPosIntMore e (W# (int2Word# w)) maxIntAsWord (boxBytes chunk0) #))--maxIntAsWord :: Word-maxIntAsWord = fromIntegral (maxBound :: Int)---- | Parse a decimal-encoded number. If the number is too large to be--- represented by a machine integer, this fails with the provided--- error message. This allows the number to optionally be prefixed--- by minus. If the minus prefix is not present, the number--- is interpreted as positive. The disallows a leading plus sign.--- For example, 'decStandardInt' rejects @+42@, but 'decSignedInt'--- allows it.-decStandardInt :: e -> Parser e s Int-decStandardInt e = Parser- (\chunk0 s0 -> case runParser (decStandardInt# e) chunk0 s0 of- (# s1, r #) -> (# s1, upcastIntResult r #)- )--decSignedInt# :: e -> Parser e s Int#-{-# noinline decSignedInt# #-}-decSignedInt# e = any e `bindFromLiftedToInt` \c -> case c of- '+' -> Parser -- plus sign- (\chunk0 s0 -> decPosIntStart e (boxBytes chunk0) s0)- '-' -> Parser -- minus sign- (\chunk0 s0 -> decNegIntStart e (boxBytes chunk0) s0)- _ -> Parser -- no sign, there should be a digit here- (\chunk0 s0 ->- let !w = char2Word c - 48- in if w < 10- then (# s0, decPosIntMore e w maxIntAsWord (boxBytes chunk0) #)- else (# s0, (# e | #) #)- )---- This is the same as decSignedInt except that we disallow--- a leading plus sign.-decStandardInt# :: e -> Parser e s Int#-{-# noinline decStandardInt# #-}-decStandardInt# e = any e `bindFromLiftedToInt` \c -> case c of- '-' -> Parser -- minus sign- (\chunk0 s0 -> decNegIntStart e (boxBytes chunk0) s0)- _ -> Parser -- no sign, there should be a digit here- (\chunk0 s0 ->- let !w = char2Word c - 48- in if w < 10- then (# s0, decPosIntMore e w maxIntAsWord (boxBytes chunk0) #)- else (# s0, (# e | #) #)- )---- | Variant of 'decUnsignedInteger' that lets the caller supply a leading--- digit. This is useful when parsing formats like JSON where integers with--- leading zeroes are considered invalid. The calling context must--- consume the first digit before calling this parser. Results are--- always positive numbers.-decTrailingInteger ::- Int -- ^ Leading digit, should be between @0@ and @9@.- -> Parser e s Integer-decTrailingInteger (I# w) =- Parser (\chunk0 s0 -> (# s0, (# | decIntegerChunks (I# w) 10 0 (boxBytes chunk0) #) #))---- | Parse a decimal-encoded positive integer of arbitrary--- size. This rejects input that begins with a plus or minus--- sign.-decUnsignedInteger :: e -> Parser e s Integer-decUnsignedInteger e = Parser- (\chunk0 s0 -> decUnsignedIntegerStart e (boxBytes chunk0) s0)---- | Parse a decimal-encoded integer of arbitrary size.--- This accepts input that begins with a plus or minus sign.--- Input without a sign prefix is interpreted as positive.-decSignedInteger :: e -> Parser e s Integer-{-# noinline decSignedInteger #-}-decSignedInteger e = any e >>= \c -> case c of- '+' -> do- decUnsignedInteger e- '-' -> do- x <- decUnsignedInteger e- pure $! negate x- _ -> Parser -- no sign, there should be a digit here- (\chunk0 s0 ->- let !w = char2Word c - 48 in- if w < 10- then- let !r = decIntegerChunks- (fromIntegral @Word @Int w)- 10- 0- (boxBytes chunk0)- in (# s0, (# | r #) #)- else (# s0, (# e | #) #)- )--decPosIntStart ::- e -- Error message- -> Bytes -- Chunk- -> ST# s (Result# e Int# )-decPosIntStart e !chunk0 s0 = if length chunk0 > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then (# s0, decPosIntMore e w maxIntAsWord (Bytes.unsafeDrop 1 chunk0) #)- else (# s0, (# e | #) #)- else (# s0, (# e | #) #)--decNegIntStart ::- e -- Error message- -> Bytes -- Chunk- -> ST# s (Result# e Int# )-decNegIntStart e !chunk0 s0 = if length chunk0 > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then- case decPosIntMore e w (maxIntAsWord + 1) (Bytes.unsafeDrop 1 chunk0) of- (# | (# x, y, z #) #) ->- (# s0, (# | (# (notI# x +# 1# ), y, z #) #) #)- (# err | #) ->- (# s0, (# err | #) #)- else (# s0, (# e | #) #)- else (# s0, (# e | #) #)--decUnsignedIntegerStart ::- e- -> Bytes- -> ST# s (Result# e Integer)-decUnsignedIntegerStart e !chunk0 s0 = if length chunk0 > 0- then- let !w = (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < (10 :: Word8)- then- let !r = decIntegerChunks- (fromIntegral @Word8 @Int w)- 10- 0- (Bytes.unsafeDrop 1 chunk0)- in (# s0, (# | r #) #)- else (# s0, (# e | #) #)- else (# s0, (# e | #) #)---- This will not inline since it is recursive, but worker--- wrapper will still happen. Fails if the accumulator--- exceeds the upper bound.-decPosIntMore ::- e -- Error message- -> Word -- Accumulator, precondition: less than or equal to bound- -> Word -- Inclusive Upper Bound, either (2^63 - 1) or 2^63- -> Bytes -- Chunk- -> Result# e Int#-decPosIntMore e !acc !upper !chunk0 = if len > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then- let (overflow,acc') = positivePushBase10 acc w upper- in if overflow- then (# e | #)- else decPosIntMore e acc' upper (Bytes.unsafeDrop 1 chunk0)- else (# | (# unI (fromIntegral acc), unI (offset chunk0), len# #) #)- else (# | (# unI (fromIntegral acc), unI (offset chunk0), 0# #) #)- where- !len@(I# len# ) = length chunk0---- This will not inline since it is recursive, but worker--- wrapper will still happen. When the accumulator--- exceeds the size of a machine integer, this pushes the--- accumulated machine int and the shift amount onto the--- stack.--- We are intentionally lazy in the accumulator. There is--- no need to force this on every iteration. We do however,--- force it preemptively every time it changes.--- Because of how we track overflow, we are able to use the--- same function for both positive and negative numbers.-decIntegerChunks ::- Int -- Chunk accumulator (e.g. 236)- -> Int -- Chunk base-ten bound (e.g. 1000)- -> Integer -- Accumulator- -> Bytes -- Chunk- -> (# Integer, Int#, Int# #)-decIntegerChunks !nAcc !eAcc acc !chunk0 = if len > 0- then- let !w = fromIntegral @Word8 @Word- (PM.indexByteArray (array chunk0) (offset chunk0)) - 48- in if w < 10- then let !eAcc' = eAcc * 10 in- if eAcc' >= eAcc- then decIntegerChunks- (nAcc * 10 + fromIntegral @Word @Int w)- eAcc'- acc- (Bytes.unsafeDrop 1 chunk0)- else- -- In this case, notice that we deliberately- -- unconsume the digit that would have caused- -- an overflow.- let !r = (acc * fromIntegral @Int @Integer eAcc)- + (fromIntegral @Int @Integer nAcc)- in decIntegerChunks 0 1 r chunk0- else- let !r = (acc * fromIntegral @Int @Integer eAcc)- + (fromIntegral @Int @Integer nAcc)- in (# r, unI (offset chunk0), len# #)- else- let !r = (acc * fromIntegral @Int @Integer eAcc)- + (fromIntegral @Int @Integer nAcc)- in (# r, unI (offset chunk0), 0# #)- where- !len@(I# len# ) = length chunk0--upcastIntResult :: Result# e Int# -> Result# e Int-upcastIntResult (# e | #) = (# e | #)-upcastIntResult (# | (# a, b, c #) #) = (# | (# I# a, b, c #) #)--char2Word :: Char -> Word-char2Word = fromIntegral . ord---- | Take characters until the specified character is encountered.--- Consumes the matched character as well. Fails if the character--- is not present. Visually, the cursor advancement and resulting--- @Bytes@ for @takeTrailedBy \'D\'@ look like this:------ > A B C D E F | input--- > |->->->-| | cursor--- > {-*-*-} | result bytes-takeTrailedBy :: e -> Char -> Parser e s Bytes-takeTrailedBy e !w = do- !start <- cursor- skipTrailedBy e w- !end <- cursor- !arr <- expose- pure (Bytes arr start (end - (start + 1)))---- | Skip all characters until the terminator is encountered--- and then consume the matching character as well. Visually,--- @skipTrailedBy \'C\'@ advances the cursor like this:------ > A Z B Y C X C W--- > |->->->->-|------ This fails if it reaches the end of input without encountering--- the character.-skipTrailedBy :: e -> Char -> Parser e s ()-skipTrailedBy e !w = uneffectful# $ \c ->- skipUntilConsumeLoop e w c---- | Skip all characters until the terminator is encountered.--- This does not consume the terminator. Visually, @skipUntil \'C\'@--- advances the cursor like this:------ > A Z B Y C X C W--- > |->->->-|------ This succeeds if it reaches the end of the input without--- encountering the terminator. It never fails.-skipUntil :: Char -> Parser e s ()-skipUntil !w = uneffectful# $ \c -> skipUntilLoop w c--skipUntilLoop ::- Char -- byte to match- -> Bytes -- Chunk- -> Result# e ()-skipUntilLoop !w !c = case length c of- 0 -> (# | (# (), unI (offset c), 0# #) #)- _ -> if indexLatinCharArray (array c) (offset c) /= w- then skipUntilLoop w (Bytes.unsafeDrop 1 c)- else (# | (# (), unI (offset c), unI (length c) #) #)--skipUntilConsumeLoop ::- e -- Error message- -> Char -- byte to match- -> Bytes -- Chunk- -> Result# e ()-skipUntilConsumeLoop e !w !c = case length c of- 0 -> (# e | #)- _ -> if indexLatinCharArray (array c) (offset c) /= w- then skipUntilConsumeLoop e w (Bytes.unsafeDrop 1 c)- else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)----- | Parse exactly eight ASCII-encoded characters, interpreting them as the--- hexadecimal encoding of a 32-bit number. Note that this rejects a sequence--- such as @BC5A9@, requiring @000BC5A9@ instead. This is insensitive to case.-hexFixedWord32 :: e -> Parser e s Word32-{-# inline hexFixedWord32 #-}-hexFixedWord32 e = Parser- (\x s0 -> case runParser (hexFixedWord32# e) x s0 of- (# s1, r #) -> case r of- (# err | #) -> (# s1, (# err | #) #)- (# | (# a, b, c #) #) -> (# s1, (# | (# W32# (-#if MIN_VERSION_base(4,16,0)- Exts.wordToWord32#-#endif- a), b, c #) #) #)- )--hexFixedWord32# :: e -> Parser e s Word#-{-# noinline hexFixedWord32# #-}-hexFixedWord32# e = uneffectfulWord# $ \chunk -> if length chunk >= 8- then- let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)- !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)- !w2@(W# n2) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 2)- !w3@(W# n3) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 3)- !w4@(W# n4) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 4)- !w5@(W# n5) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 5)- !w6@(W# n6) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 6)- !w7@(W# n7) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 7)- in if | w0 .|. w1 .|. w2 .|. w3 .|. w4 .|. w5 .|. w6 .|. w7 /= maxBound ->- (# |- (# (n0 `Exts.timesWord#` 268435456##) `Exts.plusWord#`- (n1 `Exts.timesWord#` 16777216##) `Exts.plusWord#`- (n2 `Exts.timesWord#` 1048576##) `Exts.plusWord#`- (n3 `Exts.timesWord#` 65536##) `Exts.plusWord#`- (n4 `Exts.timesWord#` 4096##) `Exts.plusWord#`- (n5 `Exts.timesWord#` 256##) `Exts.plusWord#`- (n6 `Exts.timesWord#` 16##) `Exts.plusWord#`- n7- , unI (offset chunk) +# 8#- , unI (length chunk) -# 8# #) #)- | otherwise -> (# e | #)- else (# e | #)---- | Parse exactly 16 ASCII-encoded characters, interpreting them as the--- hexadecimal encoding of a 64-bit number. Note that this rejects a sequence--- such as @BC5A9@, requiring @00000000000BC5A9@ instead. This is insensitive--- to case.-hexFixedWord64 :: e -> Parser e s Word64-{-# inline hexFixedWord64 #-}-hexFixedWord64 e = Parser- (\x s0 -> case runParser (hexFixedWord64# e) x s0 of- (# s1, r #) -> case r of- (# err | #) -> (# s1, (# err | #) #)- (# | (# a, b, c #) #) -> (# s1, (# | (# W64# (-#if MIN_VERSION_base(4,17,0)- Exts.wordToWord64# a-#else-- a-#endif- ), b, c #) #) #)- )--hexFixedWord128 :: e -> Parser e s Word128-hexFixedWord128 e = Word128- <$> hexFixedWord64 e- <*> hexFixedWord64 e--hexFixedWord256 :: e -> Parser e s Word256-hexFixedWord256 e = Word256- <$> hexFixedWord64 e- <*> hexFixedWord64 e- <*> hexFixedWord64 e- <*> hexFixedWord64 e--hexFixedWord64# :: e -> Parser e s Word#-{-# noinline hexFixedWord64# #-}-hexFixedWord64# e = uneffectfulWord# $ \chunk -> if length chunk >= 16- then- let go !off !len !acc = case len of- 0 -> case acc of- W# r ->- (# | (# r- , unI off- , unI (length chunk) -# 16# #) #)- _ -> case oneHexMaybe (PM.indexByteArray (array chunk) off) of- Nothing -> (# e | #)- Just w -> go (off + 1) (len - 1) ((acc * 16) + w)- in go (offset chunk) (16 :: Int) (0 :: Word)- else (# e | #)---- | Parse exactly four ASCII-encoded characters, interpreting--- them as the hexadecimal encoding of a 16-bit number. Note that--- this rejects a sequence such as @5A9@, requiring @05A9@ instead.--- This is insensitive to case. This is particularly useful when--- parsing escape sequences in C or JSON, which allow encoding--- characters in the Basic Multilingual Plane as @\\uhhhh@.-hexFixedWord16 :: e -> Parser e s Word16-{-# inline hexFixedWord16 #-}-hexFixedWord16 e = Parser- (\x s0 -> case runParser (hexFixedWord16# e) x s0 of- (# s1, r #) -> case r of- (# err | #) -> (# s1, (# err | #) #)- (# | (# a, b, c #) #) -> (# s1, (# | (# W16# (-#if MIN_VERSION_base(4,16,0)- Exts.wordToWord16#-#endif- a), b, c #) #) #)- )--hexFixedWord16# :: e -> Parser e s Word#-{-# noinline hexFixedWord16# #-}-hexFixedWord16# e = uneffectfulWord# $ \chunk -> if length chunk >= 4- then- let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)- !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)- !w2@(W# n2) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 2)- !w3@(W# n3) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 3)- in if | w0 .|. w1 .|. w2 .|. w3 /= maxBound ->- (# |- (# (n0 `Exts.timesWord#` 4096##) `Exts.plusWord#`- (n1 `Exts.timesWord#` 256##) `Exts.plusWord#`- (n2 `Exts.timesWord#` 16##) `Exts.plusWord#`- n3- , unI (offset chunk) +# 4#- , unI (length chunk) -# 4# #) #)- | otherwise -> (# e | #)- else (# e | #)---- | Parse exactly two ASCII-encoded characters, interpretting--- them as the hexadecimal encoding of a 8-bit number. Note that--- this rejects a sequence such as @A@, requiring @0A@ instead.--- This is insensitive to case.-hexFixedWord8 :: e -> Parser e s Word8-{-# inline hexFixedWord8 #-}-hexFixedWord8 e = Parser- (\x s0 -> case runParser (hexFixedWord8# e) x s0 of- (# s1, r #) -> case r of- (# err | #) -> (# s1, (# err | #) #)- (# | (# a, b, c #) #) -> (# s1, (# | (# W8# (-#if MIN_VERSION_base(4,16,0)- Exts.wordToWord8#-#endif- a), b, c #) #) #)- )--hexFixedWord8# :: e -> Parser e s Word#-{-# noinline hexFixedWord8# #-}-hexFixedWord8# e = uneffectfulWord# $ \chunk -> if length chunk >= 2- then- let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)- !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)- in if | w0 .|. w1 /= maxBound ->- (# |- (# (n0 `Exts.timesWord#` 16##) `Exts.plusWord#`- n1- , unI (offset chunk) +# 2#- , unI (length chunk) -# 2# #) #)- | otherwise -> (# e | #)- else (# e | #)---- | Consume a single character that is the lowercase hexadecimal--- encoding of a 4-bit word. Fails if the character is not in the class--- @[a-f0-9]@.-hexNibbleLower :: e -> Parser e s Word-hexNibbleLower e = uneffectful $ \chunk -> case length chunk of- 0 -> Failure e- _ ->- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in- if | w >= 48 && w < 58 -> Success (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)- | w >= 97 && w < 103 -> Success (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)- | otherwise -> Failure e---- | Consume a single character that is the case-insensitive hexadecimal--- encoding of a 4-bit word. Fails if the character is not in the class--- @[a-fA-F0-9]@.-hexNibble :: e -> Parser e s Word-hexNibble e = uneffectful $ \chunk -> case length chunk of- 0 -> Failure e- _ ->- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in- if | w >= 48 && w < 58 -> Success (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)- | w >= 65 && w < 71 -> Success (fromIntegral w - 55) (offset chunk + 1) (length chunk - 1)- | w >= 97 && w < 103 -> Success (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)- | otherwise -> Failure e---- | Consume a single character that is the lowercase hexadecimal--- encoding of a 4-bit word. Returns @Nothing@ without consuming--- the character if it is not in the class @[a-f0-9]@. The parser--- never fails.-tryHexNibbleLower :: Parser e s (Maybe Word)-tryHexNibbleLower = unfailing $ \chunk -> case length chunk of- 0 -> InternalStep Nothing (offset chunk) (length chunk)- _ ->- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in- if | w >= 48 && w < 58 -> InternalStep (Just (fromIntegral w - 48)) (offset chunk + 1) (length chunk - 1)- | w >= 97 && w < 103 -> InternalStep (Just (fromIntegral w - 87)) (offset chunk + 1) (length chunk - 1)- | otherwise -> InternalStep Nothing (offset chunk) (length chunk)---- | Consume a single character that is the case-insensitive hexadecimal--- encoding of a 4-bit word. Returns @Nothing@ without consuming--- the character if it is not in the class @[a-fA-F0-9]@. This parser--- never fails.-tryHexNibble :: Parser e s (Maybe Word)-tryHexNibble = unfailing $ \chunk -> case length chunk of- 0 -> InternalStep Nothing (offset chunk) (length chunk)- _ ->- let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in- if | w >= 48 && w < 58 -> InternalStep (Just (fromIntegral w - 48)) (offset chunk + 1) (length chunk - 1)- | w >= 65 && w < 71 -> InternalStep (Just (fromIntegral w - 55)) (offset chunk + 1) (length chunk - 1)- | w >= 97 && w < 103 -> InternalStep (Just (fromIntegral w - 87)) (offset chunk + 1) (length chunk - 1)- | otherwise -> InternalStep Nothing (offset chunk) (length chunk)---- Returns the maximum machine word if the argument is not--- the ASCII encoding of a hexadecimal digit.-oneHex :: Word8 -> Word-{-# inline oneHex #-}-oneHex w- | w >= 48 && w < 58 = (fromIntegral w - 48)- | w >= 65 && w < 71 = (fromIntegral w - 55)- | w >= 97 && w < 103 = (fromIntegral w - 87)- | otherwise = maxBound--oneHexMaybe :: Word8 -> Maybe Word-{-# inline oneHexMaybe #-}-oneHexMaybe w- | w >= 48 && w < 58 = Just (fromIntegral w - 48)- | w >= 65 && w < 71 = Just (fromIntegral w - 55)- | w >= 97 && w < 103 = Just (fromIntegral w - 87)- | otherwise = Nothing--uneffectfulWord# :: (Bytes -> Result# e Word#) -> Parser e s Word#-{-# inline uneffectfulWord# #-}-uneffectfulWord# f = Parser- ( \b s0 -> (# s0, (f (boxBytes b)) #) )---- Precondition: the arguments are non-negative. Boolean is--- true when overflow happens. Performs: a * 10 + b--- Postcondition: when overflow is false, the resulting--- word is less than or equal to the upper bound-positivePushBase10 :: Word -> Word -> Word -> (Bool,Word)-{-# inline positivePushBase10 #-}-positivePushBase10 (W# a) (W# b) (W# upper) =- let !(# ca, r0 #) = Exts.timesWord2# a 10##- !r1 = Exts.plusWord# r0 b- !cb = int2Word# (gtWord# r1 upper)- !cc = int2Word# (ltWord# r1 0##)- !c = ca `or#` cb `or#` cc- in (case c of { 0## -> False; _ -> True }, W# r1)--unsignedPushBase10 :: Word -> Word -> (Bool,Word)-{-# inline unsignedPushBase10 #-}-unsignedPushBase10 (W# a) (W# b) =- let !(# ca, r0 #) = Exts.timesWord2# a 10##- !r1 = Exts.plusWord# r0 b- !cb = int2Word# (ltWord# r1 r0)- !c = ca `or#` cb- in (case c of { 0## -> False; _ -> True }, W# r1)---- | Skip while the predicate is matched. This is always inlined.-skipWhile :: (Char -> Bool) -> Parser e s ()-{-# inline skipWhile #-}-skipWhile f = go where- go = isEndOfInput >>= \case- True -> pure ()- False -> do- w <- anyUnsafe- if f w- then go- else unconsume 1---- Interpret the next byte as an Latin1-encoded character.--- Does not check to see if any characters are left. This--- is not exported.-anyUnsafe :: Parser e s Char-{-# inline anyUnsafe #-}-anyUnsafe = uneffectful $ \chunk ->- let w = indexCharArray (array chunk) (offset chunk) :: Char- in Success w (offset chunk + 1) (length chunk - 1)---- Reads one byte and interprets it as Latin1-encoded character.-indexCharArray :: PM.ByteArray -> Int -> Char-{-# inline indexCharArray #-}-indexCharArray (PM.ByteArray x) (I# i) = C# (indexCharArray# x i)---- | Match any character, to perform lookahead. Returns 'Nothing' if--- end of input has been reached. Does not consume any input.------ /Note/: Because this parser does not fail, do not use it--- with combinators such as 'many', because such as 'many',--- because such parsers loop until a failure occurs. Careless--- use will thus result in an infinite loop.-peek :: Parser e s (Maybe Char)-{-# inline peek #-}-peek = uneffectful $ \(Bytes arr off len) ->- let v = if len > 0- then Just (indexCharArray arr off)- else Nothing- in Success v off len---- | Match any byte, to perform lookahead. Does not consume any--- input, but will fail if end of input has been reached.-peek' :: e -> Parser e s Char-{-# inline peek' #-}-peek' e = uneffectful $ \(Bytes arr off len) -> if len > 0- then Success (indexCharArray arr off) off len- else Failure e+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedSums #-}+{-# LANGUAGE UnboxedTuples #-}++{- | Parse input as though it were text encoded by+ISO 8859-1 (Latin-1). All byte sequences are valid+text under ISO 8859-1.+-}+module Data.Bytes.Parser.Latin+ ( -- * Matching++ -- ** Required+ char+ , char2+ , char3+ , char4+ , char5+ , char6+ , char7+ , char8+ , char9+ , char10+ , char11+ , char12++ -- ** Try+ , trySatisfy+ , trySatisfyThen++ -- * One Character+ , any+ , opt+ , opt#++ -- * Many Characters+ , takeTrailedBy++ -- * Lookahead+ , peek+ , peek'++ -- * Skip+ , skipDigits+ , skipDigits1+ , skipChar+ , skipChar1+ , skipTrailedBy+ , skipUntil+ , skipWhile++ -- * End of Input+ , endOfInput+ , isEndOfInput++ -- * Numbers++ -- ** Decimal++ -- *** Unsigned+ , decWord+ , decWord8+ , decWord16+ , decWord32+ , decWord64++ -- *** Signed+ , decUnsignedInt+ , decUnsignedInt#+ , decSignedInt+ , decStandardInt+ , decTrailingInt+ , decTrailingInt#+ , decSignedInteger+ , decUnsignedInteger+ , decTrailingInteger++ -- ** Hexadecimal++ -- *** Variable Length+ , hexWord8+ , hexWord16+ , hexWord32++ -- *** Fixed Length+ , hexFixedWord8+ , hexFixedWord16+ , hexFixedWord32+ , hexFixedWord64+ , hexFixedWord128+ , hexFixedWord256++ -- *** Digit+ , hexNibbleLower+ , tryHexNibbleLower+ , hexNibble+ , tryHexNibble+ ) where++import Prelude hiding (any, fail, length, takeWhile)++import Data.Bits ((.|.))+import Data.Bytes.Parser (bindFromLiftedToInt, endOfInput, isEndOfInput)+import Data.Bytes.Parser.Internal (InternalStep (..), Parser (..), Result (..), Result#, ST#, boxBytes, indexLatinCharArray, uneffectful, uneffectful#, unfailing, upcastUnitSuccess)+import Data.Bytes.Parser.Unsafe (cursor, expose, unconsume)+import Data.Bytes.Types (Bytes (..))+import Data.Char (ord)+import Data.Kind (Type)+import Data.WideWord (Word128 (Word128), Word256 (Word256))+import Data.Word (Word8)+import GHC.Exts (Char (C#), Char#, Int (I#), Int#, RuntimeRep, TYPE, Word#, gtWord#, indexCharArray#, int2Word#, ltWord#, notI#, or#, (+#), (-#))+import GHC.Word (Word (W#), Word16 (W16#), Word32 (W32#), Word64 (W64#), Word8 (W8#))++import qualified Data.Bytes as Bytes+import qualified Data.Primitive as PM+import qualified GHC.Exts as Exts++{- | Runs the predicate on the next character in the input. If the+predicate is matched, this consumes the character. Otherwise,+the character is not consumed. This returns @False@ if the end+of the input has been reached. This never fails.+-}+trySatisfy :: (Char -> Bool) -> Parser e s Bool+trySatisfy f = uneffectful $ \chunk -> case length chunk of+ 0 -> Success False (offset chunk) (length chunk)+ _ -> case f (indexLatinCharArray (array chunk) (offset chunk)) of+ True -> Success True (offset chunk + 1) (length chunk - 1)+ False -> Success False (offset chunk) (length chunk)++{- | Runs the function on the next character in the input. If the+function returns @Just@, this consumes the character and then+runs the parser on the remaining input. If the function returns+@Nothing@, this does not consume the tested character, and it+runs the default parser on the input (which includes the tested+character). If there is no input remaining, this also runs the+default parser. This combinator never fails.+-}+trySatisfyThen ::+ forall (r :: RuntimeRep) (e :: Type) (s :: Type) (a :: TYPE r).+ -- | Default parser. Runs on @Nothing@ or end of input.+ Parser e s a ->+ -- | Parser-selecting predicate+ (Char -> Maybe (Parser e s a)) ->+ Parser e s a+{-# INLINE trySatisfyThen #-}+trySatisfyThen (Parser g) f =+ Parser+ ( \input@(# arr, off0, len0 #) s0 -> case len0 of+ 0# -> g input s0+ _ -> case f (C# (indexCharArray# arr off0)) of+ Nothing -> g input s0+ Just (Parser p) -> p (# arr, off0 +# 1#, len0 -# 1# #) s0+ )++{- | Consume the next character, failing if it does not+match the expected value or if there is no more input.+-}+char :: e -> Char -> Parser e s ()+{-# INLINE char #-}+char e !c = uneffectful $ \chunk ->+ if length chunk > 0+ then+ if indexLatinCharArray (array chunk) (offset chunk) == c+ then Success () (offset chunk + 1) (length chunk - 1)+ else Failure e+ else Failure e++{- | Consume the next two characters, failing if they do+not match the expected values.++> char2 e a b === char e a *> char e b+-}+char2 :: e -> Char -> Char -> Parser e s ()+{-# INLINE char2 #-}+char2 e !c0 !c1 = uneffectful $ \chunk ->+ if+ | length chunk > 1+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1 ->+ Success () (offset chunk + 2) (length chunk - 2)+ | otherwise -> Failure e++{- | Consume three characters, failing if they do+not match the expected values.++> char3 e a b c === char e a *> char e b *> char e c+-}+char3 :: e -> Char -> Char -> Char -> Parser e s ()+{-# INLINE char3 #-}+char3 e !c0 !c1 !c2 = uneffectful $ \chunk ->+ if+ | length chunk > 2+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2 ->+ Success () (offset chunk + 3) (length chunk - 3)+ | otherwise -> Failure e++{- | Consume four characters, failing if they do+not match the expected values.++> char4 e a b c d === char e a *> char e b *> char e c *> char e d+-}+char4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()+{-# INLINE char4 #-}+char4 e !c0 !c1 !c2 !c3 = uneffectful $ \chunk ->+ if+ | length chunk > 3+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3 ->+ Success () (offset chunk + 4) (length chunk - 4)+ | otherwise -> Failure e++{- | Consume five characters, failing if they do+not match the expected values.+-}+char5 :: e -> Char -> Char -> Char -> Char -> Char -> Parser e s ()+{-# INLINE char5 #-}+char5 e !c0 !c1 !c2 !c3 !c4 = uneffectful $ \chunk ->+ if+ | length chunk > 4+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4 ->+ Success () (offset chunk + 5) (length chunk - 5)+ | otherwise -> Failure e++{- | Consume six characters, failing if they do+not match the expected values.+-}+char6 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()+{-# INLINE char6 #-}+char6 e !c0 !c1 !c2 !c3 !c4 !c5 = uneffectful $ \chunk ->+ if+ | length chunk > 5+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5 ->+ Success () (offset chunk + 6) (length chunk - 6)+ | otherwise -> Failure e++{- | Consume seven characters, failing if they do+not match the expected values.+-}+char7 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()+{-# INLINE char7 #-}+char7 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 = uneffectful $ \chunk ->+ if+ | length chunk > 6+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5+ , indexLatinCharArray (array chunk) (offset chunk + 6) == c6 ->+ Success () (offset chunk + 7) (length chunk - 7)+ | otherwise -> Failure e++{- | Consume eight characters, failing if they do+not match the expected values.+-}+char8 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()+{-# INLINE char8 #-}+char8 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 = uneffectful $ \chunk ->+ if+ | length chunk > 7+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5+ , indexLatinCharArray (array chunk) (offset chunk + 6) == c6+ , indexLatinCharArray (array chunk) (offset chunk + 7) == c7 ->+ Success () (offset chunk + 8) (length chunk - 8)+ | otherwise -> Failure e++{- | Consume nine characters, failing if they do+not match the expected values.+-}+char9 ::+ e ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Parser e s ()+{-# INLINE char9 #-}+char9 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 = uneffectful $ \chunk ->+ if+ | length chunk > 8+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5+ , indexLatinCharArray (array chunk) (offset chunk + 6) == c6+ , indexLatinCharArray (array chunk) (offset chunk + 7) == c7+ , indexLatinCharArray (array chunk) (offset chunk + 8) == c8 ->+ Success () (offset chunk + 9) (length chunk - 9)+ | otherwise -> Failure e++{- | Consume ten characters, failing if they do+not match the expected values.+-}+char10 ::+ e ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Parser e s ()+{-# INLINE char10 #-}+char10 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 = uneffectful $ \chunk ->+ if+ | length chunk > 9+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5+ , indexLatinCharArray (array chunk) (offset chunk + 6) == c6+ , indexLatinCharArray (array chunk) (offset chunk + 7) == c7+ , indexLatinCharArray (array chunk) (offset chunk + 8) == c8+ , indexLatinCharArray (array chunk) (offset chunk + 9) == c9 ->+ Success () (offset chunk + 10) (length chunk - 10)+ | otherwise -> Failure e++{- | Consume eleven characters, failing if they do+not match the expected values.+-}+char11 ::+ e ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Parser e s ()+{-# INLINE char11 #-}+char11 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 = uneffectful $ \chunk ->+ if+ | length chunk > 10+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5+ , indexLatinCharArray (array chunk) (offset chunk + 6) == c6+ , indexLatinCharArray (array chunk) (offset chunk + 7) == c7+ , indexLatinCharArray (array chunk) (offset chunk + 8) == c8+ , indexLatinCharArray (array chunk) (offset chunk + 9) == c9+ , indexLatinCharArray (array chunk) (offset chunk + 10) == c10 ->+ Success () (offset chunk + 11) (length chunk - 11)+ | otherwise -> Failure e++{- | Consume twelve characters, failing if they do+not match the expected values.+-}+char12 ::+ e ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Char ->+ Parser e s ()+{-# INLINE char12 #-}+char12 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 = uneffectful $ \chunk ->+ if+ | length chunk > 11+ , indexLatinCharArray (array chunk) (offset chunk) == c0+ , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+ , indexLatinCharArray (array chunk) (offset chunk + 2) == c2+ , indexLatinCharArray (array chunk) (offset chunk + 3) == c3+ , indexLatinCharArray (array chunk) (offset chunk + 4) == c4+ , indexLatinCharArray (array chunk) (offset chunk + 5) == c5+ , indexLatinCharArray (array chunk) (offset chunk + 6) == c6+ , indexLatinCharArray (array chunk) (offset chunk + 7) == c7+ , indexLatinCharArray (array chunk) (offset chunk + 8) == c8+ , indexLatinCharArray (array chunk) (offset chunk + 9) == c9+ , indexLatinCharArray (array chunk) (offset chunk + 10) == c10+ , indexLatinCharArray (array chunk) (offset chunk + 11) == c11 ->+ Success () (offset chunk + 12) (length chunk - 12)+ | otherwise -> Failure e++-- | Consumes and returns the next character in the input.+any :: e -> Parser e s Char+{-# INLINE any #-}+any e = uneffectful $ \chunk ->+ if length chunk > 0+ then+ let c = indexLatinCharArray (array chunk) (offset chunk)+ in Success c (offset chunk + 1) (length chunk - 1)+ else Failure e++{- | Consume a character from the input or return @Nothing@ if+end of the stream has been reached. Since ISO 8859-1 maps every+bytes to a character, this parser never fails.+-}+opt :: Parser e s (Maybe Char)+{-# INLINE opt #-}+opt = uneffectful $ \chunk -> case length chunk of+ 0 -> Success Nothing (offset chunk) (length chunk)+ _ ->+ Success+ (Just (indexLatinCharArray (array chunk) (offset chunk)))+ (offset chunk + 1)+ (length chunk - 1)++-- | Variant of @opt@ with unboxed result.+opt# :: Parser e s (# (# #) | Char# #)+{-# INLINE opt# #-}+opt# =+ Parser+ ( \(# arr, off, len #) s0 -> case len of+ 0# -> (# s0, (# | (# (# (# #) | #), off, len #) #) #)+ _ -> (# s0, (# | (# (# | indexCharArray# arr off #), off +# 1#, len -# 1# #) #) #)+ )++skipDigitsAsciiLoop ::+ Bytes -> -- Chunk+ (# Int#, Int# #)+skipDigitsAsciiLoop !c =+ if length c > 0+ then+ let w = indexLatinCharArray (array c) (offset c)+ in if w >= '0' && w <= '9'+ then skipDigitsAsciiLoop (Bytes.unsafeDrop 1 c)+ else (# unI (offset c), unI (length c) #)+ else (# unI (offset c), unI (length c) #)++skipDigitsAscii1LoopStart ::+ e ->+ Bytes -> -- chunk+ Result# e ()+skipDigitsAscii1LoopStart e !c =+ if length c > 0+ then+ let w = indexLatinCharArray (array c) (offset c)+ in if w >= '0' && w <= '9'+ then upcastUnitSuccess (skipDigitsAsciiLoop (Bytes.unsafeDrop 1 c))+ else (# e | #)+ else (# e | #)++{- | Variant of 'skipDigits' that requires at least one digit+to be present.+-}+skipDigits1 :: e -> Parser e s ()+{-# INLINE skipDigits1 #-}+skipDigits1 e = uneffectful# $ \c ->+ skipDigitsAscii1LoopStart e c++{- | Skip the characters @0-9@ until a non-digit is encountered.+This parser does not fail.+-}+skipDigits :: Parser e s ()+skipDigits = uneffectful# $ \c ->+ upcastUnitSuccess (skipDigitsAsciiLoop c)++unI :: Int -> Int#+{-# INLINE unI #-}+unI (I# w) = w++{- | Skip the character any number of times. This succeeds+even if the character was not present.+-}+skipChar :: Char -> Parser e s ()+{-# INLINE skipChar #-}+skipChar !w = uneffectful# $ \c ->+ upcastUnitSuccess (skipLoop w c)++{- | Skip the character any number of times. It must occur+at least once or else this will fail.+-}+skipChar1 :: e -> Char -> Parser e s ()+{-# INLINE skipChar1 #-}+skipChar1 e !w = uneffectful# $ \c ->+ skipLoop1Start e w c++skipLoop ::+ Char -> -- byte to match+ Bytes -> -- Chunk+ (# Int#, Int# #)+skipLoop !w !c =+ if length c > 0+ then+ if indexLatinCharArray (array c) (offset c) == w+ then skipLoop w (Bytes.unsafeDrop 1 c)+ else (# unI (offset c), unI (length c) #)+ else (# unI (offset c), unI (length c) #)++skipLoop1Start ::+ e ->+ Char -> -- byte to match+ Bytes -> -- chunk+ Result# e ()+skipLoop1Start e !w !chunk0 =+ if length chunk0 > 0+ then+ if indexLatinCharArray (array chunk0) (offset chunk0) == w+ then upcastUnitSuccess (skipLoop w (Bytes.unsafeDrop 1 chunk0))+ else (# e | #)+ else (# e | #)++{- | Parse a decimal-encoded 8-bit word. If the number is larger+than 255, this parser fails.+-}+decWord8 :: e -> Parser e s Word8+decWord8 e =+ Parser+ ( \chunk0 s0 -> case decSmallWordStart e 256 (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord8Result r #)+ )++{- | Parse a hexadecimal-encoded 8-bit word. If the number is larger+than 255, this parser fails. This allows leading zeroes and is+insensitive to case. For example, @00A@, @0a@ and @A@ would all+be accepted as the same number.+-}+hexWord8 :: e -> Parser e s Word8+hexWord8 e =+ Parser+ ( \chunk0 s0 -> case hexSmallWordStart e 256 (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord8Result r #)+ )++{- | Parse a hexadecimal-encoded 16-bit word. If the number is larger+than 65535, this parser fails. This allows leading zeroes and is+insensitive to case. For example, @0100a@ and @100A@ would both+be accepted as the same number.+-}+hexWord16 :: e -> Parser e s Word16+hexWord16 e =+ Parser+ ( \chunk0 s0 -> case hexSmallWordStart e 65536 (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord16Result r #)+ )++hexWord32 :: e -> Parser e s Word32+hexWord32 e =+ Parser+ ( \chunk0 s0 -> case hexSmallWordStart e 4294967296 (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord32Result r #)+ )++{- | Parse a decimal-encoded 16-bit word. If the number is larger+than 65535, this parser fails.+-}+decWord16 :: e -> Parser e s Word16+decWord16 e =+ Parser+ ( \chunk0 s0 -> case decSmallWordStart e 65536 (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord16Result r #)+ )++{- | Parse a decimal-encoded 32-bit word. If the number is larger+than 4294967295, this parser fails.+-}+decWord32 :: e -> Parser e s Word32+-- This will not work on 32-bit platforms.+decWord32 e =+ Parser+ ( \chunk0 s0 -> case decSmallWordStart e 4294967296 (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord32Result r #)+ )++{- | Parse a decimal-encoded number. If the number is too large to be+represented by a machine word, this fails with the provided+error message. This accepts any number of leading zeroes.+-}+decWord :: e -> Parser e s Word+decWord e =+ Parser+ ( \chunk0 s0 -> case decWordStart e (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWordResult r #)+ )++{- | Parse a decimal-encoded unsigned number. If the number is+too large to be represented by a 64-bit word, this fails with+the provided error message. This accepts any number of leading+zeroes.+-}+decWord64 :: e -> Parser e s Word64+decWord64 e =+ Parser+ ( \chunk0 s0 -> case decWordStart e (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastWord64Result r #)+ )++hexSmallWordStart ::+ e -> -- Error message+ Word -> -- Upper Bound+ Bytes -> -- Chunk+ ST# s (Result# e Word#)+hexSmallWordStart e !limit !chunk0 s0 =+ if length chunk0 > 0+ then case oneHexMaybe (PM.indexByteArray (array chunk0) (offset chunk0)) of+ Nothing -> (# s0, (# e | #) #)+ Just w -> (# s0, hexSmallWordMore e w limit (Bytes.unsafeDrop 1 chunk0) #)+ else (# s0, (# e | #) #)++decSmallWordStart ::+ e -> -- Error message+ Word -> -- Upper Bound+ Bytes -> -- Chunk+ ST# s (Result# e Word#)+decSmallWordStart e !limit !chunk0 s0 =+ if length chunk0 > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then (# s0, decSmallWordMore e w limit (Bytes.unsafeDrop 1 chunk0) #)+ else (# s0, (# e | #) #)+ else (# s0, (# e | #) #)++-- This will not inline since it is recursive, but worker+-- wrapper will still happen.+decWordMore ::+ e -> -- Error message+ Word -> -- Accumulator+ Bytes -> -- Chunk+ Result# e Word#+decWordMore e !acc !chunk0 = case len of+ 0 -> (# | (# unW (fromIntegral acc), unI (offset chunk0), 0# #) #)+ _ ->+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then+ let (overflow, acc') = unsignedPushBase10 acc w+ in if overflow+ then (# e | #)+ else decWordMore e acc' (Bytes.unsafeDrop 1 chunk0)+ else (# | (# unW (fromIntegral acc), unI (offset chunk0), len# #) #)+ where+ !len@(I# len#) = length chunk0++upcastWordResult :: Result# e Word# -> Result# e Word+{-# INLINE upcastWordResult #-}+upcastWordResult (# e | #) = (# e | #)+upcastWordResult (# | (# a, b, c #) #) = (# | (# W# a, b, c #) #)++{- FOURMOLU_DISABLE -}+-- This only works on 64-bit platforms.+upcastWord64Result :: Result# e Word# -> Result# e Word64+{-# inline upcastWord64Result #-}+upcastWord64Result (# e | #) = (# e | #)+upcastWord64Result (# | (# a, b, c #) #) = (# | (# W64# (+#if MIN_VERSION_base(4,17,0)+ Exts.wordToWord64# a+#else+ a+#endif+ ), b, c #) #)+{- FOURMOLU_ENABLE -}++hexSmallWordMore ::+ e -> -- Error message+ Word -> -- Accumulator+ Word -> -- Upper Bound+ Bytes -> -- Chunk+ Result# e Word#+hexSmallWordMore e !acc !limit !chunk0 =+ if length chunk0 > 0+ then case oneHexMaybe (PM.indexByteArray (array chunk0) (offset chunk0)) of+ Nothing -> (# | (# unW acc, unI (offset chunk0), unI (length chunk0) #) #)+ Just w ->+ let w' = acc * 16 + w+ in if w' < limit+ then hexSmallWordMore e w' limit (Bytes.unsafeDrop 1 chunk0)+ else (# e | #)+ else (# | (# unW acc, unI (offset chunk0), 0# #) #)++decSmallWordMore ::+ e -> -- Error message+ Word -> -- Accumulator+ Word -> -- Upper Bound+ Bytes -> -- Chunk+ Result# e Word#+decSmallWordMore e !acc !limit !chunk0 =+ if length chunk0 > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then+ let w' = acc * 10 + w+ in if w' < limit+ then decSmallWordMore e w' limit (Bytes.unsafeDrop 1 chunk0)+ else (# e | #)+ else (# | (# unW acc, unI (offset chunk0), unI (length chunk0) #) #)+ else (# | (# unW acc, unI (offset chunk0), 0# #) #)++unW :: Word -> Word#+{-# INLINE unW #-}+unW (W# w) = w++decWordStart ::+ e -> -- Error message+ Bytes -> -- Chunk+ ST# s (Result# e Word#)+decWordStart e !chunk0 s0 =+ if length chunk0 > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then (# s0, decWordMore e w (Bytes.unsafeDrop 1 chunk0) #)+ else (# s0, (# e | #) #)+ else (# s0, (# e | #) #)++{- FOURMOLU_DISABLE -}+-- Precondition: the word is small enough+upcastWord16Result :: Result# e Word# -> Result# e Word16+{-# inline upcastWord16Result #-}+upcastWord16Result (# e | #) = (# e | #)+upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# (+#if MIN_VERSION_base(4,16,0)+ Exts.wordToWord16#+#endif+ a), b, c #) #)++-- Precondition: the word is small enough+upcastWord32Result :: Result# e Word# -> Result# e Word32+{-# inline upcastWord32Result #-}+upcastWord32Result (# e | #) = (# e | #)+upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# (+#if MIN_VERSION_base(4,16,0)+ Exts.wordToWord32#+#endif+ a), b, c #) #)++-- Precondition: the word is small enough+upcastWord8Result :: Result# e Word# -> Result# e Word8+{-# inline upcastWord8Result #-}+upcastWord8Result (# e | #) = (# e | #)+upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# (+#if MIN_VERSION_base(4,16,0)+ Exts.wordToWord8#+#endif+ a), b, c #) #)+{- FOURMOLU_ENABLE -}++{- | Parse a decimal-encoded number. If the number is too large to be+represented by a machine integer, this fails with the provided+error message. This rejects input with that is preceeded by plus+or minus. Consequently, it does not parse negative numbers. Use+'decStandardInt' or 'decSignedInt' for that purpose. On a 64-bit+platform 'decWord' will successfully parse 9223372036854775808+(i.e. @2 ^ 63@), but 'decUnsignedInt' will fail. This parser allows+leading zeroes.+-}+decUnsignedInt :: e -> Parser e s Int+decUnsignedInt e =+ Parser+ ( \chunk0 s0 -> case decPosIntStart e (boxBytes chunk0) s0 of+ (# s1, r #) -> (# s1, upcastIntResult r #)+ )++-- | Variant of 'decUnsignedInt' with an unboxed result.+decUnsignedInt# :: e -> Parser e s Int#+decUnsignedInt# e =+ Parser+ (\chunk0 s0 -> decPosIntStart e (boxBytes chunk0) s0)++{- | Parse a decimal-encoded number. If the number is too large to be+represented by a machine integer, this fails with the provided+error message. This allows the number to optionally be prefixed+by plus or minus. If the sign prefix is not present, the number+is interpreted as positive. This allows leading zeroes.+-}+decSignedInt :: e -> Parser e s Int+decSignedInt e =+ Parser+ ( \chunk0 s0 -> case runParser (decSignedInt# e) chunk0 s0 of+ (# s1, r #) -> (# s1, upcastIntResult r #)+ )++{- | Variant of 'decUnsignedInt' that lets the caller supply a leading+digit. This is useful when parsing formats like JSON where integers with+leading zeroes are considered invalid. The calling context must+consume the first digit before calling this parser. Results are+always positive numbers.+-}+decTrailingInt ::+ -- | Error message+ e ->+ -- | Leading digit, should be between @0@ and @9@.+ Int ->+ Parser e s Int+decTrailingInt e (I# w) =+ Parser+ ( \chunk0 s0 -> case runParser (decTrailingInt# e w) chunk0 s0 of+ (# s1, r #) -> (# s1, upcastIntResult r #)+ )++decTrailingInt# ::+ e -> -- Error message+ Int# -> -- Leading digit, should be between @0@ and @9@.+ Parser e s Int#+decTrailingInt# e !w =+ Parser (\chunk0 s0 -> (# s0, decPosIntMore e (W# (int2Word# w)) maxIntAsWord (boxBytes chunk0) #))++maxIntAsWord :: Word+maxIntAsWord = fromIntegral (maxBound :: Int)++{- | Parse a decimal-encoded number. If the number is too large to be+represented by a machine integer, this fails with the provided+error message. This allows the number to optionally be prefixed+by minus. If the minus prefix is not present, the number+is interpreted as positive. The disallows a leading plus sign.+For example, 'decStandardInt' rejects @+42@, but 'decSignedInt'+allows it.+-}+decStandardInt :: e -> Parser e s Int+decStandardInt e =+ Parser+ ( \chunk0 s0 -> case runParser (decStandardInt# e) chunk0 s0 of+ (# s1, r #) -> (# s1, upcastIntResult r #)+ )++decSignedInt# :: e -> Parser e s Int#+{-# NOINLINE decSignedInt# #-}+decSignedInt# e =+ any e `bindFromLiftedToInt` \c -> case c of+ '+' ->+ Parser -- plus sign+ (\chunk0 s0 -> decPosIntStart e (boxBytes chunk0) s0)+ '-' ->+ Parser -- minus sign+ (\chunk0 s0 -> decNegIntStart e (boxBytes chunk0) s0)+ _ ->+ Parser -- no sign, there should be a digit here+ ( \chunk0 s0 ->+ let !w = char2Word c - 48+ in if w < 10+ then (# s0, decPosIntMore e w maxIntAsWord (boxBytes chunk0) #)+ else (# s0, (# e | #) #)+ )++-- This is the same as decSignedInt except that we disallow+-- a leading plus sign.+decStandardInt# :: e -> Parser e s Int#+{-# NOINLINE decStandardInt# #-}+decStandardInt# e =+ any e `bindFromLiftedToInt` \c -> case c of+ '-' ->+ Parser -- minus sign+ (\chunk0 s0 -> decNegIntStart e (boxBytes chunk0) s0)+ _ ->+ Parser -- no sign, there should be a digit here+ ( \chunk0 s0 ->+ let !w = char2Word c - 48+ in if w < 10+ then (# s0, decPosIntMore e w maxIntAsWord (boxBytes chunk0) #)+ else (# s0, (# e | #) #)+ )++{- | Variant of 'decUnsignedInteger' that lets the caller supply a leading+digit. This is useful when parsing formats like JSON where integers with+leading zeroes are considered invalid. The calling context must+consume the first digit before calling this parser. Results are+always positive numbers.+-}+decTrailingInteger ::+ -- | Leading digit, should be between @0@ and @9@.+ Int ->+ Parser e s Integer+decTrailingInteger (I# w) =+ Parser (\chunk0 s0 -> (# s0, (# | decIntegerChunks (I# w) 10 0 (boxBytes chunk0) #) #))++{- | Parse a decimal-encoded positive integer of arbitrary+size. This rejects input that begins with a plus or minus+sign.+-}+decUnsignedInteger :: e -> Parser e s Integer+decUnsignedInteger e =+ Parser+ (\chunk0 s0 -> decUnsignedIntegerStart e (boxBytes chunk0) s0)++{- | Parse a decimal-encoded integer of arbitrary size.+This accepts input that begins with a plus or minus sign.+Input without a sign prefix is interpreted as positive.+-}+decSignedInteger :: e -> Parser e s Integer+{-# NOINLINE decSignedInteger #-}+decSignedInteger e =+ any e >>= \c -> case c of+ '+' -> do+ decUnsignedInteger e+ '-' -> do+ x <- decUnsignedInteger e+ pure $! negate x+ _ ->+ Parser -- no sign, there should be a digit here+ ( \chunk0 s0 ->+ let !w = char2Word c - 48+ in if w < 10+ then+ let !r =+ decIntegerChunks+ (fromIntegral @Word @Int w)+ 10+ 0+ (boxBytes chunk0)+ in (# s0, (# | r #) #)+ else (# s0, (# e | #) #)+ )++decPosIntStart ::+ e -> -- Error message+ Bytes -> -- Chunk+ ST# s (Result# e Int#)+decPosIntStart e !chunk0 s0 =+ if length chunk0 > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then (# s0, decPosIntMore e w maxIntAsWord (Bytes.unsafeDrop 1 chunk0) #)+ else (# s0, (# e | #) #)+ else (# s0, (# e | #) #)++decNegIntStart ::+ e -> -- Error message+ Bytes -> -- Chunk+ ST# s (Result# e Int#)+decNegIntStart e !chunk0 s0 =+ if length chunk0 > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then case decPosIntMore e w (maxIntAsWord + 1) (Bytes.unsafeDrop 1 chunk0) of+ (# | (# x, y, z #) #) ->+ (# s0, (# | (# (notI# x +# 1#), y, z #) #) #)+ (# err | #) ->+ (# s0, (# err | #) #)+ else (# s0, (# e | #) #)+ else (# s0, (# e | #) #)++decUnsignedIntegerStart ::+ e ->+ Bytes ->+ ST# s (Result# e Integer)+decUnsignedIntegerStart e !chunk0 s0 =+ if length chunk0 > 0+ then+ let !w = (PM.indexByteArray (array chunk0) (offset chunk0)) - 48+ in if w < (10 :: Word8)+ then+ let !r =+ decIntegerChunks+ (fromIntegral @Word8 @Int w)+ 10+ 0+ (Bytes.unsafeDrop 1 chunk0)+ in (# s0, (# | r #) #)+ else (# s0, (# e | #) #)+ else (# s0, (# e | #) #)++-- This will not inline since it is recursive, but worker+-- wrapper will still happen. Fails if the accumulator+-- exceeds the upper bound.+decPosIntMore ::+ e -> -- Error message+ Word -> -- Accumulator, precondition: less than or equal to bound+ Word -> -- Inclusive Upper Bound, either (2^63 - 1) or 2^63+ Bytes -> -- Chunk+ Result# e Int#+decPosIntMore e !acc !upper !chunk0 =+ if len > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then+ let (overflow, acc') = positivePushBase10 acc w upper+ in if overflow+ then (# e | #)+ else decPosIntMore e acc' upper (Bytes.unsafeDrop 1 chunk0)+ else (# | (# unI (fromIntegral acc), unI (offset chunk0), len# #) #)+ else (# | (# unI (fromIntegral acc), unI (offset chunk0), 0# #) #)+ where+ !len@(I# len#) = length chunk0++-- This will not inline since it is recursive, but worker+-- wrapper will still happen. When the accumulator+-- exceeds the size of a machine integer, this pushes the+-- accumulated machine int and the shift amount onto the+-- stack.+-- We are intentionally lazy in the accumulator. There is+-- no need to force this on every iteration. We do however,+-- force it preemptively every time it changes.+-- Because of how we track overflow, we are able to use the+-- same function for both positive and negative numbers.+decIntegerChunks ::+ Int -> -- Chunk accumulator (e.g. 236)+ Int -> -- Chunk base-ten bound (e.g. 1000)+ Integer -> -- Accumulator+ Bytes -> -- Chunk+ (# Integer, Int#, Int# #)+decIntegerChunks !nAcc !eAcc acc !chunk0 =+ if len > 0+ then+ let !w =+ fromIntegral @Word8 @Word+ (PM.indexByteArray (array chunk0) (offset chunk0))+ - 48+ in if w < 10+ then+ let !eAcc' = eAcc * 10+ in if eAcc' >= eAcc+ then+ decIntegerChunks+ (nAcc * 10 + fromIntegral @Word @Int w)+ eAcc'+ acc+ (Bytes.unsafeDrop 1 chunk0)+ else -- In this case, notice that we deliberately+ -- unconsume the digit that would have caused+ -- an overflow.++ let !r =+ (acc * fromIntegral @Int @Integer eAcc)+ + (fromIntegral @Int @Integer nAcc)+ in decIntegerChunks 0 1 r chunk0+ else+ let !r =+ (acc * fromIntegral @Int @Integer eAcc)+ + (fromIntegral @Int @Integer nAcc)+ in (# r, unI (offset chunk0), len# #)+ else+ let !r =+ (acc * fromIntegral @Int @Integer eAcc)+ + (fromIntegral @Int @Integer nAcc)+ in (# r, unI (offset chunk0), 0# #)+ where+ !len@(I# len#) = length chunk0++upcastIntResult :: Result# e Int# -> Result# e Int+upcastIntResult (# e | #) = (# e | #)+upcastIntResult (# | (# a, b, c #) #) = (# | (# I# a, b, c #) #)++char2Word :: Char -> Word+char2Word = fromIntegral . ord++{- | Take characters until the specified character is encountered.+Consumes the matched character as well. Fails if the character+is not present. Visually, the cursor advancement and resulting+@Bytes@ for @takeTrailedBy \'D\'@ look like this:++> A B C D E F | input+> |->->->-| | cursor+> {\-*-*-\} | result bytes+-}+takeTrailedBy :: e -> Char -> Parser e s Bytes+takeTrailedBy e !w = do+ !start <- cursor+ skipTrailedBy e w+ !end <- cursor+ !arr <- expose+ pure (Bytes arr start (end - (start + 1)))++{- | Skip all characters until the terminator is encountered+and then consume the matching character as well. Visually,+@skipTrailedBy \'C\'@ advances the cursor like this:++> A Z B Y C X C W+> |->->->->-|++This fails if it reaches the end of input without encountering+the character.+-}+skipTrailedBy :: e -> Char -> Parser e s ()+skipTrailedBy e !w = uneffectful# $ \c ->+ skipUntilConsumeLoop e w c++{- | Skip all characters until the terminator is encountered.+This does not consume the terminator. Visually, @skipUntil \'C\'@+advances the cursor like this:++> A Z B Y C X C W+> |->->->-|++This succeeds if it reaches the end of the input without+encountering the terminator. It never fails.+-}+skipUntil :: Char -> Parser e s ()+skipUntil !w = uneffectful# $ \c -> skipUntilLoop w c++skipUntilLoop ::+ Char -> -- byte to match+ Bytes -> -- Chunk+ Result# e ()+skipUntilLoop !w !c = case length c of+ 0 -> (# | (# (), unI (offset c), 0# #) #)+ _ ->+ if indexLatinCharArray (array c) (offset c) /= w+ then skipUntilLoop w (Bytes.unsafeDrop 1 c)+ else (# | (# (), unI (offset c), unI (length c) #) #)++skipUntilConsumeLoop ::+ e -> -- Error message+ Char -> -- byte to match+ Bytes -> -- Chunk+ Result# e ()+skipUntilConsumeLoop e !w !c = case length c of+ 0 -> (# e | #)+ _ ->+ if indexLatinCharArray (array c) (offset c) /= w+ then skipUntilConsumeLoop e w (Bytes.unsafeDrop 1 c)+ else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)++{- FOURMOLU_DISABLE -}+-- | Parse exactly eight ASCII-encoded characters, interpreting them as the+-- hexadecimal encoding of a 32-bit number. Note that this rejects a sequence+-- such as @BC5A9@, requiring @000BC5A9@ instead. This is insensitive to case.+hexFixedWord32 :: e -> Parser e s Word32+{-# inline hexFixedWord32 #-}+hexFixedWord32 e = Parser+ (\x s0 -> case runParser (hexFixedWord32# e) x s0 of+ (# s1, r #) -> case r of+ (# err | #) -> (# s1, (# err | #) #)+ (# | (# a, b, c #) #) -> (# s1, (# | (# W32# (+#if MIN_VERSION_base(4,16,0)+ Exts.wordToWord32#+#endif+ a), b, c #) #) #)+ )+{- FOURMOLU_ENABLE -}++hexFixedWord32# :: e -> Parser e s Word#+{-# NOINLINE hexFixedWord32# #-}+hexFixedWord32# e = uneffectfulWord# $ \chunk ->+ if length chunk >= 8+ then+ let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)+ !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)+ !w2@(W# n2) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 2)+ !w3@(W# n3) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 3)+ !w4@(W# n4) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 4)+ !w5@(W# n5) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 5)+ !w6@(W# n6) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 6)+ !w7@(W# n7) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 7)+ in if+ | w0 .|. w1 .|. w2 .|. w3 .|. w4 .|. w5 .|. w6 .|. w7 /= maxBound ->+ (#+ | (#+ (n0 `Exts.timesWord#` 268435456##)+ `Exts.plusWord#` (n1 `Exts.timesWord#` 16777216##)+ `Exts.plusWord#` (n2 `Exts.timesWord#` 1048576##)+ `Exts.plusWord#` (n3 `Exts.timesWord#` 65536##)+ `Exts.plusWord#` (n4 `Exts.timesWord#` 4096##)+ `Exts.plusWord#` (n5 `Exts.timesWord#` 256##)+ `Exts.plusWord#` (n6 `Exts.timesWord#` 16##)+ `Exts.plusWord#` n7+ , unI (offset chunk) +# 8#+ , unI (length chunk) -# 8#+ #)+ #)+ | otherwise -> (# e | #)+ else (# e | #)++{- FOURMOLU_DISABLE -}+-- | Parse exactly 16 ASCII-encoded characters, interpreting them as the+-- hexadecimal encoding of a 64-bit number. Note that this rejects a sequence+-- such as @BC5A9@, requiring @00000000000BC5A9@ instead. This is insensitive+-- to case.+hexFixedWord64 :: e -> Parser e s Word64+{-# inline hexFixedWord64 #-}+hexFixedWord64 e = Parser+ (\x s0 -> case runParser (hexFixedWord64# e) x s0 of+ (# s1, r #) -> case r of+ (# err | #) -> (# s1, (# err | #) #)+ (# | (# a, b, c #) #) -> (# s1, (# | (# W64# (+#if MIN_VERSION_base(4,17,0)+ Exts.wordToWord64# a+#else++ a+#endif+ ), b, c #) #) #)+ )+{- FOURMOLU_ENABLE -}++hexFixedWord128 :: e -> Parser e s Word128+hexFixedWord128 e =+ Word128+ <$> hexFixedWord64 e+ <*> hexFixedWord64 e++hexFixedWord256 :: e -> Parser e s Word256+hexFixedWord256 e =+ Word256+ <$> hexFixedWord64 e+ <*> hexFixedWord64 e+ <*> hexFixedWord64 e+ <*> hexFixedWord64 e++hexFixedWord64# :: e -> Parser e s Word#+{-# NOINLINE hexFixedWord64# #-}+hexFixedWord64# e = uneffectfulWord# $ \chunk ->+ if length chunk >= 16+ then+ let go !off !len !acc = case len of+ 0 -> case acc of+ W# r ->+ (#+ | (#+ r+ , unI off+ , unI (length chunk) -# 16#+ #)+ #)+ _ -> case oneHexMaybe (PM.indexByteArray (array chunk) off) of+ Nothing -> (# e | #)+ Just w -> go (off + 1) (len - 1) ((acc * 16) + w)+ in go (offset chunk) (16 :: Int) (0 :: Word)+ else (# e | #)++{- FOURMOLU_DISABLE -}+-- | Parse exactly four ASCII-encoded characters, interpreting+-- them as the hexadecimal encoding of a 16-bit number. Note that+-- this rejects a sequence such as @5A9@, requiring @05A9@ instead.+-- This is insensitive to case. This is particularly useful when+-- parsing escape sequences in C or JSON, which allow encoding+-- characters in the Basic Multilingual Plane as @\\uhhhh@.+hexFixedWord16 :: e -> Parser e s Word16+{-# inline hexFixedWord16 #-}+hexFixedWord16 e = Parser+ (\x s0 -> case runParser (hexFixedWord16# e) x s0 of+ (# s1, r #) -> case r of+ (# err | #) -> (# s1, (# err | #) #)+ (# | (# a, b, c #) #) -> (# s1, (# | (# W16# (+#if MIN_VERSION_base(4,16,0)+ Exts.wordToWord16#+#endif+ a), b, c #) #) #)+ )++hexFixedWord16# :: e -> Parser e s Word#+{-# noinline hexFixedWord16# #-}+hexFixedWord16# e = uneffectfulWord# $ \chunk -> if length chunk >= 4+ then+ let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)+ !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)+ !w2@(W# n2) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 2)+ !w3@(W# n3) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 3)+ in if | w0 .|. w1 .|. w2 .|. w3 /= maxBound ->+ (# |+ (# (n0 `Exts.timesWord#` 4096##) `Exts.plusWord#`+ (n1 `Exts.timesWord#` 256##) `Exts.plusWord#`+ (n2 `Exts.timesWord#` 16##) `Exts.plusWord#`+ n3+ , unI (offset chunk) +# 4#+ , unI (length chunk) -# 4# #) #)+ | otherwise -> (# e | #)+ else (# e | #)++-- | Parse exactly two ASCII-encoded characters, interpretting+-- them as the hexadecimal encoding of a 8-bit number. Note that+-- this rejects a sequence such as @A@, requiring @0A@ instead.+-- This is insensitive to case.+hexFixedWord8 :: e -> Parser e s Word8+{-# inline hexFixedWord8 #-}+hexFixedWord8 e = Parser+ (\x s0 -> case runParser (hexFixedWord8# e) x s0 of+ (# s1, r #) -> case r of+ (# err | #) -> (# s1, (# err | #) #)+ (# | (# a, b, c #) #) -> (# s1, (# | (# W8# (+#if MIN_VERSION_base(4,16,0)+ Exts.wordToWord8#+#endif+ a), b, c #) #) #)+ )+{- FOURMOLU_ENABLE -}++hexFixedWord8# :: e -> Parser e s Word#+{-# NOINLINE hexFixedWord8# #-}+hexFixedWord8# e = uneffectfulWord# $ \chunk ->+ if length chunk >= 2+ then+ let !w0@(W# n0) = oneHex $ PM.indexByteArray (array chunk) (offset chunk)+ !w1@(W# n1) = oneHex $ PM.indexByteArray (array chunk) (offset chunk + 1)+ in if+ | w0 .|. w1 /= maxBound ->+ (#+ | (#+ (n0 `Exts.timesWord#` 16##)+ `Exts.plusWord#` n1+ , unI (offset chunk) +# 2#+ , unI (length chunk) -# 2#+ #)+ #)+ | otherwise -> (# e | #)+ else (# e | #)++{- | Consume a single character that is the lowercase hexadecimal+encoding of a 4-bit word. Fails if the character is not in the class+@[a-f0-9]@.+-}+hexNibbleLower :: e -> Parser e s Word+hexNibbleLower e = uneffectful $ \chunk -> case length chunk of+ 0 -> Failure e+ _ ->+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in if+ | w >= 48 && w < 58 -> Success (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)+ | w >= 97 && w < 103 -> Success (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)+ | otherwise -> Failure e++{- | Consume a single character that is the case-insensitive hexadecimal+encoding of a 4-bit word. Fails if the character is not in the class+@[a-fA-F0-9]@.+-}+hexNibble :: e -> Parser e s Word+hexNibble e = uneffectful $ \chunk -> case length chunk of+ 0 -> Failure e+ _ ->+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in if+ | w >= 48 && w < 58 -> Success (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)+ | w >= 65 && w < 71 -> Success (fromIntegral w - 55) (offset chunk + 1) (length chunk - 1)+ | w >= 97 && w < 103 -> Success (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)+ | otherwise -> Failure e++{- | Consume a single character that is the lowercase hexadecimal+encoding of a 4-bit word. Returns @Nothing@ without consuming+the character if it is not in the class @[a-f0-9]@. The parser+never fails.+-}+tryHexNibbleLower :: Parser e s (Maybe Word)+tryHexNibbleLower = unfailing $ \chunk -> case length chunk of+ 0 -> InternalStep Nothing (offset chunk) (length chunk)+ _ ->+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in if+ | w >= 48 && w < 58 -> InternalStep (Just (fromIntegral w - 48)) (offset chunk + 1) (length chunk - 1)+ | w >= 97 && w < 103 -> InternalStep (Just (fromIntegral w - 87)) (offset chunk + 1) (length chunk - 1)+ | otherwise -> InternalStep Nothing (offset chunk) (length chunk)++{- | Consume a single character that is the case-insensitive hexadecimal+encoding of a 4-bit word. Returns @Nothing@ without consuming+the character if it is not in the class @[a-fA-F0-9]@. This parser+never fails.+-}+tryHexNibble :: Parser e s (Maybe Word)+tryHexNibble = unfailing $ \chunk -> case length chunk of+ 0 -> InternalStep Nothing (offset chunk) (length chunk)+ _ ->+ let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ in if+ | w >= 48 && w < 58 -> InternalStep (Just (fromIntegral w - 48)) (offset chunk + 1) (length chunk - 1)+ | w >= 65 && w < 71 -> InternalStep (Just (fromIntegral w - 55)) (offset chunk + 1) (length chunk - 1)+ | w >= 97 && w < 103 -> InternalStep (Just (fromIntegral w - 87)) (offset chunk + 1) (length chunk - 1)+ | otherwise -> InternalStep Nothing (offset chunk) (length chunk)++-- Returns the maximum machine word if the argument is not+-- the ASCII encoding of a hexadecimal digit.+oneHex :: Word8 -> Word+{-# INLINE oneHex #-}+oneHex w+ | w >= 48 && w < 58 = (fromIntegral w - 48)+ | w >= 65 && w < 71 = (fromIntegral w - 55)+ | w >= 97 && w < 103 = (fromIntegral w - 87)+ | otherwise = maxBound++oneHexMaybe :: Word8 -> Maybe Word+{-# INLINE oneHexMaybe #-}+oneHexMaybe w+ | w >= 48 && w < 58 = Just (fromIntegral w - 48)+ | w >= 65 && w < 71 = Just (fromIntegral w - 55)+ | w >= 97 && w < 103 = Just (fromIntegral w - 87)+ | otherwise = Nothing++uneffectfulWord# :: (Bytes -> Result# e Word#) -> Parser e s Word#+{-# INLINE uneffectfulWord# #-}+uneffectfulWord# f =+ Parser+ (\b s0 -> (# s0, (f (boxBytes b)) #))++-- Precondition: the arguments are non-negative. Boolean is+-- true when overflow happens. Performs: a * 10 + b+-- Postcondition: when overflow is false, the resulting+-- word is less than or equal to the upper bound+positivePushBase10 :: Word -> Word -> Word -> (Bool, Word)+{-# INLINE positivePushBase10 #-}+positivePushBase10 (W# a) (W# b) (W# upper) =+ let !(# ca, r0 #) = Exts.timesWord2# a 10##+ !r1 = Exts.plusWord# r0 b+ !cb = int2Word# (gtWord# r1 upper)+ !cc = int2Word# (ltWord# r1 0##)+ !c = ca `or#` cb `or#` cc+ in (case c of 0## -> False; _ -> True, W# r1)++unsignedPushBase10 :: Word -> Word -> (Bool, Word)+{-# INLINE unsignedPushBase10 #-}+unsignedPushBase10 (W# a) (W# b) =+ let !(# ca, r0 #) = Exts.timesWord2# a 10##+ !r1 = Exts.plusWord# r0 b+ !cb = int2Word# (ltWord# r1 r0)+ !c = ca `or#` cb+ in (case c of 0## -> False; _ -> True, W# r1)++-- | Skip while the predicate is matched. This is always inlined.+skipWhile :: (Char -> Bool) -> Parser e s ()+{-# INLINE skipWhile #-}+skipWhile f = go+ where+ go =+ isEndOfInput >>= \case+ True -> pure ()+ False -> do+ w <- anyUnsafe+ if f w+ then go+ else unconsume 1++-- Interpret the next byte as an Latin1-encoded character.+-- Does not check to see if any characters are left. This+-- is not exported.+anyUnsafe :: Parser e s Char+{-# INLINE anyUnsafe #-}+anyUnsafe = uneffectful $ \chunk ->+ let w = indexCharArray (array chunk) (offset chunk) :: Char+ in Success w (offset chunk + 1) (length chunk - 1)++-- Reads one byte and interprets it as Latin1-encoded character.+indexCharArray :: PM.ByteArray -> Int -> Char+{-# INLINE indexCharArray #-}+indexCharArray (PM.ByteArray x) (I# i) = C# (indexCharArray# x i)++{- | Match any character, to perform lookahead. Returns 'Nothing' if+ end of input has been reached. Does not consume any input.++ /Note/: Because this parser does not fail, do not use it+ with combinators such as 'many', because such as 'many',+ because such parsers loop until a failure occurs. Careless+ use will thus result in an infinite loop.+-}+peek :: Parser e s (Maybe Char)+{-# INLINE peek #-}+peek = uneffectful $ \(Bytes arr off len) ->+ let v =+ if len > 0+ then Just (indexCharArray arr off)+ else Nothing+ in Success v off len++{- | Match any byte, to perform lookahead. Does not consume any+ input, but will fail if end of input has been reached.+-}+peek' :: e -> Parser e s Char+{-# INLINE peek' #-}+peek' e = uneffectful $ \(Bytes arr off len) ->+ if len > 0+ then Success (indexCharArray arr off) off len+ else Failure e
src/Data/Bytes/Parser/Leb128.hs view
@@ -1,66 +1,73 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language TypeApplications #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE TypeApplications #-} --- | Parse numbers that have been encoded with <https://en.wikipedia.org/wiki/LEB128 LEB-128>.--- LEB-128 allows arbitrarily large numbers to be encoded. Parsers in this--- module will fail if the number they attempt to parse is outside the--- range of what their target type can handle. The parsers for signed--- numbers assume that the numbers have been--- <https://developers.google.com/protocol-buffers/docs/encoding zigzig encoded>.+{- | Parse numbers that have been encoded with <https://en.wikipedia.org/wiki/LEB128 LEB-128>.+LEB-128 allows arbitrarily large numbers to be encoded. Parsers in this+module will fail if the number they attempt to parse is outside the+range of what their target type can handle. The parsers for signed+numbers assume that the numbers have been+<https://developers.google.com/protocol-buffers/docs/encoding zigzig encoded>.+-} module Data.Bytes.Parser.Leb128 ( -- * Unsigned word16 , word32 , word64+ -- * Signed (Zig-zag) , int16 , int32 , int64 ) where -import Data.Bits (testBit,(.&.),unsafeShiftR,xor,complement)-import Data.Bits (unsafeShiftL,(.|.))+import Data.Bits (complement, testBit, unsafeShiftL, unsafeShiftR, xor, (.&.), (.|.)) import Data.Bytes.Parser (Parser)-import Data.Int (Int16,Int32,Int64)-import Data.Word (Word8,Word16,Word32,Word64)+import Data.Int (Int16, Int32, Int64)+import Data.Word (Word16, Word32, Word64, Word8) import qualified Data.Bytes.Parser as P --- | Parse a LEB-128-encoded number. If the number is larger--- than @0xFFFF@, fails with the provided error.+{- | Parse a LEB-128-encoded number. If the number is larger+than @0xFFFF@, fails with the provided error.+-} word16 :: e -> Parser e s Word16 word16 e = do w <- stepBoundedWord e 16 0 0 pure (fromIntegral @Word64 @Word16 w) --- | Parse a LEB-128-encoded number. If the number is larger--- than @0xFFFFFFFF@, fails with the provided error.+{- | Parse a LEB-128-encoded number. If the number is larger+than @0xFFFFFFFF@, fails with the provided error.+-} word32 :: e -> Parser e s Word32 word32 e = do w <- stepBoundedWord e 32 0 0 pure (fromIntegral @Word64 @Word32 w) --- | Parse a LEB-128-encoded number. If the number is larger--- than @0xFFFFFFFFFFFFFFFF@, fails with the provided error.+{- | Parse a LEB-128-encoded number. If the number is larger+than @0xFFFFFFFFFFFFFFFF@, fails with the provided error.+-} word64 :: e -> Parser e s Word64 word64 e = stepBoundedWord e 64 0 0 --- | Parse a LEB-128-zigzag-encoded signed number. If the encoded--- number is outside the range @[-32768,32767]@, this fails with--- the provided error.+{- | Parse a LEB-128-zigzag-encoded signed number. If the encoded+number is outside the range @[-32768,32767]@, this fails with+the provided error.+-} int16 :: e -> Parser e s Int16 int16 = fmap zigzagDecode16 . word16 --- | Parse a LEB-128-zigzag-encoded signed number. If the encoded--- number is outside the range @[-2147483648,2147483647]@, this--- fails with the provided error.+{- | Parse a LEB-128-zigzag-encoded signed number. If the encoded+number is outside the range @[-2147483648,2147483647]@, this+fails with the provided error.+-} int32 :: e -> Parser e s Int32 int32 = fmap zigzagDecode32 . word32 --- | Parse a LEB-128-zigzag-encoded signed number. If the encoded--- number is outside the range @[-9223372036854775808,9223372036854775807]@,--- this fails with the provided error.+{- | Parse a LEB-128-zigzag-encoded signed number. If the encoded+number is outside the range @[-9223372036854775808,9223372036854775807]@,+this fails with the provided error.+-} int64 :: e -> Parser e s Int64 int64 = fmap zigzagDecode64 . word64 @@ -72,16 +79,19 @@ stepBoundedWord e !bitLimit !acc0 !accShift = do raw <- P.any e let number = raw .&. 0x7F- acc1 = acc0 .|.- unsafeShiftL (fromIntegral @Word8 @Word64 number) accShift+ acc1 =+ acc0+ .|. unsafeShiftL (fromIntegral @Word8 @Word64 number) accShift accShift' = accShift + 7 if accShift' <= bitLimit- then if testBit raw 7- then stepBoundedWord e bitLimit acc1 accShift'- else pure acc1- else if fromIntegral @Word8 @Word raw < twoExp (bitLimit - accShift)- then pure acc1 -- TODO: no need to mask upper bit in number- else P.fail e+ then+ if testBit raw 7+ then stepBoundedWord e bitLimit acc1 accShift'+ else pure acc1+ else+ if fromIntegral @Word8 @Word raw < twoExp (bitLimit - accShift)+ then pure acc1 -- TODO: no need to mask upper bit in number+ else P.fail e twoExp :: Int -> Word twoExp x = unsafeShiftL 1 x
src/Data/Bytes/Parser/LittleEndian.hs view
@@ -1,24 +1,19 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedSums #-} -- | Little-endian fixed-width numbers. module Data.Bytes.Parser.LittleEndian ( -- * One+ -- ** Unsigned word8 , word16@@ -26,36 +21,40 @@ , word64 , word128 , word256+ -- ** Signed , int8 , int16 , int32 , int64+ -- * Many+ -- ** Unsigned , word16Array , word32Array , word64Array , word128Array , word256Array+ -- ** Unsigned , int64Array ) where -import Prelude hiding (length,any,fail,takeWhile)+import Prelude hiding (any, fail, length, takeWhile) +#if MIN_VERSION_base(4,18,0)+#else import Control.Applicative (liftA2)-import Data.Bits ((.|.),unsafeShiftL)-import Data.Primitive (ByteArray(..),PrimArray(..))-import Data.Bytes.Types (Bytes(..))-import Data.Bytes.Parser.Internal (Parser,uneffectful)-import Data.Bytes.Parser.Internal (Result(..))-import Data.Bytes.Parser.Internal (swapArray16,swapArray32)-import Data.Bytes.Parser.Internal (swapArray64,swapArray128,swapArray256)-import Data.Word (Word8,Word16,Word32,Word64)-import Data.Int (Int8,Int16,Int32,Int64)-import Data.WideWord (Word128(Word128),Word256(Word256))-import GHC.ByteOrder (ByteOrder(LittleEndian,BigEndian),targetByteOrder)+#endif+import Data.Bits (unsafeShiftL, (.|.))+import Data.Bytes.Parser.Internal (Parser, Result (..), swapArray128, swapArray16, swapArray256, swapArray32, swapArray64, uneffectful)+import Data.Bytes.Types (Bytes (..))+import Data.Int (Int16, Int32, Int64, Int8)+import Data.Primitive (ByteArray (..), PrimArray (..))+import Data.WideWord (Word128 (Word128), Word256 (Word256))+import Data.Word (Word16, Word32, Word64, Word8)+import GHC.ByteOrder (ByteOrder (BigEndian, LittleEndian), targetByteOrder) import qualified Data.Bytes as Bytes import qualified Data.Bytes.Parser as P@@ -65,14 +64,18 @@ word8 :: e -> Parser e s Word8 word8 = P.any --- | Array of little-endian unsigned 16-bit words. If the host is--- little-endian, the implementation is optimized to simply @memcpy@--- bytes into the result array. The result array always has elements--- in native-endian byte order.+{- | Array of little-endian unsigned 16-bit words. If the host is+little-endian, the implementation is optimized to simply @memcpy@+bytes into the result array. The result array always has elements+in native-endian byte order.+-} word16Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of little-endian 16-bit words to expect- -> Parser e s (PrimArray Word16) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of little-endian 16-bit words to expect+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word16) word16Array e !n = case targetByteOrder of LittleEndian -> fmap (asWord16s . Bytes.toByteArrayClone) (P.take e (n * 2)) BigEndian -> do@@ -82,9 +85,12 @@ -- | Parse an array of little-endian unsigned 32-bit words. word32Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of little-endian 32-bit words to consume- -> Parser e s (PrimArray Word32) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of little-endian 32-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word32) word32Array e !n = case targetByteOrder of LittleEndian -> fmap (asWord32s . Bytes.toByteArrayClone) (P.take e (n * 4)) BigEndian -> do@@ -94,9 +100,12 @@ -- | Parse an array of little-endian unsigned 64-bit words. word64Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of little-endian 64-bit words to consume- -> Parser e s (PrimArray Word64) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of little-endian 64-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word64) word64Array e !n = case targetByteOrder of LittleEndian -> fmap (asWord64s . Bytes.toByteArrayClone) (P.take e (n * 8)) BigEndian -> do@@ -106,9 +115,12 @@ -- | Parse an array of little-endian unsigned 128-bit words. word128Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of little-endian 128-bit words to consume- -> Parser e s (PrimArray Word128) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of little-endian 128-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word128) word128Array e !n = case targetByteOrder of LittleEndian -> fmap (asWord128s . Bytes.toByteArrayClone) (P.take e (n * 16)) BigEndian -> do@@ -118,9 +130,12 @@ -- | Parse an array of little-endian unsigned 256-bit words. word256Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of little-endian 256-bit words to consume- -> Parser e s (PrimArray Word256) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of little-endian 256-bit words to consume+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Word256) word256Array e !n = case targetByteOrder of LittleEndian -> fmap (asWord256s . Bytes.toByteArrayClone) (P.take e (n * 32)) BigEndian -> do@@ -130,9 +145,12 @@ -- | Parse an array of little-endian signed 64-bit words. int64Array ::- e -- ^ Error message if not enough bytes are present- -> Int -- ^ Number of little-endian 64-bit words to expect- -> Parser e s (PrimArray Int64) -- ^ Native-endian elements+ -- | Error message if not enough bytes are present+ e ->+ -- | Number of little-endian 64-bit words to expect+ Int ->+ -- | Native-endian elements+ Parser e s (PrimArray Int64) int64Array e !n = do PrimArray x <- word64Array e n pure (PrimArray x)@@ -154,58 +172,64 @@ -- | Unsigned 16-bit word. word16 :: e -> Parser e s Word16-word16 e = uneffectful $ \chunk -> if length chunk >= 2- then- let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8- wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8- in Success- (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wb) 8 .|. fromIntegral wa))- (offset chunk + 2) (length chunk - 2)- else Failure e+word16 e = uneffectful $ \chunk ->+ if length chunk >= 2+ then+ let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8+ in Success+ (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wb) 8 .|. fromIntegral wa))+ (offset chunk + 2)+ (length chunk - 2)+ else Failure e -- | Unsigned 32-bit word. word32 :: e -> Parser e s Word32-word32 e = uneffectful $ \chunk -> if length chunk >= 4- then- let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8- wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8- wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8- wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8- in Success- (fromIntegral @Word @Word32- ( unsafeShiftL (fromIntegral wd) 24 .|.- unsafeShiftL (fromIntegral wc) 16 .|.- unsafeShiftL (fromIntegral wb) 8 .|.- fromIntegral wa+word32 e = uneffectful $ \chunk ->+ if length chunk >= 4+ then+ let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8+ wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8+ wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8+ in Success+ ( fromIntegral @Word @Word32+ ( unsafeShiftL (fromIntegral wd) 24+ .|. unsafeShiftL (fromIntegral wc) 16+ .|. unsafeShiftL (fromIntegral wb) 8+ .|. fromIntegral wa+ ) )- )- (offset chunk + 4) (length chunk - 4)- else Failure e+ (offset chunk + 4)+ (length chunk - 4)+ else Failure e -- | Unsigned 64-bit word. word64 :: e -> Parser e s Word64-word64 e = uneffectful $ \chunk -> if length chunk >= 8- then- let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8- wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8- wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8- wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8- we = PM.indexByteArray (array chunk) (offset chunk + 4) :: Word8- wf = PM.indexByteArray (array chunk) (offset chunk + 5) :: Word8- wg = PM.indexByteArray (array chunk) (offset chunk + 6) :: Word8- wh = PM.indexByteArray (array chunk) (offset chunk + 7) :: Word8- in Success- ( unsafeShiftL (fromIntegral wh) 56 .|.- unsafeShiftL (fromIntegral wg) 48 .|.- unsafeShiftL (fromIntegral wf) 40 .|.- unsafeShiftL (fromIntegral we) 32 .|.- unsafeShiftL (fromIntegral wd) 24 .|.- unsafeShiftL (fromIntegral wc) 16 .|.- unsafeShiftL (fromIntegral wb) 8 .|.- fromIntegral wa- )- (offset chunk + 8) (length chunk - 8)- else Failure e+word64 e = uneffectful $ \chunk ->+ if length chunk >= 8+ then+ let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8+ wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8+ wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8+ wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8+ we = PM.indexByteArray (array chunk) (offset chunk + 4) :: Word8+ wf = PM.indexByteArray (array chunk) (offset chunk + 5) :: Word8+ wg = PM.indexByteArray (array chunk) (offset chunk + 6) :: Word8+ wh = PM.indexByteArray (array chunk) (offset chunk + 7) :: Word8+ in Success+ ( unsafeShiftL (fromIntegral wh) 56+ .|. unsafeShiftL (fromIntegral wg) 48+ .|. unsafeShiftL (fromIntegral wf) 40+ .|. unsafeShiftL (fromIntegral we) 32+ .|. unsafeShiftL (fromIntegral wd) 24+ .|. unsafeShiftL (fromIntegral wc) 16+ .|. unsafeShiftL (fromIntegral wb) 8+ .|. fromIntegral wa+ )+ (offset chunk + 8)+ (length chunk - 8)+ else Failure e -- | Unsigned 256-bit word. word256 :: e -> Parser e s Word256
src/Data/Bytes/Parser/Rebindable.hs view
@@ -1,31 +1,30 @@-{-# language CPP #-}-{-# language DataKinds #-}-{-# language FlexibleInstances #-}-{-# language MagicHash #-}-{-# language MultiParamTypeClasses #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnboxedTuples #-} --- | Provides levity-polymorphic variants of @>>=@, @>>@, and @pure@--- used to assemble parsers whose result types are unlifted. This--- cannot be used with the @RebindableSyntax@ extension because that--- extension disallows representations other than @LiftedRep@. Consequently,--- users of this module must manually desugar do notation. See the--- @url-bytes@ library for an example of this module in action.------ Only resort to the functions in this module after checking that--- GHC is unable to optimize away @I#@ and friends in your code.+{- | Provides levity-polymorphic variants of @>>=@, @>>@, and @pure@+used to assemble parsers whose result types are unlifted. This+cannot be used with the @RebindableSyntax@ extension because that+extension disallows representations other than @LiftedRep@. Consequently,+users of this module must manually desugar do notation. See the+@url-bytes@ library for an example of this module in action.++Only resort to the functions in this module after checking that+GHC is unable to optimize away @I#@ and friends in your code.+-} module Data.Bytes.Parser.Rebindable- ( Bind(..)- , Pure(..)+ ( Bind (..)+ , Pure (..) ) where +import Data.Bytes.Parser.Internal (Parser (..))+import GHC.Exts (RuntimeRep (..), TYPE) import Prelude ()-import GHC.Exts (TYPE,RuntimeRep(..))-import Data.Bytes.Parser.Internal (Parser(..)) #if MIN_VERSION_base(4,16,0) import GHC.Exts (LiftedRep)@@ -34,411 +33,506 @@ #endif class Bind (ra :: RuntimeRep) (rb :: RuntimeRep) where- (>>=) :: forall e s (a :: TYPE ra) (b :: TYPE rb).- Parser e s a -> (a -> Parser e s b) -> Parser e s b- (>>) :: forall e s (a :: TYPE ra) (b :: TYPE rb).- Parser e s a -> Parser e s b -> Parser e s b+ (>>=) ::+ forall e s (a :: TYPE ra) (b :: TYPE rb).+ Parser e s a ->+ (a -> Parser e s b) ->+ Parser e s b+ (>>) ::+ forall e s (a :: TYPE ra) (b :: TYPE rb).+ Parser e s a ->+ Parser e s b ->+ Parser e s b class Pure (ra :: RuntimeRep) where pure :: forall e s (a :: TYPE ra). a -> Parser e s a pureParser :: a -> Parser e s a-{-# inline pureParser #-}-pureParser a = Parser- (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+{-# INLINE pureParser #-}+pureParser a =+ Parser+ (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #)) bindParser :: Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindParser #-}-bindParser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+{-# INLINE bindParser #-}+bindParser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) sequenceParser :: Parser e s a -> Parser e s b -> Parser e s b-{-# inline sequenceParser #-}-sequenceParser (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+{-# INLINE sequenceParser #-}+sequenceParser (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) -pureIntParser :: forall (a :: TYPE 'IntRep) e s.- a -> Parser e s a-{-# inline pureIntParser #-}-pureIntParser a = Parser- (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+pureIntParser ::+ forall (a :: TYPE 'IntRep) e s.+ a ->+ Parser e s a+{-# INLINE pureIntParser #-}+pureIntParser a =+ Parser+ (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #)) -bindIntParser :: forall (a :: TYPE 'IntRep) e s b.- Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindIntParser #-}-bindIntParser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+bindIntParser ::+ forall (a :: TYPE 'IntRep) e s b.+ Parser e s a ->+ (a -> Parser e s b) ->+ Parser e s b+{-# INLINE bindIntParser #-}+bindIntParser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) -bindWordParser :: forall (a :: TYPE 'WordRep) e s b.- Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindWordParser #-}-bindWordParser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+bindWordParser ::+ forall (a :: TYPE 'WordRep) e s b.+ Parser e s a ->+ (a -> Parser e s b) ->+ Parser e s b+{-# INLINE bindWordParser #-}+bindWordParser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) -sequenceIntParser :: forall (a :: TYPE 'IntRep) e s b.- Parser e s a -> Parser e s b -> Parser e s b-{-# inline sequenceIntParser #-}-sequenceIntParser (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+sequenceIntParser ::+ forall (a :: TYPE 'IntRep) e s b.+ Parser e s a ->+ Parser e s b ->+ Parser e s b+{-# INLINE sequenceIntParser #-}+sequenceIntParser (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) -sequenceWordParser :: forall (a :: TYPE 'WordRep) e s b.- Parser e s a -> Parser e s b -> Parser e s b-{-# inline sequenceWordParser #-}-sequenceWordParser (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+sequenceWordParser ::+ forall (a :: TYPE 'WordRep) e s b.+ Parser e s a ->+ Parser e s b ->+ Parser e s b+{-# INLINE sequenceWordParser #-}+sequenceWordParser (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) -pureIntPairParser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s.- a -> Parser e s a-{-# inline pureIntPairParser #-}-pureIntPairParser a = Parser- (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+pureIntPairParser ::+ forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s.+ a ->+ Parser e s a+{-# INLINE pureIntPairParser #-}+pureIntPairParser a =+ Parser+ (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #)) -bindIntPairParser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s b.- Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindIntPairParser #-}-bindIntPairParser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+bindIntPairParser ::+ forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s b.+ Parser e s a ->+ (a -> Parser e s b) ->+ Parser e s b+{-# INLINE bindIntPairParser #-}+bindIntPairParser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) -pureInt5Parser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])) e s.- a -> Parser e s a-{-# inline pureInt5Parser #-}-pureInt5Parser a = Parser- (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+pureInt5Parser ::+ forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])) e s.+ a ->+ Parser e s a+{-# INLINE pureInt5Parser #-}+pureInt5Parser a =+ Parser+ (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #)) -bindInt5Parser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])) e s b.- Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindInt5Parser #-}-bindInt5Parser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+bindInt5Parser ::+ forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])) e s b.+ Parser e s a ->+ (a -> Parser e s b) ->+ Parser e s b+{-# INLINE bindInt5Parser #-}+bindInt5Parser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) -sequenceInt5Parser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])) e s b.- Parser e s a -> Parser e s b -> Parser e s b-{-# inline sequenceInt5Parser #-}-sequenceInt5Parser (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+sequenceInt5Parser ::+ forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])) e s b.+ Parser e s a ->+ Parser e s b ->+ Parser e s b+{-# INLINE sequenceInt5Parser #-}+sequenceInt5Parser (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) -sequenceIntPairParser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s b.- Parser e s a -> Parser e s b -> Parser e s b-{-# inline sequenceIntPairParser #-}-sequenceIntPairParser (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+sequenceIntPairParser ::+ forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s b.+ Parser e s a ->+ Parser e s b ->+ Parser e s b+{-# INLINE sequenceIntPairParser #-}+sequenceIntPairParser (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) -bindInt2to5Parser :: forall- (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep]))- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]))- e s.- Parser e s a -> (a -> Parser e s b) -> Parser e s b-{-# inline bindInt2to5Parser #-}-bindInt2to5Parser (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+bindInt2to5Parser ::+ forall+ (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep]))+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]))+ e+ s.+ Parser e s a ->+ (a -> Parser e s b) ->+ Parser e s b+{-# INLINE bindInt2to5Parser #-}+bindInt2to5Parser (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) -sequenceInt2to5Parser :: forall- (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep]))- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]))- e s.- Parser e s a -> Parser e s b -> Parser e s b-{-# inline sequenceInt2to5Parser #-}-sequenceInt2to5Parser (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+sequenceInt2to5Parser ::+ forall+ (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep]))+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]))+ e+ s.+ Parser e s a ->+ Parser e s b ->+ Parser e s b+{-# INLINE sequenceInt2to5Parser #-}+sequenceInt2to5Parser (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) instance Bind LiftedRep LiftedRep where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindParser (>>) = sequenceParser instance Bind 'WordRep LiftedRep where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindWordParser (>>) = sequenceWordParser instance Bind 'IntRep LiftedRep where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindIntParser (>>) = sequenceIntParser instance Bind ('TupleRep '[ 'IntRep, 'IntRep]) LiftedRep where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindIntPairParser (>>) = sequenceIntPairParser --instance Bind ('TupleRep '[ 'IntRep, 'IntRep])- ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])+instance+ Bind+ ('TupleRep '[ 'IntRep, 'IntRep])+ ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindInt2to5Parser (>>) = sequenceInt2to5Parser -instance Bind ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])- LiftedRep+instance+ Bind+ ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])+ LiftedRep where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindInt5Parser (>>) = sequenceInt5Parser --instance Bind 'IntRep- ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])+instance+ Bind+ 'IntRep+ ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindFromIntToInt5 (>>) = sequenceIntToInt5 instance Bind LiftedRep ('TupleRep '[ 'IntRep, 'IntRep]) where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindFromLiftedToIntPair (>>) = sequenceLiftedToIntPair -instance Bind LiftedRep- ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])+instance+ Bind+ LiftedRep+ ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindFromLiftedToInt5 (>>) = sequenceLiftedToInt5 instance Bind 'IntRep ('TupleRep '[ 'IntRep, 'IntRep]) where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindFromIntToIntPair (>>) = sequenceIntToIntPair instance Bind LiftedRep 'IntRep where- {-# inline (>>=) #-}- {-# inline (>>) #-}+ {-# INLINE (>>=) #-}+ {-# INLINE (>>) #-} (>>=) = bindFromLiftedToInt (>>) = sequenceLiftedToInt instance Pure LiftedRep where- {-# inline pure #-}+ {-# INLINE pure #-} pure = pureParser instance Pure 'IntRep where- {-# inline pure #-}+ {-# INLINE pure #-} pure = pureIntParser instance Pure ('TupleRep '[ 'IntRep, 'IntRep]) where- {-# inline pure #-}+ {-# INLINE pure #-} pure = pureIntPairParser instance Pure ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep]) where- {-# inline pure #-}+ {-# INLINE pure #-} pure = pureInt5Parser bindFromIntToIntPair ::- forall s e- (a :: TYPE 'IntRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep ])).- Parser s e a- -> (a -> Parser s e b)- -> Parser s e b-{-# inline bindFromIntToIntPair #-}-bindFromIntToIntPair (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE 'IntRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])).+ Parser s e a ->+ (a -> Parser s e b) ->+ Parser s e b+{-# INLINE bindFromIntToIntPair #-}+bindFromIntToIntPair (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) sequenceIntToIntPair ::- forall s e- (a :: TYPE 'IntRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep ])).- Parser s e a- -> Parser s e b- -> Parser s e b-{-# inline sequenceIntToIntPair #-}-sequenceIntToIntPair (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE 'IntRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])).+ Parser s e a ->+ Parser s e b ->+ Parser s e b+{-# INLINE sequenceIntToIntPair #-}+sequenceIntToIntPair (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) bindFromIntToInt5 ::- forall s e- (a :: TYPE 'IntRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep ])).- Parser s e a- -> (a -> Parser s e b)- -> Parser s e b-{-# inline bindFromIntToInt5 #-}-bindFromIntToInt5 (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE 'IntRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])).+ Parser s e a ->+ (a -> Parser s e b) ->+ Parser s e b+{-# INLINE bindFromIntToInt5 #-}+bindFromIntToInt5 (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) sequenceIntToInt5 ::- forall s e- (a :: TYPE 'IntRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep ])).- Parser s e a- -> Parser s e b- -> Parser s e b-{-# inline sequenceIntToInt5 #-}-sequenceIntToInt5 (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE 'IntRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])).+ Parser s e a ->+ Parser s e b ->+ Parser s e b+{-# INLINE sequenceIntToInt5 #-}+sequenceIntToInt5 (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) bindFromLiftedToIntPair ::- forall s e- (a :: TYPE LiftedRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep ])).- Parser s e a- -> (a -> Parser s e b)- -> Parser s e b-{-# inline bindFromLiftedToIntPair #-}-bindFromLiftedToIntPair (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE LiftedRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])).+ Parser s e a ->+ (a -> Parser s e b) ->+ Parser s e b+{-# INLINE bindFromLiftedToIntPair #-}+bindFromLiftedToIntPair (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) sequenceLiftedToIntPair ::- forall s e- (a :: TYPE LiftedRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep ])).- Parser s e a- -> Parser s e b- -> Parser s e b-{-# inline sequenceLiftedToIntPair #-}-sequenceLiftedToIntPair (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )-+ forall+ s+ e+ (a :: TYPE LiftedRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])).+ Parser s e a ->+ Parser s e b ->+ Parser s e b+{-# INLINE sequenceLiftedToIntPair #-}+sequenceLiftedToIntPair (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) bindFromLiftedToInt5 ::- forall s e- (a :: TYPE LiftedRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])).- Parser s e a- -> (a -> Parser s e b)- -> Parser s e b-{-# inline bindFromLiftedToInt5 #-}-bindFromLiftedToInt5 (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE LiftedRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])).+ Parser s e a ->+ (a -> Parser s e b) ->+ Parser s e b+{-# INLINE bindFromLiftedToInt5 #-}+bindFromLiftedToInt5 (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) sequenceLiftedToInt5 ::- forall s e- (a :: TYPE LiftedRep)- (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep ])).- Parser s e a- -> Parser s e b- -> Parser s e b-{-# inline sequenceLiftedToInt5 #-}-sequenceLiftedToInt5 (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE LiftedRep)+ (b :: TYPE ('TupleRep '[ 'IntRep, 'IntRep, 'IntRep, 'IntRep, 'IntRep])).+ Parser s e a ->+ Parser s e b ->+ Parser s e b+{-# INLINE sequenceLiftedToInt5 #-}+sequenceLiftedToInt5 (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ ) bindFromLiftedToInt ::- forall s e- (a :: TYPE LiftedRep)- (b :: TYPE 'IntRep).- Parser s e a- -> (a -> Parser s e b)- -> Parser s e b-{-# inline bindFromLiftedToInt #-}-bindFromLiftedToInt (Parser f) g = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# y, b, c #) #) ->- runParser (g y) (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE LiftedRep)+ (b :: TYPE 'IntRep).+ Parser s e a ->+ (a -> Parser s e b) ->+ Parser s e b+{-# INLINE bindFromLiftedToInt #-}+bindFromLiftedToInt (Parser f) g =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# y, b, c #) #) ->+ runParser (g y) (# arr, b, c #) s1+ ) sequenceLiftedToInt ::- forall s e- (a :: TYPE LiftedRep)- (b :: TYPE 'IntRep).- Parser s e a- -> Parser s e b- -> Parser s e b-{-# inline sequenceLiftedToInt #-}-sequenceLiftedToInt (Parser f) (Parser g) = Parser- (\x@(# arr, _, _ #) s0 -> case f x s0 of- (# s1, r0 #) -> case r0 of- (# e | #) -> (# s1, (# e | #) #)- (# | (# _, b, c #) #) -> g (# arr, b, c #) s1- )+ forall+ s+ e+ (a :: TYPE LiftedRep)+ (b :: TYPE 'IntRep).+ Parser s e a ->+ Parser s e b ->+ Parser s e b+{-# INLINE sequenceLiftedToInt #-}+sequenceLiftedToInt (Parser f) (Parser g) =+ Parser+ ( \x@(# arr, _, _ #) s0 -> case f x s0 of+ (# s1, r0 #) -> case r0 of+ (# e | #) -> (# s1, (# e | #) #)+ (# | (# _, b, c #) #) -> g (# arr, b, c #) s1+ )
src/Data/Bytes/Parser/Types.hs view
@@ -1,41 +1,43 @@-{-# language DeriveFunctor #-}-{-# language DeriveFoldable #-}-{-# language DerivingStrategies #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingStrategies #-} module Data.Bytes.Parser.Types ( Parser- , Result(..)- , Slice(..)+ , Result (..)+ , Slice (..) ) where -import Data.Bytes.Parser.Internal (Parser(..))+import Data.Bytes.Parser.Internal (Parser (..)) -- | The result of running a parser. data Result e a- = Failure e- -- ^ An error message indicating what went wrong.- | Success {-# UNPACK #-} !(Slice a)- -- ^ The parsed value and the number of bytes+ = -- | An error message indicating what went wrong.+ Failure e+ | -- | The parsed value and the number of bytes -- remaining in parsed slice.- deriving stock (Eq,Show,Foldable,Functor)+ Success {-# UNPACK #-} !(Slice a)+ deriving stock (Eq, Show, Foldable, Functor) --- | Slicing metadata (an offset and a length) accompanied--- by a value. This does not represent a slice into the--- value. This type is intended to be used as the result--- of an executed parser. In this context the slicing metadata--- describe a slice into to the array (or byte array) that--- from which the value was parsed.------ It is often useful to check the @length@ when a parser--- succeeds since a non-zero length indicates that there--- was additional unconsumed input. The @offset@ is only--- ever needed to construct a new slice (via @Bytes@ or--- @SmallVector@) from the remaining input.+{- | Slicing metadata (an offset and a length) accompanied+by a value. This does not represent a slice into the+value. This type is intended to be used as the result+of an executed parser. In this context the slicing metadata+describe a slice into to the array (or byte array) that+from which the value was parsed.++It is often useful to check the @length@ when a parser+succeeds since a non-zero length indicates that there+was additional unconsumed input. The @offset@ is only+ever needed to construct a new slice (via @Bytes@ or+@SmallVector@) from the remaining input.+-} data Slice a = Slice { offset :: {-# UNPACK #-} !Int- -- ^ Offset into the array.+ -- ^ Offset into the array. , length :: {-# UNPACK #-} !Int- -- ^ Length of the slice.+ -- ^ Length of the slice. , value :: a- -- ^ The structured data that was successfully parsed.- } deriving stock (Eq,Show,Foldable,Functor)+ -- ^ The structured data that was successfully parsed.+ }+ deriving stock (Eq, Show, Foldable, Functor)
src/Data/Bytes/Parser/Unsafe.hs view
@@ -1,28 +1,22 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language DuplicateRecordFields #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language NamedFieldPuns #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnboxedTuples #-} --- | Everything in this module is unsafe and can lead to--- nondeterministic output or segfaults if used incorrectly.+{- | Everything in this module is unsafe and can lead to+nondeterministic output or segfaults if used incorrectly.+-} module Data.Bytes.Parser.Unsafe ( -- * Types- Parser(..)+ Parser (..)+ -- * Functions , cursor , cursor#@@ -34,41 +28,43 @@ import Prelude hiding (length) -import Data.Bytes.Parser.Internal (Parser(..),uneffectful,uneffectfulInt#)-import Data.Bytes.Parser.Internal (Result(..))-import Data.Bytes.Types (Bytes(..))+import Data.Bytes.Parser.Internal (Parser (..), Result (..), uneffectful, uneffectfulInt#)+import Data.Bytes.Types (Bytes (..)) import Data.Primitive (ByteArray)-import GHC.Exts (Int#,Int(I#))-+import GHC.Exts (Int (I#), Int#) --- | Get the current offset into the chunk. Using this makes--- it possible to observe the internal difference between 'Bytes'--- that refer to equivalent slices. Be careful.+{- | Get the current offset into the chunk. Using this makes+it possible to observe the internal difference between 'Bytes'+that refer to equivalent slices. Be careful.+-} cursor :: Parser e s Int-cursor = uneffectful $ \Bytes{offset,length} ->+cursor = uneffectful $ \Bytes {offset, length} -> Success offset offset length -- | Variant of 'cursor' with unboxed result. cursor# :: Parser e s Int#-cursor# = uneffectfulInt# $ \Bytes{offset=I# off,length=I# len} -> (# | (# off, off, len #) #)+cursor# = uneffectfulInt# $ \Bytes {offset = I# off, length = I# len} -> (# | (# off, off, len #) #) --- | Return the byte array being parsed. This includes bytes--- that preceed the current offset and may include bytes that--- go beyond the length. This is somewhat dangerous, so only--- use this is you know what you're doing.+{- | Return the byte array being parsed. This includes bytes+that preceed the current offset and may include bytes that+go beyond the length. This is somewhat dangerous, so only+use this is you know what you're doing.+-} expose :: Parser e s ByteArray-expose = uneffectful $ \Bytes{length,offset,array} ->+expose = uneffectful $ \Bytes {length, offset, array} -> Success array offset length --- | Move the cursor back by @n@ bytes. Precondition: you--- must have previously consumed at least @n@ bytes.+{- | Move the cursor back by @n@ bytes. Precondition: you+must have previously consumed at least @n@ bytes.+-} unconsume :: Int -> Parser e s ()-unconsume n = uneffectful $ \Bytes{length,offset} ->+unconsume n = uneffectful $ \Bytes {length, offset} -> Success () (offset - n) (length + n) --- | Set the position to the given index. Precondition: the index--- must be valid. It should be the result of an earlier call to--- 'cursor'.+{- | Set the position to the given index. Precondition: the index+must be valid. It should be the result of an earlier call to+'cursor'.+-} jump :: Int -> Parser e s ()-jump ix = uneffectful $ \(Bytes{length,offset}) ->+jump ix = uneffectful $ \(Bytes {length, offset}) -> Success () ix (length + (offset - ix))
src/Data/Bytes/Parser/Utf8.hs view
@@ -1,37 +1,33 @@-{-# language BangPatterns #-}-{-# language BinaryLiterals #-}-{-# language DataKinds #-}-{-# language DeriveFunctor #-}-{-# language DerivingStrategies #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language LambdaCase #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language PolyKinds #-}-{-# language RankNTypes #-}-{-# language ScopedTypeVariables #-}-{-# language StandaloneDeriving #-}-{-# language TypeApplications #-}-{-# language UnboxedSums #-}-{-# language UnboxedTuples #-}-{-# language CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedTuples #-} --- | Parse input as UTF-8-encoded text. Parsers in this module will--- fail if they encounter a byte above @0x7F@.+{- | Parse input as UTF-8-encoded text. Parsers in this module will+fail if they encounter a byte above @0x7F@.+-} module Data.Bytes.Parser.Utf8 ( -- * Get Character any# , shortText ) where -import Prelude hiding (length,any,fail,takeWhile)+import Prelude hiding (any, fail, length, takeWhile) -import Data.Bits ((.&.),(.|.),unsafeShiftL,xor)-import Data.Bytes.Parser.Internal (Parser(..))+import Data.Bits (unsafeShiftL, xor, (.&.), (.|.))+import Data.Bytes.Parser.Internal (Parser (..)) import Data.Text.Short (ShortText)-import GHC.Exts (Int(I#),Char(C#),Int#,Char#,(-#),(+#),(>#),chr#)-import GHC.Word (Word8(W8#))+import GHC.Exts (Char (C#), Char#, Int (I#), Int#, chr#, (+#), (-#), (>#))+import GHC.Word (Word8 (W8#)) import qualified Data.ByteString.Short.Internal as BSS import qualified Data.Bytes.Parser as Parser@@ -39,6 +35,7 @@ import qualified Data.Text.Short as TS import qualified GHC.Exts as Exts +{- FOURMOLU_DISABLE -} -- | Interpret the next one to four bytes as a UTF-8-encoded character. -- Fails if the decoded codepoint is in the range U+D800 through U+DFFF. any# :: e -> Parser e s Char#@@ -81,39 +78,46 @@ | otherwise -> (# s0, (# e | #) #) _ -> (# s0, (# e | #) #) )+{- FOURMOLU_ENABLE -} codepointFromFourBytes :: Word8 -> Word8 -> Word8 -> Word8 -> Char-codepointFromFourBytes w1 w2 w3 w4 = C#- ( chr#- ( unI $ fromIntegral- ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 18 .|.- unsafeShiftL (word8ToWord w2 .&. 0b00111111) 12 .|.- unsafeShiftL (word8ToWord w3 .&. 0b00111111) 6 .|.- (word8ToWord w4 .&. 0b00111111)- )+codepointFromFourBytes w1 w2 w3 w4 =+ C#+ ( chr#+ ( unI $+ fromIntegral+ ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 18+ .|. unsafeShiftL (word8ToWord w2 .&. 0b00111111) 12+ .|. unsafeShiftL (word8ToWord w3 .&. 0b00111111) 6+ .|. (word8ToWord w4 .&. 0b00111111)+ )+ ) )- ) codepointFromThreeBytes :: Word8 -> Word8 -> Word8 -> Char-codepointFromThreeBytes w1 w2 w3 = C#- ( chr#- ( unI $ fromIntegral- ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 12 .|.- unsafeShiftL (word8ToWord w2 .&. 0b00111111) 6 .|.- (word8ToWord w3 .&. 0b00111111)- )+codepointFromThreeBytes w1 w2 w3 =+ C#+ ( chr#+ ( unI $+ fromIntegral+ ( unsafeShiftL (word8ToWord w1 .&. 0b00001111) 12+ .|. unsafeShiftL (word8ToWord w2 .&. 0b00111111) 6+ .|. (word8ToWord w3 .&. 0b00111111)+ )+ ) )- ) codepointFromTwoBytes :: Word8 -> Word8 -> Char-codepointFromTwoBytes w1 w2 = C#- ( chr#- ( unI $ fromIntegral @Word @Int- ( unsafeShiftL (word8ToWord w1 .&. 0b00011111) 6 .|.- (word8ToWord w2 .&. 0b00111111)- )+codepointFromTwoBytes w1 w2 =+ C#+ ( chr#+ ( unI $+ fromIntegral @Word @Int+ ( unsafeShiftL (word8ToWord w1 .&. 0b00011111) 6+ .|. (word8ToWord w2 .&. 0b00111111)+ )+ ) )- ) oneByteChar :: Word8 -> Bool oneByteChar !w = w .&. 0b10000000 == 0@@ -136,13 +140,16 @@ unI :: Int -> Int# unI (I# w) = w --- | Consume input that matches the argument. Fails if the--- input does not match.+{- | Consume input that matches the argument. Fails if the+input does not match.+-} shortText :: e -> ShortText -> Parser e s ()-shortText e !t = Parser.byteArray e- (shortByteStringToByteArray (TS.toShortByteString t))+shortText e !t =+ Parser.byteArray+ e+ (shortByteStringToByteArray (TS.toShortByteString t)) shortByteStringToByteArray ::- BSS.ShortByteString- -> PM.ByteArray+ BSS.ShortByteString ->+ PM.ByteArray shortByteStringToByteArray (BSS.SBS x) = PM.ByteArray x
test/Main.hs view
@@ -1,31 +1,32 @@-{-# language BangPatterns #-}-{-# language DataKinds #-}-{-# language MagicHash #-}-{-# language MultiWayIf #-}-{-# language NumDecimals #-}-{-# language OverloadedStrings #-}-{-# language ScopedTypeVariables #-}-{-# language TypeApplications #-}-+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} +#if MIN_VERSION_base(4,18,0)+#else import Control.Applicative (liftA2)+#endif import Control.Monad (replicateM) import Control.Monad.ST (runST)-import Data.Bytes.Parser (Slice(Slice))-import Data.Bytes.Types (Bytes(Bytes))+import Data.Bytes.Parser (Slice (Slice))+import Data.Bytes.Types (Bytes (Bytes)) import Data.Char (ord) import Data.Coerce (coerce)-import Data.Int (Int16,Int32)-import Data.Primitive (ByteArray(..),PrimArray(..))+import Data.Int (Int16, Int32)+import Data.Primitive (ByteArray (..), PrimArray (..)) import Data.Text.Short (ShortText)-import Data.WideWord (Word128(Word128))-import Data.Word (Word8,Word64,Word16,Word32)+import Data.WideWord (Word128 (Word128))+import Data.Word (Word16, Word32, Word64, Word8) import Numeric.Natural (Natural)-import System.ByteOrder (Fixed(..),ByteOrder(BigEndian,LittleEndian))-import Test.Tasty (defaultMain,testGroup,TestTree)-import Test.Tasty.HUnit ((@=?),testCase)-import Test.Tasty.QuickCheck ((===),testProperty)+import System.ByteOrder (ByteOrder (BigEndian, LittleEndian), Fixed (..))+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.HUnit (testCase, (@=?))+import Test.Tasty.QuickCheck (testProperty, (===)) import qualified Data.Bits as Bits import qualified Data.Bytes as Bytes@@ -45,404 +46,413 @@ main = defaultMain tests tests :: TestTree-tests = testGroup "Parser"- [ testProperty "decStandardInt" $ \i ->- withSz (show i) $ \str len ->- P.parseBytes (Latin.decStandardInt ()) str- ===- P.Success (Slice len 0 i)- , testProperty "big-endian-word16-array" $ \(xs :: [Word16]) ->- let src = Exts.fromList (coerce xs :: [Fixed 'BigEndian Word16])- res = Exts.fromList xs :: PrimArray Word16- sz = length xs * 2- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.Success (Slice (sz + 1) 0 res)- ===- P.parseBytes (BigEndian.word16Array () (length xs)) bs- , testProperty "big-endian-word32-array" $ \(xs :: [Word32]) ->- let src = Exts.fromList (coerce xs :: [Fixed 'BigEndian Word32])- res = Exts.fromList xs :: PrimArray Word32- sz = length xs * 4- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.Success (Slice (sz + 1) 0 res)- ===- P.parseBytes (BigEndian.word32Array () (length xs)) bs- , testProperty "little-endian-word32-array" $ \(xs :: [Word32]) ->- let src = Exts.fromList (coerce xs :: [Fixed 'LittleEndian Word32])- res = Exts.fromList xs :: PrimArray Word32- sz = length xs * 4- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.Success (Slice (sz + 1) 0 res)- ===- P.parseBytes (LittleEndian.word32Array () (length xs)) bs- , testProperty "big-endian-word64-array" $ \(xs :: [Word64]) ->- let src = Exts.fromList (coerce xs :: [Fixed 'BigEndian Word64])- res = Exts.fromList xs :: PrimArray Word64- sz = length xs * 8- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.Success (Slice (sz + 1) 0 res)- ===- P.parseBytes (BigEndian.word64Array () (length xs)) bs- , testProperty "little-endian-word64-array" $ \(xs :: [Word64]) ->- let src = Exts.fromList (coerce xs :: [Fixed 'LittleEndian Word64])- res = Exts.fromList xs :: PrimArray Word64- sz = length xs * 8- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.Success (Slice (sz + 1) 0 res)- ===- P.parseBytes (LittleEndian.word64Array () (length xs)) bs- , testProperty "little-endian-word128-array" $ \(xs :: [Word128]) ->- let src = Exts.fromList xs- sz = length xs * 16- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.parseBytes (replicateM (length xs) (LittleEndian.word128 ())) bs- ===- P.parseBytes (fmap Exts.toList (LittleEndian.word128Array () (length xs))) bs- , testProperty "big-endian-word128-array" $ \(xs :: [Word128]) ->- let src = Exts.fromList xs- sz = length xs * 16- bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz- in- P.parseBytes (replicateM (length xs) (BigEndian.word128 ())) bs- ===- P.parseBytes (fmap Exts.toList (BigEndian.word128Array () (length xs))) bs- , testProperty "cstring" $ \(xs :: [Word8]) ->- let ys = Exts.fromList xs- bs = Bytes.singleton 0x31 <> ys- in- P.parseBytes (P.cstring () (Exts.Ptr "1"# ) *> P.bytes () ys *> pure 42) bs- ===- (P.Success (Slice (Bytes.length ys + 1) 0 42) :: P.Result () Integer)- , testCase "big-endian-word256" $- P.parseBytesMaybe (BigEndian.word256Array () 1) (Exts.fromList [- 0x12, 0x34, 0x56, 0x78, 0x90,- 0x12, 0x34, 0x56, 0x78, 0x90,- 0x12, 0x34, 0x56, 0x78, 0x90,- 0x12,- 0x12, 0x34, 0x56, 0x78, 0x90,- 0x12, 0x34, 0x56, 0x78, 0x90,- 0x12, 0x34, 0x56, 0x78, 0x90,- 0x12- ])- @=?- Just (Exts.fromList [0x1234567890123456789012345678901212345678901234567890123456789012])- , testProperty "big-endian-word64" bigEndianWord64- , testProperty "big-endian-word32" bigEndianWord32- , testProperty "little-endian-word32" littleEndianWord32- , testCase "delimit" $- P.Success (Slice 13 0 (167,14625))- @=?- P.parseBytes- (do len <- Latin.decUnsignedInt ()- Latin.char () ','- r <- P.delimit () () len $ (,)- <$> Latin.decUnsignedInt ()- <* Latin.char () '*'- <*> Latin.decUnsignedInt ()- Latin.char () '0'- pure r- ) (bytes "9,167*146250")- , testGroup "decUnsignedInt"- [ testCase "A" $- P.Failure ()- @=?- P.parseBytes (Latin.decUnsignedInt ())- (bytes "742493495120739103935542")- , testCase "B" $- P.Success (Slice 8 3 4654667)- @=?- P.parseBytes (Latin.decUnsignedInt ())- (bytes "4654667,55")- , testCase "C" $- P.Failure ()- @=?- P.parseBytes (Latin.decUnsignedInt ())- (bytes ('1' : show (maxBound :: Int)))- , testCase "D" $- P.Failure ()- @=?- P.parseBytes (Latin.decUnsignedInt ())- (bytes "2481030337885070917891")- , testCase "E" $- P.Failure ()- @=?- P.parseBytes (Latin.decUnsignedInt ())- (bytes (show (fromIntegral @Int @Word maxBound + 1)))- , testCase "F" $ withSz (show (maxBound :: Int)) $ \str len ->- P.Success (Slice len 0 maxBound)- @=?- P.parseBytes (Latin.decUnsignedInt ()) str- , testProperty "property" $ \(QC.NonNegative i) ->- withSz (show i) $ \str len ->- P.parseBytes (Latin.decUnsignedInt ()) str- ===- P.Success (Slice len 0 i)- ]- , testGroup "hexNibbleLower"- [ testCase "A" $- P.parseBytes (Latin.hexNibbleLower ()) (bytes "Ab") @=? P.Failure ()- , testCase "B" $- P.parseBytes (Latin.hexNibbleLower ()) (bytes "bA") @=? P.Success (Slice 2 1 0xb)- , testCase "C" $- P.parseBytes (Latin.hexNibbleLower ()) (bytes "") @=? P.Failure ()- ]- , testGroup "tryHexNibbleLower"- [ testCase "A" $- P.Success @() (Slice 1 2 Nothing)- @=?- P.parseBytes Latin.tryHexNibbleLower (bytes "Ab")- , testCase "B" $- P.Success @() (Slice 2 1 (Just 0xb))- @=?- P.parseBytes Latin.tryHexNibbleLower (bytes "bA")- , testCase "C" $- P.Success @() (Slice 1 0 Nothing)- @=?- P.parseBytes Latin.tryHexNibbleLower (bytes "")- ]- , testGroup "decPositiveInteger"- [ testCase "A" $- P.parseBytes (Latin.decUnsignedInteger ())- (bytes "5469999463123462573426452736423546373235260")- @=?- P.Success- (Slice 44 0 5469999463123462573426452736423546373235260)- , testProperty "property" $ \(LargeInteger i) ->+tests =+ testGroup+ "Parser"+ [ testProperty "decStandardInt" $ \i -> withSz (show i) $ \str len ->- i >= 0- QC.==>- P.parseBytes (Latin.decUnsignedInteger ()) str- ===- P.Success (Slice len 0 i)- ]- , testGroup "decTrailingInteger"- [ testProperty "property" $ \(LargeInteger i) ->- withSz (show i) $ \str sz ->- i >= 0- QC.==>- P.parseBytes (Latin.decTrailingInteger 2) str- ===- (P.Success (Slice sz 0 (read ('2' : show i) :: Integer)) :: P.Result () Integer)- ]- , testGroup "decSignedInteger"- [ testCase "A" $- P.parseBytes (Latin.decSignedInteger ())- (bytes "-54699994631234625734264527364235463732352601")- @=?- P.Success- ( Slice 46 0- (-54699994631234625734264527364235463732352601)+ P.parseBytes (Latin.decStandardInt ()) str+ === P.Success (Slice len 0 i)+ , testProperty "big-endian-word16-array" $ \(xs :: [Word16]) ->+ let src = Exts.fromList (coerce xs :: [Fixed 'BigEndian Word16])+ res = Exts.fromList xs :: PrimArray Word16+ sz = length xs * 2+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.Success (Slice (sz + 1) 0 res)+ === P.parseBytes (BigEndian.word16Array () (length xs)) bs+ , testProperty "big-endian-word32-array" $ \(xs :: [Word32]) ->+ let src = Exts.fromList (coerce xs :: [Fixed 'BigEndian Word32])+ res = Exts.fromList xs :: PrimArray Word32+ sz = length xs * 4+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.Success (Slice (sz + 1) 0 res)+ === P.parseBytes (BigEndian.word32Array () (length xs)) bs+ , testProperty "little-endian-word32-array" $ \(xs :: [Word32]) ->+ let src = Exts.fromList (coerce xs :: [Fixed 'LittleEndian Word32])+ res = Exts.fromList xs :: PrimArray Word32+ sz = length xs * 4+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.Success (Slice (sz + 1) 0 res)+ === P.parseBytes (LittleEndian.word32Array () (length xs)) bs+ , testProperty "big-endian-word64-array" $ \(xs :: [Word64]) ->+ let src = Exts.fromList (coerce xs :: [Fixed 'BigEndian Word64])+ res = Exts.fromList xs :: PrimArray Word64+ sz = length xs * 8+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.Success (Slice (sz + 1) 0 res)+ === P.parseBytes (BigEndian.word64Array () (length xs)) bs+ , testProperty "little-endian-word64-array" $ \(xs :: [Word64]) ->+ let src = Exts.fromList (coerce xs :: [Fixed 'LittleEndian Word64])+ res = Exts.fromList xs :: PrimArray Word64+ sz = length xs * 8+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.Success (Slice (sz + 1) 0 res)+ === P.parseBytes (LittleEndian.word64Array () (length xs)) bs+ , testProperty "little-endian-word128-array" $ \(xs :: [Word128]) ->+ let src = Exts.fromList xs+ sz = length xs * 16+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.parseBytes (replicateM (length xs) (LittleEndian.word128 ())) bs+ === P.parseBytes (fmap Exts.toList (LittleEndian.word128Array () (length xs))) bs+ , testProperty "big-endian-word128-array" $ \(xs :: [Word128]) ->+ let src = Exts.fromList xs+ sz = length xs * 16+ bs = Bytes (Exts.fromList [0x42 :: Word8] <> untype src) 1 sz+ in P.parseBytes (replicateM (length xs) (BigEndian.word128 ())) bs+ === P.parseBytes (fmap Exts.toList (BigEndian.word128Array () (length xs))) bs+ , testProperty "cstring" $ \(xs :: [Word8]) ->+ let ys = Exts.fromList xs+ bs = Bytes.singleton 0x31 <> ys+ in P.parseBytes (P.cstring () (Exts.Ptr "1"#) *> P.bytes () ys *> pure 42) bs+ === (P.Success (Slice (Bytes.length ys + 1) 0 42) :: P.Result () Integer)+ , testCase "big-endian-word256" $+ P.parseBytesMaybe+ (BigEndian.word256Array () 1)+ ( Exts.fromList+ [ 0x12+ , 0x34+ , 0x56+ , 0x78+ , 0x90+ , 0x12+ , 0x34+ , 0x56+ , 0x78+ , 0x90+ , 0x12+ , 0x34+ , 0x56+ , 0x78+ , 0x90+ , 0x12+ , 0x12+ , 0x34+ , 0x56+ , 0x78+ , 0x90+ , 0x12+ , 0x34+ , 0x56+ , 0x78+ , 0x90+ , 0x12+ , 0x34+ , 0x56+ , 0x78+ , 0x90+ , 0x12+ ] )- , testCase "B" $ - P.Success (Slice 25 0 (3,(-206173954435705292503)))- @=?- P.parseBytes- ( pure (,)- <*> Latin.decSignedInteger ()- <* Latin.char () 'e'- <*> Latin.decSignedInteger ()- ) (bytes "3e-206173954435705292503")- , testProperty "property" $ \(LargeInteger i) ->- withSz (show i) $ \str len ->- P.parseBytes (Latin.decSignedInteger ()) str- ===- P.Success (Slice len 0 i)- ]- , testGroup "decSignedInt"- [ testProperty "A" $ \i -> withSz (show i) $ \str len ->- P.parseBytes (Latin.decSignedInt ()) str- ===- P.Success (Slice len 0 i)- , testProperty "B" $ \i ->- let s = (if i >= 0 then "+" else "") ++ show i in- withSz s $ \str len ->- P.parseBytes (Latin.decSignedInt ()) str- ===- P.Success (Slice len 0 i)- , testCase "C" $- P.Failure ()- @=?- P.parseBytes (Latin.decSignedInt ())- (bytes ('1' : show (maxBound :: Int)))- , testCase "D" $- P.Failure ()- @=?- P.parseBytes (Latin.decSignedInt ())- (bytes ('-' : '3' : show (maxBound :: Int)))- , testCase "E" $- P.Failure ()- @=?- P.parseBytes (Latin.decSignedInt ())- (bytes "2481030337885070917891")- , testCase "F" $- P.Failure ()- @=?- P.parseBytes (Latin.decSignedInt ())- (bytes "-4305030950553840988981")- , testCase "G" $ withSz (show (minBound :: Int)) $ \str len ->- P.Success (Slice len 0 minBound)- @=?- P.parseBytes (Latin.decSignedInt ()) str- , testCase "H" $ withSz (show (maxBound :: Int)) $ \str len ->- P.Success (Slice len 0 maxBound)- @=?- P.parseBytes (Latin.decSignedInt ()) str- , testCase "I" $- P.Failure ()- @=?- P.parseBytes (Latin.decSignedInt ())- (bytes (show (fromIntegral @Int @Word maxBound + 1)))- , testCase "J" $- -- This is one number lower than the minimum bound for- -- a signed 64-bit number, but this test will pass on- -- 32-bit architectures as well.- P.Failure ()- @=?- P.parseBytes (Latin.decSignedInt ())- (bytes "-9223372036854775809")- ]- , testGroup "decWord64"- [ testCase "A" $- P.Failure ()- @=?- P.parseBytes (Latin.decWord64 ())- (bytes "2481030337885070917891")- ]- , testCase "decWord-composition" $- P.Success (Slice 6 0 (42,8))- @=?- P.parseBytes- ( pure (,)- <*> Ascii.decWord ()- <* Ascii.char () '.'- <*> Ascii.decWord ()- <* Ascii.char () '.'- ) (bytes "42.8.")- , testCase "decWord-replicate" $- P.Success (Slice 7 0 (Exts.fromList [42,93] :: PrimArray Word))- @=?- P.parseBytes- (P.replicate 2 (Ascii.decWord () <* Ascii.char () '.'))- (bytes "42.93.")- , testCase "ascii-takeShortWhile" $- P.Success (Slice 11 0 (Exts.fromList ["the","world"] :: PM.Array ShortText))- @=?- P.parseBytes- (P.replicate 2 (Ascii.takeShortWhile (/=',') <* Ascii.char () ','))- (bytes "the,world,")- , testGroup "hexFixedWord8"- [ testCase "A" $- P.parseBytes (Latin.hexFixedWord8 ()) (bytes "A") @=? P.Failure ()- , testCase "B" $- P.parseBytes (Latin.hexFixedWord8 ()) (bytes "0A") @=? P.Success (Slice 3 0 0x0A)- , testCase "C" $- P.parseBytes (Latin.hexFixedWord8 ()) (bytes "") @=? P.Failure ()- , testCase "D" $- P.parseBytes (Latin.hexFixedWord8 ()) (bytes "A!") @=? P.Failure ()- ]- , testGroup "hexFixedWord16"- [ testCase "A" $- P.parseBytes (Latin.hexFixedWord16 ()) (bytes "A") @=? P.Failure ()- , testCase "B" $- P.parseBytes (Latin.hexFixedWord16 ()) (bytes "0A0A") @=? P.Success (Slice 5 0 0x0A0A)- , testCase "C" $- P.parseBytes (Latin.hexFixedWord16 ()) (bytes "") @=? P.Failure ()- , testCase "D" $- P.parseBytes (Latin.hexFixedWord16 ()) (bytes "A!A!") @=? P.Failure ()- ]- , testGroup "hexFixedWord32"- [ testCase "A" $- P.parseBytes (Latin.hexFixedWord32 ()) (bytes "A") @=? P.Failure ()- , testCase "B" $- P.parseBytes (Latin.hexFixedWord32 ()) (bytes "0A0A0A0A") @=? P.Success (Slice 9 0 0x0A0A0A0A)- , testCase "C" $- P.parseBytes (Latin.hexFixedWord32 ()) (bytes "") @=? P.Failure ()- , testCase "D" $- P.parseBytes (Latin.hexFixedWord32 ()) (bytes "A!A0A0A0") @=? P.Failure ()- ]- , testGroup "hexFixedWord64"- [ testCase "A" $- P.parseBytes (Latin.hexFixedWord64 ()) (bytes "ABCD01235678BCDE")- @=? P.Success- (Slice 17 0 0xABCD01235678BCDE)- ]- , testGroup "base128-w32"- [ testCase "A" $- P.Success (Slice 2 0 0x7E)- @=?- P.parseBytes (Base128.word32 ()) (bytes "\x7E")- , testCase "B" $- P.Success (Slice 5 0 0x200000)- @=?- P.parseBytes (Base128.word32 ()) (bytes "\x81\x80\x80\x00")- , testCase "C" $- P.Success (Slice 4 0 1656614)- @=?- P.parseBytes (Base128.word32 ()) (bytes "\xE5\x8E\x26")- -- , testProperty "iso" $ \w -> -- TODO- -- P.parseBytesMaybe (Base.word32 ()) (encodeBase128 (fromIntegral w))- -- ===- -- Just w- ]- , testGroup "leb128-w32"- [ testCase "A" $- P.Success (Slice 2 0 0x7E)- @=?- P.parseBytes (Leb128.word32 ()) (bytes "\x7E")- , testCase "B" $- P.Success (Slice 5 0 0x200000)- @=?- P.parseBytes (Leb128.word32 ()) (bytes "\x80\x80\x80\x01")- , testCase "C" $- P.Success (Slice 4 0 624485)- @=?- P.parseBytes (Leb128.word32 ()) (bytes "\xE5\x8E\x26")- , testProperty "iso" $ \w -> - P.parseBytesMaybe (Leb128.word32 ()) (encodeLeb128 (fromIntegral w))- ===- Just w- ]- , testGroup "leb128-w16"- [ testCase "A" $- P.Failure ()- @=?- P.parseBytes (Leb128.word16 ()) (bytes "\x80\x80\x04")- , testCase "B" $- P.Success (Slice 4 0 0xFFFF)- @=?- P.parseBytes (Leb128.word16 ()) (bytes "\xFF\xFF\x03")- , testProperty "iso" $ \w -> - P.parseBytesMaybe (Leb128.word16 ()) (encodeLeb128 (fromIntegral w))- ===- Just w- ]- , testGroup "leb128-i16"- [ testProperty "iso" $ \(w :: Int16) -> - P.parseBytesMaybe (Leb128.int16 ())- (encodeLeb128 (fromIntegral @Word16 @Natural (zigzag16 w)))- ===- Just w- ]- , testGroup "leb128-i32"- [ testProperty "iso" $ \(w :: Int32) -> - P.parseBytesMaybe (Leb128.int32 ())- (encodeLeb128 (fromIntegral @Word32 @Natural (zigzag32 w)))- ===- Just w- ]- , testGroup "satisfy"- [ testCase "A" $- P.Success (Slice 2 0 0x20)- @=?- P.parseBytes (P.satisfy () (== 0x20)) (bytes "\x20")+ @=? Just (Exts.fromList [0x1234567890123456789012345678901212345678901234567890123456789012])+ , testProperty "big-endian-word64" bigEndianWord64+ , testProperty "big-endian-word32" bigEndianWord32+ , testProperty "little-endian-word32" littleEndianWord32+ , testCase "delimit" $+ P.Success (Slice 13 0 (167, 14625))+ @=? P.parseBytes+ ( do+ len <- Latin.decUnsignedInt ()+ Latin.char () ','+ r <-+ P.delimit () () len $+ (,)+ <$> Latin.decUnsignedInt ()+ <* Latin.char () '*'+ <*> Latin.decUnsignedInt ()+ Latin.char () '0'+ pure r+ )+ (bytes "9,167*146250")+ , testGroup+ "decUnsignedInt"+ [ testCase "A" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decUnsignedInt ())+ (bytes "742493495120739103935542")+ , testCase "B" $+ P.Success (Slice 8 3 4654667)+ @=? P.parseBytes+ (Latin.decUnsignedInt ())+ (bytes "4654667,55")+ , testCase "C" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decUnsignedInt ())+ (bytes ('1' : show (maxBound :: Int)))+ , testCase "D" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decUnsignedInt ())+ (bytes "2481030337885070917891")+ , testCase "E" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decUnsignedInt ())+ (bytes (show (fromIntegral @Int @Word maxBound + 1)))+ , testCase "F" $ withSz (show (maxBound :: Int)) $ \str len ->+ P.Success (Slice len 0 maxBound)+ @=? P.parseBytes (Latin.decUnsignedInt ()) str+ , testProperty "property" $ \(QC.NonNegative i) ->+ withSz (show i) $ \str len ->+ P.parseBytes (Latin.decUnsignedInt ()) str+ === P.Success (Slice len 0 i)+ ]+ , testGroup+ "hexNibbleLower"+ [ testCase "A" $+ P.parseBytes (Latin.hexNibbleLower ()) (bytes "Ab") @=? P.Failure ()+ , testCase "B" $+ P.parseBytes (Latin.hexNibbleLower ()) (bytes "bA") @=? P.Success (Slice 2 1 0xb)+ , testCase "C" $+ P.parseBytes (Latin.hexNibbleLower ()) (bytes "") @=? P.Failure ()+ ]+ , testGroup+ "tryHexNibbleLower"+ [ testCase "A" $+ P.Success @() (Slice 1 2 Nothing)+ @=? P.parseBytes Latin.tryHexNibbleLower (bytes "Ab")+ , testCase "B" $+ P.Success @() (Slice 2 1 (Just 0xb))+ @=? P.parseBytes Latin.tryHexNibbleLower (bytes "bA")+ , testCase "C" $+ P.Success @() (Slice 1 0 Nothing)+ @=? P.parseBytes Latin.tryHexNibbleLower (bytes "")+ ]+ , testGroup+ "decPositiveInteger"+ [ testCase "A" $+ P.parseBytes+ (Latin.decUnsignedInteger ())+ (bytes "5469999463123462573426452736423546373235260")+ @=? P.Success+ (Slice 44 0 5469999463123462573426452736423546373235260)+ , testProperty "property" $ \(LargeInteger i) ->+ withSz (show i) $ \str len ->+ i+ >= 0+ QC.==> P.parseBytes (Latin.decUnsignedInteger ()) str+ === P.Success (Slice len 0 i)+ ]+ , testGroup+ "decTrailingInteger"+ [ testProperty "property" $ \(LargeInteger i) ->+ withSz (show i) $ \str sz ->+ i+ >= 0+ QC.==> P.parseBytes (Latin.decTrailingInteger 2) str+ === (P.Success (Slice sz 0 (read ('2' : show i) :: Integer)) :: P.Result () Integer)+ ]+ , testGroup+ "decSignedInteger"+ [ testCase "A" $+ P.parseBytes+ (Latin.decSignedInteger ())+ (bytes "-54699994631234625734264527364235463732352601")+ @=? P.Success+ ( Slice+ 46+ 0+ (-54699994631234625734264527364235463732352601)+ )+ , testCase "B" $+ P.Success (Slice 25 0 (3, (-206173954435705292503)))+ @=? P.parseBytes+ ( pure (,)+ <*> Latin.decSignedInteger ()+ <* Latin.char () 'e'+ <*> Latin.decSignedInteger ()+ )+ (bytes "3e-206173954435705292503")+ , testProperty "property" $ \(LargeInteger i) ->+ withSz (show i) $ \str len ->+ P.parseBytes (Latin.decSignedInteger ()) str+ === P.Success (Slice len 0 i)+ ]+ , testGroup+ "decSignedInt"+ [ testProperty "A" $ \i -> withSz (show i) $ \str len ->+ P.parseBytes (Latin.decSignedInt ()) str+ === P.Success (Slice len 0 i)+ , testProperty "B" $ \i ->+ let s = (if i >= 0 then "+" else "") ++ show i+ in withSz s $ \str len ->+ P.parseBytes (Latin.decSignedInt ()) str+ === P.Success (Slice len 0 i)+ , testCase "C" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decSignedInt ())+ (bytes ('1' : show (maxBound :: Int)))+ , testCase "D" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decSignedInt ())+ (bytes ('-' : '3' : show (maxBound :: Int)))+ , testCase "E" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decSignedInt ())+ (bytes "2481030337885070917891")+ , testCase "F" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decSignedInt ())+ (bytes "-4305030950553840988981")+ , testCase "G" $ withSz (show (minBound :: Int)) $ \str len ->+ P.Success (Slice len 0 minBound)+ @=? P.parseBytes (Latin.decSignedInt ()) str+ , testCase "H" $ withSz (show (maxBound :: Int)) $ \str len ->+ P.Success (Slice len 0 maxBound)+ @=? P.parseBytes (Latin.decSignedInt ()) str+ , testCase "I" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decSignedInt ())+ (bytes (show (fromIntegral @Int @Word maxBound + 1)))+ , testCase "J" $+ -- This is one number lower than the minimum bound for+ -- a signed 64-bit number, but this test will pass on+ -- 32-bit architectures as well.+ P.Failure ()+ @=? P.parseBytes+ (Latin.decSignedInt ())+ (bytes "-9223372036854775809")+ ]+ , testGroup+ "decWord64"+ [ testCase "A" $+ P.Failure ()+ @=? P.parseBytes+ (Latin.decWord64 ())+ (bytes "2481030337885070917891")+ ]+ , testCase "decWord-composition" $+ P.Success (Slice 6 0 (42, 8))+ @=? P.parseBytes+ ( pure (,)+ <*> Ascii.decWord ()+ <* Ascii.char () '.'+ <*> Ascii.decWord ()+ <* Ascii.char () '.'+ )+ (bytes "42.8.")+ , testCase "decWord-replicate" $+ P.Success (Slice 7 0 (Exts.fromList [42, 93] :: PrimArray Word))+ @=? P.parseBytes+ (P.replicate 2 (Ascii.decWord () <* Ascii.char () '.'))+ (bytes "42.93.")+ , testCase "ascii-takeShortWhile" $+ P.Success (Slice 11 0 (Exts.fromList ["the", "world"] :: PM.Array ShortText))+ @=? P.parseBytes+ (P.replicate 2 (Ascii.takeShortWhile (/= ',') <* Ascii.char () ','))+ (bytes "the,world,")+ , testGroup+ "hexFixedWord8"+ [ testCase "A" $+ P.parseBytes (Latin.hexFixedWord8 ()) (bytes "A") @=? P.Failure ()+ , testCase "B" $+ P.parseBytes (Latin.hexFixedWord8 ()) (bytes "0A") @=? P.Success (Slice 3 0 0x0A)+ , testCase "C" $+ P.parseBytes (Latin.hexFixedWord8 ()) (bytes "") @=? P.Failure ()+ , testCase "D" $+ P.parseBytes (Latin.hexFixedWord8 ()) (bytes "A!") @=? P.Failure ()+ ]+ , testGroup+ "hexFixedWord16"+ [ testCase "A" $+ P.parseBytes (Latin.hexFixedWord16 ()) (bytes "A") @=? P.Failure ()+ , testCase "B" $+ P.parseBytes (Latin.hexFixedWord16 ()) (bytes "0A0A") @=? P.Success (Slice 5 0 0x0A0A)+ , testCase "C" $+ P.parseBytes (Latin.hexFixedWord16 ()) (bytes "") @=? P.Failure ()+ , testCase "D" $+ P.parseBytes (Latin.hexFixedWord16 ()) (bytes "A!A!") @=? P.Failure ()+ ]+ , testGroup+ "hexFixedWord32"+ [ testCase "A" $+ P.parseBytes (Latin.hexFixedWord32 ()) (bytes "A") @=? P.Failure ()+ , testCase "B" $+ P.parseBytes (Latin.hexFixedWord32 ()) (bytes "0A0A0A0A") @=? P.Success (Slice 9 0 0x0A0A0A0A)+ , testCase "C" $+ P.parseBytes (Latin.hexFixedWord32 ()) (bytes "") @=? P.Failure ()+ , testCase "D" $+ P.parseBytes (Latin.hexFixedWord32 ()) (bytes "A!A0A0A0") @=? P.Failure ()+ ]+ , testGroup+ "hexFixedWord64"+ [ testCase "A" $+ P.parseBytes (Latin.hexFixedWord64 ()) (bytes "ABCD01235678BCDE")+ @=? P.Success+ (Slice 17 0 0xABCD01235678BCDE)+ ]+ , testGroup+ "base128-w32"+ [ testCase "A" $+ P.Success (Slice 2 0 0x7E)+ @=? P.parseBytes (Base128.word32 ()) (bytes "\x7E")+ , testCase "B" $+ P.Success (Slice 5 0 0x200000)+ @=? P.parseBytes (Base128.word32 ()) (bytes "\x81\x80\x80\x00")+ , testCase "C" $+ P.Success (Slice 4 0 1656614)+ @=? P.parseBytes (Base128.word32 ()) (bytes "\xE5\x8E\x26")+ -- , testProperty "iso" $ \w -> -- TODO+ -- P.parseBytesMaybe (Base.word32 ()) (encodeBase128 (fromIntegral w))+ -- ===+ -- Just w+ ]+ , testGroup+ "leb128-w32"+ [ testCase "A" $+ P.Success (Slice 2 0 0x7E)+ @=? P.parseBytes (Leb128.word32 ()) (bytes "\x7E")+ , testCase "B" $+ P.Success (Slice 5 0 0x200000)+ @=? P.parseBytes (Leb128.word32 ()) (bytes "\x80\x80\x80\x01")+ , testCase "C" $+ P.Success (Slice 4 0 624485)+ @=? P.parseBytes (Leb128.word32 ()) (bytes "\xE5\x8E\x26")+ , testProperty "iso" $ \w ->+ P.parseBytesMaybe (Leb128.word32 ()) (encodeLeb128 (fromIntegral w))+ === Just w+ ]+ , testGroup+ "leb128-w16"+ [ testCase "A" $+ P.Failure ()+ @=? P.parseBytes (Leb128.word16 ()) (bytes "\x80\x80\x04")+ , testCase "B" $+ P.Success (Slice 4 0 0xFFFF)+ @=? P.parseBytes (Leb128.word16 ()) (bytes "\xFF\xFF\x03")+ , testProperty "iso" $ \w ->+ P.parseBytesMaybe (Leb128.word16 ()) (encodeLeb128 (fromIntegral w))+ === Just w+ ]+ , testGroup+ "leb128-i16"+ [ testProperty "iso" $ \(w :: Int16) ->+ P.parseBytesMaybe+ (Leb128.int16 ())+ (encodeLeb128 (fromIntegral @Word16 @Natural (zigzag16 w)))+ === Just w+ ]+ , testGroup+ "leb128-i32"+ [ testProperty "iso" $ \(w :: Int32) ->+ P.parseBytesMaybe+ (Leb128.int32 ())+ (encodeLeb128 (fromIntegral @Word32 @Natural (zigzag32 w)))+ === Just w+ ]+ , testGroup+ "satisfy"+ [ testCase "A" $+ P.Success (Slice 2 0 0x20)+ @=? P.parseBytes (P.satisfy () (== 0x20)) (bytes "\x20")+ ] ]- ] bytes :: String -> Bytes bytes s = let b = pack ('x' : s) in Bytes b 1 (PM.sizeofByteArray b - 1)@@ -451,9 +461,15 @@ pack = Exts.fromList . map (fromIntegral @Int @Word8 . ord) bigEndianWord64 ::- Word8 -> Word8 -> Word8 -> Word8- -> Word8 -> Word8 -> Word8 -> Word8- -> QC.Property+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ QC.Property bigEndianWord64 a b c d e f g h = let arr = runST $ do m <- PM.newByteArray 11@@ -469,22 +485,25 @@ PM.writeByteArray m 9 (h :: Word8) PM.writeByteArray m 10 (0xEE :: Word8) PM.unsafeFreezeByteArray m- expected = (0 :: Word64)- + fromIntegral a * 256 ^ (7 :: Integer)- + fromIntegral b * 256 ^ (6 :: Integer)- + fromIntegral c * 256 ^ (5 :: Integer)- + fromIntegral d * 256 ^ (4 :: Integer)- + fromIntegral e * 256 ^ (3 :: Integer)- + fromIntegral f * 256 ^ (2 :: Integer)- + fromIntegral g * 256 ^ (1 :: Integer)- + fromIntegral h * 256 ^ (0 :: Integer)+ expected =+ (0 :: Word64)+ + fromIntegral a * 256 ^ (7 :: Integer)+ + fromIntegral b * 256 ^ (6 :: Integer)+ + fromIntegral c * 256 ^ (5 :: Integer)+ + fromIntegral d * 256 ^ (4 :: Integer)+ + fromIntegral e * 256 ^ (3 :: Integer)+ + fromIntegral f * 256 ^ (2 :: Integer)+ + fromIntegral g * 256 ^ (1 :: Integer)+ + fromIntegral h * 256 ^ (0 :: Integer) in P.parseBytes (BigEndian.word64 ()) (Bytes arr 2 9)- ===- P.Success (Slice 10 1 expected)+ === P.Success (Slice 10 1 expected) bigEndianWord32 ::- Word8 -> Word8 -> Word8 -> Word8- -> QC.Property+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ QC.Property bigEndianWord32 a b c d = let arr = runST $ do m <- PM.newByteArray 7@@ -496,18 +515,21 @@ PM.writeByteArray m 5 (d :: Word8) PM.writeByteArray m 6 (0xEE :: Word8) PM.unsafeFreezeByteArray m- expected = (0 :: Word32)- + fromIntegral a * 256 ^ (3 :: Integer)- + fromIntegral b * 256 ^ (2 :: Integer)- + fromIntegral c * 256 ^ (1 :: Integer)- + fromIntegral d * 256 ^ (0 :: Integer)+ expected =+ (0 :: Word32)+ + fromIntegral a * 256 ^ (3 :: Integer)+ + fromIntegral b * 256 ^ (2 :: Integer)+ + fromIntegral c * 256 ^ (1 :: Integer)+ + fromIntegral d * 256 ^ (0 :: Integer) in P.parseBytes (BigEndian.word32 ()) (Bytes arr 2 5)- ===- P.Success (Slice 6 1 expected)+ === P.Success (Slice 6 1 expected) littleEndianWord32 ::- Word8 -> Word8 -> Word8 -> Word8- -> QC.Property+ Word8 ->+ Word8 ->+ Word8 ->+ Word8 ->+ QC.Property littleEndianWord32 a b c d = let arr = runST $ do m <- PM.newByteArray 7@@ -519,33 +541,34 @@ PM.writeByteArray m 5 (d :: Word8) PM.writeByteArray m 6 (0xEE :: Word8) PM.unsafeFreezeByteArray m- expected = (0 :: Word32)- + fromIntegral a * 256 ^ (0 :: Integer)- + fromIntegral b * 256 ^ (1 :: Integer)- + fromIntegral c * 256 ^ (2 :: Integer)- + fromIntegral d * 256 ^ (3 :: Integer)+ expected =+ (0 :: Word32)+ + fromIntegral a * 256 ^ (0 :: Integer)+ + fromIntegral b * 256 ^ (1 :: Integer)+ + fromIntegral c * 256 ^ (2 :: Integer)+ + fromIntegral d * 256 ^ (3 :: Integer) in P.parseBytes (LittleEndian.word32 ()) (Bytes arr 2 5)- ===- P.Success (Slice 6 1 expected)+ === P.Success (Slice 6 1 expected) -- The Arbitrary instance for Integer that comes with -- QuickCheck only generates small numbers. newtype LargeInteger = LargeInteger Integer- deriving (Eq,Show)+ deriving (Eq, Show) instance QC.Arbitrary Word128 where arbitrary = liftA2 Word128 QC.arbitrary QC.arbitrary instance QC.Arbitrary LargeInteger where arbitrary = do- n <- QC.choose (1, 27)- sign <- QC.arbitrary- r <- (if sign then negate else id) . foldr f 0+ n <- QC.choose (1, 27)+ sign <- QC.arbitrary+ r <-+ (if sign then negate else id) . foldr f 0 <$> replicateM n QC.arbitrary- pure (LargeInteger r)- where- f :: Word8 -> Integer -> Integer- f w acc = (acc `Bits.shiftL` 8) + fromIntegral w+ pure (LargeInteger r)+ where+ f :: Word8 -> Integer -> Integer+ f w acc = (acc `Bits.shiftL` 8) + fromIntegral w -- We add an extra 1 since bytes gives us a slice that -- starts at that offset.@@ -556,15 +579,17 @@ untype (PrimArray x) = ByteArray x encodeLeb128 :: Natural -> Bytes-encodeLeb128 x = Bytes.unsafeDrop 1 (Exts.fromList (0xFF : go [] x)) where+encodeLeb128 x = Bytes.unsafeDrop 1 (Exts.fromList (0xFF : go [] x))+ where go !xs !n =- let (q,r) = quotRem n 128+ let (q, r) = quotRem n 128 r' = fromIntegral @Natural @Word8 r- w = if q == 0- then r'- else Bits.setBit r' 7+ w =+ if q == 0+ then r'+ else Bits.setBit r' 7 xs' = w : xs- in if q == 0 + in if q == 0 then List.reverse xs' else go xs' q