{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
module Capnp.IO
( hGetValue
, getValue
, sGetMsg
, sGetValue
, hPutValue
, putValue
, sPutValue
, sPutMsg
, M.hGetMsg
, M.getMsg
, M.hPutMsg
, M.putMsg
) where
import Data.Bits
import Control.Exception (throwIO)
import Control.Monad.Primitive (RealWorld)
import Control.Monad.Trans.Class (lift)
import Network.Simple.TCP (Socket, recv, sendLazy)
import System.IO (Handle, stdin, stdout)
import System.IO.Error (eofErrorType, mkIOError)
import qualified Data.ByteString as BS
import Capnp.Bits (WordCount, wordsToBytes)
import Capnp.Classes
(Cerialize(..), Decerialize(..), FromStruct(..), ToStruct(..))
import Capnp.Convert (msgToLBS, valueToLBS)
import Capnp.TraversalLimit (evalLimitT)
import Codec.Capnp (getRoot, setRoot)
import Data.Mutable (Thaw(..))
import qualified Capnp.Message as M
hGetValue :: FromStruct M.ConstMsg a => Handle -> WordCount -> IO a
hGetValue handle limit = do
msg <- M.hGetMsg handle limit
evalLimitT limit (getRoot msg)
getValue :: FromStruct M.ConstMsg a => WordCount -> IO a
getValue = hGetValue stdin
sGetValue :: FromStruct M.ConstMsg a => Socket -> WordCount -> IO a
sGetValue socket limit = do
msg <- sGetMsg socket limit
evalLimitT limit (getRoot msg)
sGetMsg :: Socket -> WordCount -> IO M.ConstMsg
sGetMsg socket limit =
evalLimitT limit $ M.readMessage (lift read32) (lift . readSegment)
where
read32 = do
bytes <- recvFull 4
pure $
(fromIntegral (bytes `BS.index` 0) `shiftL` 0) .|.
(fromIntegral (bytes `BS.index` 1) `shiftL` 8) .|.
(fromIntegral (bytes `BS.index` 2) `shiftL` 16) .|.
(fromIntegral (bytes `BS.index` 3) `shiftL` 24)
readSegment !words = do
bytes <- recvFull (fromIntegral $ wordsToBytes words)
M.fromByteString bytes
recvFull :: Int -> IO BS.ByteString
recvFull !count = do
maybeBytes <- recv socket count
case maybeBytes of
Nothing ->
throwIO $ mkIOError eofErrorType "Remote socket closed" Nothing Nothing
Just bytes
| BS.length bytes == count ->
pure bytes
| otherwise ->
(bytes <>) <$> recvFull (count - BS.length bytes)
hPutValue :: (Cerialize a, ToStruct (M.MutMsg RealWorld) (Cerial (M.MutMsg RealWorld) a))
=> Handle -> a -> IO ()
hPutValue handle value = do
msg <- M.newMessage Nothing
root <- evalLimitT maxBound $ cerialize msg value
setRoot root
constMsg <- freeze msg
M.hPutMsg handle constMsg
putValue :: (Cerialize a, ToStruct (M.MutMsg RealWorld) (Cerial (M.MutMsg RealWorld) a))
=> a -> IO ()
putValue = hPutValue stdout
sPutMsg :: Socket -> M.ConstMsg -> IO ()
sPutMsg socket = sendLazy socket . msgToLBS
sPutValue :: (Cerialize a, ToStruct (M.MutMsg RealWorld) (Cerial (M.MutMsg RealWorld) a))
=> Socket -> a -> IO ()
sPutValue socket value = do
lbs <- evalLimitT maxBound $ valueToLBS value
sendLazy socket lbs