{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
#ifdef BYTESTRING_STRICT
module Data.ByteString.ReadNat
#else
module Data.ByteString.Lazy.ReadNat
#endif
( readInteger
, readNatural
) where
import qualified Data.ByteString.Internal as BI
#ifdef BYTESTRING_STRICT
import Data.ByteString
#else
import Data.ByteString.Lazy
import Data.ByteString.Lazy.Internal
#endif
import Data.Bits (finiteBitSize)
import Data.ByteString.Internal (pattern BS, plusForeignPtr)
import Data.Word
import Foreign.ForeignPtr (ForeignPtr)
import Foreign.Ptr (Ptr, minusPtr, plusPtr)
import Foreign.Storable (Storable(..))
import Numeric.Natural (Natural)
readInteger :: ByteString -> Maybe (Integer, ByteString)
readInteger :: ByteString -> Maybe (Integer, ByteString)
readInteger = \ ByteString
bs -> do
(Word8
w, ByteString
s) <- ByteString -> Maybe (Word8, ByteString)
uncons ByteString
bs
let d :: Word
d = Word8 -> Word
fromDigit Word8
w
if | Word
d forall a. Ord a => a -> a -> Bool
<= Word
9 -> Word -> ByteString -> Maybe (Integer, ByteString)
unsigned Word
d ByteString
s
| Word8
w forall a. Eq a => a -> a -> Bool
== Word8
0x2d -> ByteString -> Maybe (Integer, ByteString)
negative ByteString
s
| Word8
w forall a. Eq a => a -> a -> Bool
== Word8
0x2b -> ByteString -> Maybe (Integer, ByteString)
positive ByteString
s
| Bool
otherwise -> forall a. Maybe a
Nothing
where
unsigned :: Word -> ByteString -> Maybe (Integer, ByteString)
unsigned :: Word -> ByteString -> Maybe (Integer, ByteString)
unsigned Word
d ByteString
s =
let (!Natural
n, ByteString
rest) = Word -> ByteString -> (Natural, ByteString)
_readDecimal Word
d ByteString
s
!i :: Integer
i = forall a. Integral a => a -> Integer
toInteger Natural
n
in forall a. a -> Maybe a
Just (Integer
i, ByteString
rest)
positive :: ByteString -> Maybe (Integer, ByteString)
positive :: ByteString -> Maybe (Integer, ByteString)
positive ByteString
bs = do
(Word8
w, ByteString
s) <- ByteString -> Maybe (Word8, ByteString)
uncons ByteString
bs
let d :: Word
d = Word8 -> Word
fromDigit Word8
w
if | Word
d forall a. Ord a => a -> a -> Bool
<= Word
9 -> Word -> ByteString -> Maybe (Integer, ByteString)
unsigned Word
d ByteString
s
| Bool
otherwise -> forall a. Maybe a
Nothing
negative :: ByteString -> Maybe (Integer, ByteString)
negative :: ByteString -> Maybe (Integer, ByteString)
negative ByteString
bs = do
(Word8
w, ByteString
s) <- ByteString -> Maybe (Word8, ByteString)
uncons ByteString
bs
let d :: Word
d = Word8 -> Word
fromDigit Word8
w
if | Word
d forall a. Ord a => a -> a -> Bool
> Word
9 -> forall a. Maybe a
Nothing
| Bool
otherwise -> let (Natural
n, ByteString
rest) = Word -> ByteString -> (Natural, ByteString)
_readDecimal Word
d ByteString
s
!i :: Integer
i = forall a. Num a => a -> a
negate forall a b. (a -> b) -> a -> b
$ forall a. Integral a => a -> Integer
toInteger Natural
n
in forall a. a -> Maybe a
Just (Integer
i, ByteString
rest)
readNatural :: ByteString -> Maybe (Natural, ByteString)
readNatural :: ByteString -> Maybe (Natural, ByteString)
readNatural ByteString
bs = do
(Word8
w, ByteString
s) <- ByteString -> Maybe (Word8, ByteString)
uncons ByteString
bs
let d :: Word
d = Word8 -> Word
fromDigit Word8
w
if | Word
d forall a. Ord a => a -> a -> Bool
<= Word
9 -> forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$! Word -> ByteString -> (Natural, ByteString)
_readDecimal Word
d ByteString
s
| Bool
otherwise -> forall a. Maybe a
Nothing
data Result = Result !Int
!Word
!Int
[Natural]
_readDecimal :: Word -> ByteString -> (Natural, ByteString)
_readDecimal :: Word -> ByteString -> (Natural, ByteString)
_readDecimal =
[Natural] -> Int -> Word -> ByteString -> (Natural, ByteString)
consume [] Int
2
where
consume :: [Natural] -> Int -> Word -> ByteString
-> (Natural, ByteString)
#ifdef BYTESTRING_STRICT
consume ns cnt acc (BS fp len) =
case natdigits fp len acc cnt ns of
Result used acc' cnt' ns'
| used == len
-> convert acc' cnt' ns' $ empty
| otherwise
-> convert acc' cnt' ns' $
BS (fp `plusForeignPtr` used) (len - used)
#else
consume :: [Natural] -> Int -> Word -> ByteString -> (Natural, ByteString)
consume [Natural]
ns Int
cnt Word
acc ByteString
Empty = forall {b}. Word -> Int -> [Natural] -> b -> (Natural, b)
convert Word
acc Int
cnt [Natural]
ns ByteString
Empty
consume [Natural]
ns Int
cnt Word
acc (Chunk (BS ForeignPtr Word8
fp Int
len) ByteString
cs)
= case ForeignPtr Word8 -> Int -> Word -> Int -> [Natural] -> Result
natdigits ForeignPtr Word8
fp Int
len Word
acc Int
cnt [Natural]
ns of
Result Int
used Word
acc' Int
cnt' [Natural]
ns'
| Int
used forall a. Eq a => a -> a -> Bool
== Int
len
-> [Natural] -> Int -> Word -> ByteString -> (Natural, ByteString)
consume [Natural]
ns' Int
cnt' Word
acc' ByteString
cs
| Bool
otherwise
-> let c :: ByteString
c = ByteString -> ByteString -> ByteString
Chunk (ForeignPtr Word8 -> Int -> ByteString
BS (ForeignPtr Word8
fp forall a b. ForeignPtr a -> Int -> ForeignPtr b
`plusForeignPtr` Int
used) (Int
len forall a. Num a => a -> a -> a
- Int
used)) ByteString
cs
in forall {b}. Word -> Int -> [Natural] -> b -> (Natural, b)
convert Word
acc' Int
cnt' [Natural]
ns' ByteString
c
#endif
convert :: Word -> Int -> [Natural] -> b -> (Natural, b)
convert !Word
acc !Int
cnt ![Natural]
ns b
rest =
let !n :: Natural
n = Word -> Int -> [Natural] -> Natural
combine Word
acc Int
cnt [Natural]
ns
in (Natural
n, b
rest)
combine :: Word
-> Int
-> [Natural]
-> Natural
{-# INLINE combine #-}
combine :: Word -> Int -> [Natural] -> Natural
combine !Word
acc !Int
_ [] = Word -> Natural
wordToNatural Word
acc
combine !Word
acc !Int
cnt [Natural]
ns =
Word -> Natural
wordToNatural (Word
10forall a b. (Num a, Integral b) => a -> b -> a
^Int
cnt) forall a. Num a => a -> a -> a
* Natural -> [Natural] -> Natural
combine1 Natural
safeBase [Natural]
ns forall a. Num a => a -> a -> a
+ Word -> Natural
wordToNatural Word
acc
combine1 :: Natural -> [Natural] -> Natural
combine1 :: Natural -> [Natural] -> Natural
combine1 Natural
_ [Natural
n] = Natural
n
combine1 Natural
base [Natural]
ns = Natural -> [Natural] -> Natural
combine1 (Natural
base forall a. Num a => a -> a -> a
* Natural
base) (Natural -> [Natural] -> [Natural]
combine2 Natural
base [Natural]
ns)
combine2 :: Natural -> [Natural] -> [Natural]
combine2 :: Natural -> [Natural] -> [Natural]
combine2 Natural
base (Natural
n:Natural
m:[Natural]
ns) = let !t :: Natural
t = Natural
m forall a. Num a => a -> a -> a
* Natural
base forall a. Num a => a -> a -> a
+ Natural
n in Natural
t forall a. a -> [a] -> [a]
: Natural -> [Natural] -> [Natural]
combine2 Natural
base [Natural]
ns
combine2 Natural
_ [Natural]
ns = [Natural]
ns
natdigits :: ForeignPtr Word8
-> Int
-> Word
-> Int
-> [Natural]
-> Result
{-# INLINE natdigits #-}
natdigits :: ForeignPtr Word8 -> Int -> Word -> Int -> [Natural] -> Result
natdigits ForeignPtr Word8
fp Int
len = \ Word
acc Int
cnt [Natural]
ns ->
forall a. IO a -> a
BI.accursedUnutterablePerformIO forall a b. (a -> b) -> a -> b
$
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
BI.unsafeWithForeignPtr ForeignPtr Word8
fp forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
ptr -> do
let end :: Ptr b
end = Ptr Word8
ptr forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
len
forall {b}.
Ptr b
-> Ptr Word8 -> Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
go Ptr Word8
ptr forall {b}. Ptr b
end Word
acc Int
cnt [Natural]
ns Ptr Word8
ptr
where
go :: Ptr b
-> Ptr Word8 -> Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
go !Ptr b
start !Ptr Word8
end = Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
loop
where
loop :: Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
loop :: Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
loop !Word
acc !Int
cnt [Natural]
ns !Ptr Word8
ptr = IO Word
getDigit forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ !Word
d ->
if | Word
d forall a. Ord a => a -> a -> Bool
> Word
9
-> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int -> Word -> Int -> [Natural] -> Result
Result (Ptr Word8
ptr forall a b. Ptr a -> Ptr b -> Int
`minusPtr` Ptr b
start) Word
acc Int
cnt [Natural]
ns
| Int
cnt forall a. Ord a => a -> a -> Bool
< Int
safeLog
-> Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
loop (Word
10forall a. Num a => a -> a -> a
*Word
acc forall a. Num a => a -> a -> a
+ Word
d) (Int
cntforall a. Num a => a -> a -> a
+Int
1) [Natural]
ns forall a b. (a -> b) -> a -> b
$ Ptr Word8
ptr forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
1
| Bool
otherwise
-> let !acc' :: Natural
acc' = Word -> Natural
wordToNatural Word
acc
in Word -> Int -> [Natural] -> Ptr Word8 -> IO Result
loop Word
d Int
1 (Natural
acc' forall a. a -> [a] -> [a]
: [Natural]
ns) forall a b. (a -> b) -> a -> b
$ Ptr Word8
ptr forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
1
where
getDigit :: IO Word
getDigit | Ptr Word8
ptr forall a. Eq a => a -> a -> Bool
/= Ptr Word8
end = Word8 -> Word
fromDigit forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Storable a => Ptr a -> IO a
peek Ptr Word8
ptr
| Bool
otherwise = forall (f :: * -> *) a. Applicative f => a -> f a
pure Word
10
{-# NOINLINE getDigit #-}
safeLog :: Int
safeLog :: Int
safeLog = Int
3 forall a. Num a => a -> a -> a
* forall b. FiniteBits b => b -> Int
finiteBitSize @Word Word
0 forall a. Integral a => a -> a -> a
`div` Int
10
safeBase :: Natural
safeBase :: Natural
safeBase = Natural
10 forall a b. (Num a, Integral b) => a -> b -> a
^ Int
safeLog
fromDigit :: Word8 -> Word
{-# INLINE fromDigit #-}
fromDigit :: Word8 -> Word
fromDigit = \ !Word8
w -> forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
w forall a. Num a => a -> a -> a
- Word
0x30
wordToNatural :: Word -> Natural
{-# INLINE wordToNatural #-}
wordToNatural :: Word -> Natural
wordToNatural = forall a b. (Integral a, Num b) => a -> b
fromIntegral