{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

-- | RON version of Base64 encoding
module RON.Base64 (
    alphabet,
    decode,
    decode60,
    decode60base32,
    decode64,
    decode64base32,
    decodeLetter,
    decodeLetter4,
    encode,
    encode60,
    encode60short,
    encode64,
    encode64base32short,
    encodeLetter,
    encodeLetter4,
    isLetter,
) where

import           RON.Prelude

import           Data.Bits (complement, shiftL, shiftR, (.&.), (.|.))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL

import           RON.Util.Word (Word4, Word6 (W6), Word60, leastSignificant4,
                                leastSignificant6, leastSignificant60, safeCast)

-- | Base64 alphabet
alphabet :: ByteString
alphabet :: ByteString
alphabet = ByteString
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"

-- | Check if a character is in the Base64 alphabet.
isLetter :: Word8 -> Bool
isLetter :: Word8 -> Bool
isLetter Word8
c
    =  Word8
c Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
ord0 Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
9
    Bool -> Bool -> Bool
|| Word8
c Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
ordA Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
25
    Bool -> Bool -> Bool
|| Word8
c Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
orda Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
25
    Bool -> Bool -> Bool
|| Word8
c Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
ord_
    Bool -> Bool -> Bool
|| Word8
c Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
ordõ

-- | Convert a Base64 letter to a number [0-63]
decodeLetter :: Word8 -> Maybe Word6
decodeLetter :: Word8 -> Maybe Word6
decodeLetter Word8
x
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<  Word8
ord0 = Maybe Word6
forall a. Maybe a
Nothing
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
ord9 = Word6 -> Maybe Word6
forall a. a -> Maybe a
Just (Word6 -> Maybe Word6) -> (Word8 -> Word6) -> Word8 -> Maybe Word6
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 (Word8 -> Maybe Word6) -> Word8 -> Maybe Word6
forall a b. (a -> b) -> a -> b
$ Word8
x Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
ord0
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<  Word8
ordA = Maybe Word6
forall a. Maybe a
Nothing
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
ordZ = Word6 -> Maybe Word6
forall a. a -> Maybe a
Just (Word6 -> Maybe Word6) -> (Word8 -> Word6) -> Word8 -> Maybe Word6
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 (Word8 -> Maybe Word6) -> Word8 -> Maybe Word6
forall a b. (a -> b) -> a -> b
$ Word8
x Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
ordA Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ Word8
posA
    | Word8
x Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
ord_ = Word6 -> Maybe Word6
forall a. a -> Maybe a
Just (Word6 -> Maybe Word6) -> Word6 -> Maybe Word6
forall a b. (a -> b) -> a -> b
$ Word8 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 Word8
pos_
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<  Word8
orda = Maybe Word6
forall a. Maybe a
Nothing
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
ordz = Word6 -> Maybe Word6
forall a. a -> Maybe a
Just (Word6 -> Maybe Word6) -> (Word8 -> Word6) -> Word8 -> Maybe Word6
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 (Word8 -> Maybe Word6) -> Word8 -> Maybe Word6
forall a b. (a -> b) -> a -> b
$ Word8
x Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
orda Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ Word8
posa
    | Word8
x Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
ordõ = Word6 -> Maybe Word6
forall a. a -> Maybe a
Just (Word6 -> Maybe Word6) -> Word6 -> Maybe Word6
forall a b. (a -> b) -> a -> b
$ Word8 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 Word8
posõ
    | Bool
otherwise  = Maybe Word6
forall a. Maybe a
Nothing

-- ASCII milestone codes
ord0, ord9, ordA, ordZ, ord_, orda, ordz, ordõ :: Word8
ord0 :: Word8
ord0 = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'0'
ord9 :: Word8
ord9 = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'9'
ordA :: Word8
ordA = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'A'
ordZ :: Word8
ordZ = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'Z'
ord_ :: Word8
ord_ = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'_'
orda :: Word8
orda = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'a'
ordz :: Word8
ordz = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'z'
ordõ :: Word8
ordõ = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'~'

-- Base64 milestone codes
posA, pos_, posa, posõ :: Word8
posA :: Word8
posA = Word8
10
pos_ :: Word8
pos_ = Word8
36
posa :: Word8
posa = Word8
37
posõ :: Word8
posõ = Word8
63

-- | Convert a subset [0-F] of Base64 letters to a number [0-15]
decodeLetter4 :: Word8 -> Maybe Word4
decodeLetter4 :: Word8 -> Maybe Word4
decodeLetter4 Word8
x
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<  Word8
ord0 = Maybe Word4
forall a. Maybe a
Nothing
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
ord9 = Word4 -> Maybe Word4
forall a. a -> Maybe a
Just (Word4 -> Maybe Word4) -> (Word8 -> Word4) -> Word8 -> Maybe Word4
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Word4
forall integral. Integral integral => integral -> Word4
leastSignificant4 (Word8 -> Maybe Word4) -> Word8 -> Maybe Word4
forall a b. (a -> b) -> a -> b
$ Word8
x Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
ord0
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<  Word8
ordA = Maybe Word4
forall a. Maybe a
Nothing
    | Word8
x Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
ordZ = Word4 -> Maybe Word4
forall a. a -> Maybe a
Just (Word4 -> Maybe Word4) -> (Word8 -> Word4) -> Word8 -> Maybe Word4
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Word4
forall integral. Integral integral => integral -> Word4
leastSignificant4 (Word8 -> Maybe Word4) -> Word8 -> Maybe Word4
forall a b. (a -> b) -> a -> b
$ Word8
x Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
ordA Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ Word8
posA
    | Bool
otherwise  = Maybe Word4
forall a. Maybe a
Nothing

-- | Decode a blob from a Base64 string
decode :: ByteStringL -> Maybe ByteStringL
decode :: ByteStringL -> Maybe ByteStringL
decode =
    ([Word6] -> ByteStringL) -> Maybe [Word6] -> Maybe ByteStringL
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Word8] -> ByteStringL
BSL.pack ([Word8] -> ByteStringL)
-> ([Word6] -> [Word8]) -> [Word6] -> ByteStringL
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> [Word8]
go ([Word8] -> [Word8]) -> ([Word6] -> [Word8]) -> [Word6] -> [Word8]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word6 -> Word8) -> [Word6] -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map Word6 -> Word8
forall v w. SafeCast v w => v -> w
safeCast) (Maybe [Word6] -> Maybe ByteStringL)
-> (ByteStringL -> Maybe [Word6])
-> ByteStringL
-> Maybe ByteStringL
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8 -> Maybe Word6) -> [Word8] -> Maybe [Word6]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse Word8 -> Maybe Word6
decodeLetter ([Word8] -> Maybe [Word6])
-> (ByteStringL -> [Word8]) -> ByteStringL -> Maybe [Word6]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteStringL -> [Word8]
BSL.unpack
  where
    go :: [Word8] -> [Word8]
