Skip to content

Instantly share code, notes, and snippets.

@brendanzab

brendanzab/fmt_int.rs

Last active Aug 29, 2015
Embed
What would you like to do?
#[feature(macro_rules)];
use std::fmt;
use std::fmt::parse::FlagAlternate;
use std::fmt::parse::FlagSignAwareZeroPad;
use std::fmt::parse::FlagSignPlus;
use std::fmt::parse::{AlignLeft, AlignRight, AlignUnknown};
use std::num::{cast, zero};
use std::uint;
trait Radix {
/// The radix as an integer.
fn radix<T:Int>(_: Option<Self>) -> T;
/// A radix-specific prefix string.
fn prefix(_: Option<Self>) -> &'static str;
/// Converts an integer to a the byte representation specific to the radix.
fn to_byte<T: Int>(_: Option<Self>, x: T) -> u8;
}
/// Generates a phantom type for a specific radix
macro_rules! radix {
($T:ident, $radix:expr, $prefix:expr, $($x:pat => $conv:expr),+) => (
/// A phantom type for specifying a specific radix at compile time
enum $T {}
impl Radix for $T {
fn radix<T:Int>(_: Option<$T>) -> T { cast($radix).unwrap() }
fn prefix(_: Option<$T>) -> &'static str { $prefix }
fn to_byte<T: Int>(_: Option<$T>, x: T) -> u8 {
match x.to_u8().unwrap() {
$($x => $conv,)+
x => fail!("number not in the range 0..{}: {}", $radix - 1, x),
}
}
}
)
}
// Phantom type specifying a binary radix
radix!(Binary, 2, "0b", x @ 0 ..2 => '0' as u8 + x)
// Phantom type specifying an octal radix
radix!(Octal, 8, "0o", x @ 0 ..7 => '0' as u8 + x)
// Phantom type specifying a decimal radix
radix!(Decimal, 10, "", x @ 0 ..9 => '0' as u8 + x)
// Phantom type specifying a hexidecimal radix, with lower-case characters
radix!(LowerHex, 16, "0x", x @ 0 ..9 => '0' as u8 + x,
x @ 10..16 => 'a' as u8 + (x - 10))
// Phantom type specifying a hexidecimal radix, with upper-case characters
radix!(UpperHex, 16, "0X", x @ 0 ..9 => '0' as u8 + x,
x @ 10..16 => 'A' as u8 + (x - 10))
/// Format a generic base integer type. The radix is specified at compile time
/// using phantom types that implement the `Radix` trait.
fn format<T: Int + Pod, R: Radix>(mut x: T, f: &mut fmt::Formatter) -> fmt::Result {
// Write the sign chraracter if it is needed.
let signed;
if x < zero() {
if_ok!(f.buf.write_char('-'));
// Ensure the number is positive so that the digit accumulation
// algorithm works correctly.
x = -x;
signed = true;
} else if f.flags & 1 << (FlagSignPlus as uint) != 0 {
if_ok!(f.buf.write_char('+'));
signed = true;
} else {
signed = false;
}
// Write the radix-specific prefix if it is requested.
if f.flags & 1 << (FlagAlternate as uint) != 0 {
if_ok!(f.buf.write(Radix::prefix(None::<R>).as_bytes()));
}
// Accumulate each digit of the number in reverse order.
let mut buffer = [0u8, ..uint::BITS];
let radix = Radix::radix(None::<R>);
let mut i = 0;
while x > zero() {
let rem = x % radix;
x = x / radix;
buffer[i] = Radix::to_byte(None::<R>, rem);
i += 1;
}
// Writes the number to the buffer.
let write = |f: &mut fmt::Formatter| {
// The bytes were accumulated in reverse order, so write
// them back-to-front.
for x in buffer.slice_to(i).iter().rev() {
if_ok!(f.buf.write_u8(*x));
}
Ok(())
};
// The current accumulated width of the number
let width = i + 1 + if signed { 1 } else { 0 };
// The `width` field is more of a `min-width` parameter at this point.
match f.width {
// If there's no minimum length requirements then we can just
// write the bytes.
None => write(f),
// If we're under the maximum width, check if we're over the minimum
// width, if so then we can also write bytes.
Some(min) if width >= min => write(f),
// If we're under both the maximum and the minimum width, then fill
// up the minimum width with the specified string.
Some(min) => {
if f.flags & 1 << (FlagSignAwareZeroPad as uint) != 0 {
f.fill = '0';
}
if f.align == AlignLeft { if_ok!(write(f)) };
// Fill with the padding character.
for _ in range(0, min - width) {
if_ok!(f.buf.write_char(f.fill));
}
if f.align == AlignRight
|| f.align == AlignUnknown { if_ok!(write(f)) };
Ok(())
}
}
}
macro_rules! impl_fmt_uint {
($T:ident, $Uint:ident) => (
impl fmt::Binary for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Binary>(x, f)
}
}
impl fmt::Signed for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Decimal>(x, f)
}
}
impl fmt::Unsigned for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Decimal>(x, f)
}
}
impl fmt::Octal for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Octal>(x, f)
}
}
impl fmt::UpperHex for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, UpperHex>(x, f)
}
}
impl fmt::LowerHex for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, LowerHex>(x, f)
}
}
)
}
struct U8(u8);
struct U16(u16);
struct U32(u32);
struct U64(u64);
struct Uint(uint);
impl_fmt_uint!(U8, u8)
impl_fmt_uint!(U16, u16)
impl_fmt_uint!(U32, u32)
impl_fmt_uint!(U64, u64)
impl_fmt_uint!(Uint, uint)
macro_rules! impl_fmt_int {
($T:ident, $Int:ident, $Uint:ident) => (
impl fmt::Binary for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Binary>(x as $Uint, f)
}
}
impl fmt::Signed for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Int, Decimal>(x, f)
}
}
impl fmt::Unsigned for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Decimal>(x as $Uint, f)
}
}
impl fmt::Octal for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, Octal>(x as $Uint, f)
}
}
impl fmt::UpperHex for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, UpperHex>(x as $Uint, f)
}
}
impl fmt::LowerHex for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $T(x) = *self; format::<$Uint, LowerHex>(x as $Uint, f)
}
}
)
}
struct I8(i8);
struct I16(i16);
struct I32(i32);
struct I64(i64);
struct Int_(int);
impl_fmt_int!(I8, i8, u8)
impl_fmt_int!(I16, i16, u16)
impl_fmt_int!(I32, i32, u32)
impl_fmt_int!(I64, i64, u64)
impl_fmt_int!(Int_, int, uint)
fn main() {
println!("{:+u}", Uint(12301));
println!("{:u}", Uint(864));
println!("{:010d}", Int_(21056));
println!("{:+010d}", Int_(-21056));
println!("{:+10d}", Int_(21056));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment