packages feed

packed-data-0.2.0.0: src/Data/Packed/Unpackable.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UndecidableInstances #-}

module Data.Packed.Unpackable (
    Unpackable (..),
    PackedReader,
    unpack,
    unpack',
    runReader,
    readerWithoutShift,
) where

import Control.Monad.Identity
import Data.Packed.Packed
import Data.Packed.Reader hiding (return)
import Foreign (Storable (peek, sizeOf), castPtr, plusPtr)
import GHC.Exts
import GHC.IO

-- | An 'Unpackable' is a value that can be read (i.e. deserialised) from a 'Data.Packed' value
class Unpackable a where
    -- | The 'Data.Packed.Reader.PackedReader' to unpack a value of that type
    reader :: PackedReader '[a] r a

instance (Storable a) => Unpackable a where
    {-# INLINE reader #-}
    reader = PackedReader $ \(PF ptr int) -> case runRW# (unIO (peek $ castPtr ptr)) of
        (# _, !n #) ->
            let
                !sizeOfN = sizeOf n
                !shiftedPtr = ptr `plusPtr` sizeOfN
                !shiftedSize = int - sizeOfN
             in
                return (n, PF shiftedPtr shiftedSize)

{-# INLINE readerWithoutShift #-}

-- | In a `PackedReader`, reads a value without moving the cursor
readerWithoutShift :: (Unpackable a) => PackedReader '[] (a ': r) a
readerWithoutShift = mkPackedReader $ \pf ->
    let
        Identity !(!a, !_) = runReaderStep reader pf
     in
        return (a, pf)

{-# INLINE unpack #-}

-- | Deserialise a value from a 'Data.Packed.Packed'.
--
-- Returns the unconsumed 'Data.Packed.Packed' portion
unpack :: (Unpackable a) => Packed (a ': r) -> (a, Packed r)
unpack = runReader reader

{-# INLINE unpack' #-}

-- | Same as 'unpack', but throws away the unconsumed bytes
unpack' :: (Unpackable a) => Packed (a : r) -> a
unpack' p = fst $ unpack p