go = \case
        [Word8
a, Word8
b]       -> Word8 -> Word8 -> [Word8]
forall a. Bits a => a -> a -> [a]
decode2 Word8
a Word8
b
        [Word8
a, Word8
b, Word8
c]    -> Word8 -> Word8 -> Word8 -> [Word8]
forall a. (Bits a, Num a) => a -> a -> a -> [a]
decode3 Word8
a Word8
b Word8
c
        Word8
a:Word8
b:Word8
c:Word8
d:[Word8]
rest -> Word8 -> Word8 -> Word8 -> Word8 -> [Word8]
forall a. (Bits a, Num a) => a -> a -> a -> a -> [a]
decode4 Word8
a Word8
b Word8
c Word8
d [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Word8] -> [Word8]
go [Word8]
rest
        [Word8]
_            -> []
    decode2 :: a -> a -> [a]
decode2 a
a a
b = [(a
a a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
2) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
b a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
4)]
    decode3 :: a -> a -> a -> [a]
decode3 a
a a
b a
c =
        [ ( a
a             a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
2) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
b a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
4)
        , ((a
b a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b1111) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
4) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
c a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
2)
        ]
    decode4 :: a -> a -> a -> a -> [a]
decode4 a
a a
b a
c a
d =
        [ ( a
a             a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
2) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
b a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
4)
        , ((a
b a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b1111) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
4) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
c a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
2)
        , ((a
c a -> a -> a
forall a. Bits a => a -> a -> a
.&.   a
0b11) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
6) a -> a -> a
forall a. Bits a => a -> a -> a
.|.  a
d
        ]

