1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use std::io::Write;
use crate::Segment;


/// A writer for sending several segments over a stream using variable length encoding
/// Data is written in little-endian if the feature "big-endian" is not enabled
/// # Example
/// ```
/// use send_it::writer::VarWriter;
///
/// let mut sender = VarWriter::new();
///
/// sender.add_string("Hello");
/// sender.add_string("World");
///
/// let mut buffer = Vec::new();
/// sender.send(&mut buffer).unwrap();
/// ```
pub struct VarWriter {
    data: Vec<Segment>,
}

impl VarWriter {
    /// Create a new VarWriter
    pub fn new() -> VarWriter {
        VarWriter {
            data: Vec::new(),
        }
    }

    /// Add a segment to the writer
    pub fn add(&mut self, segment: Segment) {
        self.data.push(segment);
    }

    /// Add a string to the writer
    /// # Example
    /// ```
    /// use send_it::writer::VarWriter;
    ///
    /// let mut sender = VarWriter::new();
    ///
    /// sender.add_string("Hello");
    /// ```
    pub fn add_string<S: Into<String>>(&mut self, string: S) {
        self.add(Segment::from(string.into()))
    }

    /// Add raw data to the writer
    /// # Example
    /// ```
    /// use send_it::writer::VarWriter;
    ///
    /// let mut sender = VarWriter::new();
    ///
    /// sender.add_raw(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]);
    /// ```
    pub fn add_raw(&mut self, raw: &[u8]) {
        self.data.push(Segment::from(raw));
    }

    /// Encodes the data and sends it over the stream.
    /// * The data is cleared after sending.
    /// # Example
    /// ```
    /// use send_it::writer::VarWriter;
    ///
    /// let mut sender = VarWriter::new();
    ///
    /// sender.add_string("Hello");
    /// sender.add_string("World");
    ///
    /// let mut buffer = Vec::new();
    /// sender.send(&mut buffer).unwrap();
    /// ```
    pub fn send<W: Write>(&mut self, stream: &mut W) -> std::io::Result<()> {
        self.send_without_clearing(stream)?;

        // Clear the internal data after sending
        self.clear();

        Ok(())
    }

    /// Encodes the data and sends it over the stream.
    /// * The data is not cleared after sending.
    /// # Example
    /// ```
    /// use send_it::writer::VarWriter;
    ///
    /// let mut sender = VarWriter::new();
    ///
    /// sender.add_string("Hello");
    /// sender.add_string("World");
    ///
    /// let mut buffer = Vec::new();
    /// sender.send_without_clearing(&mut buffer).unwrap();
    /// ```
    pub fn send_without_clearing<W: Write>(&mut self, stream: &mut W) -> std::io::Result<()> {
        let total_size: usize = self.data.iter().map(|segment| segment.len() + 4).sum();

        // Write the total size as varint
        self.write_varint(stream, total_size)?;

        // Write each segment's size and the segment itself
        for segment in &self.data {
            self.write_u32(stream, segment.len() as u32)?;
            // write the segment
            stream.write_all(segment.as_ref())?;
        }

        Ok(())
    }
    
    fn write_varint<W: Write>(&self, writer: &mut W, mut value: usize) -> std::io::Result<()> {
        loop {
            let mut byte = (value & 0x7F) as u8;
            value >>= 7;
            if value != 0 {
                byte |= 0x80;
            }
            writer.write_all(&[byte])?;
            if value == 0 {
                break;
            }
        }
        Ok(())
    }

    #[cfg(not(feature = "big-endian"))]
    fn write_u32<W: Write>(&self, writer: &mut W, value: u32) -> std::io::Result<()> {
        // writes little-endian
        writer.write_all(&[
            (value & 0xFF) as u8,
            ((value >> 8) & 0xFF) as u8,
            ((value >> 16) & 0xFF) as u8,
            ((value >> 24) & 0xFF) as u8,
        ])?;
        Ok(())
    }

    #[cfg(feature = "big-endian")]
    fn write_u32<W: Write>(&self, writer: &mut W, value: u32) -> std::io::Result<()> {
        // writes big-endian
        writer.write_all(&[
            ((value >> 24) & 0xFF) as u8,
            ((value >> 16) & 0xFF) as u8,
            ((value >> 8) & 0xFF) as u8,
            (value & 0xFF) as u8,
        ])?;
        Ok(())
    }

    /// Removes all segments from the writer
    pub fn clear(&mut self) {
        self.data.clear();
    }
}

impl Write for VarWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let vec = buf.to_vec();
        let size = vec.len();
        self.data.push(Segment::from(vec));
        Ok(size)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        // nothing to do
        Ok(())
    }
}

impl Default for VarWriter {
    fn default() -> Self {
        Self::new()
    }
}