Skip to content

Instantly share code, notes, and snippets.

@drathier
Created June 13, 2017 21:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drathier/4b9795fa47f8f8bc5d6229e300ae0c7c to your computer and use it in GitHub Desktop.
Save drathier/4b9795fa47f8f8bc5d6229e300ae0c7c to your computer and use it in GitHub Desktop.
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
@drathier
Copy link
Author

-- TODO: does this handle -2^31 correctly? -> yes it does

Copy link

ghost commented Jun 13, 2017

Nice crash messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment