byte_num/
into_ascii.rs

1use crate::constants::ASCII_TO_INT_FACTOR;
2
3/// This traits converts integers to bytes, and is implemented on all integer types.
4/// The most important method on this trait is [`IntoAscii::itoa`], which is called in a method-like style.
5/// It returns a `Vec<u8>`, representing the value of `self` as bytes.
6/// Negative numbers also include a `-` when converted.
7pub trait IntoAscii {
8    /// The function performing the convertion from a number to a Vec<u8>, containing the digits of the number.
9    ///
10    /// # Examples
11    /// ```
12    /// use byte_num::into_ascii::IntoAscii;
13    ///
14    /// fn main() {
15    ///     assert_eq!(12345u32.itoa(), [b'1', b'2', b'3', b'4', b'5']);
16    ///     assert_eq!((-12345i32).itoa(), [b'-', b'1', b'2', b'3', b'4', b'5']);
17    /// }
18    /// ```
19    #[inline]
20    fn itoa(&self) -> Vec<u8>
21    where
22        Self: Copy,
23    {
24        let size = Self::digits10(*self);
25        let mut buff = vec![0; size];
26
27        self.int_to_bytes(&mut buff);
28        buff
29    }
30
31    /// Returns the size of an integer. This is how many digits the integer has.
32    fn digits10(self) -> usize;
33
34    /// Writes `self` into `buff`.
35    /// This function assumes `buff` has enough space to hold all digits of `self`. For the number of digits `self` has, see [`IntoAscii::digits10`].
36    fn int_to_bytes(self, buff: &mut [u8]);
37}
38
39#[rustfmt::skip]
40macro_rules! unsigned_into_ascii {
41    ($int:ty) => {     
42        impl IntoAscii for $int {
43            #[inline]    
44            fn digits10(mut self) -> usize {
45                let mut result = 1;
46                loop {
47                    if self < 10 { break result;}
48                    if self < 100 { break result + 1; }
49                    if self < 1000 { break result + 2; }
50                    if self < 10000 { break result + 3; }
51        
52                    self /= 10_000;
53                    result += 4;
54                }
55            }
56        
57            #[inline]
58            fn int_to_bytes(mut self, buff: &mut [u8]) {
59                let mut chunked = buff.rchunks_exact_mut(4);
60                for mut chunk in chunked.by_ref() {
61                    let q = self / 10;
62                    let q1 = self / 100;
63                    let q2 = self / 1000;
64        
65                    let r = (self % 10) as u8 + ASCII_TO_INT_FACTOR;
66                    let r1 = (q   % 10) as u8 + ASCII_TO_INT_FACTOR;
67                    let r2 = (q1  % 10) as u8 + ASCII_TO_INT_FACTOR;
68                    let r3 = (q2  % 10) as u8 + ASCII_TO_INT_FACTOR;
69        
70                    match &mut chunk {
71                        [b3, b2, b1, b] => {
72                            *b = r;
73                            *b1 = r1;
74                            *b2 = r2;
75                            *b3 = r3;
76                        }
77                        _ => unreachable!(),
78                    }
79        
80                    self /= 10_000;
81                }
82        
83                for byte in chunked.into_remainder().iter_mut().rev() {
84                    let q = self / 10;
85                    let r = (self % 10) as u8 + ASCII_TO_INT_FACTOR;
86                    *byte = r;
87        
88                    //there's nothing more to do.
89                    if q == 0 {
90                        break;
91                    }
92        
93                    self = q;
94                }
95            }
96        }
97    };
98
99    (@u8) => {
100        impl IntoAscii for u8 {
101            #[inline]
102            fn digits10(self) -> usize {
103                if self < 10 {
104                    1
105                } else if self < 100 {
106                    2
107                } else {
108                    3
109                }
110            }
111        
112            #[inline]
113            fn int_to_bytes(mut self, buff: &mut [u8]) {
114                for byte in buff.iter_mut().rev() {
115                    let q = self / 10;
116                    let r = (self % 10) as u8 + ASCII_TO_INT_FACTOR;
117                    *byte = r;
118        
119                    if self == 0 {
120                        break;
121                    }
122        
123                    self = q;
124                }
125            }
126        }
127    };
128}
129
130macro_rules! signed_into_ascii {
131    ($int:ty, $unsigned_version:ty) => {
132        impl IntoAscii for $int {
133            #[inline]
134            fn itoa(&self) -> Vec<u8>
135            where
136                Self: Copy,
137            {
138                let (n, size) = if self.is_negative() {
139                    (self * -1, self.digits10() + 1)
140                } else {
141                    (*self, self.digits10())
142                };
143        
144                let mut buff = vec![b'-'; size];
145                (n as $unsigned_version).int_to_bytes(&mut buff);
146                buff
147            }
148        
149            #[inline]
150            fn digits10(self) -> usize {
151                (self.abs() as $unsigned_version).digits10()
152            }
153        
154            #[inline]
155            fn int_to_bytes(self, buff: &mut [u8]) {
156                if self.is_negative() {
157                    (self.abs() as $unsigned_version).int_to_bytes(buff);
158                    buff[0] = b'-';
159                } else {
160                    (self as $unsigned_version).int_to_bytes(buff);
161                }
162            }
163        }
164    };
165}
166
167unsigned_into_ascii!(@u8);
168unsigned_into_ascii!(u16);
169unsigned_into_ascii!(u32);
170unsigned_into_ascii!(u64);
171unsigned_into_ascii!(usize);
172
173signed_into_ascii!(i8, u8);
174signed_into_ascii!(i16, u16);
175signed_into_ascii!(i32, u32);
176signed_into_ascii!(i64, u64);
177signed_into_ascii!(isize, usize);
178
179impl<'a, N: Copy> IntoAscii for &'a N
180where
181    N: IntoAscii,
182{
183    #[inline]
184    fn digits10(self) -> usize {
185        (*self).digits10()
186    }
187
188    #[inline]
189    fn int_to_bytes(self, buff: &mut [u8]) {
190        (*self).int_to_bytes(buff);
191    }
192}
193
194impl<'a, N: Copy> IntoAscii for &'a mut N
195where
196    N: IntoAscii,
197{
198    #[inline]
199    fn digits10(self) -> usize {
200        (*self).digits10()
201    }
202
203    #[inline]
204    fn int_to_bytes(self, buff: &mut [u8]) {
205        (*self).int_to_bytes(buff);
206    }
207}
208
209impl<N: Copy> IntoAscii for Box<N>
210where
211    N: IntoAscii,
212{
213    #[inline]
214    fn digits10(self) -> usize {
215        (*self).digits10()
216    }
217
218    #[inline]
219    fn int_to_bytes(self, buff: &mut [u8]) {
220        (*self).int_to_bytes(buff);
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::IntoAscii;
227
228    #[test]
229    fn itoa_usize() {
230        assert_eq!(
231            123_456_789usize.itoa(),
232            vec![b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9']
233        );
234    }
235
236    #[test]
237    fn itoa_isize() {
238        assert_eq!(
239            (-123_456_789isize).itoa(),
240            vec![b'-', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9']
241        );
242    }
243
244    #[test]
245    fn itoa_0usize() {
246        assert_eq!(0usize.itoa(), vec![b'0']);
247    }
248
249    #[test]
250    fn itoa_0isize() {
251        assert_eq!((-0isize).itoa(), vec![b'0']);
252    }
253
254    #[test]
255    fn digits10_usize() {
256        assert_eq!(123456789usize.digits10(), 9);
257    }
258
259    #[test]
260    fn digits10_isize() {
261        assert_eq!((-123456789isize).digits10(), 9);
262    }
263
264    #[test]
265    fn digits10_0usize() {
266        assert_eq!(0usize.digits10(), 1);
267    }
268
269    #[test]
270    fn digits10_0isize() {
271        assert_eq!((-0isize).digits10(), 1);
272    }
273}