jiff/fmt/
util.rs

1use crate::{
2    error::{err, ErrorContext},
3    fmt::Parsed,
4    util::{escape, parse, rangeint::RFrom, t},
5    Error, SignedDuration, Span, Unit,
6};
7
8/// A simple formatter for converting `i64` values to ASCII byte strings.
9///
10/// This avoids going through the formatting machinery which seems to
11/// substantially slow things down.
12///
13/// The `itoa` crate does the same thing as this formatter, but is a bit
14/// faster. We roll our own which is a bit slower, but gets us enough of a win
15/// to be satisfied with and with (almost) pure safe code.
16///
17/// By default, this only includes the sign if it's negative. To always include
18/// the sign, set `force_sign` to `true`.
19#[derive(Clone, Copy, Debug)]
20pub(crate) struct DecimalFormatter {
21    force_sign: Option<bool>,
22    minimum_digits: u8,
23    padding_byte: u8,
24}
25
26impl DecimalFormatter {
27    /// Creates a new decimal formatter using the default configuration.
28    pub(crate) const fn new() -> DecimalFormatter {
29        DecimalFormatter {
30            force_sign: None,
31            minimum_digits: 0,
32            padding_byte: b'0',
33        }
34    }
35
36    /// Format the given value using this configuration as a decimal ASCII
37    /// number.
38    #[cfg(test)]
39    pub(crate) const fn format(&self, value: i64) -> Decimal {
40        Decimal::new(self, value)
41    }
42
43    /// Forces the sign to be rendered, even if it's positive.
44    ///
45    /// When `zero_is_positive` is true, then a zero value is formatted with a
46    /// positive sign. Otherwise, it is formatted with a negative sign.
47    #[cfg(test)]
48    pub(crate) const fn force_sign(
49        self,
50        zero_is_positive: bool,
51    ) -> DecimalFormatter {
52        DecimalFormatter { force_sign: Some(zero_is_positive), ..self }
53    }
54
55    /// The minimum number of digits/padding that this number should be
56    /// formatted with. If the number would have fewer digits than this, then
57    /// it is padded out with the padding byte (which is zero by default) until
58    /// the minimum is reached.
59    ///
60    /// The minimum number of digits is capped at the maximum number of digits
61    /// for an i64 value (which is 19).
62    pub(crate) const fn padding(self, mut digits: u8) -> DecimalFormatter {
63        if digits > Decimal::MAX_I64_DIGITS {
64            digits = Decimal::MAX_I64_DIGITS;
65        }
66        DecimalFormatter { minimum_digits: digits, ..self }
67    }
68
69    /// The padding byte to use when `padding` is set.
70    ///
71    /// The default is `0`.
72    pub(crate) const fn padding_byte(self, byte: u8) -> DecimalFormatter {
73        DecimalFormatter { padding_byte: byte, ..self }
74    }
75}
76
77impl Default for DecimalFormatter {
78    fn default() -> DecimalFormatter {
79        DecimalFormatter::new()
80    }
81}
82
83/// A formatted decimal number that can be converted to a sequence of bytes.
84#[derive(Debug)]
85pub(crate) struct Decimal {
86    buf: [u8; Self::MAX_I64_LEN as usize],
87    start: u8,
88    end: u8,
89}
90
91impl Decimal {
92    /// Discovered via `i64::MIN.to_string().len()`.
93    const MAX_I64_LEN: u8 = 20;
94    /// Discovered via `i64::MAX.to_string().len()`.
95    const MAX_I64_DIGITS: u8 = 19;
96
97    /// Using the given formatter, turn the value given into a decimal
98    /// representation using ASCII bytes.
99    pub(crate) const fn new(
100        formatter: &DecimalFormatter,
101        value: i64,
102    ) -> Decimal {
103        let sign = value.signum();
104        let Some(mut value) = value.checked_abs() else {
105            let buf = [
106                b'-', b'9', b'2', b'2', b'3', b'3', b'7', b'2', b'0', b'3',
107                b'6', b'8', b'5', b'4', b'7', b'7', b'5', b'8', b'0', b'8',
108            ];
109            return Decimal { buf, start: 0, end: Self::MAX_I64_LEN };
110        };
111        let mut decimal = Decimal {
112            buf: [0; Self::MAX_I64_LEN as usize],
113            start: Self::MAX_I64_LEN,
114            end: Self::MAX_I64_LEN,
115        };
116        loop {
117            decimal.start -= 1;
118
119            let digit = (value % 10) as u8;
120            value /= 10;
121            decimal.buf[decimal.start as usize] = b'0' + digit;
122            if value == 0 {
123                break;
124            }
125        }
126        while decimal.len() < formatter.minimum_digits {
127            decimal.start -= 1;
128            decimal.buf[decimal.start as usize] = formatter.padding_byte;
129        }
130        if sign < 0 {
131            decimal.start -= 1;
132            decimal.buf[decimal.start as usize] = b'-';
133        } else if let Some(zero_is_positive) = formatter.force_sign {
134            let ascii_sign =
135                if sign > 0 || zero_is_positive { b'+' } else { b'-' };
136            decimal.start -= 1;
137            decimal.buf[decimal.start as usize] = ascii_sign;
138        }
139        decimal
140    }
141
142    /// Returns the total number of ASCII bytes (including the sign) that are
143    /// used to represent this decimal number.
144    #[inline]
145    const fn len(&self) -> u8 {
146        self.end - self.start
147    }
148
149    /// Returns the ASCII representation of this decimal as a byte slice.
150    ///
151    /// The slice returned is guaranteed to be valid ASCII.
152    #[inline]
153    pub(crate) fn as_bytes(&self) -> &[u8] {
154        &self.buf[usize::from(self.start)..usize::from(self.end)]
155    }
156
157    /// Returns the ASCII representation of this decimal as a string slice.
158    #[inline]
159    pub(crate) fn as_str(&self) -> &str {
160        // SAFETY: This is safe because all bytes written to `self.buf` are
161        // guaranteed to be ASCII (including in its initial state), and thus,
162        // any subsequence is guaranteed to be valid UTF-8.
163        unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
164    }
165}
166
167/// A simple formatter for converting fractional components to ASCII byte
168/// strings.
169///
170/// We only support precision to 9 decimal places, which corresponds to
171/// nanosecond precision as a fractional second component.
172#[derive(Clone, Copy, Debug)]
173pub(crate) struct FractionalFormatter {
174    precision: Option<u8>,
175}
176
177impl FractionalFormatter {
178    /// Creates a new fractional formatter using the given precision settings.
179    pub(crate) const fn new() -> FractionalFormatter {
180        FractionalFormatter { precision: None }
181    }
182
183    /// Format the given value using this configuration as a decimal ASCII
184    /// fractional number.
185    pub(crate) const fn format(&self, value: i64) -> Fractional {
186        Fractional::new(self, value)
187    }
188
189    /// Set the precision.
190    ///
191    /// If the `precision` is greater than `9`, then it is clamped to `9`.
192    ///
193    /// When the precision is not set, then it is automatically determined
194    /// based on the value.
195    pub(crate) const fn precision(
196        self,
197        precision: Option<u8>,
198    ) -> FractionalFormatter {
199        let precision = match precision {
200            None => None,
201            Some(p) if p > 9 => Some(9),
202            Some(p) => Some(p),
203        };
204        FractionalFormatter { precision, ..self }
205    }
206
207    /// Returns true if and only if at least one digit will be written for the
208    /// given value.
209    ///
210    /// This is useful for callers that need to know whether to write
211    /// a decimal separator, e.g., `.`, before the digits.
212    pub(crate) fn will_write_digits(self, value: i64) -> bool {
213        self.precision.map_or_else(|| value != 0, |p| p > 0)
214    }
215
216    /// Returns true if and only if this formatter has an explicit non-zero
217    /// precision setting.
218    ///
219    /// This is useful for determining whether something like `0.000` needs to
220    /// be written in the case of a `precision=Some(3)` setting and a zero
221    /// value.
222    pub(crate) fn has_non_zero_fixed_precision(self) -> bool {
223        self.precision.map_or(false, |p| p > 0)
224    }
225
226    /// Returns true if and only if this formatter has fixed zero precision.
227    /// That is, no matter what is given as input, a fraction is never written.
228    pub(crate) fn has_zero_fixed_precision(self) -> bool {
229        self.precision.map_or(false, |p| p == 0)
230    }
231}
232
233/// A formatted fractional number that can be converted to a sequence of bytes.
234#[derive(Debug)]
235pub(crate) struct Fractional {
236    buf: [u8; Self::MAX_LEN as usize],
237    end: u8,
238}
239
240impl Fractional {
241    /// Since we don't support precision bigger than this.
242    const MAX_LEN: u8 = 9;
243
244    /// Using the given formatter, turn the value given into a fractional
245    /// decimal representation using ASCII bytes.
246    ///
247    /// Note that the fractional number returned *may* expand to an empty
248    /// slice of bytes. This occurs whenever the precision is set to `0`, or
249    /// when the precision is not set and the value is `0`. Any non-zero
250    /// explicitly set precision guarantees that the slice returned is not
251    /// empty.
252    ///
253    /// This panics if the value given isn't in the range `0..=999_999_999`.
254    pub(crate) const fn new(
255        formatter: &FractionalFormatter,
256        mut value: i64,
257    ) -> Fractional {
258        assert!(0 <= value && value <= 999_999_999);
259        let mut fractional = Fractional {
260            buf: [b'0'; Self::MAX_LEN as usize],
261            end: Self::MAX_LEN,
262        };
263        let mut i = 9;
264        loop {
265            i -= 1;
266
267            let digit = (value % 10) as u8;
268            value /= 10;
269            fractional.buf[i] += digit;
270            if value == 0 {
271                break;
272            }
273        }
274        if let Some(precision) = formatter.precision {
275            fractional.end = precision;
276        } else {
277            while fractional.end > 0
278                && fractional.buf[fractional.end as usize - 1] == b'0'
279            {
280                fractional.end -= 1;
281            }
282        }
283        fractional
284    }
285
286    /// Returns the ASCII representation of this fractional number as a byte
287    /// slice. The slice returned may be empty.
288    ///
289    /// The slice returned is guaranteed to be valid ASCII.
290    pub(crate) fn as_bytes(&self) -> &[u8] {
291        &self.buf[..usize::from(self.end)]
292    }
293
294    /// Returns the ASCII representation of this fractional number as a string
295    /// slice. The slice returned may be empty.
296    pub(crate) fn as_str(&self) -> &str {
297        // SAFETY: This is safe because all bytes written to `self.buf` are
298        // guaranteed to be ASCII (including in its initial state), and thus,
299        // any subsequence is guaranteed to be valid UTF-8.
300        unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
301    }
302}
303
304/// Parses an optional fractional number from the start of `input`.
305///
306/// If `input` does not begin with a `.` (or a `,`), then this returns `None`
307/// and no input is consumed. Otherwise, up to 9 ASCII digits are parsed after
308/// the decimal separator.
309///
310/// While this is most typically used to parse the fractional component of
311/// second units, it is also used to parse the fractional component of hours or
312/// minutes in ISO 8601 duration parsing, and milliseconds and microseconds in
313/// the "friendly" duration format. The return type in that case is obviously a
314/// misnomer, but the range of possible values is still correct. (That is, the
315/// fractional component of an hour is still limited to 9 decimal places per
316/// the Temporal spec.)
317#[inline(always)]
318pub(crate) fn parse_temporal_fraction<'i>(
319    input: &'i [u8],
320) -> Result<Parsed<'i, Option<t::SubsecNanosecond>>, Error> {
321    // TimeFraction :::
322    //   TemporalDecimalFraction
323    //
324    // TemporalDecimalFraction :::
325    //   TemporalDecimalSeparator DecimalDigit
326    //   TemporalDecimalSeparator DecimalDigit DecimalDigit
327    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
328    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
329    //                            DecimalDigit
330    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
331    //                            DecimalDigit DecimalDigit
332    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
333    //                            DecimalDigit DecimalDigit DecimalDigit
334    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
335    //                            DecimalDigit DecimalDigit DecimalDigit
336    //                            DecimalDigit
337    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
338    //                            DecimalDigit DecimalDigit DecimalDigit
339    //                            DecimalDigit DecimalDigit
340    //   TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
341    //                            DecimalDigit DecimalDigit DecimalDigit
342    //                            DecimalDigit DecimalDigit DecimalDigit
343    //
344    // TemporalDecimalSeparator ::: one of
345    //   . ,
346    //
347    // DecimalDigit :: one of
348    //   0 1 2 3 4 5 6 7 8 9
349
350    #[inline(never)]
351    fn imp<'i>(
352        mut input: &'i [u8],
353    ) -> Result<Parsed<'i, Option<t::SubsecNanosecond>>, Error> {
354        let mkdigits = parse::slicer(input);
355        while mkdigits(input).len() <= 8
356            && input.first().map_or(false, u8::is_ascii_digit)
357        {
358            input = &input[1..];
359        }
360        let digits = mkdigits(input);
361        if digits.is_empty() {
362            return Err(err!(
363                "found decimal after seconds component, \
364                 but did not find any decimal digits after decimal",
365            ));
366        }
367        // I believe this error can never happen, since we know we have no more
368        // than 9 ASCII digits. Any sequence of 9 ASCII digits can be parsed
369        // into an `i64`.
370        let nanoseconds = parse::fraction(digits, 9).map_err(|err| {
371            err!(
372                "failed to parse {digits:?} as fractional component \
373                 (up to 9 digits, nanosecond precision): {err}",
374                digits = escape::Bytes(digits),
375            )
376        })?;
377        // I believe this is also impossible to fail, since the maximal
378        // fractional nanosecond is 999_999_999, and which also corresponds
379        // to the maximal expressible number with 9 ASCII digits. So every
380        // possible expressible value here is in range.
381        let nanoseconds =
382            t::SubsecNanosecond::try_new("nanoseconds", nanoseconds).map_err(
383                |err| err!("fractional nanoseconds are not valid: {err}"),
384            )?;
385        Ok(Parsed { value: Some(nanoseconds), input })
386    }
387
388    if input.is_empty() || (input[0] != b'.' && input[0] != b',') {
389        return Ok(Parsed { value: None, input });
390    }
391    imp(&input[1..])
392}
393
394/// This routine returns a span based on the given with fractional time applied
395/// to it.
396///
397/// For example, given a span like `P1dT1.5h`, the `unit` would be
398/// `Unit::Hour`, the `value` would be `1` and the `fraction` would be
399/// `500_000_000`. The span given would just be `1d`. The span returned would
400/// be `P1dT1h30m`.
401///
402/// Note that `fraction` can be a fractional hour, minute, second, millisecond
403/// or microsecond (even though its type suggests its only a fraction of a
404/// second). When milliseconds or microseconds, the given fraction has any
405/// sub-nanosecond precision truncated.
406///
407/// # Errors
408///
409/// This can error if the resulting units would be too large for the limits on
410/// a `span`. This also errors if `unit` is not `Hour`, `Minute`, `Second`,
411/// `Millisecond` or `Microsecond`.
412#[inline(never)]
413pub(crate) fn fractional_time_to_span(
414    unit: Unit,
415    value: t::NoUnits,
416    fraction: t::SubsecNanosecond,
417    mut span: Span,
418) -> Result<Span, Error> {
419    let allowed = matches!(
420        unit,
421        Unit::Hour
422            | Unit::Minute
423            | Unit::Second
424            | Unit::Millisecond
425            | Unit::Microsecond
426    );
427    if !allowed {
428        return Err(err!(
429            "fractional {unit} units are not allowed",
430            unit = unit.singular(),
431        ));
432    }
433    // We switch everything over to nanoseconds and then divy that up as
434    // appropriate. In general, we always create a balanced span, but there
435    // are some cases where we can't. For example, if one serializes a span
436    // with both the maximum number of seconds and the maximum number of
437    // milliseconds, then this just can't be balanced due to the limits on
438    // each of the units. When this kind of span is serialized to a string,
439    // it results in a second value that is actually bigger than the maximum
440    // allowed number of seconds in a span. So here, we have to reverse that
441    // operation and spread the seconds over smaller units. This in turn
442    // creates an unbalanced span. Annoying.
443    //
444    // The above is why we have `if unit_value > MAX { <do adjustments> }` in
445    // the balancing code below. Basically, if we overshoot our limit, we back
446    // out anything over the limit and carry it over to the lesser units. If
447    // our value is truly too big, then the final call to set nanoseconds will
448    // fail.
449    let value = t::NoUnits128::rfrom(value);
450    let fraction = t::NoUnits128::rfrom(fraction);
451    let mut nanos = match unit {
452        Unit::Hour => {
453            (value * t::NANOS_PER_HOUR) + (fraction * t::SECONDS_PER_HOUR)
454        }
455        Unit::Minute => {
456            (value * t::NANOS_PER_MINUTE) + (fraction * t::SECONDS_PER_MINUTE)
457        }
458        Unit::Second => (value * t::NANOS_PER_SECOND) + fraction,
459        Unit::Millisecond => {
460            (value * t::NANOS_PER_MILLI) + (fraction / t::NANOS_PER_MICRO)
461        }
462        Unit::Microsecond => {
463            (value * t::NANOS_PER_MICRO) + (fraction / t::NANOS_PER_MILLI)
464        }
465        // We return an error above if we hit this case.
466        _ => unreachable!("unsupported unit: {unit:?}"),
467    };
468
469    if unit >= Unit::Hour && nanos > 0 {
470        let mut hours = nanos / t::NANOS_PER_HOUR;
471        nanos %= t::NANOS_PER_HOUR;
472        if hours > t::SpanHours::MAX_SELF {
473            nanos += (hours - t::SpanHours::MAX_SELF) * t::NANOS_PER_HOUR;
474            hours = t::NoUnits128::rfrom(t::SpanHours::MAX_SELF);
475        }
476        // OK because we just checked that our units are in range.
477        span = span.try_hours_ranged(hours).unwrap();
478    }
479    if unit >= Unit::Minute && nanos > 0 {
480        let mut minutes = nanos / t::NANOS_PER_MINUTE;
481        nanos %= t::NANOS_PER_MINUTE;
482        if minutes > t::SpanMinutes::MAX_SELF {
483            nanos +=
484                (minutes - t::SpanMinutes::MAX_SELF) * t::NANOS_PER_MINUTE;
485            minutes = t::NoUnits128::rfrom(t::SpanMinutes::MAX_SELF);
486        }
487        // OK because we just checked that our units are in range.
488        span = span.try_minutes_ranged(minutes).unwrap();
489    }
490    if unit >= Unit::Second && nanos > 0 {
491        let mut seconds = nanos / t::NANOS_PER_SECOND;
492        nanos %= t::NANOS_PER_SECOND;
493        if seconds > t::SpanSeconds::MAX_SELF {
494            nanos +=
495                (seconds - t::SpanSeconds::MAX_SELF) * t::NANOS_PER_SECOND;
496            seconds = t::NoUnits128::rfrom(t::SpanSeconds::MAX_SELF);
497        }
498        // OK because we just checked that our units are in range.
499        span = span.try_seconds_ranged(seconds).unwrap();
500    }
501    if unit >= Unit::Millisecond && nanos > 0 {
502        let mut millis = nanos / t::NANOS_PER_MILLI;
503        nanos %= t::NANOS_PER_MILLI;
504        if millis > t::SpanMilliseconds::MAX_SELF {
505            nanos +=
506                (millis - t::SpanMilliseconds::MAX_SELF) * t::NANOS_PER_MILLI;
507            millis = t::NoUnits128::rfrom(t::SpanMilliseconds::MAX_SELF);
508        }
509        // OK because we just checked that our units are in range.
510        span = span.try_milliseconds_ranged(millis).unwrap();
511    }
512    if unit >= Unit::Microsecond && nanos > 0 {
513        let mut micros = nanos / t::NANOS_PER_MICRO;
514        nanos %= t::NANOS_PER_MICRO;
515        if micros > t::SpanMicroseconds::MAX_SELF {
516            nanos +=
517                (micros - t::SpanMicroseconds::MAX_SELF) * t::NANOS_PER_MICRO;
518            micros = t::NoUnits128::rfrom(t::SpanMicroseconds::MAX_SELF);
519        }
520        // OK because we just checked that our units are in range.
521        span = span.try_microseconds_ranged(micros).unwrap();
522    }
523    if nanos > 0 {
524        span = span.try_nanoseconds_ranged(nanos).with_context(|| {
525            err!(
526                "failed to set nanosecond value {nanos} on span \
527                 determined from {value}.{fraction}",
528            )
529        })?;
530    }
531
532    Ok(span)
533}
534
535/// Like `fractional_time_to_span`, but just converts the fraction of the given
536/// unit to a signed duration.
537///
538/// Since a signed duration doesn't keep track of individual units, there is
539/// no loss of fidelity between it and ISO 8601 durations like there is for
540/// `Span`.
541///
542/// Note that `fraction` can be a fractional hour, minute, second, millisecond
543/// or microsecond (even though its type suggests its only a fraction of a
544/// second). When milliseconds or microseconds, the given fraction has any
545/// sub-nanosecond precision truncated.
546///
547/// # Errors
548///
549/// This returns an error if `unit` is not `Hour`, `Minute`, `Second`,
550/// `Millisecond` or `Microsecond`.
551#[inline(never)]
552pub(crate) fn fractional_time_to_duration(
553    unit: Unit,
554    fraction: t::SubsecNanosecond,
555) -> Result<SignedDuration, Error> {
556    let fraction = t::NoUnits::rfrom(fraction);
557    let nanos = match unit {
558        Unit::Hour => fraction * t::SECONDS_PER_HOUR,
559        Unit::Minute => fraction * t::SECONDS_PER_MINUTE,
560        Unit::Second => fraction,
561        Unit::Millisecond => fraction / t::NANOS_PER_MICRO,
562        Unit::Microsecond => fraction / t::NANOS_PER_MILLI,
563        unit => {
564            return Err(err!(
565                "fractional {unit} units are not allowed",
566                unit = unit.singular(),
567            ))
568        }
569    };
570    Ok(SignedDuration::from_nanos(nanos.get()))
571}
572
573#[cfg(test)]
574mod tests {
575    use alloc::string::ToString;
576
577    use super::*;
578
579    #[test]
580    fn decimal() {
581        let x = DecimalFormatter::new().format(i64::MIN);
582        assert_eq!(x.as_str(), "-9223372036854775808");
583
584        let x = DecimalFormatter::new().format(i64::MIN + 1);
585        assert_eq!(x.as_str(), "-9223372036854775807");
586
587        let x = DecimalFormatter::new().format(i64::MAX);
588        assert_eq!(x.as_str(), "9223372036854775807");
589
590        let x = DecimalFormatter::new().force_sign(true).format(i64::MAX);
591        assert_eq!(x.as_str(), "+9223372036854775807");
592
593        let x = DecimalFormatter::new().format(0);
594        assert_eq!(x.as_str(), "0");
595
596        let x = DecimalFormatter::new().force_sign(true).format(0);
597        assert_eq!(x.as_str(), "+0");
598
599        let x = DecimalFormatter::new().force_sign(false).format(0);
600        assert_eq!(x.as_str(), "-0");
601
602        let x = DecimalFormatter::new().padding(4).format(0);
603        assert_eq!(x.as_str(), "0000");
604
605        let x = DecimalFormatter::new().padding(4).format(789);
606        assert_eq!(x.as_str(), "0789");
607
608        let x = DecimalFormatter::new().padding(4).format(-789);
609        assert_eq!(x.as_str(), "-0789");
610
611        let x =
612            DecimalFormatter::new().force_sign(true).padding(4).format(789);
613        assert_eq!(x.as_str(), "+0789");
614    }
615
616    #[test]
617    fn fractional_auto() {
618        let f = |n| FractionalFormatter::new().format(n).as_str().to_string();
619
620        assert_eq!(f(0), "");
621        assert_eq!(f(123_000_000), "123");
622        assert_eq!(f(123_456_000), "123456");
623        assert_eq!(f(123_456_789), "123456789");
624        assert_eq!(f(456_789), "000456789");
625        assert_eq!(f(789), "000000789");
626    }
627
628    #[test]
629    fn fractional_precision() {
630        let f = |precision, n| {
631            FractionalFormatter::new()
632                .precision(Some(precision))
633                .format(n)
634                .as_str()
635                .to_string()
636        };
637
638        assert_eq!(f(0, 0), "");
639        assert_eq!(f(1, 0), "0");
640        assert_eq!(f(9, 0), "000000000");
641
642        assert_eq!(f(3, 123_000_000), "123");
643        assert_eq!(f(6, 123_000_000), "123000");
644        assert_eq!(f(9, 123_000_000), "123000000");
645
646        assert_eq!(f(3, 123_456_000), "123");
647        assert_eq!(f(6, 123_456_000), "123456");
648        assert_eq!(f(9, 123_456_000), "123456000");
649
650        assert_eq!(f(3, 123_456_789), "123");
651        assert_eq!(f(6, 123_456_789), "123456");
652        assert_eq!(f(9, 123_456_789), "123456789");
653
654        // We use truncation, no rounding.
655        assert_eq!(f(2, 889_000_000), "88");
656        assert_eq!(f(2, 999_000_000), "99");
657    }
658}