packages feed

foreign 0.1.0.0 → 0.1.1.0

raw patch · 6 files changed

+164/−2 lines, 6 filesdep +bytestringdep +quickcheck-instancesPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, quickcheck-instances

API changes (from Hackage documentation)

+ HsForeign.Primitive: withPrimList :: Prim a => [a] -> (Ptr a -> Int -> IO b) -> IO b
+ HsForeign.String: data StdString
+ HsForeign.String: hs_delete_std_string :: Ptr StdString -> IO ()
+ HsForeign.String: hs_new_std_string :: Ptr Word8 -> Int -> IO (Ptr StdString)
+ HsForeign.String: hs_new_std_string_def :: IO (Ptr StdString)
+ HsForeign.String: hs_std_string_cstr :: Ptr StdString -> IO (Ptr Word8)
+ HsForeign.String: hs_std_string_size :: Ptr StdString -> IO Int
+ HsForeign.String: mallocFromByteString :: ByteString -> IO (CString, Int)
+ HsForeign.String: mallocFromMaybeByteString :: Maybe ByteString -> IO (CString, Int)
+ HsForeign.String: unsafePeekStdString :: Ptr StdString -> IO ByteString
+ HsForeign.String: withByteString :: ByteString -> (Ptr Word8 -> Int -> IO a) -> IO a
+ HsForeign.String: withByteStrings :: [ByteString] -> (Ptr (Ptr Word8) -> Ptr Int -> Ptr Int -> Int -> IO a) -> IO a

Files

+ cbits/hs_std_string.cpp view
@@ -0,0 +1,37 @@+#include <HsFFI.h>++#include <cstring>+#include <string>++// ----------------------------------------------------------------------------+extern "C" {++HsInt hs_std_string_size(std::string* str) {+  if (str)+    return (HsInt)str->size();+  else+    return 0;+}++const char* hs_std_string_cstr(std::string* str) {+  if (str)+    return str->c_str();+  else+    return 0;+}++void hs_copy_std_string(std::string* str, HsInt size, char* buf) {+  if (str != NULL)+    memcpy(buf, str->c_str(), size);+}++std::string* hs_new_std_string_def() { return new std::string; }++std::string* hs_new_std_string(char* s, HsInt length) {+  return new std::string(s, length);+}++void hs_delete_std_string(std::string* str) { delete str; }++// ----------------------------------------------------------------------------+} // End extern "C"
foreign.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               foreign-version:            0.1.0.0+version:            0.1.1.0 synopsis:           A collection of helpers for ffi. description:   Please see the README on Github at <https://github.com/4eUeP/foreign#readme>@@ -46,17 +46,23 @@ library   import:          common   hs-source-dirs:  src+  cxx-sources:     cbits/hs_std_string.cpp   exposed-modules:     HsForeign     HsForeign.AsyncFFI     HsForeign.Primitive+    HsForeign.String    build-depends:     , base                >=4.14 && <5+    , bytestring          >=0.10 && <=0.12     , ghc-prim            >=0.5  && <1.0     , primitive           ^>=0.7     , primitive-unlifted  ^>=1.0 +  cxx-options:     -std=c++17 -Werror=switch+  extra-libraries: stdc+++ test-suite foreign-test   import:             common   type:               exitcode-stdio-1.0@@ -64,14 +70,16 @@   other-modules:     HsForeign.AsyncFFISpec     HsForeign.PrimitiveSpec+    HsForeign.StringSpec    hs-source-dirs:     test   c-sources:          test/cbits/ffi.c   build-depends:-    , base        >=4.14 && <5+    , base                  >=4.14 && <5     , foreign     , hspec     , QuickCheck+    , quickcheck-instances    build-tool-depends: hspec-discover:hspec-discover >=2 && <3   ghc-options:        -threaded -rtsopts -with-rtsopts=-N
src/HsForeign.hs view
@@ -1,7 +1,9 @@ module HsForeign   ( module HsForeign.Primitive   , module HsForeign.AsyncFFI+  , module HsForeign.String   ) where  import           HsForeign.AsyncFFI import           HsForeign.Primitive+import           HsForeign.String
src/HsForeign/Primitive.hs view
@@ -10,6 +10,7 @@   , withPrimUnsafe   , allocPrimUnsafe   , withPrimArray+  , withPrimList   , allocPrimArray   , withPrimArrayUnsafe   , allocPrimArrayUnsafe@@ -125,6 +126,10 @@       withMutablePrimArrayContents buf $ \ptr -> f ptr siz {-# INLINABLE withPrimArray #-} +withPrimList :: Prim a => [a] -> (Ptr a -> Int -> IO b) -> IO b+withPrimList = withPrimArray . primArrayFromList+{-# INLINABLE withPrimList #-}+ -- From Z-Data package: Z.Foreign -- -- | Allocate a prim array and pass to FFI as pointer, freeze result into a 'PrimVector'.@@ -195,6 +200,7 @@       withPrimArray pa $ \ ppa _ -> do         writePrimArray ptrs i ppa         go ptrs (i+1) pas+{-# INLINABLE withPrimArrayList #-}  withForeignPtrList :: [ForeignPtr a] -> (Ptr (Ptr a) -> Int -> IO b) -> IO b withForeignPtrList fptrs f = do
+ src/HsForeign/String.hs view
@@ -0,0 +1,91 @@+module HsForeign.String+  ( -- * CString+    mallocFromByteString+  , mallocFromMaybeByteString+  , withByteString+  , withByteStrings++    -- * CXX: std::string+  , StdString+  , hs_new_std_string+  , hs_new_std_string_def+  , hs_std_string_size+  , hs_std_string_cstr+  , hs_delete_std_string+  , unsafePeekStdString+  ) where++import           Control.Exception        (AssertionFailed (..), throw)+import           Control.Monad            (unless)+import           Data.ByteString          (ByteString)+import qualified Data.ByteString.Internal as BS+import qualified Data.ByteString.Unsafe   as BS+import           Data.Word+import           Foreign.C.String+import           Foreign.ForeignPtr+import           Foreign.Marshal+import           Foreign.Ptr++import           HsForeign.Primitive++-------------------------------------------------------------------------------+-- CString++-- | Copies the content of the given ByteString.+--+-- The memory may be deallocated using free or finalizerFree when no longer+-- required.+mallocFromByteString :: ByteString -> IO (CString, Int)+mallocFromByteString bs =+  BS.unsafeUseAsCStringLen bs $ \(src, len) -> do+    buf <- mallocBytes len+    copyBytes buf src len+    return (buf, len)+{-# INLINE mallocFromByteString #-}++mallocFromMaybeByteString :: Maybe ByteString -> IO (CString, Int)+mallocFromMaybeByteString (Just bs) = mallocFromByteString bs+mallocFromMaybeByteString Nothing   = return (nullPtr, 0)+{-# INLINE mallocFromMaybeByteString #-}++withByteString :: ByteString -> (Ptr Word8 -> Int -> IO a) -> IO a+withByteString (BS.PS fp off len) f =+  -- TODO: since bytestring 0.11.0.0, it exports the 'BS' constructor.+  -- we can change to benefit from the simplified BS constructor if we only+  -- support bytestring >= 0.11+  let fp' = fp `plusForeignPtr` off+   in withForeignPtr fp' $ \p -> f p len++-- | Pass list of ByteStrings to FFI.+withByteStrings+  :: [ByteString]+  -> (Ptr (Ptr Word8) -> Ptr Int -> Ptr Int -> Int -> IO a)+  -- ^ cstring*, offset*, len*, list_len+  -> IO a+withByteStrings bss f = do+  let exbs (BS.PS payload off len) = (payload, fromIntegral off, fromIntegral len)+      (ps, offs, lens) = unzip3 (map exbs bss)+  withPrimArray (primArrayFromList lens) $ \lens' num ->+    withPrimArray (primArrayFromList offs) $ \offs' _num_offs ->+    withForeignPtrList ps $ \ps' _num_ps -> do+      unless (num == _num_offs && num == _num_ps) $ throw $+        AssertionFailed "This should never happen..."+      f ps' offs' lens' num++-------------------------------------------------------------------------------+-- std::string++data StdString++foreign import ccall unsafe hs_new_std_string :: Ptr Word8 -> Int -> IO (Ptr StdString)+foreign import ccall unsafe hs_new_std_string_def :: IO (Ptr StdString)+foreign import ccall unsafe hs_std_string_size :: Ptr StdString -> IO Int+foreign import ccall unsafe hs_std_string_cstr :: Ptr StdString -> IO (Ptr Word8)+foreign import ccall unsafe hs_delete_std_string :: Ptr StdString -> IO ()++unsafePeekStdString :: Ptr StdString -> IO ByteString+unsafePeekStdString stdstring = do+  siz <- hs_std_string_size stdstring+  ptr <- hs_std_string_cstr stdstring+  BS.unsafePackCStringFinalizer ptr siz (hs_delete_std_string stdstring)+{-# INLINE unsafePeekStdString #-}
+ test/HsForeign/StringSpec.hs view
@@ -0,0 +1,18 @@+module HsForeign.StringSpec (spec) where++import           System.IO.Unsafe                     (unsafeDupablePerformIO)+import           Test.Hspec+import           Test.Hspec.QuickCheck+import           Test.QuickCheck+import           Test.QuickCheck.Instances.ByteString ()++import           HsForeign.String++spec :: Spec+spec = describe "StdString" $ do+  prop "unsafePeekStdString" $ \s ->+    -- 1. withByteString does nothing but pass the underlying ptr+    -- 2. hs_new_std_string copy construct a new std::string+    -- 3. unsafePeekStdString peek the std::string with a delete finalizer.+    let f = unsafePeekStdString =<< (withByteString s $ \p l -> hs_new_std_string p l)+     in unsafeDupablePerformIO f === s