packages feed

storable-peek-poke-0.1.0.1: src/Foreign/Storable/PeekPoke/Internal.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments, LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables, RankNTypes, TypeApplications #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Foreign.Storable.PeekPoke.Internal (

	-- * Sizable, Peek and Poke

	Sizable(..), Peek(..), peekMaybe, Poke(..),

	-- * Peekable, WithPoked and Pokable

	Peekable, peekArray',
	WithPoked(..), PtrS(..), ptrS, withPtrS, withPokedMaybe',
	Pokable, withPoked, withPokedMaybe,

	-- * Storable' and NullPtr

	Storable', pattern NullPtr ) where

import Foreign.Ptr
import Foreign.Marshal.Alloc
import Foreign.Storable
import Data.TypeLevel.Maybe qualified as TMaybe
import Data.TypeLevel.ParMaybe qualified as TPMaybe

class Sizable a where sizeOf' :: Int; alignment' :: Int
class Peek a where peek' :: Ptr a -> IO a
class Poke a where poke' :: Ptr a -> a -> IO ()

class (Sizable a, Peek a) => Peekable a
instance (Sizable a, Peek a) => Peekable a

class (Sizable a, Poke a, WithPoked a) => Pokable a
instance (Sizable a, Poke a) => Pokable a

class (Peekable a, Pokable a, Storable a) => Storable' a
instance (Peekable a, Pokable a, Storable a) => Storable' a

instance {-# OVERLAPPABLE #-} Storable a => Sizable a where
	sizeOf' = sizeOf @a undefined
	alignment' = alignment @a undefined

instance {-# OVERLAPPABLE #-} Storable a => Peek a where peek' = peek
instance {-# OVERLAPPABLE #-} Storable a => Poke a where poke' = poke

withPoked :: Pokable a => a -> (Ptr a -> IO b) -> IO b
withPoked x f = alloca' \p -> poke' p x >> f p

withPokedMaybe :: Pokable a => Maybe a -> (Ptr a -> IO b) -> IO b
withPokedMaybe = \case Nothing -> ($ NullPtr); Just x -> withPoked x

alloca' :: forall a b . Sizable a => (Ptr a -> IO b) -> IO b
alloca' = allocaBytesAligned (sizeOf' @a) (alignment' @a)

pattern NullPtr :: Ptr a
pattern NullPtr <- ((== nullPtr) -> True) where NullPtr = nullPtr

peekMaybe :: Peek a => Ptr a -> IO (Maybe a)
peekMaybe = \case NullPtr -> pure Nothing; p -> Just <$> peek' p

peekArray' :: forall a . Peekable a => Int -> Ptr a -> IO [a]
peekArray' n ((`alignPtr` (alignment' @a)) -> p)
	| n <= 0 = pure []
	| True = (:) <$> peek' p <*> peekArray' (n - 1) (p `plusPtr` sizeOf' @a)

newtype PtrS s a = PtrS_ (Ptr a) deriving Show

ptrS :: Ptr a -> PtrS s a
ptrS = PtrS_

castPtrS :: PtrS s a -> PtrS s b
castPtrS (PtrS_ p) = PtrS_ $ castPtr p

pattern NullPtrS :: PtrS s a
pattern NullPtrS <- PtrS_ NullPtr where NullPtrS = PtrS_ NullPtr

withPtrS :: PtrS s a -> (Ptr a -> IO b) -> IO ()
withPtrS (PtrS_ p) = (() <$) . ($ p)

class WithPoked a where
	withPoked' :: a -> (forall s . PtrS s a -> IO b) -> IO b

instance {-# OVERLAPPABLE #-} Pokable a => WithPoked a where
	withPoked' x f = withPoked x $ f . ptrS

withPokedMaybe' :: WithPoked a =>
	Maybe a -> (forall s . PtrS s a -> IO b) -> IO b
withPokedMaybe' = \case Nothing -> ($ NullPtrS); Just x -> withPoked' x

instance WithPoked (TPMaybe.M t 'Nothing) where
	withPoked' TPMaybe.N f = f $ ptrS nullPtr

instance WithPoked (t a) => WithPoked (TPMaybe.M t ('Just a)) where
	withPoked' (TPMaybe.J x) f = withPoked' x (f . castPtrS)

instance {-# OVERLAPPABLE #-} WithPoked a => WithPoked (TMaybe.Id a) where
	withPoked' (TMaybe.Id x) f = withPoked' x (f . castPtrS)