Skip to content

Commit

Permalink
Merge pull request haskell#1031 from haskell/integer-conversion
Browse files Browse the repository at this point in the history
Use integer-conversion
  • Loading branch information
phadej authored Jun 8, 2023
2 parents a9a4a84 + f077e98 commit 17f8946
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 152 deletions.
2 changes: 1 addition & 1 deletion aeson.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ library
Data.Aeson.Encoding.Builder
Data.Aeson.Internal.ByteString
Data.Aeson.Internal.Functions
Data.Aeson.Internal.Integer
Data.Aeson.Internal.Prelude
Data.Aeson.Internal.Text
Data.Aeson.Internal.TH
Expand Down Expand Up @@ -121,6 +120,7 @@ library
, dlist >=1.0 && <1.1
, hashable >=1.3.5.0 && <1.5
, indexed-traversable >=0.1.2 && <0.2
, integer-conversion >=0.1 && <0.2
, OneTuple >=0.4.1.1 && <0.5
, primitive >=0.8.0.0 && <0.9
, QuickCheck >=2.14.3 && <2.15
Expand Down
68 changes: 41 additions & 27 deletions attoparsec-iso8601/attoparsec-iso8601.cabal
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
name: attoparsec-iso8601
version: 1.1.0.1
synopsis: Parsing of ISO 8601 dates, originally from aeson
description: Parsing of ISO 8601 dates, originally from aeson.
license: BSD3
license-file: LICENSE
category: Parsing
copyright: (c) 2011-2016 Bryan O'Sullivan
(c) 2011 MailRank, Inc.
author: Bryan O'Sullivan <[email protected]>
maintainer: Adam Bergmark <[email protected]>
stability: experimental
cabal-version: >=1.10
homepage: https://github.com/haskell/aeson
bug-reports: https://github.com/haskell/aeson/issues
build-type: Simple
tested-with: GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.7, GHC == 9.0.2, GHC ==9.2.8, GHC==9.4.5, GHC ==9.6.2
name: attoparsec-iso8601
version: 1.1.0.1
synopsis: Parsing of ISO 8601 dates, originally from aeson
description: Parsing of ISO 8601 dates, originally from aeson.
license: BSD3
license-file: LICENSE
category: Parsing
copyright:
(c) 2011-2016 Bryan O'Sullivan
(c) 2011 MailRank, Inc.

author: Bryan O'Sullivan <[email protected]>
maintainer: Adam Bergmark <[email protected]>
stability: experimental
cabal-version: >=1.10
homepage: https://github.com/haskell/aeson
bug-reports: https://github.com/haskell/aeson/issues
build-type: Simple
tested-with:
GHC ==8.2.2
|| ==8.4.4
|| ==8.6.5
|| ==8.8.4
|| ==8.10.7
|| ==9.0.2
|| ==9.2.8
|| ==9.4.5
|| ==9.6.2

extra-source-files:
README.md
changelog.md
README.md

library
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall
exposed-modules:
Data.Attoparsec.Time.Internal
Data.Attoparsec.Time
Data.Attoparsec.Time.Internal

build-depends:
attoparsec >= 0.14.2 && < 0.15,
base >= 4.10.0.0 && < 5,
time-compat >= 1.9.4 && < 1.10,
text >= 1.2.3.0 && < 1.3.0.0 || >= 2.0 && <2.1,
time >= 1.6.0.1 && < 1.13
attoparsec >=0.14.2 && <0.15
, base >=4.10.0.0 && <5
, integer-conversion >=0.1 && <0.2
, text >=1.2.3.0 && <1.3.0.0 || >=2.0 && <2.1
, time >=1.6.0.1 && <1.13
, time-compat >=1.9.4 && <1.10

