Blame Data/ByteArray/Pack.hs

Packit c1c4f9
-- |
Packit c1c4f9
-- Module      : Data.ByteArray.Pack
Packit c1c4f9
-- License     : BSD-Style
Packit c1c4f9
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit c1c4f9
-- Stability   : experimental
Packit c1c4f9
-- Portability : unknown
Packit c1c4f9
--
Packit c1c4f9
-- Simple Byte Array packer
Packit c1c4f9
--
Packit c1c4f9
-- Simple example:
Packit c1c4f9
--
Packit c1c4f9
-- > > flip pack 20 $ putWord8 0x41 >> putByteString "BCD" >> putWord8 0x20 >> putStorable (42 :: Word32)
Packit c1c4f9
-- > Right (ABCD *\NUL\NUL\NUL")
Packit c1c4f9
--
Packit c1c4f9
-- Original code from <https://hackage.haskell.org/package/bspack>
Packit c1c4f9
-- generalized and adapted to run on 'memory', and spellchecked / tweaked. (2015-05)
Packit c1c4f9
-- Copyright (c) 2014 Nicolas DI PRIMA
Packit c1c4f9
--
Packit c1c4f9
module Data.ByteArray.Pack
Packit c1c4f9
    ( Packer
Packit c1c4f9
    , Result(..)
Packit c1c4f9
    , fill
Packit c1c4f9
    , pack
Packit c1c4f9
      -- * Operations
Packit c1c4f9
      -- ** put
Packit c1c4f9
    , putWord8
Packit c1c4f9
    , putWord16
Packit c1c4f9
    , putWord32
Packit c1c4f9
    , putStorable
Packit c1c4f9
    , putBytes
Packit c1c4f9
    , fillList
Packit c1c4f9
    , fillUpWith
Packit c1c4f9
      -- ** skip
Packit c1c4f9
    , skip
Packit c1c4f9
    , skipStorable
Packit c1c4f9
    ) where
Packit c1c4f9
Packit c1c4f9
import           Data.Word
Packit c1c4f9
import           Foreign.Ptr
Packit c1c4f9
import           Foreign.Storable
Packit c1c4f9
import           Data.Memory.Internal.Imports ()
Packit c1c4f9
import           Data.Memory.Internal.Compat
Packit c1c4f9
import           Data.Memory.PtrMethods
Packit c1c4f9
import           Data.ByteArray.Pack.Internal
Packit c1c4f9
import           Data.ByteArray (ByteArray, ByteArrayAccess, MemView(..))
Packit c1c4f9
import qualified Data.ByteArray as B
Packit c1c4f9
Packit c1c4f9
-- | Fill a given sized buffer with the result of the Packer action
Packit c1c4f9
fill :: ByteArray byteArray => Int -> Packer a -> Either String byteArray
Packit c1c4f9
fill len packing = unsafeDoIO $ do
Packit c1c4f9
    (val, out) <- B.allocRet len $ \ptr -> runPacker_ packing (MemView ptr len)
Packit c1c4f9
    case val of 
Packit c1c4f9
        PackerMore _ (MemView _ r)
Packit c1c4f9
            | r == 0    -> return $ Right out
Packit c1c4f9
            | otherwise -> return $ Left ("remaining unpacked bytes " ++ show r ++ " at the end of buffer")
Packit c1c4f9
        PackerFail err  -> return $ Left err
Packit c1c4f9
Packit c1c4f9
-- | Pack the given packer into the given bytestring
Packit c1c4f9
pack :: ByteArray byteArray => Packer a -> Int -> Either String byteArray
Packit c1c4f9
pack packing len = fill len packing
Packit c1c4f9
{-# DEPRECATED pack "use fill instead" #-}
Packit c1c4f9
Packit c1c4f9
fillUpWithWord8' :: Word8 -> Packer ()
Packit c1c4f9
fillUpWithWord8' w = Packer $ \(MemView ptr size) -> do
Packit c1c4f9
    memSet ptr w size
Packit c1c4f9
    return $ PackerMore () (MemView (ptr `plusPtr` size) 0)
Packit c1c4f9
Packit c1c4f9
-- | Put a storable from the current position in the stream
Packit c1c4f9
putStorable :: Storable storable => storable -> Packer ()
Packit c1c4f9
putStorable s = actionPacker (sizeOf s) (\ptr -> poke (castPtr ptr) s)
Packit c1c4f9
Packit c1c4f9
-- | Put a Byte Array from the current position in the stream
Packit c1c4f9
--
Packit c1c4f9
-- If the ByteArray is null, then do nothing
Packit c1c4f9
putBytes :: ByteArrayAccess ba => ba -> Packer ()
Packit c1c4f9
putBytes bs
Packit c1c4f9
    | neededLength == 0 = return ()
Packit c1c4f9
    | otherwise         =
Packit c1c4f9
        actionPacker neededLength $ \dstPtr -> B.withByteArray bs $ \srcPtr ->
Packit c1c4f9
            memCopy dstPtr srcPtr neededLength
Packit c1c4f9
  where
Packit c1c4f9
    neededLength = B.length bs
Packit c1c4f9
Packit c1c4f9
-- | Skip some bytes from the current position in the stream
Packit c1c4f9
skip :: Int -> Packer ()
Packit c1c4f9
skip n = actionPacker n (\_ -> return ())
Packit c1c4f9
Packit c1c4f9
-- | Skip the size of a storable from the current position in the stream
Packit c1c4f9
skipStorable :: Storable storable => storable -> Packer ()
Packit c1c4f9
skipStorable = skip . sizeOf
Packit c1c4f9
Packit c1c4f9
-- | Fill up from the current position in the stream to the end
Packit c1c4f9
--
Packit c1c4f9
-- It is equivalent to:
Packit c1c4f9
--
Packit c1c4f9
-- > fillUpWith s == fillList (repeat s)
Packit c1c4f9
--
Packit c1c4f9
fillUpWith :: Storable storable => storable -> Packer ()
Packit c1c4f9
fillUpWith s = fillList $ repeat s
Packit c1c4f9
{-# RULES "fillUpWithWord8" forall s . fillUpWith s = fillUpWithWord8' s #-}
Packit c1c4f9
{-# NOINLINE fillUpWith #-}
Packit c1c4f9
Packit c1c4f9
-- | Will put the given storable list from the current position in the stream
Packit c1c4f9
-- to the end.
Packit c1c4f9
--
Packit c1c4f9
-- This function will fail with not enough storage if the given storable can't
Packit c1c4f9
-- be written (not enough space)
Packit c1c4f9
--
Packit c1c4f9
-- Example:
Packit c1c4f9
--
Packit c1c4f9
-- > > pack (fillList $ [1..] :: Word8) 9
Packit c1c4f9
-- > "\1\2\3\4\5\6\7\8\9"
Packit c1c4f9
-- > > pack (fillList $ [1..] :: Word32) 4
Packit c1c4f9
-- > "\1\0\0\0"
Packit c1c4f9
-- > > pack (fillList $ [1..] :: Word32) 64
Packit c1c4f9
-- > .. <..succesful..>
Packit c1c4f9
-- > > pack (fillList $ [1..] :: Word32) 1
Packit c1c4f9
-- > .. <.. not enough space ..>
Packit c1c4f9
-- > > pack (fillList $ [1..] :: Word32) 131
Packit c1c4f9
-- > .. <.. not enough space ..>
Packit c1c4f9
--
Packit c1c4f9
fillList :: Storable storable => [storable] -> Packer ()
Packit c1c4f9
fillList []     = return ()
Packit c1c4f9
fillList (x:xs) = putStorable x >> fillList xs
Packit c1c4f9
Packit c1c4f9
------------------------------------------------------------------------------
Packit c1c4f9
-- Common packer                                                            --
Packit c1c4f9
------------------------------------------------------------------------------
Packit c1c4f9
Packit c1c4f9
-- | put Word8 in the current position in the stream
Packit c1c4f9
putWord8 :: Word8 -> Packer ()
Packit c1c4f9
putWord8 = putStorable
Packit c1c4f9
{-# INLINE putWord8 #-}
Packit c1c4f9
Packit c1c4f9
-- | put Word16 in the current position in the stream
Packit c1c4f9
-- /!\ use Host Endianness
Packit c1c4f9
putWord16 :: Word16 -> Packer ()
Packit c1c4f9
putWord16 = putStorable
Packit c1c4f9
{-# INLINE putWord16 #-}
Packit c1c4f9
Packit c1c4f9
-- | put Word32 in the current position in the stream
Packit c1c4f9
-- /!\ use Host Endianness
Packit c1c4f9
putWord32 :: Word32 -> Packer ()
Packit c1c4f9
putWord32 = putStorable
Packit c1c4f9
{-# INLINE putWord32 #-}