leb128-binary 0.1 → 0.1.1
raw patch · 5 files changed
+454/−339 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Data.Binary.SLEB128: getNatural :: Word -> Get Natural
+ Data.Binary.SLEB128: getWord :: Get Word
+ Data.Binary.SLEB128: getWord16 :: Get Word16
+ Data.Binary.SLEB128: getWord32 :: Get Word32
+ Data.Binary.SLEB128: getWord64 :: Get Word64
+ Data.Binary.SLEB128: getWord8 :: Get Word8
+ Data.Binary.SLEB128: putNatural :: Natural -> Put
+ Data.Binary.SLEB128: putWord :: Word -> Put
+ Data.Binary.SLEB128: putWord16 :: Word16 -> Put
+ Data.Binary.SLEB128: putWord32 :: Word32 -> Put
+ Data.Binary.SLEB128: putWord64 :: Word64 -> Put
+ Data.Binary.SLEB128: putWord8 :: Word8 -> Put
+ Data.Binary.ULEB128: getByteString :: Get ByteString
+ Data.Binary.ULEB128: getInt :: Get Int
+ Data.Binary.ULEB128: getInt16 :: Get Int16
+ Data.Binary.ULEB128: getInt32 :: Get Int32
+ Data.Binary.ULEB128: getInt64 :: Get Int64
+ Data.Binary.ULEB128: getInt8 :: Get Int8
+ Data.Binary.ULEB128: getInteger :: Word -> Get Integer
+ Data.Binary.ULEB128: getLazyByteString :: Get ByteString
+ Data.Binary.ULEB128: getShortByteString :: Get ShortByteString
+ Data.Binary.ULEB128: putByteString :: ByteString -> Put
+ Data.Binary.ULEB128: putLazyByteString :: ByteString -> Put
+ Data.Binary.ULEB128: putShortByteString :: ShortByteString -> Put
- Data.Binary.SLEB128: getInteger :: Get Integer
+ Data.Binary.SLEB128: getInteger :: Word -> Get Integer
- Data.Binary.ULEB128: getNatural :: Get Natural
+ Data.Binary.ULEB128: getNatural :: Word -> Get Natural
Files
- CHANGELOG.md +16/−0
- leb128-binary.cabal +3/−3
- lib/Data/Binary/SLEB128.hs +109/−21
- lib/Data/Binary/ULEB128.hs +145/−23
- test/Main.hs +181/−292
CHANGELOG.md view
@@ -1,3 +1,19 @@+# Version 0.1.1++* COMPILER ASSISTED BREAKING CHANGE on `ULEB128`: `getNatural` takes an maximum+ number of bytes to process as input. See issue #1.++* COMPILER ASSISTED BREAKING CHANGE on `SLEB128`: `getInteger` takes an maximum+ number of bytes to process as input. See issue #1.++* Added on `ULEB128`: `putByteString`, `getByteString`, `putShortByteString`,+ `getShortByteString`, `putLazyByteString`, `getLazyByteString`, `getIntegral`,+ `getInt`, `getInt8`, `getInt16`, `getInt32`, `getInt64`.++* Added on `ULEB128`: `putNatural`, `putWord`, `putWord8`, `putWord16`, + `putWord32`, `putWord64`, `getNatural`, `getWord`, `getWord8`, `getWord16`, + `getWord32`, `getWord64`.+ # Version 0.1 * Initial version
leb128-binary.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: leb128-binary-version: 0.1+version: 0.1.1 license: Apache-2.0 license-file: LICENSE extra-source-files: README.md CHANGELOG.md@@ -18,7 +18,7 @@ common basic default-language: Haskell2010 ghc-options: -O2 -Wall - build-depends: base == 4.*, binary+ build-depends: base == 4.*, binary, bytestring library import: basic@@ -29,7 +29,7 @@ test-suite test import: basic- ghc-options: -threaded+ ghc-options: -O2 -threaded type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Main.hs
lib/Data/Binary/SLEB128.hs view
@@ -13,6 +13,12 @@ , putInt16 , putInt8 , putInt+ , putNatural+ , putWord64+ , putWord32+ , putWord16+ , putWord8+ , putWord -- * Get , getInteger , getInt64@@ -20,6 +26,12 @@ , getInt16 , getInt8 , getInt+ , getNatural+ , getWord64+ , getWord32+ , getWord16+ , getWord8+ , getWord ) where import Control.Monad@@ -28,6 +40,7 @@ import Data.Bits import Data.Int import Data.Word+import Numeric.Natural -------------------------------------------------------------------------------- @@ -41,63 +54,138 @@ else do Bin.putWord8 $! w8 .|. 0x80 putInteger b +putNatural :: Natural -> Bin.Put+putNatural = putInteger . fromIntegral+{-# INLINE putNatural #-}+ -- TODO: The following dispatch to 'putInteger'. Make faster. putInt8 :: Int8 -> Bin.Put putInt8 = putInteger . fromIntegral+{-# INLINE putInt8 #-} putInt16 :: Int16 -> Bin.Put putInt16 = putInteger . fromIntegral+{-# INLINE putInt16 #-} putInt32 :: Int32 -> Bin.Put putInt32 = putInteger . fromIntegral+{-# INLINE putInt32 #-} putInt64 :: Int64 -> Bin.Put putInt64 = putInteger . fromIntegral+{-# INLINE putInt64 #-} putInt :: Int -> Bin.Put putInt = putInteger . fromIntegral+{-# INLINE putInt #-} +putWord8 :: Word8 -> Bin.Put+putWord8 = putInteger . fromIntegral+{-# INLINE putWord8 #-}++putWord16 :: Word16 -> Bin.Put+putWord16 = putInteger . fromIntegral+{-# INLINE putWord16 #-}++putWord32 :: Word32 -> Bin.Put+putWord32 = putInteger . fromIntegral+{-# INLINE putWord32 #-}++putWord64 :: Word64 -> Bin.Put+putWord64 = putInteger . fromIntegral+{-# INLINE putWord64 #-}++putWord :: Word -> Bin.Put+putWord = putInteger . fromIntegral+{-# INLINE putWord #-}+ -------------------------------------------------------------------------------- -getInteger :: Bin.Get Integer-getInteger = f 0 0+getInteger + :: Word+ -- ^ /Maximum/ number of bytes to consume. If the 'Integer' number can be+ -- determined before consuming this number of bytes, it will be. If @0@, + -- parsing fails. + --+ -- Each ULEB128 byte encodes at most 7 bits of data. That is, + -- \(length(encoded) == \lceil\frac{length(data)}{7}\rceil\).+ -> Bin.Get Integer+getInteger mx = Bin.label "SLEB128" (f mx 0 0) where- f :: Int -> Integer -> Bin.Get Integer- f !p !a = do+ f :: Word -> Int -> Integer -> Bin.Get Integer+ f 0 _ _ = fail "input too big"+ f n !p !a = do w8 <- Bin.getWord8 let b :: Integer = a .|. unsafeShiftL (toInteger (w8 .&. 0x7f)) p case w8 .&. 0x80 of 0 -> pure $! case w8 .&. 0x40 of 0 -> b _ -> b - bit (p + 7)- _ -> f (p + 7) b+ _ -> f (n - 1) (p + 7) b +getNatural+ :: Word+ -- ^ /Maximum/ number of bytes to consume. If the 'Integer' number can be+ -- determined before consuming this number of bytes, it will be. If @0@, + -- parsing fails. + --+ -- Each ULEB128 byte encodes at most 7 bits of data. That is, + -- \(length(encoded) == \lceil\frac{length(data)}{7}\rceil\).+ -> Bin.Get Natural+getNatural mx = do+ i <- getInteger mx+ when (i < 0) $ Bin.label "SLEB128" (fail "underflow")+ pure (fromInteger i)+{-# INLINE getNatural #-} + -- TODO: The following dispatch to 'getInteger'. Make faster. -getBoundedIntegral :: forall a. (Integral a, Bounded a) => String -> Bin.Get a-getBoundedIntegral label = do- i <- getInteger- when (i < minA) (fail erru)- when (i > maxA) (fail erro)- pure $! fromInteger i- where- erru :: String = label <> ": underflow"- erro :: String = label <> ": overflow" - minA :: Integer = toInteger (minBound :: a) - maxA :: Integer = toInteger (maxBound :: a) +getBoundedIntegral + :: forall a. (Integral a, Bounded a, FiniteBits a) => Bin.Get a+getBoundedIntegral = + let bitSizeA :: Word = fromIntegral (finiteBitSize (undefined :: a))+ mxA :: Word = case divMod bitSizeA 7 of (d, m) -> d + min m 1+ in do i <- getInteger mxA+ maybe (fail "underflow or overflow") pure (toIntegralSized i)+{-# INLINE getBoundedIntegral #-} getInt8 :: Bin.Get Int8-getInt8 = getBoundedIntegral "Data.Binary.SLEB128.getInt8"+getInt8 = getBoundedIntegral+{-# INLINE getInt8 #-} getInt16 :: Bin.Get Int16-getInt16 = getBoundedIntegral "Data.Binary.SLEB128.getInt16"+getInt16 = getBoundedIntegral+{-# INLINE getInt16 #-} getInt32 :: Bin.Get Int32-getInt32 = getBoundedIntegral "Data.Binary.SLEB128.getInt32"+getInt32 = getBoundedIntegral+{-# INLINE getInt32 #-} getInt64 :: Bin.Get Int64-getInt64 = getBoundedIntegral "Data.Binary.SLEB128.getInt64"+getInt64 = getBoundedIntegral+{-# INLINE getInt64 #-} getInt :: Bin.Get Int-getInt = getBoundedIntegral "Data.Binary.SLEB128.getInt"+getInt = getBoundedIntegral+{-# INLINE getInt #-}++getWord8 :: Bin.Get Word8+getWord8 = getBoundedIntegral+{-# INLINE getWord8 #-}++getWord16 :: Bin.Get Word16+getWord16 = getBoundedIntegral+{-# INLINE getWord16 #-}++getWord32 :: Bin.Get Word32+getWord32 = getBoundedIntegral+{-# INLINE getWord32 #-}++getWord64 :: Bin.Get Word64+getWord64 = getBoundedIntegral+{-# INLINE getWord64 #-}++getWord :: Bin.Get Word+getWord = getBoundedIntegral+{-# INLINE getWord #-}
lib/Data/Binary/ULEB128.hs view
@@ -12,6 +12,7 @@ , putWord16 , putWord8 , putWord+ -- * Get , getNatural , getWord64@@ -19,11 +20,31 @@ , getWord16 , getWord8 , getWord+ , getInteger+ , getInt64+ , getInt32+ , getInt16+ , getInt8+ , getInt+ + -- * ByteString+ , putByteString+ , getByteString+ -- ** Lazy+ , putLazyByteString+ , getLazyByteString+ -- ** Short+ , putShortByteString+ , getShortByteString ) where +import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Short as BS import qualified Data.Binary.Get as Bin import qualified Data.Binary.Put as Bin import Data.Bits+import Data.Int import Data.Word import Numeric.Natural @@ -40,55 +61,156 @@ putWord8 :: Word8 -> Bin.Put putWord8 = putNatural . fromIntegral+{-# INLINE putWord8 #-} putWord16 :: Word16 -> Bin.Put putWord16 = putNatural . fromIntegral+{-# INLINE putWord16 #-} putWord32 :: Word32 -> Bin.Put putWord32 = putNatural . fromIntegral+{-# INLINE putWord32 #-} putWord64 :: Word64 -> Bin.Put putWord64 = putNatural . fromIntegral+{-# INLINE putWord64 #-} putWord :: Word -> Bin.Put putWord = putNatural . fromIntegral+{-# INLINE putWord #-} -------------------------------------------------------------------------------- -getNatural :: Bin.Get Natural-getNatural = do- w8 <- Bin.getWord8- if w8 < 0x80- then pure $! fromIntegral w8- else do - a <- getNatural- pure $! unsafeShiftL a 7 .|. fromIntegral (w8 .&. 0x7f)+getNatural + :: Word + -- ^ /Maximum/ number of bytes to consume. If the 'Natural' number can be+ -- determined before consuming this number of bytes, it will be. If @0@, + -- parsing fails. + --+ -- Each ULEB128 byte encodes at most 7 bits of data. That is, + -- \(length(encoded) == \lceil\frac{length(data)}{7}\rceil\).+ -> Bin.Get Natural+getNatural mx = Bin.label "ULEB128" (go mx)+ where + go 0 = fail "input too big"+ go n = do+ w8 <- Bin.getWord8+ if w8 < 0x80+ then pure $! fromIntegral w8+ else do + a <- go (n - 1)+ pure $! unsafeShiftL a 7 .|. fromIntegral (w8 .&. 0x7f) +getInteger + :: Word+ -- ^ /Maximum/ number of bytes to consume. If the 'Integer' number can be+ -- determined before consuming this number of bytes, it will be. If @0@, + -- parsing fails. + --+ -- Each ULEB128 byte encodes at most 7 bits of data. That is, + -- \(length(encoded) == \lceil\frac{length(data)}{7}\rceil\).+ -> Bin.Get Integer+getInteger = fmap toInteger . getNatural +{-# INLINE getInteger #-}+ -- TODO: The following dispatch to 'getNatural'. Make faster. -getBoundedIntegral :: forall a. (Integral a, Bounded a) => String -> Bin.Get a-getBoundedIntegral label = do- n <- getNatural- let i = toInteger n- if i <= maxA - then pure $! fromInteger i- else fail err- where- err :: String = label <> ": overflow" - maxA :: Integer = toInteger (maxBound :: a) +getBoundedIntegral + :: forall a. (Integral a, Bounded a, FiniteBits a) => Bin.Get a+getBoundedIntegral = + let bitSizeA :: Word = fromIntegral (finiteBitSize (undefined :: a))+ mxA :: Word = case divMod bitSizeA 7 of (d, m) -> d + min m 1+ in do n <- getNatural mxA+ maybe (fail "overflow") pure (toIntegralSized n)+{-# INLINE getBoundedIntegral #-} getWord8 :: Bin.Get Word8-getWord8 = getBoundedIntegral "Data.Binary.ULEB128.getWord8"+getWord8 = getBoundedIntegral+{-# INLINE getWord8 #-} getWord16 :: Bin.Get Word16-getWord16 = getBoundedIntegral "Data.Binary.ULEB128.getWord16"+getWord16 = getBoundedIntegral+{-# INLINE getWord16 #-} getWord32 :: Bin.Get Word32-getWord32 = getBoundedIntegral "Data.Binary.ULEB128.getWord32"+getWord32 = getBoundedIntegral+{-# INLINE getWord32 #-} getWord64 :: Bin.Get Word64-getWord64 = getBoundedIntegral "Data.Binary.ULEB128.getWord64"+getWord64 = getBoundedIntegral+{-# INLINE getWord64 #-} getWord :: Bin.Get Word-getWord = getBoundedIntegral "Data.Binary.ULEB128.getWord"+getWord = getBoundedIntegral+{-# INLINE getWord #-}++getInt8 :: Bin.Get Int8+getInt8 = getBoundedIntegral+{-# INLINE getInt8 #-}++getInt16 :: Bin.Get Int16+getInt16 = getBoundedIntegral+{-# INLINE getInt16 #-}++getInt32 :: Bin.Get Int32+getInt32 = getBoundedIntegral+{-# INLINE getInt32 #-}++getInt64 :: Bin.Get Int64+getInt64 = getBoundedIntegral+{-# INLINE getInt64 #-}++getInt :: Bin.Get Int+getInt = getBoundedIntegral+{-# INLINE getInt #-}++--------------------------------------------------------------------------------+ +-- | Puts a strict 'B.ByteString' with its ULEB128-encoded length as prefix.+--+-- See 'getByteString'.+putByteString :: B.ByteString -> Bin.Put+putByteString = \a -> do+ putNatural (fromIntegral (B.length a :: Int))+ Bin.putByteString a+{-# INLINE putByteString #-}++-- | Gets a strict 'B.ByteString' with its ULEB128-encoded length as prefix.+--+-- See 'putByteString'.+getByteString :: Bin.Get B.ByteString+getByteString = Bin.getByteString =<< getInt+{-# INLINE getByteString #-}++-- | Puts a lazy 'B.ByteString' with its ULEB128-encoded length as prefix.+--+-- See 'getLazyByteString'.+putLazyByteString :: BL.ByteString -> Bin.Put+putLazyByteString = \a -> do+ putNatural (fromIntegral (BL.length a :: Int64))+ Bin.putLazyByteString a+{-# INLINE putLazyByteString #-}++-- | Gets a lazy 'BL.ByteString' with its ULEB128-encoded length as prefix.+--+-- See 'putLazyByteString'.+getLazyByteString :: Bin.Get BL.ByteString+getLazyByteString = Bin.getLazyByteString =<< getInt64+{-# INLINE getLazyByteString #-}++-- | Puts a 'BS.ShortByteString' with its ULEB128-encoded length as prefix.+--+-- See 'getShortByteString'.+putShortByteString :: BS.ShortByteString -> Bin.Put+putShortByteString = \a -> do+ putNatural (fromIntegral (BS.length a :: Int))+ Bin.putShortByteString a+{-# INLINE putShortByteString #-}++-- | Gets a 'BS.ShortByteString' with its ULEB128-encoded length as prefix.+--+-- See 'putShortByteString'.+getShortByteString :: Bin.Get BS.ShortByteString+getShortByteString = fmap BS.toShort (Bin.getByteString =<< getInt)+{-# INLINE getShortByteString #-}
test/Main.hs view
@@ -1,19 +1,24 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} module Main where +import Control.Monad import qualified Data.Binary.Get as Bin import qualified Data.Binary.Put as Bin+import Data.Bits+import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Short as BS+import Data.Foldable import Data.Int-import Data.Word import Numeric.Natural import Test.Tasty (TestTree, testGroup) import qualified Test.Tasty as Tasty-import Test.Tasty.HUnit (testCase, (@?=))-import Test.Tasty.Hedgehog (testProperty, HedgehogTestLimit (..))+import Test.Tasty.HUnit (testCase, (@?=), Assertion)+import Test.Tasty.Hedgehog (HedgehogTestLimit (..), testProperty) import qualified Test.Tasty.Runners as Tasty-import Hedgehog (forAll, property, (===))+import Hedgehog (MonadTest, forAll, property, (===)) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range @@ -36,7 +41,16 @@ tt_ULEB128 :: TestTree tt_ULEB128 = testGroup "ULEB128" - [ testGroup "Known" + [ testGroup "Too big"+ [ testCase "0, 0" $ dec (U.getNatural 0) "" @?= Left "input too big\nULEB128"+ , testCase "0, 1" $ dec (U.getNatural 0) "\x00" @?= Left "input too big\nULEB128"+ , testCase "0, 1.5" $ dec (U.getNatural 0) "\x80" @?= Left "input too big\nULEB128"+ , testCase "1, 1.5" $ dec (U.getNatural 1) "\x80" @?= Left "input too big\nULEB128"+ , testCase "1, 2" $ dec (U.getNatural 1) "\x80\x01" @?= Left "input too big\nULEB128"+ , testCase "1, 3" $ dec (U.getNatural 1) "\xfd\x82\x01" @?= Left "input too big\nULEB128"+ , testCase "2, 3" $ dec (U.getNatural 2) "\xfd\x82\x01" @?= Left "input too big\nULEB128"+ ]+ , testGroup "Known" [ tt_ULEB128_known 0 "\x00" , tt_ULEB128_known 1 "\x01" , tt_ULEB128_known 6 "\x06"@@ -817,159 +831,82 @@ , tt_ULEB128_known 18247503505561409945 "\x99\xb3\xbb\xb5\xa4\xf8\x89\x9e\xfd\x01" ] , testGroup "Round trip"- [ testGroup "putWord8"- [ testProperty "getWord8" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord8 (enc (U.putWord8 n)) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord16 (enc (U.putWord8 n)) === Right (fromIntegral n)- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord16 (enc (U.putWord8 n)) === Right (fromIntegral n)- , testProperty "getWord32" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord32 (enc (U.putWord8 n)) === Right (fromIntegral n)- , testProperty "getWord64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord64 (enc (U.putWord8 n)) === Right (fromIntegral n)- , testProperty "getWord" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord (enc (U.putWord8 n)) === Right (fromIntegral n)- , testProperty "getNatural" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getNatural (enc (U.putWord8 n)) === Right (fromIntegral n)- ]- , testGroup "putWord16"- [ testProperty "getWord8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word8)- dec U.getWord8 (enc (U.putWord16 n')) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord16 (enc (U.putWord16 n)) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord16 (enc (U.putWord16 n)) === Right (fromIntegral n)- , testProperty "getWord32" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord32 (enc (U.putWord16 n)) === Right (fromIntegral n)- , testProperty "getWord64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord64 (enc (U.putWord16 n)) === Right (fromIntegral n)- , testProperty "getWord" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord (enc (U.putWord16 n)) === Right (fromIntegral n)- , testProperty "getNatural" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getNatural (enc (U.putWord16 n)) === Right (fromIntegral n)- ]- , testGroup "putWord32"- [ testProperty "getWord8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word8)- dec U.getWord8 (enc (U.putWord32 n')) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word16)- dec U.getWord16 (enc (U.putWord32 n')) === Right n- , testProperty "getWord32" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord32 (enc (U.putWord32 n)) === Right n- , testProperty "getWord64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord64 (enc (U.putWord32 n)) === Right (fromIntegral n)- , testProperty "getWord" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord (enc (U.putWord32 n)) === Right (fromIntegral n)- , testProperty "getNatural" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getNatural (enc (U.putWord32 n)) === Right (fromIntegral n)- ]- , testGroup "putWord64"- [ testProperty "getWord8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word8)- dec U.getWord8 (enc (U.putWord64 n')) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word16)- dec U.getWord16 (enc (U.putWord64 n')) === Right n- , testProperty "getWord32" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word32)- dec U.getWord32 (enc (U.putWord64 n')) === Right n- , testProperty "getWord64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord64 (enc (U.putWord64 n)) === Right n- , testProperty "getWord" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord (enc (U.putWord32 n)) === Right (fromIntegral n)- , testProperty "getNatural" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getNatural (enc (U.putWord64 n)) === Right (fromIntegral n)- ]- , testGroup "putWord"- [ testProperty "getWord8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word8)- dec U.getWord8 (enc (U.putWord n')) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word16)- dec U.getWord16 (enc (U.putWord n')) === Right n- , testProperty "getWord32" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word32)- dec U.getWord32 (enc (U.putWord n')) === Right n- , testProperty "getWord64" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word64)- dec U.getWord64 (enc (U.putWord n')) === Right n- , testProperty "getWord" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getWord (enc (U.putWord n)) === Right n- , testProperty "getNatural" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec U.getNatural (enc (U.putWord n)) === Right (fromIntegral n)- ]- , testGroup "putNatural"- [ testProperty "getWord8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word8)- dec U.getWord8 (enc (U.putNatural n')) === Right n- , testProperty "getWord16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word16)- dec U.getWord16 (enc (U.putNatural n')) === Right n- , testProperty "getWord32" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word32)- dec U.getWord32 (enc (U.putNatural n')) === Right n- , testProperty "getWord64" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word64)- dec U.getWord64 (enc (U.putNatural n')) === Right n- , testProperty "getWord" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Word)- dec U.getWord (enc (U.putNatural n')) === Right n- , testProperty "getNatural" $ property $ do- n <- forAll $ Gen.integral rangeNatural512- dec U.getNatural (enc (U.putNatural n)) === Right n- ]+ [ testProperty "putWord8" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecULEB128 n (enc (U.putWord8 n))+ , testProperty "putWord16" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecULEB128 n (enc (U.putWord16 n))+ , testProperty "putWord32" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecULEB128 n (enc (U.putWord32 n))+ , testProperty "putWord64" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecULEB128 n (enc (U.putWord64 n))+ , testProperty "putWord" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecULEB128 n (enc (U.putWord n))+ , testProperty "putNatural" $ property $ do+ n <- forAll $ Gen.integral rangeNatural512+ propDecULEB128 n (enc (U.putNatural n)) ] + , testGroup "ByteString (strict)"+ [ testProperty "Round trip" $ property $ do+ i <- forAll $ Gen.integral $ Range.constantFrom 0 0 1000+ let raw = B.replicate i 222+ dec U.getByteString (enc (U.putByteString raw)) === Right raw++ , testProperty "Known" $ property $ do+ i <- forAll $ Gen.integral $ Range.constantFrom 0 0 1000+ let pre = enc (U.putWord (fromIntegral (i :: Int)))+ raw = B.replicate i 222+ enc (U.putByteString raw) === pre <> BL.fromStrict raw+ ]+ , testGroup "ByteString (lazy)"+ [ testProperty "Round trip" $ property $ do+ i <- forAll $ Gen.integral $ Range.constantFrom 0 0 1000+ let raw = BL.replicate i 222+ dec U.getLazyByteString (enc (U.putLazyByteString raw)) === Right raw++ , testProperty "Known" $ property $ do+ i <- forAll $ Gen.integral $ Range.constantFrom 0 0 1000+ let pre = enc (U.putWord (fromIntegral (i :: Int64)))+ raw = BL.replicate i 222+ enc (U.putLazyByteString raw) === pre <> raw+ ]+ , testGroup "ByteString (strict)"+ [ testProperty "Round trip" $ property $ do+ i <- forAll $ Gen.integral $ Range.constantFrom 0 0 1000+ let raw = BS.toShort (B.replicate i 222)+ dec U.getShortByteString (enc (U.putShortByteString raw)) === Right raw++ , testProperty "Known" $ property $ do+ i <- forAll $ Gen.integral $ Range.constantFrom 0 0 1000+ let pre = enc (U.putWord (fromIntegral (i :: Int)))+ raw = BS.toShort (B.replicate i 222)+ enc (U.putShortByteString raw) + === pre <> BL.fromStrict (BS.fromShort raw)+ ] ] tt_ULEB128_known :: Natural -> BL.ByteString -> TestTree tt_ULEB128_known n bl = testGroup (show n) [ testCase "put" $ enc (U.putNatural n) @?= bl- , testCase "get" $ dec U.getNatural bl @?= Right n+ , testCase "get" $ assertDecULEB128 n bl ] tt_SLEB128 :: TestTree tt_SLEB128 = testGroup "SLEB128" - [ testGroup "Known" + [ testGroup "Too big" + [ testCase "0, 0" $ dec (S.getInteger 0) "" @?= Left "input too big\nSLEB128"+ , testCase "0, 1" $ dec (S.getInteger 0) "\x00" @?= Left "input too big\nSLEB128"+ , testCase "0, 1.5" $ dec (S.getInteger 0) "\x80" @?= Left "input too big\nSLEB128"+ , testCase "1, 1.5" $ dec (S.getInteger 1) "\x80" @?= Left "input too big\nSLEB128"+ , testCase "1, 2" $ dec (S.getInteger 1) "\x89\x3b" @?= Left "input too big\nSLEB128"+ , testCase "1, 3" $ dec (S.getInteger 1) "\xc9\xc1\x00" @?= Left "input too big\nSLEB128"+ , testCase "2, 3" $ dec (S.getInteger 2) "\xc9\xc1\x00" @?= Left "input too big\nSLEB128"+ ]+ , testGroup "Known" [ tt_SLEB128_known (-9015451631251509835) "\xb5\xdb\xa6\xec\x9d\xd0\xab\xf1\x82\x7f" , tt_SLEB128_known (-8845873988257394623) "\xc1\xa0\xb3\x90\x9a\x8f\xc9\x9e\x85\x7f" , tt_SLEB128_known (-8842841274936780126) "\xa2\xb5\xec\xd1\xe3\xd6\xfa\xa3\x85\x7f"@@ -1896,157 +1833,110 @@ , tt_SLEB128_known 9159289276857505834 "\xaa\xb0\x8b\xf1\x9b\xa2\x95\x8e\xff\x00" ] , testGroup "Round trip"- [ testGroup "putInt8"- [ testProperty "getInt8" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt8 (enc (S.putInt8 n)) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt16 (enc (S.putInt8 n)) === Right (fromIntegral n)- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt16 (enc (S.putInt8 n)) === Right (fromIntegral n)- , testProperty "getInt32" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt32 (enc (S.putInt8 n)) === Right (fromIntegral n)- , testProperty "getInt64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt64 (enc (S.putInt8 n)) === Right (fromIntegral n)- , testProperty "getInt" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt (enc (S.putInt8 n)) === Right (fromIntegral n)- , testProperty "getInteger" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInteger (enc (S.putInt8 n)) === Right (fromIntegral n)- ]- , testGroup "putInt16"- [ testProperty "getInt8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int8)- dec S.getInt8 (enc (S.putInt16 n')) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt16 (enc (S.putInt16 n)) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt16 (enc (S.putInt16 n)) === Right (fromIntegral n)- , testProperty "getInt32" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt32 (enc (S.putInt16 n)) === Right (fromIntegral n)- , testProperty "getInt64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt64 (enc (S.putInt16 n)) === Right (fromIntegral n)- , testProperty "getInt" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt (enc (S.putInt16 n)) === Right (fromIntegral n)- , testProperty "getInteger" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInteger (enc (S.putInt16 n)) === Right (fromIntegral n)- ]- , testGroup "putInt32"- [ testProperty "getInt8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int8)- dec S.getInt8 (enc (S.putInt32 n')) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int16)- dec S.getInt16 (enc (S.putInt32 n')) === Right n- , testProperty "getInt32" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt32 (enc (S.putInt32 n)) === Right n- , testProperty "getInt64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt64 (enc (S.putInt32 n)) === Right (fromIntegral n)- , testProperty "getInt" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt (enc (S.putInt32 n)) === Right (fromIntegral n)- , testProperty "getInteger" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInteger (enc (S.putInt32 n)) === Right (fromIntegral n)- ]- , testGroup "putInt64"- [ testProperty "getInt8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int8)- dec S.getInt8 (enc (S.putInt64 n')) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int16)- dec S.getInt16 (enc (S.putInt64 n')) === Right n- , testProperty "getInt32" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int32)- dec S.getInt32 (enc (S.putInt64 n')) === Right n- , testProperty "getInt64" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt64 (enc (S.putInt64 n)) === Right n- , testProperty "getInt" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt (enc (S.putInt32 n)) === Right (fromIntegral n)- , testProperty "getInteger" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInteger (enc (S.putInt64 n)) === Right (fromIntegral n)- ]- , testGroup "putInt"- [ testProperty "getInt8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int8)- dec S.getInt8 (enc (S.putInt n')) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int16)- dec S.getInt16 (enc (S.putInt n')) === Right n- , testProperty "getInt32" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int32)- dec S.getInt32 (enc (S.putInt n')) === Right n- , testProperty "getInt64" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int64)- dec S.getInt64 (enc (S.putInt n')) === Right n- , testProperty "getInt" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInt (enc (S.putInt n)) === Right n- , testProperty "getInteger" $ property $ do- n <- forAll $ Gen.integral Range.constantBounded- dec S.getInteger (enc (S.putInt n)) === Right (fromIntegral n)- ]- ,- testGroup "putInteger"- [ testProperty "getInt8" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int8)- dec S.getInt8 (enc (S.putInteger n')) === Right n- , testProperty "getInt16" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int16)- dec S.getInt16 (enc (S.putInteger n')) === Right n- , testProperty "getInt32" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int32)- dec S.getInt32 (enc (S.putInteger n')) === Right n- , testProperty "getInt64" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int64)- dec S.getInt64 (enc (S.putInteger n')) === Right n- , testProperty "getInt" $ property $ do- n <- forAll $ Gen.integral $ Range.constantBounded- let n' = fromIntegral (n :: Int)- dec S.getInt (enc (S.putInteger n')) === Right n- , testProperty "getInteger" $ property $ do- n <- forAll $ Gen.integral rangeInteger512- dec S.getInteger (enc (S.putInteger n)) === Right n- ]+ [ testProperty "putInt8" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putInt8 n))+ , testProperty "putInt16" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putInt16 n))+ , testProperty "putInt32" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putInt32 n))+ , testProperty "putInt64" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putInt64 n))+ , testProperty "putInt" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putInt n))+ , testProperty "putInteger" $ property $ do+ i <- forAll $ Gen.integral rangeInteger512+ propDecSLEB128 i (enc (S.putInteger i))+ , testProperty "putWord8" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putWord8 n))+ , testProperty "putWord16" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putWord16 n))+ , testProperty "putWord32" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putWord32 n))+ , testProperty "putWord64" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putWord64 n))+ , testProperty "putWord" $ property $ do+ n <- forAll $ Gen.integral Range.constantBounded+ propDecSLEB128 n (enc (S.putWord n))+ , testProperty "putNatural" $ property $ do+ n <- forAll $ Gen.integral rangeNatural512+ propDecSLEB128 n (enc (S.putNatural n)) ] ] tt_SLEB128_known :: Integer -> BL.ByteString -> TestTree tt_SLEB128_known i bl = testGroup (show i) [ testCase "put" $ enc (S.putInteger i) @?= bl- , testCase "get" $ dec S.getInteger bl @?= Right i+ , testCase "get" $ assertDecSLEB128 i bl ] +propDecULEB128 + :: (Integral a, Bits a, MonadTest m) => a -> BL.ByteString -> m ()+propDecULEB128 = decULEB128 (===)++assertDecULEB128 + :: (Integral a, Bits a) => a -> BL.ByteString -> Assertion +assertDecULEB128 = decULEB128 (@?=)++decULEB128+ :: (Integral a, Bits a, Monad m) + => (forall x. (Eq x, Show x) => x -> x -> m ()) + -> a + -> BL.ByteString + -> m ()+decULEB128 eq = \a bl -> do+ let l = fromIntegral (BL.length bl) :: Word+ for_ (toIntegralSized a) $ \b -> eq (dec (U.getNatural l) bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec (U.getInteger l) bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getWord bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getWord8 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getWord16 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getWord32 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getWord64 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getInt bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getInt8 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getInt16 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getInt32 bl) (Right b)+ for_ (toIntegralSized a) $ \b -> eq (dec U.getInt64 bl) (Right b)++propDecSLEB128 + :: (Integral a, Bits a, MonadTest m) => a -> BL.ByteString -> m ()+propDecSLEB128 = decSLEB128 (===)++assertDecSLEB128 + :: (Integral a, Bits a) => a -> BL.ByteString -> Assertion +assertDecSLEB128 = decSLEB128 (@?=)++decSLEB128+ :: (Integral a, Bits a, Monad m) + => (forall x. (Eq x, Show x) => x -> x -> m ()) + -> a + -> BL.ByteString + -> m ()+decSLEB128 eq = \a bl -> do+ let i = toInteger a+ l = fromIntegral (BL.length bl) :: Word+ when (a >= 0) $ eq (dec (S.getNatural l) bl) (Right (fromInteger i))+ for_ (toIntegralSized i) $ \b -> eq (dec (S.getInteger l) bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getWord bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getWord8 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getWord16 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getWord32 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getWord64 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getInt bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getInt8 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getInt16 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getInt32 bl) (Right b)+ for_ (toIntegralSized i) $ \b -> eq (dec S.getInt64 bl) (Right b)+ rangeInteger512 :: Range.Range Integer rangeInteger512 = let x = 2 ^ (511 :: Int) :: Integer@@ -2063,5 +1953,4 @@ Left (_, _, e) -> Left e Right (l, _, a) | BL.null l -> Right a | otherwise -> Left "parsed successfully, but got leftovers"-