// Base64 (RFC 4648 §4): the standard alphabet with `=` padding. The WebSocket handshake needs
// it to encode the Sec-WebSocket-Accept digest, and it is a small, self-contained primitive, so
// mooncat carries its own rather than pull a codec dependency in for one use.
///|
fn base64_char(i : Int) -> Byte {
if i < 26 {
(65 + i).to_byte() // 'A'..'Z'
} else if i < 52 {
(97 + (i - 26)).to_byte() // 'a'..'z'
} else if i < 62 {
(48 + (i - 52)).to_byte() // '0'..'9'
} else if i == 62 {
b'+'
} else {
b'/'
}
}
///|
fn base64_value(c : Int) -> Int {
if c >= 65 && c <= 90 {
c - 65
} else if c >= 97 && c <= 122 {
c - 97 + 26
} else if c >= 48 && c <= 57 {
c - 48 + 52
} else if c == 43 {
62
} else if c == 47 {
63
} else {
-1
}
}
///|
/// Encode `data` as standard base64 with `=` padding (RFC 4648 §4).
pub fn base64_encode(data : Bytes) -> Bytes {
let out = Buffer()
let n = data.length()
let mut i = 0
while i + 3 <= n {
let b0 = data[i].to_int()
let b1 = data[i + 1].to_int()
let b2 = data[i + 2].to_int()
out.write_byte(base64_char(b0 >> 2))
out.write_byte(base64_char(((b0 & 0x3) << 4) | (b1 >> 4)))
out.write_byte(base64_char(((b1 & 0xf) << 2) | (b2 >> 6)))
out.write_byte(base64_char(b2 & 0x3f))
i = i + 3
}
let rem = n - i
if rem == 1 {
let b0 = data[i].to_int()
out.write_byte(base64_char(b0 >> 2))
out.write_byte(base64_char((b0 & 0x3) << 4))
out.write_byte(b'=')
out.write_byte(b'=')
} else if rem == 2 {
let b0 = data[i].to_int()
let b1 = data[i + 1].to_int()
out.write_byte(base64_char(b0 >> 2))
out.write_byte(base64_char(((b0 & 0x3) << 4) | (b1 >> 4)))
out.write_byte(base64_char((b1 & 0xf) << 2))
out.write_byte(b'=')
}
out.to_bytes()
}
///|
/// Decode standard base64, ignoring padding and any non-alphabet bytes (RFC 4648 §4).
pub fn base64_decode(data : Bytes) -> Bytes {
let vals : Array[Int] = []
for i = 0; i < data.length(); i = i + 1 {
let v = base64_value(data[i].to_int())
if v >= 0 {
vals.push(v)
}
}
let out = Buffer()
let mut i = 0
while i + 4 <= vals.length() {
let n = (vals[i] << 18) |
(vals[i + 1] << 12) |
(vals[i + 2] << 6) |
vals[i + 3]
out.write_byte(((n >> 16) & 0xff).to_byte())
out.write_byte(((n >> 8) & 0xff).to_byte())
out.write_byte((n & 0xff).to_byte())
i = i + 4
}
let rem = vals.length() - i
if rem == 2 {
let n = (vals[i] << 18) | (vals[i + 1] << 12)
out.write_byte(((n >> 16) & 0xff).to_byte())
} else if rem == 3 {
let n = (vals[i] << 18) | (vals[i + 1] << 12) | (vals[i + 2] << 6)
out.write_byte(((n >> 16) & 0xff).to_byte())
out.write_byte(((n >> 8) & 0xff).to_byte())
}
out.to_bytes()
}