content: {-# LANGUAGE DerivingStrategies, DerivingVia #-}
  
  module FlatParse.Common.Position
    ( Pos(..), endPos, addrToPos#, posToAddr#
    , Span(..), unsafeSlice, leftPos, rightPos
    ) 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.
  --   Note: the `Ord` instance for `Pos` considers the earlier positions to be
  --   smaller.
  newtype Pos = Pos { unPos :: Int }
      deriving stock (Show)
      deriving Eq via Int
  
  instance Ord Pos where
    (<=) (Pos x) (Pos y) = y <= x
    {-# inline (<=) #-}
    compare (Pos x) (Pos y) = compare y x
    {-# inline compare #-}
  
  -- | 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 #-}
  
  leftPos :: Span -> Pos
  leftPos (Span p _) = p
  {-# inline leftPos #-}
  
  rightPos :: Span -> Pos
  rightPos (Span _ p) = p
  {-# inline rightPos #-}
  