// base64.mbt
//
// 标准 Base64(RFC 4648 §4,含 `=` 填充)。
//
// 用途单一但不可省:Ogg(Vorbis / Opus)的封面存在 Vorbis Comment 的
// `METADATA_BLOCK_PICTURE` 条目里,值是 FLAC PICTURE 块的**Base64 文本**,
// 不是二进制帧。没有这套编解码,Ogg 封面既读不出也写不进。
///|
/// Base64 字母表(RFC 4648 §4 标准表;不是 URL-safe 变体)。
const BASE64_ALPHABET : String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
///|
/// 字母表的字节形式。表是纯 ASCII,因此字节值即字符。
fn base64_alphabet() -> Bytes {
@utf8.encode(BASE64_ALPHABET)
}
///|
/// Base64 编码。空输入返回空串。
pub fn base64_encode(data : BytesView) -> String {
let table = base64_alphabet()
let n = data.length()
let buf = @buffer.Buffer()
let mut i = 0
while i + 2 < n {
let a = data.at(i).to_int()
let b = data.at(i + 1).to_int()
let c = data.at(i + 2).to_int()
buf.write_byte(table.at(a >> 2))
buf.write_byte(table.at(((a & 0x03) << 4) | (b >> 4)))
buf.write_byte(table.at(((b & 0x0F) << 2) | (c >> 6)))
buf.write_byte(table.at(c & 0x3F))
i = i + 3
}
let rest = n - i
if rest == 1 {
let a = data.at(i).to_int()
buf.write_byte(table.at(a >> 2))
buf.write_byte(table.at((a & 0x03) << 4))
buf.write_byte(b'=')
buf.write_byte(b'=')
} else if rest == 2 {
let a = data.at(i).to_int()
let b = data.at(i + 1).to_int()
buf.write_byte(table.at(a >> 2))
buf.write_byte(table.at(((a & 0x03) << 4) | (b >> 4)))
buf.write_byte(table.at((b & 0x0F) << 2))
buf.write_byte(b'=')
}
// 注意:这里不能对字节缓冲用 `to_string()`(它按 UTF-16 解释内容),
// 必须按 UTF-8 解码——字母表是纯 ASCII,解码无损。
@utf8.decode_lossy(buf.to_bytes().exact_view())
}
///|
/// 反向查表:ASCII 字节 → 6 位值,非法字符为 -1。
///
/// 用 256 项定长表而不是每次扫字母表:封面动辄几十 KB,
/// 逐字符 O(64) 扫描会在真实文件上变成明显的热点。
fn base64_reverse_table() -> FixedArray[Int32] {
let table = FixedArray::make(256, -1)
let alpha = base64_alphabet()
for i in 0..<64 {
table[alpha.at(i).to_int()] = i
}
table
}
///|
/// Base64 解码。
///
/// 忽略空白(空格 / `\t` / `\r` / `\n`)与尾部 `=` 填充——Vorbis Comment
/// 里的这个值经常被编辑器或工具折行,按"非法字符"报错会把能读的文件读死。
pub fn base64_decode(text : String) -> Bytes raise TagError {
let src = @utf8.encode(text)
let rev = base64_reverse_table()
let vals : Array[Int32] = []
let mut saw_pad = false
for i in 0..> 4)).to_byte())
buf.write_byte((((b & 0x0F) << 4) | (c >> 2)).to_byte())
buf.write_byte((((c & 0x03) << 6) | d).to_byte())
i = i + 4
}
let rest = n - i
if rest == 2 {
let a = vals[i]
let b = vals[i + 1]
buf.write_byte(((a << 2) | (b >> 4)).to_byte())
} else if rest == 3 {
let a = vals[i]
let b = vals[i + 1]
let c = vals[i + 2]
buf.write_byte(((a << 2) | (b >> 4)).to_byte())
buf.write_byte((((b & 0x0F) << 4) | (c >> 2)).to_byte())
}
buf.to_bytes()
}