{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
-- | @std::set@
module Hercules.CNix.Std.Set
( stdSetCtx,
instanceStdSet,
instanceStdSetCopyable,
CStdSet,
StdSet (StdSet),
Hercules.CNix.Std.Set.new,
size,
toSet,
fromList,
fromListP,
fromListFP,
Hercules.CNix.Std.Set.toList,
insert,
insertP,
insertFP,
toListFP,
)
where
import Control.Exception (mask_)
import Data.Coerce (Coercible, coerce)
import Data.Foldable (for_)
import qualified Data.Set as S
import Data.Traversable (for)
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Storable.Mutable as VSM
import Foreign
( ForeignPtr,
FunPtr,
Ptr,
Storable,
newForeignPtr,
withForeignPtr,
)
import Foreign.C (CSize)
import Hercules.CNix.Encapsulation (HasEncapsulation (..))
import qualified Language.C.Inline as C
import qualified Language.C.Inline.Cpp as C
import qualified Language.C.Inline.Unsafe as CU
import Language.Haskell.TH (DecsQ)
import Language.Haskell.TH.Syntax (Dec, Q)
import Prelude
data CStdSet a
stdSetCtx :: C.Context
stdSetCtx = C.cppCtx `mappend` C.cppTypePairs [("std::set", [t|CStdSet|])]
newtype StdSet a = StdSet (ForeignPtr (CStdSet a))
instance HasStdSet a => HasEncapsulation (CStdSet a) (StdSet a) where
moveToForeignPtrWrapper = fmap StdSet . newForeignPtr cDelete
class HasStdSet a where
cNew :: IO (Ptr (CStdSet a))
cDelete :: FunPtr (Ptr (CStdSet a) -> IO ())
cSize :: Ptr (CStdSet a) -> IO CSize
cInsertByPtr :: Ptr a -> Ptr (CStdSet a) -> IO ()
cCopies :: Ptr (CStdSet a) -> Ptr (Ptr a) -> IO ()
class HasStdSet a => HasStdSetCopyable a where
cCopyTo :: Ptr (CStdSet a) -> Ptr a -> IO ()
cInsert :: a -> Ptr (CStdSet a) -> IO ()
-- | Helper for defining templated instances
roll :: String -> Q [Dec] -> Q [Dec]
roll cType d =
concat
<$> sequence
[ C.include "<set>",
C.include "<algorithm>",
C.substitute
[ ("T", const cType),
("SET", \var -> "$(std::set<" ++ cType ++ ">* " ++ var ++ ")")
]
d
]
instanceStdSet :: String -> DecsQ
instanceStdSet cType =
roll
cType
[d|
instance HasStdSet $(C.getHaskellType False cType) where
cNew = [CU.exp| std::set<@T()>* { new std::set<@T()>() } |]
cDelete = [C.funPtr| void deleteStdSet(std::set<@T()>* set) { delete set; } |]
cSize set = [CU.exp| size_t { @SET(set)->size() } |]
cInsertByPtr ptr set = [CU.exp| void { @SET(set)->insert(*$(@T() *ptr)) } |]
cCopies set dstPtr =
[CU.block| void {
const std::set<@T()>& set = *@SET(set);
@T()** aim = $(@T()** dstPtr);
for (auto item : set) {
*aim = new @T()(item);
aim++;
}
}|]
|]
instanceStdSetCopyable :: String -> DecsQ
instanceStdSetCopyable cType =
roll
cType
[d|
instance HasStdSetCopyable $(C.getHaskellType False cType) where
cCopyTo set dstPtr =
[CU.block| void {
const std::set<@T()>* set = @SET(set);
std::copy(set->begin(), set->end(), $(@T()* dstPtr));
} |]
cInsert value set =
[CU.exp| void { @SET(set)->insert($(@T() value)) }
|]
|]
new :: forall a. HasStdSet a => IO (StdSet a)
new = mask_ $ do
moveToForeignPtrWrapper =<< cNew @a
size :: HasStdSet a => StdSet a -> IO Int
size (StdSet fptr) = fromIntegral <$> withForeignPtr fptr cSize
fromList :: HasStdSetCopyable a => [a] -> IO (StdSet a)
fromList as = do
set <- new
for_ as $ insert set
pure set
fromListP :: HasStdSet a => [Ptr a] -> IO (StdSet a)
fromListP as = do
set <- new
for_ as $ insertP set
pure set
fromListFP :: (Coercible a' (ForeignPtr a), HasStdSet a) => [a'] -> IO (StdSet a)
fromListFP as = do
set <- new
for_ as $ insertFP set
pure set
toSet :: (HasStdSetCopyable a, Storable a, Ord a) => StdSet a -> IO (S.Set a)
toSet stdSet = do
S.fromList . VS.toList <$> toVector stdSet
toVector :: (HasStdSetCopyable a, Storable a) => StdSet a -> IO (VS.Vector a)
toVector stdSet@(StdSet stdSetFPtr) = do
vecSize <- size stdSet
hsVec <- VSM.new vecSize
withForeignPtr stdSetFPtr $ \stdSetPtr ->
VSM.unsafeWith hsVec $ \hsVecPtr ->
cCopyTo stdSetPtr hsVecPtr
VS.unsafeFreeze hsVec
toList :: (HasStdSetCopyable a, Storable a) => StdSet a -> IO [a]
toList vec = VS.toList <$> toVector vec
toVectorP :: (HasStdSet a) => StdSet a -> IO (VS.Vector (Ptr a))
toVectorP stdSet@(StdSet stdSetFPtr) = do
vecSize <- size stdSet
hsVec <- VSM.new vecSize
withForeignPtr stdSetFPtr $ \stdSetPtr ->
VSM.unsafeWith hsVec $ \hsVecPtr ->
cCopies stdSetPtr hsVecPtr
VS.unsafeFreeze hsVec
toListP :: (HasStdSet a) => StdSet a -> IO [Ptr a]
toListP vec = VS.toList <$> toVectorP vec
toListFP :: (HasStdSet a, HasEncapsulation a b) => StdSet a -> IO [b]
toListFP vec = mask_ $ do
ptrs <- toListP vec
for ptrs moveToForeignPtrWrapper
insert :: HasStdSetCopyable a => StdSet a -> a -> IO ()
insert (StdSet fptr) value = withForeignPtr fptr (cInsert value)
insertP :: HasStdSet a => StdSet a -> Ptr a -> IO ()
insertP (StdSet fptr) ptr = withForeignPtr fptr (cInsertByPtr ptr)
insertFP :: (Coercible a' (ForeignPtr a), HasStdSet a) => StdSet a -> a' -> IO ()
insertFP (StdSet fptr) vfptr =
withForeignPtr fptr $ \setPtr ->
withForeignPtr (coerce vfptr) (\valPtr -> cInsertByPtr valPtr setPtr)