packages feed

bytesmith 0.1.0.0 → 0.2.0.0

raw patch · 11 files changed

+1873/−691 lines, 11 filesdep +bytestringdep +contiguousdep +gaugedep ~basedep ~byteslicedep ~primitivePVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, contiguous, gauge, run-st, tasty, tasty-hunit, tasty-quickcheck, text-short

Dependency ranges changed: base, byteslice, primitive

API changes (from Hackage documentation)

- Data.Bytes.Parser: anyAscii :: e -> Parser e s Char
- Data.Bytes.Parser: anyAscii# :: e -> Parser e s Char#
- Data.Bytes.Parser: anyAsciiOpt :: e -> Parser e s (Maybe Char)
- Data.Bytes.Parser: anyUtf8# :: e -> Parser e s Char#
- Data.Bytes.Parser: ascii :: e -> Char -> Parser e s ()
- Data.Bytes.Parser: ascii3 :: e -> Char -> Char -> Char -> Parser e s ()
- Data.Bytes.Parser: ascii4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()
- Data.Bytes.Parser: bindChar :: Parser s e Char# -> (Char# -> Parser s e a) -> Parser s e a
- Data.Bytes.Parser: cursor :: Parser e s Int
- Data.Bytes.Parser: decPositiveInteger :: e -> Parser e s Integer
- Data.Bytes.Parser: decWord :: e -> Parser e s Word
- Data.Bytes.Parser: decWord16 :: e -> Parser e s Word16
- Data.Bytes.Parser: decWord32 :: e -> Parser e s Word32
- Data.Bytes.Parser: decWord8 :: e -> Parser e s Word8
- Data.Bytes.Parser: expose :: Parser e s ByteArray
- Data.Bytes.Parser: hexWord16 :: e -> Parser e s Word16
- Data.Bytes.Parser: instance GHC.Base.Applicative (Data.Bytes.Parser.Parser e s)
- Data.Bytes.Parser: instance GHC.Base.Functor (Data.Bytes.Parser.Parser e s)
- Data.Bytes.Parser: instance GHC.Base.Monad (Data.Bytes.Parser.Parser e s)
- Data.Bytes.Parser: peekAnyAscii :: e -> Parser e s Char
- Data.Bytes.Parser: skipAlphaAscii :: Parser e s ()
- Data.Bytes.Parser: skipAlphaAscii1 :: e -> Parser e s ()
- Data.Bytes.Parser: skipAscii :: Char -> Parser e s ()
- Data.Bytes.Parser: skipAscii1 :: e -> Char -> Parser e s ()
- Data.Bytes.Parser: skipDigitsAscii :: Parser e s ()
- Data.Bytes.Parser: skipDigitsAscii1 :: e -> Parser e s ()
- Data.Bytes.Parser: skipUntilAsciiConsume :: e -> Char -> Parser e s ()
- Data.Bytes.Parser: unconsume :: Int -> Parser e s ()
+ Data.Bytes.Parser: (<?>) :: Parser x s a -> e -> Parser e s a
+ Data.Bytes.Parser: annotate :: Parser x s a -> e -> Parser e s a
+ Data.Bytes.Parser: bindFromCharToIntPair :: Parser s e Char# -> (Char# -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)
+ Data.Bytes.Parser: bindFromCharToLifted :: Parser s e Char# -> (Char# -> Parser s e a) -> Parser s e a
+ Data.Bytes.Parser: bindFromIntToIntPair :: Parser s e Int# -> (Int# -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)
+ Data.Bytes.Parser: bindFromLiftedToInt :: Parser s e a -> (a -> Parser s e Int#) -> Parser s e Int#
+ Data.Bytes.Parser: bindFromLiftedToIntPair :: Parser s e a -> (a -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)
+ Data.Bytes.Parser: bindFromMaybeCharToIntPair :: Parser s e (# (# #) | Char# #) -> ((# (# #) | Char# #) -> Parser s e (# Int#, Int# #)) -> Parser s e (# Int#, Int# #)
+ Data.Bytes.Parser: bindFromMaybeCharToLifted :: Parser s e (# (# #) | Char# #) -> ((# (# #) | Char# #) -> Parser s e a) -> Parser s e a
+ Data.Bytes.Parser: boxIntPair :: Parser e s (# Int#, Int# #) -> Parser e s (Int, Int)
+ Data.Bytes.Parser: byteArray :: e -> ByteArray -> Parser e s ()
+ Data.Bytes.Parser: bytes :: e -> Bytes -> Parser e s ()
+ Data.Bytes.Parser: delimit :: e -> e -> Int -> Parser e s a -> Parser e s a
+ Data.Bytes.Parser: failIntPair :: e -> Parser e s (# Int#, Int# #)
+ Data.Bytes.Parser: infix 0 <?>
+ Data.Bytes.Parser: infixl 3 `orElse`
+ Data.Bytes.Parser: instance (GHC.Classes.Eq e, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Bytes.Parser.Result e a)
+ Data.Bytes.Parser: instance (GHC.Show.Show e, GHC.Show.Show a) => GHC.Show.Show (Data.Bytes.Parser.Result e a)
+ Data.Bytes.Parser: measure :: Parser e s a -> Parser e s (Int, a)
+ Data.Bytes.Parser: pureIntPair :: (# Int#, Int# #) -> Parser s e (# Int#, Int# #)
+ Data.Bytes.Parser: remaining :: Parser e s Bytes
+ Data.Bytes.Parser: replicate :: forall arr e s a. (Contiguous arr, Element arr a) => Int -> Parser e s a -> Parser e s (arr a)
+ Data.Bytes.Parser: take :: e -> Int -> Parser e s Bytes
+ Data.Bytes.Parser: takeWhile :: (Word8 -> Bool) -> Parser e s Bytes
+ Data.Bytes.Parser: unboxIntPair :: Parser e s (Int, Int) -> Parser e s (# Int#, Int# #)
+ Data.Bytes.Parser.Ascii: any :: e -> Parser e s Char
+ Data.Bytes.Parser.Ascii: any# :: e -> Parser e s Char#
+ Data.Bytes.Parser.Ascii: char :: e -> Char -> Parser e s ()
+ Data.Bytes.Parser.Ascii: char2 :: e -> Char -> Char -> Parser e s ()
+ Data.Bytes.Parser.Ascii: char3 :: e -> Char -> Char -> Char -> Parser e s ()
+ Data.Bytes.Parser.Ascii: char4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()
+ Data.Bytes.Parser.Ascii: decWord :: e -> Parser e s Word
+ Data.Bytes.Parser.Ascii: decWord16 :: e -> Parser e s Word16
+ Data.Bytes.Parser.Ascii: decWord32 :: e -> Parser e s Word32
+ Data.Bytes.Parser.Ascii: decWord8 :: e -> Parser e s Word8
+ Data.Bytes.Parser.Ascii: opt :: e -> Parser e s (Maybe Char)
+ Data.Bytes.Parser.Ascii: peek :: e -> Parser e s Char
+ Data.Bytes.Parser.Ascii: shortTrailedBy :: e -> Char -> Parser e s ShortText
+ Data.Bytes.Parser.Ascii: skipAlpha :: Parser e s ()
+ Data.Bytes.Parser.Ascii: skipAlpha1 :: e -> Parser e s ()
+ Data.Bytes.Parser.Ascii: skipChar :: Char -> Parser e s ()
+ Data.Bytes.Parser.Ascii: skipChar1 :: e -> Char -> Parser e s ()
+ Data.Bytes.Parser.Ascii: skipDigits :: Parser e s ()
+ Data.Bytes.Parser.Ascii: skipDigits1 :: e -> Parser e s ()
+ Data.Bytes.Parser.Ascii: skipTrailedBy :: e -> Char -> Parser e s ()
+ Data.Bytes.Parser.BigEndian: int16 :: e -> Parser e s Int16
+ Data.Bytes.Parser.BigEndian: int32 :: e -> Parser e s Int32
+ Data.Bytes.Parser.BigEndian: int64 :: e -> Parser e s Int64
+ Data.Bytes.Parser.BigEndian: int8 :: e -> Parser e s Int8
+ Data.Bytes.Parser.BigEndian: word16 :: e -> Parser e s Word16
+ Data.Bytes.Parser.BigEndian: word32 :: e -> Parser e s Word32
+ Data.Bytes.Parser.BigEndian: word64 :: e -> Parser e s Word64
+ Data.Bytes.Parser.BigEndian: word8 :: e -> Parser e s Word8
+ Data.Bytes.Parser.Latin: any :: e -> Parser e s Char
+ Data.Bytes.Parser.Latin: char :: e -> Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: char2 :: e -> Char -> Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: char3 :: e -> Char -> Char -> Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: char4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: decSignedInt :: e -> Parser e s Int
+ Data.Bytes.Parser.Latin: decSignedInteger :: e -> Parser e s Integer
+ Data.Bytes.Parser.Latin: decStandardInt :: e -> Parser e s Int
+ Data.Bytes.Parser.Latin: decTrailingInt :: e -> Int -> Parser e s Int
+ Data.Bytes.Parser.Latin: decTrailingInt# :: e -> Int# -> Parser e s Int#
+ Data.Bytes.Parser.Latin: decTrailingInteger :: Int -> Parser e s Integer
+ Data.Bytes.Parser.Latin: decUnsignedInt :: e -> Parser e s Int
+ Data.Bytes.Parser.Latin: decUnsignedInt# :: e -> Parser e s Int#
+ Data.Bytes.Parser.Latin: decUnsignedInteger :: e -> Parser e s Integer
+ Data.Bytes.Parser.Latin: decWord :: e -> Parser e s Word
+ Data.Bytes.Parser.Latin: decWord16 :: e -> Parser e s Word16
+ Data.Bytes.Parser.Latin: decWord32 :: e -> Parser e s Word32
+ Data.Bytes.Parser.Latin: decWord8 :: e -> Parser e s Word8
+ Data.Bytes.Parser.Latin: hexWord16 :: e -> Parser e s Word16
+ Data.Bytes.Parser.Latin: opt :: Parser e s (Maybe Char)
+ Data.Bytes.Parser.Latin: opt# :: Parser e s (# (# #) | Char# #)
+ Data.Bytes.Parser.Latin: skipChar :: Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: skipChar1 :: e -> Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: skipDigits :: Parser e s ()
+ Data.Bytes.Parser.Latin: skipDigits1 :: e -> Parser e s ()
+ Data.Bytes.Parser.Latin: skipTrailedBy :: e -> Char -> Parser e s ()
+ Data.Bytes.Parser.Latin: trySatisfy :: (Char -> Bool) -> Parser e s Bool
+ Data.Bytes.Parser.Latin: trySatisfyThen :: forall (r :: RuntimeRep) (e :: Type) (s :: Type) (a :: TYPE r). Parser e s a -> (Char -> Maybe (Parser e s a)) -> Parser e s a
+ Data.Bytes.Parser.Unsafe: cursor :: Parser e s Int
+ Data.Bytes.Parser.Unsafe: expose :: Parser e s ByteArray
+ Data.Bytes.Parser.Unsafe: jump :: Int -> Parser e s ()
+ Data.Bytes.Parser.Unsafe: unconsume :: Int -> Parser e s ()
+ Data.Bytes.Parser.Utf8: any# :: e -> Parser e s Char#
+ Data.Bytes.Parser.Utf8: shortText :: e -> ShortText -> Parser e s ()
- Data.Bytes.Parser: Success :: !a -> !Int -> !Int -> Result e a
+ Data.Bytes.Parser: Success :: !a -> !Int -> Result e a
- Data.Bytes.Parser: boxWord32 :: Parser s e Word# -> Parser s e Word32
+ Data.Bytes.Parser: boxWord32 :: Parser e s Word# -> Parser e s Word32
- Data.Bytes.Parser: orElse :: Parser s e a -> Parser s e a -> Parser s e a
+ Data.Bytes.Parser: orElse :: Parser x s a -> Parser e s a -> Parser e s a
- Data.Bytes.Parser: unboxWord32 :: Parser s e Word32 -> Parser s e Word#
+ Data.Bytes.Parser: unboxWord32 :: Parser e s Word32 -> Parser e s Word#

Files

CHANGELOG.md view
@@ -1,5 +1,16 @@ # Revision history for bytesmith +## 0.2.0.0 -- 2019-09-24++* Add big-endian word parsers.+* Redo module structure so that encoding-specific functions each+  live in their own module.+* Add a lot more functions and attempt to make naming somewhat+  consistent.+* Add `delimit`.+* Add `replicate`.+* Add `annotate` and its infix synonym `<?>`.+ ## 0.1.0.0 -- 2019-08-22  * First version.
+ bench/Main.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeApplications #-}++import Data.Char (ord)+import Data.Primitive (ByteArray)+import Data.Word (Word8)+import Gauge.Main (defaultMain,bench,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+  ]++encodedBigNumber :: ByteArray+encodedBigNumber = stringToByteArray $ show $ id @Integer $+  246246357264327645234627753190240202405243024304504230544+  *+  732345623640035232405249305932503920593209520932095234651++stringToByteArray :: String -> ByteArray+stringToByteArray =+  Exts.fromList . map (fromIntegral @Int @Word8 . ord)
bytesmith.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: bytesmith-version: 0.1.0.0+version: 0.2.0.0 synopsis: Nonresumable byte parser description:   Parse bytes as fast as possible. This is a nonresumable parser@@ -19,10 +19,21 @@ library   exposed-modules:     Data.Bytes.Parser+    Data.Bytes.Parser.BigEndian+    Data.Bytes.Parser.Ascii+    Data.Bytes.Parser.Latin+    Data.Bytes.Parser.Unsafe+    Data.Bytes.Parser.Utf8+  other-modules:+    Data.Bytes.Parser.Internal   build-depends:     , base >=4.12 && <5-    , byteslice >=0.1.1 && <2+    , bytestring >=0.10.8 && <0.11+    , byteslice >=0.1.3 && <0.2+    , contiguous >= 0.4 && < 0.6     , primitive >=0.7 && <0.8+    , text-short >=0.1.3 && <0.2+    , run-st >=0.1 && <0.2   hs-source-dirs: src   ghc-options: -O2 -Wall   default-language: Haskell2010@@ -32,8 +43,26 @@   type: exitcode-stdio-1.0   hs-source-dirs: test   main-is: Main.hs+  ghc-options: -Wall -O2   build-depends:     , base >=4.12.0.0 && <5     , bytesmith     , primitive     , byteslice+    , tasty-hunit+    , tasty+    , tasty-quickcheck++benchmark bench+  type: exitcode-stdio-1.0+  build-depends:+    , base+    , byteslice+    , bytestring+    , gauge+    , primitive+    , bytesmith+  ghc-options: -Wall -O2+  default-language: Haskell2010+  hs-source-dirs: bench+  main-is: Main.hs
src/Data/Bytes/Parser.hs view
@@ -16,6 +16,10 @@ {-# language UnboxedSums #-} {-# 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. module Data.Bytes.Parser   ( -- * Types     Parser(..)@@ -24,85 +28,79 @@   , parseByteArray   , parseBytes   , parseBytesST-    -- * Build Parsers-  , fail-  , peekAnyAscii-  , ascii-  , ascii3-  , ascii4+    -- * One Byte   , any-  , anyAscii-  , anyAscii#-  , anyUtf8#-  , anyAsciiOpt-  , decWord-  , decWord8-  , decWord16-  , decWord32-  , hexWord16-  , decPositiveInteger+    -- * Many Bytes+  , take+  , takeWhile+    -- * Skip+  , skipWhile+    -- * Match+  , byteArray+  , bytes+    -- * End of Input   , endOfInput   , isEndOfInput-  , skipUntilAsciiConsume-  , skipWhile-  , skipAscii-  , skipAscii1-  , skipAlphaAscii-  , skipAlphaAscii1-  , skipDigitsAscii-  , skipDigitsAscii1+  , remaining+    -- * Control Flow+  , fail+  , orElse+  , annotate+  , (<?>)+    -- * Repetition+  , replicate+    -- * Subparsing+  , delimit+  , measure     -- * Lift Effects   , effect-    -- * Expose Internals-  , cursor-  , expose-  , unconsume-    -- * Cut down on boxing-  , unboxWord32+    -- * Box Result   , boxWord32+  , boxIntPair+    -- * Unbox Result+  , unboxWord32+  , unboxIntPair     -- * Specialized Bind-    -- $bind-  , bindChar-    -- * Alternative-  , orElse+    -- | 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+    -- get used in your original source code, GHC will not introduce them.+  , bindFromCharToLifted+  , bindFromLiftedToIntPair+  , bindFromLiftedToInt+  , bindFromIntToIntPair+  , bindFromCharToIntPair+  , bindFromMaybeCharToIntPair+  , bindFromMaybeCharToLifted+    -- * Specialized Pure+  , pureIntPair+    -- * Specialized Fail+  , failIntPair   ) where -import Prelude hiding (length,any,fail)+import Prelude hiding (length,any,fail,takeWhile,take,replicate) -import Data.Char (ord)-import Data.Bits ((.&.),(.|.),unsafeShiftL,xor)-import Data.Kind (Type)-import GHC.ST (ST(..),runST)-import GHC.Exts (Word(W#),Word#,TYPE,State#,Int#,ByteArray#)-import GHC.Exts (Int(I#),Char(C#),chr#,RuntimeRep)-import GHC.Exts (Char#,(+#),(-#),(<#),(>#),word2Int#)-import GHC.Exts (indexCharArray#,indexWord8Array#,ord#)-import GHC.Exts (timesWord#,plusWord#)-import GHC.Word (Word16(W16#),Word8(W8#),Word32(W32#))+import Data.Bytes.Parser.Internal (InternalResult(..),Parser(..),unboxBytes,boxBytes,Result#,uneffectful,fail)+import Data.Bytes.Parser.Unsafe (unconsume) import Data.Bytes.Types (Bytes(..)) import Data.Primitive (ByteArray(..))+import GHC.Exts (Int(I#),Word#,Int#,Char#,(+#),(-#),(>=#))+import GHC.ST (ST(..),runST)+import GHC.Word (Word32(W32#),Word8)+import Data.Primitive.Contiguous (Contiguous,Element) +import qualified Data.Bytes as B import qualified Data.Primitive as PM-import qualified Control.Monad--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---- | 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+import qualified Data.Primitive.Contiguous as C  -- | The result of running a parser. 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.+  | Success !a !Int+    -- ^ The parsed value and the number of bytes+    -- remaining in parsed slice.+  deriving (Eq,Show)  -- | Parse a slice of a byte array. This can succeed even if the -- entire slice was not consumed by the parser.@@ -113,7 +111,7 @@   action = case p @s of     Parser f -> ST       (\s0 -> case f (unboxBytes b) s0 of-        (# s1, r #) -> (# s1, boxResult r #)+        (# s1, r #) -> (# s1, boxPublicResult r #)       )  -- | Variant of 'parseBytes' that accepts an unsliced 'ByteArray'.@@ -126,97 +124,9 @@ parseBytesST :: Parser e s a -> Bytes -> ST s (Result e a) parseBytesST (Parser f) !b = ST   (\s0 -> case f (unboxBytes b) s0 of-    (# s1, r #) -> (# s1, boxResult r #)+    (# s1, r #) -> (# s1, boxPublicResult r #)   ) -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 #) #) #)-    )--instance Applicative (Parser e s) where-  pure = pureParser-  (<*>) = Control.Monad.ap--instance Monad (Parser e s) where-  {-# inline return #-}-  {-# inline (>>=) #-}-  return = pureParser-  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-    )--pureParser :: a -> Parser e s a-pureParser a = Parser-  (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))--upcastUnitSuccess :: (# Int#, Int# #) -> Result# e ()-upcastUnitSuccess (# b, c #) = (# | (# (), b, c #) #)--upcastWordResult :: Result# e Word# -> Result# e Word-upcastWordResult (# e | #) = (# e | #)-upcastWordResult (# | (# a, b, c #) #) = (# | (# W# a, b, c #) #)---- Precondition: the word is small enough-upcastWord16Result :: Result# e Word# -> Result# e Word16-upcastWord16Result (# e | #) = (# e | #)-upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# a, b, c #) #)---- Precondition: the word is small enough-upcastWord32Result :: Result# e Word# -> Result# e Word32-upcastWord32Result (# e | #) = (# e | #)-upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# a, b, c #) #)---- Precondition: the word is small enough-upcastWord8Result :: Result# e Word# -> Result# e Word8-upcastWord8Result (# e | #) = (# e | #)-upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# a, b, c #) #)--c2w :: Char -> Word8-c2w = fromIntegral . ord---- | 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 $ \chunk ->-  Success (offset chunk) (offset chunk) (length chunk)---- | 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 $ \chunk ->-  Success (array chunk) (offset chunk) (length chunk)---- | 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 $ \chunk ->-  Success () (offset chunk - n) (length chunk + n)--uneffectful :: (Bytes -> Result e a) -> Parser e s a-{-# inline uneffectful #-}-uneffectful f = Parser-  ( \b s0 -> (# s0, unboxResult (f (boxBytes b)) #) )--uneffectful# :: (Bytes -> Result# e a) -> Parser e s a-uneffectful# f = Parser-  ( \b s0 -> (# s0, (f (boxBytes b)) #) )--uneffectfulWord# :: (Bytes -> Result# e Word#) -> Parser e s Word#-uneffectfulWord# f = Parser-  ( \b s0 -> (# s0, (f (boxBytes b)) #) )- -- | Lift an effectful computation into a parser. effect :: ST s a -> Parser e s a effect (ST f) = Parser@@ -224,59 +134,29 @@     (# s1, a #) -> (# s1, (# | (# a, off, len #) #) #)   ) --- | Only valid for characters with a Unicode code point lower--- than 128. This consumes a single byte, decoding it as an ASCII--- character.-ascii :: e -> Char -> Parser e s ()--- GHC should decide to inline this after optimization.-ascii e !c = uneffectful $ \chunk -> if length chunk > 0-  then if PM.indexByteArray (array chunk) (offset chunk) == c2w c-    then Success () (offset chunk + 1) (length chunk - 1)-    else Failure e-  else Failure e+byteArray :: e -> ByteArray -> Parser e s ()+byteArray e !expected = bytes e (B.fromByteArray expected) --- | Parse three bytes in succession.-ascii3 :: e -> Char -> Char -> Char -> Parser e s ()--- GHC should decide to inline this after optimization.-ascii3 e !c0 !c1 !c2 = uneffectful $ \chunk ->-  if | length chunk > 2-     , PM.indexByteArray (array chunk) (offset chunk) == c2w c0-     , PM.indexByteArray (array chunk) (offset chunk + 1) == c2w c1-     , PM.indexByteArray (array chunk) (offset chunk + 2) == c2w c2-         -> Success () (offset chunk + 3) (length chunk - 3)-     | otherwise -> Failure e+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 #)+  ) --- | Parse four bytes in succession.-ascii4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()--- GHC should decide to inline this after optimization.-ascii4 e !c0 !c1 !c2 !c3 = uneffectful $ \chunk ->-  if | length chunk > 3-     , PM.indexByteArray (array chunk) (offset chunk) == c2w c0-     , PM.indexByteArray (array chunk) (offset chunk + 1) == c2w c1-     , PM.indexByteArray (array chunk) (offset chunk + 2) == c2w c2-     , PM.indexByteArray (array chunk) (offset chunk + 3) == c2w c3-         -> Success () (offset chunk + 4) (length chunk - 4)-     | otherwise -> Failure e+infix 0 <?> --- | Fail with the provided error message.-fail ::-     e -- ^ Error message-  -> Parser e s a-fail e = uneffectful $ \_ -> Failure e+-- | Infix version of 'annotate'.+(<?>) :: Parser x s a -> e -> Parser e s a+(<?>) = annotate --- | Interpret the next byte as an ASCII-encoded character.--- Fails if the byte corresponds to a number above 127.-peekAnyAscii :: e -> Parser e s Char-peekAnyAscii 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+-- | 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.@@ -285,8 +165,8 @@ any e = uneffectful $ \chunk -> if length chunk > 0   then     let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8-     in Success w (offset chunk + 1) (length chunk - 1)-  else Failure e+     in InternalSuccess w (offset chunk + 1) (length chunk - 1)+  else InternalFailure e  -- Interpret the next byte as an ASCII-encoded character. -- Does not check to see if any characters are left. This@@ -295,91 +175,28 @@ {-# inline anyUnsafe #-} anyUnsafe = uneffectful $ \chunk ->   let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8-   in Success w (offset chunk + 1) (length chunk - 1)---- | Interpret the next byte as an ASCII-encoded character.--- Fails if the byte corresponds to a number above 127.-anyAscii :: e -> Parser e s Char-{-# inline anyAscii #-}-anyAscii 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 + 1)-                 (length chunk - 1)-          else Failure e-  else Failure e+   in InternalSuccess w (offset chunk + 1) (length chunk - 1) --- | Interpret the next byte as an ASCII-encoded character.--- Fails if the byte corresponds to a number above 127.-anyAscii# :: e -> Parser e s Char#-{-# inline anyAscii# #-}-anyAscii# 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 | #) #)-  )+-- | Take while the predicate is matched. This is always inlined.+takeWhile :: (Word8 -> Bool) -> Parser e s Bytes+{-# inline takeWhile #-}+takeWhile f = uneffectful $ \chunk -> case B.takeWhile f chunk of+  bs -> InternalSuccess bs (offset chunk + length bs) (length chunk - length bs) --- | 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.-anyUtf8# :: e -> Parser e s Char#-{-# noinline anyUtf8# #-}-anyUtf8# e = Parser-  (\(# arr, off, len #) s0 -> case len ># 0# of-    1# ->-      let !w0 = indexWord8Array# arr off-       in if | oneByteChar (W8# w0) -> -                 (# s0, (# | (# chr# (word2Int# w0), off +# 1#, len -# 1# #) #) #)-             | twoByteChar (W8# w0) ->-                 if | I# len > 1-                    , w1 <- indexWord8Array# arr (off +# 1#)-                    , followingByte (W8# w1)-                    , C# c <- codepointFromTwoBytes (W8# w0) (W8# w1)-                      -> (# s0, (# | (# c, off +# 2#, len -# 2# #) #) #)-                    | otherwise -> (# s0, (# e | #) #)-             | threeByteChar (W8# w0) ->-                 if | I# len > 2-                    , w1 <- indexWord8Array# arr (off +# 1# )-                    , w2 <- indexWord8Array# arr (off +# 2# )-                    , followingByte (W8# w1)-                    , !c@(C# c#) <- codepointFromThreeBytes (W8# w0) (W8# w1) (W8# w2)-                    , c < '\xD800' || c > '\xDFFF'-                      -> (# s0, (# | (# c#, off +# 3#, len -# 3# #) #) #)-                    | otherwise -> (# s0, (# e | #) #)-             | fourByteChar (W8# w0) ->-                 if | I# len > 3-                    , w1 <- indexWord8Array# arr (off +# 1# )-                    , w2 <- indexWord8Array# arr (off +# 2# )-                    , w3 <- indexWord8Array# arr (off +# 3# )-                    , followingByte (W8# w1)-                    , !(C# c#) <- codepointFromFourBytes (W8# w0) (W8# w1) (W8# w2) (W8# w3)-                      -> (# s0, (# | (# c#, off +# 4#, len -# 4# #) #) #)-                    | otherwise -> (# s0, (# e | #) #)-             | otherwise -> (# s0, (# e | #) #)-    _ -> (# s0, (# e | #) #)-  )+-- | 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 -> InternalSuccess bs (offset chunk + n) (length chunk - n)+  else InternalFailure e --- | Interpret the next byte as an ASCII-encoded character.--- Fails if the byte corresponds to a number above 127. Returns--- nothing if the end of the input has been reached.-anyAsciiOpt :: e -> Parser e s (Maybe Char)-{-# inline anyAsciiOpt #-}-anyAsciiOpt 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 all remaining bytes in the input.+remaining :: Parser e s Bytes+{-# inline remaining #-}+remaining = uneffectful $ \chunk ->+  InternalSuccess chunk (offset chunk + length chunk) 0  -- | Skip while the predicate is matched. This is always inlined. skipWhile :: (Word8 -> Bool) -> Parser e s ()@@ -393,345 +210,27 @@         then go         else unconsume 1 --- | Parse exactly four ASCII-encoded characters, interpretting--- them as the hexadecimal encoding of a 32-bit number. Note that--- this rejects a sequence such as @5A9@, requiring @05A9@ instead.--- This is insensitive to case.-hexWord16 :: e -> Parser e s Word16-{-# inline hexWord16 #-}-hexWord16 e = Parser-  (\x s0 -> case runParser (hexWord16# e) x s0 of-    (# s1, r #) -> case r of-      (# err | #) -> (# s1, (# err | #) #)-      (# | (# a, b, c #) #) -> (# s1, (# | (# W16# a, b, c #) #) #)-  ) -hexWord16# :: e -> Parser e s Word#-{-# noinline hexWord16# #-}-hexWord16# 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 `timesWord#` 4096##) `plusWord#`-                   (n1 `timesWord#` 256##) `plusWord#`-                   (n2 `timesWord#` 16##) `plusWord#`-                   n3-                ,  unI (offset chunk) +# 4#-                ,  unI (length chunk) -# 4# #) #)-           | otherwise -> (# e | #)-  else (# e | #)----- Returns the maximum machine word if the argument is not--- the ASCII encoding of a hexadecimal digit.-oneHex :: Word8 -> Word-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---- | Skip ASCII-encoded digits until a non-digit is encountered.-skipDigitsAscii :: Parser e s ()-skipDigitsAscii = uneffectful# $ \c ->-  upcastUnitSuccess (skipDigitsAsciiLoop c)---- | Skip uppercase and lowercase letters until a non-alpha--- character is encountered.-skipDigitsAscii1 :: e -> Parser e s ()-skipDigitsAscii1 e = uneffectful# $ \c ->-  skipDigitsAscii1LoopStart e c---- | Skip uppercase and lowercase letters until a non-alpha--- character is encountered.-skipAlphaAscii :: Parser e s ()-skipAlphaAscii = uneffectful# $ \c ->-  upcastUnitSuccess (skipAlphaAsciiLoop c)---- | Skip uppercase and lowercase letters until a non-alpha--- character is encountered.-skipAlphaAscii1 :: e -> Parser e s ()-skipAlphaAscii1 e = uneffectful# $ \c ->-  skipAlphaAsciiLoop1Start e c---- | Skip the character any number of times. This succeeds--- even if the character was not present.-skipAscii :: Char -> Parser e s ()-skipAscii !w = uneffectful# $ \c ->-  upcastUnitSuccess (skipLoop (c2w w) c)---- | Skip the character any number of times. It must occur--- at least once or else this will fail.-skipAscii1 :: e -> Char -> Parser e s ()-skipAscii1 e !w = uneffectful# $ \c ->-  skipLoop1Start e (c2w w) c--skipDigitsAsciiLoop ::-     Bytes -- Chunk-  -> (# Int#, Int# #)-skipDigitsAsciiLoop !c = if length c > 0-  then-    let w = PM.indexByteArray (array c) (offset c) :: Word8-     in if w >= c2w '0' && w <= c2w '9'-          then skipDigitsAsciiLoop (advance 1 c)-          else (# unI (offset c), unI (length c) #)-  else (# unI (offset c), unI (length c) #)--skipAlphaAsciiLoop ::-     Bytes -- Chunk-  -> (# Int#, Int# #)-skipAlphaAsciiLoop !c = if length c > 0-  then-    let w = PM.indexByteArray (array c) (offset c) :: Word8-     in if (w >= c2w 'a' && w <= c2w 'z') || (w >= c2w 'A' && w <= c2w 'Z')-          then skipAlphaAsciiLoop (advance 1 c)-          else (# unI (offset c), unI (length c) #)-  else (# unI (offset c), unI (length c) #)--skipAlphaAsciiLoop1Start ::-     e-  -> Bytes -- chunk-  -> Result# e ()-skipAlphaAsciiLoop1Start e !c = if length c > 0-  then -    let w = PM.indexByteArray (array c) (offset c) :: Word8-     in if (w >= c2w 'a' && w <= c2w 'z') || (w >= c2w 'A' && w <= c2w 'Z')-          then upcastUnitSuccess (skipAlphaAsciiLoop (advance 1 c))-          else (# e | #)-  else (# e | #)--skipDigitsAscii1LoopStart ::-     e-  -> Bytes -- chunk-  -> Result# e ()-skipDigitsAscii1LoopStart e !c = if length c > 0-  then -    let w = PM.indexByteArray (array c) (offset c) :: Word8-     in if w >= c2w '0' && w <= c2w '9'-          then upcastUnitSuccess (skipDigitsAsciiLoop (advance 1 c))-          else (# e | #)-  else (# e | #)--skipLoop ::-     Word8 -- byte to match-  -> Bytes -- Chunk-  -> (# Int#, Int# #)-skipLoop !w !c = if length c > 0-  then if PM.indexByteArray (array c) (offset c) == w-    then skipLoop w (advance 1 c)-    else (# unI (offset c), unI (length c) #)-  else (# unI (offset c), unI (length c) #)--skipLoop1Start ::-     e-  -> Word8 -- byte to match-  -> Bytes -- chunk-  -> Result# e ()-skipLoop1Start e !w !chunk0 = if length chunk0 > 0-  then if PM.indexByteArray (array chunk0) (offset chunk0) == w-    then upcastUnitSuccess (skipLoop w (advance 1 chunk0))-    else (# e | #)-  else (# e | #)---- | Skip bytes until the character from the ASCII plane is encountered.--- This does not ensure that the skipped bytes were ASCII-encoded--- characters.-skipUntilAsciiConsume :: e -> Char -> Parser e s ()-skipUntilAsciiConsume e !w = uneffectful# $ \c ->-  skipUntilConsumeLoop e (c2w w) c--skipUntilConsumeLoop ::-     e -- Error message-  -> Word8 -- byte to match-  -> Bytes -- Chunk-  -> Result# e ()-skipUntilConsumeLoop e !w !c = if length c > 0-  then if PM.indexByteArray (array c) (offset c) /= w-    then skipUntilConsumeLoop e w (advance 1 c)-    else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)-  else (# e | #)- -- | Fails if there is still more input remaining. endOfInput :: e -> Parser e s () -- GHC should decide to inline this after optimization. endOfInput e = uneffectful $ \chunk -> if length chunk == 0-  then Success () (offset chunk) 0-  else Failure e+  then InternalSuccess () (offset chunk) 0+  else InternalFailure e  -- | Returns true if there are no more bytes in the input. Returns -- false otherwise. Always succeeds. isEndOfInput :: Parser e s Bool -- GHC should decide to inline this after optimization. isEndOfInput = uneffectful $ \chunk ->-  Success (length chunk == 0) (offset chunk) (length chunk)---- | 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 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 overflows rather than failing.--- This may be changed in a future release.-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 positive integer of arbitrary--- size. Note: this is not implemented efficiently. This--- pulls in one digit at a time, multiplying the accumulator--- by ten each time and adding the new digit. Since--- arithmetic involving arbitrary-precision integers is--- somewhat expensive, it would be better to pull in several--- digits at a time, convert those to a machine-sized integer,--- then upcast and perform the multiplication and addition.-decPositiveInteger :: e -> Parser e s Integer-decPositiveInteger e = Parser-  (\chunk0 s0 -> decPositiveIntegerStart e (boxBytes chunk0) s0)--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 (advance 1 chunk0) #)-          else (# s0, (# e | #) #)-  else (# s0, (# e | #) #)---- No limit on length for integers.-decPositiveIntegerStart ::-     e-  -> Bytes-  -> ST# s (Result# e Integer)-decPositiveIntegerStart e !chunk0 s0 = if length chunk0 > 0-  then-    let !w = (PM.indexByteArray (array chunk0) (offset chunk0)) - 48-     in if w < (10 :: Word8)-          then (# s0, decIntegerMore e (fromIntegral w) (advance 1 chunk0) #)-          else (# s0, (# e | #) #)-  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 (advance 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 = if length chunk0 > 0-  then-    let !w = fromIntegral @Word8 @Word-          (PM.indexByteArray (array chunk0) (offset chunk0)) - 48-     in if w < 10-          then decWordMore e (acc * 10 + w)-                 (advance 1 chunk0)-          else (# | (# unW acc, unI (offset chunk0), unI (length chunk0)  #) #)-  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 (advance 1 chunk0)-                  else (# e | #)-          else (# | (# unW acc, unI (offset chunk0), unI (length chunk0)  #) #)-  else (# | (# unW acc, unI (offset chunk0), 0# #) #)--decIntegerMore ::-     e -- Error message-  -> Integer -- Accumulator-  -> Bytes -- Chunk-  -> Result# e Integer-decIntegerMore e !acc !chunk0 = if length chunk0 > 0-  then-    let w :: Word8-        !w = (PM.indexByteArray (array chunk0) (offset chunk0)) - 48-     in if w < 10-          then-            let w' = acc * 10 + fromIntegral w-             in decIntegerMore e w' (advance 1 chunk0)-          else (# | (# acc, unI (offset chunk0), unI (length chunk0) #) #)-  else (# | (# acc, unI (offset chunk0), 0# #) #)--advance :: Int -> Bytes -> Bytes-advance n (Bytes arr off len) = Bytes arr (off + n) (len - n)--unW :: Word -> Word#-unW (W# w) = w--unI :: Int -> Int#-unI (I# w) = w--boxBytes :: Bytes# -> Bytes-boxBytes (# a, b, c #) = Bytes (ByteArray a) (I# b) (I# c)--unboxBytes :: Bytes -> Bytes#-unboxBytes (Bytes (ByteArray a) (I# b) (I# c)) = (# a,b,c #)--unboxResult :: Result e a -> Result# e a-unboxResult (Success a (I# b) (I# c)) = (# | (# a, b, c #) #)-unboxResult (Failure e) = (# e | #)+  InternalSuccess (length chunk == 0) (offset chunk) (length chunk) -boxResult :: Result# e a -> Result e a-boxResult (# | (# a, b, c #) #) = Success a (I# b) (I# c)-boxResult (# e | #) = Failure e+boxPublicResult :: Result# e a -> Result e a+boxPublicResult (# | (# a, _, c #) #) = Success a (I# c)+boxPublicResult (# e | #) = Failure e  -- | Convert a 'Word32' parser to a 'Word#' parser.-unboxWord32 :: Parser s e Word32 -> Parser s e Word#+unboxWord32 :: Parser e s Word32 -> Parser e s Word# unboxWord32 (Parser f) = Parser   (\x s0 -> case f x s0 of     (# s1, r #) -> case r of@@ -739,9 +238,18 @@       (# | (# W32# a, b, c #) #) -> (# s1, (# | (# a, b, c #) #) #)   ) +-- | Convert a @(Int,Int)@ parser to a @(# Int#, Int# #)@ parser.+unboxIntPair :: Parser e s (Int,Int) -> Parser e s (# Int#, Int# #)+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 #) #) #)+  )+ -- | Convert a 'Word#' parser to a 'Word32' parser. Precondition: -- the argument parser only returns words less than 4294967296.-boxWord32 :: Parser s e Word# -> Parser s e Word32+boxWord32 :: Parser e s Word# -> Parser e s Word32 boxWord32 (Parser f) = Parser   (\x s0 -> case f x s0 of     (# s1, r #) -> case r of@@ -749,13 +257,26 @@       (# | (# a, b, c #) #) -> (# s1, (# | (# W32# a, b, c #) #) #)   ) --- | There is a law-abiding instance of @Alternative@ for 'Parser'.+-- | Convert a @(# Int#, Int# #)@ parser to a @(Int,Int)@ parser.+boxIntPair :: Parser e s (# Int#, Int# #) -> Parser e s (Int,Int)+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 right-biased--- variant of @\<|\>@. Consequently, it lacks an identity.--- See <https://github.com/bos/attoparsec/issues/122 attoparsec #122>+-- 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.-orElse :: Parser s e a -> Parser s e a -> Parser s e a+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@@ -763,68 +284,75 @@       (# | r #) -> (# s1, (# | r #) #)   ) -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)-      )-    )+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   ) -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)-      )-    )+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   ) -codepointFromTwoBytes :: Word8 -> Word8 -> Char-codepointFromTwoBytes w1 w2 = C#-  ( chr#-    ( unI $ fromIntegral @Word @Int-      ( unsafeShiftL (word8ToWord w1 .&. 0b00011111) 6 .|. -        (word8ToWord w2 .&. 0b00111111)-      )-    )+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   ) -oneByteChar :: Word8 -> Bool-oneByteChar !w = w .&. 0b10000000 == 0--twoByteChar :: Word8 -> Bool-twoByteChar !w = w .&. 0b11100000 == 0b11000000--threeByteChar :: Word8 -> Bool-threeByteChar !w = w .&. 0b11110000 == 0b11100000--fourByteChar :: Word8 -> Bool-fourByteChar !w = w .&. 0b11111000 == 0b11110000--word8ToWord :: Word8 -> Word-word8ToWord = fromIntegral+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+  ) -followingByte :: Word8 -> Bool-followingByte !w = xor w 0b01000000 .&. 0b11000000 == 0b11000000+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+  ) -{- $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-get used in you original source code, GHC cannot introduce them.--}+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+  ) --- | Specialization of monadic bind for parsers that return 'Char#'.-bindChar :: Parser s e Char# -> (Char# -> Parser s e a) -> Parser s e a-{-# inline bindChar #-}-bindChar (Parser f) g = Parser+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 | #) #)@@ -832,3 +360,73 @@         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 #) #) #))++failIntPair :: e -> Parser e s (# Int#, Int# #)+{-# 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 #) #) #)+  )++-- | 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', 'parseBytesST', and 'endOfInput'. It is provided as+-- a single combinator because for convenience and because it is easy+-- 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 ::+     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+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 !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 0
+ src/Data/Bytes/Parser/Ascii.hs view
@@ -0,0 +1,195 @@+{-# 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 #-}++-- | 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+    -- * Get Character+  , any+  , any#+  , peek+  , opt+    -- * Match Many+  , shortTrailedBy+    -- * Skip+  , Latin.skipDigits+  , Latin.skipDigits1+  , Latin.skipChar+  , Latin.skipChar1+  , skipAlpha+  , skipAlpha1+  , skipTrailedBy+    -- * Numbers+  , Latin.decWord+  , Latin.decWord8+  , Latin.decWord16+  , Latin.decWord32+  ) where++import Prelude hiding (length,any,fail,takeWhile)++import Data.Bytes.Types (Bytes(..))+import Data.Bytes.Parser.Internal (Parser(..),uneffectful,Result#,uneffectful#)+import Data.Bytes.Parser.Internal (InternalResult(..),indexLatinCharArray,upcastUnitSuccess)+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 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++-- | 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 ()+skipTrailedBy e !c = do+  let go = do+        !d <- any e+        if d == c+          then pure ()+          else go+  go++-- | 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+  skipTrailedBy e c+  end <- Unsafe.cursor+  src <- Unsafe.expose+  let len = end - start - 1+      !r = runByteArrayST $ do+        marr <- PM.newByteArray len+        PM.copyByteArray marr 0 src start len+        PM.unsafeFreezeByteArray marr+  pure+    $ TS.fromShortByteStringUnsafe+    $ byteArrayToShortByteString+    $ r+++-- | Consumes and returns the next character in the input.+any :: e -> Parser e s Char+any e = uneffectful $ \chunk -> if length chunk > 0+  then+    let c = indexLatinCharArray (array chunk) (offset chunk)+     in if c < '\128'+          then InternalSuccess c (offset chunk + 1) (length chunk - 1)+          else InternalFailure e+  else InternalFailure 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 | #) #)+  )++unI :: Int -> Int#+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.+peek :: e -> Parser e s Char+peek e = uneffectful $ \chunk -> if length chunk > 0+  then+    let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8+     in if w < 128+          then InternalSuccess+                 (C# (chr# (unI (fromIntegral w))))+                 (offset chunk)+                 (length chunk)+          else InternalFailure e+  else InternalFailure 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.+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 InternalSuccess+                 (Just (C# (chr# (unI (fromIntegral w)))))+                 (offset chunk + 1)+                 (length chunk - 1)+          else InternalFailure e+  else InternalSuccess Nothing (offset chunk) (length chunk)++-- | Skip uppercase and lowercase letters until a non-alpha+-- character is encountered.+skipAlpha :: Parser e s ()+skipAlpha = uneffectful# $ \c ->+  upcastUnitSuccess (skipAlphaAsciiLoop c)++-- | Skip uppercase and lowercase letters until a non-alpha+-- character is encountered.+skipAlpha1 :: e -> Parser e s ()+skipAlpha1 e = uneffectful# $ \c ->+  skipAlphaAsciiLoop1Start e c++skipAlphaAsciiLoop ::+     Bytes -- Chunk+  -> (# Int#, Int# #)+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 ()+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+byteArrayToShortByteString (PM.ByteArray x) = BSS.SBS x+
+ src/Data/Bytes/Parser/BigEndian.hs view
@@ -0,0 +1,118 @@+{-# 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 #-}++-- | Big-endian fixed-width numbers.+module Data.Bytes.Parser.BigEndian+  ( -- * Unsigned+    word8+  , word16+  , word32+  , word64+    -- * Signed+  , int8+  , int16+  , int32+  , int64+  ) where++import Prelude hiding (length,any,fail,takeWhile)++import Data.Bits ((.|.),unsafeShiftL)+import Data.Bytes.Types (Bytes(..))+import Data.Bytes.Parser.Internal (Parser,uneffectful)+import Data.Bytes.Parser.Internal (InternalResult(..))+import Data.Word (Word8,Word16,Word32,Word64)+import Data.Int (Int8,Int16,Int32,Int64)++import qualified Data.Bytes.Parser as P+import qualified Data.Primitive as PM++-- | Unsigned 8-bit word.+word8 :: e -> Parser e s Word8+word8 = P.any++-- | 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 InternalSuccess+          (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wa) 8 .|. fromIntegral wb))+          (offset chunk + 2) (length chunk - 2)+  else InternalFailure 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 InternalSuccess+          (fromIntegral @Word @Word32+            ( unsafeShiftL (fromIntegral wa) 24 .|.+              unsafeShiftL (fromIntegral wb) 16 .|.+              unsafeShiftL (fromIntegral wc) 8 .|.+              fromIntegral wd+            )+          )+          (offset chunk + 4) (length chunk - 4)+  else InternalFailure 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 InternalSuccess+          ( 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 InternalFailure e++-- | Signed 8-bit integer.+int8 :: e -> Parser e s Int8+int8 = fmap fromIntegral . word8++-- | Signed 16-bit integer.+int16 :: e -> Parser e s Int16+int16 = fmap fromIntegral . word16++-- | Signed 32-bit integer.+int32 :: e -> Parser e s Int32+int32 = fmap fromIntegral . word32++-- | Signed 64-bit integer.+int64 :: e -> Parser e s Int64+int64 = fmap fromIntegral . word64
+ src/Data/Bytes/Parser/Internal.hs view
@@ -0,0 +1,148 @@+{-# 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 #-}++module Data.Bytes.Parser.Internal+  ( Parser(..)+  , InternalResult(..)+  , Bytes#+  , ST#+  , Result#+  , uneffectful+  , uneffectful#+  , boxBytes+  , unboxBytes+  , unboxResult+  , fail+  , indexLatinCharArray+  , upcastUnitSuccess+  ) where++import Prelude hiding (length,any,fail,takeWhile)++import Control.Applicative (Alternative)+import Data.Primitive (ByteArray(ByteArray))+import Data.Bytes.Types (Bytes(Bytes))+import Data.Kind (Type)+import GHC.Exts (TYPE,RuntimeRep,Int(I#),Int#,State#,ByteArray#,Char(C#))++import qualified Control.Applicative+import qualified Control.Monad+import qualified GHC.Exts as Exts++-- | 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++-- The result of running a parser. Used internally.+data InternalResult e a+  = InternalFailure e+    -- An error message indicating what went wrong.+  | InternalSuccess !a !Int !Int+    -- The parsed value, the offset after the last consumed byte, and the+    -- number of bytes remaining in parsed slice.++uneffectful :: (Bytes -> InternalResult e a) -> Parser e s a+{-# inline uneffectful #-}+uneffectful f = Parser+  ( \b s0 -> (# s0, unboxResult (f (boxBytes b)) #) )++boxBytes :: Bytes# -> Bytes+{-# 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 #)++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++unboxResult :: InternalResult e a -> Result# e a+unboxResult (InternalSuccess a (I# b) (I# c)) = (# | (# a, b, c #) #)+unboxResult (InternalFailure e) = (# e | #)++-- | 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 #) #)+    )++-- | Fail with the provided error message.+fail ::+     e -- ^ Error message+  -> Parser e s a+fail e = uneffectful $ \_ -> InternalFailure e++instance Applicative (Parser e s) where+  pure = pureParser+  (<*>) = Control.Monad.ap++pureParser :: a -> Parser e s a+pureParser a = Parser+  (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))+++instance Monad (Parser e s) where+  {-# inline return #-}+  {-# inline (>>=) #-}+  return = pureParser+  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+    )++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 #) #) #)+    )++indexLatinCharArray :: ByteArray -> Int -> Char+{-# 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)) #) )++upcastUnitSuccess :: (# Int#, Int# #) -> Result# e ()+{-# inline upcastUnitSuccess #-}+upcastUnitSuccess (# b, c #) = (# | (# (), b, c #) #)+
+ src/Data/Bytes/Parser/Latin.hs view
@@ -0,0 +1,726 @@+{-# 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 #-}++-- | 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+    -- ** Try+  , trySatisfy+  , trySatisfyThen+    -- * Get Character+  , any+  , opt+  , opt#+    -- * Skip+  , skipDigits+  , skipDigits1+  , skipChar+  , skipChar1+  , skipTrailedBy+    -- * Numbers+    -- ** Decimal+    -- *** Unsigned+  , decWord+  , decWord8+  , decWord16+  , decWord32+    -- *** Signed+  , decUnsignedInt+  , decUnsignedInt#+  , decSignedInt+  , decStandardInt+  , decTrailingInt+  , decTrailingInt#+  , decSignedInteger+  , decUnsignedInteger+  , decTrailingInteger+    -- ** Hexadecimal+  , hexWord16+  ) where++import Prelude hiding (length,any,fail,takeWhile)++import Data.Bits ((.|.))+import Data.Bytes.Types (Bytes(..))+import Data.Bytes.Parser.Internal (Parser(..),ST#,uneffectful,Result#,uneffectful#)+import Data.Bytes.Parser.Internal (InternalResult(..),indexLatinCharArray,upcastUnitSuccess)+import Data.Bytes.Parser.Internal (boxBytes)+import Data.Bytes.Parser (bindFromLiftedToInt)+import Data.Word (Word8)+import Data.Char (ord)+import Data.Kind (Type)+import GHC.Exts (Int(I#),Char(C#),Word#,Int#,Char#,(+#),(-#),indexCharArray#)+import GHC.Exts (TYPE,RuntimeRep)+import GHC.Word (Word(W#),Word8(W8#),Word16(W16#),Word32(W32#))++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 -> InternalSuccess False (offset chunk) (length chunk)+  _ -> case f (indexLatinCharArray (array chunk) (offset chunk)) of+    True -> InternalSuccess True (offset chunk + 1) (length chunk - 1)+    False -> InternalSuccess 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 ()+-- GHC should decide to inline this after optimization.+char e !c = uneffectful $ \chunk -> if length chunk > 0+  then if indexLatinCharArray (array chunk) (offset chunk) == c+    then InternalSuccess () (offset chunk + 1) (length chunk - 1)+    else InternalFailure e+  else InternalFailure e++-- | Consume the next two characters, failing if they do+-- not match they expected values.+--+-- > char2 e a b === char e a *> char e b+char2 :: e -> Char -> Char -> Parser e s ()+-- GHC should decide to inline this after optimization.+char2 e !c0 !c1 = uneffectful $ \chunk ->+  if | length chunk > 1+     , indexLatinCharArray (array chunk) (offset chunk) == c0+     , indexLatinCharArray (array chunk) (offset chunk + 1) == c1+         -> InternalSuccess () (offset chunk + 2) (length chunk - 2)+     | otherwise -> InternalFailure e++-- | Consume the three characters, failing if they do+-- not match they expected values.+--+-- > char3 e a b c === char e a *> char e b *> char e c+char3 :: e -> Char -> Char -> Char -> Parser e s ()+-- GHC should decide to inline this after optimization.+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+         -> InternalSuccess () (offset chunk + 3) (length chunk - 3)+     | otherwise -> InternalFailure e++-- | Consume the four characters, failing if they do+-- not match they 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 ()+-- GHC should decide to inline this after optimization.+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+         -> InternalSuccess () (offset chunk + 4) (length chunk - 4)+     | otherwise -> InternalFailure e++-- | Consumes and returns the next character in the input.+any :: e -> Parser e s Char+any e = uneffectful $ \chunk -> if length chunk > 0+  then+    let c = indexLatinCharArray (array chunk) (offset chunk)+     in InternalSuccess c (offset chunk + 1) (length chunk - 1)+  else InternalFailure 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)+opt = uneffectful $ \chunk -> case length chunk of+  0 -> InternalSuccess Nothing (offset chunk) (length chunk)+  _ -> InternalSuccess+    (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 ()+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#+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 ()+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 ()+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 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 #)+  )++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 = if length chunk0 > 0+  then+    let !w = fromIntegral @Word8 @Word+          (PM.indexByteArray (array chunk0) (offset chunk0)) - 48+        !acc' = acc * 10 + w+     in if w < 10 && acc' >= acc+          then decWordMore e acc' (Bytes.unsafeDrop 1 chunk0)+          else (# | (# unW acc, unI (offset chunk0), unI (length chunk0)  #) #)+  else (# | (# unW acc, unI (offset chunk0), 0# #) #)+++upcastWordResult :: Result# e Word# -> Result# e Word+upcastWordResult (# e | #) = (# e | #)+upcastWordResult (# | (# a, b, c #) #) = (# | (# W# a, b, c #) #)++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#+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+upcastWord16Result (# e | #) = (# e | #)+upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# a, b, c #) #)++-- Precondition: the word is small enough+upcastWord32Result :: Result# e Word# -> Result# e Word32+upcastWord32Result (# e | #) = (# e | #)+upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# a, b, c #) #)++-- Precondition: the word is small enough+upcastWord8Result :: Result# e Word# -> Result# e Word8+upcastWord8Result (# e | #) = (# e | #)+upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# 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 (I# w) (boxBytes chunk0) #))++-- | 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 (fromIntegral @Word @Int w) (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 (fromIntegral @Word @Int w) (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 (fromIntegral @Word @Int w) (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 (# s0, decNegIntMore e (negate (fromIntegral @Word @Int w)) (Bytes.unsafeDrop 1 chunk0) #)+          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.+decNegIntMore ::+     e -- Error message+  -> Int -- Accumulator+  -> Bytes -- Chunk+  -> Result# e Int#+decNegIntMore e !acc !chunk0 = if length chunk0 > 0+  then+    let !w = fromIntegral @Word8 @Word+          (PM.indexByteArray (array chunk0) (offset chunk0)) - 48+        !acc' = acc * 10 - (fromIntegral @Word @Int w)+     in if w < 10+          then if acc' <= acc+            then decNegIntMore e acc' (Bytes.unsafeDrop 1 chunk0)+            else (# e | #)+          else (# | (# unI acc, unI (offset chunk0), unI (length chunk0)  #) #)+  else (# | (# unI acc, unI (offset chunk0), 0# #) #)++-- This will not inline since it is recursive, but worker+-- wrapper will still happen. Fails if the accumulator+-- exceeds the size of a machine integer.+decPosIntMore ::+     e -- Error message+  -> Int -- Accumulator+  -> Bytes -- Chunk+  -> Result# e Int#+decPosIntMore e !acc !chunk0 = if len > 0+  then+    let !w = fromIntegral @Word8 @Word+          (PM.indexByteArray (array chunk0) (offset chunk0)) - 48+        !acc' = acc * 10 + (fromIntegral @Word @Int w)+     in if w < 10+          then if acc' >= acc+            then decPosIntMore e acc' (Bytes.unsafeDrop 1 chunk0)+            else (# e | #)+          else (# | (# unI acc, unI (offset chunk0), len# #) #)+  else (# | (# unI 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++-- | Skip all characters until the character from the 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+-- > |->->->->-|+skipTrailedBy :: e -> Char -> Parser e s ()+skipTrailedBy e !w = uneffectful# $ \c ->+  skipUntilConsumeLoop e w c++skipUntilConsumeLoop ::+     e -- Error message+  -> Char -- byte to match+  -> Bytes -- Chunk+  -> Result# e ()+skipUntilConsumeLoop e !w !c = if length c > 0+  then if indexLatinCharArray (array c) (offset c) /= w+    then skipUntilConsumeLoop e w (Bytes.unsafeDrop 1 c)+    else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #)+  else (# e | #)++-- | Parse exactly four ASCII-encoded characters, interpretting+-- them as the hexadecimal encoding of a 32-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@.+hexWord16 :: e -> Parser e s Word16+{-# inline hexWord16 #-}+hexWord16 e = Parser+  (\x s0 -> case runParser (hexWord16# e) x s0 of+    (# s1, r #) -> case r of+      (# err | #) -> (# s1, (# err | #) #)+      (# | (# a, b, c #) #) -> (# s1, (# | (# W16# a, b, c #) #) #)+  )++hexWord16# :: e -> Parser e s Word#+{-# noinline hexWord16# #-}+hexWord16# 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 | #)+++-- Returns the maximum machine word if the argument is not+-- the ASCII encoding of a hexadecimal digit.+oneHex :: Word8 -> Word+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++uneffectfulWord# :: (Bytes -> Result# e Word#) -> Parser e s Word#+uneffectfulWord# f = Parser+  ( \b s0 -> (# s0, (f (boxBytes b)) #) )+
+ src/Data/Bytes/Parser/Unsafe.hs view
@@ -0,0 +1,62 @@+{-# 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 #-}++-- | Everything in this module is unsafe and can lead to+-- nondeterministic output or segfaults if used incorrectly.+module Data.Bytes.Parser.Unsafe+  ( cursor+  , expose+  , unconsume+  , jump+  ) where++import Prelude hiding (length)++import Data.Primitive (ByteArray)+import Data.Bytes.Types (Bytes(..))+import Data.Bytes.Parser.Internal (Parser(..),uneffectful)+import Data.Bytes.Parser.Internal (InternalResult(..))++-- | 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 $ \chunk ->+  InternalSuccess (offset chunk) (offset chunk) (length chunk)++-- | 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 $ \chunk ->+  InternalSuccess (array chunk) (offset chunk) (length chunk)++-- | 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 $ \chunk ->+  InternalSuccess () (offset chunk - n) (length chunk + 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'.+jump :: Int -> Parser e s ()+jump ix = uneffectful $ \chunk ->+  InternalSuccess () ix (length chunk + (offset chunk - ix))+
+ src/Data/Bytes/Parser/Utf8.hs view
@@ -0,0 +1,143 @@+{-# 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 #-}++-- | 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 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 qualified Data.ByteString.Short.Internal as BSS+import qualified Data.Bytes.Parser as Parser+import qualified Data.Primitive as PM+import qualified Data.Text.Short as TS+import qualified GHC.Exts as Exts++-- | 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#+{-# noinline any# #-}+any# e = Parser+  (\(# arr, off, len #) s0 -> case len ># 0# of+    1# ->+      let !w0 = Exts.indexWord8Array# arr off+       in if | oneByteChar (W8# w0) -> +                 (# s0, (# | (# chr# (Exts.word2Int# w0), off +# 1#, len -# 1# #) #) #)+             | twoByteChar (W8# w0) ->+                 if | I# len > 1+                    , w1 <- Exts.indexWord8Array# arr (off +# 1#)+                    , followingByte (W8# w1)+                    , C# c <- codepointFromTwoBytes (W8# w0) (W8# w1)+                      -> (# s0, (# | (# c, off +# 2#, len -# 2# #) #) #)+                    | otherwise -> (# s0, (# e | #) #)+             | threeByteChar (W8# w0) ->+                 if | I# len > 2+                    , w1 <- Exts.indexWord8Array# arr (off +# 1# )+                    , w2 <- Exts.indexWord8Array# arr (off +# 2# )+                    , followingByte (W8# w1)+                    , !c@(C# c#) <- codepointFromThreeBytes (W8# w0) (W8# w1) (W8# w2)+                    , c < '\xD800' || c > '\xDFFF'+                      -> (# s0, (# | (# c#, off +# 3#, len -# 3# #) #) #)+                    | otherwise -> (# s0, (# e | #) #)+             | fourByteChar (W8# w0) ->+                 if | I# len > 3+                    , w1 <- Exts.indexWord8Array# arr (off +# 1# )+                    , w2 <- Exts.indexWord8Array# arr (off +# 2# )+                    , w3 <- Exts.indexWord8Array# arr (off +# 3# )+                    , followingByte (W8# w1)+                    , !(C# c#) <- codepointFromFourBytes (W8# w0) (W8# w1) (W8# w2) (W8# w3)+                      -> (# s0, (# | (# c#, off +# 4#, len -# 4# #) #) #)+                    | otherwise -> (# s0, (# e | #) #)+             | otherwise -> (# s0, (# e | #) #)+    _ -> (# s0, (# e | #) #)+  )++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)+      )+    )+  )++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)+      )+    )+  )++codepointFromTwoBytes :: Word8 -> Word8 -> Char+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++twoByteChar :: Word8 -> Bool+twoByteChar !w = w .&. 0b11100000 == 0b11000000++threeByteChar :: Word8 -> Bool+threeByteChar !w = w .&. 0b11110000 == 0b11100000++fourByteChar :: Word8 -> Bool+fourByteChar !w = w .&. 0b11111000 == 0b11110000++followingByte :: Word8 -> Bool+followingByte !w = xor w 0b01000000 .&. 0b11000000 == 0b11000000++word8ToWord :: Word8 -> Word+word8ToWord = fromIntegral++unI :: Int -> Int#+unI (I# w) = w++-- | 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))++shortByteStringToByteArray ::+     BSS.ShortByteString+  -> PM.ByteArray+shortByteStringToByteArray (BSS.SBS x) = PM.ByteArray x
test/Main.hs view
@@ -1,43 +1,167 @@ {-# language BangPatterns #-} {-# language MultiWayIf #-}+{-# language NumDecimals #-}+{-# language OverloadedStrings #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} -import Control.Exception (throwIO)-import Data.Primitive (ByteArray)-import Data.Word (Word8)+import Control.Monad (replicateM)+import Control.Monad.ST (runST)+import Data.Primitive (ByteArray,PrimArray)+import Data.Word (Word8,Word64) import Data.Char (ord) import Data.Bytes.Types (Bytes(Bytes))-import Data.Bytes.Parser (Parser,Result(..))+import Test.Tasty (defaultMain,testGroup,TestTree)+import Test.Tasty.HUnit ((@=?),testCase)+import Test.Tasty.QuickCheck ((===),testProperty) +import qualified Data.Bits as Bits import qualified Data.Bytes.Parser as P+import qualified Data.Bytes.Parser.Ascii as Ascii+import qualified Data.Bytes.Parser.Latin as Latin+import qualified Data.Bytes.Parser.BigEndian as BigEndian import qualified Data.Primitive as PM import qualified GHC.Exts as Exts+import qualified Test.Tasty.QuickCheck as QC  main :: IO ()-main = do-  putStrLn "Start"-  putStrLn "A"-  testA-  putStrLn "Finished"+main = defaultMain tests -testA :: IO ()-testA =-  let r = P.parseByteArray+tests :: TestTree+tests = testGroup "Parser"+  [ testProperty "decStandardInt" $ \i ->+      P.parseBytes (Latin.decStandardInt ()) (bytes (show i)) === P.Success i 0+  , testProperty "big-endian-word64" bigEndianWord64+  , testCase "delimit" $+      P.Success (167,14625) 0+      @=?+      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 4654667 3+        @=?+        P.parseBytes (Latin.decUnsignedInt ())+          (bytes "4654667,55")+    , testProperty "property" $ \(QC.NonNegative i) ->+        P.parseBytes (Latin.decUnsignedInt ()) (bytes (show i))+        ===+        P.Success i 0+    ]+  , testGroup "decPositiveInteger"+    [ testCase "A" $ +        P.parseBytes (Latin.decUnsignedInteger ())+          (bytes "5469999463123462573426452736423546373235260")+        @=?+        P.Success 5469999463123462573426452736423546373235260 0+    , testProperty "property" $ \(LargeInteger i) ->+        i >= 0+        QC.==>+        P.parseBytes (Latin.decUnsignedInteger ()) (bytes (show i))+        ===+        P.Success i 0+    ]+  , testGroup "decSignedInteger"+    [ testCase "A" $ +        P.parseBytes (Latin.decSignedInteger ())+          (bytes "-54699994631234625734264527364235463732352601")+        @=?+        P.Success (-54699994631234625734264527364235463732352601) 0+    , testProperty "property" $ \(LargeInteger i) ->+        P.parseBytes (Latin.decSignedInteger ()) (bytes (show i))+        ===+        P.Success i 0+    ]+  , testProperty "decSignedInt-A" $ \i ->+      P.parseBytes (Latin.decSignedInt ()) (bytes (show i)) === P.Success i 0+  , testProperty "decSignedInt-B" $ \i ->+      P.parseBytes+        (Latin.decSignedInt ())+        (bytes ((if i >= 0 then "+" else "") ++ show i))+      ===+      P.Success i 0+  , testCase "decWord-composition" $+      P.Success (42,8) 0+      @=?+      P.parseBytes         ( pure (,)-        <*> P.decWord ()-        <*  P.ascii () '.'-        <*> P.decWord ()-        <*  P.ascii () '.'-        ) (pack "42.8.")-   in case r of-        Failure () -> fail "test A failed parsing"-        Success (42,8) 5 0 -> pure ()-        Success _ _ _ -> fail "test A wrong result"+        <*> Ascii.decWord ()+        <*  Ascii.char () '.'+        <*> Ascii.decWord ()+        <*  Ascii.char () '.'+        ) (bytes "42.8.")+  , testCase "decWord-replicate" $+      P.Success (Exts.fromList [42,93] :: PrimArray Word) 0+      @=?+      P.parseBytes+        (P.replicate 2 (Ascii.decWord () <* Ascii.char () '.'))+        (bytes "42.93.")+  ]  bytes :: String -> Bytes-bytes s = let b = pack s in Bytes b 0 (PM.sizeofByteArray b)+bytes s = let b = pack ('x' : s) in Bytes b 1 (PM.sizeofByteArray b - 1)  pack :: String -> ByteArray pack = Exts.fromList . map (fromIntegral @Int @Word8 . ord)++bigEndianWord64 ::+     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+        PM.writeByteArray m 0 (0xFF :: Word8)+        PM.writeByteArray m 1 (0xFF :: Word8)+        PM.writeByteArray m 2 (a :: Word8)+        PM.writeByteArray m 3 (b :: Word8)+        PM.writeByteArray m 4 (c :: Word8)+        PM.writeByteArray m 5 (d :: Word8)+        PM.writeByteArray m 6 (e :: Word8)+        PM.writeByteArray m 7 (f :: Word8)+        PM.writeByteArray m 8 (g :: Word8)+        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)+   in P.parseBytes (BigEndian.word64 ()) (Bytes arr 2 9)+      ===+      P.Success expected 1++-- The Arbitrary instance for Integer that comes with+-- QuickCheck only generates small numbers.+newtype LargeInteger = LargeInteger Integer+  deriving (Eq,Show)++instance QC.Arbitrary LargeInteger where+  arbitrary = QC.sized $ \sz -> do+      n <- QC.choose (1, sz)+      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