on master help sign in
move Network/Socket to Internal
efe8242Harendra Kumar
src/Streamly/Internal/Network/Socket.hsnew file+397
@@ -0,0 +1,397 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE MagicHash #-}
5+{-# LANGUAGE RecordWildCards #-}
6+{-# LANGUAGE UnboxedTuples #-}
7+
8+#include "inline.hs"
9+
10+-- |
11+-- Module : Streamly.Internal.Network.Socket
12+-- Copyright : (c) 2018 Composewell Technologies
13+--
14+-- License : BSD3
15+-- Maintainer : streamly@composewell.com
16+-- Stability : experimental
17+-- Portability : GHC
18+--
19+module Streamly.Internal.Network.Socket
20+ (
21+ -- * Use a socket
22+ withSocket
23+ , withSocketS
24+
25+ -- * Read from connection
26+ , read
27+ -- , readUtf8
28+ -- , readLines
29+ -- , readFrames
30+ -- , readByChunks
31+
32+ -- -- * Array Read
33+ -- , readArrayUpto
34+ -- , readArrayOf
35+
36+ -- , readArraysUpto
37+ -- , readArraysOf
38+ , readArrays
39+
40+ -- * Write to connection
41+ , write
42+ -- , writeUtf8
43+ -- , writeUtf8ByLines
44+ -- , writeByFrames
45+ , writeInChunksOf
46+
47+ -- -- * Array Write
48+ , writeArray
49+ , writeArrays
50+
51+ -- reading/writing datagrams
52+ )
53+where
54+
55+import Control.Concurrent (threadWaitWrite, rtsSupportsBoundThreads)
56+import Control.Monad.Catch (MonadCatch, onException)
57+import Control.Monad.IO.Class (MonadIO(..))
58+import Control.Monad (when)
59+import Data.Word (Word8)
60+import Foreign.ForeignPtr (withForeignPtr)
61+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
62+import Foreign.Ptr (minusPtr, plusPtr, Ptr, castPtr)
63+import Foreign.Storable (Storable(..))
64+import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
65+import Network.Socket (Socket, sendBuf, recvBuf)
66+#if MIN_VERSION_network(3,1,0)
67+import Network.Socket (withFdSocket)
68+#else
69+import Network.Socket (fdSocket)
70+#endif
71+import Prelude hiding (read)
72+
73+import qualified Network.Socket as Net
74+
75+import Streamly.Internal.Memory.Array.Types (Array(..))
76+import Streamly.Streams.Serial (SerialT)
77+import Streamly.Streams.StreamK.Type (IsStream, mkStream)
78+import Streamly.Data.Fold (Fold)
79+-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
80+
81+import qualified Streamly.Data.Fold as FL
82+import qualified Streamly.Internal.Data.Fold.Types as FL
83+import qualified Streamly.Memory.Array as A
84+import qualified Streamly.Internal.Memory.ArrayStream as AS
85+import qualified Streamly.Internal.Memory.Array.Types as A
86+import qualified Streamly.Prelude as S
87+
88+-- | @'withSocket' socket act@ runs the monadic computation @act@ passing the
89+-- socket handle to it. The handle will be closed on exit from 'withSocket',
90+-- whether by normal termination or by raising an exception. If closing the
91+-- handle raises an exception, then this exception will be raised by
92+-- 'withSocket' rather than any exception raised by 'act'.
93+--
94+-- @since 0.7.0
95+{-# INLINE withSocket #-}
96+withSocket :: (MonadCatch m, MonadIO m) => Socket -> (Socket -> m ()) -> m ()
97+withSocket sk f = do
98+ f sk `onException` liftIO (Net.close sk)
99+ liftIO (Net.close sk)
100+
101+-- XXX bracket performs 2x better than 'finally'. That's perhaps because of
102+-- better fusion of "A.flattenArrays . readArrays"?
103+--
104+-- withSocketS :: (IsStream t, MonadCatch m, MonadIO m) => Socket -> t m Word8
105+-- withSocketS sk = A.flattenArrays $
106+-- S.finally (liftIO (Net.close sk)) (readArrays sk)
107+
108+-- | Like 'withSocket' but runs a streaming computation instead of a monadic
109+-- computation.
110+--
111+-- @since 0.7.0
112+{-# INLINE withSocketS #-}
113+withSocketS :: (IsStream t, MonadCatch m, MonadIO m)
114+ => Socket -> (Socket -> t m a) -> t m a
115+withSocketS sk = S.bracket (return sk) (liftIO . Net.close)
116+
117+-------------------------------------------------------------------------------
118+-- Array IO (Input)
119+-------------------------------------------------------------------------------
120+
121+{-# INLINABLE readArrayUptoWith #-}
122+readArrayUptoWith
123+ :: (h -> Ptr Word8 -> Int -> IO Int)
124+ -> Int
125+ -> h
126+ -> IO (Array Word8)
127+readArrayUptoWith f size h = do
128+ ptr <- mallocPlainForeignPtrBytes size
129+ -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
130+ withForeignPtr ptr $ \p -> do
131+ n <- f h p size
132+ let v = Array
133+ { aStart = ptr
134+ , aEnd = p `plusPtr` n
135+ , aBound = p `plusPtr` size
136+ }
137+ -- XXX shrink only if the diff is significant
138+ -- A.shrinkToFit v
139+ return v
140+
141+-- | Read a 'ByteArray' from a file handle. If no data is available on the
142+-- handle it blocks until some data becomes available. If data is available
143+-- then it immediately returns that data without blocking. It reads a maximum
144+-- of up to the size requested.
145+{-# INLINABLE readArrayOf #-}
146+readArrayOf :: Int -> Socket -> IO (Array Word8)
147+readArrayOf = readArrayUptoWith recvBuf
148+
149+-------------------------------------------------------------------------------
150+-- Array IO (output)
151+-------------------------------------------------------------------------------
152+
153+waitWhen0 :: Int -> Socket -> IO ()
154+waitWhen0 0 s = when rtsSupportsBoundThreads $
155+#if MIN_VERSION_network(3,1,0)
156+ withFdSocket s $ \fd -> threadWaitWrite $ fromIntegral fd
157+#else
158+ let fd = fdSocket s in threadWaitWrite $ fromIntegral fd
159+#endif
160+waitWhen0 _ _ = return ()
161+
162+sendAll :: Socket -> Ptr Word8 -> Int -> IO ()
163+sendAll _ _ len | len <= 0 = return ()
164+sendAll s p len = do
165+ sent <- sendBuf s p len
166+ waitWhen0 sent s
167+ -- assert (sent <= len)
168+ when (sent >= 0) $ sendAll s (p `plusPtr` sent) (len - sent)
169+
170+{-# INLINABLE writeArrayWith #-}
171+writeArrayWith :: Storable a
172+ => (h -> Ptr Word8 -> Int -> IO ())
173+ -> h
174+ -> Array a
175+ -> IO ()
176+writeArrayWith _ _ arr | A.length arr == 0 = return ()
177+writeArrayWith f h Array{..} = withForeignPtr aStart $ \p ->
178+ f h (castPtr p) aLen
179+ where
180+ aLen =
181+ let p = unsafeForeignPtrToPtr aStart
182+ in aEnd `minusPtr` p
183+
184+-- | Write an Array to a file handle.
185+--
186+-- @since 0.7.0
187+{-# INLINABLE writeArray #-}
188+writeArray :: Storable a => Socket -> Array a -> IO ()
189+writeArray = writeArrayWith sendAll
190+
191+-------------------------------------------------------------------------------
192+-- Stream of Arrays IO
193+-------------------------------------------------------------------------------
194+
195+{-# INLINABLE readArraysUptoWith #-}
196+readArraysUptoWith :: (IsStream t, MonadIO m)
197+ => (Int -> h -> IO (Array Word8))
198+ -> Int -> h -> t m (Array Word8)
199+readArraysUptoWith f size h = go
200+ where
201+ -- XXX use cons/nil instead
202+ go = mkStream $ \_ yld _ stp -> do
203+ arr <- liftIO $ f size h
204+ if A.length arr == 0
205+ then stp
206+ else yld arr go
207+
208+-- | @readArraysOf size h@ reads a stream of arrays from file handle @h@.
209+-- The maximum size of a single array is limited to @size@.
210+-- 'fromHandleArraysUpto' ignores the prevailing 'TextEncoding' and 'NewlineMode'
211+-- on the 'Handle'.
212+{-# INLINABLE readArraysOf #-}
213+readArraysOf :: (IsStream t, MonadIO m)
214+ => Int -> Socket -> t m (Array Word8)
215+readArraysOf = readArraysUptoWith readArrayOf
216+
217+-- XXX read 'Array a' instead of Word8
218+--
219+-- | @readArrays h@ reads a stream of arrays from socket handle @h@.
220+-- The maximum size of a single array is limited to @defaultChunkSize@.
221+--
222+-- @since 0.7.0
223+{-# INLINE readArrays #-}
224+readArrays :: (IsStream t, MonadIO m) => Socket -> t m (Array Word8)
225+readArrays = readArraysOf A.defaultChunkSize
226+
227+-------------------------------------------------------------------------------
228+-- Read File to Stream
229+-------------------------------------------------------------------------------
230+
231+-- TODO for concurrent streams implement readahead IO. We can send multiple
232+-- read requests at the same time. For serial case we can use async IO. We can
233+-- also control the read throughput in mbps or IOPS.
234+
235+{-
236+-- | @readInChunksOf chunkSize handle@ reads a byte stream from a file
237+-- handle, reads are performed in chunks of up to @chunkSize@. The stream ends
238+-- as soon as EOF is encountered.
239+--
240+{-# INLINE readInChunksOf #-}
241+readInChunksOf :: (IsStream t, MonadIO m) => Int -> Handle -> t m Word8
242+readInChunksOf chunkSize h = A.flattenArrays $ readArraysUpto chunkSize h
243+-}
244+
245+-- TODO
246+-- read :: (IsStream t, MonadIO m, Storable a) => Handle -> t m a
247+--
248+-- > read = 'readByChunks' A.defaultChunkSize
249+-- | Generate a stream of elements of the given type from a socket. The
250+-- stream ends when EOF is encountered.
251+--
252+-- @since 0.7.0
253+{-# INLINE read #-}
254+read :: (IsStream t, MonadIO m) => Socket -> t m Word8
255+read = AS.concat . readArrays
256+
257+-------------------------------------------------------------------------------
258+-- Writing
259+-------------------------------------------------------------------------------
260+
261+-- | Write a stream of arrays to a handle.
262+--
263+-- @since 0.7.0
264+{-# INLINE writeArraysS #-}
265+writeArraysS :: (MonadIO m, Storable a)
266+ => Socket -> SerialT m (Array a) -> m ()
267+writeArraysS h m = S.mapM_ (liftIO . writeArray h) m
268+
269+-- | Write a stream of arrays to a socket.
270+--
271+-- @since 0.7.0
272+{-# INLINE writeArrays #-}
273+writeArrays :: (MonadIO m, Storable a) => Socket -> Fold m (Array a) ()
274+writeArrays h = FL.drainBy (liftIO . writeArray h)
275+
276+-- GHC buffer size dEFAULT_FD_BUFFER_SIZE=8192 bytes.
277+--
278+-- XXX test this
279+-- Note that if you use a chunk size less than 8K (GHC's default buffer
280+-- size) then you are advised to use 'NOBuffering' mode on the 'Handle' in case you
281+-- do not want buffering to occur at GHC level as well. Same thing applies to
282+-- writes as well.
283+
284+-- | Like 'write' but provides control over the write buffer. Output will
285+-- be written to the IO device as soon as we collect the specified number of
286+-- input elements.
287+--
288+-- @since 0.7.0
289+{-# INLINE writeInChunksOfS #-}
290+writeInChunksOfS :: MonadIO m => Int -> Socket -> SerialT m Word8 -> m ()
291+writeInChunksOfS n h m = writeArraysS h $ AS.arraysOf n m
292+
293+-- | Write a byte stream to a socket. Accumulates the input in chunks of
294+-- specified number of bytes before writing.
295+--
296+-- @since 0.7.0
297+{-# INLINE writeInChunksOf #-}
298+writeInChunksOf :: MonadIO m => Int -> Socket -> Fold m Word8 ()
299+writeInChunksOf n h = FL.lchunksOf n (A.writeNUnsafe n) (writeArrays h)
300+
301+-- > write = 'writeInChunksOf' A.defaultChunkSize
302+--
303+-- | Write a byte stream to a file handle. Combines the bytes in chunks of size
304+-- up to 'A.defaultChunkSize' before writing. Note that the write behavior
305+-- depends on the 'IOMode' and the current seek position of the handle.
306+--
307+-- @since 0.7.0
308+{-# INLINE _writeS #-}
309+_writeS :: MonadIO m => Socket -> SerialT m Word8 -> m ()
310+_writeS = writeInChunksOfS A.defaultChunkSize
311+
312+-- | Write a byte stream to a socket. Accumulates the input in chunks of
313+-- up to 'A.defaultChunkSize' bytes before writing.
314+--
315+-- @
316+-- write = 'writeInChunksOf' 'A.defaultChunkSize'
317+-- @
318+--
319+-- @since 0.7.0
320+{-# INLINE write #-}
321+write :: MonadIO m => Socket -> Fold m Word8 ()
322+write = writeInChunksOf A.defaultChunkSize
323+
324+{-
325+{-# INLINE write #-}
326+write :: (MonadIO m, Storable a) => Handle -> SerialT m a -> m ()
327+write = toHandleWith A.defaultChunkSize
328+-}
329+
330+-------------------------------------------------------------------------------
331+-- IO with encoding/decoding Unicode characters
332+-------------------------------------------------------------------------------
333+
334+{-
335+-- |
336+-- > readUtf8 = decodeUtf8 . read
337+--
338+-- Read a UTF8 encoded stream of unicode characters from a file handle.
339+--
340+-- @since 0.7.0
341+{-# INLINE readUtf8 #-}
342+readUtf8 :: (IsStream t, MonadIO m) => Handle -> t m Char
343+readUtf8 = decodeUtf8 . read
344+
345+-- |
346+-- > writeUtf8 h s = write h $ encodeUtf8 s
347+--
348+-- Encode a stream of unicode characters to UTF8 and write it to the given file
349+-- handle. Default block buffering applies to the writes.
350+--
351+-- @since 0.7.0
352+{-# INLINE writeUtf8 #-}
353+writeUtf8 :: MonadIO m => Handle -> SerialT m Char -> m ()
354+writeUtf8 h s = write h $ encodeUtf8 s
355+
356+-- | Write a stream of unicode characters after encoding to UTF-8 in chunks
357+-- separated by a linefeed character @'\n'@. If the size of the buffer exceeds
358+-- @defaultChunkSize@ and a linefeed is not yet found, the buffer is written
359+-- anyway. This is similar to writing to a 'Handle' with the 'LineBuffering'
360+-- option.
361+--
362+-- @since 0.7.0
363+{-# INLINE writeUtf8ByLines #-}
364+writeUtf8ByLines :: (IsStream t, MonadIO m) => Handle -> t m Char -> m ()
365+writeUtf8ByLines = undefined
366+
367+-- | Read UTF-8 lines from a file handle and apply the specified fold to each
368+-- line. This is similar to reading a 'Handle' with the 'LineBuffering' option.
369+--
370+-- @since 0.7.0
371+{-# INLINE readLines #-}
372+readLines :: (IsStream t, MonadIO m) => Handle -> Fold m Char b -> t m b
373+readLines h f = foldLines (readUtf8 h) f
374+
375+-------------------------------------------------------------------------------
376+-- Framing on a sequence
377+-------------------------------------------------------------------------------
378+
379+-- | Read a stream from a file handle and split it into frames delimited by
380+-- the specified sequence of elements. The supplied fold is applied on each
381+-- frame.
382+--
383+-- @since 0.7.0
384+{-# INLINE readFrames #-}
385+readFrames :: (IsStream t, MonadIO m, Storable a)
386+ => Array a -> Handle -> Fold m a b -> t m b
387+readFrames = undefined -- foldFrames . read
388+
389+-- | Write a stream to the given file handle buffering up to frames separated
390+-- by the given sequence or up to a maximum of @defaultChunkSize@.
391+--
392+-- @since 0.7.0
393+{-# INLINE writeByFrames #-}
394+writeByFrames :: (IsStream t, MonadIO m, Storable a)
395+ => Array a -> Handle -> t m a -> m ()
396+writeByFrames = undefined
397+-}
src/Streamly/Network/Socket.hs+3−353
@@ -1,18 +1,9 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE BangPatterns #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE MagicHash #-}
5−{-# LANGUAGE RecordWildCards #-}
6−{-# LANGUAGE UnboxedTuples #-}
7−
8−#include "inline.hs"
9−
10-- |
1-- |
11-- Module : Streamly.Network.Socket
2-- Module : Streamly.Network.Socket
12−-- Copyright : (c) 2018 Harendra Kumar
3+-- Copyright : (c) 2018 Composewell Technologies
13--
4--
14-- License : BSD3
5-- License : BSD3
15−-- Maintainer : harendra.kumar@gmail.com
6+-- Maintainer : streamly@composewell.com
16-- Stability : experimental
7-- Stability : experimental
17-- Portability : GHC
8-- Portability : GHC
18--
9--
@@ -66,346 +57,5 @@ module Streamly.Network.Socket
66 )
57 )
67where
58where
68
59
69−import Control.Concurrent (threadWaitWrite, rtsSupportsBoundThreads)
70−import Control.Monad.Catch (MonadCatch, onException)
71−import Control.Monad.IO.Class (MonadIO(..))
72−import Control.Monad (when)
73−import Data.Word (Word8)
74−import Foreign.ForeignPtr (withForeignPtr)
75−import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
76−import Foreign.Ptr (minusPtr, plusPtr, Ptr, castPtr)
77−import Foreign.Storable (Storable(..))
78−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
79−import Network.Socket (Socket, sendBuf, recvBuf)
80−#if MIN_VERSION_network(3,1,0)
81−import Network.Socket (withFdSocket)
82−#else
83−import Network.Socket (fdSocket)
60+import Streamly.Internal.Network.Socket
84−#endif
85import Prelude hiding (read)
61import Prelude hiding (read)
86−
87−import qualified Network.Socket as Net
88−
89−import Streamly.Internal.Memory.Array.Types (Array(..))
90−import Streamly.Streams.Serial (SerialT)
91−import Streamly.Streams.StreamK.Type (IsStream, mkStream)
92−import Streamly.Data.Fold (Fold)
93−-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
94−
95−import qualified Streamly.Data.Fold as FL
96−import qualified Streamly.Internal.Data.Fold.Types as FL
97−import qualified Streamly.Memory.Array as A
98−import qualified Streamly.Internal.Memory.ArrayStream as AS
99−import qualified Streamly.Internal.Memory.Array.Types as A
100−import qualified Streamly.Prelude as S
101−
102−-- | @'withSocket' socket act@ runs the monadic computation @act@ passing the
103−-- socket handle to it. The handle will be closed on exit from 'withSocket',
104−-- whether by normal termination or by raising an exception. If closing the
105−-- handle raises an exception, then this exception will be raised by
106−-- 'withSocket' rather than any exception raised by 'act'.
107−--
108−-- @since 0.7.0
109−{-# INLINE withSocket #-}
110−withSocket :: (MonadCatch m, MonadIO m) => Socket -> (Socket -> m ()) -> m ()
111−withSocket sk f = do
112− f sk `onException` liftIO (Net.close sk)
113− liftIO (Net.close sk)
114−
115−-- XXX bracket performs 2x better than 'finally'. That's perhaps because of
116−-- better fusion of "A.flattenArrays . readArrays"?
117−--
118−-- withSocketS :: (IsStream t, MonadCatch m, MonadIO m) => Socket -> t m Word8
119−-- withSocketS sk = A.flattenArrays $
120−-- S.finally (liftIO (Net.close sk)) (readArrays sk)
121−
122−-- | Like 'withSocket' but runs a streaming computation instead of a monadic
123−-- computation.
124−--
125−-- @since 0.7.0
126−{-# INLINE withSocketS #-}
127−withSocketS :: (IsStream t, MonadCatch m, MonadIO m)
128− => Socket -> (Socket -> t m a) -> t m a
129−withSocketS sk = S.bracket (return sk) (liftIO . Net.close)
130−
131−-------------------------------------------------------------------------------
132−-- Array IO (Input)
133−-------------------------------------------------------------------------------
134−
135−{-# INLINABLE readArrayUptoWith #-}
136−readArrayUptoWith
137− :: (h -> Ptr Word8 -> Int -> IO Int)
138− -> Int
139− -> h
140− -> IO (Array Word8)
141−readArrayUptoWith f size h = do
142− ptr <- mallocPlainForeignPtrBytes size
143− -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
144− withForeignPtr ptr $ \p -> do
145− n <- f h p size
146− let v = Array
147− { aStart = ptr
148− , aEnd = p `plusPtr` n
149− , aBound = p `plusPtr` size
150− }
151− -- XXX shrink only if the diff is significant
152− -- A.shrinkToFit v
153− return v
154−
155−-- | Read a 'ByteArray' from a file handle. If no data is available on the
156−-- handle it blocks until some data becomes available. If data is available
157−-- then it immediately returns that data without blocking. It reads a maximum
158−-- of up to the size requested.
159−{-# INLINABLE readArrayOf #-}
160−readArrayOf :: Int -> Socket -> IO (Array Word8)
161−readArrayOf = readArrayUptoWith recvBuf
162−
163−-------------------------------------------------------------------------------
164−-- Array IO (output)
165−-------------------------------------------------------------------------------
166−
167−waitWhen0 :: Int -> Socket -> IO ()
168−waitWhen0 0 s = when rtsSupportsBoundThreads $
169−#if MIN_VERSION_network(3,1,0)
170− withFdSocket s $ \fd -> threadWaitWrite $ fromIntegral fd
171−#else
172− let fd = fdSocket s in threadWaitWrite $ fromIntegral fd
173−#endif
174−waitWhen0 _ _ = return ()
175−
176−sendAll :: Socket -> Ptr Word8 -> Int -> IO ()
177−sendAll _ _ len | len <= 0 = return ()
178−sendAll s p len = do
179− sent <- sendBuf s p len
180− waitWhen0 sent s
181− -- assert (sent <= len)
182− when (sent >= 0) $ sendAll s (p `plusPtr` sent) (len - sent)
183−
184−{-# INLINABLE writeArrayWith #-}
185−writeArrayWith :: Storable a
186− => (h -> Ptr Word8 -> Int -> IO ())
187− -> h
188− -> Array a
189− -> IO ()
190−writeArrayWith _ _ arr | A.length arr == 0 = return ()
191−writeArrayWith f h Array{..} = withForeignPtr aStart $ \p ->
192− f h (castPtr p) aLen
193− where
194− aLen =
195− let p = unsafeForeignPtrToPtr aStart
196− in aEnd `minusPtr` p
197−
198−-- | Write an Array to a file handle.
199−--
200−-- @since 0.7.0
201−{-# INLINABLE writeArray #-}
202−writeArray :: Storable a => Socket -> Array a -> IO ()
203−writeArray = writeArrayWith sendAll
204−
205−-------------------------------------------------------------------------------
206−-- Stream of Arrays IO
207−-------------------------------------------------------------------------------
208−
209−{-# INLINABLE readArraysUptoWith #-}
210−readArraysUptoWith :: (IsStream t, MonadIO m)
211− => (Int -> h -> IO (Array Word8))
212− -> Int -> h -> t m (Array Word8)
213−readArraysUptoWith f size h = go
214− where
215− -- XXX use cons/nil instead
216− go = mkStream $ \_ yld _ stp -> do
217− arr <- liftIO $ f size h
218− if A.length arr == 0
219− then stp
220− else yld arr go
221−
222−-- | @readArraysOf size h@ reads a stream of arrays from file handle @h@.
223−-- The maximum size of a single array is limited to @size@.
224−-- 'fromHandleArraysUpto' ignores the prevailing 'TextEncoding' and 'NewlineMode'
225−-- on the 'Handle'.
226−{-# INLINABLE readArraysOf #-}
227−readArraysOf :: (IsStream t, MonadIO m)
228− => Int -> Socket -> t m (Array Word8)
229−readArraysOf = readArraysUptoWith readArrayOf
230−
231−-- XXX read 'Array a' instead of Word8
232−--
233−-- | @readArrays h@ reads a stream of arrays from socket handle @h@.
234−-- The maximum size of a single array is limited to @defaultChunkSize@.
235−--
236−-- @since 0.7.0
237−{-# INLINE readArrays #-}
238−readArrays :: (IsStream t, MonadIO m) => Socket -> t m (Array Word8)
239−readArrays = readArraysOf A.defaultChunkSize
240−
241−-------------------------------------------------------------------------------
242−-- Read File to Stream
243−-------------------------------------------------------------------------------
244−
245−-- TODO for concurrent streams implement readahead IO. We can send multiple
246−-- read requests at the same time. For serial case we can use async IO. We can
247−-- also control the read throughput in mbps or IOPS.
248−
249−{-
250−-- | @readInChunksOf chunkSize handle@ reads a byte stream from a file
251−-- handle, reads are performed in chunks of up to @chunkSize@. The stream ends
252−-- as soon as EOF is encountered.
253−--
254−{-# INLINE readInChunksOf #-}
255−readInChunksOf :: (IsStream t, MonadIO m) => Int -> Handle -> t m Word8
256−readInChunksOf chunkSize h = A.flattenArrays $ readArraysUpto chunkSize h
257−-}
258−
259−-- TODO
260−-- read :: (IsStream t, MonadIO m, Storable a) => Handle -> t m a
261−--
262−-- > read = 'readByChunks' A.defaultChunkSize
263−-- | Generate a stream of elements of the given type from a socket. The
264−-- stream ends when EOF is encountered.
265−--
266−-- @since 0.7.0
267−{-# INLINE read #-}
268−read :: (IsStream t, MonadIO m) => Socket -> t m Word8
269−read = AS.concat . readArrays
270−
271−-------------------------------------------------------------------------------
272−-- Writing
273−-------------------------------------------------------------------------------
274−
275−-- | Write a stream of arrays to a handle.
276−--
277−-- @since 0.7.0
278−{-# INLINE writeArraysS #-}
279−writeArraysS :: (MonadIO m, Storable a)
280− => Socket -> SerialT m (Array a) -> m ()
281−writeArraysS h m = S.mapM_ (liftIO . writeArray h) m
282−
283−-- | Write a stream of arrays to a socket.
284−--
285−-- @since 0.7.0
286−{-# INLINE writeArrays #-}
287−writeArrays :: (MonadIO m, Storable a) => Socket -> Fold m (Array a) ()
288−writeArrays h = FL.drainBy (liftIO . writeArray h)
289−
290−-- GHC buffer size dEFAULT_FD_BUFFER_SIZE=8192 bytes.
291−--
292−-- XXX test this
293−-- Note that if you use a chunk size less than 8K (GHC's default buffer
294−-- size) then you are advised to use 'NOBuffering' mode on the 'Handle' in case you
295−-- do not want buffering to occur at GHC level as well. Same thing applies to
296−-- writes as well.
297−
298−-- | Like 'write' but provides control over the write buffer. Output will
299−-- be written to the IO device as soon as we collect the specified number of
300−-- input elements.
301−--
302−-- @since 0.7.0
303−{-# INLINE writeInChunksOfS #-}
304−writeInChunksOfS :: MonadIO m => Int -> Socket -> SerialT m Word8 -> m ()
305−writeInChunksOfS n h m = writeArraysS h $ AS.arraysOf n m
306−
307−-- | Write a byte stream to a socket. Accumulates the input in chunks of
308−-- specified number of bytes before writing.
309−--
310−-- @since 0.7.0
311−{-# INLINE writeInChunksOf #-}
312−writeInChunksOf :: MonadIO m => Int -> Socket -> Fold m Word8 ()
313−writeInChunksOf n h = FL.lchunksOf n (A.writeNUnsafe n) (writeArrays h)
314−
315−-- > write = 'writeInChunksOf' A.defaultChunkSize
316−--
317−-- | Write a byte stream to a file handle. Combines the bytes in chunks of size
318−-- up to 'A.defaultChunkSize' before writing. Note that the write behavior
319−-- depends on the 'IOMode' and the current seek position of the handle.
320−--
321−-- @since 0.7.0
322−{-# INLINE _writeS #-}
323−_writeS :: MonadIO m => Socket -> SerialT m Word8 -> m ()
324−_writeS = writeInChunksOfS A.defaultChunkSize
325−
326−-- | Write a byte stream to a socket. Accumulates the input in chunks of
327−-- up to 'A.defaultChunkSize' bytes before writing.
328−--
329−-- @
330−-- write = 'writeInChunksOf' 'A.defaultChunkSize'
331−-- @
332−--
333−-- @since 0.7.0
334−{-# INLINE write #-}
335−write :: MonadIO m => Socket -> Fold m Word8 ()
336−write = writeInChunksOf A.defaultChunkSize
337−
338−{-
339−{-# INLINE write #-}
340−write :: (MonadIO m, Storable a) => Handle -> SerialT m a -> m ()
341−write = toHandleWith A.defaultChunkSize
342−-}
343−
344−-------------------------------------------------------------------------------
345−-- IO with encoding/decoding Unicode characters
346−-------------------------------------------------------------------------------
347−
348−{-
349−-- |
350−-- > readUtf8 = decodeUtf8 . read
351−--
352−-- Read a UTF8 encoded stream of unicode characters from a file handle.
353−--
354−-- @since 0.7.0
355−{-# INLINE readUtf8 #-}
356−readUtf8 :: (IsStream t, MonadIO m) => Handle -> t m Char
357−readUtf8 = decodeUtf8 . read
358−
359−-- |
360−-- > writeUtf8 h s = write h $ encodeUtf8 s
361−--
362−-- Encode a stream of unicode characters to UTF8 and write it to the given file
363−-- handle. Default block buffering applies to the writes.
364−--
365−-- @since 0.7.0
366−{-# INLINE writeUtf8 #-}
367−writeUtf8 :: MonadIO m => Handle -> SerialT m Char -> m ()
368−writeUtf8 h s = write h $ encodeUtf8 s
369−
370−-- | Write a stream of unicode characters after encoding to UTF-8 in chunks
371−-- separated by a linefeed character @'\n'@. If the size of the buffer exceeds
372−-- @defaultChunkSize@ and a linefeed is not yet found, the buffer is written
373−-- anyway. This is similar to writing to a 'Handle' with the 'LineBuffering'
374−-- option.
375−--
376−-- @since 0.7.0
377−{-# INLINE writeUtf8ByLines #-}
378−writeUtf8ByLines :: (IsStream t, MonadIO m) => Handle -> t m Char -> m ()
379−writeUtf8ByLines = undefined
380−
381−-- | Read UTF-8 lines from a file handle and apply the specified fold to each
382−-- line. This is similar to reading a 'Handle' with the 'LineBuffering' option.
383−--
384−-- @since 0.7.0
385−{-# INLINE readLines #-}
386−readLines :: (IsStream t, MonadIO m) => Handle -> Fold m Char b -> t m b
387−readLines h f = foldLines (readUtf8 h) f
388−
389−-------------------------------------------------------------------------------
390−-- Framing on a sequence
391−-------------------------------------------------------------------------------
392−
393−-- | Read a stream from a file handle and split it into frames delimited by
394−-- the specified sequence of elements. The supplied fold is applied on each
395−-- frame.
396−--
397−-- @since 0.7.0
398−{-# INLINE readFrames #-}
399−readFrames :: (IsStream t, MonadIO m, Storable a)
400− => Array a -> Handle -> Fold m a b -> t m b
401−readFrames = undefined -- foldFrames . read
402−
403−-- | Write a stream to the given file handle buffering up to frames separated
404−-- by the given sequence or up to a maximum of @defaultChunkSize@.
405−--
406−-- @since 0.7.0
407−{-# INLINE writeByFrames #-}
408−writeByFrames :: (IsStream t, MonadIO m, Storable a)
409− => Array a -> Handle -> t m a -> m ()
410−writeByFrames = undefined
411−-}
streamly.cabal+1
@@ -319,6 +319,7 @@ library
319 , Streamly.Network.Server
319 , Streamly.Network.Server
320 , Streamly.Network.Client
320 , Streamly.Network.Client
321
321
322+ , Streamly.Internal.Network.Socket
322 , Streamly.Internal.Network.Server
323 , Streamly.Internal.Network.Server
323
324
324 if flag(benchmark)
325 if flag(benchmark)