Add function for zero-padded decimal encoding of two digit numers

This commit is contained in:
Andrew Martin 2019-12-30 20:14:15 -05:00 committed by GitHub
parent 10ed08c77d
commit 2b85e2cb84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 3 deletions

View file

@ -8,6 +8,7 @@
* Add `putManyConsLength`, useful for chunked HTTP encoding. * Add `putManyConsLength`, useful for chunked HTTP encoding.
* Add `runOnto` * Add `runOnto`
* Add `Data.Bytes.Chunks.length` * Add `Data.Bytes.Chunks.length`
* Add `wordPaddedTwoDigitDec`
## 0.3.1.0 -- 2019-11-20 ## 0.3.1.0 -- 2019-11-20

View file

@ -51,6 +51,8 @@ module Data.ByteArray.Builder.Bounded
, word8LowerHex , word8LowerHex
, ascii , ascii
, char , char
-- ** Native
, wordPaddedTwoDigitDec
-- ** Machine-Readable -- ** Machine-Readable
-- *** One -- *** One
, word8 , word8
@ -522,6 +524,22 @@ word8LowerHex# w#
where where
w = W# w# w = W# w#
-- | Encode a number less than 100 as a decimal number, zero-padding it to
-- two digits. For example: 0 is encoded as @00@, 5 is encoded as @05@, and
-- 73 is encoded as @73@.
--
-- Precondition: Argument less than 100. Failure to satisfy this precondition
-- will not result in a segfault, but the resulting bytes are undefined. The
-- implement uses a heuristic for division that is inaccurate for large
-- numbers.
wordPaddedTwoDigitDec :: Word -> Builder 2
wordPaddedTwoDigitDec !w = Unsafe.construct $ \arr off -> do
let d1 = approxDiv10 w
d2 = w - (10 * d1)
writeByteArray arr off (unsafeWordToWord8 (d1 + 48))
writeByteArray arr (off + 1) (unsafeWordToWord8 (d2 + 48))
pure (off + 2)
-- | Encode an ASCII character. -- | Encode an ASCII character.
-- Precondition: Input must be an ASCII character. This is not checked. -- Precondition: Input must be an ASCII character. This is not checked.
ascii :: Char -> Builder 1 ascii :: Char -> Builder 1
@ -561,9 +579,6 @@ char c
codepoint :: Word codepoint :: Word
codepoint = fromIntegral (ord c) codepoint = fromIntegral (ord c)
unsafeWordToWord8 :: Word -> Word8
unsafeWordToWord8 (W# w) = W8# w
-- precondition: codepoint is less than 0x800 -- precondition: codepoint is less than 0x800
byteTwoOne :: Word -> Word byteTwoOne :: Word -> Word
byteTwoOne w = unsafeShiftR w 6 .|. 0b11000000 byteTwoOne w = unsafeShiftR w 6 .|. 0b11000000
@ -807,3 +822,10 @@ unIntST s0 (ST f) = case f s0 of
-- result anyway. Hmm... -- result anyway. Hmm...
logBase10 :: Double -> Double logBase10 :: Double -> Double
logBase10 d = log d / 2.30258509299 logBase10 d = log d / 2.30258509299
-- Based on C code from https://stackoverflow.com/a/5558614
approxDiv10 :: Word -> Word
approxDiv10 !n = unsafeShiftR (0x1999999A * n) 32
unsafeWordToWord8 :: Word -> Word8
unsafeWordToWord8 (W# w) = W8# w

View file

@ -64,6 +64,10 @@ tests = testGroup "Tests"
runConcat 1 (word64PaddedUpperHex w) runConcat 1 (word64PaddedUpperHex w)
=== ===
pack (showWord64PaddedUpperHex w) pack (showWord64PaddedUpperHex w)
, TQC.testProperty "wordPaddedTwoDigitDec" $ TQC.forAll (TQC.choose (0,99)) $ \w ->
Bounded.run Nat.two (Bounded.wordPaddedTwoDigitDec w)
===
pack (zeroPadL 2 (show w))
, TQC.testProperty "word8Dec" $ \w -> , TQC.testProperty "word8Dec" $ \w ->
runConcat 1 (word8Dec w) runConcat 1 (word8Dec w)
=== ===
@ -278,3 +282,8 @@ c2w = fromIntegral . ord
instance Arbitrary Word128 where instance Arbitrary Word128 where
arbitrary = liftA2 Word128 TQC.arbitrary TQC.arbitrary arbitrary = liftA2 Word128 TQC.arbitrary TQC.arbitrary
zeroPadL :: Int -> String -> String
zeroPadL n s
| length s < n = replicate (n - length s) '0' ++ s
| otherwise = s