-- | Decode a 60-bit number from a Base64 string
decode60 :: ByteString -> Maybe Word60
decode60 :: ByteString -> Maybe Word60
decode60 =
    (Word64 -> Word60) -> Maybe Word64 -> Maybe Word60
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word64 -> Word60
forall integral. Integral integral => integral -> Word60
leastSignificant60 (Maybe Word64 -> Maybe Word60)
-> ([Word8] -> Maybe Word64) -> [Word8] -> Maybe Word60
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [Word8] -> Maybe Word64
go Int
10
    ([Word8] -> Maybe Word60)
-> (ByteString -> Maybe [Word8]) -> ByteString -> Maybe Word60
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< (Word8 -> Maybe Word8) -> [Word8] -> Maybe [Word8]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse ((Word6 -> Word8) -> Maybe Word6 -> Maybe Word8
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word6 -> Word8
forall v w. SafeCast v w => v -> w
safeCast (Maybe Word6 -> Maybe Word8)
-> (Word8 -> Maybe Word6) -> Word8 -> Maybe Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Maybe Word6
decodeLetter) ([Word8] -> Maybe [Word8])
-> (ByteString -> [Word8]) -> ByteString -> Maybe [Word8]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [Word8]
BS.unpack
  where
    go :: Int -> [Word8] -> Maybe Word64
    go :: Int -> [Word8] -> Maybe Word64
go Int
n = \case
        []  | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 -> Word64 -> Maybe Word64
forall a. a -> Maybe a
Just Word64
0
        [Word8
a] | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
1 -> Word64 -> Maybe Word64
forall a. a -> Maybe a
Just (Word64 -> Maybe Word64) -> Word64 -> Maybe Word64
forall a b. (a -> b) -> a -> b
$ Word8 -> Word8 -> Word8 -> Word8 -> Word64
decode4 Word8
a Word8
0 Word8
0 Word8
0
        [Word8
a, Word8
b]
            | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
2 -> Word64 -> Maybe Word64
forall a. a -> Maybe a
Just (Word64 -> Maybe Word64) -> Word64 -> Maybe Word64
forall a b. (a -> b) -> a -> b
$ Word8 -> Word8 -> Word8 -> Word8 -> Word64
decode4 Word8
a Word8
b Word8
0 Word8
0
        [Word8
a, Word8
b, Word8
c]
            | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
3 -> Word64 -> Maybe Word64
forall a. a -> Maybe a
Just (Word64 -> Maybe Word64) -> Word64 -> Maybe Word64
forall a b. (a -> b) -> a -> b
$ Word8 -> Word8 -> Word8 -> Word8 -> Word64
decode4 Word8
a Word8
b Word8
c Word8
0
        (Word8
a:Word8
b:Word8
c:Word8
d:[Word8]
rest)
            | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
4 -> do
                Word64
lowerPart <- Int -> [Word8] -> Maybe Word64
go (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
4) [Word8]
rest
                Word64 -> Maybe Word64
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> Maybe Word64) -> Word64 -> Maybe Word64
forall a b. (a -> b) -> a -> b
$ Word8 -> Word8 -> Word8 -> Word8 -> Word64
decode4 Word8
a Word8
b Word8
c Word8
d Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
lowerPart Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
24)
        [Word8]
_ -> Maybe Word64
forall a. Maybe a
Nothing  -- extra input
    decode4 :: Word8 -> Word8 -> Word8 -> Word8 -> Word64
    decode4 :: Word8 -> Word8 -> Word8 -> Word8 -> Word64
decode4 Word8
a Word8
b Word8
c Word8
d =
        (Word8 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word8
a Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
54) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|.
        (Word8 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word8
b Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
48) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|.
        (Word8 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word8
c Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
42) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|.
        (Word8 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word8
d Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
36)

