primitive-serial-0.1: src/Data/PrimitiveSerial.hs
module Data.PrimitiveSerial
( BSRead(..)
, runWholeBSRead
, bsRead
, bsReadN
, bsReadEverything
, bsReadCount
, decodeNative
, encodeNative
, FixedNumeric(..)
, decodeLittleEndian
, encodeLittleEndian
, decodeBigEndian
, encodeBigEndian
) where
import Control.Applicative
import Control.Monad
import Data.ByteString
import Data.ByteString.Internal
import Data.Int
import Data.Word
import Foreign
import Prelude hiding (drop, length, null, take)
import System.Endian
import System.IO.Unsafe
newtype BSRead a = MkBSRead
{ runBSRead :: StrictByteString -> Maybe (StrictByteString, a)
}
instance Functor BSRead where
fmap ab (MkBSRead f) = MkBSRead $ \bs -> fmap (fmap ab) $ f bs
instance Applicative BSRead where
pure a = MkBSRead $ \bs -> Just (bs, a)
MkBSRead fab <*> MkBSRead fa =
MkBSRead $ \bs -> do
(bs', ab) <- fab bs
(bs'', a) <- fa bs'
return (bs'', ab a)
instance Alternative BSRead where
MkBSRead fa <|> MkBSRead fb = MkBSRead $ \bs -> fa bs <|> fb bs
empty = MkBSRead $ \_ -> Nothing
instance Monad BSRead where
MkBSRead f >>= q =
MkBSRead $ \bs -> do
(bs', a) <- f bs
runBSRead (q a) bs'
instance MonadPlus BSRead
runWholeBSRead :: BSRead a -> StrictByteString -> Maybe a
runWholeBSRead ra bs = do
(bs', a) <- runBSRead ra bs
if null bs'
then return a
else Nothing
bsRead :: BSRead Word8
bsRead =
MkBSRead $ \bs -> do
w <- indexMaybe bs 0
return (drop 1 bs, w)
bsReadN :: Int -> BSRead StrictByteString
bsReadN n =
MkBSRead $ \bs -> do
if n > length bs
then Nothing
else return (drop n bs, take n bs)
bsReadEverything :: BSRead StrictByteString
bsReadEverything = MkBSRead $ \bs -> Just (mempty, bs)
bsReadCount :: BSRead Int
bsReadCount = MkBSRead $ \bs -> Just (bs, length bs)
decodeNative ::
forall a. Storable a
=> BSRead a
decodeNative = let
typeSize :: Int
typeSize = sizeOf (undefined :: a)
in MkBSRead $ \bs@(BS fptr len) ->
if typeSize <= len
then Just $ (drop typeSize bs, unsafePerformIO $ withForeignPtr fptr $ \ptr -> peek $ castPtr ptr)
else Nothing
encodeNative ::
forall a. Storable a
=> a
-> StrictByteString
encodeNative a = let
typeSize :: Int
typeSize = sizeOf (undefined :: a)
in unsafePerformIO $ create typeSize $ \ptr -> poke (castPtr ptr) a
unsafeStorableCoerce ::
forall a b. (Storable a, Storable b)
=> a
-> b
unsafeStorableCoerce a = unsafePerformIO $ with a $ \ptr -> peek $ castPtr ptr
class Storable a => FixedNumeric a where
nativeToLittleEndian :: a -> a
nativeToBigEndian :: a -> a
instance FixedNumeric Word8 where
nativeToLittleEndian = id
nativeToBigEndian = id
instance FixedNumeric Word16 where
nativeToLittleEndian = fromLE16
nativeToBigEndian = fromBE16
instance FixedNumeric Word32 where
nativeToLittleEndian = fromLE32
nativeToBigEndian = fromBE32
instance FixedNumeric Word64 where
nativeToLittleEndian = fromLE64
nativeToBigEndian = fromBE64
instance FixedNumeric Int8 where
nativeToLittleEndian = id
nativeToBigEndian = id
instance FixedNumeric Int16 where
nativeToLittleEndian = unsafeStorableCoerce . nativeToLittleEndian @Word16 . unsafeStorableCoerce
nativeToBigEndian = unsafeStorableCoerce . nativeToBigEndian @Word16 . unsafeStorableCoerce
instance FixedNumeric Int32 where
nativeToLittleEndian = unsafeStorableCoerce . nativeToLittleEndian @Word32 . unsafeStorableCoerce
nativeToBigEndian = unsafeStorableCoerce . nativeToBigEndian @Word32 . unsafeStorableCoerce
instance FixedNumeric Int64 where
nativeToLittleEndian = unsafeStorableCoerce . nativeToLittleEndian @Word64 . unsafeStorableCoerce
nativeToBigEndian = unsafeStorableCoerce . nativeToBigEndian @Word64 . unsafeStorableCoerce
instance FixedNumeric Float where
nativeToLittleEndian = unsafeStorableCoerce . nativeToLittleEndian @Word32 . unsafeStorableCoerce
nativeToBigEndian = unsafeStorableCoerce . nativeToBigEndian @Word32 . unsafeStorableCoerce
instance FixedNumeric Double where
nativeToLittleEndian = unsafeStorableCoerce . nativeToLittleEndian @Word64 . unsafeStorableCoerce
nativeToBigEndian = unsafeStorableCoerce . nativeToBigEndian @Word64 . unsafeStorableCoerce
decodeLittleEndian ::
forall a. FixedNumeric a
=> BSRead a
decodeLittleEndian = fmap nativeToLittleEndian $ decodeNative
encodeLittleEndian ::
forall a. FixedNumeric a
=> a
-> StrictByteString
encodeLittleEndian = encodeNative . nativeToLittleEndian
decodeBigEndian ::
forall a. FixedNumeric a
=> BSRead a
decodeBigEndian = fmap nativeToBigEndian $ decodeNative
encodeBigEndian ::
forall a. FixedNumeric a
=> a
-> StrictByteString
encodeBigEndian = encodeNative . nativeToBigEndian