@@ -84,6 +84,7 @@ module Language.Rust.Inline (
84
84
-- externCrate,
85
85
86
86
import Language.Rust.Inline.Context
87
+ import Language.Rust.Inline.Context.ByteString (bytestrings )
87
88
import Language.Rust.Inline.Context.Prelude (prelude )
88
89
import Language.Rust.Inline.Internal
89
90
import Language.Rust.Inline.Marshal
@@ -101,13 +102,17 @@ import Foreign.Marshal.Alloc (alloca, free)
101
102
import Foreign.Marshal.Array (newArray , withArrayLen )
102
103
import Foreign.Marshal.Unsafe (unsafeLocalState )
103
104
import Foreign.Marshal.Utils (new , with )
104
- import Foreign.Ptr (Ptr , freeHaskellFunPtr )
105
+ import Foreign.Ptr (FunPtr , Ptr , freeHaskellFunPtr )
105
106
106
107
import Control.Monad (void )
107
108
import Data.List (intercalate )
108
109
import Data.Traversable (for )
110
+ import Data.Word (Word8 )
109
111
import System.Random (randomIO )
110
112
113
+ import qualified Data.ByteString.Unsafe as ByteString
114
+ import Foreign.Storable (Storable (.. ))
115
+
111
116
{- $overview
112
117
113
118
This module provides the facility for dropping in bits of Rust code into your
@@ -307,17 +312,17 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
307
312
308
313
-- Convert the Haskell return type to a marshallable FFI type
309
314
(returnFfi, haskRet') <- do
310
- marshalFrom <- ghcMarshallable haskRet
311
- ret <- case marshalFrom of
315
+ marshalForm <- ghcMarshallable haskRet
316
+ ret <- case marshalForm of
312
317
BoxedDirect -> [t |IO $(pure haskRet)|]
313
318
BoxedIndirect -> [t |Ptr $(pure haskRet) -> IO ()|]
314
319
UnboxedDirect
315
320
| isPure -> pure haskRet
316
321
| otherwise ->
317
322
let retTy = showTy haskRet
318
323
in fail (" Cannot put unlifted type ‘" ++ retTy ++ " ’ in IO" )
319
- ByteString -> undefined
320
- pure (marshalFrom , pure ret)
324
+ ByteString -> [ t |Ptr (Ptr Word8, Word, FunPtr (Ptr Word8 -> Word -> IO ())) -> IO ()|]
325
+ pure (marshalForm , pure ret)
321
326
322
327
-- Convert the Haskell arguments to marshallable FFI types
323
328
(marshalForms, haskArgs') <- fmap unzip $
@@ -341,14 +346,17 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
341
346
ptr <- [t |Ptr $(pure haskArg)|]
342
347
pure (BoxedIndirect , ptr)
343
348
ByteString -> do
344
- rbsT <- [t |Ptr RustByteString |]
349
+ rbsT <- [t |Ptr (Ptr Word8, Word) |]
345
350
pure (ByteString , rbsT)
346
351
_ -> pure (marshalForm, haskArg)
347
352
348
353
-- Generate the Haskell FFI import declaration and emit it
354
+ bsFree <- newName $ " bsFree" ++ show (abs q)
355
+ bsFreeSig <- [t |FunPtr (Ptr Word8 -> Word -> IO ()) -> Ptr Word8 -> Word -> IO ()|]
349
356
haskSig <- foldr (\ l r -> [t |$(pure l) -> $r|]) haskRet' haskArgs'
350
357
let ffiImport = ForeignD (ImportF CCall safety qqStrName qqName haskSig)
351
- addTopDecls [ffiImport]
358
+ let ffiBsFree = ForeignD (ImportF CCall Safe " dynamic" bsFree bsFreeSig)
359
+ addTopDecls [ffiImport, ffiBsFree]
352
360
353
361
-- Generate the Haskell FFI call
354
362
let goArgs ::
@@ -363,7 +371,24 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
363
371
-- accumulated arguments. If the return value is not marshallable, we have to
364
372
-- 'alloca' some space to put the return value.
365
373
goArgs acc []
366
- | returnFfi /= BoxedIndirect = appsE (varE qqName : reverse acc)
374
+ | returnFfi == ByteString = do
375
+ ret <- newName " ret"
376
+ ptr <- newName " ptr"
377
+ len <- newName " len"
378
+ finalizer <- newName " finalizer"
379
+ [e |
380
+ alloca
381
+ ( \($(varP ret)) ->
382
+ do
383
+ $(appsE (varE qqName : reverse (varE ret : acc)))
384
+ ($(varP ptr), $(varP len), $(varP finalizer)) <- peek $(varE ret)
385
+ ByteString.unsafePackCStringFinalizer
386
+ $(varE ptr)
387
+ (fromIntegral $(varE len))
388
+ ($(varE bsFree) $(varE finalizer) $(varE ptr) $(varE len))
389
+ )
390
+ |]
391
+ | byValue returnFfi = appsE (varE qqName : reverse acc)
367
392
| otherwise = do
368
393
ret <- newName " ret"
369
394
[e |
@@ -385,17 +410,15 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
385
410
| marshalForm == ByteString -> do
386
411
ptr <- newName " ptr"
387
412
len <- newName " len"
388
- bs <- newName " bs"
389
413
bsp <- newName " bsp"
390
414
[e |
391
415
withByteString
392
416
$(varE argName)
393
417
( \($(varP ptr)) ($(varP len)) ->
394
- let $(varP bs) = RustByteString $(varE ptr) $(varE len)
395
- in with $(varE bs) (\($(varP bsp)) -> $(goArgs (varE bsp : acc) args))
418
+ with ($(varE ptr), $(varE len)) (\($(varP bsp)) -> $(goArgs (varE bsp : acc) args))
396
419
)
397
420
|]
398
- | passByValue marshalForm -> goArgs (varE argName : acc) args
421
+ | byValue marshalForm -> goArgs (varE argName : acc) args
399
422
| otherwise -> do
400
423
x <- newName " x"
401
424
[e |
@@ -421,7 +444,7 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
421
444
mergeArgs t (Just tInter) = (fmap (const mempty ) tInter, t)
422
445
423
446
-- Generate the Rust function.
424
- let retByVal = returnFfi /= BoxedIndirect
447
+ let retByVal = byValue returnFfi
425
448
(retArg, retTy, ret)
426
449
| retByVal =
427
450
( []
@@ -441,15 +464,15 @@ processQQ safety isPure (QQParse rustRet rustBody rustNamedArgs) = do
441
464
" , "
442
465
( [ s ++ " : " ++ marshal (renderType t)
443
466
| (s, t, v) <- zip3 rustArgNames rustArgs' marshalForms
444
- , let marshal x = if passByValue v then x else " *const " ++ x
467
+ , let marshal x = if byValue v then x else " *const " ++ x
445
468
]
446
469
++ retArg
447
470
)
448
471
, " ) -> " ++ retTy ++ " {"
449
472
, unlines
450
473
[ " let " ++ s ++ " : " ++ renderType t ++ " = " ++ marshal s ++ " .marshal();"
451
474
| (s, t, v) <- zip3 rustArgNames rustConvertedArgs marshalForms
452
- , let marshal x = if passByValue v then x else " unsafe { ::std::ptr::read(" ++ x ++ " ) }"
475
+ , let marshal x = if byValue v then x else " unsafe { ::std::ptr::read(" ++ x ++ " ) }"
453
476
]
454
477
, " let out: " ++ renderType rustConvertedRet ++ " = (|| {" ++ renderTokens rustBody ++ " })();"
455
478
, " " ++ ret
0 commit comments