diff options
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/fmt/mod.rs | 843 | ||||
| -rw-r--r-- | src/libcore/fmt/num.rs | 470 | ||||
| -rw-r--r-- | src/libcore/fmt/rt.rs | 91 | ||||
| -rw-r--r-- | src/libcore/lib.rs | 3 |
4 files changed, 1407 insertions, 0 deletions
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs new file mode 100644 index 00000000000..0a45712616d --- /dev/null +++ b/src/libcore/fmt/mod.rs @@ -0,0 +1,843 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Utilities for formatting and printing strings + +#![allow(unused_variable)] + +use any; +use cast; +use cell::Cell; +use char::Char; +use container::Container; +use iter::{Iterator, range}; +use kinds::Copy; +use option::{Option, Some, None}; +use owned::Box; +use result; +use result::{Ok, Err}; +use slice::{Vector, ImmutableVector}; +use slice; +use str::StrSlice; +use str; + +pub use self::num::radix; +pub use self::num::Radix; +pub use self::num::RadixFmt; + +macro_rules! write( + ($dst:expr, $($arg:tt)*) => ({ + let dst: &mut ::fmt::FormatWriter = $dst; + format_args!(|args| { ::std::fmt::write(dst, args) }, $($arg)*) + }) +) + +mod num; +mod float; +pub mod rt; + +#[cfg(stage0)] +#[allow(missing_doc)] +pub mod parse { + #[deriving(Eq)] + pub enum Alignment { + AlignLeft, + AlignRight, + AlignUnknown, + } + + pub enum PluralKeyword { + Zero, + One, + Two, + Few, + Many, + } + + pub enum Flag { + FlagSignPlus, + FlagSignMinus, + FlagAlternate, + FlagSignAwareZeroPad, + } +} + +pub type Result = result::Result<(), FormatError>; + +/// dox +pub enum FormatError { + /// dox + WriteError, +} + +/// dox +pub trait FormatWriter { + /// dox + fn write(&mut self, bytes: &[u8]) -> Result; +} + +/// A struct to represent both where to emit formatting strings to and how they +/// should be formatted. A mutable version of this is passed to all formatting +/// traits. +pub struct Formatter<'a> { + /// Flags for formatting (packed version of rt::Flag) + pub flags: uint, + /// Character used as 'fill' whenever there is alignment + pub fill: char, + /// Boolean indication of whether the output should be left-aligned + pub align: rt::Alignment, + /// Optionally specified integer width that the output should be + pub width: Option<uint>, + /// Optionally specified precision for numeric types + pub precision: Option<uint>, + + /// dox + #[cfg(stage0)] + pub buf: &'a mut FormatWriter, + #[cfg(not(stage0))] + buf: &'a mut FormatWriter, + curarg: slice::Items<'a, Argument<'a>>, + args: &'a [Argument<'a>], +} + +enum CurrentlyFormatting<'a> { + Nothing, + RawString(&'a str), + Number(uint), +} + +/// This struct represents the generic "argument" which is taken by the Xprintf +/// family of functions. It contains a function to format the given value. At +/// compile time it is ensured that the function and the value have the correct +/// types, and then this struct is used to canonicalize arguments to one type. +pub struct Argument<'a> { + formatter: extern "Rust" fn(&any::Void, &mut Formatter) -> Result, + value: &'a any::Void, +} + +impl<'a> Arguments<'a> { + /// When using the format_args!() macro, this function is used to generate the + /// Arguments structure. The compiler inserts an `unsafe` block to call this, + /// which is valid because the compiler performs all necessary validation to + /// ensure that the resulting call to format/write would be safe. + #[doc(hidden)] #[inline] + pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>], + args: &'a [Argument<'a>]) -> Arguments<'a> { + Arguments{ fmt: cast::transmute(fmt), args: args } + } +} + +/// This structure represents a safely precompiled version of a format string +/// and its arguments. This cannot be generated at runtime because it cannot +/// safely be done so, so no constructors are given and the fields are private +/// to prevent modification. +/// +/// The `format_args!` macro will safely create an instance of this structure +/// and pass it to a user-supplied function. The macro validates the format +/// string at compile-time so usage of the `write` and `format` functions can +/// be safely performed. +pub struct Arguments<'a> { + fmt: &'a [rt::Piece<'a>], + args: &'a [Argument<'a>], +} + +impl<'a> Show for Arguments<'a> { + fn fmt(&self, fmt: &mut Formatter) -> Result { + write(fmt.buf, self) + } +} + +/// When a format is not otherwise specified, types are formatted by ascribing +/// to this trait. There is not an explicit way of selecting this trait to be +/// used for formatting, it is only if no other format is specified. +pub trait Show { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `b` character +pub trait Bool { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `c` character +pub trait Char { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `i` and `d` characters +pub trait Signed { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `u` character +pub trait Unsigned { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `o` character +pub trait Octal { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `t` character +pub trait Binary { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `x` character +pub trait LowerHex { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `X` character +pub trait UpperHex { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `s` character +pub trait String { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `p` character +pub trait Pointer { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `f` character +pub trait Float { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `e` character +pub trait LowerExp { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +/// Format trait for the `E` character +pub trait UpperExp { + /// Formats the value using the given formatter. + fn fmt(&self, &mut Formatter) -> Result; +} + +// FIXME #11938 - UFCS would make us able call the above methods +// directly Show::show(x, fmt). +macro_rules! uniform_fn_call_workaround { + ($( $name: ident, $trait_: ident; )*) => { + $( + #[doc(hidden)] + pub fn $name<T: $trait_>(x: &T, fmt: &mut Formatter) -> Result { + x.fmt(fmt) + } + )* + } +} +uniform_fn_call_workaround! { + secret_show, Show; + secret_bool, Bool; + secret_char, Char; + secret_signed, Signed; + secret_unsigned, Unsigned; + secret_octal, Octal; + secret_binary, Binary; + secret_lower_hex, LowerHex; + secret_upper_hex, UpperHex; + secret_string, String; + secret_pointer, Pointer; + secret_float, Float; + secret_lower_exp, LowerExp; + secret_upper_exp, UpperExp; +} + +/// The `write` function takes an output stream, a precompiled format string, +/// and a list of arguments. The arguments will be formatted according to the +/// specified format string into the output stream provided. +/// +/// # Arguments +/// +/// * output - the buffer to write output to +/// * args - the precompiled arguments generated by `format_args!` +pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result { + let mut formatter = Formatter { + flags: 0, + width: None, + precision: None, + buf: output, + align: rt::AlignUnknown, + fill: ' ', + args: args.args, + curarg: args.args.iter(), + }; + for piece in args.fmt.iter() { + try!(formatter.run(piece, Nothing)); + } + Ok(()) +} + +impl<'a> Formatter<'a> { + + // First up is the collection of functions used to execute a format string + // at runtime. This consumes all of the compile-time statics generated by + // the format! syntax extension. + + fn run(&mut self, piece: &rt::Piece, cur: CurrentlyFormatting) -> Result { + match *piece { + rt::String(s) => self.buf.write(s.as_bytes()), + rt::CurrentArgument(()) => { + match cur { + Nothing => Ok(()), + Number(n) => secret_show(&radix(n, 10), self), + RawString(s) => self.buf.write(s.as_bytes()), + } + } + rt::Argument(ref arg) => { + // Fill in the format parameters into the formatter + self.fill = arg.format.fill; + self.align = arg.format.align; + self.flags = arg.format.flags; + self.width = self.getcount(&arg.format.width); + self.precision = self.getcount(&arg.format.precision); + + // Extract the correct argument + let value = match arg.position { + rt::ArgumentNext => { *self.curarg.next().unwrap() } + rt::ArgumentIs(i) => self.args[i], + }; + + // Then actually do some printing + match arg.method { + None => (value.formatter)(value.value, self), + Some(ref method) => self.execute(*method, value) + } + } + } + } + + fn getcount(&mut self, cnt: &rt::Count) -> Option<uint> { + match *cnt { + rt::CountIs(n) => { Some(n) } + rt::CountImplied => { None } + rt::CountIsParam(i) => { + let v = self.args[i].value; + unsafe { Some(*(v as *any::Void as *uint)) } + } + rt::CountIsNextParam => { + let v = self.curarg.next().unwrap().value; + unsafe { Some(*(v as *any::Void as *uint)) } + } + } + } + + fn execute(&mut self, method: &rt::Method, arg: Argument) -> Result { + match *method { + // Pluralization is selection upon a numeric value specified as the + // parameter. + rt::Plural(offset, ref selectors, ref default) => { + // This is validated at compile-time to be a pointer to a + // '&uint' value. + let value: &uint = unsafe { cast::transmute(arg.value) }; + let value = *value; + + // First, attempt to match against explicit values without the + // offsetted value + for s in selectors.iter() { + match s.selector { + rt::Literal(val) if value == val => { + return self.runplural(value, s.result); + } + _ => {} + } + } + + // Next, offset the value and attempt to match against the + // keyword selectors. + let value = value - match offset { Some(i) => i, None => 0 }; + for s in selectors.iter() { + let run = match s.selector { + rt::Keyword(rt::Zero) => value == 0, + rt::Keyword(rt::One) => value == 1, + rt::Keyword(rt::Two) => value == 2, + + // FIXME: Few/Many should have a user-specified boundary + // One possible option would be in the function + // pointer of the 'arg: Argument' struct. + rt::Keyword(rt::Few) => value < 8, + rt::Keyword(rt::Many) => value >= 8, + + rt::Literal(..) => false + }; + if run { + return self.runplural(value, s.result); + } + } + + self.runplural(value, *default) + } + + // Select is just a matching against the string specified. + rt::Select(ref selectors, ref default) => { + // This is validated at compile-time to be a pointer to a + // string slice, + let value: & &str = unsafe { cast::transmute(arg.value) }; + let value = *value; + + for s in selectors.iter() { + if s.selector == value { + for piece in s.result.iter() { + try!(self.run(piece, RawString(value))); + } + return Ok(()); + } + } + for piece in default.iter() { + try!(self.run(piece, RawString(value))); + } + Ok(()) + } + } + } + + fn runplural(&mut self, value: uint, pieces: &[rt::Piece]) -> Result { + for piece in pieces.iter() { + try!(self.run(piece, Number(value))); + } + Ok(()) + } + + // Helper methods used for padding and processing formatting arguments that + // all formatting traits can use. + + /// Performs the correct padding for an integer which has already been + /// emitted into a byte-array. The byte-array should *not* contain the sign + /// for the integer, that will be added by this method. + /// + /// # Arguments + /// + /// * is_positive - whether the original integer was positive or not. + /// * prefix - if the '#' character (FlagAlternate) is provided, this + /// is the prefix to put in front of the number. + /// * buf - the byte array that the number has been formatted into + /// + /// This function will correctly account for the flags provided as well as + /// the minimum width. It will not take precision into account. + pub fn pad_integral(&mut self, is_positive: bool, prefix: &str, + buf: &[u8]) -> Result { + use fmt::rt::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad}; + + let mut width = buf.len(); + + let mut sign = None; + if !is_positive { + sign = Some('-'); width += 1; + } else if self.flags & (1 << (FlagSignPlus as uint)) != 0 { + sign = Some('+'); width += 1; + } + + let mut prefixed = false; + if self.flags & (1 << (FlagAlternate as uint)) != 0 { + prefixed = true; width += prefix.len(); + } + + // Writes the sign if it exists, and then the prefix if it was requested + let write_prefix = |f: &mut Formatter| { + for c in sign.move_iter() { + let mut b = [0, ..4]; + let n = c.encode_utf8(b); + try!(f.buf.write(b.slice_to(n))); + } + if prefixed { f.buf.write(prefix.as_bytes()) } + else { Ok(()) } + }; + + // The `width` field is more of a `min-width` parameter at this point. + match self.width { + // If there's no minimum length requirements then we can just + // write the bytes. + None => { + try!(write_prefix(self)); self.buf.write(buf) + } + // Check if we're over the minimum width, if so then we can also + // just write the bytes. + Some(min) if width >= min => { + try!(write_prefix(self)); self.buf.write(buf) + } + // The sign and prefix goes before the padding if the fill character + // is zero + Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => { + self.fill = '0'; + try!(write_prefix(self)); + self.with_padding(min - width, rt::AlignRight, |f| f.buf.write(buf)) + } + // Otherwise, the sign and prefix goes after the padding + Some(min) => { + self.with_padding(min - width, rt::AlignRight, |f| { + try!(write_prefix(f)); f.buf.write(buf) + }) + } + } + } + + /// This function takes a string slice and emits it to the internal buffer + /// after applying the relevant formatting flags specified. The flags + /// recognized for generic strings are: + /// + /// * width - the minimum width of what to emit + /// * fill/align - what to emit and where to emit it if the string + /// provided needs to be padded + /// * precision - the maximum length to emit, the string is truncated if it + /// is longer than this length + /// + /// Notably this function ignored the `flag` parameters + pub fn pad(&mut self, s: &str) -> Result { + // Make sure there's a fast path up front + if self.width.is_none() && self.precision.is_none() { + return self.buf.write(s.as_bytes()); + } + // The `precision` field can be interpreted as a `max-width` for the + // string being formatted + match self.precision { + Some(max) => { + // If there's a maximum width and our string is longer than + // that, then we must always have truncation. This is the only + // case where the maximum length will matter. + let char_len = s.char_len(); + if char_len >= max { + let nchars = ::cmp::min(max, char_len); + return self.buf.write(s.slice_chars(0, nchars).as_bytes()); + } + } + None => {} + } + // The `width` field is more of a `min-width` parameter at this point. + match self.width { + // If we're under the maximum length, and there's no minimum length + // requirements, then we can just emit the string + None => self.buf.write(s.as_bytes()), + // If we're under the maximum width, check if we're over the minimum + // width, if so it's as easy as just emitting the string. + Some(width) if s.char_len() >= width => { + self.buf.write(s.as_bytes()) + } + // If we're under both the maximum and the minimum width, then fill + // up the minimum width with the specified string + some alignment. + Some(width) => { + self.with_padding(width - s.len(), rt::AlignLeft, |me| { + me.buf.write(s.as_bytes()) + }) + } + } + } + + /// Runs a callback, emitting the correct padding either before or + /// afterwards depending on whether right or left alingment is requested. + fn with_padding(&mut self, + padding: uint, + default: rt::Alignment, + f: |&mut Formatter| -> Result) -> Result { + let align = match self.align { + rt::AlignUnknown => default, + rt::AlignLeft | rt::AlignRight => self.align + }; + if align == rt::AlignLeft { + try!(f(self)); + } + let mut fill = [0u8, ..4]; + let len = self.fill.encode_utf8(fill); + for _ in range(0, padding) { + try!(self.buf.write(fill.slice_to(len))); + } + if align == rt::AlignRight { + try!(f(self)); + } + Ok(()) + } + + /// Writes some data to the underlying buffer contained within this + /// formatter. + pub fn write(&mut self, data: &[u8]) -> Result { + self.buf.write(data) + } + + /// Writes some formatted information into this instance + pub fn write_fmt(&mut self, fmt: &Arguments) -> Result { + write(self.buf, fmt) + } +} + +/// This is a function which calls are emitted to by the compiler itself to +/// create the Argument structures that are passed into the `format` function. +#[doc(hidden)] #[inline] +pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter) -> Result, + t: &'a T) -> Argument<'a> { + unsafe { + Argument { + formatter: cast::transmute(f), + value: cast::transmute(t) + } + } +} + +/// When the compiler determines that the type of an argument *must* be a string +/// (such as for select), then it invokes this method. +#[doc(hidden)] #[inline] +pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> { + argument(secret_string, s) +} + +/// When the compiler determines that the type of an argument *must* be a uint +/// (such as for plural), then it invokes this method. +#[doc(hidden)] #[inline] +pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> { + argument(secret_unsigned, s) +} + +// Implementations of the core formatting traits + +impl<T: Show> Show for @T { + fn fmt(&self, f: &mut Formatter) -> Result { secret_show(&**self, f) } +} +impl<T: Show> Show for Box<T> { + fn fmt(&self, f: &mut Formatter) -> Result { secret_show(&**self, f) } +} +impl<'a, T: Show> Show for &'a T { + fn fmt(&self, f: &mut Formatter) -> Result { secret_show(*self, f) } +} + +impl Bool for bool { + fn fmt(&self, f: &mut Formatter) -> Result { + secret_string(&(if *self {"true"} else {"false"}), f) + } +} + +impl<'a, T: str::Str> String for T { + fn fmt(&self, f: &mut Formatter) -> Result { + f.pad(self.as_slice()) + } +} + +impl Char for char { + fn fmt(&self, f: &mut Formatter) -> Result { + let mut utf8 = [0u8, ..4]; + let amt = self.encode_utf8(utf8); + let s: &str = unsafe { cast::transmute(utf8.slice_to(amt)) }; + secret_string(&s, f) + } +} + +impl<T> Pointer for *T { + fn fmt(&self, f: &mut Formatter) -> Result { + f.flags |= 1 << (rt::FlagAlternate as uint); + secret_lower_hex::<uint>(&(*self as uint), f) + } +} +impl<T> Pointer for *mut T { + fn fmt(&self, f: &mut Formatter) -> Result { + secret_pointer::<*T>(&(*self as *T), f) + } +} +impl<'a, T> Pointer for &'a T { + fn fmt(&self, f: &mut Formatter) -> Result { + secret_pointer::<*T>(&(&**self as *T), f) + } +} +impl<'a, T> Pointer for &'a mut T { + fn fmt(&self, f: &mut Formatter) -> Result { + secret_pointer::<*T>(&(&**self as *T), f) + } +} + +macro_rules! floating(($ty:ident) => { + impl Float for $ty { + fn fmt(&self, fmt: &mut Formatter) -> Result { + use num::Signed; + + let digits = match fmt.precision { + Some(i) => float::DigExact(i), + None => float::DigMax(6), + }; + float::float_to_str_bytes_common(self.abs(), + 10, + true, + float::SignNeg, + digits, + float::ExpNone, + false, + |bytes| { + fmt.pad_integral(*self >= 0.0, "", bytes) + }) + } + } + + impl LowerExp for $ty { + fn fmt(&self, fmt: &mut Formatter) -> Result { + use num::Signed; + + let digits = match fmt.precision { + Some(i) => float::DigExact(i), + None => float::DigMax(6), + }; + float::float_to_str_bytes_common(self.abs(), + 10, + true, + float::SignNeg, + digits, + float::ExpDec, + false, + |bytes| { + fmt.pad_integral(*self >= 0.0, "", bytes) + }) + } + } + + impl UpperExp for $ty { + fn fmt(&self, fmt: &mut Formatter) -> Result { + use num::Signed; + + let digits = match fmt.precision { + Some(i) => float::DigExact(i), + None => float::DigMax(6), + }; + float::float_to_str_bytes_common(self.abs(), + 10, + true, + float::SignNeg, + digits, + float::ExpDec, + true, + |bytes| { + fmt.pad_integral(*self >= 0.0, "", bytes) + }) + } + } +}) +floating!(f32) +floating!(f64) + +// Implementation of Show for various core types + +macro_rules! delegate(($ty:ty to $other:ident) => { + impl<'a> Show for $ty { + fn fmt(&self, f: &mut Formatter) -> Result { + (concat_idents!(secret_, $other)(self, f)) + } + } +}) +delegate!(~str to string) +delegate!(&'a str to string) +delegate!(bool to bool) +delegate!(char to char) +delegate!(f32 to float) +delegate!(f64 to float) + +impl<T> Show for *T { + fn fmt(&self, f: &mut Formatter) -> Result { secret_pointer(self, f) } +} +impl<T> Show for *mut T { + fn fmt(&self, f: &mut Formatter) -> Result { secret_pointer(self, f) } +} + +macro_rules! peel(($name:ident, $($other:ident,)*) => (tuple!($($other,)*))) + +macro_rules! tuple ( + () => (); + ( $($name:ident,)+ ) => ( + impl<$($name:Show),*> Show for ($($name,)*) { + #[allow(uppercase_variables, dead_assignment)] + fn fmt(&self, f: &mut Formatter) -> Result { + try!(write!(f.buf, "(")); + let ($(ref $name,)*) = *self; + let mut n = 0; + $( + if n > 0 { + try!(write!(f.buf, ", ")); + } + try!(write!(f.buf, "{}", *$name)); + n += 1; + )* + if n == 1 { + try!(write!(f.buf, ",")); + } + write!(f.buf, ")") + } + } + peel!($($name,)*) + ) +) + +tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } + +impl Show for Box<any::Any> { + fn fmt(&self, f: &mut Formatter) -> Result { f.pad("Box<Any>") } +} + +impl<'a> Show for &'a any::Any { + fn fmt(&self, f: &mut Formatter) -> Result { f.pad("&Any") } +} + +impl<'a, T: Show> Show for &'a [T] { + fn fmt(&self, f: &mut Formatter) -> Result { + if f.flags & (1 << (rt::FlagAlternate as uint)) == 0 { + try!(write!(f.buf, "[")); + } + let mut is_first = true; + for x in self.iter() { + if is_first { + is_first = false; + } else { + try!(write!(f.buf, ", ")); + } + try!(write!(f.buf, "{}", *x)) + } + if f.flags & (1 << (rt::FlagAlternate as uint)) == 0 { + try!(write!(f.buf, "]")); + } + Ok(()) + } +} + +impl<'a, T: Show> Show for &'a mut [T] { + fn fmt(&self, f: &mut Formatter) -> Result { + secret_show(&self.as_slice(), f) + } +} + +impl<T: Show> Show for ~[T] { + fn fmt(&self, f: &mut Formatter) -> Result { + secret_show(&self.as_slice(), f) + } +} + +impl Show for () { + fn fmt(&self, f: &mut Formatter) -> Result { + f.pad("()") + } +} + +impl<T: Copy + Show> Show for Cell<T> { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f.buf, r"Cell \{ value: {} \}", self.get()) + } +} + +// If you expected tests to be here, look instead at the run-pass/ifmt.rs test, +// it's a lot easier than creating all of the rt::Piece structures here. diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs new file mode 100644 index 00000000000..12adcee2f0f --- /dev/null +++ b/src/libcore/fmt/num.rs @@ -0,0 +1,470 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Integer and floating-point number formatting + +// FIXME: #6220 Implement floating point formatting + +#![allow(unsigned_negate)] + +use container::Container; +use fmt; +use iter::{Iterator, DoubleEndedIterator}; +use num::{Int, cast, zero}; +use option::{Some, None}; +use slice::{ImmutableVector, MutableVector}; + +/// A type that represents a specific radix +trait GenericRadix { + /// The number of digits. + fn base(&self) -> u8; + + /// A radix-specific prefix string. + fn prefix(&self) -> &'static str { "" } + + /// Converts an integer to corresponding radix digit. + fn digit(&self, x: u8) -> u8; + + /// Format an integer using the radix using a formatter. + fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result { + // The radix can be as low as 2, so we need a buffer of at least 64 + // characters for a base 2 number. + let mut buf = [0u8, ..64]; + let base = cast(self.base()).unwrap(); + let mut curr = buf.len(); + let is_positive = x >= zero(); + if is_positive { + // Accumulate each digit of the number from the least significant + // to the most significant figure. + for byte in buf.mut_iter().rev() { + let n = x % base; // Get the current place value. + x = x / base; // Deaccumulate the number. + *byte = self.digit(cast(n).unwrap()); // Store the digit in the buffer. + curr -= 1; + if x == zero() { break; } // No more digits left to accumulate. + } + } else { + // Do the same as above, but accounting for two's complement. + for byte in buf.mut_iter().rev() { + let n = -(x % base); // Get the current place value. + x = x / base; // Deaccumulate the number. + *byte = self.digit(cast(n).unwrap()); // Store the digit in the buffer. + curr -= 1; + if x == zero() { break; } // No more digits left to accumulate. + } + } + f.pad_integral(is_positive, self.prefix(), buf.slice_from(curr)) + } +} + +/// A binary (base 2) radix +#[deriving(Clone, Eq)] +struct Binary; + +/// An octal (base 8) radix +#[deriving(Clone, Eq)] +struct Octal; + +/// A decimal (base 10) radix +#[deriving(Clone, Eq)] +struct Decimal; + +/// A hexadecimal (base 16) radix, formatted with lower-case characters +#[deriving(Clone, Eq)] +struct LowerHex; + +/// A hexadecimal (base 16) radix, formatted with upper-case characters +#[deriving(Clone, Eq)] +pub struct UpperHex; + +macro_rules! radix { + ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { + impl GenericRadix for $T { + fn base(&self) -> u8 { $base } + fn prefix(&self) -> &'static str { $prefix } + fn digit(&self, x: u8) -> u8 { + match x { + $($x => $conv,)+ + x => fail!("number not in the range 0..{}: {}", self.base() - 1, x), + } + } + } + } +} + +radix!(Binary, 2, "0b", x @ 0 .. 2 => '0' as u8 + x) +radix!(Octal, 8, "0o", x @ 0 .. 7 => '0' as u8 + x) +radix!(Decimal, 10, "", x @ 0 .. 9 => '0' as u8 + x) +radix!(LowerHex, 16, "0x", x @ 0 .. 9 => '0' as u8 + x, + x @ 10 ..15 => 'a' as u8 + (x - 10)) +radix!(UpperHex, 16, "0x", x @ 0 .. 9 => '0' as u8 + x, + x @ 10 ..15 => 'A' as u8 + (x - 10)) + +/// A radix with in the range of `2..36`. +#[deriving(Clone, Eq)] +pub struct Radix { + base: u8, +} + +impl Radix { + fn new(base: u8) -> Radix { + assert!(2 <= base && base <= 36, "the base must be in the range of 0..36: {}", base); + Radix { base: base } + } +} + +impl GenericRadix for Radix { + fn base(&self) -> u8 { self.base } + fn digit(&self, x: u8) -> u8 { + match x { + x @ 0 ..9 => '0' as u8 + x, + x if x < self.base() => 'a' as u8 + (x - 10), + x => fail!("number not in the range 0..{}: {}", self.base() - 1, x), + } + } +} + +/// A helper type for formatting radixes. +pub struct RadixFmt<T, R>(T, R); + +/// Constructs a radix formatter in the range of `2..36`. +/// +/// # Example +/// +/// ~~~ +/// use std::fmt::radix; +/// assert_eq!(format!("{}", radix(55, 36)), "1j".to_owned()); +/// ~~~ +pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> { + RadixFmt(x, Radix::new(base)) +} + +macro_rules! radix_fmt { + ($T:ty as $U:ty, $fmt:ident) => { + impl fmt::Show for RadixFmt<$T, Radix> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { RadixFmt(ref x, radix) => radix.$fmt(*x as $U, f) } + } + } + } +} +macro_rules! int_base { + ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { + impl fmt::$Trait for $T { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + $Radix.fmt_int(*self as $U, f) + } + } + } +} +macro_rules! integer { + ($Int:ident, $Uint:ident) => { + int_base!(Show for $Int as $Int -> Decimal) + int_base!(Signed for $Int as $Int -> Decimal) + int_base!(Binary for $Int as $Uint -> Binary) + int_base!(Octal for $Int as $Uint -> Octal) + int_base!(LowerHex for $Int as $Uint -> LowerHex) + int_base!(UpperHex for $Int as $Uint -> UpperHex) + radix_fmt!($Int as $Uint, fmt_int) + + int_base!(Show for $Uint as $Uint -> Decimal) + int_base!(Unsigned for $Uint as $Uint -> Decimal) + int_base!(Binary for $Uint as $Uint -> Binary) + int_base!(Octal for $Uint as $Uint -> Octal) + int_base!(LowerHex for $Uint as $Uint -> LowerHex) + int_base!(UpperHex for $Uint as $Uint -> UpperHex) + radix_fmt!($Uint as $Uint, fmt_int) + } +} +integer!(int, uint) +integer!(i8, u8) +integer!(i16, u16) +integer!(i32, u32) +integer!(i64, u64) + +#[cfg(test)] +mod tests { + use fmt::radix; + use super::{Binary, Octal, Decimal, LowerHex, UpperHex}; + use super::{GenericRadix, Radix}; + use str::StrAllocating; + + #[test] + fn test_radix_base() { + assert_eq!(Binary.base(), 2); + assert_eq!(Octal.base(), 8); + assert_eq!(Decimal.base(), 10); + assert_eq!(LowerHex.base(), 16); + assert_eq!(UpperHex.base(), 16); + assert_eq!(Radix { base: 36 }.base(), 36); + } + + #[test] + fn test_radix_prefix() { + assert_eq!(Binary.prefix(), "0b"); + assert_eq!(Octal.prefix(), "0o"); + assert_eq!(Decimal.prefix(), ""); + assert_eq!(LowerHex.prefix(), "0x"); + assert_eq!(UpperHex.prefix(), "0x"); + assert_eq!(Radix { base: 36 }.prefix(), ""); + } + + #[test] + fn test_radix_digit() { + assert_eq!(Binary.digit(0), '0' as u8); + assert_eq!(Binary.digit(2), '2' as u8); + assert_eq!(Octal.digit(0), '0' as u8); + assert_eq!(Octal.digit(7), '7' as u8); + assert_eq!(Decimal.digit(0), '0' as u8); + assert_eq!(Decimal.digit(9), '9' as u8); + assert_eq!(LowerHex.digit(0), '0' as u8); + assert_eq!(LowerHex.digit(10), 'a' as u8); + assert_eq!(LowerHex.digit(15), 'f' as u8); + assert_eq!(UpperHex.digit(0), '0' as u8); + assert_eq!(UpperHex.digit(10), 'A' as u8); + assert_eq!(UpperHex.digit(15), 'F' as u8); + assert_eq!(Radix { base: 36 }.digit(0), '0' as u8); + assert_eq!(Radix { base: 36 }.digit(15), 'f' as u8); + assert_eq!(Radix { base: 36 }.digit(35), 'z' as u8); + } + + #[test] + #[should_fail] + fn test_hex_radix_digit_overflow() { + let _ = LowerHex.digit(16); + } + + #[test] + fn test_format_int() { + // Formatting integers should select the right implementation based off + // the type of the argument. Also, hex/octal/binary should be defined + // for integers, but they shouldn't emit the negative sign. + assert_eq!(format!("{}", 1i), "1".to_owned()); + assert_eq!(format!("{}", 1i8), "1".to_owned()); + assert_eq!(format!("{}", 1i16), "1".to_owned()); + assert_eq!(format!("{}", 1i32), "1".to_owned()); + assert_eq!(format!("{}", 1i64), "1".to_owned()); + assert_eq!(format!("{:d}", -1i), "-1".to_owned()); + assert_eq!(format!("{:d}", -1i8), "-1".to_owned()); + assert_eq!(format!("{:d}", -1i16), "-1".to_owned()); + assert_eq!(format!("{:d}", -1i32), "-1".to_owned()); + assert_eq!(format!("{:d}", -1i64), "-1".to_owned()); + assert_eq!(format!("{:t}", 1i), "1".to_owned()); + assert_eq!(format!("{:t}", 1i8), "1".to_owned()); + assert_eq!(format!("{:t}", 1i16), "1".to_owned()); + assert_eq!(format!("{:t}", 1i32), "1".to_owned()); + assert_eq!(format!("{:t}", 1i64), "1".to_owned()); + assert_eq!(format!("{:x}", 1i), "1".to_owned()); + assert_eq!(format!("{:x}", 1i8), "1".to_owned()); + assert_eq!(format!("{:x}", 1i16), "1".to_owned()); + assert_eq!(format!("{:x}", 1i32), "1".to_owned()); + assert_eq!(format!("{:x}", 1i64), "1".to_owned()); + assert_eq!(format!("{:X}", 1i), "1".to_owned()); + assert_eq!(format!("{:X}", 1i8), "1".to_owned()); + assert_eq!(format!("{:X}", 1i16), "1".to_owned()); + assert_eq!(format!("{:X}", 1i32), "1".to_owned()); + assert_eq!(format!("{:X}", 1i64), "1".to_owned()); + assert_eq!(format!("{:o}", 1i), "1".to_owned()); + assert_eq!(format!("{:o}", 1i8), "1".to_owned()); + assert_eq!(format!("{:o}", 1i16), "1".to_owned()); + assert_eq!(format!("{:o}", 1i32), "1".to_owned()); + assert_eq!(format!("{:o}", 1i64), "1".to_owned()); + + assert_eq!(format!("{}", 1u), "1".to_owned()); + assert_eq!(format!("{}", 1u8), "1".to_owned()); + assert_eq!(format!("{}", 1u16), "1".to_owned()); + assert_eq!(format!("{}", 1u32), "1".to_owned()); + assert_eq!(format!("{}", 1u64), "1".to_owned()); + assert_eq!(format!("{:u}", 1u), "1".to_owned()); + assert_eq!(format!("{:u}", 1u8), "1".to_owned()); + assert_eq!(format!("{:u}", 1u16), "1".to_owned()); + assert_eq!(format!("{:u}", 1u32), "1".to_owned()); + assert_eq!(format!("{:u}", 1u64), "1".to_owned()); + assert_eq!(format!("{:t}", 1u), "1".to_owned()); + assert_eq!(format!("{:t}", 1u8), "1".to_owned()); + assert_eq!(format!("{:t}", 1u16), "1".to_owned()); + assert_eq!(format!("{:t}", 1u32), "1".to_owned()); + assert_eq!(format!("{:t}", 1u64), "1".to_owned()); + assert_eq!(format!("{:x}", 1u), "1".to_owned()); + assert_eq!(format!("{:x}", 1u8), "1".to_owned()); + assert_eq!(format!("{:x}", 1u16), "1".to_owned()); + assert_eq!(format!("{:x}", 1u32), "1".to_owned()); + assert_eq!(format!("{:x}", 1u64), "1".to_owned()); + assert_eq!(format!("{:X}", 1u), "1".to_owned()); + assert_eq!(format!("{:X}", 1u8), "1".to_owned()); + assert_eq!(format!("{:X}", 1u16), "1".to_owned()); + assert_eq!(format!("{:X}", 1u32), "1".to_owned()); + assert_eq!(format!("{:X}", 1u64), "1".to_owned()); + assert_eq!(format!("{:o}", 1u), "1".to_owned()); + assert_eq!(format!("{:o}", 1u8), "1".to_owned()); + assert_eq!(format!("{:o}", 1u16), "1".to_owned()); + assert_eq!(format!("{:o}", 1u32), "1".to_owned()); + assert_eq!(format!("{:o}", 1u64), "1".to_owned()); + + // Test a larger number + assert_eq!(format!("{:t}", 55), "110111".to_owned()); + assert_eq!(format!("{:o}", 55), "67".to_owned()); + assert_eq!(format!("{:d}", 55), "55".to_owned()); + assert_eq!(format!("{:x}", 55), "37".to_owned()); + assert_eq!(format!("{:X}", 55), "37".to_owned()); + } + + #[test] + fn test_format_int_zero() { + assert_eq!(format!("{}", 0i), "0".to_owned()); + assert_eq!(format!("{:d}", 0i), "0".to_owned()); + assert_eq!(format!("{:t}", 0i), "0".to_owned()); + assert_eq!(format!("{:o}", 0i), "0".to_owned()); + assert_eq!(format!("{:x}", 0i), "0".to_owned()); + assert_eq!(format!("{:X}", 0i), "0".to_owned()); + + assert_eq!(format!("{}", 0u), "0".to_owned()); + assert_eq!(format!("{:u}", 0u), "0".to_owned()); + assert_eq!(format!("{:t}", 0u), "0".to_owned()); + assert_eq!(format!("{:o}", 0u), "0".to_owned()); + assert_eq!(format!("{:x}", 0u), "0".to_owned()); + assert_eq!(format!("{:X}", 0u), "0".to_owned()); + } + + #[test] + fn test_format_int_flags() { + assert_eq!(format!("{:3d}", 1), " 1".to_owned()); + assert_eq!(format!("{:>3d}", 1), " 1".to_owned()); + assert_eq!(format!("{:>+3d}", 1), " +1".to_owned()); + assert_eq!(format!("{:<3d}", 1), "1 ".to_owned()); + assert_eq!(format!("{:#d}", 1), "1".to_owned()); + assert_eq!(format!("{:#x}", 10), "0xa".to_owned()); + assert_eq!(format!("{:#X}", 10), "0xA".to_owned()); + assert_eq!(format!("{:#5x}", 10), " 0xa".to_owned()); + assert_eq!(format!("{:#o}", 10), "0o12".to_owned()); + assert_eq!(format!("{:08x}", 10), "0000000a".to_owned()); + assert_eq!(format!("{:8x}", 10), " a".to_owned()); + assert_eq!(format!("{:<8x}", 10), "a ".to_owned()); + assert_eq!(format!("{:>8x}", 10), " a".to_owned()); + assert_eq!(format!("{:#08x}", 10), "0x00000a".to_owned()); + assert_eq!(format!("{:08d}", -10), "-0000010".to_owned()); + assert_eq!(format!("{:x}", -1u8), "ff".to_owned()); + assert_eq!(format!("{:X}", -1u8), "FF".to_owned()); + assert_eq!(format!("{:t}", -1u8), "11111111".to_owned()); + assert_eq!(format!("{:o}", -1u8), "377".to_owned()); + assert_eq!(format!("{:#x}", -1u8), "0xff".to_owned()); + assert_eq!(format!("{:#X}", -1u8), "0xFF".to_owned()); + assert_eq!(format!("{:#t}", -1u8), "0b11111111".to_owned()); + assert_eq!(format!("{:#o}", -1u8), "0o377".to_owned()); + } + + #[test] + fn test_format_int_sign_padding() { + assert_eq!(format!("{:+5d}", 1), " +1".to_owned()); + assert_eq!(format!("{:+5d}", -1), " -1".to_owned()); + assert_eq!(format!("{:05d}", 1), "00001".to_owned()); + assert_eq!(format!("{:05d}", -1), "-0001".to_owned()); + assert_eq!(format!("{:+05d}", 1), "+0001".to_owned()); + assert_eq!(format!("{:+05d}", -1), "-0001".to_owned()); + } + + #[test] + fn test_format_int_twos_complement() { + use {i8, i16, i32, i64}; + assert_eq!(format!("{}", i8::MIN), "-128".to_owned()); + assert_eq!(format!("{}", i16::MIN), "-32768".to_owned()); + assert_eq!(format!("{}", i32::MIN), "-2147483648".to_owned()); + assert_eq!(format!("{}", i64::MIN), "-9223372036854775808".to_owned()); + } + + #[test] + fn test_format_radix() { + assert_eq!(format!("{:04}", radix(3, 2)), "0011".to_owned()); + assert_eq!(format!("{}", radix(55, 36)), "1j".to_owned()); + } + + #[test] + #[should_fail] + fn test_radix_base_too_large() { + let _ = radix(55, 37); + } +} + +#[cfg(test)] +mod bench { + extern crate test; + + mod uint { + use super::test::Bencher; + use fmt::radix; + use rand::{XorShiftRng, Rng}; + + #[bench] + fn format_bin(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:t}", rng.gen::<uint>()); }) + } + + #[bench] + fn format_oct(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:o}", rng.gen::<uint>()); }) + } + + #[bench] + fn format_dec(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:u}", rng.gen::<uint>()); }) + } + + #[bench] + fn format_hex(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:x}", rng.gen::<uint>()); }) + } + + #[bench] + fn format_base_36(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{}", radix(rng.gen::<uint>(), 36)); }) + } + } + + mod int { + use super::test::Bencher; + use fmt::radix; + use rand::{XorShiftRng, Rng}; + + #[bench] + fn format_bin(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:t}", rng.gen::<int>()); }) + } + + #[bench] + fn format_oct(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:o}", rng.gen::<int>()); }) + } + + #[bench] + fn format_dec(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:d}", rng.gen::<int>()); }) + } + + #[bench] + fn format_hex(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{:x}", rng.gen::<int>()); }) + } + + #[bench] + fn format_base_36(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + b.iter(|| { format!("{}", radix(rng.gen::<int>(), 36)); }) + } + } +} diff --git a/src/libcore/fmt/rt.rs b/src/libcore/fmt/rt.rs new file mode 100644 index 00000000000..00c8661c8e3 --- /dev/null +++ b/src/libcore/fmt/rt.rs @@ -0,0 +1,91 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This is an internal module used by the ifmt! runtime. These structures are +//! emitted to static arrays to precompile format strings ahead of time. +//! +//! These definitions are similar to their `ct` equivalents, but differ in that +//! these can be statically allocated and are slightly optimized for the runtime + +#![allow(missing_doc)] +#![doc(hidden)] + +use option::Option; + +pub enum Piece<'a> { + String(&'a str), + // FIXME(#8259): this shouldn't require the unit-value here + CurrentArgument(()), + Argument(Argument<'a>), +} + +pub struct Argument<'a> { + pub position: Position, + pub format: FormatSpec, + pub method: Option<&'a Method<'a>> +} + +pub struct FormatSpec { + pub fill: char, + pub align: Alignment, + pub flags: uint, + pub precision: Count, + pub width: Count, +} + +#[deriving(Eq)] +pub enum Alignment { + AlignLeft, + AlignRight, + AlignUnknown, +} + +pub enum Count { + CountIs(uint), CountIsParam(uint), CountIsNextParam, CountImplied, +} + +pub enum Position { + ArgumentNext, ArgumentIs(uint) +} + +pub enum Flag { + FlagSignPlus, + FlagSignMinus, + FlagAlternate, + FlagSignAwareZeroPad, +} + +pub enum Method<'a> { + Plural(Option<uint>, &'a [PluralArm<'a>], &'a [Piece<'a>]), + Select(&'a [SelectArm<'a>], &'a [Piece<'a>]), +} + +pub enum PluralSelector { + Keyword(PluralKeyword), + Literal(uint), +} + +pub enum PluralKeyword { + Zero, + One, + Two, + Few, + Many, +} + +pub struct PluralArm<'a> { + pub selector: PluralSelector, + pub result: &'a [Piece<'a>], +} + +pub struct SelectArm<'a> { + pub selector: &'a str, + pub result: &'a [Piece<'a>], +} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 22719dc9f2d..a126766b0de 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -117,6 +117,7 @@ pub mod result; pub mod slice; pub mod str; pub mod tuple; +pub mod fmt; // FIXME: this module should not exist. Once owned allocations are no longer a // language type, this module can move outside to the owned allocation @@ -130,7 +131,9 @@ mod core { mod std { pub use clone; pub use cmp; + pub use fmt; pub use kinds; + pub use option; #[cfg(test)] pub use realstd::fmt; // needed for fail!() #[cfg(test)] pub use realstd::rt; // needed for fail!() |