source-repository head
type: git
Expand Down
58 changes: 4 additions & 54 deletions attoparsec-iso8601/src/Data/Attoparsec/Time.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ module Data.Attoparsec.Time
import Control.Applicative ((<|>))
import Control.Monad (void, when)
import Data.Attoparsec.Text (Parser, char, digit, option, anyChar, peekChar, peekChar', takeWhile1, satisfy)
import Data.Attoparsec.Time.Internal (toPico)
import Data.Bits ((.&.))
import Data.Char (isDigit, ord)
import Data.Fixed (Pico)
import Data.Fixed (Pico, Fixed (..))
import Data.Int (Int64)
import Data.Integer.Conversion (textToInteger)
import Data.Maybe (fromMaybe)
import Data.Time.Calendar (Day, fromGregorianValid)
import Data.Time.Calendar.Compat (Year)
Expand Down Expand Up @@ -92,7 +92,7 @@ year = do
if T.length ds < 4 then
fail "expected year with at least 4 digits"
else
pure (txtToInteger ds)
pure (textToInteger ds)

-- | Parse a two-digit integer (e.g. day of month, hour).
twoDigits :: Parser Int
Expand Down Expand Up @@ -126,7 +126,7 @@ seconds = do
return $! parsePicos real t
_ -> return $! fromIntegral real
where
parsePicos a0 t = toPico (fromIntegral (t' * 10^n))
parsePicos a0 t = MkFixed (fromIntegral (t' * 10^n))
where T n t' = T.foldl' step (T 12 (fromIntegral a0)) t
step ma@(T m a) c
| m <= 0 = ma
Expand Down Expand Up @@ -194,53 +194,3 @@ zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)

utc :: Local.TimeZone
utc = Local.TimeZone 0 False ""

------------------ Copy-pasted and adapted from base ------------------------

txtToInteger :: T.Text -> Integer
txtToInteger bs
| l > 40 = valInteger 10 l [ fromIntegral (ord w - 48) | w <- T.unpack bs ]
| otherwise = txtToIntegerSimple bs
where
l = T.length bs

txtToIntegerSimple :: T.Text -> Integer
txtToIntegerSimple = T.foldl' step 0 where
step a b = a * 10 + fromIntegral (ord b - 48) -- 48 = '0'

-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
-- digits are combined into a single radix b^2 digit. This process is
-- repeated until we are left with a single digit. This algorithm
-- performs well only on large inputs, so we use the simple algorithm
-- for smaller inputs.
valInteger :: Integer -> Int -> [Integer] -> Integer
valInteger = go
where
go :: Integer -> Int -> [Integer] -> Integer
go _ _ [] = 0
go _ _ [d] = d
go b l ds
| l > 40 = b' `seq` go b' l' (combine b ds')
| otherwise = valSimple b ds
where
-- ensure that we have an even number of digits
-- before we call combine:
ds' = if even l then ds else 0 : ds
b' = b * b
l' = (l + 1) `quot` 2

combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
where
d = d1 * b + d2
combine _ [] = []
combine _ [_] = errorWithoutStackTrace "this should not happen"

-- The following algorithm is only linear for types whose Num operations
-- are in constant time.
valSimple :: Integer -> [Integer] -> Integer
valSimple base = go 0
where
go r [] = r
go r (d : ds) = r' `seq` go r' ds
where
r' = r * base + fromIntegral d
10 changes: 5 additions & 5 deletions src/Data/Aeson/Decoding/ByteString.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Data.Aeson.Decoding.ByteString (

import Data.ByteString (ByteString)
import Data.Char (chr)
import Data.Integer.Conversion (byteStringToInteger)
import Data.Text (Text)
import Data.Word (Word8)

Expand All @@ -20,7 +21,6 @@ import qualified Data.Scientific as Sci

import Data.Aeson.Decoding.Internal
import Data.Aeson.Decoding.Tokens
import Data.Aeson.Internal.Integer
import Data.Aeson.Internal.Text (unsafeDecodeASCII)
import Data.Aeson.Internal.Word8
import Data.Aeson.Parser.Unescape (unescapeText)
Expand Down Expand Up @@ -209,7 +209,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_e == w8 || W8_E == w8 -> go_sci int 0 bs'
| otherwise -> kont (NumInteger int) bs
where
int = bsToInteger (BS.Unsafe.unsafeTake n bs0)
int = byteStringToInteger (BS.Unsafe.unsafeTake n bs0)

go_dec :: Integer -> ByteString -> r
go_dec !int !bs1 = case BS.uncons bs1 of
Expand All @@ -226,7 +226,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_e == w8 || W8_E == w8 -> go_sci coef (negate n) bs'
| otherwise -> kont (NumDecimal dec) bs
where
frac = bsToInteger (BS.Unsafe.unsafeTake n bs1)
frac = byteStringToInteger (BS.Unsafe.unsafeTake n bs1)
coef = int * 10 ^ n + frac
dec = Sci.scientific coef (negate n)

Expand Down Expand Up @@ -254,7 +254,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_0 <= w8, w8 <= W8_9 -> go_sci_pos coef exp10 bs2 (n + 1) bs'
| otherwise -> kont (NumScientific sci) bs
where
exp10' = fromInteger (bsToInteger (BS.Unsafe.unsafeTake n bs2))
exp10' = fromInteger (byteStringToInteger (BS.Unsafe.unsafeTake n bs2))
sci = Sci.scientific coef (exp10 + exp10')

go_sci_neg :: Integer -> Int -> ByteString -> Int -> ByteString -> r
Expand All @@ -264,7 +264,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_0 <= w8, w8 <= W8_9 -> go_sci_neg coef exp10 bs2 (n + 1) bs'
| otherwise -> kont (NumScientific sci) bs
where
exp10' = fromInteger (bsToInteger (BS.Unsafe.unsafeTake n bs2))
exp10' = fromInteger (byteStringToInteger (BS.Unsafe.unsafeTake n bs2))
sci = Sci.scientific coef (exp10 - exp10')

errEnd = err "Unexpected end-of-input while parsing number literal"
Expand Down
10 changes: 5 additions & 5 deletions src/Data/Aeson/Decoding/ByteString/Lazy.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Data.Aeson.Decoding.ByteString.Lazy (

import Data.ByteString.Lazy (ByteString)
import Data.Char (chr)
import Data.Integer.Conversion (byteStringToInteger)
import Data.Text (Text)
import Data.Word (Word8)

Expand All @@ -20,7 +21,6 @@ import qualified Data.Scientific as Sci

import Data.Aeson.Decoding.Internal
import Data.Aeson.Decoding.Tokens
import Data.Aeson.Internal.Integer
import Data.Aeson.Internal.Text (unsafeDecodeASCII)
import Data.Aeson.Internal.Word8
import Data.Aeson.Parser.Unescape (unescapeText)
Expand Down Expand Up @@ -215,7 +215,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_e == w8 || W8_E == w8 -> go_sci int 0 bs'
| otherwise -> kont (NumInteger int) bs
where
int = bsToInteger (lbsTake n bs0)
int = byteStringToInteger (lbsTake n bs0)

go_dec :: Integer -> ByteString -> r
go_dec !int !bs1 = case LBS.uncons bs1 of
Expand All @@ -232,7 +232,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_e == w8 || W8_E == w8 -> go_sci coef (negate n) bs'
| otherwise -> kont (NumDecimal dec) bs
where
frac = bsToInteger (lbsTake n bs1)
frac = byteStringToInteger (lbsTake n bs1)
coef = int * 10 ^ n + frac
dec = Sci.scientific coef (negate n)

Expand Down Expand Up @@ -260,7 +260,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_0 <= w8, w8 <= W8_9 -> go_sci_pos coef exp10 bs2 (n + 1) bs'
| otherwise -> kont (NumScientific sci) bs
where
exp10' = fromInteger (bsToInteger (lbsTake n bs2))
exp10' = fromInteger (byteStringToInteger (lbsTake n bs2))
sci = Sci.scientific coef (exp10 + exp10')

go_sci_neg :: Integer -> Int -> ByteString -> Int -> ByteString -> r
Expand All @@ -270,7 +270,7 @@ scanNumberLiteral kont err bs0 = state_start bs0 where
| W8_0 <= w8, w8 <= W8_9 -> go_sci_neg coef exp10 bs2 (n + 1) bs'
| otherwise -> kont (NumScientific sci) bs
where
exp10' = fromInteger (bsToInteger (lbsTake n bs2))
exp10' = fromInteger (byteStringToInteger (lbsTake n bs2))
sci = Sci.scientific coef (exp10 - exp10')

errEnd = err "Unexpected end-of-input while parsing number literal"
Expand Down
58 changes: 0 additions & 58 deletions src/Data/Aeson/Internal/Integer.hs

This file was deleted.

4 changes: 2 additions & 2 deletions src/Data/Aeson/Parser/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Val
import qualified Data.Aeson.KeyMap as KM
import qualified Data.Aeson.Key as Key
import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
import Data.Integer.Conversion (byteStringToInteger)
import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
import qualified Data.Attoparsec.ByteString as A
import qualified Data.Attoparsec.Lazy as L
Expand All @@ -61,7 +62,6 @@ import qualified Data.ByteString.Lazy.Char8 as C
import qualified Data.ByteString.Builder as B
import qualified Data.Scientific as Sci
import Data.Aeson.Parser.Unescape (unescapeText)
import Data.Aeson.Internal.Integer
import Data.Aeson.Internal.Text
import Data.Aeson.Internal.Word8

Expand Down Expand Up @@ -440,7 +440,7 @@ decimal0 = do
digits <- A.takeWhile1 isDigit_w8
if B.length digits > 1 && B.unsafeHead digits == W8_0
then fail "leading zero"
else return (bsToInteger digits)
else return (byteStringToInteger digits)

-- | Parse a JSON number.
scientific :: Parser Scientific
Expand Down

0 comments on commit 17f8946

Please sign in to comment.