Skip to main content

fit/
output_stream.rs

1//! Growing byte buffer with little-endian write helpers.
2//!
3//! Mirrors [`crate::stream::ByteStream`] on the write side. Used by the
4//! encoder to assemble FIT binary (header + definition/data records + CRC).
5
6/// A growable byte buffer for writing FIT binary.
7#[derive(Debug, Default)]
8pub struct OutputStream {
9    buf: Vec<u8>,
10}
11
12impl OutputStream {
13    pub fn new() -> Self {
14        Self { buf: Vec::new() }
15    }
16
17    pub fn with_capacity(cap: usize) -> Self {
18        Self {
19            buf: Vec::with_capacity(cap),
20        }
21    }
22
23    /// Current write position (length of the buffer).
24    #[inline]
25    pub fn position(&self) -> usize {
26        self.buf.len()
27    }
28
29    /// Borrow the written bytes.
30    #[inline]
31    pub fn as_slice(&self) -> &[u8] {
32        &self.buf
33    }
34
35    /// Consume the stream and return the buffer.
36    #[inline]
37    pub fn into_bytes(self) -> Vec<u8> {
38        self.buf
39    }
40
41    /// Write a single byte.
42    #[inline]
43    pub fn write_u8(&mut self, v: u8) {
44        self.buf.push(v);
45    }
46
47    /// Write a u16 in little-endian byte order.
48    #[inline]
49    pub fn write_u16(&mut self, v: u16) {
50        self.buf.extend_from_slice(&v.to_le_bytes());
51    }
52
53    /// Write a u32 in little-endian byte order.
54    #[inline]
55    pub fn write_u32(&mut self, v: u32) {
56        self.buf.extend_from_slice(&v.to_le_bytes());
57    }
58
59    /// Write a i16 in little-endian byte order.
60    #[inline]
61    pub fn write_i16(&mut self, v: i16) {
62        self.buf.extend_from_slice(&v.to_le_bytes());
63    }
64
65    /// Write a i32 in little-endian byte order.
66    #[inline]
67    pub fn write_i32(&mut self, v: i32) {
68        self.buf.extend_from_slice(&v.to_le_bytes());
69    }
70
71    /// Write a u64 in little-endian byte order.
72    #[inline]
73    pub fn write_u64(&mut self, v: u64) {
74        self.buf.extend_from_slice(&v.to_le_bytes());
75    }
76
77    /// Write a i64 in little-endian byte order.
78    #[inline]
79    pub fn write_i64(&mut self, v: i64) {
80        self.buf.extend_from_slice(&v.to_le_bytes());
81    }
82
83    /// Write raw bytes.
84    #[inline]
85    pub fn write_bytes(&mut self, bytes: &[u8]) {
86        self.buf.extend_from_slice(bytes);
87    }
88
89    /// Patch bytes at a specific offset (for backpatching header fields).
90    #[inline]
91    pub fn patch(&mut self, offset: usize, bytes: &[u8]) {
92        self.buf[offset..offset + bytes.len()].copy_from_slice(bytes);
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn write_primitives() {
102        let mut s = OutputStream::new();
103        s.write_u8(0x01);
104        s.write_u16(0x0203);
105        s.write_u32(0x04050607);
106        assert_eq!(s.as_slice(), &[0x01, 0x03, 0x02, 0x07, 0x06, 0x05, 0x04]);
107    }
108
109    #[test]
110    fn write_bytes_and_position() {
111        let mut s = OutputStream::new();
112        assert_eq!(s.position(), 0);
113        s.write_bytes(b".FIT");
114        assert_eq!(s.position(), 4);
115        assert_eq!(s.as_slice(), b".FIT");
116    }
117
118    #[test]
119    fn patch_overwrites() {
120        let mut s = OutputStream::new();
121        s.write_u32(0); // placeholder
122        s.write_u32(0x12345678);
123        s.patch(0, &0xAABBCCDDu32.to_le_bytes());
124        assert_eq!(&s.as_slice()[0..4], &0xAABBCCDDu32.to_le_bytes());
125        assert_eq!(&s.as_slice()[4..8], &0x12345678u32.to_le_bytes());
126    }
127
128    #[test]
129    fn into_bytes_consumes() {
130        let mut s = OutputStream::with_capacity(16);
131        s.write_u8(42);
132        let bytes = s.into_bytes();
133        assert_eq!(bytes, vec![42]);
134    }
135
136    #[test]
137    fn signed_types() {
138        let mut s = OutputStream::new();
139        s.write_i16(-1);
140        s.write_i32(-2);
141        assert_eq!(&s.as_slice()[0..2], &(-1i16).to_le_bytes());
142        assert_eq!(&s.as_slice()[2..6], &(-2i32).to_le_bytes());
143    }
144}