高效地将Foreign.Ptr Word8(或ByteString)转换为UArray Int Word8

5
我正在使用Network.Pcappcap)进行网络捕获,并计划使用Net.PacketParsingnetwork-house)进行一些检查。为此,看起来我必须将我的数据包解析放在任何一个中:
Pcap.Callback :: PktHdr -> Ptr Word8 -> IO ()

或者
Pcap.CallbackBS :: PktHdr -> ByteString -> IO ().

您可以将数据包作为“Ptr Word8”或“ByteString”进行处理。在数据包解析方面,我有以下内容:

Net.Packet.toInPack :: UArray Int Word8 -> InPacket

要解析数据,需要使用InPacket类型。因此,我需要将'Ptr'或'ByteString'转换为'UArray'——可以纯粹地转换或在IO中进行转换。我想我可以将ByteString解包成[Word8],然后再转换为UArray,但似乎必须有更好的方法。

我还担心我的库选择。我以前使用过network-house,并发现它非常好,但它已经过时了,而且使用的是UArray,这本身似乎有点古老。因此,欢迎提供更好的起点建议。

1个回答

1

ByteStringPtr Word8指向外部堆,而UArray位于GHC堆上,因此任何转换函数都必须复制数据。

我没有在库中找到任何直接的转换函数,但幸运的是有一个GHC原语恰好可以满足我们的需求,叫做copyAddrToByteArray#。这让我们以最小的开销进行转换:

{-# language MagicHash, UnboxedTuples #-}

import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import qualified Data.Array.Base as A

import GHC.Types
import GHC.Prim
import GHC.Magic (runRW#)
import GHC.ForeignPtr
import Data.Word

-- when using GHC 8.2.x or later:
byteStringToUArray :: B.ByteString -> A.UArray Int Word8
byteStringToUArray (B.PS (ForeignPtr addr _) (I# start) (I# len)) =
  runRW# $ \s -> case newByteArray# len s of
    (# s, marr #) -> case copyAddrToByteArray# (plusAddr# addr start) marr 0# len s of
      s -> case unsafeFreezeByteArray# marr s of
        (# _, arr #) -> A.UArray 0 (I# (len -# 1#)) (I# len) arr
{-# inline byteStringToUArray #-}

-- when using GHC 8.0.x:
byteStringToUArray :: B.ByteString -> A.UArray Int Word8
byteStringToUArray (B.PS (ForeignPtr addr _) (I# start) (I# len)) =
  case (runRW# $ \s -> case newByteArray# len s of
    (# s, marr #) -> case copyAddrToByteArray# (plusAddr# addr start) marr 0# len s of
      s -> case unsafeFreezeByteArray# marr s of
        (# s, arr #) -> (# s, A.UArray 0 (I# (len -# 1#)) (I# len) arr #)) of
    (# _, res #) -> res
{-# inline byteStringToUArray #-}

但总的来说,你是对的,array已经过时并且现在很少使用。


谢谢,看起来不错,但我无法编译。你使用的ghc-prim版本是什么?如果超过0.5.0.0,对我来说会很困难。 - trevor cook
抱歉,我也添加了ghc-8.0.x版本(prim-0.5.0.0)。区别在于runRW#的类型。 - András Kovács

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接