-- | Decode a 60-bit number from a Base32 string
decode60base32 :: ByteString -> Maybe Word60
decode60base32 :: ByteString -> Maybe Word60
decode60base32 =
    (Word64 -> Word60) -> Maybe Word64 -> Maybe Word60
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word64 -> Word60
forall integral. Integral integral => integral -> Word60
leastSignificant60 (Maybe Word64 -> Maybe Word60)
-> ([Word8] -> Maybe Word64) -> [Word8] -> Maybe Word60
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> Maybe Word64
go12
    ([Word8] -> Maybe Word60)
-> (ByteString -> Maybe [Word8]) -> ByteString -> Maybe Word60
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< (Word8 -> Maybe Word8) -> [Word8] -> Maybe [Word8]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse ((Word6 -> Word8) -> Maybe Word6 -> Maybe Word8
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word6 -> Word8
forall v w. SafeCast v w => v -> w
safeCast (Maybe Word6 -> Maybe Word8)
-> (Word8 -> Maybe Word6) -> Word8 -> Maybe Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Maybe Word6
decodeLetter) ([Word8] -> Maybe [Word8])
-> (ByteString -> [Word8]) -> ByteString -> Maybe [Word8]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [Word8]
BS.unpack
  where
    go12 :: [Word8] -> Maybe Word64
    go12 :: [Word8] -> Maybe Word64
go12 [Word8]
letters = do
        let ([Word8]
letters8, [Word8]
letters4) = Int -> [Word8] -> ([Word8], [Word8])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
8 [Word8]
letters
            w8 :: Word64
w8 = Int -> [Word8] -> Word64
decodeBase32 Int
8 [Word8]
letters8
        Word64
w4 <- [Word8] -> Maybe Word64
go4 [Word8]
letters4
        Word64 -> Maybe Word64
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> Maybe Word64) -> Word64 -> Maybe Word64
forall a b. (a -> b) -> a -> b
$ (Word64
w8 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
20) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word64
w4
    go4 :: [Word8] -> Maybe Word64
    go4 :: [Word8] -> Maybe Word64
go4 [Word8]
letters = case Int -> [Word8] -> ([Word8], [Word8])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
4 [Word8]
letters of
        ([Word8]
letters4, []) -> Word64 -> Maybe Word64
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word64 -> Maybe Word64) -> Word64 -> Maybe Word64
forall a b. (a -> b) -> a -> b
$ Int -> [Word8] -> Word64
decodeBase32 Int
4 [Word8]
letters4
        ([Word8], [Word8])
_ -> Maybe Word64
forall a. Maybe a
Nothing  -- extra input
    decodeBase32 :: Int -> [Word8] -> Word64
    decodeBase32 :: Int -> [Word8] -> Word64
decodeBase32 Int
len
        = (Word64 -> Word8 -> Word64) -> Word64 -> [Word8] -> Word64
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\Word64
acc Word8
b -> (Word64
acc Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
5) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word8
b) Word64
0
        ([Word8] -> Word64) -> ([Word8] -> [Word8]) -> [Word8] -> Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [Word8] -> [Word8]
forall a. Int -> [a] -> [a]
take Int
len
        ([Word8] -> [Word8]) -> ([Word8] -> [Word8]) -> [Word8] -> [Word8]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ Word8 -> [Word8]
forall a. a -> [a]
repeat Word8
0)

-- | Decode a 64-bit number from a Base64 string
decode64 :: ByteString -> Maybe Word64
decode64 :: ByteString -> Maybe Word64
decode64 ByteString
s = do
    (Word8
s0, ByteString
s1) <- ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
s
    Word4 -> Word60 -> Word64
cons64 (Word4 -> Word60 -> Word64)
-> Maybe Word4 -> Maybe (Word60 -> Word64)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Word8 -> Maybe Word4
decodeLetter4 Word8
s0 Maybe (Word60 -> Word64) -> Maybe Word60 -> Maybe Word64
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ByteString -> Maybe Word60
decode60 ByteString
s1

-- | Decode a 64-bit number from a Base32 string
decode64base32 :: ByteString -> Maybe Word64
decode64base32 :: ByteString -> Maybe Word64
decode64base32 ByteString
s = do
    (Word8
s0, ByteString
s1) <- ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
s
    Word4 -> Word60 -> Word64
