-- |
-- 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
    | forall a b. (Integral a, Num b) => a -> b
Prelude.fromIntegral (forall a. Bounded a => a
maxBound :: Int) forall a. Ord a => a -> a -> Bool
< Word64
sz = forall a. HasCallStack => String -> a
error (String
"cannot map file in entirety as size overflow " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show Word64
sz)
    | Bool
otherwise                                   = 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
fileMapping <- FileMapReadF
I.fileMapRead FilePath
fp
    FinalPtr Word8
fptr <- FileMapping -> IO (FinalPtr Word8)
I.fileMappingToFinalPtr FileMapping
fileMapping
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall ty. PrimType ty => FinalPtr ty -> CountOf ty -> UArray ty
V.foreignMem FinalPtr Word8
fptr (forall ty. Int -> CountOf ty
CountOf forall a b. (a -> b) -> a -> b
$ FileMapping -> Int
getSize FileMapping
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
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (FileMapReadF
I.fileMapRead FilePath
fp) FileMapping -> IO ()
I.fileMappingUnmap forall a b. (a -> b) -> a -> b
$ \FileMapping
fm -> do
        FinalPtr Word8
fptr <- forall (prim :: * -> *) a.
PrimMonad prim =>
Ptr a -> (Ptr a -> IO ()) -> prim (FinalPtr a)
toFinalPtr (FileMapping -> Ptr Word8
I.fileMappingPtr FileMapping
fm) (\Ptr Word8
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return ())
        UArray Word8 -> IO a
f (forall ty. PrimType ty => FinalPtr ty -> CountOf ty -> UArray ty
V.foreignMem FinalPtr Word8
fptr (forall ty. Int -> CountOf ty
CountOf forall a b. (a -> b) -> a -> b
$ FileMapping -> Int
getSize FileMapping
fm))