numfmt/
lib.rs

1//! Fast and friendly number formatting.
2//!
3//! Provides a [`Formatter`] to format decimal numbers with various methods. Formatting is
4//! performance focused, it is generally faster than `std` with more features. There is also a
5//! [string parser](#parsing) which can use a string to define a [`Formatter`] following a specific
6//! grammar.
7//!
8//! # Procedure
9//! Formatting is done through the [`Formatter::fmt`] which follows the procedure:
10//! 1. Scale the number with the defined [`Scales`],
11//! 2. Check if _scaled number_ is above or below the [scientific notation
12//!    cutoffs](#scientific-notation),
13//! 3. Add defined thousands separator,
14//! 4. Stop at defined [`Precision`],
15//! 5. Applies valid prefix, suffix, and unit decorations.
16//!
17//! # Usage
18//! ## Default use
19//! [`Default::default`] provides a general use default formatter with the following properties:
20//! - [`Scales::short`] scaling,
21//! - `,` thousands separator,
22//! - 3 decimal places
23//!
24//! ```rust
25//! # use numfmt::*;
26//! let mut f = Formatter::default();
27//! assert_eq!(f.fmt(0.0), "0");
28//! assert_eq!(f.fmt(12345.6789), "12.345 K");
29//! assert_eq!(f.fmt(0.00012345), "1.234e-4");
30//! assert_eq!(f.fmt(123456e22), "1,234.559 Y");
31//! ```
32//!
33//! ## Custom use
34//! The [`Formatter`] has many different options to customise how the number should be formatted.
35//! The example below shows how a currency format would be developed:
36//! ```rust
37//! # use numfmt::*;
38//! let mut f = Formatter::new() // start with blank representation
39//!     .separator(',').unwrap()
40//!     .prefix("AU$").unwrap()
41//!     .precision(Precision::Decimals(2));
42//!
43//! assert_eq!(f.fmt(0.52), "AU$0.52");
44//! assert_eq!(f.fmt(1234.567), "AU$1,234.56");
45//! assert_eq!(f.fmt(12345678900.0), "AU$12,345,678,900.0");
46//! ```
47//!
48//! # Scientific Notation
49//! Scientific notation kicks in when the scaled number is greater than 12 integer digits
50//! (123,456,789,000) or less than 3 leading zeros (0.0001234). The number _always_ has a leading
51//! integer digit and has a default of **7 significant figures**.
52//!
53//! # Precision
54//! Precision, either with number of decimals or significant figures can be specified with
55//! [`Precision`].
56//! ```rust
57//! # use numfmt::*;
58//! let mut f = Formatter::new();
59//! assert_eq!(f.fmt(1234.56789), "1234.56789");
60//!
61//! f = f.precision(Precision::Decimals(2));
62//! assert_eq!(f.fmt(1234.56789), "1234.56");
63//!
64//! f = f.precision(Precision::Significance(5));
65//! assert_eq!(f.fmt(1234.56789), "1234.5");
66//! ```
67//!
68//! # Performance
69//! Formatting is generally faster than `std`'s `f64::to_string` implementation. When constructing
70//! a [`Formatter`] there is an allocation for the buffer, and an allocation for any scales.
71//! Reusing a [`Formatter`] is recommended to avoid unnecessary allocations. The `cached` row shows
72//! the better performance reusing a formatter.
73//!
74//! | Time (ns)        | 0.0 | 0.1234 | 2.718281828459045 | 1.797693148623157e307 |
75//! | ---------------- | --- | ------ | ----------------- | --------------------- |
76//! | numfmt - default | 35  | 115    | 153               | 195                   |
77//! | numfmt - cached  | 2   | 75     | 89                | 126                   |
78//! | std              | 35  | 96     | 105               | 214                   |
79//!
80//! # Example - File size formatter
81//! Using a combination of a scale, suffix, and precision, a file size printer can be constructed:
82//! ```rust
83//! # use numfmt::*;
84//! let mut f = Formatter::new()
85//!                 .scales(Scales::binary())
86//!                 .precision(Precision::Significance(3))
87//!                 .suffix("B").unwrap();
88//!
89//! assert_eq!(f.fmt(123_f64), "123 B");
90//! assert_eq!(f.fmt(1234_f64), "1.20 kiB");
91//! assert_eq!(f.fmt(1_048_576_f64), "1.0 MiB");
92//! assert_eq!(f.fmt(123456789876543_f64), "112 TiB");
93//! ```
94//!
95//! # Parsing
96//! A grammar is defined that can parse into a [`Formatter`]. This string representation can be
97//! used as a user input for formatting numbers. The grammar is defined by a _prefix_, the number
98//! format enclosed in brackets, and then the _suffix_.
99//! ```text
100//! prefix[[.#|,#|~#|.*|,*][%|s|b|n][/<char>]]suffix
101//! ^----^ ^--------------^^-------^^-------^ ^----^
102//! prefix precision scale    separator suffix
103//! ```
104//! > Each component is optional, including the number format. All formats are applied to the
105//! > _default_ [`Formatter`] so an empty format results in the default _formatter_.
106//!
107//! ## Prefix and Suffix
108//! The prefix and suffix are bound to the supported lengths, and can have any character in them.
109//! To use `[]` characters, a double bracket must be used.
110//!
111//! ### Example
112//! ```rust
113//! # use numfmt::*;
114//! let mut f: Formatter;
115//! f = "".parse().unwrap();
116//! assert_eq!(f.fmt(1.234), "1.234");
117//!
118//! f = "prefix ".parse().unwrap();
119//! assert_eq!(f.fmt(1.234), "prefix 1.234");
120//!
121//! f = "[] suffix".parse().unwrap();
122//! assert_eq!(f.fmt(1.234), "1.234 suffix");
123//!
124//! f = "[[prefix [] suffix]]".parse().unwrap();
125//! assert_eq!(f.fmt(1.234), "[prefix 1.234 suffix]");
126//! ```
127//!
128//! ## Precision
129//! Precision is defined using a `.`/`,` for decimals, or a `~` for significant figures, followed by
130//! a number. A maximum of 255 is supported. There is a special case: `.*`/`,*` which removes any
131//! default precision and uses [`Precision::Unspecified`].
132//! Note that usage of `,` signals to use periods as the separator and comma as the
133//! decimal marker. To use a comma with signficant figures, use a period separator.
134//!
135//! ### Example
136//! ```rust
137//! # use numfmt::*;
138//! let mut f: Formatter;
139//! f = "[.2]".parse().unwrap(); // use two decimal places
140//! assert_eq!(f.fmt(1.2345), "1.23");
141//!
142//! f = "[,2]".parse().unwrap(); // use two decimal places with comma
143//! assert_eq!(f.fmt(1.2345), "1,23");
144//!
145//! f = "[.0]".parse().unwrap(); // use zero decimal places
146//! assert_eq!(f.fmt(10.234), "10");
147//!
148//! f = "[.*]".parse().unwrap(); // arbitrary precision
149//! assert_eq!(f.fmt(1.234), "1.234");
150//! assert_eq!(f.fmt(12.2), "12.2");
151//!
152//! f = "[,*]".parse().unwrap(); // arbitrary precision with comma
153//! assert_eq!(f.fmt(1.234), "1,234");
154//!
155//! f = "[~3]".parse().unwrap(); // 3 significant figures
156//! assert_eq!(f.fmt(1.234), "1.23");
157//! assert_eq!(f.fmt(10.234), "10.2");
158
159//! f = "[~3/.]".parse().unwrap(); // 3 significant figures with comma
160//! assert_eq!(f.fmt(1.234), "1,23");
161//! ```
162//!
163//! ## Scale
164//! Scale uses a character to denote what scaling should be used. By default the SI scaling is
165//! used. The following characters are supported:
166//! - `s` for SI scaling ([`Scales::short`]),
167//! - `%` for percentage scaling ([`Formatter::percentage`]),
168//! - `m` for metric scaling ([`Scales::metric`]),
169//! - `b` for binary scaling ([`Scales::binary`]),
170//! - `n` for no scaling ([`Scales::none`])
171//!
172//! ### Example
173//! ```rust
174//! # use numfmt::*;
175//! let mut f: Formatter;
176//! f = "".parse().unwrap(); // default si scaling used
177//! assert_eq!(f.fmt(12345.0), "12.345 K");
178//!
179//! f = "[n]".parse().unwrap(); // turn off scaling
180//! assert_eq!(f.fmt(12345.0), "12,345.0");
181//!
182//! f = "[%.2]".parse().unwrap(); // format as percentages with 2 decimal places
183//! assert_eq!(f.fmt(0.234), "23.40%");
184//!
185//! f = "[b]".parse().unwrap(); // use a binary scaler
186//! assert_eq!(f.fmt(3.14 * 1024.0 * 1024.0), "3.14 Mi");
187//! ```
188//!
189//! ## Separator
190//! A separator character can be specified by using a forward slash `/` followed by a character.
191//! The parser uses the _next character_, unless that character is `]` in which case the
192//! separator is set to `None`. The default separator is a comma.
193//! If a period separator `.` is specified, we take this as a signal to use a comma `,` as
194//! the decimal signifier.
195//!
196//! ### Example
197//! ```rust
198//! # use numfmt::*;
199//! let mut f: Formatter;
200//! f = "[n]".parse().unwrap(); // turn off scaling to see separator
201//! assert_eq!(f.fmt(12345.0), "12,345.0");
202//!
203//! f = "[n/]".parse().unwrap(); // use no separator
204//! assert_eq!(f.fmt(12345.0), "12345.0");
205//!
206//! f = "[n/_]".parse().unwrap(); // use a underscroll
207//! assert_eq!(f.fmt(12345.0), "12_345.0");
208//!
209//! f = "[n/ ]".parse().unwrap(); // use a space
210//! assert_eq!(f.fmt(12345.0), "12 345.0");
211//!
212//! f = "[n/.]".parse().unwrap(); // use period and commas
213//! assert_eq!(f.fmt(12345.0), "12.345,0");
214//! ```
215//!
216//! ## Composing formats
217//! There have been examples of composing formats already. The `prefix[num]suffix` order must be
218//! adhered to, but the ordering within the number format is arbitrary. It is recommended to keep it
219//! consistent with _precision, scaling, separator_ as this assists with readability and lowers the
220//! risk of malformed formats (which will error on the parsing phase).
221//!
222//! ### Various composed examples
223//! ```rust
224//! # use numfmt::*;
225//! let mut f: Formatter;
226//!
227//! // Percentages to two decimal places
228//! f = "[.2%]".parse().unwrap();
229//! assert_eq!(f.fmt(0.012345), "1.23%");
230//!
231//! // Currency to zero decimal places
232//! // notice the `n` for no scaling
233//! f = "$[.0n] USD".parse().unwrap();
234//! assert_eq!(f.fmt(123_456_789.12345), "$123,456,789 USD");
235//!
236//! // Formatting file sizes
237//! f = "[~3b]B".parse().unwrap();
238//! assert_eq!(f.fmt(123_456_789.0), "117 MiB");
239//!
240//! // Units to 1 decimal place
241//! f = "[.1n] m/s".parse().unwrap();
242//! assert_eq!(f.fmt(12345.68), "12,345.6 m/s");
243//!
244//! // Using custom separator and period for decimals
245//! f = "[,1n/_]".parse().unwrap();
246//! assert_eq!(f.fmt(12345.68), "12_345,6");
247//! ```
248#![warn(missing_docs)]
249use std::{cmp::*, error, fmt, hash::*};
250use Precision::*;
251
252mod numeric;
253mod parse;
254#[cfg(test)]
255mod tests;
256
257pub use numeric::Numeric;
258pub use parse::ParseError;
259
260/// Result type for [`Formatter`] methods.
261pub type Result = std::result::Result<Formatter, Error>;
262
263const SN_BIG_CUTOFF: f64 = 1_000_000_000_000f64;
264const SN_SML_CUTOFF: f64 = 0.001;
265const SN_PREC: Precision = Significance(7);
266const PREFIX_LIM: usize = 12;
267const UNITS_LIM: usize = 12;
268const SUFFIX_LIM: usize = 12;
269const FLOATBUF_LEN: usize = 22;
270const BUF_LEN: usize = PREFIX_LIM + FLOATBUF_LEN + 3 + UNITS_LIM + SUFFIX_LIM;
271
272// ########### FORMATTER #################################################################
273/// The number formatter configurations. See the [module documentation for use][link].
274///
275/// [`Formatter`] has a `FromStr` implementation that can parse a string into a formatter using a
276/// specific grammar. Please [consult the parsing section in the module
277/// documentation](./index.html#parsing).
278///
279/// [link]: crate
280#[derive(Debug, Clone)]
281pub struct Formatter {
282    /// The formatter uses a buffer to avoid allocating when constructing the formatted string.
283    /// The formatting algorithm assumes the buffer size is large enough to accommodate writes into
284    /// it, care must be taken when altering what gets written to buffer. Ensure buffer is of
285    /// adequate size.
286    ///
287    /// The buffer is sized for:
288    /// - 12 bytes: prefix
289    /// - 22 bytes: float repr <https://github.com/dtolnay/dtoa/issues/22>
290    /// - 3  bytes: 3x thou separator
291    /// - 12 bytes: units
292    /// - 12 bytes: suffix
293    strbuf: Vec<u8>,
294    /// Optional thousands separator character (restricted to a single byte)
295    thou_sep: Option<u8>,
296    /// comma separation
297    comma: bool,
298    /// If prefixed with something, this is the start of the _number_ portion.
299    start: usize,
300    /// Precision limits to formatting.
301    precision: Precision,
302    /// The auto scales.
303    scales: Scales,
304    /// Optional suffix.
305    suffix: [u8; SUFFIX_LIM],
306    suffix_len: usize,
307    /// Direct conversion.
308    convert: fn(f64) -> f64,
309}
310
311impl Formatter {
312    /// Construct a new formatter.
313    ///
314    /// No scaling is set, so this is only does a single allocation for the buffer.
315    ///
316    /// # Example
317    /// ```rust
318    /// # use numfmt::*;
319    /// let mut f = Formatter::new();
320    /// assert_eq!(f.fmt(12345.6789), "12345.6789");
321    /// ```
322    pub fn new() -> Self {
323        Self {
324            strbuf: vec![0; BUF_LEN],
325            thou_sep: None,
326            start: 0,
327            precision: Precision::Unspecified,
328            scales: Scales::none(),
329            suffix: [0; SUFFIX_LIM],
330            suffix_len: 0,
331            convert: |x| x,
332            comma: false,
333        }
334    }
335
336    /// Create a formatter that formats numbers as a currency.
337    ///
338    /// # Example
339    /// ```rust
340    /// # use numfmt::*;
341    /// let mut f = Formatter::currency("$").unwrap();
342    /// assert_eq!(f.fmt(12345.6789), "$12,345.67");
343    /// assert_eq!(f.fmt(1234_f64), "$1,234.0");
344    /// ```
345    pub fn currency(prefix: &str) -> Result {
346        Self::new()
347            .separator(',')
348            .unwrap()
349            .precision(Decimals(2))
350            .prefix(prefix)
351    }
352
353    /// Create a formatter that formats numbers as a percentage.
354    ///
355    /// # Example
356    /// ```rust
357    /// # use numfmt::*;
358    /// let mut f = Formatter::percentage();
359    /// assert_eq!(f.fmt(0.678912), "67.8912%");
360    /// assert_eq!(f.fmt(1.23), "123.0%");
361    /// assert_eq!(f.fmt(1.2), "120.0%");
362    ///
363    /// f = f.precision(Precision::Decimals(2));
364    /// assert_eq!(f.fmt(0.01234), "1.23%");
365    /// ```
366    pub fn percentage() -> Self {
367        Self::new().convert(|x| x * 100.0).suffix("%").unwrap()
368    }
369
370    /// Set the value converter.
371    ///
372    /// Use a converter to transform the input number into another number. This is done before all
373    /// steps and the number follows the same procedure as normal. A good example of a use of a
374    /// converter is to make a percentage number by _always_ multiplying by 100.
375    pub fn convert(mut self, f: fn(f64) -> f64) -> Self {
376        self.convert = f;
377        self
378    }
379
380    /// Set the precision.
381    pub fn precision(mut self, precision: Precision) -> Self {
382        self.precision = precision;
383        self
384    }
385
386    /// Set the scaling.
387    pub fn scales(mut self, scales: Scales) -> Self {
388        self.scales = scales;
389        self
390    }
391
392    /// Set the scaling via [`Scales::new`].
393    pub fn build_scales(mut self, base: u16, units: Vec<&'static str>) -> Result {
394        let scales = Scales::new(base, units)?;
395        self.scales = scales;
396        Ok(self)
397    }
398
399    /// Set the thousands separator.
400    ///
401    /// If separator is not a single byte, an error is returned.
402    /// If the separator is a period `.`, this signals to use a comma for the decimal marker.
403    ///
404    /// # Example
405    /// ```rust
406    /// # use numfmt::*;
407    /// let mut f = Formatter::new().separator(',').unwrap(); // use a comma
408    /// assert_eq!(f.fmt(12345.67), "12,345.67");
409    ///
410    /// f = f.separator(' ').unwrap(); // use a space
411    /// assert_eq!(f.fmt(12345.67), "12 345.67");
412    ///
413    /// f = f.separator(None).unwrap(); // no separator
414    /// assert_eq!(f.fmt(12345.67), "12345.67");
415    ///
416    /// f = f.separator('.').unwrap(); // use a period separator and comma for decimal
417    /// assert_eq!(f.fmt(12345.67), "12.345,67");
418    /// ```
419    pub fn separator<S: Into<Option<char>>>(mut self, sep: S) -> Result {
420        if let Some(sep) = sep.into() {
421            if sep.len_utf8() != 1 {
422                Err(Error::InvalidSeparator(sep))
423            } else {
424                if sep == '.' {
425                    self.comma = true;
426                }
427                let mut buf = [0];
428                sep.encode_utf8(&mut buf);
429                self.thou_sep = Some(buf[0]);
430                Ok(self)
431            }
432        } else {
433            self.thou_sep = None;
434            Ok(self)
435        }
436    }
437
438    /// Set the comma option.
439    ///
440    /// If set to true it will use a comma instead of a period.
441    /// If a comma is the separator, a period will be used instead.
442    ///
443    /// # Example
444    ///
445    /// ```rust
446    /// # use numfmt::*;
447    /// let mut f = Formatter::new();
448    /// assert_eq!(f.fmt(12345.67), "12345.67");
449    /// f = f.comma(true);
450    /// assert_eq!(f.fmt(12345.67), "12345,67");
451    ///
452    /// f = f.separator('.').unwrap();
453    /// assert_eq!(f.fmt(12345.67), "12.345,67");
454    /// ```
455    pub fn comma(mut self, comma: bool) -> Self {
456        self.comma = comma;
457        if comma && self.thou_sep == Some(b',') {
458            self.thou_sep = Some(b'.');
459        }
460        self
461    }
462
463    /// Sets the prefix.
464    ///
465    /// If the prefix is longer than the supported length, an error is returned.
466    pub fn prefix(mut self, prefix: &str) -> Result {
467        if prefix.len() > PREFIX_LIM {
468            Err(Error::InvalidPrefix(prefix.to_string()))
469        } else {
470            let n = prefix.len();
471            self.strbuf[..n].copy_from_slice(prefix.as_bytes());
472            self.start = n;
473            Ok(self)
474        }
475    }
476
477    /// Set the suffix.
478    ///
479    /// If the suffix is longer than the supported length, an error is returned.
480    pub fn suffix(mut self, suffix: &str) -> Result {
481        if suffix.len() > SUFFIX_LIM {
482            Err(Error::InvalidSuffix(suffix.to_string()))
483        } else {
484            let n = suffix.len();
485            self.suffix[..n].copy_from_slice(suffix.as_bytes());
486            self.suffix_len = n;
487            Ok(self)
488        }
489    }
490
491    /// Format the number!
492    #[deprecated = "consider using Formatter::fmt2 instead"]
493    pub fn fmt(&mut self, num: f64) -> &str {
494        self.fmt2(num)
495    }
496
497    /// Format any number implementing [`Numeric`].
498    pub fn fmt2<N: Numeric>(&mut self, num: N) -> &str {
499        let mut buf = std::mem::take(&mut self.strbuf);
500        let bytes = self.fmt_into_buf(&mut buf, num);
501        self.strbuf = buf;
502        std::str::from_utf8(&self.strbuf[..bytes]).expect("will be valid string")
503    }
504
505    /// Format any number implementing [`Numeric`], appending to the supplied `buf`.
506    ///
507    /// This is functionally the same as [`Self::fmt2`], however it does not use the backing
508    /// buffer, instead extending the supplied string.
509    /// Useful when the receiver is shared.
510    pub fn fmt_into<N: Numeric>(&self, buf: &mut String, num: N) {
511        let start = buf.len();
512
513        // pad string buffer to write into
514        buf.extend(std::iter::repeat_n('\0', self.strbuf.len()));
515
516        // SAFETY: Only UTF-8 characters will be written.
517        let bytes = unsafe { buf.as_bytes_mut() };
518
519        // write the prefix into the buffer
520        bytes[start..start + self.start].copy_from_slice(&self.strbuf[..self.start]);
521
522        let written = self.fmt_into_buf(&mut bytes[start..], num);
523        let end = start + written;
524
525        buf.truncate(end); // drop the unused padding
526
527        debug_assert!(std::str::from_utf8(buf.as_bytes()).is_ok());
528    }
529
530    /// Format any number implementing [`Numeric`], returning an owned [`String`].
531    ///
532    /// This is functionally the same as [`Self::fmt2`], however it does not use the backing
533    /// buffer, instead allocating a new string to write into.
534    /// Useful when the receiver is shared.
535    pub fn fmt_string<N: Numeric>(&self, num: N) -> String {
536        let mut buf = String::new();
537        self.fmt_into(&mut buf, num);
538        buf
539    }
540
541    /// Format the number into `strbuf`. Returns the number of bytes written.
542    fn fmt_into_buf<N: Numeric>(&self, strbuf: &mut [u8], num: N) -> usize {
543        debug_assert_eq!(
544            strbuf.len(),
545            BUF_LEN,
546            "the buffer is expected to be {BUF_LEN} wide"
547        );
548
549        if num.is_nan() {
550            strbuf[..3].copy_from_slice(b"NaN");
551            3
552        } else if num.is_infinite() && num.is_negative() {
553            strbuf[..4].copy_from_slice(b"-\xE2\x88\x9E"); // -∞
554            4
555        } else if num.is_infinite() {
556            strbuf[..3].copy_from_slice(b"\xE2\x88\x9E");
557            3
558        } else if num.is_zero() {
559            strbuf[..1].copy_from_slice(b"0");
560            1
561        } else {
562            let num = (self.convert)(num.to_f64());
563
564            // scale num to supplied scales
565            let (scaled, unit) = self.scales.scale(num);
566
567            // check if the scaled version hits sn cutoffs
568            // use original number if it does
569            let abs = scaled.abs();
570            // This adjusts the sn cutoff if decimals is low
571            let sn_sml_cutoff = match self.precision {
572                Decimals(d) | Significance(d) if d <= 3 => 10f64.powi(d as i32).recip(),
573                _ => SN_SML_CUTOFF,
574            };
575            if abs >= SN_BIG_CUTOFF || abs < sn_sml_cutoff {
576                // fmt with scientific notation
577                let (num, exponent) = reduce_to_sn(num);
578                let precision = match self.precision {
579                    Unspecified => SN_PREC,
580                    x => x,
581                };
582                let cursor = self.start + self.write_num(strbuf, num, precision);
583                strbuf[cursor] = b'e'; // exponent
584                let cursor = 1 + cursor;
585                let written = {
586                    let mut buf = itoa::Buffer::new();
587                    let s = buf.format(exponent);
588                    let end = cursor + s.len();
589                    strbuf[cursor..end].copy_from_slice(s.as_bytes());
590                    s.len()
591                };
592                let cursor = cursor + written;
593                self.apply_suffix(strbuf, cursor)
594            } else {
595                // write out the scaled number
596                let mut cursor = self.start + self.write_num(strbuf, scaled, self.precision);
597                if !unit.is_empty() {
598                    let s = cursor;
599                    cursor += unit.len();
600                    strbuf[s..cursor].copy_from_slice(unit.as_bytes());
601                }
602                self.apply_suffix(strbuf, cursor)
603            }
604        }
605    }
606
607    /// Writes `num` into the string buffer with the specified `precision`.
608    /// Returns the number of bytes written.
609    /// Injects the thousands separator into the integer portion if it exists.
610    fn write_num(&self, strbuf: &mut [u8], num: f64, precision: Precision) -> usize {
611        let mut tmp = dtoa::Buffer::new();
612        let s = tmp.format(num);
613        let tmp = s.as_bytes();
614        let n = tmp.len();
615        let mut digits = 0;
616        let mut written = 0;
617        let mut in_frac = false;
618        let mut thou = 2 - (num.abs().log10().trunc() as u8) % 3;
619        let mut idx = self.start;
620
621        for i in 0..n {
622            let byte = tmp[i]; // obtain byte
623            strbuf[idx] = byte; // write byte
624            idx += 1;
625            written += 1; // increment counter
626
627            if byte.is_ascii_digit() {
628                digits += 1;
629                thou += 1;
630            }
631
632            // look ahead otherwise it would include the decimal always even for 0 precision
633            if i + 1 < n && tmp[i + 1] == b'.' {
634                in_frac = true;
635                if let Decimals(_) = precision {
636                    digits = 0
637                }
638            } else if in_frac && byte == b'.' && self.comma {
639                strbuf[idx - 1] = b',';
640            } else if !in_frac && thou == 3 {
641                if let Some(sep) = self.thou_sep {
642                    thou = 0;
643                    strbuf[idx] = sep;
644                    idx += 1;
645                    written += 1;
646                }
647            }
648
649            match precision {
650                Significance(d) | Decimals(d) if in_frac => {
651                    if digits >= d {
652                        break;
653                    }
654                }
655                _ => (),
656            }
657        }
658
659        written
660    }
661
662    fn apply_suffix(&self, strbuf: &mut [u8], mut pos: usize) -> usize {
663        if !self.suffix.is_empty() {
664            let s = pos;
665            pos = s + self.suffix_len;
666            strbuf[s..pos].copy_from_slice(&self.suffix[..self.suffix_len]);
667        }
668        pos
669    }
670}
671
672impl Default for Formatter {
673    fn default() -> Self {
674        Self::new()
675            .separator(',')
676            .unwrap()
677            .scales(Scales::short())
678            .precision(Decimals(3))
679    }
680}
681
682impl std::str::FromStr for Formatter {
683    type Err = parse::ParseError;
684    fn from_str(s: &str) -> std::result::Result<Self, ParseError> {
685        parse::parse_formatter(s)
686    }
687}
688
689// Eq and Hash have specialised impls as the _state_ of the buffer should not impact equality
690// checking
691impl PartialEq for Formatter {
692    #[allow(clippy::suspicious_operation_groupings)]
693    fn eq(&self, other: &Self) -> bool {
694        std::ptr::fn_addr_eq(self.convert, other.convert)
695            && self.precision == other.precision
696            && self.thou_sep == other.thou_sep
697            // need to use the other suffix len.
698            && self.suffix[..self.suffix_len] == other.suffix[..other.suffix_len]
699            && self.strbuf[..self.start] == other.strbuf[..other.start]
700            && self.scales == other.scales
701    }
702}
703
704impl Eq for Formatter {}
705
706impl Hash for Formatter {
707    fn hash<H: Hasher>(&self, hasher: &mut H) {
708        self.strbuf[..self.start].hash(hasher);
709        self.thou_sep.hash(hasher);
710        self.precision.hash(hasher);
711        self.scales.hash(hasher);
712        self.suffix[..self.suffix_len].hash(hasher);
713        self.convert.hash(hasher);
714    }
715}
716
717/// Returns `(reduced, exponent)`.
718fn reduce_to_sn(n: f64) -> (f64, i32) {
719    if n == 0.0 || n == -0.0 {
720        (0.0, 0)
721    } else {
722        let abs = n.abs();
723        let mut e = abs.log10().trunc() as i32;
724        if abs < 1.0 {
725            e -= 1;
726        }
727        let n = n * 10_f64.powi(-e);
728        (n, e)
729    }
730}
731
732// ########### ERROR #####################################################################
733/// Errors when configuring a [`Formatter`].
734#[derive(Debug, PartialEq)]
735pub enum Error {
736    /// Prefix is longer than supported length.
737    InvalidPrefix(String),
738    /// Separator is not a byte long.
739    InvalidSeparator(char),
740    /// Suffix is longer than supported length.
741    InvalidSuffix(String),
742    /// Unit is longer than supported length.
743    InvalidUnit(&'static str),
744    /// Scaling base is 0.
745    ZeroBase,
746}
747
748impl error::Error for Error {}
749
750impl fmt::Display for Error {
751    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
752        use Error::*;
753        match self {
754            InvalidPrefix(prefix) => write!(
755                f,
756                "Invalid prefix `{prefix}`. Prefix is longer than the supported {PREFIX_LIM} bytes"
757            ),
758            InvalidSeparator(sep) => write!(
759                f,
760                "Invalid separator `{sep}`. Separator can only be one byte long"
761            ),
762            InvalidSuffix(suffix) => write!(
763                f,
764                "Invalid suffix `{suffix}`. Suffix is longer than the supported {SUFFIX_LIM} bytes"
765            ),
766            InvalidUnit(unit) => write!(
767                f,
768                "Invalid unit `{unit}`. Unit is longer than the supported {UNITS_LIM} bytes"
769            ),
770            ZeroBase => write!(f, "Invalid scale base, base must be greater than zero"),
771        }
772    }
773}
774
775// ########### PRECISION #################################################################
776/// Number precision.
777#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
778#[allow(missing_docs)]
779pub enum Precision {
780    Significance(u8),
781    Decimals(u8),
782    Unspecified,
783}
784
785// ########### SCALES ####################################################################
786/// Scale numbers.
787#[derive(Debug, PartialEq, Eq, Clone, Hash)]
788pub struct Scales {
789    base: u16,
790    units: Vec<&'static str>,
791}
792
793impl Scales {
794    /// Create a new scale.
795    ///
796    /// If a unit is longer than the supported length, an error will be returned.
797    pub fn new(base: u16, units: Vec<&'static str>) -> std::result::Result<Self, Error> {
798        if base == 0 {
799            return Err(Error::ZeroBase);
800        }
801
802        for unit in &units {
803            if unit.len() > UNITS_LIM {
804                return Err(Error::InvalidUnit(unit));
805            }
806        }
807        Ok(Self { base, units })
808    }
809
810    /// Create a scale which is dummy and does not scale.
811    pub fn none() -> Self {
812        Self {
813            base: u16::MAX,
814            units: Vec::new(),
815        }
816    }
817
818    /// The default scaling method.
819    ///
820    /// Based on a [short scale](https://en.wikipedia.org/wiki/Long_and_short_scales)
821    /// the scaling uses base `1000`. The units are meant to be used to denote _magnitude_ of the
822    /// number, so the empty base is empty.
823    ///
824    /// # Example
825    /// ```rust
826    /// # use numfmt::*;
827    /// let mut f = Formatter::default()
828    ///             .scales(Scales::short())
829    ///             .precision(Precision::Decimals(1));
830    /// assert_eq!(f.fmt(12.34e0), "12.3");
831    /// assert_eq!(f.fmt(12.34e3), "12.3 K");
832    /// assert_eq!(f.fmt(12.34e6), "12.3 M");
833    /// assert_eq!(f.fmt(12.34e9), "12.3 B");
834    /// assert_eq!(f.fmt(12.34e12), "12.3 T");
835    /// assert_eq!(f.fmt(12.34e15), "12.3 P");
836    /// assert_eq!(f.fmt(12.34e18), "12.3 E");
837    /// assert_eq!(f.fmt(12.34e21), "12.3 Z");
838    /// assert_eq!(f.fmt(12.34e24), "12.3 Y");
839    /// assert_eq!(f.fmt(12.34e27), "12,339.9 Y");
840    /// ```
841    pub fn short() -> Self {
842        Scales {
843            base: 1000,
844            units: vec!["", " K", " M", " B", " T", " P", " E", " Z", " Y"],
845        }
846    }
847
848    /// Create a metric SI scale.
849    ///
850    /// The [SI scale](https://en.wikipedia.org/wiki/International_System_of_Units#Prefixes)
851    /// steps with base `1000`. It is intended for use as a units prefix, so the empty base
852    /// contains a space.
853    ///
854    /// # Example
855    /// ```rust
856    /// # use numfmt::*;
857    /// let mut f = Formatter::new().scales(Scales::metric());
858    /// assert_eq!(f.fmt(123456.0), "123.456 k");
859    /// assert_eq!(f.fmt(123456789.0), "123.456789 M");
860    /// ```
861    pub fn metric() -> Self {
862        Scales {
863            base: 1000,
864            units: vec![" ", " k", " M", " G", " T", " P", " E", " Z", " Y"],
865        }
866    }
867
868    /// Create a binary scale.
869    ///
870    /// The [binary scale](https://en.wikipedia.org/wiki/Binary_prefix)
871    /// steps with base `1024`. It is intended for use as a units prefix, so the empty base
872    /// contains a space.
873    ///
874    /// # Example
875    /// ```rust
876    /// # use numfmt::*;
877    /// let mut f = Formatter::new().scales(Scales::binary());
878    /// assert_eq!(f.fmt(1024.0 * 1024.0), "1.0 Mi");
879    /// assert_eq!(f.fmt(3.14 * 1024.0 * 1024.0), "3.14 Mi");
880    /// ```
881    pub fn binary() -> Self {
882        Scales {
883            base: 1024,
884            units: vec![" ", " ki", " Mi", " Gi", " Ti", " Pi", " Ei", " Zi", " Yi"],
885        }
886    }
887
888    /// The set base.
889    pub fn base(&self) -> u16 {
890        self.base
891    }
892
893    /// The set units.
894    pub fn units(&self) -> &[&'static str] {
895        self.units.as_slice()
896    }
897
898    /// Extract the `(base, units)`.
899    pub fn into_inner(self) -> (u16, Vec<&'static str>) {
900        (self.base, self.units)
901    }
902
903    /// Scale a number and return the scaled number with the unit.
904    pub fn scale(&self, mut num: f64) -> (f64, &'static str) {
905        let base = self.base as f64;
906        let mut u = "";
907        let mut n2 = num;
908        // use n2 as a delayed write to not downsize num on last entry numbers
909        for unit in &self.units {
910            num = n2;
911            u = unit;
912            if num.abs() >= base {
913                n2 = num / base;
914            } else {
915                break;
916            }
917        }
918        (num, u)
919    }
920}