Blame Data/Memory/Encoding/Base32.hs

Packit c1c4f9
-- |
Packit c1c4f9
-- Module      : Data.Memory.Encoding.Base32
Packit c1c4f9
-- License     : BSD-style
Packit c1c4f9
-- Maintainer  : Nicolas DI PRIMA <nicolas@di-prima.fr>
Packit c1c4f9
-- Stability   : experimental
Packit c1c4f9
-- Portability : unknown
Packit c1c4f9
--
Packit c1c4f9
-- Base32
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.Base32
Packit c1c4f9
    ( toBase32
Packit c1c4f9
    , unBase32Length
Packit c1c4f9
    , fromBase32
Packit c1c4f9
    ) where
Packit c1c4f9
Packit c1c4f9
import           Data.Memory.Internal.Compat
Packit c1c4f9
import           Data.Memory.Internal.CompatPrim
Packit c1c4f9
import           Data.Word
Packit c1c4f9
import           Data.Bits ((.|.))
Packit c1c4f9
import           GHC.Prim
Packit c1c4f9
import           GHC.Word
Packit c1c4f9
import           Control.Monad
Packit c1c4f9
import           Foreign.Storable
Packit c1c4f9
import           Foreign.Ptr (Ptr)
Packit c1c4f9
Packit c1c4f9
-- | Transform a number of bytes pointed by.@src in the base32 binary representation in @dst
Packit c1c4f9
--
Packit c1c4f9
-- destination memory need to be of correct size, otherwise it will lead
Packit c1c4f9
-- to really bad things.
Packit c1c4f9
toBase32 :: Ptr Word8 -- ^ input
Packit c1c4f9
         -> Ptr Word8 -- ^ output
Packit c1c4f9
         -> Int       -- ^ input len
Packit c1c4f9
         -> IO ()
Packit c1c4f9
toBase32 dst src len = loop 0 0
Packit c1c4f9
  where
Packit c1c4f9
    eqChar :: Word8
Packit c1c4f9
    eqChar = 0x3d
Packit c1c4f9
Packit c1c4f9
    peekOrZero :: Int -> IO Word8
Packit c1c4f9
    peekOrZero i
Packit c1c4f9
        | i >= len  = return 0
Packit c1c4f9
        | otherwise = peekByteOff src i
Packit c1c4f9
Packit c1c4f9
    pokeOrPadding :: Int -- for the test
Packit c1c4f9
                  -> Int -- src index
Packit c1c4f9
                  -> Word8 -- the value
Packit c1c4f9
                  -> IO ()
Packit c1c4f9
    pokeOrPadding i di v
Packit c1c4f9
        | i <  len  = pokeByteOff dst di v
Packit c1c4f9
        | otherwise = pokeByteOff dst di eqChar
Packit c1c4f9
Packit c1c4f9
    loop :: Int -- index input
Packit c1c4f9
         -> Int -- index output
Packit c1c4f9
         -> IO ()
Packit c1c4f9
    loop i di
Packit c1c4f9
        | i >  len  = return ()
Packit c1c4f9
        | otherwise = do
Packit c1c4f9
            i1 <- peekByteOff src i
Packit c1c4f9
            i2 <- peekOrZero (i + 1)
Packit c1c4f9
            i3 <- peekOrZero (i + 2)
Packit c1c4f9
            i4 <- peekOrZero (i + 3)
Packit c1c4f9
            i5 <- peekOrZero (i + 4)
Packit c1c4f9
Packit c1c4f9
            let (o1,o2,o3,o4,o5,o6,o7,o8) = toBase32Per5Bytes (i1, i2, i3, i4, i5)
Packit c1c4f9
Packit c1c4f9
            pokeByteOff dst di o1
Packit c1c4f9
            pokeByteOff dst (di + 1) o2
Packit c1c4f9
            pokeOrPadding (i + 1) (di + 2) o3
Packit c1c4f9
            pokeOrPadding (i + 1) (di + 3) o4
Packit c1c4f9
            pokeOrPadding (i + 2) (di + 4) o5
Packit c1c4f9
            pokeOrPadding (i + 3) (di + 5) o6
Packit c1c4f9
            pokeOrPadding (i + 3) (di + 6) o7
Packit c1c4f9
            pokeOrPadding (i + 4) (di + 7) o8
Packit c1c4f9
Packit c1c4f9
            loop (i+5) (di+8)
Packit c1c4f9
Packit c1c4f9
toBase32Per5Bytes :: (Word8, Word8, Word8, Word8, Word8)
Packit c1c4f9
                  -> (Word8, Word8, Word8, Word8, Word8, Word8, Word8, Word8)
