Отказ от ответственности: все здесь является деталями реализации и относится к GHC и внутренним представлениям рассматриваемых библиотек на момент публикации.
Этот ответ получен спустя пару лет, но действительно возможно получить указатель на содержимое байтового массива. Это проблематично, так как GC любит перемещать данные в куче, и вещи за пределами кучи GC могут протекать, что не обязательно идеально. GHC решает это с помощью:
newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)
Примитивные массивы байтов (массивы C char с внутренним определением типа) могут быть статически привязаны к адресу. GC гарантирует, что они не будут перемещены. Вы можете преобразовать ссылку на bytearray в указатель с помощью этой функции:
byteArrayContents# :: ByteArray# -> Addr#
Тип адреса лежит в основе типов Ptr и ForeignPtr. Ptrs — это адреса, помеченные фантомным типом, а ForeignPtrs — это плюс необязательные ссылки на память GHC и финализаторы IORef.
Отказ от ответственности: это будет только работать, если ваша ByteString была построена на Haskell. В противном случае вы не сможете получить ссылку на 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, вам нужно импортировать конструктор из 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, когда они выходят за рамки. Однако адреса не в счет. GHC предполагает, что они указывают на вещи за пределами кучи GC. Если сам массив байтов выпадает из области видимости, у вас остается висячий указатель.
data PlainPtr = (MutableByteArray# RealWorld)
(\(PlainPtr mutableByteArray#) -> mutableByteArray#)
MutableByteArrays идентичны ByteArrays. Если вам нужна настоящая конструкция с нулевым копированием, убедитесь, что вы используете unsafeCoerce# или unsafeFreeze# для массива байтов. В противном случае GHC создает дубликат.
mbarrTobarr :: MutableByteArray# s -> ByteArray#
mbarrTobarr = unsafeCoerce#
И теперь у вас есть необработанное содержимое ByteString, готовое для преобразования в вектор.
С наилучшими пожеланиями,
person
user2472093
schedule
25.08.2013