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 qualified Control.Exception as E
23import Data.IORef (readIORef, writeIORef)
24import Data.Text (Text)
25import Data.Text.Fusion (unstream)
26import Data.Text.Fusion.Internal (Step(..), Stream(..))
27import Data.Text.Fusion.Size (exactSize, maxSize)
28import Data.Text.Unsafe (inlinePerformIO)
29import Foreign.Storable (peekElemOff)
30import GHC.IO.Buffer (Buffer(..), CharBuffer, RawCharBuffer, bufferAdjustL,
31 bufferElems, charSize, isEmptyBuffer, readCharBuf,
32 withRawBuffer, writeCharBuf)
33import GHC.IO.Handle.Internals (ioe_EOF, readTextDevice, wantReadableHandle_)
34import GHC.IO.Handle.Types (Handle__(..), Newline(..))
35import System.IO (Handle)
36import System.IO.Error (isEOFError)
37import qualified Data.Text as T
39-- | Read a single line of input from a handle, constructing a list of
40-- decoded chunks as we go. When we're done, transform them into the
42hGetLineWith :: ([Text] -> t) -> Handle -> IO t
43hGetLineWith f h = wantReadableHandle_ "hGetLine" h go
45 go hh@Handle__{..} = readIORef haCharBuffer >>= fmap f . hGetLineLoop hh []
47hGetLineLoop :: Handle__ -> [Text] -> CharBuffer -> IO [Text]
48hGetLineLoop hh@Handle__{..} = go where
49 go ts buf@Buffer{ bufL=r0, bufR=w, bufRaw=raw0 } = do
50 let findEOL raw r | r == w = return (False, w)
52 (c,r') <- readCharBuf raw r
56 (eol, off) <- findEOL raw0 r0
57 (t,r') <- if haInputNL == CRLF
58 then unpack_nl raw0 r0 off
59 else do t <- unpack raw0 r0 off
62 then do writeIORef haCharBuffer (bufferAdjustL (off+1) buf)
63 return $ reverse (t:ts)
65 let buf1 = bufferAdjustL r' buf
66 maybe_buf <- maybeFillReadBuffer hh buf1
68 -- Nothing indicates we caught an EOF, and we may have a
69 -- partial line to return.
71 -- we reached EOF. There might be a lone \r left
72 -- in the buffer, so check for that and
73 -- append it to the line if necessary.
74 let pre | isEmptyBuffer buf1 = T.empty
75 | otherwise = T.singleton '\r'
76 writeIORef haCharBuffer buf1{ bufL=0, bufR=0 }
77 let str = reverse . filter (not . T.null) $ pre:t:ts
81 Just new_buf -> go (t:ts) new_buf
83-- This function is lifted almost verbatim from GHC.IO.Handle.Text.
84maybeFillReadBuffer :: Handle__ -> CharBuffer -> IO (Maybe CharBuffer)
85maybeFillReadBuffer handle_ buf
86 = E.catch (Just `fmap` getSomeCharacters handle_ buf) $ \e ->
91unpack :: RawCharBuffer -> Int -> Int -> IO Text
93 | charSize /= 4 = sizeError "unpack"
94 | r >= w = return T.empty
95 | otherwise = withRawBuffer buf go
97 go pbuf = return $! unstream (Stream next r (exactSize (w-r)))
99 next !i | i >= w = Done
100 | otherwise = Yield (ix i) (i+1)
101 ix i = inlinePerformIO $ peekElemOff pbuf i
103unpack_nl :: RawCharBuffer -> Int -> Int -> IO (Text, Int)
105 | charSize /= 4 = sizeError "unpack_nl"
106 | r >= w = return (T.empty, 0)
107 | otherwise = withRawBuffer buf $ go
110 let !t = unstream (Stream next r (maxSize (w-r)))
112 return $ if ix w' == '\r'
116 next !i | i >= w = Done
117 | c == '\r' = let i' = i + 1
119 then if ix i' == '\n'
120 then Yield '\n' (i+2)
123 | otherwise = Yield c (i+1)
125 ix i = inlinePerformIO $ peekElemOff pbuf i
127-- This function is completely lifted from GHC.IO.Handle.Text.
128getSomeCharacters :: Handle__ -> CharBuffer -> IO CharBuffer
129getSomeCharacters handle_@Handle__{..} buf@Buffer{..} =
130 case bufferElems buf of
131 -- buffer empty: read some more
132 0 -> {-# SCC "readTextDevice" #-} readTextDevice handle_ buf
134 -- if the buffer has a single '\r' in it and we're doing newline
135 -- translation: read some more
136 1 | haInputNL == CRLF -> do
137 (c,_) <- readCharBuf bufRaw bufL
139 then do -- shuffle the '\r' to the beginning. This is only safe
140 -- if we're about to call readTextDevice, otherwise it
141 -- would mess up flushCharBuffer.
142 -- See [note Buffer Flushing], GHC.IO.Handle.Types
143 _ <- writeCharBuf bufRaw 0 '\r'
144 let buf' = buf{ bufL=0, bufR=1 }
145 readTextDevice handle_ buf'
149 -- buffer has some chars in it already: just return it
150 _otherwise -> {-# SCC "otherwise" #-} return buf
152-- | Read a single chunk of strict text from a buffer. Used by both
153-- the strict and lazy implementations of hGetContents.
154readChunk :: Handle__ -> CharBuffer -> IO Text
155readChunk hh@Handle__{..} buf = do
156 buf'@Buffer{..} <- getSomeCharacters hh buf
157 (t,r) <- if haInputNL == CRLF
158 then unpack_nl bufRaw bufL bufR
159 else do t <- unpack bufRaw bufL bufR
161 writeIORef haCharBuffer (bufferAdjustL r buf')
164sizeError :: String -> a
165sizeError 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