{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}

{- | Byte-level token tests: Bool fields, the canonical missing-token
set, and the default @%Y-%m-%d@ date shape. All length-bucketed with
first-byte dispatch; no Text decode, no list walk.
-}
module DataFrame.Internal.Parsing.Fast.Token (
    parseBoolField#,
    isMissingFieldSlice,
    isMissingFieldIn,
    parseDateFieldSlice,
) where

import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BSU

import Data.Time (Day, fromGregorianValid)
import Data.Word (Word8)
import GHC.Exts (Int#)

import DataFrame.Internal.Parsing (readByteStringDate)
import DataFrame.Internal.Parsing.Fast.Common (isDigitByte)

{- | Exactly @True|true|TRUE|False|false|FALSE@, no strip (the
'readByteStringBool' grammar). Result is @(# ok, bool #)@.
-}
parseBoolField# :: BS.ByteString -> Int -> Int -> (# Int#, Int# #)
parseBoolField# :: ByteString -> Int -> Int -> (# Int#, Int# #)
parseBoolField# ByteString
bs Int
s Int
e = case Int
e Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s of
    Int
4
        | Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x54 Bool -> Bool -> Bool
&& Word8 -> Word8 -> Word8 -> Bool
rue Word8
0x72 Word8
0x75 Word8
0x65 -> (# Int#
1#, Int#
1# #)
        | Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x54 Bool -> Bool -> Bool
&& Word8 -> Word8 -> Word8 -> Bool
rue Word8
0x52 Word8
0x55 Word8
0x45 -> (# Int#
1#, Int#
1# #)
        | Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x74 Bool -> Bool -> Bool
&& Word8 -> Word8 -> Word8 -> Bool
rue Word8
0x72 Word8
0x75 Word8
0x65 -> (# Int#
1#, Int#
1# #)
    Int
5
        | Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x46 Bool -> Bool -> Bool
&& Word8 -> Word8 -> Word8 -> Word8 -> Bool
alse Word8
0x61 Word8
0x6C Word8
0x73 Word8
0x65 -> (# Int#
1#, Int#
0# #)
        | Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x46 Bool -> Bool -> Bool
&& Word8 -> Word8 -> Word8 -> Word8 -> Bool
alse Word8
0x41 Word8
0x4C Word8
0x53 Word8
0x45 -> (# Int#
1#, Int#
0# #)
        | Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x66 Bool -> Bool -> Bool
&& Word8 -> Word8 -> Word8 -> Word8 -> Bool
alse Word8
0x61 Word8
0x6C Word8
0x73 Word8
0x65 -> (# Int#
1#, Int#
0# #)
    Int
_ -> (# Int#
0#, Int#
0# #)
  where
    ix :: Int -> Word8
ix Int
d = ByteString -> Int -> Word8
BSU.unsafeIndex ByteString
bs (Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d)
    rue :: Word8 -> Word8 -> Word8 -> Bool
rue Word8
a Word8
b Word8
c = Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
a Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
b Bool -> Bool -> Bool
&& Int -> Word8
ix Int
3 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
c
    alse :: Word8 -> Word8 -> Word8 -> Word8 -> Bool
alse Word8
a Word8
b Word8
c Word8
d = Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
a Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
b Bool -> Bool -> Bool
&& Int -> Word8
ix Int
3 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
c Bool -> Bool -> Bool
&& Int -> Word8
ix Int
4 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
d
{-# INLINE parseBoolField# #-}

{- | Membership in the canonical missing list
@[\"Nothing\",\"NULL\",\"\",\" \",\"nan\",\"null\",\"N\/A\",\"NaN\",\"NAN\",\"NA\"]@
(case-sensitive, exact), dispatched on length then first byte.
-}
isMissingFieldSlice :: BS.ByteString -> Int -> Int -> Bool
isMissingFieldSlice :: ByteString -> Int -> Int -> Bool
isMissingFieldSlice ByteString
bs Int
s Int
e = case Int
e Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s of
    Int
0 -> Bool
True
    Int
1 -> Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x20
    Int
2 -> Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x4E Bool -> Bool -> Bool
&& Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x41
    Int
3 -> case Int -> Word8
ix Int
0 of
        Word8
0x6E -> Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x61 Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x6E
        Word8
0x4E ->
            (Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x4E Bool -> Bool -> Bool
&& (Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x61 Bool -> Bool -> Bool
|| Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x41))
                Bool -> Bool -> Bool
|| (Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x2F Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x41)
        Word8
_ -> Bool
False
    Int
4 ->
        (Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x4E Bool -> Bool -> Bool
&& Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x55 Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x4C Bool -> Bool -> Bool
&& Int -> Word8
ix Int
3 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x4C)
            Bool -> Bool -> Bool
|| (Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x6E Bool -> Bool -> Bool
&& Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x75 Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x6C Bool -> Bool -> Bool
&& Int -> Word8
ix Int
3 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x6C)
    Int
7 ->
        Int -> Word8
ix Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x4E
            Bool -> Bool -> Bool
&& Int -> Word8
ix Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x6F
            Bool -> Bool -> Bool
&& Int -> Word8
ix Int
2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x74
            Bool -> Bool -> Bool
&& Int -> Word8
ix Int
3 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x68
            Bool -> Bool -> Bool
&& Int -> Word8
ix Int
4 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x69
            Bool -> Bool -> Bool
&& Int -> Word8
ix Int
5 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x6E
            Bool -> Bool -> Bool
&& Int -> Word8
ix Int
6 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x67
    Int
_ -> Bool
False
  where
    ix :: Int -> Word8
    ix :: Int -> Word8
ix Int
d = ByteString -> Int -> Word8
BSU.unsafeIndex ByteString
bs (Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d)
    {-# INLINE ix #-}
{-# INLINE isMissingFieldSlice #-}

-- | Generic fallback for user-supplied missing-indicator lists.
isMissingFieldIn :: [BS.ByteString] -> BS.ByteString -> Bool
isMissingFieldIn :: [ByteString] -> ByteString -> Bool
isMissingFieldIn [ByteString]
toks ByteString
f = ByteString
f ByteString -> [ByteString] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ByteString]
toks
{-# INLINE isMissingFieldIn #-}

{- | @%Y-%m-%d@: byte-level fast path for the padded 10-byte shape
(@dddd-dd-dd@); anything else (unpadded, whitespace-tolerant, long
years, invalid) falls back to 'readByteStringDate'.
-}
parseDateFieldSlice :: BS.ByteString -> Int -> Int -> Maybe Day
parseDateFieldSlice :: ByteString -> Int -> Int -> Maybe Day
parseDateFieldSlice ByteString
bs Int
s Int
e
    | Int
e Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
10
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
0)
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
1)
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
2)
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
3)
        Bool -> Bool -> Bool
&& Int -> Word8
ix Int
4 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x2D
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
5)
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
6)
        Bool -> Bool -> Bool
&& Int -> Word8
ix Int
7 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x2D
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
8)
        Bool -> Bool -> Bool
&& Word8 -> Bool
isDigitByte (Int -> Word8
ix Int
9) =
        Year -> Int -> Int -> Maybe Day
fromGregorianValid
            (Int -> Year
forall a. Integral a => a -> Year
toInteger (Int -> Int
dig Int
0 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1000 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
dig Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
100 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
dig Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
10 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
dig Int
3))
            (Int -> Int
dig Int
5 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
10 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
dig Int
6)
            (Int -> Int
dig Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
10 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int
dig Int
9)
    | Bool
otherwise =
        String -> ByteString -> Maybe Day
readByteStringDate String
"%Y-%m-%d" (Int -> ByteString -> ByteString
BSU.unsafeTake (Int
e Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s) (Int -> ByteString -> ByteString
BSU.unsafeDrop Int
s ByteString
bs))
  where
    ix :: Int -> Word8
ix Int
d = ByteString -> Int -> Word8
BSU.unsafeIndex ByteString
bs (Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d)
    dig :: Int -> Int
    dig :: Int -> Int
dig Int
d = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8
ix Int
d) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
0x30
{-# INLINE parseDateFieldSlice #-}