cons64 (Word4 -> Word60 -> Word64)
-> Maybe Word4 -> Maybe (Word60 -> Word64)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Word8 -> Maybe Word4
decodeLetter4 Word8
s0 Maybe (Word60 -> Word64) -> Maybe Word60 -> Maybe Word64
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ByteString -> Maybe Word60
decode60base32 ByteString
s1

cons64 :: Word4 -> Word60 -> Word64
cons64 :: Word4 -> Word60 -> Word64
cons64 Word4
v Word60
w = (Word4 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word4
v Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
60) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word60 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word60
w

-- | Encode a blob to a Base64 string
encode :: ByteStringL -> ByteStringL
encode :: ByteStringL -> ByteStringL
encode = [Word8] -> ByteStringL
BSL.pack ([Word8] -> ByteStringL)
-> (ByteStringL -> [Word8]) -> ByteStringL -> ByteStringL
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> [Word8]
go ([Word8] -> [Word8])
-> (ByteStringL -> [Word8]) -> ByteStringL -> [Word8]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteStringL -> [Word8]
BSL.unpack
  where
    go :: [Word8] -> [Word8]
go = \case
        []         -> []
        [Word8
a]        -> Word8 -> [Word8]
forall a. (Integral a, Bits a) => a -> [Word8]
encode1 Word8
a
        [Word8
a, Word8
b]     -> Word8 -> Word8 -> [Word8]
forall a. (Integral a, Bits a) => a -> a -> [Word8]
encode2 Word8
a Word8
b
        Word8
a:Word8
b:Word8
c:[Word8]
rest -> Word8 -> Word8 -> Word8 -> [Word8]
forall a. (Integral a, Bits a) => a -> a -> a -> [Word8]
encode3 Word8
a Word8
b Word8
c [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Word8] -> [Word8]
go [Word8]
rest
    encode1 :: a -> [Word8]
encode1 a
a =
        (a -> Word8) -> [a] -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map (Word6 -> Word8
encodeLetter (Word6 -> Word8) -> (a -> Word6) -> a -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6)
            [a
a a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
2, (a
a a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b11) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
4]
    encode2 :: a -> a -> [Word8]
encode2 a
a a
b = (a -> Word8) -> [a] -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map (Word6 -> Word8
encodeLetter (Word6 -> Word8) -> (a -> Word6) -> a -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6)
        [                                  a
a a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
2
        , ((a
a a -> a -> a
forall a. Bits a => a -> a -> a
.&.   a
0b11) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
4) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
b a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
4)
        ,  (a
b a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b1111) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
2
        ]
    encode3 :: a -> a -> a -> [Word8]
encode3 a
a a
b a
c = (a -> Word8) -> [a] -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map (Word6 -> Word8
encodeLetter (Word6 -> Word8) -> (a -> Word6) -> a -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6)
        [                                    a
a a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
2
        , ((a
a a -> a -> a
forall a. Bits a => a -> a -> a
.&.     a
0b11) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
4) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
b a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
4)
        , ((a
b a -> a -> a
forall a. Bits a => a -> a -> a
.&.   a
0b1111) a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` Int
2) a -> a -> a
forall a. Bits a => a -> a -> a
.|. (a
c a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` Int
6)
        ,   a
c a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b111111
        ]

-- | Convert a number from [0..63] to a single letter
encodeLetter :: Word6 -> Word8
encodeLetter :: Word6 -> Word8
encodeLetter Word6
i = ByteString
alphabet ByteString -> Int -> Word8
`BS.index` Word6 -> Int
forall v w. SafeCast v w => v -> w
safeCast Word6
i

-- | Convert a number from [0..15] to a single letter
encodeLetter4 :: Word4 -> Word8
encodeLetter4 :: Word4 -> Word8
encodeLetter4 Word4
i = ByteString
alphabet ByteString -> Int -> Word8
`BS.index` Word4 -> Int
forall v w. SafeCast v w => v -> w
safeCast Word4
i

