Skip to content

Commit b500f08

Browse files
LysxiaBodigrim
authored andcommitted
Move hPutStream to Data.Text.Internal.IO
1 parent 21318ac commit b500f08

File tree

2 files changed

+119
-120
lines changed

2 files changed

+119
-120
lines changed

src/Data/Text/IO.hs

Lines changed: 8 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,18 @@ module Data.Text.IO
4545
import Data.Text (Text)
4646
import Prelude hiding (appendFile, getContents, getLine, interact,
4747
putStr, putStrLn, readFile, writeFile)
48-
import System.IO (Handle, IOMode(..), hPutChar, openFile, stdin, stdout,
48+
import System.IO (Handle, IOMode(..), openFile, stdin, stdout,
4949
withFile)
5050
import qualified Control.Exception as E
5151
import Control.Monad (liftM2, when)
52-
import Data.IORef (readIORef, writeIORef)
52+
import Data.IORef (readIORef)
5353
import qualified Data.Text as T
5454
import Data.Text.Internal.Fusion (stream, streamLn)
55-
import Data.Text.Internal.Fusion.Types (Step(..), Stream(..))
56-
import Data.Text.Internal.IO (hGetLineWith, readChunk)
57-
import GHC.IO.Buffer (Buffer(..), BufferState(..), RawCharBuffer, CharBuffer,
58-
emptyBuffer, isEmptyBuffer, newCharBuffer)
59-
import qualified GHC.IO.Buffer
55+
import Data.Text.Internal.IO (hGetLineWith, readChunk, hPutStream)
56+
import GHC.IO.Buffer (CharBuffer, isEmptyBuffer)
6057
import GHC.IO.Exception (IOException(ioe_type), IOErrorType(InappropriateType))
61-
import GHC.IO.Handle.Internals (augmentIOError, hClose_help, wantReadableHandle,
62-
wantWritableHandle)
63-
import GHC.IO.Handle.Text (commitBuffer')
64-
import GHC.IO.Handle.Types (BufferList(..), BufferMode(..), Handle__(..),
65-
HandleType(..), Newline(..))
58+
import GHC.IO.Handle.Internals (augmentIOError, hClose_help, wantReadableHandle)
59+
import GHC.IO.Handle.Types (BufferMode(..), Handle__(..), HandleType(..))
6660
import System.IO (hGetBuffering, hFileSize, hSetBuffering, hTell)
6761
import System.IO.Error (isEOFError)
6862

@@ -174,111 +168,11 @@ hGetLine = hGetLineWith T.concat
174168

175169
-- | Write a string to a handle.
176170
hPutStr :: Handle -> Text -> IO ()
177-
hPutStr h = hPutStr' h . stream
178-
179-
-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
180-
hPutStr' :: Handle -> Stream Char -> IO ()
181-
hPutStr' h str = do
182-
(buffer_mode, nl) <-
183-
wantWritableHandle "hPutStr" h $ \h_ -> do
184-
bmode <- getSpareBuffer h_
185-
return (bmode, haOutputNL h_)
186-
case buffer_mode of
187-
(NoBuffering, _) -> hPutChars h str
188-
(LineBuffering, buf) -> writeLines h nl buf str
189-
(BlockBuffering _, buf) -> writeBlocks (nl == CRLF) h buf str
190-
191-
hPutChars :: Handle -> Stream Char -> IO ()
192-
hPutChars h (Stream next0 s0 _len) = loop s0
193-
where
194-
loop !s = case next0 s of
195-
Done -> return ()
196-
Skip s' -> loop s'
197-
Yield x s' -> hPutChar h x >> loop s'
198-
199-
-- The following functions are largely lifted from GHC.IO.Handle.Text,
200-
-- but adapted to a coinductive stream of data instead of an inductive
201-
-- list.
202-
--
203-
-- We have several variations of more or less the same code for
204-
-- performance reasons. Splitting the original buffered write
205-
-- function into line- and block-oriented versions gave us a 2.1x
206-
-- performance improvement. Lifting out the raw/cooked newline
207-
-- handling gave a few more percent on top.
208-
209-
writeLines :: Handle -> Newline -> CharBuffer -> Stream Char -> IO ()
210-
writeLines h nl buf0 (Stream next0 s0 _len) = outer s0 buf0
211-
where
212-
outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
213-
where
214-
inner !s !n =
215-
case next0 s of
216-
Done -> commit n False{-no flush-} True{-release-} >> return ()
217-
Skip s' -> inner s' n
218-
Yield x s'
219-
| n + 1 >= len -> commit n True{-needs flush-} False >>= outer s
220-
| x == '\n' -> do
221-
n' <- if nl == CRLF
222-
then do n1 <- writeCharBuf raw len n '\r'
223-
writeCharBuf raw len n1 '\n'
224-
else writeCharBuf raw len n x
225-
commit n' True{-needs flush-} False >>= outer s'
226-
| otherwise -> writeCharBuf raw len n x >>= inner s'
227-
commit = commitBuffer h raw len
228-
229-
writeBlocks :: Bool -> Handle -> CharBuffer -> Stream Char -> IO ()
230-
writeBlocks isCRLF h buf0 (Stream next0 s0 _len) = outer s0 buf0
231-
where
232-
outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
233-
where
234-
inner !s !n =
235-
case next0 s of
236-
Done -> commit n False{-no flush-} True{-release-} >> return ()
237-
Skip s' -> inner s' n
238-
Yield x s'
239-
| isCRLF && x == '\n' && n + 1 < len -> do
240-
n1 <- writeCharBuf raw len n '\r'
241-
writeCharBuf raw len n1 '\n' >>= inner s'
242-
| n < len -> writeCharBuf raw len n x >>= inner s'
243-
| otherwise -> commit n True{-needs flush-} False >>= outer s
244-
commit = commitBuffer h raw len
245-
246-
-- | Only modifies the raw buffer and not the buffer attributes
247-
writeCharBuf :: RawCharBuffer -> Int -> Int -> Char -> IO Int
248-
writeCharBuf bufRaw bufSize n c = E.assert (n >= 0 && n < bufSize) $
249-
GHC.IO.Buffer.writeCharBuf bufRaw n c
250-
251-
-- This function is completely lifted from GHC.IO.Handle.Text.
252-
getSpareBuffer :: Handle__ -> IO (BufferMode, CharBuffer)
253-
getSpareBuffer Handle__{haCharBuffer=ref,
254-
haBuffers=spare_ref,
255-
haBufferMode=mode}
256-
= do
257-
case mode of
258-
NoBuffering -> return (mode, error "no buffer!")
259-
_ -> do
260-
bufs <- readIORef spare_ref
261-
buf <- readIORef ref
262-
case bufs of
263-
BufferListCons b rest -> do
264-
writeIORef spare_ref rest
265-
return ( mode, emptyBuffer b (bufSize buf) WriteBuffer)
266-
BufferListNil -> do
267-
new_buf <- newCharBuffer (bufSize buf) WriteBuffer
268-
return (mode, new_buf)
269-
270-
271-
-- This function is modified from GHC.Internal.IO.Handle.Text.
272-
commitBuffer :: Handle -> RawCharBuffer -> Int -> Int -> Bool -> Bool
273-
-> IO CharBuffer
274-
commitBuffer hdl !raw !sz !count flush release =
275-
wantWritableHandle "commitAndReleaseBuffer" hdl $
276-
commitBuffer' raw sz count flush release
277-
{-# INLINE commitBuffer #-}
171+
hPutStr h = hPutStream h . stream
278172

279173
-- | Write a string to a handle, followed by a newline.
280174
hPutStrLn :: Handle -> Text -> IO ()
281-
hPutStrLn h = hPutStr' h . streamLn
175+
hPutStrLn h = hPutStream h . streamLn
282176

283177
-- | The 'interact' function takes a function of type @Text -> Text@
284178
-- as its argument. The entire input from the standard input device is

src/Data/Text/Internal/IO.hs

Lines changed: 111 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Data.Text.Internal.IO
1818
(
1919
hGetLineWith
2020
, readChunk
21+
, hPutStream
2122
) where
2223

2324
import qualified Control.Exception as E
@@ -28,12 +29,15 @@ import Data.Text.Internal.Fusion.Types (Step(..), Stream(..))
2829
import Data.Text.Internal.Fusion.Size (exactSize, maxSize)
2930
import Data.Text.Unsafe (inlinePerformIO)
3031
import Foreign.Storable (peekElemOff)
31-
import GHC.IO.Buffer (Buffer(..), CharBuffer, RawCharBuffer, bufferAdjustL,
32-
bufferElems, charSize, isEmptyBuffer, readCharBuf,
33-
withRawBuffer, writeCharBuf)
34-
import GHC.IO.Handle.Internals (ioe_EOF, readTextDevice, wantReadableHandle_)
35-
import GHC.IO.Handle.Types (Handle__(..), Newline(..))
36-
import System.IO (Handle)
32+
import GHC.IO.Buffer (Buffer(..), BufferState(..), CharBuffer, RawCharBuffer,
33+
bufferAdjustL, bufferElems, charSize, emptyBuffer,
34+
isEmptyBuffer, newCharBuffer, readCharBuf, withRawBuffer,
35+
writeCharBuf)
36+
import GHC.IO.Handle.Internals (ioe_EOF, readTextDevice, wantReadableHandle_,
37+
wantWritableHandle)
38+
import GHC.IO.Handle.Text (commitBuffer')
39+
import GHC.IO.Handle.Types (BufferList(..), BufferMode(..), Handle__(..), Newline(..))
40+
import System.IO (Handle, hPutChar)
3741
import System.IO.Error (isEOFError)
3842
import qualified Data.Text as T
3943

@@ -162,5 +166,106 @@ readChunk hh@Handle__{..} buf = do
162166
writeIORef haCharBuffer (bufferAdjustL r buf')
163167
return t
164168

169+
-- | Print a @Stream Char@.
170+
hPutStream :: Handle -> Stream Char -> IO ()
171+
-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
172+
hPutStream h str = do
173+
(buffer_mode, nl) <-
174+
wantWritableHandle "hPutStr" h $ \h_ -> do
175+
bmode <- getSpareBuffer h_
176+
return (bmode, haOutputNL h_)
177+
case buffer_mode of
178+
(NoBuffering, _) -> hPutChars h str
179+
(LineBuffering, buf) -> writeLines h nl buf str
180+
(BlockBuffering _, buf) -> writeBlocks (nl == CRLF) h buf str
181+
182+
hPutChars :: Handle -> Stream Char -> IO ()
183+
hPutChars h (Stream next0 s0 _len) = loop s0
184+
where
185+
loop !s = case next0 s of
186+
Done -> return ()
187+
Skip s' -> loop s'
188+
Yield x s' -> hPutChar h x >> loop s'
189+
190+
-- The following functions are largely lifted from GHC.IO.Handle.Text,
191+
-- but adapted to a coinductive stream of data instead of an inductive
192+
-- list.
193+
--
194+
-- We have several variations of more or less the same code for
195+
-- performance reasons. Splitting the original buffered write
196+
-- function into line- and block-oriented versions gave us a 2.1x
197+
-- performance improvement. Lifting out the raw/cooked newline
198+
-- handling gave a few more percent on top.
199+
200+
writeLines :: Handle -> Newline -> CharBuffer -> Stream Char -> IO ()
201+
writeLines h nl buf0 (Stream next0 s0 _len) = outer s0 buf0
202+
where
203+
outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
204+
where
205+
inner !s !n =
206+
case next0 s of
207+
Done -> commit n False{-no flush-} True{-release-} >> return ()
208+
Skip s' -> inner s' n
209+
Yield x s'
210+
| n + 1 >= len -> commit n True{-needs flush-} False >>= outer s
211+
| x == '\n' -> do
212+
n' <- if nl == CRLF
213+
then do n1 <- writeCharBuf' raw len n '\r'
214+
writeCharBuf' raw len n1 '\n'
215+
else writeCharBuf' raw len n x
216+
commit n' True{-needs flush-} False >>= outer s'
217+
| otherwise -> writeCharBuf' raw len n x >>= inner s'
218+
commit = commitBuffer h raw len
219+
220+
writeBlocks :: Bool -> Handle -> CharBuffer -> Stream Char -> IO ()
221+
writeBlocks isCRLF h buf0 (Stream next0 s0 _len) = outer s0 buf0
222+
where
223+
outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
224+
where
225+
inner !s !n =
226+
case next0 s of
227+
Done -> commit n False{-no flush-} True{-release-} >> return ()
228+
Skip s' -> inner s' n
229+
Yield x s'
230+
| isCRLF && x == '\n' && n + 1 < len -> do
231+
n1 <- writeCharBuf' raw len n '\r'
232+
writeCharBuf' raw len n1 '\n' >>= inner s'
233+
| n < len -> writeCharBuf' raw len n x >>= inner s'
234+
| otherwise -> commit n True{-needs flush-} False >>= outer s
235+
commit = commitBuffer h raw len
236+
237+
-- | Only modifies the raw buffer and not the buffer attributes
238+
writeCharBuf' :: RawCharBuffer -> Int -> Int -> Char -> IO Int
239+
writeCharBuf' bufRaw bufSize n c = E.assert (n >= 0 && n < bufSize) $
240+
writeCharBuf bufRaw n c
241+
242+
-- This function is completely lifted from GHC.IO.Handle.Text.
243+
getSpareBuffer :: Handle__ -> IO (BufferMode, CharBuffer)
244+
getSpareBuffer Handle__{haCharBuffer=ref,
245+
haBuffers=spare_ref,
246+
haBufferMode=mode}
247+
= do
248+
case mode of
249+
NoBuffering -> return (mode, error "no buffer!")
250+
_ -> do
251+
bufs <- readIORef spare_ref
252+
buf <- readIORef ref
253+
case bufs of
254+
BufferListCons b rest -> do
255+
writeIORef spare_ref rest
256+
return ( mode, emptyBuffer b (bufSize buf) WriteBuffer)
257+
BufferListNil -> do
258+
new_buf <- newCharBuffer (bufSize buf) WriteBuffer
259+
return (mode, new_buf)
260+
261+
262+
-- This function is modified from GHC.Internal.IO.Handle.Text.
263+
commitBuffer :: Handle -> RawCharBuffer -> Int -> Int -> Bool -> Bool
264+
-> IO CharBuffer
265+
commitBuffer hdl !raw !sz !count flush release =
266+
wantWritableHandle "commitAndReleaseBuffer" hdl $
267+
commitBuffer' raw sz count flush release
268+
{-# INLINE commitBuffer #-}
269+
165270
sizeError :: String -> a
166271
sizeError loc = error $ "Data.Text.IO." ++ loc ++ ": bad internal buffer size"

0 commit comments

Comments
 (0)