将 GHC ByteArray# 复制到 Ptr

Copying GHC ByteArray# to Ptr

我正在尝试编写以下函数:

memcpyByteArrayToPtr :: 
     ByteArray# -- ^ source
  -> Int -- ^ start
  -> Int -- ^ length
  -> Ptr a -- ^ destination
  -> IO ()

行为应该是在内部使用 memcpyByteArray# 的内容复制到 Ptr。我看到有两种技术可以做这样的事情,但我很难推断出它们的安全性。

第一个是在memory package. There is an auxiliary function withPtr中找到定义为:

data Bytes = Bytes (MutableByteArray# RealWorld)

withPtr :: Bytes -> (Ptr p -> IO a) -> IO a
withPtr b@(Bytes mba) f = do
    a <- f (Ptr (byteArrayContents# (unsafeCoerce# mba)))
    touchBytes b
    return a

但是,我很确定这只是安全的,因为构造 Bytes 的唯一方法是使用调用 newAlignedPinnedByteArray# 的智能构造函数。 An answer given to a similar question and the docs for byteArrayContents# indicate that it is only safe when dealing with pinned ByteArray#s. In my situation, I'm dealing with the ByteArray#s that the text library uses internally,而且它们没有固定,所以我认为这不安全。

我偶然发现的第二种可能性是 text 本身。 Data.Text.Array source code最下面有个ffi函数memcpyI:

foreign import ccall unsafe "_hs_text_memcpy" memcpyI
  :: MutableByteArray# s -> CSize -> ByteArray# -> CSize -> CSize -> IO ()

这由以下 C 代码支持:

void _hs_text_memcpy(void *dest, size_t doff, const void *src, size_t soff, size_t n)
{
  memcpy(dest + (doff<<1), src + (soff<<1), n<<1);
}

因为它是 text 的一部分,我相信这是安全的。它看起来很危险,因为它正在从未固定的 ByteArray# 获取内存位置,这正是 byteArrayContents# 文档所警告的。我怀疑这没关系,因为 ffi 调用被标记为不安全,我认为这会阻止 GC 在 ffi 调用期间移动 ByteArray#

这就是我到目前为止所做的研究。到目前为止,我最好的猜测是我可以复制在 text 中完成的内容。最大的区别在于,我不会将 MutableByteArray#ByteArray# 作为两个指针传递,而是传递 ByteArray#Ptr a(或者 Addr#, 我不确定您通常将哪些与 ffi 一起使用)。

我建议的安全吗?有没有更好的方法可以让我避免使用 ffi? base 中有什么东西可以做到这一点吗?请随时纠正我所做的任何错误假设,并感谢您的任何建议或指导。

copyByteArrayToAddr# :: ByteArray# -> Int# -> Addr# -> Int# -> State# s -> State# s

看起来像正确的 primop。您只需要确保不要尝试将其复制到它占用的内存中。所以你可能应该安全

copyByteArrayToPtr :: ByteArray# -> Int -> Ptr a -> Int -> ST s ()
copyByteArrayToPtr ba (I# x) (Ptr p) (I# y) = ST $ \ s ->
  (# copyByteArrayToAddr# ba x p y s, () #)

不幸的是,文档没有告诉我每个 Int# 应该是什么意思,但我想你可以通过试验和段错误来弄明白。