{-# language BangPatterns #-}
{-# language LambdaCase #-}
{-# language MagicHash #-}
{-# language ScopedTypeVariables #-}
{-# language UnboxedTuples #-}
module Datagram.Receive.Many.Indefinite
( receiveMany
) where
import Control.Monad.Primitive (primitive)
import Data.Bytes.Types (MutableBytes(..))
import Data.Primitive (MutablePrimArray,MutableByteArray)
import Data.Primitive.Unlifted.Array (MutableUnliftedArray)
import Data.Word (Word8)
import Foreign.C.Types (CInt)
import GHC.Exts (RealWorld,Int(I#))
import Socket.Debug (debug,whenDebugging)
import Socket.Datagram (ReceiveException)
import Socket.Datagram.Instantiate (receiveLoop,receiveAttempt)
import Socket.Interrupt (Intr,Interrupt)
import Socket.Interrupt (wait,tokenToDatagramReceiveException)
import System.Posix.Types (Fd)
import qualified Datagram.Receive as Receive
import qualified Data.Primitive as PM
import qualified Data.Primitive.Unlifted.Array as PM
import qualified GHC.Exts as Exts
import qualified Socket.EventManager as EM
receiveMany ::
Interrupt
-> Fd
-> MutablePrimArray RealWorld CInt
-> Receive.AddressBuffer
-> MutableUnliftedArray RealWorld (MutableByteArray RealWorld)
-> IO (Either (ReceiveException Intr) Int)
receiveMany !intr !sock !lens !addrs !bufs = do
let !mngr = EM.manager
!lenBufs = PM.sizeofMutableUnliftedArray bufs
tv <- EM.reader mngr sock
token0 <- wait intr tv
case tokenToDatagramReceiveException token0 of
Left err -> pure (Left err)
Right _ -> do
buf0 <- readMutableByteArrayArray bufs 0
sz0 <- PM.getSizeofMutableByteArray buf0
whenDebugging $ do
(byte0 :: Word8) <- if sz0 > 0
then PM.readByteArray buf0 0
else pure 0
debug $ "receiveMany: datagram 0 pre-run first byte: " ++ show byte0
receiveLoop intr tv token0 sock (MutableBytes buf0 0 sz0) (Receive.offsetAddress addrs 0) >>= \case
Left err -> pure (Left err)
Right recvSz0 -> do
PM.writePrimArray lens 0 (intToCInt recvSz0)
whenDebugging $ do
(byte0 :: Word8) <- if recvSz0 > 0
then PM.readByteArray buf0 0
else pure 0
debug $ "receiveMany: datagram 0 received " ++ show recvSz0
++ " bytes into " ++ show sz0 ++ "-byte buffer (first byte: "
++ show byte0 ++ ")"
let go !ix = if ix < lenBufs
then do
buf <- readMutableByteArrayArray bufs ix
sz <- PM.getSizeofMutableByteArray buf
whenDebugging $ do
(byte0 :: Word8) <- if sz > 0
then PM.readByteArray buf 0
else pure 0
let addr0 = PM.mutableByteArrayContents buf
len0 <- PM.readPrimArray lens ix
debug $ "receiveMany: datagram " ++ show ix ++ " pre-run [first_byte="
++ show byte0 ++ "][length=" ++ show len0 ++ "][buf_addr="
++ show addr0 ++ "]"
receiveAttempt sock (MutableBytes buf 0 sz) (Receive.offsetAddress addrs ix) >>= \case
Left err -> pure (Left err)
Right m -> case m of
Nothing -> pure (Right ix)
Just recvSz -> do
PM.writePrimArray lens ix (intToCInt recvSz)
whenDebugging $ do
(byte0 :: Word8) <- if recvSz > 0
then PM.readByteArray buf 0
else pure 0
debug $ "receiveMany: datagram " ++ show ix ++ " received "
++ show recvSz ++ " bytes into " ++ show sz
++ "-byte buffer [first_byte=" ++ show byte0 ++ "]"
go (ix + 1)
else pure (Right ix)
go 1
intToCInt :: Int -> CInt
{-# inline intToCInt #-}
intToCInt = fromIntegral
readMutableByteArrayArray
:: MutableUnliftedArray RealWorld (MutableByteArray RealWorld)
-> Int
-> IO (MutableByteArray RealWorld)
readMutableByteArrayArray (PM.MutableUnliftedArray maa#) (I# i#)
= primitive $ \s -> case Exts.readMutableByteArrayArray# maa# i# s of
(# s', aa# #) -> (# s', PM.MutableByteArray aa# #)