@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns, CPP #-}
1+{-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}
3-- Module : Data.Text.IO
3-- Module : Data.Text.IO
4-- Copyright : (c) Bryan O'Sullivan 2009,
4-- Copyright : (c) Bryan O'Sullivan 2009,
@@ -31,6 +31,7 @@ module Data.Text.IO
31 , putStrLn
31 , putStrLn
34import Data.Text (Text)
35import Data.Text (Text)
35import Prelude hiding (appendFile, getContents, getLine, interact, putStr,
36import Prelude hiding (appendFile, getContents, getLine, interact, putStr,
36 putStrLn, readFile, writeFile)
37 putStrLn, readFile, writeFile)
@@ -40,15 +41,22 @@ import qualified Data.ByteString as B
40import Data.Text.Encoding (decodeUtf8, encodeUtf8)
41import Data.Text.Encoding (decodeUtf8, encodeUtf8)
42import Data.IORef (readIORef, writeIORef)
43import Data.IORef (readIORef, writeIORef)
44+import qualified Data.Text as T
43−import Data.Text.Fusion (stream)
45+import Data.Text.Fusion (stream, unstream)
44import Data.Text.Fusion.Internal (Step(..), Stream(..))
46import Data.Text.Fusion.Internal (Step(..), Stream(..))
47+import Data.Text.Fusion.Size (exactSize, maxSize)
48+import Data.Text.Unsafe (inlinePerformIO)
49+import Foreign.Storable (peekElemOff)
45import GHC.IO.Buffer (Buffer(..), BufferState(..), CharBufElem, CharBuffer,
50import GHC.IO.Buffer (Buffer(..), BufferState(..), CharBufElem, CharBuffer,
46− RawCharBuffer, emptyBuffer, newCharBuffer, writeCharBuf)
51+ RawCharBuffer, bufferAdjustL, bufferElems, charSize, emptyBuffer, isEmptyBuffer, newCharBuffer, readCharBuf, withRawBuffer,
53+import GHC.IO.Handle.Internals (ioe_EOF, readTextDevice, wantReadableHandle_,
47−import GHC.IO.Handle.Internals (wantWritableHandle)
54+ wantWritableHandle)
48import GHC.IO.Handle.Text (commitBuffer')
55import GHC.IO.Handle.Text (commitBuffer')
49import GHC.IO.Handle.Types (BufferList(..), BufferMode(..), Handle__(..),
56import GHC.IO.Handle.Types (BufferList(..), BufferMode(..), Handle__(..),
50 Newline(..))
57 Newline(..))
51import System.IO (IOMode(..), openFile, withFile)
58import System.IO (IOMode(..), openFile, withFile)
59+import System.IO.Error (isEOFError)
54-- | The 'readFile' function reads a file and returns the contents of
62-- | The 'readFile' function reads a file and returns the contents of
@@ -79,7 +87,119 @@ hGetLine :: Handle -> IO Text
79#if __GLASGOW_HASKELL__ <= 610
87#if __GLASGOW_HASKELL__ <= 610
80hGetLine = fmap decodeUtf8 . B.hGetLine
88hGetLine = fmap decodeUtf8 . B.hGetLine
82−hGetLine = undefined
90+hGetLine h = wantReadableHandle_ "hGetLine" h go
91+ where go hh@Handle__{..} = readIORef haCharBuffer >>= hGetLineLoop hh []
93+hGetLineLoop :: Handle__ -> [Text] -> CharBuffer -> IO Text
94+hGetLineLoop hh@Handle__{..} ts buf@Buffer{ bufL=r0, bufR=w, bufRaw=raw0 } = do
96+ | r == w = return (False, w)
98+ (c,r') <- readCharBuf raw r
100+ then return (True, r)
102+ (eol, off) <- findEOL raw0 r0
103+ (t,r') <- if haInputNL == CRLF
104+ then unpack_nl raw0 r0 off
105+ else do t <- unpack raw0 r0 off
108+ then do writeIORef haCharBuffer (bufferAdjustL (off+1) buf)
109+ return $! T.concat (reverse (t:ts))
111+ let buf1 = bufferAdjustL r' buf
112+ maybe_buf <- maybeFillReadBuffer hh buf1
114+ -- Nothing indicates we caught an EOF, and we may have a
115+ -- partial line to return.
117+ -- we reached EOF. There might be a lone \r left
118+ -- in the buffer, so check for that and
119+ -- append it to the line if necessary.
121+ let pre | isEmptyBuffer buf1 = T.empty
122+ | otherwise = T.singleton '\r'
123+ writeIORef haCharBuffer buf1{ bufL=0, bufR=0 }
124+ let str = T.concat . reverse $ pre:t:ts
129+ hGetLineLoop hh (t:ts) new_buf
131+-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
132+maybeFillReadBuffer :: Handle__ -> CharBuffer -> IO (Maybe CharBuffer)
133+maybeFillReadBuffer handle_ buf
134+ = catch (Just `fmap` getSomeCharacters handle_ buf) $ \e ->
139+unpack :: RawCharBuffer -> Int -> Int -> IO Text
141+ | charSize /= 4 = sizeError "unpack"
142+ | r == w = return T.empty
143+ | otherwise = withRawBuffer buf go
145+ go pbuf = return $! unstream (Stream next r (exactSize (w-r)))
147+ next !i | i >= w = Done
148+ | otherwise = Yield (let c = ix i in trace (show (w,i,c)) c) (i+1)
149+ ix i = inlinePerformIO $ peekElemOff pbuf i
151+unpack_nl :: RawCharBuffer -> Int -> Int -> IO (Text, Int)
153+ | charSize /= 4 = sizeError "unpack_nl"
154+ | r == w = return (T.empty, 0)
155+ | otherwise = withRawBuffer buf $ go
158+ let t = unstream (Stream next r (maxSize (w-r)))
160+ return $ if ix w' == '\r'
164+ next !i | i >= w = Done
165+ | c == '\r' = let i' = i + 1
167+ then if ix i' == '\n'
168+ then Yield '\n' (i+2)
171+ | otherwise = Yield c (i+1)
173+ ix i = inlinePerformIO $ peekElemOff pbuf i
175+sizeError :: String -> a
176+sizeError loc = error $ "Data.Text.IO." ++ loc ++ ": bad internal buffer size"
178+-- This function is completely lifted from GHC.IO.Handle.Text.
179+getSomeCharacters :: Handle__ -> CharBuffer -> IO CharBuffer
180+getSomeCharacters handle_@Handle__{..} buf@Buffer{..} =
181+ case bufferElems buf of
182+ -- buffer empty: read some more
183+ 0 -> readTextDevice handle_ buf
185+ -- if the buffer has a single '\r' in it and we're doing newline
186+ -- translation: read some more
187+ 1 | haInputNL == CRLF -> do
188+ (c,_) <- readCharBuf bufRaw bufL
190+ then do -- shuffle the '\r' to the beginning. This is only safe
191+ -- if we're about to call readTextDevice, otherwise it
192+ -- would mess up flushCharBuffer.
193+ -- See [note Buffer Flushing], GHC.IO.Handle.Types
194+ _ <- writeCharBuf bufRaw 0 '\r'
195+ let buf' = buf{ bufL=0, bufR=1 }
196+ readTextDevice handle_ buf'
200+ -- buffer has some chars in it already: just return it
85-- | Write a string to a handle.
205-- | Write a string to a handle.
@@ -87,6 +207,7 @@ hPutStr :: Handle -> Text -> IO ()
87#if __GLASGOW_HASKELL__ <= 610
207#if __GLASGOW_HASKELL__ <= 610
88hPutStr h = B.hPutStr h . encodeUtf8
208hPutStr h = B.hPutStr h . encodeUtf8
210+-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
90hPutStr h t = do
211hPutStr h t = do
91 (buffer_mode, nl) <-
212 (buffer_mode, nl) <-
92 wantWritableHandle "hPutStr" h $ \h_ -> do
213 wantWritableHandle "hPutStr" h $ \h_ -> do
@@ -193,7 +314,16 @@ putStrLn = hPutStrLn stdout
193-- Under GHC 6.10 and earlier, the system I\/O libraries do not
314-- Under GHC 6.10 and earlier, the system I\/O libraries do not
194-- support locale-sensitive I\/O. All data read by functions in this
315-- support locale-sensitive I\/O. All data read by functions in this
195-- module is decoded as UTF-8, and before data is written, it is first
316-- module is decoded as UTF-8, and before data is written, it is first
196−-- encoded as UTF-8.
317+-- encoded as UTF-8. If you must use a non-UTF-8 locale on an older
318+-- version of GHC, you will have to perform the transcoding yourself,
321+-- > import qualified Data.ByteString as B
322+-- > import Data.Text (Text)
323+-- > import Data.Text.Encoding (encodeUtf16)
325+-- > putStr_Utf16LE :: Text -> IO ()
326+-- > putStr_Utf16LE t = B.putStr (encodeUtf16LE t)
198-- Beginning with GHC 6.12, text I\/O is performed using the system or
328-- Beginning with GHC 6.12, text I\/O is performed using the system or
199-- handle's current locale.
329-- handle's current locale.