mirror of
https://github.com/zap-proto/haskell.git
synced 2026-10-30 15:05:00 +00:00
224 lines
7.5 KiB
Haskell
224 lines
7.5 KiB
Haskell
{-# LANGUAGE ConstraintKinds #-}
|
|
{-# LANGUAGE DataKinds #-}
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE RankNTypes #-}
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
{-# LANGUAGE TypeApplications #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
-- | Module: Capnp.New.Accessors
|
|
-- Description: Functions for accessing parts of messaages.
|
|
module Capnp.New.Accessors
|
|
( readField
|
|
, getField
|
|
, setField
|
|
, newField
|
|
, hasField
|
|
, encodeField
|
|
, parseField
|
|
, setVariant
|
|
, initVariant
|
|
, encodeVariant
|
|
, structWhich
|
|
, unionWhich
|
|
, structUnion
|
|
, unionStruct
|
|
) where
|
|
|
|
|
|
import qualified Capnp.Fields as F
|
|
import Capnp.Message (Mutability(..))
|
|
import qualified Capnp.New.Classes as C
|
|
import qualified Capnp.Repr as R
|
|
import Capnp.TraversalLimit (evalLimitT)
|
|
import qualified Capnp.Untyped as U
|
|
import Data.Bits
|
|
import Data.Maybe (fromJust, isJust)
|
|
import Data.Word
|
|
import GHC.Prim (coerce)
|
|
|
|
{-# INLINE readField #-}
|
|
-- | Read the value of a field of a struct.
|
|
readField
|
|
:: forall k a b mut m.
|
|
( R.IsStruct a
|
|
, U.ReadCtx m mut
|
|
)
|
|
=> F.Field k a b
|
|
-> R.Raw mut a
|
|
-> m (R.Raw mut b)
|
|
readField (F.Field field) (R.Raw struct) =
|
|
case field of
|
|
F.DataField F.DataFieldLoc{ shift, index, mask, defaultValue } -> do
|
|
word <- U.getData (fromIntegral index) struct
|
|
pure $ R.Raw $ C.fromWord $ ((word .&. mask) `shiftR` fromIntegral shift) `xor` defaultValue
|
|
F.PtrField index ->
|
|
U.getPtr (fromIntegral index) struct >>= readPtrField
|
|
F.GroupField ->
|
|
pure $ R.Raw struct
|
|
F.VoidField ->
|
|
pure $ R.Raw ()
|
|
where
|
|
-- This is broken out because the type checker needs some extra help:
|
|
readPtrField
|
|
:: forall pr.
|
|
( R.ReprFor b ~ 'R.Ptr pr
|
|
, R.IsPtrRepr pr
|
|
) => Maybe (U.Ptr mut) -> m (R.Raw mut b)
|
|
readPtrField ptr =
|
|
R.Raw <$> R.fromPtr @pr (U.message struct) ptr
|
|
|
|
-- | Return whether the specified field is present. Only applicable for pointer
|
|
-- fields.
|
|
hasField ::
|
|
( U.ReadCtx m mut
|
|
, R.IsStruct a
|
|
, R.IsPtr b
|
|
) => F.Field 'F.Slot a b -> R.Raw mut a -> m Bool
|
|
hasField (F.Field (F.PtrField index)) (R.Raw struct) =
|
|
isJust <$> U.getPtr (fromIntegral index) struct
|
|
|
|
{-# INLINE getField #-}
|
|
-- | Like 'readField', but:
|
|
--
|
|
-- * Doesn't need the monadic context; can be used in pure code.
|
|
-- * Only works for immutable values.
|
|
-- * Only works for fields in the struct's data section.
|
|
getField
|
|
:: ( R.IsStruct a
|
|
, R.ReprFor b ~ 'R.Data sz
|
|
, C.Parse b bp
|
|
)
|
|
=> F.Field 'F.Slot a b
|
|
-> R.Raw 'Const a
|
|
-> bp
|
|
getField field struct =
|
|
fromJust $ evalLimitT maxBound $
|
|
readField field struct >>= C.parse
|
|
|
|
{-# INLINE setField #-}
|
|
-- | Set a struct field to a value. Not usable for group fields.
|
|
setField ::
|
|
forall a b m s.
|
|
( R.IsStruct a
|
|
, U.RWCtx m s
|
|
) => F.Field 'F.Slot a b -> R.Raw ('Mut s) b -> R.Raw ('Mut s) a -> m ()
|
|
setField (F.Field field) (R.Raw value) (R.Raw struct) =
|
|
case field of
|
|
F.DataField fieldLoc ->
|
|
setDataField fieldLoc
|
|
F.PtrField index ->
|
|
setPtrField index value struct
|
|
F.VoidField ->
|
|
pure ()
|
|
where
|
|
-- This was originally broken out because the type checker needs some extra
|
|
-- help, but it's probably more readable this way anyway.
|
|
setPtrField
|
|
:: forall pr.
|
|
( R.ReprFor b ~ 'R.Ptr pr
|
|
, R.IsPtrRepr pr
|
|
) => Word16 -> R.UntypedPtr ('Mut s) pr -> U.Struct ('Mut s) -> m ()
|
|
setPtrField index value struct =
|
|
U.setPtr (R.toPtr @pr value) (fromIntegral index) struct
|
|
|
|
setDataField
|
|
:: forall sz.
|
|
( R.ReprFor b ~ 'R.Data sz
|
|
, C.IsWord (R.UntypedData sz)
|
|
) => F.DataFieldLoc sz -> m ()
|
|
setDataField F.DataFieldLoc{ shift, index, mask, defaultValue } = do
|
|
oldWord <- U.getData (fromIntegral index) struct
|
|
let valueWord = C.toWord value `xor` defaultValue
|
|
newWord = (oldWord .&. complement mask)
|
|
.|. (valueWord `shiftL` fromIntegral shift)
|
|
U.setData newWord (fromIntegral index) struct
|
|
|
|
-- | Allocate space for the value of a field, and return it.
|
|
newField ::
|
|
forall a b m s.
|
|
( R.IsStruct a
|
|
, C.Allocate b
|
|
, U.RWCtx m s
|
|
) => F.Field 'F.Slot a b -> C.AllocHint b -> R.Raw ('Mut s) a -> m (R.Raw ('Mut s) b)
|
|
newField field hint parent = do
|
|
value <- C.new @b hint (U.message parent)
|
|
setField field value parent
|
|
pure value
|
|
|
|
-- | Marshal a parsed value into a struct's field.
|
|
encodeField ::
|
|
forall a b m s bp.
|
|
( R.IsStruct a
|
|
, C.Parse b bp
|
|
, U.RWCtx m s
|
|
) => F.Field 'F.Slot a b -> bp -> R.Raw ('Mut s) a -> m ()
|
|
encodeField field parsed struct = do
|
|
encoded <- C.encode (U.message struct) parsed
|
|
setField field encoded struct
|
|
|
|
-- | parse a struct's field and return its parsed form.
|
|
parseField ::
|
|
( R.IsStruct a
|
|
, C.Parse b bp
|
|
, U.ReadCtx m 'Const
|
|
) => F.Field k a b -> R.Raw 'Const a -> m bp
|
|
parseField field raw =
|
|
readField field raw >>= C.parse
|
|
|
|
-- | Set the struct's anonymous union to the given variant, with the
|
|
-- supplied value as its argument. Not applicable for variants whose
|
|
-- argument is a group; use 'initVariant' instead.
|
|
setVariant
|
|
:: forall a b m s.
|
|
( F.HasUnion a
|
|
, U.RWCtx m s
|
|
) => F.Variant 'F.Slot a b -> R.Raw ('Mut s) a -> R.Raw ('Mut s) b -> m ()
|
|
setVariant F.Variant{field, tagValue} struct value = do
|
|
setField (F.unionField @a) (R.Raw tagValue) struct
|
|
setField field value struct
|
|
|
|
-- | Set the struct's anonymous union to the given variant, marshalling
|
|
-- the supplied value into the message to be its argument. Not applicable
|
|
-- for variants whose argument is a group; use 'initVariant' instead.
|
|
encodeVariant
|
|
:: forall a b m s bp.
|
|
( F.HasUnion a
|
|
, C.Parse b bp
|
|
, U.RWCtx m s
|
|
) => F.Variant 'F.Slot a b -> bp -> R.Raw ('Mut s) a -> m ()
|
|
encodeVariant F.Variant{field, tagValue} value struct = do
|
|
setField (F.unionField @a) (R.Raw tagValue) struct
|
|
encodeField field value struct
|
|
|
|
-- | Set the struct's anonymous union to the given variant, returning
|
|
-- the variant's argument, which must be a group (for non-group fields,
|
|
-- use 'setVariant' or 'encodeVariant'.
|
|
initVariant
|
|
:: forall a b m s. (F.HasUnion a, U.RWCtx m s)
|
|
=> F.Variant 'F.Group a b -> R.Raw ('Mut s) a -> m (R.Raw ('Mut s) b)
|
|
initVariant F.Variant{field, tagValue} struct = do
|
|
setField (F.unionField @a) (R.Raw tagValue) struct
|
|
readField field struct
|
|
|
|
-- | Get the anonymous union for a struct.
|
|
structUnion :: F.HasUnion a => R.Raw mut a -> R.Raw mut (F.Which a)
|
|
structUnion = coerce
|
|
|
|
-- | Get the struct enclosing an anonymous union.
|
|
unionStruct :: F.HasUnion a => R.Raw mut (F.Which a) -> R.Raw mut a
|
|
unionStruct = coerce
|
|
|
|
-- | Get a non-opaque view on the struct's anonymous union, which
|
|
-- can be used to pattern match on.
|
|
structWhich :: forall a mut m. (U.ReadCtx m mut, F.HasUnion a) => R.Raw mut a -> m (F.RawWhich mut a)
|
|
structWhich struct = do
|
|
R.Raw tagValue <- readField (F.unionField @a) struct
|
|
F.internalWhich tagValue struct
|
|
|
|
-- | Get a non-opaque view on the anonymous union, which can be
|
|
-- used to pattern match on.
|
|
unionWhich :: forall a mut m. (U.ReadCtx m mut, F.HasUnion a) => R.Raw mut (F.Which a) -> m (F.RawWhich mut a)
|
|
unionWhich = structWhich . unionStruct
|