5

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.

trevor cook
  • 1,531
  • 8
  • 22

1 Answers1

1

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.

András Kovács
  • 29,931
  • 3
  • 53
  • 99
  • Thanks, this looks good but I'm unable to compile. What version of ghc-prim are you using? It will be difficult for me to go past 0.5.0.0? – trevor cook Jun 14 '18 at 13:00
  • Sorry, I added a version for ghc-8.0.x (prim-0.5.0.0) as well. The difference is the type of `runRW#`. – András Kovács Jun 14 '18 at 13:42