haskus-binary-1.3: src/lib/Haskus/Format/Binary/Serialize.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Binary serialization of Haskell values
module Haskus.Format.Binary.Serialize
( PutMonad (..)
, GetMonad (..)
, Serializable (..)
, Size (..)
-- * Endianness helpers
, putWord16BE
, putWord32BE
, putWord64BE
, putWord16LE
, putWord32LE
, putWord64LE
, putWord16BEs
, putWord32BEs
, putWord64BEs
, putWord16LEs
, putWord32LEs
, putWord64LEs
, getWord16BE
, getWord32BE
, getWord64BE
, getWord16LE
, getWord32LE
, getWord64LE
, getWord16BEs
, getWord32BEs
, getWord64BEs
, getWord16LEs
, getWord32LEs
, getWord64LEs
)
where
import Haskus.Memory.Buffer
import Haskus.Format.Binary.Word
import Haskus.Format.Binary.Endianness
import Haskus.Utils.Types
import Haskus.Utils.Monad
import GHC.Exts (IsList(..))
-- | Monad which can build a sequence of bytes
class Monad m => PutMonad m where
-- | Write a Word8
putWord8 :: Word8 -> m ()
-- | Write a Word16
putWord16 :: Word16 -> m ()
-- | Write a Word32
putWord32 :: Word32 -> m ()
-- | Write a Word64
putWord64 :: Word64 -> m ()
-- | Write some Word8
putWord8s :: [Word8] -> m ()
putWord8s xs = forM_ xs putWord8
-- | Write some Word16
putWord16s :: [Word16] -> m ()
putWord16s xs = forM_ xs putWord16
-- | Write some Word32
putWord32s :: [Word32] -> m ()
putWord32s xs = forM_ xs putWord32
-- | Write some Word64
putWord64s :: [Word64] -> m ()
putWord64s xs = forM_ xs putWord64
-- | Write the contents of a buffer
putBuffer :: Buffer mut pin gc heap -> m ()
-- | Pre-allocate at least the given amount of bytes
--
-- This is a hint for the putter to speed up the allocation of memory
preAllocateAtLeast :: Word -> m ()
preAllocateAtLeast _ = return ()
-- | Monad which can read a sequence of bytes
class Monad m => GetMonad m where
-- | Read a Word8
getWord8 :: m Word8
-- | Read a Word16 with host endianness
getWord16 :: m Word16
-- | Read a Word32 with host endianness
getWord32 :: m Word32
-- | Read a Word64 with host endianness
getWord64 :: m Word64
-- | Read some Word8
getWord8s :: Word -> m [Word8]
getWord8s n = replicateM (fromIntegral n) getWord8
-- | Read some Word16 with host endianness
getWord16s :: Word -> m [Word16]
getWord16s n = replicateM (fromIntegral n) getWord16
-- | Read some Word32 with host endianness
getWord32s :: Word -> m [Word32]
getWord32s n = replicateM (fromIntegral n) getWord32
-- | Read some Word64 with host endianness
getWord64s :: Word -> m [Word64]
getWord64s n = replicateM (fromIntegral n) getWord64
-- | Read the given amount of bytes into a new buffer
getBuffer :: Word -> m BufferI
getBuffer n = do
xs <- replicateM (fromIntegral n) getWord8
return (fromListN (fromIntegral n) xs)
-- | Read the given amount of bytes into the specified buffer
getBufferInto :: Word -> Buffer 'Mutable pin gc heap -> m ()
-- | Size in bytes
data Size
= Exactly Nat -- ^ Exactly the given size
| AtLeast Nat -- ^ At least the given size
| Dynamic -- ^ Dynamically known size
-- | Binary serializable data
class Serializable a where
-- | Size of the data in bytes
type SizeOf a :: Size
-- | Sensible to endianness
type Endian a :: Bool
-- | Dynamic size of the data in bytes
sizeOf :: a -> Word
-- | Serialize a value
put :: PutMonad m => Endianness -> a -> m ()
-- | Deserialize a value
get :: GetMonad m => Endianness -> Word -> m a
--------------------------------------------
-- Helpers for endianness
--------------------------------------------
-- | Write a Word16 with little-endian order
putWord16LE :: PutMonad m => Word16 -> m ()
putWord16LE x = putWord16 (hostToLittleEndian x)
-- | Write a Word32 with little-endian order
putWord32LE :: PutMonad m => Word32 -> m ()
putWord32LE x = putWord32 (hostToLittleEndian x)
-- | Write a Word64 with little-endian order
putWord64LE :: PutMonad m => Word64 -> m ()
putWord64LE x = putWord64 (hostToLittleEndian x)
-- | Write a Word16 with big-endian order
putWord16BE :: PutMonad m => Word16 -> m ()
putWord16BE x = putWord16 (hostToBigEndian x)
-- | Write a Word32 with big-endian order
putWord32BE :: PutMonad m => Word32 -> m ()
putWord32BE x = putWord32 (hostToBigEndian x)
-- | Write a Word64 with big-endian order
putWord64BE :: PutMonad m => Word64 -> m ()
putWord64BE x = putWord64 (hostToBigEndian x)
-- | Write some Word16 with little-endian order
putWord16LEs :: PutMonad m => [Word16] -> m ()
putWord16LEs xs = putWord16s (fmap hostToLittleEndian xs)
-- | Write some Word32 with little-endian order
putWord32LEs :: PutMonad m => [Word32] -> m ()
putWord32LEs xs = putWord32s (fmap hostToLittleEndian xs)
-- | Write some Word64 with little-endian order
putWord64LEs :: PutMonad m => [Word64] -> m ()
putWord64LEs xs = putWord64s (fmap hostToLittleEndian xs)
-- | Write some Word16 with big-endian order
putWord16BEs :: PutMonad m => [Word16] -> m ()
putWord16BEs xs = putWord16s (fmap hostToBigEndian xs)
-- | Write some Word32 with big-endian order
putWord32BEs :: PutMonad m => [Word32] -> m ()
putWord32BEs xs = putWord32s (fmap hostToBigEndian xs)
-- | Write some Word64 with big-endian order
putWord64BEs :: PutMonad m => [Word64] -> m ()
putWord64BEs xs = putWord64s (fmap hostToBigEndian xs)
-- | Read a Word16 with little-endian order
getWord16LE :: GetMonad m => m Word16
getWord16LE = littleEndianToHost <$> getWord16
-- | Read a Word32 with little-endian order
getWord32LE :: GetMonad m => m Word32
getWord32LE = littleEndianToHost <$> getWord32
-- | Read a Word64 with little-endian order
getWord64LE :: GetMonad m => m Word64
getWord64LE = littleEndianToHost <$> getWord64
-- | Read a Word16 with big-endian order
getWord16BE :: GetMonad m => m Word16
getWord16BE = bigEndianToHost <$> getWord16
-- | Read a Word32 with big-endian order
getWord32BE :: GetMonad m => m Word32
getWord32BE = bigEndianToHost <$> getWord32
-- | Read a Word64 with big-endian order
getWord64BE :: GetMonad m => m Word64
getWord64BE = bigEndianToHost <$> getWord64
-- | Read some Word16 with little-endian order
getWord16LEs :: GetMonad m => Word -> m [Word16]
getWord16LEs n = fmap littleEndianToHost <$> getWord16s n
-- | Read some Word32 with little-endian order
getWord32LEs :: GetMonad m => Word -> m [Word32]
getWord32LEs n = fmap littleEndianToHost <$> getWord32s n
-- | Read some Word64 with little-endian order
getWord64LEs :: GetMonad m => Word -> m [Word64]
getWord64LEs n = fmap littleEndianToHost <$> getWord64s n
-- | Read some Word16 with big-endian order
getWord16BEs :: GetMonad m => Word -> m [Word16]
getWord16BEs n = fmap bigEndianToHost <$> getWord16s n
-- | Read some Word32 with big-endian order
getWord32BEs :: GetMonad m => Word -> m [Word32]
getWord32BEs n = fmap bigEndianToHost <$> getWord32s n
-- | Read some Word64 with big-endian order
getWord64BEs :: GetMonad m => Word -> m [Word64]
getWord64BEs n = fmap bigEndianToHost <$> getWord64s n
--------------------------------------------
-- Instances
--------------------------------------------
instance Serializable Word8 where
type SizeOf Word8 = 'Exactly 1
type Endian Word8 = 'False
sizeOf _ = 1
put _ x = putWord8 x
get _ _ = getWord8
instance Serializable Word16 where
type SizeOf Word16 = 'Exactly 2
type Endian Word16 = 'True
sizeOf _ = 2
put LittleEndian x = putWord16LE x
put BigEndian x = putWord16BE x
get LittleEndian _ = getWord16LE
get BigEndian _ = getWord16BE
instance Serializable Word32 where
type SizeOf Word32 = 'Exactly 4
type Endian Word32 = 'True
sizeOf _ = 4
put LittleEndian x = putWord32LE x
put BigEndian x = putWord32BE x
get LittleEndian _ = getWord32LE
get BigEndian _ = getWord32BE
instance Serializable Word64 where
type SizeOf Word64 = 'Exactly 8
type Endian Word64 = 'True
sizeOf _ = 8
put LittleEndian x = putWord64LE x
put BigEndian x = putWord64BE x
get LittleEndian _ = getWord64LE
get BigEndian _ = getWord64BE
instance Serializable Int8 where
type SizeOf Int8 = 'Exactly 1
type Endian Int8 = 'False
sizeOf _ = 1
put _ x = putWord8 (fromIntegral x)
get _ _ = fromIntegral <$> getWord8
instance Serializable Int16 where
type SizeOf Int16 = 'Exactly 2
type Endian Int16 = 'True
sizeOf _ = 2
put LittleEndian x = putWord16LE (fromIntegral x)
put BigEndian x = putWord16BE (fromIntegral x)
get LittleEndian _ = fromIntegral <$> getWord16LE
get BigEndian _ = fromIntegral <$> getWord16BE
instance Serializable Int32 where
type SizeOf Int32 = 'Exactly 4
type Endian Int32 = 'True
sizeOf _ = 4
put LittleEndian x = putWord32LE (fromIntegral x)
put BigEndian x = putWord32BE (fromIntegral x)
get LittleEndian _ = fromIntegral <$> getWord32LE
get BigEndian _ = fromIntegral <$> getWord32BE
instance Serializable Int64 where
type SizeOf Int64 = 'Exactly 8
type Endian Int64 = 'True
sizeOf _ = 8
put LittleEndian x = putWord64LE (fromIntegral x)
put BigEndian x = putWord64BE (fromIntegral x)
get LittleEndian _ = fromIntegral <$> getWord64LE
get BigEndian _ = fromIntegral <$> getWord64BE
instance Serializable BufferI where
type SizeOf BufferI = 'Dynamic
type Endian BufferI = 'False
sizeOf b = bufferSize b
put _ x = putBuffer x
get _ sz = getBuffer sz
instance Serializable a => Serializable (AsBigEndian a) where
type SizeOf (AsBigEndian a) = SizeOf a
type Endian (AsBigEndian a) = 'False
sizeOf (AsBigEndian b) = sizeOf b
put _ (AsBigEndian x) = put BigEndian x
get _ sz = AsBigEndian <$> get BigEndian sz
instance Serializable a => Serializable (AsLittleEndian a) where
type SizeOf (AsLittleEndian a) = SizeOf a
type Endian (AsLittleEndian a) = 'False
sizeOf (AsLittleEndian b) = sizeOf b
put _ (AsLittleEndian x) = put LittleEndian x
get _ sz = AsLittleEndian <$> get LittleEndian sz