免責事項:ここに記載されているすべての実装の詳細は、GHC固有のものであり、掲載時点の問題の図書館の内部表現です。
この応答は事実の数年後ですが、実際にはバイラルコンテンツのポインタを取得することは可能です。 GCがヒープ内のデータを移動するのが好きで、GCヒープ以外のものがリークする可能性があるので問題になります。これは必ずしも理想的ではありません。 GHCは、でこれを解決:
newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)
プリミティブByteArrayとして定義(内部typedefさCチャーアレイ)は静的アドレスに固定することができます。 GCはそれらを移動させないことを保証します。あなたはこの機能をポインタにByteArrayの参照を変換することができます:
byteArrayContents# :: ByteArray# -> Addr#
アドレスタイプは、PtrをとForeignPtr種類の基礎を形成します。 Ptrsはファントムタイプでマークされたアドレスであり、ForeignPtrsはGHCメモリとIORefファイナライザへのオプションの参照です。
免責事項:これはのみです。ByteStringがHaskellの場合はのみが動作します。それ以外の場合は、bytearrayへの参照を取得することはできません。あなたは任意のアドレスを逆参照することはできません。 bytearrayにあなたの方法をキャストまたは強制しようとしないでください。その方法はsegfaultsです。例:
{-# 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からのByteArrayを取得するには、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は固定バイト配列にラップされたPlainPtrsで構築されます。彼らはファイナライザを持っていません。彼らは範囲外になると、通常のHaskellデータのようにGCされます。しかし、Addrsは数えません。 GHCはGCヒープの外にあるものを指していると仮定します。 bytearray自体がスコープから外れた場合は、ぶら下がったポインタが残っています。
data PlainPtr = (MutableByteArray# RealWorld)
(\(PlainPtr mutableByteArray#) -> mutableByteArray#)
MutableByteArraysは、ByteArraysと同じです。真のゼロコピー構築が必要な場合は、unsafeCoerce#またはunsafeFreeze#のいずれかをbytearrayに設定してください。それ以外の場合、GHCは複製を作成します。
mbarrTobarr :: MutableByteArray# s -> ByteArray#
mbarrTobarr = unsafeCoerce#
これで、ByteStringの生の内容がベクターに変換されるようになりました。
最高の願い、
うわー、私はその機能を見逃しています。それは良いスタートです。私の通常の 'unsafePerformIO ... withForeignPtr ... peekElemOff idx'の組み合わせよりも、それ以上のパフォーマンスを得ることはできませんが、それは確かにきれいです。 –
また、興味深いのはvector-mmapパッケージです –