1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
{-# OPTIONS -#include "HsBase.h" #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.Array.IO
-- Copyright : (c) The University of Glasgow 2001
-- License : BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer : libraries@haskell.org
-- Stability : experimental
-- Portability : non-portable
--
-- Mutable boxed and unboxed arrays in the IO monad.
--
-----------------------------------------------------------------------------
module Data.Array.IO (
-- * @IO@ arrays with boxed elements
IOArray, -- instance of: Eq, Typeable
-- * @IO@ arrays with unboxed elements
IOUArray, -- instance of: Eq, Typeable
castIOUArray, -- :: IOUArray i a -> IO (IOUArray i b)
-- * Overloaded mutable array interface
module Data.Array.MArray,
-- * Doing I\/O with @IOUArray@s
hGetArray, -- :: Handle -> IOUArray Int Word8 -> Int -> IO Int
hPutArray, -- :: Handle -> IOUArray Int Word8 -> Int -> IO ()
) where
import Prelude
import Data.Array.Base
import Data.Array.IO.Internals
import Data.Array ( Array )
import Data.Array.MArray
import Data.Int
import Data.Word
#ifdef __GLASGOW_HASKELL__
import Foreign
import Foreign.C
import GHC.Arr
import GHC.IOBase
import GHC.Handle
#else
import Data.Char
import System.IO
import System.IO.Error
#endif
#ifdef __GLASGOW_HASKELL__
-----------------------------------------------------------------------------
-- Freezing
freezeIOArray :: Ix ix => IOArray ix e -> IO (Array ix e)
freezeIOArray (IOArray marr) = stToIO (freezeSTArray marr)
freezeIOUArray :: Ix ix => IOUArray ix e -> IO (UArray ix e)
freezeIOUArray (IOUArray marr) = stToIO (freezeSTUArray marr)
{-# RULES
"freeze/IOArray" freeze = freezeIOArray
"freeze/IOUArray" freeze = freezeIOUArray
#-}
{-# INLINE unsafeFreezeIOArray #-}
unsafeFreezeIOArray :: Ix ix => IOArray ix e -> IO (Array ix e)
unsafeFreezeIOArray (IOArray marr) = stToIO (unsafeFreezeSTArray marr)
{-# INLINE unsafeFreezeIOUArray #-}
unsafeFreezeIOUArray :: Ix ix => IOUArray ix e -> IO (UArray ix e)
unsafeFreezeIOUArray (IOUArray marr) = stToIO (unsafeFreezeSTUArray marr)
{-# RULES
"unsafeFreeze/IOArray" unsafeFreeze = unsafeFreezeIOArray
"unsafeFreeze/IOUArray" unsafeFreeze = unsafeFreezeIOUArray
#-}
-----------------------------------------------------------------------------
-- Thawing
thawIOArray :: Ix ix => Array ix e -> IO (IOArray ix e)
thawIOArray arr = stToIO $ do
marr <- thawSTArray arr
return (IOArray marr)
thawIOUArray :: Ix ix => UArray ix e -> IO (IOUArray ix e)
thawIOUArray arr = stToIO $ do
marr <- thawSTUArray arr
return (IOUArray marr)
{-# RULES
"thaw/IOArray" thaw = thawIOArray
"thaw/IOUArray" thaw = thawIOUArray
#-}
{-# INLINE unsafeThawIOArray #-}
unsafeThawIOArray :: Ix ix => Array ix e -> IO (IOArray ix e)
unsafeThawIOArray arr = stToIO $ do
marr <- unsafeThawSTArray arr
return (IOArray marr)
{-# INLINE unsafeThawIOUArray #-}
unsafeThawIOUArray :: Ix ix => UArray ix e -> IO (IOUArray ix e)
unsafeThawIOUArray arr = stToIO $ do
marr <- unsafeThawSTUArray arr
return (IOUArray marr)
{-# RULES
"unsafeThaw/IOArray" unsafeThaw = unsafeThawIOArray
"unsafeThaw/IOUArray" unsafeThaw = unsafeThawIOUArray
#-}
-- ---------------------------------------------------------------------------
-- hGetArray
-- | Reads a number of 'Word8's from the specified 'Handle' directly
-- into an array.
hGetArray
:: Handle -- ^ Handle to read from
-> IOUArray Int Word8 -- ^ Array in which to place the values
-> Int -- ^ Number of 'Word8's to read
-> IO Int
-- ^ Returns: the number of 'Word8's actually
-- read, which might be smaller than the number requested
-- if the end of file was reached.
hGetArray handle (IOUArray (STUArray l u ptr)) count
| count == 0
= return 0
| count < 0 || count > rangeSize (l,u)
= illegalBufferSize handle "hGetArray" count
| otherwise = do
wantReadableHandle "hGetArray" handle $
\ handle_@Handle__{ haFD=fd, haBuffer=ref, haIsStream=is_stream } -> do
buf@Buffer{ bufBuf=raw, bufWPtr=w, bufRPtr=r } <- readIORef ref
if bufferEmpty buf
then readChunk fd is_stream ptr 0 count
else do
let avail = w - r
copied <- if (count >= avail)
then do
memcpy_ba_baoff ptr raw r (fromIntegral avail)
writeIORef ref buf{ bufWPtr=0, bufRPtr=0 }
return avail
else do
memcpy_ba_baoff ptr raw r (fromIntegral count)
writeIORef ref buf{ bufRPtr = r + count }
return count
let remaining = count - copied
if remaining > 0
then do rest <- readChunk fd is_stream ptr copied remaining
return (rest + copied)
else return count
readChunk :: FD -> Bool -> RawBuffer -> Int -> Int -> IO Int
readChunk fd is_stream ptr init_off bytes = loop init_off bytes
where
loop :: Int -> Int -> IO Int
loop off bytes | bytes <= 0 = return (off - init_off)
loop off bytes = do
r' <- readRawBuffer "readChunk" (fromIntegral fd) is_stream ptr
(fromIntegral off) (fromIntegral bytes)
let r = fromIntegral r'
if r == 0
then return (off - init_off)
else loop (off + r) (bytes - r)
-- ---------------------------------------------------------------------------
-- hPutArray
-- | Writes an array of 'Word8' to the specified 'Handle'.
hPutArray
:: Handle -- ^ Handle to write to
-> IOUArray Int Word8 -- ^ Array to write from
-> Int -- ^ Number of 'Word8's to write
-> IO ()
hPutArray handle (IOUArray (STUArray l u raw)) count
| count == 0
= return ()
| count < 0 || count > rangeSize (l,u)
= illegalBufferSize handle "hPutArray" count
| otherwise
= do wantWritableHandle "hPutArray" handle $
\ handle_@Handle__{ haFD=fd, haBuffer=ref, haIsStream=stream } -> do
old_buf@Buffer{ bufBuf=old_raw, bufRPtr=r, bufWPtr=w, bufSize=size }
<- readIORef ref
-- enough room in handle buffer?
if (size - w > count)
-- There's enough room in the buffer:
-- just copy the data in and update bufWPtr.
then do memcpy_baoff_ba old_raw w raw (fromIntegral count)
writeIORef ref old_buf{ bufWPtr = w + count }
return ()
-- else, we have to flush
else do flushed_buf <- flushWriteBuffer fd stream old_buf
writeIORef ref flushed_buf
let this_buf =
Buffer{ bufBuf=raw, bufState=WriteBuffer,
bufRPtr=0, bufWPtr=count, bufSize=count }
flushWriteBuffer fd stream this_buf
return ()
-- ---------------------------------------------------------------------------
-- Internal Utils
foreign import ccall unsafe "__hscore_memcpy_dst_off"
memcpy_baoff_ba :: RawBuffer -> Int -> RawBuffer -> CSize -> IO (Ptr ())
foreign import ccall unsafe "__hscore_memcpy_src_off"
memcpy_ba_baoff :: RawBuffer -> RawBuffer -> Int -> CSize -> IO (Ptr ())
illegalBufferSize :: Handle -> String -> Int -> IO a
illegalBufferSize handle fn sz =
ioException (IOError (Just handle)
InvalidArgument fn
("illegal buffer size " ++ showsPrec 9 (sz::Int) [])
Nothing)
#else /* !__GLASGOW_HASKELL__ */
hGetArray :: Handle -> IOUArray Int Word8 -> Int -> IO Int
hGetArray handle arr count
| count < 0 || count > rangeSize (bounds arr)
= illegalBufferSize handle "hGetArray" count
| otherwise = get 0
where
get i | i == count = return i
| otherwise = do
error_or_c <- try (hGetChar handle)
case error_or_c of
Left ex
| isEOFError ex -> return i
| otherwise -> ioError ex
Right c -> do
unsafeWrite arr i (fromIntegral (ord c))
get (i+1)
hPutArray :: Handle -> IOUArray Int Word8 -> Int -> IO ()
hPutArray handle arr count
| count < 0 || count > rangeSize (bounds arr)
= illegalBufferSize handle "hPutArray" count
| otherwise = put 0
where
put i | i == count = return ()
| otherwise = do
w <- unsafeRead arr i
hPutChar handle (chr (fromIntegral w))
put (i+1)
illegalBufferSize :: Handle -> String -> Int -> IO a
illegalBufferSize _ fn sz = ioError $
userError (fn ++ ": illegal buffer size " ++ showsPrec 9 (sz::Int) [])
#endif /* !__GLASGOW_HASKELL__ */
|