module FlatParse.Common.Strings where

import Data.Bits
import Data.Foldable (foldl')

import Data.Word
import Data.Int

import qualified Data.Char

-- | @isDigit c = \'0\' <= c && c <= \'9\'@
-- TODO exists in Data.Char, but maybe loses inlining
isDigit :: Char -> Bool
--isDigit c = '0' <= c && c <= '9'
isDigit :: Char -> Bool
isDigit = Char -> Bool
Data.Char.isDigit
{-# inline isDigit #-}

-- | @isAsciiLetter c = (\'A\' <= c && c <= \'Z\') || (\'a\' <= c && c <= \'z\')@
-- TODO exists in Data.Char, but maybe loses inlining
isAsciiLetter :: Char -> Bool
--isAsciiLetter c = ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
isAsciiLetter :: Char -> Bool
isAsciiLetter Char
c = Char -> Bool
Data.Char.isAsciiUpper Char
c Bool -> Bool -> Bool
|| Char -> Bool
Data.Char.isAsciiLower Char
c
{-# inline isAsciiLetter #-}

-- | @isGreekLetter c = (\'Α\' <= c && c <= \'Ω\') || (\'α\' <= c && c <= \'ω\')@
isGreekLetter :: Char -> Bool
isGreekLetter :: Char -> Bool
isGreekLetter Char
c = (Char
'Α' forall a. Ord a => a -> a -> Bool
<= Char
c Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'Ω') Bool -> Bool -> Bool
|| (Char
'α' forall a. Ord a => a -> a -> Bool
<= Char
c Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'ω')
{-# inline isGreekLetter #-}

-- UTF conversions
--------------------------------------------------------------------------------

packBytes :: [Word] -> Word
packBytes :: [Word] -> Word
packBytes = forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall {a} {a}.
(Bits a, Integral a, Num a) =>
(a, Int) -> a -> (a, Int)
go (Word
0, Int
0) where
  go :: (a, Int) -> a -> (a, Int)
go (a
acc, Int
shift) a
w | Int
shift forall a. Eq a => a -> a -> Bool
== Int
64 = forall a. HasCallStack => [Char] -> a
error [Char]
"packWords: too many bytes"
  go (a
acc, Int
shift) a
w = (forall a. Bits a => a -> Int -> a
unsafeShiftL (forall a b. (Integral a, Num b) => a -> b
fromIntegral a
w) Int
shift forall a. Bits a => a -> a -> a
.|. a
acc, Int
shiftforall a. Num a => a -> a -> a
+Int
8)

-- TODO chunks into 8-bytes for 64-bit performance
splitBytes :: [Word] -> ([Word], [Word])
splitBytes :: [Word] -> ([Word], [Word])
splitBytes [Word]
ws = case forall a. Integral a => a -> a -> (a, a)
quotRem (forall (t :: * -> *) a. Foldable t => t a -> Int
length [Word]
ws) Int
8 of
  (Int
0, Int
_) -> ([Word]
ws, [])
  (Int
_, Int
r) -> ([Word]
as, [Word] -> [Word]
chunk8s [Word]
bs) where
              ([Word]
as, [Word]
bs) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
r [Word]
ws
              chunk8s :: [Word] -> [Word]
chunk8s [] = []
              chunk8s [Word]
ws = let ([Word]
as, [Word]
bs) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
8 [Word]
ws in
                           [Word] -> Word
packBytes [Word]
as forall a. a -> [a] -> [a]
: [Word] -> [Word]
chunk8s [Word]
bs

--------------------------------------------------------------------------------

{- $boxed-integer-coercion

These functions should be no-ops. They correspond to the similarly-named GHC 9.4
primops which work on unboxed integers.
-}

-- | Coerce a 'Word16' to 'Int16'.
word16ToInt16 :: Word16 -> Int16
word16ToInt16 :: Word16 -> Int16
word16ToInt16 = forall a b. (Integral a, Num b) => a -> b
fromIntegral
{-# inline word16ToInt16 #-}

-- | Coerce a 'Word32' to 'Int32'.
word32ToInt32 :: Word32 -> Int32
word32ToInt32 :: Word32 -> Int32
word32ToInt32 = forall a b. (Integral a, Num b) => a -> b
fromIntegral
{-# inline word32ToInt32 #-}

-- | Coerce a 'Word64' to 'Int64'.
word64ToInt64 :: Word64 -> Int64
word64ToInt64 :: Word64 -> Int64
word64ToInt64 = forall a b. (Integral a, Num b) => a -> b
fromIntegral
{-# inline word64ToInt64 #-}