on master help sign in
Add some utility functions, improve the test suite with some random tests
7cca4dfLennart Kolmodin
Bits.hs+57−21
@@ -37,8 +37,12 @@ data R a where
37 RWord8 :: Int -> R Word8
37 RWord8 :: Int -> R Word8
38 RWord16be :: Int -> R Word16
38 RWord16be :: Int -> R Word16
39 RWord32be :: Int -> R Word32
39 RWord32be :: Int -> R Word32
40+ RByteString :: Int -> R ByteString
41+ RThis :: a -> R a
40 RNextTo :: R a -> R b -> R (T a b)
42 RNextTo :: R a -> R b -> R (T a b)
43+ RMap :: R b -> (b -> R a) -> R a
44+ RMapPure :: R a -> (a -> b) -> R b
41− RList :: R a -> Int -> R [a]
45+ RList :: Int -> R a -> R [a]
42 RCheck :: R a -> (a -> Bool) -> String -> R a
46 RCheck :: R a -> (a -> Bool) -> String -> R a
43
47
44data T a b = !a :*: !b deriving (Show)
48data T a b = !a :*: !b deriving (Show)
@@ -54,8 +58,11 @@ size_in_bits r = case r of
54 RWord8 n -> min n 8
58 RWord8 n -> min n 8
55 RWord16be n -> min n 16
59 RWord16be n -> min n 16
56 RWord32be n -> min n 16
60 RWord32be n -> min n 16
61+ RByteString n -> 8 * n
62+ RThis _ -> 0
57− RList r n -> n * size_in_bits r
63+ RList n r -> n * size_in_bits r
58− --RCase r cs -> size_in_bits r + maximum (map size_in_bits cs)
64+ RMap r _ -> size_in_bits r
65+ RMapPure r _ -> size_in_bits r
59 RCheck r _ _ -> size_in_bits r
66 RCheck r _ _ -> size_in_bits r
60 RNextTo a b -> size_in_bits a + size_in_bits b
67 RNextTo a b -> size_in_bits a + size_in_bits b
61
68
@@ -65,7 +72,7 @@ data S = S !ByteString -- ^ Input
65
72
66size_in_bytes :: forall a. R a
73size_in_bytes :: forall a. R a
67 -> Int
74 -> Int
68−size_in_bytes r = (size_in_bits r + 7) `div` 8
75+size_in_bytes r = byte_offset (size_in_bits r + 7)
69
76
70get :: R a -> Get a
77get :: R a -> Get a
71get r = do
78get r = do
@@ -73,17 +80,29 @@ get r = do
73 a :*: s <- getS (S bs 0) r
80 a :*: s <- getS (S bs 0) r
74 return a
81 return a
75
82
76−getS :: Monad m => S -> R a -> m (T a S)
83+getS :: S -> R a -> Get (T a S)
77getS s0 r = do
84getS s0 r = do
78 case r of
85 case r of
79 RBool -> return (readBool s0)
86 RBool -> return (readBool s0)
80 RWord8 n -> return (readWord8 s0 n)
87 RWord8 n -> return (readWord8 s0 n)
81 RWord16be n -> return (readWord16be s0 n)
88 RWord16be n -> return (readWord16be s0 n)
89+ RWord32be n -> return (readWord32be s0 n)
90+ RByteString n -> return (readByteString s0 n)
91+ RThis x -> return (x :*: s0)
82 RNextTo a b -> do
92 RNextTo a b -> do
83 t :*: s <- getS s0 a
93 t :*: s <- getS s0 a
84 u :*: s' <- getS s b
94 u :*: s' <- getS s b
85 return (t:*:u:*:s')
95 return (t:*:u:*:s')
96+ RMap r p -> do
97+ a :*: s@(S bs o) <- getS s0 r
98+ let codepath = p a
99+ required_bytes = byte_offset (size_in_bits r - o)
100+ bs' <- getByteString required_bytes
101+ getS (S (bs `append` bs') o) codepath
102+ RMapPure r f -> do
103+ a :*: s <- getS s0 r
104+ return (f a :*: s)
86− RList r n ->
105+ RList n r ->
87 let loop 0 s acc = return (List.reverse acc :*: s)
106 let loop 0 s acc = return (List.reverse acc :*: s)
88 loop m s acc = do
107 loop m s acc = do
89 a :*: s' <- getS s r
108 a :*: s' <- getS s r
@@ -118,6 +137,12 @@ incS (S bs n) o =
118 let (d,n') = divMod (n+o) 8
137 let (d,n') = divMod (n+o) 8
119 in S (unsafeDrop d bs) n'
138 in S (unsafeDrop d bs) n'
120
139
140+
141+readByteString :: S -> Int -> T ByteString S
142+readByteString s@(S bs o) n
143+ | o == 0 = unsafeTake n bs :*: (S (unsafeDrop n bs) 0)
144+ | otherwise = unsafeTake n (unsafeTail bs) :*: (S (unsafeDrop (n+1) bs) 0)
145+
121readWord8 :: S -> Int -> T Word8 S
146readWord8 :: S -> Int -> T Word8 S
122readWord8 s@(S bs o) n
147readWord8 s@(S bs o) n
123 -- no bits at all, return 0
148 -- no bits at all, return 0
@@ -166,24 +191,31 @@ readWord16be s@(S bs o) n
166 in w :*: incS s n
191 in w :*: incS s n
167
192
168 -- with offset, and n=9-16
193 -- with offset, and n=9-16
169− | n <= 16 = let bits_in_msb = 8 - o
194+ | n <= 16 = readWithOffset s shiftl_w16 shiftr_w16 n
170− (n',top) = (n - bits_in_msb
171− , (fromIntegral (unsafeHead bs) .&. make_mask bits_in_msb) `shiftl_w16` (n - bits_in_msb))
172−
173− segs = byte_offset n
174
195
175− bn 1 = fromIntegral (unsafeIndex bs 1)
196+ | otherwise = error "readWord16be: tried to read more than 16 bits"
176− bn n = (bn (n-1) `shiftl_w16` 8) .|. fromIntegral (unsafeIndex bs n)
177
197
178− mseg = bn segs
198+readWithoutOffset s@(S bs o) shifterL shifterR n
199+ | o /= 0 = error "readWithoutOffset: there is an offset"
179
200
180− last | bit_offset n' > 0 = (fromIntegral (unsafeIndex bs (segs + 1))) `shiftr_w16` (8 - (bit_offset n'))
201+ -- | n == 8 = readWord8 s n
181− | otherwise = 0
202+ -- | n == 16 = readWord16be s n
203+ | n == 24 || n == 32 =
204+ let segs = byte_offset n
205+ bn 0 = fromIntegral (unsafeHead bs)
206+ bn n = (bn (n-1) `shifterL` 8) .|. fromIntegral (unsafeIndex bs n)
182
207
183− w = top .|. mseg .|. last
184− in w :*: incS s n
208+ in (bn (segs-1) :*: incS s n)
185
209
186− | otherwise = error "readWord16be: tried to read more than 16 bits"
210+ | n <= 64 = let segs = byte_offset n
211+
212+ bn 0 = fromIntegral (unsafeHead bs)
213+ bn n = (bn (n-1) `shifterL` 8) .|. fromIntegral (unsafeIndex bs n)
214+
215+ last = (fromIntegral (unsafeIndex bs (segs + 1))) `shifterR` (8 - (bit_offset n))
216+
217+ w = bn segs .|. last
218+ in w :*: incS s n
187
219
188readWithOffset s@(S bs o) shifterL shifterR n
220readWithOffset s@(S bs o) shifterL shifterR n
189 | n <= 64 = let bits_in_msb = 8 - o
221 | n <= 64 = let bits_in_msb = 8 - o
@@ -214,8 +246,12 @@ readWord32be s@(S bs o) n
214 | n <= 16 = let w :*: s' = readWord16be s n
246 | n <= 16 = let w :*: s' = readWord16be s n
215 in fromIntegral w :*: s'
247 in fromIntegral w :*: s'
216
248
217− -- not implemented
249+ | o == 0 = readWithoutOffset s shiftl_w32 shiftr_w32 n
250+ -- error "readWord32be: can't read without offset..."
251+
252+ | n <= 32 = readWithOffset s shiftl_w32 shiftr_w32 n
253+
218− | otherwise = error "readWord32be: Not fully implemented"
254+ | otherwise = error "readWord32be: tried to read more than 32 bits"
219
255
220{-
256{-
221
257
BitsQC.hs+45−1
@@ -4,6 +4,7 @@ import Data.Binary ( encode )
4import Data.Binary.Get
4import Data.Binary.Get
5import Data.Binary.Put
5import Data.Binary.Put
6
6
7+import Data.Bits
7import Data.Word
8import Data.Word
8import System.Random
9import System.Random
9
10
@@ -11,7 +12,24 @@ import Bits
11
12
12import Test.QuickCheck
13import Test.QuickCheck
13
14
15+main = do
14−main = quickCheck prop_Word16be_with_offset
16+ quickCheck prop_Word16be_with_offset
17+ quickCheck prop_Word16be_list
18+ quickCheck prop_SimpleCase
19+ quickCheck prop_Word32_from_2_Word16
20+ quickCheck prop_Word32_from_Word8_and_Word16
21+
22+prop_SimpleCase :: Word16 -> Property
23+prop_SimpleCase w = w < 0x8000 ==>
24+ let p = RMap RBool $ \v -> case v of
25+ True -> RWord16be 15
26+ False -> RMapPure
27+ (RWord8 7 `RNextTo` RWord8 8)
28+ (\(msb:*:lsb)-> (fromIntegral msb `shiftL` 8) .|. fromIntegral lsb)
29+ w' = runGet (get p) lbs
30+ in w == w'
31+ where
32+ lbs = runPut (putWord16be w)
15
33
16prop_Word16be_with_offset :: Word16 -> Property
34prop_Word16be_with_offset :: Word16 -> Property
17prop_Word16be_with_offset w = w < 0x8000 ==>
35prop_Word16be_with_offset w = w < 0x8000 ==>
@@ -20,6 +38,32 @@ prop_Word16be_with_offset w = w < 0x8000 ==>
20 where
38 where
21 lbs = runPut (putWord16be w >> putWord16be w)
39 lbs = runPut (putWord16be w >> putWord16be w)
22
40
41+prop_Word16be_list :: Word8 -> [Word16] -> Property
42+prop_Word16be_list w ws = property $
43+ let p = RWord8 8 `RNextTo` RList (length ws) (RWord16be 16) :: R (T Word8 [Word16])
44+ w' :*: ws' = runGet (get p) lbs :: T Word8 [Word16]
45+ in ws == ws' && w == w'
46+ where
47+ lbs = runPut (putWord8 w >> mapM_ putWord16be ws)
48+
49+prop_Word32_from_Word8_and_Word16 :: Word8 -> Word16 -> Property
50+prop_Word32_from_Word8_and_Word16 w8 w16 = property $
51+ let p = RWord32be 24
52+ w' = runGet (get p) lbs
53+ in w0 == w'
54+ where
55+ lbs = runPut (putWord8 w8 >> putWord16be w16)
56+ w0 = ((fromIntegral w8) `shiftL` 16) .|. fromIntegral w16
57+
58+prop_Word32_from_2_Word16 :: Word16 -> Word16 -> Property
59+prop_Word32_from_2_Word16 w1 w2 = property $
60+ let p = RWord32be 32
61+ w' = runGet (get p) lbs
62+ in w0 == w'
63+ where
64+ lbs = encode w0
65+ w0 = ((fromIntegral w1) `shiftL` 16) .|. fromIntegral w2
66+
23instance Arbitrary Word8 where
67instance Arbitrary Word8 where
24 arbitrary = choose (minBound, maxBound)
68 arbitrary = choose (minBound, maxBound)
25 shrink 0 = []
69 shrink 0 = []