repa-convert-4.2.0.1: Data/Repa/Convert.hs
-- | 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 #-}