{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Capnp.Untyped
( Ptr(..), List(..), Struct, ListOf
, dataSection, ptrSection
, getData, getPtr
, setData, setPtr
, copyStruct
, get, index, length
, setIndex
, take
, rootPtr
, setRoot
, rawBytes
, ReadCtx
, RWCtx
, HasMessage(..), MessageDefault(..)
, allocStruct
, allocCompositeList
, allocList0
, allocList1
, allocList8
, allocList16
, allocList32
, allocList64
, allocListPtr
)
where
import Prelude hiding (length, take)
import Data.Bits
import Data.Word
import Control.Monad (forM_)
import Control.Monad.Catch (MonadThrow(throwM))
import qualified Data.ByteString as BS
import Data.Capnp.Address (OffsetError(..), WordAddr(..), pointerFrom)
import Data.Capnp.Bits
( BitCount(..)
, ByteCount(..)
, Word1(..)
, WordCount(..)
, bitsToBytesCeil
, bytesToWordsCeil
, replaceBits
, wordsToBytes
)
import Data.Capnp.Pointer (ElementSize(..))
import Data.Capnp.TraversalLimit (MonadLimit(invoice))
import qualified Data.Capnp.Errors as E
import qualified Data.Capnp.Message as M
import qualified Data.Capnp.Pointer as P
type ReadCtx m msg = (M.Message m msg, MonadThrow m, MonadLimit m)
type RWCtx m s = (ReadCtx m (M.MutMsg s), M.WriteCtx m s)
data Ptr msg
= PtrCap msg !Word32
| PtrList (List msg)
| PtrStruct (Struct msg)
data List msg
= List0 (ListOf msg ())
| List1 (ListOf msg Bool)
| List8 (ListOf msg Word8)
| List16 (ListOf msg Word16)
| List32 (ListOf msg Word32)
| List64 (ListOf msg Word64)
| ListPtr (ListOf msg (Maybe (Ptr msg)))
| ListStruct (ListOf msg (Struct msg))
data NormalList msg = NormalList
{ nMsg :: msg
, nAddr :: WordAddr
, nLen :: Int
}
data ListOf msg a where
ListOfVoid
:: msg
-> !Int
-> ListOf msg ()
ListOfStruct
:: Struct msg
-> !Int
-> ListOf msg (Struct msg)
ListOfBool :: !(NormalList msg) -> ListOf msg Bool
ListOfWord8 :: !(NormalList msg) -> ListOf msg Word8
ListOfWord16 :: !(NormalList msg) -> ListOf msg Word16
ListOfWord32 :: !(NormalList msg) -> ListOf msg Word32
ListOfWord64 :: !(NormalList msg) -> ListOf msg Word64
ListOfPtr :: !(NormalList msg) -> ListOf msg (Maybe (Ptr msg))
data Struct msg
= Struct
msg
!WordAddr
!Word16
!Word16
instance M.Mutable msg => M.Mutable (Ptr msg) where
type Scope (Ptr msg) = M.Scope msg
type Frozen (Ptr msg) = Ptr (M.Frozen msg)
thaw (PtrCap cmsg n) = do
mmsg <- M.thaw cmsg
pure $ PtrCap mmsg n
thaw (PtrList l) = PtrList <$> M.thaw l
thaw (PtrStruct s) = PtrStruct <$> M.thaw s
freeze (PtrCap mmsg n) = do
cmsg <- M.freeze mmsg
pure $ PtrCap cmsg n
freeze (PtrList l) = PtrList <$> M.freeze l
freeze (PtrStruct s) = PtrStruct <$> M.freeze s
instance M.Mutable msg => M.Mutable (List msg) where
type Scope (List msg) = M.Scope msg
type Frozen (List msg) = List (M.Frozen msg)
thaw (List0 l) = List0 <$> M.thaw l
thaw (List1 l) = List1 <$> M.thaw l
thaw (List8 l) = List8 <$> M.thaw l
thaw (List16 l) = List16 <$> M.thaw l
thaw (List32 l) = List32 <$> M.thaw l
thaw (List64 l) = List64 <$> M.thaw l
thaw (ListPtr l) = ListPtr <$> M.thaw l
thaw (ListStruct l) = ListStruct <$> M.thaw l
freeze (List0 l) = List0 <$> M.freeze l
freeze (List1 l) = List1 <$> M.freeze l
freeze (List8 l) = List8 <$> M.freeze l
freeze (List16 l) = List16 <$> M.freeze l
freeze (List32 l) = List32 <$> M.freeze l
freeze (List64 l) = List64 <$> M.freeze l
freeze (ListPtr l) = ListPtr <$> M.freeze l
freeze (ListStruct l) = ListStruct <$> M.freeze l
instance M.Mutable msg => M.Mutable (NormalList msg) where
type Scope (NormalList msg) = M.Scope msg
type Frozen (NormalList msg) = NormalList (M.Frozen msg)
thaw NormalList{..} = do
mmsg <- M.thaw nMsg
pure NormalList { nMsg = mmsg, .. }
freeze NormalList{..} = do
cmsg <- M.freeze nMsg
pure NormalList { nMsg = cmsg, .. }
instance M.Mutable msg => M.Mutable (ListOf msg ()) where
type Scope (ListOf msg ()) = M.Scope msg
type Frozen (ListOf msg ()) = ListOf (M.Frozen msg) ()
thaw (ListOfVoid cmsg n) = do
mmsg <- M.thaw cmsg
pure $ ListOfVoid mmsg n
freeze (ListOfVoid mmsg n) = do
cmsg <- M.freeze mmsg
pure $ ListOfVoid cmsg n
instance M.Mutable msg => M.Mutable (ListOf msg Bool) where
type Scope (ListOf msg Bool) = M.Scope msg
type Frozen (ListOf msg Bool) = ListOf (M.Frozen msg) Bool
thaw (ListOfBool msg) = ListOfBool <$> M.thaw msg
freeze (ListOfBool msg) = ListOfBool <$> M.freeze msg
instance M.Mutable msg => M.Mutable (ListOf msg Word8) where
type Scope (ListOf msg Word8) = M.Scope msg
type Frozen (ListOf msg Word8) = ListOf (M.Frozen msg) Word8
thaw (ListOfWord8 msg) = ListOfWord8 <$> M.thaw msg
freeze (ListOfWord8 msg) = ListOfWord8 <$> M.freeze msg
instance M.Mutable msg => M.Mutable (ListOf msg Word16) where
type Scope (ListOf msg Word16) = M.Scope msg
type Frozen (ListOf msg Word16) = ListOf (M.Frozen msg) Word16
thaw (ListOfWord16 msg) = ListOfWord16 <$> M.thaw msg
freeze (ListOfWord16 msg) = ListOfWord16 <$> M.freeze msg
instance M.Mutable msg => M.Mutable (ListOf msg Word32) where
type Scope (ListOf msg Word32) = M.Scope msg
type Frozen (ListOf msg Word32) = ListOf (M.Frozen msg) Word32
thaw (ListOfWord32 msg) = ListOfWord32 <$> M.thaw msg
freeze (ListOfWord32 msg) = ListOfWord32 <$> M.freeze msg
instance M.Mutable msg => M.Mutable (ListOf msg Word64) where
type Scope (ListOf msg Word64) = M.Scope msg
type Frozen (ListOf msg Word64) = ListOf (M.Frozen msg) Word64
thaw (ListOfWord64 msg) = ListOfWord64 <$> M.thaw msg
freeze (ListOfWord64 msg) = ListOfWord64 <$> M.freeze msg
instance M.Mutable msg => M.Mutable (ListOf msg (Struct msg)) where
type Scope (ListOf msg (Struct msg)) = M.Scope msg
type Frozen (ListOf msg (Struct msg)) = ListOf (M.Frozen msg) (Struct (M.Frozen msg))
thaw (ListOfStruct ctag size) = do
mtag <- M.thaw ctag
pure $ ListOfStruct mtag size
freeze (ListOfStruct mtag size) = do
ctag <- M.freeze mtag
pure $ ListOfStruct ctag size
instance M.Mutable msg => M.Mutable (ListOf msg (Maybe (Ptr msg))) where
type Scope (ListOf msg (Maybe (Ptr msg))) = M.Scope msg
type Frozen (ListOf msg (Maybe (Ptr msg))) = ListOf (M.Frozen msg) (Maybe (Ptr (M.Frozen msg)))
thaw (ListOfPtr msg) = ListOfPtr <$> M.thaw msg
freeze (ListOfPtr msg) = ListOfPtr <$> M.freeze msg
instance M.Mutable msg => M.Mutable (Struct msg) where
type Scope (Struct msg) = M.Scope msg
type Frozen (Struct msg) = Struct (M.Frozen msg)
thaw (Struct cmsg addr dataSz ptrSz) = do
mmsg <- M.thaw cmsg
pure $ Struct mmsg addr dataSz ptrSz
freeze (Struct mmsg addr dataSz ptrSz) = do
cmsg <- M.freeze mmsg
pure $ Struct cmsg addr dataSz ptrSz
class HasMessage a msg where
message :: a -> msg
class HasMessage a msg => MessageDefault a msg where
messageDefault :: msg -> a
instance HasMessage (Ptr msg) msg where
message (PtrCap msg _) = msg
message (PtrList list) = message list
message (PtrStruct struct) = message struct
instance HasMessage (Struct msg) msg where
message (Struct msg _ _ _) = msg
instance MessageDefault (Struct msg) msg where
messageDefault msg = Struct msg (WordAt 0 0) 0 0
instance HasMessage (List msg) msg where
message (List0 list) = message list
message (List1 list) = message list
message (List8 list) = message list
message (List16 list) = message list
message (List32 list) = message list
message (List64 list) = message list
message (ListPtr list) = message list
message (ListStruct list) = message list
instance HasMessage (ListOf msg a) msg where
message (ListOfVoid msg _) = msg
message (ListOfStruct tag _) = message tag
message (ListOfBool list) = message list
message (ListOfWord8 list) = message list
message (ListOfWord16 list) = message list
message (ListOfWord32 list) = message list
message (ListOfWord64 list) = message list
message (ListOfPtr list) = message list
instance MessageDefault (ListOf msg ()) msg where
messageDefault msg = ListOfVoid msg 0
instance MessageDefault (ListOf msg (Struct msg)) msg where
messageDefault msg = ListOfStruct (messageDefault msg) 0
instance MessageDefault (ListOf msg Bool) msg where
messageDefault msg = ListOfBool (messageDefault msg)
instance MessageDefault (ListOf msg Word8) msg where
messageDefault msg = ListOfWord8 (messageDefault msg)
instance MessageDefault (ListOf msg Word16) msg where
messageDefault msg = ListOfWord16 (messageDefault msg)
instance MessageDefault (ListOf msg Word32) msg where
messageDefault msg = ListOfWord32 (messageDefault msg)
instance MessageDefault (ListOf msg Word64) msg where
messageDefault msg = ListOfWord64 (messageDefault msg)
instance MessageDefault (ListOf msg (Maybe (Ptr msg))) msg where
messageDefault msg = ListOfPtr (messageDefault msg)
instance HasMessage (NormalList msg) msg where
message = nMsg
instance MessageDefault (NormalList msg) msg where
messageDefault msg = NormalList msg (WordAt 0 0) 0
get :: ReadCtx m msg => msg -> WordAddr -> m (Maybe (Ptr msg))
get msg addr = do
word <- getWord msg addr
case P.parsePtr word of
Nothing -> return Nothing
Just p -> case p of
P.CapPtr cap -> return $ Just $ PtrCap msg cap
P.StructPtr off dataSz ptrSz -> return $ Just $ PtrStruct $
Struct msg (resolveOffset addr off) dataSz ptrSz
P.ListPtr off eltSpec -> Just <$> getList (resolveOffset addr off) eltSpec
P.FarPtr twoWords offset segment -> do
let addr' = WordAt { wordIndex = fromIntegral offset
, segIndex = fromIntegral segment
}
if not twoWords
then get msg addr'
else do
landingPad <- getWord msg addr'
case P.parsePtr landingPad of
Just (P.FarPtr False off seg) -> do
tagWord <- getWord
msg
addr' { wordIndex = wordIndex addr' + 1 }
let finalAddr = WordAt { wordIndex = fromIntegral off
, segIndex = fromIntegral seg
}
case P.parsePtr tagWord of
Just (P.StructPtr 0 dataSz ptrSz) ->
return $ Just $ PtrStruct $
Struct msg finalAddr dataSz ptrSz
Just (P.ListPtr 0 eltSpec) ->
Just <$> getList finalAddr eltSpec
Just (P.CapPtr cap) ->
return $ Just $ PtrCap msg cap
ptr -> throwM $ E.InvalidDataError $
"The tag word of a far pointer's " ++
"2-word landing pad should be an intra " ++
"segment pointer with offset 0, but " ++
"we read " ++ show ptr
ptr -> throwM $ E.InvalidDataError $
"The first word of a far pointer's 2-word " ++
"landing pad should be another far pointer " ++
"(with a one-word landing pad), but we read " ++
show ptr
where
getWord msg addr = invoice 1 *> M.getWord msg addr
resolveOffset addr@WordAt{..} off =
addr { wordIndex = wordIndex + fromIntegral off + 1 }
getList addr@WordAt{..} eltSpec = PtrList <$>
case eltSpec of
P.EltNormal sz len -> pure $ case sz of
Sz0 -> List0 (ListOfVoid msg (fromIntegral len))
Sz1 -> List1 (ListOfBool nlist)
Sz8 -> List8 (ListOfWord8 nlist)
Sz16 -> List16 (ListOfWord16 nlist)
Sz32 -> List32 (ListOfWord32 nlist)
Sz64 -> List64 (ListOfWord64 nlist)
SzPtr -> ListPtr (ListOfPtr nlist)
where
nlist = NormalList msg addr (fromIntegral len)
P.EltComposite _ -> do
tagWord <- getWord msg addr
case P.parsePtr tagWord of
Just (P.StructPtr numElts dataSz ptrSz) ->
pure $ ListStruct $ ListOfStruct
(Struct msg
addr { wordIndex = wordIndex + 1 }
dataSz
ptrSz)
(fromIntegral numElts)
tag -> throwM $ E.InvalidDataError $
"Composite list tag was not a struct-" ++
"formatted word: " ++ show tag
listEltSpec :: List msg -> P.EltSpec
listEltSpec (ListStruct list@(ListOfStruct (Struct msg _ dataSz ptrSz) _)) =
P.EltComposite $ fromIntegral (length list) * (fromIntegral dataSz + fromIntegral ptrSz)
listEltSpec (List0 list) = P.EltNormal Sz0 $ fromIntegral (length list)
listEltSpec (List1 list) = P.EltNormal Sz1 $ fromIntegral (length list)
listEltSpec (List8 list) = P.EltNormal Sz8 $ fromIntegral (length list)
listEltSpec (List16 list) = P.EltNormal Sz16 $ fromIntegral (length list)
listEltSpec (List32 list) = P.EltNormal Sz32 $ fromIntegral (length list)
listEltSpec (List64 list) = P.EltNormal Sz64 $ fromIntegral (length list)
listEltSpec (ListPtr list) = P.EltNormal SzPtr $ fromIntegral (length list)
listAddr :: List msg -> WordAddr
listAddr (ListStruct (ListOfStruct (Struct _ addr _ _) _)) =
addr { wordIndex = wordIndex addr - 1 }
listAddr (List0 _) = WordAt { segIndex = 0, wordIndex = 1 }
listAddr (List1 (ListOfBool NormalList{nAddr})) = nAddr
listAddr (List8 (ListOfWord8 NormalList{nAddr})) = nAddr
listAddr (List16 (ListOfWord16 NormalList{nAddr})) = nAddr
listAddr (List32 (ListOfWord32 NormalList{nAddr})) = nAddr
listAddr (List64 (ListOfWord64 NormalList{nAddr})) = nAddr
listAddr (ListPtr (ListOfPtr NormalList{nAddr})) = nAddr
ptrAddr :: Ptr msg -> WordAddr
ptrAddr (PtrCap _ _) = error "ptrAddr called on a capability pointer."
ptrAddr (PtrStruct (Struct _ addr _ _)) = addr
ptrAddr (PtrList list) = listAddr list
setIndex :: (ReadCtx m (M.MutMsg s), M.WriteCtx m s) => a -> Int -> ListOf (M.MutMsg s) a -> m ()
setIndex value i list | length list <= i =
throwM E.BoundsError { E.index = i, E.maxIndex = length list }
setIndex value i list = case list of
ListOfVoid _ _ -> pure ()
ListOfBool nlist -> setNIndex nlist 64 (Word1 value)
ListOfWord8 nlist -> setNIndex nlist 8 value
ListOfWord16 nlist -> setNIndex nlist 4 value
ListOfWord32 nlist -> setNIndex nlist 2 value
ListOfWord64 nlist -> setNIndex nlist 1 value
ListOfPtr nlist -> case value of
Nothing -> setNIndex nlist 1 (P.serializePtr Nothing)
Just (PtrCap _ cap) -> setNIndex nlist 1 (P.serializePtr (Just (P.CapPtr cap)))
Just p@(PtrList ptrList) ->
setPtrIndex nlist p $ P.ListPtr 0 (listEltSpec ptrList)
Just p@(PtrStruct (Struct _ addr dataSz ptrSz)) ->
setPtrIndex nlist p $ P.StructPtr 0 dataSz ptrSz
list@(ListOfStruct _ _) -> do
dest <- index i list
copyStruct dest value
where
setNIndex :: (ReadCtx m (M.MutMsg s), M.WriteCtx m s, Bounded a, Integral a) => NormalList (M.MutMsg s) -> Int -> a -> m ()
setNIndex NormalList{nAddr=nAddr@WordAt{..},..} eltsPerWord value = do
let wordAddr = nAddr { wordIndex = wordIndex + WordCount (i `div` eltsPerWord) }
word <- M.getWord nMsg wordAddr
let shift = (i `mod` eltsPerWord) * (64 `div` eltsPerWord)
M.setWord nMsg wordAddr $ replaceBits value word shift
setPtrIndex :: (ReadCtx m (M.MutMsg s), M.WriteCtx m s) => NormalList (M.MutMsg s) -> Ptr (M.MutMsg s) -> P.Ptr -> m ()
setPtrIndex nlist@NormalList{..} absPtr relPtr =
let srcAddr = nAddr { wordIndex = wordIndex nAddr + WordCount i }
in setPointerTo nMsg srcAddr (ptrAddr absPtr) relPtr
setPointerTo :: M.WriteCtx m s => M.MutMsg s -> WordAddr -> WordAddr -> P.Ptr -> m ()
setPointerTo msg srcAddr dstAddr relPtr =
case pointerFrom srcAddr dstAddr relPtr of
Right absPtr ->
M.setWord msg srcAddr (P.serializePtr $ Just absPtr)
Left OutOfRange ->
error "BUG: segment is too large to set the pointer."
Left DifferentSegments -> do
let WordAt{segIndex} = dstAddr
landingPadAddr <- M.allocInSeg msg segIndex 1
case pointerFrom landingPadAddr dstAddr relPtr of
Right landingPad -> do
M.setWord msg landingPadAddr (P.serializePtr $ Just landingPad)
let WordAt{segIndex,wordIndex} = landingPadAddr
M.setWord msg srcAddr $
P.serializePtr $ Just $ P.FarPtr False (fromIntegral wordIndex) (fromIntegral segIndex)
Left DifferentSegments ->
error "BUG: allocated a landing pad in the wrong segment!"
Left OutOfRange ->
error "BUG: segment is too large to set the pointer."
copyStruct :: (ReadCtx m (M.MutMsg s), M.WriteCtx m s)
=> Struct (M.MutMsg s) -> Struct (M.MutMsg s) -> m ()
copyStruct dest src = do
copySection (dataSection dest) (dataSection src) 0
copySection (ptrSection dest) (ptrSection src) Nothing
where
copySection dest src pad = do
forM_ [0..length src - 1] $ \i -> do
value <- index i src
setIndex value i dest
forM_ [length src..length dest - 1] $ \i ->
setIndex pad i dest
index :: ReadCtx m msg => Int -> ListOf msg a -> m a
index i list = invoice 1 >> index' list
where
index' :: ReadCtx m msg => ListOf msg a -> m a
index' (ListOfVoid _ len)
| i < len = pure ()
| otherwise = throwM E.BoundsError { E.index = i, E.maxIndex = len - 1 }
index' (ListOfStruct (Struct msg addr@WordAt{..} dataSz ptrSz) len)
| i < len = do
let offset = WordCount $ i * (fromIntegral dataSz + fromIntegral ptrSz)
let addr' = addr { wordIndex = wordIndex + offset }
return $ Struct msg addr' dataSz ptrSz
| otherwise = throwM E.BoundsError { E.index = i, E.maxIndex = len - 1}
index' (ListOfBool nlist) = do
Word1 val <- indexNList nlist 64
pure val
index' (ListOfWord8 nlist) = indexNList nlist 8
index' (ListOfWord16 nlist) = indexNList nlist 4
index' (ListOfWord32 nlist) = indexNList nlist 2
index' (ListOfWord64 (NormalList msg addr@WordAt{..} len))
| i < len = M.getWord msg addr { wordIndex = wordIndex + WordCount i }
| otherwise = throwM E.BoundsError { E.index = i, E.maxIndex = len - 1}
index' (ListOfPtr (NormalList msg addr@WordAt{..} len))
| i < len = get msg addr { wordIndex = wordIndex + WordCount i }
| otherwise = throwM E.BoundsError { E.index = i, E.maxIndex = len - 1}
indexNList :: (ReadCtx m msg, Integral a) => NormalList msg -> Int -> m a
indexNList (NormalList msg addr@WordAt{..} len) eltsPerWord
| i < len = do
let wordIndex' = wordIndex + WordCount (i `div` eltsPerWord)
word <- M.getWord msg addr { wordIndex = wordIndex' }
let shift = (i `mod` eltsPerWord) * (64 `div` eltsPerWord)
pure $ fromIntegral $ word `shiftR` shift
| otherwise = throwM E.BoundsError { E.index = i, E.maxIndex = len - 1 }
length :: ListOf msg a -> Int
length (ListOfVoid _ len) = len
length (ListOfStruct _ len) = len
length (ListOfBool nlist) = nLen nlist
length (ListOfWord8 nlist) = nLen nlist
length (ListOfWord16 nlist) = nLen nlist
length (ListOfWord32 nlist) = nLen nlist
length (ListOfWord64 nlist) = nLen nlist
length (ListOfPtr nlist) = nLen nlist
take :: MonadThrow m => Int -> ListOf msg a -> m (ListOf msg a)
take count list
| length list < count =
throwM E.BoundsError { E.index = count, E.maxIndex = length list - 1 }
| otherwise = pure $ go list
where
go (ListOfVoid msg _) = ListOfVoid msg count
go (ListOfStruct tag _) = ListOfStruct tag count
go (ListOfBool nlist) = ListOfBool $ nTake nlist
go (ListOfWord8 nlist) = ListOfWord8 $ nTake nlist
go (ListOfWord16 nlist) = ListOfWord16 $ nTake nlist
go (ListOfWord32 nlist) = ListOfWord32 $ nTake nlist
go (ListOfWord64 nlist) = ListOfWord64 $ nTake nlist
go (ListOfPtr nlist) = ListOfPtr $ nTake nlist
nTake :: NormalList msg -> NormalList msg
nTake NormalList{..} = NormalList { nLen = count, .. }
dataSection :: Struct msg -> ListOf msg Word64
dataSection (Struct msg addr dataSz _) =
ListOfWord64 $ NormalList msg addr (fromIntegral dataSz)
ptrSection :: Struct msg -> ListOf msg (Maybe (Ptr msg))
ptrSection (Struct msg addr@WordAt{..} dataSz ptrSz) =
ListOfPtr $ NormalList
msg
addr { wordIndex = wordIndex + fromIntegral dataSz }
(fromIntegral ptrSz)
getData :: ReadCtx m msg => Int -> Struct msg -> m Word64
getData i struct
| length (dataSection struct) <= i = 0 <$ invoice 1
| otherwise = index i (dataSection struct)
getPtr :: ReadCtx m msg => Int -> Struct msg -> m (Maybe (Ptr msg))
getPtr i struct
| length (ptrSection struct) <= i = Nothing <$ invoice 1
| otherwise = index i (ptrSection struct)
setData :: (ReadCtx m (M.MutMsg s), M.WriteCtx m s)
=> Word64 -> Int -> Struct (M.MutMsg s) -> m ()
setData value i = setIndex value i . dataSection
setPtr :: (ReadCtx m (M.MutMsg s), M.WriteCtx m s) => Maybe (Ptr (M.MutMsg s)) -> Int -> Struct (M.MutMsg s) -> m ()
setPtr value i = setIndex value i . ptrSection
rawBytes :: ReadCtx m msg => ListOf msg Word8 -> m BS.ByteString
rawBytes (ListOfWord8 (NormalList msg WordAt{..} len)) = do
invoice len
bytes <- M.getSegment msg segIndex >>= M.toByteString
let ByteCount byteOffset = wordsToBytes wordIndex
pure $ BS.take len $ BS.drop byteOffset bytes
rootPtr :: ReadCtx m msg => msg -> m (Struct msg)
rootPtr msg = do
root <- get msg (WordAt 0 0)
case root of
Just (PtrStruct struct) -> pure struct
Nothing -> pure (messageDefault msg)
_ -> throwM $ E.SchemaViolationError
"Unexpected root type; expected struct."
setRoot :: M.WriteCtx m s => Struct (M.MutMsg s) -> m ()
setRoot (Struct msg addr dataSz ptrSz) =
setPointerTo msg (WordAt 0 0) addr (P.StructPtr 0 dataSz ptrSz)
allocStruct :: M.WriteCtx m s => M.MutMsg s -> Word16 -> Word16 -> m (Struct (M.MutMsg s))
allocStruct msg dataSz ptrSz = do
let totalSz = fromIntegral dataSz + fromIntegral ptrSz
addr <- M.alloc msg totalSz
pure $ Struct msg addr dataSz ptrSz
allocCompositeList
:: M.WriteCtx m s
=> M.MutMsg s
-> Word16
-> Word16
-> Int
-> m (ListOf (M.MutMsg s) (Struct (M.MutMsg s)))
allocCompositeList msg dataSz ptrSz len = do
let eltSize = fromIntegral dataSz + fromIntegral ptrSz
addr <- M.alloc msg (WordCount $ len * eltSize + 1)
M.setWord msg addr $ P.serializePtr $ Just $ P.StructPtr (fromIntegral len) dataSz ptrSz
let firstStruct = Struct
msg
addr { wordIndex = wordIndex addr + 1 }
dataSz
ptrSz
pure $ ListOfStruct firstStruct len
allocList0 :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) ())
allocList1 :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) Bool)
allocList8 :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) Word8)
allocList16 :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) Word16)
allocList32 :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) Word32)
allocList64 :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) Word64)
allocListPtr :: M.WriteCtx m s => M.MutMsg s -> Int -> m (ListOf (M.MutMsg s) (Maybe (Ptr (M.MutMsg s))))
allocList0 msg len = pure $ ListOfVoid msg len
allocList1 msg len = ListOfBool <$> allocNormalList 1 msg len
allocList8 msg len = ListOfWord8 <$> allocNormalList 8 msg len
allocList16 msg len = ListOfWord16 <$> allocNormalList 16 msg len
allocList32 msg len = ListOfWord32 <$> allocNormalList 32 msg len
allocList64 msg len = ListOfWord64 <$> allocNormalList 64 msg len
allocListPtr msg len = ListOfPtr <$> allocNormalList 64 msg len
allocNormalList
:: M.WriteCtx m s
=> Int
-> M.MutMsg s
-> Int
-> m (NormalList (M.MutMsg s))
allocNormalList bitsPerElt msg len = do
let totalBits = BitCount (len * bitsPerElt)
totalWords = bytesToWordsCeil $ bitsToBytesCeil totalBits
addr <- M.alloc msg totalWords
pure NormalList
{ nMsg = msg
, nAddr = addr
, nLen = len
}