    -- |
    -- Module      : Basement.UTF8.Table
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : portable
    --
    -- UTF8 lookup tables for fast continuation & nb bytes per header queries
    {-# LANGUAGE MagicHash #-}
    module Basement.UTF8.Table
        ( isContinuation
        , isContinuation2
        , isContinuation3
        , getNbBytes
        , isContinuation#
        , getNbBytes#
        ) where

    import           GHC.Prim
    import           GHC.Types
    import           GHC.Word
    import           Basement.Compat.Base
    import           Basement.Compat.Primitive
    import           Basement.UTF8.Types (StepASCII(..))

    -- | Check if the byte is a continuation byte
    isContinuation :: Word8 -> Bool
    isContinuation :: Word8 -> Bool
    isContinuation (W8# w :: Word#
    w) = Word# -> Bool
    isContinuation# Word#
    w
    {-# INLINE isContinuation #-}

    isContinuation2 :: Word8 -> Word8 -> Bool
    isContinuation2 :: Word8 -> Word8 -> Bool
    isContinuation2 (W8# w1 :: Word#
    w1) (W8# w2 :: Word#
    w2) =
        Int# -> Bool
    bool# (Word# -> Int#
    mask Word#
    w1 Int# -> Int# -> Int#
    `andI#` Word# -> Int#
    mask Word#
    w2)
      where
        mask :: Word# -> Int#
    mask v :: Word#
    v = (Word# -> Word# -> Word#
    and# 0xC0## Word#
    v) Word# -> Word# -> Int#
    `eqWord#` 0x80##
    {-# INLINE isContinuation2 #-}

    isContinuation3 :: Word8 -> Word8 -> Word8 -> Bool
    isContinuation3 :: Word8 -> Word8 -> Word8 -> Bool
    isContinuation3 (W8# w1 :: Word#
    w1) (W8# w2 :: Word#
    w2) (W8# w3 :: Word#
    w3) =
        Int# -> Bool
    bool# (Word# -> Int#
    mask Word#
    w1) Bool -> Bool -> Bool
    && Int# -> Bool
    bool# (Word# -> Int#
    mask Word#
    w2) Bool -> Bool -> Bool
    && Int# -> Bool
    bool# (Word# -> Int#
    mask Word#
    w3)
      where
        mask :: Word# -> Int#
    mask v :: Word#
    v = (Word# -> Word# -> Word#
    and# 0xC0## Word#
    v) Word# -> Word# -> Int#
    `eqWord#` 0x80##
    {-# INLINE isContinuation3 #-}

    -- | Number of bytes associated with a specific header byte
    --
    -- If the header byte is invalid then NbBytesInvalid is returned,
    data NbBytesCont = NbBytesInvalid | NbBytesCont0 | NbBytesCont1 | NbBytesCont2 | NbBytesCont3

    -- | Identical to 'NbBytesCont' but doesn't allow to represent any failure.
    --
    -- Only use in validated place
    data NbBytesCont_ = NbBytesCont0_ | NbBytesCont1_ | NbBytesCont2_ | NbBytesCont3_

    -- | Get the number of following bytes given the first byte of a UTF8 sequence.
    getNbBytes :: StepASCII -> Int
    getNbBytes :: StepASCII -> Int
    getNbBytes (StepASCII (W8# w :: Word#
    w)) = Int# -> Int
    I# (Word# -> Int#
    getNbBytes# Word#
    w)
    {-# INLINE getNbBytes #-}

    -- | Check if the byte is a continuation byte
    isContinuation# :: Word# -> Bool
    isContinuation# :: Word# -> Bool
    isContinuation# w :: Word#
    w = Word# -> Word
    W# (Addr# -> Int# -> Word#
    indexWord8OffAddr# (Table -> Addr#
    unTable Table
    contTable) (Word# -> Int#
    word2Int# Word#
    w)) Word -> Word -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word# -> Word
    W# 0##
    {-# INLINE isContinuation# #-}

    -- | Get the number of following bytes given the first byte of a UTF8 sequence.
    getNbBytes# :: Word# -> Int#
    getNbBytes# :: Word# -> Int#
    getNbBytes# w :: Word#
    w = Word# -> Int#
    word2Int# (Addr# -> Int# -> Word#
    indexWord8OffAddr# (Table -> Addr#
    unTable Table
    headTable) (Word# -> Int#
    word2Int# Word#
    w))
    {-# INLINE getNbBytes# #-}

    data Table = Table { Table -> Addr#
    unTable :: !Addr# }

    contTable :: Table
    contTable :: Table
    contTable = Addr# -> Table
    Table
            "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"#
    {-# NOINLINE contTable #-}

    headTable :: Table
    headTable :: Table
    headTable = Addr# -> Table
    Table
            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
            \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
            \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
            \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
            \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
            \\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\
            \\x03\x03\x03\x03\x03\x03\x03\x03\xff\xff\xff\xff\xff\xff\xff\xff"#
    {-# NOINLINE headTable #-}