packages feed

sdl3-bindgen-sys-0.0.0.1: runtime/HsBindgen/Runtime/BitfieldPtr.hs

-- | Pointers to bitfields
--
-- This module is intended to be imported qualified.
--
-- > import HsBindgen.Runtime.Prelude
-- > import HsBindgen.Runtime.BitfieldPtr qualified as BitfieldPtr
module HsBindgen.Runtime.BitfieldPtr (
  Bitfield (..),
  BitfieldPtr, -- opaque
  mkBitfieldPtr,
  startingByte,
  offset,
  width,
  bounds,
  peek,
  poke,
) where

import Data.Kind
import Data.Proxy
import Foreign.Ptr

import HsBindgen.Runtime.Marshal qualified as Marshal
import HsBindgen.Runtime.Support.Bitfield as Bitfield

-- | Pointer to a bit-field of a C object
--
-- A @BitfieldPtr a@ is for a bit-field of type @a@.  For example, a
-- @Bitfield CUInt@ points to a bit-field of type @CUInt@.
type BitfieldPtr :: Type -> Type

type role BitfieldPtr nominal
data BitfieldPtr a = UnsafeBitfieldPtr
  { startingByte :: Ptr ()
  -- ^ Pointer to the byte where the bit-field starts
  --
  -- We do /not/ assume that the pointer is aligned.
  , offset :: Int
  -- ^ Offset of the bit-field (0 to 7 bits)
  , width :: Int
  -- ^ Width of the bit-field (1 to 64 bits)
  , bounds :: (Ptr (), Ptr ())
  -- ^ Memory bounds of the @struct@
  --
  -- The lower bound is inclusive, and the upper bound is exclusive.
  --
  -- To peek/poke a bit-field, we may peek/poke any memory within these
  -- bounds.  We must not peek/poke memory outside of these bounds.
  }

-- | Construct a 'BitfieldPtr' given the C object pointer, offset, and width
mkBitfieldPtr
  :: forall s a
   . (Marshal.StaticSize s)
  => Ptr s
  -- ^ Pointer to the C object with the bit-field
  -> Int
  -- ^ Offset of the bit-field (0 or more bits)
  -> Int
  -- ^ Width of the bit-field (1 to 64 bits)
  -> BitfieldPtr a
mkBitfieldPtr ptr off width
  | width < 1 || width > 64 =
      error $ "invalid bit-field width: " ++ show width
  | otherwise =
      UnsafeBitfieldPtr
        { startingByte = castPtr $ ptr `plusPtr` offBytes
        , offset = offBits
        , width = width
        , bounds = (ptrL, ptrH)
        }
 where
  offBytes, offBits :: Int
  (offBytes, offBits) = off `quotRem` 8

  ptrL, ptrH :: Ptr ()
  ptrL = castPtr ptr
  ptrH = ptrL `plusPtr` Marshal.staticSizeOf @s Proxy

{-# INLINE peek #-}

-- | Read from a bit-field
peek :: (Bitfield a) => BitfieldPtr a -> IO a
peek (UnsafeBitfieldPtr p o w b) = Bitfield.peekBitOffWidth p o w b

{-# INLINE poke #-}

-- | Write to a bit-field
poke :: (Bitfield a) => BitfieldPtr a -> a -> IO ()
poke (UnsafeBitfieldPtr p o w b) = Bitfield.pokeBitOffWidth p o w b