benc-0.1.0.0: src/Data/Bencode/Util.hs
module Data.Bencode.Util
( readKnownNaturalAsInt
, readKnownNaturalAsWord
) where
import Data.Bits
import qualified Data.ByteString as B
-- | The input string must be an unsigned decimal integer with no extraneous
-- leading zeros. Returns Nothing if the value is outside the bounds of an
-- @Int@.
readKnownNaturalAsInt :: Bool -> B.ByteString -> Maybe Int
readKnownNaturalAsInt neg s = case B.uncons sr of
Nothing -> Just $! if neg then -n else n
Just (d,sr')
| B.null sr'
, let d' = fromIntegral d - 48
, n < iMaxDiv10 || n == iMaxDiv10 && d' <= (7 + fromEnum neg)
-- last digit of maxBound = 7, minBound = 8
-> Just $! if neg then -n * 10 - d' else n * 10 + d'
| otherwise -> Nothing
where
(sl,sr) = B.splitAt (maxIntLen - 1) s
n = B.foldl' (\acc d -> acc * 10 + fromIntegral d - 48) 0 sl
iMaxDiv10 :: Int
iMaxDiv10 = maxBound `div` 10
maxIntLen :: Int
maxIntLen = case finiteBitSize (0 :: Int) of
32 -> 10
64 -> 19
_ -> error "unsupported word size"
-- | The input string must be an unsigned decimal integer with no extraneous
-- leading zeros. Returns Nothing if the value is outside the bounds of a
-- @Word@.
readKnownNaturalAsWord :: B.ByteString -> Maybe Word
readKnownNaturalAsWord s = case B.uncons sr of
Nothing -> Just $! n
Just (d,sr')
| B.null sr'
, let d' = fromIntegral d - 48
, n < wMaxDiv10 || n == wMaxDiv10 && d' <= 5 -- last digit of maxBound is 5
-> Just $! n * 10 + d'
| otherwise -> Nothing
where
(sl,sr) = B.splitAt (maxWordLen - 1) s
n = B.foldl' (\acc d -> acc * 10 + fromIntegral d - 48) 0 sl
wMaxDiv10 :: Word
wMaxDiv10 = maxBound `div` 10
maxWordLen :: Int
maxWordLen = case finiteBitSize (0 :: Word) of
32 -> 10
64 -> 20
_ -> error "unsupported word size"