module Network.Socket.All
( recvAllBuf
, recvStorable
, sendAllBuf
, sendStorable
, sendBuilderWith
, sendBuilder
) where
import Control.Monad (unless)
import qualified Data.ByteString.Builder as B
import qualified Data.ByteString.Builder.Extra as B
import Data.Word (Word8)
import Foreign.Ptr (Ptr, castPtr, plusPtr)
import Foreign.Marshal (alloca, allocaBytes, with)
import Foreign.Storable (Storable, sizeOf, peek)
import Network.Socket
import qualified Network.Socket.ByteString as BS
allBufWith :: (Ptr a -> Int -> IO Int) -> Ptr a -> Int -> IO Int
allBufWith f p n = run 0 where
run l
| l >= n = return l
| otherwise = do
r <- f (p `plusPtr` l) (n l)
if r == 0
then return l
else run (l + r)
recvAllBuf :: Socket -> Ptr Word8 -> Int -> IO Int
recvAllBuf = allBufWith . recvBuf
recvStorable :: forall a . Storable a => Socket -> IO (Maybe a)
recvStorable s = alloca $ \p -> do
r <- recvAllBuf s (castPtr p) n
if r < n
then return Nothing
else Just <$> peek p
where
n = sizeOf (undefined :: a)
sendAllBuf :: Socket -> Ptr Word8 -> Int -> IO ()
sendAllBuf s p n = do
r <- allBufWith (sendBuf s) p n
unless (r == n) $ fail $ "sendAllBuf: sent " ++ show r ++ "/" ++ show n
sendStorable :: Storable a => Socket -> a -> IO ()
sendStorable s x = with x $ \p ->
sendAllBuf s (castPtr p) (sizeOf x)
sendBuilderWith :: Socket -> Int -> B.Builder -> IO ()
sendBuilderWith s z0 = buf z0 . B.runBuilder where
buf z w = do
n <- allocaBytes z $ run z w
case n of
B.More z' w' -> buf z' w'
~B.Done -> return ()
run z w p = do
(l, n) <- w p z
sendAllBuf s p l
case n of
B.More z' w' | z' <= z ->
run z w' p
B.Chunk b w' -> do
BS.sendAll s b
run z w' p
_ -> return n
sendBuilder :: Socket -> B.Builder -> IO ()
sendBuilder s = sendBuilderWith s B.defaultChunkSize