radix_fmt/
lib.rs

1//! [![Latest Version](https://img.shields.io/crates/v/radix_fmt.svg)](https://crates.io/crates/radix_fmt)
2//! [![Documentation](https://img.shields.io/badge/api-rustdoc-purple.svg)](https://docs.rs/radix_fmt)
3//!
4//! This crate adds a tool to format a number in an arbitrary base from 2 to 36.
5//!
6//! This is a light crate, without any dependency.
7//!
8//! For primitive signed integers (`i8` to `i128`, and `isize`), negative values are
9//! formatted as the two’s complement representation.
10//!
11//! There is also one specific function for each radix that does not
12//! already exists in the standard library, *e.g.* [`radix_3`](fn.radix_3.html)
13//! to format a number in base 3.
14//!
15//! Get started
16//! -----------
17//!
18//! Add the crate to the cargo manifest:
19//!
20//! ```toml
21//! radix_fmt = "1"
22//! ```
23//!
24//! Import [`radix`](fn.radix.html) in scope,
25//! and you are ready to go:
26//!
27//! ```rust
28//! use radix_fmt::radix;
29//! ```
30//!
31//! Examples
32//! --------
33//!
34//! ```rust
35//! use radix_fmt::*;
36//!
37//! let n = 35;
38//!
39//! // Ouput: "z"
40//! println!("{}", radix(n, 36));
41//! // Same ouput: "z"
42//! println!("{}", radix_36(n));
43//! ```
44//!
45//! You can use the *alternate* modifier to capitalize the letter-digits:
46//!
47//! ```rust
48//! use radix_fmt::radix;
49//!
50//! let n = 35;
51//!
52//! // Ouput: "Z"
53//! println!("{:#}", radix(n, 36));
54//! ```
55
56#[cfg(test)]
57mod tests;
58
59use core::num::{NonZeroI128, NonZeroU128};
60use core::num::{NonZeroI16, NonZeroU16};
61use core::num::{NonZeroI32, NonZeroU32};
62use core::num::{NonZeroI64, NonZeroU64};
63use core::num::{NonZeroI8, NonZeroU8};
64use core::num::{NonZeroIsize, NonZeroUsize};
65use std::fmt::{Display, Formatter, Result};
66use std::iter::successors;
67
68/// A struct to format a number in an arbitrary radix.
69///
70/// # Example:
71///
72/// ```rust
73/// use radix_fmt::Radix;
74/// use std::fmt::Write;
75///
76/// let n = 15;
77/// let mut s = String::new();
78///
79/// write!(&mut s, "{}", Radix::new(n, 3));
80/// assert_eq!(s, "120"); // 15 is "120" in base 3
81/// ```
82#[derive(Debug, Clone, Copy)]
83pub struct Radix<T> {
84    n: T,
85    base: u8,
86}
87
88impl<T> Radix<T>
89where
90    Radix<T>: Display,
91{
92    /// Create a new displayable number from number and base.
93    /// The base must be in the range [2, 36].
94    pub fn new(n: T, base: u8) -> Self {
95        assert!(base >= 2 && base <= 36);
96
97        Radix { n, base }
98    }
99}
100
101#[inline(always)]
102fn digit(u: u8, alternate: bool) -> u8 {
103    let a = if alternate { b'A' } else { b'a' };
104
105    match u {
106        0...9 => u + b'0',
107        10...35 => u - 10 + a,
108        _ => unreachable!("Digit is not in range [0..36]"),
109    }
110}
111
112const BUF_SIZE: usize = 81; // u128::max_value() in base 3 takes 81 digits to write.
113
114macro_rules! impl_display_for {
115    ($i: ty => $via: ty as $u: ty) => {
116        impl Display for Radix<$i> {
117            fn fmt(&self, f: &mut Formatter) -> Result {
118                fn do_format(n: $u, base: $u, f: &mut Formatter) -> Result {
119                    let mut buffer = [0_u8; BUF_SIZE];
120                    let divided = successors(Some(n), |n| match n / base {
121                        0 => None,
122                        n => Some(n),
123                    });
124                    let written = buffer
125                        .iter_mut()
126                        .rev()
127                        .zip(divided)
128                        .map(|(c, n)| *c = digit((n % base) as u8, f.alternate()))
129                        .count();
130                    let index = BUF_SIZE - written;
131
132                    // There are only ASCII chars inside the buffer, so the string
133                    // is guaranteed to be a valid UTF-8 string.
134                    let s = unsafe { std::str::from_utf8_unchecked(&buffer[index..]) };
135
136                    f.write_str(s)
137                }
138
139                match (self.base, f.alternate()) {
140                    (2, _) => write!(f, "{:b}", self.n),
141                    (8, _) => write!(f, "{:o}", self.n),
142                    (10, _) => write!(f, "{}", self.n),
143                    (16, false) => write!(f, "{:x}", self.n),
144                    (16, true) => write!(f, "{:X}", self.n),
145                    (base, _) => do_format(<$via>::from(self.n) as $u, base.into(), f),
146                }
147            }
148        }
149    };
150}
151
152impl_display_for!(i8 => i8 as u8);
153impl_display_for!(u8 => u8 as u8);
154
155impl_display_for!(i16 => i16 as u16);
156impl_display_for!(u16 => u16 as u16);
157
158impl_display_for!(i32 => i32 as u32);
159impl_display_for!(u32 => u32 as u32);
160
161impl_display_for!(i64 => i64 as u64);
162impl_display_for!(u64 => u64 as u64);
163
164impl_display_for!(i128 => i128 as u128);
165impl_display_for!(u128 => u128 as u128);
166
167impl_display_for!(isize => isize as usize);
168impl_display_for!(usize => usize as usize);
169
170impl_display_for!(NonZeroI8 => i8 as u8);
171impl_display_for!(NonZeroU8 => u8 as u8);
172
173impl_display_for!(NonZeroI16 => i16 as u16);
174impl_display_for!(NonZeroU16 => u16 as u16);
175
176impl_display_for!(NonZeroI32 => i32 as u32);
177impl_display_for!(NonZeroU32 => u32 as u32);
178
179impl_display_for!(NonZeroI64 => i64 as u64);
180impl_display_for!(NonZeroU64 => u64 as u64);
181
182impl_display_for!(NonZeroI128 => i128 as u128);
183impl_display_for!(NonZeroU128 => u128 as u128);
184
185impl_display_for!(NonZeroIsize => isize as usize);
186impl_display_for!(NonZeroUsize => usize as usize);
187
188/// A helper for creating a new formatter from
189/// [`Radix::new`](struct.Radix.html#method.new).
190///
191/// # Example:
192///
193/// ```rust
194/// use radix_fmt::radix;
195///
196/// // Similar to println!("{}", Radix::new(123, 10));
197/// // Prints: "123"
198/// println!("{}", radix(123, 10));
199/// ```
200pub fn radix<T>(n: T, base: u8) -> Radix<T>
201where
202    Radix<T>: Display,
203{
204    Radix::new(n, base)
205}
206/// Formats a number in base 3.
207pub fn radix_3<T>(n: T) -> Radix<T>
208where
209    Radix<T>: Display,
210{
211    Radix::new(n, 3)
212}
213/// Formats a number in base 4.
214pub fn radix_4<T>(n: T) -> Radix<T>
215where
216    Radix<T>: Display,
217{
218    Radix::new(n, 4)
219}
220/// Formats a number in base 5.
221pub fn radix_5<T>(n: T) -> Radix<T>
222where
223    Radix<T>: Display,
224{
225    Radix::new(n, 5)
226}
227/// Formats a number in base 6.
228pub fn radix_6<T>(n: T) -> Radix<T>
229where
230    Radix<T>: Display,
231{
232    Radix::new(n, 6)
233}
234/// Formats a number in base 7.
235pub fn radix_7<T>(n: T) -> Radix<T>
236where
237    Radix<T>: Display,
238{
239    Radix::new(n, 7)
240}
241/// Formats a number in base 9.
242pub fn radix_9<T>(n: T) -> Radix<T>
243where
244    Radix<T>: Display,
245{
246    Radix::new(n, 9)
247}
248/// Formats a number in base 11.
249pub fn radix_11<T>(n: T) -> Radix<T>
250where
251    Radix<T>: Display,
252{
253    Radix::new(n, 11)
254}
255/// Formats a number in base 12.
256pub fn radix_12<T>(n: T) -> Radix<T>
257where
258    Radix<T>: Display,
259{
260    Radix::new(n, 12)
261}
262/// Formats a number in base 13.
263pub fn radix_13<T>(n: T) -> Radix<T>
264where
265    Radix<T>: Display,
266{
267    Radix::new(n, 13)
268}
269/// Formats a number in base 14.
270pub fn radix_14<T>(n: T) -> Radix<T>
271where
272    Radix<T>: Display,
273{
274    Radix::new(n, 14)
275}
276/// Formats a number in base 15.
277pub fn radix_15<T>(n: T) -> Radix<T>
278where
279    Radix<T>: Display,
280{
281    Radix::new(n, 15)
282}
283/// Formats a number in base 17.
284pub fn radix_17<T>(n: T) -> Radix<T>
285where
286    Radix<T>: Display,
287{
288    Radix::new(n, 17)
289}
290/// Formats a number in base 18.
291pub fn radix_18<T>(n: T) -> Radix<T>
292where
293    Radix<T>: Display,
294{
295    Radix::new(n, 18)
296}
297/// Formats a number in base 19.
298pub fn radix_19<T>(n: T) -> Radix<T>
299where
300    Radix<T>: Display,
301{
302    Radix::new(n, 19)
303}
304/// Formats a number in base 20.
305pub fn radix_20<T>(n: T) -> Radix<T>
306where
307    Radix<T>: Display,
308{
309    Radix::new(n, 20)
310}
311/// Formats a number in base 21.
312pub fn radix_21<T>(n: T) -> Radix<T>
313where
314    Radix<T>: Display,
315{
316    Radix::new(n, 21)
317}
318/// Formats a number in base 22.
319pub fn radix_22<T>(n: T) -> Radix<T>
320where
321    Radix<T>: Display,
322{
323    Radix::new(n, 22)
324}
325/// Formats a number in base 23.
326pub fn radix_23<T>(n: T) -> Radix<T>
327where
328    Radix<T>: Display,
329{
330    Radix::new(n, 23)
331}
332/// Formats a number in base 24.
333pub fn radix_24<T>(n: T) -> Radix<T>
334where
335    Radix<T>: Display,
336{
337    Radix::new(n, 24)
338}
339/// Formats a number in base 25.
340pub fn radix_25<T>(n: T) -> Radix<T>
341where
342    Radix<T>: Display,
343{
344    Radix::new(n, 25)
345}
346/// Formats a number in base 26.
347pub fn radix_26<T>(n: T) -> Radix<T>
348where
349    Radix<T>: Display,
350{
351    Radix::new(n, 26)
352}
353/// Formats a number in base 27.
354pub fn radix_27<T>(n: T) -> Radix<T>
355where
356    Radix<T>: Display,
357{
358    Radix::new(n, 27)
359}
360/// Formats a number in base 28.
361pub fn radix_28<T>(n: T) -> Radix<T>
362where
363    Radix<T>: Display,
364{
365    Radix::new(n, 28)
366}
367/// Formats a number in base 29.
368pub fn radix_29<T>(n: T) -> Radix<T>
369where
370    Radix<T>: Display,
371{
372    Radix::new(n, 29)
373}
374/// Formats a number in base 30.
375pub fn radix_30<T>(n: T) -> Radix<T>
376where
377    Radix<T>: Display,
378{
379    Radix::new(n, 30)
380}
381/// Formats a number in base 31.
382pub fn radix_31<T>(n: T) -> Radix<T>
383where
384    Radix<T>: Display,
385{
386    Radix::new(n, 31)
387}
388/// Formats a number in base 32.
389pub fn radix_32<T>(n: T) -> Radix<T>
390where
391    Radix<T>: Display,
392{
393    Radix::new(n, 32)
394}
395/// Formats a number in base 33.
396pub fn radix_33<T>(n: T) -> Radix<T>
397where
398    Radix<T>: Display,
399{
400    Radix::new(n, 33)
401}
402/// Formats a number in base 34.
403pub fn radix_34<T>(n: T) -> Radix<T>
404where
405    Radix<T>: Display,
406{
407    Radix::new(n, 34)
408}
409/// Formats a number in base 35.
410pub fn radix_35<T>(n: T) -> Radix<T>
411where
412    Radix<T>: Display,
413{
414    Radix::new(n, 35)
415}
416/// Formats a number in base 36.
417pub fn radix_36<T>(n: T) -> Radix<T>
418where
419    Radix<T>: Display,
420{
421    Radix::new(n, 36)
422}