-- Copyright : (C) 2009 Corey O'Connor -- License : BSD-style (see the file LICENSE) {-# LANGUAGE MagicHash #-} module Bind.Marshal.StdLib.Dynamic.ByteString.Lazy.Ser ( encode , encode_ , with_bytestring_handler ) where import Bind.Marshal.Prelude import Bind.Marshal.Action import Bind.Marshal.SerAction import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Internal as L import qualified Data.ByteString as S import qualified Data.ByteString.Internal as S import Data.IORef import Data.Maybe import System.IO.Unsafe ( unsafePerformIO ) import Foreign.Marshal.Array import Foreign.Marshal.Alloc import Foreign.Ptr import GHC.Exts import GHC.ForeignPtr ( ForeignPtr(..) , mallocPlainForeignPtrBytes , unsafeForeignPtrToPtr , touchForeignPtr ) import GHC.Prim import System.IO -- I think I need to provide a IO-free formulation for dynamic&static ops to get sufficient -- laziness. Or at least that would make it easier for me to understand the laziness properties. data LazyBSSer = LazyBSSer { out_bytestring :: L.ByteString , ser_fp :: ForeignPtr Word8 } defaultChunkSize = L.defaultChunkSize instance BufferDelegate LazyBSSer where {-# INLINE gen_region #-} gen_region !required_size !bd = do let !buffer_size = max required_size defaultChunkSize next_ser_fp <- mallocPlainForeignPtrBytes buffer_size let !bd' = LazyBSSer (out_bytestring bd) next_ser_fp let !(Ptr start_addr) = unsafeForeignPtrToPtr next_ser_fp returnM $! BDIter buffer_size 0 bd' start_addr start_addr :: IO (BDIter LazyBSSer) {-# INLINE finalize_region #-} finalize_region !bd_iter = case buffer_delegate bd_iter of !bd -> do returnM $! LazyBSSer (finalize_chunk bd $! bytes_final bd_iter) undefined :: IO LazyBSSer finalize_chunk :: LazyBSSer -> Size -> L.ByteString finalize_chunk !bd 0 = out_bytestring bd finalize_chunk !bd !bytes_final = let strict_bs = S.fromForeignPtr (ser_fp bd) 0 bytes_final in out_bytestring bd `L.append` L.chunk strict_bs L.Empty -- XXX: not exception safe. with_bytestring_handler :: forall a . L.ByteString -> ( LazyBSSer -> IO (a, LazyBSSer) ) -> IO (a, L.ByteString) with_bytestring_handler bs_0 f = do let bd = LazyBSSer bs_0 undefined !(v, !bd') <- f bd returnM $! (v, out_bytestring bd') :: IO (a, L.ByteString) {-# NOINLINE encode_ #-} encode_ :: DynamicSerAction Sealed Sealed Sealed LazyBSSer () -> L.ByteString encode_ !ser_action = unsafePerformIO ( do !( (), !bd') <- ser_to_buffer_delegate ser_action (LazyBSSer L.empty undefined) returnM $! out_bytestring bd' :: IO L.ByteString ) {-# NOINLINE encode #-} encode :: DynamicSerAction Sealed Sealed Sealed LazyBSSer a -> (a, L.ByteString) encode ser_action = unsafePerformIO ( with_bytestring_handler L.empty (ser_to_buffer_delegate ser_action) )