module Database.Bolt.Transport.Chunked
( send
, recv
, put
, get
) where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Monoid
import Data.Serialize.Get
import Data.Serialize.Put
import qualified Data.Text as T
import Database.Bolt.Exception
import Database.Bolt.Transport (Transport)
import qualified Database.Bolt.Transport as Trans
send :: Transport t => t -> LBS.ByteString -> IO ()
send conn lbs = Trans.put conn $ mapM_ putChunks (LBS.toChunks lbs) >> done
where
maxChunk = 0xffff
done = putWord16be 0
putChunks bs = do
let sz = BS.length bs
if | sz == 0 -> return ()
| sz > maxChunk -> split bs
| otherwise -> sendChunk bs
split bs = do
let (bs1, bs2) = BS.splitAt maxChunk bs
sendChunk bs1
putChunks bs2
sendChunk bs = do
putWord16be $ fromIntegral $ BS.length bs
putByteString bs
recv :: Transport t => t -> IO LBS.ByteString
recv conn = LBS.fromChunks <$> getChunks
where
getChunks = do
size <- Trans.get conn 2 getWord16be
case size of
0 -> return []
csz -> do bs <- getChunk csz
bs' <- getChunks
return (bs ++ bs')
getChunk n = do
bs <- Trans.recv conn (fromIntegral n)
let sz = fromIntegral $ BS.length bs
if | sz == 0 -> transportErr "Unexpected end of stream"
| sz < n -> do bs' <- getChunk (n sz)
return (bs : bs')
| otherwise -> return [bs]
put :: Transport t => t -> Put -> IO ()
put conn = send conn . runPutLazy
get :: Transport t => t -> Get a -> IO a
get conn g = do
lbs <- recv conn
case runGetLazy g lbs of
Left err -> transportErr $ "Unexpected data received: " <> T.pack err
Right a -> return a