packages feed

pcapng-0.1.0.0: src/Data/WordString32.hs

{-# LANGUAGE ScopedTypeVariables #-}
-- | Word32 strings for convenient handling
--   Just like ByteString, but with 32-bit each
--
--   Note that we only use methods from fast-parsing instance of xeno and XML Typelift
--   NOTE: put paper reference here when it is published
module Data.WordString32 (
    WordString -- opaque for safety
  , fromBS
  , toBS
  , null
  , index
  , drop
  , take
  , takeBytes
  , empty
  , length
  , append -- Use Semigroup.<>
  ) where

import           Control.Exception        (assert)
import qualified Data.ByteString          as BS (empty, take)
import qualified Data.ByteString.Internal as BS
import           Data.Function            (on)
import           Data.Word
import           Debug.Trace
import           Foreign.C.Types          (CSize (..))
import           Foreign.ForeignPtr       (ForeignPtr, castForeignPtr,
                                           withForeignPtr)
import           Foreign.Ptr              (Ptr, plusPtr)
import           Foreign.Storable
import           GHC.ForeignPtr           (mallocPlainForeignPtrBytes)
import           Prelude                  hiding (drop, length, null, take)
import           System.IO.Unsafe         (unsafeDupablePerformIO)

-- | Opaque array of 32-bit words
data WordString = WS {
    wsPtr    :: ForeignPtr Word32
  , wsOffset :: Int
  , wsLen    :: Int
  }

instance Show WordString where
  --show ws = show (BS.take 16 $ toBS ws)
  show ws =
    unwords [show bsOfs
            ,show bsLen
            ,show $ BS.take 16 bs
            ,show $ BS.take 16 nullBs]
    where
      bs@(BS.PS bsPtr bsOfs bsLen) = toBS ws
      nullBs = BS.PS bsPtr 0 bsLen

-- | Assume these instances will not be used often.
instance Eq WordString where
  (==) = (==) `on` toBS

instance Ord WordString where
  compare = compare `on` toBS

-- TODO: make a direct conduit?
{-# INLINE fromBS #-}
fromBS :: BS.ByteString -> WordString
fromBS (BS.PS wsPtr bsOffset bsLen) =
    if bsOffset `mod` 4 /= 0
      then error $ "ByteString offset must be divisible by 4 for Data.WordString32.fromBS, is: " <> show bsOffset
      else
        if bsLen `mod` 4 /= 0
          then error $ "ByteString length must be divisible by 4 for Data.WordString32.fromBS, is: " <> show bsLen
          else WS (castForeignPtr wsPtr) wsOffset wsLen
  where
    wsOffset = bsOffset `div` 4
    wsLen    = bsLen    `div` 4

{-# INLINE toBS #-}
toBS :: WordString -> BS.ByteString
toBS (WS wsPtr wsOffset wsLen) = BS.PS (castForeignPtr wsPtr) (wsOffset*4) (wsLen*4)

{-# INLINE index #-}
index :: WordString -> Int -> Word32
index  _                        i | i < 0 = error "index cannot be negative"
index (WS wsPtr wsOffset wsLen) i =
    assert (offset < wsLen) $
    unsafeDupablePerformIO  $
    withForeignPtr wsPtr    $
      (`peekElemOff` offset)
  where
    offset = wsOffset + i

{-# INLINE null #-}
null :: WordString -> Bool
null (WS wsPtr wsOffset wsLen) = wsOffset == wsLen

{-# INLINE drop #-}
drop :: Int -> WordString -> WordString
drop i  _                | i < 0 = error "Dropping negative number of words"
drop i (WS wsPtr wsOffset wsLen) =
    assert (newWsOffset<wsLen)   $
      WS wsPtr (min newWsOffset wsLen) wsLen
  where
    newWsOffset = wsOffset+i

{-# INLINE take #-}
take :: Int -> WordString -> WordString
take i _ | i < 0 = error "Taking negative number of words"
take i (WS wsPtr wsOffset wsLen) =
        WS wsPtr wsOffset $ min wsLen $ wsOffset+i

{-# INLINE takeBytes #-}
takeBytes :: Int -> WordString -> WordString
takeBytes i _ | i < 0          = error "Taking negative number of bytes"
takeBytes i _ | i `mod` 4 /= 0 = error "Can only take number of bytes divisible by 4"
takeBytes i ws = take (i `div` 4) ws

empty :: WordString
empty  = fromBS $ BS.empty

length :: WordString -> Int
length (WS _ ofs len) = len-ofs

instance Monoid WordString where
  mempty = empty

instance Semigroup WordString where
  (<>) = append

foreign import ccall unsafe "string.h memcpy" c_memcpy
    :: Ptr Word32 -> Ptr Word32 -> CSize -> IO (Ptr Word32)

memcpy :: Ptr Word32 -> Ptr Word32 -> Int -> IO ()
memcpy p q s = do
  _ <- c_memcpy p q $ fromIntegral s*4
  return ()

append a b | null b = a
append a b | null a = b
append a@(WS aPtr aOffset aLen)
       b@(WS bPtr bOffset bLen) = --trace ("<append here>: " <> show a <> " <> " <> show b) $
  unsafeDupablePerformIO $ do
    newPtr :: ForeignPtr Word32 <- mallocPlainForeignPtrBytes (4*totalLen)
    withForeignPtr newPtr $ \destPtrA -> do
      withForeignPtr aPtr $ \srcPtrA ->
        memcpy destPtrA (srcPtrA `plusWords` aOffset) aWords
      let destPtrB = (destPtrA :: Ptr Word32) `plusWords` aWords
      withForeignPtr bPtr $ \srcPtrB ->
        memcpy destPtrB (srcPtrB `plusWords` bOffset) bWords
    let result = WS newPtr 0 totalLen
    --trace ("result is: " <> show result) $
    return result
  where
    totalLen = aWords+bWords
    aWords   = aLen-aOffset
    bWords   = bLen-bOffset

plusWords :: Ptr a -> Int -> Ptr a
plusWords a b = a `plusPtr` (b*4)

indexWord16 :: BS.ByteString -> Word16
indexWord16  = undefined

indexWord64 :: WordString -> Word64
indexWord64  = undefined