Created
June 13, 2017 21:37
-
-
Save drathier/4b9795fa47f8f8bc5d6229e300ae0c7c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Fact.Base64 exposing (..) | |
{-| Decode/encode base64 strings to/from ints. | |
-} | |
import Bitwise | |
import Char | |
import List.Extra | |
alphabet = | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" | |
toBytes : String -> List Int | |
toBytes input = | |
case String.toList input of | |
a :: b :: c :: d :: rest -> | |
let | |
x = | |
Bitwise.or | |
(a |> toInt |> Bitwise.and (1 + 2 + 4 + 8 + 16 + 32) |> Bitwise.shiftLeftBy 2) | |
(b |> toInt |> Bitwise.and (16 + 32) |> Bitwise.shiftRightZfBy 4) | |
y = | |
Bitwise.or | |
(b |> toInt |> Bitwise.and (1 + 2 + 4 + 8) |> Bitwise.shiftLeftBy 4) | |
(c |> toInt |> Bitwise.and (4 + 8 + 16 + 32) |> Bitwise.shiftRightZfBy 2) | |
z = | |
Bitwise.or | |
(c |> toInt |> Bitwise.and (1 + 2) |> Bitwise.shiftLeftBy 6) | |
(d |> toInt |> Bitwise.and (1 + 2 + 4 + 8 + 16 + 32) |> Bitwise.shiftRightZfBy 0) | |
in | |
x :: y :: z :: (toBytes <| String.fromList rest) | |
[] -> | |
[] | |
_ -> | |
-- append a zero until we have an even multiple of 4 | |
-- note that this only works because we're appending zeroes, and because it's a non-even multiple of 4. | |
toBytes (input ++ "A") |> List.reverse |> List.drop 1 |> List.reverse | |
toBase64 : List Int -> String | |
toBase64 input = | |
toBase64Impl input |> List.map toBase64Char |> String.fromList | |
toBase64Impl : List Int -> List Int | |
toBase64Impl input = | |
case input of | |
x :: y :: z :: rest -> | |
let | |
a = | |
x |> Bitwise.and (4 + 8 + 16 + 32 + 64 + 128) |> Bitwise.shiftRightZfBy 2 | |
b = | |
Bitwise.or | |
(x |> Bitwise.and (1 + 2) |> Bitwise.shiftLeftBy 4) | |
(y |> Bitwise.and (16 + 32 + 64 + 128) |> Bitwise.shiftRightZfBy 4) | |
c = | |
Bitwise.or | |
(y |> Bitwise.and (1 + 2 + 4 + 8) |> Bitwise.shiftLeftBy 2) | |
(z |> Bitwise.and (64 + 128) |> Bitwise.shiftRightZfBy 6) | |
d = | |
z |> Bitwise.and (1 + 2 + 4 + 8 + 16 + 32) | |
in | |
[ a, b, c, d ] ++ toBase64Impl rest | |
[] -> | |
[] | |
_ -> | |
toBase64Impl (input ++ [ 0 ]) |> List.reverse |> List.drop 1 |> List.reverse | |
-- TODO: does this handle -2^31 correctly? | |
bytesToInts : List Int -> List Int | |
bytesToInts ints = | |
case ints of | |
a :: b :: c :: d :: rest -> | |
List.foldl | |
Bitwise.or | |
0 | |
[ a |> Bitwise.shiftLeftBy 24 | |
, b |> Bitwise.shiftLeftBy 16 | |
, c |> Bitwise.shiftLeftBy 8 | |
, d |> Bitwise.shiftLeftBy 0 | |
] | |
:: bytesToInts rest | |
[] -> | |
[] | |
_ -> | |
Debug.crash "pack4" | |
intsToBytes : List Int -> List Int | |
intsToBytes ints = | |
case ints of | |
a :: rest -> | |
(a |> Bitwise.shiftRightZfBy 24 |> Bitwise.and 255) | |
:: (a |> Bitwise.shiftRightZfBy 16 |> Bitwise.and 255) | |
:: (a |> Bitwise.shiftRightZfBy 8 |> Bitwise.and 255) | |
:: (a |> Bitwise.shiftRightZfBy 0 |> Bitwise.and 255) | |
:: intsToBytes rest | |
[] -> | |
[] | |
toInt : Char -> Int | |
toInt c = | |
case String.indices (String.fromChar c) alphabet of | |
[ i ] -> | |
i | |
_ -> | |
Debug.crash (toString ( "wtf1", c, alphabet )) | |
toBase64Char : Int -> Char | |
toBase64Char i = | |
case i of | |
0 -> | |
'A' | |
1 -> | |
'B' | |
2 -> | |
'C' | |
3 -> | |
'D' | |
4 -> | |
'E' | |
5 -> | |
'F' | |
6 -> | |
'G' | |
7 -> | |
'H' | |
8 -> | |
'I' | |
9 -> | |
'J' | |
10 -> | |
'K' | |
11 -> | |
'L' | |
12 -> | |
'M' | |
13 -> | |
'N' | |
14 -> | |
'O' | |
15 -> | |
'P' | |
16 -> | |
'Q' | |
17 -> | |
'R' | |
18 -> | |
'S' | |
19 -> | |
'T' | |
20 -> | |
'U' | |
21 -> | |
'V' | |
22 -> | |
'W' | |
23 -> | |
'X' | |
24 -> | |
'Y' | |
25 -> | |
'Z' | |
26 -> | |
'a' | |
27 -> | |
'b' | |
28 -> | |
'c' | |
29 -> | |
'd' | |
30 -> | |
'e' | |
31 -> | |
'f' | |
32 -> | |
'g' | |
33 -> | |
'h' | |
34 -> | |
'i' | |
35 -> | |
'j' | |
36 -> | |
'k' | |
37 -> | |
'l' | |
38 -> | |
'm' | |
39 -> | |
'n' | |
40 -> | |
'o' | |
41 -> | |
'p' | |
42 -> | |
'q' | |
43 -> | |
'r' | |
44 -> | |
's' | |
45 -> | |
't' | |
46 -> | |
'u' | |
47 -> | |
'v' | |
48 -> | |
'w' | |
49 -> | |
'x' | |
50 -> | |
'y' | |
51 -> | |
'z' | |
52 -> | |
'0' | |
53 -> | |
'1' | |
54 -> | |
'2' | |
55 -> | |
'3' | |
56 -> | |
'4' | |
57 -> | |
'5' | |
58 -> | |
'6' | |
59 -> | |
'7' | |
60 -> | |
'8' | |
61 -> | |
'9' | |
62 -> | |
'_' | |
63 -> | |
'-' | |
_ -> | |
Debug.crash (toString ( "wtf2", i, alphabet )) | |
debug msg value = | |
let | |
x = | |
Debug.log (toString msg) () | |
in | |
value |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-- TODO: does this handle -2^31 correctly? -> yes it does