use crate::{ucs2_from_utf8_at_offset, Error};
pub const fn str_num_ucs2_chars(s: &str) -> Result<usize, Error> {
let bytes = s.as_bytes();
let len = bytes.len();
let mut offset = 0;
let mut num_ucs2_chars = 0;
while offset < len {
match unsafe { ucs2_from_utf8_at_offset(bytes, offset) } {
Ok(ch) => {
offset += ch.num_bytes as usize;
num_ucs2_chars += 1;
}
Err(err) => {
return Err(err);
}
}
}
Ok(num_ucs2_chars)
}
pub const fn str_to_ucs2<const N: usize>(s: &str) -> Result<[u16; N], Error> {
let bytes = s.as_bytes();
let len = bytes.len();
let mut output = [0; N];
let mut output_offset = 0;
let mut input_offset = 0;
while input_offset < len {
match unsafe { ucs2_from_utf8_at_offset(bytes, input_offset) } {
Ok(ch) => {
if ch.val == 0 {
panic!("interior null character");
} else {
output[output_offset] = ch.val;
output_offset += 1;
input_offset += ch.num_bytes as usize;
}
}
Err(err) => {
return Err(err);
}
}
}
if output_offset + 1 != N {
panic!("incorrect array length");
}
Ok(output)
}
#[macro_export]
macro_rules! ucs2_cstr {
($s:literal) => {{
const NUM_CHARS: usize = match $crate::str_num_ucs2_chars($s) {
Ok(num) => num + 1,
Err(_) => panic!("input contains a character which cannot be represented in UCS-2"),
};
const VAL: [u16; NUM_CHARS] = match $crate::str_to_ucs2($s) {
Ok(val) => val,
Err(_) => {
unreachable!();
}
};
VAL
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_str_num_chars() {
assert_eq!(str_num_ucs2_chars("abc"), Ok(3));
assert_eq!(str_num_ucs2_chars("Τη γλώσσα μου έδωσαν ελληνική"), Ok(29));
assert_eq!(str_num_ucs2_chars("ვეპხის ტყაოსანი შოთა რუსთაველი"), Ok(30));
assert_eq!(str_num_ucs2_chars("😎🔥"), Err(Error::MultiByte));
}
#[test]
fn test_ucs2_cstr() {
let s = ucs2_cstr!("abc");
assert_eq!(s, [97, 98, 99, 0]);
}
}