1 {-# OPTIONS_GHC -fno-implicit-prelude -funbox-strict-fields #-}
2 {-# OPTIONS_HADDOCK hide #-}
3 -----------------------------------------------------------------------------
5 -- Module : GHC.IO.Buffer
6 -- Copyright : (c) The University of Glasgow 2008
7 -- License : see libraries/base/LICENSE
9 -- Maintainer : cvs-ghc@haskell.org
10 -- Stability : internal
11 -- Portability : non-portable (GHC Extensions)
13 -- Buffers used in the IO system
15 -----------------------------------------------------------------------------
17 module GHC.IO.Buffer (
18 -- * Buffers of any element
19 Buffer(..), BufferState(..), CharBuffer, CharBufElem,
27 -- ** Insertion/removal
42 -- ** Operating on the raw buffer as a Ptr
69 import Foreign.C.Types
70 import Foreign.ForeignPtr
71 import Foreign.Storable
73 -- Char buffers use either UTF-16 or UTF-32, with the endianness matching
74 -- the endianness of the host.
77 -- * a Char buffer consists of *valid* UTF-16 or UTF-32
78 -- * only whole characters: no partial surrogate pairs
82 -- #define CHARBUF_UTF16
84 -- NB. it won't work to just change this to CHARBUF_UTF16. Some of
85 -- the code to make this work is there, and it has been tested with
86 -- the Iconv codec, but there are some pieces that are known to be
87 -- broken. In particular, the built-in codecs
88 -- e.g. GHC.IO.Encoding.UTF{8,16,32} need to use isFullCharBuffer or
89 -- similar in place of the ow >= os comparisions.
91 -- ---------------------------------------------------------------------------
94 type RawBuffer e = ForeignPtr e
96 readWord8Buf :: RawBuffer Word8 -> Int -> IO Word8
97 readWord8Buf arr ix = withForeignPtr arr $ \p -> peekByteOff p ix
99 writeWord8Buf :: RawBuffer Word8 -> Int -> Word8 -> IO ()
100 writeWord8Buf arr ix w = withForeignPtr arr $ \p -> pokeByteOff p ix w
103 type CharBufElem = Word16
105 type CharBufElem = Char
108 type RawCharBuffer = RawBuffer CharBufElem
110 peekCharBuf :: RawCharBuffer -> Int -> IO Char
111 peekCharBuf arr ix = withForeignPtr arr $ \p -> do
112 (c,_) <- readCharBufPtr p ix
115 {-# INLINE readCharBuf #-}
116 readCharBuf :: RawCharBuffer -> Int -> IO (Char, Int)
117 readCharBuf arr ix = withForeignPtr arr $ \p -> readCharBufPtr p ix
119 {-# INLINE writeCharBuf #-}
120 writeCharBuf :: RawCharBuffer -> Int -> Char -> IO Int
121 writeCharBuf arr ix c = withForeignPtr arr $ \p -> writeCharBufPtr p ix c
123 {-# INLINE readCharBufPtr #-}
124 readCharBufPtr :: Ptr CharBufElem -> Int -> IO (Char, Int)
126 readCharBufPtr p ix = do
127 c1 <- peekElemOff p ix
128 if (c1 < 0xd800 || c1 > 0xdbff)
129 then return (chr (fromIntegral c1), ix+1)
130 else do c2 <- peekElemOff p (ix+1)
131 return (unsafeChr ((fromIntegral c1 - 0xd800)*0x400 +
132 (fromIntegral c2 - 0xdc00) + 0x10000), ix+2)
134 readCharBufPtr p ix = do c <- peekElemOff (castPtr p) ix; return (c, ix+1)
137 {-# INLINE writeCharBufPtr #-}
138 writeCharBufPtr :: Ptr CharBufElem -> Int -> Char -> IO Int
140 writeCharBufPtr p ix ch
141 | c < 0x10000 = do pokeElemOff p ix (fromIntegral c)
143 | otherwise = do let c' = c - 0x10000
144 pokeElemOff p ix (fromIntegral (c' `div` 0x400 + 0xd800))
145 pokeElemOff p (ix+1) (fromIntegral (c' `mod` 0x400 + 0xdc00))
150 writeCharBufPtr p ix ch = do pokeElemOff (castPtr p) ix ch; return (ix+1)
160 -- ---------------------------------------------------------------------------
163 -- The buffer is represented by a mutable variable containing a
164 -- record, where the record contains the raw buffer and the start/end
165 -- points of the filled portion. We use a mutable variable so that
166 -- the common operation of writing (or reading) some data from (to)
167 -- the buffer doesn't need to modify, and hence copy, the handle
168 -- itself, it just updates the buffer.
170 -- There will be some allocation involved in a simple hPutChar in
171 -- order to create the new Buffer structure (below), but this is
172 -- relatively small, and this only has to be done once per write
175 -- | A mutable array of bytes that can be passed to foreign functions.
178 bufRaw :: !(RawBuffer e),
179 bufState :: BufferState,
180 bufSize :: !Int, -- in elements, not bytes
181 bufL :: !Int, -- offset of first item in the buffer
182 bufR :: !Int -- offset of last item + 1
186 type CharBuffer = Buffer Word16
188 type CharBuffer = Buffer Char
191 data BufferState = ReadBuffer | WriteBuffer deriving (Eq)
193 withBuffer :: Buffer e -> (Ptr e -> IO a) -> IO a
194 withBuffer Buffer{ bufRaw=raw } f = withForeignPtr (castForeignPtr raw) f
196 withRawBuffer :: RawBuffer e -> (Ptr e -> IO a) -> IO a
197 withRawBuffer raw f = withForeignPtr (castForeignPtr raw) f
199 isEmptyBuffer :: Buffer e -> Bool
200 isEmptyBuffer Buffer{ bufR=w } = w == 0
202 isFullBuffer :: Buffer e -> Bool
203 isFullBuffer Buffer{ bufR=w, bufSize=s } = s == w
205 -- if a Char buffer does not have room for a surrogate pair, it is "full"
206 isFullCharBuffer :: Buffer e -> Bool
208 isFullCharBuffer buf = bufferAvailable buf < 2
210 isFullCharBuffer = isFullBuffer
213 isWriteBuffer :: Buffer e -> Bool
214 isWriteBuffer buf = case bufState buf of
218 bufferElems :: Buffer e -> Int
219 bufferElems Buffer{ bufR=w, bufL=r } = w - r
221 bufferAvailable :: Buffer e -> Int
222 bufferAvailable Buffer{ bufR=w, bufSize=s } = s - w
224 bufferRemove :: Int -> Buffer e -> Buffer e
225 bufferRemove i buf@Buffer{ bufL=r } = bufferAdjustL (r+i) buf
227 bufferAdjustL :: Int -> Buffer e -> Buffer e
228 bufferAdjustL l buf@Buffer{ bufR=w }
229 | l == w = buf{ bufL=0, bufR=0 }
230 | otherwise = buf{ bufL=l, bufR=w }
232 bufferAdd :: Int -> Buffer e -> Buffer e
233 bufferAdd i buf@Buffer{ bufR=w } = buf{ bufR=w+i }
235 emptyBuffer :: RawBuffer e -> Int -> BufferState -> Buffer e
236 emptyBuffer raw sz state =
237 Buffer{ bufRaw=raw, bufState=state, bufR=0, bufL=0, bufSize=sz }
239 newByteBuffer :: Int -> BufferState -> IO (Buffer Word8)
240 newByteBuffer c st = newBuffer c c st
242 newCharBuffer :: Int -> BufferState -> IO CharBuffer
243 newCharBuffer c st = newBuffer (c * charSize) c st
245 newBuffer :: Int -> Int -> BufferState -> IO (Buffer e)
246 newBuffer bytes sz state = do
247 fp <- mallocForeignPtrBytes bytes
248 return (emptyBuffer fp sz state)
250 -- | slides the contents of the buffer to the beginning
251 slideContents :: Buffer Word8 -> IO (Buffer Word8)
252 slideContents buf@Buffer{ bufL=l, bufR=r, bufRaw=raw } = do
254 withRawBuffer raw $ \p ->
255 do _ <- memcpy p (p `plusPtr` l) (fromIntegral elems)
257 return buf{ bufL=0, bufR=elems }
259 foreign import ccall unsafe "memcpy"
260 memcpy :: Ptr a -> Ptr a -> CSize -> IO (Ptr ())
262 summaryBuffer :: Buffer a -> String
263 summaryBuffer buf = "buf" ++ show (bufSize buf) ++ "(" ++ show (bufL buf) ++ "-" ++ show (bufR buf) ++ ")"
265 -- INVARIANTS on Buffers:
267 -- * if r == w, then r == 0 && w == 0
268 -- * if state == WriteBuffer, then r == 0
269 -- * a write buffer is never full. If an operation
270 -- fills up the buffer, it will always flush it before
272 -- * a read buffer may be full as a result of hLookAhead. In normal
273 -- operation, a read buffer always has at least one character of space.
275 checkBuffer :: Buffer a -> IO ()
276 checkBuffer buf@Buffer{ bufState = state, bufL=r, bufR=w, bufSize=size } = do
281 && ( r /= w || (r == 0 && w == 0) )
282 && ( state /= WriteBuffer || r == 0 )
283 && ( state /= WriteBuffer || w < size ) -- write buffer is never full
286 check :: Buffer a -> Bool -> IO ()
287 check _ True = return ()
288 check buf False = error ("buffer invariant violation: " ++ summaryBuffer buf)