binary 0.4.5 → 0.10.0.0
raw patch · 32 files changed
Files
- README +0/−74
- README.md +93/−0
- benchmarks/Benchmark.hs +1464/−0
- benchmarks/Builder.hs +197/−0
- benchmarks/CBenchmark.c +39/−0
- benchmarks/CBenchmark.h +4/−0
- benchmarks/Cabal24.hs +360/−0
- benchmarks/GenericsBench.hs +52/−0
- benchmarks/GenericsBenchCache.hs +40/−0
- benchmarks/GenericsBenchTypes.hs +35/−0
- benchmarks/Get.hs +381/−0
- benchmarks/MemBench.hs +85/−0
- benchmarks/Put.hs +178/−0
- binary.cabal +245/−41
- changelog.md +183/−0
- docs/hcar/binary-Lb.tex +48/−0
- index.html +0/−161
- src/Data/Binary.hs +96/−552
- src/Data/Binary/Builder.hs +121/−273
- src/Data/Binary/Class.hs +1022/−0
- src/Data/Binary/FloatCast.hs +45/−0
- src/Data/Binary/Generic.hs +157/−0
- src/Data/Binary/Get.hs +519/−388
- src/Data/Binary/Get/Internal.hs +430/−0
- src/Data/Binary/Internal.hs +15/−0
- src/Data/Binary/Put.hs +222/−19
- tests/Action.hs +406/−0
- tests/Arbitrary.hs +23/−0
- tests/File.hs +43/−0
- tests/QC.hs +713/−0
- tools/derive/BinaryDerive.hs +57/−0
- tools/derive/Example.hs +72/−0
− README
@@ -1,74 +0,0 @@-- binary: efficient, pure binary serialisation using lazy ByteStrings---------------------------------------------------------------------------The 'binary' package provides Data.Binary, containing the Binary class,-and associated methods, for serialising values to and from lazy-ByteStrings. --A key feature of 'binary' is that the interface is both pure, and efficient.--The 'binary' package is portable to GHC and Hugs.--Building:-- runhaskell Setup.lhs configure- runhaskell Setup.lhs build- runhaskell Setup.lhs install--First:- import Data.Binary--and then write an instance of Binary for the type you wish to serialise.-More information in the haddock documentation.--Deriving:--It is possible to mechanically derive new instances of Binary for your-types, if they support the Data and Typeable classes. A script is-provided in tools/derive. Here's an example of its use.-- $ cd binary - $ cd tools/derive -- $ ghci -fglasgow-exts BinaryDerive.hs-- *BinaryDerive> :l Example.hs -- *Main> deriveM (undefined :: Drinks)-- instance Binary Main.Drinks where- put (Beer a) = putWord8 0 >> put a- put Coffee = putWord8 1- put Tea = putWord8 2- put EnergyDrink = putWord8 3- put Water = putWord8 4- put Wine = putWord8 5- put Whisky = putWord8 6- get = do- tag_ <- getWord8- case tag_ of- 0 -> get >>= \a -> return (Beer a)- 1 -> return Coffee- 2 -> return Tea- 3 -> return EnergyDrink- 4 -> return Water- 5 -> return Wine- 6 -> return Whisky--Contributors:-- Lennart Kolmodin- Duncan Coutts- Don Stewart- Spencer Janssen- David Himmelstrup- Björn Bringert- Ross Paterson- Einar Karttunen- John Meacham- Ulf Norell- Tomasz Zielonka- Stefan Karrmann- Bryan O'Sullivan- Florian Weimer
+ README.md view
@@ -0,0 +1,93 @@+# binary package #++[](https://hackage.haskell.org/package/binary) [](https://www.stackage.org/package/binary) [](http://travis-ci.org/kolmodin/binary)++*Efficient, pure binary serialisation using lazy ByteStrings.*++The ``binary`` package provides Data.Binary, containing the Binary class,+and associated methods, for serialising values to and from lazy+ByteStrings. +A key feature of ``binary`` is that the interface is both pure, and efficient.+The ``binary`` package is portable to GHC and Hugs.++## Installing binary from Hackage ##++``binary`` is part of The Glasgow Haskell Compiler (GHC) and therefore if you+have either GHC or [The Haskell Platform](http://www.haskell.org/platform/)+installed, you already have ``binary``.++More recent versions of ``binary`` than you might have installed may be+available. You can use ``cabal-install`` to install a later version from+[Hackage](http://hackage.haskell.org/package/binary).++ $ cabal update+ $ cabal install binary++## Building binary ##++``binary`` comes with both a test suite and a set of benchmarks.+While developing, you probably want to enable both.+Here's how to get the latest version of the repository, configure and build.++ $ git clone git@github.com:kolmodin/binary.git+ $ cd binary+ $ cabal update+ $ cabal configure --enable-tests --enable-benchmarks+ $ cabal build++Run the test suite.++ $ cabal test++## Using binary ##++First:++ import Data.Binary++and then write an instance of Binary for the type you wish to serialise.+An example doing exactly this can be found in the Data.Binary module.+You can also use the Data.Binary.Builder module to efficiently build+lazy bytestrings using the ``Builder`` monoid. Or, alternatively, the+Data.Binary.Get and Data.Binary.Put to serialize/deserialize using+the ``Get`` and ``Put`` monads.++More information in the haddock documentation.++## Deriving binary instances using GHC's Generic ##++Beginning with GHC 7.2, it is possible to use binary serialization without+writing any instance boilerplate code.++```haskell+{-# LANGUAGE DeriveGeneric #-}++import Data.Binary+import GHC.Generics (Generic)++data Foo = Foo deriving (Generic)++-- GHC will automatically fill out the instance+instance Binary Foo+```++## Contributors ##++* Lennart Kolmodin+* Duncan Coutts+* Don Stewart+* Spencer Janssen+* David Himmelstrup+* Björn Bringert+* Ross Paterson+* Einar Karttunen+* John Meacham+* Ulf Norell+* Tomasz Zielonka+* Stefan Karrmann+* Bryan O'Sullivan+* Bas van Dijk+* Florian Weimer++For a full list of contributors, see+[here](https://github.com/kolmodin/binary/graphs/contributors).
+ benchmarks/Benchmark.hs view
@@ -0,0 +1,1464 @@+{-# LANGUAGE BangPatterns #-}+module Main (main) where++import qualified Data.ByteString.Lazy as L+import Data.Binary+import Data.Binary.Put+import Data.Binary.Get++import Control.Exception+import System.CPUTime+import Numeric+import Text.Printf+import System.Environment++import MemBench++data Endian+ = Big+ | Little+ | Host+ deriving (Eq,Ord,Show)++main :: IO ()+main = do+ args <- getArgs+ mb <- case args of+ (arg:_) -> readIO arg+ _ -> return 100+ memBench (mb*10) + putStrLn ""+ putStrLn "Binary (de)serialisation benchmarks:"++ -- do bytewise + sequence_+ [ test wordSize chunkSize Host mb+ | wordSize <- [1]+ , chunkSize <- [16] --1,2,4,8,16]+ ]++ -- now Word16 .. Word64+ sequence_+ [ test wordSize chunkSize end mb+ | wordSize <- [2,4,8]+ , chunkSize <- [16]+ , end <- [Host] -- ,Big,Little]+ ]++------------------------------------------------------------------------++time :: IO a -> IO Double+time action = do+ start <- getCPUTime+ action+ end <- getCPUTime+ return $! (fromIntegral (end - start)) / (10^12)++------------------------------------------------------------------------++test :: Int -> Int -> Endian -> Int -> IO ()+test wordSize chunkSize end mb = do+ let bytes :: Int+ bytes = mb * 2^20+ iterations = bytes `div` wordSize+ bs = runPut (doPut wordSize chunkSize end iterations)+ sum = runGet (doGet wordSize chunkSize end iterations) bs++ case (chunkSize,end) of (1,Host) -> putStrLn "" ; _ -> return ()++ printf "%dMB of Word%-2d in chunks of %2d (%6s endian): "+ (mb :: Int) (8 * wordSize :: Int) (chunkSize :: Int) (show end)++ putSeconds <- time $ evaluate (L.length bs)+ getSeconds <- time $ evaluate sum+-- print (L.length bs, sum)+ let putThroughput = fromIntegral mb / putSeconds+ getThroughput = fromIntegral mb / getSeconds++ printf "%6.1f MB/s write, %6.1f MB/s read, %5.1f get/put-ratio\n"+ putThroughput+ getThroughput+ (getThroughput/putThroughput)++------------------------------------------------------------------------++doPut :: Int -> Int -> Endian -> Int -> Put+doPut wordSize chunkSize end = case (wordSize, chunkSize, end) of+ (1, 1,_) -> putWord8N1+ (1, 2,_) -> putWord8N2+ (1, 4,_) -> putWord8N4+ (1, 8,_) -> putWord8N8+ (1, 16, _) -> putWord8N16++ (2, 1, Big) -> putWord16N1Big+ (2, 2, Big) -> putWord16N2Big+ (2, 4, Big) -> putWord16N4Big+ (2, 8, Big) -> putWord16N8Big+ (2, 16, Big) -> putWord16N16Big+ (2, 1, Little) -> putWord16N1Little+ (2, 2, Little) -> putWord16N2Little+ (2, 4, Little) -> putWord16N4Little+ (2, 8, Little) -> putWord16N8Little+ (2, 16, Little) -> putWord16N16Little+ (2, 1, Host) -> putWord16N1Host+ (2, 2, Host) -> putWord16N2Host+ (2, 4, Host) -> putWord16N4Host+ (2, 8, Host) -> putWord16N8Host+ (2, 16, Host) -> putWord16N16Host++ (4, 1, Big) -> putWord32N1Big+ (4, 2, Big) -> putWord32N2Big+ (4, 4, Big) -> putWord32N4Big+ (4, 8, Big) -> putWord32N8Big+ (4, 16, Big) -> putWord32N16Big+ (4, 1, Little) -> putWord32N1Little+ (4, 2, Little) -> putWord32N2Little+ (4, 4, Little) -> putWord32N4Little+ (4, 8, Little) -> putWord32N8Little+ (4, 16, Little) -> putWord32N16Little+ (4, 1, Host) -> putWord32N1Host+ (4, 2, Host) -> putWord32N2Host+ (4, 4, Host) -> putWord32N4Host+ (4, 8, Host) -> putWord32N8Host+ (4, 16, Host) -> putWord32N16Host++ (8, 1, Host) -> putWord64N1Host+ (8, 2, Host) -> putWord64N2Host+ (8, 4, Host) -> putWord64N4Host+ (8, 8, Host) -> putWord64N8Host+ (8, 16, Host) -> putWord64N16Host+ (8, 1, Big) -> putWord64N1Big+ (8, 2, Big) -> putWord64N2Big+ (8, 4, Big) -> putWord64N4Big+ (8, 8, Big) -> putWord64N8Big+ (8, 16, Big) -> putWord64N16Big+ (8, 1, Little) -> putWord64N1Little+ (8, 2, Little) -> putWord64N2Little+ (8, 4, Little) -> putWord64N4Little+ (8, 8, Little) -> putWord64N8Little+ (8, 16, Little) -> putWord64N16Little++------------------------------------------------------------------------++doGet :: Int -> Int -> Endian -> Int -> Get Int+doGet wordSize chunkSize end =+ case (wordSize, chunkSize, end) of+ (1, 1,_) -> fmap fromIntegral . getWord8N1+ (1, 2,_) -> fmap fromIntegral . getWord8N2+ (1, 4,_) -> fmap fromIntegral . getWord8N4+ (1, 8,_) -> fmap fromIntegral . getWord8N8+ (1, 16,_) -> fmap fromIntegral . getWord8N16++ (2, 1,Big) -> fmap fromIntegral . getWord16N1Big+ (2, 2,Big) -> fmap fromIntegral . getWord16N2Big+ (2, 4,Big) -> fmap fromIntegral . getWord16N4Big+ (2, 8,Big) -> fmap fromIntegral . getWord16N8Big+ (2, 16,Big) -> fmap fromIntegral . getWord16N16Big+ (2, 1,Little) -> fmap fromIntegral . getWord16N1Little+ (2, 2,Little) -> fmap fromIntegral . getWord16N2Little+ (2, 4,Little) -> fmap fromIntegral . getWord16N4Little+ (2, 8,Little) -> fmap fromIntegral . getWord16N8Little+ (2, 16,Little) -> fmap fromIntegral . getWord16N16Little+ (2, 1,Host) -> fmap fromIntegral . getWord16N1Host+ (2, 2,Host) -> fmap fromIntegral . getWord16N2Host+ (2, 4,Host) -> fmap fromIntegral . getWord16N4Host+ (2, 8,Host) -> fmap fromIntegral . getWord16N8Host+ (2, 16,Host) -> fmap fromIntegral . getWord16N16Host++ (4, 1,Big) -> fmap fromIntegral . getWord32N1Big+ (4, 2,Big) -> fmap fromIntegral . getWord32N2Big+ (4, 4,Big) -> fmap fromIntegral . getWord32N4Big+ (4, 8,Big) -> fmap fromIntegral . getWord32N8Big+ (4, 16,Big) -> fmap fromIntegral . getWord32N16Big+ (4, 1,Little) -> fmap fromIntegral . getWord32N1Little+ (4, 2,Little) -> fmap fromIntegral . getWord32N2Little+ (4, 4,Little) -> fmap fromIntegral . getWord32N4Little+ (4, 8,Little) -> fmap fromIntegral . getWord32N8Little+ (4, 16,Little) -> fmap fromIntegral . getWord32N16Little+ (4, 1,Host) -> fmap fromIntegral . getWord32N1Host+ (4, 2,Host) -> fmap fromIntegral . getWord32N2Host+ (4, 4,Host) -> fmap fromIntegral . getWord32N4Host+ (4, 8,Host) -> fmap fromIntegral . getWord32N8Host+ (4, 16,Host) -> fmap fromIntegral . getWord32N16Host++ (8, 1,Host) -> fmap fromIntegral . getWord64N1Host+ (8, 2,Host) -> fmap fromIntegral . getWord64N2Host+ (8, 4,Host) -> fmap fromIntegral . getWord64N4Host+ (8, 8,Host) -> fmap fromIntegral . getWord64N8Host+ (8, 16,Host) -> fmap fromIntegral . getWord64N16Host+ (8, 1,Big) -> fmap fromIntegral . getWord64N1Big+ (8, 2,Big) -> fmap fromIntegral . getWord64N2Big+ (8, 4,Big) -> fmap fromIntegral . getWord64N4Big+ (8, 8,Big) -> fmap fromIntegral . getWord64N8Big+ (8, 16,Big) -> fmap fromIntegral . getWord64N16Big+ (8, 1,Little) -> fmap fromIntegral . getWord64N1Little+ (8, 2,Little) -> fmap fromIntegral . getWord64N2Little+ (8, 4,Little) -> fmap fromIntegral . getWord64N4Little+ (8, 8,Little) -> fmap fromIntegral . getWord64N8Little+ (8, 16,Little) -> fmap fromIntegral . getWord64N16Little++------------------------------------------------------------------------++putWord8N1 bytes = loop 0 0+ where loop :: Word8 -> Int -> Put+ loop !s !n | n == bytes = return ()+ | otherwise = do putWord8 s+ loop (s+1) (n+1)++putWord8N2 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ loop (s+2) (n-2)++putWord8N4 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ putWord8 (s+2)+ putWord8 (s+3)+ loop (s+4) (n-4)++putWord8N8 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ putWord8 (s+2)+ putWord8 (s+3)+ putWord8 (s+4)+ putWord8 (s+5)+ putWord8 (s+6)+ putWord8 (s+7)+ loop (s+8) (n-8)++putWord8N16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord8 (s+0)+ putWord8 (s+1)+ putWord8 (s+2)+ putWord8 (s+3)+ putWord8 (s+4)+ putWord8 (s+5)+ putWord8 (s+6)+ putWord8 (s+7)+ putWord8 (s+8)+ putWord8 (s+9)+ putWord8 (s+10)+ putWord8 (s+11)+ putWord8 (s+12)+ putWord8 (s+13)+ putWord8 (s+14)+ putWord8 (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Big endian, word16 writes++putWord16N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ loop (s+1) (n-1)++putWord16N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ loop (s+2) (n-2)++putWord16N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ putWord16be (s+2)+ putWord16be (s+3)+ loop (s+4) (n-4)++putWord16N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ putWord16be (s+2)+ putWord16be (s+3)+ putWord16be (s+4)+ putWord16be (s+5)+ putWord16be (s+6)+ putWord16be (s+7)+ loop (s+8) (n-8)++putWord16N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16be (s+0)+ putWord16be (s+1)+ putWord16be (s+2)+ putWord16be (s+3)+ putWord16be (s+4)+ putWord16be (s+5)+ putWord16be (s+6)+ putWord16be (s+7)+ putWord16be (s+8)+ putWord16be (s+9)+ putWord16be (s+10)+ putWord16be (s+11)+ putWord16be (s+12)+ putWord16be (s+13)+ putWord16be (s+14)+ putWord16be (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Little endian, word16 writes++putWord16N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ loop (s+1) (n-1)++putWord16N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ loop (s+2) (n-2)++putWord16N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ putWord16le (s+2)+ putWord16le (s+3)+ loop (s+4) (n-4)++putWord16N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ putWord16le (s+2)+ putWord16le (s+3)+ putWord16le (s+4)+ putWord16le (s+5)+ putWord16le (s+6)+ putWord16le (s+7)+ loop (s+8) (n-8)++putWord16N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16le (s+0)+ putWord16le (s+1)+ putWord16le (s+2)+ putWord16le (s+3)+ putWord16le (s+4)+ putWord16le (s+5)+ putWord16le (s+6)+ putWord16le (s+7)+ putWord16le (s+8)+ putWord16le (s+9)+ putWord16le (s+10)+ putWord16le (s+11)+ putWord16le (s+12)+ putWord16le (s+13)+ putWord16le (s+14)+ putWord16le (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Host endian, unaligned, word16 writes++putWord16N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ loop (s+1) (n-1)++putWord16N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ loop (s+2) (n-2)++putWord16N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ putWord16host (s+2)+ putWord16host (s+3)+ loop (s+4) (n-4)++putWord16N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ putWord16host (s+2)+ putWord16host (s+3)+ putWord16host (s+4)+ putWord16host (s+5)+ putWord16host (s+6)+ putWord16host (s+7)+ loop (s+8) (n-8)++putWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord16host (s+0)+ putWord16host (s+1)+ putWord16host (s+2)+ putWord16host (s+3)+ putWord16host (s+4)+ putWord16host (s+5)+ putWord16host (s+6)+ putWord16host (s+7)+ putWord16host (s+8)+ putWord16host (s+9)+ putWord16host (s+10)+ putWord16host (s+11)+ putWord16host (s+12)+ putWord16host (s+13)+ putWord16host (s+14)+ putWord16host (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord32N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ loop (s+1) (n-1)++putWord32N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ loop (s+2) (n-2)++putWord32N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ putWord32be (s+2)+ putWord32be (s+3)+ loop (s+4) (n-4)++putWord32N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ putWord32be (s+2)+ putWord32be (s+3)+ putWord32be (s+4)+ putWord32be (s+5)+ putWord32be (s+6)+ putWord32be (s+7)+ loop (s+8) (n-8)++putWord32N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32be (s+0)+ putWord32be (s+1)+ putWord32be (s+2)+ putWord32be (s+3)+ putWord32be (s+4)+ putWord32be (s+5)+ putWord32be (s+6)+ putWord32be (s+7)+ putWord32be (s+8)+ putWord32be (s+9)+ putWord32be (s+10)+ putWord32be (s+11)+ putWord32be (s+12)+ putWord32be (s+13)+ putWord32be (s+14)+ putWord32be (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord32N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ loop (s+1) (n-1)++putWord32N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ loop (s+2) (n-2)++putWord32N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ putWord32le (s+2)+ putWord32le (s+3)+ loop (s+4) (n-4)++putWord32N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ putWord32le (s+2)+ putWord32le (s+3)+ putWord32le (s+4)+ putWord32le (s+5)+ putWord32le (s+6)+ putWord32le (s+7)+ loop (s+8) (n-8)++putWord32N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32le (s+0)+ putWord32le (s+1)+ putWord32le (s+2)+ putWord32le (s+3)+ putWord32le (s+4)+ putWord32le (s+5)+ putWord32le (s+6)+ putWord32le (s+7)+ putWord32le (s+8)+ putWord32le (s+9)+ putWord32le (s+10)+ putWord32le (s+11)+ putWord32le (s+12)+ putWord32le (s+13)+ putWord32le (s+14)+ putWord32le (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord32N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ loop (s+1) (n-1)++putWord32N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ loop (s+2) (n-2)++putWord32N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ putWord32host (s+2)+ putWord32host (s+3)+ loop (s+4) (n-4)++putWord32N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ putWord32host (s+2)+ putWord32host (s+3)+ putWord32host (s+4)+ putWord32host (s+5)+ putWord32host (s+6)+ putWord32host (s+7)+ loop (s+8) (n-8)++putWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord32host (s+0)+ putWord32host (s+1)+ putWord32host (s+2)+ putWord32host (s+3)+ putWord32host (s+4)+ putWord32host (s+5)+ putWord32host (s+6)+ putWord32host (s+7)+ putWord32host (s+8)+ putWord32host (s+9)+ putWord32host (s+10)+ putWord32host (s+11)+ putWord32host (s+12)+ putWord32host (s+13)+ putWord32host (s+14)+ putWord32host (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord64N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ loop (s+1) (n-1)++putWord64N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ loop (s+2) (n-2)++putWord64N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ putWord64be (s+2)+ putWord64be (s+3)+ loop (s+4) (n-4)++putWord64N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ putWord64be (s+2)+ putWord64be (s+3)+ putWord64be (s+4)+ putWord64be (s+5)+ putWord64be (s+6)+ putWord64be (s+7)+ loop (s+8) (n-8)++putWord64N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64be (s+0)+ putWord64be (s+1)+ putWord64be (s+2)+ putWord64be (s+3)+ putWord64be (s+4)+ putWord64be (s+5)+ putWord64be (s+6)+ putWord64be (s+7)+ putWord64be (s+8)+ putWord64be (s+9)+ putWord64be (s+10)+ putWord64be (s+11)+ putWord64be (s+12)+ putWord64be (s+13)+ putWord64be (s+14)+ putWord64be (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord64N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ loop (s+1) (n-1)++putWord64N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ loop (s+2) (n-2)++putWord64N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ putWord64le (s+2)+ putWord64le (s+3)+ loop (s+4) (n-4)++putWord64N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ putWord64le (s+2)+ putWord64le (s+3)+ putWord64le (s+4)+ putWord64le (s+5)+ putWord64le (s+6)+ putWord64le (s+7)+ loop (s+8) (n-8)++putWord64N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64le (s+0)+ putWord64le (s+1)+ putWord64le (s+2)+ putWord64le (s+3)+ putWord64le (s+4)+ putWord64le (s+5)+ putWord64le (s+6)+ putWord64le (s+7)+ putWord64le (s+8)+ putWord64le (s+9)+ putWord64le (s+10)+ putWord64le (s+11)+ putWord64le (s+12)+ putWord64le (s+13)+ putWord64le (s+14)+ putWord64le (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------++putWord64N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ loop (s+1) (n-1)++putWord64N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ loop (s+2) (n-2)++putWord64N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ putWord64host (s+2)+ putWord64host (s+3)+ loop (s+4) (n-4)++putWord64N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ putWord64host (s+2)+ putWord64host (s+3)+ putWord64host (s+4)+ putWord64host (s+5)+ putWord64host (s+6)+ putWord64host (s+7)+ loop (s+8) (n-8)++putWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = return ()+ loop s n = do+ putWord64host (s+0)+ putWord64host (s+1)+ putWord64host (s+2)+ putWord64host (s+3)+ putWord64host (s+4)+ putWord64host (s+5)+ putWord64host (s+6)+ putWord64host (s+7)+ putWord64host (s+8)+ putWord64host (s+9)+ putWord64host (s+10)+ putWord64host (s+11)+ putWord64host (s+12)+ putWord64host (s+13)+ putWord64host (s+14)+ putWord64host (s+15)+ loop (s+16) (n-16)++------------------------------------------------------------------------+------------------------------------------------------------------------++getWord8N1 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ loop (s+s0) (n-1)++getWord8N2 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ loop (s+s0+s1) (n-2)++getWord8N4 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ s2 <- getWord8+ s3 <- getWord8+ loop (s+s0+s1+s2+s3) (n-4)++getWord8N8 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ s2 <- getWord8+ s3 <- getWord8+ s4 <- getWord8+ s5 <- getWord8+ s6 <- getWord8+ s7 <- getWord8+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord8N16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ s2 <- getWord8+ s3 <- getWord8+ s4 <- getWord8+ s5 <- getWord8+ s6 <- getWord8+ s7 <- getWord8+ s8 <- getWord8+ s9 <- getWord8+ s10 <- getWord8+ s11 <- getWord8+ s12 <- getWord8+ s13 <- getWord8+ s14 <- getWord8+ s15 <- getWord8+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord16N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16be+ loop (s+s0) (n-1)++getWord16N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16be+ s1 <- getWord16be+ loop (s+s0+s1) (n-2)++getWord16N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16be+ s1 <- getWord16be+ s2 <- getWord16be+ s3 <- getWord16be+ loop (s+s0+s1+s2+s3) (n-4)++getWord16N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16be+ s1 <- getWord16be+ s2 <- getWord16be+ s3 <- getWord16be+ s4 <- getWord16be+ s5 <- getWord16be+ s6 <- getWord16be+ s7 <- getWord16be+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord16N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16be+ s1 <- getWord16be+ s2 <- getWord16be+ s3 <- getWord16be+ s4 <- getWord16be+ s5 <- getWord16be+ s6 <- getWord16be+ s7 <- getWord16be+ s8 <- getWord16be+ s9 <- getWord16be+ s10 <- getWord16be+ s11 <- getWord16be+ s12 <- getWord16be+ s13 <- getWord16be+ s14 <- getWord16be+ s15 <- getWord16be+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord16N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16le+ loop (s+s0) (n-1)++getWord16N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16le+ s1 <- getWord16le+ loop (s+s0+s1) (n-2)++getWord16N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16le+ s1 <- getWord16le+ s2 <- getWord16le+ s3 <- getWord16le+ loop (s+s0+s1+s2+s3) (n-4)++getWord16N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16le+ s1 <- getWord16le+ s2 <- getWord16le+ s3 <- getWord16le+ s4 <- getWord16le+ s5 <- getWord16le+ s6 <- getWord16le+ s7 <- getWord16le+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord16N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16le+ s1 <- getWord16le+ s2 <- getWord16le+ s3 <- getWord16le+ s4 <- getWord16le+ s5 <- getWord16le+ s6 <- getWord16le+ s7 <- getWord16le+ s8 <- getWord16le+ s9 <- getWord16le+ s10 <- getWord16le+ s11 <- getWord16le+ s12 <- getWord16le+ s13 <- getWord16le+ s14 <- getWord16le+ s15 <- getWord16le+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord16N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16host+ loop (s+s0) (n-1)++getWord16N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16host+ s1 <- getWord16host+ loop (s+s0+s1) (n-2)++getWord16N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16host+ s1 <- getWord16host+ s2 <- getWord16host+ s3 <- getWord16host+ loop (s+s0+s1+s2+s3) (n-4)++getWord16N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16host+ s1 <- getWord16host+ s2 <- getWord16host+ s3 <- getWord16host+ s4 <- getWord16host+ s5 <- getWord16host+ s6 <- getWord16host+ s7 <- getWord16host+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord16host+ s1 <- getWord16host+ s2 <- getWord16host+ s3 <- getWord16host+ s4 <- getWord16host+ s5 <- getWord16host+ s6 <- getWord16host+ s7 <- getWord16host+ s8 <- getWord16host+ s9 <- getWord16host+ s10 <- getWord16host+ s11 <- getWord16host+ s12 <- getWord16host+ s13 <- getWord16host+ s14 <- getWord16host+ s15 <- getWord16host+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord32N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32be+ loop (s+s0) (n-1)++getWord32N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32be+ s1 <- getWord32be+ loop (s+s0+s1) (n-2)++getWord32N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32be+ s1 <- getWord32be+ s2 <- getWord32be+ s3 <- getWord32be+ loop (s+s0+s1+s2+s3) (n-4)++getWord32N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32be+ s1 <- getWord32be+ s2 <- getWord32be+ s3 <- getWord32be+ s4 <- getWord32be+ s5 <- getWord32be+ s6 <- getWord32be+ s7 <- getWord32be+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++-- getWordhostN16 = loop 0+getWord32N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32be+ s1 <- getWord32be+ s2 <- getWord32be+ s3 <- getWord32be+ s4 <- getWord32be+ s5 <- getWord32be+ s6 <- getWord32be+ s7 <- getWord32be+ s8 <- getWord32be+ s9 <- getWord32be+ s10 <- getWord32be+ s11 <- getWord32be+ s12 <- getWord32be+ s13 <- getWord32be+ s14 <- getWord32be+ s15 <- getWord32be+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord32N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32le+ loop (s+s0) (n-1)++getWord32N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32le+ s1 <- getWord32le+ loop (s+s0+s1) (n-2)++getWord32N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32le+ s1 <- getWord32le+ s2 <- getWord32le+ s3 <- getWord32le+ loop (s+s0+s1+s2+s3) (n-4)++getWord32N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32le+ s1 <- getWord32le+ s2 <- getWord32le+ s3 <- getWord32le+ s4 <- getWord32le+ s5 <- getWord32le+ s6 <- getWord32le+ s7 <- getWord32le+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++-- getWordhostN16 = loop 0+getWord32N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32le+ s1 <- getWord32le+ s2 <- getWord32le+ s3 <- getWord32le+ s4 <- getWord32le+ s5 <- getWord32le+ s6 <- getWord32le+ s7 <- getWord32le+ s8 <- getWord32le+ s9 <- getWord32le+ s10 <- getWord32le+ s11 <- getWord32le+ s12 <- getWord32le+ s13 <- getWord32le+ s14 <- getWord32le+ s15 <- getWord32le+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord32N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32host+ loop (s+s0) (n-1)++getWord32N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32host+ s1 <- getWord32host+ loop (s+s0+s1) (n-2)++getWord32N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32host+ s1 <- getWord32host+ s2 <- getWord32host+ s3 <- getWord32host+ loop (s+s0+s1+s2+s3) (n-4)++getWord32N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32host+ s1 <- getWord32host+ s2 <- getWord32host+ s3 <- getWord32host+ s4 <- getWord32host+ s5 <- getWord32host+ s6 <- getWord32host+ s7 <- getWord32host+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++-- getWordhostN16 = loop 0+getWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord32host+ s1 <- getWord32host+ s2 <- getWord32host+ s3 <- getWord32host+ s4 <- getWord32host+ s5 <- getWord32host+ s6 <- getWord32host+ s7 <- getWord32host+ s8 <- getWord32host+ s9 <- getWord32host+ s10 <- getWord32host+ s11 <- getWord32host+ s12 <- getWord32host+ s13 <- getWord32host+ s14 <- getWord32host+ s15 <- getWord32host+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord64N1Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64be+ loop (s+s0) (n-1)++getWord64N2Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64be+ s1 <- getWord64be+ loop (s+s0+s1) (n-2)++getWord64N4Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64be+ s1 <- getWord64be+ s2 <- getWord64be+ s3 <- getWord64be+ loop (s+s0+s1+s2+s3) (n-4)++getWord64N8Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64be+ s1 <- getWord64be+ s2 <- getWord64be+ s3 <- getWord64be+ s4 <- getWord64be+ s5 <- getWord64be+ s6 <- getWord64be+ s7 <- getWord64be+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord64N16Big = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64be+ s1 <- getWord64be+ s2 <- getWord64be+ s3 <- getWord64be+ s4 <- getWord64be+ s5 <- getWord64be+ s6 <- getWord64be+ s7 <- getWord64be+ s8 <- getWord64be+ s9 <- getWord64be+ s10 <- getWord64be+ s11 <- getWord64be+ s12 <- getWord64be+ s13 <- getWord64be+ s14 <- getWord64be+ s15 <- getWord64be+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord64N1Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64le+ loop (s+s0) (n-1)++getWord64N2Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64le+ s1 <- getWord64le+ loop (s+s0+s1) (n-2)++getWord64N4Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64le+ s1 <- getWord64le+ s2 <- getWord64le+ s3 <- getWord64le+ loop (s+s0+s1+s2+s3) (n-4)++getWord64N8Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64le+ s1 <- getWord64le+ s2 <- getWord64le+ s3 <- getWord64le+ s4 <- getWord64le+ s5 <- getWord64le+ s6 <- getWord64le+ s7 <- getWord64le+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord64N16Little = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64le+ s1 <- getWord64le+ s2 <- getWord64le+ s3 <- getWord64le+ s4 <- getWord64le+ s5 <- getWord64le+ s6 <- getWord64le+ s7 <- getWord64le+ s8 <- getWord64le+ s9 <- getWord64le+ s10 <- getWord64le+ s11 <- getWord64le+ s12 <- getWord64le+ s13 <- getWord64le+ s14 <- getWord64le+ s15 <- getWord64le+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)++------------------------------------------------------------------------++getWord64N1Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64host+ loop (s+s0) (n-1)++getWord64N2Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64host+ s1 <- getWord64host+ loop (s+s0+s1) (n-2)++getWord64N4Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64host+ s1 <- getWord64host+ s2 <- getWord64host+ s3 <- getWord64host+ loop (s+s0+s1+s2+s3) (n-4)++getWord64N8Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64host+ s1 <- getWord64host+ s2 <- getWord64host+ s3 <- getWord64host+ s4 <- getWord64host+ s5 <- getWord64host+ s6 <- getWord64host+ s7 <- getWord64host+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7) (n-8)++getWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord64host+ s1 <- getWord64host+ s2 <- getWord64host+ s3 <- getWord64host+ s4 <- getWord64host+ s5 <- getWord64host+ s6 <- getWord64host+ s7 <- getWord64host+ s8 <- getWord64host+ s9 <- getWord64host+ s10 <- getWord64host+ s11 <- getWord64host+ s12 <- getWord64host+ s13 <- getWord64host+ s14 <- getWord64host+ s15 <- getWord64host+ loop (s+s0+s1+s2+s3+s4+s5+s6+s7+s9+s10+s11+s12+s13+s14+s15) (n-16)
+ benchmarks/Builder.hs view
@@ -0,0 +1,197 @@+{-# LANGUAGE CPP, ExistentialQuantification #-}++#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+#include "MachDeps.h"+#endif++module Main (main) where++#if ! MIN_VERSION_base(4,8,0)+import Data.Monoid (Monoid(mappend, mempty))+#endif++import Control.DeepSeq+import Control.Exception (evaluate)+import Criterion.Main+import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as C+import qualified Data.ByteString.Lazy as L+import Data.Char (ord)+import Data.Word (Word8)++import Data.Binary.Builder++main :: IO ()+main = do+ evaluate $ rnf+ [ rnf word8s+ , rnf smallByteString+ , rnf largeByteString+ ]+ defaultMain+ [ -- Test GHC loop optimization of continuation based code.+ bench "[Word8]" $ whnf (run . fromWord8s) word8s++ -- Test bounds check merging+ , bench "bounds/[Word8]" $ whnf (run . from4Word8s) word8s++ , bench "small ByteString" $ whnf (run . fromByteString) smallByteString+ , bench "large ByteString" $ whnf (run . fromByteString) largeByteString+ , bench "length-prefixed ByteString" $ whnf (run . lengthPrefixedBS)+ smallByteString++ , bgroup "Host endian"+ [ bench "1MB of Word8 in chunks of 16" $ whnf (run . putWord8N16) n+ , bench "1MB of Word16 in chunks of 16" $ whnf (run . putWord16N16Host)+ (n `div` 2)+ , bench "1MB of Word32 in chunks of 16" $ whnf (run . putWord32N16Host)+ (n `div` 4)+ , bench "1MB of Word64 in chunks of 16" $ whnf (run . putWord64N16Host)+ (n `div` 8)+ ]+ ]+ where+ run = L.length . toLazyByteString+ n = 1 * (2 ^ (20 :: Int)) -- one MB++-- Input data++word8s :: [Word8]+word8s = replicate 10000 $ fromIntegral $ ord 'a'+{-# NOINLINE word8s #-}++smallByteString :: S.ByteString+smallByteString = C.pack "abcdefghi"++largeByteString :: S.ByteString+largeByteString = S.pack word8s++------------------------------------------------------------------------+-- Benchmarks++fromWord8s :: [Word8] -> Builder+fromWord8s [] = mempty+fromWord8s (x:xs) = singleton x <> fromWord8s xs++from4Word8s :: [Word8] -> Builder+from4Word8s [] = mempty+from4Word8s (x:xs) = singleton x <> singleton x <> singleton x <> singleton x <>+ from4Word8s xs++-- Write 100 short, length-prefixed ByteStrings.+lengthPrefixedBS :: S.ByteString -> Builder+lengthPrefixedBS bs = loop (100 :: Int)+ where loop n | n `seq` False = undefined+ loop 0 = mempty+ loop n =+#if WORD_SIZE_IN_BITS == 32+ putWord32be (fromIntegral $ S.length bs) <>+#elif WORD_SIZE_IN_BITS == 64+ putWord64be (fromIntegral $ S.length bs) <>+#else+# error Unsupported platform+#endif+ fromByteString bs <>+ loop (n-1)++putWord8N16 :: Int -> Builder+putWord8N16 = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n =+ singleton (s+0) <>+ singleton (s+1) <>+ singleton (s+2) <>+ singleton (s+3) <>+ singleton (s+4) <>+ singleton (s+5) <>+ singleton (s+6) <>+ singleton (s+7) <>+ singleton (s+8) <>+ singleton (s+9) <>+ singleton (s+10) <>+ singleton (s+11) <>+ singleton (s+12) <>+ singleton (s+13) <>+ singleton (s+14) <>+ singleton (s+15) <>+ loop (s+16) (n-16)++putWord16N16Host :: Int -> Builder+putWord16N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n =+ putWord16host (s+0) <>+ putWord16host (s+1) <>+ putWord16host (s+2) <>+ putWord16host (s+3) <>+ putWord16host (s+4) <>+ putWord16host (s+5) <>+ putWord16host (s+6) <>+ putWord16host (s+7) <>+ putWord16host (s+8) <>+ putWord16host (s+9) <>+ putWord16host (s+10) <>+ putWord16host (s+11) <>+ putWord16host (s+12) <>+ putWord16host (s+13) <>+ putWord16host (s+14) <>+ putWord16host (s+15) <>+ loop (s+16) (n-16)++putWord32N16Host :: Int -> Builder+putWord32N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n =+ putWord32host (s+0) <>+ putWord32host (s+1) <>+ putWord32host (s+2) <>+ putWord32host (s+3) <>+ putWord32host (s+4) <>+ putWord32host (s+5) <>+ putWord32host (s+6) <>+ putWord32host (s+7) <>+ putWord32host (s+8) <>+ putWord32host (s+9) <>+ putWord32host (s+10) <>+ putWord32host (s+11) <>+ putWord32host (s+12) <>+ putWord32host (s+13) <>+ putWord32host (s+14) <>+ putWord32host (s+15) <>+ loop (s+16) (n-16)++putWord64N16Host :: Int -> Builder+putWord64N16Host = loop 0+ where loop s n | s `seq` n `seq` False = undefined+ loop _ 0 = mempty+ loop s n =+ putWord64host (s+0) <>+ putWord64host (s+1) <>+ putWord64host (s+2) <>+ putWord64host (s+3) <>+ putWord64host (s+4) <>+ putWord64host (s+5) <>+ putWord64host (s+6) <>+ putWord64host (s+7) <>+ putWord64host (s+8) <>+ putWord64host (s+9) <>+ putWord64host (s+10) <>+ putWord64host (s+11) <>+ putWord64host (s+12) <>+ putWord64host (s+13) <>+ putWord64host (s+14) <>+ putWord64host (s+15) <>+ loop (s+16) (n-16)++------------------------------------------------------------------------+-- Utilities++#if !MIN_VERSION_base(4,11,0)+infixr 6 <>++(<>) :: Monoid m => m -> m -> m+(<>) = mappend+#endif
+ benchmarks/CBenchmark.c view
@@ -0,0 +1,39 @@+#include "CBenchmark.h"++void bytewrite(unsigned char *a, int bytes) {+ unsigned char n = 0;+ int i = 0;+ int iterations = bytes;+ while (i < iterations) {+ a[i++] = n++;+ }+}++unsigned char byteread(unsigned char *a, int bytes) {+ unsigned char n = 0;+ int i = 0;+ int iterations = bytes;+ while (i < iterations) {+ n += a[i++];+ }+ return n;+}++void wordwrite(unsigned long *a, int bytes) {+ unsigned long n = 0;+ int i = 0;+ int iterations = bytes / sizeof(unsigned long) ;+ while (i < iterations) {+ a[i++] = n++;+ }+}++unsigned int wordread(unsigned long *a, int bytes) {+ unsigned long n = 0;+ int i = 0;+ int iterations = bytes / sizeof(unsigned long);+ while (i < iterations) {+ n += a[i++];+ }+ return n;+}
+ benchmarks/CBenchmark.h view
@@ -0,0 +1,4 @@+void bytewrite(unsigned char *a, int bytes);+unsigned char byteread(unsigned char *a, int bytes);+void wordwrite(unsigned long *a, int bytes);+unsigned int wordread(unsigned long *a, int bytes);
+ benchmarks/Cabal24.hs view
@@ -0,0 +1,360 @@+{-# LANGUAGE DeriveGeneric #-}++-- | This module contains type definitions copied from Cabal-1.24.2.0+-- to avoid a dependency on Cabal. Their contents for the benchmark are read+-- from a cache file using their 'Read' instance, see "GenericsBenchCache".+--+module Cabal24 where++import Data.Version (Version)+import GHC.Generics (Generic)+import Data.Map (Map)++data Benchmark = Benchmark {+ benchmarkName :: String,+ benchmarkInterface :: BenchmarkInterface,+ benchmarkBuildInfo :: BuildInfo,+ benchmarkEnabled :: Bool+ } deriving (Generic, Eq, Ord, Read, Show)++data BenchmarkInterface =+ BenchmarkExeV10 Version FilePath+ | BenchmarkUnsupported BenchmarkType+ deriving (Generic, Eq, Ord, Read, Show)++data BenchmarkType = BenchmarkTypeExe Version+ | BenchmarkTypeUnknown String Version+ deriving (Generic, Eq, Ord, Read, Show)++data BuildInfo = BuildInfo {+ buildable :: Bool,+ buildTools :: [Dependency],+ cppOptions :: [String],+ ccOptions :: [String],+ ldOptions :: [String],+ pkgconfigDepends :: [Dependency],+ frameworks :: [String],+ extraFrameworkDirs:: [String],+ cSources :: [FilePath],+ jsSources :: [FilePath],+ hsSourceDirs :: [FilePath],+ otherModules :: [ModuleName],+ defaultLanguage :: Maybe Language,+ otherLanguages :: [Language],+ defaultExtensions :: [Extension],+ otherExtensions :: [Extension],+ oldExtensions :: [Extension],+ extraLibs :: [String],+ extraGHCiLibs :: [String],+ extraLibDirs :: [String],+ includeDirs :: [FilePath],+ includes :: [FilePath],+ installIncludes :: [FilePath],+ options :: [(CompilerFlavor,[String])],+ profOptions :: [(CompilerFlavor,[String])],+ sharedOptions :: [(CompilerFlavor,[String])],+ customFieldsBI :: [(String,String)],+ targetBuildDepends :: [Dependency],+ targetBuildRenaming :: Map PackageName ModuleRenaming+ } deriving (Generic, Eq, Ord, Read, Show)++data BuildType+ = Simple+ | Configure+ | Make+ | Custom+ | UnknownBuildType String+ deriving (Generic, Eq, Ord, Read, Show)++data CompilerFlavor = GHC | GHCJS | NHC | YHC | Hugs | HBC | Helium+ | JHC | LHC | UHC+ | HaskellSuite String+ | OtherCompiler String+ deriving (Generic, Eq, Ord, Read, Show)++data Dependency = Dependency PackageName VersionRange+ deriving (Generic, Eq, Ord, Read, Show)++data Executable = Executable {+ exeName :: String,+ modulePath :: FilePath,+ buildInfo :: BuildInfo+ }+ deriving (Generic, Eq, Ord, Read, Show)++data Extension =+ EnableExtension KnownExtension+ | DisableExtension KnownExtension+ | UnknownExtension String+ deriving (Generic, Eq, Ord, Read, Show)++newtype FlagName = FlagName String+ deriving (Generic, Eq, Ord, Read, Show)++data KnownExtension =+ OverlappingInstances+ | UndecidableInstances+ | IncoherentInstances+ | DoRec+ | RecursiveDo+ | ParallelListComp+ | MultiParamTypeClasses+ | MonomorphismRestriction+ | FunctionalDependencies+ | Rank2Types+ | RankNTypes+ | PolymorphicComponents+ | ExistentialQuantification+ | ScopedTypeVariables+ | PatternSignatures+ | ImplicitParams+ | FlexibleContexts+ | FlexibleInstances+ | EmptyDataDecls+ | CPP+ | KindSignatures+ | BangPatterns+ | TypeSynonymInstances+ | TemplateHaskell+ | ForeignFunctionInterface+ | Arrows+ | Generics+ | ImplicitPrelude+ | NamedFieldPuns+ | PatternGuards+ | GeneralizedNewtypeDeriving+ | ExtensibleRecords+ | RestrictedTypeSynonyms+ | HereDocuments+ | MagicHash+ | TypeFamilies+ | StandaloneDeriving+ | UnicodeSyntax+ | UnliftedFFITypes+ | InterruptibleFFI+ | CApiFFI+ | LiberalTypeSynonyms+ | TypeOperators+ | RecordWildCards+ | RecordPuns+ | DisambiguateRecordFields+ | TraditionalRecordSyntax+ | OverloadedStrings+ | GADTs+ | GADTSyntax+ | MonoPatBinds+ | RelaxedPolyRec+ | ExtendedDefaultRules+ | UnboxedTuples+ | DeriveDataTypeable+ | DeriveGeneric+ | DefaultSignatures+ | InstanceSigs+ | ConstrainedClassMethods+ | PackageImports+ | ImpredicativeTypes+ | NewQualifiedOperators+ | PostfixOperators+ | QuasiQuotes+ | TransformListComp+ | MonadComprehensions+ | ViewPatterns+ | XmlSyntax+ | RegularPatterns+ | TupleSections+ | GHCForeignImportPrim+ | NPlusKPatterns+ | DoAndIfThenElse+ | MultiWayIf+ | LambdaCase+ | RebindableSyntax+ | ExplicitForAll+ | DatatypeContexts+ | MonoLocalBinds+ | DeriveFunctor+ | DeriveTraversable+ | DeriveFoldable+ | NondecreasingIndentation+ | SafeImports+ | Safe+ | Trustworthy+ | Unsafe+ | ConstraintKinds+ | PolyKinds+ | DataKinds+ | ParallelArrays+ | RoleAnnotations+ | OverloadedLists+ | EmptyCase+ | AutoDeriveTypeable+ | NegativeLiterals+ | BinaryLiterals+ | NumDecimals+ | NullaryTypeClasses+ | ExplicitNamespaces+ | AllowAmbiguousTypes+ | JavaScriptFFI+ | PatternSynonyms+ | PartialTypeSignatures+ | NamedWildCards+ | DeriveAnyClass+ | DeriveLift+ | StaticPointers+ | StrictData+ | Strict+ | ApplicativeDo+ | DuplicateRecordFields+ | TypeApplications+ | TypeInType+ | UndecidableSuperClasses+ | MonadFailDesugaring+ | TemplateHaskellQuotes+ | OverloadedLabels+ deriving (Generic, Eq, Ord, Read, Show)++data Language =+ Haskell98+ | Haskell2010+ | UnknownLanguage String+ deriving (Generic, Eq, Ord, Read, Show)++data Library = Library {+ exposedModules :: [ModuleName],+ reexportedModules :: [ModuleReexport],+ requiredSignatures:: [ModuleName],+ exposedSignatures:: [ModuleName],+ libExposed :: Bool,+ libBuildInfo :: BuildInfo+ }+ deriving (Generic, Eq, Ord, Read, Show)++data License =+ GPL (Maybe Version)+ | AGPL (Maybe Version)+ | LGPL (Maybe Version)+ | BSD2+ | BSD3+ | BSD4+ | MIT+ | ISC+ | MPL Version+ | Apache (Maybe Version)+ | PublicDomain+ | AllRightsReserved+ | UnspecifiedLicense+ | OtherLicense+ | UnknownLicense String+ deriving (Generic, Eq, Ord, Read, Show)++newtype ModuleName = ModuleName [String]+ deriving (Generic, Eq, Ord, Read, Show)++data ModuleReexport = ModuleReexport {+ moduleReexportOriginalPackage :: Maybe PackageName,+ moduleReexportOriginalName :: ModuleName,+ moduleReexportName :: ModuleName+ } deriving (Generic, Eq, Ord, Read, Show)++data ModuleRenaming = ModuleRenaming Bool [(ModuleName, ModuleName)]+ deriving (Generic, Eq, Ord, Read, Show)++data PackageDescription+ = PackageDescription {+ package :: PackageIdentifier,+ license :: License,+ licenseFiles :: [FilePath],+ copyright :: String,+ maintainer :: String,+ author :: String,+ stability :: String,+ testedWith :: [(CompilerFlavor,VersionRange)],+ homepage :: String,+ pkgUrl :: String,+ bugReports :: String,+ sourceRepos :: [SourceRepo],+ synopsis :: String,+ description :: String,+ category :: String,+ customFieldsPD :: [(String,String)],+ buildDepends :: [Dependency],+ specVersionRaw :: Either Version VersionRange,+ buildType :: Maybe BuildType,+ setupBuildInfo :: Maybe SetupBuildInfo,+ library :: Maybe Library,+ executables :: [Executable],+ testSuites :: [TestSuite],+ benchmarks :: [Benchmark],+ dataFiles :: [FilePath],+ dataDir :: FilePath,+ extraSrcFiles :: [FilePath],+ extraTmpFiles :: [FilePath],+ extraDocFiles :: [FilePath]+ } deriving (Generic, Eq, Ord, Read, Show)++data PackageIdentifier+ = PackageIdentifier {+ pkgName :: PackageName,+ pkgVersion :: Version+ }+ deriving (Generic, Eq, Ord, Read, Show)++newtype PackageName = PackageName { unPackageName :: String }+ deriving (Generic, Eq, Ord, Read, Show)++data RepoKind =+ RepoHead+ | RepoThis+ | RepoKindUnknown String+ deriving (Generic, Eq, Ord, Read, Show)++data RepoType = Darcs | Git | SVN | CVS+ | Mercurial | GnuArch | Bazaar | Monotone+ | OtherRepoType String+ deriving (Generic, Eq, Ord, Read, Show)++data SetupBuildInfo = SetupBuildInfo {+ setupDepends :: [Dependency],+ defaultSetupDepends :: Bool+ }+ deriving (Generic, Eq, Ord, Read, Show)++data SourceRepo = SourceRepo {+ repoKind :: RepoKind,+ repoType :: Maybe RepoType,+ repoLocation :: Maybe String,+ repoModule :: Maybe String,+ repoBranch :: Maybe String,+ repoTag :: Maybe String,+ repoSubdir :: Maybe FilePath+}+ deriving (Generic, Eq, Ord, Read, Show)++data TestSuite = TestSuite {+ testName :: String,+ testInterface :: TestSuiteInterface,+ testBuildInfo :: BuildInfo,+ testEnabled :: Bool+ }+ deriving (Generic, Eq, Ord, Read, Show)++data TestSuiteInterface =+ TestSuiteExeV10 Version FilePath+ | TestSuiteLibV09 Version ModuleName+ | TestSuiteUnsupported TestType+ deriving (Generic, Eq, Ord, Read, Show)++data TestType = TestTypeExe Version+ | TestTypeLib Version+ | TestTypeUnknown String Version+ deriving (Generic, Eq, Ord, Read, Show)++data VersionRange+ = AnyVersion+ | ThisVersion Version+ | LaterVersion Version+ | EarlierVersion Version+ | WildcardVersion Version+ | UnionVersionRanges VersionRange VersionRange+ | IntersectVersionRanges VersionRange VersionRange+ | VersionRangeParens VersionRange+ deriving (Generic, Eq, Ord, Read, Show)
+ benchmarks/GenericsBench.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE DeriveGeneric, StandaloneDeriving, BangPatterns #-}+module Main where++import qualified Data.ByteString.Lazy as L+import Cabal24 (PackageDescription)++import Criterion.Main++import qualified Data.Binary as Binary+import Data.Binary.Get (Get)+import qualified Data.Binary.Get as Binary++import GenericsBenchCache++main :: IO ()+main = benchmark =<< readPackageDescriptionCache 100++benchmark :: [PackageDescription] -> IO ()+benchmark pds = do+ let lbs = encode pds+ !_ = L.length lbs+ str = show pds+ !_ = length str+ defaultMain [+ bench "encode" (nf encode pds)+ , bench "decode" (nf decode lbs)+ , bench "decode null" (nf decodeNull lbs)+ , bgroup "embarrassment" [+ bench "read" (nf readPackageDescription str)+ , bench "show" (nf show pds)+ ]+ ]++encode :: [PackageDescription] -> L.ByteString+encode = Binary.encode++decode :: L.ByteString -> Int+decode = length . (Binary.decode :: L.ByteString -> [PackageDescription])++decodeNull :: L.ByteString -> ()+decodeNull =+ Binary.runGet $ do+ n <- Binary.get :: Get Int+ go n+ where+ go 0 = return ()+ go i = do+ x <- Binary.get :: Get PackageDescription+ x `seq` go (i-1)++readPackageDescription :: String -> Int+readPackageDescription = length . (read :: String -> [PackageDescription])
+ benchmarks/GenericsBenchCache.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DeriveGeneric, StandaloneDeriving, BangPatterns, CPP #-}+module GenericsBenchCache (readPackageDescriptionCache) where++import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as LC8+import qualified Codec.Compression.GZip as GZip++import Cabal24 (PackageDescription)++import System.Directory+import System.Exit++import GenericsBenchTypes ()++#if ! MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>))+#endif++readPackageDescriptionCache :: Int -> IO [PackageDescription]+readPackageDescriptionCache amount = do+ cacheExists <- doesFileExist cacheFilePath+ bs <-+ if cacheExists+ then do+ putStrLn "reading the cache file, might take a moment..."+ L.readFile cacheFilePath+ else do+ -- In older versions of this benchmark, there was machinery to+ -- regenerate the cache using the data in @~/.cabal@. Now the cache is+ -- simply stored in the repo to avoid a dependency on Cabal the library.+ putStrLn (cacheFilePath ++ " missing, aborting")+ exitFailure+ let str = LC8.unpack (GZip.decompress bs)+ pds = take amount (read str)+ -- PackageDescription doesn't implement NFData, let's force with the following line+ (length (show pds)) `seq` putStrLn "done reading the cache file"+ return pds++cacheFilePath :: String+cacheFilePath = "generics-bench.cache.gz"
+ benchmarks/GenericsBenchTypes.hs view
@@ -0,0 +1,35 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+module GenericsBenchTypes where++import Cabal24+import Generics.Deriving.Instances ()+import Data.Binary++instance Binary Benchmark+instance Binary BenchmarkInterface+instance Binary BenchmarkType+instance Binary BuildInfo+instance Binary BuildType+instance Binary CompilerFlavor+instance Binary Dependency+instance Binary Executable+instance Binary Extension+instance Binary FlagName+instance Binary KnownExtension+instance Binary Language+instance Binary Library+instance Binary License+instance Binary ModuleName+instance Binary ModuleReexport+instance Binary ModuleRenaming+instance Binary PackageDescription+instance Binary PackageIdentifier+instance Binary PackageName+instance Binary RepoKind+instance Binary RepoType+instance Binary SetupBuildInfo+instance Binary SourceRepo+instance Binary TestSuite+instance Binary TestSuiteInterface+instance Binary TestType+instance Binary VersionRange
+ benchmarks/Get.hs view
@@ -0,0 +1,381 @@+{-# LANGUAGE CPP, OverloadedStrings, ExistentialQuantification, BangPatterns #-}++#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+#include "MachDeps.h"+#endif++module Main where++import Control.DeepSeq+import Control.Exception (evaluate)+import Criterion.Main+import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as C8+import qualified Data.ByteString.Lazy as L+import Data.Bits+import Data.Char (ord)+import Data.List (foldl')++import Control.Applicative+import Data.Binary+import Data.Binary.Get++import qualified Data.Serialize.Get as Cereal++import qualified Data.Attoparsec.ByteString as A+import qualified Data.Attoparsec.ByteString.Lazy as AL++#if !MIN_VERSION_bytestring(0,10,0)+instance NFData S.ByteString+instance NFData L.ByteString where+ rnf = rnf . L.toChunks+#endif++main :: IO ()+main = do+ evaluate $ rnf [+ rnf brackets,+ rnf bracketsInChunks,+ rnf bracketCount,+ rnf oneMegabyte,+ rnf oneMegabyteLBS,+ rnf manyBytes,+ rnf encodedBigInteger+ ]+ defaultMain+ [ bgroup "brackets"+ [ bench "Binary 100kb, one chunk" $+ whnf (checkBracket . runTest bracketParser) brackets+ , bench "Binary 100kb, 100 byte chunks" $+ whnf (checkBracket . runTest bracketParser) bracketsInChunks+ , bench "Attoparsec lazy-bs 100kb, one chunk" $+ whnf (checkBracket . runAttoL bracketParser_atto) brackets+ , bench "Attoparsec lazy-bs 100kb, 100 byte chunks" $+ whnf (checkBracket . runAttoL bracketParser_atto) bracketsInChunks+ , bench "Attoparsec strict-bs 100kb" $+ whnf (checkBracket . runAtto bracketParser_atto) $ S.concat (L.toChunks brackets)+ , bench "Cereal strict-bs 100kb" $+ whnf (checkBracket . runCereal bracketParser_cereal) $ S.concat (L.toChunks brackets)+ ]+ , bgroup "comparison getStruct4, 1MB of struct of 4 Word8s"+ [ bench "Attoparsec" $+ whnf (runAtto (getStruct4_atto mega)) oneMegabyte+ , bench "Binary" $+ whnf (runTest (getStruct4 mega)) oneMegabyteLBS+ , bench "Cereal" $+ whnf (runCereal (getStruct4_cereal mega)) oneMegabyte+ ]+ , bgroup "comparison getWord8, 1MB"+ [ bench "Attoparsec" $+ whnf (runAtto (getWord8N1_atto mega)) oneMegabyte+ , bench "Binary" $+ whnf (runTest (getWord8N1 mega)) oneMegabyteLBS+ , bench "Cereal" $+ whnf (runCereal (getWord8N1_cereal mega)) oneMegabyte+ ]+ , bgroup "getWord8 1MB"+ [ bench "chunk size 2 bytes" $+ whnf (runTest (getWord8N2 mega)) oneMegabyteLBS+ , bench "chunk size 4 bytes" $+ whnf (runTest (getWord8N4 mega)) oneMegabyteLBS+ , bench "chunk size 8 bytes" $+ whnf (runTest (getWord8N8 mega)) oneMegabyteLBS+ , bench "chunk size 16 bytes" $+ whnf (runTest (getWord8N16 mega)) oneMegabyteLBS+ ]+ , bgroup "getWord8 1MB Applicative"+ [ bench "chunk size 2 bytes" $+ whnf (runTest (getWord8N2A mega)) oneMegabyteLBS+ , bench "chunk size 4 bytes" $+ whnf (runTest (getWord8N4A mega)) oneMegabyteLBS+ , bench "chunk size 8 bytes" $+ whnf (runTest (getWord8N8A mega)) oneMegabyteLBS+ , bench "chunk size 16 bytes" $+ whnf (runTest (getWord8N16A mega)) oneMegabyteLBS+ ]+ , bgroup "roll"+ [ bench "foldr" $ nf (roll_foldr :: [Word8] -> Integer) manyBytes+ , bench "foldl'" $ nf (roll_foldl' :: [Word8] -> Integer) manyBytes+ ]+ , bgroup "Integer"+ [ bench "decode" $ nf (decode :: L.ByteString -> Integer) encodedBigInteger+ ]+ ]++checkBracket :: Int -> Int+checkBracket x | x == bracketCount = x+ | otherwise = error "argh!"++runTest :: Get a -> L.ByteString -> a+runTest decoder inp = runGet decoder inp++runCereal :: Cereal.Get a -> C8.ByteString -> a+runCereal decoder inp = case Cereal.runGet decoder inp of+ Right a -> a+ Left err -> error err++runAtto :: AL.Parser a -> C8.ByteString -> a+runAtto decoder inp = case A.parseOnly decoder inp of+ Right a -> a+ Left err -> error err++runAttoL :: Show a => AL.Parser a -> L.ByteString -> a+runAttoL decoder inp = case AL.parse decoder inp of+ AL.Done _ r -> r+ a -> error (show a)++-- Defs.++oneMegabyte :: S.ByteString+oneMegabyte = S.replicate mega $ fromIntegral $ ord 'a'++oneMegabyteLBS :: L.ByteString+oneMegabyteLBS = L.fromChunks [oneMegabyte]++mega :: Int+mega = 1024 * 1024++-- 100k of brackets+bracketTest :: L.ByteString -> Int+bracketTest inp = runTest bracketParser inp++bracketCount :: Int+bracketCount = fromIntegral $ L.length brackets `div` 2++brackets :: L.ByteString+brackets = L.fromChunks [C8.concat (L.toChunks bracketsInChunks)]++bracketsInChunks :: L.ByteString+bracketsInChunks = L.fromChunks (replicate chunksOfBrackets oneChunk)+ where+ oneChunk = "((()((()()))((()(()()()()()()()(((()()()()(()()(()(()())))))()((())())))()())(((())())(()))))()(()))"+ chunksOfBrackets = 102400 `div` S.length oneChunk++bracketParser :: Get Int+bracketParser = cont <|> return 0+ where+ cont = do v <- some ( do 40 <- getWord8+ n <- many cont+ 41 <- getWord8+ return $! sum n + 1)+ return $! sum v++bracketParser_cereal :: Cereal.Get Int+bracketParser_cereal = cont <|> return 0+ where+ cont = do v <- some ( do 40 <- Cereal.getWord8+ n <- many cont+ 41 <- Cereal.getWord8+ return $! sum n + 1)+ return $! sum v++bracketParser_atto :: A.Parser Int+bracketParser_atto = cont <|> return 0+ where+ cont = do v <- some ( do _ <- A.word8 40+ n <- bracketParser_atto+ _ <- A.word8 41+ return $! n + 1)+ return $! sum v++-- Strict struct of 4 Word8s+data S2 = S2 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+data S4 = S4 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+data S8 = S8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+ {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+data S16 = S16 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+ {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+ {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8+ {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8++getStruct4 :: Int -> Get [S4]+getStruct4 = loop []+ where loop acc 0 = return acc+ loop acc n = do+ !w0 <- getWord8+ !w1 <- getWord8+ !w2 <- getWord8+ !w3 <- getWord8+ let !s = S4 w0 w1 w2 w3+ loop (s : acc) (n - 4)++getStruct4_cereal :: Int -> Cereal.Get [S4]+getStruct4_cereal = loop []+ where loop acc 0 = return acc+ loop acc n = do+ !w0 <- Cereal.getWord8+ !w1 <- Cereal.getWord8+ !w2 <- Cereal.getWord8+ !w3 <- Cereal.getWord8+ let !s = S4 w0 w1 w2 w3+ loop (s : acc) (n - 4)++getStruct4_atto :: Int -> A.Parser [S4]+getStruct4_atto = loop []+ where loop acc 0 = return acc+ loop acc n = do+ !w0 <- A.anyWord8+ !w1 <- A.anyWord8+ !w2 <- A.anyWord8+ !w3 <- A.anyWord8+ let !s = S4 w0 w1 w2 w3+ loop (s : acc) (n - 4)++getWord8N1 :: Int -> Get [Word8]+getWord8N1 = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ loop (s0:s) (n-1)++getWord8N1_cereal :: Int -> Cereal.Get [Word8]+getWord8N1_cereal = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- Cereal.getWord8+ loop (s0:s) (n-1)++getWord8N1_atto :: Int -> A.Parser [Word8]+getWord8N1_atto = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- A.anyWord8+ loop (s0:s) (n-1)++getWord8N2 :: Int -> Get [S2]+getWord8N2 = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ let !v = S2 s0 s1+ loop (v:s) (n-2)++getWord8N2A :: Int -> Get [S2]+getWord8N2A = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ !v <- S2 <$> getWord8 <*> getWord8+ loop (v:s) (n-2)++getWord8N4 :: Int -> Get [S4]+getWord8N4 = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ s2 <- getWord8+ s3 <- getWord8+ let !v = S4 s0 s1 s2 s3+ loop (v:s) (n-4)++getWord8N4A :: Int -> Get [S4]+getWord8N4A = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ !v <- S4 <$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8+ loop (v:s) (n-4)++getWord8N8 :: Int -> Get [S8]+getWord8N8 = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ s2 <- getWord8+ s3 <- getWord8+ s4 <- getWord8+ s5 <- getWord8+ s6 <- getWord8+ s7 <- getWord8+ let !v = S8 s0 s1 s2 s3 s4 s5 s6 s7+ loop (v:s) (n-8)++getWord8N8A :: Int -> Get [S8]+getWord8N8A = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ !v <- S8 <$> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ loop (v:s) (n-8)++getWord8N16 :: Int -> Get [S16]+getWord8N16 = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ s0 <- getWord8+ s1 <- getWord8+ s2 <- getWord8+ s3 <- getWord8+ s4 <- getWord8+ s5 <- getWord8+ s6 <- getWord8+ s7 <- getWord8+ s8 <- getWord8+ s9 <- getWord8+ s10 <- getWord8+ s11 <- getWord8+ s12 <- getWord8+ s13 <- getWord8+ s14 <- getWord8+ s15 <- getWord8+ let !v = S16 s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15+ loop (v:s) (n-16)++getWord8N16A :: Int -> Get [S16]+getWord8N16A = loop []+ where loop s n | s `seq` n `seq` False = undefined+ loop s 0 = return s+ loop s n = do+ !v <- S16 <$> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ <*> getWord8+ loop (v:s) (n-16)++manyBytes :: [Word8]+manyBytes = concat $ replicate 256 [0..255]++bigInteger :: Integer+bigInteger = roll_foldl' manyBytes++encodedBigInteger :: L.ByteString+encodedBigInteger = encode bigInteger++roll_foldr :: (Integral a, Bits a) => [Word8] -> a+roll_foldr = foldr unstep 0+ where+ unstep b a = a `shiftL` 8 .|. fromIntegral b++roll_foldl' :: (Integral a, Bits a) => [Word8] -> a+roll_foldl' = foldl' unstep 0 . reverse+ where+ unstep a b = a `shiftL` 8 .|. fromIntegral b
+ benchmarks/MemBench.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE ForeignFunctionInterface, BangPatterns #-}+module MemBench (memBench) where++import Foreign+import Foreign.C++import Control.Exception+import System.CPUTime+import Numeric++memBench :: Int -> IO ()+memBench mb = do+ let bytes = mb * 2^20+ allocaBytes bytes $ \ptr -> do+ let bench label test = do+ seconds <- time $ test (castPtr ptr) (fromIntegral bytes)+ let throughput = fromIntegral mb / seconds+ putStrLn $ show mb ++ "MB of " ++ label+ ++ " in " ++ showFFloat (Just 3) seconds "s, at: "+ ++ showFFloat (Just 1) throughput "MB/s"+ bench "setup " c_wordwrite+ putStrLn ""+ putStrLn "C memory throughput benchmarks:"+ bench "bytes written" c_bytewrite+ bench "bytes read " c_byteread+ bench "words written" c_wordwrite+ bench "words read " c_wordread+ putStrLn ""+ putStrLn "Haskell memory throughput benchmarks:"+ bench "bytes written" hs_bytewrite+ bench "bytes read " hs_byteread+ bench "words written" hs_wordwrite+ bench "words read " hs_wordread++hs_bytewrite :: Ptr CUChar -> Int -> IO ()+hs_bytewrite !ptr bytes = loop 0 0+ where iterations = bytes+ loop :: Int -> CUChar -> IO ()+ loop !i !n | i == iterations = return ()+ | otherwise = do pokeByteOff ptr i n+ loop (i+1) (n+1)++hs_byteread :: Ptr CUChar -> Int -> IO CUChar+hs_byteread !ptr bytes = loop 0 0+ where iterations = bytes+ loop :: Int -> CUChar -> IO CUChar+ loop !i !n | i == iterations = return n+ | otherwise = do x <- peekByteOff ptr i+ loop (i+1) (n+x)++hs_wordwrite :: Ptr CULong -> Int -> IO ()+hs_wordwrite !ptr bytes = loop 0 0+ where iterations = bytes `div` sizeOf (undefined :: CULong)+ loop :: Int -> CULong -> IO ()+ loop !i !n | i == iterations = return ()+ | otherwise = do pokeElemOff ptr i n+ loop (i+1) (n+1)++hs_wordread :: Ptr CULong -> Int -> IO CULong+hs_wordread !ptr bytes = loop 0 0+ where iterations = bytes `div` sizeOf (undefined :: CULong)+ loop :: Int -> CULong -> IO CULong+ loop !i !n | i == iterations = return n+ | otherwise = do x <- peekElemOff ptr i+ loop (i+1) (n+x)+++foreign import ccall unsafe "CBenchmark.h byteread"+ c_byteread :: Ptr CUChar -> CInt -> IO ()++foreign import ccall unsafe "CBenchmark.h bytewrite"+ c_bytewrite :: Ptr CUChar -> CInt -> IO ()++foreign import ccall unsafe "CBenchmark.h wordread"+ c_wordread :: Ptr CUInt -> CInt -> IO ()++foreign import ccall unsafe "CBenchmark.h wordwrite"+ c_wordwrite :: Ptr CUInt -> CInt -> IO ()++time :: IO a -> IO Double+time action = do+ start <- getCPUTime+ action+ end <- getCPUTime+ return $! (fromIntegral (end - start)) / (10^12)
+ benchmarks/Put.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE CPP, ExistentialQuantification #-}+{-# LANGUAGE DeriveGeneric #-}++module Main (main) where++import Control.DeepSeq+import Control.Exception (evaluate)+import Criterion.Main+import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as C+import qualified Data.ByteString.Lazy as L+import Data.Monoid++import GHC.Generics++import Data.Binary+import Data.Binary.Put+import Data.ByteString.Builder as BB+import Prelude -- Silence Monoid import warning.++main :: IO ()+main = do+ evaluate $ rnf+ [ rnf bigIntegers+ , rnf smallIntegers+ , rnf smallByteStrings+ , rnf smallStrings+ , rnf doubles+ , rnf word8s+ , rnf word16s+ , rnf word32s+ , rnf word64s+ ]+ defaultMain+ [+ bench "small Integers" $ whnf (run . fromIntegers) smallIntegers,+ bench "big Integers" $ whnf (run . fromIntegers) bigIntegers,++ bench "[small Integer]" $ whnf (run . put) smallIntegers,+ bench "[big Integer]" $ whnf (run . put) bigIntegers,++ bench "small ByteStrings" $ whnf (run . fromByteStrings) smallByteStrings,+ bench "[small ByteString]" $ whnf (run . put) smallByteStrings,++ bench "small Strings" $ whnf (run . fromStrings) smallStrings,+ bench "[small String]" $ whnf (run . put) smallStrings,++ bench "Double" $ whnf (run . put) doubles,++ bench "Word8s monoid put" $ whnf (run . fromWord8s) word8s,+ bench "Word8s builder" $ whnf (L.length . toLazyByteString . fromWord8sBuilder) word8s,+ bench "[Word8]" $ whnf (run . put) word8s,+ bench "Word16s monoid put" $ whnf (run . fromWord16s) word16s,+ bench "Word16s builder" $ whnf (L.length . toLazyByteString . fromWord16sBuilder) word16s,+ bench "[Word16]" $ whnf (run . put) word16s,+ bench "Word32s monoid put" $ whnf (run . fromWord32s) word32s,+ bench "Word32s builder" $ whnf (L.length . toLazyByteString . fromWord32sBuilder) word32s,+ bench "[Word32]" $ whnf (run . put) word32s,+ bench "Word64s monoid put" $ whnf (run . fromWord64s) word64s,+ bench "Word64s builder" $ whnf (L.length . toLazyByteString . fromWord64sBuilder) word64s,+ bench "[Word64]" $ whnf (run . put) word64s++ , bgroup "Generics" [+ bench "Struct monoid put" $ whnf (run . fromStructs) structs,+ bench "Struct put as list" $ whnf (run . put) structs,+ bench "StructList monoid put" $ whnf (run . fromStructLists) structLists,+ bench "StructList put as list" $ whnf (run . put) structLists+ ]+ ]+ where+ run = L.length . runPut++data Struct = Struct Word8 Word16 Word32 Word64 deriving Generic+instance Binary Struct++data StructList = StructList [Struct] deriving Generic+instance Binary StructList++structs :: [Struct]+structs = take 10000 $ [ Struct a b 0 0 | a <- [0 .. maxBound], b <- [0 .. maxBound] ]++structLists :: [StructList]+structLists = replicate 1000 (StructList (take 10 structs))++-- Input data++smallIntegers :: [Integer]+smallIntegers = [0..10000]+{-# NOINLINE smallIntegers #-}++bigIntegers :: [Integer]+bigIntegers = [m .. m + 10000]+ where+ m :: Integer+ m = fromIntegral (maxBound :: Word64)+{-# NOINLINE bigIntegers #-}++smallByteStrings :: [S.ByteString]+smallByteStrings = replicate 10000 $ C.pack "abcdefghi"+{-# NOINLINE smallByteStrings #-}++smallStrings :: [String]+smallStrings = replicate 10000 "abcdefghi"+{-# NOINLINE smallStrings #-}++doubles :: [Double]+doubles = take 10000 $ [ sign * 2 ** n | sign <- [-1, 1], n <- [ 0, 0.2 .. 1023 ]]++word8s :: [Word8]+word8s = take 10000 $ cycle [minBound .. maxBound]+{-# NOINLINE word8s #-}++word16s :: [Word16]+word16s = take 10000 $ cycle [minBound .. maxBound]+{-# NOINLINE word16s #-}++word32s :: [Word32]+word32s = take 10000 $ cycle [minBound .. maxBound]+{-# NOINLINE word32s #-}++word64s :: [Word64]+word64s = take 10000 $ cycle [minBound .. maxBound]+{-# NOINLINE word64s #-}++------------------------------------------------------------------------+-- Benchmarks++fromIntegers :: [Integer] -> Put+fromIntegers [] = mempty+fromIntegers (x:xs) = put x `mappend` fromIntegers xs++fromByteStrings :: [S.ByteString] -> Put+fromByteStrings [] = mempty+fromByteStrings (x:xs) = put x `mappend` fromByteStrings xs++fromStrings :: [String] -> Put+fromStrings [] = mempty+fromStrings (x:xs) = put x `mappend` fromStrings xs++fromWord8s :: [Word8] -> Put+fromWord8s [] = mempty+fromWord8s (x:xs) = put x `mappend` fromWord8s xs++fromWord8sBuilder :: [Word8] -> BB.Builder+fromWord8sBuilder [] = mempty+fromWord8sBuilder (x:xs) = BB.word8 x `mappend` fromWord8sBuilder xs++fromWord16s :: [Word16] -> Put+fromWord16s [] = mempty+fromWord16s (x:xs) = put x `mappend` fromWord16s xs++fromWord16sBuilder :: [Word16] -> BB.Builder+fromWord16sBuilder [] = mempty+fromWord16sBuilder (x:xs) = BB.word16BE x `mappend` fromWord16sBuilder xs++fromWord32s :: [Word32] -> Put+fromWord32s [] = mempty+fromWord32s (x:xs) = put x `mappend` fromWord32s xs++fromWord32sBuilder :: [Word32] -> BB.Builder+fromWord32sBuilder [] = mempty+fromWord32sBuilder (x:xs) = BB.word32BE x `mappend` fromWord32sBuilder xs++fromWord64s :: [Word64] -> Put+fromWord64s [] = mempty+fromWord64s (x:xs) = put x `mappend` fromWord64s xs++fromWord64sBuilder :: [Word64] -> BB.Builder+fromWord64sBuilder [] = mempty+fromWord64sBuilder (x:xs) = BB.word64BE x `mappend` fromWord64sBuilder xs++fromStructs :: [Struct] -> Put+fromStructs [] = mempty+fromStructs (x:xs) = put x `mappend` fromStructs xs++fromStructLists :: [StructList] -> Put+fromStructLists [] = mempty+fromStructLists (x:xs) = put x `mappend` fromStructLists xs
binary.cabal view
@@ -1,13 +1,15 @@ name: binary-version: 0.4.5+version: 0.10.0.0 license: BSD3 license-file: LICENSE-author: Lennart Kolmodin <kolmodin@dtek.chalmers.se>-maintainer: Lennart Kolmodin, Don Stewart <dons@galois.com>-homepage: http://code.haskell.org/binary/+author: Lennart Kolmodin <kolmodin@gmail.com>+maintainer: Lennart Kolmodin, Don Stewart <dons00@gmail.com>+homepage: https://github.com/kolmodin/binary description: Efficient, pure binary serialisation using lazy ByteStrings.- Haskell values may be encoded to and from binary formats, + Haskell values may be encoded to and from binary formats, written to disk as binary, or sent over the network.+ The format used can be automatically generated, or+ you can choose to implement a custom format if needed. Serialisation speeds of over 1 G\/sec have been observed, so this library should be suitable for high performance scenarios.@@ -15,50 +17,252 @@ category: Data, Parsing stability: provisional build-type: Simple-cabal-version: >= 1.2-tested-with: GHC ==6.4.2, GHC ==6.6.1, GHC ==6.8.0, GHC ==6.10.1-extra-source-files: README index.html--flag bytestring-in-base-flag split-base-flag applicative-in-base+cabal-version: >= 1.8+tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2+extra-source-files:+ README.md changelog.md docs/hcar/binary-Lb.tex tools/derive/*.hs -library- if flag(bytestring-in-base)- -- bytestring was in base-2.0 and 2.1.1- build-depends: base >= 2.0 && < 2.2- cpp-options: -DBYTESTRING_IN_BASE- else- -- in base 1.0 and 3.0 bytestring is a separate package- build-depends: base < 2.0 || >= 3, bytestring >= 0.9+-- from the benchmark 'bench'+extra-source-files:+ benchmarks/CBenchmark.h - if flag(split-base)- build-depends: base >= 3.0, containers, array- else- build-depends: base < 3.0+source-repository head+ type: git+ location: git://github.com/kolmodin/binary.git - if flag(applicative-in-base)- build-depends: base >= 2.0- cpp-options: -DAPPLICATIVE_IN_BASE- else- build-depends: base < 2.0+library+ build-depends: base >= 4.5.0.0 && < 5, bytestring >= 0.10.4, containers, array hs-source-dirs: src- exposed-modules: Data.Binary, Data.Binary.Put, Data.Binary.Get,+ Data.Binary.Get.Internal, Data.Binary.Builder - extensions: CPP,- FlexibleContexts+ other-modules: Data.Binary.Class,+ Data.Binary.Internal,+ Data.Binary.Generic,+ Data.Binary.FloatCast+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim - ghc-options: -O2- -Wall- -fspec-constr- -funbox-strict-fields - -fdicts-cheap- -fliberate-case-threshold=1000- -fmax-simplifier-iterations10+ ghc-options: -O2 -Wall -fliberate-case-threshold=1000 --- if impl(ghc < 6.5)--- ghc-options: -fallow-undecidable-instances+ if impl(ghc >= 8.0)+ ghc-options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances++-- Due to circular dependency, we cannot make any of the test-suites or+-- benchmark depend on the binary library. Instead, for each test-suite and+-- benchmark, we include the source directory of binary and build-depend on all+-- the dependencies binary has.++test-suite qc+ type: exitcode-stdio-1.0+ hs-source-dirs: src tests+ main-is: QC.hs+ other-modules:+ Action+ Arbitrary+ other-modules:+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get+ Data.Binary.Get.Internal+ Data.Binary.Internal+ Data.Binary.Put+ build-depends:+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4,+ random>=1.0.1.0,+ test-framework,+ test-framework-quickcheck2 >= 0.3,+ QuickCheck >= 2.9++ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ ghc-options: -Wall -O2 -threaded+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim+++test-suite read-write-file+ type: exitcode-stdio-1.0+ hs-source-dirs: src tests+ main-is: File.hs+ other-modules:+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get+ Data.Binary.Get.Internal+ Data.Binary.Internal+ Data.Binary.Put+ build-depends:+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4,+ Cabal,+ directory,+ filepath,+ HUnit++ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ ghc-options: -Wall+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim+++benchmark bench+ type: exitcode-stdio-1.0+ hs-source-dirs: src benchmarks+ main-is: Benchmark.hs+ other-modules:+ MemBench+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get+ Data.Binary.Get.Internal+ Data.Binary.Internal+ Data.Binary.Put+ build-depends:+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4+ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ c-sources: benchmarks/CBenchmark.c+ include-dirs: benchmarks+ ghc-options: -O2+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim+++benchmark get+ type: exitcode-stdio-1.0+ hs-source-dirs: src benchmarks+ main-is: Get.hs+ other-modules:+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get.Internal+ Data.Binary.Internal+ Data.Binary.Put+ build-depends:+ attoparsec,+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4,+ cereal,+ criterion == 1.*,+ deepseq,+ mtl+ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ ghc-options: -O2 -Wall+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim+++benchmark put+ type: exitcode-stdio-1.0+ hs-source-dirs: src benchmarks+ main-is: Put.hs+ other-modules:+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get+ Data.Binary.Get.Internal+ Data.Binary.Internal+ build-depends:+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4,+ criterion == 1.*,+ deepseq+ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ ghc-options: -O2 -Wall+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim++benchmark generics-bench+ type: exitcode-stdio-1.0+ hs-source-dirs: src benchmarks+ main-is: GenericsBench.hs+ other-modules:+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get+ Data.Binary.Get.Internal+ Data.Binary.Internal+ Data.Binary.Put+ build-depends:+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4,+ -- The benchmark already depended on 'generic-deriving' transitively. That's+ -- what caused one of the problems, as both 'generic-deriving' and+ -- 'GenericsBenchTypes' used to define 'instance Generic Version'.+ generic-deriving >= 0.10,+ directory,+ filepath,+ unordered-containers,+ zlib,+ criterion+ + other-modules:+ Cabal24+ GenericsBenchCache+ GenericsBenchTypes+ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ ghc-options: -O2 -Wall+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim++benchmark builder+ type: exitcode-stdio-1.0+ hs-source-dirs: src benchmarks+ main-is: Builder.hs+ other-modules:+ Data.Binary+ Data.Binary.Builder+ Data.Binary.Class+ Data.Binary.FloatCast+ Data.Binary.Generic+ Data.Binary.Get+ Data.Binary.Get.Internal+ Data.Binary.Internal+ Data.Binary.Put+ build-depends:+ base >= 4.5.0.0 && < 5,+ bytestring >= 0.10.4,+ criterion == 1.*,+ deepseq,+ mtl+ -- build dependencies from using binary source rather than depending on the library+ build-depends: array, containers+ ghc-options: -O2+ if impl(ghc <= 7.6)+ -- prior to ghc-7.4 generics lived in ghc-prim+ build-depends: ghc-prim
+ changelog.md view
@@ -0,0 +1,183 @@+binary+======++binary-0.10.0.0+---------------++- Add binary instance for Data.Functor.Identity from base, #146.+- Don't use * when we have TypeOperators, #148.++binary-0.9.0.0+--------------++- `0.8.5.0` was first released as version `0.9.0.0`. It didn't have any+ breaking changes though, so it was again released as version `0.8.5.0`+ according to PVP. Next breaking release of `binary` will be version+ `0.10.0.0`.++binary-0.8.5.0+--------------++- Add Binary instances for Typeable TypeReps, #131.++binary-0.8.4.1+--------------++- Fix compilation with bytestring < 0.10.4.++binary-0.8.4.0+--------------++- `binary` supports GHC >= 7.4.2+- Performance improvements for `Alternative` functions.+- put/get functions for IEEE-754 floats and doubles, #119.+- Fix performance bugs, #115.+- Binary instances for datatypes in `Data.Monoid` and `Data.Semigroup`, #114.++binary-0.8.3.0+--------------++- Replace binary's home grown `Builder` with `Data.ByteString.Builder`.+ `Data.Binary.Builder` now exports `Data.ByteString.Builder.Builder`.+- Add `putList :: [a] -> Put` to the `Binary` class. This is used to be able to+ use the list writing primitives of the new Builder. This brought a number of speedups;+ Encoding a String is now 70% faster. [Word8] is 76% faster, which also makes+ Integer 34% faster. Similar numbers for all [IntXX] and [WordXX].+- Fail gracefully within `Get` when decoding `Bool` and `Ordering`. Previously+ when decoding invalid data these instances would fail with `error`.+- Add Binary instance for `Complex a`.+- Add Monoid and Semigroup instance for `Put`.++binary-0.8.2.1+--------------++- Fix compilation error when using older GHC versions and clang. clang barfs on some of its CPP input (#105).++binary-0.8.2.0+--------------++- When using GHC >= 8, `Data.Binary.Get.Get` implements MonadFail and delegates its `fail` to `MonadFail.fail`.++binary-0.8.1.0+--------------++- Add binary instance for `Data.ByteString.Short`.+- Add get/put functions for all Int sizes to `Data.Binary.Builder`, `Data.Binary.Get` and `Data.Binary.Put`.++binary-0.8.0.1+--------------++- Address compiler warnings.++binary-0.8.0.0+--------------++- Added binary instance for `Version` from `Data.Version`.+- Added binary instance for `Void` from GHC 7.10.1.+- Added binary instance for `(Data.Fixed a)` from GHC 7.8.1.+- Added semigroup instance for `Data.Binary.Builder` from GHC 8.0.++binary-0.7.6.1+--------------++- Fix compilation for GHC == 7.2.*.++binary-0.7.6.0+--------------++- Added binary instance for GHC.Fingerprint (from GHC >= 7.4).++binary-0.7.5.0+--------------++- Fix performance bug that was noticable when you get a big strict ByteString+ and the input to the decoder consists of many small chunks.+ - https://github.com/kolmodin/binary/issues/73+ - https://github.com/kolmodin/binary/pull/76+- Fix memory leak when decoding Double and Float.+ - Commit 497a181c083fa9faf7fa3aa64d1d8deb9ac76ecb+- We now require QuickCheck >= 2.8. Remove our version of arbitrarySizedNatural.++binary-0.7.4.0+--------------++- Some invalid UTF-8 strings caused an exception when decoded. Those errors will+ now now fail in the Get monad instead. See #70.+ Patch contributed by @ttuegel.++binary-0.7.3.0+--------------++- Add Binary instance for Natural (only with base > 4.8).++binary-0.7.2.3+--------------++- Remove INLINEs from GBinary/GSum methods. These interact very badly with the+ GHC 7.9.x simplifier. See also;+ - https://github.com/kolmodin/binary/pull/62+ - https://ghc.haskell.org/trac/ghc/ticket/9630+ - https://ghc.haskell.org/trac/ghc/ticket/9583++binary-0.7.2.2+--------------++- Make import of GHC.Base future-proof (https://github.com/kolmodin/binary/pull/59).++binary-0.7.2.1+--------------++- Fix to compile on GHC 6.10.4 and older (https://github.com/kolmodin/binary/issues/55).++binary-0.7.2.0+--------------++- Add `isolate :: Int -> Get a -> Get a`.+- Add `label :: String -> Get a -> Get a`.++binary-0.7.1.0+--------------++- Add `lookAheadE :: Get (Either a b) -> Get (Either a b)`.+- Add MonadPlus instance for Get. +++binary-0.7.0.1+--------------++- Updates to documentation.++binary-0.7.0.0+--------------++- Add `lookAhead :: Get a -> Get a`.+- Add `lookAheadM :: Get (Maybe a) -> Get (Maybe a)`.+- Add Alternative instance for Get (provides `<|>`).+- Add `decodeOrFail :: Binary a => L.ByteString -> Either (L.ByteString, ByteOffset, String) (L.ByteString, ByteOffset, a)`+- Add `decodeFileOrFail :: Binary a => FilePath -> IO (Either (ByteOffset, String) a)`.+- Remove `Ord` class constraint from `Set` and `Map` Binary instances.++binary-0.6.4+------------++- Add `runGetOrFail :: Get a -> L.ByteString -> Either (L.ByteString, ByteOffset, String) (L.ByteString, ByteOffset, a)`.++binary-0.6.3+------------++- Documentation tweeks, internal restructuring, more tests.++binary-0.6.2+------------++- `some` and `many` more efficient.+- Fix bug where `bytesRead` returned the wrong value.+- Documentation improvements.++binary-0.6.1+------------++- Fix bug where a decoder could return with `Partial` after the previous reply was `Nothing`.++binary-0.6.0.0+--------------
+ docs/hcar/binary-Lb.tex view
@@ -0,0 +1,48 @@+\begin{hcarentry}{binary}+\label{binary}+\report{Lennart Kolmodin}+\status{active}+\participants{Duncan Coutts, Don Stewart, Binary Strike Team}+\makeheader++The Binary Strike Team is pleased to announce yet a release of a new,+pure, efficient binary serialisation library.++The `binary' package provides efficient serialisation of Haskell values+to and from lazy ByteStrings. ByteStrings constructed this way may then+be written to disk, written to the network, or further processed (e.g.+stored in memory directly, or compressed in memory with zlib or bzlib).++The binary library has been heavily tuned for performance, particularly for+writing speed. Throughput of up to 160M/s has been achieved in practice, and+in general speed is on par or better than NewBinary, with the advantage of a+pure interface. Efforts are underway to improve performance still further.+Plans are also taking shape for a parser combinator library on top of+binary, for bit parsing and foreign structure parsing (e.g. network+protocols).++Data.Derive~\cref{derive} has support for automatically generating Binary+instances, allowing to read and write your data structures with little fuzz.++Binary was developed by a team of 8 during the Haskell Hackathon in Oxford+2007, and since then has about 15 people contributed code and many more+given feedback and cheerleading on \verb|#haskell|.++The package is cabalized and available through Hackage~\cref{hackagedb}.+% to editors: ref. to cabal?++\FurtherReading+\begin{compactitem}+\item Homepage++ \url{http://code.haskell.org/binary/}+\item Hackage++ \url{http://hackage.haskell.org/cgi-bin/hackage-scripts/package/binary}+\item Development version++ \texttt{darcs get --partial}++ \url{http://code.haskell.org/binary}+\end{compactitem}+\end{hcarentry}
− index.html
@@ -1,161 +0,0 @@-<?xml version="1.0" encoding="iso-8859-1"?>-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">--<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">-<head>- <title>Data.Binary - efficient, pure binary serialisation for Haskell</title>- <link rel="stylesheet" href="http://www.cse.unsw.edu.au/~dons/main.css" type="text/css" />-</head>--<body xml:lang="en" lang="en">-- <div id="content">-- <h2>Data.Binary</h2>--<table width="80%" align="center"> <tr><td>-- <h3>About</h3>- <p>- Data.Binary is a library for high performance binary serialisation- of <a href="http://haskell.org">Haskell</a> data. It uses the- <a href="http://www.cse.unsw.edu.au/~dons/fps.html"- >ByteString</a> library to achieve efficient, lazy reading and- writing of structures in binary format.- </p>-- <p>- Chris Eidhof writes on his use of Data.Binary implementing a- full-text search engine:- </p>- <pre>- "The communication with Sphinx is done using a quite low-level binary- protocol, but Data.Binary saved the day: it made it very easy for us- to parse all the binary things. Especially the use of the Get and- Put monads are a big improvement over the manual reading and keeping- track of positions, as is done in the PHP/Python clients."- </pre>-- <h3>Example</h3>- For example, to serialise an interpreter's abstract syntax tree to- binary format:-<pre><span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Binary</span>-<span class='keyword'>import</span> <span class='conid'>Control</span><span class='varop'>.</span><span class='conid'>Monad</span>-<span class='keyword'>import</span> <span class='conid'>Codec</span><span class='varop'>.</span><span class='conid'>Compression</span><span class='varop'>.</span><span class='conid'>GZip</span>--<span class='comment'>-- A Haskell AST structure</span>-<span class='keyword'>data</span> <span class='conid'>Exp</span> <span class='keyglyph'>=</span> <span class='conid'>IntE</span> <span class='conid'>Int</span>- <span class='keyglyph'>|</span> <span class='conid'>OpE</span> <span class='conid'>String</span> <span class='conid'>Exp</span> <span class='conid'>Exp</span>- <span class='keyword'>deriving</span> <span class='conid'>Eq</span>--<span class='comment'>-- An instance of Binary to encode and decode an Exp in binary</span>-<span class='keyword'>instance</span> <span class='conid'>Binary</span> <span class='conid'>Exp</span> <span class='keyword'>where</span>- <span class='varid'>put</span> <span class='layout'>(</span><span class='conid'>IntE</span> <span class='varid'>i</span><span class='layout'>)</span> <span class='keyglyph'>=</span> <span class='varid'>put</span> <span class='layout'>(</span><span class='num'>0</span> <span class='keyglyph'>::</span> <span class='conid'>Word8</span><span class='layout'>)</span> <span class='varop'>>></span> <span class='varid'>put</span> <span class='varid'>i</span>- <span class='varid'>put</span> <span class='layout'>(</span><span class='conid'>OpE</span> <span class='varid'>s</span> <span class='varid'>e1</span> <span class='varid'>e2</span><span class='layout'>)</span> <span class='keyglyph'>=</span> <span class='varid'>put</span> <span class='layout'>(</span><span class='num'>1</span> <span class='keyglyph'>::</span> <span class='conid'>Word8</span><span class='layout'>)</span> <span class='varop'>>></span> <span class='varid'>put</span> <span class='varid'>s</span> <span class='varop'>>></span> <span class='varid'>put</span> <span class='varid'>e1</span> <span class='varop'>>></span> <span class='varid'>put</span> <span class='varid'>e2</span>- <span class='varid'>get</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span> <span class='varid'>tag</span> <span class='keyglyph'><-</span> <span class='varid'>getWord8</span>- <span class='keyword'>case</span> <span class='varid'>tag</span> <span class='keyword'>of</span>- <span class='num'>0</span> <span class='keyglyph'>-></span> <span class='varid'>liftM</span> <span class='conid'>IntE</span> <span class='varid'>get</span>- <span class='num'>1</span> <span class='keyglyph'>-></span> <span class='varid'>liftM3</span> <span class='conid'>OpE</span> <span class='varid'>get</span> <span class='varid'>get</span> <span class='varid'>get</span>--<span class='comment'>-- A test expression</span>-<span class='varid'>e</span> <span class='keyglyph'>=</span> <span class='conid'>OpE</span> <span class='str'>"*"</span> <span class='layout'>(</span><span class='conid'>IntE</span> <span class='num'>7</span><span class='layout'>)</span> <span class='layout'>(</span><span class='conid'>OpE</span> <span class='str'>"/"</span> <span class='layout'>(</span><span class='conid'>IntE</span> <span class='num'>4</span><span class='layout'>)</span> <span class='layout'>(</span><span class='conid'>IntE</span> <span class='num'>2</span><span class='layout'>)</span><span class='layout'>)</span>--<span class='comment'>-- Serialise and compress with gzip, then decompress and deserialise</span>-<span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>- <span class='keyword'>let</span> <span class='varid'>t</span> <span class='keyglyph'>=</span> <span class='varid'>compress</span> <span class='layout'>(</span><span class='varid'>encode</span> <span class='varid'>e</span><span class='layout'>)</span>- <span class='varid'>print</span> <span class='varid'>t</span>- <span class='keyword'>let</span> <span class='varid'>e'</span> <span class='keyglyph'>=</span> <span class='varid'>decode</span> <span class='layout'>(</span><span class='varid'>decompress</span> <span class='varid'>t</span><span class='layout'>)</span>- <span class='varid'>print</span> <span class='layout'>(</span><span class='varid'>e</span> <span class='varop'>==</span> <span class='varid'>e'</span><span class='layout'>)</span>-</pre>-- <h3>Download</h3>-- <table width="100%"><tr valign="top">- <td><h4>stable release</h4>- <table>- <tr><td>- <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/binary-0.4.2"- >binary 0.4.2</a> - </td><td>(Apr 2008)</td></tr>-- <tr><td>- <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/binary-0.4.1"- >binary 0.4.1</a> - </td><td>(Oct 2007)</td></tr>-- <tr><td>- <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/binary-0.4"- >binary 0.4</a> - </td><td>(Oct 2007)</td></tr>-- <tr><td>- <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/binary-0.3"- >binary 0.3</a> - </td><td>(Mar 2007)</td></tr>-- <tr><td>- <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/binary-0.3"- >binary 0.2</a> - </td><td>(Jan 2007)</td></tr>-- </table> - </td>- <td><h4>development branch</h4>- <table>- <tr><td>- darcs get <a href="http://code.haskell.org/binary"- >http://code.haskell.org/binary</a>- </td></tr>- </table>- </td> </tr> </table>-- <h3>Download</h3>- <ul>- <li>- <a href="http://hackage.haskell.org/packages/archive/binary/0.4.1/doc/html/Data-Binary.html">Documentation</a>- </li>- </ul>-- <h3>Project Activity</h3>-- <center>- <img src="http://www.cse.unsw.edu.au/~dons/images/commits/community/binary-commits.png"- alt="binary commit statistics" />- </center>-- <h3>Starring...</h3>-- The Binary Strike Force- <ul>- <li>Lennart Kolmodin </li>- <li>Duncan Coutts </li>- <li>Don Stewart </li>- <li>Spencer Janssen </li>- <li>David Himmelstrup </li>- <li>Björn Bringert </li>- <li>Ross Paterson </li>- <li>Einar Karttunen </li>- <li>John Meacham </li>- <li>Ulf Norell </li>- <li>Bryan O'Sullivan </li>- <li>Tomasz Zielonka </li>- <li>Florian Weimer </li>- <li>Judah Jacobson </li>- </ul>--</td></tr> </table>--<img src="http://xmonad.org/images/HPC.badge.jpg" alt="covered by HPC" />-<img src="http://xmonad.org/images/cabal.png" alt="built with Cabal" />-<img src="http://xmonad.org/images/quickcheck.png" alt="tested with QuickCheck" />-- </div>--- <div id="footer">-Mon Jul 14 11:37:21 PDT 2008- </div>--</body>-</html>
src/Data/Binary.hs view
@@ -1,37 +1,54 @@-{-# LANGUAGE CPP, FlexibleInstances, FlexibleContexts #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE Trustworthy #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Binary -- Copyright : Lennart Kolmodin -- License : BSD3-style (see LICENSE)--- --- Maintainer : Lennart Kolmodin <kolmodin@dtek.chalmers.se>+--+-- Maintainer : Lennart Kolmodin <kolmodin@gmail.com> -- Stability : unstable--- Portability : portable to Hugs and GHC. Requires the FFI and some flexible instances+-- Portability : portable to Hugs and GHC. Requires the FFI and some flexible instances. ----- Binary serialisation of Haskell values to and from lazy ByteStrings.+-- Binary serialisation of Haskell values to and from lazy 'ByteString's. -- The Binary library provides methods for encoding Haskell values as--- streams of bytes directly in memory. The resulting @ByteString@ can--- then be written to disk, sent over the network, or futher processed+-- streams of bytes directly in memory. The resulting 'ByteString' can+-- then be written to disk, sent over the network, or further processed -- (for example, compressed with gzip). ----- The 'Binary' package is notable in that it provides both pure, and+-- The @binary@ package is notable in that it provides both pure, and -- high performance serialisation. ----- Values are always encoded in network order (big endian) form, and--- encoded data should be portable across machine endianess, word size,--- or compiler version. For example, data encoded using the Binary class--- could be written from GHC, and read back in Hugs.+-- Values encoded using the 'Binary' class are always encoded in network order+-- (big endian) form, and encoded data should be portable across+-- machine endianness, word size, or compiler version. For example,+-- data encoded using the 'Binary' class could be written on any machine,+-- and read back on any another. --+-- If the specifics of the data format is not important to you, for example,+-- you are more interested in serializing and deserializing values than+-- in which format will be used, it is possible to derive 'Binary'+-- instances using the generic support. See 'GBinaryGet' and+-- 'GBinaryPut'.+--+-- If you have specific requirements about the encoding format, you can use+-- the encoding and decoding primitives directly, see the modules+-- "Data.Binary.Get" and "Data.Binary.Put".+-- ----------------------------------------------------------------------------- module Data.Binary ( -- * The Binary class Binary(..)-+ -- ** Example -- $example + -- * Generic support+ -- $generics+ , GBinaryGet(..)+ , GBinaryPut(..)+ -- * The Get and Put monads , Get , Put@@ -43,14 +60,12 @@ -- * Binary serialisation , encode -- :: Binary a => a -> ByteString , decode -- :: Binary a => ByteString -> a+ , decodeOrFail -- * IO functions for serialisation , encodeFile -- :: Binary a => FilePath -> a -> IO () , decodeFile -- :: Binary a => FilePath -> IO a---- Lazy put and get--- , lazyPut--- , lazyGet+ , decodeFileOrFail , module Data.Word -- useful @@ -58,64 +73,19 @@ import Data.Word +import Data.Binary.Class import Data.Binary.Put import Data.Binary.Get--import Control.Monad-import Foreign-import System.IO+import Data.Binary.Generic () +import qualified Data.ByteString as B ( hGet, length ) import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as L--import Data.Char (chr,ord)-import Data.List (unfoldr)---- And needed for the instances:-import qualified Data.ByteString as B-import qualified Data.Map as Map-import qualified Data.Set as Set-import qualified Data.IntMap as IntMap-import qualified Data.IntSet as IntSet-import qualified Data.Ratio as R--import qualified Data.Tree as T--import Data.Array.Unboxed------- This isn't available in older Hugs or older GHC----#if __GLASGOW_HASKELL__ >= 606-import qualified Data.Sequence as Seq-import qualified Data.Foldable as Fold-#endif+import qualified Data.ByteString.Lazy.Internal as L ( defaultChunkSize )+import System.IO ( withBinaryFile, IOMode(ReadMode) ) ------------------------------------------------------------------------ --- | The @Binary@ class provides 'put' and 'get', methods to encode and--- decode a Haskell value to a lazy ByteString. It mirrors the Read and--- Show classes for textual representation of Haskell types, and is--- suitable for serialising Haskell values to disk, over the network.------ For parsing and generating simple external binary formats (e.g. C--- structures), Binary may be used, but in general is not suitable--- for complex protocols. Instead use the Put and Get primitives--- directly.------ Instances of Binary should satisfy the following property:------ > decode . encode == id------ That is, the 'get' and 'put' methods should be the inverse of each--- other. A range of instances are provided for basic Haskell types. ----class Binary t where- -- | Encode a value in the Put monad.- put :: t -> Put- -- | Decode a value in the Get monad- get :: Get t- -- $example -- To serialise a custom type, an instance of Binary for that type is -- required. For example, suppose we have a data structure:@@ -129,13 +99,13 @@ -- structure to serialise: -- -- > instance Binary Exp where--- > put (IntE i) = do put (0 :: Word8)--- > put i--- > put (OpE s e1 e2) = do put (1 :: Word8)--- > put s--- > put e1--- > put e2--- > +-- > put (IntE i) = do put (0 :: Word8)+-- > put i+-- > put (OpE s e1 e2) = do put (1 :: Word8)+-- > put s+-- > put e1+-- > put e2+-- > -- > get = do t <- get :: Get Word8 -- > case t of -- > 0 -> do i <- get@@ -150,44 +120,12 @@ -- -- We can simplify the writing of 'get' instances using monadic -- combinators:--- +-- -- > get = do tag <- getWord8 -- > case tag of -- > 0 -> liftM IntE get -- > 1 -> liftM3 OpE get get get ----- The generation of Binary instances has been automated by a script--- using Scrap Your Boilerplate generics. Use the script here:--- <http://darcs.haskell.org/binary/tools/derive/BinaryDerive.hs>.------ To derive the instance for a type, load this script into GHCi, and--- bring your type into scope. Your type can then have its Binary--- instances derived as follows:------ > $ ghci -fglasgow-exts BinaryDerive.hs--- > *BinaryDerive> :l Example.hs--- > *Main> deriveM (undefined :: Drinks)--- >--- > instance Binary Main.Drinks where--- > put (Beer a) = putWord8 0 >> put a--- > put Coffee = putWord8 1--- > put Tea = putWord8 2--- > put EnergyDrink = putWord8 3--- > put Water = putWord8 4--- > put Wine = putWord8 5--- > put Whisky = putWord8 6--- > get = do--- > tag_ <- getWord8--- > case tag_ of--- > 0 -> get >>= \a -> return (Beer a)--- > 1 -> return Coffee--- > 2 -> return Tea--- > 3 -> return EnergyDrink--- > 4 -> return Water--- > 5 -> return Wine--- > 6 -> return Whisky--- >--- -- To serialise this to a bytestring, we use 'encode', which packs the -- data structure into a binary format, in a lazy bytestring --@@ -212,7 +150,7 @@ -- > OpE "*" (IntE 7) (OpE "/" (IntE 4) (IntE 2)) -- -- We can also directly serialise a value to and from a Handle, or a file:--- +-- -- > > v <- decodeFile "/tmp/exp.txt" :: IO Exp -- > OpE "*" (IntE 7) (OpE "/" (IntE 4) (IntE 2)) --@@ -231,14 +169,25 @@ {-# INLINE encode #-} -- | Decode a value from a lazy ByteString, reconstructing the original structure.--- decode :: Binary a => ByteString -> a decode = runGet get +-- | Decode a value from a lazy ByteString. Returning 'Left' on failure and+-- 'Right' on success. In both cases the unconsumed input and the number of+-- consumed bytes is returned. In case of failure, a human-readable error+-- message will be returned as well.+--+-- /Since: 0.7.0.0/+decodeOrFail :: Binary a => L.ByteString+ -> Either (L.ByteString, ByteOffset, String)+ (L.ByteString, ByteOffset, a)+decodeOrFail = runGetOrFail get++ ------------------------------------------------------------------------ -- Convenience IO operations --- | Lazily serialise a value to a file+-- | Lazily serialise a value to a file. -- -- This is just a convenience function, it's defined simply as: --@@ -251,454 +200,49 @@ encodeFile :: Binary a => FilePath -> a -> IO () encodeFile f v = L.writeFile f (encode v) --- | Lazily reconstruct a value previously written to a file.------ This is just a convenience function, it's defined simply as:------ > decodeFile f = return . decode =<< B.readFile f------ So for example if you wanted to decompress as well, you could use:------ > return . decode . decompress =<< B.readFile f------ After contructing the data from the input file, 'decodeFile' checks--- if the file is empty, and in doing so will force the associated file--- handle closed, if it is indeed empty. If the file is not empty, --- it is up to the decoding instance to consume the rest of the data,--- or otherwise finalise the resource.+-- | Decode a value from a file. In case of errors, 'error' will+-- be called with the error message. --+-- /Since: 0.7.0.0/ decodeFile :: Binary a => FilePath -> IO a decodeFile f = do- s <- L.readFile f- return $ runGet (do v <- get- m <- isEmpty- m `seq` return v) s---- needs bytestring 0.9.1.x to work ----------------------------------------------------------------------------- Lazy put and get---- lazyPut :: (Binary a) => a -> Put--- lazyPut a = put (encode a)---- lazyGet :: (Binary a) => Get a--- lazyGet = fmap decode get----------------------------------------------------------------------------- Simple instances---- The () type need never be written to disk: values of singleton type--- can be reconstructed from the type alone-instance Binary () where- put () = return ()- get = return ()---- Bools are encoded as a byte in the range 0 .. 1-instance Binary Bool where- put = putWord8 . fromIntegral . fromEnum- get = liftM (toEnum . fromIntegral) getWord8---- Values of type 'Ordering' are encoded as a byte in the range 0 .. 2-instance Binary Ordering where- put = putWord8 . fromIntegral . fromEnum- get = liftM (toEnum . fromIntegral) getWord8----------------------------------------------------------------------------- Words and Ints---- Words8s are written as bytes-instance Binary Word8 where- put = putWord8- get = getWord8---- Words16s are written as 2 bytes in big-endian (network) order-instance Binary Word16 where- put = putWord16be- get = getWord16be---- Words32s are written as 4 bytes in big-endian (network) order-instance Binary Word32 where- put = putWord32be- get = getWord32be---- Words64s are written as 8 bytes in big-endian (network) order-instance Binary Word64 where- put = putWord64be- get = getWord64be---- Int8s are written as a single byte.-instance Binary Int8 where- put i = put (fromIntegral i :: Word8)- get = liftM fromIntegral (get :: Get Word8)---- Int16s are written as a 2 bytes in big endian format-instance Binary Int16 where- put i = put (fromIntegral i :: Word16)- get = liftM fromIntegral (get :: Get Word16)---- Int32s are written as a 4 bytes in big endian format-instance Binary Int32 where- put i = put (fromIntegral i :: Word32)- get = liftM fromIntegral (get :: Get Word32)---- Int64s are written as a 4 bytes in big endian format-instance Binary Int64 where- put i = put (fromIntegral i :: Word64)- get = liftM fromIntegral (get :: Get Word64)------------------------------------------------------------------------------ Words are are written as Word64s, that is, 8 bytes in big endian format-instance Binary Word where- put i = put (fromIntegral i :: Word64)- get = liftM fromIntegral (get :: Get Word64)---- Ints are are written as Int64s, that is, 8 bytes in big endian format-instance Binary Int where- put i = put (fromIntegral i :: Int64)- get = liftM fromIntegral (get :: Get Int64)----------------------------------------------------------------------------- --- Portable, and pretty efficient, serialisation of Integer------- Fixed-size type for a subset of Integer-type SmallInt = Int32---- Integers are encoded in two ways: if they fit inside a SmallInt,--- they're written as a byte tag, and that value. If the Integer value--- is too large to fit in a SmallInt, it is written as a byte array,--- along with a sign and length field.--instance Binary Integer where-- {-# INLINE put #-}- put n | n >= lo && n <= hi = do- putWord8 0- put (fromIntegral n :: SmallInt) -- fast path- where- lo = fromIntegral (minBound :: SmallInt) :: Integer- hi = fromIntegral (maxBound :: SmallInt) :: Integer-- put n = do- putWord8 1- put sign- put (unroll (abs n)) -- unroll the bytes- where- sign = fromIntegral (signum n) :: Word8-- {-# INLINE get #-}- get = do- tag <- get :: Get Word8- case tag of- 0 -> liftM fromIntegral (get :: Get SmallInt)- _ -> do sign <- get- bytes <- get- let v = roll bytes- return $! if sign == (1 :: Word8) then v else - v------- Fold and unfold an Integer to and from a list of its bytes----unroll :: Integer -> [Word8]-unroll = unfoldr step- where- step 0 = Nothing- step i = Just (fromIntegral i, i `shiftR` 8)--roll :: [Word8] -> Integer-roll = foldr unstep 0- where- unstep b a = a `shiftL` 8 .|. fromIntegral b--{-------- An efficient, raw serialisation for Integer (GHC only)------- TODO This instance is not architecture portable. GMP stores numbers as--- arrays of machine sized words, so the byte format is not portable across--- architectures with different endianess and word size.--import Data.ByteString.Base (toForeignPtr,unsafePackAddress, memcpy)-import GHC.Base hiding (ord, chr)-import GHC.Prim-import GHC.Ptr (Ptr(..))-import GHC.IOBase (IO(..))--instance Binary Integer where- put (S# i) = putWord8 0 >> put (I# i)- put (J# s ba) = do- putWord8 1- put (I# s)- put (BA ba)-- get = do- b <- getWord8- case b of- 0 -> do (I# i#) <- get- return (S# i#)- _ -> do (I# s#) <- get- (BA a#) <- get- return (J# s# a#)--instance Binary ByteArray where-- -- Pretty safe.- put (BA ba) =- let sz = sizeofByteArray# ba -- (primitive) in *bytes*- addr = byteArrayContents# ba- bs = unsafePackAddress (I# sz) addr- in put bs -- write as a ByteString. easy, yay!-- -- Pretty scary. Should be quick though- get = do- (fp, off, n@(I# sz)) <- liftM toForeignPtr get -- so decode a ByteString- assert (off == 0) $ return $ unsafePerformIO $ do- (MBA arr) <- newByteArray sz -- and copy it into a ByteArray#- let to = byteArrayContents# (unsafeCoerce# arr) -- urk, is this safe?- withForeignPtr fp $ \from -> memcpy (Ptr to) from (fromIntegral n)- freezeByteArray arr---- wrapper for ByteArray#-data ByteArray = BA {-# UNPACK #-} !ByteArray#-data MBA = MBA {-# UNPACK #-} !(MutableByteArray# RealWorld)--newByteArray :: Int# -> IO MBA-newByteArray sz = IO $ \s ->- case newPinnedByteArray# sz s of { (# s', arr #) ->- (# s', MBA arr #) }--freezeByteArray :: MutableByteArray# RealWorld -> IO ByteArray-freezeByteArray arr = IO $ \s ->- case unsafeFreezeByteArray# arr s of { (# s', arr' #) ->- (# s', BA arr' #) }---}--instance (Binary a,Integral a) => Binary (R.Ratio a) where- put r = put (R.numerator r) >> put (R.denominator r)- get = liftM2 (R.%) get get------------------------------------------------------------------------------ Char is serialised as UTF-8-instance Binary Char where- put a | c <= 0x7f = put (fromIntegral c :: Word8)- | c <= 0x7ff = do put (0xc0 .|. y)- put (0x80 .|. z)- | c <= 0xffff = do put (0xe0 .|. x)- put (0x80 .|. y)- put (0x80 .|. z)- | c <= 0x10ffff = do put (0xf0 .|. w)- put (0x80 .|. x)- put (0x80 .|. y)- put (0x80 .|. z)- | otherwise = error "Not a valid Unicode code point"- where- c = ord a- z, y, x, w :: Word8- z = fromIntegral (c .&. 0x3f)- y = fromIntegral (shiftR c 6 .&. 0x3f)- x = fromIntegral (shiftR c 12 .&. 0x3f)- w = fromIntegral (shiftR c 18 .&. 0x7)-- get = do- let getByte = liftM (fromIntegral :: Word8 -> Int) get- shiftL6 = flip shiftL 6 :: Int -> Int- w <- getByte- r <- case () of- _ | w < 0x80 -> return w- | w < 0xe0 -> do- x <- liftM (xor 0x80) getByte- return (x .|. shiftL6 (xor 0xc0 w))- | w < 0xf0 -> do- x <- liftM (xor 0x80) getByte- y <- liftM (xor 0x80) getByte- return (y .|. shiftL6 (x .|. shiftL6- (xor 0xe0 w)))- | otherwise -> do- x <- liftM (xor 0x80) getByte- y <- liftM (xor 0x80) getByte- z <- liftM (xor 0x80) getByte- return (z .|. shiftL6 (y .|. shiftL6- (x .|. shiftL6 (xor 0xf0 w))))- return $! chr r----------------------------------------------------------------------------- Instances for the first few tuples--instance (Binary a, Binary b) => Binary (a,b) where- put (a,b) = put a >> put b- get = liftM2 (,) get get--instance (Binary a, Binary b, Binary c) => Binary (a,b,c) where- put (a,b,c) = put a >> put b >> put c- get = liftM3 (,,) get get get--instance (Binary a, Binary b, Binary c, Binary d) => Binary (a,b,c,d) where- put (a,b,c,d) = put a >> put b >> put c >> put d- get = liftM4 (,,,) get get get get--instance (Binary a, Binary b, Binary c, Binary d, Binary e) => Binary (a,b,c,d,e) where- put (a,b,c,d,e) = put a >> put b >> put c >> put d >> put e- get = liftM5 (,,,,) get get get get get---- --- and now just recurse:-----instance (Binary a, Binary b, Binary c, Binary d, Binary e, Binary f)- => Binary (a,b,c,d,e,f) where- put (a,b,c,d,e,f) = put (a,(b,c,d,e,f))- get = do (a,(b,c,d,e,f)) <- get ; return (a,b,c,d,e,f)--instance (Binary a, Binary b, Binary c, Binary d, Binary e, Binary f, Binary g)- => Binary (a,b,c,d,e,f,g) where- put (a,b,c,d,e,f,g) = put (a,(b,c,d,e,f,g))- get = do (a,(b,c,d,e,f,g)) <- get ; return (a,b,c,d,e,f,g)--instance (Binary a, Binary b, Binary c, Binary d, Binary e,- Binary f, Binary g, Binary h)- => Binary (a,b,c,d,e,f,g,h) where- put (a,b,c,d,e,f,g,h) = put (a,(b,c,d,e,f,g,h))- get = do (a,(b,c,d,e,f,g,h)) <- get ; return (a,b,c,d,e,f,g,h)--instance (Binary a, Binary b, Binary c, Binary d, Binary e,- Binary f, Binary g, Binary h, Binary i)- => Binary (a,b,c,d,e,f,g,h,i) where- put (a,b,c,d,e,f,g,h,i) = put (a,(b,c,d,e,f,g,h,i))- get = do (a,(b,c,d,e,f,g,h,i)) <- get ; return (a,b,c,d,e,f,g,h,i)--instance (Binary a, Binary b, Binary c, Binary d, Binary e,- Binary f, Binary g, Binary h, Binary i, Binary j)- => Binary (a,b,c,d,e,f,g,h,i,j) where- put (a,b,c,d,e,f,g,h,i,j) = put (a,(b,c,d,e,f,g,h,i,j))- get = do (a,(b,c,d,e,f,g,h,i,j)) <- get ; return (a,b,c,d,e,f,g,h,i,j)----------------------------------------------------------------------------- Container types--instance Binary a => Binary [a] where- put l = put (length l) >> mapM_ put l- get = do n <- get :: Get Int- replicateM n get--instance (Binary a) => Binary (Maybe a) where- put Nothing = putWord8 0- put (Just x) = putWord8 1 >> put x- get = do- w <- getWord8- case w of- 0 -> return Nothing- _ -> liftM Just get--instance (Binary a, Binary b) => Binary (Either a b) where- put (Left a) = putWord8 0 >> put a- put (Right b) = putWord8 1 >> put b- get = do- w <- getWord8- case w of- 0 -> liftM Left get- _ -> liftM Right get----------------------------------------------------------------------------- ByteStrings (have specially efficient instances)--instance Binary B.ByteString where- put bs = do put (B.length bs)- putByteString bs- get = get >>= getByteString------- Using old versions of fps, this is a type synonym, and non portable--- --- Requires 'flexible instances'----instance Binary ByteString where- put bs = do put (fromIntegral (L.length bs) :: Int)- putLazyByteString bs- get = get >>= getLazyByteString----------------------------------------------------------------------------- Maps and Sets--instance (Ord a, Binary a) => Binary (Set.Set a) where- put s = put (Set.size s) >> mapM_ put (Set.toAscList s)- get = liftM Set.fromDistinctAscList get--instance (Ord k, Binary k, Binary e) => Binary (Map.Map k e) where- put m = put (Map.size m) >> mapM_ put (Map.toAscList m)- get = liftM Map.fromDistinctAscList get--instance Binary IntSet.IntSet where- put s = put (IntSet.size s) >> mapM_ put (IntSet.toAscList s)- get = liftM IntSet.fromDistinctAscList get+ result <- decodeFileOrFail f+ case result of+ Right x -> return x+ Left (_,str) -> error str -instance (Binary e) => Binary (IntMap.IntMap e) where- put m = put (IntMap.size m) >> mapM_ put (IntMap.toAscList m)- get = liftM IntMap.fromDistinctAscList get+-- | Decode a value from a file. In case of success, the value will be returned+-- in 'Right'. In case of decoder errors, the error message together with+-- the byte offset will be returned.+decodeFileOrFail :: Binary a => FilePath -> IO (Either (ByteOffset, String) a)+decodeFileOrFail f =+ withBinaryFile f ReadMode $ \h -> do+ feed (runGetIncremental get) h+ where -- TODO: put in Data.Binary.Get and name pushFromHandle?+ feed (Done _ _ x) _ = return (Right x)+ feed (Fail _ pos str) _ = return (Left (pos, str))+ feed (Partial k) h = do+ chunk <- B.hGet h L.defaultChunkSize+ case B.length chunk of+ 0 -> feed (k Nothing) h+ _ -> feed (k (Just chunk)) h --------------------------------------------------------------------------- Queues and Sequences--#if __GLASGOW_HASKELL__ >= 606------ This is valid Hugs, but you need the most recent Hugs+-- $generics ----instance (Binary e) => Binary (Seq.Seq e) where- -- any better way to do this?- put = put . Fold.toList- get = fmap Seq.fromList get--#endif----------------------------------------------------------------------------- Floating point--instance Binary Double where- put d = put (decodeFloat d)- get = liftM2 encodeFloat get get--instance Binary Float where- put f = put (decodeFloat f)- get = liftM2 encodeFloat get get----------------------------------------------------------------------------- Trees--instance (Binary e) => Binary (T.Tree e) where- put (T.Node r s) = put r >> put s- get = liftM2 T.Node get get----------------------------------------------------------------------------- Arrays--instance (Binary i, Ix i, Binary e) => Binary (Array i e) where- put a = do- put (bounds a)- put (rangeSize $ bounds a) -- write the length- mapM_ put (elems a) -- now the elems.- get = do- bs <- get- n <- get -- read the length- xs <- replicateM n get -- now the elems.- return (listArray bs xs)-+-- Beginning with GHC 7.2, it is possible to use binary serialization+-- without writing any instance boilerplate code. ----- The IArray UArray e constraint is non portable. Requires flexible instances+-- > {-# LANGUAGE DeriveGeneric #-}+-- >+-- > import Data.Binary+-- > import GHC.Generics (Generic)+-- >+-- > data Foo = Foo+-- > deriving (Generic)+-- >+-- > -- GHC will automatically fill out the instance+-- > instance Binary Foo ---instance (Binary i, Ix i, Binary e, IArray UArray e) => Binary (UArray i e) where- put a = do- put (bounds a)- put (rangeSize $ bounds a) -- now write the length- mapM_ put (elems a)- get = do- bs <- get- n <- get- xs <- replicateM n get- return (listArray bs xs)+-- This mechanism makes use of GHC's efficient built-in generics+-- support.
src/Data/Binary/Builder.hs view
@@ -1,27 +1,23 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fglasgow-exts #-}--- for unboxed shifts+{-# LANGUAGE CPP, MagicHash #-}+{-# LANGUAGE Safe #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Binary.Builder -- Copyright : Lennart Kolmodin, Ross Paterson -- License : BSD3-style (see LICENSE)--- --- Maintainer : Lennart Kolmodin <kolmodin@dtek.chalmers.se>+--+-- Maintainer : Lennart Kolmodin <kolmodin@gmail.com> -- Stability : experimental -- Portability : portable to Hugs and GHC ----- Efficient construction of lazy bytestrings.+-- Efficient constructions of lazy bytestrings. --+-- This now re-exports 'Data.ByteString.Lazy.Builder'.+-- ----------------------------------------------------------------------------- -#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)-#include "MachDeps.h"-#endif- module Data.Binary.Builder (- -- * The Builder type Builder , toLazyByteString@@ -32,7 +28,9 @@ , append , fromByteString -- :: S.ByteString -> Builder , fromLazyByteString -- :: L.ByteString -> Builder-+#if MIN_VERSION_bytestring(0,10,4)+ , fromShortByteString -- :: T.ByteString -> Builder+#endif -- * Flushing the buffer state , flush @@ -41,67 +39,48 @@ , putWord16be -- :: Word16 -> Builder , putWord32be -- :: Word32 -> Builder , putWord64be -- :: Word64 -> Builder+ , putInt16be -- :: Int16 -> Builder+ , putInt32be -- :: Int32 -> Builder+ , putInt64be -- :: Int64 -> Builder -- ** Little-endian writes , putWord16le -- :: Word16 -> Builder , putWord32le -- :: Word32 -> Builder , putWord64le -- :: Word64 -> Builder+ , putInt16le -- :: Int16 -> Builder+ , putInt32le -- :: Int32 -> Builder+ , putInt64le -- :: Int64 -> Builder -- ** Host-endian, unaligned writes , putWordhost -- :: Word -> Builder , putWord16host -- :: Word16 -> Builder , putWord32host -- :: Word32 -> Builder , putWord64host -- :: Word64 -> Builder+ , putInthost -- :: Int -> Builder+ , putInt16host -- :: Int16 -> Builder+ , putInt32host -- :: Int32 -> Builder+ , putInt64host -- :: Int64 -> Builder - ) where+ -- ** Unicode+ , putCharUtf8+ , putStringUtf8+ ) where -import Foreign-import Data.Monoid-import Data.Word import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L -#ifdef BYTESTRING_IN_BASE-import Data.ByteString.Base (inlinePerformIO)-import qualified Data.ByteString.Base as S-#else-import Data.ByteString.Internal (inlinePerformIO)-import qualified Data.ByteString.Internal as S-import qualified Data.ByteString.Lazy.Internal as L-#endif--#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)-import GHC.Base-import GHC.Word (Word32(..),Word16(..),Word64(..))--#if WORD_SIZE_IN_BITS < 64 && __GLASGOW_HASKELL__ >= 608-import GHC.Word (uncheckedShiftRL64#)-#endif+#if MIN_VERSION_bytestring(0,10,4)+import qualified Data.ByteString.Short as T #endif ----------------------------------------------------------------------------- | A 'Builder' is an efficient way to build lazy 'L.ByteString's.--- There are several functions for constructing 'Builder's, but only one--- to inspect them: to extract any data, you have to turn them into lazy--- 'L.ByteString's using 'toLazyByteString'.------ Internally, a 'Builder' constructs a lazy 'L.Bytestring' by filling byte--- arrays piece by piece. As each buffer is filled, it is \'popped\'--- off, to become a new chunk of the resulting lazy 'L.ByteString'.--- All this is hidden from the user of the 'Builder'.--newtype Builder = Builder {- -- Invariant (from Data.ByteString.Lazy):- -- The lists include no null ByteStrings.- runBuilder :: (Buffer -> [S.ByteString]) -> Buffer -> [S.ByteString]- }--instance Monoid Builder where- mempty = empty- {-# INLINE mempty #-}- mappend = append- {-# INLINE mappend #-}+import qualified Data.ByteString.Builder as B+import qualified Data.ByteString.Builder.Prim as Prim+import Data.ByteString.Builder ( Builder, toLazyByteString )+import Data.ByteString.Builder.Extra ( flush )+import Data.Monoid+import Data.Word+import Data.Int+import Prelude -- Silence AMP warning. ------------------------------------------------------------------------ @@ -110,7 +89,7 @@ -- * @'toLazyByteString' 'empty' = 'L.empty'@ -- empty :: Builder-empty = Builder id+empty = mempty {-# INLINE empty #-} -- | /O(1)./ A Builder taking a single byte, satisfying@@ -118,7 +97,7 @@ -- * @'toLazyByteString' ('singleton' b) = 'L.singleton' b@ -- singleton :: Word8 -> Builder-singleton = writeN 1 . flip poke+singleton = B.word8 {-# INLINE singleton #-} ------------------------------------------------------------------------@@ -129,7 +108,7 @@ -- * @'toLazyByteString' ('append' x y) = 'L.append' ('toLazyByteString' x) ('toLazyByteString' y)@ -- append :: Builder -> Builder -> Builder-append (Builder f) (Builder g) = Builder (f . g)+append = mappend {-# INLINE append #-} -- | /O(1)./ A Builder taking a 'S.ByteString', satisfying@@ -137,9 +116,7 @@ -- * @'toLazyByteString' ('fromByteString' bs) = 'L.fromChunks' [bs]@ -- fromByteString :: S.ByteString -> Builder-fromByteString bs- | S.null bs = empty- | otherwise = flush `append` mapBuilder (bs :)+fromByteString = B.byteString {-# INLINE fromByteString #-} -- | /O(1)./ A Builder taking a lazy 'L.ByteString', satisfying@@ -147,219 +124,80 @@ -- * @'toLazyByteString' ('fromLazyByteString' bs) = bs@ -- fromLazyByteString :: L.ByteString -> Builder-fromLazyByteString bss = flush `append` mapBuilder (L.toChunks bss ++)+fromLazyByteString = B.lazyByteString {-# INLINE fromLazyByteString #-} ----------------------------------------------------------------------------- Our internal buffer type-data Buffer = Buffer {-# UNPACK #-} !(ForeignPtr Word8)- {-# UNPACK #-} !Int -- offset- {-# UNPACK #-} !Int -- used bytes- {-# UNPACK #-} !Int -- length left------------------------------------------------------------------------------ | /O(n)./ Extract a lazy 'L.ByteString' from a 'Builder'.--- The construction work takes place if and when the relevant part of--- the lazy 'L.ByteString' is demanded.----toLazyByteString :: Builder -> L.ByteString-toLazyByteString m = L.fromChunks $ unsafePerformIO $ do- buf <- newBuffer defaultSize- return (runBuilder (m `append` flush) (const []) buf)---- | /O(1)./ Pop the 'S.ByteString' we have constructed so far, if any,--- yielding a new chunk in the result lazy 'L.ByteString'.-flush :: Builder-flush = Builder $ \ k buf@(Buffer p o u l) ->- if u == 0- then k buf- else S.PS p o u : k (Buffer p (o+u) 0 l)--------------------------------------------------------------------------------- copied from Data.ByteString.Lazy+#if MIN_VERSION_bytestring(0,10,4)+-- | /O(n)./ A builder taking 'T.ShortByteString' and copy it to a Builder,+-- satisfying ---defaultSize :: Int-defaultSize = 32 * k - overhead- where k = 1024- overhead = 2 * sizeOf (undefined :: Int)------------------------------------------------------------------------------ | Sequence an IO operation on the buffer-unsafeLiftIO :: (Buffer -> IO Buffer) -> Builder-unsafeLiftIO f = Builder $ \ k buf -> inlinePerformIO $ do- buf' <- f buf- return (k buf')-{-# INLINE unsafeLiftIO #-}---- | Get the size of the buffer-withSize :: (Int -> Builder) -> Builder-withSize f = Builder $ \ k buf@(Buffer _ _ _ l) ->- runBuilder (f l) k buf---- | Map the resulting list of bytestrings.-mapBuilder :: ([S.ByteString] -> [S.ByteString]) -> Builder-mapBuilder f = Builder (f .)------------------------------------------------------------------------------ | Ensure that there are at least @n@ many bytes available.-ensureFree :: Int -> Builder-ensureFree n = n `seq` withSize $ \ l ->- if n <= l then empty else- flush `append` unsafeLiftIO (const (newBuffer (max n defaultSize)))-{-# INLINE ensureFree #-}---- | Ensure that @n@ many bytes are available, and then use @f@ to write some--- bytes into the memory.-writeN :: Int -> (Ptr Word8 -> IO ()) -> Builder-writeN n f = ensureFree n `append` unsafeLiftIO (writeNBuffer n f)-{-# INLINE writeN #-}--writeNBuffer :: Int -> (Ptr Word8 -> IO ()) -> Buffer -> IO Buffer-writeNBuffer n f (Buffer fp o u l) = do- withForeignPtr fp (\p -> f (p `plusPtr` (o+u)))- return (Buffer fp o (u+n) (l-n))-{-# INLINE writeNBuffer #-}--newBuffer :: Int -> IO Buffer-newBuffer size = do- fp <- S.mallocByteString size- return $! Buffer fp 0 0 size-{-# INLINE newBuffer #-}----------------------------------------------------------------------------- Aligned, host order writes of storable values---- | Ensure that @n@ many bytes are available, and then use @f@ to write some--- storable values into the memory.-writeNbytes :: Storable a => Int -> (Ptr a -> IO ()) -> Builder-writeNbytes n f = ensureFree n `append` unsafeLiftIO (writeNBufferBytes n f)-{-# INLINE writeNbytes #-}--writeNBufferBytes :: Storable a => Int -> (Ptr a -> IO ()) -> Buffer -> IO Buffer-writeNBufferBytes n f (Buffer fp o u l) = do- withForeignPtr fp (\p -> f (p `plusPtr` (o+u)))- return (Buffer fp o (u+n) (l-n))-{-# INLINE writeNBufferBytes #-}+-- * @'toLazyByteString' ('fromShortByteString' bs) = 'L.fromChunks' ['T.fromShort' bs]+fromShortByteString :: T.ShortByteString -> Builder+fromShortByteString = B.shortByteString+{-# INLINE fromShortByteString #-}+#endif ------------------------------------------------------------------------ ------ We rely on the fromIntegral to do the right masking for us.--- The inlining here is critical, and can be worth 4x performance---- -- | Write a Word16 in big endian format putWord16be :: Word16 -> Builder-putWord16be w = writeN 2 $ \p -> do- poke p (fromIntegral (shiftr_w16 w 8) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (w) :: Word8)+putWord16be = B.word16BE {-# INLINE putWord16be #-} -- | Write a Word16 in little endian format putWord16le :: Word16 -> Builder-putWord16le w = writeN 2 $ \p -> do- poke p (fromIntegral (w) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w16 w 8) :: Word8)+putWord16le = B.word16LE {-# INLINE putWord16le #-} --- putWord16le w16 = writeN 2 (\p -> poke (castPtr p) w16)- -- | Write a Word32 in big endian format putWord32be :: Word32 -> Builder-putWord32be w = writeN 4 $ \p -> do- poke p (fromIntegral (shiftr_w32 w 24) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 w 16) :: Word8)- poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 w 8) :: Word8)- poke (p `plusPtr` 3) (fromIntegral (w) :: Word8)+putWord32be = B.word32BE {-# INLINE putWord32be #-} ------ a data type to tag Put/Check. writes construct these which are then--- inlined and flattened. matching Checks will be more robust with rules.---- -- | Write a Word32 in little endian format putWord32le :: Word32 -> Builder-putWord32le w = writeN 4 $ \p -> do- poke p (fromIntegral (w) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 w 8) :: Word8)- poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 w 16) :: Word8)- poke (p `plusPtr` 3) (fromIntegral (shiftr_w32 w 24) :: Word8)+putWord32le = B.word32LE {-# INLINE putWord32le #-} --- on a little endian machine:--- putWord32le w32 = writeN 4 (\p -> poke (castPtr p) w32)- -- | Write a Word64 in big endian format putWord64be :: Word64 -> Builder-#if WORD_SIZE_IN_BITS < 64------ To avoid expensive 64 bit shifts on 32 bit machines, we cast to--- Word32, and write that----putWord64be w =- let a = fromIntegral (shiftr_w64 w 32) :: Word32- b = fromIntegral w :: Word32- in writeN 8 $ \p -> do- poke p (fromIntegral (shiftr_w32 a 24) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 a 16) :: Word8)- poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 a 8) :: Word8)- poke (p `plusPtr` 3) (fromIntegral (a) :: Word8)- poke (p `plusPtr` 4) (fromIntegral (shiftr_w32 b 24) :: Word8)- poke (p `plusPtr` 5) (fromIntegral (shiftr_w32 b 16) :: Word8)- poke (p `plusPtr` 6) (fromIntegral (shiftr_w32 b 8) :: Word8)- poke (p `plusPtr` 7) (fromIntegral (b) :: Word8)-#else-putWord64be w = writeN 8 $ \p -> do- poke p (fromIntegral (shiftr_w64 w 56) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w64 w 48) :: Word8)- poke (p `plusPtr` 2) (fromIntegral (shiftr_w64 w 40) :: Word8)- poke (p `plusPtr` 3) (fromIntegral (shiftr_w64 w 32) :: Word8)- poke (p `plusPtr` 4) (fromIntegral (shiftr_w64 w 24) :: Word8)- poke (p `plusPtr` 5) (fromIntegral (shiftr_w64 w 16) :: Word8)- poke (p `plusPtr` 6) (fromIntegral (shiftr_w64 w 8) :: Word8)- poke (p `plusPtr` 7) (fromIntegral (w) :: Word8)-#endif+putWord64be = B.word64BE {-# INLINE putWord64be #-} -- | Write a Word64 in little endian format putWord64le :: Word64 -> Builder--#if WORD_SIZE_IN_BITS < 64-putWord64le w =- let b = fromIntegral (shiftr_w64 w 32) :: Word32- a = fromIntegral w :: Word32- in writeN 8 $ \p -> do- poke (p) (fromIntegral (a) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 a 8) :: Word8)- poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 a 16) :: Word8)- poke (p `plusPtr` 3) (fromIntegral (shiftr_w32 a 24) :: Word8)- poke (p `plusPtr` 4) (fromIntegral (b) :: Word8)- poke (p `plusPtr` 5) (fromIntegral (shiftr_w32 b 8) :: Word8)- poke (p `plusPtr` 6) (fromIntegral (shiftr_w32 b 16) :: Word8)- poke (p `plusPtr` 7) (fromIntegral (shiftr_w32 b 24) :: Word8)-#else-putWord64le w = writeN 8 $ \p -> do- poke p (fromIntegral (w) :: Word8)- poke (p `plusPtr` 1) (fromIntegral (shiftr_w64 w 8) :: Word8)- poke (p `plusPtr` 2) (fromIntegral (shiftr_w64 w 16) :: Word8)- poke (p `plusPtr` 3) (fromIntegral (shiftr_w64 w 24) :: Word8)- poke (p `plusPtr` 4) (fromIntegral (shiftr_w64 w 32) :: Word8)- poke (p `plusPtr` 5) (fromIntegral (shiftr_w64 w 40) :: Word8)- poke (p `plusPtr` 6) (fromIntegral (shiftr_w64 w 48) :: Word8)- poke (p `plusPtr` 7) (fromIntegral (shiftr_w64 w 56) :: Word8)-#endif+putWord64le = B.word64LE {-# INLINE putWord64le #-} --- on a little endian machine:--- putWord64le w64 = writeN 8 (\p -> poke (castPtr p) w64)+-- | Write a Int16 in big endian format+putInt16be :: Int16 -> Builder+putInt16be = B.int16BE+{-# INLINE putInt16be #-} +-- | Write a Int16 in little endian format+putInt16le :: Int16 -> Builder+putInt16le = B.int16LE+{-# INLINE putInt16le #-}++-- | Write a Int32 in big endian format+putInt32be :: Int32 -> Builder+putInt32be = B.int32BE+{-# INLINE putInt32be #-}++-- | Write a Int32 in little endian format+putInt32le :: Int32 -> Builder+putInt32le = B.int32LE+{-# INLINE putInt32le #-}++-- | Write a Int64 in big endian format+putInt64be :: Int64 -> Builder+putInt64be = B.int64BE++-- | Write a Int64 in little endian format+putInt64le :: Int64 -> Builder+putInt64le = B.int64LE++ ------------------------------------------------------------------------ -- Unaligned, word size ops @@ -370,57 +208,67 @@ -- different endian or word sized machines, without conversion. -- putWordhost :: Word -> Builder-putWordhost w = writeNbytes (sizeOf (undefined :: Word)) (\p -> poke p w)+putWordhost = Prim.primFixed Prim.wordHost {-# INLINE putWordhost #-} -- | Write a Word16 in native host order and host endianness. -- 2 bytes will be written, unaligned. putWord16host :: Word16 -> Builder-putWord16host w16 = writeNbytes (sizeOf (undefined :: Word16)) (\p -> poke p w16)+putWord16host = Prim.primFixed Prim.word16Host {-# INLINE putWord16host #-} -- | Write a Word32 in native host order and host endianness. -- 4 bytes will be written, unaligned. putWord32host :: Word32 -> Builder-putWord32host w32 = writeNbytes (sizeOf (undefined :: Word32)) (\p -> poke p w32)+putWord32host = Prim.primFixed Prim.word32Host {-# INLINE putWord32host #-} -- | Write a Word64 in native host order. -- On a 32 bit machine we write two host order Word32s, in big endian form. -- 8 bytes will be written, unaligned. putWord64host :: Word64 -> Builder-putWord64host w = writeNbytes (sizeOf (undefined :: Word64)) (\p -> poke p w)+putWord64host = Prim.primFixed Prim.word64Host {-# INLINE putWord64host #-} ---------------------------------------------------------------------------- Unchecked shifts+-- | /O(1)./ A Builder taking a single native machine word. The word is+-- written in host order, host endian form, for the machine you're on.+-- On a 64 bit machine the Int is an 8 byte value, on a 32 bit machine,+-- 4 bytes. Values written this way are not portable to+-- different endian or word sized machines, without conversion.+--+putInthost :: Int -> Builder+putInthost = Prim.primFixed Prim.intHost+{-# INLINE putInthost #-} -{-# INLINE shiftr_w16 #-}-shiftr_w16 :: Word16 -> Int -> Word16-{-# INLINE shiftr_w32 #-}-shiftr_w32 :: Word32 -> Int -> Word32-{-# INLINE shiftr_w64 #-}-shiftr_w64 :: Word64 -> Int -> Word64+-- | Write a Int16 in native host order and host endianness.+-- 2 bytes will be written, unaligned.+putInt16host :: Int16 -> Builder+putInt16host = Prim.primFixed Prim.int16Host+{-# INLINE putInt16host #-} -#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)-shiftr_w16 (W16# w) (I# i) = W16# (w `uncheckedShiftRL#` i)-shiftr_w32 (W32# w) (I# i) = W32# (w `uncheckedShiftRL#` i)+-- | Write a Int32 in native host order and host endianness.+-- 4 bytes will be written, unaligned.+putInt32host :: Int32 -> Builder+putInt32host = Prim.primFixed Prim.int32Host+{-# INLINE putInt32host #-} -#if WORD_SIZE_IN_BITS < 64-shiftr_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftRL64#` i)+-- | Write a Int64 in native host order.+-- On a 32 bit machine we write two host order Int32s, in big endian form.+-- 8 bytes will be written, unaligned.+putInt64host :: Int64 -> Builder+putInt64host = Prim.primFixed Prim.int64Host+{-# INLINE putInt64host #-} -#if __GLASGOW_HASKELL__ <= 606--- Exported by GHC.Word in GHC 6.8 and higher-foreign import ccall unsafe "stg_uncheckedShiftRL64"- uncheckedShiftRL64# :: Word64# -> Int# -> Word64#-#endif -#else-shiftr_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftRL#` i)-#endif+------------------------------------------------------------------------+-- Unicode -#else-shiftr_w16 = shiftR-shiftr_w32 = shiftR-shiftr_w64 = shiftR-#endif+-- | Write a character using UTF-8 encoding.+putCharUtf8 :: Char -> Builder+putCharUtf8 = Prim.primBounded Prim.charUtf8+{-# INLINE putCharUtf8 #-}++-- | Write a String using UTF-8 encoding.+putStringUtf8 :: String -> Builder+putStringUtf8 = Prim.primMapListBounded Prim.charUtf8+{-# INLINE putStringUtf8 #-}
+ src/Data/Binary/Class.hs view
@@ -0,0 +1,1022 @@+{-# LANGUAGE CPP, FlexibleContexts #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE Trustworthy #-}++#if __GLASGOW_HASKELL__ >= 706+{-# LANGUAGE PolyKinds #-}+#endif++#if MIN_VERSION_base(4,8,0)+#define HAS_NATURAL+#define HAS_VOID+#endif++#if MIN_VERSION_base(4,7,0)+#define HAS_FIXED_CONSTRUCTOR+#endif++-----------------------------------------------------------------------------+-- |+-- Module : Data.Binary.Class+-- Copyright : Lennart Kolmodin+-- License : BSD3-style (see LICENSE)+--+-- Maintainer : Lennart Kolmodin <kolmodin@gmail.com>+-- Stability : unstable+-- Portability : portable to Hugs and GHC. Requires the FFI and some flexible instances+--+-- Typeclass and instances for binary serialization.+--+-----------------------------------------------------------------------------++module Data.Binary.Class (++ -- * The Binary class+ Binary(..)++ -- * Support for generics+ , GBinaryGet(..)+ , GBinaryPut(..)++ ) where++import Data.Word+import Data.Bits+import Data.Int+import Data.Complex (Complex(..))+#ifdef HAS_VOID+import Data.Void+#endif++import Data.Binary.Put+import Data.Binary.Get++#if ! MIN_VERSION_base(4,8,0)+import Control.Applicative+import Data.Monoid (mempty)+#endif+import qualified Data.Monoid as Monoid+import Data.Monoid ((<>))+#if MIN_VERSION_base(4,8,0)+import Data.Functor.Identity (Identity (..))+#endif+#if MIN_VERSION_base(4,9,0)+import qualified Data.List.NonEmpty as NE+import qualified Data.Semigroup as Semigroup+#endif+import Control.Monad++import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Builder.Prim as Prim++import Data.List (unfoldr, foldl')++-- And needed for the instances:+#if MIN_VERSION_base(4,10,0)+import Type.Reflection+import Type.Reflection.Unsafe+import Data.Kind (Type)+import GHC.Exts (RuntimeRep(..), VecCount, VecElem)+#endif+import qualified Data.ByteString as B+#if MIN_VERSION_bytestring(0,10,4)+import qualified Data.ByteString.Short as BS+#endif+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import qualified Data.Ratio as R++import qualified Data.Tree as T++import Data.Array.Unboxed++import GHC.Generics++#ifdef HAS_NATURAL+import Numeric.Natural+#endif++import qualified Data.Fixed as Fixed++--+-- This isn't available in older Hugs or older GHC+--+import qualified Data.Sequence as Seq+import qualified Data.Foldable as Fold++import GHC.Fingerprint++import Data.Version (Version(..))++------------------------------------------------------------------------++-- Factored into two classes because this makes GHC optimize the+-- instances faster. This doesn't matter for builds of binary,+-- but it matters a lot for end-users who write 'instance Binary T'.+-- See also: https://ghc.haskell.org/trac/ghc/ticket/9630+class GBinaryPut f where+ gput :: f t -> Put++class GBinaryGet f where+ gget :: Get (f t)++-- | The 'Binary' class provides 'put' and 'get', methods to encode and+-- decode a Haskell value to a lazy 'ByteString'. It mirrors the 'Read' and+-- 'Show' classes for textual representation of Haskell types, and is+-- suitable for serialising Haskell values to disk, over the network.+--+-- For decoding and generating simple external binary formats (e.g. C+-- structures), Binary may be used, but in general is not suitable+-- for complex protocols. Instead use the 'Put' and 'Get' primitives+-- directly.+--+-- Instances of Binary should satisfy the following property:+--+-- > decode . encode == id+--+-- That is, the 'get' and 'put' methods should be the inverse of each+-- other. A range of instances are provided for basic Haskell types.+--+class Binary t where+ -- | Encode a value in the Put monad.+ put :: t -> Put+ -- | Decode a value in the Get monad+ get :: Get t++ -- | Encode a list of values in the Put monad.+ -- The default implementation may be overridden to be more efficient+ -- but must still have the same encoding format.+ putList :: [t] -> Put+ putList = defaultPutList++ default put :: (Generic t, GBinaryPut (Rep t)) => t -> Put+ put = gput . from++ default get :: (Generic t, GBinaryGet (Rep t)) => Get t+ get = to `fmap` gget++{-# INLINE defaultPutList #-}+defaultPutList :: Binary a => [a] -> Put+defaultPutList xs = put (length xs) <> mapM_ put xs++------------------------------------------------------------------------+-- Simple instances++#ifdef HAS_VOID+-- Void never gets written nor reconstructed since it's impossible to have a+-- value of that type++-- | /Since: 0.8.0.0/+instance Binary Void where+ put = absurd+ get = mzero+#endif++-- The () type need never be written to disk: values of singleton type+-- can be reconstructed from the type alone+instance Binary () where+ put () = mempty+ get = return ()++-- Bools are encoded as a byte in the range 0 .. 1+instance Binary Bool where+ put = putWord8 . fromIntegral . fromEnum+ get = getWord8 >>= toBool+ where+ toBool 0 = return False+ toBool 1 = return True+ toBool c = fail ("Could not map value " ++ show c ++ " to Bool")++-- Values of type 'Ordering' are encoded as a byte in the range 0 .. 2+instance Binary Ordering where+ put = putWord8 . fromIntegral . fromEnum+ get = getWord8 >>= toOrd+ where+ toOrd 0 = return LT+ toOrd 1 = return EQ+ toOrd 2 = return GT+ toOrd c = fail ("Could not map value " ++ show c ++ " to Ordering")++------------------------------------------------------------------------+-- Words and Ints++-- Words8s are written as bytes+instance Binary Word8 where+ put = putWord8+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.word8 xs)+ get = getWord8++-- Words16s are written as 2 bytes in big-endian (network) order+instance Binary Word16 where+ put = putWord16be+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.word16BE xs)+ get = getWord16be++-- Words32s are written as 4 bytes in big-endian (network) order+instance Binary Word32 where+ put = putWord32be+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.word32BE xs)+ get = getWord32be++-- Words64s are written as 8 bytes in big-endian (network) order+instance Binary Word64 where+ put = putWord64be+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.word64BE xs)+ get = getWord64be++-- Int8s are written as a single byte.+instance Binary Int8 where+ put = putInt8+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.int8 xs)+ get = getInt8++-- Int16s are written as a 2 bytes in big endian format+instance Binary Int16 where+ put = putInt16be+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.int16BE xs)+ get = getInt16be++-- Int32s are written as a 4 bytes in big endian format+instance Binary Int32 where+ put = putInt32be+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.int32BE xs)+ get = getInt32be++-- Int64s are written as a 8 bytes in big endian format+instance Binary Int64 where+ put = putInt64be+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.int64BE xs)+ get = getInt64be++------------------------------------------------------------------------++-- Words are are written as Word64s, that is, 8 bytes in big endian format+instance Binary Word where+ put = putWord64be . fromIntegral+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.word64BE (map fromIntegral xs))+ get = liftM fromIntegral getWord64be++-- Ints are are written as Int64s, that is, 8 bytes in big endian format+instance Binary Int where+ put = putInt64be . fromIntegral+ {-# INLINE putList #-}+ putList xs =+ put (length xs)+ <> putBuilder (Prim.primMapListFixed Prim.int64BE (map fromIntegral xs))+ get = liftM fromIntegral getInt64be++------------------------------------------------------------------------+--+-- Portable, and pretty efficient, serialisation of Integer+--++-- Fixed-size type for a subset of Integer+type SmallInt = Int32++-- Integers are encoded in two ways: if they fit inside a SmallInt,+-- they're written as a byte tag, and that value. If the Integer value+-- is too large to fit in a SmallInt, it is written as a byte array,+-- along with a sign and length field.++instance Binary Integer where++ {-# INLINE put #-}+ put n | n >= lo && n <= hi =+ putBuilder (Prim.primFixed (Prim.word8 Prim.>*< Prim.int32BE) (0, fromIntegral n))+ where+ lo = fromIntegral (minBound :: SmallInt) :: Integer+ hi = fromIntegral (maxBound :: SmallInt) :: Integer++ put n =+ putWord8 1+ <> put sign+ <> put (unroll (abs n)) -- unroll the bytes+ where+ sign = fromIntegral (signum n) :: Word8++ {-# INLINE get #-}+ get = do+ tag <- get :: Get Word8+ case tag of+ 0 -> liftM fromIntegral (get :: Get SmallInt)+ _ -> do sign <- get+ bytes <- get+ let v = roll bytes+ return $! if sign == (1 :: Word8) then v else - v++-- | /Since: 0.8.0.0/+#ifdef HAS_FIXED_CONSTRUCTOR+instance Binary (Fixed.Fixed a) where+ put (Fixed.MkFixed a) = put a+ get = Fixed.MkFixed `liftM` get+#else+instance forall a. Fixed.HasResolution a => Binary (Fixed.Fixed a) where+ -- Using undefined :: Maybe a as a proxy, as Data.Proxy is introduced only in base-4.7+ put x = put (truncate (x * fromInteger (Fixed.resolution (undefined :: Maybe a))) :: Integer)+ get = (\x -> fromInteger x / fromInteger (Fixed.resolution (undefined :: Maybe a))) `liftM` get+#endif++--+-- Fold and unfold an Integer to and from a list of its bytes+--+unroll :: (Integral a, Bits a) => a -> [Word8]+unroll = unfoldr step+ where+ step 0 = Nothing+ step i = Just (fromIntegral i, i `shiftR` 8)++roll :: (Integral a, Bits a) => [Word8] -> a+roll = foldl' unstep 0 . reverse+ where+ unstep a b = a `shiftL` 8 .|. fromIntegral b++#ifdef HAS_NATURAL+-- Fixed-size type for a subset of Natural+type NaturalWord = Word64++-- | /Since: 0.7.3.0/+instance Binary Natural where+ {-# INLINE put #-}+ put n | n <= hi =+ putWord8 0+ <> put (fromIntegral n :: NaturalWord) -- fast path+ where+ hi = fromIntegral (maxBound :: NaturalWord) :: Natural++ put n =+ putWord8 1+ <> put (unroll (abs n)) -- unroll the bytes++ {-# INLINE get #-}+ get = do+ tag <- get :: Get Word8+ case tag of+ 0 -> liftM fromIntegral (get :: Get NaturalWord)+ _ -> do bytes <- get+ return $! roll bytes+#endif++{-++--+-- An efficient, raw serialisation for Integer (GHC only)+--++-- TODO This instance is not architecture portable. GMP stores numbers as+-- arrays of machine sized words, so the byte format is not portable across+-- architectures with different endianness and word size.++import Data.ByteString.Base (toForeignPtr,unsafePackAddress, memcpy)+import GHC.Base hiding (ord, chr)+import GHC.Prim+import GHC.Ptr (Ptr(..))+import GHC.IOBase (IO(..))++instance Binary Integer where+ put (S# i) = putWord8 0 >> put (I# i)+ put (J# s ba) = do+ putWord8 1+ put (I# s)+ put (BA ba)++ get = do+ b <- getWord8+ case b of+ 0 -> do (I# i#) <- get+ return (S# i#)+ _ -> do (I# s#) <- get+ (BA a#) <- get+ return (J# s# a#)++instance Binary ByteArray where++ -- Pretty safe.+ put (BA ba) =+ let sz = sizeofByteArray# ba -- (primitive) in *bytes*+ addr = byteArrayContents# ba+ bs = unsafePackAddress (I# sz) addr+ in put bs -- write as a ByteString. easy, yay!++ -- Pretty scary. Should be quick though+ get = do+ (fp, off, n@(I# sz)) <- liftM toForeignPtr get -- so decode a ByteString+ assert (off == 0) $ return $ unsafePerformIO $ do+ (MBA arr) <- newByteArray sz -- and copy it into a ByteArray#+ let to = byteArrayContents# (unsafeCoerce# arr) -- urk, is this safe?+ withForeignPtr fp $ \from -> memcpy (Ptr to) from (fromIntegral n)+ freezeByteArray arr++-- wrapper for ByteArray#+data ByteArray = BA {-# UNPACK #-} !ByteArray#+data MBA = MBA {-# UNPACK #-} !(MutableByteArray# RealWorld)++newByteArray :: Int# -> IO MBA+newByteArray sz = IO $ \s ->+ case newPinnedByteArray# sz s of { (# s', arr #) ->+ (# s', MBA arr #) }++freezeByteArray :: MutableByteArray# RealWorld -> IO ByteArray+freezeByteArray arr = IO $ \s ->+ case unsafeFreezeByteArray# arr s of { (# s', arr' #) ->+ (# s', BA arr' #) }++-}++instance (Binary a,Integral a) => Binary (R.Ratio a) where+ put r = put (R.numerator r) <> put (R.denominator r)+ get = liftM2 (R.%) get get++instance Binary a => Binary (Complex a) where+ {-# INLINE put #-}+ put (r :+ i) = put (r, i)+ {-# INLINE get #-}+ get = (\(r,i) -> r :+ i) <$> get++------------------------------------------------------------------------++-- Char is serialised as UTF-8+instance Binary Char where+ put = putCharUtf8+ putList str = put (length str) <> putStringUtf8 str+ get = do+ let getByte = liftM (fromIntegral :: Word8 -> Int) get+ shiftL6 = flip shiftL 6 :: Int -> Int+ w <- getByte+ r <- case () of+ _ | w < 0x80 -> return w+ | w < 0xe0 -> do+ x <- liftM (xor 0x80) getByte+ return (x .|. shiftL6 (xor 0xc0 w))+ | w < 0xf0 -> do+ x <- liftM (xor 0x80) getByte+ y <- liftM (xor 0x80) getByte+ return (y .|. shiftL6 (x .|. shiftL6+ (xor 0xe0 w)))+ | otherwise -> do+ x <- liftM (xor 0x80) getByte+ y <- liftM (xor 0x80) getByte+ z <- liftM (xor 0x80) getByte+ return (z .|. shiftL6 (y .|. shiftL6+ (x .|. shiftL6 (xor 0xf0 w))))+ getChr r+ where+ getChr w+ | w <= 0x10ffff = return $! toEnum $ fromEnum w+ | otherwise = fail "Not a valid Unicode code point!"++------------------------------------------------------------------------+-- Instances for the first few tuples++instance (Binary a, Binary b) => Binary (a,b) where+ {-# INLINE put #-}+ put (a,b) = put a <> put b+ {-# INLINE get #-}+ get = liftM2 (,) get get++instance (Binary a, Binary b, Binary c) => Binary (a,b,c) where+ {-# INLINE put #-}+ put (a,b,c) = put a <> put b <> put c+ {-# INLINE get #-}+ get = liftM3 (,,) get get get++instance (Binary a, Binary b, Binary c, Binary d) => Binary (a,b,c,d) where+ {-# INLINE put #-}+ put (a,b,c,d) = put a <> put b <> put c <> put d+ {-# INLINE get #-}+ get = liftM4 (,,,) get get get get++instance (Binary a, Binary b, Binary c, Binary d, Binary e) => Binary (a,b,c,d,e) where+ {-# INLINE put #-}+ put (a,b,c,d,e) = put a <> put b <> put c <> put d <> put e+ {-# INLINE get #-}+ get = liftM5 (,,,,) get get get get get++--+-- and now just recurse:+--++instance (Binary a, Binary b, Binary c, Binary d, Binary e, Binary f)+ => Binary (a,b,c,d,e,f) where+ {-# INLINE put #-}+ put (a,b,c,d,e,f) = put (a,(b,c,d,e,f))+ {-# INLINE get #-}+ get = do (a,(b,c,d,e,f)) <- get ; return (a,b,c,d,e,f)++instance (Binary a, Binary b, Binary c, Binary d, Binary e, Binary f, Binary g)+ => Binary (a,b,c,d,e,f,g) where+ {-# INLINE put #-}+ put (a,b,c,d,e,f,g) = put (a,(b,c,d,e,f,g))+ {-# INLINE get #-}+ get = do (a,(b,c,d,e,f,g)) <- get ; return (a,b,c,d,e,f,g)++instance (Binary a, Binary b, Binary c, Binary d, Binary e,+ Binary f, Binary g, Binary h)+ => Binary (a,b,c,d,e,f,g,h) where+ {-# INLINE put #-}+ put (a,b,c,d,e,f,g,h) = put (a,(b,c,d,e,f,g,h))+ {-# INLINE get #-}+ get = do (a,(b,c,d,e,f,g,h)) <- get ; return (a,b,c,d,e,f,g,h)++instance (Binary a, Binary b, Binary c, Binary d, Binary e,+ Binary f, Binary g, Binary h, Binary i)+ => Binary (a,b,c,d,e,f,g,h,i) where+ {-# INLINE put #-}+ put (a,b,c,d,e,f,g,h,i) = put (a,(b,c,d,e,f,g,h,i))+ {-# INLINE get #-}+ get = do (a,(b,c,d,e,f,g,h,i)) <- get ; return (a,b,c,d,e,f,g,h,i)++instance (Binary a, Binary b, Binary c, Binary d, Binary e,+ Binary f, Binary g, Binary h, Binary i, Binary j)+ => Binary (a,b,c,d,e,f,g,h,i,j) where+ {-# INLINE put #-}+ put (a,b,c,d,e,f,g,h,i,j) = put (a,(b,c,d,e,f,g,h,i,j))+ {-# INLINE get #-}+ get = do (a,(b,c,d,e,f,g,h,i,j)) <- get ; return (a,b,c,d,e,f,g,h,i,j)++------------------------------------------------------------------------+-- Container types++#if MIN_VERSION_base(4,8,0)+instance Binary a => Binary (Identity a) where+ put (Identity x) = put x+ get = Identity <$> get+#endif++instance Binary a => Binary [a] where+ put = putList+ get = do n <- get :: Get Int+ getMany n++-- | 'getMany n' get 'n' elements in order, without blowing the stack.+getMany :: Binary a => Int -> Get [a]+getMany n = go [] n+ where+ go xs 0 = return $! reverse xs+ go xs i = do x <- get+ -- we must seq x to avoid stack overflows due to laziness in+ -- (>>=)+ x `seq` go (x:xs) (i-1)+{-# INLINE getMany #-}++instance (Binary a) => Binary (Maybe a) where+ put Nothing = putWord8 0+ put (Just x) = putWord8 1 <> put x+ get = do+ w <- getWord8+ case w of+ 0 -> return Nothing+ _ -> liftM Just get++instance (Binary a, Binary b) => Binary (Either a b) where+ put (Left a) = putWord8 0 <> put a+ put (Right b) = putWord8 1 <> put b+ get = do+ w <- getWord8+ case w of+ 0 -> liftM Left get+ _ -> liftM Right get++------------------------------------------------------------------------+-- ByteStrings (have specially efficient instances)++instance Binary B.ByteString where+ put bs = put (B.length bs)+ <> putByteString bs+ get = get >>= getByteString++--+-- Using old versions of fps, this is a type synonym, and non portable+--+-- Requires 'flexible instances'+--+instance Binary ByteString where+ put bs = put (fromIntegral (L.length bs) :: Int)+ <> putLazyByteString bs+ get = get >>= getLazyByteString+++#if MIN_VERSION_bytestring(0,10,4)+instance Binary BS.ShortByteString where+ put bs = put (BS.length bs)+ <> putShortByteString bs+ get = get >>= fmap BS.toShort . getByteString+#endif++------------------------------------------------------------------------+-- Maps and Sets++instance (Binary a) => Binary (Set.Set a) where+ put s = put (Set.size s) <> mapM_ put (Set.toAscList s)+ get = liftM Set.fromDistinctAscList get++instance (Binary k, Binary e) => Binary (Map.Map k e) where+ put m = put (Map.size m) <> mapM_ put (Map.toAscList m)+ get = liftM Map.fromDistinctAscList get++instance Binary IntSet.IntSet where+ put s = put (IntSet.size s) <> mapM_ put (IntSet.toAscList s)+ get = liftM IntSet.fromDistinctAscList get++instance (Binary e) => Binary (IntMap.IntMap e) where+ put m = put (IntMap.size m) <> mapM_ put (IntMap.toAscList m)+ get = liftM IntMap.fromDistinctAscList get++------------------------------------------------------------------------+-- Queues and Sequences++--+-- This is valid Hugs, but you need the most recent Hugs+--++instance (Binary e) => Binary (Seq.Seq e) where+ put s = put (Seq.length s) <> Fold.mapM_ put s+ get = do n <- get :: Get Int+ rep Seq.empty n get+ where rep xs 0 _ = return $! xs+ rep xs n g = xs `seq` n `seq` do+ x <- g+ rep (xs Seq.|> x) (n-1) g++------------------------------------------------------------------------+-- Floating point++instance Binary Double where+ put d = put (decodeFloat d)+ get = do+ x <- get+ y <- get+ return $! encodeFloat x y++instance Binary Float where+ put f = put (decodeFloat f)+ get = do+ x <- get+ y <- get+ return $! encodeFloat x y++------------------------------------------------------------------------+-- Trees++instance (Binary e) => Binary (T.Tree e) where+ put (T.Node r s) = put r <> put s+ get = liftM2 T.Node get get++------------------------------------------------------------------------+-- Arrays++instance (Binary i, Ix i, Binary e) => Binary (Array i e) where+ put a =+ put (bounds a)+ <> put (rangeSize $ bounds a) -- write the length+ <> mapM_ put (elems a) -- now the elems.+ get = do+ bs <- get+ n <- get -- read the length+ xs <- getMany n -- now the elems.+ return (listArray bs xs)++--+-- The IArray UArray e constraint is non portable. Requires flexible instances+--+instance (Binary i, Ix i, Binary e, IArray UArray e) => Binary (UArray i e) where+ put a =+ put (bounds a)+ <> put (rangeSize $ bounds a) -- now write the length+ <> mapM_ put (elems a)+ get = do+ bs <- get+ n <- get+ xs <- getMany n+ return (listArray bs xs)++------------------------------------------------------------------------+-- Fingerprints++-- | /Since: 0.7.6.0/+instance Binary Fingerprint where+ put (Fingerprint x1 x2) = put x1 <> put x2+ get = do+ x1 <- get+ x2 <- get+ return $! Fingerprint x1 x2++------------------------------------------------------------------------+-- Version++-- | /Since: 0.8.0.0/+instance Binary Version where+ put (Version br tags) = put br <> put tags+ get = Version <$> get <*> get++------------------------------------------------------------------------+-- Data.Monoid datatypes++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Monoid.Dual a) where+ get = fmap Monoid.Dual get+ put = put . Monoid.getDual++-- | /Since: 0.8.4.0/+instance Binary Monoid.All where+ get = fmap Monoid.All get+ put = put . Monoid.getAll++-- | /Since: 0.8.4.0/+instance Binary Monoid.Any where+ get = fmap Monoid.Any get+ put = put . Monoid.getAny++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Monoid.Sum a) where+ get = fmap Monoid.Sum get+ put = put . Monoid.getSum++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Monoid.Product a) where+ get = fmap Monoid.Product get+ put = put . Monoid.getProduct++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Monoid.First a) where+ get = fmap Monoid.First get+ put = put . Monoid.getFirst++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Monoid.Last a) where+ get = fmap Monoid.Last get+ put = put . Monoid.getLast++#if MIN_VERSION_base(4,8,0)+-- | /Since: 0.8.4.0/+instance Binary (f a) => Binary (Monoid.Alt f a) where+ get = fmap Monoid.Alt get+ put = put . Monoid.getAlt+#endif++#if MIN_VERSION_base(4,9,0)+------------------------------------------------------------------------+-- Data.Semigroup datatypes++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Semigroup.Min a) where+ get = fmap Semigroup.Min get+ put = put . Semigroup.getMin++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Semigroup.Max a) where+ get = fmap Semigroup.Max get+ put = put . Semigroup.getMax++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Semigroup.First a) where+ get = fmap Semigroup.First get+ put = put . Semigroup.getFirst++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Semigroup.Last a) where+ get = fmap Semigroup.Last get+ put = put . Semigroup.getLast++-- | /Since: 0.8.4.0/+instance Binary a => Binary (Semigroup.Option a) where+ get = fmap Semigroup.Option get+ put = put . Semigroup.getOption++-- | /Since: 0.8.4.0/+instance Binary m => Binary (Semigroup.WrappedMonoid m) where+ get = fmap Semigroup.WrapMonoid get+ put = put . Semigroup.unwrapMonoid++-- | /Since: 0.8.4.0/+instance (Binary a, Binary b) => Binary (Semigroup.Arg a b) where+ get = liftM2 Semigroup.Arg get get+ put (Semigroup.Arg a b) = put a <> put b++------------------------------------------------------------------------+-- Non-empty lists++-- | /Since: 0.8.4.0/+instance Binary a => Binary (NE.NonEmpty a) where+ get = fmap NE.fromList get+ put = put . NE.toList+#endif++------------------------------------------------------------------------+-- Typeable/Reflection++#if MIN_VERSION_base(4,10,0)++-- $typeable-instances+--+-- 'Binary' instances for GHC's "Type.Reflection", "Data.Typeable", and+-- kind-system primitives are only provided with @base-4.10.0@ (shipped with GHC+-- 8.2.1). In prior GHC releases some of these instances were provided by+-- 'GHCi.TH.Binary' in the @ghci@ package.+--+-- These include instances for,+--+-- * 'VecCount'+-- * 'VecElem'+-- * 'RuntimeRep'+-- * 'KindRep'+-- * 'TypeLitSort'+-- * 'TyCon'+-- * 'TypeRep'+-- * 'SomeTypeRep' (also known as 'Data.Typeable.TypeRep')+--++-- | @since 0.8.5.0. See #typeable-instances#+instance Binary VecCount where+ put = putWord8 . fromIntegral . fromEnum+ get = toEnum . fromIntegral <$> getWord8++-- | @since 0.8.5.0. See #typeable-instances#+instance Binary VecElem where+ put = putWord8 . fromIntegral . fromEnum+ get = toEnum . fromIntegral <$> getWord8++-- | @since 0.8.5.0. See #typeable-instances#+instance Binary RuntimeRep where+ put (VecRep a b) = putWord8 0 >> put a >> put b+ put (TupleRep reps) = putWord8 1 >> put reps+ put (SumRep reps) = putWord8 2 >> put reps+ put LiftedRep = putWord8 3+ put UnliftedRep = putWord8 4+ put IntRep = putWord8 5+ put WordRep = putWord8 6+ put Int64Rep = putWord8 7+ put Word64Rep = putWord8 8+ put AddrRep = putWord8 9+ put FloatRep = putWord8 10+ put DoubleRep = putWord8 11++ get = do+ tag <- getWord8+ case tag of+ 0 -> VecRep <$> get <*> get+ 1 -> TupleRep <$> get+ 2 -> SumRep <$> get+ 3 -> pure LiftedRep+ 4 -> pure UnliftedRep+ 5 -> pure IntRep+ 6 -> pure WordRep+ 7 -> pure Int64Rep+ 8 -> pure Word64Rep+ 9 -> pure AddrRep+ 10 -> pure FloatRep+ 11 -> pure DoubleRep+ _ -> fail "GHCi.TH.Binary.putRuntimeRep: invalid tag"++-- | @since 0.8.5.0. See #typeable-instances#+instance Binary TyCon where+ put tc = do+ put (tyConPackage tc)+ put (tyConModule tc)+ put (tyConName tc)+ put (tyConKindArgs tc)+ put (tyConKindRep tc)+ get = mkTyCon <$> get <*> get <*> get <*> get <*> get++-- | @since 0.8.5.0. See #typeable-instances#+instance Binary KindRep where+ put (KindRepTyConApp tc k) = putWord8 0 >> put tc >> put k+ put (KindRepVar bndr) = putWord8 1 >> put bndr+ put (KindRepApp a b) = putWord8 2 >> put a >> put b+ put (KindRepFun a b) = putWord8 3 >> put a >> put b+ put (KindRepTYPE r) = putWord8 4 >> put r+ put (KindRepTypeLit sort r) = putWord8 5 >> put sort >> put r++ get = do+ tag <- getWord8+ case tag of+ 0 -> KindRepTyConApp <$> get <*> get+ 1 -> KindRepVar <$> get+ 2 -> KindRepApp <$> get <*> get+ 3 -> KindRepFun <$> get <*> get+ 4 -> KindRepTYPE <$> get+ 5 -> KindRepTypeLit <$> get <*> get+ _ -> fail "GHCi.TH.Binary.putKindRep: invalid tag"++-- | @since 0.8.5.0. See #typeable-instances#+instance Binary TypeLitSort where+ put TypeLitSymbol = putWord8 0+ put TypeLitNat = putWord8 1+ get = do+ tag <- getWord8+ case tag of+ 0 -> pure TypeLitSymbol+ 1 -> pure TypeLitNat+ _ -> fail "GHCi.TH.Binary.putTypeLitSort: invalid tag"++putTypeRep :: TypeRep a -> Put+-- Special handling for TYPE, (->), and RuntimeRep due to recursive kind+-- relations.+-- See Note [Mutually recursive representations of primitive types]+putTypeRep rep -- Handle Type specially since it's so common+ | Just HRefl <- rep `eqTypeRep` (typeRep :: TypeRep Type)+ = put (0 :: Word8)+putTypeRep (Con' con ks) = do+ put (1 :: Word8)+ put con+ put ks+putTypeRep (App f x) = do+ put (2 :: Word8)+ putTypeRep f+ putTypeRep x+putTypeRep (Fun arg res) = do+ put (3 :: Word8)+ putTypeRep arg+ putTypeRep res+putTypeRep _ = fail "GHCi.TH.Binary.putTypeRep: Impossible"++getSomeTypeRep :: Get SomeTypeRep+getSomeTypeRep = do+ tag <- get :: Get Word8+ case tag of+ 0 -> return $ SomeTypeRep (typeRep :: TypeRep Type)+ 1 -> do con <- get :: Get TyCon+ ks <- get :: Get [SomeTypeRep]+ return $ SomeTypeRep $ mkTrCon con ks+ 2 -> do SomeTypeRep f <- getSomeTypeRep+ SomeTypeRep x <- getSomeTypeRep+ case typeRepKind f of+ Fun arg res ->+ case arg `eqTypeRep` typeRepKind x of+ Just HRefl -> do+ case typeRepKind res `eqTypeRep` (typeRep :: TypeRep Type) of+ Just HRefl -> return $ SomeTypeRep $ mkTrApp f x+ _ -> failure "Kind mismatch" []+ _ -> failure "Kind mismatch"+ [ "Found argument of kind: " ++ show (typeRepKind x)+ , "Where the constructor: " ++ show f+ , "Expects an argument of kind: " ++ show arg+ ]+ _ -> failure "Applied non-arrow type"+ [ "Applied type: " ++ show f+ , "To argument: " ++ show x+ ]+ 3 -> do SomeTypeRep arg <- getSomeTypeRep+ SomeTypeRep res <- getSomeTypeRep+ case typeRepKind arg `eqTypeRep` (typeRep :: TypeRep Type) of+ Just HRefl ->+ case typeRepKind res `eqTypeRep` (typeRep :: TypeRep Type) of+ Just HRefl -> return $ SomeTypeRep $ Fun arg res+ Nothing -> failure "Kind mismatch" []+ Nothing -> failure "Kind mismatch" []+ _ -> failure "Invalid SomeTypeRep" []+ where+ failure description info =+ fail $ unlines $ [ "GHCi.TH.Binary.getSomeTypeRep: "++description ]+ ++ map (" "++) info++instance Typeable a => Binary (TypeRep (a :: k)) where+ put = putTypeRep+ get = do+ SomeTypeRep rep <- getSomeTypeRep+ case rep `eqTypeRep` expected of+ Just HRefl -> pure rep+ Nothing -> fail $ unlines+ [ "GHCi.TH.Binary: Type mismatch"+ , " Deserialized type: " ++ show rep+ , " Expected type: " ++ show expected+ ]+ where expected = typeRep :: TypeRep a++instance Binary SomeTypeRep where+ put (SomeTypeRep rep) = putTypeRep rep+ get = getSomeTypeRep+#endif+
+ src/Data/Binary/FloatCast.hs view
@@ -0,0 +1,45 @@++{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Trustworthy #-}++-- | This module was written based on+-- <http://hackage.haskell.org/package/reinterpret-cast-0.1.0/docs/src/Data-ReinterpretCast-Internal-ImplArray.html>.+--+-- Implements casting via a 1-elemnt STUArray, as described in+-- <http://stackoverflow.com/a/7002812/263061>.+module Data.Binary.FloatCast+ ( floatToWord+ , wordToFloat+ , doubleToWord+ , wordToDouble+ ) where++import Data.Word (Word32, Word64)+import Data.Array.ST (newArray, readArray, MArray, STUArray)+import Data.Array.Unsafe (castSTUArray)+import GHC.ST (runST, ST)++-- | Reinterpret-casts a `Float` to a `Word32`.+floatToWord :: Float -> Word32+floatToWord x = runST (cast x)+{-# INLINE floatToWord #-}++-- | Reinterpret-casts a `Word32` to a `Float`.+wordToFloat :: Word32 -> Float+wordToFloat x = runST (cast x)+{-# INLINE wordToFloat #-}++-- | Reinterpret-casts a `Double` to a `Word64`.+doubleToWord :: Double -> Word64+doubleToWord x = runST (cast x)+{-# INLINE doubleToWord #-}++-- | Reinterpret-casts a `Word64` to a `Double`.+wordToDouble :: Word64 -> Double+wordToDouble x = runST (cast x)+{-# INLINE wordToDouble #-}++cast :: (MArray (STUArray s) a (ST s),+ MArray (STUArray s) b (ST s)) => a -> ST s b+cast x = newArray (0 :: Int, 0) x >>= castSTUArray >>= flip readArray 0+{-# INLINE cast #-}
+ src/Data/Binary/Generic.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE BangPatterns, CPP, FlexibleInstances, KindSignatures,+ ScopedTypeVariables, TypeOperators, TypeSynonymInstances #-}+{-# LANGUAGE Safe #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++#if __GLASGOW_HASKELL__ >= 800+#define HAS_DATA_KIND+#endif++-----------------------------------------------------------------------------+-- |+-- Module : Data.Binary.Generic+-- Copyright : Bryan O'Sullivan+-- License : BSD3-style (see LICENSE)+--+-- Maintainer : Bryan O'Sullivan <bos@serpentine.com>+-- Stability : unstable+-- Portability : Only works with GHC 7.2 and newer+--+-- Instances for supporting GHC generics.+--+-----------------------------------------------------------------------------+module Data.Binary.Generic+ (+ ) where++import Control.Applicative+import Data.Binary.Class+import Data.Binary.Get+import Data.Binary.Put+import Data.Bits+import Data.Word+import Data.Monoid ((<>))+#ifdef HAS_DATA_KIND+import Data.Kind+#endif+import GHC.Generics+import Prelude -- Silence AMP warning.++-- Type without constructors+instance GBinaryPut V1 where+ gput _ = pure ()++instance GBinaryGet V1 where+ gget = return undefined++-- Constructor without arguments+instance GBinaryPut U1 where+ gput U1 = pure ()++instance GBinaryGet U1 where+ gget = return U1++-- Product: constructor with parameters+instance (GBinaryPut a, GBinaryPut b) => GBinaryPut (a :*: b) where+ gput (x :*: y) = gput x <> gput y++instance (GBinaryGet a, GBinaryGet b) => GBinaryGet (a :*: b) where+ gget = (:*:) <$> gget <*> gget++-- Metadata (constructor name, etc)+instance GBinaryPut a => GBinaryPut (M1 i c a) where+ gput = gput . unM1++instance GBinaryGet a => GBinaryGet (M1 i c a) where+ gget = M1 <$> gget++-- Constants, additional parameters, and rank-1 recursion+instance Binary a => GBinaryPut (K1 i a) where+ gput = put . unK1++instance Binary a => GBinaryGet (K1 i a) where+ gget = K1 <$> get++-- Borrowed from the cereal package.++-- The following GBinary instance for sums has support for serializing+-- types with up to 2^64-1 constructors. It will use the minimal+-- number of bytes needed to encode the constructor. For example when+-- a type has 2^8 constructors or less it will use a single byte to+-- encode the constructor. If it has 2^16 constructors or less it will+-- use two bytes, and so on till 2^64-1.++#define GUARD(WORD) (size - 1) <= fromIntegral (maxBound :: WORD)+#define PUTSUM(WORD) GUARD(WORD) = putSum (0 :: WORD) (fromIntegral size)+#define GETSUM(WORD) GUARD(WORD) = (get :: Get WORD) >>= checkGetSum (fromIntegral size)++instance ( GSumPut a, GSumPut b+ , SumSize a, SumSize b) => GBinaryPut (a :+: b) where+ gput | PUTSUM(Word8) | PUTSUM(Word16) | PUTSUM(Word32) | PUTSUM(Word64)+ | otherwise = sizeError "encode" size+ where+ size = unTagged (sumSize :: Tagged (a :+: b) Word64)++instance ( GSumGet a, GSumGet b+ , SumSize a, SumSize b) => GBinaryGet (a :+: b) where+ gget | GETSUM(Word8) | GETSUM(Word16) | GETSUM(Word32) | GETSUM(Word64)+ | otherwise = sizeError "decode" size+ where+ size = unTagged (sumSize :: Tagged (a :+: b) Word64)++sizeError :: Show size => String -> size -> error+sizeError s size =+ error $ "Can't " ++ s ++ " a type with " ++ show size ++ " constructors"++------------------------------------------------------------------------++checkGetSum :: (Ord word, Num word, Bits word, GSumGet f)+ => word -> word -> Get (f a)+checkGetSum size code | code < size = getSum code size+ | otherwise = fail "Unknown encoding for constructor"+{-# INLINE checkGetSum #-}++class GSumGet f where+ getSum :: (Ord word, Num word, Bits word) => word -> word -> Get (f a)++class GSumPut f where+ putSum :: (Num w, Bits w, Binary w) => w -> w -> f a -> Put++instance (GSumGet a, GSumGet b) => GSumGet (a :+: b) where+ getSum !code !size | code < sizeL = L1 <$> getSum code sizeL+ | otherwise = R1 <$> getSum (code - sizeL) sizeR+ where+ sizeL = size `shiftR` 1+ sizeR = size - sizeL++instance (GSumPut a, GSumPut b) => GSumPut (a :+: b) where+ putSum !code !size s = case s of+ L1 x -> putSum code sizeL x+ R1 x -> putSum (code + sizeL) sizeR x+ where+ sizeL = size `shiftR` 1+ sizeR = size - sizeL++instance GBinaryGet a => GSumGet (C1 c a) where+ getSum _ _ = gget++instance GBinaryPut a => GSumPut (C1 c a) where+ putSum !code _ x = put code <> gput x++------------------------------------------------------------------------++class SumSize f where+ sumSize :: Tagged f Word64++#ifdef HAS_DATA_KIND+newtype Tagged (s :: Type -> Type) b = Tagged {unTagged :: b}+#else+newtype Tagged (s :: * -> *) b = Tagged {unTagged :: b}+#endif++instance (SumSize a, SumSize b) => SumSize (a :+: b) where+ sumSize = Tagged $ unTagged (sumSize :: Tagged a Word64) ++ unTagged (sumSize :: Tagged b Word64)++instance SumSize (C1 c a) where+ sumSize = Tagged 1
src/Data/Binary/Get.hs view
@@ -1,49 +1,165 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fglasgow-exts #-}--- for unboxed shifts+{-# LANGUAGE CPP, RankNTypes, MagicHash, BangPatterns #-}+{-# LANGUAGE Trustworthy #-} +#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+#include "MachDeps.h"+#endif+ ----------------------------------------------------------------------------- -- | -- Module : Data.Binary.Get -- Copyright : Lennart Kolmodin -- License : BSD3-style (see LICENSE)--- --- Maintainer : Lennart Kolmodin <kolmodin@dtek.chalmers.se>+--+-- Maintainer : Lennart Kolmodin <kolmodin@gmail.com> -- Stability : experimental -- Portability : portable to Hugs and GHC. ----- The Get monad. A monad for efficiently building structures from--- encoded lazy ByteStrings+-- The 'Get' monad. A monad for efficiently building structures from+-- encoded lazy ByteStrings. --+-- Primitives are available to decode words of various sizes, both big and+-- little endian.+--+-- Let's decode binary data representing illustrated here.+-- In this example the values are in little endian.+--+-- > +------------------+--------------+-----------------++-- > | 32 bit timestamp | 32 bit price | 16 bit quantity |+-- > +------------------+--------------+-----------------++--+-- A corresponding Haskell value looks like this:+--+-- @+--data Trade = Trade+-- { timestamp :: !'Word32'+-- , price :: !'Word32'+-- , qty :: !'Word16'+-- } deriving ('Show')+-- @+--+-- The fields in @Trade@ are marked as strict (using @!@) since we don't need+-- laziness here. In practise, you would probably consider using the UNPACK+-- pragma as well.+-- <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#unpack-pragma>+--+-- Now, let's have a look at a decoder for this format.+--+-- @+--getTrade :: 'Get' Trade+--getTrade = do+-- timestamp <- 'getWord32le'+-- price <- 'getWord32le'+-- quantity <- 'getWord16le'+-- return '$!' Trade timestamp price quantity+-- @+--+-- Or even simpler using applicative style:+--+-- @+--getTrade' :: 'Get' Trade+--getTrade' = Trade '<$>' 'getWord32le' '<*>' 'getWord32le' '<*>' 'getWord16le'+-- @+--+-- There are two kinds of ways to execute this decoder, the lazy input+-- method and the incremental input method. Here we will use the lazy+-- input method.+--+-- Let's first define a function that decodes many @Trade@s.+--+-- @+--getTrades :: Get [Trade]+--getTrades = do+-- empty <- 'isEmpty'+-- if empty+-- then return []+-- else do trade <- getTrade+-- trades <- getTrades+-- return (trade:trades)+-- @+--+-- Finally, we run the decoder:+--+-- @+--lazyIOExample :: IO [Trade]+--lazyIOExample = do+-- input <- BL.readFile \"trades.bin\"+-- return ('runGet' getTrades input)+-- @+--+-- This decoder has the downside that it will need to read all the input before+-- it can return. On the other hand, it will not return anything until+-- it knows it could decode without any decoder errors.+--+-- You could also refactor to a left-fold, to decode in a more streaming fashion,+-- and get the following decoder. It will start to return data without knowing+-- that it can decode all input.+--+-- @+--incrementalExample :: BL.ByteString -> [Trade]+--incrementalExample input0 = go decoder input0+-- where+-- decoder = 'runGetIncremental' getTrade+-- go :: 'Decoder' Trade -> BL.ByteString -> [Trade]+-- go ('Done' leftover _consumed trade) input =+-- trade : go decoder (BL.chunk leftover input)+-- go ('Partial' k) input =+-- go (k . takeHeadChunk $ input) (dropHeadChunk input)+-- go ('Fail' _leftover _consumed msg) _input =+-- error msg+--+--takeHeadChunk :: BL.ByteString -> Maybe BS.ByteString+--takeHeadChunk lbs =+-- case lbs of+-- (BL.Chunk bs _) -> Just bs+-- _ -> Nothing+--+--dropHeadChunk :: BL.ByteString -> BL.ByteString+--dropHeadChunk lbs =+-- case lbs of+-- (BL.Chunk _ lbs') -> lbs'+-- _ -> BL.Empty+-- @+--+-- The @lazyIOExample@ uses lazy I/O to read the file from the disk, which is+-- not suitable in all applications, and certainly not if you need to read+-- from a socket which has higher likelihood to fail. To address these needs,+-- use the incremental input method like in @incrementalExample@.+-- For an example of how to read incrementally from a Handle,+-- see the implementation of 'decodeFileOrFail' in "Data.Binary". ----------------------------------------------------------------------------- -#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)-#include "MachDeps.h"-#endif module Data.Binary.Get ( - -- * The Get type+ -- * The Get monad Get++ -- * The lazy input interface+ -- $lazyinterface , runGet- , runGetState+ , runGetOrFail+ , ByteOffset - -- * Parsing+ -- * The incremental input interface+ -- $incrementalinterface+ , Decoder(..)+ , runGetIncremental++ -- ** Providing input+ , pushChunk+ , pushChunks+ , pushEndOfInput++ -- * Decoding , skip- , uncheckedSkip+ , isEmpty+ , bytesRead+ , isolate , lookAhead , lookAheadM , lookAheadE- , uncheckedLookAhead-- -- * Utility- , bytesRead- , getBytes- , remaining- , isEmpty-- -- * Parsing particular types- , getWord8+ , label -- ** ByteStrings , getByteString@@ -51,445 +167,411 @@ , getLazyByteStringNul , getRemainingLazyByteString - -- ** Big-endian reads+ -- ** Decoding Words+ , getWord8++ -- *** Big-endian decoding , getWord16be , getWord32be , getWord64be - -- ** Little-endian reads+ -- *** Little-endian decoding , getWord16le , getWord32le , getWord64le - -- ** Host-endian, unaligned reads+ -- *** Host-endian, unaligned decoding , getWordhost , getWord16host , getWord32host , getWord64host - ) where+ -- ** Decoding Ints+ , getInt8 -import Control.Monad (when,liftM,ap)-import Control.Monad.Fix-import Data.Maybe (isNothing)+ -- *** Big-endian decoding+ , getInt16be+ , getInt32be+ , getInt64be -import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L+ -- *** Little-endian decoding+ , getInt16le+ , getInt32le+ , getInt64le -#ifdef BYTESTRING_IN_BASE-import qualified Data.ByteString.Base as B-#else-import qualified Data.ByteString.Internal as B-import qualified Data.ByteString.Lazy.Internal as L-#endif+ -- *** Host-endian, unaligned decoding+ , getInthost+ , getInt16host+ , getInt32host+ , getInt64host -#ifdef APPLICATIVE_IN_BASE-import Control.Applicative (Applicative(..))+ -- ** Decoding Floats/Doubles+ , getFloatbe+ , getFloatle+ , getFloathost+ , getDoublebe+ , getDoublele+ , getDoublehost++ -- * Deprecated functions+ , runGetState -- DEPRECATED+ , remaining -- DEPRECATED+ , getBytes -- DEPRECATED+ ) where+#if ! MIN_VERSION_base(4,8,0)+import Control.Applicative #endif import Foreign+import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as B+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Internal as L --- used by splitAtST-import Control.Monad.ST-import Data.STRef+import Data.Binary.Get.Internal hiding ( Decoder(..), runGetIncremental )+import qualified Data.Binary.Get.Internal as I #if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)+-- needed for (# unboxing #) with magic hash import GHC.Base import GHC.Word-import GHC.Int #endif --- | The parse state-data S = S {-# UNPACK #-} !B.ByteString -- current chunk- L.ByteString -- the rest of the input- {-# UNPACK #-} !Int64 -- bytes read---- | The Get monad is just a State monad carrying around the input ByteString--- We treat it as a strict state monad. -newtype Get a = Get { unGet :: S -> (a, S) }--instance Functor Get where- fmap f m = Get (\s -> case unGet m s of- (a, s') -> (f a, s'))- {-# INLINE fmap #-}+-- needed for casting words to float/double+import Data.Binary.FloatCast (wordToFloat, wordToDouble) -#ifdef APPLICATIVE_IN_BASE-instance Applicative Get where- pure = return- (<*>) = ap-#endif+-- $lazyinterface+-- The lazy interface consumes a single lazy 'L.ByteString'. It's the easiest+-- interface to get started with, but it doesn't support interleaving I\/O and+-- parsing, unless lazy I/O is used.+--+-- There is no way to provide more input other than the initial data. To be+-- able to incrementally give more data, see the incremental input interface. --- Definition directly from Control.Monad.State.Strict-instance Monad Get where- return a = Get (\s -> (a, s))- {-# INLINE return #-}+-- $incrementalinterface+-- The incremental interface gives you more control over how input is+-- provided during parsing. This lets you e.g. interleave parsing and+-- I\/O.+--+-- The incremental interface consumes a strict 'B.ByteString' at a time, each+-- being part of the total amount of input. If your decoder needs more input to+-- finish it will return a 'Partial' with a continuation.+-- If there is no more input, provide it 'Nothing'.+--+-- 'Fail' will be returned if it runs into an error, together with a message,+-- the position and the remaining input.+-- If it succeeds it will return 'Done' with the resulting value,+-- the position and the remaining input. - m >>= k = Get (\s -> let (a, s') = unGet m s- in unGet (k a) s')- {-# INLINE (>>=) #-}+-- | A decoder procuced by running a 'Get' monad.+data Decoder a = Fail !B.ByteString {-# UNPACK #-} !ByteOffset String+ -- ^ The decoder ran into an error. The decoder either used+ -- 'fail' or was not provided enough input. Contains any+ -- unconsumed input and the number of bytes consumed.+ | Partial (Maybe B.ByteString -> Decoder a)+ -- ^ The decoder has consumed the available input and needs+ -- more to continue. Provide 'Just' if more input is available+ -- and 'Nothing' otherwise, and you will get a new 'Decoder'.+ | Done !B.ByteString {-# UNPACK #-} !ByteOffset a+ -- ^ The decoder has successfully finished. Except for the+ -- output value you also get any unused input as well as the+ -- number of bytes consumed. - fail = failDesc+-- | Run a 'Get' monad. See 'Decoder' for what to do next, like providing+-- input, handling decoder errors and to get the output value.+-- Hint: Use the helper functions 'pushChunk', 'pushChunks' and+-- 'pushEndOfInput'.+runGetIncremental :: Get a -> Decoder a+runGetIncremental = calculateOffset . I.runGetIncremental -instance MonadFix Get where- mfix f = Get (\s -> let (a,s') = unGet (f a) s- in (a,s'))+calculateOffset :: I.Decoder a -> Decoder a+calculateOffset r0 = go r0 0+ where+ go r !acc = case r of+ I.Done inp a -> Done inp (acc - fromIntegral (B.length inp)) a+ I.Fail inp s -> Fail inp (acc - fromIntegral (B.length inp)) s+ I.Partial k ->+ Partial $ \ms ->+ case ms of+ Nothing -> go (k Nothing) acc+ Just i -> go (k ms) (acc + fromIntegral (B.length i))+ I.BytesRead unused k ->+ go (k $! (acc - unused)) acc -------------------------------------------------------------------------+-- | DEPRECATED. Provides compatibility with previous versions of this library.+-- Run a 'Get' monad and return a tuple with three values.+-- The first value is the result of the decoder. The second and third are the+-- unused input, and the number of consumed bytes.+{-# DEPRECATED runGetState "Use runGetIncremental instead. This function will be removed." #-}+runGetState :: Get a -> L.ByteString -> ByteOffset -> (a, L.ByteString, ByteOffset)+runGetState g lbs0 pos' = go (runGetIncremental g) lbs0+ where+ go (Done s pos a) lbs = (a, L.chunk s lbs, pos+pos')+ go (Partial k) lbs = go (k (takeHeadChunk lbs)) (dropHeadChunk lbs)+ go (Fail _ pos msg) _ =+ error ("Data.Binary.Get.runGetState at position " ++ show pos ++ ": " ++ msg) -get :: Get S-get = Get (\s -> (s, s))+takeHeadChunk :: L.ByteString -> Maybe B.ByteString+takeHeadChunk lbs =+ case lbs of+ (L.Chunk bs _) -> Just bs+ _ -> Nothing -put :: S -> Get ()-put s = Get (\_ -> ((), s))+dropHeadChunk :: L.ByteString -> L.ByteString+dropHeadChunk lbs =+ case lbs of+ (L.Chunk _ lbs') -> lbs'+ _ -> L.Empty ------------------------------------------------------------------------------- dons, GHC 6.10: explicit inlining disabled, was killing performance.--- Without it, GHC seems to do just fine. And we get similar--- performance with 6.8.2 anyway.+-- | Run a 'Get' monad and return 'Left' on failure and 'Right' on+-- success. In both cases any unconsumed input and the number of bytes+-- consumed is returned. In the case of failure, a human-readable+-- error message is included as well. --+-- /Since: 0.6.4.0/+runGetOrFail :: Get a -> L.ByteString+ -> Either (L.ByteString, ByteOffset, String) (L.ByteString, ByteOffset, a)+runGetOrFail g lbs0 = feedAll (runGetIncremental g) lbs0+ where+ feedAll (Done bs pos x) lbs = Right (L.chunk bs lbs, pos, x)+ feedAll (Partial k) lbs = feedAll (k (takeHeadChunk lbs)) (dropHeadChunk lbs)+ feedAll (Fail x pos msg) xs = Left (L.chunk x xs, pos, msg) -initState :: L.ByteString -> S-initState xs = mkState xs 0-{- INLINE initState -}+-- | An offset, counted in bytes.+type ByteOffset = Int64 -{--initState (B.LPS xs) =- case xs of- [] -> S B.empty L.empty 0- (x:xs') -> S x (B.LPS xs') 0--}+-- | The simplest interface to run a 'Get' decoder. If the decoder runs into+-- an error, calls 'fail', or runs out of input, it will call 'error'.+runGet :: Get a -> L.ByteString -> a+runGet g lbs0 = feedAll (runGetIncremental g) lbs0+ where+ feedAll (Done _ _ x) _ = x+ feedAll (Partial k) lbs = feedAll (k (takeHeadChunk lbs)) (dropHeadChunk lbs)+ feedAll (Fail _ pos msg) _ =+ error ("Data.Binary.Get.runGet at position " ++ show pos ++ ": " ++ msg) -#ifndef BYTESTRING_IN_BASE-mkState :: L.ByteString -> Int64 -> S-mkState l = case l of- L.Empty -> S B.empty L.empty- L.Chunk x xs -> S x xs-{- INLINE mkState -} -#else-mkState :: L.ByteString -> Int64 -> S-mkState (B.LPS xs) =- case xs of- [] -> S B.empty L.empty- (x:xs') -> S x (B.LPS xs')-#endif+-- | Feed a 'Decoder' with more input. If the 'Decoder' is 'Done' or 'Fail' it+-- will add the input to 'B.ByteString' of unconsumed input.+--+-- @+-- 'runGetIncremental' myParser \`pushChunk\` myInput1 \`pushChunk\` myInput2+-- @+pushChunk :: Decoder a -> B.ByteString -> Decoder a+pushChunk r inp =+ case r of+ Done inp0 p a -> Done (inp0 `B.append` inp) p a+ Partial k -> k (Just inp)+ Fail inp0 p s -> Fail (inp0 `B.append` inp) p s --- | Run the Get monad applies a 'get'-based parser on the input ByteString-runGet :: Get a -> L.ByteString -> a-runGet m str = case unGet m (initState str) of (a, _) -> a --- | Run the Get monad applies a 'get'-based parser on the input--- ByteString. Additional to the result of get it returns the number of--- consumed bytes and the rest of the input.-runGetState :: Get a -> L.ByteString -> Int64 -> (a, L.ByteString, Int64)-runGetState m str off =- case unGet m (mkState str off) of- (a, ~(S s ss newOff)) -> (a, s `join` ss, newOff)--------------------------------------------------------------------------+-- | Feed a 'Decoder' with more input. If the 'Decoder' is 'Done' or 'Fail' it+-- will add the input to 'ByteString' of unconsumed input.+--+-- @+-- 'runGetIncremental' myParser \`pushChunks\` myLazyByteString+-- @+pushChunks :: Decoder a -> L.ByteString -> Decoder a+pushChunks r0 = go r0 . L.toChunks+ where+ go r [] = r+ go (Done inp pos a) xs = Done (B.concat (inp:xs)) pos a+ go (Fail inp pos s) xs = Fail (B.concat (inp:xs)) pos s+ go (Partial k) (x:xs) = go (k (Just x)) xs -failDesc :: String -> Get a-failDesc err = do- S _ _ bytes <- get- Get (error (err ++ ". Failed reading at byte position " ++ show bytes))+-- | Tell a 'Decoder' that there is no more input. This passes 'Nothing' to a+-- 'Partial' decoder, otherwise returns the decoder unchanged.+pushEndOfInput :: Decoder a -> Decoder a+pushEndOfInput r =+ case r of+ Done _ _ _ -> r+ Partial k -> k Nothing+ Fail _ _ _ -> r -- | Skip ahead @n@ bytes. Fails if fewer than @n@ bytes are available. skip :: Int -> Get ()-skip n = readN (fromIntegral n) (const ())---- | Skip ahead @n@ bytes. No error if there isn't enough bytes.-uncheckedSkip :: Int64 -> Get ()-uncheckedSkip n = do- S s ss bytes <- get- if fromIntegral (B.length s) >= n- then put (S (B.drop (fromIntegral n) s) ss (bytes + n))- else do- let rest = L.drop (n - fromIntegral (B.length s)) ss- put $! mkState rest (bytes + n)---- | Run @ga@, but return without consuming its input.--- Fails if @ga@ fails.-lookAhead :: Get a -> Get a-lookAhead ga = do- s <- get- a <- ga- put s- return a---- | Like 'lookAhead', but consume the input if @gma@ returns 'Just _'.--- Fails if @gma@ fails.-lookAheadM :: Get (Maybe a) -> Get (Maybe a)-lookAheadM gma = do- s <- get- ma <- gma- when (isNothing ma) $- put s- return ma---- | Like 'lookAhead', but consume the input if @gea@ returns 'Right _'.--- Fails if @gea@ fails.-lookAheadE :: Get (Either a b) -> Get (Either a b)-lookAheadE gea = do- s <- get- ea <- gea- case ea of- Left _ -> put s- _ -> return ()- return ea---- | Get the next up to @n@ bytes as a lazy ByteString, without consuming them. -uncheckedLookAhead :: Int64 -> Get L.ByteString-uncheckedLookAhead n = do- S s ss _ <- get- if n <= fromIntegral (B.length s)- then return (L.fromChunks [B.take (fromIntegral n) s])- else return $ L.take n (s `join` ss)----------------------------------------------------------------------------- Utility---- | Get the total number of bytes read to this point.-bytesRead :: Get Int64-bytesRead = do- S _ _ b <- get- return b+skip n = withInputChunks (fromIntegral n) consumeBytes (const ()) failOnEOF --- | Get the number of remaining unparsed bytes.--- Useful for checking whether all input has been consumed.--- Note that this forces the rest of the input.-remaining :: Get Int64-remaining = do- S s ss _ <- get- return (fromIntegral (B.length s) + L.length ss)+-- | An efficient get method for lazy ByteStrings. Fails if fewer than @n@+-- bytes are left in the input.+getLazyByteString :: Int64 -> Get L.ByteString+getLazyByteString n0 = withInputChunks n0 consumeBytes L.fromChunks failOnEOF --- | Test whether all input has been consumed,--- i.e. there are no remaining unparsed bytes.-isEmpty :: Get Bool-isEmpty = do- S s ss _ <- get- return (B.null s && L.null ss)+consumeBytes :: Consume Int64+consumeBytes n str+ | fromIntegral (B.length str) >= n = Right (B.splitAt (fromIntegral n) str)+ | otherwise = Left (n - fromIntegral (B.length str)) ---------------------------------------------------------------------------- Utility with ByteStrings+consumeUntilNul :: Consume ()+consumeUntilNul _ str =+ case B.break (==0) str of+ (want, rest) | B.null rest -> Left ()+ | otherwise -> Right (want, B.drop 1 rest) --- | An efficient 'get' method for strict ByteStrings. Fails if fewer--- than @n@ bytes are left in the input.-getByteString :: Int -> Get B.ByteString-getByteString n = readN n id-{-# INLINE getByteString #-}+consumeAll :: Consume ()+consumeAll _ _ = Left () --- | An efficient 'get' method for lazy ByteStrings. Does not fail if fewer than--- @n@ bytes are left in the input.-getLazyByteString :: Int64 -> Get L.ByteString-getLazyByteString n = do- S s ss bytes <- get- let big = s `join` ss- case splitAtST n big of- (consume, rest) -> do put $ mkState rest (bytes + n)- return consume-{-# INLINE getLazyByteString #-}+resumeOnEOF :: [B.ByteString] -> Get L.ByteString+resumeOnEOF = return . L.fromChunks --- | Get a lazy ByteString that is terminated with a NUL byte. Fails--- if it reaches the end of input without hitting a NUL.+-- | Get a lazy ByteString that is terminated with a NUL byte.+-- The returned string does not contain the NUL byte. Fails+-- if it reaches the end of input without finding a NUL. getLazyByteStringNul :: Get L.ByteString-getLazyByteStringNul = do- S s ss bytes <- get- let big = s `join` ss- (consume, t) = L.break (== 0) big- (h, rest) = L.splitAt 1 t- if L.null h- then fail "too few bytes"- else do- put $ mkState rest (bytes + L.length consume + 1)- return consume-{-# INLINE getLazyByteStringNul #-}+getLazyByteStringNul = withInputChunks () consumeUntilNul L.fromChunks failOnEOF --- | Get the remaining bytes as a lazy ByteString+-- | Get the remaining bytes as a lazy ByteString.+-- Note that this can be an expensive function to use as it forces reading+-- all input and keeping the string in-memory. getRemainingLazyByteString :: Get L.ByteString-getRemainingLazyByteString = do- S s ss _ <- get- return (s `join` ss)----------------------------------------------------------------------------- Helpers---- | Pull @n@ bytes from the input, as a strict ByteString.-getBytes :: Int -> Get B.ByteString-getBytes n = do- S s ss bytes <- get- if n <= B.length s- then do let (consume,rest) = B.splitAt n s- put $! S rest ss (bytes + fromIntegral n)- return $! consume- else- case L.splitAt (fromIntegral n) (s `join` ss) of- (consuming, rest) ->- do let now = B.concat . L.toChunks $ consuming- put $! mkState rest (bytes + fromIntegral n)- -- forces the next chunk before this one is returned- if (B.length now < n)- then- fail "too few bytes"- else- return now-{- INLINE getBytes -}--- ^ important--#ifndef BYTESTRING_IN_BASE-join :: B.ByteString -> L.ByteString -> L.ByteString-join bb lb- | B.null bb = lb- | otherwise = L.Chunk bb lb--#else-join :: B.ByteString -> L.ByteString -> L.ByteString-join bb (B.LPS lb)- | B.null bb = B.LPS lb- | otherwise = B.LPS (bb:lb)-#endif- -- don't use L.append, it's strict in it's second argument :/-{- INLINE join -}---- | Split a ByteString. If the first result is consumed before the ----- second, this runs in constant heap space.------ You must force the returned tuple for that to work, e.g.--- --- > case splitAtST n xs of--- > (ys,zs) -> consume ys ... consume zs----splitAtST :: Int64 -> L.ByteString -> (L.ByteString, L.ByteString)-splitAtST i ps | i <= 0 = (L.empty, ps)-#ifndef BYTESTRING_IN_BASE-splitAtST i ps = runST (- do r <- newSTRef undefined- xs <- first r i ps- ys <- unsafeInterleaveST (readSTRef r)- return (xs, ys))-- where- first r 0 xs@(L.Chunk _ _) = writeSTRef r xs >> return L.Empty- first r _ L.Empty = writeSTRef r L.Empty >> return L.Empty-- first r n (L.Chunk x xs)- | n < l = do writeSTRef r (L.Chunk (B.drop (fromIntegral n) x) xs)- return $ L.Chunk (B.take (fromIntegral n) x) L.Empty- | otherwise = do writeSTRef r (L.drop (n - l) xs)- liftM (L.Chunk x) $ unsafeInterleaveST (first r (n - l) xs)-- where l = fromIntegral (B.length x)-#else-splitAtST i (B.LPS ps) = runST (- do r <- newSTRef undefined- xs <- first r i ps- ys <- unsafeInterleaveST (readSTRef r)- return (B.LPS xs, B.LPS ys))-- where first r 0 xs = writeSTRef r xs >> return []- first r _ [] = writeSTRef r [] >> return []- first r n (x:xs)- | n < l = do writeSTRef r (B.drop (fromIntegral n) x : xs)- return [B.take (fromIntegral n) x]- | otherwise = do writeSTRef r (L.toChunks (L.drop (n - l) (B.LPS xs)))- fmap (x:) $ unsafeInterleaveST (first r (n - l) xs)-- where l = fromIntegral (B.length x)-#endif-{- INLINE splitAtST -}---- Pull n bytes from the input, and apply a parser to those bytes,--- yielding a value. If less than @n@ bytes are available, fail with an--- error. This wraps @getBytes@.-readN :: Int -> (B.ByteString -> a) -> Get a-readN n f = fmap f $ getBytes n-{- INLINE readN -}--- ^ important+getRemainingLazyByteString = withInputChunks () consumeAll L.fromChunks resumeOnEOF ------------------------------------------------------------------------ -- Primtives -- helper, get a raw Ptr onto a strict ByteString copied out of the--- underlying lazy byteString. So many indirections from the raw parser--- state that my head hurts...+-- underlying lazy byteString. getPtr :: Storable a => Int -> Get a-getPtr n = do- (fp,o,_) <- readN n B.toForeignPtr- return . B.inlinePerformIO $ withForeignPtr fp $ \p -> peek (castPtr $ p `plusPtr` o)-{- INLINE getPtr -}--------------------------------------------------------------------------+getPtr n = readNWith n peek+{-# INLINE getPtr #-} -- | Read a Word8 from the monad state getWord8 :: Get Word8-getWord8 = getPtr (sizeOf (undefined :: Word8))-{- INLINE getWord8 -}+getWord8 = readN 1 B.unsafeHead+{-# INLINE[2] getWord8 #-} +-- | Read an Int8 from the monad state+getInt8 :: Get Int8+getInt8 = fromIntegral <$> getWord8+{-# INLINE getInt8 #-}+++-- force GHC to inline getWordXX+{-# RULES+"getWord8/readN" getWord8 = readN 1 B.unsafeHead+"getWord16be/readN" getWord16be = readN 2 word16be+"getWord16le/readN" getWord16le = readN 2 word16le+"getWord32be/readN" getWord32be = readN 4 word32be+"getWord32le/readN" getWord32le = readN 4 word32le+"getWord64be/readN" getWord64be = readN 8 word64be+"getWord64le/readN" getWord64le = readN 8 word64le #-}+ -- | Read a Word16 in big endian format getWord16be :: Get Word16-getWord16be = do- s <- readN 2 id- return $! (fromIntegral (s `B.index` 0) `shiftl_w16` 8) .|.- (fromIntegral (s `B.index` 1))-{- INLINE getWord16be -}+getWord16be = readN 2 word16be +word16be :: B.ByteString -> Word16+word16be = \s ->+ (fromIntegral (s `B.unsafeIndex` 0) `shiftl_w16` 8) .|.+ (fromIntegral (s `B.unsafeIndex` 1))+{-# INLINE[2] getWord16be #-}+{-# INLINE word16be #-}+ -- | Read a Word16 in little endian format getWord16le :: Get Word16-getWord16le = do- s <- readN 2 id- return $! (fromIntegral (s `B.index` 1) `shiftl_w16` 8) .|.- (fromIntegral (s `B.index` 0) )-{- INLINE getWord16le -}+getWord16le = readN 2 word16le +word16le :: B.ByteString -> Word16+word16le = \s ->+ (fromIntegral (s `B.unsafeIndex` 1) `shiftl_w16` 8) .|.+ (fromIntegral (s `B.unsafeIndex` 0) )+{-# INLINE[2] getWord16le #-}+{-# INLINE word16le #-}+ -- | Read a Word32 in big endian format getWord32be :: Get Word32-getWord32be = do- s <- readN 4 id- return $! (fromIntegral (s `B.index` 0) `shiftl_w32` 24) .|.- (fromIntegral (s `B.index` 1) `shiftl_w32` 16) .|.- (fromIntegral (s `B.index` 2) `shiftl_w32` 8) .|.- (fromIntegral (s `B.index` 3) )-{- INLINE getWord32be -}+getWord32be = readN 4 word32be +word32be :: B.ByteString -> Word32+word32be = \s ->+ (fromIntegral (s `B.unsafeIndex` 0) `shiftl_w32` 24) .|.+ (fromIntegral (s `B.unsafeIndex` 1) `shiftl_w32` 16) .|.+ (fromIntegral (s `B.unsafeIndex` 2) `shiftl_w32` 8) .|.+ (fromIntegral (s `B.unsafeIndex` 3) )+{-# INLINE[2] getWord32be #-}+{-# INLINE word32be #-}+ -- | Read a Word32 in little endian format getWord32le :: Get Word32-getWord32le = do- s <- readN 4 id- return $! (fromIntegral (s `B.index` 3) `shiftl_w32` 24) .|.- (fromIntegral (s `B.index` 2) `shiftl_w32` 16) .|.- (fromIntegral (s `B.index` 1) `shiftl_w32` 8) .|.- (fromIntegral (s `B.index` 0) )-{- INLINE getWord32le -}+getWord32le = readN 4 word32le +word32le :: B.ByteString -> Word32+word32le = \s ->+ (fromIntegral (s `B.unsafeIndex` 3) `shiftl_w32` 24) .|.+ (fromIntegral (s `B.unsafeIndex` 2) `shiftl_w32` 16) .|.+ (fromIntegral (s `B.unsafeIndex` 1) `shiftl_w32` 8) .|.+ (fromIntegral (s `B.unsafeIndex` 0) )+{-# INLINE[2] getWord32le #-}+{-# INLINE word32le #-}+ -- | Read a Word64 in big endian format getWord64be :: Get Word64-getWord64be = do- s <- readN 8 id- return $! (fromIntegral (s `B.index` 0) `shiftl_w64` 56) .|.- (fromIntegral (s `B.index` 1) `shiftl_w64` 48) .|.- (fromIntegral (s `B.index` 2) `shiftl_w64` 40) .|.- (fromIntegral (s `B.index` 3) `shiftl_w64` 32) .|.- (fromIntegral (s `B.index` 4) `shiftl_w64` 24) .|.- (fromIntegral (s `B.index` 5) `shiftl_w64` 16) .|.- (fromIntegral (s `B.index` 6) `shiftl_w64` 8) .|.- (fromIntegral (s `B.index` 7) )-{- INLINE getWord64be -}+getWord64be = readN 8 word64be +word64be :: B.ByteString -> Word64+word64be = \s ->+ (fromIntegral (s `B.unsafeIndex` 0) `shiftl_w64` 56) .|.+ (fromIntegral (s `B.unsafeIndex` 1) `shiftl_w64` 48) .|.+ (fromIntegral (s `B.unsafeIndex` 2) `shiftl_w64` 40) .|.+ (fromIntegral (s `B.unsafeIndex` 3) `shiftl_w64` 32) .|.+ (fromIntegral (s `B.unsafeIndex` 4) `shiftl_w64` 24) .|.+ (fromIntegral (s `B.unsafeIndex` 5) `shiftl_w64` 16) .|.+ (fromIntegral (s `B.unsafeIndex` 6) `shiftl_w64` 8) .|.+ (fromIntegral (s `B.unsafeIndex` 7) )+{-# INLINE[2] getWord64be #-}+{-# INLINE word64be #-}+ -- | Read a Word64 in little endian format getWord64le :: Get Word64-getWord64le = do- s <- readN 8 id- return $! (fromIntegral (s `B.index` 7) `shiftl_w64` 56) .|.- (fromIntegral (s `B.index` 6) `shiftl_w64` 48) .|.- (fromIntegral (s `B.index` 5) `shiftl_w64` 40) .|.- (fromIntegral (s `B.index` 4) `shiftl_w64` 32) .|.- (fromIntegral (s `B.index` 3) `shiftl_w64` 24) .|.- (fromIntegral (s `B.index` 2) `shiftl_w64` 16) .|.- (fromIntegral (s `B.index` 1) `shiftl_w64` 8) .|.- (fromIntegral (s `B.index` 0) )-{- INLINE getWord64le -}+getWord64le = readN 8 word64le +word64le :: B.ByteString -> Word64+word64le = \s ->+ (fromIntegral (s `B.unsafeIndex` 7) `shiftl_w64` 56) .|.+ (fromIntegral (s `B.unsafeIndex` 6) `shiftl_w64` 48) .|.+ (fromIntegral (s `B.unsafeIndex` 5) `shiftl_w64` 40) .|.+ (fromIntegral (s `B.unsafeIndex` 4) `shiftl_w64` 32) .|.+ (fromIntegral (s `B.unsafeIndex` 3) `shiftl_w64` 24) .|.+ (fromIntegral (s `B.unsafeIndex` 2) `shiftl_w64` 16) .|.+ (fromIntegral (s `B.unsafeIndex` 1) `shiftl_w64` 8) .|.+ (fromIntegral (s `B.unsafeIndex` 0) )+{-# INLINE[2] getWord64le #-}+{-# INLINE word64le #-}+++-- | Read an Int16 in big endian format.+getInt16be :: Get Int16+getInt16be = fromIntegral <$> getWord16be+{-# INLINE getInt16be #-}++-- | Read an Int32 in big endian format.+getInt32be :: Get Int32+getInt32be = fromIntegral <$> getWord32be+{-# INLINE getInt32be #-}++-- | Read an Int64 in big endian format.+getInt64be :: Get Int64+getInt64be = fromIntegral <$> getWord64be+{-# INLINE getInt64be #-}+++-- | Read an Int16 in little endian format.+getInt16le :: Get Int16+getInt16le = fromIntegral <$> getWord16le+{-# INLINE getInt16le #-}++-- | Read an Int32 in little endian format.+getInt32le :: Get Int32+getInt32le = fromIntegral <$> getWord32le+{-# INLINE getInt32le #-}++-- | Read an Int64 in little endian format.+getInt64le :: Get Int64+getInt64le = fromIntegral <$> getWord64le+{-# INLINE getInt64le #-}++ ------------------------------------------------------------------------ -- Host-endian reads @@ -498,24 +580,79 @@ -- machine the Word is an 8 byte value, on a 32 bit machine, 4 bytes. getWordhost :: Get Word getWordhost = getPtr (sizeOf (undefined :: Word))-{- INLINE getWordhost -}+{-# INLINE getWordhost #-} -- | /O(1)./ Read a 2 byte Word16 in native host order and host endianness. getWord16host :: Get Word16 getWord16host = getPtr (sizeOf (undefined :: Word16))-{- INLINE getWord16host -}+{-# INLINE getWord16host #-} -- | /O(1)./ Read a Word32 in native host order and host endianness. getWord32host :: Get Word32 getWord32host = getPtr (sizeOf (undefined :: Word32))-{- INLINE getWord32host -}+{-# INLINE getWord32host #-} -- | /O(1)./ Read a Word64 in native host order and host endianess. getWord64host :: Get Word64 getWord64host = getPtr (sizeOf (undefined :: Word64))-{- INLINE getWord64host -}+{-# INLINE getWord64host #-} +-- | /O(1)./ Read a single native machine word in native host+-- order. It works in the same way as 'getWordhost'.+getInthost :: Get Int+getInthost = getPtr (sizeOf (undefined :: Int))+{-# INLINE getInthost #-}++-- | /O(1)./ Read a 2 byte Int16 in native host order and host endianness.+getInt16host :: Get Int16+getInt16host = getPtr (sizeOf (undefined :: Int16))+{-# INLINE getInt16host #-}++-- | /O(1)./ Read an Int32 in native host order and host endianness.+getInt32host :: Get Int32+getInt32host = getPtr (sizeOf (undefined :: Int32))+{-# INLINE getInt32host #-}++-- | /O(1)./ Read an Int64 in native host order and host endianess.+getInt64host :: Get Int64+getInt64host = getPtr (sizeOf (undefined :: Int64))+{-# INLINE getInt64host #-}++ ------------------------------------------------------------------------+-- Double/Float reads++-- | Read a 'Float' in big endian IEEE-754 format.+getFloatbe :: Get Float+getFloatbe = wordToFloat <$> getWord32be+{-# INLINE getFloatbe #-}++-- | Read a 'Float' in little endian IEEE-754 format.+getFloatle :: Get Float+getFloatle = wordToFloat <$> getWord32le+{-# INLINE getFloatle #-}++-- | Read a 'Float' in IEEE-754 format and host endian.+getFloathost :: Get Float+getFloathost = wordToFloat <$> getWord32host+{-# INLINE getFloathost #-}++-- | Read a 'Double' in big endian IEEE-754 format.+getDoublebe :: Get Double+getDoublebe = wordToDouble <$> getWord64be+{-# INLINE getDoublebe #-}++-- | Read a 'Double' in little endian IEEE-754 format.+getDoublele :: Get Double+getDoublele = wordToDouble <$> getWord64le+{-# INLINE getDoublele #-}++-- | Read a 'Double' in IEEE-754 format and host endian.+getDoublehost :: Get Double+getDoublehost = wordToDouble <$> getWord64host+{-# INLINE getDoublehost #-}++------------------------------------------------------------------------ -- Unchecked shifts shiftl_w16 :: Word16 -> Int -> Word16@@ -528,12 +665,6 @@ #if WORD_SIZE_IN_BITS < 64 shiftl_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftL64#` i)--#if __GLASGOW_HASKELL__ <= 606--- Exported by GHC.Word in GHC 6.8 and higher-foreign import ccall unsafe "stg_uncheckedShiftL64"- uncheckedShiftL64# :: Word64# -> Int# -> Word64#-#endif #else shiftl_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftL#` i)
+ src/Data/Binary/Get/Internal.hs view
@@ -0,0 +1,430 @@+{-# LANGUAGE CPP, RankNTypes, MagicHash, BangPatterns, TypeFamilies #-}++-- CPP C style pre-precessing, the #if defined lines+-- RankNTypes forall r. statement+-- MagicHash the (# unboxing #), also needs GHC.primitives++module Data.Binary.Get.Internal (++ -- * The Get type+ Get+ , runCont+ , Decoder(..)+ , runGetIncremental++ , readN+ , readNWith++ -- * Parsing+ , bytesRead+ , isolate++ -- * With input chunks+ , withInputChunks+ , Consume+ , failOnEOF++ , get+ , put+ , ensureN++ -- * Utility+ , remaining+ , getBytes+ , isEmpty+ , lookAhead+ , lookAheadM+ , lookAheadE+ , label++ -- ** ByteStrings+ , getByteString++ ) where++import Foreign+import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as B++import Control.Applicative+import Control.Monad+#if MIN_VERSION_base(4,9,0)+import qualified Control.Monad.Fail as Fail+#endif++import Data.Binary.Internal ( accursedUnutterablePerformIO )++-- Kolmodin 20100427: at zurihac we discussed of having partial take a+-- "Maybe ByteString" and implemented it in this way.+-- The reasoning was that you could accidently provide an empty bytestring,+-- and it should not terminate the decoding (empty would mean eof).+-- However, I'd say that it's also a risk that you get stuck in a loop,+-- where you keep providing an empty string. Anyway, no new input should be+-- rare, as the RTS should only wake you up if you actually have some data+-- to read from your fd.++-- | A decoder produced by running a 'Get' monad.+data Decoder a = Fail !B.ByteString String+ -- ^ The decoder ran into an error. The decoder either used+ -- 'fail' or was not provided enough input.+ | Partial (Maybe B.ByteString -> Decoder a)+ -- ^ The decoder has consumed the available input and needs+ -- more to continue. Provide 'Just' if more input is available+ -- and 'Nothing' otherwise, and you will get a new 'Decoder'.+ | Done !B.ByteString a+ -- ^ The decoder has successfully finished. Except for the+ -- output value you also get the unused input.+ | BytesRead {-# UNPACK #-} !Int64 (Int64 -> Decoder a)+ -- ^ The decoder needs to know the current position in the input.+ -- Given the number of bytes remaning in the decoder, the outer+ -- decoder runner needs to calculate the position and+ -- resume the decoding.++-- unrolled codensity/state monad+newtype Get a = C { runCont :: forall r.+ B.ByteString ->+ Success a r ->+ Decoder r }++type Success a r = B.ByteString -> a -> Decoder r++instance Monad Get where+ return = pure+ (>>=) = bindG+#if MIN_VERSION_base(4,9,0)+ fail = Fail.fail++instance Fail.MonadFail Get where+#endif+ fail = failG++bindG :: Get a -> (a -> Get b) -> Get b+bindG (C c) f = C $ \i ks -> c i (\i' a -> (runCont (f a)) i' ks)+{-# INLINE bindG #-}++failG :: String -> Get a+failG str = C $ \i _ks -> Fail i str++apG :: Get (a -> b) -> Get a -> Get b+apG d e = do+ b <- d+ a <- e+ return (b a)+{-# INLINE [0] apG #-}++fmapG :: (a -> b) -> Get a -> Get b+fmapG f m = C $ \i ks -> runCont m i (\i' a -> ks i' (f a))+{-# INLINE fmapG #-}++instance Applicative Get where+ pure = \x -> C $ \s ks -> ks s x+ {-# INLINE [0] pure #-}+ (<*>) = apG+ {-# INLINE (<*>) #-}++-- | /Since: 0.7.1.0/+instance MonadPlus Get where+ mzero = empty+ mplus = (<|>)++instance Functor Get where+ fmap = fmapG++instance Functor Decoder where+ fmap f (Done s a) = Done s (f a)+ fmap f (Partial k) = Partial (fmap f . k)+ fmap _ (Fail s msg) = Fail s msg+ fmap f (BytesRead b k) = BytesRead b (fmap f . k)++instance (Show a) => Show (Decoder a) where+ show (Fail _ msg) = "Fail: " ++ msg+ show (Partial _) = "Partial _"+ show (Done _ a) = "Done: " ++ show a+ show (BytesRead _ _) = "BytesRead"++-- | Run a 'Get' monad. See 'Decoder' for what to do next, like providing+-- input, handling decoding errors and to get the output value.+runGetIncremental :: Get a -> Decoder a+runGetIncremental g = noMeansNo $+ runCont g B.empty (\i a -> Done i a)++-- | Make sure we don't have to pass Nothing to a Partial twice.+-- This way we don't need to pass around an EOF value in the Get monad, it+-- can safely ask several times if it needs to.+noMeansNo :: Decoder a -> Decoder a+noMeansNo r0 = go r0+ where+ go r =+ case r of+ Partial k -> Partial $ \ms ->+ case ms of+ Just _ -> go (k ms)+ Nothing -> neverAgain (k ms)+ BytesRead n k -> BytesRead n (go . k)+ Done _ _ -> r+ Fail _ _ -> r+ neverAgain r =+ case r of+ Partial k -> neverAgain (k Nothing)+ BytesRead n k -> BytesRead n (neverAgain . k)+ Fail _ _ -> r+ Done _ _ -> r++prompt :: B.ByteString -> Decoder a -> (B.ByteString -> Decoder a) -> Decoder a+prompt inp kf ks = prompt' kf (\inp' -> ks (inp `B.append` inp'))++prompt' :: Decoder a -> (B.ByteString -> Decoder a) -> Decoder a+prompt' kf ks =+ let loop =+ Partial $ \sm ->+ case sm of+ Just s | B.null s -> loop+ | otherwise -> ks s+ Nothing -> kf+ in loop++-- | Get the total number of bytes read to this point.+bytesRead :: Get Int64+bytesRead = C $ \inp k -> BytesRead (fromIntegral $ B.length inp) (k inp)++-- | Isolate a decoder to operate with a fixed number of bytes, and fail if+-- fewer bytes were consumed, or more bytes were attempted to be consumed.+-- If the given decoder fails, 'isolate' will also fail.+-- Offset from 'bytesRead' will be relative to the start of 'isolate', not the+-- absolute of the input.+--+-- /Since: 0.7.2.0/+isolate :: Int -- ^ The number of bytes that must be consumed+ -> Get a -- ^ The decoder to isolate+ -> Get a+isolate n0 act+ | n0 < 0 = fail "isolate: negative size"+ | otherwise = go n0 (runCont act B.empty Done)+ where+ go !n (Done left x)+ | n == 0 && B.null left = return x+ | otherwise = do+ pushFront left+ let consumed = n0 - n - B.length left+ fail $ "isolate: the decoder consumed " ++ show consumed ++ " bytes" +++ " which is less than the expected " ++ show n0 ++ " bytes"+ go 0 (Partial resume) = go 0 (resume Nothing)+ go n (Partial resume) = do+ inp <- C $ \inp k -> do+ let takeLimited str =+ let (inp', out) = B.splitAt n str+ in k out (Just inp')+ case not (B.null inp) of+ True -> takeLimited inp+ False -> prompt inp (k B.empty Nothing) takeLimited+ case inp of+ Nothing -> go n (resume Nothing)+ Just str -> go (n - B.length str) (resume (Just str))+ go _ (Fail bs err) = pushFront bs >> fail err+ go n (BytesRead r resume) =+ go n (resume $! fromIntegral n0 - fromIntegral n - r)++type Consume s = s -> B.ByteString -> Either s (B.ByteString, B.ByteString)++withInputChunks :: s -> Consume s -> ([B.ByteString] -> b) -> ([B.ByteString] -> Get b) -> Get b+withInputChunks initS consume onSucc onFail = go initS []+ where+ go state acc = C $ \inp ks ->+ case consume state inp of+ Left state' -> do+ let acc' = inp : acc+ prompt'+ (runCont (onFail (reverse acc')) B.empty ks)+ (\str' -> runCont (go state' acc') str' ks)+ Right (want,rest) -> do+ ks rest (onSucc (reverse (want:acc)))++failOnEOF :: [B.ByteString] -> Get a+failOnEOF bs = C $ \_ _ -> Fail (B.concat bs) "not enough bytes"++-- | Test whether all input has been consumed, i.e. there are no remaining+-- undecoded bytes.+isEmpty :: Get Bool+isEmpty = C $ \inp ks ->+ if B.null inp+ then prompt inp (ks inp True) (\inp' -> ks inp' False)+ else ks inp False++-- | DEPRECATED. Same as 'getByteString'.+{-# DEPRECATED getBytes "Use 'getByteString' instead of 'getBytes'." #-}+getBytes :: Int -> Get B.ByteString+getBytes = getByteString+{-# INLINE getBytes #-}++-- | /Since: 0.7.0.0/+instance Alternative Get where+ empty = C $ \inp _ks -> Fail inp "Data.Binary.Get(Alternative).empty"+ {-# INLINE empty #-}+ (<|>) f g = do+ (decoder, bs) <- runAndKeepTrack f+ case decoder of+ Done inp x -> C $ \_ ks -> ks inp x+ Fail _ _ -> pushBack bs >> g+ _ -> error "Binary: impossible"+ {-# INLINE (<|>) #-}+ some p = (:) <$> p <*> many p+ {-# INLINE some #-}+ many p = do+ v <- (Just <$> p) <|> pure Nothing+ case v of+ Nothing -> pure []+ Just x -> (:) x <$> many p+ {-# INLINE many #-}++-- | Run a decoder and keep track of all the input it consumes.+-- Once it's finished, return the final decoder (always 'Done' or 'Fail'),+-- and unconsume all the the input the decoder required to run.+-- Any additional chunks which was required to run the decoder+-- will also be returned.+runAndKeepTrack :: Get a -> Get (Decoder a, [B.ByteString])+runAndKeepTrack g = C $ \inp ks ->+ let r0 = runCont g inp (\inp' a -> Done inp' a)+ go !acc r = case r of+ Done inp' a -> ks inp (Done inp' a, reverse acc)+ Partial k -> Partial $ \minp -> go (maybe acc (:acc) minp) (k minp)+ Fail inp' s -> ks inp (Fail inp' s, reverse acc)+ BytesRead unused k -> BytesRead unused (go acc . k)+ in go [] r0+{-# INLINE runAndKeepTrack #-}++pushBack :: [B.ByteString] -> Get ()+pushBack [] = C $ \ inp ks -> ks inp ()+pushBack bs = C $ \ inp ks -> ks (B.concat (inp : bs)) ()+{-# INLINE pushBack #-}++pushFront :: B.ByteString -> Get ()+pushFront bs = C $ \ inp ks -> ks (B.append bs inp) ()+{-# INLINE pushFront #-}++-- | Run the given decoder, but without consuming its input. If the given+-- decoder fails, then so will this function.+--+-- /Since: 0.7.0.0/+lookAhead :: Get a -> Get a+lookAhead g = do+ (decoder, bs) <- runAndKeepTrack g+ case decoder of+ Done _ a -> pushBack bs >> return a+ Fail inp s -> C $ \_ _ -> Fail inp s+ _ -> error "Binary: impossible"++-- | Run the given decoder, and only consume its input if it returns 'Just'.+-- If 'Nothing' is returned, the input will be unconsumed.+-- If the given decoder fails, then so will this function.+--+-- /Since: 0.7.0.0/+lookAheadM :: Get (Maybe a) -> Get (Maybe a)+lookAheadM g = do+ let g' = maybe (Left ()) Right <$> g+ either (const Nothing) Just <$> lookAheadE g'++-- | Run the given decoder, and only consume its input if it returns 'Right'.+-- If 'Left' is returned, the input will be unconsumed.+-- If the given decoder fails, then so will this function.+--+-- /Since: 0.7.1.0/+lookAheadE :: Get (Either a b) -> Get (Either a b)+lookAheadE g = do+ (decoder, bs) <- runAndKeepTrack g+ case decoder of+ Done _ (Left x) -> pushBack bs >> return (Left x)+ Done inp (Right x) -> C $ \_ ks -> ks inp (Right x)+ Fail inp s -> C $ \_ _ -> Fail inp s+ _ -> error "Binary: impossible"++-- | Label a decoder. If the decoder fails, the label will be appended on+-- a new line to the error message string.+--+-- /Since: 0.7.2.0/+label :: String -> Get a -> Get a+label msg decoder = C $ \inp ks ->+ let r0 = runCont decoder inp (\inp' a -> Done inp' a)+ go r = case r of+ Done inp' a -> ks inp' a+ Partial k -> Partial (go . k)+ Fail inp' s -> Fail inp' (s ++ "\n" ++ msg)+ BytesRead u k -> BytesRead u (go . k)+ in go r0++-- | DEPRECATED. Get the number of bytes of remaining input.+-- Note that this is an expensive function to use as in order to calculate how+-- much input remains, all input has to be read and kept in-memory.+-- The decoder keeps the input as a strict bytestring, so you are likely better+-- off by calculating the remaining input in another way.+{-# DEPRECATED remaining "This will force all remaining input, don't use it." #-}+remaining :: Get Int64+remaining = C $ \ inp ks ->+ let loop acc = Partial $ \ minp ->+ case minp of+ Nothing -> let all_inp = B.concat (inp : (reverse acc))+ in ks all_inp (fromIntegral $ B.length all_inp)+ Just inp' -> loop (inp':acc)+ in loop []++------------------------------------------------------------------------+-- ByteStrings+--++-- | An efficient get method for strict ByteStrings. Fails if fewer than @n@+-- bytes are left in the input. If @n <= 0@ then the empty string is returned.+getByteString :: Int -> Get B.ByteString+getByteString n | n > 0 = readN n (B.unsafeTake n)+ | otherwise = return B.empty+{-# INLINE getByteString #-}++-- | Get the current chunk.+get :: Get B.ByteString+get = C $ \inp ks -> ks inp inp++-- | Replace the current chunk.+put :: B.ByteString -> Get ()+put s = C $ \_inp ks -> ks s ()++-- | Return at least @n@ bytes, maybe more. If not enough data is available+-- the computation will escape with 'Partial'.+readN :: Int -> (B.ByteString -> a) -> Get a+readN !n f = ensureN n >> unsafeReadN n f+{-# INLINE [0] readN #-}++{-# RULES++"readN/readN merge" forall n m f g.+ apG (readN n f) (readN m g) = readN (n+m) (\bs -> f bs $ g (B.unsafeDrop n bs)) #-}++-- | Ensure that there are at least @n@ bytes available. If not, the+-- computation will escape with 'Partial'.+ensureN :: Int -> Get ()+ensureN !n0 = C $ \inp ks -> do+ if B.length inp >= n0+ then ks inp ()+ else runCont (withInputChunks n0 enoughChunks onSucc onFail >>= put) inp ks+ where -- might look a bit funny, but plays very well with GHC's inliner.+ -- GHC won't inline recursive functions, so we make ensureN non-recursive+ enoughChunks n str+ | B.length str >= n = Right (str,B.empty)+ | otherwise = Left (n - B.length str)+ -- Sometimes we will produce leftovers lists of the form [B.empty, nonempty]+ -- where `nonempty` is a non-empty ByteString. In this case we can avoid a copy+ -- by simply dropping the empty prefix. In principle ByteString might want+ -- to gain this optimization as well+ onSucc = B.concat . dropWhile B.null+ onFail bss = C $ \_ _ -> Fail (B.concat bss) "not enough bytes"+{-# INLINE ensureN #-}++unsafeReadN :: Int -> (B.ByteString -> a) -> Get a+unsafeReadN !n f = C $ \inp ks -> do+ ks (B.unsafeDrop n inp) $! f inp -- strict return++-- | @readNWith n f@ where @f@ must be deterministic and not have side effects.+readNWith :: Int -> (Ptr a -> IO a) -> Get a+readNWith n f = do+ -- It should be safe to use accursedUnutterablePerformIO here.+ -- The action must be deterministic and not have any external side effects.+ -- It depends on the value of the ByteString so the value dependencies look OK.+ readN n $ \s -> accursedUnutterablePerformIO $ B.unsafeUseAsCString s (f . castPtr)+{-# INLINE readNWith #-}
+ src/Data/Binary/Internal.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE CPP #-}++module Data.Binary.Internal + ( accursedUnutterablePerformIO ) where++#if MIN_VERSION_bytestring(0,10,6)+import Data.ByteString.Internal( accursedUnutterablePerformIO )+#else+import Data.ByteString.Internal( inlinePerformIO )++{-# INLINE accursedUnutterablePerformIO #-}+-- | You must be truly desperate to come to me for help.+accursedUnutterablePerformIO :: IO a -> a+accursedUnutterablePerformIO = inlinePerformIO+#endif
src/Data/Binary/Put.hs view
@@ -1,10 +1,18 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE Safe #-}++#if MIN_VERSION_base(4,9,0)+#define HAS_SEMIGROUP+#endif+ ----------------------------------------------------------------------------- -- | -- Module : Data.Binary.Put -- Copyright : Lennart Kolmodin -- License : BSD3-style (see LICENSE)--- --- Maintainer : Lennart Kolmodin <kolmodin@dtek.chalmers.se>+--+-- Maintainer : Lennart Kolmodin <kolmodin@gmail.com> -- Stability : stable -- Portability : Portable to Hugs and GHC. Requires MPTCs --@@ -18,50 +26,86 @@ Put , PutM(..) , runPut+ , runPutM+ , putBuilder+ , execPut -- * Flushing the implicit parse state , flush -- * Primitives , putWord8+ , putInt8 , putByteString , putLazyByteString+#if MIN_VERSION_bytestring(0,10,4)+ , putShortByteString+#endif -- * Big-endian primitives , putWord16be , putWord32be , putWord64be+ , putInt16be+ , putInt32be+ , putInt64be+ , putFloatbe+ , putDoublebe -- * Little-endian primitives , putWord16le , putWord32le , putWord64le+ , putInt16le+ , putInt32le+ , putInt64le+ , putFloatle+ , putDoublele -- * Host-endian, unaligned writes , putWordhost -- :: Word -> Put , putWord16host -- :: Word16 -> Put , putWord32host -- :: Word32 -> Put , putWord64host -- :: Word64 -> Put+ , putInthost -- :: Int -> Put+ , putInt16host -- :: Int16 -> Put+ , putInt32host -- :: Int32 -> Put+ , putInt64host -- :: Int64 -> Put+ , putFloathost+ , putDoublehost + -- * Unicode+ , putCharUtf8+ , putStringUtf8+ ) where -import Data.Monoid+import qualified Data.Monoid as Monoid import Data.Binary.Builder (Builder, toLazyByteString) import qualified Data.Binary.Builder as B +import Data.Int import Data.Word import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L+#if MIN_VERSION_bytestring(0,10,4)+import Data.ByteString.Short+#endif -#ifdef APPLICATIVE_IN_BASE-import Control.Applicative+#ifdef HAS_SEMIGROUP+import Data.Semigroup #endif +import Control.Applicative+import Prelude -- Silence AMP warning. +-- needed for casting Floats/Doubles to words.+import Data.Binary.FloatCast (floatToWord, doubleToWord)+ ------------------------------------------------------------------------ --- XXX Strict in buffer only. -data PairS a = PairS a {-# UNPACK #-}!Builder+-- XXX Strict in buffer only.+data PairS a = PairS a !Builder sndS :: PairS a -> Builder sndS (PairS _ b) = b@@ -76,41 +120,82 @@ fmap f m = Put $ let PairS a w = unPut m in PairS (f a) w {-# INLINE fmap #-} -#ifdef APPLICATIVE_IN_BASE instance Applicative PutM where- pure = return+ pure a = Put $ PairS a Monoid.mempty+ {-# INLINE pure #-}+ m <*> k = Put $ let PairS f w = unPut m PairS x w' = unPut k- in PairS (f x) (w `mappend` w')-#endif+ in PairS (f x) (w `Monoid.mappend` w') + m *> k = Put $+ let PairS _ w = unPut m+ PairS b w' = unPut k+ in PairS b (w `Monoid.mappend` w')+ {-# INLINE (*>) #-}+ -- Standard Writer monad, with aggressive inlining instance Monad PutM where- return a = Put $ PairS a mempty- {-# INLINE return #-}- m >>= k = Put $ let PairS a w = unPut m PairS b w' = unPut (k a)- in PairS b (w `mappend` w')+ in PairS b (w `Monoid.mappend` w') {-# INLINE (>>=) #-} - m >> k = Put $- let PairS _ w = unPut m- PairS b w' = unPut k- in PairS b (w `mappend` w')+ return = pure+ {-# INLINE return #-}++ (>>) = (*>) {-# INLINE (>>) #-} +instance Monoid.Monoid (PutM ()) where+ mempty = pure ()+ {-# INLINE mempty #-}++#ifdef HAS_SEMIGROUP+ mappend = (<>)+#else+ mappend = mappend'+#endif+ {-# INLINE mappend #-}++mappend' :: Put -> Put -> Put+mappend' m k = Put $+ let PairS _ w = unPut m+ PairS _ w' = unPut k+ in PairS () (w `Monoid.mappend` w')+{-# INLINE mappend' #-}++#ifdef HAS_SEMIGROUP+instance Semigroup (PutM ()) where+ (<>) = mappend'+ {-# INLINE (<>) #-}+#endif+ tell :: Builder -> Put tell b = Put $ PairS () b {-# INLINE tell #-} +putBuilder :: Builder -> Put+putBuilder = tell+{-# INLINE putBuilder #-}++-- | Run the 'Put' monad+execPut :: PutM a -> Builder+execPut = sndS . unPut+{-# INLINE execPut #-}+ -- | Run the 'Put' monad with a serialiser runPut :: Put -> L.ByteString runPut = toLazyByteString . sndS . unPut {-# INLINE runPut #-} +-- | Run the 'Put' monad with a serialiser and get its result+runPutM :: PutM a -> (a, L.ByteString)+runPutM (Put (PairS f s)) = (f, toLazyByteString s)+{-# INLINE runPutM #-}+ ------------------------------------------------------------------------ -- | Pop the ByteString we have constructed so far, if any, yielding a@@ -124,6 +209,11 @@ putWord8 = tell . B.singleton {-# INLINE putWord8 #-} +-- | Efficiently write a signed byte into the output buffer+putInt8 :: Int8 -> Put+putInt8 = tell . B.singleton . fromIntegral+{-# INLINE putInt8 #-}+ -- | An efficient primitive to write a strict ByteString into the output buffer. -- It flushes the current buffer, and writes the argument into a new chunk. putByteString :: S.ByteString -> Put@@ -136,6 +226,13 @@ putLazyByteString = tell . B.fromLazyByteString {-# INLINE putLazyByteString #-} +#if MIN_VERSION_bytestring(0,10,4)+-- | Write 'ShortByteString' to the buffer+putShortByteString :: ShortByteString -> Put+putShortByteString = tell . B.fromShortByteString+{-# INLINE putShortByteString #-}+#endif+ -- | Write a Word16 in big endian format putWord16be :: Word16 -> Put putWord16be = tell . B.putWord16be@@ -166,6 +263,37 @@ putWord64le = tell . B.putWord64le {-# INLINE putWord64le #-} +-- | Write an Int16 in big endian format+putInt16be :: Int16 -> Put+putInt16be = tell . B.putInt16be+{-# INLINE putInt16be #-}++-- | Write an Int16 in little endian format+putInt16le :: Int16 -> Put+putInt16le = tell . B.putInt16le+{-# INLINE putInt16le #-}++-- | Write an Int32 in big endian format+putInt32be :: Int32 -> Put+putInt32be = tell . B.putInt32be+{-# INLINE putInt32be #-}++-- | Write an Int32 in little endian format+putInt32le :: Int32 -> Put+putInt32le = tell . B.putInt32le+{-# INLINE putInt32le #-}++-- | Write an Int64 in big endian format+putInt64be :: Int64 -> Put+putInt64be = tell . B.putInt64be+{-# INLINE putInt64be #-}++-- | Write an Int64 in little endian format+putInt64le :: Int64 -> Put+putInt64le = tell . B.putInt64le+{-# INLINE putInt64le #-}++ ------------------------------------------------------------------------ -- | /O(1)./ Write a single native machine word. The word is@@ -196,3 +324,78 @@ putWord64host :: Word64 -> Put putWord64host = tell . B.putWord64host {-# INLINE putWord64host #-}++-- | /O(1)./ Write a single native machine word. The word is+-- written in host order, host endian form, for the machine you're on.+-- On a 64 bit machine the Int is an 8 byte value, on a 32 bit machine,+-- 4 bytes. Values written this way are not portable to+-- different endian or word sized machines, without conversion.+--+putInthost :: Int -> Put+putInthost = tell . B.putInthost+{-# INLINE putInthost #-}++-- | /O(1)./ Write an Int16 in native host order and host endianness.+-- For portability issues see @putInthost@.+putInt16host :: Int16 -> Put+putInt16host = tell . B.putInt16host+{-# INLINE putInt16host #-}++-- | /O(1)./ Write an Int32 in native host order and host endianness.+-- For portability issues see @putInthost@.+putInt32host :: Int32 -> Put+putInt32host = tell . B.putInt32host+{-# INLINE putInt32host #-}++-- | /O(1)./ Write an Int64 in native host order+-- On a 32 bit machine we write two host order Int32s, in big endian form.+-- For portability issues see @putInthost@.+putInt64host :: Int64 -> Put+putInt64host = tell . B.putInt64host+{-# INLINE putInt64host #-}++------------------------------------------------------------------------+-- Floats/Doubles++-- | Write a 'Float' in big endian IEEE-754 format.+putFloatbe :: Float -> Put+putFloatbe = putWord32be . floatToWord+{-# INLINE putFloatbe #-}++-- | Write a 'Float' in little endian IEEE-754 format.+putFloatle :: Float -> Put+putFloatle = putWord32le . floatToWord+{-# INLINE putFloatle #-}++-- | Write a 'Float' in native in IEEE-754 format and host endian.+putFloathost :: Float -> Put+putFloathost = putWord32host . floatToWord+{-# INLINE putFloathost #-}++-- | Write a 'Double' in big endian IEEE-754 format.+putDoublebe :: Double -> Put+putDoublebe = putWord64be . doubleToWord+{-# INLINE putDoublebe #-}++-- | Write a 'Double' in little endian IEEE-754 format.+putDoublele :: Double -> Put+putDoublele = putWord64le . doubleToWord+{-# INLINE putDoublele #-}++-- | Write a 'Double' in native in IEEE-754 format and host endian.+putDoublehost :: Double -> Put+putDoublehost = putWord64host . doubleToWord+{-# INLINE putDoublehost #-}++------------------------------------------------------------------------+-- Unicode++-- | Write a character using UTF-8 encoding.+putCharUtf8 :: Char -> Put+putCharUtf8 = tell . B.putCharUtf8+{-# INLINE putCharUtf8 #-}++-- | Write a String using UTF-8 encoding.+putStringUtf8 :: String -> Put+putStringUtf8 = tell . B.putStringUtf8+{-# INLINE putStringUtf8 #-}
+ tests/Action.hs view
@@ -0,0 +1,406 @@+{-# LANGUAGE PatternGuards #-}+module Action where++import Control.Applicative+import Control.Monad+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import Data.Char+import Data.List (intersperse, nub)++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck++import Arbitrary ()+import qualified Data.Binary.Get as Binary++tests :: [Test]+tests = [ testProperty "action" prop_action+ , testProperty "label" prop_label+ , testProperty "fail" prop_fail ]++data Action+ = Actions [Action]+ | GetByteString Int+ | GetByteStringL Int+ | Skip Int+ | Isolate Int [Action]+ | Try [Action] [Action]+ | Label String [Action]+ | LookAhead [Action]+ -- | First argument is True if this action returns Just, otherwise False.+ | LookAheadM Bool [Action]+ -- | First argument is True if this action returns Right, otherwise Left.+ | LookAheadE Bool [Action]+ | BytesRead+ | Fail+ deriving (Show, Eq)++instance Arbitrary Action where+ arbitrary = fmap Actions (gen_actions False)+ shrink action =+ case action of+ Actions [a] -> [a]+ Actions as -> [ Actions as' | as' <- shrink as ]+ BytesRead -> []+ Fail -> []+ GetByteString n -> [ GetByteString n' | n' <- shrink n ]+ GetByteStringL n -> [ GetByteStringL n' | n' <- shrink n ]+ Skip n -> [ Skip n' | n' <- shrink n ]+ Isolate n as -> nub $ Actions as :+ [ Isolate n' as' | (n',as') <- shrink (n,as)+ , n' >= 0+ , n' <= max_len as' + 1 ]+ Label str a -> Actions a : [ Label str a' | a' <- shrink a ]+ LookAhead a -> Actions a : [ LookAhead a' | a' <- shrink a ]+ LookAheadM b a -> Actions a : [ LookAheadM b a' | a' <- shrink a ]+ LookAheadE b a -> Actions a : [ LookAheadE b a' | a' <- shrink a ]+ Try [Fail] b -> Actions b : [ Try [Fail] b' | b' <- shrink b ]+ Try a b ->+ [Actions a | not (willFail' a)]+ ++ [ Try a' b' | (a',b') <- shrink (a,b) ]++willFail :: Int -> [Action] -> Bool+willFail inp xxs =+ case eval inp xxs of+ EFail {} -> True+ _ -> False++willFail' :: [Action] -> Bool+willFail' = willFail maxBound++-- | The maximum length of input decoder can request.+-- The decoder may end up using less, but never more.+-- This way, you know how much input to generate for running a decoder test.+max_len :: [Action] -> Int+max_len [] = 0+max_len (x:xs) =+ case x of+ Actions xs' -> max_len (xs' ++ xs)+ BytesRead -> max_len xs+ Fail -> 0+ GetByteString n -> n + max_len xs+ GetByteStringL n -> n + max_len xs+ Skip n -> n + max_len xs+ Isolate n xs'+ | Just _ <- actual_len' [Isolate n xs'] -> n + max_len xs+ | otherwise -> n+ Label _ xs' -> max_len (xs' ++ xs)+ LookAhead xs'+ | willFail' xs' -> max_len xs'+ | otherwise -> max (max_len xs') (max_len xs)+ LookAheadM consume xs'+ | consume -> max_len (xs' ++ xs)+ | otherwise -> max_len (LookAhead xs' : xs)+ LookAheadE consume xs'+ | consume -> max_len (xs' ++ xs)+ | otherwise -> max_len (LookAhead xs' : xs)+ Try a b+ | willFail' a && willFail' b -> max (max_len a) (max_len b)+ | willFail' a -> max (max_len a) (max_len b) + max_len xs+ | otherwise -> max_len (a ++ xs)++-- | The actual length of input that will be consumed when+-- a decoder is executed, or Nothing if the decoder will fail.+actual_len :: Int -> [Action] -> Maybe Int+actual_len inp xs =+ case eval inp xs of+ ESuccess inp' -> Just (inp - inp')+ _ -> Nothing++actual_len' :: [Action] -> Maybe Int+actual_len' = actual_len maxBound++randomInput :: Int -> Gen L.ByteString+randomInput 0 = return L.empty+randomInput n = do+ m <- choose (1, min n 10)+ s <- vectorOf m $ choose ('a', 'z')+ let b = B.pack $ map (fromIntegral.ord) s+ rest <- randomInput (n-m)+ return (L.append (L.fromChunks [b]) rest)++-- | Build binary programs and compare running them to running a (hopefully)+-- identical model.+-- Tests that 'bytesRead' returns correct values when used together with '<|>'+-- and 'fail'.+prop_action :: Property+prop_action =+ forAllShrink (gen_actions False) shrink $ \ actions ->+ let max_len_input = max_len actions in+ forAll (randomInput max_len_input) $ \ lbs ->+ let allInput = B.concat (L.toChunks lbs) in+ case Binary.runGetOrFail (execute allInput actions) lbs of+ Right (_inp, _off, _x) -> True+ Left (_inp, _off, _msg) -> True++-- | When a decoder aborts with 'fail', check that all relevant uses of 'label'+-- are respected.+prop_label :: Property+prop_label =+ forAllShrink (gen_actions True) shrink $ \ actions ->+ let max_len_input = max_len actions in+ forAll (randomInput max_len_input) $ \ lbs ->+ let allInput = B.concat (L.toChunks lbs) in+ collect (failReason $ eval max_len_input actions) $+ case Binary.runGetOrFail (execute allInput actions) lbs of+ Left (_inp, _off, msg) ->+ let lbls = case collectLabels max_len_input actions of+ Just lbls' -> lbls'+ Nothing -> error ("expected labels, got: " ++ msg)+ expectedMsg = concat $ intersperse "\n" lbls+ in expectedMsg === msg+ Right (_inp, _off, _value) -> label "test case without 'fail'" $ True++-- | When a decoder aborts with 'fail', check the fail position and+-- remaining input.+prop_fail :: Property+prop_fail =+ forAllShrink (gen_actions True) shrink $ \ actions ->+ let max_len_input = max_len actions in+ forAll (randomInput max_len_input) $ \ lbs ->+ let allInput = B.concat (L.toChunks lbs) in+ collect (failReason $ eval max_len_input actions) $+ case Binary.runGetOrFail (execute allInput actions) lbs of+ Left (inp, off, _msg) ->+ case () of+ _ | Just off /= findFailPosition max_len_input actions ->+ error ("fail position incorrect, expected " +++ show (findFailPosition max_len_input actions) +++ " but got " ++ show off)+ | inp /= L.drop (fromIntegral off) lbs ->+ error $ "remaining output incorrect, was: " ++ show inp +++ ", should hav been: " ++ show (L.drop (fromIntegral off) lbs)+ | otherwise -> property True+ Right (_inp, _off, _value) -> label "test case without 'fail'" $ property True++-- | Collect all the labels up to a 'fail', or Nothing if the+-- decoder will not fail.+collectLabels :: Int -> [Action] -> Maybe [String]+collectLabels inp xxs =+ case eval inp xxs of+ EFail _ lbls _ -> Just lbls+ _ -> Nothing++-- | Finds at which byte offset the decoder will fail,+-- or Nothing if it won't fail.+findFailPosition :: Int -> [Action] -> Maybe Binary.ByteOffset+findFailPosition inp xxs =+ case eval inp xxs of+ EFail _ _ inp' -> return (fromIntegral (inp-inp'))+ _ -> Nothing++failReason :: Eval -> String+failReason (EFail fr _ _) = show fr+failReason _ = "NoFail"++-- | The result of an evaluation.+data Eval = ESuccess Int+ -- ^ The evalutation completed successfully. Contains the number of+ -- remaining bytes of the input.+ | EFail FailReason [String] Int+ -- ^ The evaluation completed with a failure. Contains the labels up+ -- to the failure, and the number of remaining bytes of the input.+ deriving (Show,Eq)++data FailReason+ = FRFail+ | FRIsolateTooMuch+ | FRIsolateTooLittle+ | FRTooMuch+ deriving (Show,Eq)++-- | Given the number of input bytes and a list of actions, evaluate the+-- actions and return whether the actions succeeed or fail.+eval :: Int -> [Action] -> Eval+eval inp0 = go inp0 []+ where+ step :: Int -> Int -> [String] -> [Action] -> Eval+ step inp n lbls xs+ | inp - n < 0 =+ let msg = "not enough bytes"+ in EFail FRTooMuch (msg:lbls) inp+ | otherwise = go (inp-n) lbls xs+ go :: Int -> [String] -> [Action] -> Eval+ go inp _lbls [] = ESuccess inp+ go inp lbls (x:xs) =+ case x of+ Actions xs' -> go inp lbls (xs'++xs)+ BytesRead -> go inp lbls xs+ Fail -> EFail FRFail ("fail":lbls) inp+ GetByteString n -> step inp n lbls xs+ GetByteStringL n -> step inp n lbls xs+ Skip n -> step inp n lbls xs+ Isolate n xs'+ | n > inp ->+ case go inp lbls xs' of+ ESuccess inp' ->+ let msg = "isolate: the decoder consumed " ++ show (inp - inp') +++ " bytes which is less than the expected " ++ (show n) +++ " bytes"+ in EFail FRTooMuch (msg:lbls) inp'+ efail -> efail+ | otherwise ->+ case go n lbls xs' of+ EFail fr lbls' inp' -> EFail fr lbls' (inp - n + inp')+ ESuccess 0 -> go (inp-n) lbls xs+ ESuccess inp' ->+ let msg = "isolate: the decoder consumed " ++ show (n - inp') +++ " bytes which is less than the expected " ++ (show n) +++ " bytes"+ in EFail FRIsolateTooLittle (msg:lbls) (inp - n + inp')+ Label str xs' ->+ case go inp (str:lbls) xs' of+ EFail fr lbls' inp' -> EFail fr lbls' inp'+ ESuccess inp' -> go inp' lbls xs+ LookAhead xs'+ | EFail fr lbls' inp' <- go inp lbls xs' -> EFail fr lbls' inp'+ | otherwise -> go inp lbls xs+ LookAheadM consume xs'+ | consume -> go inp lbls (xs'++xs)+ | otherwise -> go inp lbls (LookAhead xs' : xs)+ LookAheadE consume xs'+ | consume -> go inp lbls (xs'++xs)+ | otherwise -> go inp lbls (LookAhead xs' : xs)+ Try a b ->+ case go inp lbls a of+ ESuccess inp' -> go inp' lbls xs+ EFail {} -> go inp lbls (b++xs)+ +-- | Execute (run) the model.+-- First argument is all the input that will be used when executing+-- this decoder. It is used in this function to compare the expected+-- value with the actual value from the decoder functions.+-- The second argument is the model - the actions we will execute.+execute :: B.ByteString -> [Action] -> Binary.Get ()+execute inp acts0 = go 0 acts0 >> return ()+ where+ inp_len = B.length inp+ go _ [] = return ()+ go pos (x:xs) =+ case x of+ Actions a -> go pos (a++xs)+ GetByteString n -> do+ -- Run the operation in the Get monad...+ actual <- Binary.getByteString n+ let expected = B.take n . B.drop pos $ inp+ -- ... and compare that we got what we expected.+ when (actual /= expected) $ error $+ "execute(getByteString): actual /= expected at pos " ++ show pos +++ ", got: " ++ show actual ++ ", expected: " ++ show expected+ go (pos+n) xs+ GetByteStringL n -> do+ -- Run the operation in the Get monad...+ actual <- L.toStrict <$> Binary.getLazyByteString (fromIntegral n)+ let expected = B.take n . B.drop pos $ inp+ -- ... and compare that we got what we expected.+ when (actual /= expected) $ error $+ "execute(getLazyByteString): actual /= expected at pos " ++ show pos +++ ", got: " ++ show actual ++ ", expected: " ++ show expected+ go (pos+n) xs+ Skip n -> do+ Binary.skip n+ go (pos+n) xs+ BytesRead -> do+ pos' <- Binary.bytesRead+ if pos == fromIntegral pos'+ then go pos xs+ else error $ "execute(bytesRead): expected " +++ show pos ++ " but got " ++ show pos'+ Fail -> fail "fail"+ Isolate n as -> do+ let str = B.take n (B.drop pos inp)+ _ <- Binary.isolate n (execute str as)+ when (willFail (inp_len - pos) [Isolate n as]) $+ error "expected isolate to fail"+ go (pos + n) xs+ Label str as -> do+ len <- Binary.label str (leg pos as)+ go (pos+len) xs+ LookAhead a -> do+ _ <- Binary.lookAhead (go pos a)+ go pos xs+ LookAheadM b a -> do+ let f True = Just <$> leg pos a+ f False = go pos a >> return Nothing+ len <- Binary.lookAheadM (f b)+ case len of+ Nothing -> go pos xs+ Just offset -> go (pos+offset) xs+ LookAheadE b a -> do+ let f True = Right <$> leg pos a+ f False = go pos a >> return (Left ())+ len <- Binary.lookAheadE (f b)+ case len of+ Left _ -> go pos xs+ Right offset -> go (pos+offset) xs+ Try a b -> do+ offset <- leg pos a <|> leg pos b+ go (pos+offset) xs+ leg pos t = do+ go pos t+ case actual_len (inp_len - pos) t of+ Nothing -> error "impossible: branch should have failed"+ Just offset -> return offset++gen_actions :: Bool -> Gen [Action]+gen_actions genFail = do+ acts <- sized (go False)+ return acts+ where+ go :: Bool -> Int -> Gen [Action]+ go _ 0 = return []+ go inTry s = oneof $ [ do n <- choose (0,10)+ (:) (GetByteString n) <$> go inTry (s-1)+ , do n <- choose (0,10)+ (:) (GetByteStringL n) <$> go inTry (s-1)+ , do n <- choose (0,10)+ (:) (Skip n) <$> go inTry (s-1)+ , do (:) BytesRead <$> go inTry (s-1)+ , do t1 <- go True (s `div` 2)+ t2 <- go inTry (s `div` 2)+ (:) (Try t1 t2) <$> go inTry (s `div` 2)+ , do t <- go inTry (s`div`2)+ (:) (LookAhead t) <$> go inTry (s-1)+ , do t <- go inTry (s`div`2)+ b <- arbitrary+ (:) (LookAheadM b t) <$> go inTry (s-1)+ , do t <- go inTry (s`div`2)+ b <- arbitrary+ (:) (LookAheadE b t) <$> go inTry (s-1)+ , do t <- go inTry (s`div`2)+ Positive n <- arbitrary :: Gen (Positive Int)+ (:) (Label ("some label: " ++ show n) t) <$> go inTry (s-1)+ , do t <- resize (s`div`2) (gen_isolate (genFail || inTry))+ (:) t <$> go inTry (s-1)+ ] ++ [frequency [(if inTry || genFail then 1 else 0, return [Fail])+ ,(9 , go inTry s)]]++gen_isolate :: Bool -> Gen Action+gen_isolate genFail = gen_actions genFail >>= go+ where+ go t0 = do+ -- We can isolate the decoder with three different ranges;+ -- * give too few bytes -> isolate will fail+ -- * give exactly right amount of bytes -> isolate+ -- will succeed if the given decoder succeeds+ -- * give too many bytes -> isolate will fail+ -- Here we generate Isolates that belong to the different+ -- buckets.+ let t = t0+ tooFewBytes n = do+ n' <- choose (0, n)+ return (n',t)+ requiredBytes n = return (n,t)+ tooManyBytes n = do+ n' <- choose (n+1, n+10)+ return (n+n',t)+ let trees+ | Just n <- actual_len' t = oneof $+ [ requiredBytes n ] +++ [ tooFewBytes n | genFail ] +++ [ tooManyBytes n | genFail ]+ | otherwise = return (max_len t, t)+ (n,t') <- trees+ return (Isolate n t')
+ tests/Arbitrary.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Arbitrary where++import Test.QuickCheck++import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+#if MIN_VERSION_bytestring(0,10,4)+import qualified Data.ByteString.Short as S+#endif++instance Arbitrary L.ByteString where+ arbitrary = fmap L.fromChunks arbitrary++instance Arbitrary B.ByteString where+ arbitrary = B.pack `fmap` arbitrary++#if MIN_VERSION_bytestring(0,10,4)+instance Arbitrary S.ShortByteString where+ arbitrary = S.toShort `fmap` arbitrary+#endif
+ tests/File.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE CPP #-}+module Main where++#if ! MIN_VERSION_base(4,8,0)+import Control.Applicative+#endif++import System.Directory (getTemporaryDirectory)+import System.FilePath ((</>))+import Test.HUnit++import Distribution.Simple.Utils (withTempDirectory)+import Distribution.Verbosity (silent)++import Data.Binary++data Foo = Bar !Word32 !Word32 !Word32 deriving (Eq, Show)++instance Binary Foo where+ get = Bar <$> get <*> get <*> get+ put (Bar a b c) = put (a,b,c)++exampleData :: [Foo]+exampleData = make bytes+ where+ make (a:b:c:xs) = Bar a b c : make xs+ make _ = []+ bytes = take (256*1024) (cycle [minBound..maxBound])++readWriteTest :: Test+readWriteTest = TestCase $ do+ tmpDir <- getTemporaryDirectory+ withTempDirectory silent tmpDir "foo-dir" $ \dir -> do+ let fn = dir </> "foo.bin"+ encodeFile fn exampleData+ content <- decodeFile fn+ -- It'd be nice to use lsof to verify that 'fn' isn't still open.+ exampleData @=? content++main :: IO ()+main = do + _ <- runTestTT readWriteTest+ return ()
+ tests/QC.hs view
@@ -0,0 +1,713 @@+{-# LANGUAGE CPP, ScopedTypeVariables, DataKinds, TypeSynonymInstances #-}+module Main ( main ) where++#if MIN_VERSION_base(4,8,0)+#define HAS_NATURAL+#endif++#if MIN_VERSION_base(4,7,0)+#define HAS_FIXED_CONSTRUCTOR+#endif++import Control.Applicative+import Control.Exception as C (SomeException,+ catch, evaluate)+import Control.Monad (unless, liftM2)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Internal as L+#if MIN_VERSION_bytestring(0,10,4)+import Data.ByteString.Short (ShortByteString)+#endif+import Data.Int+import Data.Ratio+import Data.Typeable+import System.IO.Unsafe++#ifdef HAS_NATURAL+import Numeric.Natural+#endif++import GHC.Fingerprint++import qualified Data.Fixed as Fixed++import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck hiding (total)++import qualified Action (tests)+import Arbitrary ()+import Data.Binary+import Data.Binary.Get+import Data.Binary.Put+import qualified Data.Binary.Class as Class++------------------------------------------------------------------------++roundTrip :: (Eq a, Binary a) => a -> (L.ByteString -> L.ByteString) -> Bool+roundTrip a f = a ==+ {-# SCC "decode.refragment.encode" #-} decode (f (encode a))++roundTripWith :: Eq a => (a -> Put) -> Get a -> a -> Property+roundTripWith putter getter x =+ forAll positiveList $ \xs ->+ x == runGet getter (refragment xs (runPut (putter x)))++-- make sure that a test fails+mustThrowError :: B a+mustThrowError a = unsafePerformIO $+ C.catch (do _ <- C.evaluate a+ return False)+ (\(_e :: SomeException) -> return True)++-- low level ones:+--+-- Words++prop_Word8 :: Word8 -> Property+prop_Word8 = roundTripWith putWord8 getWord8++prop_Word16be :: Word16 -> Property+prop_Word16be = roundTripWith putWord16be getWord16be++prop_Word16le :: Word16 -> Property+prop_Word16le = roundTripWith putWord16le getWord16le++prop_Word16host :: Word16 -> Property+prop_Word16host = roundTripWith putWord16host getWord16host++prop_Word32be :: Word32 -> Property+prop_Word32be = roundTripWith putWord32be getWord32be++prop_Word32le :: Word32 -> Property+prop_Word32le = roundTripWith putWord32le getWord32le++prop_Word32host :: Word32 -> Property+prop_Word32host = roundTripWith putWord32host getWord32host++prop_Word64be :: Word64 -> Property+prop_Word64be = roundTripWith putWord64be getWord64be++prop_Word64le :: Word64 -> Property+prop_Word64le = roundTripWith putWord64le getWord64le++prop_Word64host :: Word64 -> Property+prop_Word64host = roundTripWith putWord64host getWord64host++prop_Wordhost :: Word -> Property+prop_Wordhost = roundTripWith putWordhost getWordhost++-- Ints++prop_Int8 :: Int8 -> Property+prop_Int8 = roundTripWith putInt8 getInt8++prop_Int16be :: Int16 -> Property+prop_Int16be = roundTripWith putInt16be getInt16be++prop_Int16le :: Int16 -> Property+prop_Int16le = roundTripWith putInt16le getInt16le++prop_Int16host :: Int16 -> Property+prop_Int16host = roundTripWith putInt16host getInt16host++prop_Int32be :: Int32 -> Property+prop_Int32be = roundTripWith putInt32be getInt32be++prop_Int32le :: Int32 -> Property+prop_Int32le = roundTripWith putInt32le getInt32le++prop_Int32host :: Int32 -> Property+prop_Int32host = roundTripWith putInt32host getInt32host++prop_Int64be :: Int64 -> Property+prop_Int64be = roundTripWith putInt64be getInt64be++prop_Int64le :: Int64 -> Property+prop_Int64le = roundTripWith putInt64le getInt64le++prop_Int64host :: Int64 -> Property+prop_Int64host = roundTripWith putInt64host getInt64host++prop_Inthost :: Int -> Property+prop_Inthost = roundTripWith putInthost getInthost++-- Floats and Doubles++prop_Floatbe :: Float -> Property+prop_Floatbe = roundTripWith putFloatbe getFloatbe++prop_Floatle :: Float -> Property+prop_Floatle = roundTripWith putFloatle getFloatle++prop_Floathost :: Float -> Property+prop_Floathost = roundTripWith putFloathost getFloathost++prop_Doublebe :: Double -> Property+prop_Doublebe = roundTripWith putDoublebe getDoublebe++prop_Doublele :: Double -> Property+prop_Doublele = roundTripWith putDoublele getDoublele++prop_Doublehost :: Double -> Property+prop_Doublehost = roundTripWith putDoublehost getDoublehost++#if MIN_VERSION_base(4,10,0)+testTypeable :: Test+testTypeable = testProperty "TypeRep" prop_TypeRep++prop_TypeRep :: TypeRep -> Property+prop_TypeRep = roundTripWith Class.put Class.get++atomicTypeReps :: [TypeRep]+atomicTypeReps =+ [ typeRep (Proxy :: Proxy ())+ , typeRep (Proxy :: Proxy String)+ , typeRep (Proxy :: Proxy Int)+ , typeRep (Proxy :: Proxy (,))+ , typeRep (Proxy :: Proxy ((,) (Maybe Int)))+ , typeRep (Proxy :: Proxy Maybe)+ , typeRep (Proxy :: Proxy 'Nothing)+ , typeRep (Proxy :: Proxy 'Left)+ , typeRep (Proxy :: Proxy "Hello")+ , typeRep (Proxy :: Proxy 42)+ , typeRep (Proxy :: Proxy '[1,2,3,4])+ , typeRep (Proxy :: Proxy ('Left Int))+ , typeRep (Proxy :: Proxy (Either Int String))+ , typeRep (Proxy :: Proxy (() -> ()))+ ]++instance Arbitrary TypeRep where+ arbitrary = oneof (map pure atomicTypeReps)+#else+testTypeable :: Test+testTypeable = testGroup "Skipping Typeable tests" []+#endif++-- done, partial and fail++-- | Test partial results.+-- May or may not use the whole input, check conditions for the different+-- outcomes.+prop_partial :: L.ByteString -> Property+prop_partial lbs = forAll (choose (0, L.length lbs * 2)) $ \skipN ->+ let result = pushChunks (runGetIncremental decoder) lbs+ decoder = do+ s <- getByteString (fromIntegral skipN)+ return (L.fromChunks [s])+ in case result of+ Partial _ -> L.length lbs < skipN+ Done unused _pos value ->+ and [ L.length value == skipN+ , L.append value (L.fromChunks [unused]) == lbs+ ]+ Fail _ _ _ -> False++-- | Fail a decoder and make sure the result is sane.+prop_fail :: L.ByteString -> String -> Property+prop_fail lbs msg = forAll (choose (0, L.length lbs)) $ \pos ->+ let result = pushChunks (runGetIncremental decoder) lbs+ decoder = do+ -- use part of the input...+ _ <- getByteString (fromIntegral pos)+ -- ... then fail+ fail msg+ in case result of+ Fail unused pos' msg' ->+ and [ pos == pos'+ , msg == msg'+ , L.length lbs - pos == fromIntegral (B.length unused)+ , L.fromChunks [unused] `L.isSuffixOf` lbs+ ]+ _ -> False -- wuut?++-- read negative length+prop_getByteString_negative :: Int -> Property+prop_getByteString_negative n =+ n < 1 ==>+ runGet (getByteString n) L.empty == B.empty+++prop_bytesRead :: L.ByteString -> Property+prop_bytesRead lbs =+ forAll (makeChunks 0 totalLength) $ \chunkSizes ->+ let result = pushChunks (runGetIncremental decoder) lbs+ decoder = do+ -- Read some data and invoke bytesRead several times.+ -- Each time, check that the values are what we expect.+ flip mapM_ chunkSizes $ \(total, step) -> do+ _ <- getByteString (fromIntegral step)+ n <- bytesRead+ unless (n == total) $ fail "unexpected position"+ bytesRead+ in case result of+ Done unused pos value ->+ and [ value == totalLength+ , pos == value+ , B.null unused+ ]+ Partial _ -> False+ Fail _ _ _ -> False+ where+ totalLength = L.length lbs+ makeChunks total i+ | i == 0 = return []+ | otherwise = do+ n <- choose (0,i)+ let total' = total + n+ rest <- makeChunks total' (i - n)+ return ((total',n):rest)+++-- | We're trying to guarantee that the Decoder will not ask for more input+-- with Partial if it has been given Nothing once.+-- In this test we're making the decoder return 'Partial' to get more+-- input, and to get knownledge of the current position using 'BytesRead'.+-- Both of these operations, when used with the <|> operator, result internally+-- in that the decoder return with Partial and BytesRead multiple times,+-- in which case we need to keep track of if the user has passed Nothing to a+-- Partial in the past.+prop_partialOnlyOnce :: Property+prop_partialOnlyOnce = property $+ let result = runGetIncremental (decoder <|> decoder)+ decoder = do+ 0 <- bytesRead+ _ <- getWord8 -- this will make the decoder return with Partial+ return "shouldn't get here"+ in case result of+ -- we expect Partial followed by Fail+ Partial k -> case k Nothing of -- push down a Nothing+ Fail _ _ _ -> True+ Partial _ -> error $ "partial twice! oh noes!"+ Done _ _ _ -> error $ "we're not supposed to be done."+ _ -> error $ "not partial, error!"++-- read too much+prop_readTooMuch :: (Eq a, Binary a) => a -> Bool+prop_readTooMuch x = mustThrowError $ x == a && x /= b+ where+ -- encode 'a', but try to read 'b' too+ (a,b) = decode (encode x)+ _types = [a,b]++-- In binary-0.5 the Get monad looked like+--+-- > data S = S {-# UNPACK #-} !B.ByteString+-- > L.ByteString+-- > {-# UNPACK #-} !Int64+-- >+-- > newtype Get a = Get { unGet :: S -> (# a, S #) }+--+-- with a helper function+--+-- > mkState :: L.ByteString -> Int64 -> S+-- > mkState l = case l of+-- > L.Empty -> S B.empty L.empty+-- > L.Chunk x xs -> S x xs+--+-- Note that mkState is strict in its first argument. This goes wrong in this+-- function:+--+-- > getBytes :: Int -> Get B.ByteString+-- > getBytes n = do+-- > S s ss bytes <- traceNumBytes n $ get+-- > if n <= B.length s+-- > then do let (consume,rest) = B.splitAt n s+-- > put $! S rest ss (bytes + fromIntegral n)+-- > return $! consume+-- > else+-- > case L.splitAt (fromIntegral n) (s `join` ss) of+-- > (consuming, rest) ->+-- > do let now = B.concat . L.toChunks $ consuming+-- > put $ mkState rest (bytes + fromIntegral n)+-- > -- forces the next chunk before this one is returned+-- > if (B.length now < n)+-- > then+-- > fail "too few bytes"+-- > else+-- > return now+--+-- Consider the else-branch of this function; suppose we ask for n bytes;+-- the call to L.splitAt gives us a lazy bytestring 'consuming' of precisely @n@+-- bytes (unless we don't have enough data, in which case we fail); but then+-- the strict evaluation of mkState on 'rest' means we look ahead too far.+--+-- Although this is all done completely differently in binary-0.7 it is+-- important that the same bug does not get introduced in some other way. The+-- test is basically the same test that already exists in this test suite,+-- verifying that+--+-- > decode . refragment . encode == id+--+-- However, we use a different 'refragment', one that introduces an exception+-- as the tail of the bytestring after rechunking. If we don't look ahead too+-- far then this should make no difference, but if we do then this will throw+-- an exception (for instance, in binary-0.5, this will throw an exception for+-- certain rechunkings, but not for others).+--+-- To make sure that the property holds no matter what refragmentation we use,+-- we test exhaustively for a single chunk, and all ways to break the string+-- into 2, 3 and 4 chunks.+prop_lookAheadIndepOfChunking :: (Eq a, Binary a) => a -> Property+prop_lookAheadIndepOfChunking testInput =+ forAll (testCuts (L.length (encode testInput))) $+ roundTrip testInput . rechunk+ where+ testCuts :: forall a. (Num a, Enum a) => a -> Gen [a]+ testCuts len = elements $ [ [] ]+ ++ [ [i]+ | i <- [0 .. len] ]+ ++ [ [i, j]+ | i <- [0 .. len]+ , j <- [0 .. len - i] ]+ ++ [ [i, j, k]+ | i <- [0 .. len]+ , j <- [0 .. len - i]+ , k <- [0 .. len - i - j] ]++ -- Rechunk a bytestring, leaving the tail as an exception rather than Empty+ rechunk :: forall a. Integral a => [a] -> L.ByteString -> L.ByteString+ rechunk cuts = fromChunks . cut cuts . B.concat . L.toChunks+ where+ cut :: [a] -> B.ByteString -> [B.ByteString]+ cut [] bs = [bs]+ cut (i:is) bs = let (bs0, bs1) = B.splitAt (fromIntegral i) bs+ in bs0 : cut is bs1++ fromChunks :: [B.ByteString] -> L.ByteString+ fromChunks [] = error "Binary should not have to ask for this chunk!"+ fromChunks (bs:bss) = L.Chunk bs (fromChunks bss)++-- String utilities++prop_getLazyByteString :: L.ByteString -> Property+prop_getLazyByteString lbs = forAll (choose (0, 2 * L.length lbs)) $ \len ->+ let result = pushChunks (runGetIncremental decoder) lbs+ decoder = getLazyByteString len+ in case result of+ Done unused _pos value ->+ and [ value == L.take len lbs+ , L.fromChunks [unused] == L.drop len lbs+ ]+ Partial _ -> len > L.length lbs+ _ -> False++prop_getLazyByteStringNul :: Word16 -> [Int] -> Property+prop_getLazyByteStringNul count0 fragments = count >= 0 ==>+ forAll (choose (0, count)) $ \pos ->+ let lbs = case L.splitAt pos (L.replicate count 65) of+ (start,end) -> refragment fragments $ L.concat [start, L.singleton 0, end]+ result = pushEndOfInput $ pushChunks (runGetIncremental getLazyByteStringNul) lbs+ in case result of+ Done unused pos' value ->+ and [ value == L.take pos lbs+ , pos + 1 == pos' -- 1 for the NUL+ , L.fromChunks [unused] == L.drop (pos + 1) lbs+ ]+ _ -> False+ where+ count = fromIntegral count0 -- to make the generated numbers a bit smaller++-- | Same as prop_getLazyByteStringNul, but without any NULL in the string.+prop_getLazyByteStringNul_noNul :: Word16 -> [Int] -> Property+prop_getLazyByteStringNul_noNul count0 fragments = count >= 0 ==>+ let lbs = refragment fragments $ L.replicate count 65+ result = pushEndOfInput $ pushChunks (runGetIncremental getLazyByteStringNul) lbs+ in case result of+ Fail _ _ _ -> True+ _ -> False+ where+ count = fromIntegral count0 -- to make the generated numbers a bit smaller++prop_getRemainingLazyByteString :: L.ByteString -> Property+prop_getRemainingLazyByteString lbs = property $+ let result = pushEndOfInput $ pushChunks (runGetIncremental getRemainingLazyByteString) lbs+ in case result of+ Done unused pos value ->+ and [ value == lbs+ , B.null unused+ , fromIntegral pos == L.length lbs+ ]+ _ -> False++-- sanity:++invariant_lbs :: L.ByteString -> Bool+invariant_lbs (L.Empty) = True+invariant_lbs (L.Chunk x xs) = not (B.null x) && invariant_lbs xs++prop_invariant :: (Binary a) => a -> Bool+prop_invariant = invariant_lbs . encode++-- refragment a lazy bytestring's chunks+refragment :: [Int] -> L.ByteString -> L.ByteString+refragment [] lbs = lbs+refragment (x:xs) lbs =+ let x' = fromIntegral . (+1) . abs $ x+ rest = refragment xs (L.drop x' lbs) in+ L.append (L.fromChunks [B.concat . L.toChunks . L.take x' $ lbs]) rest++-- check identity of refragmentation+prop_refragment :: L.ByteString -> [Int] -> Bool+prop_refragment lbs xs = lbs == refragment xs lbs++-- check that refragmention still hold invariant+prop_refragment_inv :: L.ByteString -> [Int] -> Bool+prop_refragment_inv lbs xs = invariant_lbs $ refragment xs lbs++main :: IO ()+main = defaultMain tests++------------------------------------------------------------------------++genInteger :: Gen Integer+genInteger = do+ b <- arbitrary+ if b then genIntegerSmall else genIntegerSmall++genIntegerSmall :: Gen Integer+genIntegerSmall = arbitrary++genIntegerBig :: Gen Integer+genIntegerBig = do+ x <- arbitrarySizedIntegral :: Gen Integer+ -- arbitrarySizedIntegral generates numbers smaller than+ -- (maxBound :: Word32), so let's make them bigger to better test+ -- the Binary instance.+ return (x + fromIntegral (maxBound :: Word32))++#ifdef HAS_NATURAL+genNatural :: Gen Natural+genNatural = do+ b <- arbitrary+ if b then genNaturalSmall else genNaturalBig++genNaturalSmall :: Gen Natural+genNaturalSmall = arbitrarySizedNatural++genNaturalBig :: Gen Natural+genNaturalBig = do+ x <- arbitrarySizedNatural :: Gen Natural+ -- arbitrarySizedNatural generates numbers smaller than+ -- (maxBound :: Word64), so let's make them bigger to better test+ -- the Binary instance.+ return (x + fromIntegral (maxBound :: Word64))+#endif++------------------------------------------------------------------------++genFingerprint :: Gen Fingerprint+genFingerprint = liftM2 Fingerprint arbitrary arbitrary+#if !MIN_VERSION_base(4,7,0)+instance Show Fingerprint where+ show (Fingerprint x1 x2) = show (x1,x2)+#endif++------------------------------------------------------------------------++#ifdef HAS_FIXED_CONSTRUCTOR++fixedPut :: forall a. Fixed.HasResolution a => Fixed.Fixed a -> Put+fixedPut x = put (truncate (x * fromInteger (Fixed.resolution (undefined :: Maybe a))) :: Integer)++fixedGet :: forall a. Fixed.HasResolution a => Get (Fixed.Fixed a)+fixedGet = (\x -> fromInteger x / fromInteger (Fixed.resolution (undefined :: Maybe a))) `liftA` get++-- | Serialise using base >=4.7 and <4.7 methods agree+prop_fixed_ser :: Fixed.Fixed Fixed.E3 -> Bool+prop_fixed_ser x = runPut (put x) == runPut (fixedPut x)++-- | Serialised with base >=4.7, unserialised with base <4.7 method roundtrip+prop_fixed_constr_resolution :: Fixed.Fixed Fixed.E3 -> Bool+prop_fixed_constr_resolution x = runGet fixedGet (runPut (put x)) == x++-- | Serialised with base <4.7, unserialised with base >=4.7 method roundtrip+prop_fixed_resolution_constr :: Fixed.Fixed Fixed.E3 -> Bool+prop_fixed_resolution_constr x = runGet get (runPut (fixedPut x)) == x++#endif++------------------------------------------------------------------------++type T a = a -> Property+type B a = a -> Bool++p :: (Testable p) => p -> Property+p = property++test :: (Eq a, Binary a) => a -> Property+test a = forAll positiveList (roundTrip a . refragment)++test' :: (Show a, Arbitrary a) => String -> (a -> Property) -> ([a] -> Property) -> Test+test' desc prop propList =+ testGroup desc [+ testProperty desc prop,+ testProperty ("[" ++ desc ++ "]") propList+ ]++testWithGen :: (Show a, Eq a, Binary a) => String -> Gen a -> Test+testWithGen desc gen =+ testGroup desc [+ testProperty desc (forAll gen test),+ testProperty ("[" ++ desc ++ "]") (forAll (listOf gen) test)+ ]++positiveList :: Gen [Int]+positiveList = fmap (filter (/=0) . map abs) $ arbitrary++tests :: [Test]+tests =+ [ testGroup "Utils"+ [ testProperty "refragment id" (p prop_refragment)+ , testProperty "refragment invariant" (p prop_refragment_inv)+ ]++ , testGroup "Boundaries"+ [ testProperty "read to much" (p (prop_readTooMuch :: B Word8))+ , testProperty "read negative length" (p (prop_getByteString_negative :: T Int))+ , -- Arbitrary test input+ let testInput :: [Int] ; testInput = [0 .. 10]+ in testProperty "look-ahead independent of chunking" (p (prop_lookAheadIndepOfChunking testInput))+ ]++ , testGroup "Partial"+ [ testProperty "partial" (p prop_partial)+ , testProperty "fail" (p prop_fail)+ , testProperty "bytesRead" (p prop_bytesRead)+ , testProperty "partial only once" (p prop_partialOnlyOnce)+ ]++ , testGroup "Model"+ Action.tests++ , testGroup "Primitives"+ [ testProperty "Word8" (p prop_Word8)+ , testProperty "Word16be" (p prop_Word16be)+ , testProperty "Word16le" (p prop_Word16le)+ , testProperty "Word16host" (p prop_Word16host)+ , testProperty "Word32be" (p prop_Word32be)+ , testProperty "Word32le" (p prop_Word32le)+ , testProperty "Word32host" (p prop_Word32host)+ , testProperty "Word64be" (p prop_Word64be)+ , testProperty "Word64le" (p prop_Word64le)+ , testProperty "Word64host" (p prop_Word64host)+ , testProperty "Wordhost" (p prop_Wordhost)+ -- Int+ , testProperty "Int8" (p prop_Int8)+ , testProperty "Int16be" (p prop_Int16be)+ , testProperty "Int16le" (p prop_Int16le)+ , testProperty "Int16host" (p prop_Int16host)+ , testProperty "Int32be" (p prop_Int32be)+ , testProperty "Int32le" (p prop_Int32le)+ , testProperty "Int32host" (p prop_Int32host)+ , testProperty "Int64be" (p prop_Int64be)+ , testProperty "Int64le" (p prop_Int64le)+ , testProperty "Int64host" (p prop_Int64host)+ , testProperty "Inthost" (p prop_Inthost)+ -- Float/Double+ , testProperty "Floatbe" (p prop_Floatbe)+ , testProperty "Floatle" (p prop_Floatle)+ , testProperty "Floathost" (p prop_Floathost)+ , testProperty "Doublebe" (p prop_Doublebe)+ , testProperty "Doublele" (p prop_Doublele)+ , testProperty "Doublehost" (p prop_Doublehost)+ ]++ , testGroup "String utils"+ [ testProperty "getLazyByteString" prop_getLazyByteString+ , testProperty "getLazyByteStringNul" prop_getLazyByteStringNul+ , testProperty "getLazyByteStringNul No Null" prop_getLazyByteStringNul_noNul+ , testProperty "getRemainingLazyByteString" prop_getRemainingLazyByteString+ ]++ , testGroup "Using Binary class, refragmented ByteString"+ [ test' "()" (test :: T () ) test+ , test' "Bool" (test :: T Bool ) test+ , test' "Char" (test :: T Char ) test+ , test' "Ordering" (test :: T Ordering ) test+ , test' "Ratio Int" (test :: T (Ratio Int)) test++ , test' "Word" (test :: T Word ) test+ , test' "Word8" (test :: T Word8 ) test+ , test' "Word16" (test :: T Word16) test+ , test' "Word32" (test :: T Word32) test+ , test' "Word64" (test :: T Word64) test++ , test' "Int" (test :: T Int ) test+ , test' "Int8" (test :: T Int8 ) test+ , test' "Int16" (test :: T Int16) test+ , test' "Int32" (test :: T Int32) test+ , test' "Int64" (test :: T Int64) test++ , testWithGen "Integer mixed" genInteger+ , testWithGen "Integer small" genIntegerSmall+ , testWithGen "Integer big" genIntegerBig++ , test' "Fixed" (test :: T (Fixed.Fixed Fixed.E3) ) test+#ifdef HAS_NATURAL+ , testWithGen "Natural mixed" genNatural+ , testWithGen "Natural small" genNaturalSmall+ , testWithGen "Natural big" genNaturalBig+#endif+ , testWithGen "GHC.Fingerprint" genFingerprint++ , test' "Float" (test :: T Float ) test+ , test' "Double" (test :: T Double) test++ , test' "((), ())" (test :: T ((), ()) ) test+ , test' "(Word8, Word32)" (test :: T (Word8, Word32) ) test+ , test' "(Int8, Int32)" (test :: T (Int8, Int32) ) test+ , test' "(Int32, [Int])" (test :: T (Int32, [Int]) ) test+ , test' "Maybe Int8" (test :: T (Maybe Int8) ) test+ , test' "Either Int8 Int16" (test :: T (Either Int8 Int16) ) test++ , test' "(Int, ByteString)"+ (test :: T (Int, B.ByteString) ) test+ , test' "[(Int, ByteString)]"+ (test :: T [(Int, B.ByteString)] ) test++ , test' "(Maybe Int64, Bool, [Int])"+ (test :: T (Maybe Int64, Bool, [Int])) test+ , test' "(Maybe Word8, Bool, [Int], Either Bool Word8)"+ (test :: T (Maybe Word8, Bool, [Int], Either Bool Word8)) test+ , test' "(Maybe Word16, Bool, [Int], Either Bool Word16, Int)"+ (test :: T (Maybe Word16, Bool, [Int], Either Bool Word16, Int)) test++ , test' "(Int,Int,Int,Int,Int,Int)"+ (test :: T (Int,Int,Int,Int,Int,Int)) test+ , test' "(Int,Int,Int,Int,Int,Int,Int)"+ (test :: T (Int,Int,Int,Int,Int,Int,Int)) test+ , test' "(Int,Int,Int,Int,Int,Int,Int,Int)"+ (test :: T (Int,Int,Int,Int,Int,Int,Int,Int)) test+ , test' "(Int,Int,Int,Int,Int,Int,Int,Int,Int)"+ (test :: T (Int,Int,Int,Int,Int,Int,Int,Int,Int)) test+ , test' "(Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)"+ (test :: T (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)) test++ , test' "B.ByteString" (test :: T B.ByteString) test+ , test' "L.ByteString" (test :: T L.ByteString) test+#if MIN_VERSION_bytestring(0,10,4)+ , test' "ShortByteString" (test :: T ShortByteString) test+#endif+ ]++ , testGroup "Invariants" $ map (uncurry testProperty)+ [ ("B.ByteString invariant", p (prop_invariant :: B B.ByteString ))+ , ("[B.ByteString] invariant", p (prop_invariant :: B [B.ByteString] ))+ , ("L.ByteString invariant", p (prop_invariant :: B L.ByteString ))+ , ("[L.ByteString] invariant", p (prop_invariant :: B [L.ByteString] ))+#if MIN_VERSION_bytestring(0,10,4)+ , ("ShortByteString invariant", p (prop_invariant :: B ShortByteString ))+ , ("[ShortByteString] invariant", p (prop_invariant :: B [ShortByteString] ))+#endif+ ]+#ifdef HAS_FIXED_CONSTRUCTOR+ , testGroup "Fixed"+ [ testProperty "Serialisation same" $ p prop_fixed_ser+ , testProperty "MkFixed -> HasResolution" $ p prop_fixed_constr_resolution+ , testProperty "HasResolution -> MkFixed" $ p prop_fixed_resolution_constr+ ]+#endif+ , testTypeable+ ]
+ tools/derive/BinaryDerive.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE ScopedTypeVariables #-}++module BinaryDerive where++import Data.Generics+import Data.List++deriveM :: (Typeable a, Data a) => a -> IO ()+deriveM (a :: a) = mapM_ putStrLn . lines $ derive (undefined :: a)++derive :: (Typeable a, Data a) => a -> String+derive x = + "instance " ++ context ++ "Binary " ++ inst ++ " where\n" +++ concat putDefs ++ getDefs+ where+ context+ | nTypeChildren > 0 =+ wrap (join ", " (map ("Binary "++) typeLetters)) ++ " => "+ | otherwise = ""+ inst = wrap $ tyConName typeName ++ concatMap (" "++) typeLetters+ wrap x = if nTypeChildren > 0 then "("++x++")" else x + join sep lst = concat $ intersperse sep lst+ nTypeChildren = length typeChildren+ typeLetters = take nTypeChildren manyLetters+ manyLetters = map (:[]) ['a'..'z']+ (typeName,typeChildren) = splitTyConApp (typeOf x)+ constrs :: [(Int, (String, Int))]+ constrs = zip [0..] $ map gen $ dataTypeConstrs (dataTypeOf x)+ gen con = ( showConstr con+ , length $ gmapQ undefined $ fromConstr con `asTypeOf` x+ )+ putDefs = map ((++"\n") . putDef) constrs+ putDef (n, (name, ps)) =+ let wrap = if ps /= 0 then ("("++) . (++")") else id+ pattern = name ++ concatMap (' ':) (take ps manyLetters)+ in+ " put " ++ wrap pattern ++" = "+ ++ concat [ "putWord8 " ++ show n | length constrs > 1 ]+ ++ concat [ " >> " | length constrs > 1 && ps > 0 ]+ ++ concat [ "return ()" | length constrs == 1 && ps == 0 ]+ ++ join " >> " (map ("put "++) (take ps manyLetters))+ getDefs =+ (if length constrs > 1+ then " get = do\n tag_ <- getWord8\n case tag_ of\n"+ else " get =")+ ++ concatMap ((++"\n")) (map getDef constrs) +++ (if length constrs > 1+ then " _ -> fail \"no decoding\""+ else ""+ )+ getDef (n, (name, ps)) =+ let wrap = if ps /= 0 then ("("++) . (++")") else id+ in+ concat [ " " ++ show n ++ " ->" | length constrs > 1 ]+ ++ concatMap (\x -> " get >>= \\"++x++" ->") (take ps manyLetters)+ ++ " return "+ ++ wrap (name ++ concatMap (" "++) (take ps manyLetters))
+ tools/derive/Example.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE DeriveDataTypeable #-}++import Data.Generics++import Data.Binary++import BinaryDerive++data Foo = Bar+ deriving (Typeable, Data, Show, Eq)++instance Binary Main.Foo where+ put Bar = return ()+ get = return Bar++data Color = RGB Int Int Int+ | CMYK Int Int Int Int+ deriving (Typeable, Data, Show, Eq)++instance Binary Main.Color where+ put (RGB a b c) = putWord8 0 >> put a >> put b >> put c+ put (CMYK a b c d) = putWord8 1 >> put a >> put b >> put c >> put d+ get = do+ tag_ <- getWord8+ case tag_ of+ 0 -> get >>= \a -> get >>= \b -> get >>= \c -> return (RGB a b c)+ 1 -> get >>= \a -> get >>= \b -> get >>= \c -> get >>= \d -> return (CMYK a b c d)++data Computer = Laptop { weight :: Int }+ | Desktop { speed :: Int, memory :: Int }+ deriving (Typeable, Data, Show, Eq)++instance Binary Main.Computer where+ put (Laptop a) = putWord8 0 >> put a+ put (Desktop a b) = putWord8 1 >> put a >> put b+ get = do+ tag_ <- getWord8+ case tag_ of+ 0 -> get >>= \a -> return (Laptop a)+ 1 -> get >>= \a -> get >>= \b -> return (Desktop a b)++data Exp = ExpOr Exp Exp+ | ExpAnd Exp Exp+ | ExpEq Exp Exp+ | ExpNEq Exp Exp+ | ExpAdd Exp Exp+ | ExpSub Exp Exp+ | ExpVar String+ | ExpInt Int+ deriving (Typeable, Data, Show, Eq)++instance Binary Main.Exp where+ put (ExpOr a b) = putWord8 0 >> put a >> put b+ put (ExpAnd a b) = putWord8 1 >> put a >> put b+ put (ExpEq a b) = putWord8 2 >> put a >> put b+ put (ExpNEq a b) = putWord8 3 >> put a >> put b+ put (ExpAdd a b) = putWord8 4 >> put a >> put b+ put (ExpSub a b) = putWord8 5 >> put a >> put b+ put (ExpVar a) = putWord8 6 >> put a+ put (ExpInt a) = putWord8 7 >> put a+ get = do+ tag_ <- getWord8+ case tag_ of+ 0 -> get >>= \a -> get >>= \b -> return (ExpOr a b)+ 1 -> get >>= \a -> get >>= \b -> return (ExpAnd a b)+ 2 -> get >>= \a -> get >>= \b -> return (ExpEq a b)+ 3 -> get >>= \a -> get >>= \b -> return (ExpNEq a b)+ 4 -> get >>= \a -> get >>= \b -> return (ExpAdd a b)+ 5 -> get >>= \a -> get >>= \b -> return (ExpSub a b)+ 6 -> get >>= \a -> return (ExpVar a)+ 7 -> get >>= \a -> return (ExpInt a)+ _ -> fail "no decoding"