module Data.Packed.Vector.MMap (
unsafeMMapVector,
unsafeLazyMMapVectors,
hPutVector,
writeVector
) where
import Control.Monad (when)
import System.IO
import System.IO.MMap
import System.IO.Unsafe
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
import qualified Data.Packed.Development as I
import qualified Data.Packed.Vector as I
import Data.Int
unsafeMMapVector :: forall a. Storable a => FilePath
-> Maybe (Int64, Int)
-> IO (I.Vector a)
unsafeMMapVector path range =
do (foreignPtr, offset, size) <- mmapFileForeignPtr path ReadOnly $
case range of
Nothing -> Nothing
Just (start, length) -> Just (start, length * sizeOf (undefined :: a))
return $ I.unsafeFromForeignPtr foreignPtr offset (size `div` sizeOf (undefined :: a))
unsafeLazyMMapVectors :: forall a. Storable a => FilePath
-> Maybe (Int64, Int64)
-> Int
-> IO (Int64,[I.Vector a])
unsafeLazyMMapVectors path range vsize = do
when (vecSize > maxChunkSize) vecTooBigError
filesize <- withFile path ReadMode hFileSize
let filesize' :: Int64
filesize' = fI filesize
imgs <- unsafeInterleaveIO $ unsafeLazyMMapVectors' filesize' path range vsize
return (nimages range filesize', imgs)
where
nimages :: Maybe (Int64, Int64) -> Int64 -> Int64
nimages Nothing fsz = fsz `div` imageSize
nimages (Just (_,sz)) _ = sz `div` imageSize
imageSize = fI vsize * eltSize
eltSize = fI (sizeOf (undefined :: a))
vecSize = fI vsize * eltSize
vecTooBigError = fail "The requested vector size can't be mapped into memory"
unsafeLazyMMapVectors' :: forall a. Storable a => Int64
-> FilePath
-> Maybe (Int64, Int64)
-> Int
-> IO [I.Vector a]
unsafeLazyMMapVectors' fileSize
fileName
fileRange
numEltsPerVec
| mapSize < maxChunkSize = mmapAll
| otherwise = mmapChunks 0
where
mapSize, eltSize, vecSize, chunkSize, baseOffset :: Int64
eltSize = fI $ sizeOf (undefined :: a)
(baseOffset,mapSize) = case fileRange of
Just (off,nelts) -> (off,nelts*eltSize)
_ -> (0,fileSize)
vecSize = fI numEltsPerVec * eltSize
chunkSize = (maxChunkSize `div` vecSize) * vecSize
fileRange' = do
(offset, nelts) <- fileRange
return (offset, fI nelts)
splitVecs :: I.Vector a -> [I.Vector a]
splitVecs bigVec = let nvecs = I.dim bigVec `div` numEltsPerVec
in I.takesV (replicate nvecs numEltsPerVec) bigVec
mmapAll :: IO [I.Vector a]
mmapAll = do
allVecs <- unsafeMMapVector fileName fileRange'
return $ splitVecs allVecs
mmapChunks :: Int64 -> IO [I.Vector a]
mmapChunks offs | remaining <= 0 = return []
| otherwise = do
chunk <- unsafeMMapVector fileName mmapRange
rest <- unsafeInterleaveIO $ mmapChunks (offs+chunkSize')
return $ splitVecs chunk ++ rest
where
mmapRange = Just (baseOffset+offs,fI (chunkSize' `div` eltSize))
remaining = mapSizeoffs
chunkSize' = min chunkSize remaining
maxChunkSize :: Int64
maxChunkSize = fI (maxBound `div` 256 :: Int)
fI :: (Integral a, Num b) => a -> b
fI = fromIntegral
hPutVector :: forall a. Storable a => Handle -> I.Vector a -> IO ()
hPutVector h v = withForeignPtr fp $ \p -> hPutBuf h (p `plusPtr` offset) sz
where
(fp, offset, n) = I.unsafeToForeignPtr v
eltsize = sizeOf (undefined :: a)
sz = n * eltsize
writeVector :: forall a. Storable a => FilePath -> I.Vector a -> IO ()
writeVector fp v = withFile fp WriteMode $ \h -> hPutVector h v