use alloc::string::{String, ToString};
pub const CALL_ALPHA: &[u8; 37] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
pub const NBASE: u32 = 37 * 36 * 10 * 27 * 27 * 27;
pub fn pack_call28(call: &str, nchar: impl Fn(u8) -> Option<u32>) -> Option<u32> {
let bytes = call.as_bytes();
if bytes.is_empty() || bytes.len() > 6 {
return None;
}
let mut tmp = [b' '; 6];
if bytes.len() >= 3 && bytes[2].is_ascii_digit() {
for (i, &b) in bytes.iter().enumerate() {
tmp[i] = b;
}
} else if bytes.len() >= 2 && bytes[1].is_ascii_digit() {
if bytes.len() > 5 {
return None;
}
for (i, &b) in bytes.iter().enumerate() {
tmp[i + 1] = b;
}
} else {
return None;
}
let n = [
nchar(tmp[0])?,
nchar(tmp[1])?,
nchar(tmp[2])?,
nchar(tmp[3])?,
nchar(tmp[4])?,
nchar(tmp[5])?,
];
if n[1] == 36 {
return None;
}
if n[2] >= 10 {
return None;
}
for v in &n[3..6] {
if *v < 10 {
return None;
}
}
let mut ncall = n[0];
ncall = 36 * ncall + n[1];
ncall = 10 * ncall + n[2];
ncall = 27 * ncall + n[3] - 10;
ncall = 27 * ncall + n[4] - 10;
ncall = 27 * ncall + n[5] - 10;
Some(ncall)
}
pub fn unpack_call28(ncall: u32) -> Option<String> {
if ncall >= NBASE {
return None;
}
let mut n = ncall;
let mut chars = [b' '; 6];
let c6 = (n % 27) + 10;
chars[5] = CALL_ALPHA[c6 as usize];
n /= 27;
let c5 = (n % 27) + 10;
chars[4] = CALL_ALPHA[c5 as usize];
n /= 27;
let c4 = (n % 27) + 10;
chars[3] = CALL_ALPHA[c4 as usize];
n /= 27;
let c3 = n % 10;
chars[2] = CALL_ALPHA[c3 as usize];
n /= 10;
let c2 = n % 36;
chars[1] = CALL_ALPHA[c2 as usize];
n /= 36;
let c1 = n; chars[0] = CALL_ALPHA[c1 as usize];
let s = core::str::from_utf8(&chars).ok()?;
Some(s.trim().to_string())
}
#[cfg(test)]
mod tests {
use super::*;
fn nchar_upper_only(c: u8) -> Option<u32> {
match c {
b'0'..=b'9' => Some((c - b'0') as u32),
b'A'..=b'Z' => Some((c - b'A' + 10) as u32),
b' ' => Some(36),
_ => None,
}
}
#[test]
fn round_trip_k1abc() {
let n = pack_call28("K1ABC", nchar_upper_only).unwrap();
assert_eq!(unpack_call28(n).as_deref(), Some("K1ABC"));
}
#[test]
fn round_trip_digit_in_slot_2() {
let n = pack_call28("K9AN", nchar_upper_only).unwrap();
assert_eq!(unpack_call28(n).as_deref(), Some("K9AN"));
}
#[test]
fn nbase_is_wsjtx_constant() {
assert_eq!(NBASE, 262_177_560);
}
#[test]
fn rejects_lowercase_when_nchar_is_upper_only() {
assert_eq!(pack_call28("k1abc", nchar_upper_only), None);
}
}