send_it/
writer.rs

1use std::io::Write;
2use crate::Segment;
3
4
5/// A writer for sending several segments over a stream using variable length encoding
6/// Data is written in little-endian if the feature "big-endian" is not enabled
7/// # Example
8/// ```
9/// use send_it::writer::VarWriter;
10///
11/// let mut sender = VarWriter::new();
12///
13/// sender.add_string("Hello");
14/// sender.add_string("World");
15///
16/// let mut buffer = Vec::new();
17/// sender.send(&mut buffer).unwrap();
18/// ```
19pub struct VarWriter {
20    data: Vec<Segment>,
21}
22
23impl VarWriter {
24    /// Create a new VarWriter
25    pub fn new() -> VarWriter {
26        VarWriter {
27            data: Vec::new(),
28        }
29    }
30
31    /// Add a segment to the writer
32    pub fn add(&mut self, segment: Segment) {
33        self.data.push(segment);
34    }
35
36    /// Add a string to the writer
37    /// # Example
38    /// ```
39    /// use send_it::writer::VarWriter;
40    ///
41    /// let mut sender = VarWriter::new();
42    ///
43    /// sender.add_string("Hello");
44    /// ```
45    pub fn add_string<S: Into<String>>(&mut self, string: S) {
46        self.add(Segment::from(string.into()))
47    }
48
49    /// Add raw data to the writer
50    /// # Example
51    /// ```
52    /// use send_it::writer::VarWriter;
53    ///
54    /// let mut sender = VarWriter::new();
55    ///
56    /// sender.add_raw(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]);
57    /// ```
58    pub fn add_raw(&mut self, raw: &[u8]) {
59        self.data.push(Segment::from(raw));
60    }
61
62    /// Encodes the data and sends it over the stream.
63    /// * The data is cleared after sending.
64    /// # Example
65    /// ```
66    /// use send_it::writer::VarWriter;
67    ///
68    /// let mut sender = VarWriter::new();
69    ///
70    /// sender.add_string("Hello");
71    /// sender.add_string("World");
72    ///
73    /// let mut buffer = Vec::new();
74    /// sender.send(&mut buffer).unwrap();
75    /// ```
76    pub fn send<W: Write>(&mut self, stream: &mut W) -> std::io::Result<()> {
77        self.send_without_clearing(stream)?;
78
79        // Clear the internal data after sending
80        self.clear();
81
82        Ok(())
83    }
84
85    /// Encodes the data and sends it over the stream.
86    /// * The data is not cleared after sending.
87    /// # Example
88    /// ```
89    /// use send_it::writer::VarWriter;
90    ///
91    /// let mut sender = VarWriter::new();
92    ///
93    /// sender.add_string("Hello");
94    /// sender.add_string("World");
95    ///
96    /// let mut buffer = Vec::new();
97    /// sender.send_without_clearing(&mut buffer).unwrap();
98    /// ```
99    pub fn send_without_clearing<W: Write>(&mut self, stream: &mut W) -> std::io::Result<()> {
100        let total_size: usize = self.data.iter().map(|segment| segment.len() + 4).sum();
101
102        // Write the total size as varint
103        self.write_varint(stream, total_size)?;
104
105        // Write each segment's size and the segment itself
106        for segment in &self.data {
107            self.write_u32(stream, segment.len() as u32)?;
108            // write the segment
109            stream.write_all(segment.as_ref())?;
110        }
111
112        Ok(())
113    }
114    
115    fn write_varint<W: Write>(&self, writer: &mut W, mut value: usize) -> std::io::Result<()> {
116        loop {
117            let mut byte = (value & 0x7F) as u8;
118            value >>= 7;
119            if value != 0 {
120                byte |= 0x80;
121            }
122            writer.write_all(&[byte])?;
123            if value == 0 {
124                break;
125            }
126        }
127        Ok(())
128    }
129
130    #[cfg(not(feature = "big-endian"))]
131    fn write_u32<W: Write>(&self, writer: &mut W, value: u32) -> std::io::Result<()> {
132        // writes little-endian
133        writer.write_all(&[
134            (value & 0xFF) as u8,
135            ((value >> 8) & 0xFF) as u8,
136            ((value >> 16) & 0xFF) as u8,
137            ((value >> 24) & 0xFF) as u8,
138        ])?;
139        Ok(())
140    }
141
142    #[cfg(feature = "big-endian")]
143    fn write_u32<W: Write>(&self, writer: &mut W, value: u32) -> std::io::Result<()> {
144        // writes big-endian
145        writer.write_all(&[
146            ((value >> 24) & 0xFF) as u8,
147            ((value >> 16) & 0xFF) as u8,
148            ((value >> 8) & 0xFF) as u8,
149            (value & 0xFF) as u8,
150        ])?;
151        Ok(())
152    }
153
154    /// Removes all segments from the writer
155    pub fn clear(&mut self) {
156        self.data.clear();
157    }
158}
159
160impl Write for VarWriter {
161    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
162        let vec = buf.to_vec();
163        let size = vec.len();
164        self.data.push(Segment::from(vec));
165        Ok(size)
166    }
167
168    fn flush(&mut self) -> std::io::Result<()> {
169        // nothing to do
170        Ok(())
171    }
172}
173
174impl Default for VarWriter {
175    fn default() -> Self {
176        Self::new()
177    }
178}