1{-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}
3-- Module : Data.Text.IO.Internal
4-- Copyright : (c) 2009, 2010 Bryan O'Sullivan,
5-- (c) 2009 Simon Marlow
7-- Maintainer : bos@serpentine.com
8-- Stability : experimental
11-- Low-level support for text I\/O.
13module Data.Text.IO.Internal
15#if __GLASGOW_HASKELL__ >= 612
21#if __GLASGOW_HASKELL__ >= 612
22import Data.IORef (readIORef, writeIORef)
23import Data.Text (Text)
24import Data.Text.Fusion (unstream)
25import Data.Text.Fusion.Internal (Step(..), Stream(..))
26import Data.Text.Fusion.Size (exactSize, maxSize)
27import Data.Text.Unsafe (inlinePerformIO)
28import Foreign.Storable (peekElemOff)
29import GHC.IO.Buffer (Buffer(..), CharBuffer, RawCharBuffer, bufferAdjustL,
30 bufferElems, charSize, isEmptyBuffer, readCharBuf,
31 withRawBuffer, writeCharBuf)
32import GHC.IO.Handle.Internals (ioe_EOF, readTextDevice, wantReadableHandle_)
33import GHC.IO.Handle.Types (Handle__(..), Newline(..))
34import System.IO (Handle)
35import System.IO.Error (isEOFError)
36import qualified Data.Text as T
38-- | Read a single line of input from a handle, constructing a list of
39-- decoded chunks as we go. When we're done, transform them into the
41hGetLineWith :: ([Text] -> t) -> Handle -> IO t
42hGetLineWith f h = wantReadableHandle_ "hGetLine" h go
44 go hh@Handle__{..} = readIORef haCharBuffer >>= fmap f . hGetLineLoop hh []
46hGetLineLoop :: Handle__ -> [Text] -> CharBuffer -> IO [Text]
47hGetLineLoop hh@Handle__{..} = go where
48 go ts buf@Buffer{ bufL=r0, bufR=w, bufRaw=raw0 } = do
49 let findEOL raw r | r == w = return (False, w)
51 (c,r') <- readCharBuf raw r
55 (eol, off) <- findEOL raw0 r0
56 (t,r') <- if haInputNL == CRLF
57 then unpack_nl raw0 r0 off
58 else do t <- unpack raw0 r0 off
61 then do writeIORef haCharBuffer (bufferAdjustL (off+1) buf)
62 return $ reverse (t:ts)
64 let buf1 = bufferAdjustL r' buf
65 maybe_buf <- maybeFillReadBuffer hh buf1
67 -- Nothing indicates we caught an EOF, and we may have a
68 -- partial line to return.
70 -- we reached EOF. There might be a lone \r left
71 -- in the buffer, so check for that and
72 -- append it to the line if necessary.
73 let pre | isEmptyBuffer buf1 = T.empty
74 | otherwise = T.singleton '\r'
75 writeIORef haCharBuffer buf1{ bufL=0, bufR=0 }
76 let str = reverse . filter (not . T.null) $ pre:t:ts
80 Just new_buf -> go (t:ts) new_buf
82-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
83maybeFillReadBuffer :: Handle__ -> CharBuffer -> IO (Maybe CharBuffer)
84maybeFillReadBuffer handle_ buf
85 = catch (Just `fmap` getSomeCharacters handle_ buf) $ \e ->
90unpack :: RawCharBuffer -> Int -> Int -> IO Text
92 | charSize /= 4 = sizeError "unpack"
93 | r >= w = return T.empty
94 | otherwise = withRawBuffer buf go
96 go pbuf = return $! unstream (Stream next r (exactSize (w-r)))
98 next !i | i >= w = Done
99 | otherwise = Yield (ix i) (i+1)
100 ix i = inlinePerformIO $ peekElemOff pbuf i
102unpack_nl :: RawCharBuffer -> Int -> Int -> IO (Text, Int)
104 | charSize /= 4 = sizeError "unpack_nl"
105 | r >= w = return (T.empty, 0)
106 | otherwise = withRawBuffer buf $ go
109 let !t = unstream (Stream next r (maxSize (w-r)))
111 return $ if ix w' == '\r'
115 next !i | i >= w = Done
116 | c == '\r' = let i' = i + 1
118 then if ix i' == '\n'
119 then Yield '\n' (i+2)
122 | otherwise = Yield c (i+1)
124 ix i = inlinePerformIO $ peekElemOff pbuf i
126-- This function is completely lifted from GHC.IO.Handle.Text.
127getSomeCharacters :: Handle__ -> CharBuffer -> IO CharBuffer
128getSomeCharacters handle_@Handle__{..} buf@Buffer{..} =
129 case bufferElems buf of
130 -- buffer empty: read some more
131 0 -> {-# SCC "readTextDevice" #-} readTextDevice handle_ buf
133 -- if the buffer has a single '\r' in it and we're doing newline
134 -- translation: read some more
135 1 | haInputNL == CRLF -> do
136 (c,_) <- readCharBuf bufRaw bufL
138 then do -- shuffle the '\r' to the beginning. This is only safe
139 -- if we're about to call readTextDevice, otherwise it
140 -- would mess up flushCharBuffer.
141 -- See [note Buffer Flushing], GHC.IO.Handle.Types
142 _ <- writeCharBuf bufRaw 0 '\r'
143 let buf' = buf{ bufL=0, bufR=1 }
144 readTextDevice handle_ buf'
148 -- buffer has some chars in it already: just return it
149 _otherwise -> {-# SCC "otherwise" #-} return buf
151-- | Read a single chunk of strict text from a buffer. Used by both
152-- the strict and lazy implementations of hGetContents.
153readChunk :: Handle__ -> CharBuffer -> IO Text
154readChunk hh@Handle__{..} buf = do
155 buf'@Buffer{..} <- getSomeCharacters hh buf
156 (t,r) <- if haInputNL == CRLF
157 then unpack_nl bufRaw bufL bufR
158 else do t <- unpack bufRaw bufL bufR
160 writeIORef haCharBuffer (bufferAdjustL r buf')
163sizeError :: String -> a
164sizeError loc = error $ "Data.Text.IO." ++ loc ++ ": bad internal buffer size"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list