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