|
1 | 1 | #![cfg_attr(not(feature = "std"), no_std)]
|
2 | 2 |
|
3 |
| -use core::fmt; |
| 3 | +use core::fmt::{self, Write}; |
4 | 4 | use core::iter::Sum;
|
5 | 5 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
6 | 6 |
|
@@ -1072,6 +1072,7 @@ impl Bandwidth {
|
1072 | 1072 | }
|
1073 | 1073 | }
|
1074 | 1074 |
|
| 1075 | +#[rustversion::before(1.67)] |
1075 | 1076 | impl fmt::Debug for Bandwidth {
|
1076 | 1077 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
1077 | 1078 | /// Formats a floating point number in decimal notation.
|
@@ -1197,3 +1198,196 @@ impl fmt::Debug for Bandwidth {
|
1197 | 1198 | }
|
1198 | 1199 | }
|
1199 | 1200 | }
|
| 1201 | + |
| 1202 | +#[rustversion::since(1.67)] |
| 1203 | +impl fmt::Debug for Bandwidth { |
| 1204 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 1205 | + /// Formats a floating point number in decimal notation. |
| 1206 | + /// |
| 1207 | + /// The number is given as the `integer_part` and a fractional part. |
| 1208 | + /// The value of the fractional part is `fractional_part / divisor`. So |
| 1209 | + /// `integer_part` = 3, `fractional_part` = 12 and `divisor` = 100 |
| 1210 | + /// represents the number `3.012`. Trailing zeros are omitted. |
| 1211 | + /// |
| 1212 | + /// `divisor` must not be above 100_000_000. It also should be a power |
| 1213 | + /// of 10, everything else doesn't make sense. `fractional_part` has |
| 1214 | + /// to be less than `10 * divisor`! |
| 1215 | + /// |
| 1216 | + /// A prefix and postfix may be added. The whole thing is padded |
| 1217 | + /// to the formatter's `width`, if specified. |
| 1218 | + fn fmt_decimal( |
| 1219 | + f: &mut fmt::Formatter<'_>, |
| 1220 | + integer_part: u64, |
| 1221 | + mut fractional_part: u32, |
| 1222 | + mut divisor: u32, |
| 1223 | + prefix: &str, |
| 1224 | + postfix: &str, |
| 1225 | + ) -> fmt::Result { |
| 1226 | + // Encode the fractional part into a temporary buffer. The buffer |
| 1227 | + // only need to hold 9 elements, because `fractional_part` has to |
| 1228 | + // be smaller than 10^9. The buffer is prefilled with '0' digits |
| 1229 | + // to simplify the code below. |
| 1230 | + let mut buf = [b'0'; 9]; |
| 1231 | + |
| 1232 | + // The next digit is written at this position |
| 1233 | + let mut pos = 0; |
| 1234 | + |
| 1235 | + // We keep writing digits into the buffer while there are non-zero |
| 1236 | + // digits left and we haven't written enough digits yet. |
| 1237 | + while fractional_part > 0 && pos < f.precision().unwrap_or(9) { |
| 1238 | + // Write new digit into the buffer |
| 1239 | + buf[pos] = b'0' + (fractional_part / divisor) as u8; |
| 1240 | + |
| 1241 | + fractional_part %= divisor; |
| 1242 | + divisor /= 10; |
| 1243 | + pos += 1; |
| 1244 | + } |
| 1245 | + |
| 1246 | + // If a precision < 9 was specified, there may be some non-zero |
| 1247 | + // digits left that weren't written into the buffer. In that case we |
| 1248 | + // need to perform rounding to match the semantics of printing |
| 1249 | + // normal floating point numbers. However, we only need to do work |
| 1250 | + // when rounding up. This happens if the first digit of the |
| 1251 | + // remaining ones is >= 5. |
| 1252 | + let integer_part = if fractional_part > 0 && fractional_part >= divisor * 5 { |
| 1253 | + // Round up the number contained in the buffer. We go through |
| 1254 | + // the buffer backwards and keep track of the carry. |
| 1255 | + let mut rev_pos = pos; |
| 1256 | + let mut carry = true; |
| 1257 | + while carry && rev_pos > 0 { |
| 1258 | + rev_pos -= 1; |
| 1259 | + |
| 1260 | + // If the digit in the buffer is not '9', we just need to |
| 1261 | + // increment it and can stop then (since we don't have a |
| 1262 | + // carry anymore). Otherwise, we set it to '0' (overflow) |
| 1263 | + // and continue. |
| 1264 | + if buf[rev_pos] < b'9' { |
| 1265 | + buf[rev_pos] += 1; |
| 1266 | + carry = false; |
| 1267 | + } else { |
| 1268 | + buf[rev_pos] = b'0'; |
| 1269 | + } |
| 1270 | + } |
| 1271 | + |
| 1272 | + // If we still have the carry bit set, that means that we set |
| 1273 | + // the whole buffer to '0's and need to increment the integer |
| 1274 | + // part. |
| 1275 | + if carry { |
| 1276 | + // If `integer_part == u64::MAX` and precision < 9, any |
| 1277 | + // carry of the overflow during rounding of the |
| 1278 | + // `fractional_part` into the `integer_part` will cause the |
| 1279 | + // `integer_part` itself to overflow. Avoid this by using an |
| 1280 | + // `Option<u64>`, with `None` representing `u64::MAX + 1`. |
| 1281 | + integer_part.checked_add(1) |
| 1282 | + } else { |
| 1283 | + Some(integer_part) |
| 1284 | + } |
| 1285 | + } else { |
| 1286 | + Some(integer_part) |
| 1287 | + }; |
| 1288 | + |
| 1289 | + // Determine the end of the buffer: if precision is set, we just |
| 1290 | + // use as many digits from the buffer (capped to 9). If it isn't |
| 1291 | + // set, we only use all digits up to the last non-zero one. |
| 1292 | + let end = f.precision().map(|p| core::cmp::min(p, 9)).unwrap_or(pos); |
| 1293 | + |
| 1294 | + // This closure emits the formatted duration without emitting any |
| 1295 | + // padding (padding is calculated below). |
| 1296 | + let emit_without_padding = |f: &mut fmt::Formatter<'_>| { |
| 1297 | + if let Some(integer_part) = integer_part { |
| 1298 | + write!(f, "{prefix}{integer_part}")?; |
| 1299 | + } else { |
| 1300 | + // u64::MAX + 1 == 18446744073709551616 |
| 1301 | + write!(f, "{prefix}18446744073709551616")?; |
| 1302 | + } |
| 1303 | + |
| 1304 | + // Write the decimal point and the fractional part (if any). |
| 1305 | + if end > 0 { |
| 1306 | + // SAFETY: We are only writing ASCII digits into the buffer and |
| 1307 | + // it was initialized with '0's, so it contains valid UTF8. |
| 1308 | + let s = unsafe { core::str::from_utf8_unchecked(&buf[..end]) }; |
| 1309 | + |
| 1310 | + // If the user request a precision > 9, we pad '0's at the end. |
| 1311 | + let w = f.precision().unwrap_or(pos); |
| 1312 | + write!(f, ".{s:0<w$}")?; |
| 1313 | + } |
| 1314 | + |
| 1315 | + write!(f, "{postfix}") |
| 1316 | + }; |
| 1317 | + |
| 1318 | + match f.width() { |
| 1319 | + None => { |
| 1320 | + // No `width` specified. There's no need to calculate the |
| 1321 | + // length of the output in this case, just emit it. |
| 1322 | + emit_without_padding(f) |
| 1323 | + } |
| 1324 | + Some(requested_w) => { |
| 1325 | + // A `width` was specified. Calculate the actual width of |
| 1326 | + // the output in order to calculate the required padding. |
| 1327 | + // It consists of 4 parts: |
| 1328 | + // 1. The prefix: is either "+" or "", so we can just use len(). |
| 1329 | + // 2. The postfix: can be "µs" so we have to count UTF8 characters. |
| 1330 | + let mut actual_w = prefix.len() + postfix.chars().count(); |
| 1331 | + // 3. The integer part: |
| 1332 | + if let Some(integer_part) = integer_part { |
| 1333 | + if let Some(log) = integer_part.checked_ilog10() { |
| 1334 | + // integer_part is > 0, so has length log10(x)+1 |
| 1335 | + actual_w += 1 + log as usize; |
| 1336 | + } else { |
| 1337 | + // integer_part is 0, so has length 1. |
| 1338 | + actual_w += 1; |
| 1339 | + } |
| 1340 | + } else { |
| 1341 | + // integer_part is u64::MAX + 1, so has length 20 |
| 1342 | + actual_w += 20; |
| 1343 | + } |
| 1344 | + // 4. The fractional part (if any): |
| 1345 | + if end > 0 { |
| 1346 | + let frac_part_w = f.precision().unwrap_or(pos); |
| 1347 | + actual_w += 1 + frac_part_w; |
| 1348 | + } |
| 1349 | + |
| 1350 | + if requested_w <= actual_w { |
| 1351 | + // Output is already longer than `width`, so don't pad. |
| 1352 | + emit_without_padding(f) |
| 1353 | + } else { |
| 1354 | + // We need to add padding. |
| 1355 | + let post_padding_len = requested_w - actual_w; |
| 1356 | + emit_without_padding(f)?; |
| 1357 | + for _ in 0..post_padding_len { |
| 1358 | + f.write_char(f.fill())?; |
| 1359 | + } |
| 1360 | + Ok(()) |
| 1361 | + } |
| 1362 | + } |
| 1363 | + } |
| 1364 | + } |
| 1365 | + |
| 1366 | + // Print leading '+' sign if requested |
| 1367 | + let prefix = if f.sign_plus() { "+" } else { "" }; |
| 1368 | + |
| 1369 | + if self.gbps > 0 { |
| 1370 | + fmt_decimal(f, self.gbps, self.bps.0, BPS_PER_GBPS / 10, prefix, "gbps") |
| 1371 | + } else if self.bps.0 >= BPS_PER_MBPS { |
| 1372 | + fmt_decimal( |
| 1373 | + f, |
| 1374 | + (self.bps.0 / BPS_PER_MBPS) as u64, |
| 1375 | + self.bps.0 % BPS_PER_MBPS, |
| 1376 | + BPS_PER_MBPS / 10, |
| 1377 | + prefix, |
| 1378 | + "mbps", |
| 1379 | + ) |
| 1380 | + } else if self.bps.0 >= BPS_PER_KBPS { |
| 1381 | + fmt_decimal( |
| 1382 | + f, |
| 1383 | + (self.bps.0 / BPS_PER_KBPS) as u64, |
| 1384 | + self.bps.0 % BPS_PER_KBPS, |
| 1385 | + BPS_PER_KBPS / 10, |
| 1386 | + prefix, |
| 1387 | + "kbps", |
| 1388 | + ) |
| 1389 | + } else { |
| 1390 | + fmt_decimal(f, self.bps.0 as u64, 0, 1, prefix, "bps") |
| 1391 | + } |
| 1392 | + } |
| 1393 | +} |
0 commit comments