-- | Simpler implementation of 'DiskBytes' using 'mkWeakIORef'.
-- It looks like this implementation is not significantly worse than the
-- one trying to use GHC-specific primitives.
module System.Mem.Disk.BytesPlain
    ( DiskBytes
    , toDiskBytes
    , fromDiskBytes
    ) where

import Data.ByteString
    ( ByteString )
import Data.Int
    ( Int64 )
import Data.IORef
    ( IORef, newIORef, readIORef, mkWeakIORef )
import System.IO.Unsafe
    ( unsafePerformIO )
import System.Mem.Disk.DiskApi
    ( Disk (..) )

{-----------------------------------------------------------------------------
    DiskBytes
------------------------------------------------------------------------------}
-- | A sequence of bytes that is stored on disk
-- -- if and only if the value is evaluated to WHNF.
--
-- The value is subject to normal garbage collection:
-- When the value is no longer referenced,
-- the disk memory will be freed (eventually).
newtype DiskBytes = DiskBytes { DiskBytes -> IORef DiskBytesData
unDiskBytes :: IORef DiskBytesData }

data DiskBytesData = DiskBytesData
    { DiskBytesData -> Int64
index :: !Int64
    , DiskBytesData -> Disk
disk  :: Disk
    }

-- | Offload a sequence of bytes onto a 'Disk'.
-- 
-- NOTE: The result must be evaluated to WHNF before the data actually
-- on disk! Also keep in mind that the original 'ByteString' needs
-- to be garbage collected.
toDiskBytes :: Disk -> ByteString -> DiskBytes
toDiskBytes :: Disk -> ByteString -> DiskBytes
toDiskBytes Disk
disk = forall a. IO a -> a
unsafePerformIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Disk -> ByteString -> IO DiskBytes
mkDiskBytes Disk
disk

mkDiskBytes :: Disk -> ByteString -> IO DiskBytes
mkDiskBytes :: Disk -> ByteString -> IO DiskBytes
mkDiskBytes Disk
disk ByteString
bytes = do
    Int64
index <- Disk -> ByteString -> IO Int64
put Disk
disk ByteString
bytes
    IORef DiskBytesData
ptr <- forall a. a -> IO (IORef a)
newIORef DiskBytesData{Int64
index :: Int64
index :: Int64
index, Disk
disk :: Disk
disk :: Disk
disk}
    Weak (IORef DiskBytesData)
_ <- forall a. IORef a -> IO () -> IO (Weak (IORef a))
mkWeakIORef IORef DiskBytesData
ptr (IORef DiskBytesData -> IO ()
finalizer IORef DiskBytesData
ptr)
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ IORef DiskBytesData -> DiskBytes
DiskBytes IORef DiskBytesData
ptr

finalizer :: IORef DiskBytesData -> IO ()
finalizer :: IORef DiskBytesData -> IO ()
finalizer IORef DiskBytesData
ptr = do
    DiskBytesData{Int64
index :: Int64
index :: DiskBytesData -> Int64
index,Disk
disk :: Disk
disk :: DiskBytesData -> Disk
disk} <- forall a. IORef a -> IO a
readIORef IORef DiskBytesData
ptr
    Disk -> Int64 -> IO ()
delete Disk
disk Int64
index

-- | Read the sequence of bytes back into RAM.
fromDiskBytes :: DiskBytes -> ByteString
fromDiskBytes :: DiskBytes -> ByteString
fromDiskBytes = forall a. IO a -> a
unsafePerformIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskBytes -> IO ByteString
unpackM

unpackM :: DiskBytes -> IO ByteString
unpackM :: DiskBytes -> IO ByteString
unpackM (DiskBytes IORef DiskBytesData
ptr) = do
    DiskBytesData{Int64
index :: Int64
index :: DiskBytesData -> Int64
index,Disk
disk :: Disk
disk :: DiskBytesData -> Disk
disk} <- forall a. IORef a -> IO a
readIORef IORef DiskBytesData
ptr
    Disk -> Int64 -> IO ByteString
get Disk
disk Int64
index