1use crate::check::Check;
5use crate::error::{Error, Result};
6use crate::io::{Reader, Writer};
7
8pub const HEAD_FIXED_LEN: usize = 44;
10pub const ENCODER_NAME_MAX: usize = 256;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct Head {
16 pub head_version: u32,
18 pub encoder_name: String,
20 pub created_unix_sec: u64,
22 pub display_width_px: u32,
24 pub display_height_px: u32,
26 pub build_width_um: u32,
28 pub build_depth_um: u32,
30 pub build_height_um: u32,
32 pub layer_height_um: u32,
34 pub total_layers: u32,
36}
37
38impl Head {
39 pub fn parse(payload: &[u8]) -> Result<Head> {
41 let mut r = Reader::checked(payload, Check::HeadFrame);
42 let head_version = r.u32()?;
43 if head_version != 1 {
44 return Err(Error::new(
45 Check::HeadVersion,
46 format!("unsupported head_version {head_version}"),
47 ));
48 }
49 let encoder_name_len = r.u32()?;
50 if encoder_name_len as usize > ENCODER_NAME_MAX {
51 return Err(Error::new(
52 Check::HeadFrame,
53 format!(
54 "encoder_name_len {encoder_name_len} exceeds the {ENCODER_NAME_MAX} byte limit"
55 ),
56 ));
57 }
58 let encoder_name = r.utf8(encoder_name_len as usize)?.to_owned();
59 let created_unix_sec = r.u64()?;
60 let display_width_px = r.u32()?;
61 let display_height_px = r.u32()?;
62 let build_width_um = r.u32()?;
63 let build_depth_um = r.u32()?;
64 let build_height_um = r.u32()?;
65 let layer_height_um = r.u32()?;
66 let total_layers = r.u32()?;
67 let expected = HEAD_FIXED_LEN + encoder_name_len as usize;
72 if payload.len() != expected {
73 return Err(Error::new(
74 Check::HeadFrame,
75 format!(
76 "HEAD is {} bytes, not the {expected} its fields and name total",
77 payload.len()
78 ),
79 ));
80 }
81 Ok(Head {
82 head_version,
83 encoder_name,
84 created_unix_sec,
85 display_width_px,
86 display_height_px,
87 build_width_um,
88 build_depth_um,
89 build_height_um,
90 layer_height_um,
91 total_layers,
92 })
93 }
94
95 pub fn to_bytes(&self) -> Vec<u8> {
97 let mut w = Writer::with_capacity(HEAD_FIXED_LEN + self.encoder_name.len());
98 w.u32(self.head_version);
99 w.u32(self.encoder_name.len() as u32);
100 w.bytes(self.encoder_name.as_bytes());
101 w.u64(self.created_unix_sec);
102 w.u32(self.display_width_px);
103 w.u32(self.display_height_px);
104 w.u32(self.build_width_um);
105 w.u32(self.build_depth_um);
106 w.u32(self.build_height_um);
107 w.u32(self.layer_height_um);
108 w.u32(self.total_layers);
109 w.into_vec()
110 }
111
112 pub fn total_pixels(&self) -> u32 {
114 self.display_width_px.saturating_mul(self.display_height_px)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn sample(name: &str) -> Head {
123 Head {
124 head_version: 1,
125 encoder_name: name.to_owned(),
126 created_unix_sec: 1_710_000_000,
127 display_width_px: 64,
128 display_height_px: 48,
129 build_width_um: 218_000,
130 build_depth_um: 123_000,
131 build_height_um: 250_000,
132 layer_height_um: 50,
133 total_layers: 4,
134 }
135 }
136
137 #[test]
138 fn hdr_round_trips_with_a_multibyte_name() {
139 let head = sample("DragonFruit 1.0 \u{2713}");
140 let bytes = head.to_bytes();
141 assert_eq!(bytes.len(), HEAD_FIXED_LEN + head.encoder_name.len());
143 assert_eq!(
144 u32::from_le_bytes(bytes[4..8].try_into().unwrap()) as usize,
145 head.encoder_name.len()
146 );
147 let parsed = Head::parse(&bytes).unwrap();
148 assert_eq!(parsed, head);
149 }
150
151 #[test]
152 fn hdr_rejects_a_length_measured_in_characters() {
153 let head = sample("DragonFruit \u{2713}");
154 let mut bytes = head.to_bytes();
155 let chars = head.encoder_name.chars().count() as u32;
158 bytes[4..8].copy_from_slice(&chars.to_le_bytes());
159 let err = Head::parse(&bytes).unwrap_err();
160 assert_eq!(err.check(), Check::HeadFrame);
161 }
162
163 #[test]
164 fn hdr_rejects_an_oversized_or_truncated_frame() {
165 let mut bytes = sample("DragonFruit 1.0").to_bytes();
166 bytes[4..8].copy_from_slice(&(ENCODER_NAME_MAX as u32 + 1).to_le_bytes());
167 assert_eq!(Head::parse(&bytes).unwrap_err().check(), Check::HeadFrame);
168
169 let bytes = sample("DragonFruit 1.0").to_bytes();
170 assert_eq!(
171 Head::parse(&bytes[..bytes.len() - 1]).unwrap_err().check(),
172 Check::HeadFrame
173 );
174 assert_eq!(
175 Head::parse(&bytes[..4]).unwrap_err().check(),
176 Check::HeadFrame
177 );
178
179 let mut longer = bytes.clone();
184 longer.extend_from_slice(&[0u8; 8]);
185 assert_eq!(Head::parse(&longer).unwrap_err().check(), Check::HeadFrame);
186 }
187
188 #[test]
189 fn hdr_rejects_unknown_versions_and_any_extra_bytes() {
190 let mut bytes = sample("X").to_bytes();
191 bytes[0..4].copy_from_slice(&2u32.to_le_bytes());
192 assert_eq!(Head::parse(&bytes).unwrap_err().check(), Check::HeadVersion);
193
194 let mut bytes = sample("X").to_bytes();
199 bytes.push(0);
200 assert_eq!(Head::parse(&bytes).unwrap_err().check(), Check::HeadFrame);
201 }
202}