-- |
-- 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
fileMapping <- FileMapReadF
I.fileMapRead FilePath
fp
    FinalPtr Word8
fptr <- FileMapping -> IO (FinalPtr Word8)
I.fileMappingToFinalPtr FileMapping
fileMapping
    UArray Word8 -> IO (UArray Word8)
forall (m :: * -> *) a. Monad m => a -> m a
return (UArray Word8 -> IO (UArray Word8))
-> UArray Word8 -> IO (UArray Word8)
forall a b. (a -> b) -> a -> b
$ FinalPtr Word8 -> CountOf Word8 -> UArray Word8
forall ty. PrimType ty => FinalPtr ty -> CountOf ty -> UArray ty
V.foreignMem FinalPtr Word8
fptr (Int -> CountOf Word8
forall ty. Int -> CountOf ty
CountOf (Int -> CountOf Word8) -> Int -> CountOf Word8
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 :: 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
        FinalPtr Word8
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 (m :: * -> *) a. Monad m => a -> m a
return ())
        UArray Word8 -> IO a
f (FinalPtr Word8 -> CountOf Word8 -> UArray Word8
forall ty. PrimType ty => FinalPtr ty -> CountOf ty -> UArray ty
V.foreignMem FinalPtr Word8
fptr (Int -> CountOf Word8
forall ty. Int -> CountOf ty
CountOf (Int -> CountOf Word8) -> Int -> CountOf Word8
forall a b. (a -> b) -> a -> b
$ FileMapping -> Int
getSize FileMapping
fm))