{- Copyright 2010-2012 Cognimeta Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -} {-# LANGUAGE TypeFamilies, GeneralizedNewtypeDeriving, RankNTypes, FlexibleInstances, TupleSections, ScopedTypeVariables, MagicHash, UnboxedTuples, BangPatterns, GADTs #-} module Database.Perdure.CSerializer ( SerializerContext, cSer, serializeToArray, Address, module Database.Perdure.CDeserializer, module Cgm.Control.Concurrent.NotificationCount ) where import Prelude () import Cgm.Prelude import Control.Concurrent import GHC.Base hiding ((.), id) import GHC.IO hiding (liftIO) import Cgm.Data.Functor.Sum import Cgm.Data.Word import Cgm.Control.Concurrent.NotificationCount import Database.Perdure.CDeserializer import Database.Perdure.Count import Cgm.Data.Multiset as MS import Database.Perdure.AllocCopy import Cgm.Data.Maybe import Database.Perdure.Allocator import Database.Perdure.ArrayRef import Database.Perdure.WordArrayRef() import Database.Perdure.WordNArrayRef() import qualified Database.Perdure.Cache as Cache import Cgm.Data.MapMultiset import Data.Dynamic import Control.Monad.Random -- Important : We must not read (deref) a ref that has just been written in the current writeState. -- Since reodering of operations is allowed except across the storeFileFullBarrier (performed at the end of -- write State), this ensures that we do not read data which has not yet been fully written. -- TODO make sure just written ref's can be inserted in the cache, since that data is likely to be accessed again type CountDest c = Maybe (MVar (c Address)) addCount :: Multiset c => Address -> CountDest c -> IO () addCount a = maybe (return ()) $ flip modifyMVar_ $ return . MS.insert a type SerializerContext l c = (MVar Cache, l, CountDest c) -- TODO create type with strict fields -- TODO: consider testing whether returning a CSer (Maybe a) might be more performant, where Nothing represent the input a type Dest = ABitSeq RealWorld cSer :: (Multiset c, Allocator l) => Persister a -> SerializerContext l c -> (a -> Dest -> IO z) -> a -> Dest -> IO z cSer !p !sc !k !a !d = case p of PartialWordPersister n | n == wordBits -> stToIO (addWord a d) >>= k a | n == 0 -> k a d | otherwise -> stToIO (addBits n a d) >>= k a PairPersister pb pc -> case a of (b, c) -> cSer pb sc (\b' -> cSer pc sc (\c' -> k (b', c')) c) b d EitherPersister pb pc -> either (\b -> stToIO (addBit 0 d) >>= cSer pb sc (k . Left) b) (\c -> stToIO (addBit 1 d) >>= cSer pc sc (k . Right) c) a ViewPersister i pb -> cSer pb sc (k . fromJust . unapply i) (apply i a) d SummationPersister pi' _ s -> s (\i pb ba b -> cSer pi' sc (const $ cSer pb sc (k . ba) b) i d) a -- i' is ignored, i type should not contain CRefs DRefPersister' -> case a of (DRef _ _ w) -> case sc of (_, _, c) -> addCount (arrayRefAddr w) c >> cSer persister sc (const $ k a) w d -- TODO verify if call to persister is wastful CRefPersister' rp prb -> onCRef (\rb -> cSer prb sc (const $ k a) rb d) (\b -> cSerRef rp sc (k . Refed) b d) a -- Creates the ref and serializes it cSerRef :: (Multiset c, Allocator l, Persistent a, Typeable a) => RefPersister r -> SerializerContext l c -> (r a -> Dest -> IO z) -> a -> Dest -> IO z cSerRef !rp !sc !k !a !d = case rp of Ref0Persister -> cSer persister sc (k . Ref0) a d RefView rbra pb -> cSerRef pb sc (k . rbra) a d SizeRefPersister maxSize -> let p = persister -- TODO verify if call to persister is wastful start = d asDRef (Ref0 a') dStart dEnd = do --readIORef writeCount >>= putStr . (++ ",") . show --readIORef writeCount >>= writeIORef writeCount . (+ 1) --empty <- stToIO $ mkABitSeq --putStrLn $ ("CSerializer: Popped size = " ++) $ showLen $ addedBits dEnd dStart --putStrLn $ ("CSerializer: Stack size = " ++) $ showLen $ addedBits dStart empty dRef <- mkDRef sc p (Just a') dStart dEnd afterTrue <- stToIO $ addBit 1 start cSer DRefPersister' (noCount sc) (k . Sum . Right) dRef afterTrue -- Doesn't pass CountDest. We don't count first references. in stToIO (addBit 0 start) >>= \afterFalse -> cSerRef Ref0Persister sc (\r0 end0 -> bool (k (Sum $ Left r0) end0) (asDRef r0 afterFalse end0) (addedBits end0 afterFalse > maxSize)) a afterFalse CRefPersister _ -> error "CRef of CRef not yet implemented" -- TODO write DRefPersister -> let p = persister in -- TODO verify if call to persister is wastful stToIO mkABitSeq >>= \start -> cSer p sc (\a' end -> mkDRef sc p (Just a') start end >>= \dRef -> cSer DRefPersister' (noCount sc) k dRef d) a start -- Doesn't pass CountDest. We don't count first references.q -- TODO optimization : if the WordSeq fits in a single chunk, we should be able to writeDRef directly from it, since it is already aligned IRefPersister pb -> cSerRef pb sc (k . IRef) a d noCount :: SerializerContext l c -> SerializerContext l c noCount (cache, l, _) = (cache, l, Nothing) mkDRef :: (Allocator l, BitSrc s, SrcDestState s ~ RealWorld, Typeable a) => SerializerContext l c -> Persister a -> Maybe a -> s -> s -> IO (DRef a) mkDRef (cache, l, _) p ma = writeDRef p ma (DeserializerContext (allocatorStoreFile l) cache) l writeDRef :: (Allocator l, BitSrc s, SrcDestState s ~ RealWorld, Typeable a) => Persister a -> Maybe a -> DeserializerContext -> l -> s -> s -> IO (DRef a) writeDRef p ma dc@(DeserializerContext _ c) l start end = DRef p dc <$> maybe id (\a -> (>>= \w -> w <$ addToCache a w)) ma (allocCopyBits start end >>= writeArrayRef l) where addToCache a w = let addr = arrayRefAddr w in modifyMVar_ c $ evalRandIO . {-(trace ("writing/adding to cache at" ++ show addr) $ -} Cache.insert addr (Cache.Entry (toDyn a) $ arrayRefSize w) -- | The passed Persister must hace no references {-# NOINLINE serializeToArray #-} serializeToArray :: AllocCopy w => Persister a -> a -> PrimArray Pinned w serializeToArray p a = unsafePerformIO $ do start <- stToIO mkABitSeq noCache <- newMVar $ Cache.empty 0 cSer p (noCache, NoAllocator, Nothing :: CountDest MapMultiset) (const $ allocCopyBits start) a start data NoAllocator = NoAllocator instance Allocator NoAllocator where alloc = error "NoAllocator" allocatorStoreFile = error "NoAllocator"