    -- |
    -- Module      : Foundation.IO.FileMap
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : portable
    --
    -- Note that the memory mapping is handled by the system, not at the haskell level.
    -- The system can modify the content of the memory as any moment under your feet.
    --
    -- It also have the limitation of your system, no emulation or nice handling of all
    -- those corners cases is attempted here.
    --
    -- for example mapping a large file (> 4G), on a 32 bits system is likely to just
    -- fail or returns inconsistent result.
    --
    -- In doubt, use 'readFile' or other simple routine that brings
    -- the content of the file in IO.
    --
    {-# LANGUAGE OverloadedStrings #-}
    module Foundation.IO.FileMap
        ( fileMapRead
        , fileMapReadWith
        ) where

    import           Control.Exception
    import           Basement.Types.OffsetSize
    import           Basement.Imports
    import           Foundation.VFS (FilePath)
    import           Basement.FinalPtr
    import qualified Basement.UArray as V
    import qualified Foundation.Foreign.MemoryMap as I
    import qualified Prelude

    getSize :: I.FileMapping -> Int
    getSize :: FileMapping -> Int
    getSize FileMapping
    fm
        | Int -> Word64
    forall a b. (Integral a, Num b) => a -> b
    Prelude.fromIntegral (Int
    forall a. Bounded a => a
    maxBound :: Int) Word64 -> Word64 -> Bool
    forall a. Ord a => a -> a -> Bool
    < Word64
    sz = String -> Int
    forall a. HasCallStack => String -> a
    error (String
    "cannot map file in entirety as size overflow " String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Word64 -> String
    forall a. Show a => a -> String
    show Word64
    sz)
        | Bool
    otherwise                                   = Word64 -> Int
    forall a b. (Integral a, Num b) => a -> b
    Prelude.fromIntegral Word64
    sz
      where
        (FileSize Word64
    sz) = FileMapping -> FileSize
    I.fileMappingSize FileMapping
    fm

    -- | Map in memory the whole content of a file.
    --
    -- Once the array goes out of scope, the memory get (eventually) unmap
    fileMapRead :: FilePath -> IO (V.UArray Word8)
    fileMapRead :: FilePath -> IO (UArray Word8)
    fileMapRead FilePath
    fp = do
        fileMapping <- FileMapReadF
    I.fileMapRead FilePath
    fp
        fptr <- I.fileMappingToFinalPtr fileMapping
        return $ V.foreignMem fptr (CountOf $ getSize fileMapping)

    -- | Map in memory the whole content of a file,

    -- the whole map is unmapped at the end of function after the function has been called
    -- so any things that is still holding on to this memory will very likely trigger segfault
    -- or other really bad behavior.
    fileMapReadWith :: FilePath -> (V.UArray Word8 -> IO a) -> IO a
    fileMapReadWith :: forall a. FilePath -> (UArray Word8 -> IO a) -> IO a
    fileMapReadWith FilePath
    fp UArray Word8 -> IO a
    f = do
        IO FileMapping
    -> (FileMapping -> IO ()) -> (FileMapping -> IO a) -> IO a
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
    bracket (FileMapReadF
    I.fileMapRead FilePath
    fp) FileMapping -> IO ()
    I.fileMappingUnmap ((FileMapping -> IO a) -> IO a) -> (FileMapping -> IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ \FileMapping
    fm -> do
            fptr <- Ptr Word8 -> (Ptr Word8 -> IO ()) -> IO (FinalPtr Word8)
    forall (prim :: * -> *) a.
    PrimMonad prim =>
    Ptr a -> (Ptr a -> IO ()) -> prim (FinalPtr a)
    toFinalPtr (FileMapping -> Ptr Word8
    I.fileMappingPtr FileMapping
    fm) (\Ptr Word8
    _ -> () -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ())
            f (V.foreignMem fptr (CountOf $ getSize fm))