dotnet_binary_io/
writer.rs

1use zerocopy::IntoBytes;
2
3extern crate alloc;
4use alloc::vec::Vec;
5
6pub type Result<T> = core::result::Result<T, BinaryWriterError>;
7
8/// Encodes binary values, using the same rules as .NET's `System.IO.BinaryWriter`.
9pub struct BinaryWriter {
10    /// The output data.
11    pub out: Vec<u8>,
12}
13
14impl BinaryWriter {
15    /// Constructor
16    pub fn wrap(out: Vec<u8>) -> Self {
17        Self { out }
18    }
19
20    /// Extracts the inner buffer
21    pub fn into_inner(self) -> Vec<u8> {
22        self.out
23    }
24
25    /// Accesses the inner buffer
26    pub fn inner_mut(&mut self) -> &mut Vec<u8> {
27        &mut self.out
28    }
29
30    /// Creates a new `BinaryWriter` over a `Vec<u8>`
31    pub fn new() -> Self {
32        Self { out: Vec::new() }
33    }
34
35    /// Creates a new `BinaryWriter` over a `Vec<u8>` with the given capacity.
36    pub fn with_capacity(len: usize) -> Self {
37        Self {
38            out: Vec::with_capacity(len),
39        }
40    }
41
42    /// Writes `bytes` to the output.
43    pub fn write_bytes(&mut self, bytes: &[u8]) {
44        self.out.extend_from_slice(bytes);
45    }
46
47    /// Writes a small, fixed-size array of bytes.
48    pub fn write_cbytes<const N: usize>(&mut self, value: [u8; N]) {
49        self.write_bytes(&value)
50    }
51
52    /// Writes a single `u8` value
53    pub fn write_u8(&mut self, value: u8) {
54        self.write_bytes(&[value])
55    }
56
57    /// Writes a single `i8` value
58    pub fn write_i8(&mut self, value: i8) {
59        self.write_bytes(&[value as u8])
60    }
61
62    /// Writes a single `u16` value
63    pub fn write_u16(&mut self, value: u16) {
64        self.write_cbytes(value.to_le_bytes())
65    }
66
67    /// Writes a single `u32` value
68    pub fn write_u32(&mut self, value: u32) {
69        self.write_cbytes(value.to_le_bytes())
70    }
71
72    /// Writes a single `u64` value
73    pub fn write_u64(&mut self, value: u64) {
74        self.write_cbytes(value.to_le_bytes())
75    }
76
77    /// Writes a single `i16` value
78    pub fn write_i16(&mut self, value: i16) {
79        self.write_cbytes(value.to_le_bytes())
80    }
81
82    /// Writes a single `i32` value
83    pub fn write_i32(&mut self, value: i32) {
84        self.write_cbytes(value.to_le_bytes())
85    }
86
87    /// Writes a single `i64` value
88    pub fn write_i64(&mut self, value: i64) {
89        self.write_cbytes(value.to_le_bytes())
90    }
91
92    /// Encodes an `i32` value using a variable-length encoding.
93    ///
94    /// Although this function takes `i32` values, applications should avoid using this for
95    /// negative values. This function can correctly encode negative values, but most "small"
96    /// negative value (e.g. `-10`) will be encoded with the maximum number of bytes, which wastes
97    /// space.
98    pub fn write_7bit_encoded_i32(&mut self, value: i32) {
99        const MORE: u8 = 0x80; // bit indicating there are more bits
100        const MASK: u8 = 0x7f;
101
102        let w0: u8 = value as u8 & MASK; // 7 significant bits
103        let w1: u8 = (value >> 7) as u8 & MASK; // 7 significant bits
104        let w2: u8 = (value >> 14) as u8 & MASK; // 7 significant bits
105        let w3: u8 = (value >> 21) as u8 & MASK; // 7 significant bits
106        let w4: u8 = (value >> 28) as u8 & 0xF; // only 4 significant bits
107
108        if w4 != 0 {
109            self.write_cbytes([w0 | MORE, w1 | MORE, w2 | MORE, w3 | MORE, w4]);
110        } else if w3 != 0 {
111            self.write_cbytes([w0 | MORE, w1 | MORE, w2 | MORE, w3]);
112        } else if w2 != 0 {
113            self.write_cbytes([w0 | MORE, w1 | MORE, w2]);
114        } else if w1 != 0 {
115            self.write_cbytes([w0 | MORE, w1]);
116        } else {
117            self.write_cbytes([w0]);
118        }
119    }
120
121    /// Encodes an `i64` value using a variable-length encoding.
122    ///
123    /// Although this function takes `i64` values, applications should avoid using this for
124    /// negative values. This function can correctly encode negative values, but most "small"
125    /// negative value (e.g. `-10`) will be encoded with the maximum number of bytes, which wastes
126    /// space.
127    pub fn write_7bit_encoded_i64(&mut self, value: i64) {
128        let mut n: u64 = value as u64;
129
130        loop {
131            if n < 0x80 {
132                self.write_u8(n as u8);
133                break;
134            }
135            self.write_u8((n & 0x7f) as u8 | 0x80);
136            n >>= 7;
137        }
138    }
139
140    /// Writes a `bool` value. True is encoded as 1. False is encoded as 0.
141    pub fn write_bool(&mut self, value: bool) {
142        self.write_u8(value as u8)
143    }
144
145    /// Writes an `f32` value. The value is encoded using its 4-byte little-endian in-memory
146    /// representation.
147    pub fn write_f32(&mut self, value: f32) {
148        self.write_cbytes(value.to_le_bytes());
149    }
150
151    /// Writes an `f64` value. The value is encoded using its 4-byte little-endian in-memory
152    /// representation.
153    pub fn write_f64(&mut self, value: f64) {
154        self.write_cbytes(value.to_le_bytes());
155    }
156
157    /// Writes a UTF-8 string in length-prefixed form.
158    pub fn write_utf8_str(&mut self, s: &str) -> Result<()> {
159        let len_i32 = i32::try_from(s.len()).map_err(|_| BinaryWriterError::CannotEncode)?;
160        self.write_7bit_encoded_i32(len_i32);
161        self.write_bytes(s.as_bytes());
162        Ok(())
163    }
164
165    /// Writes a UTF-8 string in length-prefixed form.
166    ///
167    /// This function does not validate that the input string is well-formed UTF-8.
168    pub fn write_utf8_bytes(&mut self, s: &[u8]) -> Result<()> {
169        let len_i32 = i32::try_from(s.len()).map_err(|_| BinaryWriterError::CannotEncode)?;
170        self.write_7bit_encoded_i32(len_i32);
171        self.write_bytes(s);
172        Ok(())
173    }
174
175    /// Writes a UTF-16 string in length-prefixed form.
176    ///
177    /// This function does not validate that the input string is well-formed UTF-16.
178    pub fn write_utf16_wchars(&mut self, s: &[u16]) -> Result<()> {
179        let s_bytes = s.as_bytes();
180        let len_i32 = i32::try_from(s_bytes.len()).map_err(|_| BinaryWriterError::CannotEncode)?;
181        self.write_7bit_encoded_i32(len_i32);
182        self.write_bytes(s_bytes);
183        Ok(())
184    }
185
186    /// Converts a UTF-8 string into UTF-16 and writes it in length-prefixed form.
187    pub fn write_utf16_encode(&mut self, s: &str) {
188        let num_utf16_code_units = s.encode_utf16().count();
189        let len_bytes: usize = num_utf16_code_units * 2;
190        self.write_7bit_encoded_i32(len_bytes as i32);
191
192        self.out.reserve(len_bytes);
193        for c in s.encode_utf16() {
194            self.write_u16(c);
195        }
196    }
197}
198
199/// Error type for some `write_*` functions of `BinaryWriter`.
200#[derive(Clone, Eq, PartialEq, Debug)]
201pub enum BinaryWriterError {
202    /// Indicates that a value cannot be encoded. This is used for cases where a string or slice
203    /// is too large to encode using the variable-length encoding rules.
204    CannotEncode,
205}
206
207impl core::error::Error for BinaryWriterError {}
208
209impl core::fmt::Display for BinaryWriterError {
210    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211        match self {
212            Self::CannotEncode => f.write_str("The data cannot be encoded"),
213        }
214    }
215}