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 +6/−0
- small-bytearray-builder.cabal +1/−1
- src/Data/ByteArray/Builder.hs +51/−4
- src/Data/ByteArray/Builder/Bounded.hs +107/−1
- test/Main.hs +30/−3
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