write_into/
leb128.rs

1use super::{write_into, WriteInto};
2use std::io;
3use std::mem::{size_of, MaybeUninit};
4
5/// Used to write values in LEB-128 format _(unsigned)_.
6///
7/// # Example
8///
9/// ```
10/// use write_into::{Uleb128, write_into};
11///
12/// let mut buffer = Vec::new();
13/// write_into(&mut buffer, Uleb128(69u32)).unwrap();
14/// assert_eq!(&buffer, &[0x45]);
15/// ```
16pub struct Uleb128<T>(pub T);
17
18/// Used to write values in LEB-128 format _(signed)_.
19///
20/// # Example
21///
22/// ```
23/// use write_into::{Sleb128, write_into};
24///
25/// let mut buffer = Vec::new();
26/// write_into(&mut buffer, Sleb128(-69i32)).unwrap();
27/// assert_eq!(&buffer, &[0xBB, 0x7F]);
28/// ```
29pub struct Sleb128<T>(pub T);
30
31macro_rules! impl_write_into {
32    ($($wrapper:ident => { $($primitive:ident)* }),*,) => {
33        $(
34            $(
35                impl_impl!($wrapper, $primitive);
36            )*
37        )*
38    }
39}
40
41macro_rules! impl_impl {
42    (Uleb128, $primitive:ident) => {
43        impl WriteInto for Uleb128<$primitive> {
44            type Output = usize;
45
46            fn write_into(mut self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
47                // SAFETY:
48                // The uninitialized value is valid.
49                let mut buffer = unsafe {
50                    // https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.uninit_array
51                    MaybeUninit::<[MaybeUninit<u8>; max_leb128_size(size_of::<Self>())]>::uninit()
52                        .assume_init()
53                };
54
55                let mut written = 0;
56                for byte in buffer.iter_mut() {
57                    let mut value = self.0 as u8 & 0x7F;
58                    self.0 >>= 7;
59                    if self.0 != 0 {
60                        value |= 0x80;
61                    }
62
63                    *byte = MaybeUninit::new(value);
64                    written += 1;
65
66                    if self.0 == 0 {
67                        break;
68                    }
69                }
70
71                // SAFETY:
72                // - The slice is initialized.
73                let bytes = unsafe {
74                    // https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref
75                    &*(&buffer[..written] as *const [MaybeUninit<u8>] as *const [u8])
76                };
77
78                sink.write_all(bytes)?;
79                Ok(written)
80            }
81        }
82
83        impl WriteInto for &Uleb128<$primitive> {
84            type Output = usize;
85
86            fn write_into(self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
87                write_into(sink, Uleb128(self.0))
88            }
89        }
90    };
91    (Sleb128, $primitive:ident) => {
92        impl WriteInto for Sleb128<$primitive> {
93            type Output = usize;
94
95            fn write_into(mut self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
96                // SAFETY:
97                // The uninitialized value is valid.
98                let mut buffer = unsafe {
99                    // https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.uninit_array
100                    MaybeUninit::<[MaybeUninit<u8>; max_leb128_size(size_of::<Self>())]>::uninit()
101                        .assume_init()
102                };
103
104                let mut written = 0;
105                for byte in buffer.iter_mut() {
106                    let mut value = self.0 as u8;
107                    self.0 >>= 6; // Keeping sign bit.
108                    let done = self.0 == 0 || self.0 == -1;
109                    if done {
110                        value &= 0x7F;
111                    } else {
112                        self.0 >>= 1;
113                        value |= 0x80;
114                    }
115
116                    *byte = MaybeUninit::new(value);
117                    written += 1;
118
119                    if done {
120                        break;
121                    }
122                }
123
124                // SAFETY:
125                // - The slice is initialized.
126                let bytes = unsafe {
127                    // https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref
128                    &*(&buffer[..written] as *const [MaybeUninit<u8>] as *const [u8])
129                };
130
131                sink.write_all(bytes)?;
132                Ok(written)
133            }
134        }
135
136        impl WriteInto for &Sleb128<$primitive> {
137            type Output = usize;
138
139            fn write_into(self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
140                write_into(sink, Sleb128(self.0))
141            }
142        }
143    };
144}
145
146impl_write_into! {
147    Uleb128 => {
148        u8 u16 u32 u64 u128 usize
149    },
150    Sleb128 => {
151        i8 i16 i32 i64 i128 isize
152    },
153}
154
155const fn max_leb128_size(bytes: usize) -> usize {
156    let bits = bytes * 8;
157    let septets = count_bits_in_chunks(bits, 7);
158    let bits_for_septents = septets * 7;
159    let bits_for_continutation_bits = septets * 1;
160    count_bits_in_chunks(bits_for_septents + bits_for_continutation_bits, 8)
161}
162
163const fn count_bits_in_chunks(bits: usize, chunk_size: usize) -> usize {
164    let chunks = bits / chunk_size;
165    let remaining = bits % chunk_size;
166    chunks + if remaining != 0 { 1 } else { 0 }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::super::*;
172    use super::*;
173    use test_case::test_case;
174    use validators::vec;
175
176    mod validators {
177        pub fn vec(expected: &[u8]) -> impl FnOnce(Vec<u8>) {
178            let expected = expected.to_vec();
179            move |actual| assert_eq!(&expected, &actual)
180        }
181    }
182
183    #[test_case(  1 =>  2; "when u8"   )]
184    #[test_case(  2 =>  3; "when u16"  )]
185    #[test_case(  4 =>  5; "when u32"  )]
186    #[test_case(  8 => 10; "when u64"  )]
187    #[test_case( 16 => 19; "when u128" )]
188    fn max_leb128_size_for_primitive_types(bytes: usize) -> usize {
189        max_leb128_size(bytes)
190    }
191
192    #[test_case(     0 => using vec(&[ 0x00             ]); "when     0" )]
193    #[test_case(    69 => using vec(&[ 0x45             ]); "when    69" )]
194    #[test_case(   123 => using vec(&[ 0x7B             ]); "when   123" )]
195    #[test_case(   127 => using vec(&[ 0x7F             ]); "when   127" )]
196    #[test_case(   128 => using vec(&[ 0x80, 0x01       ]); "when   128" )]
197    #[test_case(   228 => using vec(&[ 0xE4, 0x01       ]); "when   228" )]
198    #[test_case(   255 => using vec(&[ 0xFF, 0x01       ]); "when   255" )]
199    #[test_case(  4200 => using vec(&[ 0xE8, 0x20       ]); "when  4200" )]
200    #[test_case( 16383 => using vec(&[ 0xFF, 0x7F       ]); "when 16383" )]
201    #[test_case( 32767 => using vec(&[ 0xFF, 0xFF, 0x01 ]); "when 32767" )]
202    #[test_case( 42000 => using vec(&[ 0x90, 0xC8, 0x02 ]); "when 42000" )]
203    #[test_case( 65535 => using vec(&[ 0xFF, 0xFF, 0x03 ]); "when 65535" )]
204    fn write_u16(number: u16) -> Vec<u8> {
205        let mut buffer = Vec::new();
206        write_into(&mut buffer, Uleb128(number)).unwrap();
207        buffer
208    }
209
210    #[test_case( -32768 => using vec(&[ 0x80, 0x80, 0x7E ]); "when  minus 32768" )]
211    #[test_case(  -8192 => using vec(&[ 0x80, 0x40       ]); "when  minus  8192" )]
212    #[test_case(  -4200 => using vec(&[ 0x98, 0x5F       ]); "when  minus  4200" )]
213    #[test_case(   -128 => using vec(&[ 0x80, 0x7F       ]); "when  minus   128" )]
214    #[test_case(   -123 => using vec(&[ 0x85, 0x7F       ]); "when  minus   123" )]
215    #[test_case(    -69 => using vec(&[ 0xBB, 0x7F       ]); "when  minus    69" )]
216    #[test_case(    -34 => using vec(&[ 0x5E             ]); "when  minus    34" )]
217    #[test_case(      0 => using vec(&[ 0x00             ]); "when            0" )]
218    #[test_case(     34 => using vec(&[ 0x22             ]); "when           34" )]
219    #[test_case(     69 => using vec(&[ 0xC5, 0x00       ]); "when           69" )]
220    #[test_case(    123 => using vec(&[ 0xFB, 0x00       ]); "when          123" )]
221    #[test_case(    127 => using vec(&[ 0xFF, 0x00       ]); "when          127" )]
222    #[test_case(   4200 => using vec(&[ 0xE8, 0x20       ]); "when         4200" )]
223    #[test_case(   8191 => using vec(&[ 0xFF, 0x3F       ]); "when         8191" )]
224    #[test_case(  32767 => using vec(&[ 0xFF, 0xFF, 0x01 ]); "when        32767" )]
225    fn write_i16(number: i16) -> Vec<u8> {
226        let mut buffer = Vec::new();
227        write_into(&mut buffer, Sleb128(number)).unwrap();
228        buffer
229    }
230}