csharp_binary_encoding/encoding/
binarywriter.rs

1
2use std::io;
3use std::io::Write;
4/// Analagous to the [`System.IO.BinaryWriter`] C# Class.
5///
6/// Writes to any Write implementor.
7///
8/// [`System.IO.BinaryWriter`]:
9/// <https://learn.microsoft.com/en-us/dotnet/api/system.io.binarywriter>
10pub struct BinaryWriter<T: Write> {
11    output: T
12}
13
14impl<T> BinaryWriter<T>
15where T: Write {
16    
17    ///Creates a new BinaryWriter which will write data to the provided Writer
18    pub fn new(output: T) -> Self {
19        Self {
20            output
21        }
22    }
23    
24    /// Equivalent to the Write method in C# called with an argument of type Byte.
25    pub fn write_byte(&mut self, data: u8) -> io::Result<usize> {
26        self.output.write(&[data])
27    }
28
29    /// Equivalent to the Write method in C# called with an argument of type Byte[].
30    pub fn write_bytes(&mut self, data: &[u8]) -> io::Result<usize> {
31        self.output.write(data)
32    }
33
34    // Implementation translated from the c# dotnet runtime's implementation of BinaryWriter
35    // MIT Licensed by the .NET foundation, can be found at https://github.com/dotnet/runtime
36    /// Equivalent to the Write7BitEncodedInt method in C#.
37    pub fn write_7_bit_encoded_int(&mut self, data: i32) -> io::Result<usize> {
38        let mut value = data as u32;
39        let mut out_bytes: Vec<u8> = Vec::new();
40        while value > 0x7F {
41            let low_bits_and_flag: u8 = (value | !0x7F).to_le_bytes()[0];
42            value >>= 7;
43            out_bytes.push(low_bits_and_flag);
44        }
45        out_bytes.push(value.to_le_bytes()[0]);
46        self.write_bytes(&out_bytes)
47    }
48
49    // Implementation translated from the c# dotnet runtime's implementation of BinaryWriter
50    // MIT Licensed by the .NET foundation, can be found at https://github.com/dotnet/runtime
51    /// Equivalent to the Write7BitEncodedInt64 method in C#. 
52    pub fn write_7_bit_encoded_int64(&mut self, data: i64) -> io::Result<usize> {
53        let mut value = data as u64;
54        let mut out_bytes: Vec<u8> = Vec::new();
55        while value > 0x7F {
56            let low_bits_and_flag: u8 = (value | !0x7F).to_le_bytes()[0];
57            value >>= 7;
58            out_bytes.push(low_bits_and_flag);
59        }
60        out_bytes.push(value.to_le_bytes()[0]);
61        self.write_bytes(&out_bytes)
62    }
63    
64    /// Equivalent to the Write method in C# called with an argument of type Boolean.
65    pub fn write_boolean(&mut self, data: bool) -> io::Result<usize> {
66        // explicitely use C#'s binary representation of bool
67        // without making assumptions about how rust stores bool values 
68        // in memory
69        if data {
70            self.write_byte(1) 
71        } else {
72            self.write_byte(0)
73        }
74    }
75    
76    /// Equivalent to the Write method in C# called with an argument of type Single
77    pub fn write_f32(&mut self, data: f32) -> io::Result<usize> {
78        self.output.write(&data.to_le_bytes())
79    }
80
81    /// Equivalent to the Write method in C# called with an argument of type Double
82    pub fn write_f64(&mut self, data: f64) -> io::Result<usize> {
83        self.output.write(&data.to_le_bytes())
84    }
85
86    /// Equivalent to the Write method in C# called with an argument of type Half
87    #[cfg_attr(docsrs, doc(cfg(feature = "f16")))]
88    #[cfg(feature = "f16")]
89    pub fn write_f16(&mut self, data: f16) -> io::Result<usize> {
90        self.output.write(&data.to_le_bytes())
91    }
92
93    /// Equivalent to the Write method in C# called with an argument of type String
94    pub fn write_string(&mut self, data: &str) -> io::Result<usize> {
95        // first, write the number of bytes the string will take up in utf-8
96        if let Err(e) = self.write_7_bit_encoded_int(data.len().try_into().unwrap()) {
97            return Err(e)
98        }
99        // then, write the utf-8 data. rust str is gauranteed to be valid utf-8 so no further
100        // processing is needed.
101        self.write_bytes(data.as_bytes())
102    }
103    
104    /// Equivalent to the Write method in C# called with an argument of type SByte
105    pub fn write_i8(&mut self, data: i8) -> io::Result<usize> {
106        self.output.write(&data.to_le_bytes())
107    }
108
109    /// Equivalent to the Write method in C# called with an argument of type Int16
110    pub fn write_i16(&mut self, data: i16) -> io::Result<usize> {
111        self.output.write(&data.to_le_bytes())
112    }
113
114    /// Equivalent to the Write method in C# called with an argument of type Int32
115    pub fn write_i32(&mut self, data: i32) -> io::Result<usize> {
116        self.output.write(&data.to_le_bytes())
117    }
118
119    /// Equivalent to the Write method in C# called with an argument of type Int64
120    pub fn write_i64(&mut self, data: i64) -> io::Result<usize> {
121        self.output.write(&data.to_le_bytes())
122    }
123
124    /// Equivalent to the Write method in C# called with an argument of type UInt16
125    pub fn write_u16(&mut self, data: u16) -> io::Result<usize> {
126        self.output.write(&data.to_le_bytes())
127    }
128
129    /// Equivalent to the Write method in C# called with an argument of type UInt32
130    pub fn write_u32(&mut self, data: u32) -> io::Result<usize> {
131        self.output.write(&data.to_le_bytes())
132    }
133
134    /// Equivalent to the Write method in C# called with an argument of type UInt64
135    pub fn write_u64(&mut self, data: u64) -> io::Result<usize> {
136        self.output.write(&data.to_le_bytes())
137    }
138
139    /// Equivalent to the Write method in C# called with an argument of type Char
140    pub fn write_char(&mut self, data: char) -> io::Result<usize> {
141        let mut buf: [u8; 4] = [0; 4];
142        self.write_bytes(data.encode_utf8(buf.as_mut_slice()).as_bytes())
143    }
144
145}