module GHC.Compact.Serialized(
SerializedCompact(..),
withSerializedCompact,
importCompact,
importCompactByteStrings,
) where
import GHC.Prim
import GHC.Types
import GHC.Word (Word8)
import GHC.IO (unIO)
import GHC.Ptr (Ptr(..), plusPtr)
import Control.Concurrent
import qualified Data.ByteString as ByteString
import Data.ByteString.Internal(toForeignPtr)
import Data.IORef(newIORef, readIORef, writeIORef)
import Foreign.ForeignPtr(withForeignPtr)
import Foreign.Marshal.Utils(copyBytes)
import GHC.Compact
data SerializedCompact a = SerializedCompact
{ serializedCompactBlockList :: [(Ptr (), Word)]
, serializedCompactRoot :: Ptr ()
}
addrIsNull :: Addr# -> Bool
addrIsNull addr = isTrue# (nullAddr# `eqAddr#` addr)
compactGetFirstBlock :: Compact# -> IO (Ptr a, Word)
compactGetFirstBlock buffer =
IO (\s -> case compactGetFirstBlock# buffer s of
(# s', addr, size #) -> (# s', (Ptr addr, W# size) #) )
compactGetNextBlock :: Compact# -> Addr# -> IO (Ptr a, Word)
compactGetNextBlock buffer block =
IO (\s -> case compactGetNextBlock# buffer block s of
(# s', addr, size #) -> (# s', (Ptr addr, W# size) #) )
mkBlockList :: Compact# -> IO [(Ptr a, Word)]
mkBlockList buffer = compactGetFirstBlock buffer >>= go
where
go :: (Ptr a, Word) -> IO [(Ptr a, Word)]
go (Ptr block, _) | addrIsNull block = return []
go item@(Ptr block, _) = do
next <- compactGetNextBlock buffer block
rest <- go next
return $ item : rest
withSerializedCompact :: Compact a ->
(SerializedCompact a -> IO c) -> IO c
withSerializedCompact (Compact buffer root lock) func = withMVar lock $ \_ -> do
rootPtr <- IO (\s -> case anyToAddr# root s of
(# s', rootAddr #) -> (# s', Ptr rootAddr #) )
blockList <- mkBlockList buffer
let serialized = SerializedCompact blockList rootPtr
IO $ \s -> keepAlive# buffer s (unIO $ func serialized)
fixupPointers :: Addr# -> Addr# -> State# RealWorld ->
(# State# RealWorld, Maybe (Compact a) #)
fixupPointers firstBlock rootAddr s =
case compactFixupPointers# firstBlock rootAddr s of
(# s', buffer, adjustedRoot #) ->
if addrIsNull adjustedRoot then (# s', Nothing #)
else case addrToAny# adjustedRoot of
(# root #) -> case mkCompact buffer root s' of
(# s'', c #) -> (# s'', Just c #)
importCompact :: SerializedCompact a -> (Ptr b -> Word -> IO ()) ->
IO (Maybe (Compact a))
importCompact (SerializedCompact [] _) _ = return Nothing
importCompact (SerializedCompact blocks root) filler = do
let !((_, W# firstSize):otherBlocks) = blocks
let !(Ptr rootAddr) = root
IO $ \s0 ->
case compactAllocateBlock# firstSize nullAddr# s0 of {
(# s1, firstBlock #) ->
case fillBlock firstBlock firstSize s1 of { s2 ->
case go firstBlock otherBlocks s2 of { s3 ->
fixupPointers firstBlock rootAddr s3
}}}
where
fillBlock :: Addr# -> Word# -> State# RealWorld -> State# RealWorld
fillBlock addr size s = case filler (Ptr addr) (W# size) of
IO action -> case action s of
(# s', _ #) -> s'
go :: Addr# -> [(Ptr a, Word)] -> State# RealWorld -> State# RealWorld
go _ [] s = s
go previous ((_, W# size):rest) s =
case compactAllocateBlock# size previous s of
(# s', block #) -> case fillBlock block size s' of
s'' -> go block rest s''
sanityCheckByteStrings :: SerializedCompact a -> [ByteString.ByteString] -> Bool
sanityCheckByteStrings (SerializedCompact scl _) bsl = go scl bsl
where
go [] [] = True
go (_:_) [] = False
go [] (_:_) = False
go ((_, size):scs) (bs:bss) =
fromIntegral size == ByteString.length bs && go scs bss
importCompactByteStrings :: SerializedCompact a -> [ByteString.ByteString] ->
IO (Maybe (Compact a))
importCompactByteStrings serialized stringList =
if not (sanityCheckByteStrings serialized stringList) then
return Nothing
else do
state <- newIORef stringList
let filler :: Ptr Word8 -> Word -> IO ()
filler to size = do
(next:rest) <- readIORef state
let (fp, off, _) = toForeignPtr next
withForeignPtr fp $ \from -> do
copyBytes to (from `plusPtr` off) (fromIntegral size)
writeIORef state rest
importCompact serialized filler