{-# OPTIONS_GHC -#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 (uses Data.Array.MArray) -- -- 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 (fromIntegral r) (fromIntegral avail) writeIORef ref buf{ bufWPtr=0, bufRPtr=0 } return avail else do memcpy_ba_baoff ptr raw (fromIntegral 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 (fromIntegral 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 -> CInt -> RawBuffer -> CSize -> IO (Ptr ()) foreign import ccall unsafe "__hscore_memcpy_src_off" memcpy_ba_baoff :: RawBuffer -> RawBuffer -> CInt -> 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 = do bds <- getBounds arr if count < 0 || count > rangeSize bds then illegalBufferSize handle "hGetArray" count else 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 = do bds <- getBounds arr if count < 0 || count > rangeSize bds then illegalBufferSize handle "hPutArray" count else 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__ */