    {-# LANGUAGE MagicHash     #-}
    {-# LANGUAGE UnboxedTuples #-}
    {-# LANGUAGE BangPatterns  #-}
    module Basement.Base16
        ( unsafeConvertByte
        , hexWord16
        , hexWord32
        , escapeByte
        , Base16Escape(..)
        ) where

    import GHC.Prim (Addr#, indexWord8OffAddr#, word2Int#, chr#)
    import GHC.Types
    import GHC.Word
    import Basement.Types.Char7
    import Basement.Compat.Primitive

    data Base16Escape = Base16Escape {-# UNPACK #-} !Char7 {-# UNPACK #-} !Char7

    -- | Convert a byte value in Word# to two Word#s containing
    -- the hexadecimal representation of the Word#
    --
    -- The output words# are guaranteed to be included in the 0 to 2^7-1 range
    --
    -- Note that calling convertByte with a value greater than 256
    -- will cause segfault or other horrible effect. From GHC9.2, Word8#
    -- cannot be >= 256.
    unsafeConvertByte :: Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte :: Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte Word8#
    b = (# Table -> Word8# -> Word8#
    r Table
    tableHi Word8#
    b, Table -> Word8# -> Word8#
    r Table
    tableLo Word8#
    b #)
      where
        r :: Table -> Word8# -> Word8#
        r :: Table -> Word8# -> Word8#
    r (Table !Addr#
    table) Word8#
    index = Addr# -> Int# -> Word8#
    indexWord8OffAddr# Addr#
    table (Word# -> Int#
    word2Int# (Word8# -> Word#
    word8ToWord# Word8#
    index))
    {-# INLINE unsafeConvertByte #-}

    escapeByte :: Word8 -> Base16Escape
    escapeByte :: Word8 -> Base16Escape
    escapeByte !(W8# Word8#
    b) = Char7 -> Char7 -> Base16Escape
    Base16Escape (Table -> Word8# -> Char7
    r Table
    tableHi Word8#
    b) (Table -> Word8# -> Char7
    r Table
    tableLo Word8#
    b)
      where
        r :: Table -> Word8# -> Char7
        r :: Table -> Word8# -> Char7
    r (Table !Addr#
    table) Word8#
    index = Word8 -> Char7
    Char7 (Word8# -> Word8
    W8# (Addr# -> Int# -> Word8#
    indexWord8OffAddr# Addr#
    table (Word# -> Int#
    word2Int# (Word8# -> Word#
    word8ToWord# Word8#
    index))))
    {-# INLINE escapeByte #-}

    -- | hex word16
    hexWord16 :: Word16 -> (Char, Char, Char, Char)
    hexWord16 :: Word16 -> (Char, Char, Char, Char)
    hexWord16 (W16# Word16#
    w) = (Word8# -> Char
    toChar Word8#
    w1,Word8# -> Char
    toChar Word8#
    w2,Word8# -> Char
    toChar Word8#
    w3,Word8# -> Char
    toChar Word8#
    w4)
      where
        toChar :: Word8# -> Char
        toChar :: Word8# -> Char
    toChar Word8#
    c = Char# -> Char
    C# (Int# -> Char#
    chr# (Word# -> Int#
    word2Int# (Word8# -> Word#
    word8ToWord# Word8#
    c)))
        !(# Word8#
    w1, Word8#
    w2 #) = Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte (Word16# -> Word8#
    word16ToWord8# (Word16# -> Int# -> Word16#
    uncheckedShiftRLWord16# Word16#
    w Int#
    8#))
        !(# Word8#
    w3, Word8#
    w4 #) = Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte (Word16# -> Word8#
    word16ToWord8# Word16#
    w)

    -- | hex word32
    hexWord32 :: Word32 -> (Char, Char, Char, Char, Char, Char, Char, Char)
    hexWord32 :: Word32 -> (Char, Char, Char, Char, Char, Char, Char, Char)
    hexWord32 (W32# Word32#
    w) = (Word8# -> Char
    toChar Word8#
    w1,Word8# -> Char
    toChar Word8#
    w2,Word8# -> Char
    toChar Word8#
    w3,Word8# -> Char
    toChar Word8#
    w4
                         ,Word8# -> Char
    toChar Word8#
    w5,Word8# -> Char
    toChar Word8#
    w6,Word8# -> Char
    toChar Word8#
    w7,Word8# -> Char
    toChar Word8#
    w8)
      where
        toChar :: Word8# -> Char
        toChar :: Word8# -> Char
    toChar Word8#
    c = Char# -> Char
    C# (Int# -> Char#
    chr# (Word# -> Int#
    word2Int# (Word8# -> Word#
    word8ToWord# Word8#
    c)))
        !(# Word8#
    w1, Word8#
    w2 #) = Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte (Word32# -> Word8#
    word32ToWord8# (Word32# -> Int# -> Word32#
    uncheckedShiftRLWord32# Word32#
    w Int#
    24#))
        !(# Word8#
    w3, Word8#
    w4 #) = Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte (Word32# -> Word8#
    word32ToWord8# (Word32# -> Int# -> Word32#
    uncheckedShiftRLWord32# Word32#
    w Int#
    16#))
        !(# Word8#
    w5, Word8#
    w6 #) = Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte (Word32# -> Word8#
    word32ToWord8# (Word32# -> Int# -> Word32#
    uncheckedShiftRLWord32# Word32#
    w Int#
    8#))
        !(# Word8#
    w7, Word8#
    w8 #) = Word8# -> (# Word8#, Word8# #)
    unsafeConvertByte (Word32# -> Word8#
    word32ToWord8# Word32#
    w)

    data Table = Table Addr#

    tableLo:: Table
    tableLo :: Table
    tableLo = Addr# -> Table
    Table
        Addr#
    "0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef\
        \0123456789abcdef0123456789abcdef"#

    tableHi :: Table
    tableHi :: Table
    tableHi = Addr# -> Table
    Table
        Addr#
    "00000000000000001111111111111111\
        \22222222222222223333333333333333\
        \44444444444444445555555555555555\
        \66666666666666667777777777777777\
        \88888888888888889999999999999999\
        \aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb\
        \ccccccccccccccccdddddddddddddddd\
        \eeeeeeeeeeeeeeeeffffffffffffffff"#