--- /dev/null
+--------------------------------------------------------------------------------
+-- |
+-- Module : Foreign.Marshal.Pool
+-- Copyright : (c) Sven Panne 2003
+-- License : BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer : sven_panne@yahoo.com
+-- Stability : provisional
+-- Portability : portable
+--
+-- This module contains support for pooled memory management. Under this scheme,
+-- (re-)allocations belong to a given pool, and everything in a pool is
+-- deallocated when the pool itself is deallocated. This is useful when
+-- 'Foreign.Marshal.Alloc.alloca' with its implicit allocation and deallocation
+-- is not flexible enough, but explicit uses of 'Foreign.Marshal.Alloc.malloc'
+-- and 'free' are too awkward.
+--
+--------------------------------------------------------------------------------
+
+module Foreign.Marshal.Pool (
+ -- * Pool management
+ Pool,
+ newPool, -- :: IO Pool
+ freePool, -- :: Pool -> IO ()
+ withPool, -- :: (Pool -> IO b) -> IO b
+
+ -- * (Re-)Allocation within a pool
+ pooledMalloc, -- :: Storable a => Pool -> IO (Ptr a)
+ pooledMallocBytes, -- :: Pool -> Int -> IO (Ptr a)
+
+ pooledRealloc, -- :: Storable a => Pool -> Ptr a -> IO (Ptr a)
+ pooledReallocBytes, -- :: Pool -> Ptr a -> Int -> IO (Ptr a)
+
+ pooledMallocArray, -- :: Storable a => Pool -> Int -> IO (Ptr a)
+ pooledMallocArray0, -- :: Storable a => Pool -> Int -> IO (Ptr a)
+
+ pooledReallocArray, -- :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
+ pooledReallocArray0, -- :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
+
+ -- * Combined allocation and marshalling
+ pooledNew, -- :: Storable a => Pool -> a -> IO (Ptr a)
+ pooledNewArray, -- :: Storable a => Pool -> [a] -> IO (Ptr a)
+ pooledNewArray0 -- :: Storable a => Pool -> a -> [a] -> IO (Ptr a)
+) where
+
+#if defined(__NHC__)
+import IO ( bracket )
+#else
+import Control.Exception ( bracket )
+#endif
+import Control.Monad ( liftM )
+import Data.IORef ( IORef, newIORef, readIORef, modifyIORef )
+import Data.List ( delete )
+import Foreign.Marshal.Alloc ( mallocBytes, reallocBytes, free )
+import Foreign.Marshal.Array ( pokeArray, pokeArray0 )
+import Foreign.Marshal.Error ( throwIf )
+import Foreign.Ptr ( Ptr, castPtr )
+import Foreign.Storable ( Storable(sizeOf, poke) )
+
+--------------------------------------------------------------------------------
+
+-- To avoid non-H98 stuff like existentially quantified data constructors, we
+-- simply use pointers to () below. Not very nice, but...
+
+-- | A memory pool.
+
+newtype Pool = Pool (IORef [Ptr ()])
+
+-- | Allocate a fresh memory pool.
+
+newPool :: IO Pool
+newPool = liftM Pool $ newIORef []
+
+-- | Deallocate a memory pool and everything which has been allocated in the
+-- pool itself.
+
+freePool :: Pool -> IO ()
+freePool (Pool ps) = readIORef ps >>= mapM_ free
+
+-- | Execute an action with a fresh memory pool, which gets automatically
+-- deallocated (including its contents) after the action has finished.
+
+withPool :: (Pool -> IO b) -> IO b
+withPool = bracket newPool freePool
+
+--------------------------------------------------------------------------------
+
+-- | Allocate space for storable type in the given pool. The size of the area
+-- allocated is determined by the 'sizeOf' method from the instance of
+-- 'Storable' for the appropriate type.
+
+pooledMalloc :: Storable a => Pool -> IO (Ptr a)
+pooledMalloc = pm undefined
+ where
+ pm :: Storable a => a -> Pool -> IO (Ptr a)
+ pm dummy pool = pooledMallocBytes pool (sizeOf dummy)
+
+-- | Allocate the given number of bytes of storage in the pool.
+
+pooledMallocBytes :: Pool -> Int -> IO (Ptr a)
+pooledMallocBytes (Pool pool) size = do
+ ptr <- mallocBytes size
+ modifyIORef pool (ptr:)
+ return (castPtr ptr)
+
+-- | Adjust the storage area for an element in the pool to the given size of
+-- the required type.
+
+pooledRealloc :: Storable a => Pool -> Ptr a -> IO (Ptr a)
+pooledRealloc = pr undefined
+ where
+ pr :: Storable a => a -> Pool -> Ptr a -> IO (Ptr a)
+ pr dummy pool ptr = pooledReallocBytes pool ptr (sizeOf dummy)
+
+-- | Adjust the storage area for an element in the pool to the given size.
+
+pooledReallocBytes :: Pool -> Ptr a -> Int -> IO (Ptr a)
+pooledReallocBytes (Pool pool) ptr size = do
+ let cPtr = castPtr ptr
+ throwIf (not . (cPtr `elem`)) (const "pointer not in pool") (readIORef pool)
+ newPtr <- reallocBytes cPtr size
+ modifyIORef pool ((newPtr :) . delete cPtr)
+ return (castPtr newPtr)
+
+-- | Allocate storage for the given number of elements of a storable type in the
+-- pool.
+
+pooledMallocArray :: Storable a => Pool -> Int -> IO (Ptr a)
+pooledMallocArray = pma undefined
+ where
+ pma :: Storable a => a -> Pool -> Int -> IO (Ptr a)
+ pma dummy pool size = pooledMallocBytes pool (size * sizeOf dummy)
+
+-- | Allocate storage for the given number of elements of a storable type in the
+-- pool, but leave room for an extra element to signal the end of the array.
+
+pooledMallocArray0 :: Storable a => Pool -> Int -> IO (Ptr a)
+pooledMallocArray0 pool size =
+ pooledMallocArray pool (size + 1)
+
+-- | Adjust the size of an array in the given pool.
+
+pooledReallocArray :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
+pooledReallocArray = pra undefined
+ where
+ pra :: Storable a => a -> Pool -> Ptr a -> Int -> IO (Ptr a)
+ pra dummy pool ptr size = pooledReallocBytes pool ptr (size * sizeOf dummy)
+
+-- | Adjust the size of an array with an end marker in the given pool.
+
+pooledReallocArray0 :: Storable a => Pool -> Ptr a -> Int -> IO (Ptr a)
+pooledReallocArray0 pool ptr size =
+ pooledReallocArray pool ptr (size + 1)
+
+--------------------------------------------------------------------------------
+
+-- | Allocate storage for a value in the given pool and marshal the value into
+-- this storage.
+
+pooledNew :: Storable a => Pool -> a -> IO (Ptr a)
+pooledNew pool val = do
+ ptr <- pooledMalloc pool
+ poke ptr val
+ return ptr
+
+-- | Allocate consecutive storage for a list of values in the given pool and
+-- marshal these values into it.
+
+pooledNewArray :: Storable a => Pool -> [a] -> IO (Ptr a)
+pooledNewArray pool vals = do
+ ptr <- pooledMallocArray pool (length vals)
+ pokeArray ptr vals
+ return ptr
+
+-- | Allocate consecutive storage for a list of values in the given pool and
+-- marshal these values into it, terminating the end with the given marker.
+
+pooledNewArray0 :: Storable a => Pool -> a -> [a] -> IO (Ptr a)
+pooledNewArray0 pool marker vals = do
+ ptr <- pooledMallocArray0 pool (length vals)
+ pokeArray0 marker ptr vals
+ return ptr