module Data.Text.Internal.Fusion.Size
(
Size
, exactSize
, maxSize
, betweenSize
, unknownSize
, unionSize
, charSize
, codePointsSize
, exactly
, smaller
, larger
, upperBound
, lowerBound
, compareSize
, isEmpty
) where
import Data.Char (ord)
import Data.Text.Internal (mul)
#if defined(ASSERTS)
import Control.Exception (assert)
#endif
data Size = Between !Int !Int
| Unknown
deriving (Eq, Show)
exactly :: Size -> Maybe Int
exactly (Between na nb) | na == nb = Just na
exactly _ = Nothing
charSize :: Char -> Size
charSize c
| ord c < 0x10000 = exactSize 1
| otherwise = exactSize 2
codePointsSize :: Int -> Size
codePointsSize n = Between n (2*n)
exactSize :: Int -> Size
exactSize n =
#if defined(ASSERTS)
assert (n >= 0)
#endif
Between n n
maxSize :: Int -> Size
maxSize n =
#if defined(ASSERTS)
assert (n >= 0)
#endif
Between 0 n
betweenSize :: Int -> Int -> Size
betweenSize m n =
#if defined(ASSERTS)
assert (m >= 0)
assert (n >= m)
#endif
Between m n
unionSize :: Size -> Size -> Size
unionSize (Between a b) (Between c d) = Between (min a c) (max b d)
unionSize _ _ = Unknown
unknownSize :: Size
unknownSize = Unknown
instance Num Size where
(+) = addSize
() = subtractSize
(*) = mulSize
fromInteger = f where f = exactSize . fromInteger
add :: Int -> Int -> Int
add m n | mn >= 0 = mn
| otherwise = overflowError
where mn = m + n
addSize :: Size -> Size -> Size
addSize (Between ma mb) (Between na nb) = Between (add ma na) (add mb nb)
addSize _ _ = Unknown
subtractSize :: Size -> Size -> Size
subtractSize (Between ma mb) (Between na nb) = Between (max (manb) 0) (max (mbna) 0)
subtractSize a@(Between 0 _) Unknown = a
subtractSize (Between _ mb) Unknown = Between 0 mb
subtractSize _ _ = Unknown
mulSize :: Size -> Size -> Size
mulSize (Between ma mb) (Between na nb) = Between (mul ma na) (mul mb nb)
mulSize _ _ = Unknown
smaller :: Size -> Size -> Size
smaller a@(Between ma mb) b@(Between na nb)
| mb <= na = a
| nb <= ma = b
| otherwise = Between (ma `min` na) (mb `min` nb)
smaller a@(Between 0 _) Unknown = a
smaller (Between _ mb) Unknown = Between 0 mb
smaller Unknown b@(Between 0 _) = b
smaller Unknown (Between _ nb) = Between 0 nb
smaller Unknown Unknown = Unknown
larger :: Size -> Size -> Size
larger a@(Between ma mb) b@(Between na nb)
| ma >= nb = a
| na >= mb = b
| otherwise = Between (ma `max` na) (mb `max` nb)
larger _ _ = Unknown
upperBound :: Int -> Size -> Int
upperBound _ (Between _ n) = n
upperBound k _ = k
lowerBound :: Int -> Size -> Int
lowerBound _ (Between n _) = n
lowerBound k _ = k
compareSize :: Size -> Size -> Maybe Ordering
compareSize (Between ma mb) (Between na nb)
| mb < na = Just LT
| ma > nb = Just GT
| ma == mb
, ma == na
, ma == nb = Just EQ
compareSize _ _ = Nothing
isEmpty :: Size -> Bool
isEmpty (Between _ n) = n <= 0
isEmpty _ = False
overflowError :: Int
overflowError = error "Data.Text.Internal.Fusion.Size: size overflow"