binrep-0.7.0: src/Binrep/Get/Struct.hs
{-# LANGUAGE UndecidableInstances #-} -- for Generically instance
module Binrep.Get.Struct where
import Binrep.Get.Error
import Bytezap.Parser.Struct
import Bytezap.Parser.Struct.Generic
import Binrep.CBLen
import Foreign.Ptr ( Ptr )
import Data.Void ( Void )
import GHC.Exts ( Proxy#, Int(I#) )
import GHC.TypeNats ( KnownNat )
import GHC.Generics
import Binrep.Common.Via.Prim ( ViaPrim(..) )
import Raehik.Compat.Data.Primitive.Types ( Prim' )
import Data.Word ( Word8 )
import Data.Int ( Int8 )
import Binrep.Util.ByteOrder
import Data.Functor.Identity
import Raehik.Compat.Data.Primitive.Types.Endian ( ByteSwap )
import Data.ByteString qualified as B
import Generic.Type.Assert
type GetterC = Parser E
-- | constant size parser
class GetC a where getC :: GetterC a
runGetCBs
:: forall a. (GetC a, KnownNat (CBLen a))
=> B.ByteString -> Either E a
runGetCBs bs =
if cblen @a <= B.length bs
then unsafeRunGetC' unsafeRunParserBs bs
else Left $ E 0 $ EBase $ ERanOut 0 -- TODO made up numbers
-- | doesn't check len
unsafeRunGetC'
:: forall a buf. GetC a
=> (forall e. buf -> Parser e a -> Result e a)
-> buf -> Either E a
unsafeRunGetC' p buf =
case p buf getC of
OK a -> Right a
Fail -> Left EFail
Err e -> Left e
-- | doesn't check len
unsafeRunGetCPtr
:: forall a. GetC a
=> Ptr Word8 -> Either E a
unsafeRunGetCPtr = unsafeRunGetC' unsafeRunParserPtr
instance GParseBase GetC where
type GParseBaseSt GetC = Proxy# Void
type GParseBaseC GetC a = GetC a
type GParseBaseE GetC = E
gParseBase = getC
type GParseBaseLenTF GetC = CBLenSym
-- | Serialize a term of the struct-like type @a@ via its 'Generic' instance.
getGenericStruct
:: forall a
. ( Generic a, GParse GetC (Rep a)
, GAssertNotVoid a, GAssertNotSum a
) => GetterC a
getGenericStruct = to <$> gParse @GetC
instance
( Generic a, GParse GetC (Rep a)
, GAssertNotVoid a, GAssertNotSum a
) => GetC (Generically a) where
getC = Generically <$> getGenericStruct
instance GetC () where
{-# INLINE getC #-}
getC = constParse ()
instance Prim' a => GetC (ViaPrim a) where
getC = ViaPrim <$> prim
{-# INLINE getC #-}
instance GetC a => GetC (Identity a) where getC = Identity <$> getC
deriving via ViaPrim Word8 instance GetC Word8
deriving via ViaPrim Int8 instance GetC Int8
deriving via Word8 instance GetC (ByteOrdered end Word8)
deriving via Int8 instance GetC (ByteOrdered end Int8)
-- ByteSwap is required on opposite endian platforms, but we're not checking
-- here, so make sure to keep it on both.
deriving via ViaPrim (ByteOrdered 'LittleEndian a)
instance (Prim' a, ByteSwap a) => GetC (ByteOrdered 'LittleEndian a)
deriving via ViaPrim (ByteOrdered 'BigEndian a)
instance (Prim' a, ByteSwap a) => GetC (ByteOrdered 'BigEndian a)
{-
instance TypeError ENoEmpty => PutC Void where putC = undefined
instance TypeError ENoSum => PutC (Either a b) where putC = undefined
instance PutC a => PutC (Identity a) where putC = putC . runIdentity
instance PutC PutterC where putC = id
-- | Look weird? Yeah. But it's correct :)
instance (PutC l, KnownNat (CBLen l), PutC r) => PutC (l, r) where
{-# INLINE putC #-}
putC (l, r) = sequencePokes (putC l) (cblen @l) (putC r)
-}
eCBase :: EBase -> GetterC a
eCBase eb = ParserT $ \_fpc _base os# st ->
Err# st (E (I# os#) $ EBase eb)
getECBase :: GetterC a -> EBase -> GetterC a
getECBase (ParserT p) eb = ParserT $ \fpc base os# st0 ->
case p fpc base os# st0 of
Fail# st1 -> Err# st1 (E (I# os#) $ EBase eb)
Err# st1 e -> Err# st1 (E (I# os#) $ EAnd e eb)
x -> x