1#[macro_use]
5extern crate structure;
6
7use std::collections::BTreeMap;
8use std::f64;
9use std::io::{Seek, SeekFrom, Write};
10
11use chrono::{DateTime, Utc};
12use formats::{ImageStride, PixelFormat, pixel_format::PixFmt};
13use machine_vision_formats as formats;
14use strand_dynamic_frame::{DynamicFrame, match_all_dynamic_fmts};
15
16pub type UFMFResult<M> = std::result::Result<M, UFMFError>;
17
18mod save_indices;
19
20#[derive(Debug, thiserror::Error)]
21pub enum UFMFError {
22 #[error("unimplemented pixel_format {0}")]
23 UnimplementedPixelFormat(PixFmt),
24
25 #[error("already closed")]
26 AlreadyClosed,
27
28 #[error("the pixel format changed")]
29 FormatChanged,
30
31 #[error("{source}")]
32 Io {
33 #[from]
34 source: std::io::Error,
35 },
36 #[error("{0}")]
37 Cast(#[from] cast::Error),
38}
39
40const KEYFRAME_CHUNK: u8 = 0;
41const FRAME_CHUNK: u8 = 1;
42const INDEX_DICT_CHUNK: u8 = 2;
43
44fn pack_header(v: u32, index_loc: u64, w: u16, h: u16, cl: u8) -> std::io::Result<Vec<u8>> {
45 #[expect(clippy::too_many_arguments)]
46 structure!("<4sIQHHB").pack(b"ufmf", v, index_loc, w, h, cl)
47}
48
49fn write_header<F: Write + Seek>(
50 f: &mut F,
51 index_loc: usize,
52 max_width: u16,
53 max_height: u16,
54 pixel_format: PixFmt,
55) -> UFMFResult<usize> {
56 let coding = get_format(pixel_format)?;
57
58 let buf: Vec<u8> = pack_header(
59 3,
60 cast::u64(index_loc),
61 max_width,
62 max_height,
63 cast::u8(coding.len())?,
64 )?;
65
66 let mut pos = 0;
67 pos += f.write(&buf)?;
68 pos += f.write(&coding)?;
69 Ok(pos)
70}
71
72fn write_image<F: Write + Seek, FMT>(
73 f: &mut F,
74 frame: &dyn ImageStride<FMT>,
75 bytes_per_pixel: u8,
76 rect: &RectFromCorner,
77) -> UFMFResult<usize> {
78 let image_data = frame.image_data();
79 let xoffset = rect.x0 as usize * bytes_per_pixel as usize;
80 let row_bytes = rect.w as usize * bytes_per_pixel as usize;
81 let mut pos = 0;
82
83 for i in rect.y0 as usize..(rect.y0 + rect.h) as usize {
84 let start = i * frame.stride() + xoffset;
85 let stop = start + row_bytes;
86 let row_data = &image_data[start..stop];
87 pos += f.write(row_data)?;
88 }
89 Ok(pos)
90}
91
92fn get_format(pixel_format: PixFmt) -> UFMFResult<Vec<u8>> {
93 use PixFmt::*;
94 let r = match pixel_format {
95 Mono8 => b"MONO8".to_vec(),
96 BayerRG8 => b"RAW8:RGGB".to_vec(),
98 BayerGB8 => b"RAW8:GBRG".to_vec(),
99 BayerGR8 => b"RAW8:GRBG".to_vec(),
100 BayerBG8 => b"RAW8:BGGR".to_vec(),
101 YUV422 => b"YUV422".to_vec(),
102 RGB8 => b"RGB8".to_vec(),
103 f => {
104 return Err(UFMFError::UnimplementedPixelFormat(f));
105 }
106 };
107 Ok(r)
108}
109
110fn get_dtype(pixel_format: formats::pixel_format::PixFmt) -> UFMFResult<u8> {
111 use formats::pixel_format::PixFmt::*;
112 let r = match pixel_format {
113 Mono8 | BayerRG8 | BayerGB8 | BayerGR8 | BayerBG8 | YUV422 | RGB8 => b'B',
114 Mono32f | BayerRG32f | BayerGB32f | BayerGR32f | BayerBG32f => b'f',
115 x => {
116 return Err(UFMFError::UnimplementedPixelFormat(x));
117 }
118 };
119 Ok(r)
120}
121
122pub struct UFMFWriter<F: Write + Seek> {
123 f: Option<F>,
124 pos: usize,
125 max_width: u16,
126 max_height: u16,
127 xinc: u8,
128 yinc: u8,
129 index_frame: Vec<TimestampLoc>,
130 index_keyframes: BTreeMap<Vec<u8>, Vec<TimestampLoc>>,
131 bytes_per_pixel: u8,
132 pixel_format: formats::pixel_format::PixFmt,
133}
134
135impl<F: Write + Seek> std::fmt::Debug for UFMFWriter<F> {
136 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
137 write!(f, "UFMFWriter {{ }}")
138 }
139}
140
141struct TimestampLoc {
142 timestamp: f64,
143 loc: u64,
144}
145
146pub struct RectFromCenter {
148 x: u16,
150 y: u16,
152 w: u16,
154 h: u16,
156}
157
158impl RectFromCenter {
159 pub fn from_xy_wh(x: u16, y: u16, w: u16, h: u16) -> Self {
160 Self { x, y, w, h }
161 }
162}
163
164pub struct RectFromCorner {
166 x0: u16,
168 y0: u16,
170 w: u16,
172 h: u16,
174}
175
176struct Region<'a, 'b> {
177 origframe: &'a DynamicFrame<'b>,
178 rect: &'a RectFromCorner,
179}
180
181fn do_size(x: u16, mut w: u16, xinc: u16, max_width: u16) -> (u16, u16) {
182 let w_radius = w / 2;
183
184 let xmin = x.saturating_sub(w_radius) / xinc * xinc; let xmax = xmin + w;
187 let newxmax = if xmax < max_width { xmax } else { max_width };
188 if newxmax != xmax {
189 w = newxmax - xmin;
190 }
191 (xmin, w)
192}
193
194impl<F> UFMFWriter<F>
195where
196 F: Write + Seek,
197{
198 pub fn new(
199 mut f: F,
200 max_width: u16,
201 max_height: u16,
202 pixel_format: PixFmt,
203 frame_timestamp0: Option<(&DynamicFrame, DateTime<Utc>)>,
204 ) -> UFMFResult<Self> {
205 if let Some((frame0, _timestamp0)) = frame_timestamp0.as_ref()
206 && frame0.pixel_format() != pixel_format
207 {
208 return Err(UFMFError::FormatChanged);
209 }
210 let pos = write_header(&mut f, 0, max_width, max_height, pixel_format)?;
211
212 use PixFmt::*;
213 let (xinc, yinc) = match pixel_format {
214 Mono8 | BayerRG8 | BayerGB8 | BayerGR8 | BayerBG8 => (2, 2),
215 YUV422 => (4, 1),
216 e => {
217 return Err(UFMFError::UnimplementedPixelFormat(e));
218 }
219 };
220
221 let bytes_per_pixel = pixel_format.bits_per_pixel() / 8;
222
223 let f = Some(f);
224
225 let mut result = Self {
226 f,
227 pos,
228 max_width,
229 max_height,
230 xinc,
231 yinc,
232 index_frame: Vec::new(),
233 index_keyframes: BTreeMap::new(),
234 bytes_per_pixel,
235 pixel_format,
236 };
237
238 if let Some((frame0, timestamp0)) = frame_timestamp0 {
239 match_all_dynamic_fmts!(
240 frame0,
241 x,
242 result.add_keyframe(b"frame0", &x, timestamp0)?,
243 UFMFError::UnimplementedPixelFormat(pixel_format)
244 );
245 }
246
247 Ok(result)
248 }
249
250 pub fn add_frame(
251 &mut self,
252 origframe: &DynamicFrame,
253 timestamp: DateTime<Utc>,
254 point_data: &[RectFromCenter],
255 ) -> UFMFResult<Vec<RectFromCorner>> {
256 if origframe.pixel_format() != self.pixel_format {
257 return Err(UFMFError::FormatChanged);
258 }
259 let timestamp = strand_datetime_conversion::datetime_to_f64(×tamp);
260
261 let rects: Vec<RectFromCorner> = point_data
262 .iter()
263 .map(|i| {
264 let (x0, w) = do_size(i.x, i.w, self.xinc as u16, self.max_width);
265 let (y0, h) = do_size(i.y, i.h, self.yinc as u16, self.max_height);
266 RectFromCorner { x0, y0, w, h }
267 })
268 .collect();
269
270 {
271 let regions = rects
273 .iter()
274 .map(|rect| Region { origframe, rect })
275 .collect();
276 self.add_frame_regions(timestamp, regions)?;
277 }
278 Ok(rects)
279 }
280
281 fn add_frame_regions(&mut self, timestamp: f64, regions: Vec<Region>) -> UFMFResult<()> {
282 let mut self_f = match self.f {
283 Some(ref mut f) => f,
284 None => {
285 return Err(UFMFError::AlreadyClosed);
286 }
287 };
288
289 self.index_frame.push(TimestampLoc {
290 timestamp,
291 loc: self.pos as u64,
292 });
293
294 let n_pts = cast::u16(regions.len())?;
295 let bytes_per_pixel = self.bytes_per_pixel;
296
297 let buf0 = vec![FRAME_CHUNK];
298 let buf1 = structure!("<dH").pack(timestamp, n_pts)?;
299
300 self.pos += self_f.write(&buf0)?;
301 self.pos += self_f.write(&buf1)?;
302
303 for region in regions.iter() {
304 let this_str_head = structure!("<HHHH").pack(
305 region.rect.x0,
306 region.rect.y0,
307 region.rect.w,
308 region.rect.h,
309 )?;
310 self.pos += self_f.write(&this_str_head)?;
311 self.pos += match_all_dynamic_fmts!(
312 region.origframe,
313 frame,
314 write_image(&mut self_f, &frame, bytes_per_pixel, region.rect)?,
315 UFMFError::UnimplementedPixelFormat(self.pixel_format)
316 );
317 }
318 Ok(())
319 }
320
321 pub fn add_keyframe<FRAME, FMT>(
322 &mut self,
323 keyframe_type: &[u8],
324 frame: &FRAME,
325 timestamp_dt: DateTime<Utc>,
326 ) -> UFMFResult<()>
327 where
328 FRAME: ImageStride<FMT>,
329 FMT: PixelFormat,
330 {
331 let mut self_f = match self.f {
332 Some(ref mut f) => f,
333 None => {
334 return Err(UFMFError::AlreadyClosed);
335 }
336 };
337
338 let bytes_per_pixel = formats::pixel_format::pixfmt::<FMT>()
339 .unwrap()
340 .bits_per_pixel()
341 / 8;
342
343 let timestamp = strand_datetime_conversion::datetime_to_f64(×tamp_dt);
344 let dtype = get_dtype(formats::pixel_format::pixfmt::<FMT>().unwrap())?;
345 let width = cast::u16(frame.width())?;
346 let height = cast::u16(frame.height())?;
347
348 {
349 let entry = self
350 .index_keyframes
351 .entry(keyframe_type.to_vec())
352 .or_default();
353 let timestamp = strand_datetime_conversion::datetime_to_f64(×tamp_dt);
354 entry.push(TimestampLoc {
355 timestamp,
356 loc: self.pos as u64,
357 });
358 }
359
360 let buf = vec![KEYFRAME_CHUNK, cast::u8(keyframe_type.len())?];
361 self.pos += self_f.write(&buf)?;
362 self.pos += self_f.write(keyframe_type)?;
363
364 let buf = structure!("<BHHd").pack(dtype, width, height, timestamp)?;
365 let rect = RectFromCorner {
366 x0: 0,
367 y0: 0,
368 w: width,
369 h: height,
370 };
371 self.pos += self_f.write(&buf)?;
372 self.pos += write_image(&mut self_f, frame, bytes_per_pixel, &rect)?;
373 Ok(())
374 }
375}
376
377impl<F> UFMFWriter<F>
378where
379 F: Write + Seek,
380{
381 pub fn close(&mut self) -> UFMFResult<F> {
386 let opt_f = self.f.take();
387
388 let mut self_f = match opt_f {
389 Some(f) => f,
390 None => {
391 return Err(UFMFError::AlreadyClosed);
392 }
393 };
394
395 self.pos += self_f.write(&[INDEX_DICT_CHUNK])?;
396 save_indices::save_indices(&mut self_f, &self.index_frame, &self.index_keyframes)?;
397 self_f.seek(SeekFrom::Start(0))?;
398 write_header(
399 &mut self_f,
400 self.pos,
401 self.max_width,
402 self.max_height,
403 self.pixel_format,
404 )?;
405 Ok(self_f)
406 }
407}
408
409impl<F: Write + Seek> Drop for UFMFWriter<F> {
411 fn drop(&mut self) {
412 match self.close() {
414 Ok(_f) => {}
415 Err(_e) => {} }
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use crate::*;
423 use byteorder::WriteBytesExt;
424 use formats::{
425 owned::OImage,
426 pixel_format::{Mono8, Mono32f},
427 };
428 use strand_dynamic_frame::DynamicFrameOwned;
429
430 #[expect(clippy::float_cmp)]
431 fn arange(start: u8, timestamp: f64) -> (DynamicFrameOwned, DateTime<Utc>) {
432 let w = 10;
433 let h = 10;
434 let mut image_data = Vec::new();
435 for i in 0..100 {
436 image_data.push(start + i as u8);
437 }
438
439 let dt = strand_datetime_conversion::f64_to_datetime(timestamp);
440 let host_timestamp = dt.with_timezone(&chrono::Utc);
441
442 let roundtrip = strand_datetime_conversion::datetime_to_f64(&host_timestamp);
443 assert_eq!(timestamp, roundtrip); (
449 DynamicFrameOwned::from_static(
450 OImage::<Mono8>::new(w, h, w.try_into().unwrap(), image_data).unwrap(),
451 ),
452 dt,
453 )
454 }
455
456 #[expect(clippy::float_cmp)]
457 fn arange_float(start: f32, timestamp: f64) -> (DynamicFrameOwned, DateTime<Utc>) {
458 let w = 10;
459 let h = 10;
460
461 let mut f = std::io::Cursor::new(Vec::with_capacity(4 * 100));
462 for i in 0..100 {
463 let value: f32 = start + i as f32;
464 f.write_f32::<byteorder::LittleEndian>(value).unwrap();
465 }
466 let image_data = f.into_inner();
467
468 let ts_utc = strand_datetime_conversion::f64_to_datetime(timestamp);
469
470 let roundtrip = strand_datetime_conversion::datetime_to_f64(&ts_utc);
471 assert_eq!(timestamp, roundtrip); (
477 DynamicFrameOwned::from_static(
478 OImage::<Mono32f>::new(w, h, usize::try_from(w).unwrap() * 4, image_data).unwrap(),
479 ),
480 ts_utc,
481 )
482 }
483
484 #[test]
485 fn test_pack_header() {
486 let buf = pack_header(1, 2, 3, 4, 5).unwrap();
487 assert_eq!(
488 buf,
489 &[
490 b'u', b'f', b'm', b'f', 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 4, 0, 5,
491 ]
492 );
493 }
494
495 #[test]
496 fn test_empty_file() {
497 let w = 320;
498 let h = 240;
499 let pixel_format = formats::pixel_format::PixFmt::Mono8;
500 let f = std::io::Cursor::new(Vec::new());
501 let mut writer = UFMFWriter::new(f, w, h, pixel_format, None).unwrap();
502 let f = writer.close().unwrap();
503
504 match writer.close() {
506 Ok(_) => panic!("expected error"),
507 Err(_e) => {} };
509
510 let buf = f.into_inner();
512
513 let expected: &[u8] = &[
526 0x75, 0x66, 0x6d, 0x66, 0x3, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
527 0x40, 0x1, 0xf0, 0x0, 0x5, 0x4d, 0x4f, 0x4e, 0x4f, 0x38, 0x2, 0x64, 0x2, 0x5, 0x0,
528 0x66, 0x72, 0x61, 0x6d, 0x65, 0x64, 0x0, 0x8, 0x0, 0x6b, 0x65, 0x79, 0x66, 0x72, 0x61,
529 0x6d, 0x65, 0x64, 0x0,
530 ];
531 assert_eq!(&buf[0..], expected);
532 }
533
534 #[test]
535 fn test_saving_regions() {
536 let (arr, dt) = arange(0, 123.456);
537 let frame0_owned = arr.borrow();
538 let frame0 = Some((&frame0_owned, dt));
539 let w = 10;
540 let h = 10;
541 let pixel_format = formats::pixel_format::PixFmt::Mono8;
542 let f = std::io::Cursor::new(Vec::new());
543 let mut writer = UFMFWriter::new(f, w, h, pixel_format, frame0).unwrap();
544
545 let (arr2, dt2) = arange(100, 42.42);
546 let point_data = vec![
547 RectFromCenter::from_xy_wh(0, 0, 4, 4),
548 RectFromCenter::from_xy_wh(4, 4, 4, 4),
549 RectFromCenter::from_xy_wh(9, 9, 4, 4),
550 ];
551
552 writer.add_frame(&arr2.borrow(), dt2, &point_data).unwrap();
553
554 let f = writer.close().unwrap();
555
556 match writer.close() {
558 Ok(_) => panic!("expected error"),
559 Err(_e) => {} };
561
562 let buf = f.into_inner();
564
565 let expected: &[u8] = &[
608 0x75, 0x66, 0x6d, 0x66, 0x3, 0x0, 0x0, 0x0, 0xe7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
609 0xa, 0x0, 0xa, 0x0, 0x5, 0x4d, 0x4f, 0x4e, 0x4f, 0x38, 0x0, 0x6, 0x66, 0x72, 0x61,
610 0x6d, 0x65, 0x30, 0x42, 0xa, 0x0, 0xa, 0x0, 0x77, 0xbe, 0x9f, 0x1a, 0x2f, 0xdd, 0x5e,
611 0x40, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
612 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
613 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
614 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
615 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
616 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
617 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
618 0x1, 0xf6, 0x28, 0x5c, 0x8f, 0xc2, 0x35, 0x45, 0x40, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
619 0x0, 0x4, 0x0, 0x64, 0x65, 0x66, 0x67, 0x6e, 0x6f, 0x70, 0x71, 0x78, 0x79, 0x7a, 0x7b,
620 0x82, 0x83, 0x84, 0x85, 0x2, 0x0, 0x2, 0x0, 0x4, 0x0, 0x4, 0x0, 0x7a, 0x7b, 0x7c, 0x7d,
621 0x84, 0x85, 0x86, 0x87, 0x8e, 0x8f, 0x90, 0x91, 0x98, 0x99, 0x9a, 0x9b, 0x6, 0x0, 0x6,
622 0x0, 0x4, 0x0, 0x4, 0x0, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xb1, 0xb2, 0xb3, 0xba, 0xbb,
623 0xbc, 0xbd, 0xc4, 0xc5, 0xc6, 0xc7, 0x2, 0x64, 0x2, 0x5, 0x0, 0x66, 0x72, 0x61, 0x6d,
624 0x65, 0x64, 0x2, 0x3, 0x0, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x8, 0x0, 0x0, 0x0, 0x93, 0x0,
625 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
626 0x70, 0x61, 0x64, 0x8, 0x0, 0x0, 0x0, 0xf6, 0x28, 0x5c, 0x8f, 0xc2, 0x35, 0x45, 0x40,
627 0x8, 0x0, 0x6b, 0x65, 0x79, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x64, 0x1, 0x6, 0x0, 0x66,
628 0x72, 0x61, 0x6d, 0x65, 0x30, 0x64, 0x2, 0x3, 0x0, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x8,
629 0x0, 0x0, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x74, 0x69, 0x6d,
630 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x61, 0x64, 0x8, 0x0, 0x0, 0x0, 0x77, 0xbe, 0x9f,
631 0x1a, 0x2f, 0xdd, 0x5e, 0x40,
632 ];
633 assert_eq!(&buf[0..], expected);
634 }
635
636 #[test]
637 fn test_float_keyframe() {
638 use formats::pixel_format::Mono32f;
639 let w = 10;
640 let h = 10;
641 let pixel_format = formats::pixel_format::PixFmt::Mono8;
642 let f = std::io::Cursor::new(Vec::new());
643 let mut writer = UFMFWriter::new(f, w, h, pixel_format, None).unwrap();
644 let (running_mean, ts) = arange_float(0.1, 123.456);
645
646 let running_mean_owned = running_mean.borrow();
647 let running_mean = running_mean_owned.as_static::<Mono32f>().unwrap();
648
649 writer.add_keyframe(b"mean", &running_mean, ts).unwrap();
650 let f = writer.close().unwrap();
651
652 match writer.close() {
654 Ok(_) => panic!("expected error"),
655 Err(_e) => {} };
657
658 let buf = f.into_inner();
660
661 let expected: &[u8] = &[
686 0x75, 0x66, 0x6d, 0x66, 0x3, 0x0, 0x0, 0x0, 0xbe, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
687 0xa, 0x0, 0xa, 0x0, 0x5, 0x4d, 0x4f, 0x4e, 0x4f, 0x38, 0x0, 0x4, 0x6d, 0x65, 0x61,
688 0x6e, 0x66, 0xa, 0x0, 0xa, 0x0, 0x77, 0xbe, 0x9f, 0x1a, 0x2f, 0xdd, 0x5e, 0x40, 0xcd,
689 0xcc, 0xcc, 0x3d, 0xcd, 0xcc, 0x8c, 0x3f, 0x66, 0x66, 0x6, 0x40, 0x66, 0x66, 0x46,
690 0x40, 0x33, 0x33, 0x83, 0x40, 0x33, 0x33, 0xa3, 0x40, 0x33, 0x33, 0xc3, 0x40, 0x33,
691 0x33, 0xe3, 0x40, 0x9a, 0x99, 0x1, 0x41, 0x9a, 0x99, 0x11, 0x41, 0x9a, 0x99, 0x21,
692 0x41, 0x9a, 0x99, 0x31, 0x41, 0x9a, 0x99, 0x41, 0x41, 0x9a, 0x99, 0x51, 0x41, 0x9a,
693 0x99, 0x61, 0x41, 0x9a, 0x99, 0x71, 0x41, 0xcd, 0xcc, 0x80, 0x41, 0xcd, 0xcc, 0x88,
694 0x41, 0xcd, 0xcc, 0x90, 0x41, 0xcd, 0xcc, 0x98, 0x41, 0xcd, 0xcc, 0xa0, 0x41, 0xcd,
695 0xcc, 0xa8, 0x41, 0xcd, 0xcc, 0xb0, 0x41, 0xcd, 0xcc, 0xb8, 0x41, 0xcd, 0xcc, 0xc0,
696 0x41, 0xcd, 0xcc, 0xc8, 0x41, 0xcd, 0xcc, 0xd0, 0x41, 0xcd, 0xcc, 0xd8, 0x41, 0xcd,
697 0xcc, 0xe0, 0x41, 0xcd, 0xcc, 0xe8, 0x41, 0xcd, 0xcc, 0xf0, 0x41, 0xcd, 0xcc, 0xf8,
698 0x41, 0x66, 0x66, 0x0, 0x42, 0x66, 0x66, 0x4, 0x42, 0x66, 0x66, 0x8, 0x42, 0x66, 0x66,
699 0xc, 0x42, 0x66, 0x66, 0x10, 0x42, 0x66, 0x66, 0x14, 0x42, 0x66, 0x66, 0x18, 0x42,
700 0x66, 0x66, 0x1c, 0x42, 0x66, 0x66, 0x20, 0x42, 0x66, 0x66, 0x24, 0x42, 0x66, 0x66,
701 0x28, 0x42, 0x66, 0x66, 0x2c, 0x42, 0x66, 0x66, 0x30, 0x42, 0x66, 0x66, 0x34, 0x42,
702 0x66, 0x66, 0x38, 0x42, 0x66, 0x66, 0x3c, 0x42, 0x66, 0x66, 0x40, 0x42, 0x66, 0x66,
703 0x44, 0x42, 0x66, 0x66, 0x48, 0x42, 0x66, 0x66, 0x4c, 0x42, 0x66, 0x66, 0x50, 0x42,
704 0x66, 0x66, 0x54, 0x42, 0x66, 0x66, 0x58, 0x42, 0x66, 0x66, 0x5c, 0x42, 0x66, 0x66,
705 0x60, 0x42, 0x66, 0x66, 0x64, 0x42, 0x66, 0x66, 0x68, 0x42, 0x66, 0x66, 0x6c, 0x42,
706 0x66, 0x66, 0x70, 0x42, 0x66, 0x66, 0x74, 0x42, 0x66, 0x66, 0x78, 0x42, 0x66, 0x66,
707 0x7c, 0x42, 0x33, 0x33, 0x80, 0x42, 0x33, 0x33, 0x82, 0x42, 0x33, 0x33, 0x84, 0x42,
708 0x33, 0x33, 0x86, 0x42, 0x33, 0x33, 0x88, 0x42, 0x33, 0x33, 0x8a, 0x42, 0x33, 0x33,
709 0x8c, 0x42, 0x33, 0x33, 0x8e, 0x42, 0x33, 0x33, 0x90, 0x42, 0x33, 0x33, 0x92, 0x42,
710 0x33, 0x33, 0x94, 0x42, 0x33, 0x33, 0x96, 0x42, 0x33, 0x33, 0x98, 0x42, 0x33, 0x33,
711 0x9a, 0x42, 0x33, 0x33, 0x9c, 0x42, 0x33, 0x33, 0x9e, 0x42, 0x33, 0x33, 0xa0, 0x42,
712 0x33, 0x33, 0xa2, 0x42, 0x33, 0x33, 0xa4, 0x42, 0x33, 0x33, 0xa6, 0x42, 0x33, 0x33,
713 0xa8, 0x42, 0x33, 0x33, 0xaa, 0x42, 0x33, 0x33, 0xac, 0x42, 0x33, 0x33, 0xae, 0x42,
714 0x33, 0x33, 0xb0, 0x42, 0x33, 0x33, 0xb2, 0x42, 0x33, 0x33, 0xb4, 0x42, 0x33, 0x33,
715 0xb6, 0x42, 0x33, 0x33, 0xb8, 0x42, 0x33, 0x33, 0xba, 0x42, 0x33, 0x33, 0xbc, 0x42,
716 0x33, 0x33, 0xbe, 0x42, 0x33, 0x33, 0xc0, 0x42, 0x33, 0x33, 0xc2, 0x42, 0x33, 0x33,
717 0xc4, 0x42, 0x33, 0x33, 0xc6, 0x42, 0x2, 0x64, 0x2, 0x5, 0x0, 0x66, 0x72, 0x61, 0x6d,
718 0x65, 0x64, 0x0, 0x8, 0x0, 0x6b, 0x65, 0x79, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x64, 0x1,
719 0x4, 0x0, 0x6d, 0x65, 0x61, 0x6e, 0x64, 0x2, 0x3, 0x0, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
720 0x8, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x74, 0x69,
721 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x61, 0x64, 0x8, 0x0, 0x0, 0x0, 0x77, 0xbe,
722 0x9f, 0x1a, 0x2f, 0xdd, 0x5e, 0x40,
723 ];
724 assert_eq!(&buf[0..], expected);
725 }
726}