packages feed

small-bytearray-builder 0.2.0.0 → 0.2.1.0

raw patch · 7 files changed

+225/−10 lines, 7 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.ByteArray.Builder: Builder :: (forall s. MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)) -> Builder
- Data.ByteArray.Builder: newtype Builder
+ Data.ByteArray.Builder: data Builder
+ Data.ByteArray.Builder: word16LowerHex :: Word16 -> Builder
+ Data.ByteArray.Builder: word16PaddedLowerHex :: Word16 -> Builder
+ Data.ByteArray.Builder: word16UpperHex :: Word16 -> Builder
+ Data.ByteArray.Builder: word8LowerHex :: Word8 -> Builder
+ Data.ByteArray.Builder.Bounded: word16LowerHex :: Word16 -> Builder 4
+ Data.ByteArray.Builder.Bounded: word16PaddedLowerHex :: Word16 -> Builder 4
+ Data.ByteArray.Builder.Bounded: word16UpperHex :: Word16 -> Builder 4
+ Data.ByteArray.Builder.Bounded: word8LowerHex :: Word8 -> Builder 2

Files

CHANGELOG.md view
@@ -1,5 +1,15 @@ # Revision history for small-bytearray-builder +## 0.2.1.0 -- 2019-09-05++* Stop exporting data constructor in `Data.ByteArray.Builder`.+  This is technically a breaking change, but it was only+  exported by accident. So, with this release, we will technically+  violate PVP, and the previous release will be deprecated on hackage.+* Add more functions for encoding unsigned words: `word16PaddedLowerHex`,+  `word16LowerHex`, `word16UpperHex`, `word8LowerHex`.+* Unroll loop for `word8Dec`.+ ## 0.2.0.0 -- 2019-09-04  * Use `natural-arithmetic` to make manipulation of bounds possible.
small-bytearray-builder.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: small-bytearray-builder-version: 0.2.0.0+version: 0.2.1.0 synopsis: Serialize to a small byte arrays description:   This is similar to the builder facilities provided by
src/Data/ByteArray/Builder.hs view
@@ -8,7 +8,7 @@  module Data.ByteArray.Builder   ( -- * Bounded Primitives-    Builder(..)+    Builder   , construct   , fromBounded     -- * Evaluation@@ -38,10 +38,19 @@   , int16Dec   , int8Dec   , intDec+    -- * Unsigned Words+    -- ** 64-bit   , word64PaddedUpperHex+    -- ** 32-bit   , word32PaddedUpperHex+    -- ** 16-bit   , word16PaddedUpperHex+  , word16PaddedLowerHex+  , word16LowerHex+  , word16UpperHex+    -- ** 8-bit   , word8PaddedUpperHex+  , word8LowerHex   , ascii   , char     -- ** Machine-Readable@@ -351,6 +360,34 @@ word16PaddedUpperHex :: Word16 -> Builder word16PaddedUpperHex w =   fromBounded Nat.constant (Bounded.word16PaddedUpperHex w)++-- | Encode a 16-bit unsigned integer as hexadecimal, zero-padding+-- the encoding to 4 digits. This uses lowercase for the alphabetical+-- digits. For example, this encodes the number 1022 as @03fe@.+word16PaddedLowerHex :: Word16 -> Builder+word16PaddedLowerHex w =+  fromBounded Nat.constant (Bounded.word16PaddedLowerHex w)++-- | Encode a 16-bit unsigned integer as hexadecimal without leading+-- zeroes. This uses lowercase for the alphabetical digits. For+-- example, this encodes the number 1022 as @3fe@.+word16LowerHex :: Word16 -> Builder+word16LowerHex w =+  fromBounded Nat.constant (Bounded.word16LowerHex w)++-- | Encode a 16-bit unsigned integer as hexadecimal without leading+-- zeroes. This uses uppercase for the alphabetical digits. For+-- example, this encodes the number 1022 as @3FE@.+word16UpperHex :: Word16 -> Builder+word16UpperHex w =+  fromBounded Nat.constant (Bounded.word16UpperHex w)++-- | Encode a 16-bit unsigned integer as hexadecimal without leading+-- zeroes. This uses lowercase for the alphabetical digits. For+-- example, this encodes the number 1022 as @3FE@.+word8LowerHex :: Word8 -> Builder+word8LowerHex w =+  fromBounded Nat.constant (Bounded.word8LowerHex w)  -- | Encode a 8-bit unsigned integer as hexadecimal, zero-padding -- the encoding to 2 digits. This uses uppercase for the alphabetical
src/Data/ByteArray/Builder/Bounded.hs view
@@ -10,7 +10,8 @@ {-# language TypeOperators #-} {-# language UnboxedTuples #-} --- | The functions in this module are explict in the amount of bytes they require.+-- | The functions in this module are explict about the maximum number+-- of bytes they require. module Data.ByteArray.Builder.Bounded   ( -- * Builder     Builder@@ -35,10 +36,19 @@   , int16Dec   , int8Dec   , intDec+    -- * Unsigned Words+    -- ** 64-bit   , word64PaddedUpperHex+    -- ** 32-bit   , word32PaddedUpperHex+    -- ** 16-bit+  , word16PaddedLowerHex   , word16PaddedUpperHex+  , word16LowerHex+  , word16UpperHex+    -- ** 8-bit   , word8PaddedUpperHex+  , word8LowerHex   , ascii   , char     -- ** Machine-Readable@@ -67,6 +77,7 @@  import qualified Arithmetic.Types as Arithmetic import qualified Arithmetic.Nat as Nat+import qualified Arithmetic.Lte as Lte import qualified Data.ByteArray.Builder.Bounded.Unsafe as Unsafe import qualified Data.Primitive as PM @@ -161,7 +172,14 @@ -- | Requires up to 3 bytes. Encodes an unsigned 8-bit integer as decimal. -- This encoding never starts with a zero unless the argument was zero. word8Dec :: Word8 -> Builder 3-word8Dec (W8# w) = wordCommonDec# w+word8Dec (W8# w) =+  -- We unroll the loop when encoding Word8s. This speeds things+  -- up IPv4 encoding by about 10% in the @ip@ library. We can+  -- encode Word8s at twice this speed by using a lookup table.+  -- However, I (Andrew Martin) am concerned that although lookup+  -- table perform very well in microbenchmarks, they can thrash+  -- L1 cache in real applications.+  word8Dec# w  -- | Requires up to 19 bytes. Encodes an unsigned machine-sized integer -- as decimal. This encoding never starts with a zero unless the argument@@ -204,6 +222,22 @@ intDec :: Int -> Builder 20 intDec (I# w) = intCommonDec# w +word8Dec# :: Word# -> Builder 3+{-# noinline word8Dec# #-}+word8Dec# w# = Unsafe.construct $ \arr off0 -> do+  let !(I# off0# ) = off0+      !(!x,!ones) = quotRem w 10+      !(hundreds@(W# hundreds# ),tens@(W# tens# )) = quotRem x 10+  writeByteArray arr off0 (fromIntegral (hundreds + 0x30) :: Word8)+  let !hasHundreds = gtWord# hundreds# 0##+      !off1@(I# off1# ) = I# (off0# +# hasHundreds)+  writeByteArray arr off1 (fromIntegral (tens + 0x30) :: Word8)+  let !off2 = I# (off1# +# (orI# hasHundreds (gtWord# tens# 0## )))+  writeByteArray arr off2 (fromIntegral (ones + 0x30) :: Word8)+  pure (off2 + 1)+  where+  w = W# w#+ -- Requires a number of bytes that is bounded by the size of -- the word. This is only used internally. wordCommonDec# :: Word# -> Builder n@@ -259,6 +293,17 @@   loSolved = w + 48   hiSolved = w + 55 +toHexLower :: Word -> Word8+toHexLower w' = fromIntegral+    $ (complement theMask .&. loSolved)+  .|. (theMask .&. hiSolved)+  where+  w = w' .&. 0xF+  -- This is all ones if the value was >= 10+  theMask = (1 .&. unsafeShiftR (w - 10) 63) - 1+  loSolved = w + 48+  hiSolved = w + 87+ -- | 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@@ -275,9 +320,45 @@ -- | 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.+--+-- >>> word16PaddedUpperHex 0xab0+-- 0AB0 word16PaddedUpperHex :: Word16 -> Builder 4 word16PaddedUpperHex (W16# w) = word16PaddedUpperHex# w +-- | Requires exactly 4 bytes. Encodes a 16-bit unsigned integer as+-- hexadecimal, zero-padding the encoding to 4 digits. This uses+-- lowercase for the alphabetical digits.+--+-- >>> word16PaddedLowerHex 0xab0+-- 0ab0+word16PaddedLowerHex :: Word16 -> Builder 4+word16PaddedLowerHex (W16# w) = word16PaddedLowerHex# w++-- | Requires at most 4 bytes. Encodes a 16-bit unsigned integer as+-- hexadecimal. No leading zeroes are displayed. Letters are presented+-- in lowercase. If the number is zero, a single zero digit is used.+--+-- >>> word16LowerHex 0xab0+-- ab0+word16LowerHex :: Word16 -> Builder 4+word16LowerHex (W16# w) = word16LowerHex# w++-- | Requires at most 4 bytes. Encodes a 16-bit unsigned integer as+-- hexadecimal. No leading zeroes are displayed. Letters are presented+-- in uppercase. If the number is zero, a single zero digit is used.+--+-- >>> word16UpperHex 0xab0+-- AB0+word16UpperHex :: Word16 -> Builder 4+word16UpperHex (W16# w) = word16UpperHex# w++-- | Requires at most 2 bytes. Encodes a 8-bit unsigned integer as+-- hexadecimal. No leading zeroes are displayed. If the number is zero,+-- a single zero digit is used.+word8LowerHex :: Word8 -> Builder 2+word8LowerHex (W8# w) = word8LowerHex# w+ -- | Requires exactly 2 bytes. Encodes a 8-bit unsigned integer as -- hexadecimal, zero-padding the encoding to 2 digits. This uses -- uppercase for the alphabetical digits.@@ -337,13 +418,92 @@   where   w = W# w# +word16PaddedLowerHex# :: Word# -> Builder 4+word16PaddedLowerHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexLower (unsafeShiftR w 12))+  writeByteArray arr (off + 1) (toHexLower (unsafeShiftR w 8))+  writeByteArray arr (off + 2) (toHexLower (unsafeShiftR w 4))+  writeByteArray arr (off + 3) (toHexLower (unsafeShiftR w 0))+  pure (off + 4)+  where+  w = W# w#++word12PaddedLowerHex# :: Word# -> Builder 3+word12PaddedLowerHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexLower (unsafeShiftR w 8))+  writeByteArray arr (off + 1) (toHexLower (unsafeShiftR w 4))+  writeByteArray arr (off + 2) (toHexLower (unsafeShiftR w 0))+  pure (off + 3)+  where+  w = W# w#++word12PaddedUpperHex# :: Word# -> Builder 3+word12PaddedUpperHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexUpper (unsafeShiftR w 8))+  writeByteArray arr (off + 1) (toHexUpper (unsafeShiftR w 4))+  writeByteArray arr (off + 2) (toHexUpper (unsafeShiftR w 0))+  pure (off + 3)+  where+  w = W# w#+ -- Definitely want this to inline. It's maybe a dozen instructions total. word8PaddedUpperHex# :: Word# -> Builder 2-{-# inline word8PaddedUpperHex #-}+{-# inline word8PaddedUpperHex# #-} word8PaddedUpperHex# w# = Unsafe.construct $ \arr off -> do   writeByteArray arr off (toHexUpper (unsafeShiftR w 4))   writeByteArray arr (off + 1) (toHexUpper (unsafeShiftR w 0))   pure (off + 2)+  where+  w = W# w#++word8PaddedLowerHex# :: Word# -> Builder 2+{-# inline word8PaddedLowerHex# #-}+word8PaddedLowerHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexLower (unsafeShiftR w 4))+  writeByteArray arr (off + 1) (toHexLower (unsafeShiftR w 0))+  pure (off + 2)+  where+  w = W# w#++word4PaddedLowerHex# :: Word# -> Builder 1+{-# inline word4PaddedLowerHex# #-}+word4PaddedLowerHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexLower w)+  pure (off + 1)+  where+  w = W# w#++word4PaddedUpperHex# :: Word# -> Builder 1+{-# inline word4PaddedUpperHex# #-}+word4PaddedUpperHex# w# = Unsafe.construct $ \arr off -> do+  writeByteArray arr off (toHexUpper w)+  pure (off + 1)+  where+  w = W# w#++word16UpperHex# :: Word# -> Builder 4+word16UpperHex# w#+  | w <= 0xF = weaken Lte.constant (word4PaddedUpperHex# w#)+  | w <= 0xFF = weaken Lte.constant (word8PaddedUpperHex# w#)+  | w <= 0xFFF = weaken Lte.constant (word12PaddedUpperHex# w#)+  | otherwise = word16PaddedUpperHex# w#+  where+  w = W# w#++word16LowerHex# :: Word# -> Builder 4+word16LowerHex# w#+  | w <= 0xF = weaken Lte.constant (word4PaddedLowerHex# w#)+  | w <= 0xFF = weaken Lte.constant (word8PaddedLowerHex# w#)+  | w <= 0xFFF = weaken Lte.constant (word12PaddedLowerHex# w#)+  | otherwise = word16PaddedLowerHex# w#+  where+  w = W# w#++-- Precondition: argument less than 256+word8LowerHex# :: Word# -> Builder 2+word8LowerHex# w#+  | w <= 0xF = weaken Lte.constant (word4PaddedLowerHex# w#)+  | otherwise = weaken Lte.constant (word8PaddedLowerHex# w#)   where   w = W# w# 
src/Data/ByteArray/Builder/Bounded/Unsafe.hs view
@@ -27,8 +27,11 @@ -- when executed. newtype Builder :: Nat -> Type where    Builder ::-        (forall s. MutableByteArray# s -> Int# -> State# s -> (# State# s, Int# #))+        (forall s. MutableByteArray# s -> Int# -> State# s -> (# State# s, Int# #)) +        -- ^ This function takes a buffer, an offset, and a number of remaining bytes.+        --   It returns the new offset.      -> Builder n+     -- | Constructor for 'Builder' that works on a function with lifted -- arguments instead of unlifted ones. This is just as unsafe as the
src/Data/ByteArray/Builder/Unsafe.hs view
@@ -31,10 +31,11 @@  -- | An unmaterialized sequence of bytes that may be pasted -- into a mutable byte array.-newtype Builder = Builder-  -- This functions takes an offset and a number of remaining bytes-  -- and returns the new offset.-  (forall s. MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #))+newtype Builder+  = Builder (forall s. MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #))+    -- ^ This function takes a buffer, an offset, and a number of remaining bytes.+    --   It returns the new offset (should be greater than the old offset), or if+    --   there was not enough space left in buffer, it returns -1.  instance IsString Builder where   {-# inline fromString #-}
test/Main.hs view
@@ -53,6 +53,10 @@         run 1 (word64PaddedUpperHex w)         ===         pack (showWord64PaddedUpperHex w)+    , TQC.testProperty "word8Dec" $ \w ->+        run 1 (word8Dec w)+        ===+        pack (show w)     , TQC.testProperty "pasteArrayST" $ \(xs :: [Word64]) ->         (runArray word64Dec (V.fromList xs))         ===