Ответ 1
Отказ от ответственности: здесь все детали реализации и специфические для GHC и внутренние представления соответствующих библиотек во время публикации.
Этот ответ через пару лет после факта, но действительно возможно получить указатель на контент bytearray. Это проблематично, так как GC любит перемещать данные в кучу, а вещи вне кучи GC могут протекать, что не обязательно идеально. GHC решает это с помощью:
newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)
Примитивные байты (внутренние typedef'd C char массивы) могут быть статически привязаны к адресу. GC гарантирует их не перемещать. Вы можете преобразовать ссылку bytearray в указатель с помощью этой функции:
byteArrayContents# :: ByteArray# -> Addr#
Тип адреса формирует основу типов Ptr и ForeignPtr. Ptrs - это адреса, помеченные типом phantom, а 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 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 garbage.
case unIO (print (I# y#)) s7 of (# s8, _ #) -> (# s8, () #)}}}}}}}}
Output:
1
1
(some garbage value)
Чтобы получить bytearray из 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 построен с помощью PlainPtrs, которые обернуты вокруг закрепленных байт-массивов. Они не имеют финализаторов. Они являются GC'd, как обычные данные Haskell, когда они выходят за рамки. Однако addrs не учитываются. GHC предполагает, что они указывают на вещи вне кучи GC. Если сам bytearray выходит из области действия, вы остаетесь с висящим указателем.
data PlainPtr = (MutableByteArray# RealWorld)
(\(PlainPtr mutableByteArray#) -> mutableByteArray#)
MutableByteArrays идентичны байтам. Если вам нужна истинная конструкция с нулевой копией, убедитесь, что вы либо небезопасныCoerce #, либо небезопасноFreeze # в bytearray. В противном случае GHC создает дубликат.
mbarrTobarr :: MutableByteArray# s -> ByteArray#
mbarrTobarr = unsafeCoerce#
И теперь у вас есть исходное содержимое ByteString, готовое к превращению в вектор.
Лучшие пожелания,