{-# 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 na :: Int
na nb :: 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 _ = Maybe Int
forall a. Maybe a
Nothing
{-# INLINE exactly #-}

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

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

exactSize :: Int -> Size
exactSize :: Int -> Size
exactSize n :: 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 n :: Int
n =
#if defined(ASSERTS)
    assert (n >= 0)
#endif
    Int -> Int -> Size
Between 0 Int
n
{-# INLINE maxSize #-}

betweenSize :: Int -> Int -> Size
betweenSize :: Int -> Int -> Size
betweenSize m :: Int
m n :: 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 a :: Int
a b :: Int
b) (Between c :: Int
c d :: 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
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 m :: Int
m n :: Int
n | Int
mn Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>=   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 ma :: Int
ma mb :: Int
mb) (Between na :: Int
na nb :: Int
nb) = Int -> Int -> Size
Between (Int -> Int -> Int
add Int
ma Int
na) (Int -> Int -> Int
add Int
mb Int
nb)
addSize _               _               = Size
Unknown
{-# INLINE addSize #-}

subtractSize :: Size -> Size -> Size
subtractSize :: Size -> Size -> Size
subtractSize (Between ma :: Int
ma mb :: Int
mb) (Between na :: Int
na nb :: 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) 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) 0)
subtractSize a :: Size
a@(Between 0 _) Unknown         = Size
a
subtractSize (Between _ mb :: Int
mb)  Unknown         = Int -> Int -> Size
Between 0 Int
mb
subtractSize _               _               = Size
Unknown
{-# INLINE subtractSize #-}

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

-- | Minimum of two size hints.
smaller :: Size -> Size -> Size
smaller :: Size -> Size -> Size
smaller a :: Size
a@(Between ma :: Int
ma mb :: Int
mb) b :: Size
b@(Between na :: Int
na nb :: 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 0 _) Unknown         = Size
a
smaller (Between _ mb :: Int
mb)  Unknown         = Int -> Int -> Size
Between 0 Int
mb
smaller Unknown         b :: Size
b@(Between 0 _) = Size
b
smaller Unknown         (Between _ nb :: Int
nb)  = Int -> Int -> Size
Between 0 Int
nb
smaller Unknown         Unknown         = Size
Unknown
{-# INLINE smaller #-}

-- | Maximum of two size hints.
larger :: Size -> Size -> Size
larger :: Size -> Size -> Size
larger a :: Size
a@(Between ma :: Int
ma mb :: Int
mb) b :: Size
b@(Between na :: Int
na nb :: 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
Unknown
{-# INLINE larger #-}

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

-- | Compute the maximum size from a size hint, if possible.
lowerBound :: Int -> Size -> Int
lowerBound :: Int -> Size -> Int
lowerBound _ (Between n :: Int
n _) = Int
n
lowerBound k :: Int
k _             = 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 ma :: Int
ma mb :: Int
mb) (Between na :: Int
na nb :: 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 _ _        = Maybe Ordering
forall a. Maybe a
Nothing


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

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