{-# LANGUAGE CPP, PatternGuards #-}
{-# OPTIONS_GHC -fno-warn-missing-methods #-}
-- |
-- Module      : Data.Text.Internal.Fusion.Internal
-- Copyright   : (c) Roman Leshchinskiy 2008,
--               (c) Bryan O'Sullivan 2009
--
-- License     : BSD-style
-- Maintainer  : bos@serpentine.com
-- Stability   : experimental
-- Portability : portable
--
-- /Warning/: this is an internal module, and does not have a stable
-- API or name. Functions in this module may not check or enforce
-- preconditions expected by public modules. Use at your own risk!
--
-- Size hints.

module Data.Text.Internal.Fusion.Size
    (
      Size
      -- * Sizes
    , exactSize
    , maxSize
    , betweenSize
    , unknownSize
    , unionSize
    , charSize
    , codePointsSize
      -- * Querying sizes
    , 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

-- | A size in UTF-16 code units.
data Size = Between {-# UNPACK #-} !Int {-# UNPACK #-} !Int -- ^ Lower and upper bounds on size.
          | Unknown                                         -- ^ Unknown size.
            deriving (Size -> Size -> Bool
(Size -> Size -> Bool) -> (Size -> Size -> Bool) -> Eq Size
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Size -> Size -> Bool
$c/= :: Size -> Size -> Bool
== :: Size -> Size -> Bool
$c== :: Size -> Size -> Bool
Eq, Int -> Size -> ShowS
[Size] -> ShowS
Size -> String
(Int -> Size -> ShowS)
-> (Size -> String) -> ([Size] -> ShowS) -> Show Size
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Size] -> ShowS
$cshowList :: [Size] -> ShowS
show :: Size -> String
$cshow :: Size -> String
showsPrec :: Int -> Size -> ShowS
$cshowsPrec :: Int -> Size -> ShowS
Show)

exactly :: Size -> Maybe Int
exactly :: Size -> Maybe Int
exactly (Between Int
na Int
nb) | Int
na Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
nb = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
na
exactly Size
_ = Maybe Int
forall a. Maybe a
Nothing
{-# INLINE exactly #-}

-- | The 'Size' of the given code point.
charSize :: Char -> Size
charSize :: Char -> Size
charSize Char
c
  | Char -> Int
ord Char
c Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0x10000 = Int -> Size
exactSize Int
1
  | Bool
otherwise       = Int -> Size
exactSize Int
2

-- | The 'Size' of @n@ code points.
codePointsSize :: Int -> Size
codePointsSize :: Int -> Size
codePointsSize Int
n =
#if defined(ASSERTS)
    assert (n >= 0)
#endif
    Int -> Int -> Size
Between Int
n (Int
2Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
n)
{-# INLINE codePointsSize #-}

exactSize :: Int -> Size
exactSize :: Int -> Size
exactSize Int
n =
#if defined(ASSERTS)
    assert (n >= 0)
#endif
    Int -> Int -> Size
Between Int
n Int
n
{-# INLINE exactSize #-}

maxSize :: Int -> Size
maxSize :: Int -> Size
maxSize Int
n =
#if defined(ASSERTS)
    assert (n >= 0)
#endif
    Int -> Int -> Size
Between Int
0 Int
n
{-# INLINE maxSize #-}

betweenSize :: Int -> Int -> Size
betweenSize :: Int -> Int -> Size
betweenSize Int
m Int
n =
#if defined(ASSERTS)
    assert (m >= 0)
    assert (n >= m)
#endif
    Int -> Int -> Size
Between Int
m Int
n
{-# INLINE betweenSize #-}

unionSize :: Size -> Size -> Size
unionSize :: Size -> Size -> Size
unionSize (Between Int
a Int
b) (Between Int
c Int
d) = Int -> Int -> Size
Between (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
a Int
c) (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
b Int
d)
unionSize Size
_ Size
_ = Size
Unknown

unknownSize :: Size
unknownSize :: Size
unknownSize = Size
Unknown
{-# INLINE unknownSize #-}

instance Num Size where
    + :: Size -> Size -> Size
(+) = Size -> Size -> Size
addSize
    (-) = Size -> Size -> Size
subtractSize
    * :: Size -> Size -> Size
(*) = Size -> Size -> Size
mulSize

    fromInteger :: Integer -> Size
fromInteger = Integer -> Size
f where f :: Integer -> Size
f = Int -> Size
exactSize (Int -> Size) -> (Integer -> Int) -> Integer -> Size
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Int
forall a. Num a => Integer -> a
fromInteger
                          {-# INLINE f #-}

add :: Int -> Int -> Int
add :: Int -> Int -> Int
add Int
m Int
n | Int
mn Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>=   Int
0 = Int
mn
        | Bool
otherwise = Int
overflowError
  where mn :: Int
mn = Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n
{-# INLINE add #-}

addSize :: Size -> Size -> Size
addSize :: Size -> Size -> Size
addSize (Between Int
ma Int
mb) (Between Int
na Int
nb) = Int -> Int -> Size
Between (Int -> Int -> Int
add Int
ma Int
na) (Int -> Int -> Int
add Int
mb Int
nb)
addSize Size
_               Size
_               = Size
Unknown
{-# INLINE addSize #-}

subtractSize :: Size -> Size -> Size
subtractSize :: Size -> Size -> Size
subtractSize (Between Int
ma Int
mb) (Between Int
na Int
nb) = Int -> Int -> Size
Between (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Int
maInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
nb) Int
0) (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Int
mbInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
na) Int
0)
subtractSize a :: Size
a@(Between Int
0 Int
_) Size
Unknown         = Size
a
subtractSize (Between Int
_ Int
mb)  Size
Unknown         = Int -> Int -> Size
Between Int
0 Int
mb
subtractSize Size
_               Size
_               = Size
Unknown
{-# INLINE subtractSize #-}

mulSize :: Size -> Size -> Size
mulSize :: Size -> Size -> Size
mulSize (Between Int
ma Int
mb) (Between Int
na Int
nb) = Int -> Int -> Size
Between (Int -> Int -> Int
mul Int
ma Int
na) (Int -> Int -> Int
mul Int
mb Int
nb)
mulSize Size
_               Size
_               = Size
Unknown
{-# INLINE mulSize #-}

-- | Minimum of two size hints.
smaller :: Size -> Size -> Size
smaller :: Size -> Size -> Size
smaller a :: Size
a@(Between Int
ma Int
mb) b :: Size
b@(Between Int
na Int
nb)
    | Int
mb Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
na  = Size
a
    | Int
nb Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
ma  = Size
b
    | Bool
otherwise = Int -> Int -> Size
Between (Int
ma Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` Int
na) (Int
mb Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` Int
nb)
smaller a :: Size
a@(Between Int
0 Int
_) Size
Unknown         = Size
a
smaller (Between Int
_ Int
mb)  Size
Unknown         = Int -> Int -> Size
Between Int
0 Int
mb
smaller Size
Unknown         b :: Size
b@(Between Int
0 Int
_) = Size
b
smaller Size
Unknown         (Between Int
_ Int
nb)  = Int -> Int -> Size
Between Int
0 Int
nb
smaller Size
Unknown         Size
Unknown         = Size
Unknown
{-# INLINE smaller #-}

-- | Maximum of two size hints.
larger :: Size -> Size -> Size
larger :: Size -> Size -> Size
larger a :: Size
a@(Between Int
ma Int
mb) b :: Size
b@(Between Int
na Int
nb)
    | Int
ma Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
nb  = Size
a
    | Int
na Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
mb  = Size
b
    | Bool
otherwise = Int -> Int -> Size
Between (Int
ma Int -> Int -> Int
forall a. Ord a => a -> a -> a
`max` Int
na) (Int
mb Int -> Int -> Int
forall a. Ord a => a -> a -> a
`max` Int
nb)
larger Size
_ Size
_ = Size
Unknown
{-# INLINE larger #-}

-- | Compute the maximum size from a size hint, if possible.
upperBound :: Int -> Size -> Int
upperBound :: Int -> Size -> Int
upperBound Int
_ (Between Int
_ Int
n) = Int
n
upperBound Int
k Size
_             = Int
k
{-# INLINE upperBound #-}

-- | Compute the maximum size from a size hint, if possible.
lowerBound :: Int -> Size -> Int
lowerBound :: Int -> Size -> Int
lowerBound Int
_ (Between Int
n Int
_) = Int
n
lowerBound Int
k Size
_             = Int
k
{-# INLINE lowerBound #-}

-- | Determine the ordering relationship between two 'Size's, or 'Nothing' in
-- the indeterminate case.
compareSize :: Size -> Size -> Maybe Ordering
compareSize :: Size -> Size -> Maybe Ordering
compareSize (Between Int
ma Int
mb) (Between Int
na Int
nb)
  | Int
mb Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
na            = Ordering -> Maybe Ordering
forall a. a -> Maybe a
Just Ordering
LT
  | Int
ma Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
nb            = Ordering -> Maybe Ordering
forall a. a -> Maybe a
Just Ordering
GT
  | Int
ma Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
mb
  , Int
ma Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
na
  , Int
ma Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
nb           = Ordering -> Maybe Ordering
forall a. a -> Maybe a
Just Ordering
EQ
compareSize Size
_ Size
_        = Maybe Ordering
forall a. Maybe a
Nothing


isEmpty :: Size -> Bool
isEmpty :: Size -> Bool
isEmpty (Between Int
_ Int
n) = Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0
isEmpty Size
_             = Bool
False
{-# INLINE isEmpty #-}

overflowError :: Int
overflowError :: Int
overflowError = String -> Int
forall a. HasCallStack => String -> a
error String
"Data.Text.Internal.Fusion.Size: size overflow"