    {-# LANGUAGE MagicHash, UnboxedTuples #-}

    -- |
    -- Module      : Data.Primitive.Addr
    -- Copyright   : (c) Roman Leshchinskiy 2009-2012
    -- License     : BSD-style
    --
    -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
    -- Portability : non-portable
    --
    -- Primitive operations on machine addresses
    --

    module Data.Primitive.Addr (
      -- * Types
      Addr(..),

      -- * Address arithmetic
      nullAddr, plusAddr, minusAddr, remAddr,

      -- * Element access
      indexOffAddr, readOffAddr, writeOffAddr,

      -- * Block operations
      copyAddr, moveAddr, setAddr
    ) where

    import Control.Monad.Primitive
    import Data.Primitive.Types

    import GHC.Base ( Int(..) )
    import GHC.Prim

    import GHC.Ptr
    import Foreign.Marshal.Utils

    -- | The null address
    nullAddr :: Addr
    nullAddr = Addr nullAddr#

    infixl 6 `plusAddr`, `minusAddr`
    infixl 7 `remAddr`

    -- | Offset an address by the given number of bytes
    plusAddr :: Addr -> Int -> Addr
    plusAddr (Addr a#) (I# i#) = Addr (plusAddr# a# i#)

    -- | Distance in bytes between two addresses. The result is only valid if the
    -- difference fits in an 'Int'.
    minusAddr :: Addr -> Addr -> Int
    minusAddr (Addr a#) (Addr b#) = I# (minusAddr# a# b#)

    -- | The remainder of the address and the integer.
    remAddr :: Addr -> Int -> Int
    remAddr (Addr a#) (I# i#) = I# (remAddr# a# i#)

    -- | Read a value from a memory position given by an address and an offset.
    -- The memory block the address refers to must be immutable. The offset is in
    -- elements of type @a@ rather than in bytes.
    indexOffAddr :: Prim a => Addr -> Int -> a
    {-# INLINE indexOffAddr #-}
    indexOffAddr (Addr addr#) (I# i#) = indexOffAddr# addr# i#

    -- | Read a value from a memory position given by an address and an offset.
    -- The offset is in elements of type @a@ rather than in bytes.
    readOffAddr :: (Prim a, PrimMonad m) => Addr -> Int -> m a
    {-# INLINE readOffAddr #-}
    readOffAddr (Addr addr#) (I# i#) = primitive (readOffAddr# addr# i#)

    -- | Write a value to a memory position given by an address and an offset.
    -- The offset is in elements of type @a@ rather than in bytes.
    writeOffAddr :: (Prim a, PrimMonad m) => Addr -> Int -> a -> m ()
    {-# INLINE writeOffAddr #-}
    writeOffAddr (Addr addr#) (I# i#) x = primitive_ (writeOffAddr# addr# i# x)

    -- | Copy the given number of bytes from the second 'Addr' to the first. The
    -- areas may not overlap.
    copyAddr :: PrimMonad m => Addr         -- ^ destination address
                            -> Addr         -- ^ source address
                            -> Int          -- ^ number of bytes
                            -> m ()
    {-# INLINE copyAddr #-}
    copyAddr (Addr dst#) (Addr src#) n
      = unsafePrimToPrim $ copyBytes (Ptr dst#) (Ptr src#) n

    -- | Copy the given number of bytes from the second 'Addr' to the first. The
    -- areas may overlap.
    moveAddr :: PrimMonad m => Addr         -- ^ destination address
                            -> Addr         -- ^ source address
                            -> Int          -- ^ number of bytes
                            -> m ()
    {-# INLINE moveAddr #-}
    moveAddr (Addr dst#) (Addr src#) n
      = unsafePrimToPrim $ moveBytes (Ptr dst#) (Ptr src#) n

    -- | Fill a memory block of with the given value. The length is in
    -- elements of type @a@ rather than in bytes.
    setAddr :: (Prim a, PrimMonad m) => Addr -> Int -> a -> m ()
    {-# INLINE setAddr #-}
    setAddr (Addr addr#) (I# n#) x = primitive_ (setOffAddr# addr# 0# n# x)