Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
fn sextet(b: u8) -> i64 {
match b {
b'A'..=b'Z' => (b - b'A') as i64,
b'a'..=b'z' => (b - b'a' + 26) as i64,
b'0'..=b'9' => (b - b'0' + 52) as i64,
b'+' => 62,
b'/' => 63,
_ => -1,
}
}
/// Standard, padded base64 back to bytes, strictly: no whitespace, no
/// characters outside the alphabet, padding to a multiple of four, and zero
/// bits after the last byte, so every byte string has exactly one spelling.
///
/// # Panics
/// Panics on any of those.
pub fn base64_decode(text: &str) -> Vec<i64> {
let mut values: Vec<i64> = Vec::with_capacity(text.len());
let mut padding = 0usize;
// Bytes, not chars: every accepted character is ASCII, and any byte of a
// multi-byte character fails the alphabet check just as the character does.
for &b in text.as_bytes() {
if b == b'=' {
padding += 1;
continue;
}
let v = sextet(b);
if v < 0 || padding > 0 {
panic!("base64 text may only contain A-Z, a-z, 0-9, + and /, with = padding at the end");
}
values.push(v);
}
if text.len() % 4 != 0 {
panic!("base64 text must be a multiple of 4 characters long, received {}", text.len());
}
if padding > 2 {
panic!("base64 text has too much = padding");
}
let mut out = Vec::with_capacity(values.len() / 4 * 3);
let mut chunks = values.chunks_exact(4);
for c in &mut chunks {
let n = (c[0] << 18) | (c[1] << 12) | (c[2] << 6) | c[3];
out.extend_from_slice(&[(n >> 16) & 255, (n >> 8) & 255, n & 255]);
}
match chunks.remainder() {
[a, b] => {
if b & 15 != 0 {
panic!("base64 text has non-zero bits after its last byte");
}
out.push(((a << 2) | (b >> 4)) & 255);
}
[a, b, c] => {
if c & 3 != 0 {
panic!("base64 text has non-zero bits after its last byte");
}
let n = (a << 18) | (b << 12) | (c << 6);
out.extend_from_slice(&[(n >> 16) & 255, (n >> 8) & 255]);
}
_ => {}
}
out
}
pub fn fune_vector(args: &[Value]) -> Value {
let text = match &args[0] {
Value::Str(s) => s.as_str(),
_ => panic!("base64 text must be a string"),
};
Value::Arr(base64_decode(text).into_iter().map(Value::Int).collect())
}