bitbuffer/
write.rs

1use crate::{BitReadStream, BitWriteStream, Endianness, Result};
2use std::borrow::Cow;
3use std::rc::Rc;
4use std::sync::Arc;
5
6/// Trait for types that can be written to a stream without requiring the size to be configured
7///
8/// The `BitWrite` trait can be used with `#[derive]` on structs and enums
9///
10/// # Structs
11///
12/// The implementation can be derived for a struct as long as every field in the struct implements `BitWrite` or [`BitWriteSized`]
13///
14/// The struct is written field by field in the order they are defined in, if the size for a field is set [`stream.write_sized()`][write_sized]
15/// will be used, otherwise [`write_read()`][write] will be used.
16///
17/// The size for a field can be set using 3 different methods
18///  - set the size as an integer using the `size` attribute,
19///  - use a previously defined field as the size using the `size` attribute
20///
21/// ## Examples
22///
23/// ```
24/// # use bitbuffer::BitWrite;
25/// #
26/// #[derive(BitWrite)]
27/// struct TestStruct {
28///     foo: u8,
29///     str: String,
30///     #[size = 2] // when `size` is set, the attributed will be read using `read_sized`
31///     truncated: String,
32///     bar: u16,
33///     float: f32,
34///     #[size = 3]
35///     asd: u8,
36///     #[size = "asd"] // use a previously defined field as size
37///     previous_field: u8,
38/// }
39/// ```
40///
41/// # Enums
42///
43/// The implementation can be derived for an enum as long as every variant of the enum either has no field, or an unnamed field that implements `BitWrite` or [`BitWriteSized`]
44///
45/// The enum is written by first writing a set number of bits as the discriminant of the enum, then the variant written.
46///
47/// For details about setting the input size for fields implementing [`BitWriteSized`] see the block about size in the `Structs` section above.
48///
49/// The discriminant for the variants defaults to incrementing by one for every field, starting with `0`.
50/// You can overwrite the discriminant for a field, which will also change the discriminant for every following field.
51///
52/// ## Examples
53///
54/// ```
55/// # use bitbuffer::BitWrite;
56/// #
57/// #[derive(BitWrite)]
58/// #[discriminant_bits = 2]
59/// enum TestBareEnum {
60///     Foo,
61///     Bar,
62///     Asd = 3, // manually set the discriminant value for a field
63/// }
64/// ```
65///
66/// ```
67/// # use bitbuffer::BitWrite;
68/// #
69/// #[derive(BitWrite)]
70/// #[discriminant_bits = 2]
71/// enum TestUnnamedFieldEnum {
72///     #[size = 5]
73///     Foo(i8),
74///     Bar(bool),
75///     #[discriminant = 3] // since rust only allows setting the discriminant on field-less enums, you can use an attribute instead
76///     Asd(u8),
77/// }
78/// ```
79///
80/// [write_sized]: BitWriteStream::write_sized
81/// [write]: BitWriteStream::write
82pub trait BitWrite<E: Endianness> {
83    /// Write the type to stream
84    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()>;
85}
86
87macro_rules! impl_write_int {
88    ($type:ty) => {
89        impl<E: Endianness> BitWrite<E> for $type {
90            #[inline]
91            fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
92                stream.write_int::<$type>(*self, <$type>::BITS as usize)
93            }
94        }
95    };
96}
97
98impl_write_int!(u8);
99impl_write_int!(u16);
100impl_write_int!(u32);
101impl_write_int!(u64);
102impl_write_int!(u128);
103impl_write_int!(i8);
104impl_write_int!(i16);
105impl_write_int!(i32);
106impl_write_int!(i64);
107impl_write_int!(i128);
108
109impl<E: Endianness> BitWrite<E> for f32 {
110    #[inline]
111    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
112        stream.write_float::<f32>(*self)
113    }
114}
115
116impl<E: Endianness> BitWrite<E> for f64 {
117    #[inline]
118    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
119        stream.write_float::<f64>(*self)
120    }
121}
122
123impl<E: Endianness> BitWrite<E> for bool {
124    #[inline]
125    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
126        stream.write_bool(*self)
127    }
128}
129
130impl<E: Endianness> BitWrite<E> for str {
131    #[inline]
132    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
133        stream.write_string(self, None)
134    }
135}
136
137impl<E: Endianness> BitWrite<E> for String {
138    #[inline]
139    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
140        stream.write_string(self, None)
141    }
142}
143
144impl<E: Endianness> BitWrite<E> for BitReadStream<'_, E> {
145    #[inline]
146    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
147        stream.write_bits(self)
148    }
149}
150
151impl<E: Endianness, T: BitWrite<E>, const N: usize> BitWrite<E> for [T; N] {
152    #[inline]
153    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
154        for element in self.iter() {
155            stream.write(element)?;
156        }
157        Ok(())
158    }
159}
160
161impl<T: BitWrite<E>, E: Endianness> BitWrite<E> for Box<T> {
162    #[inline]
163    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
164        stream.write(self.as_ref())
165    }
166}
167
168impl<T: BitWrite<E>, E: Endianness> BitWrite<E> for Rc<T> {
169    #[inline]
170    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
171        stream.write(self.as_ref())
172    }
173}
174
175impl<T: BitWrite<E>, E: Endianness> BitWrite<E> for Arc<T> {
176    #[inline]
177    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
178        stream.write(self.as_ref())
179    }
180}
181
182impl<T: BitWrite<E>, E: Endianness> BitWrite<E> for Vec<T> {
183    #[inline]
184    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
185        for item in self {
186            stream.write(item)?;
187        }
188        Ok(())
189    }
190}
191
192impl<T: BitWrite<E>, E: Endianness> BitWrite<E> for Option<T> {
193    #[inline]
194    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
195        self.is_some().write(stream)?;
196        if let Some(val) = self {
197            val.write(stream)?;
198        }
199        Ok(())
200    }
201}
202
203impl<T: BitWrite<E> + ToOwned + ?Sized, E: Endianness> BitWrite<E> for Cow<'_, T> {
204    #[inline]
205    fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
206        self.as_ref().write(stream)
207    }
208}
209
210macro_rules! impl_write_tuple {
211    ($($i:tt: $type:ident),*) => {
212        impl<E: Endianness, $($type: BitWrite<E>),*> BitWrite<E> for ($($type),*) {
213            #[inline]
214            fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
215                $(self.$i.write(stream)?;)*
216                Ok(())
217            }
218        }
219    };
220}
221
222impl_write_tuple!(0: T1, 1: T2);
223impl_write_tuple!(0: T1, 1: T2, 2: T3);
224impl_write_tuple!(0: T1, 1: T2, 2: T3, 3: T4);
225
226/// Trait for types that can be written to a stream, requiring the size to be configured
227///
228/// The meaning of the set sized depends on the type being written (e.g, number of bits for integers,
229/// number of bytes for strings, number of items for Vec's, etc)
230///
231/// The `BitReadSized` trait can be used with `#[derive]` on structs
232///
233/// The implementation can be derived for a struct as long as every field in the struct implements [`BitWrite`] or `BitWriteSized`
234///
235/// The struct is written field by field in the order they are defined in, if the size for a field is set [`stream.write_sized()`][write_sized]
236/// will be used, otherwise [`stream.write()`][write] will be used.
237///
238/// The size for a field can be set using 4 different methods
239///  - set the size as an integer using the `size` attribute,
240///  - use a previously defined field as the size using the `size` attribute
241///  - based on the input size by setting `size` attribute to `"input_size"`
242///
243/// ## Examples
244///
245/// ```
246/// # use bitbuffer::BitWriteSized;
247/// #
248/// #[derive(BitWriteSized, PartialEq, Debug)]
249/// struct TestStructSized {
250///     foo: u8,
251///     #[size = "input_size"]
252///     string: String,
253///     #[size = "input_size"]
254///     int: u8,
255/// }
256/// ```
257///
258/// # Enums
259///
260/// The implementation can be derived for an enum as long as every variant of the enum either has no field, or an unnamed field that implements [`BitWrite`] or `BitWriteSized`
261///
262/// The enum is written by first writing a set number of bits as the discriminant of the enum, then the variant is written.
263///
264/// For details about setting the input size for fields implementing `BitWriteSized` see the block about size in the `Structs` section above.
265///
266/// The discriminant for the variants defaults to incrementing by one for every field, starting with `0`.
267/// You can overwrite the discriminant for a field, which will also change the discriminant for every following field.
268///
269/// ## Examples
270///
271/// ```
272/// # use bitbuffer::BitWriteSized;
273/// #
274/// #[derive(BitWriteSized)]
275/// #[discriminant_bits = 2]
276/// enum TestUnnamedFieldEnum {
277///     #[size = 5]
278///     Foo(i8),
279///     Bar(bool),
280///     #[discriminant = 3] // since rust only allows setting the discriminant on field-less enums, you can use an attribute instead
281///     #[size = "input_size"]
282///     Asd(u8),
283/// }
284/// ```
285///
286/// [write_sized]: BitReadStream::write_sized
287/// [write]: BitReadStream::write
288pub trait BitWriteSized<E: Endianness> {
289    /// Write the type to stream
290    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()>;
291}
292
293impl<E: Endianness> BitWriteSized<E> for str {
294    #[inline]
295    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
296        stream.write_string(self, Some(len))
297    }
298}
299
300impl<E: Endianness> BitWriteSized<E> for String {
301    #[inline]
302    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
303        stream.write_string(self, Some(len))
304    }
305}
306
307macro_rules! impl_write_sized_int {
308    ($type:ty) => {
309        impl<E: Endianness> BitWriteSized<E> for $type {
310            #[inline]
311            fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
312                stream.write_int::<$type>(*self, len)
313            }
314        }
315    };
316}
317
318impl_write_sized_int!(u8);
319impl_write_sized_int!(u16);
320impl_write_sized_int!(u32);
321impl_write_sized_int!(u64);
322impl_write_sized_int!(u128);
323impl_write_sized_int!(usize);
324impl_write_sized_int!(i8);
325impl_write_sized_int!(i16);
326impl_write_sized_int!(i32);
327impl_write_sized_int!(i64);
328impl_write_sized_int!(i128);
329impl_write_sized_int!(isize);
330
331impl<E: Endianness> BitWriteSized<E> for BitReadStream<'_, E> {
332    #[inline]
333    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
334        let bits = self.clone().read_bits(len)?;
335        stream.write_bits(&bits)
336    }
337}
338
339impl<E: Endianness, T: BitWriteSized<E>, const N: usize> BitWriteSized<E> for [T; N] {
340    #[inline]
341    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
342        for element in self.iter() {
343            stream.write_sized(element, len)?;
344        }
345        Ok(())
346    }
347}
348
349impl<T: BitWriteSized<E>, E: Endianness> BitWriteSized<E> for Box<T> {
350    #[inline]
351    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
352        stream.write_sized(self.as_ref(), len)
353    }
354}
355
356impl<T: BitWriteSized<E>, E: Endianness> BitWriteSized<E> for Rc<T> {
357    #[inline]
358    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
359        stream.write_sized(self.as_ref(), len)
360    }
361}
362
363impl<T: BitWriteSized<E>, E: Endianness> BitWriteSized<E> for Arc<T> {
364    #[inline]
365    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
366        stream.write_sized(self.as_ref(), len)
367    }
368}
369
370impl<T: BitWriteSized<E>, E: Endianness> BitWriteSized<E> for Option<T> {
371    #[inline]
372    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
373        self.is_some().write(stream)?;
374        if let Some(val) = self {
375            val.write_sized(stream, len)?;
376        }
377        Ok(())
378    }
379}
380
381impl<T: BitWriteSized<E> + ToOwned + ?Sized, E: Endianness> BitWriteSized<E> for Cow<'_, T> {
382    #[inline]
383    fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
384        self.as_ref().write_sized(stream, len)
385    }
386}