1 (* Copyright (C
) 2009 Matthew Fluet
.
2 * Copyright (C
) 1999-2006 Henry Cejtin
, Matthew Fluet
, Suresh
3 * Jagannathan
, and Stephen Weeks
.
5 * MLton is released under a BSD
-style license
.
6 * See the file MLton
-LICENSE for details
.
9 structure Base64
: BASE64
=
12 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
14 fun word8ToChar(w
: Word8.t
): char
=
15 String.sub(chars
, Word8.toInt w
)
19 ("Base64.word8ToChar", Word8.layout
, Char.layout
,
20 fn w
=> (0w0
<= w
andalso w
< 0w64
, fn _
=> true))
23 val charToWord8
: char
-> Word8.t option
=
25 Option
.map(String.peeki(chars
, fn (_
, c
') => c
= c
'),
26 fn (i
, _
) => Word8.fromInt i
))
29 Trace
.trace("Base64.charToWord8",
30 Char.layout
, Option
.layout
Word8.layout
)
35 fun 'a encodeGen
{array
: 'a
,
37 sub
: 'a
* int -> Word8.t
} =
40 val sub
= fn i
=> sub(array
, i
)
41 val (d
, m
) = Int.divMod(n
, 3)
42 val (d
, n
') = if m
= 0
47 val sub
= fn i
=> if i
>= n
then 0w0
else sub i
48 val _
= Assert
.assert("Base64.encodeGen", fn () => n
' mod 3 = 0)
49 val numChars
: int = 4 * d
50 val chars
= CharArray
.array(numChars
, #
"\000")
51 fun updateChar(j
, c
) = CharArray
.update(chars
, j
, c
)
52 fun update(j
: int, w
: Word8.t
) = updateChar(j
, word8ToChar w
)
53 fun loop(i
: int, j
: int) =
62 in update(j
, >>(w1
, 0w2
))
63 ; update(j
+ 1, orb(<<(andb(w1
, 0w3
), 0w4
),
65 ; update(j
+ 2, orb(<<(andb(w2
, 0wxF
), 0w2
),
67 ; update(j
+ 3, andb(w3
, 0wx3F
))
74 else (updateChar(j
- 1, pad
)
76 then updateChar(j
- 2, pad
)
78 (* need to patch for leftover bits
*)
79 in String.fromCharArray chars
82 fun encode s
= encodeGen
{array
= s
,
84 sub
= Char.toWord8
o String.sub
}
87 Trace
.trace("Base64.encode", String.layout
, String.layout
) encode
89 fun 'a decodeGen
{string: string,
90 new
: int * Word8.t
-> 'a
,
91 update
: 'a
* int * Word8.t
-> unit
}: 'a
=
93 val n
= String.size
string
94 val (d
, m
) = Int.divMod(n
, 4)
95 val _
= Assert
.assert("Base64.decodeGen", fn () => m
= 0)
96 fun sub i
= String.sub(string, i
)
99 then if pad
= sub(n
- 2)
103 val outputLength
= d
* 3 - numPads
104 val a
= new(outputLength
, 0w0
)
105 fun loop(i
: int, j
: int): unit
=
116 case charToWord8 c
of
119 ["Base64.decodeGen: strange char ",
129 if j
+ k
>= outputLength
131 else update(a
, j
+ k
, w
)
134 in update(0, orb(<<(w0
, 0w2
), >>(w1
, 0w4
)))
135 ; update(1, orb(<<(andb(w1
, 0wxF
), 0w4
), >>(w2
, 0w2
)))
136 ; update(2, orb(<<(andb(w2
, 0w3
), 0w6
), w3
))
138 in loop(i
+ 4, j
+ 3)
148 new
= fn (n
, w
) => CharArray
.array(n
, Char.fromWord8 w
),
149 update
= fn (a
, i
, w
) => CharArray
.update(a
, i
, Char.fromWord8 w
)})
152 Trace
.trace("Base64.decode", String.layout
, String.layout
) decode