pub fn plmn_to_bytes(mcc: &str, mnc: &str) -> Vec<u8> {
assert!(
mcc.len() == 3 && mcc.chars().all(|c| c.is_ascii_digit()),
"MCC must be 3 ASCII digits"
);
assert!(
(mnc.len() == 2 || mnc.len() == 3) && mnc.chars().all(|c| c.is_ascii_digit()),
"MNC must be 2 or 3 ASCII digits"
);
let mcc: Vec<u8> = mcc.bytes().map(|b| b - b'0').collect();
let mnc: Vec<u8> = mnc.bytes().map(|b| b - b'0').collect();
let mut result = vec![0u8; 3];
result[0] = (mcc[1] << 4) | mcc[0];
if mnc.len() == 2 {
result[1] = 0xF0 | mcc[2];
result[2] = (mnc[1] << 4) | mnc[0];
} else {
result[1] = (mnc[2] << 4) | mcc[2];
result[2] = (mnc[1] << 4) | mnc[0];
}
result
}
pub fn plmn_from_bytes(bytes: &[u8]) -> Option<(String, String)> {
if bytes.len() < 3 {
return None;
}
let mcc0 = bytes[0] & 0x0F;
let mcc1 = (bytes[0] >> 4) & 0x0F;
let mcc2 = bytes[1] & 0x0F;
let mnc_hi = (bytes[1] >> 4) & 0x0F;
let mnc0 = bytes[2] & 0x0F;
let mnc1 = (bytes[2] >> 4) & 0x0F;
let mcc = format!("{}{}{}", mcc0, mcc1, mcc2);
let mnc = if mnc_hi == 0xF {
format!("{}{}", mnc0, mnc1)
} else {
format!("{}{}{}", mnc0, mnc1, mnc_hi)
};
Some((mcc, mnc))
}
pub fn tbcd_decode(bytes: &[u8]) -> String {
let mut s = String::new();
for &b in bytes {
let lo = b & 0x0F;
let hi = (b >> 4) & 0x0F;
if lo <= 9 {
s.push((b'0' + lo) as char);
}
if hi <= 9 {
s.push((b'0' + hi) as char);
}
}
s
}
pub fn tbcd_encode(value: &str) -> Vec<u8> {
let digits: Vec<u8> = value.bytes().map(|b| b - b'0').collect();
let mut result = Vec::with_capacity(digits.len().div_ceil(2));
let mut i = 0;
while i < digits.len() {
let lo = digits[i];
let hi = if i + 1 < digits.len() {
digits[i + 1]
} else {
0xF
};
result.push((hi << 4) | lo);
i += 2;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plmn_2digit_mnc_roundtrip() {
let bytes = plmn_to_bytes("208", "93");
assert_eq!(bytes, vec![0x02, 0xF8, 0x39]);
let (mcc, mnc) = plmn_from_bytes(&bytes).unwrap();
assert_eq!(mcc, "208");
assert_eq!(mnc, "93");
}
#[test]
fn plmn_3digit_mnc_roundtrip() {
let bytes = plmn_to_bytes("999", "070");
let (mcc, mnc) = plmn_from_bytes(&bytes).unwrap();
assert_eq!(mcc, "999");
assert_eq!(mnc, "070");
}
#[test]
fn plmn_from_bytes_too_short() {
assert!(plmn_from_bytes(&[0x02, 0xF8]).is_none());
}
#[test]
fn tbcd_decode_even() {
assert_eq!(tbcd_decode(&[0x21, 0x43]), "1234");
}
#[test]
fn tbcd_decode_odd() {
assert_eq!(tbcd_decode(&[0x21, 0xF3]), "123");
}
#[test]
fn tbcd_encode_even() {
assert_eq!(tbcd_encode("1234"), vec![0x21, 0x43]);
}
#[test]
fn tbcd_encode_odd() {
assert_eq!(tbcd_encode("123"), vec![0x21, 0xF3]);
}
#[test]
fn tbcd_roundtrip() {
let encoded = tbcd_encode("9876543210");
assert_eq!(tbcd_decode(&encoded), "9876543210");
}
}