-- | Convert tuples of Haskell values to and from ASCII or packed binary -- representations. -- -- This package is intended for cheap and cheerful serialisation and -- deserialisation of flat tables, where each row has a fixed format. -- If you have a table consisting of a couple hundred megs of -- Pipe-Separated-Variables issued by some filthy enterprise system, -- then this package is for you. -- -- If you want to parse context-free, or context-sensitive -- languages then try the @parsec@ or @attoparsec@ packages. -- If you have binary data that does not have a fixed format then -- try the @binary@ or @cereal@ packages. -- -- For testing purposes, use `packToString` which takes a format, -- a record, and returns a list of bytes. -- -- @ -- > import Data.Repa.Convert -- -- > let format = Sep '|' (VarAsc :*: IntAsc :*: DoubleAsc :*: ()) -- > let Just str = packToString format ("foo" :*: 66 :*: 93.42 :*: ()) -- > str -- "foo|66|93.42" -- @ -- -- We can then unpack the raw bytes back to Haskell values with `unpackFromString`. -- -- @ -- > unpackFromString format str -- Just ("foo" :*: (66 :*: (93.42 :*: ()))) -- @ -- -- In production code use `unsafeRunPacker` and `unsafeRunUnpacker` to work directly -- with a buffer in foreign memory. -- -- * NOTE that in the current version the separating character is un-escapable. -- * The above means that the format @(Sep ',')@ does NOT parse a CSV -- file according to the CSV specification: http://tools.ietf.org/html/rfc4180. -- module Data.Repa.Convert ( -- | The @Formats@ module contains the pre-defined data formats. module Data.Repa.Convert.Formats -- * Data formats , Format (..) -- * Type constraints , forFormat , listFormat -- * Packing data , Packable (..) -- ** Packer monoid , Packer (..) , unsafeRunPacker -- ** Unpacker monad , Unpacker (..) , unsafeRunUnpacker -- * Interfaces -- ** Default Ascii , packToAscii -- ** List Interface , packToList8, unpackFromList8 -- ** String Interface , packToString, unpackFromString) where import Data.Repa.Convert.Format import Data.Repa.Convert.Formats import Data.Char import Data.Word import Control.Monad import System.IO.Unsafe import qualified Foreign.Storable as S import qualified Foreign.Marshal.Alloc as S import qualified GHC.Ptr as S --------------------------------------------------------------------------------------------------- -- | Pack a value to a list of `Word8` using the default Ascii format. packToAscii :: ( FormatAscii a , Value (FormatAscii' a) ~ a , Packable (FormatAscii' a)) => a -> Maybe String packToAscii a = packToString (formatAscii a) a --------------------------------------------------------------------------------------------------- -- | Pack a value to a list of `Word8`. packToList8 :: Packable format => format -> Value format -> Maybe [Word8] packToList8 f x | Just lenMax <- packedSize f x = unsafePerformIO $ do buf <- S.mallocBytes lenMax mResult <- unsafeRunPacker (pack f x) buf case mResult of Nothing -> return Nothing Just buf' -> do let lenUsed = S.minusPtr buf' buf xs <- mapM (S.peekByteOff buf) [0 .. lenUsed - 1] S.free buf return $ Just xs | otherwise = Nothing -- | Unpack a value from a list of `Word8`. unpackFromList8 :: Packable format => format -> [Word8] -> Maybe (Value format) unpackFromList8 format xs = unsafePerformIO $ do let len = length xs buf <- S.mallocBytes len mapM_ (\(o, x) -> S.pokeByteOff buf o x) $ zip [0 .. len - 1] xs r <- unsafeRunUnpacker (unpack format) buf len (const False) return $ fmap fst r -- | Pack a value to a String. packToString :: Packable format => format -> Value format -> Maybe String packToString f v = liftM (map (chr . fromIntegral)) $ packToList8 f v -- | Unpack a value from a String. unpackFromString :: Packable format => format -> String -> Maybe (Value format) unpackFromString f s = unpackFromList8 f $ map (fromIntegral . ord) s --------------------------------------------------------------------------------------------------- -- | Constrain the type of a value to match the given format. -- -- The value itself is not used. -- forFormat :: format -> Value format -> Value format forFormat _ v = v {-# INLINE forFormat #-} -- | Constrain the type of some values to match the given format. -- -- The value itself is not used. -- listFormat :: format -> [Value format] -> [Value format] listFormat _ v = v {-# INLINE listFormat #-}