codec (empty) → 0.1
raw patch · 16 files changed
+749/−0 lines, 16 filesdep +aesondep +basedep +binarysetup-changed
Dependencies added: aeson, base, binary, binary-bits, bytestring, data-default-class, mtl, template-haskell, text, transformers, unordered-containers
Files
- Control/Lens/Codec.hs +18/−0
- Data/Aeson/Codec.hs +54/−0
- Data/Binary/Bits/Codec.hs +37/−0
- Data/Binary/Codec.hs +73/−0
- Data/Codec.hs +50/−0
- Data/Codec/Testing.hs +34/−0
- Examples/Foreign.hsc +75/−0
- Examples/IP.hs +40/−0
- Examples/JSON.hs +31/−0
- Examples/Multi.hs +27/−0
- Examples/Tar.hs +78/−0
- Foreign/Codec.hs +57/−0
- LICENSE +12/−0
- Setup.hs +2/−0
- TestExamples.hs +14/−0
- codec.cabal +147/−0
+ Control/Lens/Codec.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE RankNTypes #-}++module Control.Lens.Codec (lensCodec) where++import Control.Applicative+import Control.Monad.Identity+import Control.Monad.Reader+import Control.Monad.State++import Data.Codec++-- | Turn a `Lens` into a `Codec` operating in a `MonadReader`/`MonadState`.+lensCodec :: (MonadReader s fr, MonadState s fw)+ => (forall f. (a -> f a) -> s -> f s) -> Codec fr fw a+lensCodec l = Codec+ { parse = liftM getConst $ asks (l Const)+ , produce = \x -> modify (runIdentity . l (const $ Identity x))+ }
+ Data/Aeson/Codec.hs view
@@ -0,0 +1,54 @@+module Data.Aeson.Codec+ (+ -- * JSON codecs+ JSONCodec+ -- * JSON object codecs+ , ObjectParser, ObjectBuilder, ObjectCodec+ , entry, pair, obj+ ) where++import Control.Applicative+import Data.Aeson+import Data.Aeson.Types (Parser, Pair)+import Control.Monad.Reader+import Control.Monad.Writer+import Data.Default.Class+import qualified Data.Text as T+import Data.String++import Data.Codec++-- | JSON codec. This is just a `ToJSON`/`FromJSON` implementation wrapped up in newtypes.+-- Use `def` to get a `JSONCodec` for a `ToJSON`/`FromJSON` instance.+type JSONCodec a = ConcreteCodec Value Parser a++instance (ToJSON a, FromJSON a) => Default (JSONCodec a) where+ def = Codec (ReaderT parseJSON) (Const . toJSON)++type ObjectParser = ReaderT Object Parser+type ObjectBuilder = Const (Endo [ Pair ])++-- | A codec that parses values out of a given `Object`, and produces+-- key-value pairs into a new one.+type ObjectCodec a = Codec ObjectParser ObjectBuilder a++-- | Produce a key-value pair.+pair :: ToJSON a => T.Text -> a -> ObjectBuilder ()+pair key val = Const $ Endo ((key .= val):)++-- | Read\/write a given value from/to a given key in the current object, using a given sub-codec.+-- ObjectCodec's `IsString` instance is equal to `entry` `def`.+entry :: T.Text -> JSONCodec a -> ObjectCodec a+entry key cd = Codec+ { parse = ReaderT $ \o -> (o .: key) >>= parseVal cd+ , produce = pair key . produceVal cd+ }++-- | Turn an `ObjectCodec` into a `JSONCodec` with an expected name (see `withObject`).+obj :: String -> ObjectCodec a -> JSONCodec a+obj err (Codec r w) = concrete+ (withObject err $ runReaderT r)+ (\x -> object $ appEndo (getConst $ w x) [])++instance (ToJSON a, FromJSON a) => IsString (ObjectCodec a) where+ fromString s = entry (fromString s) def
+ Data/Binary/Bits/Codec.hs view
@@ -0,0 +1,37 @@+module Data.Binary.Bits.Codec+ ( BitCodec+ , bool+ , word8, word16be, word32be, word64be+ , toBytes+ )+where++import Control.Applicative+import qualified Data.Binary.Bits.Get as G+import Data.Binary.Bits.Put+import qualified Data.Binary.Codec as B++import Data.Codec+import Data.Word++type BitCodec a = Codec G.Block BitPut a++bool :: BitCodec Bool+bool = Codec G.bool putBool++word8 :: Int -> BitCodec Word8+word8 = Codec <$> G.word8 <*> putWord8++word16be :: Int -> BitCodec Word16+word16be = Codec <$> G.word16be <*> putWord16be++word32be :: Int -> BitCodec Word32+word32be = Codec <$> G.word32be <*> putWord32be++word64be :: Int -> BitCodec Word64+word64be = Codec <$> G.word64be <*> putWord64be++-- | Convert a `BitCodec` into a `B.BinaryCodec`.+toBytes :: BitCodec a -> B.BinaryCodec a+toBytes (Codec r w)+ = Codec (G.runBitGet $ G.block r) (runBitPut . w)
+ Data/Binary/Codec.hs view
@@ -0,0 +1,73 @@+module Data.Binary.Codec+ (+ -- * Binary codecs+ BinaryCodec+ , byteString+ , toLazyByteString+ , word8+ , word16be, word16le, word16host+ , word32be, word32le, word32host+ , word64be, word64le, word64host+ , wordhost+ )+ where++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import Data.Binary.Get+import Data.Binary.Put+import Data.Word++import Data.Codec.Codec++type BinaryCodec a = Codec Get PutM a++-- | Get/put an n-byte field.+byteString :: Int -> BinaryCodec BS.ByteString+byteString n = Codec+ { parse = getByteString n+ , produce = \bs -> if BS.length bs == n+ then putByteString bs+ else fail "ByteString wrong size for field."+ }++word8 :: BinaryCodec Word8+word8 = Codec getWord8 putWord8++word16be :: BinaryCodec Word16+word16be = Codec getWord16be putWord16be++word16le :: BinaryCodec Word16+word16le = Codec getWord16le putWord16le++word16host :: BinaryCodec Word16+word16host = Codec getWord16host putWord16host++word32be :: BinaryCodec Word32+word32be = Codec getWord32be putWord32be++word32le :: BinaryCodec Word32+word32le = Codec getWord32le putWord32le++word32host :: BinaryCodec Word32+word32host = Codec getWord32host putWord32host++word64be :: BinaryCodec Word64+word64be = Codec getWord64be putWord64be++word64le :: BinaryCodec Word64+word64le = Codec getWord64le putWord64le++word64host :: BinaryCodec Word64+word64host = Codec getWord64host putWord64host++wordhost :: BinaryCodec Word+wordhost = Codec getWordhost putWordhost++-- | Convert a `BinaryCodec` into a `ConcreteCodec` on lazy `LBS.ByteString`s.+toLazyByteString :: BinaryCodec a -> ConcreteCodec LBS.ByteString (Either String) a+toLazyByteString (Codec r w) = concrete+ (\bs -> case runGetOrFail r bs of+ Left ( _ , _, err ) -> Left err+ Right ( _, _, x ) -> Right x)+ (runPut . w)
+ Data/Codec.hs view
@@ -0,0 +1,50 @@+module Data.Codec (+ -- $constructing+ module Data.Codec.Field+ , module Data.Codec.Codec+ , module Data.Codec.TH+ , module Data.Codec.Tuple+ ) where++import Data.Codec.Field +import Data.Codec.Codec+import Data.Codec.TH+import Data.Codec.Tuple++-- $constructing+-- The main purpose of this package is to make the creation of `Codec`s as easy and painless as possible.+-- If we have a data type such as:+--+-- @+-- data User = User+-- { username :: Text+-- , userEmail :: Text+-- , userLanguages :: [ Text ]+-- , userReferrer :: Maybe Text+-- } deriving Show+-- @+--+-- we can use the `genFields` function to generate `Field`s for each record field:+--+-- @+-- genFields ''User+-- @+--+-- This will create `Field`s named @f_username@, @f_userEmail@, etc. These fields can be associated with an+-- appropriate `Codec` with the `>-<` operator to specify the representation of the data structure. These+-- associations can then be combined with the `>>>` operator in the order of serialization/deserialization.+-- These associations can then be finalized into a `Codec` by providng the constructor to use.+-- For example, using the JSON `entry` `Codec` that assigns a value to a JSON key, we could write a codec for+-- @User@ as:+--+-- @+-- userCodec :: JSONCodec User+-- userCodec = obj "user object' $+-- User+-- $>> f_username >-< "user"+-- >>> f_userEmail >-< "email"+-- >>> f_userLanguages >-< "languages"+-- >>> f_userReferrer >-< opt "referrer"+-- @+--+-- The type system ensures that every field is provided exactly once.
+ Data/Codec/Testing.hs view
@@ -0,0 +1,34 @@+module Data.Codec.Testing+ ( -- * Testing+ ParseResult(..)+ , roundTrip, roundTripStorable+ )+where++import Data.Aeson.Types (Result(..))+import Data.Codec.Codec+import Foreign++class ParseResult f where+ toEither :: f a -> Either String a++instance ParseResult (Either String) where+ toEither = id++instance ParseResult Maybe where+ toEither = maybe (Left "Nothing") Right++instance ParseResult Result where+ toEither = \case+ Error err -> Left err+ Success x -> Right x++-- | Round-trip a value through a `ConcreteCodec` to an `Either String a`.+roundTrip :: ParseResult f => ConcreteCodec c f a -> a -> Either String a+roundTrip cd+ = toEither . parseVal cd . produceVal cd++-- | Round-trip a value through its `Storable` instance.+roundTripStorable :: Storable a => a -> IO a+roundTripStorable x+ = with x peek
+ Examples/Foreign.hsc view
@@ -0,0 +1,75 @@+module Examples.Foreign where++#include "time.h"++#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)++import Foreign+import Foreign.C+import Data.Codec+import Foreign.Codec++data TM = TM+ { seconds :: Int+ , minutes :: Int+ , hours :: Int+ , monthDay :: Int+ , month :: Int+ , year :: Int+ , weekDay :: Int+ , yearDay :: Int+ , daylightSavingTime :: Bool+ } deriving Show++genFields ''TM++-- convenience macro that enforces the correct codec type for a field+#define hsc_numField(s, f) \+ hsc_printf("field (%ld) . codecFor (undefined :: ", offsetof(s, f)); \+ hsc_type(typeof(((s*)0)->f)); \+ hsc_printf(")");++cTimeCodec :: ForeignCodec TM+cTimeCodec =+ TM+ $>> f_seconds >-< (#numField struct tm, tm_sec) cast+ >>> f_minutes >-< (#numField struct tm, tm_min) cast+ >>> f_hours >-< (#numField struct tm, tm_hour) cast+ >>> f_monthDay >-< (#numField struct tm, tm_mday) cast+ >>> f_month >-< (#numField struct tm, tm_mon) cast+ >>> f_year >-< (#numField struct tm, tm_year) cast+ >>> f_weekDay >-< (#numField struct tm, tm_wday) cast+ >>> f_yearDay >-< (#numField struct tm, tm_yday) cast+ >>> f_daylightSavingTime >-< (#numField struct tm, tm_yday) cBool++instance Storable TM where+ sizeOf _ = #{size struct tm}+ alignment _ = #{alignment struct tm}+ peek = peekWith cTimeCodec+ poke = pokeWith cTimeCodec++foreign import ccall "time.h strftime" strftime+ :: CString -> CInt -> CString -> Ptr TM -> IO CSize++formatTM :: String -> TM -> IO String+formatTM fmt tm+ = allocaBytes maxSize $ \str -> do+ _ <- withCString fmt $ \cfmt ->+ with tm $ \tmp ->+ strftime str (fromIntegral maxSize) cfmt tmp+ peekCString str+ where+ maxSize = 512++testTime :: TM+testTime = TM+ { seconds = 42+ , minutes = 49+ , hours = 13+ , monthDay = 4+ , month = 4+ , year = 115+ , weekDay = 0+ , yearDay = 0+ , daylightSavingTime = False+ }
+ Examples/IP.hs view
@@ -0,0 +1,40 @@+module Examples.IP where++import Data.Codec+import Data.Binary.Bits.Codec+import Data.Word++data IPv4 = IPv4+ { version :: Word8+ , ihl :: Word8+ , dscp :: Word8+ , ecn :: Word8+ , totalLength :: Word16+ , identification :: Word16+ , flags :: Word8+ , fragmentOffset :: Word16+ , timeToLive :: Word8+ , protocol :: Word8+ , headerChecksum :: Word16+ , sourceIP :: Word32+ , destIP :: Word32+ }++genFields ''IPv4++ipv4Codec :: BitCodec IPv4+ipv4Codec = + IPv4+ $>> f_version >-< word8 4+ >>> f_ihl >-< word8 4+ >>> f_dscp >-< word8 6+ >>> f_ecn >-< word8 2+ >>> f_totalLength >-< word16be 16+ >>> f_identification >-< word16be 16+ >>> f_flags >-< word8 3+ >>> f_fragmentOffset >-< word16be 13+ >>> f_timeToLive >-< word8 8+ >>> f_protocol >-< word8 8+ >>> f_headerChecksum >-< word16be 16+ >>> f_sourceIP >-< word32be 32+ >>> f_destIP >-< word32be 32
+ Examples/JSON.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE OverloadedStrings #-}++module Examples.JSON where++import Data.Aeson+import Data.Aeson.Codec+import Data.Codec+import Data.Text (Text)++data User = User+ { username :: Text+ , userEmail :: Text+ , userLanguages :: [ Text ]+ , userReferrer :: Maybe User+ } deriving Show++genFields ''User++userCodec :: JSONCodec User+userCodec = obj "user object" $+ User+ $>> f_username >-< "user" -- entry with FromJSON/ToJSON serialization+ >>> f_userEmail >-< "email"+ >>> f_userLanguages >-< "languages"+ >>> f_userReferrer >-< opt (entry "referrer" userCodec) -- entry with specific codec++instance FromJSON User where+ parseJSON = parseVal userCodec++instance ToJSON User where+ toJSON = produceVal userCodec
+ Examples/Multi.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE OverloadedStrings #-}++module Examples.Multi where++import Data.Aeson+import Data.Aeson.Codec+import Data.Codec++data Multi a+ = Con1 { foo :: String, bar :: Int }+ | Con2 { baz :: a }+ | Con3+ deriving Show++genFields ''Multi++multiCodec :: (ToJSON a, FromJSON a) => JSONCodec (Multi a)+multiCodec = obj "multi object" $ covered $ (+ cbuild c_Con1+ $ f_foo >-< "foo"+ >>> f_bar >-< "bar"+ ) <-> (+ cbuild c_Con2+ $ f_baz >-< "baz"+ ) <-> (+ cbuild c_Con3 done+ )
+ Examples/Tar.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE TemplateHaskell #-}++module Examples.Tar where++import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import Data.Codec+import Data.Word+import Data.Binary.Codec+import Numeric++-- stolen from tar-conduit+data Header = Header {+ headerName :: B.ByteString, -- ^ 100 bytes long+ headerMode :: Word64,+ headerOwnerUID :: Word64,+ headerOwnerGID :: Word64,+ headerFileSize :: Integer, -- ^ 12 bytes+ headerModifyTime :: Integer, -- ^ 12 bytes+ headerChecksum :: Word64,+ headerType :: Word8, -- ^ 1 byte+ headerLinkName :: B.ByteString, -- ^ 100 bytes+ headerMagic :: B.ByteString, -- ^ 6 bytes+ headerVersion :: Word16,+ headerOwnerUserName :: B.ByteString, -- ^ 32 bytes+ headerOwnerGroupName :: B.ByteString, -- ^ 32 bytes+ headerDeviceMajorNumber :: Word64,+ headerDeviceMinorNumber :: Word64,+ headerFilenamePrefix :: B.ByteString -- ^ 155 bytes+ }+ deriving (Eq, Show, Read)++genFields ''Header++-- easy peasy+headerCodec :: BinaryCodec Header+headerCodec =+ Header+ $>> f_headerName >-< bytes' 100 -- Codec will de/serialize in this order+ >>> f_headerMode >-< octal 8+ >>> f_headerOwnerUID >-< octal 8+ >>> f_headerOwnerGID >-< octal 8+ >>> f_headerFileSize >-< octal 12+ >>> f_headerModifyTime >-< octal 12+ >>> f_headerChecksum >-< octal 8+ >>> f_headerType >-< word8+ >>> f_headerLinkName >-< bytes' 100+ >>> f_headerMagic >-< bytes' 6+ >>> f_headerVersion >-< octal 2+ >>> f_headerOwnerUserName >-< bytes' 32+ >>> f_headerOwnerGroupName >-< bytes' 32+ >>> f_headerDeviceMajorNumber >-< octal 8+ >>> f_headerDeviceMinorNumber >-< octal 8+ >>> f_headerFilenamePrefix >-< bytes' 155++-- byte field with trailing nulls stripped+bytes' :: Int -> BinaryCodec B.ByteString+bytes' n = mapCodecM trim pad (byteString n)+ where+ trim bs = return (fst $ B.spanEnd (==0) bs)+ pad bs+ | B.length bs <= n = return $ bs `B.append` B.replicate (n - B.length bs) 0+ | otherwise = fail "Serialized ByteString too large for field."++-- zero-padded null/space-terminated ASCII octal+octal :: (Show i, Integral i) => Int -> BinaryCodec i+octal n = mapCodecM parseOct makeOct (byteString n)+ where+ parseOct bs+ | B.null trimmed = return 0+ | otherwise = case readOct (BC.unpack trimmed) of+ [ ( x, _ ) ] -> return x+ _ -> fail $ "Could not parse octal value: " ++ show bs+ where trimmed = BC.takeWhile (`notElem` " \NUL") bs+ makeOct x+ | B.length octBS > n - 1 = fail "Octal value too large for field."+ | otherwise = return $ BC.replicate (n - 1 - B.length octBS) '0' `B.append` octBS `B.snoc` 0+ where octBS = BC.pack $ showOct x ""
+ Foreign/Codec.hs view
@@ -0,0 +1,57 @@+module Foreign.Codec+ ( -- * Foreign codecs+ ForeignContext, ForeignCodec, ForeignCodec'+ , peekWith, pokeWith, storable, field+ , cast, cBool+ , codecFor+ ) where++import Control.Monad.Reader+import Foreign++import Data.Codec.Codec++type ForeignContext a = ReaderT (Ptr a) IO+-- | A foreign codec for @a@ given a pointer to @p@.+type ForeignCodec' p a = Codec (ForeignContext p) (ForeignContext p) a+-- | A foreign codec for @a@ given a pointer to itself.+-- Use `def` from `Default` to get a codec that uses a `Storable` instance,+type ForeignCodec a = ForeignCodec' a a++-- | Peek a value using a `ForeignCodec'`.+peekWith :: ForeignCodec' p a -> Ptr p -> IO a+peekWith (Codec r _)+ = runReaderT r++-- | Poke a value using a `ForeignCodec'`.+pokeWith :: ForeignCodec' p a -> Ptr p -> a -> IO ()+pokeWith (Codec _ w) ptr x+ = runReaderT (w x) ptr++-- | A codec for a field of a foreign structure, given its byte offset and a sub-codec.+-- You can get an offset easily using @{#offset struct_type, field}@ with @hsc2hs@.+field :: Int -> ForeignCodec' f a -> ForeignCodec' p a+field off cd = Codec+ { parse = inField $ parse cd+ , produce = inField . produce cd+ } where inField = withReaderT (`plusPtr` off)++-- | A `ForeignCodec` for any `Storable` type.+storable :: Storable a => ForeignCodec a+storable = Codec (ReaderT peek) (\x -> ReaderT (`poke`x))++castContext :: ForeignCodec' c a -> ForeignCodec' c' a+castContext = mapCodecF castc castc+ where castc = withReaderT castPtr++-- | Store any integral type.+cast :: (Integral c, Storable c, Integral a) => ForeignCodec' c a+cast = mapCodec fromIntegral fromIntegral storable++-- | Store a `Bool` in any `Integral` `Ptr`.+cBool :: Integral c => ForeignCodec' c Bool+cBool = castContext storable++-- | Restrict the pointer type of a given codec. Utility function for the @numField@ macro.+codecFor :: c -> ForeignCodec' c a -> ForeignCodec' c a+codecFor _ = id
+ LICENSE view
@@ -0,0 +1,12 @@+Copyright (c) 2015, Patrick Chilton+All rights reserved.++Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ TestExamples.hs view
@@ -0,0 +1,14 @@+module Main where++-- No testing yet, just check that the examples all build.++import Examples.Foreign+import Examples.IP+import Examples.JSON+import Examples.Multi+import Examples.Tar++import System.Exit++main :: IO ()+main = exitSuccess
+ codec.cabal view
@@ -0,0 +1,147 @@+name: codec+version: 0.1+license: BSD3+license-file: LICENSE+synopsis: First-class record construction and bidirectional serialization+description:+ Tired of writing complementary @parseJSON@\/@toJSON@, @peek@\/@poke@ or+ Binary @get@\/@put@ functions?+ .+ @codec@ provides easy bidirectional serialization of plain Haskell+ records in any Applicative context. All you need to do is provide a+ de\/serializer for every record field in any order you like, and you get+ a de\/serializer for the whole structure. The type system ensures that+ you provide every record exactly once. It also includes a library for+ general record construction in an Applicative context, of which creating+ codecs is just one application.+ .+ JSON!+ .+ > userCodec :: JSONCodec User+ > userCodec = obj "user object" $+ > User+ > $>> f_username >-< "user"+ > >>> f_userEmail >-< "email"+ > >>> f_userLanguages >-< "languages"+ > >>> f_userReferrer >-< opt "referrer"+ >+ > instance FromJSON User where+ > parseJSON = parseVal userCodec+ >+ > instance ToJSON User where+ > toJSON = produceVal userCodec+ .+ Bit fields!+ .+ > ipv4Codec :: BinaryCodec IPv4+ > ipv4Codec = toBytes $+ > IPv4+ > $>> f_version >-< word8 4+ > >>> f_ihl >-< word8 4+ > >>> f_dscp >-< word8 6+ > >>> f_ecn >-< word8 2+ > >>> f_totalLength >-< word16be 16+ > >>> f_identification >-< word16be 16+ > >>> f_flags >-< word8 3+ > >>> f_fragmentOffset >-< word16be 13+ > >>> f_timeToLive >-< word8 8+ > >>> f_protocol >-< word8 8+ > >>> f_headerChecksum >-< word16be 16+ > >>> f_sourceIP >-< word32be 32+ > >>> f_destIP >-< word32be 32+ >+ > instance Binary IPv4 where+ > get = parse ipv4Codec+ > put = produce ipv4Codec+ .+ Storable!+ .+ > timeSpecCodec :: ForeignCodec TimeSpec+ > timeSpecCodec =+ > TimeSpec+ > $>> f_seconds >-< field (#offset struct timespec, tv_sec) cInt+ > >>> f_nanoseconds >-< field (#offset struct timespec, tv_nsec) cInt+ >+ > instance Storable TimeSpec where+ > peek = peekWith timeSpecCodec+ > poke = pokeWith timeSpecCodec+ > ...+ .+ All of these examples use the same types and logic for constructing+ Codecs, and it\'s very easy to create Codecs for any+ parsing\/serialization library.+ .+ See "Data.Codec" for an introduction.+author: Patrick Chilton+maintainer: chpatrick@gmail.com+-- copyright: +category: Data+build-type: Simple+-- extra-source-files: +cabal-version: >=1.10+homepage: https://github.com/chpatrick/codec++source-repository head+ type: git+ location: https://github.com/chpatrick/codec.git++library+ exposed-modules: Data.Codec,+ Data.Codec.Testing,++ Control.Lens.Codec,+ Data.Aeson.Codec,+ Data.Binary.Codec,+ Data.Binary.Bits.Codec,+ Foreign.Codec+ -- other-modules: + default-extensions: TemplateHaskell,+ MultiParamTypeClasses,+ FlexibleContexts,+ FlexibleInstances,+ LambdaCase,+ FunctionalDependencies,+ DeriveFunctor,+ ScopedTypeVariables+ build-depends: base >=4.6 && < 4.9,+ bytestring >=0.10,+ binary >=0.7,+ binary-bits >=0.5,+ template-haskell >=2.8,+ mtl >= 2.2.1,+ aeson >= 0.8.0.2,+ text >= 1.2.0.4,+ unordered-containers >= 0.2.5.1,+ data-default-class >= 0.0.1,+ transformers >= 0.4.2.0+ -- hs-source-dirs: + default-language: Haskell2010+ ghc-options: -Wall -fno-warn-orphans++test-suite Examples+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: TestExamples.hs+ other-modules: Examples.Foreign,+ Examples.IP,+ Examples.JSON,+ Examples.Multi,+ Examples.Tar+ build-depends: base >=4.6,+ bytestring >=0.10,+ binary >=0.7,+ binary-bits >=0.5,+ template-haskell >=2.8,+ mtl >= 2.2.1,+ aeson >= 0.8.0.2,+ text >= 1.2.0.4,+ unordered-containers >= 0.2.5.1,+ data-default-class >= 0.0.1,+ transformers >= 0.4.2.0+ default-extensions: TemplateHaskell,+ MultiParamTypeClasses,+ FlexibleInstances,+ DeriveFunctor,+ FunctionalDependencies,+ LambdaCase,+ ScopedTypeVariables