{- | Unsafe, highly dangerous parsing primitives using 'Addr#'.

Ensure to read the documentation before using any definitions from this module.

This module exports primitives useful for efficiently parsing binary files that
store data using an internal index.

Often, such indices describes records using a starting offset and a length.
Offsets are often relative to the file start, or some dynamic address in the
file. This way, individual records can be read out efficiently (much faster than
opening lots of small files!).

We may parse these in-place efficiently by adding record offsets to a base
memory address somewhere in the input. This is also extremely unsafe, and easy
to get catastrophically wrong. Thus, we provide as much utility as reasonable to
enable performing such parsing safely. (That means CPS functions.)

Note that all definitions here should be considered unsafe. Any 'Int#' is not
checked for positivity. You must perform any necessary checks when you obtain
your offsets and lengths as 'Int#'. Failure to do so may result in undefined
behaviour.
-}

module FlatParse.Stateful.Addr where

import FlatParse.Stateful.Parser
import FlatParse.Stateful.Base ( takeUnsafe#, atSkipUnsafe#, lookahead )

import GHC.Exts

import qualified Data.ByteString as B

-- | Run a parser, passing it the current address the parser is at.
--
-- Useful for parsing offset-based data tables. For example, you may use this to
-- save the base address to use together with various relative offsets.
withAddr# :: (Addr# -> ParserT st r e a) -> ParserT st r e a
withAddr# :: forall (st :: ZeroBitType) r e a.
(Addr# -> ParserT st r e a) -> ParserT st r e a
withAddr# Addr# -> ParserT st r e a
p = (ForeignPtrContents
 -> r -> Addr# -> Addr# -> Int# -> st -> Res# st e a)
-> ParserT st r e a
forall (st :: ZeroBitType) r e a.
(ForeignPtrContents
 -> r -> Addr# -> Addr# -> Int# -> st -> Res# st e a)
-> ParserT st r e a
ParserT \ForeignPtrContents
fp !r
r Addr#
eob Addr#
s Int#
n st
st -> ParserT st r e a
-> ForeignPtrContents
-> r
-> Addr#
-> Addr#
-> Int#
-> st
-> Res# st e a
forall (st :: ZeroBitType) r e a.
ParserT st r e a
-> ForeignPtrContents
-> r
-> Addr#
-> Addr#
-> Int#
-> st
-> Res# st e a
runParserT# (Addr# -> ParserT st r e a
p Addr#
s) ForeignPtrContents
fp r
r Addr#
eob Addr#
s Int#
n st
st
{-# inline withAddr# #-}

-- | @takeOffAddr# addr# offset# len#@ moves to @addr#@, skips @offset#@
--   bytes, reads @len#@ bytes into a 'ByteString', and restores the original
--   address.
--
-- The 'Addr#' should be from 'withAddr#'.
--
-- Useful for parsing offset-based data tables. Ex: Your file contains an index
-- storing @(OFFSET, LENGTH)@ entries where the offset is the byte position in
-- the file. Begin with @'withAddr#' $ \tableBase# -> ...@, then read each entry
-- like @'takeOffAddr#' tableBase# OFFSET LENGTH@.
--
-- Fails if you attempt to read outside the input.
--
-- Undefined behaviour if @offset#@ or @len#@ is negative.
--
-- Name adopted from the similar-ish @indexXOffAddr#@ primops.
takeOffAddr# :: Addr# -> Int# -> Int# -> ParserT st r e B.ByteString
takeOffAddr# :: forall (st :: ZeroBitType) r e.
Addr# -> Int# -> Int# -> ParserT st r e ByteString
takeOffAddr# Addr#
addr# Int#
offset# Int#
len# = Addr#
-> Int# -> ParserT st r e ByteString -> ParserT st r e ByteString
forall (st :: ZeroBitType) r e a.
Addr# -> Int# -> ParserT st r e a -> ParserT st r e a
withOffAddr# Addr#
addr# Int#
offset# (Int# -> ParserT st r e ByteString
forall (st :: ZeroBitType) r e. Int# -> ParserT st r e ByteString
takeUnsafe# Int#
len#)
{-# inline takeOffAddr# #-}

-- | @withOffAddr# addr# offset# p@ moves to @addr#@, skips @offset#@
--   bytes, then runs the given parser @p@.
--
-- The 'Addr#' should be from 'withAddr#'.
--
-- Fails if you attempt to read outside the input.
--
-- Undefined behaviour if @offset#@ is negative.
--
-- Name adopted from the similar-ish @indexXOffAddr#@ primops.
withOffAddr# :: Addr# -> Int# -> ParserT st r e a -> ParserT st r e a
withOffAddr# :: forall (st :: ZeroBitType) r e a.
Addr# -> Int# -> ParserT st r e a -> ParserT st r e a
withOffAddr# Addr#
addr# Int#
offset# =
    Addr# -> ParserT st r e a -> ParserT st r e a
forall (st :: ZeroBitType) r e a.
Addr# -> ParserT st r e a -> ParserT st r e a
lookaheadFromAddr# Addr#
addr# (ParserT st r e a -> ParserT st r e a)
-> (ParserT st r e a -> ParserT st r e a)
-> ParserT st r e a
-> ParserT st r e a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int# -> ParserT st r e a -> ParserT st r e a
forall (st :: ZeroBitType) r e ret.
Int# -> ParserT st r e ret -> ParserT st r e ret
atSkipUnsafe# Int#
offset#
{-# inline withOffAddr# #-}

-- | 'lookahead', but specify the address to lookahead from.
--
-- The 'Addr#' should be from 'withAddr#'.
lookaheadFromAddr# :: Addr# -> ParserT st r e a -> ParserT st r e a
lookaheadFromAddr# :: forall (st :: ZeroBitType) r e a.
Addr# -> ParserT st r e a -> ParserT st r e a
lookaheadFromAddr# Addr#
s = ParserT st r e a -> ParserT st r e a
forall (st :: ZeroBitType) r e a.
ParserT st r e a -> ParserT st r e a
lookahead (ParserT st r e a -> ParserT st r e a)
-> (ParserT st r e a -> ParserT st r e a)
-> ParserT st r e a
-> ParserT st r e a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Addr# -> ParserT st r e a -> ParserT st r e a
forall (st :: ZeroBitType) r e a.
Addr# -> ParserT st r e a -> ParserT st r e a
atAddr# Addr#
s
{-# inline lookaheadFromAddr# #-}

-- | Run a parser at the given address.
--
-- The 'Addr#' should be from 'withAddr#'.
--
-- This is a highly internal function -- you likely want 'lookaheadFromAddr#',
-- which will reset the address after running the parser.
atAddr# :: Addr# -> ParserT st r e a -> ParserT st r e a
atAddr# :: forall (st :: ZeroBitType) r e a.
Addr# -> ParserT st r e a -> ParserT st r e a
atAddr# Addr#
s (ParserT ForeignPtrContents
-> r -> Addr# -> Addr# -> Int# -> st -> Res# st e a
p) = (ForeignPtrContents
 -> r -> Addr# -> Addr# -> Int# -> st -> Res# st e a)
-> ParserT st r e a
forall (st :: ZeroBitType) r e a.
(ForeignPtrContents
 -> r -> Addr# -> Addr# -> Int# -> st -> Res# st e a)
-> ParserT st r e a
ParserT \ForeignPtrContents
fp !r
r Addr#
eob Addr#
_ Int#
n st
st -> ForeignPtrContents
-> r -> Addr# -> Addr# -> Int# -> st -> Res# st e a
p ForeignPtrContents
fp r
r Addr#
eob Addr#
s Int#
n st
st
{-# inline atAddr# #-}