bytesmith 0.3.4.0 → 0.3.5.0
raw patch · 7 files changed
+125/−6 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Bytes.Parser.BigEndian: word256 :: e -> Parser e s Word256
+ Data.Bytes.Parser.BigEndian: word256Array :: e -> Int -> Parser e s (PrimArray Word256)
+ Data.Bytes.Parser.Latin: hexFixedWord64 :: e -> Parser e s Word64
+ Data.Bytes.Parser.LittleEndian: word256 :: e -> Parser e s Word256
+ Data.Bytes.Parser.LittleEndian: word256Array :: e -> Int -> Parser e s (PrimArray Word256)
Files
- CHANGELOG.md +7/−0
- bytesmith.cabal +1/−1
- src/Data/Bytes/Parser/BigEndian.hs +23/−2
- src/Data/Bytes/Parser/Internal.hs +18/−0
- src/Data/Bytes/Parser/Latin.hs +34/−1
- src/Data/Bytes/Parser/LittleEndian.hs +23/−2
- test/Main.hs +19/−0
CHANGELOG.md view
@@ -1,9 +1,16 @@ # Revision history for bytesmith +## 0.3.5.0 -- 2020-02-10++* Add big-endian and little-endian `word256` and `word256Array` parsers.+* Add `hexFixedWord64`.+ ## 0.3.4.0 -- 2020-02-03+ * Add `hexFixedWord32`. ## 0.3.3.0 -- 2020-01-22+ * Add `hexWord8`, `hexWord16`, and `hexFixedWord8`. ## 0.3.2.0 -- 2019-12-27
bytesmith.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: bytesmith-version: 0.3.4.0+version: 0.3.5.0 synopsis: Nonresumable byte parser description: Parse bytes as fast as possible. This is a nonresumable parser
src/Data/Bytes/Parser/BigEndian.hs view
@@ -24,6 +24,7 @@ , word32 , word64 , word128+ , word256 -- * Signed , int8 , int16@@ -35,6 +36,7 @@ , word32Array , word64Array , word128Array+ , word256Array ) where import Prelude hiding (length,any,fail,takeWhile)@@ -44,12 +46,12 @@ import Data.Bytes.Types (Bytes(..)) import Data.Bytes.Parser.Internal (Parser,uneffectful) import Data.Bytes.Parser.Internal (InternalResult(..))-import Data.Bytes.Parser.Internal (swapArray16,swapArray32,swapArray64)+import Data.Bytes.Parser.Internal (swapArray16,swapArray32,swapArray64,swapArray256) import Data.Bytes.Parser.Internal (swapArray128) import Data.Word (Word8,Word16,Word32,Word64) import Data.Int (Int8,Int16,Int32,Int64) import Data.Primitive (ByteArray(..),PrimArray(..))-import Data.WideWord (Word128(Word128))+import Data.WideWord (Word128(Word128),Word256(Word256)) import GHC.ByteOrder (ByteOrder(LittleEndian,BigEndian),targetByteOrder) import qualified Data.Bytes as Bytes@@ -99,6 +101,18 @@ let r = swapArray64 bs pure (asWord64s r) +-- | Parse an array of big-endian unsigned 256-bit words.+word256Array ::+ e -- ^ Error message if not enough bytes are present+ -> Int -- ^ Number of big-endian 256-bit words to consume+ -> Parser e s (PrimArray Word256) -- ^ Native-endian elements+word256Array e !n = case targetByteOrder of+ BigEndian -> fmap (asWord256s . Bytes.toByteArrayClone) (P.take e (n * 32))+ LittleEndian -> do+ bs <- P.take e (n * 32)+ let r = swapArray256 bs+ pure (asWord256s r)+ -- | Parse an array of big-endian unsigned 128-bit words. word128Array :: e -- ^ Error message if not enough bytes are present@@ -123,6 +137,9 @@ asWord128s :: ByteArray -> PrimArray Word128 asWord128s (ByteArray x) = PrimArray x +asWord256s :: ByteArray -> PrimArray Word256+asWord256s (ByteArray x) = PrimArray x+ -- | Unsigned 16-bit word. word16 :: e -> Parser e s Word16 word16 e = uneffectful $ \chunk -> if length chunk >= 2@@ -181,6 +198,10 @@ -- | Unsigned 128-bit word. word128 :: e -> Parser e s Word128 word128 e = liftA2 Word128 (word64 e) (word64 e)++-- | Unsigned 256-bit word.+word256 :: e -> Parser e s Word256+word256 e = (\a b c d -> Word256 a b c d) <$> word64 e <*> word64 e <*> word64 e <*> word64 e -- | Signed 8-bit integer. int8 :: e -> Parser e s Int8
src/Data/Bytes/Parser/Internal.hs view
@@ -38,6 +38,7 @@ , swapArray32 , swapArray64 , swapArray128+ , swapArray256 ) where import Prelude hiding (length,any,fail,takeWhile)@@ -262,6 +263,23 @@ PM.writeByteArray dst (ixDst + 14) v1 PM.writeByteArray dst (ixDst + 15) v0 go (ixSrc + 16) (ixDst + 16) (len - 16)+ else pure ()+ go offset 0 length+ PM.unsafeFreezeByteArray dst++swapArray256 :: Bytes -> ByteArray+swapArray256 (Bytes{array,offset,length}) = runByteArrayST $ do+ dst <- PM.newByteArray length+ let go !ixSrc !ixDst !len = if len > 0+ then do+ let loop !i+ | i < 32 = do+ let v = PM.indexByteArray array (ixSrc + i) :: Word8+ PM.writeByteArray dst (ixDst + (31 - i)) v+ loop (i + 1)+ | otherwise = pure ()+ loop 0+ go (ixSrc + 32) (ixDst + 32) (len - 32) else pure () go offset 0 length PM.unsafeFreezeByteArray dst
src/Data/Bytes/Parser/Latin.hs view
@@ -73,6 +73,7 @@ , hexFixedWord8 , hexFixedWord16 , hexFixedWord32+ , hexFixedWord64 -- *** Digit , hexNibbleLower , tryHexNibbleLower@@ -895,6 +896,9 @@ else (# | (# (), unI (offset c + 1), unI (length c - 1) #) #) +-- | Parse exactly eight ASCII-encoded characters, interpreting them as the+-- hexadecimal encoding of a 32-bit number. Note that this rejects a sequence+-- such as @BC5A9@, requiring @000BC5A9@ instead. This is insensitive to case. hexFixedWord32 :: e -> Parser e s Word32 {-# inline hexFixedWord32 #-} hexFixedWord32 e = Parser@@ -931,7 +935,36 @@ | otherwise -> (# e | #) else (# e | #) --- | Parse exactly four ASCII-encoded characters, interpretting+-- | Parse exactly 16 ASCII-encoded characters, interpreting them as the+-- hexadecimal encoding of a 64-bit number. Note that this rejects a sequence+-- such as @BC5A9@, requiring @00000000000BC5A9@ instead. This is insensitive+-- to case.+hexFixedWord64 :: e -> Parser e s Word64+{-# inline hexFixedWord64 #-}+hexFixedWord64 e = Parser+ (\x s0 -> case runParser (hexFixedWord64# e) x s0 of+ (# s1, r #) -> case r of+ (# err | #) -> (# s1, (# err | #) #)+ (# | (# a, b, c #) #) -> (# s1, (# | (# W64# a, b, c #) #) #)+ )++hexFixedWord64# :: e -> Parser e s Word#+{-# noinline hexFixedWord64# #-}+hexFixedWord64# e = uneffectfulWord# $ \chunk -> if length chunk >= 16+ then+ let go !off !len !acc = case len of+ 0 -> case acc of+ W# r -> + (# | (# r+ , unI off+ , unI (length chunk) -# 16# #) #)+ _ -> case oneHexMaybe (PM.indexByteArray (array chunk) off) of+ Nothing -> (# e | #)+ Just w -> go (off + 1) (len - 1) ((acc * 16) + w)+ in go (offset chunk) (16 :: Int) (0 :: Word)+ else (# e | #)++-- | Parse exactly four ASCII-encoded characters, interpreting -- them as the hexadecimal encoding of a 16-bit number. Note that -- this rejects a sequence such as @5A9@, requiring @05A9@ instead. -- This is insensitive to case. This is particularly useful when
src/Data/Bytes/Parser/LittleEndian.hs view
@@ -25,6 +25,7 @@ , word32 , word64 , word128+ , word256 -- ** Signed , int8 , int16@@ -36,6 +37,7 @@ , word32Array , word64Array , word128Array+ , word256Array -- ** Unsigned , int64Array ) where@@ -49,10 +51,10 @@ import Data.Bytes.Parser.Internal (Parser,uneffectful) import Data.Bytes.Parser.Internal (InternalResult(..)) import Data.Bytes.Parser.Internal (swapArray16,swapArray32)-import Data.Bytes.Parser.Internal (swapArray64,swapArray128)+import Data.Bytes.Parser.Internal (swapArray64,swapArray128,swapArray256) import Data.Word (Word8,Word16,Word32,Word64) import Data.Int (Int8,Int16,Int32,Int64)-import Data.WideWord (Word128(Word128))+import Data.WideWord (Word128(Word128),Word256(Word256)) import GHC.ByteOrder (ByteOrder(LittleEndian,BigEndian),targetByteOrder) import qualified Data.Bytes as Bytes@@ -114,6 +116,18 @@ let r = swapArray128 bs pure (asWord128s r) +-- | Parse an array of little-endian unsigned 256-bit words.+word256Array ::+ e -- ^ Error message if not enough bytes are present+ -> Int -- ^ Number of little-endian 256-bit words to consume+ -> Parser e s (PrimArray Word256) -- ^ Native-endian elements+word256Array e !n = case targetByteOrder of+ LittleEndian -> fmap (asWord256s . Bytes.toByteArrayClone) (P.take e (n * 32))+ BigEndian -> do+ bs <- P.take e (n * 32)+ let r = swapArray256 bs+ pure (asWord256s r)+ -- | Parse an array of little-endian signed 64-bit words. int64Array :: e -- ^ Error message if not enough bytes are present@@ -135,6 +149,9 @@ asWord128s :: ByteArray -> PrimArray Word128 asWord128s (ByteArray x) = PrimArray x +asWord256s :: ByteArray -> PrimArray Word256+asWord256s (ByteArray x) = PrimArray x+ -- | Unsigned 16-bit word. word16 :: e -> Parser e s Word16 word16 e = uneffectful $ \chunk -> if length chunk >= 2@@ -189,6 +206,10 @@ ) (offset chunk + 8) (length chunk - 8) else InternalFailure e++-- | Unsigned 256-bit word.+word256 :: e -> Parser e s Word256+word256 e = (\d c b a -> Word256 a b c d) <$> word64 e <*> word64 e <*> word64 e <*> word64 e -- | Unsigned 128-bit word. word128 :: e -> Parser e s Word128
test/Main.hs view
@@ -105,6 +105,19 @@ P.parseBytes (replicateM (length xs) (BigEndian.word128 ())) bs === P.parseBytes (fmap Exts.toList (BigEndian.word128Array () (length xs))) bs+ , testCase "big-endian-word256" $+ P.parseBytesMaybe (BigEndian.word256Array () 1) (Exts.fromList [+ 0x12, 0x34, 0x56, 0x78, 0x90,+ 0x12, 0x34, 0x56, 0x78, 0x90,+ 0x12, 0x34, 0x56, 0x78, 0x90,+ 0x12,+ 0x12, 0x34, 0x56, 0x78, 0x90,+ 0x12, 0x34, 0x56, 0x78, 0x90,+ 0x12, 0x34, 0x56, 0x78, 0x90,+ 0x12+ ])+ @=?+ Just (Exts.fromList [0x1234567890123456789012345678901212345678901234567890123456789012]) , testProperty "big-endian-word64" bigEndianWord64 , testProperty "big-endian-word32" bigEndianWord32 , testProperty "little-endian-word32" littleEndianWord32@@ -338,6 +351,12 @@ P.parseBytes (Latin.hexFixedWord32 ()) (bytes "") @=? P.Failure () , testCase "D" $ P.parseBytes (Latin.hexFixedWord32 ()) (bytes "A!A0A0A0") @=? P.Failure ()+ ]+ , testGroup "hexFixedWord64"+ [ testCase "A" $+ P.parseBytes (Latin.hexFixedWord64 ()) (bytes "ABCD01235678BCDE")+ @=? P.Success+ (Slice 17 0 0xABCD01235678BCDE) ] ]