packages feed

biohazard-2.1: Bio/Util/MMap.hsc

module Bio.Util.MMap ( mmapFile, createMmapFile ) where

import BasePrelude
import Foreign.C.Error      ( getErrno, errnoToIOError )
import Foreign.C.Types
import System.Posix.Files   ( fileSize, getFdStatus, setFdSize )
import System.Posix.IO      ( openFd, closeFd, defaultFileFlags, OpenMode(ReadOnly,ReadWrite) )
import System.Posix.Types   ( Fd(..), COff(..) )

#include <sys/mman.h>

-- | Maps a whole file into memory, returns the size in bytes and a
-- 'ForeignPtr' to the contents.
mmapFile :: FilePath -> IO (Int, ForeignPtr a)
mmapFile fp =
    bracket (openFd fp ReadOnly Nothing defaultFileFlags) closeFd $ \fd -> do
        size <- fileSize <$> getFdStatus fd
        if size <= 0
            then (,) 0 <$> newForeignPtr_ nullPtr
            else do
                ptr <- mmap nullPtr (fromIntegral size) (#const PROT_READ) (#const MAP_SHARED) fd 0
                if ptrToIntPtr ptr == #const MAP_FAILED
                    then do errno <- getErrno
                            ioError $ errnoToIOError "mmapFile" errno Nothing (Just fp)
                    else (,) (fromIntegral size) <$> newForeignPtrEnv my_munmap (intPtrToPtr $ fromIntegral size) ptr

-- | Creates a new file of a desired initial size, maps it into memory,
-- and calls a function to fill it.  That function returns a pointer to
-- the first unused byte in the file, and it is truncated accordingly.
createMmapFile :: FilePath -> CSize -> (Ptr a -> IO (Ptr a, b)) -> IO b
createMmapFile fp sz k =
    bracket (openFd fp ReadWrite (Just 0x1b6) defaultFileFlags) closeFd $ \fd -> do
        setFdSize fd (fromIntegral sz)
        bracket (mmap nullPtr sz (#const PROT_READ | PROT_WRITE) (#const MAP_SHARED) fd 0)
                (flip munmap sz) $ \p -> do
            (p',r) <- k p
            setFdSize fd (fromIntegral $ minusPtr p' p)
            return r

foreign import ccall unsafe "&my_munmap"        my_munmap :: FunPtr (Ptr () -> Ptr a -> IO ())
foreign import ccall unsafe "sys/mman.h mmap"   mmap      :: Ptr a -> CSize -> CInt -> CInt -> Fd -> COff -> IO (Ptr a)
foreign import ccall unsafe "sys/mman.h munmap" munmap    :: Ptr a -> CSize -> IO ()