Holumbus-Distribution-0.0.1: source/Holumbus/Common/FileHandling.hs
-- ----------------------------------------------------------------------------
{- |
Module : Holumbus.Common.FileHandling
Copyright : Copyright (C) 2008 Stefan Schmidt
License : MIT
Maintainer : Stefan Schmidt (stefanschmidt@web.de)
Stability : experimental
Portability: portable
Version : 0.1
Some nice helper functions for strict file writing, appending and
reading. All read-functions are strict, in the sense that
the whole file is read at once and the content is stored into memory.
So you won't have any semi-closed handles which might bring you in trouble
from time to time. The files are immedialty closed after the reading.
This Module can handle four filetypes: XML-Files, List-Files,
Binary-Files and Text-Files.
XML-Files:
Normal textfiles, but the content is stored as a xml-representation. For
the pickling, we use the HXT-Library
(see http:\/\/www.fh-wedel.de\/~si\/HXmlToolbox\/)
List-Files:
Binary-Files, which store a list of data-objects. The main difference
between Binary-Files and List-Files is, that you can append data to a
List-File which will be automatically concatenated to the existing list.
If you try this with a normal Binary-File, you'll get only the original
list and the appended data will be lost.
Binary-Files:
Normal binary files, for the encoding and decoding, we use the Haskell
binary-package.
Text-Files:
Normal text files.
-}
-- ----------------------------------------------------------------------------
module Holumbus.Common.FileHandling
(
-- * xml files
loadFromXmlFile
, saveToXmlFile
-- * lists in binary files
, writeToListFile
, appendToListFile
, readFromListFile
, parseByteStringToList
-- * bytestring file handling
, writeToBinFile
, appendToBinFile
, readFromBinFile
-- * string file handling
, writeToTextFile
, appendToTextFile
, readFromTextFile
)
where
import Control.Exception
import Data.Binary
import qualified Data.ByteString.Lazy as B
import Data.Char
import Foreign
import System.IO
import System.IO.Unsafe
import Text.XML.HXT.Arrow
-- ----------------------------------------------------------------------------
-- xml files
-- ----------------------------------------------------------------------------
-- | Loads an XML-File and parses the content to a specified datatype.
loadFromXmlFile :: (XmlPickler a) => FilePath -> IO a
loadFromXmlFile f
= do
r <- runX (xunpickleDocument xpickle options f)
return $! head r
where
options = [ (a_validate,v_0), (a_remove_whitespace, v_1), (a_encoding, utf8), (a_validate, v_0) ]
-- | Converts a dataset to XML and saves the XML-string into a file.
saveToXmlFile :: (XmlPickler a) => FilePath -> a -> IO ()
saveToXmlFile f i
= do
runX (constA i >>> xpickleDocument xpickle options f)
return ()
where
options = [ (a_indent, v_1), (a_output_encoding, utf8), (a_validate, v_0) ]
-- ----------------------------------------------------------------------------
-- generic lists
-- ----------------------------------------------------------------------------
-- | Writes data to a list file.
writeToListFile :: (Binary a) => FilePath -> [a] -> IO ()
writeToListFile fp bs = writeToBinFile fp $ B.concat $ map encode bs
-- | Appends data to a list file.
appendToListFile :: (Binary a) => FilePath -> [a] -> IO ()
appendToListFile fp bs = appendToBinFile fp $ B.concat $ map encode bs
-- | reads from a list file.
readFromListFile :: (Binary a) => FilePath -> IO [a]
readFromListFile f
= do
b <- readFromBinFile f
return $ parseByteStringToList b
-- | You'll need this function, if you read the files a a normal binary file,
-- but the content itself is a list. This function encodes the bytestring
-- into a list of the specified datatype.
parseByteStringToList :: (Binary a) => B.ByteString -> [a]
parseByteStringToList b = reverse $ parse b []
where
parse :: (Binary a) => B.ByteString -> [a] -> [a]
parse bs accu
| (B.null bs) = accu
| otherwise = parse (B.drop count bs) ([nextElem] ++ accu)
where
nextElem = decode bs
count = B.length (encode nextElem)
-- ----------------------------------------------------------------------------
-- strict functions, bytestrings only
-- ----------------------------------------------------------------------------
-- | Write data to a binary file.
writeToBinFile :: FilePath -> B.ByteString -> IO ()
writeToBinFile = B.writeFile
-- | Append data to a binary file, this doesn't mean, that the contents
-- are really concatenated when you read the file.
appendToBinFile :: FilePath -> B.ByteString -> IO ()
appendToBinFile = B.appendFile
-- | Reads the data from a binary file as a bytestring.
readFromBinFile :: FilePath -> IO B.ByteString
readFromBinFile f
= bracket (openBinaryFile f ReadMode) hClose $
\h -> do
s <- hFileSize h
c <- B.hGetNonBlocking h (fromInteger s)
return $! c
-- ----------------------------------------------------------------------------
-- strict functions, strings only
-- ----------------------------------------------------------------------------
-- | Writes a sting to a text file.
writeToTextFile :: FilePath -> String -> IO ()
writeToTextFile = writeFile
-- | Appends a string to a text file.
appendToTextFile :: FilePath -> String -> IO ()
appendToTextFile = appendFile
-- | Strict file reading by Simon Marlow.
-- found on
-- http:\/\/users.aber.ac.uk\/afc\/stricthaskell.html
readFromTextFile :: FilePath -> IO String
readFromTextFile f
= do
bracket (openFile f ReadMode) hClose $
\h -> do
s <- hFileSize h
fp <- mallocForeignPtrBytes (fromIntegral s)
len <- withForeignPtr fp $ \buf -> hGetBuf h buf (fromIntegral s)
lazySlurp fp 0 len
buf_size :: Int
buf_size = 4096
lazySlurp :: ForeignPtr Word8 -> Int -> Int -> IO String
lazySlurp fp ix len
| fp `seq` False = undefined
| ix >= len = return []
| otherwise = do
cs <- unsafeInterleaveIO (lazySlurp fp (ix + buf_size) len)
ws <- withForeignPtr fp $ \p -> loop' (min (len-ix) buf_size - 1)
((p :: Ptr Word8) `plusPtr` ix) cs
return ws
where
loop' :: Int -> Ptr Word8 -> String -> IO String
loop' len' p acc
| len' `seq` p `seq` False = undefined
| len' < 0 = return acc
| otherwise = do
w <- peekElemOff p len'
loop' (len'-1) p (chr (fromIntegral w):acc)