1#![forbid(unsafe_code)]
10#![deny(missing_docs)]
11
12use core::fmt;
13
14pub const TAG_DEDUCED_TIMESTAMP: u8 = 1;
16pub const TAG_TRANSMITTED_TIMESTAMP: u8 = 2;
18
19pub const DEDUCED_TIMESTAMP: f64 = -1.0;
23
24pub const IRREGULAR_RATE: f64 = 0.0;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31#[repr(u8)]
32pub enum Format {
33 Undefined = 0,
35 Float32 = 1,
37 Double64 = 2,
39 String = 3,
41 Int32 = 4,
43 Int16 = 5,
45 Int8 = 6,
47 Int64 = 7,
49}
50
51impl Format {
52 pub fn from_u8(v: u8) -> Option<Self> {
54 Some(match v {
55 0 => Format::Undefined,
56 1 => Format::Float32,
57 2 => Format::Double64,
58 3 => Format::String,
59 4 => Format::Int32,
60 5 => Format::Int16,
61 6 => Format::Int8,
62 7 => Format::Int64,
63 _ => return None,
64 })
65 }
66
67 pub fn width(self) -> Option<usize> {
69 Some(match self {
70 Format::Undefined | Format::String => return None,
71 Format::Float32 | Format::Int32 => 4,
72 Format::Double64 | Format::Int64 => 8,
73 Format::Int16 => 2,
74 Format::Int8 => 1,
75 })
76 }
77
78 pub fn is_float(self) -> bool {
82 matches!(self, Format::Float32 | Format::Double64)
83 }
84}
85
86#[derive(Debug, Clone, PartialEq)]
88pub enum Value {
89 F32(f32),
91 F64(f64),
93 Str(Vec<u8>),
95 I32(i32),
97 I16(i16),
99 I8(i8),
101 I64(i64),
103}
104
105#[derive(Debug, Clone, PartialEq)]
107pub struct Sample {
108 pub timestamp: f64,
110 pub values: Vec<Value>,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum ByteOrder {
117 Little,
119 Big,
121}
122
123impl ByteOrder {
124 pub fn wire_value(self) -> i32 {
126 match self {
127 ByteOrder::Little => 1234,
128 ByteOrder::Big => 4321,
129 }
130 }
131
132 pub fn from_wire(v: i32) -> Option<Self> {
137 match v {
138 1234 => Some(ByteOrder::Little),
139 4321 => Some(ByteOrder::Big),
140 0 => Some(ByteOrder::native()),
141 _ => None,
142 }
143 }
144
145 pub const fn native() -> Self {
147 if cfg!(target_endian = "little") {
148 ByteOrder::Little
149 } else {
150 ByteOrder::Big
151 }
152 }
153}
154
155#[derive(Debug, Clone, Copy)]
157pub struct Codec {
158 pub format: Format,
160 pub channels: usize,
162 pub order: ByteOrder,
164 pub suppress_subnormals: bool,
166}
167
168impl Codec {
169 pub fn new(format: Format, channels: usize, order: ByteOrder) -> Self {
171 Codec {
172 format,
173 channels,
174 order,
175 suppress_subnormals: false,
176 }
177 }
178
179 pub fn with_suppress_subnormals(mut self, on: bool) -> Self {
181 self.suppress_subnormals = on;
182 self
183 }
184
185 fn swaps(&self) -> bool {
189 self.order != ByteOrder::native() && self.format.width().map_or(true, |w| w > 1)
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub enum Error {
196 Truncated,
198 BadTag(u8),
200 BadLengthWidth(u8),
205 LengthTooLarge,
207 UndefinedFormat,
209 FormatMismatch,
211 ChannelCountMismatch {
213 expected: usize,
215 actual: usize,
217 },
218}
219
220impl fmt::Display for Error {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 match self {
223 Error::Truncated => write!(f, "the buffer ended inside a value"),
224 Error::BadTag(t) => write!(f, "the tag byte {t} is not 1 or 2"),
225 Error::BadLengthWidth(w) => write!(f, "the length width {w} is not 1, 2, 4, or 8"),
226 Error::LengthTooLarge => write!(f, "the string length does not fit in usize"),
227 Error::UndefinedFormat => write!(f, "the format is undefined"),
228 Error::FormatMismatch => write!(f, "a value does not match the codec format"),
229 Error::ChannelCountMismatch { expected, actual } => {
230 write!(
231 f,
232 "the sample holds {actual} values and the codec expects {expected}"
233 )
234 }
235 }
236 }
237}
238
239impl std::error::Error for Error {}
240
241macro_rules! put {
246 ($out:expr, $v:expr, $swap:expr) => {{
247 let b = if $swap {
248 $v.to_be_bytes()
249 } else {
250 $v.to_le_bytes()
251 };
252 $out.extend_from_slice(&b);
253 }};
254}
255
256impl Codec {
257 pub fn encode(&self, s: &Sample, out: &mut Vec<u8>) -> Result<(), Error> {
262 if self.format == Format::Undefined {
263 return Err(Error::UndefinedFormat);
264 }
265 if s.values.len() != self.channels {
266 return Err(Error::ChannelCountMismatch {
267 expected: self.channels,
268 actual: s.values.len(),
269 });
270 }
271
272 if s.timestamp == DEDUCED_TIMESTAMP {
274 out.push(TAG_DEDUCED_TIMESTAMP);
275 } else {
276 out.push(TAG_TRANSMITTED_TIMESTAMP);
277 let swap = self.order != ByteOrder::native();
280 put!(out, s.timestamp.to_bits(), swap);
281 }
282
283 let swap = self.swaps();
284 for v in &s.values {
285 match (self.format, v) {
286 (Format::Float32, Value::F32(x)) => put!(out, x.to_bits(), swap),
287 (Format::Double64, Value::F64(x)) => put!(out, x.to_bits(), swap),
288 (Format::Int32, Value::I32(x)) => put!(out, x, swap),
289 (Format::Int16, Value::I16(x)) => put!(out, x, swap),
290 (Format::Int8, Value::I8(x)) => out.push(*x as u8),
291 (Format::Int64, Value::I64(x)) => put!(out, x, swap),
292 (Format::String, Value::Str(b)) => {
293 encode_string_len(b.len(), swap, out);
294 out.extend_from_slice(b);
295 }
296 _ => return Err(Error::FormatMismatch),
297 }
298 }
299 Ok(())
300 }
301
302 pub fn encode_to_vec(&self, s: &Sample) -> Result<Vec<u8>, Error> {
304 let mut v = Vec::new();
305 self.encode(s, &mut v)?;
306 Ok(v)
307 }
308}
309
310fn encode_string_len(len: usize, swap: bool, out: &mut Vec<u8>) {
315 if len <= 0xFF {
316 out.push(1);
317 out.push(len as u8);
318 } else if len <= 0xFFFF_FFFF {
319 out.push(4);
320 put!(out, (len as u32), swap);
321 } else {
322 out.push(8);
323 put!(out, (len as u64), swap);
324 }
325}
326
327struct Reader<'a> {
333 buf: &'a [u8],
334 pos: usize,
335}
336
337impl<'a> Reader<'a> {
338 fn new(buf: &'a [u8]) -> Self {
339 Reader { buf, pos: 0 }
340 }
341
342 fn byte(&mut self) -> Result<u8, Error> {
343 let b = *self.buf.get(self.pos).ok_or(Error::Truncated)?;
344 self.pos += 1;
345 Ok(b)
346 }
347
348 fn take(&mut self, n: usize) -> Result<&'a [u8], Error> {
349 let end = self.pos.checked_add(n).ok_or(Error::Truncated)?;
350 let s = self.buf.get(self.pos..end).ok_or(Error::Truncated)?;
351 self.pos = end;
352 Ok(s)
353 }
354}
355
356macro_rules! get {
357 ($r:expr, $t:ty, $swap:expr) => {{
358 const N: usize = core::mem::size_of::<$t>();
359 let s = $r.take(N)?;
360 let mut a = [0u8; N];
361 a.copy_from_slice(s);
362 if $swap {
363 <$t>::from_be_bytes(a)
364 } else {
365 <$t>::from_le_bytes(a)
366 }
367 }};
368}
369
370impl Codec {
371 pub fn decode(&self, buf: &[u8]) -> Result<(Sample, usize), Error> {
375 if self.format == Format::Undefined {
376 return Err(Error::UndefinedFormat);
377 }
378 let mut r = Reader::new(buf);
379
380 let tag = r.byte()?;
381 let timestamp = match tag {
382 TAG_DEDUCED_TIMESTAMP => DEDUCED_TIMESTAMP,
383 TAG_TRANSMITTED_TIMESTAMP => {
384 let swap = self.order != ByteOrder::native();
385 f64::from_bits(get!(r, u64, swap))
386 }
387 other => return Err(Error::BadTag(other)),
388 };
389
390 let swap = self.swaps();
391 let mut values = Vec::with_capacity(self.channels);
392 for _ in 0..self.channels {
393 values.push(match self.format {
394 Format::Float32 => {
395 let mut bits = get!(r, u32, swap);
396 if self.suppress_subnormals && bits != 0 && (bits & 0x7fff_ffff) <= 0x007f_ffff
399 {
400 bits &= 0x8000_0000;
401 }
402 Value::F32(f32::from_bits(bits))
403 }
404 Format::Double64 => {
405 let mut bits = get!(r, u64, swap);
406 if self.suppress_subnormals
407 && bits != 0
408 && (bits & 0x7fff_ffff_ffff_ffff) <= 0x000f_ffff_ffff_ffff
409 {
410 bits &= 0x8000_0000_0000_0000;
411 }
412 Value::F64(f64::from_bits(bits))
413 }
414 Format::Int32 => Value::I32(get!(r, i32, swap)),
415 Format::Int16 => Value::I16(get!(r, i16, swap)),
416 Format::Int8 => Value::I8(r.byte()? as i8),
417 Format::Int64 => Value::I64(get!(r, i64, swap)),
418 Format::String => {
419 let len = decode_string_len(&mut r, swap)?;
420 Value::Str(r.take(len)?.to_vec())
421 }
422 Format::Undefined => return Err(Error::UndefinedFormat),
423 });
424 }
425
426 Ok((Sample { timestamp, values }, r.pos))
427 }
428
429 pub fn decode_all(&self, buf: &[u8]) -> Result<(Vec<Sample>, usize), Error> {
434 let mut out = Vec::new();
435 let mut off = 0;
436 loop {
437 match self.decode(&buf[off..]) {
438 Ok((s, n)) => {
439 out.push(s);
440 off += n;
441 if off >= buf.len() {
442 break;
443 }
444 }
445 Err(Error::Truncated) => break,
446 Err(e) => return Err(e),
447 }
448 }
449 Ok((out, off))
450 }
451}
452
453fn decode_string_len(r: &mut Reader<'_>, swap: bool) -> Result<usize, Error> {
458 let width = r.byte()?;
459 let len: u64 = match width {
460 1 => r.byte()? as u64,
461 2 => get!(r, u16, swap) as u64,
462 4 => get!(r, u32, swap) as u64,
463 8 => get!(r, u64, swap),
464 other => return Err(Error::BadLengthWidth(other)),
465 };
466 usize::try_from(len).map_err(|_| Error::LengthTooLarge)
467}
468
469pub const TEST_PATTERN_TIMESTAMP: f64 = 123456.789;
477
478pub const TEST_PATTERN_OFFSETS: [i64; 2] = [4, 2];
480
481pub fn test_pattern(format: Format, channels: usize, offset: i64) -> Sample {
486 let base: i64 = match format {
489 Format::Float32 => 0,
490 Format::Double64 => 16_777_217,
491 Format::Int32 => 65_537,
492 Format::Int16 => 257,
493 Format::Int8 => 1,
494 Format::Int64 => 2_147_483_649,
495 Format::String | Format::Undefined => 0,
496 };
497 let off = base + offset;
498
499 let values = (0..channels)
500 .map(|k| {
501 let k = k as i64;
502 if format == Format::String {
504 let v = (k + 10) * if k % 2 == 0 { 1 } else { -1 };
505 return Value::Str(v.to_string().into_bytes());
506 }
507 let raw = (k as u64).wrapping_add(off as u64);
511 let sign = k % 2 == 0;
512 match format {
513 Format::Float32 => {
514 let v = raw as f32;
515 Value::F32(if sign { v } else { -v })
516 }
517 Format::Double64 => {
518 let v = raw as f64;
519 Value::F64(if sign { v } else { -v })
520 }
521 Format::Int32 => {
522 let v = (raw % i32::MAX as u64) as i32;
523 Value::I32(if sign { v } else { -v })
524 }
525 Format::Int16 => {
526 let v = (raw % i16::MAX as u64) as i16;
527 Value::I16(if sign { v } else { -v })
528 }
529 Format::Int8 => {
530 let v = (raw % i8::MAX as u64) as i8;
531 Value::I8(if sign { v } else { -v })
532 }
533 Format::Int64 => {
534 let v = (raw % i64::MAX as u64) as i64;
535 Value::I64(if sign { v } else { -v })
536 }
537 Format::String | Format::Undefined => unreachable!(),
538 }
539 })
540 .collect();
541
542 Sample {
543 timestamp: TEST_PATTERN_TIMESTAMP,
544 values,
545 }
546}
547
548#[derive(Debug, Clone)]
557pub struct TimestampDeducer {
558 last: f64,
559 srate: f64,
560}
561
562impl TimestampDeducer {
563 pub fn new(srate: f64) -> Self {
567 TimestampDeducer { last: 0.0, srate }
568 }
569
570 pub fn apply(&mut self, timestamp: f64) -> f64 {
572 let mut t = timestamp;
573 if t == DEDUCED_TIMESTAMP {
574 t = self.last;
575 if self.srate != IRREGULAR_RATE {
576 t += 1.0 / self.srate;
577 }
578 }
579 self.last = t;
580 t
581 }
582}
583
584#[cfg(test)]
585mod tests {
586 use super::*;
587
588 fn rt(c: &Codec, s: &Sample) -> Sample {
589 let b = c.encode_to_vec(s).unwrap();
590 let (got, n) = c.decode(&b).unwrap();
591 assert_eq!(n, b.len(), "decode did not use every byte");
592 got
593 }
594
595 #[test]
596 fn deduced_tag_is_one_byte() {
597 let c = Codec::new(Format::Float32, 2, ByteOrder::Little);
598 let s = Sample {
599 timestamp: DEDUCED_TIMESTAMP,
600 values: vec![Value::F32(1.0), Value::F32(2.0)],
601 };
602 let b = c.encode_to_vec(&s).unwrap();
603 assert_eq!(b[0], TAG_DEDUCED_TIMESTAMP);
604 assert_eq!(b.len(), 1 + 8);
605 assert_eq!(rt(&c, &s), s);
606 }
607
608 #[test]
609 fn transmitted_tag_carries_eight_bytes() {
610 let c = Codec::new(Format::Int8, 1, ByteOrder::Little);
611 let s = Sample {
612 timestamp: 1.5,
613 values: vec![Value::I8(-3)],
614 };
615 let b = c.encode_to_vec(&s).unwrap();
616 assert_eq!(b[0], TAG_TRANSMITTED_TIMESTAMP);
617 assert_eq!(b.len(), 1 + 8 + 1);
618 assert_eq!(rt(&c, &s), s);
619 }
620
621 #[test]
622 fn string_width_boundaries() {
623 let c = Codec::new(Format::String, 1, ByteOrder::Little);
624 for (len, want_width) in [(0usize, 1u8), (255, 1), (256, 4), (257, 4)] {
625 let s = Sample {
626 timestamp: 0.0,
627 values: vec![Value::Str(vec![b'x'; len])],
628 };
629 let b = c.encode_to_vec(&s).unwrap();
630 assert_eq!(b[9], want_width, "length {len} picked the wrong width");
631 assert_eq!(rt(&c, &s), s);
632 }
633 }
634
635 #[test]
636 fn decoder_accepts_width_two_that_no_encoder_writes() {
637 let c = Codec::new(Format::String, 1, ByteOrder::Little);
639 let mut b = vec![TAG_TRANSMITTED_TIMESTAMP];
640 b.extend_from_slice(&0.0f64.to_le_bytes());
641 b.push(2);
642 b.extend_from_slice(&3u16.to_le_bytes());
643 b.extend_from_slice(b"abc");
644 let (s, n) = c.decode(&b).unwrap();
645 assert_eq!(n, b.len());
646 assert_eq!(s.values, vec![Value::Str(b"abc".to_vec())]);
647 }
648
649 #[test]
650 fn bad_length_width_is_an_error() {
651 let c = Codec::new(Format::String, 1, ByteOrder::Little);
652 let mut b = vec![TAG_TRANSMITTED_TIMESTAMP];
653 b.extend_from_slice(&0.0f64.to_le_bytes());
654 b.push(3);
655 assert_eq!(c.decode(&b), Err(Error::BadLengthWidth(3)));
656 }
657
658 #[test]
659 fn bad_tag_is_an_error() {
660 let c = Codec::new(Format::Int8, 1, ByteOrder::Little);
661 assert_eq!(c.decode(&[7, 0]), Err(Error::BadTag(7)));
662 }
663
664 #[test]
665 fn truncated_input_is_an_error() {
666 let c = Codec::new(Format::Double64, 4, ByteOrder::Little);
667 let b = vec![TAG_DEDUCED_TIMESTAMP, 0, 0, 0];
668 assert_eq!(c.decode(&b), Err(Error::Truncated));
669 }
670
671 #[test]
672 fn nan_and_infinity_survive_a_round_trip() {
673 let c = Codec::new(Format::Double64, 4, ByteOrder::Little);
674 let s = Sample {
675 timestamp: 0.0,
676 values: vec![
677 Value::F64(f64::INFINITY),
678 Value::F64(f64::NEG_INFINITY),
679 Value::F64(-0.0),
680 Value::F64(f64::MIN_POSITIVE / 2.0),
681 ],
682 };
683 let got = rt(&c, &s);
684 assert_eq!(got, s);
685 if let (Value::F64(a), Value::F64(b)) = (&got.values[2], &s.values[2]) {
687 assert_eq!(a.to_bits(), b.to_bits());
688 }
689 }
690
691 #[test]
692 fn big_endian_differs_from_little_endian() {
693 let le = Codec::new(Format::Int32, 1, ByteOrder::Little);
694 let be = Codec::new(Format::Int32, 1, ByteOrder::Big);
695 let s = Sample {
696 timestamp: 0.0,
697 values: vec![Value::I32(1)],
698 };
699 assert_ne!(le.encode_to_vec(&s).unwrap(), be.encode_to_vec(&s).unwrap());
700 assert_eq!(be.decode(&be.encode_to_vec(&s).unwrap()).unwrap().0, s);
701 }
702
703 #[test]
704 fn int8_never_swaps() {
705 let le = Codec::new(Format::Int8, 3, ByteOrder::Little);
706 let be = Codec::new(Format::Int8, 3, ByteOrder::Big);
707 let s = Sample {
708 timestamp: DEDUCED_TIMESTAMP,
709 values: vec![Value::I8(1), Value::I8(-2), Value::I8(3)],
710 };
711 assert_eq!(le.encode_to_vec(&s).unwrap(), be.encode_to_vec(&s).unwrap());
712 }
713
714 #[test]
715 fn subnormal_suppression_keeps_the_sign() {
716 let c = Codec::new(Format::Float32, 2, ByteOrder::Little).with_suppress_subnormals(true);
717 let plain = Codec::new(Format::Float32, 2, ByteOrder::Little);
718 let s = Sample {
719 timestamp: 0.0,
720 values: vec![
721 Value::F32(f32::from_bits(0x0000_0001)),
722 Value::F32(f32::from_bits(0x8000_0001)),
723 ],
724 };
725 let b = plain.encode_to_vec(&s).unwrap();
726 let (got, _) = c.decode(&b).unwrap();
727 assert_eq!(got.values[0], Value::F32(0.0));
728 match got.values[1] {
729 Value::F32(v) => assert_eq!(v.to_bits(), 0x8000_0000),
730 _ => panic!("wrong format"),
731 }
732 }
733
734 #[test]
735 fn channel_count_mismatch_is_an_error() {
736 let c = Codec::new(Format::Int16, 4, ByteOrder::Little);
737 let s = Sample {
738 timestamp: 0.0,
739 values: vec![Value::I16(1)],
740 };
741 assert_eq!(
742 c.encode(&s, &mut Vec::new()),
743 Err(Error::ChannelCountMismatch {
744 expected: 4,
745 actual: 1
746 })
747 );
748 }
749
750 #[test]
751 fn deducer_adds_one_period() {
752 let mut d = TimestampDeducer::new(100.0);
753 assert!((d.apply(DEDUCED_TIMESTAMP) - 0.01).abs() < 1e-12);
754 assert!((d.apply(DEDUCED_TIMESTAMP) - 0.02).abs() < 1e-12);
755 assert_eq!(d.apply(5.0), 5.0);
756 assert!((d.apply(DEDUCED_TIMESTAMP) - 5.01).abs() < 1e-12);
757 }
758
759 #[test]
760 fn deducer_holds_the_value_for_an_irregular_rate() {
761 let mut d = TimestampDeducer::new(IRREGULAR_RATE);
762 assert_eq!(d.apply(DEDUCED_TIMESTAMP), 0.0);
763 assert_eq!(d.apply(7.0), 7.0);
764 assert_eq!(d.apply(DEDUCED_TIMESTAMP), 7.0);
765 }
766
767 #[test]
768 fn test_pattern_matches_the_captured_stream() {
769 let s = test_pattern(Format::Float32, 8, 4);
771 assert_eq!(s.timestamp, TEST_PATTERN_TIMESTAMP);
772 let want: Vec<f32> = vec![4.0, -5.0, 6.0, -7.0, 8.0, -9.0, 10.0, -11.0];
773 let got: Vec<f32> = s
774 .values
775 .iter()
776 .map(|v| match v {
777 Value::F32(x) => *x,
778 _ => panic!("wrong format"),
779 })
780 .collect();
781 assert_eq!(got, want);
782 }
783}