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 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
|
-----------------------------------------------------------------------------
-- |
-- Module : Data.Array.Diff
-- 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
--
-- Functional arrays with constant-time update.
--
-----------------------------------------------------------------------------
module Data.Array.Diff (
-- * Diff array types
-- | Diff arrays have an immutable interface, but rely on internal
-- updates in place to provide fast functional update operator
-- '//'.
--
-- When the '//' operator is applied to a diff array, its contents
-- are physically updated in place. The old array silently changes
-- its representation without changing the visible behavior:
-- it stores a link to the new current array along with the
-- difference to be applied to get the old contents.
--
-- So if a diff array is used in a single-threaded style,
-- i.e. after '//' application the old version is no longer used,
-- @a'!'i@ takes O(1) time and @a '//' d@ takes O(@length d@).
-- Accessing elements of older versions gradually becomes slower.
--
-- Updating an array which is not current makes a physical copy.
-- The resulting array is unlinked from the old family. So you
-- can obtain a version which is guaranteed to be current and
-- thus have fast element access by @a '//' []@.
-- Possible improvement for the future (not implemented now):
-- make it possible to say "I will make an update now, but when
-- I later return to the old version, I want it to mutate back
-- instead of being copied".
IOToDiffArray, -- data IOToDiffArray
-- (a :: * -> * -> *) -- internal mutable array
-- (i :: *) -- indices
-- (e :: *) -- elements
-- | Type synonyms for the two most important IO array types.
-- Two most important diff array types are fully polymorphic
-- lazy boxed DiffArray:
DiffArray, -- = IOToDiffArray IOArray
-- ...and strict unboxed DiffUArray, working only for elements
-- of primitive types but more compact and usually faster:
DiffUArray, -- = IOToDiffArray IOUArray
-- * Overloaded immutable array interface
-- | Module "Data.Array.IArray" provides the interface of diff arrays.
-- They are instances of class 'IArray'.
module Data.Array.IArray,
-- * Low-level interface
-- | These are really internal functions, but you will need them
-- to make further 'IArray' instances of various diff array types
-- (for either more 'MArray' types or more unboxed element types).
newDiffArray, readDiffArray, replaceDiffArray
)
where
------------------------------------------------------------------------
-- Imports.
import Prelude
import Data.Ix
import Data.Array.Base
import Data.Array.IArray
import Data.Array.IO
import Foreign.Ptr ( Ptr, FunPtr )
import Foreign.StablePtr ( StablePtr )
import Data.Int ( Int8, Int16, Int32, Int64 )
#ifdef __GLASGOW_HASKELL__
import Data.Word ( Word )
#endif
import Data.Word ( Word8, Word16, Word32, Word64 )
import System.IO.Unsafe ( unsafePerformIO )
import Control.Concurrent.MVar ( MVar, newMVar, takeMVar, putMVar, readMVar )
------------------------------------------------------------------------
-- Diff array types.
-- | An arbitrary 'MArray' type living in the 'IO' monad can be converted
-- to a diff array.
newtype IOToDiffArray a i e =
DiffArray {varDiffArray :: MVar (DiffArrayData a i e)}
-- Internal representation: either a mutable array, or a link to
-- another diff array patched with a list of index+element pairs.
data DiffArrayData a i e = Current (a i e)
| Diff (IOToDiffArray a i e) [(Int, e)]
-- | Fully polymorphic lazy boxed diff array.
type DiffArray = IOToDiffArray IOArray
-- | Strict unboxed diff array, working only for elements
-- of primitive types but more compact and usually faster than 'DiffArray'.
type DiffUArray = IOToDiffArray IOUArray
-- Having 'MArray a e IO' in instance context would require
-- -fallow-undecidable-instances, so each instance is separate here.
------------------------------------------------------------------------
-- Showing DiffArrays
instance (Ix ix, Show ix, Show e) => Show (DiffArray ix e) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Char) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Int) where
showsPrec = showsIArray
#ifdef __GLASGOW_HASKELL__
instance (Ix ix, Show ix) => Show (DiffUArray ix Word) where
showsPrec = showsIArray
#endif
instance (Ix ix, Show ix) => Show (DiffUArray ix Float) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Double) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Int8) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Int16) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Int32) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Int64) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Word8) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Word16) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Word32) where
showsPrec = showsIArray
instance (Ix ix, Show ix) => Show (DiffUArray ix Word64) where
showsPrec = showsIArray
------------------------------------------------------------------------
-- Boring instances.
instance HasBounds a => HasBounds (IOToDiffArray a) where
bounds a = unsafePerformIO $ boundsDiffArray a
instance IArray (IOToDiffArray IOArray) e where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Char where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Int where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
#ifdef __GLASGOW_HASKELL__
instance IArray (IOToDiffArray IOUArray) Word where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
#endif
instance IArray (IOToDiffArray IOUArray) (Ptr a) where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) (FunPtr a) where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Float where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Double where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) (StablePtr a) where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Int8 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Int16 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Int32 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Int64 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Word8 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Word16 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Word32 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
instance IArray (IOToDiffArray IOUArray) Word64 where
unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray` ies
------------------------------------------------------------------------
-- The important stuff.
newDiffArray :: (MArray a e IO, Ix i)
=> (i,i)
-> [(Int, e)]
-> IO (IOToDiffArray a i e)
newDiffArray (l,u) ies = do
a <- newArray_ (l,u)
sequence_ [unsafeWrite a i e | (i, e) <- ies]
var <- newMVar (Current a)
return (DiffArray var)
readDiffArray :: (MArray a e IO, Ix i)
=> IOToDiffArray a i e
-> Int
-> IO e
a `readDiffArray` i = do
d <- readMVar (varDiffArray a)
case d of
Current a' -> unsafeRead a' i
Diff a' ies -> maybe (readDiffArray a' i) return (lookup i ies)
replaceDiffArray :: (MArray a e IO, Ix i)
=> IOToDiffArray a i e
-> [(Int, e)]
-> IO (IOToDiffArray a i e)
a `replaceDiffArray` ies = do
d <- takeMVar (varDiffArray a)
case d of
Current a' -> case ies of
[] -> do
-- We don't do the copy when there is nothing to change
-- and this is the current version. But see below.
putMVar (varDiffArray a) d
return a
_:_ -> do
diff <- sequence [do e <- unsafeRead a' i; return (i, e)
| (i, _) <- ies]
sequence_ [unsafeWrite a' i e | (i, e) <- ies]
var' <- newMVar (Current a')
putMVar (varDiffArray a) (Diff (DiffArray var') diff)
return (DiffArray var')
Diff _ _ -> do
-- We still do the copy when there is nothing to change
-- but this is not the current version. So you can use
-- 'a // []' to make sure that the resulting array has
-- fast element access.
putMVar (varDiffArray a) d
a' <- thawDiffArray a
-- thawDiffArray gives a fresh array which we can
-- safely mutate.
sequence_ [unsafeWrite a' i e | (i, e) <- ies]
var' <- newMVar (Current a')
return (DiffArray var')
boundsDiffArray :: (HasBounds a, Ix ix)
=> IOToDiffArray a ix e
-> IO (ix,ix)
boundsDiffArray a = do
d <- readMVar (varDiffArray a)
case d of
Current a' -> return (bounds a')
Diff a' _ -> boundsDiffArray a'
freezeDiffArray :: (MArray a e IO, Ix ix)
=> a ix e
-> IO (IOToDiffArray a ix e)
freezeDiffArray a = case bounds a of
(l,u) -> do
a' <- newArray_ (l,u)
sequence_ [unsafeRead a i >>= unsafeWrite a' i | i <- [0 .. rangeSize (l,u) - 1]]
var <- newMVar (Current a')
return (DiffArray var)
{-# RULES
"freeze/DiffArray" freeze = freezeDiffArray
#-}
-- unsafeFreezeDiffArray is really unsafe. Better don't use the old
-- array at all after freezing. The contents of the source array will
-- be changed when '//' is applied to the resulting array.
unsafeFreezeDiffArray :: (MArray a e IO, Ix ix)
=> a ix e
-> IO (IOToDiffArray a ix e)
unsafeFreezeDiffArray a = do
var <- newMVar (Current a)
return (DiffArray var)
{-# RULES
"unsafeFreeze/DiffArray" unsafeFreeze = unsafeFreezeDiffArray
#-}
thawDiffArray :: (MArray a e IO, Ix ix)
=> IOToDiffArray a ix e
-> IO (a ix e)
thawDiffArray a = do
d <- readMVar (varDiffArray a)
case d of
Current a' -> case bounds a' of
(l,u) -> do
a'' <- newArray_ (l,u)
sequence_ [unsafeRead a' i >>= unsafeWrite a'' i | i <- [0 .. rangeSize (l,u) - 1]]
return a''
Diff a' ies -> do
a'' <- thawDiffArray a'
sequence_ [unsafeWrite a'' i e | (i, e) <- ies]
return a''
{-# RULES
"thaw/DiffArray" thaw = thawDiffArray
#-}
-- unsafeThawDiffArray is really unsafe. Better don't use the old
-- array at all after thawing. The contents of the resulting array
-- will be changed when '//' is applied to the source array.
unsafeThawDiffArray :: (MArray a e IO, Ix ix)
=> IOToDiffArray a ix e
-> IO (a ix e)
unsafeThawDiffArray a = do
d <- readMVar (varDiffArray a)
case d of
Current a' -> return a'
Diff a' ies -> do
a'' <- unsafeThawDiffArray a'
sequence_ [unsafeWrite a'' i e | (i, e) <- ies]
return a''
{-# RULES
"unsafeThaw/DiffArray" unsafeThaw = unsafeThawDiffArray
#-}
|