// 文本与字节工具。
//
// 报文本身是 ASCII,但数据区的取值允许出现中文(UTF-8)。
// 长度字段按「数据段的 ASCII 字符数」定义,实际部署中普遍按 UTF-8
// 字节数计算,因此这里统一以 UTF-8 字节为准,并把码点级工具单独列出。
///|
fn chars_of(s : String) -> Array[Char] {
let out : Array[Char] = []
for c in s {
out.push(c)
}
out
}
///|
/// 取 `[start, end)` 的字符区间拼成字符串,越界部分自动截断。
fn slice_chars(cs : Array[Char], start : Int32, end : Int32) -> String {
let sb = StringBuilder()
let mut i = if start < 0 { 0 } else { start }
let stop = if end > cs.length() { cs.length() } else { end }
while i < stop {
sb.write_char(cs[i])
i = i + 1
}
sb.to_string()
}
///|
/// 取 `[start, end)` 的字符区间副本,越界部分自动截断。
fn chars_slice(cs : Array[Char], start : Int32, end : Int32) -> Array[Char] {
let out : Array[Char] = []
let mut i = if start < 0 { 0 } else { start }
let stop = if end > cs.length() { cs.length() } else { end }
while i < stop {
out.push(cs[i])
i = i + 1
}
out
}
///|
/// 在字符数组中查找子串首次出现的位置,找不到返回 `-1`。
fn find_seq(cs : Array[Char], pat : String, from : Int32) -> Int32 {
let p = chars_of(pat)
let n = p.length()
if n == 0 {
return from
}
let mut i = if from < 0 { 0 } else { from }
while i + n <= cs.length() {
let mut j = 0
let mut matched = true
while j < n {
if cs[i + j] != p[j] {
matched = false
break
}
j = j + 1
}
if matched {
return i
}
i = i + 1
}
-1
}
///|
fn is_digit_char(c : Char) -> Bool {
c >= '0' && c <= '9'
}
///|
/// 十六进制字符转数值,非法字符返回 `-1`。
fn hex_value(c : Char) -> Int32 {
if c >= '0' && c <= '9' {
c.to_int() - 48
} else if c >= 'a' && c <= 'f' {
c.to_int() - 87
} else if c >= 'A' && c <= 'F' {
c.to_int() - 55
} else {
-1
}
}
///|
/// 解析一段十进制数字,空段或含非数字返回 `None`。
fn parse_uint(cs : Array[Char], start : Int32, len : Int32) -> Int32? {
if len <= 0 || start < 0 || start + len > cs.length() {
return None
}
let mut value = 0
let mut i = 0
while i < len {
let c = cs[start + i]
if !is_digit_char(c) {
return None
}
value = value * 10 + (c.to_int() - 48)
i = i + 1
}
Some(value)
}
///|
/// 解析一个完整字符串里的十进制数字。
fn parse_uint_all(s : String) -> Int32? {
let cs = chars_of(s)
parse_uint(cs, 0, cs.length())
}
///|
fn is_space_char(c : Char) -> Bool {
c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
///|
/// 去掉首尾空白,返回新的字符数组。
fn trim_chars(cs : Array[Char]) -> Array[Char] {
let mut start = 0
let mut stop = cs.length()
while start < stop && is_space_char(cs[start]) {
start = start + 1
}
while stop > start && is_space_char(cs[stop - 1]) {
stop = stop - 1
}
let out : Array[Char] = []
let mut i = start
while i < stop {
out.push(cs[i])
i = i + 1
}
out
}
///|
/// 去空白后的字符串。
fn trim_text(s : String) -> String {
let cs = trim_chars(chars_of(s))
slice_chars(cs, 0, cs.length())
}
///|
/// 单个码点在 UTF-8 下占用的字节数。
fn utf8_char_len(c : Char) -> Int32 {
let cp = c.to_int()
if cp < 0x80 {
1
} else if cp < 0x800 {
2
} else if cp < 0x10000 {
3
} else {
4
}
}
///|
/// 字符串的 UTF-8 字节数。
///
/// 报文的长度字段按字节计,且 CRC 也按字节计算;两者必须使用同一套计数,
/// 否则含中文的数据区会算错。
pub fn utf8_len(s : String) -> Int32 {
let mut n = 0
for c in s {
n = n + utf8_char_len(c)
}
n
}
///|
/// 把字符串编码成 UTF-8 字节序列。
fn utf8_bytes(s : String) -> Array[Byte] {
let out : Array[Byte] = []
for c in s {
let cp = c.to_int()
if cp < 0x80 {
out.push(cp.to_byte())
} else if cp < 0x800 {
out.push((0xC0 | (cp >> 6)).to_byte())
out.push((0x80 | (cp & 0x3F)).to_byte())
} else if cp < 0x10000 {
out.push((0xE0 | (cp >> 12)).to_byte())
out.push((0x80 | ((cp >> 6) & 0x3F)).to_byte())
out.push((0x80 | (cp & 0x3F)).to_byte())
} else {
out.push((0xF0 | (cp >> 18)).to_byte())
out.push((0x80 | ((cp >> 12) & 0x3F)).to_byte())
out.push((0x80 | ((cp >> 6) & 0x3F)).to_byte())
out.push((0x80 | (cp & 0x3F)).to_byte())
}
}
out
}
///|
/// 两位大写十六进制。
fn pad_hex2(value : Int32) -> String {
let v = value & 0xFF
let hi = (v >> 4) & 0xF
let lo = v & 0xF
"\{hex_digit(hi)}\{hex_digit(lo)}"
}
///|
fn hex_digit(v : Int32) -> String {
if v < 10 {
(v + 48).unsafe_to_char().to_string()
} else {
(v - 10 + 65).unsafe_to_char().to_string()
}
}
///|
/// 四位大写十六进制。
fn hex4(value : Int32) -> String {
let v = value & 0xFFFF
"\{hex_digit((v >> 12) & 0xF)}\{hex_digit((v >> 8) & 0xF)}\{hex_digit((v >> 4) & 0xF)}\{hex_digit(v & 0xF)}"
}
///|
/// 左补零到固定宽度。
fn zero_pad(value : Int32, width : Int32) -> String {
let digits = value.to_string()
let mut pad = width - digits.length()
if pad < 0 {
pad = 0
}
let sb = StringBuilder()
let mut i = 0
while i < pad {
sb.write_char('0')
i = i + 1
}
sb.write_string(digits)
sb.to_string()
}
///|
/// 等宽终端下的显示宽度:CJK 与全角符号记 2 列,其余记 1 列。
fn display_width(s : String) -> Int32 {
let mut width = 0
for c in s {
let cp = c.to_int()
if cp < 0x20 {
width = width + 0
} else if cp < 0x7F {
width = width + 1
} else if is_wide_codepoint(cp) {
width = width + 2
} else {
width = width + 1
}
}
width
}
///|
/// 粗略的东亚宽字符判定,仅用于终端表格对齐。
fn is_wide_codepoint(cp : Int32) -> Bool {
if cp >= 0x1100 && cp <= 0x115F {
return true
}
if cp >= 0x2E80 && cp <= 0xA4CF {
return true
}
if cp >= 0xAC00 && cp <= 0xD7A3 {
return true
}
if cp >= 0xF900 && cp <= 0xFAFF {
return true
}
if cp >= 0xFE30 && cp <= 0xFE6F {
return true
}
if cp >= 0xFF00 && cp <= 0xFF60 {
return true
}
if cp >= 0xFFE0 && cp <= 0xFFE6 {
return true
}
false
}
///|
/// 按显示宽度右补空格。
fn pad_display(s : String, width : Int32) -> String {
let w = display_width(s)
if w >= width {
return s
}
let sb = StringBuilder()
sb.write_string(s)
let mut i = w
while i < width {
sb.write_char(' ')
i = i + 1
}
sb.to_string()
}