packages feed

bytearray-parsing-0.1: library/ByteArray/Parsing/Parse.hs

module ByteArray.Parsing.Parse
where

import ByteArray.Parsing.Prelude hiding (take, takeWhile)
import qualified Data.ByteString.Internal as A
import qualified GHC.ForeignPtr as B
import qualified Data.Primitive.ByteArray as C
import qualified Data.Primitive.Addr as D
import qualified ByteArray.Parsing.Prim as E


newtype Parse a =
  Parse (forall x. C.ByteArray -> Int -> (Int -> x) -> (Text -> x) -> (Int -> a -> x) -> x)

instance Functor Parse where
  {-# INLINE fmap #-}
  fmap mapping (Parse parse) =
    Parse $ \ array index needMoreResult errorResult successResult ->
    {-# SCC "fmap" #-} 
    parse array index needMoreResult errorResult $ \ newIndex parsed ->
    successResult newIndex (mapping parsed)

instance Applicative Parse where
  {-# INLINE pure #-}
  pure x =
    Parse $ \ _ index _ _ successResult -> successResult index x
  {-# INLINE (<*>) #-}
  (<*>) (Parse parseLeft) (Parse parseRight) =
    Parse $ \ array index needMoreResult errorResult successResult ->
    {-# SCC "<*>" #-} 
    parseLeft array index needMoreResult errorResult $ \ leftConsumed leftParsed ->
    parseRight array leftConsumed needMoreResult errorResult $ \ rightConsumed rightParsed ->
    successResult rightConsumed (leftParsed rightParsed)

instance Alternative Parse where
  {-# INLINE empty #-}
  empty =
    Parse $ \ _ _ needMoreResult _ _ -> needMoreResult 0
  {-# INLINE (<|>) #-}
  (<|>) (Parse parseLeft) (Parse parseRight) =
    Parse $ \ array index needMoreResult errorResult successResult ->
    {-# SCC "<|>" #-} 
    parseLeft array index
      (\ _ -> parseRight array index needMoreResult errorResult successResult)
      errorResult
      successResult

instance Monad Parse where
  {-# INLINE return #-}
  return =
    pure
  {-# INLINE (>>=) #-}
  (>>=) (Parse parseLeft) rightK =
    Parse $ \ array index needMoreResult errorResult successResult ->
    {-# SCC ">>=" #-} 
    parseLeft array index needMoreResult errorResult $ \ leftConsumed leftParsed ->
    case rightK leftParsed of
      Parse parseRight ->
        parseRight array leftConsumed needMoreResult errorResult successResult

instance MonadPlus Parse where
  mzero = empty
  mplus = (<|>)

{-# INLINE fail #-}
fail :: Text -> Parse a
fail message =
  Parse $ \ _ _ _ errorResult _ ->
  errorResult message

{-# INLINE word8 #-}
word8 :: Parse Word8
word8 =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "word8" #-} 
  if C.sizeofByteArray array >= index
    then successResult (succ index) (C.indexByteArray array index)
    else needMoreResult 1

{-# INLINE beNum #-}
beNum :: (Bits a, Num a) => Int -> Parse a
beNum size =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "beNum" #-} 
  let
    arrayLength = C.sizeofByteArray array
    nextIndex = index + size
    in if arrayLength >= size
      then let
        fold !index !state =
          if index < nextIndex
            then fold (succ index) (shiftL state 8 .|. fromIntegral (C.indexByteArray array index :: Word8))
            else state
        in successResult (nextIndex) (fold index 0)
      else needMoreResult (nextIndex - arrayLength)

{-# INLINE beWord16 #-}
beWord16 :: Parse Word16
beWord16 =
  {-# SCC "beWord16" #-} 
  beNum 2

{-# INLINE beWord32 #-}
beWord32 :: Parse Word32
beWord32 =
  {-# SCC "beWord32" #-} 
  beNum 4

{-# INLINE beWord64 #-}
beWord64 :: Parse Word64
beWord64 =
  {-# SCC "beWord64" #-} 
  beNum 8

{-# INLINE shortByteString #-}
shortByteString :: Int -> Parse ShortByteString
shortByteString size =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "shortByteString" #-} 
  let
    arrayLength = C.sizeofByteArray array
    nextIndex = index + size
    in if arrayLength >= nextIndex
      then successResult nextIndex (E.byteArraySliceToShortByteString index size array)
      else needMoreResult (nextIndex - arrayLength)

{-# INLINE byteString #-}
byteString :: Int -> Parse ByteString
byteString size =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "byteString" #-} 
  let
    arrayLength = C.sizeofByteArray array
    nextIndex = index + size
    in if arrayLength >= nextIndex
      then successResult nextIndex (E.byteArraySliceToByteString index size array)
      else needMoreResult (nextIndex - arrayLength)

{-# INLINE remainingByteString #-}
remainingByteString :: Parse ByteString
remainingByteString =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "remainingByteString" #-} 
  let
    arrayLength =
      C.sizeofByteArray array
    size =
      arrayLength - index
    in successResult arrayLength (E.byteArraySliceToByteString index (arrayLength - index) array)

{-# INLINE nullTerminatedByteString #-}
nullTerminatedByteString :: Parse ByteString
nullTerminatedByteString =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "nullTerminatedBytes" #-}
  let
    findNullIndex !index =
      if (C.indexByteArray array index :: Word8) == 0
        then index
        else findNullIndex (succ index)
    nullIndex =
      findNullIndex index
    nextIndex =
      succ nullIndex
    size =
      nullIndex - index
    in successResult nextIndex (E.byteArraySliceToByteString index size array)

{-# INLINE byteStringWhile #-}
byteStringWhile :: (Word8 -> Bool) -> Parse ByteString
byteStringWhile predicate =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "byteStringWhile" #-} 
  let
    iterate !index =
      if predicate (C.indexByteArray array index)
        then iterate (succ index)
        else index
    nextIndex =
      iterate index
    size =
      nextIndex - index
    in successResult nextIndex (E.byteArraySliceToByteString index size array)

{-# INLINE skipWhile #-}
skipWhile :: (Word8 -> Bool) -> Parse ()
skipWhile predicate =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "skipWhile" #-} 
  let
    iterate !index =
      if predicate (C.indexByteArray array index)
        then iterate (succ index)
        else index
    nextIndex =
      iterate index
    size =
      nextIndex - index
    in successResult nextIndex ()

{-# INLINE foldWhile #-}
foldWhile :: (Word8 -> Bool) -> (state -> Word8 -> state) -> state -> Parse state
foldWhile predicate step start =
  Parse $ \ array index needMoreResult errorResult successResult ->
  {-# SCC "foldWhile" #-} 
  let
    iterate !index !state =
      let
        byte =
          C.indexByteArray array index
        in if predicate byte
          then iterate (succ index) (step state byte)
          else successResult index state
    in iterate index start

-- |
-- Unsigned integral number encoded in ASCII.
{-# INLINE unsignedASCIIIntegral #-}
unsignedASCIIIntegral :: Integral a => Parse a
unsignedASCIIIntegral =
  {-# SCC "unsignedASCIIIntegral" #-} 
  foldWhile byteIsDigit step 0
  where
    byteIsDigit byte =
      byte - 48 <= 9
    step !state !byte =
      state * 10 + fromIntegral byte - 48