module Ptr.Receive.Core where import Ptr.Prelude import qualified Data.ByteString.Internal as A write :: (Ptr Word8 -> Int -> IO (Either Text Int)) -> ForeignPtr Word8 -> IORef (Int, Int) -> Int -> Int -> Ptr Word8 -> IO (Either Text ()) write fetch bufferFP bufferStateRef chunkSize howMany destination = do (offset, end) <- readIORef bufferStateRef if end == offset then -- Buffer is empty, we need to fetch right away fetchMany fetch bufferFP bufferStateRef chunkSize howMany destination else -- We still have something in the buffer, so we'll read from it first withForeignPtr bufferFP $ \bufferPtr -> let amountInBuffer = end - offset in if amountInBuffer >= howMany then -- Buffer contains all we need, so we don't need to fetch at all do A.memcpy destination (plusPtr bufferPtr offset) howMany writeIORef bufferStateRef (offset + howMany, end) return (Right ()) else do A.memcpy destination (plusPtr bufferPtr offset) amountInBuffer fetchMany fetch bufferFP bufferStateRef chunkSize (howMany - amountInBuffer) (plusPtr destination amountInBuffer) fetchMany :: (Ptr Word8 -> Int -> IO (Either Text Int)) -> ForeignPtr Word8 -> IORef (Int, Int) -> Int -> Int -> Ptr Word8 -> IO (Either Text ()) fetchMany fetch bufferFP bufferStateRef chunkSize remaining destination = if remaining >= chunkSize then -- Circumvent the buffer and write to destination directly fetchingSome destination chunkSize $ \amountFetched -> if amountFetched == remaining then -- We've fetched all we've wanted, time to stop do writeIORef bufferStateRef (0, 0) return (Right ()) else -- Go on and get some more fetchMany fetch bufferFP bufferStateRef chunkSize (remaining - amountFetched) (plusPtr destination amountFetched) else -- Write to buffer first and then stream a part of it to the destination withForeignPtr bufferFP $ \bufferPtr -> fetchingSome bufferPtr chunkSize $ \amountFetched -> do A.memcpy destination bufferPtr remaining writeIORef bufferStateRef (remaining, amountFetched) return (Right ()) where fetchingSome destination amount handle = do fetchResult <- fetch destination amount case fetchResult of Left msg -> return (Left msg) Right amountFetched -> if amountFetched == 0 then return (Left "End of input") else handle amountFetched peek :: (Ptr Word8 -> Int -> IO (Either Text Int)) -> ForeignPtr Word8 -> IORef (Int, Int) -> Int -> Int -> (Ptr Word8 -> IO peekd) -> IO (Either Text peekd) peek fetch bufferFP bufferStateRef chunkSize howMany peek = do (offset, end) <- readIORef bufferStateRef let amountInBuffer = end - offset in if amountInBuffer >= howMany then -- We have enough bytes in the buffer, so need not to allocate anything and can directly decode from the buffer withForeignPtr bufferFP $ \bufferPtr -> do peekd <- peek bufferPtr writeIORef bufferStateRef (offset + howMany, end) return (Right peekd) else -- We have to allocate a temporary space to prefetch the data into allocaBytes howMany $ \tmpPtr -> do writeResult <- if end == offset then -- Buffer is empty, we need to fetch right away fetchMany fetch bufferFP bufferStateRef chunkSize howMany tmpPtr else -- We still have something in the buffer, so we'll read from it first withForeignPtr bufferFP $ \bufferPtr -> do A.memcpy tmpPtr (plusPtr bufferPtr offset) amountInBuffer fetchMany fetch bufferFP bufferStateRef chunkSize (howMany - amountInBuffer) (plusPtr tmpPtr amountInBuffer) case writeResult of Right () -> do peekd <- peek tmpPtr return (Right peekd) Left msg -> return (Left msg)