@@ -9,6 +9,7 @@ import Data.Binary.Put ( runPut )
99import Data.Binary.Bits
1010import Data.Binary.Bits.Get
1111import Data.Binary.Bits.Put
12+import Data.Binary.Bits.BitOrder
1213
1314import qualified Data.ByteString as B
1415import qualified Data.ByteString.Lazy as L
@@ -17,7 +18,8 @@ import Control.Applicative
1718import Data.Bits
1819import Data.Word
1920import Foreign.Storable
20−import System.Random
21+import Data.Traversable (traverse)
22+import Data.Foldable (traverse_)
2123
2224import Test.Framework.Providers.QuickCheck2 ( testProperty )
2325import Test.Framework.Runners.Console ( defaultMain )
@@ -36,7 +38,10 @@ tests =
3638 [ testProperty "prop_composite_case" prop_composite_case ]
3739
3840 , testGroup "getByteString"
39− [ testProperty "prop_getByteString_negative" prop_getByteString_negative ]
41+ [ testProperty "prop_getByteString_negative" prop_getByteString_negative
42+ , testProperty "prop_putByteString_getByteString" (prop_putByteString_getByteString :: BitOrder -> B.ByteString -> Property)
43+ , testProperty "prop_putByteString_getByteString_many" (prop_putByteString_getByteString_many :: BitOrder -> [B.ByteString] -> Property)
44+ ]
4045
4146 , testGroup "getLazyByteString"
4247 [ testProperty "getLazyByteString == getByteString"
@@ -84,10 +89,10 @@ tests =
8489 ]
8590
8691 , testGroup "prop_put_with_bitreq"
87− [ testProperty "Word8" (prop_putget_with_bitreq :: W Word8 -> Property)
88− , testProperty "Word16" (prop_putget_with_bitreq :: W Word16 -> Property)
89− , testProperty "Word32" (prop_putget_with_bitreq :: W Word32 -> Property)
90− , testProperty "Word64" (prop_putget_with_bitreq :: W Word64 -> Property)
92+ [ testProperty "Word8" (prop_putget_with_bitreq :: BitOrder -> W Word8 -> Property)
93+ , testProperty "Word16" (prop_putget_with_bitreq :: BitOrder -> W Word16 -> Property)
94+ , testProperty "Word32" (prop_putget_with_bitreq :: BitOrder -> W Word32 -> Property)
95+ , testProperty "Word64" (prop_putget_with_bitreq :: BitOrder -> W Word64 -> Property)
9196 ]
9297
9398 , testGroup "prop_putget_list_simple"
@@ -112,10 +117,10 @@ tests =
112117 , testProperty "Word64" (prop_putget_list_with_bitreq :: W [Word64] -> Property)
113118 ]
114119 , testGroup "prop_bitget_bytestring_interspersed"
115− [ testProperty "Word8" (prop_bitget_bytestring_interspersed :: W Word8 -> [B.ByteString] -> Property)
116− , testProperty "Word16" (prop_bitget_bytestring_interspersed :: W Word16 -> [B.ByteString] -> Property)
117− , testProperty "Word32" (prop_bitget_bytestring_interspersed :: W Word32 -> [B.ByteString] -> Property)
118− , testProperty "Word64" (prop_bitget_bytestring_interspersed :: W Word64 -> [B.ByteString] -> Property)
120+ [ testProperty "Word8" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word8 -> [B.ByteString] -> Property)
121+ , testProperty "Word16" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word16 -> [B.ByteString] -> Property)
122+ , testProperty "Word32" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word32 -> [B.ByteString] -> Property)
123+ , testProperty "Word64" (prop_bitget_bytestring_interspersed :: BitOrder -> W Word64 -> [B.ByteString] -> Property)
119124 ]
120125 , testGroup "Simulate programs"
121126 [ testProperty "primitive" prop_primitive
@@ -158,13 +163,28 @@ prop_getByteString_negative n =
158163 n < 1 ==>
159164 runGet (runBitGet (getByteString n)) L.empty == B.empty
160165
161−prop_putget_with_bitreq :: (BinaryBit a, Num a, Bits a, Ord a) => W a -> Property
162−prop_putget_with_bitreq (W w) = property $
166+prop_putByteString_getByteString :: BitOrder -> B.ByteString -> Property
167+prop_putByteString_getByteString bo bs = property $ bs' == bs
168+ where
169+ n = B.length bs
170+ w = runPut (runBitPut (withBitOrder bo (putByteString bs)))
171+ bs' = runGet (runBitGet (withBitOrder bo (getByteString n))) w
172+
173+prop_putByteString_getByteString_many :: BitOrder -> [B.ByteString] -> Property
174+prop_putByteString_getByteString_many bo bs = property $ bs' == bs
175+ where
176+ n = fmap B.length bs
177+ w = runPut (runBitPut (withBitOrder bo (traverse_ putByteString bs)))
178+ bs' = runGet (runBitGet (withBitOrder bo (traverse getByteString n))) w
179+
180+
181+prop_putget_with_bitreq :: (BinaryBit a, Num a, Bits a, Ord a) => BitOrder -> W a -> Property
182+prop_putget_with_bitreq bo (W w) = property $
163183 -- write all words with as many bits as it's required
164184 let p = putBits (bitreq w) w
165185 g = getBits (bitreq w)
166− lbs = runPut (runBitPut p)
167− w' = runGet (runBitGet g) lbs
186+ lbs = runPut (runBitPut (withBitOrder bo p))
187+ w' = runGet (runBitGet (withBitOrder bo g)) lbs
168188 in w == w'
169189
170190-- | Write a list of items. Each item is written with the maximum amount of
@@ -226,12 +246,12 @@ prop_bitget_with_put_from_binary (W ws) = property $
226246 in ws == ws'
227247
228248-- | Write each 'ByteString' with a variable sized value as a separator.
229−prop_bitget_bytestring_interspersed :: (BinaryBit a, Binary a, Num a, Ord a, Bits a) => W a -> [B.ByteString] -> Property
230−prop_bitget_bytestring_interspersed (W ws) bss = property $
249+prop_bitget_bytestring_interspersed :: (BinaryBit a, Binary a, Num a, Ord a, Bits a) => BitOrder -> W a -> [B.ByteString] -> Property
250+prop_bitget_bytestring_interspersed bo (W ws) bss = property $
231251 let p = mapM_ (\bs -> putBits (bitreq ws) ws >> putByteString bs) bss
232252 g = mapM (\bs -> (,) <$> getBits (bitreq ws) <*> getByteString (B.length bs)) bss
233− lbs = runPut (runBitPut p)
234− r = runGet (runBitGet g) lbs
253+ lbs = runPut (runBitPut (withBitOrder bo p))
254+ r = runGet (runBitGet (withBitOrder bo g)) lbs
235255 in map (ws,) bss == r
236256
237257-- | Test failing.
@@ -241,8 +261,8 @@ prop_fail lbs errMsg0 = forAll (choose (0, 8 * L.length lbs)) $ \len ->
241261 expectedBytesConsumed
242262 | bits == 0 = bytes
243263 | otherwise = bytes + 1
244− p = do getByteString (fromIntegral bytes)
245− getBits (fromIntegral bits) :: BitGet Word8
264+ p = do _ <- getByteString (fromIntegral bytes)
265+ _ <- getBits (fromIntegral bits) :: BitGet Word8
246266 fail errMsg0
247267 r = runGetIncremental (runBitGet p) `pushChunks` lbs
248268 in case r of
@@ -268,7 +288,7 @@ prop_bitreq (W w) = property $
268288prop_composite_case :: Bool -> W Word16 -> Property
269289prop_composite_case b (W w) = w < 0x8000 ==>
270290 let p = do putBool b
271− putWord16be 15 w
291+ putWord16 15 w
272292 g = do v <- getBool
273293 case v of
274294 True -> getWord16 15
@@ -374,11 +394,6 @@ instance (Arbitrary (W a), Arbitrary (W b), Arbitrary (W c)) => Arbitrary (W (a,
374394 arbitrary = ((W .) .) . (,,) <$> arbitraryW <*> arbitraryW <*> arbitraryW
375395 shrink (W (a,b,c)) = ((W .) .) . (,,) <$> shrinkW a <*> shrinkW b <*> shrinkW c
376396
377−integralRandomR :: (Integral a, RandomGen g) => (a,a) -> g -> (a,g)
378−integralRandomR (a,b) g = case randomR (fromIntegral a :: Integer,
379− fromIntegral b :: Integer) g of
380− (x,g) -> (fromIntegral x, g)
381−
382397data Primitive
383398 = Bool Bool
384399 | W8 Int Word8
@@ -387,6 +402,7 @@ data Primitive
387402 | W64 Int Word64
388403 | BS Int B.ByteString
389404 | LBS Int L.ByteString
405+ | Skip Int
390406 | IsEmpty
391407 deriving (Eq, Show)
392408
@@ -405,6 +421,7 @@ instance Arbitrary Primitive where
405421 , gen W16
406422 , gen W32
407423 , gen W64
424+ , Skip <$> choose (0, 3000)
408425 , do n <- choose (0,10)
409426 cs <- vector n
410427 return (BS n (B.pack cs))
@@ -421,10 +438,19 @@ instance Arbitrary Primitive where
421438 W16 _ x -> snk W16 x
422439 W32 _ x -> snk W32 x
423440 W64 _ x -> snk W64 x
441+ Skip x -> Skip <$> shrink x
424442 BS _ bs -> let ws = B.unpack bs in map (\ws' -> BS (length ws') (B.pack ws')) (shrink ws)
425443 LBS _ lbs -> let ws = L.unpack lbs in map (\ws' -> LBS (length ws') (L.pack ws')) (shrink ws)
426444 IsEmpty -> []
427445
446+instance Arbitrary BitOrder where
447+ arbitrary = elements [BB, LB, LL, BL]
448+ shrink LL = [BB,LB,BL]
449+ shrink BL = [BB,LB]
450+ shrink LB = [BB]
451+ shrink BB = []
452+
453+
428454prop_primitive :: Primitive -> Property
429455prop_primitive prim = property $
430456 let p = putPrimitive prim
@@ -446,9 +472,10 @@ putPrimitive p =
446472 case p of
447473 Bool b -> putBool b
448474 W8 n x -> putWord8 n x
449− W16 n x -> putWord16be n x
450− W32 n x -> putWord32be n x
451− W64 n x -> putWord64be n x
475+ W16 n x -> putWord16 n x
476+ W32 n x -> putWord32 n x
477+ W64 n x -> putWord64 n x
478+ Skip n -> skipBits n
452479 BS _ bs -> putByteString bs
453480 LBS _ lbs -> mapM_ putByteString (L.toChunks lbs)
454481 IsEmpty -> return ()
@@ -461,10 +488,22 @@ getPrimitive p =
461488 W16 n _ -> W16 n <$> getWord16 n
462489 W32 n _ -> W32 n <$> getWord32 n
463490 W64 n _ -> W64 n <$> getWord64 n
491+ Skip n -> skipBits n >> return (Skip n)
464492 BS n _ -> BS n <$> getByteString n
465493 LBS n _ -> LBS n <$> getLazyByteString n
466494 IsEmpty -> isEmpty >> return IsEmpty
467495
496+getPrimitiveSize :: Primitive -> Int
497+getPrimitiveSize p = case p of
498+ Bool _ -> 1
499+ W8 n _ -> n
500+ W16 n _ -> n
501+ W32 n _ -> n
502+ W64 n _ -> n
503+ Skip n -> n
504+ BS n _ -> n*8
505+ LBS n _ -> n*8
506+ IsEmpty -> 0
468507
469508verifyProgram :: Int -> Program -> BitGet Bool
470509verifyProgram totalLength ps0 = go 0 ps0
@@ -472,13 +511,6 @@ verifyProgram totalLength ps0 = go 0 ps0
472511 go _ [] = return True
473512 go pos (p:ps) =
474513 case p of
475− Bool x -> check x getBool >> go (pos+1) ps
476− W8 n x -> check x (getWord8 n) >> go (pos+n) ps
477− W16 n x -> check x (getWord16 n) >> go (pos+n) ps
478− W32 n x -> check x (getWord32 n) >> go (pos+n) ps
479− W64 n x -> check x (getWord64 n) >> go (pos+n) ps
480− BS n x -> check x (getByteString n) >> go (pos+(8*n)) ps
481− LBS n x -> check x (getLazyByteString n) >> go (pos+(8*n)) ps
482514 IsEmpty -> do
483515 let expected = pos == totalLength
484516 actual <- isEmpty
@@ -486,6 +518,7 @@ verifyProgram totalLength ps0 = go 0 ps0
486518 then go pos ps
487519 else error $ "isEmpty returned wrong value, expected "
488520 ++ show expected ++ " but got " ++ show actual
521+ _ -> check p (getPrimitive p) >> go (pos + getPrimitiveSize p) ps
489522 check x g = do
490523 y <- g
491524 if x == y