@@ -41,8 +41,9 @@ import Control.Monad.IO.Class
4141import Control.Monad.Trans.Class (lift)
4242
4343import Control.Exception (throwIO, Exception, finally)
44−import Data.Enumerator
4544import Control.Applicative
45+import Control.Monad.Trans.Resource
46+import qualified Data.Conduit as C
4647
4748data Event =
4849 EventStreamStart
@@ -120,8 +121,8 @@ eventSize = 104
120121foreign import ccall unsafe "yaml_parser_initialize"
121122 c_yaml_parser_initialize :: Parser -> IO CInt
122123
123−foreign import ccall unsafe "&yaml_parser_delete"
124− c_yaml_parser_delete :: FunPtr (Parser -> IO ())
124+foreign import ccall unsafe "yaml_parser_delete"
125+ c_yaml_parser_delete :: Parser -> IO ()
125126
126127foreign import ccall unsafe "yaml_parser_set_input_string"
127128 c_yaml_parser_set_input_string :: Parser
@@ -146,8 +147,8 @@ foreign import ccall unsafe "fclose"
146147 c_fclose :: File
147148 -> IO ()
148149
149−foreign import ccall unsafe "&fclose_helper"
150− c_fclose_helper :: FunPtr (File -> Parser -> IO ())
150+foreign import ccall unsafe "fclose_helper"
151+ c_fclose_helper :: File -> IO ()
151152
152153withForeignPtr' :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b
153154withForeignPtr' fp f = do
@@ -455,60 +456,67 @@ newtype ToEventRawException = ToEventRawException CInt
455456 deriving (Show, Typeable)
456457instance Exception ToEventRawException
457458
458−decode :: MonadIO m => B.ByteString -> Enumerator Event m a
459−decode bs i = do
460− fp <- liftIO $ mallocForeignPtrBytes parserSize
461− res <- liftIO $ withForeignPtr fp c_yaml_parser_initialize
462− liftIO $ addForeignPtrFinalizer c_yaml_parser_delete fp
463− a <-
464− if (res == 0)
465− then throwError $ YamlException "Yaml out of memory"
466− else do -- NOTE: can't replace the following with unsafeUseAsCString
467− -- since it must run in a MonadIO
468− let (fptr, offset, len) = B.toForeignPtr bs
469− withForeignPtr' fptr $ \ptr -> do
470− let ptr' = castPtr ptr `plusPtr` offset
471− len' = fromIntegral len
472− liftIO $ withForeignPtr fp $ \p ->
473− c_yaml_parser_set_input_string p ptr' len'
474− runParser fp i
475− return a
476−
477−decodeFile :: MonadIO m => FilePath -> Enumerator Event m a
478−decodeFile file i = do
479− fp <- liftIO $ mallocForeignPtrBytes parserSize
480− liftIO $ addForeignPtrFinalizer c_yaml_parser_delete fp
481− res <- liftIO $ withForeignPtr fp c_yaml_parser_initialize
482− a <-
483− if res == 0
484− then throwError $ YamlException "Yaml out of memory"
485− else do
486− file' <- liftIO
487− $ withCString file $ \file' -> withCString "r" $ \r' ->
488− c_fopen file' r'
489− liftIO $ addForeignPtrFinalizerEnv c_fclose_helper file' fp
490− if (file' == nullPtr)
491− then throwError $ YamlException
492− $ "Yaml file not found: " ++ file
493− else do
494− liftIO $ withForeignPtr fp $ \p ->
495− c_yaml_parser_set_input_file p file'
496− a <- runParser fp i
497− return a
498− return a
499−
500−runParser :: MonadIO m
501− => ForeignPtr ParserStruct
502− -> Enumerator Event m a
503−runParser fp (Continue k) = do
504− e <- liftIO $ withForeignPtr fp parserParseOne'
459+decode :: ResourceIO m => B.ByteString -> C.Source m Event
460+decode bs =
461+ C.sourceIO alloc cleanup (runParser . fst)
462+ where
463+ alloc = mask_ $ do
464+ ptr <- mallocBytes parserSize
465+ res <- c_yaml_parser_initialize ptr
466+ if res == 0
467+ then do
468+ c_yaml_parser_delete ptr
469+ free ptr
470+ throwIO $ YamlException "Yaml out of memory"
471+ else do
472+ let (bsfptr, offset, len) = B.toForeignPtr bs
473+ let bsptrOrig = unsafeForeignPtrToPtr bsfptr
474+ let bsptr = castPtr bsptrOrig `plusPtr` offset
475+ c_yaml_parser_set_input_string ptr bsptr (fromIntegral len)
476+ return (ptr, bsfptr)
477+ cleanup (ptr, bsfptr) = do
478+ touchForeignPtr bsfptr
479+ c_yaml_parser_delete ptr
480+ free ptr
481+
482+decodeFile :: ResourceIO m => FilePath -> C.Source m Event
483+decodeFile file =
484+ C.sourceIO alloc cleanup (runParser . fst)
485+ where
486+ alloc = mask_ $ do
487+ ptr <- mallocBytes parserSize
488+ res <- c_yaml_parser_initialize ptr
489+ if res == 0
490+ then do
491+ c_yaml_parser_delete ptr
492+ free ptr
493+ throwIO $ YamlException "Yaml out of memory"
494+ else do
495+ file' <- liftIO
496+ $ withCString file $ \file' -> withCString "r" $ \r' ->
497+ c_fopen file' r'
498+ if file' == nullPtr
499+ then do
500+ c_fclose_helper file'
501+ c_yaml_parser_delete ptr
502+ free ptr
503+ throwIO $ YamlException
504+ $ "Yaml file not found: " ++ file
505+ else do
506+ c_yaml_parser_set_input_file ptr file'
507+ return (ptr, file')
508+ cleanup (ptr, file') = do
509+ c_fclose_helper file'
510+ c_yaml_parser_delete ptr
511+ free ptr
512+
513+runParser :: ResourceIO m => Parser -> m (C.SourceResult Event)
514+runParser parser = liftIO $ do
515+ e <- parserParseOne' parser
505516 case e of
506− Left err -> throwError $ YamlException err
507− Right Nothing -> continue k
508− Right (Just ev) -> do
509− step' <- lift $ runIteratee $ k $ Chunks [ev]
510− runParser fp step'
511−runParser _ step = returnI step
517+ Left err -> throwIO $ YamlException err
518+ Right Nothing -> return $ C.Closed
519+ Right (Just ev) -> return $ C.Open ev
512520
513521parserParseOne' :: Parser
514522 -> IO (Either String (Maybe Event))
@@ -529,57 +537,60 @@ parserParseOne' parser = allocaBytes eventSize $ \er -> do
529537 , show offset
530538 , "\n"
531539 ]
532− else liftIO $ Right <$> getEvent er
533−
534−encode :: MonadIO m => Iteratee Event m B.ByteString
535−encode = do
536− fp <- liftIO $ mallocForeignPtrBytes emitterSize
537− res <- liftIO $ withForeignPtr fp c_yaml_emitter_initialize
538− when (res == 0) $ throwError
539− $ YamlException "c_yaml_emitter_initialize failed"
540− buf <- liftIO $ mallocForeignPtrBytes bufferSize
541− liftIO $ withForeignPtr buf c_buffer_init
542− liftIO $ withForeignPtr fp $
543− \emitter -> withForeignPtr buf $
544− \b -> c_my_emitter_set_output emitter b
545− runEmitter (go buf) fp
540+ else Right <$> getEvent er
541+
542+encode :: ResourceIO m => C.Sink Event m ByteString
543+encode =
544+ runEmitter alloc close
546545 where
547− go buf = withForeignPtr buf $ \b -> do
546+ alloc emitter = do
547+ fbuf <- mallocForeignPtrBytes bufferSize
548+ withForeignPtr fbuf c_buffer_init
549+ withForeignPtr fbuf $ c_my_emitter_set_output emitter
550+ return fbuf
551+ close fbuf = withForeignPtr fbuf $ \b -> do
548552 ptr' <- c_get_buffer_buff b
549553 len <- c_get_buffer_used b
550554 fptr <- newForeignPtr_ $ castPtr ptr'
551555 return $ B.fromForeignPtr fptr 0 $ fromIntegral len
552556
553−encodeFile :: MonadIO m
557+encodeFile :: ResourceIO m
554558 => FilePath
555− -> Iteratee Event m ()
556−encodeFile filePath = do
557− fp <- liftIO $ mallocForeignPtrBytes emitterSize
558− res <- liftIO $ withForeignPtr fp c_yaml_emitter_initialize
559− when (res == 0) $ throwError
560− $ YamlException "c_yaml_emitter_initialize failed"
561− file <- liftIO $ withCString filePath $
562− \filePath' -> withCString "w" $
563− \w' -> c_fopen filePath' w'
564− when (file == nullPtr) $ throwError
565− $ YamlException $ "could not open file for write: " ++ filePath
566− liftIO $ withForeignPtr fp $ flip c_yaml_emitter_set_output_file file
567− runEmitter (c_fclose file) fp
568−
569−runEmitter :: MonadIO m
570− => IO a
571− -> ForeignPtr EmitterStruct
572− -> Iteratee Event m a
573−runEmitter close fp = continue go
559+ -> C.Sink Event m ()
560+encodeFile filePath = C.Sink $ do
561+ (releaseKey, file) <- flip withIO c_fclose $ do
562+ file <- liftIO $ withCString filePath $
563+ \filePath' -> withCString "w" $
564+ \w' -> c_fopen filePath' w'
565+ if (file == nullPtr)
566+ then throwIO $ YamlException $ "could not open file for write: " ++ filePath
567+ else return file
568+ C.prepareSink $ runEmitter (alloc file) (return) -- FIXME close file early
569+ where
570+ alloc file emitter = do
571+ c_yaml_emitter_set_output_file emitter file
572+ return ()
573+
574+runEmitter :: ResourceIO m
575+ => (Emitter -> IO a) -- ^ alloc
576+ -> (a -> IO b) -- ^ close
577+ -> C.Sink Event m b
578+runEmitter allocI closeI =
579+ C.sinkIO alloc cleanup push close
574580 where
575− go EOF = do
576− liftIO $ withForeignPtr fp c_yaml_emitter_delete
577− a <- liftIO close
578− yield a EOF
579− go (Chunks c) = do
580− liftIO $ withForeignPtr fp $ \emitter ->
581− mapM_ (\e -> toEventRaw e $ c_yaml_emitter_emit emitter) c
582− continue go
581+ alloc = mask_ $ do
582+ emitter <- mallocBytes emitterSize
583+ res <- c_yaml_emitter_initialize emitter
584+ when (res == 0) $ throwIO $ YamlException "c_yaml_emitter_initialize failed"
585+ a <- allocI emitter
586+ return (emitter, a)
587+ cleanup (emitter, _) = do
588+ c_yaml_emitter_delete emitter
589+ free emitter
590+ push (emitter, _) e = do
591+ liftIO $ toEventRaw e $ c_yaml_emitter_emit emitter
592+ return C.Processing
593+ close (_, a) = liftIO $ closeI a
583594
584595data YamlException = YamlException String
585596 deriving (Show, Typeable)