{-# 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"#