packages feed

flatparse-0.4.0.0: src/FlatParse/Common/Position.hs

{-# LANGUAGE DerivingStrategies, DerivingVia #-}

module FlatParse.Common.Position
  ( Pos(..), endPos, addrToPos#, posToAddr#
  , Span(..), unsafeSlice
  ) where

import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import GHC.Int
import GHC.ForeignPtr ( ForeignPtr(..) )
import GHC.Exts

-- | Byte offset counted backwards from the end of the buffer.
newtype Pos = Pos { unPos :: Int }
    deriving stock (Show)
    deriving (Eq, Ord) via Int

-- | A pair of positions.
data Span = Span !Pos !Pos
    deriving stock (Eq, Show)

-- | Very unsafe conversion between a primitive address and a position.  The
--   first argument points to the end of the buffer, the second argument is
--   being converted.
addrToPos# :: Addr# -> Addr# -> Pos
addrToPos# eob s = Pos (I# (minusAddr# eob s))
{-# inline addrToPos# #-}

-- | Very unsafe conversion between a primitive address and a position.  The
--   first argument points to the end of the buffer.
posToAddr# :: Addr# -> Pos -> Addr#
posToAddr# eob (Pos (I# n)) = unsafeCoerce# (minusAddr# eob (unsafeCoerce# n))
{-# inline posToAddr# #-}

-- | Slice into a `B.ByteString` using a `Span`. The result is invalid if the `Span`
--   is not a valid slice of the first argument.
unsafeSlice :: B.ByteString -> Span -> B.ByteString
unsafeSlice (B.PS (ForeignPtr addr fp) (I# start) (I# len))
            (Span (Pos (I# o1)) (Pos (I# o2))) =
  let end = addr `plusAddr#` start `plusAddr#` len
  in B.PS (ForeignPtr (plusAddr# end (negateInt# o1)) fp) (I# 0#) (I# (o1 -# o2))
{-# inline unsafeSlice #-}

-- | The end of the input.
endPos :: Pos
endPos = Pos 0
{-# inline endPos #-}