// ============================================================
// 文本编码自动检测与解码
//
// 支持:UTF-8 / UTF-16(LE/BE) / UTF-32(LE/BE) / GBK / Big5 / Latin-1
// 检测策略(按优先级):
// 1. BOM 嗅探(UTF-8 / UTF-16LE/BE / UTF-32LE/BE)
// 2. UTF-32 无 BOM 的零字节模式(每 4 字节组 3 个 0x00)
// 3. UTF-16 无 BOM 的零字节模式(某一奇偶位大量 0x00)
// 4. 严格 UTF-8 校验
// 5. GBK / Big5 双字节启发式(GBK 独有尾字节 0x80–0xA0 优先判为 GBK)
// 6. Latin-1 兜底
//
// GBK/Big5 码表来自 Unicode 官方映射文件(CP936.TXT / CP950.TXT),
// 见 gbk_table.mbt / big5_table.mbt。码表按线性索引存储(UInt16 小端),
// 查找 O(1)。
// ============================================================
///|
/// 文本编码类型
pub(all) enum Encoding {
Utf8
Utf16Le
Utf16Be
Utf32Le
Utf32Be
Gbk
Big5
Latin1
} derive(Eq, @debug.Debug)
///|
/// 检测字节流的文本编码(启发式,面向文本文件)。
///
/// 无法确定时优先 UTF-8 → GBK → Latin-1,简体中文环境下默认判 GBK。
pub fn detect_encoding(bytes : Bytes) -> Encoding {
let arr = bytes.to_array()
let n = arr.length()
// 1. BOM 嗅探
if n >= 4 &&
arr[0] == b'\xFF' &&
arr[1] == b'\xFE' &&
arr[2] == b'\x00' &&
arr[3] == b'\x00' {
return Encoding::Utf32Le
}
if n >= 4 &&
arr[0] == b'\x00' &&
arr[1] == b'\x00' &&
arr[2] == b'\xFE' &&
arr[3] == b'\xFF' {
return Encoding::Utf32Be
}
if n >= 2 && arr[0] == b'\xFF' && arr[1] == b'\xFE' {
return Encoding::Utf16Le
}
if n >= 2 && arr[0] == b'\xFE' && arr[1] == b'\xFF' {
return Encoding::Utf16Be
}
if n >= 3 && arr[0] == b'\xEF' && arr[1] == b'\xBB' && arr[2] == b'\xBF' {
return Encoding::Utf8
}
// 2. UTF-32 无 BOM:每 4 字节组中 3 个 0x00
let u32 = utf32_endian(arr)
if u32 == 0 {
return Encoding::Utf32Le
}
if u32 == 1 {
return Encoding::Utf32Be
}
// 3. UTF-16 无 BOM:某一奇偶位大量 0x00
let u16 = utf16_endian(arr)
if u16 == 0 {
return Encoding::Utf16Le
}
if u16 == 1 {
return Encoding::Utf16Be
}
// 4. 严格 UTF-8 校验
if is_valid_utf8(arr) {
return Encoding::Utf8
}
// 5. GBK / Big5 双字节启发式
let (gbk_only, gbk_pairs, stray) = classify_dbc(arr)
if gbk_only > 0 || gbk_pairs > stray {
return Encoding::Gbk
}
// 6. 其余高位字节 → Latin-1 兜底
Encoding::Latin1
}
///|
/// 按指定编码解码为字符串(损坏字节替换为 U+FFFD)
pub fn decode(bytes : Bytes, encoding : Encoding) -> String {
match encoding {
Encoding::Utf8 => @utf8.decode_lossy(strip_utf8_bom(bytes))
Encoding::Utf16Le =>
@utf16.decode_lossy(
bytes,
ignore_bom=true,
endianness=@utf16.Endian::Little,
)
Encoding::Utf16Be =>
@utf16.decode_lossy(bytes, ignore_bom=true, endianness=@utf16.Endian::Big)
Encoding::Utf32Le => utf32_decode(bytes, false)
Encoding::Utf32Be => utf32_decode(bytes, true)
Encoding::Gbk => gbk_decode(bytes)
Encoding::Big5 => big5_decode(bytes)
Encoding::Latin1 => latin1_decode(bytes)
}
}
///|
/// 自动检测编码并解码(= decode(bytes, detect_encoding(bytes)))
pub fn decode_auto(bytes : Bytes) -> String {
decode(bytes, detect_encoding(bytes))
}
///|
/// 显式按 GBK(简体中文)解码。自动检测对双字节文本默认即判 GBK。
pub fn decode_gbk(bytes : Bytes) -> String {
decode(bytes, Encoding::Gbk)
}
///|
/// 显式按 Big5(繁体中文)解码。
/// GBK 与 Big5 在字节层面无法可靠区分,自动检测默认判 GBK,
/// 繁体文本需显式调用本函数。
pub fn decode_big5(bytes : Bytes) -> String {
decode(bytes, Encoding::Big5)
}
///|
/// 解析编码名称为 Encoding(不区分大小写),支持常见别名。
/// 无法识别时返回 None,调用方可回退到自动检测。
pub fn parse_encoding(name : String) -> Encoding? {
let n = name.to_lower()
if n == "utf-8" || n == "utf8" {
Some(Encoding::Utf8)
} else if n == "utf-16le" ||
n == "utf16le" ||
n == "utf-16" ||
n == "utf16" ||
n == "unicode" {
Some(Encoding::Utf16Le)
} else if n == "utf-16be" || n == "utf16be" {
Some(Encoding::Utf16Be)
} else if n == "utf-32le" || n == "utf32le" || n == "utf-32" || n == "utf32" {
Some(Encoding::Utf32Le)
} else if n == "utf-32be" || n == "utf32be" {
Some(Encoding::Utf32Be)
} else if n == "gbk" || n == "gb2312" || n == "cp936" || n == "gb18030" {
Some(Encoding::Gbk)
} else if n == "big5" || n == "cp950" {
Some(Encoding::Big5)
} else if n == "latin1" || n == "latin-1" || n == "iso-8859-1" {
Some(Encoding::Latin1)
} else {
None
}
}
///|
/// 按指定编码把字符串编码为字节序列。
/// 不可映射的字符替换为 '?'(0x3F)。bom 为 true 时对 UTF-8/16/32 写出 BOM。
pub fn encode(text : String, encoding : Encoding, bom? : Bool = false) -> Bytes {
match encoding {
Encoding::Utf8 => @utf8.encode(text.to_string_view(), bom~)
Encoding::Utf16Le =>
@utf16.encode(
text.to_string_view(),
bom~,
endianness=@utf16.Endian::Little,
)
Encoding::Utf16Be =>
@utf16.encode(text.to_string_view(), bom~, endianness=@utf16.Endian::Big)
Encoding::Utf32Le => utf32_encode(text, false, bom)
Encoding::Utf32Be => utf32_encode(text, true, bom)
Encoding::Gbk => gbk_encode(text)
Encoding::Big5 => big5_encode(text)
Encoding::Latin1 => latin1_encode(text)
}
}
///|
/// 在任意两种编码之间转换字节内容:先按 from 解码,再按 to 编码。
/// 等价于 encode(decode(bytes, from), to)。
pub fn convert(bytes : Bytes, from : Encoding, to : Encoding) -> Bytes {
encode(decode(bytes, from), to)
}
// ---------------- 编码辅助 ----------------
///|
/// UTF-32 编码(big=true 大端),可选 BOM
fn utf32_encode(text : String, big : Bool, bom : Bool) -> Bytes {
let out : Array[Byte] = []
if bom {
if big {
out.push(b'\x00')
out.push(b'\x00')
out.push(b'\xFE')
out.push(b'\xFF')
} else {
out.push(b'\xFF')
out.push(b'\xFE')
out.push(b'\x00')
out.push(b'\x00')
}
}
for c in text {
let cp = c.to_int()
if big {
out.push(((cp >> 24) & 0xFF).to_byte())
out.push(((cp >> 16) & 0xFF).to_byte())
out.push(((cp >> 8) & 0xFF).to_byte())
out.push((cp & 0xFF).to_byte())
} else {
out.push((cp & 0xFF).to_byte())
out.push(((cp >> 8) & 0xFF).to_byte())
out.push(((cp >> 16) & 0xFF).to_byte())
out.push(((cp >> 24) & 0xFF).to_byte())
}
}
Bytes::from_array(out)
}
///|
/// Latin-1 编码:码点 ≤ 0xFF 直写,否则 '?'
fn latin1_encode(text : String) -> Bytes {
let out : Array[Byte] = []
for c in text {
let cp = c.to_int()
if cp <= 0xFF {
out.push(cp.to_byte())
} else {
out.push(b'\x3F')
}
}
Bytes::from_array(out)
}
///|
/// GBK 编码:ASCII 直通,其余查反向表,不可映射写 '?'
fn gbk_encode(text : String) -> Bytes {
let out : Array[Byte] = []
for c in text {
let cp = c.to_int()
if cp < 0x80 {
out.push(cp.to_byte())
} else {
let lin = gbk_enc_lookup(cp)
if lin < 0 {
out.push(b'\x3F')
} else {
let lead = 0x81 + lin / 190
let slot = lin % 190
let trail = if slot < 0x3F { slot + 0x40 } else { slot + 0x41 }
out.push(lead.to_byte())
out.push(trail.to_byte())
}
}
}
Bytes::from_array(out)
}
///|
/// Big5 编码:ASCII 直通,其余查反向表,不可映射写 '?'
fn big5_encode(text : String) -> Bytes {
let out : Array[Byte] = []
for c in text {
let cp = c.to_int()
if cp < 0x80 {
out.push(cp.to_byte())
} else {
let lin = big5_enc_lookup(cp)
if lin < 0 {
out.push(b'\x3F')
} else {
let lead = 0x81 + lin / 157
let slot = lin % 157
let trail = if slot < 63 { slot + 0x40 } else { slot + 0x62 }
out.push(lead.to_byte())
out.push(trail.to_byte())
}
}
}
Bytes::from_array(out)
}
///|
/// GBK 反向查找:Unicode 码点 → 线性索引(二分),找不到返回 -1
fn gbk_enc_lookup(cp : Int) -> Int {
let n = gbk_encode_table.length() / 4
let mut lo = 0
let mut hi = n - 1
while lo <= hi {
let mid = (lo + hi) / 2
let off = mid * 4
let c = gbk_encode_table[off].to_int() |
(gbk_encode_table[off + 1].to_int() << 8)
if c == cp {
let idx = gbk_encode_table[off + 2].to_int() |
(gbk_encode_table[off + 3].to_int() << 8)
return idx
} else if c < cp {
lo = mid + 1
} else {
hi = mid - 1
}
}
-1
}
///|
/// Big5 反向查找:Unicode 码点 → 线性索引(二分),找不到返回 -1
fn big5_enc_lookup(cp : Int) -> Int {
let n = big5_encode_table.length() / 4
let mut lo = 0
let mut hi = n - 1
while lo <= hi {
let mid = (lo + hi) / 2
let off = mid * 4
let c = big5_encode_table[off].to_int() |
(big5_encode_table[off + 1].to_int() << 8)
if c == cp {
let idx = big5_encode_table[off + 2].to_int() |
(big5_encode_table[off + 3].to_int() << 8)
return idx
} else if c < cp {
lo = mid + 1
} else {
hi = mid - 1
}
}
-1
}
// ---------------- 检测辅助 ----------------
///|
/// 无 BOM 的 UTF-16 端序判定:返回 0=LE、1=BE、-1=不像 UTF-16
fn utf16_endian(arr : Array[Byte]) -> Int {
let n = arr.length()
if n < 4 || n % 2 != 0 {
return -1
}
let mut even_zero = 0
let mut odd_zero = 0
let mut i = 0
while i < n {
if arr[i].to_int() == 0 {
even_zero = even_zero + 1
}
if arr[i + 1].to_int() == 0 {
odd_zero = odd_zero + 1
}
i = i + 2
}
let pairs = n / 2
// 高位字节 0x00 出现在奇数位 → 小端;偶数位 → 大端
if odd_zero * 2 >= pairs && even_zero * 4 <= pairs {
0
} else if even_zero * 2 >= pairs && odd_zero * 4 <= pairs {
1
} else {
-1
}
}
///|
/// 无 BOM 的 UTF-32 端序判定:返回 0=LE、1=BE、-1=不像 UTF-32
fn utf32_endian(arr : Array[Byte]) -> Int {
let n = arr.length()
if n < 8 || n % 4 != 0 {
return -1
}
let groups = n / 4
let mut le = 0
let mut be = 0
let mut i = 0
while i < n {
let b0 = arr[i].to_int()
let b1 = arr[i + 1].to_int()
let b2 = arr[i + 2].to_int()
let b3 = arr[i + 3].to_int()
if b1 == 0 && b2 == 0 && b3 == 0 && b0 != 0 {
le = le + 1
}
if b0 == 0 && b1 == 0 && b2 == 0 && b3 != 0 {
be = be + 1
}
i = i + 4
}
if le * 2 >= groups && le > be {
0
} else if be * 2 >= groups && be > le {
1
} else {
-1
}
}
///|
/// 严格 UTF-8 校验:拒绝过长编码、代理区、非法前导字节
fn is_valid_utf8(arr : Array[Byte]) -> Bool {
let n = arr.length()
let mut i = 0
while i < n {
let b = arr[i].to_int()
if b < 0x80 {
i = i + 1
} else if b >= 0xC2 && b <= 0xDF {
if i + 1 >= n || arr[i + 1].to_int() < 0x80 || arr[i + 1].to_int() > 0xBF {
return false
}
i = i + 2
} else if b == 0xE0 {
if i + 2 >= n ||
arr[i + 1].to_int() < 0xA0 ||
arr[i + 1].to_int() > 0xBF ||
arr[i + 2].to_int() < 0x80 ||
arr[i + 2].to_int() > 0xBF {
return false
}
i = i + 3
} else if (b >= 0xE1 && b <= 0xEC) || (b >= 0xEE && b <= 0xEF) {
if i + 2 >= n ||
arr[i + 1].to_int() < 0x80 ||
arr[i + 1].to_int() > 0xBF ||
arr[i + 2].to_int() < 0x80 ||
arr[i + 2].to_int() > 0xBF {
return false
}
i = i + 3
} else if b == 0xED {
if i + 2 >= n ||
arr[i + 1].to_int() < 0x80 ||
arr[i + 1].to_int() > 0x9F ||
arr[i + 2].to_int() < 0x80 ||
arr[i + 2].to_int() > 0xBF {
return false
}
i = i + 3
} else if b == 0xF0 {
if i + 3 >= n ||
arr[i + 1].to_int() < 0x90 ||
arr[i + 1].to_int() > 0xBF ||
arr[i + 2].to_int() < 0x80 ||
arr[i + 2].to_int() > 0xBF ||
arr[i + 3].to_int() < 0x80 ||
arr[i + 3].to_int() > 0xBF {
return false
}
i = i + 4
} else if b >= 0xF1 && b <= 0xF3 {
if i + 3 >= n ||
arr[i + 1].to_int() < 0x80 ||
arr[i + 1].to_int() > 0xBF ||
arr[i + 2].to_int() < 0x80 ||
arr[i + 2].to_int() > 0xBF ||
arr[i + 3].to_int() < 0x80 ||
arr[i + 3].to_int() > 0xBF {
return false
}
i = i + 4
} else if b == 0xF4 {
if i + 3 >= n ||
arr[i + 1].to_int() < 0x80 ||
arr[i + 1].to_int() > 0x8F ||
arr[i + 2].to_int() < 0x80 ||
arr[i + 2].to_int() > 0x8F ||
arr[i + 3].to_int() < 0x80 ||
arr[i + 3].to_int() > 0xBF {
return false
}
i = i + 4
} else {
// 0x80–0xC1、0xF5–0xFF 等非法前导
return false
}
}
true
}
///|
/// 统计双字节序列:(GBK 独有对, 合法 GBK/Big5 对, 孤立高位字节)
fn classify_dbc(arr : Array[Byte]) -> (Int, Int, Int) {
let n = arr.length()
let mut gbk_only = 0
let mut gbk_pairs = 0
let mut stray = 0
let mut i = 0
while i < n {
let b = arr[i].to_int()
if b < 0x80 {
i = i + 1
} else if b >= 0x81 && b <= 0xFE && i + 1 < n {
let t = arr[i + 1].to_int()
if t >= 0x40 && t <= 0xFE && t != 0x7F {
gbk_pairs = gbk_pairs + 1
// GBK 尾字节 0x80–0xA0 是 Big5 不具备的
if t >= 0x80 && t <= 0xA0 {
gbk_only = gbk_only + 1
}
i = i + 2
} else {
stray = stray + 1
i = i + 1
}
} else {
stray = stray + 1
i = i + 1
}
}
(gbk_only, gbk_pairs, stray)
}
// ---------------- 解码辅助 ----------------
///|
/// 剥离 UTF-8 BOM(EF BB BF)
fn strip_utf8_bom(bytes : Bytes) -> Bytes {
let arr = bytes.to_array()
if arr.length() >= 3 &&
arr[0] == b'\xEF' &&
arr[1] == b'\xBB' &&
arr[2] == b'\xBF' {
Bytes::from_array(slice_bytes(arr, 3, arr.length()))
} else {
bytes
}
}
///|
/// UTF-32 解码(big=true 大端),剥离 BOM,代理区/越界替换为 U+FFFD
fn utf32_decode(bytes : Bytes, big : Bool) -> String {
let arr = bytes.to_array()
let n = arr.length()
let chars : Array[Char] = []
let mut i = 0
if n >= 4 {
let is_bom = if big {
arr[0] == b'\x00' &&
arr[1] == b'\x00' &&
arr[2] == b'\xFE' &&
arr[3] == b'\xFF'
} else {
arr[0] == b'\xFF' &&
arr[1] == b'\xFE' &&
arr[2] == b'\x00' &&
arr[3] == b'\x00'
}
if is_bom {
i = 4
}
}
while i + 3 < n {
let cp = if big {
(arr[i].to_int() << 24) |
(arr[i + 1].to_int() << 16) |
(arr[i + 2].to_int() << 8) |
arr[i + 3].to_int()
} else {
arr[i].to_int() |
(arr[i + 1].to_int() << 8) |
(arr[i + 2].to_int() << 16) |
(arr[i + 3].to_int() << 24)
}
if cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF) {
chars.push('\u{FFFD}')
} else {
chars.push(cp.unsafe_to_char())
}
i = i + 4
}
String::from_array(chars)
}
///|
/// Latin-1(ISO-8859-1)解码:字节直接映射到码点
fn latin1_decode(bytes : Bytes) -> String {
let arr = bytes.to_array()
let chars : Array[Char] = []
for b in arr {
chars.push(b.to_int().unsafe_to_char())
}
String::from_array(chars)
}
///|
/// GBK 解码:ASCII 直通,双字节查表,非法序列替换为 U+FFFD
fn gbk_decode(bytes : Bytes) -> String {
let arr = bytes.to_array()
let n = arr.length()
let chars : Array[Char] = []
let mut i = 0
while i < n {
let b = arr[i].to_int()
if b < 0x80 {
chars.push(b.unsafe_to_char())
i = i + 1
} else if b >= 0x81 && b <= 0xFE && i + 1 < n {
let t = arr[i + 1].to_int()
if t >= 0x40 && t <= 0xFE && t != 0x7F {
let slot = if t < 0x7F { t - 0x40 } else { t - 0x41 }
let lin = (b - 0x81) * 190 + slot
let uni = gbk_lookup(lin)
if uni == 0 {
chars.push('\u{FFFD}')
} else {
chars.push(uni.unsafe_to_char())
}
i = i + 2
} else {
chars.push('\u{FFFD}')
i = i + 1
}
} else {
chars.push('\u{FFFD}')
i = i + 1
}
}
String::from_array(chars)
}
///|
/// Big5 解码:ASCII 直通,双字节查表,非法序列替换为 U+FFFD
fn big5_decode(bytes : Bytes) -> String {
let arr = bytes.to_array()
let n = arr.length()
let chars : Array[Char] = []
let mut i = 0
while i < n {
let b = arr[i].to_int()
if b < 0x80 {
chars.push(b.unsafe_to_char())
i = i + 1
} else if b >= 0x81 && b <= 0xFE && i + 1 < n {
let t = arr[i + 1].to_int()
let valid = (t >= 0x40 && t <= 0x7E) || (t >= 0xA1 && t <= 0xFE)
if valid {
let slot = if t <= 0x7E { t - 0x40 } else { t - 0xA1 + 63 }
let lin = (b - 0x81) * 157 + slot
let uni = big5_lookup(lin)
if uni == 0 {
chars.push('\u{FFFD}')
} else {
chars.push(uni.unsafe_to_char())
}
i = i + 2
} else {
chars.push('\u{FFFD}')
i = i + 1
}
} else {
chars.push('\u{FFFD}')
i = i + 1
}
}
String::from_array(chars)
}
///|
/// GBK 码表查找:线性索引 → UInt16 小端读取
fn gbk_lookup(lin : Int) -> Int {
let off = lin * 2
let lo = gbk_decode_table[off].to_int()
let hi = gbk_decode_table[off + 1].to_int()
lo | (hi << 8)
}
///|
/// Big5 码表查找:线性索引 → UInt16 小端读取
fn big5_lookup(lin : Int) -> Int {
let off = lin * 2
let lo = big5_decode_table[off].to_int()
let hi = big5_decode_table[off + 1].to_int()
lo | (hi << 8)
}