module Database.VCache.VPutFini
( vputFini
, runVPutIO
, runVPut
) where
import Control.Exception (onException)
import Data.IORef
import Data.ByteString (ByteString)
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Marshal.Alloc
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.ByteString.Internal as BSI
import Database.VCache.Types
import Database.VCache.VPutAux
vputFini :: VPut ()
vputFini = do
szStart <- peekBufferSize
lChildren <- peekChildren
putChildren lChildren
szFini <- peekBufferSize
putVarNatR (szFini szStart)
putChildren :: [PutChild] -> VPut ()
putChildren [] = return ()
putChildren (x:xs) =
let addr0 = putChildAddr x in
putVarNat (fromIntegral addr0) >>
putChildren' addr0 xs
putChildren' :: Address -> [PutChild] -> VPut ()
putChildren' _ [] = return ()
putChildren' !prev (x:xs) =
let addrX = putChildAddr x in
let offset = (fromIntegral addrX) (fromIntegral prev) in
putVarInt offset >>
putChildren' addrX xs
runVPutIO :: VSpace -> VPut a -> IO (a, ByteString, [PutChild])
runVPutIO vs action = do
let initialSize = 1000
pBuff <- mallocBytes initialSize
vBuff <- newIORef pBuff
let s0 = VPutS { vput_space = vs
, vput_children = []
, vput_buffer = vBuff
, vput_target = pBuff
, vput_limit = pBuff `plusPtr` initialSize
}
let freeBuff = readIORef vBuff >>= free
let fullWrite = do { result <- action; vputFini; return result }
let runPut = _vput fullWrite s0
(VPutR r sf) <- runPut `onException` freeBuff
pBuff' <- readIORef vBuff
let len = vput_target sf `minusPtr` pBuff'
pBuffR <- reallocBytes pBuff len
fpBuff' <- newForeignPtr finalizerFree pBuffR
let bytes = BSI.fromForeignPtr fpBuff' 0 len
return (r, bytes, vput_children sf)
runVPut :: VSpace -> VPut a -> (a, ByteString, [PutChild])
runVPut vs action = unsafePerformIO (runVPutIO vs action)