packages feed

sockets-0.6.1.0: src/Socket/SequencedPacket/Uninterruptible/Bytes.hs

{-# language BangPatterns #-}
{-# language DataKinds #-}
{-# language LambdaCase #-}
{-# language MagicHash #-}

module Socket.SequencedPacket.Uninterruptible.Bytes
  ( send
  , trySend
  , receive
  , tryReceive
  ) where

import Socket.SequencedPacket.Unix (Connection(..))
import Data.Primitive (ByteArray)
import Data.Bytes.Types (Bytes,MutableBytes(MutableBytes))
import GHC.Exts (proxy#)
import Socket (Interruptibility(Uninterruptible))
import Socket.SequencedPacket (SendException,ReceiveException)
import Socket.SequencedPacket.Unix (Connection(..))

import qualified Data.Primitive as PM
import qualified Socket.SequencedPacket.Uninterruptible.Bytes.Send as Send
import qualified Socket.SequencedPacket.Uninterruptible.MutableBytes.Receive as Receive

-- Sadly, SEQPACKET sockets are poorly documented and, in Linux,
-- behave inconsisently depending on whether the socket is
-- internet-domain or unix-domain. More thorough coverage of
-- this is found at https://stackoverflow.com/q/3595684
--
-- Since internet-domain SEQPACKET sockets are rare and unsupported
-- by most hardware, we only support unix-domain sockets. On Linux,
-- we can do this by just reusing the send and receive functions for
-- datagram sockets.

-- | Send a message over a connection.
send ::
     Connection -- ^ Connection
  -> Bytes -- ^ Slice of a buffer
  -> IO (Either (SendException 'Uninterruptible) ())
send (Connection !sock) !buf = Send.send proxy# sock buf

-- | Variant of 'send' that never blocks. If the datagram cannot
-- immidiately be copied to the send queue, returns @False@.
trySend ::
     Connection -- ^ Connection
  -> Bytes -- ^ Slice of a buffer
  -> IO (Either (SendException 'Uninterruptible) Bool)
trySend (Connection !sock) !buf = Send.attemptSend sock buf

-- | Receive a message.
receive ::
     Connection -- ^ Connection
  -> Int -- ^ Maximum number of bytes to receive
  -> IO (Either (ReceiveException 'Uninterruptible) ByteArray)
{-# inline receive #-}
receive (Connection conn) !n = do
  !marr0 <- PM.newByteArray n
  Receive.receive proxy# conn (MutableBytes marr0 0 n) >>= \case
    Left err -> pure (Left err)
    Right sz -> do
      marr1 <- PM.resizeMutableByteArray marr0 sz
      !arr <- PM.unsafeFreezeByteArray marr1
      pure $! Right $! arr

-- | Receive a message. The message may be truncated if the size
-- of it is larger than the maximum allowed size.
tryReceive ::
     Connection -- ^ Connection
  -> Int -- ^ Maximum message size
  -> IO (Either (ReceiveException 'Uninterruptible) (Maybe ByteArray))
tryReceive (Connection !conn) !n = do
  !marr0 <- PM.newByteArray n
  Receive.receiveAttempt conn (MutableBytes marr0 0 n) >>= \case
    Left err -> pure (Left err)
    Right Nothing -> pure (Right Nothing)
    Right (Just sz) -> do
      marr1 <- PM.resizeMutableByteArray marr0 sz
      !arr <- PM.unsafeFreezeByteArray marr1
      pure $! Right $! Just $! arr