flatparse-0.5.3.0: src/FlatParse/Common/Numbers.hs
{-# LANGUAGE UnboxedTuples, BinaryLiterals #-}
module FlatParse.Common.Numbers where
import FlatParse.Common.Assorted ( shortInteger )
import Language.Haskell.TH.Syntax (lift)
import GHC.Exts
import GHC.ForeignPtr
import qualified Data.ByteString.Char8 as BC8
import qualified Data.ByteString.Internal as B
-- | Parse a non-empty ASCII decimal digit sequence as a 'Word'.
-- Fails on overflow.
anyAsciiDecimalWord# :: Addr# -> Addr# -> (# (##) | (# Word#, Addr# #) #)
anyAsciiDecimalWord# eob s = case anyAsciiDecimalWord_# 0## eob s of
(# | (# n, s' #) #) | 0# <- eqAddr# s s'
-> (# | (# n, s' #) #)
_ -> (# (# #) | #)
{-# inline anyAsciiDecimalWord# #-}
-- | Parse a non-empty ASCII decimal digit sequence as a positive 'Int'.
-- Fails on overflow.
anyAsciiDecimalInt# :: Addr# -> Addr# -> (# (##) | (# Int#, Addr# #) #)
anyAsciiDecimalInt# eob s = case anyAsciiDecimalWord_# 0## eob s of
(# | (# n, s' #) #) | 0# <- eqAddr# s s'
, 1# <- leWord# n (int2Word# (unI# maxBound))
-> (# | (# word2Int# n, s' #) #)
_ -> (# (##) | #)
{-# inline anyAsciiDecimalInt# #-}
anyAsciiDecimalWord_# :: Word# -> Addr# -> Addr# -> (# (##) | (# Word#, Addr# #) #)
anyAsciiDecimalWord_# acc eob s = case eqAddr# s eob of
1# -> (# | (# acc, s #) #)
_ -> case indexWord8OffAddr# s 0# of
#if MIN_VERSION_base(4,16,0)
w | 1# <- leWord8# (wordToWord8# 0x30##) w
, 1# <- leWord8# w (wordToWord8# 0x39##)
-> case timesWord2# acc 10## of
(# 0##, r #) -> case addWordC# r (word8ToWord# w `minusWord#` 0x30##) of
#else
w | 1# <- leWord# 0x30## w
, 1# <- leWord# w 0x39##
-> case timesWord2# acc 10## of
(# 0##, r #) -> case addWordC# r (w `minusWord#` 0x30##) of
#endif
(# q, 0# #) -> anyAsciiDecimalWord_# q eob (s `plusAddr#` 1#)
_ -> (# (##) | #)
_ -> (# (##) | #)
_ -> (# | (# acc, s #) #)
--------------------------------------------------------------------------------
-- | Parse a non-empty ASCII decimal digit sequence as a positive 'Int'.
-- May overflow.
anyAsciiDecimalIntOverflow# :: Addr# -> Addr# -> (# (##) | (# Int#, Addr# #) #)
anyAsciiDecimalIntOverflow# eob s = case anyAsciiDecimalIntOverflow_# 0# eob s of
(# n, s' #) | 0# <- eqAddr# s s'
-> (# | (# n, s' #) #)
| otherwise
-> (# (##) | #)
{-# inline anyAsciiDecimalIntOverflow# #-}
-- | Parse a non-empty ASCII decimal digit sequence as a positive 'Integer'.
anyAsciiDecimalInteger# :: ForeignPtrContents -> Addr# -> Addr# -> (# (##) | (# Integer, Addr# #) #)
anyAsciiDecimalInteger# fp eob s = case anyAsciiDecimalIntOverflow_# 0# eob s of
(# n, s' #)
| 1# <- eqAddr# s s' -> (# (##) | #)
-- Simple heuristic, using the largest number of digits that can be in an
-- 'Int#', such that we can use the 'IS' constructor.
| 1# <- minusAddr# s' s <=# maxDigitsInt -> (# | (# shortInteger n, s' #) #)
| otherwise -> case BC8.readInteger (B.PS (ForeignPtr s fp) 0 (I# (minusAddr# s' s))) of
Nothing -> (# (##) | #)
Just (i, _) -> (# | (# i, s' #) #)
where
maxDigitsInt :: Int#
!(I# maxDigitsInt) = $(let p e = 10 ^ e - 1 <= toInteger (maxBound :: Int)
in lift $ last $ takeWhile p [0 :: Int ..])
{-# inline anyAsciiDecimalInteger# #-}
-- | Parse a non-empty ASCII decimal digit sequence as a positive 'Int'.
-- May overflow.
anyAsciiDecimalIntOverflow_# :: Int# -> Addr# -> Addr# -> (# Int#, Addr# #)
anyAsciiDecimalIntOverflow_# acc eob s = case eqAddr# s eob of
1# -> (# acc, s #)
_ -> case indexWord8OffAddr# s 0# of
#if MIN_VERSION_base(4,16,0)
w | 1# <- leWord8# (wordToWord8# 0x30##) w, 1# <- leWord8# w (wordToWord8# 0x39##) ->
anyAsciiDecimalIntOverflow_# (mul10# acc +# (word2Int# (word8ToWord# w) -# 0x30#)) eob (plusAddr# s 1#)
#else
w | 1# <- leWord# 0x30## w, 1# <- leWord# w 0x39## ->
anyAsciiDecimalIntOverflow_# (mul10# acc +# (word2Int# w -# 0x30#)) eob (plusAddr# s 1#)
#endif
_ -> (# acc, s #)
--------------------------------------------------------------------------------
-- | Parse a non-empty, case-insensitive ASCII hexadecimal digit sequence as a
-- 'Word'.
-- Fails on overflow.
anyAsciiHexWord# :: Addr# -> Addr# -> (# (##) | (# Word#, Addr# #) #)
anyAsciiHexWord# eob s = case anyAsciiHexWord_# 0## eob s of
(# | (# n, s' #) #) | 0# <- eqAddr# s s'
-> (# | (# n, s' #) #)
_ -> (# (# #) | #)
{-# inline anyAsciiHexWord# #-}
-- | Parse a non-empty, case-insensitive ASCII hexadecimal digit sequence as a
-- positive 'Int'.
-- Fails on overflow.
anyAsciiHexInt# :: Addr# -> Addr# -> (# (##) | (# Int#, Addr# #) #)
anyAsciiHexInt# eob s = case anyAsciiHexWord_# 0## eob s of
(# | (# n, s' #) #) | 0# <- eqAddr# s s'
, 1# <- leWord# n (int2Word# (unI# maxBound))
-> (# | (# word2Int# n, s' #) #)
| otherwise
-> (# (##) | #)
(# (##) | #) -> (# (##) | #)
{-# inline anyAsciiHexInt# #-}
anyAsciiHexWord_# :: Word# -> Addr# -> Addr# -> (# (##) | (# Word#, Addr# #) #)
anyAsciiHexWord_# acc eob s = case eqAddr# s eob of
1# -> (# | (# acc, s #) #)
_ -> case indexWord8OffAddr# s 0# of
#if MIN_VERSION_base(4,16,0)
w | 1# <- leWord8# (wordToWord8# 0x30##) w
, 1# <- leWord8# w (wordToWord8# 0x39##)
-> case timesWord2# acc 16## of
(# 0##, r #) -> case addWordC# r (word8ToWord# w `minusWord#` 0x30##) of
#else
w | 1# <- leWord# 0x30## w
, 1# <- leWord# w 0x39##
-> case timesWord2# acc 16## of
(# 0##, r #) -> case addWordC# r (w `minusWord#` 0x30##) of
#endif
(# q, 0# #) -> anyAsciiHexWord_# q eob (s `plusAddr#` 1#)
_ -> (# (##) | #)
_ -> (# (##) | #)
#if MIN_VERSION_base(4,16,0)
| 1# <- leWord8# (wordToWord8# 0x41##) w
, 1# <- leWord8# w (wordToWord8# 0x46##)
-> case timesWord2# acc 16## of
(# 0##, r #) -> case addWordC# r (word8ToWord# w `minusWord#` 0x37##) of
#else
| 1# <- leWord# 0x41## w
, 1# <- leWord# w 0x46##
-> case timesWord2# acc 16## of
(# 0##, r #) -> case addWordC# r (w `minusWord#` 0x37##) of
#endif
(# q, 0# #) -> anyAsciiHexWord_# q eob (s `plusAddr#` 1#)
_ -> (# (##) | #)
_ -> (# (##) | #)
#if MIN_VERSION_base(4,16,0)
| 1# <- leWord8# (wordToWord8# 0x61##) w
, 1# <- leWord8# w (wordToWord8# 0x66##)
-> case timesWord2# acc 16## of
(# 0##, r #) -> case addWordC# r (word8ToWord# w `minusWord#` 0x57##) of
#else
| 1# <- leWord# 0x61## w
, 1# <- leWord# w 0x66##
-> case timesWord2# acc 16## of
(# 0##, r #) -> case addWordC# r (w `minusWord#` 0x57##) of
#endif
(# q, 0# #) -> anyAsciiHexWord_# q eob (s `plusAddr#` 1#)
_ -> (# (##) | #)
_ -> (# (##) | #)
_ -> (# | (# acc, s #) #)
--------------------------------------------------------------------------------
-- Zigzag encoding
-- See: https://hackage.haskell.org/package/zigzag-0.0.1.0/docs/src/Data.Word.Zigzag.html
fromZigzagNative :: Word -> Int
fromZigzagNative (W# w#) = I# (fromZigzagNative# w#)
{-# inline fromZigzagNative #-}
-- GHC should optimize to this, but to be sure, here it is
fromZigzagNative# :: Word# -> Int#
fromZigzagNative# w# =
word2Int# ((w# `uncheckedShiftRL#` 1#) `xor#` (not# (w# `and#` 1##) `plusWord#` 1##))
{-# inline fromZigzagNative# #-}
toZigzagNative :: Int -> Word
toZigzagNative (I# i#) = W# (toZigzagNative# i#)
{-# inline toZigzagNative #-}
-- GHC should optimize to this, but to be sure, here it is
toZigzagNative# :: Int# -> Word#
toZigzagNative# i# = toZigzagNative'# (int2Word# i#)
{-# inline toZigzagNative# #-}
-- GHC should optimize to this, but to be sure, here it is
toZigzagNative'# :: Word# -> Word#
toZigzagNative'# w# =
(w# `uncheckedShiftL#` 1#) `xor#` (w# `uncheckedShiftRL#` 63#)
{-# inline toZigzagNative'# #-}
--------------------------------------------------------------------------------
-- | protobuf style (LE, redundant, on continues)
anyVarintProtobuf# :: Addr# -> Addr# -> (# (##) | (# Int#, Addr#, Int# #) #)
#if MIN_VERSION_base(4,16,0)
anyVarintProtobuf# end# = go 0# 0#
where
word8ToInt# :: Word8# -> Int#
word8ToInt# w8# = word2Int# (word8ToWord# w8#)
{-# inline word8ToInt# #-}
go :: Int# -> Int# -> Addr# -> (# (##) | (# Int#, Addr#, Int# #) #)
go i# n# s# = case eqAddr# s# end# of
1# -> (# (##) | #)
_ ->
let w8# = indexWord8OffAddr# s# 0#
w8'# = word8ToInt# (w8# `andWord8#` (wordToWord8# 0b01111111##))
i'# = i# `orI#` (w8'# `uncheckedIShiftL#` n#)
s'# = s# `plusAddr#` 1#
n'# = n# +# 7#
in case w8# `geWord8#` wordToWord8# 0b10000000## of
1# -> go i'# n'# s'#
_ -> (# | (# i'#, s'#, n'# #) #)
#else
anyVarintProtobuf# end# = go 0# 0#
where
go :: Int# -> Int# -> Addr# -> (# (##) | (# Int#, Addr#, Int# #) #)
go i# n# s# = case eqAddr# s# end# of
1# -> (# (##) | #)
_ ->
let w8# = indexWord8OffAddr# s# 0#
w8'# = word2Int# (w8# `and#` 0b01111111##)
i'# = i# `orI#` (w8'# `uncheckedIShiftL#` n#)
s'# = s# `plusAddr#` 1#
n'# = n# +# 7#
in case w8# `geWord#` 0b10000000## of
1# -> go i'# n'# s'#
_ -> (# | (# i'#, s'#, n'# #) #)
#endif
--------------------------------------------------------------------------------
unI# :: Int -> Int#
unI# (I# i) = i
{-# inline unI# #-}
mul10# :: Int# -> Int#
mul10# n = uncheckedIShiftL# n 3# +# uncheckedIShiftL# n 1#
{-# inline mul10# #-}