packages feed

small-bytearray-builder 0.3.2.0 → 0.3.3.0

raw patch · 5 files changed

+195/−9 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.ByteArray.Builder: word256ArrayBE :: PrimArray Word256 -> Int -> Int -> Builder
+ Data.ByteArray.Builder: word256ArrayLE :: PrimArray Word256 -> Int -> Int -> Builder
+ Data.ByteArray.Builder: word256BE :: Word256 -> Builder
+ Data.ByteArray.Builder: word256LE :: Word256 -> Builder
+ Data.ByteArray.Builder.Bounded: word128PaddedLowerHex :: Word128 -> Builder 32
+ Data.ByteArray.Builder.Bounded: word128PaddedUpperHex :: Word128 -> Builder 32
+ Data.ByteArray.Builder.Bounded: word256BE :: Word256 -> Builder 32
+ Data.ByteArray.Builder.Bounded: word256LE :: Word256 -> Builder 32
+ Data.ByteArray.Builder.Bounded: word256PaddedLowerHex :: Word256 -> Builder 64
+ Data.ByteArray.Builder.Bounded: word256PaddedUpperHex :: Word256 -> Builder 64
+ Data.ByteArray.Builder.Bounded: word32PaddedLowerHex :: Word32 -> Builder 8
+ Data.ByteArray.Builder.Bounded: word64PaddedLowerHex :: Word64 -> Builder 16

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for small-bytearray-builder +## 0.3.3.0 -- 2020-02-10++* Add `word64PaddedLowerHex` and `word32PaddedLowerHex`+* Add `word256Array{LE,BE}` and `word256{LE,BE}`+* Add `word{128,256}Padded{Lower,Upper}Hex`+ ## 0.3.2.0 -- 2020-01-20  * Add `putMany`, which allows pasting into the same mutable byte
small-bytearray-builder.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: small-bytearray-builder-version: 0.3.2.0+version: 0.3.3.0 synopsis: Serialize to a small byte arrays description:   This is similar to the builder facilities provided by
src/Data/ByteArray/Builder.hs view
@@ -57,6 +57,7 @@     -- *** One   , word8     -- **** Big Endian+  , word256BE   , word128BE   , word64BE   , word32BE@@ -65,6 +66,7 @@   , int32BE   , int16BE     -- **** Little Endian+  , word256LE   , word128LE   , word64LE   , word32LE@@ -79,6 +81,7 @@   , word32ArrayBE   , word64ArrayBE   , word128ArrayBE+  , word256ArrayBE   , int64ArrayBE   , int32ArrayBE   , int16ArrayBE@@ -87,6 +90,7 @@   , word32ArrayLE   , word64ArrayLE   , word128ArrayLE+  , word256ArrayLE   , int64ArrayLE   , int32ArrayLE   , int16ArrayLE@@ -119,7 +123,7 @@ import Data.Int (Int64,Int32,Int16,Int8) import Data.Primitive (ByteArray(..),MutableByteArray(..),PrimArray(..)) import Data.Text.Short (ShortText)-import Data.WideWord (Word128)+import Data.WideWord (Word128,Word256) import Data.Word (Word64,Word32,Word16,Word8) import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder) import GHC.Exts (Int(I#),Char(C#),Int#,State#,ByteArray#,(>=#))@@ -390,6 +394,16 @@   BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 16) (slen0 * 16))   LittleEndian -> word128ArraySwap src soff0 slen0 +word256ArrayLE :: PrimArray Word256 -> Int -> Int -> Builder+word256ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of+  LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 32) (slen0 * 32))+  BigEndian -> word256ArraySwap src soff0 slen0++word256ArrayBE :: PrimArray Word256 -> Int -> Int -> Builder+word256ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of+  BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 32) (slen0 * 32))+  LittleEndian -> word256ArraySwap src soff0 slen0+ word64ArrayLE :: PrimArray Word64 -> Int -> Int -> Builder word64ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of   LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 8) (slen0 * 8))@@ -525,6 +539,28 @@       go (soff + 16) send dst (doff + 16)     else pure doff +word256ArraySwap :: PrimArray Word256 -> Int -> Int -> Builder+word256ArraySwap src soff0 slen0 =+  fromFunction (slen0 * 32) (go (soff0 * 32) ((soff0 + slen0) * 32))+  where+  -- TODO: Perhaps we could put byteswapping functions to use+  -- rather than indexing tons of Word8s. This could be done+  -- both here and in the other swap functions. There are a+  -- decent number of tests for these array-swapping functions,+  -- which makes changing this less scary.+  go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int+  go !soff !send !dst !doff = if soff < send+    then do+      let loop !i+            | i < 32 = do+              let v = PM.indexPrimArray (asWord8s src) (soff + i)+              PM.writeByteArray dst (doff + (31 - i)) v+              loop (i + 1)+            | otherwise = pure ()+      loop 0+      go (soff + 32) send dst (doff + 32)+    else pure doff+ asWord8s :: PrimArray a -> PrimArray Word8 asWord8s (PrimArray x) = PrimArray x @@ -766,6 +802,11 @@ int16BE :: Int16 -> Builder int16BE w = fromBounded Nat.constant (Bounded.int16BE w) +-- | Requires exactly 32 bytes. Dump the octets of a 256-bit+-- word in a little-endian fashion.+word256LE :: Word256 -> Builder+word256LE w = fromBounded Nat.constant (Bounded.word256LE w)+ -- | Requires exactly 16 bytes. Dump the octets of a 128-bit -- word in a little-endian fashion. word128LE :: Word128 -> Builder@@ -786,15 +827,21 @@ word16LE :: Word16 -> Builder word16LE w = fromBounded Nat.constant (Bounded.word16LE w) --- | Requires exactly 8 bytes. Dump the octets of a 64-bit++-- | Requires exactly 32 bytes. Dump the octets of a 256-bit -- word in a big-endian fashion.-word64BE :: Word64 -> Builder-word64BE w = fromBounded Nat.constant (Bounded.word64BE w)+word256BE :: Word256 -> Builder+word256BE w = fromBounded Nat.constant (Bounded.word256BE w)  -- | Requires exactly 16 bytes. Dump the octets of a 128-bit -- word in a big-endian fashion. word128BE :: Word128 -> Builder word128BE w = fromBounded Nat.constant (Bounded.word128BE w)++-- | Requires exactly 8 bytes. Dump the octets of a 64-bit+-- word in a big-endian fashion.+word64BE :: Word64 -> Builder+word64BE w = fromBounded Nat.constant (Bounded.word64BE w)  -- | Requires exactly 4 bytes. Dump the octets of a 32-bit -- word in a big-endian fashion.
src/Data/ByteArray/Builder/Bounded.hs view
@@ -37,9 +37,16 @@   , int8Dec   , intDec     -- * Unsigned Words+    -- ** Wide Words+  , word128PaddedLowerHex+  , word128PaddedUpperHex+  , word256PaddedLowerHex+  , word256PaddedUpperHex     -- ** 64-bit+  , word64PaddedLowerHex   , word64PaddedUpperHex     -- ** 32-bit+  , word32PaddedLowerHex   , word32PaddedUpperHex     -- ** 16-bit   , word16PaddedLowerHex@@ -59,6 +66,7 @@     -- *** One   , word8     -- **** Big Endian+  , word256BE   , word128BE   , word64BE   , word32BE@@ -67,6 +75,7 @@   , int32BE   , int16BE     -- **** Little Endian+  , word256LE   , word128LE   , word64LE   , word32LE@@ -87,7 +96,7 @@ import Data.Char (ord) import Data.Primitive import Data.Primitive.ByteArray.Offset (MutableByteArrayOffset(..))-import Data.WideWord (Word128(Word128))+import Data.WideWord (Word128(Word128),Word256(Word256)) import GHC.Exts import GHC.Int (Int64(I64#),Int32(I32#),Int16(I16#),Int8(I8#)) import GHC.ST (ST(ST))@@ -323,6 +332,44 @@   loSolved = w + 48   hiSolved = w + 87 +-- | Requires exactly 64 bytes. Encodes a 256-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 64 digits. This uses+-- lowercase for the alphabetical digits.+word256PaddedLowerHex :: Word256 -> Builder 64+word256PaddedLowerHex (Word256 w192 w128 w64 w0) =+           word64PaddedLowerHex w192+  `append` word64PaddedLowerHex w128+  `append` word64PaddedLowerHex w64+  `append` word64PaddedLowerHex w0++-- | Requires exactly 64 bytes. Encodes a 256-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 64 digits. This uses+-- uppercase for the alphabetical digits.+word256PaddedUpperHex :: Word256 -> Builder 64+word256PaddedUpperHex (Word256 w192 w128 w64 w0) =+           word64PaddedUpperHex w192+  `append` word64PaddedUpperHex w128+  `append` word64PaddedUpperHex w64+  `append` word64PaddedUpperHex w0+++-- | Requires exactly 32 bytes. Encodes a 128-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 32 digits. This uses+-- lowercase for the alphabetical digits.+word128PaddedLowerHex :: Word128 -> Builder 32+word128PaddedLowerHex (Word128 w64 w0) =+           word64PaddedLowerHex w64+  `append` word64PaddedLowerHex w0++-- | Requires exactly 32 bytes. Encodes a 128-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 32 digits. This uses+-- uppercase for the alphabetical digits.+word128PaddedUpperHex :: Word128 -> Builder 32+word128PaddedUpperHex (Word128 w64 w0) =+           word64PaddedUpperHex w64+  `append` word64PaddedUpperHex w0++ -- | Requires exactly 16 bytes. Encodes a 64-bit unsigned integer as -- hexadecimal, zero-padding the encoding to 16 digits. This uses -- uppercase for the alphabetical digits. For example, this encodes the@@ -330,12 +377,25 @@ word64PaddedUpperHex :: Word64 -> Builder 16 word64PaddedUpperHex (W64# w) = word64PaddedUpperHex# w +-- | Requires exactly 16 bytes. Encodes a 64-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 16 digits. This uses+-- lowercase for the alphabetical digits. For example, this encodes the+-- number 1022 as @00000000000003fe@.+word64PaddedLowerHex :: Word64 -> Builder 16+word64PaddedLowerHex (W64# w) = word64PaddedLowerHex# w+ -- | Requires exactly 8 bytes. Encodes a 32-bit unsigned integer as -- hexadecimal, zero-padding the encoding to 8 digits. This uses -- uppercase for the alphabetical digits. word32PaddedUpperHex :: Word32 -> Builder 8 word32PaddedUpperHex (W32# w) = word32PaddedUpperHex# w +-- | Requires exactly 8 bytes. Encodes a 32-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 8 digits. This uses+-- lowercase for the alphabetical digits.+word32PaddedLowerHex :: Word32 -> Builder 8+word32PaddedLowerHex (W32# w) = word32PaddedLowerHex# w+ -- | Requires exactly 4 bytes. Encodes a 16-bit unsigned integer as -- hexadecimal, zero-padding the encoding to 4 digits. This uses -- uppercase for the alphabetical digits.@@ -415,6 +475,31 @@   where   w = W# w# +-- TODO: Is it actually worth unrolling this loop. I suspect that it+-- might not be. Benchmark this.+word64PaddedLowerHex# :: Word# -> Builder 16+{-# noinline word64PaddedLowerHex# #-}+word64PaddedLowerHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexLower (unsafeShiftR w 60))+  writeByteArray arr (off + 1) (toHexLower (unsafeShiftR w 56))+  writeByteArray arr (off + 2) (toHexLower (unsafeShiftR w 52))+  writeByteArray arr (off + 3) (toHexLower (unsafeShiftR w 48))+  writeByteArray arr (off + 4) (toHexLower (unsafeShiftR w 44))+  writeByteArray arr (off + 5) (toHexLower (unsafeShiftR w 40))+  writeByteArray arr (off + 6) (toHexLower (unsafeShiftR w 36))+  writeByteArray arr (off + 7) (toHexLower (unsafeShiftR w 32))+  writeByteArray arr (off + 8) (toHexLower (unsafeShiftR w 28))+  writeByteArray arr (off + 9) (toHexLower (unsafeShiftR w 24))+  writeByteArray arr (off + 10) (toHexLower (unsafeShiftR w 20))+  writeByteArray arr (off + 11) (toHexLower (unsafeShiftR w 16))+  writeByteArray arr (off + 12) (toHexLower (unsafeShiftR w 12))+  writeByteArray arr (off + 13) (toHexLower (unsafeShiftR w 8))+  writeByteArray arr (off + 14) (toHexLower (unsafeShiftR w 4))+  writeByteArray arr (off + 15) (toHexLower (unsafeShiftR w 0))+  pure (off + 16)+  where+  w = W# w#+ word32PaddedUpperHex# :: Word# -> Builder 8 {-# noinline word32PaddedUpperHex# #-} word32PaddedUpperHex# w# = Unsafe.construct $ \arr off -> do@@ -430,6 +515,21 @@   where   w = W# w# +word32PaddedLowerHex# :: Word# -> Builder 8+{-# noinline word32PaddedLowerHex# #-}+word32PaddedLowerHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexLower (unsafeShiftR w 28))+  writeByteArray arr (off + 1) (toHexLower (unsafeShiftR w 24))+  writeByteArray arr (off + 2) (toHexLower (unsafeShiftR w 20))+  writeByteArray arr (off + 3) (toHexLower (unsafeShiftR w 16))+  writeByteArray arr (off + 4) (toHexLower (unsafeShiftR w 12))+  writeByteArray arr (off + 5) (toHexLower (unsafeShiftR w 8))+  writeByteArray arr (off + 6) (toHexLower (unsafeShiftR w 4))+  writeByteArray arr (off + 7) (toHexLower (unsafeShiftR w 0))+  pure (off + 8)+  where+  w = W# w#+ -- Not sure if it is beneficial to inline this. We just let -- GHC make the decision. Open an issue on github if this is -- a problem.@@ -665,6 +765,12 @@  word128BE :: Word128 -> Builder 16 word128BE (Word128 hi lo) = append (word64BE hi) (word64BE lo)++word256LE :: Word256 -> Builder 32+word256LE (Word256 hi mhi mlo lo) = word64LE lo `append` word64LE mlo `append` word64LE mhi `append` word64LE hi++word256BE :: Word256 -> Builder 32+word256BE (Word256 hi mhi mlo lo) = word64BE hi `append` word64BE mhi `append` word64BE mlo `append` word64BE lo  -- | Requires exactly 8 bytes. Dump the octets of a 64-bit -- word in a little-endian fashion.
test/Main.hs view
@@ -14,7 +14,7 @@ import Data.Char (ord,chr) import Data.IORef (IORef,newIORef,readIORef,writeIORef) import Data.Primitive (ByteArray)-import Data.WideWord (Word128(Word128))+import Data.WideWord (Word128(Word128),Word256(Word256)) import Test.Tasty (defaultMain,testGroup,TestTree) import Test.QuickCheck ((===),Arbitrary) import Text.Printf (printf)@@ -57,6 +57,14 @@         runConcat 1 (word64BE x <> word64BE y <> word64BE z)         ===         pack (LB.unpack (BB.toLazyByteString (BB.word64BE x <> BB.word64BE y <> BB.word64BE z)))+    , TQC.testProperty "word256PaddedLowerHex" $ \w ->+        Bounded.run Nat.constant (Bounded.word256PaddedLowerHex w)+        ===+        pack (showWord256PaddedLowerHex w)+    , TQC.testProperty "word128PaddedUpperHex" $ \w ->+        Bounded.run Nat.constant (Bounded.word128PaddedUpperHex w)+        ===+        pack (showWord128PaddedUpperHex w)     , TQC.testProperty "word64PaddedUpperHex" $ \w ->         runConcat 1 (word64PaddedUpperHex w)         ===@@ -193,6 +201,16 @@          in runConcat 1 (foldMap word128BE xs)             ===             runConcat 1 (word128ArrayBE ys 0 (Prelude.length xs))+    , TQC.testProperty "word256ArrayLE" $ \(xs :: [Word256]) ->+        let ys = Exts.fromList xs :: PrimArray Word256+         in runConcat 1 (foldMap word256LE xs)+            ===+            runConcat 1 (word256ArrayLE ys 0 (Prelude.length xs))+    , TQC.testProperty "word256ArrayBE" $ \(xs :: [Word256]) ->+        let ys = Exts.fromList xs :: PrimArray Word256+         in runConcat 1 (foldMap word256BE xs)+            ===+            runConcat 1 (word256ArrayBE ys 0 (Prelude.length xs))     ]   , testGroup "alternate"     [ TQC.testProperty "HexWord64" $ \x y ->@@ -256,11 +274,17 @@ packUtf8 :: String -> ByteArray packUtf8 = Exts.fromList . ByteString.unpack . TE.encodeUtf8 . T.pack +showWord256PaddedLowerHex :: Word256 -> String+showWord256PaddedLowerHex (Word256 hi mhi mlo lo) = printf "%016x%016x%016x%016x" hi mhi mlo lo++showWord128PaddedUpperHex :: Word128 -> String+showWord128PaddedUpperHex (Word128 hi lo) = printf "%016X%016X" hi lo+ showWord64PaddedUpperHex :: Word64 -> String-showWord64PaddedUpperHex = printf "%016X" +showWord64PaddedUpperHex = printf "%016X"  showWord16PaddedLowerHex :: Word16 -> String-showWord16PaddedLowerHex = printf "%04x" +showWord16PaddedLowerHex = printf "%04x"  runConcat :: Int -> Builder -> ByteArray runConcat n = Chunks.concatU . run n@@ -270,6 +294,9 @@  instance Arbitrary Word128 where   arbitrary = liftA2 Word128 TQC.arbitrary TQC.arbitrary++instance Arbitrary Word256 where+  arbitrary = Word256 <$> TQC.arbitrary <*> TQC.arbitrary <*> TQC.arbitrary <*> TQC.arbitrary  zeroPadL :: Int -> String -> String zeroPadL n s