Packit c1c4f9
toBase32Per5Bytes (W8# i1, W8# i2, W8# i3, W8# i4, W8# i5) =
Packit c1c4f9
    (index o1, index o2, index o3, index o4, index o5, index o6, index o7, index o8)
Packit c1c4f9
  where
Packit c1c4f9
    -- 1111 1000 >> 3
Packit c1c4f9
    !o1 =     (uncheckedShiftRL# (and# i1 0xF8##) 3#)
Packit c1c4f9
    -- 0000 0111 << 2 | 1100 0000 >> 6
Packit c1c4f9
    !o2 = or# (uncheckedShiftL#  (and# i1 0x07##) 2#) (uncheckedShiftRL# (and# i2 0xC0##) 6#)
Packit c1c4f9
    -- 0011 1110 >> 1
Packit c1c4f9
    !o3 =     (uncheckedShiftRL# (and# i2 0x3E##) 1#)
Packit c1c4f9
    -- 0000 0001 << 4 | 1111 0000 >> 4
Packit c1c4f9
    !o4 = or# (uncheckedShiftL#  (and# i2 0x01##) 4#) (uncheckedShiftRL# (and# i3 0xF0##) 4#)
Packit c1c4f9
    -- 0000 1111 << 1 | 1000 0000 >> 7
Packit c1c4f9
    !o5 = or# (uncheckedShiftL#  (and# i3 0x0F##) 1#) (uncheckedShiftRL# (and# i4 0x80##) 7#)
Packit c1c4f9
    -- 0111 1100 >> 2
Packit c1c4f9
    !o6 =     (uncheckedShiftRL# (and# i4 0x7C##) 2#)
Packit c1c4f9
    -- 0000 0011 << 3 | 1110 0000 >> 5
Packit c1c4f9
    !o7 = or# (uncheckedShiftL#  (and# i4 0x03##) 3#) (uncheckedShiftRL# (and# i5 0xE0##) 5#)
Packit c1c4f9
    -- 0001 1111
Packit c1c4f9
    !o8 =     ((and# i5 0x1F##))
Packit c1c4f9
Packit c1c4f9
    !set = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"#
Packit c1c4f9
Packit c1c4f9
    index :: Word# -> Word8
Packit c1c4f9
    index idx = W8# (indexWord8OffAddr# set (word2Int# idx))
Packit c1c4f9
Packit c1c4f9
-- | Get the length needed for the destination buffer for a base32 decoding.
Packit c1c4f9
--
Packit c1c4f9
-- if the length is not a multiple of 8, Nothing is returned
Packit c1c4f9
unBase32Length :: Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
unBase32Length src len
Packit c1c4f9
    | (len `mod` 8) /= 0 = return Nothing
Packit c1c4f9
    | otherwise          = do
Packit c1c4f9
        last1Byte <- peekByteOff src (len - 1)
Packit c1c4f9
        last2Byte <- peekByteOff src (len - 2)
Packit c1c4f9
        last3Byte <- peekByteOff src (len - 3)
Packit c1c4f9
        last4Byte <- peekByteOff src (len - 4)
Packit c1c4f9
        last5Byte <- peekByteOff src (len - 5)
Packit c1c4f9
        last6Byte <- peekByteOff src (len - 6)
Packit c1c4f9
Packit c1c4f9
        let dstLen = caseByte last1Byte last2Byte last3Byte last4Byte last5Byte last6Byte
Packit c1c4f9
        return $ Just $ (len `div` 8) * 5 - dstLen
Packit c1c4f9
  where
Packit c1c4f9
    caseByte :: Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Int
Packit c1c4f9
    caseByte last1 last2 last3 last4 last5 last6
Packit c1c4f9
        | last6 == eqAscii = 4
Packit c1c4f9
        | last5 == eqAscii = 3 -- error this padding is not expected (error will be detected in fromBase32)
Packit c1c4f9
        | last4 == eqAscii = 3
Packit c1c4f9
        | last3 == eqAscii = 2
Packit c1c4f9
        | last2 == eqAscii = 1 -- error this padding is not expected (error will be detected in fromBase32)
Packit c1c4f9
        | last1 == eqAscii = 1
Packit c1c4f9
        | otherwise        = 0
Packit c1c4f9
Packit c1c4f9
    eqAscii :: Word8
Packit c1c4f9
    eqAscii = 0x3D
Packit c1c4f9
Packit c1c4f9
-- | convert from base32 in @src to binary in @dst, using the number of bytes specified
Packit c1c4f9
--
Packit c1c4f9
-- the user should use unBase32Length to compute the correct length, or check that
Packit c1c4f9
-- the length specification is proper. no check is done here.
Packit c1c4f9
fromBase32 :: Ptr Word8 -> Ptr Word8 -> Int -> IO (Maybe Int)
Packit c1c4f9
fromBase32 dst src len
Packit c1c4f9
    | len == 0  = return Nothing
Packit c1c4f9
    | otherwise = loop 0 0
Packit c1c4f9
  where
Packit c1c4f9
    loop :: Int -- the index dst
Packit c1c4f9
         -> Int -- the index src
Packit c1c4f9
         -> IO (Maybe Int)
Packit c1c4f9
    loop di i
Packit c1c4f9
        | i == (len - 8) = do
Packit c1c4f9
            i1 <- peekByteOff src i
Packit c1c4f9
            i2 <- peekByteOff src (i + 1)
Packit c1c4f9
            i3 <- peekByteOff src (i + 2)
Packit c1c4f9
            i4 <- peekByteOff src (i + 3)
Packit c1c4f9
            i5 <- peekByteOff src (i + 4)
Packit c1c4f9
            i6 <- peekByteOff src (i + 5)
Packit c1c4f9
            i7 <- peekByteOff src (i + 6)
Packit c1c4f9
            i8 <- peekByteOff src (i + 7)
Packit c1c4f9
Packit c1c4f9
            let (nbBytes, i3', i4', i5', i6', i7', i8') =
Packit c1c4f9
                    case (i3, i4, i5, i6, i7, i8) of
Packit c1c4f9
                        (0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D) -> (6, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41)
Packit c1c4f9
                        (0x3D, _   , _   , _   , _   , _   ) -> (0, i3, i4, i5, i6, i7, i8) -- invalid
Packit c1c4f9
                        (_   , 0x3D, 0x3D, 0x3D, 0x3D, 0x3D) -> (5, i3  , 0x41, 0x41, 0x41, 0x41, 0x41)
Packit c1c4f9
                        (_   , 0x3D, _   , _   , _   , _   ) -> (0, i3, i4, i5, i6, i7, i8) -- invalid
Packit c1c4f9
                        (_   , _   , 0x3D, 0x3D, 0x3D, 0x3D) -> (4, i3  , i4  , 0x41, 0x41, 0x41, 0x41)
Packit c1c4f9
                        (_   , _   , 0x3D, _   , _   , _   ) -> (0, i3, i4, i5, i6, i7, i8) -- invalid
Packit c1c4f9
                        (_   , _   , _   , 0x3D, 0x3D, 0x3D) -> (3, i3  , i4  , i5  , 0x41, 0x41, 0x41)
Packit c1c4f9
                        (_   , _   , _   , 0x3D, _   , _   ) -> (0, i3, i4, i5, i6, i7, i8) -- invalid
Packit c1c4f9
                        (_   , _   , _   , _   , 0x3D, 0x3D) -> (2, i3  , i4  , i5  , i6  , 0x41, 0x41)
Packit c1c4f9
                        (_   , _   , _   , _   , 0x3D, _   ) -> (0, i3, i4, i5, i6, i7, i8) -- invalid
Packit c1c4f9
                        (_   , _   , _   , _   , _   , 0x3D) -> (1, i3  , i4  , i5  , i6  , i7  , 0x41)
Packit c1c4f9
                        (_   , _   , _   , _   , _   , _   ) -> (0 :: Int, i3, i4, i5, i6, i7, i8)
Packit c1c4f9
Packit c1c4f9
            case fromBase32Per8Bytes (i1, i2, i3', i4', i5', i6', i7', i8') of
Packit c1c4f9
                Left  ofs                  -> return $ Just (i + ofs)
Packit c1c4f9
                Right (o1, o2, o3, o4, o5) -> do
Packit c1c4f9
                    pokeByteOff dst  di    o1
Packit c1c4f9
                    pokeByteOff dst (di+1) o2
Packit c1c4f9
                    when (nbBytes < 5) $ pokeByteOff dst (di+2) o3
Packit c1c4f9
                    when (nbBytes < 4) $ pokeByteOff dst (di+3) o4
Packit c1c4f9
                    when (nbBytes < 2) $ pokeByteOff dst (di+4) o5
Packit c1c4f9
                    return Nothing
Packit c1c4f9
Packit c1c4f9
        | otherwise = do
Packit c1c4f9
            i1 <- peekByteOff src i
Packit c1c4f9
            i2 <- peekByteOff src (i + 1)
Packit c1c4f9
            i3 <- peekByteOff src (i + 2)
Packit c1c4f9
            i4 <- peekByteOff src (i + 3)
Packit c1c4f9
            i5 <- peekByteOff src (i + 4)
Packit c1c4f9
            i6 <- peekByteOff src (i + 5)
Packit c1c4f9
            i7 <- peekByteOff src (i + 6)
Packit c1c4f9
            i8 <- peekByteOff src (i + 7)
Packit c1c4f9
Packit c1c4f9
            case fromBase32Per8Bytes (i1, i2, i3, i4, i5, i6, i7, i8) of
Packit c1c4f9
                Left  ofs                  -> return $ Just (i + ofs)
Packit c1c4f9
                Right (o1, o2, o3, o4, o5) -> do
Packit c1c4f9
                    pokeByteOff dst  di    o1
Packit c1c4f9
                    pokeByteOff dst (di+1) o2
Packit c1c4f9
                    pokeByteOff dst (di+2) o3
Packit c1c4f9
                    pokeByteOff dst (di+3) o4
Packit c1c4f9
                    pokeByteOff dst (di+4) o5
Packit c1c4f9
                    loop (di+5) (i+8)
Packit c1c4f9
Packit c1c4f9
fromBase32Per8Bytes :: (Word8, Word8, Word8, Word8, Word8, Word8, Word8, Word8)
Packit c1c4f9
                    -> Either Int (Word8, Word8, Word8, Word8, Word8)
Packit c1c4f9
fromBase32Per8Bytes (i1, i2, i3, i4, i5, i6, i7, i8) =
Packit c1c4f9
    case (rset i1, rset i2, rset i3, rset i4, rset i5, rset i6, rset i7, rset i8) of
Packit c1c4f9
        (0xFF, _   , _   , _   , _   , _   , _   , _   ) -> Left 0
Packit c1c4f9
        (_   , 0xFF, _   , _   , _   , _   , _   , _   ) -> Left 1
Packit c1c4f9
        (_   , _   , 0xFF, _   , _   , _   , _   , _   ) -> Left 2
Packit c1c4f9
        (_   , _   , _   , 0xFF, _   , _   , _   , _   ) -> Left 3
Packit c1c4f9
        (_   , _   , _   , _   , 0xFF, _   , _   , _   ) -> Left 4
Packit c1c4f9
        (_   , _   , _   , _   , _   , 0xFF, _   , _   ) -> Left 5
Packit c1c4f9
        (_   , _   , _   , _   , _   , _   , 0xFF, _   ) -> Left 6
Packit c1c4f9
        (_   , _   , _   , _   , _   , _   , _   , 0xFF) -> Left 7
Packit c1c4f9
        (ri1 , ri2 , ri3 , ri4 , ri5 , ri6 , ri7 , ri8 ) ->
Packit c1c4f9
                -- 0001 1111 << 3 | 0001 11xx >> 2
Packit c1c4f9
            let o1 = (ri1 `unsafeShiftL` 3) .|. (ri2 `unsafeShiftR` 2)
Packit c1c4f9
                -- 000x xx11 << 6 | 0001 1111 << 1 | 0001 xxxx >> 4
Packit c1c4f9
                o2 = (ri2 `unsafeShiftL` 6) .|. (ri3 `unsafeShiftL` 1) .|. (ri4 `unsafeShiftR` 4)
Packit c1c4f9
                -- 000x 1111 << 4 | 0001 111x >> 1
Packit c1c4f9
                o3 = (ri4 `unsafeShiftL` 4) .|. (ri5 `unsafeShiftR` 1)
Packit c1c4f9
                -- 000x xxx1 << 7 | 0001 1111 << 2 | 0001 1xxx >> 3
Packit c1c4f9
                o4 = (ri5 `unsafeShiftL` 7) .|. (ri6 `unsafeShiftL` 2) .|. (ri7 `unsafeShiftR` 3)
Packit c1c4f9
                -- 000x x111 << 5 | 0001 1111
Packit c1c4f9
                o5 = (ri7 `unsafeShiftL` 5) .|. ri8
Packit c1c4f9
             in Right (o1, o2, o3, o4, o5)
Packit c1c4f9
  where
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\xFF\xFF\xFF\xFF\xFF\
Packit c1c4f9
                 \\xFF\xFF\x1A\x1B\x1C\x1D\x1E\x1F\xFF\xFF\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\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
                 \\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