on dd20fc2 help sign in
text/Data/Text/IO.hs
338 lines 13 kB history raw
1{-# LANGUAGE BangPatterns, CPP, RecordWildCards, ScopedTypeVariables #-}
2#if __GLASGOW_HASKELL__ >= 702
3{-# LANGUAGE Trustworthy #-}
4#endif
5-- |
6-- Module : Data.Text.IO
7-- Copyright : (c) 2009, 2010 Bryan O'Sullivan,
8-- (c) 2009 Simon Marlow
9-- License : BSD-style
10-- Maintainer : bos@serpentine.com
11-- Stability : experimental
12-- Portability : GHC
13--
14-- Efficient locale-sensitive support for text I\/O.
15--
16-- Skip past the synopsis for some important notes on performance and
17-- portability across different versions of GHC.
19module Data.Text.IO
20 (
21 -- * Performance
22 -- $performance
24 -- * Locale support
25 -- $locale
26 -- * File-at-a-time operations
27 readFile
28 , writeFile
29 , appendFile
30 -- * Operations on handles
31 , hGetContents
32 , hGetChunk
33 , hGetLine
34 , hPutStr
35 , hPutStrLn
36 -- * Special cases for standard input and output
37 , interact
38 , getContents
39 , getLine
40 , putStr
41 , putStrLn
42 ) where
44import Data.Text (Text)
45import Prelude hiding (appendFile, getContents, getLine, interact,
46 putStr, putStrLn, readFile, writeFile)
47import System.IO (Handle, IOMode(..), hPutChar, openFile, stdin, stdout,
48 withFile)
49import qualified Control.Exception as E
50import Control.Monad (liftM2, when)
51import Data.IORef (readIORef, writeIORef)
52import qualified Data.Text as T
53import Data.Text.Internal.Fusion (stream)
54import Data.Text.Internal.Fusion.Types (Step(..), Stream(..))
55import Data.Text.Internal.IO (hGetLineWith, readChunk)
56import GHC.IO.Buffer (Buffer(..), BufferState(..), CharBufElem, CharBuffer,
57 RawCharBuffer, emptyBuffer, isEmptyBuffer, newCharBuffer,
58 writeCharBuf)
59import GHC.IO.Exception (IOException(ioe_type), IOErrorType(InappropriateType))
60import GHC.IO.Handle.Internals (augmentIOError, hClose_help, wantReadableHandle,
61 wantWritableHandle)
62import GHC.IO.Handle.Text (commitBuffer')
63import GHC.IO.Handle.Types (BufferList(..), BufferMode(..), Handle__(..),
64 HandleType(..), Newline(..))
65import System.IO (hGetBuffering, hFileSize, hSetBuffering, hTell)
66import System.IO.Error (isEOFError)
68-- $performance
69-- #performance#
70--
71-- The functions in this module obey the runtime system's locale,
72-- character set encoding, and line ending conversion settings.
73--
74-- If you know in advance that you will be working with data that has
75-- a specific encoding (e.g. UTF-8), and your application is highly
76-- performance sensitive, you may find that it is faster to perform
77-- I\/O with bytestrings and to encode and decode yourself than to use
78-- the functions in this module.
79--
80-- Whether this will hold depends on the version of GHC you are using,
81-- the platform you are working on, the data you are working with, and
82-- the encodings you are using, so be sure to test for yourself.
84-- | The 'readFile' function reads a file and returns the contents of
85-- the file as a string. The entire file is read strictly, as with
86-- 'getContents'.
87readFile :: FilePath -> IO Text
88readFile name = openFile name ReadMode >>= hGetContents
90-- | Write a string to a file. The file is truncated to zero length
91-- before writing begins.
92writeFile :: FilePath -> Text -> IO ()
93writeFile p = withFile p WriteMode . flip hPutStr
95-- | Write a string the end of a file.
96appendFile :: FilePath -> Text -> IO ()
97appendFile p = withFile p AppendMode . flip hPutStr
99catchError :: String -> Handle -> Handle__ -> IOError -> IO Text
100catchError caller h Handle__{..} err
101 | isEOFError err = do
102 buf <- readIORef haCharBuffer
103 return $ if isEmptyBuffer buf
104 then T.empty
105 else T.singleton '\r'
106 | otherwise = E.throwIO (augmentIOError err caller h)
108-- | /Experimental./ Read a single chunk of strict text from a
109-- 'Handle'. The size of the chunk depends on the amount of input
110-- currently buffered.
111--
112-- This function blocks only if there is no data available, and EOF
113-- has not yet been reached. Once EOF is reached, this function
114-- returns an empty string instead of throwing an exception.
115hGetChunk :: Handle -> IO Text
116hGetChunk h = wantReadableHandle "hGetChunk" h readSingleChunk
117 where
118 readSingleChunk hh@Handle__{..} = do
119 buf <- readIORef haCharBuffer
120 t <- readChunk hh buf `E.catch` catchError "hGetChunk" h hh
121 return (hh, t)
123-- | Read the remaining contents of a 'Handle' as a string. The
124-- 'Handle' is closed once the contents have been read, or if an
125-- exception is thrown.
126--
127-- Internally, this function reads a chunk at a time from the
128-- lower-level buffering abstraction, and concatenates the chunks into
129-- a single string once the entire file has been read.
130--
131-- As a result, it requires approximately twice as much memory as its
132-- result to construct its result. For files more than a half of
133-- available RAM in size, this may result in memory exhaustion.
134hGetContents :: Handle -> IO Text
135hGetContents h = do
136 chooseGoodBuffering h
137 wantReadableHandle "hGetContents" h readAll
138 where
139 readAll hh@Handle__{..} = do
140 let readChunks = do
141 buf <- readIORef haCharBuffer
142 t <- readChunk hh buf `E.catch` catchError "hGetContents" h hh
143 if T.null t
144 then return [t]
145 else (t:) `fmap` readChunks
146 ts <- readChunks
147 (hh', _) <- hClose_help hh
148 return (hh'{haType=ClosedHandle}, T.concat ts)
150-- | Use a more efficient buffer size if we're reading in
151-- block-buffered mode with the default buffer size. When we can
152-- determine the size of the handle we're reading, set the buffer size
153-- to that, so that we can read the entire file in one chunk.
154-- Otherwise, use a buffer size of at least 16KB.
155chooseGoodBuffering :: Handle -> IO ()
156chooseGoodBuffering h = do
157 bufMode <- hGetBuffering h
158 case bufMode of
159 BlockBuffering Nothing -> do
160 d <- E.catch (liftM2 (-) (hFileSize h) (hTell h)) $ \(e::IOException) ->
161 if ioe_type e == InappropriateType
162 then return 16384 -- faster than the 2KB default
163 else E.throwIO e
164 when (d > 0) . hSetBuffering h . BlockBuffering . Just . fromIntegral $ d
165 _ -> return ()
167-- | Read a single line from a handle.
168hGetLine :: Handle -> IO Text
169hGetLine = hGetLineWith T.concat
171-- | Write a string to a handle.
172hPutStr :: Handle -> Text -> IO ()
173-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
174hPutStr h t = do
175 (buffer_mode, nl) <-
176 wantWritableHandle "hPutStr" h $ \h_ -> do
177 bmode <- getSpareBuffer h_
178 return (bmode, haOutputNL h_)
179 let str = stream t
180 case buffer_mode of
181 (NoBuffering, _) -> hPutChars h str
182 (LineBuffering, buf) -> writeLines h nl buf str
183 (BlockBuffering _, buf)
184 | nl == CRLF -> writeBlocksCRLF h buf str
185 | otherwise -> writeBlocksRaw h buf str
187hPutChars :: Handle -> Stream Char -> IO ()
188hPutChars h (Stream next0 s0 _len) = loop s0
189 where
190 loop !s = case next0 s of
191 Done -> return ()
192 Skip s' -> loop s'
193 Yield x s' -> hPutChar h x >> loop s'
195-- The following functions are largely lifted from GHC.IO.Handle.Text,
196-- but adapted to a coinductive stream of data instead of an inductive
197-- list.
198--
199-- We have several variations of more or less the same code for
200-- performance reasons. Splitting the original buffered write
201-- function into line- and block-oriented versions gave us a 2.1x
202-- performance improvement. Lifting out the raw/cooked newline
203-- handling gave a few more percent on top.
205writeLines :: Handle -> Newline -> Buffer CharBufElem -> Stream Char -> IO ()
206writeLines h nl buf0 (Stream next0 s0 _len) = outer s0 buf0
207 where
208 outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
209 where
210 inner !s !n =
211 case next0 s of
212 Done -> commit n False{-no flush-} True{-release-} >> return ()
213 Skip s' -> inner s' n
214 Yield x s'
215 | n + 1 >= len -> commit n True{-needs flush-} False >>= outer s
216 | x == '\n' -> do
217 n' <- if nl == CRLF
218 then do n1 <- writeCharBuf raw n '\r'
219 writeCharBuf raw n1 '\n'
220 else writeCharBuf raw n x
221 commit n' True{-needs flush-} False >>= outer s'
222 | otherwise -> writeCharBuf raw n x >>= inner s'
223 commit = commitBuffer h raw len
225writeBlocksCRLF :: Handle -> Buffer CharBufElem -> Stream Char -> IO ()
226writeBlocksCRLF h buf0 (Stream next0 s0 _len) = outer s0 buf0
227 where
228 outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
229 where
230 inner !s !n =
231 case next0 s of
232 Done -> commit n False{-no flush-} True{-release-} >> return ()
233 Skip s' -> inner s' n
234 Yield x s'
235 | n + 1 >= len -> commit n True{-needs flush-} False >>= outer s
236 | x == '\n' -> do n1 <- writeCharBuf raw n '\r'
237 writeCharBuf raw n1 '\n' >>= inner s'
238 | otherwise -> writeCharBuf raw n x >>= inner s'
239 commit = commitBuffer h raw len
241writeBlocksRaw :: Handle -> Buffer CharBufElem -> Stream Char -> IO ()
242writeBlocksRaw h buf0 (Stream next0 s0 _len) = outer s0 buf0
243 where
244 outer s1 Buffer{bufRaw=raw, bufSize=len} = inner s1 (0::Int)
245 where
246 inner !s !n =
247 case next0 s of
248 Done -> commit n False{-no flush-} True{-release-} >> return ()
249 Skip s' -> inner s' n
250 Yield x s'
251 | n + 1 >= len -> commit n True{-needs flush-} False >>= outer s
252 | otherwise -> writeCharBuf raw n x >>= inner s'
253 commit = commitBuffer h raw len
255-- This function is completely lifted from GHC.IO.Handle.Text.
256getSpareBuffer :: Handle__ -> IO (BufferMode, CharBuffer)
257getSpareBuffer Handle__{haCharBuffer=ref,
258 haBuffers=spare_ref,
259 haBufferMode=mode}
260 = do
261 case mode of
262 NoBuffering -> return (mode, error "no buffer!")
263 _ -> do
264 bufs <- readIORef spare_ref
265 buf <- readIORef ref
266 case bufs of
267 BufferListCons b rest -> do
268 writeIORef spare_ref rest
269 return ( mode, emptyBuffer b (bufSize buf) WriteBuffer)
270 BufferListNil -> do
271 new_buf <- newCharBuffer (bufSize buf) WriteBuffer
272 return (mode, new_buf)
275-- This function is completely lifted from GHC.IO.Handle.Text.
276commitBuffer :: Handle -> RawCharBuffer -> Int -> Int -> Bool -> Bool
277 -> IO CharBuffer
278commitBuffer hdl !raw !sz !count flush release =
279 wantWritableHandle "commitAndReleaseBuffer" hdl $
280 commitBuffer' raw sz count flush release
281{-# INLINE commitBuffer #-}
283-- | Write a string to a handle, followed by a newline.
284hPutStrLn :: Handle -> Text -> IO ()
285hPutStrLn h t = hPutStr h t >> hPutChar h '\n'
287-- | The 'interact' function takes a function of type @Text -> Text@
288-- as its argument. The entire input from the standard input device is
289-- passed to this function as its argument, and the resulting string
290-- is output on the standard output device.
291interact :: (Text -> Text) -> IO ()
292interact f = putStr . f =<< getContents
294-- | Read all user input on 'stdin' as a single string.
295getContents :: IO Text
296getContents = hGetContents stdin
298-- | Read a single line of user input from 'stdin'.
299getLine :: IO Text
300getLine = hGetLine stdin
302-- | Write a string to 'stdout'.
303putStr :: Text -> IO ()
304putStr = hPutStr stdout
306-- | Write a string to 'stdout', followed by a newline.
307putStrLn :: Text -> IO ()
308putStrLn = hPutStrLn stdout
310-- $locale
311--
312-- /Note/: The behaviour of functions in this module depends on the
313-- version of GHC you are using.
314--
315-- Beginning with GHC 6.12, text I\/O is performed using the system or
316-- handle's current locale and line ending conventions.
317--
318-- Under GHC 6.10 and earlier, the system I\/O libraries do not
319-- support locale-sensitive I\/O or line ending conversion. On these
320-- versions of GHC, functions in this library all use UTF-8. What
321-- does this mean in practice?
322--
323-- * All data that is read will be decoded as UTF-8.
324--
325-- * Before data is written, it is first encoded as UTF-8.
326--
327-- * On both reading and writing, the platform's native newline
328-- conversion is performed.
329--
330-- If you must use a non-UTF-8 locale on an older version of GHC, you
331-- will have to perform the transcoding yourself, e.g. as follows:
332--
333-- > import qualified Data.ByteString as B
334-- > import Data.Text (Text)
335-- > import Data.Text.Encoding (encodeUtf16)
336-- >
337-- > putStr_Utf16LE :: Text -> IO ()
338-- > putStr_Utf16LE t = B.putStr (encodeUtf16LE t)