1 //! Fast lexical integer-to-string conversion routines.
2 
3 //  The following benchmarks were run on an "Intel(R) Core(TM) i7-6560U
4 //  CPU @ 2.20GHz" CPU, on Fedora 28, Linux kernel version 4.18.16-200
5 //  (x86-64), using the lexical formatter, `itoa::write()` or `x.to_string()`,
6 //  avoiding any inefficiencies in Rust string parsing for `format!(...)`
7 //  or `write!()` macros. The code was compiled with LTO and at an optimization
8 //  level of 3.
9 //
10 //  The benchmarks with `std` were compiled using "rustc 1.29.2 (17a9dc751
11 //  2018-10-05", and the `no_std` benchmarks were compiled using "rustc
12 //  1.31.0-nightly (46880f41b 2018-10-15)".
13 //
14 //  The benchmark code may be found `benches/itoa.rs`.
15 //
16 //  # Benchmarks
17 //
18 //  | Type  |  lexical (ns/iter) | to_string (ns/iter)   | Relative Increase |
19 //  |:-----:|:------------------:|:---------------------:|:-----------------:|
20 //  | u8    | 118,355            | 580,365               | 4.90x             |
21 //  | u16   | 130,145            | 559,563               | 4.30x             |
22 //  | u32   | 168,984            | 572,838               | 3.39x             |
23 //  | u64   | 307,052            | 706,427               | 2.30x             |
24 //  | i8    | 149,634            | 788,111               | 5.27x             |
25 //  | i16   | 169,105            | 810,891               | 4.80x             |
26 //  | i32   | 218,203            | 868,149               | 3.98x             |
27 //  | i64   | 323,406            | 923,783               | 2.86x             |
28 //
29 //  # Raw Benchmarks
30 //
31 //  ```text
32 //  test itoa_i8_itoa       ... bench:     148,488 ns/iter (+/- 5,138)
33 //  test itoa_i8_lexical    ... bench:     149,634 ns/iter (+/- 5,080)
34 //  test itoa_i8_to_string  ... bench:     788,111 ns/iter (+/- 22,323)
35 //  test itoa_i16_itoa      ... bench:     165,307 ns/iter (+/- 6,178)
36 //  test itoa_i16_lexical   ... bench:     169,105 ns/iter (+/- 5,531)
37 //  test itoa_i16_to_string ... bench:     810,891 ns/iter (+/- 31,014)
38 //  test itoa_i32_itoa      ... bench:     220,269 ns/iter (+/- 11,670)
39 //  test itoa_i32_lexical   ... bench:     218,203 ns/iter (+/- 7,772)
40 //  test itoa_i32_to_string ... bench:     868,149 ns/iter (+/- 50,224)
41 //  test itoa_i64_itoa      ... bench:     275,732 ns/iter (+/- 10,747)
42 //  test itoa_i64_lexical   ... bench:     323,406 ns/iter (+/- 9,937)
43 //  test itoa_i64_to_string ... bench:     923,783 ns/iter (+/- 21,573)
44 //  test itoa_u8_itoa       ... bench:     119,045 ns/iter (+/- 6,135)
45 //  test itoa_u8_lexical    ... bench:     118,355 ns/iter (+/- 5,028)
46 //  test itoa_u8_to_string  ... bench:     580,365 ns/iter (+/- 24,363)
47 //  test itoa_u16_itoa      ... bench:     109,700 ns/iter (+/- 4,134)
48 //  test itoa_u16_lexical   ... bench:     130,145 ns/iter (+/- 2,553)
49 //  test itoa_u16_to_string ... bench:     559,563 ns/iter (+/- 18,978)
50 //  test itoa_u32_itoa      ... bench:     154,892 ns/iter (+/- 5,122)
51 //  test itoa_u32_lexical   ... bench:     168,984 ns/iter (+/- 5,195)
52 //  test itoa_u32_to_string ... bench:     572,838 ns/iter (+/- 15,236)
53 //  test itoa_u64_itoa      ... bench:     284,219 ns/iter (+/- 9,696)
54 //  test itoa_u64_lexical   ... bench:     307,052 ns/iter (+/- 11,039)
55 //  test itoa_u64_to_string ... bench:     706,427 ns/iter (+/- 32,680)
56 //  ```
57 
58 // Code the generate the benchmark plot:
59 //  import numpy as np
60 //  import pandas as pd
61 //  import matplotlib.pyplot as plt
62 //  plt.style.use('ggplot')
63 //  lexical = np.array([118355, 130145, 168984, 307052, 149634, 169105, 218203, 323406]) / 1e6
64 //  itoa = np.array([119045, 109700, 154892, 284219, 148488, 165307, 220269, 275732]) / 1e6
65 //  rustcore = np.array([580365, 559563, 572838, 706427, 788111, 810891, 868149, 923783]) / 1e6
66 //  index = ["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64"]
67 //  df = pd.DataFrame({'lexical': lexical, 'itoa': itoa, 'rustcore': rustcore}, index = index, columns=['lexical', 'itoa', 'rustcore'])
68 //  ax = df.plot.bar(rot=0, figsize=(16, 8), fontsize=14, color=['#E24A33', '#988ED5', '#348ABD'])
69 //  ax.set_ylabel("ms/iter")
70 //  ax.figure.tight_layout()
71 //  ax.legend(loc=2, prop={'size': 14})
72 //  plt.show()
73 
74 use util::*;
75 
76 // OPTIMIZED
77 
78 /// Optimized implementation for radix-N numbers.
79 ///
80 /// Use a macro to allow for u32 or u64 to be used (u32 is generally faster).
81 ///
82 /// `value` must be non-negative and mutable.
83 #[cfg(feature = "table")]
84 #[inline]
optimized<T>(mut value: T, radix: T, table: &[u8], buffer: &mut [u8]) -> usize where T: UnsignedInteger85 fn optimized<T>(mut value: T, radix: T, table: &[u8], buffer: &mut [u8])
86     -> usize
87     where T: UnsignedInteger
88 {
89     // Use power-reduction to minimize the number of operations.
90     // Idea taken from "3 Optimization Tips for C++".
91     let radix2 = radix * radix;
92     let radix4 = radix2 * radix2;
93 
94     // Decode 4-digits at a time
95     let mut iter = buffer.iter_mut().rev();
96     while value >= radix4 {
97         let rem = value % radix4;
98         value /= radix4;
99         let r1 = (T::TWO * (rem / radix2)).as_usize();
100         let r2 = (T::TWO * (rem % radix2)).as_usize();
101 
102         // This is always safe, since the table is 2*radix^2, and
103         // r1 and r2 must be in the range [0, 2*radix^2-1), since the maximum
104         // value of rem is `radix4-1`, which must have a div and rem
105         // in the range [0, radix^2-1).
106         *iter.next().unwrap() = index!(table[r2+1]);
107         *iter.next().unwrap() = index!(table[r2]);
108         *iter.next().unwrap() = index!(table[r1+1]);
109         *iter.next().unwrap() = index!(table[r1]);
110     }
111 
112     // Decode 2 digits at a time.
113     while value >= radix2 {
114         let rem = (T::TWO * (value % radix2)).as_usize();
115         value /= radix2;
116 
117         // This is always safe, since the table is 2*radix^2, and
118         // rem must be in the range [0, 2*radix^2-1).
119         *iter.next().unwrap() = index!(table[rem+1]);
120         *iter.next().unwrap() = index!(table[rem]);
121     }
122 
123     // Decode last 2 digits.
124     if value < radix {
125         // This is always safe, since value < radix, so it must be < 36.
126         // Digit must be <= 36.
127         *iter.next().unwrap() = digit_to_char(value);
128     } else {
129         let rem = (T::TWO * value).as_usize();
130         // This is always safe, since the table is 2*radix^2, and the value
131         // must <= radix^2, so rem must be in the range [0, 2*radix^2-1).
132         *iter.next().unwrap() = index!(table[rem+1]);
133         *iter.next().unwrap() = index!(table[rem]);
134     }
135 
136     iter.count()
137 }
138 
139 // NAIVE
140 
141 /// Naive implementation for radix-N numbers.
142 ///
143 /// Use a macro to allow for u32 or u64 to be used (u32 is generally faster).
144 ///
145 /// `value` must be non-negative and mutable.
146 #[cfg(not(feature = "table"))]
147 #[inline]
naive<T>(mut value: T, radix: T, buffer: &mut [u8]) -> usize where T: UnsignedInteger148 fn naive<T>(mut value: T, radix: T, buffer: &mut [u8])
149     -> usize
150     where T: UnsignedInteger
151 {
152     // Decode all but last digit, 1 at a time.
153     let mut iter = buffer.iter_mut().rev();
154     while value >= radix {
155         let rem = (value % radix).as_usize();
156         value /= radix;
157 
158         // This is always safe, since rem must be [0, radix).
159         *iter.next().unwrap() = digit_to_char(rem);
160     }
161 
162     // Decode last digit.
163     let rem = (value % radix).as_usize();
164     // This is always safe, since rem must be [0, radix).
165     *iter.next().unwrap() = digit_to_char(rem);
166 
167     iter.count()
168 }
169 
170 /// Forward the correct arguments to the implementation.
171 ///
172 /// Use a macro to allow for u32 or u64 to be used (u32 is generally faster).
173 ///
174 /// `value` must be non-negative and mutable.
175 #[inline]
forward<T>(value: T, radix: u32, bytes: &mut [u8]) -> usize where T: UnsignedInteger176 pub(crate) fn forward<T>(value: T, radix: u32, bytes: &mut [u8])
177     -> usize
178     where T: UnsignedInteger
179 {
180     // Check simple use-cases
181     if value == T::ZERO {
182         // We know this is safe, because we confirmed the buffer is >= 2
183         // in total (since we also handled the sign by here).
184         index_mut!(bytes[0] = b'0');
185         return 1;
186     }
187 
188     // Create a temporary buffer, and copy into it.
189     // Way faster than reversing a buffer in-place.
190     debug_assert_radix!(radix);
191     let mut buffer: [u8; BUFFER_SIZE] = explicit_uninitialized();
192 
193     let count = {
194         #[cfg(not(feature = "table"))] {
195             naive(value, as_cast(radix), &mut buffer)
196         }
197 
198         #[cfg(all(not(feature = "radix"), feature = "table"))] {
199             optimized(value, as_cast(radix), &DIGIT_TO_BASE10_SQUARED, &mut buffer)
200         }
201 
202         #[cfg(all(feature = "radix", feature = "table"))]{
203             let table: &[u8] = match radix {
204                 2   => &DIGIT_TO_BASE2_SQUARED,
205                 3   => &DIGIT_TO_BASE3_SQUARED,
206                 4   => &DIGIT_TO_BASE4_SQUARED,
207                 5   => &DIGIT_TO_BASE5_SQUARED,
208                 6   => &DIGIT_TO_BASE6_SQUARED,
209                 7   => &DIGIT_TO_BASE7_SQUARED,
210                 8   => &DIGIT_TO_BASE8_SQUARED,
211                 9   => &DIGIT_TO_BASE9_SQUARED,
212                 10  => &DIGIT_TO_BASE10_SQUARED,
213                 11  => &DIGIT_TO_BASE11_SQUARED,
214                 12  => &DIGIT_TO_BASE12_SQUARED,
215                 13  => &DIGIT_TO_BASE13_SQUARED,
216                 14  => &DIGIT_TO_BASE14_SQUARED,
217                 15  => &DIGIT_TO_BASE15_SQUARED,
218                 16  => &DIGIT_TO_BASE16_SQUARED,
219                 17  => &DIGIT_TO_BASE17_SQUARED,
220                 18  => &DIGIT_TO_BASE18_SQUARED,
221                 19  => &DIGIT_TO_BASE19_SQUARED,
222                 20  => &DIGIT_TO_BASE20_SQUARED,
223                 21  => &DIGIT_TO_BASE21_SQUARED,
224                 22  => &DIGIT_TO_BASE22_SQUARED,
225                 23  => &DIGIT_TO_BASE23_SQUARED,
226                 24  => &DIGIT_TO_BASE24_SQUARED,
227                 25  => &DIGIT_TO_BASE25_SQUARED,
228                 26  => &DIGIT_TO_BASE26_SQUARED,
229                 27  => &DIGIT_TO_BASE27_SQUARED,
230                 28  => &DIGIT_TO_BASE28_SQUARED,
231                 29  => &DIGIT_TO_BASE29_SQUARED,
232                 30  => &DIGIT_TO_BASE30_SQUARED,
233                 31  => &DIGIT_TO_BASE31_SQUARED,
234                 32  => &DIGIT_TO_BASE32_SQUARED,
235                 33  => &DIGIT_TO_BASE33_SQUARED,
236                 34  => &DIGIT_TO_BASE34_SQUARED,
237                 35  => &DIGIT_TO_BASE35_SQUARED,
238                 36  => &DIGIT_TO_BASE36_SQUARED,
239                 _   => unreachable!(),
240             };
241             optimized(value, as_cast(radix), table, &mut buffer)
242         }
243     };
244 
245     // We know that count <= buffer.len(), so we can safely extract a subslice
246     // of buffer. This is because count is generated from `buffer.iter_mut().count()`,
247     // after writing a certain number of elements, so it must be <= buffer.len().
248     debug_assert!(count <= buffer.len());
249     copy_to_dst(bytes, &index!(buffer[count..]))
250 }
251 
252 /// Sanitizer for an unsigned number-to-string implementation.
253 #[inline]
unsigned<Value, UWide>(value: Value, radix: u32, bytes: &mut [u8]) -> usize where Value: UnsignedInteger, UWide: UnsignedInteger254 pub(crate) fn unsigned<Value, UWide>(value: Value, radix: u32, bytes: &mut [u8])
255     -> usize
256     where Value: UnsignedInteger,
257           UWide: UnsignedInteger
258 {
259     // Invoke forwarder
260     let v: UWide = as_cast(value);
261     forward(v, radix, bytes)
262 }
263 
264 /// Sanitizer for an signed number-to-string implementation.
265 #[inline]
signed<Value, UWide, IWide>(value: Value, radix: u32, bytes: &mut [u8]) -> usize where Value: SignedInteger, UWide: UnsignedInteger, IWide: SignedInteger266 pub(crate) fn signed<Value, UWide, IWide>(value: Value, radix: u32, bytes: &mut [u8])
267     -> usize
268     where Value: SignedInteger,
269           UWide: UnsignedInteger,
270           IWide: SignedInteger
271 {
272     // Handle negative numbers, use an unsigned type to avoid overflow.
273     // Use a wrapping neg to allow overflow.
274     // These routines wrap on one condition, where the input number is equal
275     // to the minimum possible value of that type (for example, -128 for i8).
276     // In this case, and this case only, the value wraps to itself with
277     // `x.wrapping_neg()`, so `-128i8.wrapping_neg() == -128i8` in two's
278     // complement (the only true integer representation). Conversion of
279     // this wrapped value to an unsigned integer of the same size with
280     // effectively negates the value, for example, `-128i8 as u8 == 128u8`.
281     // Due to type widening, this wrap only occurs for `i64::min_value()`,
282     // and since it is converted to `u64`, this algorithm is correct
283     // for all numerical input values, since Rust guarantees two's
284     // complement representation for signed integers.
285     let v: UWide;
286     if value < Value::ZERO {
287         let wide: IWide = as_cast(value);
288         v = as_cast(wide.wrapping_neg());
289         // We know this is safe, because we confirmed the buffer is >= 1.
290         index_mut!(bytes[0] = b'-');
291         forward(v, radix, &mut index_mut!(bytes[1..])) + 1
292     } else {
293         v = as_cast(value);
294         forward(v, radix, bytes)
295     }
296 }
297 
298 // UNSAFE API
299 
300 /// Expand the generic unsigned itoa function for specified types.
301 macro_rules! wrap_unsigned {
302     ($name:ident, $t:ty, $uwide:ty) => (
303         /// Serialize unsigned integer and return bytes written to.
304         #[inline]
305         fn $name<'a>(value: $t, radix: u8, bytes: &'a mut [u8])
306             -> usize
307         {
308             unsigned::<$t, $uwide>(value, radix.into(), bytes)
309         }
310     )
311 }
312 
313 wrap_unsigned!(u8toa_impl, u8, u32);
314 wrap_unsigned!(u16toa_impl, u16, u32);
315 wrap_unsigned!(u32toa_impl, u32, u32);
316 wrap_unsigned!(u64toa_impl, u64, u64);
317 wrap_unsigned!(usizetoa_impl, usize, usize);
318 
319 #[cfg(has_i128)]
320 wrap_unsigned!(u128toa_impl, u128, u128);
321 
322 /// Expand the generic signed itoa function for specified types.
323 macro_rules! wrap_signed {
324     ($name:ident, $t:ty, $uwide:ty, $iwide:ty) => (
325         /// Serialize signed integer and return bytes written to.
326         #[inline]
327         fn $name<'a>(value: $t, radix: u8, bytes: &'a mut [u8])
328             -> usize
329         {
330             signed::<$t, $uwide, $iwide>(value, radix.into(), bytes)
331         }
332     )
333 }
334 
335 wrap_signed!(i8toa_impl, i8, u32, i32);
336 wrap_signed!(i16toa_impl, i16, u32, i32);
337 wrap_signed!(i32toa_impl, i32, u32, i32);
338 wrap_signed!(i64toa_impl, i64, u64, i64);
339 wrap_signed!(isizetoa_impl, isize, usize, isize);
340 
341 #[cfg(has_i128)]
342 wrap_signed!(i128toa_impl, i128, u128, i128);
343 
344 // LOW-LEVEL API
345 // -------------
346 
347 // RANGE API (FFI)
348 generate_to_range_api!(u8toa_range, u8toa_radix_range, u8, u8toa_impl, MAX_U8_SIZE);
349 generate_to_range_api!(u16toa_range, u16toa_radix_range, u16, u16toa_impl, MAX_U16_SIZE);
350 generate_to_range_api!(u32toa_range, u32toa_radix_range, u32, u32toa_impl, MAX_U32_SIZE);
351 generate_to_range_api!(u64toa_range, u64toa_radix_range, u64, u64toa_impl, MAX_U64_SIZE);
352 generate_to_range_api!(usizetoa_range, usizetoa_radix_range, usize, usizetoa_impl, MAX_USIZE_SIZE);
353 generate_to_range_api!(i8toa_range, i8toa_radix_range, i8, i8toa_impl, MAX_I8_SIZE);
354 generate_to_range_api!(i16toa_range, i16toa_radix_range, i16, i16toa_impl, MAX_I16_SIZE);
355 generate_to_range_api!(i32toa_range, i32toa_radix_range, i32, i32toa_impl, MAX_I32_SIZE);
356 generate_to_range_api!(i64toa_range, i64toa_radix_range, i64, i64toa_impl, MAX_I64_SIZE);
357 generate_to_range_api!(isizetoa_range, isizetoa_radix_range, isize, isizetoa_impl, MAX_ISIZE_SIZE);
358 
359 #[cfg(has_i128)] generate_to_range_api!(u128toa_range, u128toa_radix_range, u128, u128toa_impl, MAX_U128_SIZE);
360 #[cfg(has_i128)] generate_to_range_api!(i128toa_range, i128toa_radix_range, i128, i128toa_impl, MAX_I128_SIZE);
361 
362 // SLICE API
363 generate_to_slice_api!(u8toa_slice, u8toa_radix_slice, u8, u8toa_impl, MAX_U8_SIZE);
364 generate_to_slice_api!(u16toa_slice, u16toa_radix_slice, u16, u16toa_impl, MAX_U16_SIZE);
365 generate_to_slice_api!(u32toa_slice, u32toa_radix_slice, u32, u32toa_impl, MAX_U32_SIZE);
366 generate_to_slice_api!(u64toa_slice, u64toa_radix_slice, u64, u64toa_impl, MAX_U64_SIZE);
367 generate_to_slice_api!(usizetoa_slice, usizetoa_radix_slice, usize, usizetoa_impl, MAX_USIZE_SIZE);
368 generate_to_slice_api!(i8toa_slice, i8toa_radix_slice, i8, i8toa_impl, MAX_I8_SIZE);
369 generate_to_slice_api!(i16toa_slice, i16toa_radix_slice, i16, i16toa_impl, MAX_I16_SIZE);
370 generate_to_slice_api!(i32toa_slice, i32toa_radix_slice, i32, i32toa_impl, MAX_I32_SIZE);
371 generate_to_slice_api!(i64toa_slice, i64toa_radix_slice, i64, i64toa_impl, MAX_I64_SIZE);
372 generate_to_slice_api!(isizetoa_slice, isizetoa_radix_slice, isize, isizetoa_impl, MAX_ISIZE_SIZE);
373 
374 #[cfg(has_i128)] generate_to_slice_api!(u128toa_slice, u128toa_radix_slice, u128, u128toa_impl, MAX_U128_SIZE);
375 #[cfg(has_i128)] generate_to_slice_api!(i128toa_slice, i128toa_radix_slice, i128, i128toa_impl, MAX_I128_SIZE);
376 
377 // TESTS
378 // -----
379 
380 #[cfg(test)]
381 mod tests {
382     use atoi::*;
383     use util::test::*;
384     use super::*;
385 
386     #[test]
u8toa_test()387     fn u8toa_test() {
388         let mut buffer = new_buffer();
389         assert_eq!(b"0", u8toa_slice(0, &mut buffer));
390         assert_eq!(b"1", u8toa_slice(1, &mut buffer));
391         assert_eq!(b"127", u8toa_slice(127, &mut buffer));
392         assert_eq!(b"128", u8toa_slice(128, &mut buffer));
393         assert_eq!(b"255", u8toa_slice(255, &mut buffer));
394         assert_eq!(b"255", u8toa_slice(-1i8 as u8, &mut buffer));
395     }
396 
397     #[test]
i8toa_test()398     fn i8toa_test() {
399         let mut buffer = new_buffer();
400         assert_eq!(b"0", i8toa_slice(0, &mut buffer));
401         assert_eq!(b"1", i8toa_slice(1, &mut buffer));
402         assert_eq!(b"127", i8toa_slice(127, &mut buffer));
403         assert_eq!(b"-128", i8toa_slice(128u8 as i8, &mut buffer));
404         assert_eq!(b"-1", i8toa_slice(255u8 as i8, &mut buffer));
405         assert_eq!(b"-1", i8toa_slice(-1, &mut buffer));
406     }
407 
408     #[test]
u16toa_test()409     fn u16toa_test() {
410         let mut buffer = new_buffer();
411         assert_eq!(b"0", u16toa_slice(0, &mut buffer));
412         assert_eq!(b"1", u16toa_slice(1, &mut buffer));
413         assert_eq!(b"32767", u16toa_slice(32767, &mut buffer));
414         assert_eq!(b"32768", u16toa_slice(32768, &mut buffer));
415         assert_eq!(b"65535", u16toa_slice(65535, &mut buffer));
416         assert_eq!(b"65535", u16toa_slice(-1i16 as u16, &mut buffer));
417     }
418 
419     #[test]
i16toa_test()420     fn i16toa_test() {
421         let mut buffer = new_buffer();
422         assert_eq!(b"0", i16toa_slice(0, &mut buffer));
423         assert_eq!(b"1", i16toa_slice(1, &mut buffer));
424         assert_eq!(b"32767", i16toa_slice(32767, &mut buffer));
425         assert_eq!(b"-32768", i16toa_slice(32768u16 as i16, &mut buffer));
426         assert_eq!(b"-1", i16toa_slice(65535u16 as i16, &mut buffer));
427         assert_eq!(b"-1", i16toa_slice(-1, &mut buffer));
428     }
429 
430     #[test]
u32toa_test()431     fn u32toa_test() {
432         let mut buffer = new_buffer();
433         assert_eq!(b"0", u32toa_slice(0, &mut buffer));
434         assert_eq!(b"1", u32toa_slice(1, &mut buffer));
435         assert_eq!(b"2147483647", u32toa_slice(2147483647, &mut buffer));
436         assert_eq!(b"2147483648", u32toa_slice(2147483648, &mut buffer));
437         assert_eq!(b"4294967295", u32toa_slice(4294967295, &mut buffer));
438         assert_eq!(b"4294967295", u32toa_slice(-1i32 as u32, &mut buffer));
439     }
440 
441     #[test]
i32toa_test()442     fn i32toa_test() {
443         let mut buffer = new_buffer();
444         assert_eq!(b"0", i32toa_slice(0, &mut buffer));
445         assert_eq!(b"1", i32toa_slice(1, &mut buffer));
446         assert_eq!(b"2147483647", i32toa_slice(2147483647, &mut buffer));
447         assert_eq!(b"-2147483648", i32toa_slice(2147483648u32 as i32, &mut buffer));
448         assert_eq!(b"-1", i32toa_slice(4294967295u32 as i32, &mut buffer));
449         assert_eq!(b"-1", i32toa_slice(-1, &mut buffer));
450     }
451 
452     #[test]
u64toa_test()453     fn u64toa_test() {
454         let mut buffer = new_buffer();
455         assert_eq!(b"0", u64toa_slice(0, &mut buffer));
456         assert_eq!(b"1", u64toa_slice(1, &mut buffer));
457         assert_eq!(b"9223372036854775807", u64toa_slice(9223372036854775807, &mut buffer));
458         assert_eq!(b"9223372036854775808", u64toa_slice(9223372036854775808, &mut buffer));
459         assert_eq!(b"18446744073709551615", u64toa_slice(18446744073709551615, &mut buffer));
460         assert_eq!(b"18446744073709551615", u64toa_slice(-1i64 as u64, &mut buffer));
461     }
462 
463     #[test]
i64toa_test()464     fn i64toa_test() {
465         let mut buffer = new_buffer();
466         assert_eq!(b"0", i64toa_slice(0, &mut buffer));
467         assert_eq!(b"1", i64toa_slice(1, &mut buffer));
468         assert_eq!(b"9223372036854775807", i64toa_slice(9223372036854775807, &mut buffer));
469         assert_eq!(b"-9223372036854775808", i64toa_slice(9223372036854775808u64 as i64, &mut buffer));
470         assert_eq!(b"-1", i64toa_slice(18446744073709551615u64 as i64, &mut buffer));
471         assert_eq!(b"-1", i64toa_slice(-1, &mut buffer));
472     }
473 
474     #[cfg(feature = "radix")]
475     #[test]
basen_test()476     fn basen_test() {
477         let data = [
478             (2, "100101"),
479             (3, "1101"),
480             (4, "211"),
481             (5, "122"),
482             (6, "101"),
483             (7, "52"),
484             (8, "45"),
485             (9, "41"),
486             (10, "37"),
487             (11, "34"),
488             (12, "31"),
489             (13, "2B"),
490             (14, "29"),
491             (15, "27"),
492             (16, "25"),
493             (17, "23"),
494             (18, "21"),
495             (19, "1I"),
496             (20, "1H"),
497             (21, "1G"),
498             (22, "1F"),
499             (23, "1E"),
500             (24, "1D"),
501             (25, "1C"),
502             (26, "1B"),
503             (27, "1A"),
504             (28, "19"),
505             (29, "18"),
506             (30, "17"),
507             (31, "16"),
508             (32, "15"),
509             (33, "14"),
510             (34, "13"),
511             (35, "12"),
512             (36, "11"),
513         ];
514 
515         let mut buffer = new_buffer();
516         for (base, expected) in data.iter() {
517             assert_eq!(expected.as_bytes(), i8toa_radix_slice(37, *base, &mut buffer));
518         }
519     }
520 
521     quickcheck! {
522         fn u8_quickcheck(i: u8) -> bool {
523             let mut buffer = new_buffer();
524             i == atou8_slice(u8toa_slice(i, &mut buffer))
525         }
526 
527         fn u16_quickcheck(i: u16) -> bool {
528             let mut buffer = new_buffer();
529             i == atou16_slice(u16toa_slice(i, &mut buffer))
530         }
531 
532         fn u32_quickcheck(i: u32) -> bool {
533             let mut buffer = new_buffer();
534             i == atou32_slice(u32toa_slice(i, &mut buffer))
535         }
536 
537         fn u64_quickcheck(i: u64) -> bool {
538             let mut buffer = new_buffer();
539             i == atou64_slice(u64toa_slice(i, &mut buffer))
540         }
541 
542         fn usize_quickcheck(i: usize) -> bool {
543             let mut buffer = new_buffer();
544             i == atousize_slice(usizetoa_slice(i, &mut buffer))
545         }
546 
547         fn i8_quickcheck(i: i8) -> bool {
548             let mut buffer = new_buffer();
549             i == atoi8_slice(i8toa_slice(i, &mut buffer))
550         }
551 
552         fn i16_quickcheck(i: i16) -> bool {
553             let mut buffer = new_buffer();
554             i == atoi16_slice(i16toa_slice(i, &mut buffer))
555         }
556 
557         fn i32_quickcheck(i: i32) -> bool {
558             let mut buffer = new_buffer();
559             i == atoi32_slice(i32toa_slice(i, &mut buffer))
560         }
561 
562         fn i64_quickcheck(i: i64) -> bool {
563             let mut buffer = new_buffer();
564             i == atoi64_slice(i64toa_slice(i, &mut buffer))
565         }
566 
567         fn isize_quickcheck(i: isize) -> bool {
568             let mut buffer = new_buffer();
569             i == atoisize_slice(isizetoa_slice(i, &mut buffer))
570         }
571     }
572 
573     proptest! {
574         #[test]
575         fn u8_proptest(i in u8::min_value()..u8::max_value()) {
576             let mut buffer = new_buffer();
577             i == atou8_slice(u8toa_slice(i, &mut buffer))
578         }
579 
580         #[test]
581         fn i8_proptest(i in i8::min_value()..i8::max_value()) {
582             let mut buffer = new_buffer();
583             i == atoi8_slice(i8toa_slice(i, &mut buffer))
584         }
585 
586         #[test]
587         fn u16_proptest(i in u16::min_value()..u16::max_value()) {
588             let mut buffer = new_buffer();
589             i == atou16_slice(u16toa_slice(i, &mut buffer))
590         }
591 
592         #[test]
593         fn i16_proptest(i in i16::min_value()..i16::max_value()) {
594             let mut buffer = new_buffer();
595             i == atoi16_slice(i16toa_slice(i, &mut buffer))
596         }
597 
598         #[test]
599         fn u32_proptest(i in u32::min_value()..u32::max_value()) {
600             let mut buffer = new_buffer();
601             i == atou32_slice(u32toa_slice(i, &mut buffer))
602         }
603 
604         #[test]
605         fn i32_proptest(i in i32::min_value()..i32::max_value()) {
606             let mut buffer = new_buffer();
607             i == atoi32_slice(i32toa_slice(i, &mut buffer))
608         }
609 
610         #[test]
611         fn u64_proptest(i in u64::min_value()..u64::max_value()) {
612             let mut buffer = new_buffer();
613             i == atou64_slice(u64toa_slice(i, &mut buffer))
614         }
615 
616         #[test]
617         fn i64_proptest(i in i64::min_value()..i64::max_value()) {
618             let mut buffer = new_buffer();
619             i == atoi64_slice(i64toa_slice(i, &mut buffer))
620         }
621 
622         #[test]
623         fn u128_proptest(i in u128::min_value()..u128::max_value()) {
624             let mut buffer = new_buffer();
625             i == atou128_slice(u128toa_slice(i, &mut buffer))
626         }
627 
628         #[test]
629         fn i128_proptest(i in i128::min_value()..i128::max_value()) {
630             let mut buffer = new_buffer();
631             i == atoi128_slice(i128toa_slice(i, &mut buffer))
632         }
633 
634         #[test]
635         fn usize_proptest(i in usize::min_value()..usize::max_value()) {
636             let mut buffer = new_buffer();
637             i == atousize_slice(usizetoa_slice(i, &mut buffer))
638         }
639 
640         #[test]
641         fn isize_proptest(i in isize::min_value()..isize::max_value()) {
642             let mut buffer = new_buffer();
643             i == atoisize_slice(isizetoa_slice(i, &mut buffer))
644         }
645     }
646 
647     #[test]
648     #[should_panic]
i8toa_buffer_test()649     fn i8toa_buffer_test() {
650         let mut buffer = [b'0'; MAX_I8_SIZE-1];
651         i8toa_slice(12, &mut buffer);
652     }
653 
654     #[test]
655     #[should_panic]
i16toa_buffer_test()656     fn i16toa_buffer_test() {
657         let mut buffer = [b'0'; MAX_I16_SIZE-1];
658         i16toa_slice(12, &mut buffer);
659     }
660 
661     #[test]
662     #[should_panic]
i32toa_buffer_test()663     fn i32toa_buffer_test() {
664         let mut buffer = [b'0'; MAX_I32_SIZE-1];
665         i32toa_slice(12, &mut buffer);
666     }
667 
668     #[test]
669     #[should_panic]
i64toa_buffer_test()670     fn i64toa_buffer_test() {
671         let mut buffer = [b'0'; MAX_I64_SIZE-1];
672         i64toa_slice(12, &mut buffer);
673     }
674 
675     #[test]
676     #[should_panic]
i128toa_buffer_test()677     fn i128toa_buffer_test() {
678         let mut buffer = [b'0'; MAX_I128_SIZE-1];
679         i128toa_slice(12, &mut buffer);
680     }
681 
682     #[test]
683     #[should_panic]
isizetoa_buffer_test()684     fn isizetoa_buffer_test() {
685         let mut buffer = [b'0'; MAX_ISIZE_SIZE-1];
686         isizetoa_slice(12, &mut buffer);
687     }
688 
689     #[test]
690     #[should_panic]
u8toa_buffer_test()691     fn u8toa_buffer_test() {
692         let mut buffer = [b'0'; MAX_U8_SIZE-1];
693         i8toa_slice(12, &mut buffer);
694     }
695 
696     #[test]
697     #[should_panic]
u16toa_buffer_test()698     fn u16toa_buffer_test() {
699         let mut buffer = [b'0'; MAX_U16_SIZE-1];
700         i16toa_slice(12, &mut buffer);
701     }
702 
703     #[test]
704     #[should_panic]
u32toa_buffer_test()705     fn u32toa_buffer_test() {
706         let mut buffer = [b'0'; MAX_U32_SIZE-1];
707         i32toa_slice(12, &mut buffer);
708     }
709 
710     #[test]
711     #[should_panic]
u64toa_buffer_test()712     fn u64toa_buffer_test() {
713         let mut buffer = [b'0'; MAX_U64_SIZE-1];
714         i64toa_slice(12, &mut buffer);
715     }
716 
717     #[test]
718     #[should_panic]
u128toa_buffer_test()719     fn u128toa_buffer_test() {
720         let mut buffer = [b'0'; MAX_U128_SIZE-1];
721         i128toa_slice(12, &mut buffer);
722     }
723 
724     #[test]
725     #[should_panic]
usizetoa_buffer_test()726     fn usizetoa_buffer_test() {
727         let mut buffer = [b'0'; MAX_USIZE_SIZE-1];
728         usizetoa_slice(12, &mut buffer);
729     }
730 }
731