-- | An applicative version of parser. This provides a restricted -- parser which has only an applicative instance. module Raaz.Core.Parse.Applicative ( Parser, parseWidth, parseError, runParser , unsafeRunParser , parse, parseStorable , parseVector, parseStorableVector , unsafeParseVector, unsafeParseStorableVector , parseByteString , skip ) where import Data.ByteString (ByteString) import Data.Vector.Generic (Vector, generateM) import Foreign.Ptr (castPtr) import Foreign.Storable (Storable, peek, peekElemOff) import Prelude hiding ( length ) import System.IO.Unsafe (unsafePerformIO) import Raaz.Core.MonoidalAction import Raaz.Core.Types.Endian import Raaz.Core.Types.Pointer import Raaz.Core.Util.ByteString (createFrom, length, withByteString) type BytesMonoid = BYTES Int type ParseAction = FieldM IO Pointer -- | An applicative parser type for reading data from a pointer. type Parser = TwistRF ParseAction BytesMonoid makeParser :: LengthUnit l => l -> (Pointer -> IO a) -> Parser a makeParser l action = TwistRF (liftToFieldM action) $ inBytes l -- | Skip over some data. skip :: LengthUnit u => u -> Parser () skip = flip makeParser $ const $ return () -- | A parser that fails with a given error message. parseError :: String -> Parser a parseError msg = makeParser (0 :: BYTES Int) $ \ _ -> fail msg -- | Return the bytes that this parser will read. parseWidth :: Parser a -> BYTES Int parseWidth = twistMonoidValue -- | Runs a parser on a byte string. It returns `Nothing` if the byte string is smaller than -- what the parser would consume. runParser :: Parser a -> ByteString -> Maybe a runParser pr bs | length bs < parseWidth pr = Nothing | otherwise = Just $ unsafePerformIO $ withByteString bs $ unsafeRunParser pr -- | Run the parser without checking the length constraints. unsafeRunParser :: Parser a -> Pointer -> IO a unsafeRunParser = runFieldM . twistFunctorValue -- | The primary purpose of this function is to satisfy type checkers. undefParse :: Parser a -> a undefParse _ = undefined -- | Parses a value which is an instance of Storable. Beware that this -- parser expects that the value is stored in machine endian. Mostly -- it is useful in defining the `peek` function in a complicated -- `Storable` instance. parseStorable :: Storable a => Parser a parseStorable = pa where pa = makeParser (sizeOf $ undefParse pa) (peek . castPtr) -- | Parse a crypto value. Endian safety is take into account -- here. This is what you would need when you parse packets from an -- external source. You can also use this to define the `load` -- function in a complicated `EndianStore` instance. parse :: EndianStore a => Parser a parse = pa where pa = makeParser (sizeOf $ undefParse pa) (load . castPtr) -- | Parses a strict bytestring of a given length. parseByteString :: LengthUnit l => l -> Parser ByteString parseByteString l = makeParser l $ createFrom l -- | Similar to @parseStorableVector@ but is expected to be slightly -- faster. It does not check whether the length parameter is -- non-negative and hence is unsafe. Use it only if you can prove that -- the length parameter is non-negative. unsafeParseStorableVector :: (Storable a, Vector v a) => Int -> Parser (v a) unsafeParseStorableVector n = pvec where pvec = makeParser width $ \ cptr -> generateM n (getA cptr) width = fromIntegral n * sizeOf (undefA pvec) undefA :: (Storable a, Vector v a)=> Parser (v a) -> a undefA _ = undefined getA = peekElemOff . castPtr -- | Similar to @parseVector@ but is expected to be slightly -- faster. It does not check whether the length parameter is -- non-negative and hence is unsafe. Use it only if you can prove that -- the length parameter is non-negative. unsafeParseVector :: (EndianStore a, Vector v a) => Int -> Parser (v a) unsafeParseVector n = pvec where pvec = makeParser width $ \ cptr -> generateM n (loadFromIndex (castPtr cptr)) width = fromIntegral n * sizeOf (undefA pvec) undefA :: (EndianStore a, Vector v a)=> Parser (v a) -> a undefA _ = undefined -- | Similar to `parseVector` but parses according to the host -- endian. This function is essentially used to define storable -- instances of complicated data. It is unlikely to be of use when -- parsing externally serialised data as one would want to keep track -- of the endianness of the data. parseStorableVector :: (Storable a, Vector v a) => Int -> Parser (v a) parseStorableVector n | n < 0 = parseError $ "parseStorableVector on " ++ show n | otherwise = unsafeParseStorableVector n -- | Parses a vector of elements. It takes care of the correct endian -- conversion. This is the function to use while parsing external -- data. parseVector :: (EndianStore a, Vector v a) => Int -> Parser (v a) parseVector n | n < 0 = parseError $ "parseVector on " ++ show n | otherwise = unsafeParseVector n