-- | Encode a 60-bit number to a Base64 string
encode60 :: Word60 -> ByteString
encode60 :: Word60 -> ByteString
encode60 Word60
w = [Word8] -> ByteString
BS.pack
    [ Word6 -> Word8
encodeLetter (Word6 -> Word8) -> Word6 -> Word8
forall a b. (a -> b) -> a -> b
$ Word64 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 (Word60 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word60
w Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` (Int
6 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i) :: Word64)
    | Int
i <- [Int
9, Int
8 .. Int
0]
    ]

-- | Encode a 60-bit number to a Base64 string, dropping trailing zeroes
encode60short :: Word60 -> ByteString
encode60short :: Word60 -> ByteString
encode60short Word60
v = case Word60 -> Word64
forall v w. SafeCast v w => v -> w
safeCast Word60
v :: Word64 of
    Word64
0 -> ByteString
"0"
    Word64
x -> [Word8] -> ByteString
BS.pack ([Word8] -> ByteString)
-> ([Word64] -> [Word8]) -> [Word64] -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word64 -> Word8) -> [Word64] -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map (Word6 -> Word8
encodeLetter (Word6 -> Word8) -> (Word64 -> Word6) -> Word64 -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6) ([Word64] -> ByteString) -> [Word64] -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> Word64 -> [Word64]
forall a. (Num a, Bits a) => Int -> a -> [a]
go Int
9 Word64
x
  where
    go :: Int -> a -> [a]
go Int
_ a
0 = []
    go Int
i a
w =
        (a
w a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` (Int
6 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i)) a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b111111 a -> [a] -> [a]
forall a. a -> [a] -> [a]
:
        Int -> a -> [a]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (a
w a -> a -> a
forall a. Bits a => a -> a -> a
.&. a -> a
forall a. Bits a => a -> a
complement (a
0b111111 a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` (Int
6 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i)))

-- | Encode a 64-bit number to a Base32 string, dropping trailing zeroes
encode64base32short :: Word64 -> ByteString
encode64base32short :: Word64 -> ByteString
encode64base32short = \case
    Word64
0 -> ByteString
"0"
    Word64
x -> [Word8] -> ByteString
BS.pack ([Word8] -> ByteString)
-> ([Word64] -> [Word8]) -> [Word64] -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word64 -> Word8) -> [Word64] -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map (Word6 -> Word8
encodeLetter (Word6 -> Word8) -> (Word64 -> Word6) -> Word64 -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant5) ([Word64] -> ByteString) -> [Word64] -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> Word64 -> [Word64]
forall a. (Num a, Bits a) => Int -> a -> [a]
go Int
12 Word64
x
  where
    go :: Int -> a -> [a]
go Int
_ a
0 = []
    go Int
i a
w =
        (a
w a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftR` (Int
5 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i)) a -> a -> a
forall a. Bits a => a -> a -> a
.&. a
0b11111 a -> [a] -> [a]
forall a. a -> [a] -> [a]
:
        Int -> a -> [a]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (a
w a -> a -> a
forall a. Bits a => a -> a -> a
.&. a -> a
forall a. Bits a => a -> a
complement (a
0b11111 a -> Int -> a
forall a. Bits a => a -> Int -> a
`shiftL` (Int
5 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
i)))

    leastSignificant5 :: a -> Word6
leastSignificant5 a
w = Word8 -> Word6
W6 (Word8 -> Word6) -> Word8 -> Word6
forall a b. (a -> b) -> a -> b
$ a -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
w Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
0b11111

-- | Encode a 64-bit number to a Base64 string
encode64 :: Word64 -> ByteString
encode64 :: Word64 -> ByteString
encode64 Word64
w =
    Word6 -> Word8
encodeLetter (Word64 -> Word6
forall integral. Integral integral => integral -> Word6
leastSignificant6 (Word64 -> Word6) -> Word64 -> Word6
forall a b. (a -> b) -> a -> b
$ Word64
w Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftR` Int
60)
    Word8 -> ByteString -> ByteString
`BS.cons` Word60 -> ByteString
encode60 (Word64 -> Word60
forall integral. Integral integral => integral -> Word60
leastSignificant60 Word64
w)