1use std::fmt::Display;
2
3use bytemuck::{Pod, Zeroable};
4use serde::{de, Deserialize, Serialize};
5use snafu::{Backtrace, Snafu};
6
7#[derive(Clone, Copy)]
9pub struct AsciiArray<const N: usize>(pub [u8; N]);
10
11#[derive(Debug, Snafu)]
13pub enum AsciiArrayError {
14 #[snafu(display("the provided string '{string}' contains one or more non-ASCII characters:\n{backtrace}"))]
16 NotAscii {
17 string: String,
19 backtrace: Backtrace,
21 },
22}
23
24impl<const N: usize> AsciiArray<N> {
25 pub fn from_str(string: &str) -> Result<Self, AsciiArrayError> {
31 let mut chars = [0u8; N];
32 for (i, ch) in string.chars().take(N).enumerate() {
33 if !ch.is_ascii() {
34 return NotAsciiSnafu { string: string.to_string() }.fail();
35 }
36 chars[i] = ch as u8;
37 }
38 Ok(Self(chars))
39 }
40}
41
42impl AsciiArray<4> {
43 pub fn to_le_u32(&self) -> u32 {
45 u32::from_le_bytes(self.0)
46 }
47}
48
49impl<const N: usize> Display for AsciiArray<N> {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 for ch in self.0 {
52 if ch == 0 {
53 break;
54 }
55 write!(f, "{}", ch as char)?;
56 }
57 Ok(())
58 }
59}
60
61impl<const N: usize> Serialize for AsciiArray<N> {
62 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63 where
64 S: serde::Serializer,
65 {
66 serializer.serialize_str(&self.to_string())
67 }
68}
69
70impl<'de, const N: usize> Deserialize<'de> for AsciiArray<N> {
71 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
72 where
73 D: serde::Deserializer<'de>,
74 {
75 let string: String = Deserialize::deserialize(deserializer)?;
76 Ok(Self::from_str(&string).map_err(de::Error::custom)?)
77 }
78}
79
80#[derive(Clone, Copy)]
82pub struct Unicode16Array<const N: usize>(pub [u16; N]);
83
84unsafe impl<const N: usize> Zeroable for Unicode16Array<N> {}
85unsafe impl<const N: usize> Pod for Unicode16Array<N> {}
86
87impl<const N: usize> Unicode16Array<N> {
88 pub fn from_str(string: &str) -> Self {
90 let mut chars = [0u16; N];
91 let mut i = 0;
92 for ch in string.chars() {
93 let mut codepoints = [0u16; 2];
94 ch.encode_utf16(&mut codepoints);
95
96 let len = if codepoints[1] != 0 { 2 } else { 1 };
97 if i + len >= N {
98 break;
99 }
100
101 for j in 0..len {
102 chars[i] = codepoints[j];
103 i += 1;
104 }
105 }
106 Self(chars)
107 }
108}
109
110impl<const N: usize> Display for Unicode16Array<N> {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 for ch in self.0 {
113 if ch == 0 {
114 break;
115 }
116 let Some(ch) = char::from_u32(ch as u32) else {
117 break;
118 };
119 write!(f, "{ch}")?;
120 }
121 Ok(())
122 }
123}
124
125pub(crate) struct BlobSize(pub usize);
126
127impl Display for BlobSize {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 let size = self.0;
130 match size {
131 0..=0x3ff => write!(f, "{}B", size),
132 0x400..=0xfffff => write!(f, "{:.1}kB", size as f32 / 0x400 as f32),
133 0x100000.. => write!(f, "{:.1}MB", size as f32 / 0x100000 as f32),
134 }
135 }
136}
137
138#[allow(unused)]
140pub(crate) fn write_hex(f: &mut std::fmt::Formatter<'_>, data: &[u8]) -> std::fmt::Result {
141 for (offset, chunk) in data.chunks(16).enumerate() {
142 write!(f, "{:08x} ", offset * 16)?;
143 for byte in chunk {
144 write!(f, " {byte:02x}")?;
145 }
146 writeln!(f)?;
147 }
148 writeln!(f)?;
149 Ok(())
150}
151
152#[allow(unused)]
154pub(crate) fn print_hex(data: &[u8]) {
155 for (offset, chunk) in data.chunks(16).enumerate() {
156 print!("{:08x} ", offset * 16);
157 for byte in chunk {
158 print!(" {byte:02x}");
159 }
160 println!();
161 }
162 println!();
163}