sockets-0.5.0.0: src/Socket/Stream/Uninterruptible/ByteString.hs
{-# language BangPatterns #-}
{-# language DataKinds #-}
{-# language LambdaCase #-}
{-# language MagicHash #-}
{-# language ScopedTypeVariables #-}
module Socket.Stream.Uninterruptible.ByteString
( send
, receiveExactly
, receiveOnce
, receiveBetween
) where
import Data.ByteString (ByteString)
import Data.Bytes.Types (UnmanagedBytes(..))
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Data.Primitive.Addr (Addr(..))
import Data.Bytes.Types (MutableBytes(..))
import GHC.Exts (Ptr(Ptr),proxy#)
import Socket.Stream (Connection,ReceiveException,SendException)
import Socket (Interruptibility(Uninterruptible))
import Socket.Interop (fromPinned)
import qualified Data.Primitive as PM
import qualified Socket.Stream.Uninterruptible.Addr.Send as Send
import qualified Socket.Stream.Uninterruptible.MutableBytes.Receive as Receive
-- | Send a slice of a buffer. If needed, this calls POSIX @send@ repeatedly
-- until the entire contents of the buffer slice have been sent.
send ::
Connection -- ^ Connection
-> ByteString -- ^ Slice of a buffer
-> IO (Either (SendException 'Uninterruptible) ())
{-# inline send #-}
send !conn !bs = unsafeUseAsCStringLen bs
(\(Ptr addr#,len) -> Send.send proxy# conn (UnmanagedBytes (Addr addr#) len))
-- | Receive a number of bytes exactly equal to the length of the
-- buffer slice. If needed, this may call @recv@ repeatedly until
-- the requested number of bytes have been received.
receiveExactly ::
Connection -- ^ Connection
-> Int -- ^ Exact number of bytes to receive
-> IO (Either (ReceiveException 'Uninterruptible) ByteString)
{-# inline receiveExactly #-}
receiveExactly !conn !n = do
!marr <- PM.newPinnedByteArray n
Receive.receiveExactly proxy# conn (MutableBytes marr 0 n) >>= \case
Left err -> pure (Left err)
Right (_ :: ()) -> pure $! Right $! fromPinned marr 0 n
-- | Receive at most the specified number of bytes. This
-- only makes multiple calls to POSIX @recv@ if EAGAIN is returned. It makes at
-- most one @recv@ call that successfully fills the buffer.
receiveOnce ::
Connection -- ^ Connection
-> Int -- ^ Maximum number of bytes to receive
-> IO (Either (ReceiveException 'Uninterruptible) ByteString)
{-# inline receiveOnce #-}
receiveOnce !conn !n = do
!marr0 <- PM.newPinnedByteArray n
Receive.receiveOnce proxy# conn (MutableBytes marr0 0 n) >>= \case
Left err -> pure (Left err)
Right sz -> pure $! Right $! fromPinned marr0 0 sz
-- | Receive a number of bytes that is between the inclusive lower and
-- upper bounds. If needed, this may call @recv@ repeatedly until the
-- minimum requested number of bytes have been received.
receiveBetween ::
Connection -- ^ Connection
-> Int -- ^ Minimum number of bytes to receive
-> Int -- ^ Maximum number of bytes to receive
-> IO (Either (ReceiveException 'Uninterruptible) ByteString)
{-# inline receiveBetween #-}
receiveBetween !conn !minLen !maxLen = do
!marr0 <- PM.newPinnedByteArray maxLen
Receive.receiveBetween proxy# conn (MutableBytes marr0 0 maxLen) minLen >>= \case
Left err -> pure (Left err)
Right sz -> pure $! Right $! fromPinned marr0 0 sz