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