-- |
-- 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           Foundation.Primitive.Types.OffsetSize
import           Foundation.Primitive.Imports
import           Foundation.VFS (FilePath)
import           Foundation.Primitive.FinalPtr
import qualified Foundation.Array.Unboxed as V
import qualified Foundation.Foreign.MemoryMap as I
import qualified Prelude

getSize :: I.FileMapping -> Int
getSize fm
    | Prelude.fromIntegral (maxBound :: Int) < sz = error ("cannot map file in entirety as size overflow " <> show sz)
    | otherwise                                   = Prelude.fromIntegral sz
  where
    (FileSize sz) = I.fileMappingSize 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 fp = do
    fileMapping <- I.fileMapRead 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 fp f = do
    bracket (I.fileMapRead fp) I.fileMappingUnmap $ \fm -> do
        fptr <- toFinalPtr (I.fileMappingPtr fm) (\_ -> return ())
        f (V.foreignMem fptr (CountOf $ getSize fm))