]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Integer and floating-point number formatting | |
12 | ||
9cc50fc6 SL |
13 | #![allow(deprecated)] |
14 | ||
1a4d82fc JJ |
15 | // FIXME: #6220 Implement floating point formatting |
16 | ||
e9174d1e | 17 | use prelude::v1::*; |
9346a6ac | 18 | |
1a4d82fc | 19 | use fmt; |
9346a6ac AL |
20 | use num::Zero; |
21 | use ops::{Div, Rem, Sub}; | |
1a4d82fc | 22 | use str; |
c1a9b12d SL |
23 | use slice; |
24 | use ptr; | |
25 | use mem; | |
1a4d82fc | 26 | |
9346a6ac AL |
27 | #[doc(hidden)] |
28 | trait Int: Zero + PartialEq + PartialOrd + Div<Output=Self> + Rem<Output=Self> + | |
29 | Sub<Output=Self> + Copy { | |
30 | fn from_u8(u: u8) -> Self; | |
31 | fn to_u8(&self) -> u8; | |
c1a9b12d SL |
32 | fn to_u32(&self) -> u32; |
33 | fn to_u64(&self) -> u64; | |
9346a6ac AL |
34 | } |
35 | ||
36 | macro_rules! doit { | |
37 | ($($t:ident)*) => ($(impl Int for $t { | |
38 | fn from_u8(u: u8) -> $t { u as $t } | |
39 | fn to_u8(&self) -> u8 { *self as u8 } | |
c1a9b12d SL |
40 | fn to_u32(&self) -> u32 { *self as u32 } |
41 | fn to_u64(&self) -> u64 { *self as u64 } | |
9346a6ac AL |
42 | })*) |
43 | } | |
44 | doit! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } | |
45 | ||
1a4d82fc JJ |
46 | /// A type that represents a specific radix |
47 | #[doc(hidden)] | |
48 | trait GenericRadix { | |
49 | /// The number of digits. | |
50 | fn base(&self) -> u8; | |
51 | ||
52 | /// A radix-specific prefix string. | |
b039eaaf SL |
53 | fn prefix(&self) -> &'static str { |
54 | "" | |
55 | } | |
1a4d82fc JJ |
56 | |
57 | /// Converts an integer to corresponding radix digit. | |
58 | fn digit(&self, x: u8) -> u8; | |
59 | ||
60 | /// Format an integer using the radix using a formatter. | |
61 | fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result { | |
62 | // The radix can be as low as 2, so we need a buffer of at least 64 | |
63 | // characters for a base 2 number. | |
9346a6ac | 64 | let zero = T::zero(); |
9cc50fc6 | 65 | let is_nonnegative = x >= zero; |
c34b1796 | 66 | let mut buf = [0; 64]; |
1a4d82fc | 67 | let mut curr = buf.len(); |
9346a6ac | 68 | let base = T::from_u8(self.base()); |
9cc50fc6 | 69 | if is_nonnegative { |
1a4d82fc JJ |
70 | // Accumulate each digit of the number from the least significant |
71 | // to the most significant figure. | |
72 | for byte in buf.iter_mut().rev() { | |
9346a6ac AL |
73 | let n = x % base; // Get the current place value. |
74 | x = x / base; // Deaccumulate the number. | |
75 | *byte = self.digit(n.to_u8()); // Store the digit in the buffer. | |
1a4d82fc | 76 | curr -= 1; |
b039eaaf SL |
77 | if x == zero { |
78 | // No more digits left to accumulate. | |
79 | break | |
80 | }; | |
1a4d82fc JJ |
81 | } |
82 | } else { | |
83 | // Do the same as above, but accounting for two's complement. | |
84 | for byte in buf.iter_mut().rev() { | |
9346a6ac AL |
85 | let n = zero - (x % base); // Get the current place value. |
86 | x = x / base; // Deaccumulate the number. | |
87 | *byte = self.digit(n.to_u8()); // Store the digit in the buffer. | |
1a4d82fc | 88 | curr -= 1; |
b039eaaf SL |
89 | if x == zero { |
90 | // No more digits left to accumulate. | |
91 | break | |
92 | }; | |
1a4d82fc JJ |
93 | } |
94 | } | |
95 | let buf = unsafe { str::from_utf8_unchecked(&buf[curr..]) }; | |
9cc50fc6 | 96 | f.pad_integral(is_nonnegative, self.prefix(), buf) |
1a4d82fc JJ |
97 | } |
98 | } | |
99 | ||
100 | /// A binary (base 2) radix | |
101 | #[derive(Clone, PartialEq)] | |
102 | struct Binary; | |
103 | ||
104 | /// An octal (base 8) radix | |
105 | #[derive(Clone, PartialEq)] | |
106 | struct Octal; | |
107 | ||
108 | /// A decimal (base 10) radix | |
109 | #[derive(Clone, PartialEq)] | |
110 | struct Decimal; | |
111 | ||
112 | /// A hexadecimal (base 16) radix, formatted with lower-case characters | |
113 | #[derive(Clone, PartialEq)] | |
114 | struct LowerHex; | |
115 | ||
116 | /// A hexadecimal (base 16) radix, formatted with upper-case characters | |
117 | #[derive(Clone, PartialEq)] | |
c34b1796 | 118 | struct UpperHex; |
1a4d82fc JJ |
119 | |
120 | macro_rules! radix { | |
121 | ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { | |
122 | impl GenericRadix for $T { | |
123 | fn base(&self) -> u8 { $base } | |
124 | fn prefix(&self) -> &'static str { $prefix } | |
125 | fn digit(&self, x: u8) -> u8 { | |
126 | match x { | |
127 | $($x => $conv,)+ | |
128 | x => panic!("number not in the range 0..{}: {}", self.base() - 1, x), | |
129 | } | |
130 | } | |
131 | } | |
132 | } | |
133 | } | |
134 | ||
135 | radix! { Binary, 2, "0b", x @ 0 ... 2 => b'0' + x } | |
136 | radix! { Octal, 8, "0o", x @ 0 ... 7 => b'0' + x } | |
137 | radix! { Decimal, 10, "", x @ 0 ... 9 => b'0' + x } | |
138 | radix! { LowerHex, 16, "0x", x @ 0 ... 9 => b'0' + x, | |
139 | x @ 10 ... 15 => b'a' + (x - 10) } | |
140 | radix! { UpperHex, 16, "0x", x @ 0 ... 9 => b'0' + x, | |
141 | x @ 10 ... 15 => b'A' + (x - 10) } | |
142 | ||
1a4d82fc JJ |
143 | macro_rules! int_base { |
144 | ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { | |
85aaf69f | 145 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
146 | impl fmt::$Trait for $T { |
147 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
148 | $Radix.fmt_int(*self as $U, f) | |
149 | } | |
150 | } | |
151 | } | |
152 | } | |
153 | ||
c34b1796 AL |
154 | macro_rules! debug { |
155 | ($T:ident) => { | |
85aaf69f SL |
156 | #[stable(feature = "rust1", since = "1.0.0")] |
157 | impl fmt::Debug for $T { | |
1a4d82fc | 158 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
85aaf69f | 159 | fmt::Display::fmt(self, f) |
1a4d82fc JJ |
160 | } |
161 | } | |
162 | } | |
163 | } | |
c1a9b12d | 164 | |
1a4d82fc JJ |
165 | macro_rules! integer { |
166 | ($Int:ident, $Uint:ident) => { | |
1a4d82fc JJ |
167 | int_base! { Binary for $Int as $Uint -> Binary } |
168 | int_base! { Octal for $Int as $Uint -> Octal } | |
169 | int_base! { LowerHex for $Int as $Uint -> LowerHex } | |
170 | int_base! { UpperHex for $Int as $Uint -> UpperHex } | |
c34b1796 | 171 | debug! { $Int } |
1a4d82fc | 172 | |
1a4d82fc JJ |
173 | int_base! { Binary for $Uint as $Uint -> Binary } |
174 | int_base! { Octal for $Uint as $Uint -> Octal } | |
175 | int_base! { LowerHex for $Uint as $Uint -> LowerHex } | |
176 | int_base! { UpperHex for $Uint as $Uint -> UpperHex } | |
c34b1796 | 177 | debug! { $Uint } |
1a4d82fc JJ |
178 | } |
179 | } | |
c34b1796 | 180 | integer! { isize, usize } |
1a4d82fc JJ |
181 | integer! { i8, u8 } |
182 | integer! { i16, u16 } | |
183 | integer! { i32, u32 } | |
184 | integer! { i64, u64 } | |
c1a9b12d SL |
185 | |
186 | const DEC_DIGITS_LUT: &'static[u8] = | |
187 | b"0001020304050607080910111213141516171819\ | |
188 | 2021222324252627282930313233343536373839\ | |
189 | 4041424344454647484950515253545556575859\ | |
190 | 6061626364656667686970717273747576777879\ | |
191 | 8081828384858687888990919293949596979899"; | |
192 | ||
193 | macro_rules! impl_Display { | |
194 | ($($t:ident),*: $conv_fn:ident) => ($( | |
92a42be0 | 195 | #[stable(feature = "rust1", since = "1.0.0")] |
c1a9b12d SL |
196 | impl fmt::Display for $t { |
197 | #[allow(unused_comparisons)] | |
198 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
9cc50fc6 SL |
199 | let is_nonnegative = *self >= 0; |
200 | let mut n = if is_nonnegative { | |
c1a9b12d SL |
201 | self.$conv_fn() |
202 | } else { | |
203 | // convert the negative num to positive by summing 1 to it's 2 complement | |
204 | (!self.$conv_fn()).wrapping_add(1) | |
205 | }; | |
206 | let mut buf: [u8; 20] = unsafe { mem::uninitialized() }; | |
207 | let mut curr = buf.len() as isize; | |
208 | let buf_ptr = buf.as_mut_ptr(); | |
209 | let lut_ptr = DEC_DIGITS_LUT.as_ptr(); | |
210 | ||
211 | unsafe { | |
212 | // eagerly decode 4 characters at a time | |
213 | if <$t>::max_value() as u64 >= 10000 { | |
214 | while n >= 10000 { | |
215 | let rem = (n % 10000) as isize; | |
216 | n /= 10000; | |
217 | ||
218 | let d1 = (rem / 100) << 1; | |
219 | let d2 = (rem % 100) << 1; | |
220 | curr -= 4; | |
221 | ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); | |
222 | ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); | |
223 | } | |
224 | } | |
225 | ||
226 | // if we reach here numbers are <= 9999, so at most 4 chars long | |
227 | let mut n = n as isize; // possibly reduce 64bit math | |
228 | ||
229 | // decode 2 more chars, if > 2 chars | |
230 | if n >= 100 { | |
231 | let d1 = (n % 100) << 1; | |
232 | n /= 100; | |
233 | curr -= 2; | |
234 | ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); | |
235 | } | |
236 | ||
237 | // decode last 1 or 2 chars | |
238 | if n < 10 { | |
239 | curr -= 1; | |
240 | *buf_ptr.offset(curr) = (n as u8) + 48; | |
241 | } else { | |
242 | let d1 = n << 1; | |
243 | curr -= 2; | |
244 | ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); | |
245 | } | |
246 | } | |
247 | ||
248 | let buf_slice = unsafe { | |
249 | str::from_utf8_unchecked( | |
250 | slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)) | |
251 | }; | |
9cc50fc6 | 252 | f.pad_integral(is_nonnegative, "", buf_slice) |
c1a9b12d SL |
253 | } |
254 | })*); | |
255 | } | |
256 | ||
257 | impl_Display!(i8, u8, i16, u16, i32, u32: to_u32); | |
258 | impl_Display!(i64, u64: to_u64); | |
259 | #[cfg(target_pointer_width = "32")] | |
260 | impl_Display!(isize, usize: to_u32); | |
261 | #[cfg(target_pointer_width = "64")] | |
262 | impl_Display!(isize, usize: to_u64); |