Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiently convert Foreign.Ptr Word8 (or ByteString) to UArray Int Word8

I'm doing some Network capture with Network.Pcap (pcap) and plan to do some inspection using Net.PacketParsing (network-house). To do so, it looks like i have to put my packet parsing in either

Pcap.Callback :: PktHdr -> Ptr Word8 -> IO ()

or

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

And work on the packet as either a 'Ptr Word8' or 'ByteString'. On the packet parsing side, I have:

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

to get to the InPacket type that's needed for the parsing. So, what's left for me is to convert the 'Ptr' or 'ByteString' to 'UArray'--either purely or in IO. I suppose I can unpack the ByteString to [Word8], and from there to the UArray, but there seems like there must be a better way.

I'm also concerned about my choice of libraries. I've used network-house in the past and found it quite nice, but it is getting old and uses UArray, which itself seems a little archaic. So suggestions for a better starting point are welcome.

like image 852
trevor cook Avatar asked Nov 08 '22 05:11

trevor cook


1 Answers

ByteString and Ptr Word8 point to external heap, while UArray is on GHC heap, so any conversion function must copy over the data.

I have not found any direct conversion function in libraries, but fortunately there's a GHC primitive which does exactly what we want, called copyAddrToByteArray#. This lets us convert with the bare minimum overhead:

{-# 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 #-}

But in general, you're right that array is outdated and rarely used now.

like image 88
András Kovács Avatar answered Nov 13 '22 17:11

András Kovács