Blame Data/Memory/Encoding/Base64.hs

Packit c1c4f9
-- |
Packit c1c4f9
-- Module      : Data.Memory.Encoding.Base64
Packit c1c4f9
-- License     : BSD-style
Packit c1c4f9
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit c1c4f9
-- Stability   : experimental
Packit c1c4f9
-- Portability : unknown
Packit c1c4f9
--
Packit c1c4f9
-- Base64
Packit c1c4f9
--
Packit c1c4f9
{-# LANGUAGE MagicHash         #-}
Packit c1c4f9
{-# LANGUAGE UnboxedTuples     #-}
Packit c1c4f9
{-# LANGUAGE OverloadedStrings #-}
Packit c1c4f9
{-# LANGUAGE BangPatterns      #-}
Packit c1c4f9
{-# LANGUAGE Rank2Types        #-}
Packit c1c4f9
module Data.Memory.Encoding.Base64
Packit c1c4f9
    ( toBase64
Packit c1c4f9
    , toBase64URL
Packit c1c4f9
    , toBase64OpenBSD
Packit c1c4f9
    , unBase64Length
Packit c1c4f9
    , unBase64LengthUnpadded
Packit c1c4f9
    , fromBase64
Packit c1c4f9
    , fromBase64URLUnpadded
Packit c1c4f9
    , fromBase64OpenBSD
Packit c1c4f9
    ) where
Packit c1c4f9
Packit c1c4f9
import           Control.Monad
Packit c1c4f9
import           Data.Memory.Internal.Compat
Packit c1c4f9
import           Data.Memory.Internal.CompatPrim
Packit c1c4f9
import           Data.Memory.Internal.Imports
Packit c1c4f9
import           Data.Bits ((.|.))
Packit c1c4f9
import           GHC.Prim
Packit c1c4f9
import           GHC.Word
Packit c1c4f9
import           Foreign.Storable
Packit c1c4f9
import           Foreign.Ptr (Ptr)
Packit c1c4f9
Packit c1c4f9
-- | Transform a number of bytes pointed by @src@ to base64 binary representation in @dst@
Packit c1c4f9
--
Packit c1c4f9
-- The destination memory need to be of correct size, otherwise it will lead
Packit c1c4f9
-- to really bad things.
Packit c1c4f9
toBase64 :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
Packit c1c4f9
toBase64 dst src len = toBase64Internal set dst src len True
Packit c1c4f9
  where
Packit c1c4f9
        !set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"#
Packit c1c4f9
Packit c1c4f9
-- | Transform a number of bytes pointed by @src@ to, URL-safe base64 binary
Packit c1c4f9
-- representation in @dst@. The result will be either padded or unpadded,
Packit c1c4f9
-- depending on the boolean @padded@ argument.
Packit c1c4f9
--
Packit c1c4f9
-- The destination memory need to be of correct size, otherwise it will lead
Packit c1c4f9
-- to really bad things.
Packit c1c4f9
toBase64URL :: Bool -> Ptr Word8 -> Ptr Word8 -> Int -> IO ()
Packit c1c4f9
toBase64URL padded dst src len = toBase64Internal set dst src len padded
Packit c1c4f9
  where
Packit c1c4f9
        !set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"#
Packit c1c4f9
Packit c1c4f9
toBase64OpenBSD :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
Packit c1c4f9
toBase64OpenBSD dst src len = toBase64Internal set dst src len False
Packit c1c4f9
  where
Packit c1c4f9
        !set = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"#
Packit c1c4f9
Packit c1c4f9
toBase64Internal :: Addr# -> Ptr Word8 -> Ptr Word8 -> Int -> Bool -> IO ()
Packit c1c4f9
toBase64Internal table dst src len padded = loop 0 0
Packit c1c4f9
  where
Packit c1c4f9
        eqChar = 0x3d :: Word8
Packit c1c4f9
Packit c1c4f9
        loop i di
Packit c1c4f9
            | i >= len  = return ()
Packit c1c4f9
            | otherwise = do
Packit c1c4f9
                a <- peekByteOff src i
Packit c1c4f9
                b <- if i + 1 >= len then return 0 else peekByteOff src (i+1)
Packit c1c4f9
                c <- if i + 2 >= len then return 0 else peekByteOff src (i+2)
Packit c1c4f9
Packit c1c4f9
                let (w,x,y,z) = convert3 table a b c
Packit c1c4f9
Packit c1c4f9
                pokeByteOff dst di     w
Packit c1c4f9
                pokeByteOff dst (di+1) x
Packit c1c4f9
Packit c1c4f9
                if i + 1 < len
Packit c1c4f9
                    then
Packit c1c4f9
                        pokeByteOff dst (di+2) y
Packit c1c4f9
                    else
Packit c1c4f9
                        when padded (pokeByteOff dst (di+2) eqChar)
Packit c1c4f9
                if i + 2 < len
Packit c1c4f9
                    then
Packit c1c4f9
                        pokeByteOff dst (di+3) z
Packit c1c4f9
                    else
Packit c1c4f9
                        when padded (pokeByteOff dst (di+3) eqChar)
Packit c1c4f9
Packit c1c4f9
                loop (i+3) (di+4)
Packit c1c4f9
Packit c1c4f9
convert3 :: Addr# -> Word8 -> Word8 -> Word8 -> (Word8, Word8, Word8, Word8)
Packit c1c4f9
convert3 table (W8# a) (W8# b) (W8# c) =
Packit c1c4f9
    let !w = narrow8Word# (uncheckedShiftRL# a 2#)
Packit c1c4f9
        !x = or# (and# (uncheckedShiftL# a 4#) 0x30##) (uncheckedShiftRL# b 4#)
Packit c1c4f9
        !y = or# (and# (uncheckedShiftL# b 2#) 0x3c##) (uncheckedShiftRL# c 6#)
Packit c1c4f9
        !z = and# c 0x3f##
Packit c1c4f9
     in (index w, index x, index y, index z)
Packit c1c4f9
  where
Packit c1c4f9
        index :: Word# -> Word8
Packit c1c4f9
        index idx = W8# (indexWord8OffAddr# table (word2Int# idx))
Packit c1c4f9
Packit c1c4f9
-- | Get the length needed for the destination buffer for a base64 decoding.
Packit c1c4f9
--
Packit c1c4f9
-- if the length is not a multiple of 4, Nothing is returned
Packit c1c4f9
unBase64Length :: Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
unBase64Length src len
Packit c1c4f9
    | len < 1            = return Nothing
Packit c1c4f9
    | (len `mod` 4) /= 0 = return Nothing
Packit c1c4f9
    | otherwise          = do
Packit c1c4f9
        last1Byte <- peekByteOff src (len - 1)
Packit c1c4f9
        last2Byte <- peekByteOff src (len - 2)
Packit c1c4f9
        let dstLen = if last1Byte == eqAscii
Packit c1c4f9
                        then if last2Byte == eqAscii then 2 else 1
Packit c1c4f9
                        else 0
Packit c1c4f9
        return $ Just $ (len `div` 4) * 3 - dstLen
Packit c1c4f9
  where
Packit c1c4f9
        eqAscii :: Word8
Packit c1c4f9
        eqAscii = fromIntegral (fromEnum '=')
Packit c1c4f9
Packit c1c4f9
-- | Get the length needed for the destination buffer for an
Packit c1c4f9
-- <http://tools.ietf.org/html/rfc4648#section-3.2 unpadded> base64 decoding.
Packit c1c4f9
--
Packit c1c4f9
-- If the length of the encoded string is a multiple of 4, plus one, Nothing is
Packit c1c4f9
-- returned. Any other value can be valid without padding.
Packit c1c4f9
unBase64LengthUnpadded :: Int -> Maybe Int
Packit c1c4f9
unBase64LengthUnpadded len = case r of
Packit c1c4f9
    0 -> Just (3*q)
Packit c1c4f9
    2 -> Just (3*q + 1)
Packit c1c4f9
    3 -> Just (3*q + 2)
Packit c1c4f9
    _ -> Nothing
Packit c1c4f9
  where (q, r) = len `divMod` 4
Packit c1c4f9
Packit c1c4f9
fromBase64OpenBSD :: Ptr Word8 -> Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
fromBase64OpenBSD dst src len = fromBase64Unpadded rsetOpenBSD dst src len
Packit c1c4f9
Packit c1c4f9
fromBase64URLUnpadded :: Ptr Word8 -> Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
fromBase64URLUnpadded dst src len = fromBase64Unpadded rsetURL dst src len
Packit c1c4f9
Packit c1c4f9
fromBase64Unpadded :: (Word8 -> Word8) -> Ptr Word8 -> Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
fromBase64Unpadded rset dst src len = loop 0 0
Packit c1c4f9
  where loop di i
Packit c1c4f9
            | i == len       = return Nothing
Packit c1c4f9
            | i == len - 1   = return Nothing -- Shouldn't happen if len is valid
Packit c1c4f9
            | i == len - 2   = do
Packit c1c4f9
                a <- peekByteOff src i
Packit c1c4f9
                b <- peekByteOff src (i+1)
Packit c1c4f9
Packit c1c4f9
                case decode2 a b of
Packit c1c4f9
                    Left ofs -> return $ Just (i + ofs)
Packit c1c4f9
                    Right x  -> do
Packit c1c4f9
                        pokeByteOff dst di x
Packit c1c4f9
                        return Nothing
Packit c1c4f9
            | i == len - 3   = do
Packit c1c4f9
                a <- peekByteOff src i
Packit c1c4f9
                b <- peekByteOff src (i+1)
Packit c1c4f9
                c <- peekByteOff src (i+2)
Packit c1c4f9
Packit c1c4f9
                case decode3 a b c of
Packit c1c4f9
                    Left ofs    -> return $ Just (i + ofs)
Packit c1c4f9
                    Right (x,y) -> do
Packit c1c4f9
                        pokeByteOff dst di     x
Packit c1c4f9
                        pokeByteOff dst (di+1) y
Packit c1c4f9
                        return Nothing
Packit c1c4f9
            | otherwise      = do
Packit c1c4f9
                a <- peekByteOff src i
Packit c1c4f9
                b <- peekByteOff src (i+1)
Packit c1c4f9
                c <- peekByteOff src (i+2)
Packit c1c4f9
                d <- peekByteOff src (i+3)
Packit c1c4f9
Packit c1c4f9
                case decode4 a b c d of
Packit c1c4f9
                    Left ofs      -> return $ Just (i + ofs)
Packit c1c4f9
                    Right (x,y,z) -> do
Packit c1c4f9
                        pokeByteOff dst di     x
Packit c1c4f9
                        pokeByteOff dst (di+1) y
Packit c1c4f9
                        pokeByteOff dst (di+2) z
Packit c1c4f9
                        loop (di + 3) (i + 4)
Packit c1c4f9
Packit c1c4f9
        decode2 :: Word8 -> Word8 -> Either Int Word8
Packit c1c4f9
        decode2 a b =
Packit c1c4f9
            case (rset a, rset b) of
Packit c1c4f9
                (0xff, _   ) -> Left 0
Packit c1c4f9
                (_   , 0xff) -> Left 1
Packit c1c4f9
                (ra  , rb  ) -> Right ((ra `unsafeShiftL` 2) .|. (rb `unsafeShiftR` 4))
Packit c1c4f9
Packit c1c4f9
        decode3 :: Word8 -> Word8 -> Word8 -> Either Int (Word8, Word8)
Packit c1c4f9
        decode3 a b c =
Packit c1c4f9
            case (rset a, rset b, rset c) of
Packit c1c4f9
                (0xff, _   , _   ) -> Left 0
Packit c1c4f9
                (_   , 0xff, _   ) -> Left 1
Packit c1c4f9
                (_   , _   , 0xff) -> Left 2
Packit c1c4f9
                (ra  , rb  , rc  ) ->
Packit c1c4f9
                    let x = (ra `unsafeShiftL` 2) .|. (rb `unsafeShiftR` 4)
Packit c1c4f9
                        y = (rb `unsafeShiftL` 4) .|. (rc `unsafeShiftR` 2)
Packit c1c4f9
                     in Right (x,y)
Packit c1c4f9
Packit c1c4f9
Packit c1c4f9
        decode4 :: Word8 -> Word8 -> Word8 -> Word8 -> Either Int (Word8, Word8, Word8)
Packit c1c4f9
        decode4 a b c d =
Packit c1c4f9
            case (rset a, rset b, rset c, rset d) of
Packit c1c4f9
                (0xff, _   , _   , _   ) -> Left 0
Packit c1c4f9
                (_   , 0xff, _   , _   ) -> Left 1
Packit c1c4f9
                (_   , _   , 0xff, _   ) -> Left 2
Packit c1c4f9
                (_   , _   , _   , 0xff) -> Left 3
Packit c1c4f9
                (ra  , rb  , rc  , rd  ) ->
Packit c1c4f9
                    let x = (ra `unsafeShiftL` 2) .|. (rb `unsafeShiftR` 4)
Packit c1c4f9
                        y = (rb `unsafeShiftL` 4) .|. (rc `unsafeShiftR` 2)
Packit c1c4f9
                        z = (rc `unsafeShiftL` 6) .|. rd
Packit c1c4f9
                     in Right (x,y,z)
Packit c1c4f9
Packit c1c4f9
rsetURL :: Word8 -> Word8
Packit c1c4f9
rsetURL (W8# w)
Packit c1c4f9
    | booleanPrim (w `leWord#` 0xff##) = W8# (indexWord8OffAddr# rsetTable (word2Int# w))
Packit c1c4f9
    | otherwise                        = 0xff
Packit c1c4f9
  where !rsetTable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3e\xff\xff\
Packit c1c4f9
                     \\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\
Packit c1c4f9
                     \\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xff\xff\xff\xff\x3f\
Packit c1c4f9
                     \\xff\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\
Packit c1c4f9
                     \\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"#
Packit c1c4f9
Packit c1c4f9
rsetOpenBSD :: Word8 -> Word8
Packit c1c4f9
rsetOpenBSD (W8# w)
Packit c1c4f9
    | booleanPrim (w `leWord#` 0xff##) = W8# (indexWord8OffAddr# rsetTable (word2Int# w))
Packit c1c4f9
    | otherwise                        = 0xff
Packit c1c4f9
  where !rsetTable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\
Packit c1c4f9
                     \\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\
Packit c1c4f9
                     \\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\
Packit c1c4f9
                     \\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"#
Packit c1c4f9
Packit c1c4f9
Packit c1c4f9
-- | convert from base64 in @src@ to binary in @dst@, using the number of bytes specified
Packit c1c4f9
--
Packit c1c4f9
-- the user should use unBase64Length to compute the correct length, or check that
Packit c1c4f9
-- the length specification is proper. no check is done here.
Packit c1c4f9
fromBase64 :: Ptr Word8 -> Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
fromBase64 dst src len
Packit c1c4f9
    | len == 0  = return Nothing
Packit c1c4f9
    | otherwise = loop 0 0
Packit c1c4f9
  where loop di i
Packit c1c4f9
            | i == (len-4) = do
Packit c1c4f9
                a <- peekByteOff src i
Packit c1c4f9
                b <- peekByteOff src (i+1)
Packit c1c4f9
                c <- peekByteOff src (i+2)
Packit c1c4f9
                d <- peekByteOff src (i+3)
Packit c1c4f9
Packit c1c4f9
                let (nbBytes, c',d') =
Packit c1c4f9
                        case (c,d) of
Packit c1c4f9
                            (0x3d, 0x3d) -> (2, 0x30, 0x30)
Packit c1c4f9
                            (0x3d, _   ) -> (0, c, d) -- invalid: automatically 'c' will make it error out
Packit c1c4f9
                            (_   , 0x3d) -> (1, c, 0x30)
Packit c1c4f9
                            (_   , _   ) -> (0 :: Int, c, d)
Packit c1c4f9
                case decode4 a b c' d' of
Packit c1c4f9
                    Left ofs -> return $ Just (i + ofs)
Packit c1c4f9
                    Right (x,y,z) -> do
Packit c1c4f9
                        pokeByteOff dst di x
Packit c1c4f9
                        when (nbBytes < 2) $ pokeByteOff dst (di+1) y
Packit c1c4f9
                        when (nbBytes < 1) $ pokeByteOff dst (di+2) z
Packit c1c4f9
                        return Nothing
Packit c1c4f9
            | otherwise    = do
Packit c1c4f9
                a <- peekByteOff src i
Packit c1c4f9
                b <- peekByteOff src (i+1)
Packit c1c4f9
                c <- peekByteOff src (i+2)
Packit c1c4f9
                d <- peekByteOff src (i+3)
Packit c1c4f9
Packit c1c4f9
                case decode4 a b c d of
Packit c1c4f9
                    Left ofs      -> return $ Just (i + ofs)
Packit c1c4f9
                    Right (x,y,z) -> do
Packit c1c4f9
                        pokeByteOff dst di     x
Packit c1c4f9
                        pokeByteOff dst (di+1) y
Packit c1c4f9
                        pokeByteOff dst (di+2) z
Packit c1c4f9
                        loop (di + 3) (i + 4)
Packit c1c4f9
Packit c1c4f9
        decode4 :: Word8 -> Word8 -> Word8 -> Word8 -> Either Int (Word8, Word8, Word8)
Packit c1c4f9
        decode4 a b c d =
Packit c1c4f9
            case (rset a, rset b, rset c, rset d) of
Packit c1c4f9
                (0xff, _   , _   , _   ) -> Left 0
Packit c1c4f9
                (_   , 0xff, _   , _   ) -> Left 1
Packit c1c4f9
                (_   , _   , 0xff, _   ) -> Left 2
Packit c1c4f9
                (_   , _   , _   , 0xff) -> Left 3
Packit c1c4f9
                (ra  , rb  , rc  , rd  ) ->
Packit c1c4f9
                    let x = (ra `unsafeShiftL` 2) .|. (rb `unsafeShiftR` 4)
Packit c1c4f9
                        y = (rb `unsafeShiftL` 4) .|. (rc `unsafeShiftR` 2)
Packit c1c4f9
                        z = (rc `unsafeShiftL` 6) .|. rd
Packit c1c4f9
                     in Right (x,y,z)
Packit c1c4f9
Packit c1c4f9
        rset :: Word8 -> Word8
Packit c1c4f9
        rset (W8# w)
Packit c1c4f9
            | booleanPrim (w `leWord#` 0xff##) = W8# (indexWord8OffAddr# rsetTable (word2Int# w))
Packit c1c4f9
            | otherwise                        = 0xff
Packit c1c4f9
Packit c1c4f9
        !rsetTable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3e\xff\xff\xff\x3f\
Packit c1c4f9
                     \\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\
Packit c1c4f9
                     \\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\
Packit c1c4f9
                     \\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
Packit c1c4f9
                     \\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"#