免责声明:这里的所有内容都是实施细节,特定于 GHC 以及发布时相关库的内部表示。
这个响应是在几年之后,但确实有可能获得指向字节数组内容的指针。这是有问题的,因为 GC 喜欢移动堆中的数据,而 GC 堆之外的数据可能会泄漏,这不一定是理想的。 GHC 通过以下方式解决了这个问题:
newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)
原始字节数组(内部类型定义的 C 字符数组)可以静态固定到地址。 GC 保证不移动它们。您可以使用以下函数将字节数组引用转换为指针:
byteArrayContents# :: ByteArray# -> Addr#
地址类型构成了 Ptr 和foreignPtr 类型的基础。 Ptrs 是用幻像类型标记的地址,ForeignPtrs 是加上对 GHC 内存和 IORef 终结器的可选引用。
免责声明:这将only如果你的 ByteString 是 Haskell 构建的,那么就可以工作。否则,您无法获取对字节数组的引用。您不能取消引用任意地址。不要尝试强制转换或强制转换为字节数组;这样就存在段错误。例子:
{-# LANGUAGE MagicHash, UnboxedTuples #-}
import GHC.IO
import GHC.Prim
import GHC.Types
main :: IO()
main = test
test :: IO () -- Create the test array.
test = IO $ \s0 -> case newPinnedByteArray# 8# s0 of {(# s1, mbarr# #) ->
-- Write something and read it back as baseline.
case writeInt64Array# mbarr# 0# 1# s1 of {s2 ->
case readInt64Array# mbarr# 0# s2 of {(# s3, x# #) ->
-- Print it. Should match what was written.
case unIO (print (I# x#)) s3 of {(# s4, _ #) ->
-- Convert bytearray to pointer.
case byteArrayContents# (unsafeCoerce# mbarr#) of {addr# ->
-- Dereference the pointer.
case readInt64OffAddr# addr# 0# s4 of {(# s5, x'# #) ->
-- Print what's read. Should match the above.
case unIO (print (I# x'#)) s5 of {(# s6, _ #) ->
-- Coerce the pointer into an array and try to read.
case readInt64Array# (unsafeCoerce# addr#) 0# s6 of {(# s7, y# #) ->
-- Haskell is not C. Arrays are not pointers.
-- This won't match. It might segfault. At best, it's garbage.
case unIO (print (I# y#)) s7 of (# s8, _ #) -> (# s8, () #)}}}}}}}}
Output:
1
1
(some garbage value)
要从 ByteString 获取字节数组,您需要从 Data.ByteString.Internal 导入构造函数并进行模式匹配。
data ByteString = PS !(ForeignPtr Word8) !Int !Int
(\(PS foreignPointer offset length) -> foreignPointer)
现在我们需要从ForeignPtr 中取出货物。这部分完全是特定于实现的。对于 GHC,从 GHC.ForeignPtr 导入。
data ForeignPtr a = ForeignPtr Addr# ForeignPtrContents
(\(ForeignPtr addr# foreignPointerContents) -> foreignPointerContents)
data ForeignPtrContents = PlainForeignPtr !(IORef (Finalizers, [IO ()]))
| MallocPtr (MutableByteArray# RealWorld) !(IORef (Finalizers, [IO ()]))
| PlainPtr (MutableByteArray# RealWorld)
在 GHC 中,ByteString 是使用 PlainPtr 构建的,这些 PlainPtr 包裹在固定字节数组周围。它们没有终结器。当它们超出范围时,它们会像常规 Haskell 数据一样被 GC 处理。不过,地址不算数。 GHC 假设它们指向 GC 堆之外的东西。如果字节数组本身超出了范围,那么您将留下一个悬空指针。
data PlainPtr = (MutableByteArray# RealWorld)
(\(PlainPtr mutableByteArray#) -> mutableByteArray#)
MutableByteArray 与 ByteArray 相同。如果您想要真正的零拷贝构造,请确保对字节数组使用 unsafeCoerce# 或 unsafeFreeze# 。否则,GHC 会创建一个副本。
mbarrTobarr :: MutableByteArray# s -> ByteArray#
mbarrTobarr = unsafeCoerce#
现在,您已准备好将 ByteString 的原始内容转换为向量。
最好的祝愿,