Merge remote-tracking branch 'remotes/origin/master' into remove-str-trailing-nulls
This commit is contained in:
commit
56730c094c
159 changed files with 3803 additions and 910 deletions
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
use clone::Clone;
|
||||
use container::Container;
|
||||
use iterator::{Iterator, range};
|
||||
use iterator::Iterator;
|
||||
use option::{Option, Some, None};
|
||||
use sys;
|
||||
use unstable::raw::Repr;
|
||||
|
|
@ -92,8 +92,8 @@ pub fn append<T:Clone>(lhs: @[T], rhs: &[T]) -> @[T] {
|
|||
for x in lhs.iter() {
|
||||
push((*x).clone());
|
||||
}
|
||||
for i in range(0u, rhs.len()) {
|
||||
push(rhs[i].clone());
|
||||
for elt in rhs.iter() {
|
||||
push(elt.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ mod pipesy {
|
|||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod oneshot {
|
||||
priv use std::kinds::Send;
|
||||
use std::kinds::Send;
|
||||
use ptr::to_mut_unsafe_ptr;
|
||||
|
||||
pub fn init<T: Send>() -> (server::Oneshot<T>, client::Oneshot<T>) {
|
||||
|
|
@ -341,7 +341,7 @@ mod pipesy {
|
|||
#[allow(non_camel_case_types)]
|
||||
pub mod client {
|
||||
|
||||
priv use std::kinds::Send;
|
||||
use std::kinds::Send;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub fn try_send<T: Send>(pipe: Oneshot<T>, x_0: T) ->
|
||||
|
|
@ -489,7 +489,7 @@ mod pipesy {
|
|||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod streamp {
|
||||
priv use std::kinds::Send;
|
||||
use std::kinds::Send;
|
||||
|
||||
pub fn init<T: Send>() -> (server::Open<T>, client::Open<T>) {
|
||||
pub use std::pipes::HasBuffer;
|
||||
|
|
@ -501,7 +501,7 @@ mod pipesy {
|
|||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod client {
|
||||
priv use std::kinds::Send;
|
||||
use std::kinds::Send;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub fn try_data<T: Send>(pipe: Open<T>, x_0: T) ->
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use vec;
|
|||
use vec::{OwnedVector, ImmutableVector};
|
||||
|
||||
/// `Either` is a type that represents one of two alternatives
|
||||
#[deriving(Clone, Eq)]
|
||||
#[deriving(Clone, Eq, IterBytes)]
|
||||
pub enum Either<L, R> {
|
||||
Left(L),
|
||||
Right(R)
|
||||
|
|
|
|||
368
src/libstd/fmt/mod.rs
Normal file
368
src/libstd/fmt/mod.rs
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
// 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.
|
||||
|
||||
use prelude::*;
|
||||
|
||||
use cast;
|
||||
use int;
|
||||
use rt::io::Decorator;
|
||||
use rt::io::mem::MemWriter;
|
||||
use rt::io;
|
||||
use str;
|
||||
use sys;
|
||||
use uint;
|
||||
use util;
|
||||
use vec;
|
||||
|
||||
pub mod parse;
|
||||
pub mod rt;
|
||||
|
||||
/// 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<'self> {
|
||||
/// Flags for formatting (packed version of rt::Flag)
|
||||
flags: uint,
|
||||
/// Character used as 'fill' whenever there is alignment
|
||||
fill: char,
|
||||
/// Boolean indication of whether the output should be left-aligned
|
||||
alignleft: bool,
|
||||
/// Optionally specified integer width that the output should be
|
||||
width: Option<uint>,
|
||||
/// Optionally specified precision for numeric types
|
||||
precision: Option<uint>,
|
||||
|
||||
/// Output buffer.
|
||||
buf: &'self mut io::Writer,
|
||||
|
||||
priv curarg: vec::VecIterator<'self, Argument<'self>>,
|
||||
priv args: &'self [Argument<'self>],
|
||||
}
|
||||
|
||||
/// 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<'self> {
|
||||
priv formatter: extern "Rust" fn(&util::Void, &mut Formatter),
|
||||
priv value: &'self util::Void,
|
||||
}
|
||||
|
||||
#[allow(missing_doc)]
|
||||
pub trait Bool { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Char { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Signed { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Unsigned { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Octal { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Binary { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait LowerHex { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait UpperHex { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait String { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Poly { fn fmt(&Self, &mut Formatter); }
|
||||
#[allow(missing_doc)]
|
||||
pub trait Pointer { fn fmt(&Self, &mut Formatter); }
|
||||
|
||||
/// The sprintf function takes a precompiled format string and a list of
|
||||
/// arguments, to return the resulting formatted string.
|
||||
///
|
||||
/// This is currently an unsafe function because the types of all arguments
|
||||
/// aren't verified by immediate callers of this function. This currently does
|
||||
/// not validate that the correct types of arguments are specified for each
|
||||
/// format specifier, nor that each argument itself contains the right function
|
||||
/// for formatting the right type value. Because of this, the function is marked
|
||||
/// as `unsafe` if this is being called manually.
|
||||
///
|
||||
/// Thankfully the rust compiler provides the macro `ifmt!` which will perform
|
||||
/// all of this validation at compile-time and provides a safe interface for
|
||||
/// invoking this function.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * fmts - the precompiled format string to emit.
|
||||
/// * args - the list of arguments to the format string. These are only the
|
||||
/// positional arguments (not named)
|
||||
///
|
||||
/// Note that this function assumes that there are enough arguments for the
|
||||
/// format string.
|
||||
pub unsafe fn sprintf(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
|
||||
let output = MemWriter::new();
|
||||
{
|
||||
let mut formatter = Formatter {
|
||||
flags: 0,
|
||||
width: None,
|
||||
precision: None,
|
||||
// FIXME(#8248): shouldn't need a transmute
|
||||
buf: cast::transmute(&output as &io::Writer),
|
||||
alignleft: false,
|
||||
fill: ' ',
|
||||
args: args,
|
||||
curarg: args.iter(),
|
||||
};
|
||||
for piece in fmt.iter() {
|
||||
formatter.run(piece, None);
|
||||
}
|
||||
}
|
||||
return str::from_bytes_owned(output.inner());
|
||||
}
|
||||
|
||||
impl<'self> Formatter<'self> {
|
||||
fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
|
||||
let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
|
||||
match *cnt {
|
||||
parse::CountIs(n) => { *slot = Some(n); }
|
||||
parse::CountImplied => { *slot = None; }
|
||||
parse::CountIsParam(i) => {
|
||||
let v = self.args[i].value;
|
||||
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
|
||||
}
|
||||
parse::CountIsNextParam => {
|
||||
let v = self.curarg.next().unwrap().value;
|
||||
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match *piece {
|
||||
rt::String(s) => { self.buf.write(s.as_bytes()); }
|
||||
rt::CurrentArgument(()) => { self.buf.write(cur.unwrap().as_bytes()); }
|
||||
rt::Argument(ref arg) => {
|
||||
// Fill in the format parameters into the formatter
|
||||
self.fill = arg.format.fill;
|
||||
self.alignleft = arg.format.alignleft;
|
||||
self.flags = arg.format.flags;
|
||||
setcount(&mut self.width, &arg.format.width);
|
||||
setcount(&mut self.precision, &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 execute(&mut self, method: &rt::Method, arg: Argument) {
|
||||
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 {
|
||||
Right(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 {
|
||||
Left(parse::Zero) => value == 0,
|
||||
Left(parse::One) => value == 1,
|
||||
Left(parse::Two) => value == 2,
|
||||
|
||||
// XXX: Few/Many should have a user-specified boundary
|
||||
// One possible option would be in the function
|
||||
// pointer of the 'arg: Argument' struct.
|
||||
Left(parse::Few) => value < 8,
|
||||
Left(parse::Many) => value >= 8,
|
||||
|
||||
Right(*) => 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() {
|
||||
self.run(piece, Some(value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
for piece in default.iter() {
|
||||
self.run(piece, Some(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn runplural(&mut self, value: uint, pieces: &[rt::Piece]) {
|
||||
do uint::to_str_bytes(value, 10) |buf| {
|
||||
let valuestr = str::from_bytes_slice(buf);
|
||||
for piece in pieces.iter() {
|
||||
self.run(piece, Some(valuestr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a function which calls are emitted to by the compiler itself to
|
||||
/// create the Argument structures that are passed into the `sprintf` function.
|
||||
#[doc(hidden)]
|
||||
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
|
||||
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)]
|
||||
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
|
||||
argument(String::fmt, 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)]
|
||||
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
|
||||
argument(Unsigned::fmt, s)
|
||||
}
|
||||
|
||||
// Implementations of the core formatting traits
|
||||
|
||||
impl Bool for bool {
|
||||
fn fmt(b: &bool, f: &mut Formatter) {
|
||||
String::fmt(&(if *b {"true"} else {"false"}), f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> String for &'self str {
|
||||
fn fmt(s: & &'self str, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
f.buf.write(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Char for char {
|
||||
fn fmt(c: &char, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
// XXX: shouldn't require an allocation
|
||||
let mut s = ~"";
|
||||
s.push_char(*c);
|
||||
f.buf.write(s.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl Signed for int {
|
||||
fn fmt(c: &int, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
do int::to_str_bytes(*c, 10) |buf| {
|
||||
f.buf.write(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unsigned for uint {
|
||||
fn fmt(c: &uint, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
do uint::to_str_bytes(*c, 10) |buf| {
|
||||
f.buf.write(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Octal for uint {
|
||||
fn fmt(c: &uint, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
do uint::to_str_bytes(*c, 8) |buf| {
|
||||
f.buf.write(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for uint {
|
||||
fn fmt(c: &uint, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
do uint::to_str_bytes(*c, 16) |buf| {
|
||||
f.buf.write(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpperHex for uint {
|
||||
fn fmt(c: &uint, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
do uint::to_str_bytes(*c, 16) |buf| {
|
||||
let mut local = [0u8, ..16];
|
||||
for (l, &b) in local.mut_iter().zip(buf.iter()) {
|
||||
*l = match b as char {
|
||||
'a' .. 'f' => (b - 'a' as u8) + 'A' as u8,
|
||||
_ => b,
|
||||
};
|
||||
}
|
||||
f.buf.write(local.slice_to(buf.len()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Poly for T {
|
||||
fn fmt(t: &T, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
let s = sys::log_str(t);
|
||||
f.buf.write(s.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// n.b. use 'const' to get an implementation for both '*mut' and '*' at the same
|
||||
// time.
|
||||
impl<T> Pointer for *const T {
|
||||
fn fmt(t: &*const T, f: &mut Formatter) {
|
||||
// XXX: formatting args
|
||||
f.buf.write("0x".as_bytes());
|
||||
LowerHex::fmt(&(*t as uint), f);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
896
src/libstd/fmt/parse.rs
Normal file
896
src/libstd/fmt/parse.rs
Normal file
|
|
@ -0,0 +1,896 @@
|
|||
// 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.
|
||||
|
||||
use prelude::*;
|
||||
|
||||
use char;
|
||||
use str;
|
||||
use iterator;
|
||||
|
||||
condition! { pub parse_error: ~str -> (); }
|
||||
|
||||
/// A piece is a portion of the format string which represents the next part to
|
||||
/// emit. These are emitted as a stream by the `Parser` class.
|
||||
#[deriving(Eq)]
|
||||
pub enum Piece<'self> {
|
||||
/// A literal string which should directly be emitted
|
||||
String(&'self str),
|
||||
/// A back-reference to whatever the current argument is. This is used
|
||||
/// inside of a method call to refer back to the original argument.
|
||||
CurrentArgument,
|
||||
/// This describes that formatting should process the next argument (as
|
||||
/// specified inside) for emission.
|
||||
Argument(Argument<'self>),
|
||||
}
|
||||
|
||||
/// Representation of an argument specification.
|
||||
#[deriving(Eq)]
|
||||
pub struct Argument<'self> {
|
||||
/// Where to find this argument
|
||||
position: Position<'self>,
|
||||
/// How to format the argument
|
||||
format: FormatSpec<'self>,
|
||||
/// If not `None`, what method to invoke on the argument
|
||||
method: Option<~Method<'self>>
|
||||
}
|
||||
|
||||
/// Specification for the formatting of an argument in the format string.
|
||||
#[deriving(Eq)]
|
||||
pub struct FormatSpec<'self> {
|
||||
/// Optionally specified character to fill alignment with
|
||||
fill: Option<char>,
|
||||
/// Optionally specified alignment
|
||||
align: Option<Alignment>,
|
||||
/// Packed version of various flags provided
|
||||
flags: uint,
|
||||
/// The integer precision to use
|
||||
precision: Count,
|
||||
/// The string width requested for the resulting format
|
||||
width: Count,
|
||||
/// The descriptor string representing the name of the format desired for
|
||||
/// this argument, this can be empty or any number of characters, although
|
||||
/// it is required to be one word.
|
||||
ty: &'self str
|
||||
}
|
||||
|
||||
/// Enum describing where an argument for a format can be located.
|
||||
#[deriving(Eq)]
|
||||
pub enum Position<'self> {
|
||||
ArgumentNext, ArgumentIs(uint), ArgumentNamed(&'self str)
|
||||
}
|
||||
|
||||
/// Enum of alignments which are supoprted.
|
||||
#[deriving(Eq)]
|
||||
pub enum Alignment { AlignLeft, AlignRight }
|
||||
|
||||
/// Various flags which can be applied to format strings, the meaning of these
|
||||
/// flags is defined by the formatters themselves.
|
||||
#[deriving(Eq)]
|
||||
pub enum Flag {
|
||||
FlagSignPlus,
|
||||
FlagSignMinus,
|
||||
FlagAlternate,
|
||||
}
|
||||
|
||||
/// A count is used for the precision and width parameters of an integer, and
|
||||
/// can reference either an argument or a literal integer.
|
||||
#[deriving(Eq)]
|
||||
pub enum Count {
|
||||
CountIs(uint),
|
||||
CountIsParam(uint),
|
||||
CountIsNextParam,
|
||||
CountImplied,
|
||||
}
|
||||
|
||||
/// Enum describing all of the possible methods which the formatting language
|
||||
/// currently supports.
|
||||
#[deriving(Eq)]
|
||||
pub enum Method<'self> {
|
||||
/// A plural method selects on an integer over a list of either integer or
|
||||
/// keyword-defined clauses. The meaning of the keywords is defined by the
|
||||
/// current locale.
|
||||
///
|
||||
/// An offset is optionally present at the beginning which is used to match
|
||||
/// against keywords, but it is not matched against the literal integers.
|
||||
///
|
||||
/// The final element of this enum is the default "other" case which is
|
||||
/// always required to be specified.
|
||||
Plural(Option<uint>, ~[PluralArm<'self>], ~[Piece<'self>]),
|
||||
|
||||
/// A select method selects over a string. Each arm is a different string
|
||||
/// which can be selected for.
|
||||
///
|
||||
/// As with `Plural`, a default "other" case is required as well.
|
||||
Select(~[SelectArm<'self>], ~[Piece<'self>]),
|
||||
}
|
||||
|
||||
/// Structure representing one "arm" of the `plural` function.
|
||||
#[deriving(Eq)]
|
||||
pub struct PluralArm<'self> {
|
||||
/// A selector can either be specified by a keyword or with an integer
|
||||
/// literal.
|
||||
selector: Either<PluralKeyword, uint>,
|
||||
/// Array of pieces which are the format of this arm
|
||||
result: ~[Piece<'self>],
|
||||
}
|
||||
|
||||
/// Enum of the 5 CLDR plural keywords. There is one more, "other", but that is
|
||||
/// specially placed in the `Plural` variant of `Method`
|
||||
///
|
||||
/// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
|
||||
#[deriving(Eq, IterBytes)]
|
||||
pub enum PluralKeyword {
|
||||
Zero, One, Two, Few, Many
|
||||
}
|
||||
|
||||
/// Structure representing one "arm" of the `select` function.
|
||||
#[deriving(Eq)]
|
||||
pub struct SelectArm<'self> {
|
||||
/// String selector which guards this arm
|
||||
selector: &'self str,
|
||||
/// Array of pieces which are the format of this arm
|
||||
result: ~[Piece<'self>],
|
||||
}
|
||||
|
||||
/// The parser structure for interpreting the input format string. This is
|
||||
/// modelled as an iterator over `Piece` structures to form a stream of tokens
|
||||
/// being output.
|
||||
///
|
||||
/// This is a recursive-descent parser for the sake of simplicity, and if
|
||||
/// necessary there's probably lots of room for improvement performance-wise.
|
||||
pub struct Parser<'self> {
|
||||
priv input: &'self str,
|
||||
priv cur: str::CharOffsetIterator<'self>,
|
||||
}
|
||||
|
||||
impl<'self> iterator::Iterator<Piece<'self>> for Parser<'self> {
|
||||
fn next(&mut self) -> Option<Piece<'self>> {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '#')) => { self.cur.next(); Some(CurrentArgument) }
|
||||
Some((_, '{')) => {
|
||||
self.cur.next();
|
||||
let ret = Some(Argument(self.argument()));
|
||||
if !self.consume('}') {
|
||||
self.err(~"unterminated format string");
|
||||
}
|
||||
ret
|
||||
}
|
||||
Some((pos, '\\')) => {
|
||||
self.cur.next();
|
||||
self.escape(); // ensure it's a valid escape sequence
|
||||
Some(String(self.string(pos + 1))) // skip the '\' character
|
||||
}
|
||||
Some((_, '}')) | None => { None }
|
||||
Some((pos, _)) => {
|
||||
Some(String(self.string(pos)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> Parser<'self> {
|
||||
/// Creates a new parser for the given format string
|
||||
pub fn new<'a>(s: &'a str) -> Parser<'a> {
|
||||
Parser {
|
||||
input: s,
|
||||
cur: s.char_offset_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies of an error. The message doesn't actually need to be of type
|
||||
/// ~str, but I think it does when this eventually uses conditions so it
|
||||
/// might as well start using it now.
|
||||
fn err(&self, msg: ~str) {
|
||||
parse_error::cond.raise(msg);
|
||||
}
|
||||
|
||||
/// Optionally consumes the specified character. If the character is not at
|
||||
/// the current position, then the current iterator isn't moved and false is
|
||||
/// returned, otherwise the character is consumed and true is returned.
|
||||
fn consume(&mut self, c: char) -> bool {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, maybe)) if c == maybe => {
|
||||
self.cur.next();
|
||||
true
|
||||
}
|
||||
Some(*) | None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to consume any amount of whitespace followed by a character
|
||||
fn wsconsume(&mut self, c: char) -> bool {
|
||||
self.ws(); self.consume(c)
|
||||
}
|
||||
|
||||
/// Consumes all whitespace characters until the first non-whitespace
|
||||
/// character
|
||||
fn ws(&mut self) {
|
||||
loop {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, c)) if char::is_whitespace(c) => { self.cur.next(); }
|
||||
Some(*) | None => { return }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes an escape sequence, failing if there is not a valid character
|
||||
/// to be escaped.
|
||||
fn escape(&mut self) -> char {
|
||||
match self.cur.next() {
|
||||
Some((_, c @ '#')) | Some((_, c @ '{')) |
|
||||
Some((_, c @ '\\')) | Some((_, c @ '}')) => { c }
|
||||
Some((_, c)) => {
|
||||
self.err(fmt!("invalid escape character `%c`", c));
|
||||
c
|
||||
}
|
||||
None => {
|
||||
self.err(~"expected an escape sequence, but format string was \
|
||||
terminated");
|
||||
' '
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses all of a string which is to be considered a "raw literal" in a
|
||||
/// format string. This is everything outside of the braces.
|
||||
fn string(&mut self, start: uint) -> &'self str {
|
||||
loop {
|
||||
// we may not consume the character, so clone the iterator
|
||||
match self.cur.clone().next() {
|
||||
Some((pos, '\\')) | Some((pos, '#')) |
|
||||
Some((pos, '}')) | Some((pos, '{')) => {
|
||||
return self.input.slice(start, pos);
|
||||
}
|
||||
Some(*) => { self.cur.next(); }
|
||||
None => {
|
||||
self.cur.next();
|
||||
return self.input.slice(start, self.input.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an Argument structure, or what's contained within braces inside
|
||||
/// the format string
|
||||
fn argument(&mut self) -> Argument<'self> {
|
||||
Argument {
|
||||
position: self.position(),
|
||||
format: self.format(),
|
||||
method: self.method(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a positional argument for a format. This could either be an
|
||||
/// integer index of an argument, a named argument, or a blank string.
|
||||
fn position(&mut self) -> Position<'self> {
|
||||
match self.integer() {
|
||||
Some(i) => { ArgumentIs(i) }
|
||||
None => {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, c)) if char::is_alphabetic(c) => {
|
||||
ArgumentNamed(self.word())
|
||||
}
|
||||
_ => ArgumentNext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a format specifier at the current position, returning all of the
|
||||
/// relevant information in the FormatSpec struct.
|
||||
fn format(&mut self) -> FormatSpec<'self> {
|
||||
let mut spec = FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: self.input.slice(0, 0),
|
||||
};
|
||||
if !self.consume(':') { return spec }
|
||||
|
||||
// fill character
|
||||
match self.cur.clone().next() {
|
||||
Some((_, c)) => {
|
||||
match self.cur.clone().skip(1).next() {
|
||||
Some((_, '>')) | Some((_, '<')) => {
|
||||
spec.fill = Some(c);
|
||||
self.cur.next();
|
||||
}
|
||||
Some(*) | None => {}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// Alignment
|
||||
if self.consume('<') {
|
||||
spec.align = Some(AlignLeft);
|
||||
} else if self.consume('>') {
|
||||
spec.align = Some(AlignRight);
|
||||
}
|
||||
// Sign flags
|
||||
if self.consume('+') {
|
||||
spec.flags |= 1 << (FlagSignPlus as uint);
|
||||
} else if self.consume('-') {
|
||||
spec.flags |= 1 << (FlagSignMinus as uint);
|
||||
}
|
||||
// Alternate marker
|
||||
if self.consume('#') {
|
||||
spec.flags |= 1 << (FlagAlternate as uint);
|
||||
}
|
||||
// Width and precision
|
||||
spec.width = self.count();
|
||||
if self.consume('.') {
|
||||
if self.consume('*') {
|
||||
spec.precision = CountIsNextParam;
|
||||
} else {
|
||||
spec.precision = self.count();
|
||||
}
|
||||
}
|
||||
// Finally the actual format specifier
|
||||
spec.ty = self.word();
|
||||
return spec;
|
||||
}
|
||||
|
||||
/// Parses a method to be applied to the previously specified argument and
|
||||
/// its format. The two current supported methods are 'plural' and 'select'
|
||||
fn method(&mut self) -> Option<~Method<'self>> {
|
||||
if !self.wsconsume(',') {
|
||||
return None;
|
||||
}
|
||||
self.ws();
|
||||
match self.word() {
|
||||
"select" => {
|
||||
if !self.wsconsume(',') {
|
||||
self.err(~"`select` must be followed by `,`");
|
||||
}
|
||||
Some(self.select())
|
||||
}
|
||||
"plural" => {
|
||||
if !self.wsconsume(',') {
|
||||
self.err(~"`plural` must be followed by `,`");
|
||||
}
|
||||
Some(self.plural())
|
||||
}
|
||||
"" => {
|
||||
self.err(~"expected method after comma");
|
||||
return None;
|
||||
}
|
||||
method => {
|
||||
self.err(fmt!("unknown method: `%s`", method));
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a 'select' statement (after the initial 'select' word)
|
||||
fn select(&mut self) -> ~Method<'self> {
|
||||
let mut other = None;
|
||||
let mut arms = ~[];
|
||||
// Consume arms one at a time
|
||||
loop {
|
||||
self.ws();
|
||||
let selector = self.word();
|
||||
if selector == "" {
|
||||
self.err(~"cannot have an empty selector");
|
||||
break
|
||||
}
|
||||
if !self.wsconsume('{') {
|
||||
self.err(~"selector must be followed by `{`");
|
||||
}
|
||||
let pieces = self.collect();
|
||||
if !self.wsconsume('}') {
|
||||
self.err(~"selector case must be terminated by `}`");
|
||||
}
|
||||
if selector == "other" {
|
||||
if !other.is_none() {
|
||||
self.err(~"multiple `other` statements in `select");
|
||||
}
|
||||
other = Some(pieces);
|
||||
} else {
|
||||
arms.push(SelectArm { selector: selector, result: pieces });
|
||||
}
|
||||
self.ws();
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '}')) => { break }
|
||||
Some(*) | None => {}
|
||||
}
|
||||
}
|
||||
// The "other" selector must be present
|
||||
let other = match other {
|
||||
Some(arm) => { arm }
|
||||
None => {
|
||||
self.err(~"`select` statement must provide an `other` case");
|
||||
~[]
|
||||
}
|
||||
};
|
||||
~Select(arms, other)
|
||||
}
|
||||
|
||||
/// Parses a 'plural' statement (after the initial 'plural' word)
|
||||
fn plural(&mut self) -> ~Method<'self> {
|
||||
let mut offset = None;
|
||||
let mut other = None;
|
||||
let mut arms = ~[];
|
||||
|
||||
// First, attempt to parse the 'offset:' field. We know the set of
|
||||
// selector words which can appear in plural arms, and the only ones
|
||||
// which start with 'o' are "other" and "offset", hence look two
|
||||
// characters deep to see if we can consume the word "offset"
|
||||
self.ws();
|
||||
let mut it = self.cur.clone();
|
||||
match it.next() {
|
||||
Some((_, 'o')) => {
|
||||
match it.next() {
|
||||
Some((_, 'f')) => {
|
||||
let word = self.word();
|
||||
if word != "offset" {
|
||||
self.err(fmt!("expected `offset`, found `%s`",
|
||||
word));
|
||||
} else {
|
||||
if !self.consume(':') {
|
||||
self.err(~"`offset` must be followed by `:`");
|
||||
}
|
||||
match self.integer() {
|
||||
Some(i) => { offset = Some(i); }
|
||||
None => {
|
||||
self.err(~"offset must be an integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(*) | None => {}
|
||||
}
|
||||
}
|
||||
Some(*) | None => {}
|
||||
}
|
||||
|
||||
// Next, generate all the arms
|
||||
loop {
|
||||
let mut isother = false;
|
||||
let selector = if self.wsconsume('=') {
|
||||
match self.integer() {
|
||||
Some(i) => Right(i),
|
||||
None => {
|
||||
self.err(~"plural `=` selectors must be followed by an \
|
||||
integer");
|
||||
Right(0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let word = self.word();
|
||||
match word {
|
||||
"other" => { isother = true; Left(Zero) }
|
||||
"zero" => Left(Zero),
|
||||
"one" => Left(One),
|
||||
"two" => Left(Two),
|
||||
"few" => Left(Few),
|
||||
"many" => Left(Many),
|
||||
word => {
|
||||
self.err(fmt!("unexpected plural selector `%s`", word));
|
||||
if word == "" {
|
||||
break
|
||||
} else {
|
||||
Left(Zero)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if !self.wsconsume('{') {
|
||||
self.err(~"selector must be followed by `{`");
|
||||
}
|
||||
let pieces = self.collect();
|
||||
if !self.wsconsume('}') {
|
||||
self.err(~"selector case must be terminated by `}`");
|
||||
}
|
||||
if isother {
|
||||
if !other.is_none() {
|
||||
self.err(~"multiple `other` statements in `select");
|
||||
}
|
||||
other = Some(pieces);
|
||||
} else {
|
||||
arms.push(PluralArm { selector: selector, result: pieces });
|
||||
}
|
||||
self.ws();
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '}')) => { break }
|
||||
Some(*) | None => {}
|
||||
}
|
||||
}
|
||||
|
||||
let other = match other {
|
||||
Some(arm) => { arm }
|
||||
None => {
|
||||
self.err(~"`plural` statement must provide an `other` case");
|
||||
~[]
|
||||
}
|
||||
};
|
||||
~Plural(offset, arms, other)
|
||||
}
|
||||
|
||||
/// Parses a Count parameter at the current position. This does not check
|
||||
/// for 'CountIsNextParam' because that is only used in precision, not
|
||||
/// width.
|
||||
fn count(&mut self) -> Count {
|
||||
match self.integer() {
|
||||
Some(i) => {
|
||||
if self.consume('$') {
|
||||
CountIsParam(i)
|
||||
} else {
|
||||
CountIs(i)
|
||||
}
|
||||
}
|
||||
None => { CountImplied }
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a word starting at the current position. A word is considered to
|
||||
/// be an alphabetic character followed by any number of alphanumeric
|
||||
/// characters.
|
||||
fn word(&mut self) -> &'self str {
|
||||
let start = match self.cur.clone().next() {
|
||||
Some((pos, c)) if char::is_alphabetic(c) => {
|
||||
self.cur.next();
|
||||
pos
|
||||
}
|
||||
Some(*) | None => { return self.input.slice(0, 0); }
|
||||
};
|
||||
let mut end;
|
||||
loop {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, c)) if char::is_alphanumeric(c) => {
|
||||
self.cur.next();
|
||||
}
|
||||
Some((pos, _)) => { end = pos; break }
|
||||
None => { end = self.input.len(); break }
|
||||
}
|
||||
}
|
||||
self.input.slice(start, end)
|
||||
}
|
||||
|
||||
/// Optionally parses an integer at the current position. This doesn't deal
|
||||
/// with overflow at all, it's just accumulating digits.
|
||||
fn integer(&mut self) -> Option<uint> {
|
||||
let mut cur = 0;
|
||||
let mut found = false;
|
||||
loop {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, c)) => {
|
||||
match char::to_digit(c, 10) {
|
||||
Some(i) => {
|
||||
cur = cur * 10 + i;
|
||||
found = true;
|
||||
self.cur.next();
|
||||
}
|
||||
None => { break }
|
||||
}
|
||||
}
|
||||
None => { break }
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return Some(cur);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use prelude::*;
|
||||
use realstd::fmt::{String};
|
||||
|
||||
fn same(fmt: &'static str, p: ~[Piece<'static>]) {
|
||||
let mut parser = Parser::new(fmt);
|
||||
assert_eq!(p, parser.collect());
|
||||
}
|
||||
|
||||
fn fmtdflt() -> FormatSpec<'static> {
|
||||
return FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "",
|
||||
}
|
||||
}
|
||||
|
||||
fn musterr(s: &str) {
|
||||
Parser::new(s).next();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
same("asdf", ~[String("asdf")]);
|
||||
same("a\\{b", ~[String("a"), String("{b")]);
|
||||
same("a\\#b", ~[String("a"), String("#b")]);
|
||||
same("a\\}b", ~[String("a"), String("}b")]);
|
||||
same("a\\}", ~[String("a"), String("}")]);
|
||||
same("\\}", ~[String("}")]);
|
||||
}
|
||||
|
||||
#[test] #[should_fail] fn invalid01() { musterr("{") }
|
||||
#[test] #[should_fail] fn invalid02() { musterr("\\") }
|
||||
#[test] #[should_fail] fn invalid03() { musterr("\\a") }
|
||||
#[test] #[should_fail] fn invalid04() { musterr("{3a}") }
|
||||
#[test] #[should_fail] fn invalid05() { musterr("{:|}") }
|
||||
#[test] #[should_fail] fn invalid06() { musterr("{:>>>}") }
|
||||
|
||||
#[test]
|
||||
fn format_nothing() {
|
||||
same("{}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: fmtdflt(),
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_position() {
|
||||
same("{3}", ~[Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: fmtdflt(),
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_position_nothing_else() {
|
||||
same("{3:}", ~[Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: fmtdflt(),
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_type() {
|
||||
same("{3:a}", ~[Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "a",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_align_fill() {
|
||||
same("{3:>}", ~[Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: Some(AlignRight),
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
same("{3:0<}", ~[Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: FormatSpec {
|
||||
fill: Some('0'),
|
||||
align: Some(AlignLeft),
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
same("{3:*<abcd}", ~[Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: FormatSpec {
|
||||
fill: Some('*'),
|
||||
align: Some(AlignLeft),
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "abcd",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_counts() {
|
||||
same("{:10s}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountIs(10),
|
||||
ty: "s",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
same("{:10$.10s}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountIs(10),
|
||||
width: CountIsParam(10),
|
||||
ty: "s",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
same("{:.*s}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountIsNextParam,
|
||||
width: CountImplied,
|
||||
ty: "s",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
same("{:.10$s}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountIsParam(10),
|
||||
width: CountImplied,
|
||||
ty: "s",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_flags() {
|
||||
same("{:-}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: (1 << FlagSignMinus as uint),
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
same("{:+#}", ~[Argument(Argument {
|
||||
position: ArgumentNext,
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: (1 << FlagSignPlus as uint) | (1 << FlagAlternate as uint),
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "",
|
||||
},
|
||||
method: None,
|
||||
})]);
|
||||
}
|
||||
#[test]
|
||||
fn format_mixture() {
|
||||
same("abcd {3:a} efg", ~[String("abcd "), Argument(Argument {
|
||||
position: ArgumentIs(3),
|
||||
format: FormatSpec {
|
||||
fill: None,
|
||||
align: None,
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
width: CountImplied,
|
||||
ty: "a",
|
||||
},
|
||||
method: None,
|
||||
}), String(" efg")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_simple() {
|
||||
same("{, select, other { haha } }", ~[Argument(Argument{
|
||||
position: ArgumentNext,
|
||||
format: fmtdflt(),
|
||||
method: Some(~Select(~[], ~[String(" haha ")]))
|
||||
})]);
|
||||
same("{1, select, other { haha } }", ~[Argument(Argument{
|
||||
position: ArgumentIs(1),
|
||||
format: fmtdflt(),
|
||||
method: Some(~Select(~[], ~[String(" haha ")]))
|
||||
})]);
|
||||
same("{1, select, other {#} }", ~[Argument(Argument{
|
||||
position: ArgumentIs(1),
|
||||
format: fmtdflt(),
|
||||
method: Some(~Select(~[], ~[CurrentArgument]))
|
||||
})]);
|
||||
same("{1, select, other {{2, select, other {lol}}} }", ~[Argument(Argument{
|
||||
position: ArgumentIs(1),
|
||||
format: fmtdflt(),
|
||||
method: Some(~Select(~[], ~[Argument(Argument{
|
||||
position: ArgumentIs(2),
|
||||
format: fmtdflt(),
|
||||
method: Some(~Select(~[], ~[String("lol")]))
|
||||
})])) // wat
|
||||
})]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_cases() {
|
||||
same("{1, select, a{1} b{2} c{3} other{4} }", ~[Argument(Argument{
|
||||
position: ArgumentIs(1),
|
||||
format: fmtdflt(),
|
||||
method: Some(~Select(~[
|
||||
SelectArm{ selector: "a", result: ~[String("1")] },
|
||||
SelectArm{ selector: "b", result: ~[String("2")] },
|
||||
SelectArm{ selector: "c", result: ~[String("3")] },
|
||||
], ~[String("4")]))
|
||||
})]);
|
||||
}
|
||||
|
||||
#[test] #[should_fail] fn badselect01() {
|
||||
musterr("{select, }")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect02() {
|
||||
musterr("{1, select}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect03() {
|
||||
musterr("{1, select, }")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect04() {
|
||||
musterr("{1, select, a {}}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect05() {
|
||||
musterr("{1, select, other }}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect06() {
|
||||
musterr("{1, select, other {}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect07() {
|
||||
musterr("{select, other {}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect08() {
|
||||
musterr("{1 select, other {}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect09() {
|
||||
musterr("{:d select, other {}")
|
||||
}
|
||||
#[test] #[should_fail] fn badselect10() {
|
||||
musterr("{1:d select, other {}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plural_simple() {
|
||||
same("{, plural, other { haha } }", ~[Argument(Argument{
|
||||
position: ArgumentNext,
|
||||
format: fmtdflt(),
|
||||
method: Some(~Plural(None, ~[], ~[String(" haha ")]))
|
||||
})]);
|
||||
same("{:, plural, other { haha } }", ~[Argument(Argument{
|
||||
position: ArgumentNext,
|
||||
format: fmtdflt(),
|
||||
method: Some(~Plural(None, ~[], ~[String(" haha ")]))
|
||||
})]);
|
||||
same("{, plural, offset:1 =2{2} =3{3} many{yes} other{haha} }",
|
||||
~[Argument(Argument{
|
||||
position: ArgumentNext,
|
||||
format: fmtdflt(),
|
||||
method: Some(~Plural(Some(1), ~[
|
||||
PluralArm{ selector: Right(2), result: ~[String("2")] },
|
||||
PluralArm{ selector: Right(3), result: ~[String("3")] },
|
||||
PluralArm{ selector: Left(Many), result: ~[String("yes")] }
|
||||
], ~[String("haha")]))
|
||||
})]);
|
||||
}
|
||||
}
|
||||
62
src/libstd/fmt/rt.rs
Normal file
62
src/libstd/fmt/rt.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// 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 either::Either;
|
||||
use fmt::parse;
|
||||
use option::Option;
|
||||
|
||||
pub enum Piece<'self> {
|
||||
String(&'self str),
|
||||
// FIXME(#8259): this shouldn't require the unit-value here
|
||||
CurrentArgument(()),
|
||||
Argument(Argument<'self>),
|
||||
}
|
||||
|
||||
pub struct Argument<'self> {
|
||||
position: Position,
|
||||
format: FormatSpec,
|
||||
method: Option<&'self Method<'self>>
|
||||
}
|
||||
|
||||
pub struct FormatSpec {
|
||||
fill: char,
|
||||
alignleft: bool,
|
||||
flags: uint,
|
||||
precision: parse::Count,
|
||||
width: parse::Count,
|
||||
}
|
||||
|
||||
pub enum Position {
|
||||
ArgumentNext, ArgumentIs(uint)
|
||||
}
|
||||
|
||||
pub enum Method<'self> {
|
||||
Plural(Option<uint>, &'self [PluralArm<'self>], &'self [Piece<'self>]),
|
||||
Select(&'self [SelectArm<'self>], &'self [Piece<'self>]),
|
||||
}
|
||||
|
||||
pub struct PluralArm<'self> {
|
||||
selector: Either<parse::PluralKeyword, uint>,
|
||||
result: &'self [Piece<'self>],
|
||||
}
|
||||
|
||||
pub struct SelectArm<'self> {
|
||||
selector: &'self str,
|
||||
result: &'self [Piece<'self>],
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ use container::{Container, Mutable, Map, MutableMap, Set, MutableSet};
|
|||
use clone::Clone;
|
||||
use cmp::{Eq, Equiv};
|
||||
use hash::Hash;
|
||||
use iterator::{Iterator, IteratorUtil, FromIterator, Extendable, range};
|
||||
use iterator::{Iterator, IteratorUtil, FromIterator, Extendable};
|
||||
use iterator::{FilterMap, Chain, Repeat, Zip};
|
||||
use num;
|
||||
use option::{None, Option, Some};
|
||||
|
|
@ -238,7 +238,7 @@ impl<K:Hash + Eq,V> HashMap<K, V> {
|
|||
let len_buckets = self.buckets.len();
|
||||
let bucket = self.buckets[idx].take();
|
||||
|
||||
let value = do bucket.map_consume |bucket| {
|
||||
let value = do bucket.map_move |bucket| {
|
||||
bucket.value
|
||||
};
|
||||
|
||||
|
|
@ -265,8 +265,8 @@ impl<K:Hash + Eq,V> Container for HashMap<K, V> {
|
|||
impl<K:Hash + Eq,V> Mutable for HashMap<K, V> {
|
||||
/// Clear the map, removing all key-value pairs.
|
||||
fn clear(&mut self) {
|
||||
for idx in range(0u, self.buckets.len()) {
|
||||
self.buckets[idx] = None;
|
||||
for bkt in self.buckets.mut_iter() {
|
||||
*bkt = None;
|
||||
}
|
||||
self.size = 0;
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
|
|||
impl<K: Hash + Eq, V: Clone> HashMap<K, V> {
|
||||
/// Like `find`, but returns a copy of the value.
|
||||
pub fn find_copy(&self, k: &K) -> Option<V> {
|
||||
self.find(k).map_consume(|v| (*v).clone())
|
||||
self.find(k).map_move(|v| (*v).clone())
|
||||
}
|
||||
|
||||
/// Like `get`, but returns a copy of the value.
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ implementing the `Iterator` trait.
|
|||
*/
|
||||
|
||||
use cmp;
|
||||
use num::{Zero, One, Saturating};
|
||||
use num::{Zero, One, Integer, Saturating};
|
||||
use option::{Option, Some, None};
|
||||
use ops::{Add, Mul};
|
||||
use ops::{Add, Mul, Sub};
|
||||
use cmp::Ord;
|
||||
use clone::Clone;
|
||||
use uint;
|
||||
|
|
@ -674,7 +674,7 @@ impl<A, T: Iterator<A>> IteratorUtil<A> for T {
|
|||
Some((y, y_val))
|
||||
}
|
||||
}
|
||||
}).map_consume(|(x, _)| x)
|
||||
}).map_move(|(x, _)| x)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -689,7 +689,7 @@ impl<A, T: Iterator<A>> IteratorUtil<A> for T {
|
|||
Some((y, y_val))
|
||||
}
|
||||
}
|
||||
}).map_consume(|(x, _)| x)
|
||||
}).map_move(|(x, _)| x)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1382,7 +1382,7 @@ impl<'self, A, T: Iterator<A>, B, U: Iterator<B>> Iterator<B> for
|
|||
return Some(x)
|
||||
}
|
||||
}
|
||||
match self.iter.next().map_consume(|x| (self.f)(x)) {
|
||||
match self.iter.next().map_move(|x| (self.f)(x)) {
|
||||
None => return self.backiter.chain_mut_ref(|it| it.next()),
|
||||
next => self.frontiter = next,
|
||||
}
|
||||
|
|
@ -1414,7 +1414,7 @@ impl<'self,
|
|||
y => return y
|
||||
}
|
||||
}
|
||||
match self.iter.next_back().map_consume(|x| (self.f)(x)) {
|
||||
match self.iter.next_back().map_move(|x| (self.f)(x)) {
|
||||
None => return self.frontiter.chain_mut_ref(|it| it.next_back()),
|
||||
next => self.backiter = next,
|
||||
}
|
||||
|
|
@ -1531,7 +1531,7 @@ pub fn range<A: Add<A, A> + Ord + Clone + One>(start: A, stop: A) -> Range<A> {
|
|||
Range{state: start, stop: stop, one: One::one()}
|
||||
}
|
||||
|
||||
impl<A: Add<A, A> + Ord + Clone + One> Iterator<A> for Range<A> {
|
||||
impl<A: Add<A, A> + Ord + Clone> Iterator<A> for Range<A> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
if self.state < self.stop {
|
||||
|
|
@ -1544,6 +1544,22 @@ impl<A: Add<A, A> + Ord + Clone + One> Iterator<A> for Range<A> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: Sub<A, A> + Integer + Ord + Clone> DoubleEndedIterator<A> for Range<A> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<A> {
|
||||
if self.stop > self.state {
|
||||
// Integer doesn't technically define this rule, but we're going to assume that every
|
||||
// Integer is reachable from every other one by adding or subtracting enough Ones. This
|
||||
// seems like a reasonable-enough rule that every Integer should conform to, even if it
|
||||
// can't be statically checked.
|
||||
self.stop = self.stop - self.one;
|
||||
Some(self.stop.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Add<A, A> + Clone> Iterator<A> for Counter<A> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
|
|
@ -2121,4 +2137,17 @@ mod tests {
|
|||
check_randacc_iter(xs.iter().cycle().take_(27), 27);
|
||||
check_randacc_iter(empty.iter().cycle(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_ended_range() {
|
||||
assert_eq!(range(11i, 14).invert().collect::<~[int]>(), ~[13i, 12, 11]);
|
||||
for _ in range(10i, 0).invert() {
|
||||
fail!("unreachable");
|
||||
}
|
||||
|
||||
assert_eq!(range(11u, 14).invert().collect::<~[uint]>(), ~[13u, 12, 11]);
|
||||
for _ in range(10u, 0).invert() {
|
||||
fail!("unreachable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,16 +110,16 @@ fn test_tls_multitask() {
|
|||
set(my_key, @~"parent data");
|
||||
do task::spawn {
|
||||
// TLS shouldn't carry over.
|
||||
assert!(get(my_key, |k| k.map(|&k| *k)).is_none());
|
||||
assert!(get(my_key, |k| k.map_move(|k| *k)).is_none());
|
||||
set(my_key, @~"child data");
|
||||
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) ==
|
||||
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) ==
|
||||
~"child data");
|
||||
// should be cleaned up for us
|
||||
}
|
||||
// Must work multiple times
|
||||
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
|
||||
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
|
||||
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
|
||||
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
|
||||
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
|
||||
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -127,7 +127,7 @@ fn test_tls_overwrite() {
|
|||
static my_key: Key<@~str> = &Key;
|
||||
set(my_key, @~"first data");
|
||||
set(my_key, @~"next data"); // Shouldn't leak.
|
||||
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"next data");
|
||||
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"next data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -278,18 +278,22 @@ impl One for f64 {
|
|||
|
||||
#[cfg(not(test))]
|
||||
impl Add<f64,f64> for f64 {
|
||||
#[inline]
|
||||
fn add(&self, other: &f64) -> f64 { *self + *other }
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
impl Sub<f64,f64> for f64 {
|
||||
#[inline]
|
||||
fn sub(&self, other: &f64) -> f64 { *self - *other }
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
impl Mul<f64,f64> for f64 {
|
||||
#[inline]
|
||||
fn mul(&self, other: &f64) -> f64 { *self * *other }
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
impl Div<f64,f64> for f64 {
|
||||
#[inline]
|
||||
fn div(&self, other: &f64) -> f64 { *self / *other }
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
|
|
|
|||
|
|
@ -124,14 +124,6 @@ pub fn range_step_inclusive(start: $T, last: $T, step: $T, it: &fn($T) -> bool)
|
|||
range_step_core(start, last, step, Closed, it)
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
/// Iterate over the range (`hi`..`lo`]
|
||||
pub fn range_rev(hi: $T, lo: $T, it: &fn($T) -> bool) -> bool {
|
||||
if hi == min_value { return true; }
|
||||
range_step_inclusive(hi-1, lo, -1 as $T, it)
|
||||
}
|
||||
|
||||
impl Num for $T {}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
@ -889,10 +881,6 @@ mod tests {
|
|||
fn test_ranges() {
|
||||
let mut l = ~[];
|
||||
|
||||
do range_rev(14,11) |i| {
|
||||
l.push(i);
|
||||
true
|
||||
};
|
||||
do range_step(20,26,2) |i| {
|
||||
l.push(i);
|
||||
true
|
||||
|
|
@ -917,8 +905,7 @@ mod tests {
|
|||
l.push(i);
|
||||
true
|
||||
};
|
||||
assert_eq!(l, ~[13,12,11,
|
||||
20,22,24,
|
||||
assert_eq!(l, ~[20,22,24,
|
||||
36,34,32,
|
||||
max_value-2,
|
||||
max_value-3,max_value-1,
|
||||
|
|
@ -926,9 +913,6 @@ mod tests {
|
|||
min_value+3,min_value+1]);
|
||||
|
||||
// None of the `fail`s should execute.
|
||||
do range_rev(0,10) |_i| {
|
||||
fail!(~"unreachable");
|
||||
};
|
||||
do range_step(10,0,1) |_i| {
|
||||
fail!(~"unreachable");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -422,9 +422,9 @@ pub fn float_to_str_common<T:NumCast+Zero+One+Eq+Ord+NumStrConv+Float+Round+
|
|||
|
||||
// Some constants for from_str_bytes_common's input validation,
|
||||
// they define minimum radix values for which the character is a valid digit.
|
||||
priv static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
|
||||
priv static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
|
||||
priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
|
||||
static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
|
||||
static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
|
||||
static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
|
||||
|
||||
/**
|
||||
* Parses a byte slice as a number. This is meant to
|
||||
|
|
|
|||
|
|
@ -125,13 +125,6 @@ pub fn range_step_inclusive(start: $T, last: $T, step: $T_SIGNED, it: &fn($T) ->
|
|||
range_step_core(start, last, step, Closed, it)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Iterate over the range (`hi`..`lo`]
|
||||
pub fn range_rev(hi: $T, lo: $T, it: &fn($T) -> bool) -> bool {
|
||||
if hi == min_value { return true; }
|
||||
range_step_inclusive(hi-1, lo, -1 as $T_SIGNED, it)
|
||||
}
|
||||
|
||||
impl Num for $T {}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
@ -654,10 +647,6 @@ mod tests {
|
|||
pub fn test_ranges() {
|
||||
let mut l = ~[];
|
||||
|
||||
do range_rev(14,11) |i| {
|
||||
l.push(i);
|
||||
true
|
||||
};
|
||||
do range_step(20,26,2) |i| {
|
||||
l.push(i);
|
||||
true
|
||||
|
|
@ -683,8 +672,7 @@ mod tests {
|
|||
true
|
||||
};
|
||||
|
||||
assert_eq!(l, ~[13,12,11,
|
||||
20,22,24,
|
||||
assert_eq!(l, ~[20,22,24,
|
||||
36,34,32,
|
||||
max_value-2,
|
||||
max_value-3,max_value-1,
|
||||
|
|
@ -692,9 +680,6 @@ mod tests {
|
|||
min_value+3,min_value+1]);
|
||||
|
||||
// None of the `fail`s should execute.
|
||||
do range_rev(0,0) |_i| {
|
||||
fail!("unreachable");
|
||||
};
|
||||
do range_step(10,0,1) |_i| {
|
||||
fail!("unreachable");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -208,6 +208,12 @@ impl<T> Option<T> {
|
|||
match *self { Some(ref mut x) => Some(f(x)), None => None }
|
||||
}
|
||||
|
||||
/// Applies a function to the contained value or returns a default
|
||||
#[inline]
|
||||
pub fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U {
|
||||
match *self { None => def, Some(ref t) => f(t) }
|
||||
}
|
||||
|
||||
/// Maps a `Some` value from one type to another by a mutable reference,
|
||||
/// or returns a default value.
|
||||
#[inline]
|
||||
|
|
@ -218,21 +224,15 @@ impl<T> Option<T> {
|
|||
/// As `map`, but consumes the option and gives `f` ownership to avoid
|
||||
/// copying.
|
||||
#[inline]
|
||||
pub fn map_consume<U>(self, f: &fn(v: T) -> U) -> Option<U> {
|
||||
match self { None => None, Some(v) => Some(f(v)) }
|
||||
}
|
||||
|
||||
/// Applies a function to the contained value or returns a default
|
||||
#[inline]
|
||||
pub fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U {
|
||||
match *self { None => def, Some(ref t) => f(t) }
|
||||
pub fn map_move<U>(self, f: &fn(T) -> U) -> Option<U> {
|
||||
match self { Some(x) => Some(f(x)), None => None }
|
||||
}
|
||||
|
||||
/// As `map_default`, but consumes the option and gives `f`
|
||||
/// ownership to avoid copying.
|
||||
#[inline]
|
||||
pub fn map_consume_default<U>(self, def: U, f: &fn(v: T) -> U) -> U {
|
||||
match self { None => def, Some(v) => f(v) }
|
||||
pub fn map_move_default<U>(self, def: U, f: &fn(T) -> U) -> U {
|
||||
match self { None => def, Some(t) => f(t) }
|
||||
}
|
||||
|
||||
/// Take the value out of the option, leaving a `None` in its place.
|
||||
|
|
@ -241,20 +241,6 @@ impl<T> Option<T> {
|
|||
util::replace(self, None)
|
||||
}
|
||||
|
||||
/// As `map_consume`, but swaps a None into the original option rather
|
||||
/// than consuming it by-value.
|
||||
#[inline]
|
||||
pub fn take_map<U>(&mut self, blk: &fn(T) -> U) -> Option<U> {
|
||||
self.take().map_consume(blk)
|
||||
}
|
||||
|
||||
/// As `map_consume_default`, but swaps a None into the original option
|
||||
/// rather than consuming it by-value.
|
||||
#[inline]
|
||||
pub fn take_map_default<U> (&mut self, def: U, blk: &fn(T) -> U) -> U {
|
||||
self.take().map_consume_default(def, blk)
|
||||
}
|
||||
|
||||
/// Apply a function to the contained value or do nothing.
|
||||
/// Returns true if the contained value was mutated.
|
||||
pub fn mutate(&mut self, f: &fn(T) -> T) -> bool {
|
||||
|
|
|
|||
|
|
@ -501,9 +501,7 @@ pub fn self_exe_path() -> Option<Path> {
|
|||
}
|
||||
}
|
||||
|
||||
do load_self().map |pth| {
|
||||
Path(*pth).dir_path()
|
||||
}
|
||||
load_self().map_move(|path| Path(path).dir_path())
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -610,15 +610,32 @@ impl<R: Rng> RngUtil for R {
|
|||
}
|
||||
|
||||
/// Create a random number generator with a default algorithm and seed.
|
||||
///
|
||||
/// It returns the cryptographically-safest `Rng` algorithm currently
|
||||
/// available in Rust. If you require a specifically seeded `Rng` for
|
||||
/// consistency over time you should pick one algorithm and create the
|
||||
/// `Rng` yourself.
|
||||
pub fn rng() -> IsaacRng {
|
||||
IsaacRng::new()
|
||||
}
|
||||
|
||||
/// Create a weak random number generator with a default algorithm and seed.
|
||||
///
|
||||
/// It returns the fatest `Rng` algorithm currently available in Rust without
|
||||
/// consideration for cryptography or security. If you require a specifically
|
||||
/// seeded `Rng` for consistency over time you should pick one algorithm and
|
||||
/// create the `Rng` yourself.
|
||||
pub fn weak_rng() -> XorShiftRng {
|
||||
XorShiftRng::new()
|
||||
}
|
||||
|
||||
static RAND_SIZE_LEN: u32 = 8;
|
||||
static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN;
|
||||
|
||||
/// A random number generator that uses the [ISAAC
|
||||
/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29).
|
||||
///
|
||||
/// The ISAAC algorithm is suitable for cryptographic purposes.
|
||||
pub struct IsaacRng {
|
||||
priv cnt: u32,
|
||||
priv rsl: [u32, .. RAND_SIZE],
|
||||
|
|
@ -794,8 +811,11 @@ impl Rng for IsaacRng {
|
|||
}
|
||||
|
||||
/// An [Xorshift random number
|
||||
/// generator](http://en.wikipedia.org/wiki/Xorshift). Not suitable for
|
||||
/// cryptographic purposes.
|
||||
/// generator](http://en.wikipedia.org/wiki/Xorshift).
|
||||
///
|
||||
/// The Xorshift algorithm is not suitable for cryptographic purposes
|
||||
/// but is very fast. If you do not know for sure that it fits your
|
||||
/// requirements, use a more secure one such as `IsaacRng`.
|
||||
pub struct XorShiftRng {
|
||||
priv x: u32,
|
||||
priv y: u32,
|
||||
|
|
|
|||
|
|
@ -149,6 +149,40 @@ impl<T, E: ToStr> Result<T, E> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Call a method based on a previous result
|
||||
///
|
||||
/// If `self` is `Ok` then the value is extracted and passed to `op`
|
||||
/// whereupon `op`s result is wrapped in `Ok` and returned. if `self` is
|
||||
/// `Err` then it is immediately returned. This function can be used to
|
||||
/// compose the results of two functions.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// let res = do read_file(file).map_move |buf| {
|
||||
/// parse_bytes(buf)
|
||||
/// }
|
||||
#[inline]
|
||||
pub fn map_move<U>(self, op: &fn(T) -> U) -> Result<U,E> {
|
||||
match self {
|
||||
Ok(t) => Ok(op(t)),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a method based on a previous result
|
||||
///
|
||||
/// If `self` is `Err` then the value is extracted and passed to `op`
|
||||
/// whereupon `op`s result is wrapped in an `Err` and returned. if `self` is
|
||||
/// `Ok` then it is immediately returned. This function can be used to pass
|
||||
/// through a successful result while handling an error.
|
||||
#[inline]
|
||||
pub fn map_err_move<F>(self, op: &fn(E) -> F) -> Result<T,F> {
|
||||
match self {
|
||||
Ok(t) => Ok(t),
|
||||
Err(e) => Err(op(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a method based on a previous result
|
||||
///
|
||||
/// If `self` is `Ok` then the value is extracted and passed to `op`
|
||||
|
|
@ -312,7 +346,9 @@ pub fn iter_vec2<S, T, U: ToStr>(ss: &[S], ts: &[T],
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use either;
|
||||
use str::OwnedStr;
|
||||
|
||||
pub fn op1() -> Result<int, ~str> { Ok(666) }
|
||||
|
||||
|
|
@ -359,14 +395,26 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
pub fn test_impl_map() {
|
||||
assert_eq!(Ok::<~str, ~str>(~"a").map(|_x| ~"b"), Ok(~"b"));
|
||||
assert_eq!(Err::<~str, ~str>(~"a").map(|_x| ~"b"), Err(~"a"));
|
||||
assert_eq!(Ok::<~str, ~str>(~"a").map(|x| (~"b").append(*x)), Ok(~"ba"));
|
||||
assert_eq!(Err::<~str, ~str>(~"a").map(|x| (~"b").append(*x)), Err(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_impl_map_err() {
|
||||
assert_eq!(Ok::<~str, ~str>(~"a").map_err(|_x| ~"b"), Ok(~"a"));
|
||||
assert_eq!(Err::<~str, ~str>(~"a").map_err(|_x| ~"b"), Err(~"b"));
|
||||
assert_eq!(Ok::<~str, ~str>(~"a").map_err(|x| (~"b").append(*x)), Ok(~"a"));
|
||||
assert_eq!(Err::<~str, ~str>(~"a").map_err(|x| (~"b").append(*x)), Err(~"ba"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_impl_map_move() {
|
||||
assert_eq!(Ok::<~str, ~str>(~"a").map_move(|x| x + "b"), Ok(~"ab"));
|
||||
assert_eq!(Err::<~str, ~str>(~"a").map_move(|x| x + "b"), Err(~"a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_impl_map_err_move() {
|
||||
assert_eq!(Ok::<~str, ~str>(~"a").map_err_move(|x| x + "b"), Ok(~"a"));
|
||||
assert_eq!(Err::<~str, ~str>(~"a").map_err_move(|x| x + "b"), Err(~"ab"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ impl<T> ChanOne<T> {
|
|||
// Port is blocked. Wake it up.
|
||||
let recvr = BlockedTask::cast_from_uint(task_as_state);
|
||||
if do_resched {
|
||||
do recvr.wake().map_consume |woken_task| {
|
||||
do recvr.wake().map_move |woken_task| {
|
||||
Scheduler::run_task(woken_task);
|
||||
};
|
||||
} else {
|
||||
|
|
@ -225,9 +225,10 @@ impl<T> Select for PortOne<T> {
|
|||
fn optimistic_check(&mut self) -> bool {
|
||||
// The optimistic check is never necessary for correctness. For testing
|
||||
// purposes, making it randomly return false simulates a racing sender.
|
||||
use rand::{Rand, rng};
|
||||
let mut rng = rng();
|
||||
let actually_check = Rand::rand(&mut rng);
|
||||
use rand::{Rand};
|
||||
let actually_check = do Local::borrow::<Scheduler, bool> |sched| {
|
||||
Rand::rand(&mut sched.rng)
|
||||
};
|
||||
if actually_check {
|
||||
unsafe { (*self.packet()).state.load(Acquire) == STATE_ONE }
|
||||
} else {
|
||||
|
|
@ -381,7 +382,7 @@ impl<T> Drop for ChanOne<T> {
|
|||
// The port is blocked waiting for a message we will never send. Wake it.
|
||||
assert!((*this.packet()).payload.is_none());
|
||||
let recvr = BlockedTask::cast_from_uint(task_as_state);
|
||||
do recvr.wake().map_consume |woken_task| {
|
||||
do recvr.wake().map_move |woken_task| {
|
||||
Scheduler::run_task(woken_task);
|
||||
};
|
||||
}
|
||||
|
|
@ -508,7 +509,11 @@ impl<T> Peekable<T> for Port<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Select for Port<T> {
|
||||
// XXX: Kind of gross. A Port<T> should be selectable so you can make an array
|
||||
// of them, but a &Port<T> should also be selectable so you can select2 on it
|
||||
// alongside a PortOne<U> without passing the port by value in recv_ready.
|
||||
|
||||
impl<'self, T> Select for &'self Port<T> {
|
||||
#[inline]
|
||||
fn optimistic_check(&mut self) -> bool {
|
||||
do self.next.with_mut_ref |pone| { pone.optimistic_check() }
|
||||
|
|
@ -526,12 +531,29 @@ impl<T> Select for Port<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> SelectPort<(T, Port<T>)> for Port<T> {
|
||||
fn recv_ready(self) -> Option<(T, Port<T>)> {
|
||||
impl<T> Select for Port<T> {
|
||||
#[inline]
|
||||
fn optimistic_check(&mut self) -> bool {
|
||||
(&*self).optimistic_check()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool {
|
||||
(&*self).block_on(sched, task)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unblock_from(&mut self) -> bool {
|
||||
(&*self).unblock_from()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self, T> SelectPort<T> for &'self Port<T> {
|
||||
fn recv_ready(self) -> Option<T> {
|
||||
match self.next.take().recv_ready() {
|
||||
Some(StreamPayload { val, next }) => {
|
||||
self.next.put_back(next);
|
||||
Some((val, self))
|
||||
Some(val)
|
||||
}
|
||||
None => None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@
|
|||
|
||||
//! Runtime environment settings
|
||||
|
||||
use from_str::FromStr;
|
||||
use libc::{size_t, c_char, c_int};
|
||||
use option::{Some, None};
|
||||
use os;
|
||||
|
||||
// OLD RT stuff
|
||||
|
||||
pub struct Environment {
|
||||
/// The number of threads to use by default
|
||||
|
|
@ -47,3 +52,26 @@ pub fn get() -> &Environment {
|
|||
extern {
|
||||
fn rust_get_rt_env() -> &Environment;
|
||||
}
|
||||
|
||||
// NEW RT stuff
|
||||
|
||||
// Note that these are all accessed without any synchronization.
|
||||
// They are expected to be initialized once then left alone.
|
||||
|
||||
static mut MIN_STACK: uint = 2000000;
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
match os::getenv("RUST_MIN_STACK") {
|
||||
Some(s) => match FromStr::from_str(s) {
|
||||
Some(i) => MIN_STACK = i,
|
||||
None => ()
|
||||
},
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_stack() -> uint {
|
||||
unsafe { MIN_STACK }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub struct MemWriter {
|
|||
}
|
||||
|
||||
impl MemWriter {
|
||||
pub fn new() -> MemWriter { MemWriter { buf: ~[] } }
|
||||
pub fn new() -> MemWriter { MemWriter { buf: vec::with_capacity(128) } }
|
||||
}
|
||||
|
||||
impl Writer for MemWriter {
|
||||
|
|
|
|||
|
|
@ -402,10 +402,10 @@ impl KillHandle {
|
|||
|| {
|
||||
// Prefer to check tombstones that were there first,
|
||||
// being "more fair" at the expense of tail-recursion.
|
||||
others.take().map_consume_default(true, |f| f()) && {
|
||||
others.take().map_move_default(true, |f| f()) && {
|
||||
let mut inner = this.take().unwrap();
|
||||
(!inner.any_child_failed) &&
|
||||
inner.child_tombstones.take_map_default(true, |f| f())
|
||||
inner.child_tombstones.take().map_move_default(true, |f| f())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -424,7 +424,7 @@ impl KillHandle {
|
|||
let others = Cell::new(other_tombstones); // :(
|
||||
|| {
|
||||
// Prefer fairness to tail-recursion, as in above case.
|
||||
others.take().map_consume_default(true, |f| f()) &&
|
||||
others.take().map_move_default(true, |f| f()) &&
|
||||
f.take()()
|
||||
}
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ impl Death {
|
|||
{ use util; util::ignore(group); }
|
||||
|
||||
// Step 1. Decide if we need to collect child failures synchronously.
|
||||
do self.on_exit.take_map |on_exit| {
|
||||
do self.on_exit.take().map_move |on_exit| {
|
||||
if success {
|
||||
// We succeeded, but our children might not. Need to wait for them.
|
||||
let mut inner = self.kill_handle.take_unwrap().unwrap();
|
||||
|
|
@ -501,7 +501,7 @@ impl Death {
|
|||
success = false;
|
||||
} else {
|
||||
// Lockless access to tombstones protected by unwrap barrier.
|
||||
success = inner.child_tombstones.take_map_default(true, |f| f());
|
||||
success = inner.child_tombstones.take().map_move_default(true, |f| f());
|
||||
}
|
||||
}
|
||||
on_exit(success);
|
||||
|
|
@ -510,12 +510,12 @@ impl Death {
|
|||
// Step 2. Possibly alert possibly-watching parent to failure status.
|
||||
// Note that as soon as parent_handle goes out of scope, the parent
|
||||
// can successfully unwrap its handle and collect our reported status.
|
||||
do self.watching_parent.take_map |mut parent_handle| {
|
||||
do self.watching_parent.take().map_move |mut parent_handle| {
|
||||
if success {
|
||||
// Our handle might be None if we had an exit callback, and
|
||||
// already unwrapped it. But 'success' being true means no
|
||||
// child failed, so there's nothing to do (see below case).
|
||||
do self.kill_handle.take_map |own_handle| {
|
||||
do self.kill_handle.take().map_move |own_handle| {
|
||||
own_handle.reparent_children_to(&mut parent_handle);
|
||||
};
|
||||
} else {
|
||||
|
|
@ -590,7 +590,8 @@ impl Death {
|
|||
#[inline]
|
||||
pub fn assert_may_sleep(&self) {
|
||||
if self.wont_sleep != 0 {
|
||||
rtabort!("illegal atomic-sleep: can't deschedule inside atomically()");
|
||||
rtabort!("illegal atomic-sleep: attempt to reschedule while \
|
||||
using an Exclusive or LittleLock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -614,6 +615,7 @@ mod test {
|
|||
// Test cases don't care about the spare killed flag.
|
||||
fn make_kill_handle() -> KillHandle { let (h,_) = KillHandle::new(); h }
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
fn no_tombstone_success() {
|
||||
do run_in_newsched_task {
|
||||
|
|
@ -819,6 +821,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
fn block_and_get_killed() {
|
||||
do with_test_task |mut task| {
|
||||
|
|
@ -830,6 +833,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
fn block_already_killed() {
|
||||
do with_test_task |mut task| {
|
||||
|
|
@ -839,6 +843,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
fn block_unkillably_and_get_killed() {
|
||||
do with_test_task |mut task| {
|
||||
|
|
@ -856,6 +861,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
fn block_on_pipe() {
|
||||
// Tests the "killable" path of casting to/from uint.
|
||||
|
|
@ -869,6 +875,7 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
fn block_unkillably_on_pipe() {
|
||||
// Tests the "indestructible" path of casting to/from uint.
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ impl Local for IoFactoryObject {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use option::None;
|
||||
use unstable::run_in_bare_thread;
|
||||
use rt::test::*;
|
||||
use super::*;
|
||||
|
|
@ -137,7 +138,7 @@ mod test {
|
|||
do run_in_bare_thread {
|
||||
local_ptr::init_tls_key();
|
||||
let mut sched = ~new_test_uv_sched();
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, || {});
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
|
||||
Local::put(task);
|
||||
let task: ~Task = Local::take();
|
||||
cleanup_task(task);
|
||||
|
|
@ -149,11 +150,11 @@ mod test {
|
|||
do run_in_bare_thread {
|
||||
local_ptr::init_tls_key();
|
||||
let mut sched = ~new_test_uv_sched();
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, || {});
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
|
||||
Local::put(task);
|
||||
let task: ~Task = Local::take();
|
||||
cleanup_task(task);
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, || {});
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
|
||||
Local::put(task);
|
||||
let task: ~Task = Local::take();
|
||||
cleanup_task(task);
|
||||
|
|
@ -166,7 +167,7 @@ mod test {
|
|||
do run_in_bare_thread {
|
||||
local_ptr::init_tls_key();
|
||||
let mut sched = ~new_test_uv_sched();
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, || {});
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
|
||||
Local::put(task);
|
||||
|
||||
unsafe {
|
||||
|
|
@ -182,7 +183,7 @@ mod test {
|
|||
do run_in_bare_thread {
|
||||
local_ptr::init_tls_key();
|
||||
let mut sched = ~new_test_uv_sched();
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, || {});
|
||||
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
|
||||
Local::put(task);
|
||||
|
||||
let res = do Local::borrow::<Task,bool> |_task| {
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ Several modules in `core` are clients of `rt`:
|
|||
use cell::Cell;
|
||||
use clone::Clone;
|
||||
use container::Container;
|
||||
use iter::Times;
|
||||
use iterator::{Iterator, IteratorUtil};
|
||||
use iterator::{Iterator, IteratorUtil, range};
|
||||
use option::{Some, None};
|
||||
use ptr::RawPtr;
|
||||
use rt::local::Local;
|
||||
|
|
@ -212,6 +211,7 @@ pub fn init(argc: int, argv: **u8, crate_map: *u8) {
|
|||
// Need to propagate the unsafety to `start`.
|
||||
unsafe {
|
||||
args::init(argc, argv);
|
||||
env::init();
|
||||
logging::init(crate_map);
|
||||
rust_update_gc_metadata(crate_map);
|
||||
}
|
||||
|
|
@ -246,11 +246,16 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
|
|||
|
||||
let main = Cell::new(main);
|
||||
|
||||
// The shared list of sleeping schedulers. Schedulers wake each other
|
||||
// occassionally to do new work.
|
||||
// The shared list of sleeping schedulers.
|
||||
let sleepers = SleeperList::new();
|
||||
// The shared work queue. Temporary until work stealing is implemented.
|
||||
let work_queue = WorkQueue::new();
|
||||
|
||||
// Create a work queue for each scheduler, ntimes. Create an extra
|
||||
// for the main thread if that flag is set. We won't steal from it.
|
||||
let mut work_queues = ~[];
|
||||
for _ in range(0u, nscheds) {
|
||||
let work_queue: WorkQueue<~Task> = WorkQueue::new();
|
||||
work_queues.push(work_queue);
|
||||
}
|
||||
|
||||
// The schedulers.
|
||||
let mut scheds = ~[];
|
||||
|
|
@ -258,12 +263,15 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
|
|||
// sent the Shutdown message to terminate the schedulers.
|
||||
let mut handles = ~[];
|
||||
|
||||
do nscheds.times {
|
||||
for i in range(0u, nscheds) {
|
||||
rtdebug!("inserting a regular scheduler");
|
||||
|
||||
// Every scheduler is driven by an I/O event loop.
|
||||
let loop_ = ~UvEventLoop::new();
|
||||
let mut sched = ~Scheduler::new(loop_, work_queue.clone(), sleepers.clone());
|
||||
let mut sched = ~Scheduler::new(loop_,
|
||||
work_queues[i].clone(),
|
||||
work_queues.clone(),
|
||||
sleepers.clone());
|
||||
let handle = sched.make_handle();
|
||||
|
||||
scheds.push(sched);
|
||||
|
|
@ -279,9 +287,14 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
|
|||
let friend_handle = friend_sched.make_handle();
|
||||
scheds.push(friend_sched);
|
||||
|
||||
// This scheduler needs a queue that isn't part of the stealee
|
||||
// set.
|
||||
let work_queue = WorkQueue::new();
|
||||
|
||||
let main_loop = ~UvEventLoop::new();
|
||||
let mut main_sched = ~Scheduler::new_special(main_loop,
|
||||
work_queue.clone(),
|
||||
work_queue,
|
||||
work_queues.clone(),
|
||||
sleepers.clone(),
|
||||
false,
|
||||
Some(friend_handle));
|
||||
|
|
@ -330,8 +343,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
|
|||
// In the case where we do not use a main_thread scheduler we
|
||||
// run the main task in one of our threads.
|
||||
|
||||
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
|
||||
main.take());
|
||||
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, None, main.take());
|
||||
main_task.death.on_exit = Some(on_exit.take());
|
||||
let main_task_cell = Cell::new(main_task);
|
||||
|
||||
|
|
@ -351,7 +363,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
|
|||
let sched_cell = Cell::new(sched);
|
||||
let thread = do Thread::start {
|
||||
let mut sched = sched_cell.take();
|
||||
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool) || {
|
||||
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
|
||||
rtdebug!("boostraping a non-primary scheduler");
|
||||
};
|
||||
sched.bootstrap(bootstrap_task);
|
||||
|
|
@ -368,10 +380,10 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
|
|||
let mut main_sched = main_sched.unwrap();
|
||||
|
||||
let home = Sched(main_sched.make_handle());
|
||||
let mut main_task = ~Task::new_root_homed(&mut main_sched.stack_pool,
|
||||
let mut main_task = ~Task::new_root_homed(&mut main_sched.stack_pool, None,
|
||||
home, main.take());
|
||||
main_task.death.on_exit = Some(on_exit.take());
|
||||
rtdebug!("boostrapping main_task");
|
||||
rtdebug!("bootstrapping main_task");
|
||||
|
||||
main_sched.bootstrap(main_task);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use option::{Option, Some, None};
|
|||
use cast::{transmute, transmute_mut_region, transmute_mut_unsafe};
|
||||
use clone::Clone;
|
||||
use unstable::raw;
|
||||
|
||||
use super::sleeper_list::SleeperList;
|
||||
use super::work_queue::WorkQueue;
|
||||
use super::stack::{StackPool};
|
||||
|
|
@ -28,6 +27,9 @@ use rt::rtio::RemoteCallback;
|
|||
use rt::metrics::SchedMetrics;
|
||||
use borrow::{to_uint};
|
||||
use cell::Cell;
|
||||
use rand::{XorShiftRng, RngUtil};
|
||||
use iterator::{range};
|
||||
use vec::{OwnedVector};
|
||||
|
||||
/// The Scheduler is responsible for coordinating execution of Coroutines
|
||||
/// on a single thread. When the scheduler is running it is owned by
|
||||
|
|
@ -37,9 +39,11 @@ use cell::Cell;
|
|||
/// XXX: This creates too many callbacks to run_sched_once, resulting
|
||||
/// in too much allocation and too many events.
|
||||
pub struct Scheduler {
|
||||
/// A queue of available work. Under a work-stealing policy there
|
||||
/// is one per Scheduler.
|
||||
work_queue: WorkQueue<~Task>,
|
||||
/// There are N work queues, one per scheduler.
|
||||
priv work_queue: WorkQueue<~Task>,
|
||||
/// Work queues for the other schedulers. These are created by
|
||||
/// cloning the core work queues.
|
||||
work_queues: ~[WorkQueue<~Task>],
|
||||
/// The queue of incoming messages from other schedulers.
|
||||
/// These are enqueued by SchedHandles after which a remote callback
|
||||
/// is triggered to handle the message.
|
||||
|
|
@ -70,7 +74,10 @@ pub struct Scheduler {
|
|||
run_anything: bool,
|
||||
/// If the scheduler shouldn't run some tasks, a friend to send
|
||||
/// them to.
|
||||
friend_handle: Option<SchedHandle>
|
||||
friend_handle: Option<SchedHandle>,
|
||||
/// A fast XorShift rng for scheduler use
|
||||
rng: XorShiftRng
|
||||
|
||||
}
|
||||
|
||||
pub struct SchedHandle {
|
||||
|
|
@ -97,10 +104,13 @@ impl Scheduler {
|
|||
|
||||
pub fn new(event_loop: ~EventLoopObject,
|
||||
work_queue: WorkQueue<~Task>,
|
||||
work_queues: ~[WorkQueue<~Task>],
|
||||
sleeper_list: SleeperList)
|
||||
-> Scheduler {
|
||||
|
||||
Scheduler::new_special(event_loop, work_queue, sleeper_list, true, None)
|
||||
Scheduler::new_special(event_loop, work_queue,
|
||||
work_queues,
|
||||
sleeper_list, true, None)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +118,7 @@ impl Scheduler {
|
|||
// task field is None.
|
||||
pub fn new_special(event_loop: ~EventLoopObject,
|
||||
work_queue: WorkQueue<~Task>,
|
||||
work_queues: ~[WorkQueue<~Task>],
|
||||
sleeper_list: SleeperList,
|
||||
run_anything: bool,
|
||||
friend: Option<SchedHandle>)
|
||||
|
|
@ -120,12 +131,14 @@ impl Scheduler {
|
|||
no_sleep: false,
|
||||
event_loop: event_loop,
|
||||
work_queue: work_queue,
|
||||
work_queues: work_queues,
|
||||
stack_pool: StackPool::new(),
|
||||
sched_task: None,
|
||||
cleanup_job: None,
|
||||
metrics: SchedMetrics::new(),
|
||||
run_anything: run_anything,
|
||||
friend_handle: friend
|
||||
friend_handle: friend,
|
||||
rng: XorShiftRng::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +261,7 @@ impl Scheduler {
|
|||
|
||||
// Second activity is to try resuming a task from the queue.
|
||||
|
||||
let result = sched.resume_task_from_queue();
|
||||
let result = sched.do_work();
|
||||
let mut sched = match result {
|
||||
Some(sched) => {
|
||||
// Failed to dequeue a task, so we return.
|
||||
|
|
@ -325,7 +338,7 @@ impl Scheduler {
|
|||
/// As enqueue_task, but with the possibility for the blocked task to
|
||||
/// already have been killed.
|
||||
pub fn enqueue_blocked_task(&mut self, blocked_task: BlockedTask) {
|
||||
do blocked_task.wake().map_consume |task| {
|
||||
do blocked_task.wake().map_move |task| {
|
||||
self.enqueue_task(task);
|
||||
};
|
||||
}
|
||||
|
|
@ -415,47 +428,98 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
// Resume a task from the queue - but also take into account that
|
||||
// it might not belong here.
|
||||
// Workstealing: In this iteration of the runtime each scheduler
|
||||
// thread has a distinct work queue. When no work is available
|
||||
// locally, make a few attempts to steal work from the queues of
|
||||
// other scheduler threads. If a few steals fail we end up in the
|
||||
// old "no work" path which is fine.
|
||||
|
||||
// If we perform a scheduler action we give away the scheduler ~
|
||||
// pointer, if it is still available we return it.
|
||||
|
||||
fn resume_task_from_queue(~self) -> Option<~Scheduler> {
|
||||
|
||||
let mut this = self;
|
||||
|
||||
match this.work_queue.pop() {
|
||||
// First step in the process is to find a task. This function does
|
||||
// that by first checking the local queue, and if there is no work
|
||||
// there, trying to steal from the remote work queues.
|
||||
fn find_work(&mut self) -> Option<~Task> {
|
||||
rtdebug!("scheduler looking for work");
|
||||
match self.work_queue.pop() {
|
||||
Some(task) => {
|
||||
let mut task = task;
|
||||
let home = task.take_unwrap_home();
|
||||
match home {
|
||||
Sched(home_handle) => {
|
||||
if home_handle.sched_id != this.sched_id() {
|
||||
task.give_home(Sched(home_handle));
|
||||
Scheduler::send_task_home(task);
|
||||
return Some(this);
|
||||
} else {
|
||||
this.event_loop.callback(Scheduler::run_sched_once);
|
||||
task.give_home(Sched(home_handle));
|
||||
this.resume_task_immediately(task);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
AnySched if this.run_anything => {
|
||||
this.event_loop.callback(Scheduler::run_sched_once);
|
||||
task.give_home(AnySched);
|
||||
this.resume_task_immediately(task);
|
||||
return None;
|
||||
}
|
||||
AnySched => {
|
||||
task.give_home(AnySched);
|
||||
this.send_to_friend(task);
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
rtdebug!("found a task locally");
|
||||
return Some(task)
|
||||
}
|
||||
None => {
|
||||
// Our naive stealing, try kinda hard.
|
||||
rtdebug!("scheduler trying to steal");
|
||||
let _len = self.work_queues.len();
|
||||
return self.try_steals(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With no backoff try stealing n times from the queues the
|
||||
// scheduler knows about. This naive implementation can steal from
|
||||
// our own queue or from other special schedulers.
|
||||
fn try_steals(&mut self, n: uint) -> Option<~Task> {
|
||||
for _ in range(0, n) {
|
||||
let index = self.rng.gen_uint_range(0, self.work_queues.len());
|
||||
let work_queues = &mut self.work_queues;
|
||||
match work_queues[index].steal() {
|
||||
Some(task) => {
|
||||
rtdebug!("found task by stealing"); return Some(task)
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
};
|
||||
rtdebug!("giving up on stealing");
|
||||
return None;
|
||||
}
|
||||
|
||||
// Given a task, execute it correctly.
|
||||
fn process_task(~self, task: ~Task) -> Option<~Scheduler> {
|
||||
let mut this = self;
|
||||
let mut task = task;
|
||||
|
||||
rtdebug!("processing a task");
|
||||
|
||||
let home = task.take_unwrap_home();
|
||||
match home {
|
||||
Sched(home_handle) => {
|
||||
if home_handle.sched_id != this.sched_id() {
|
||||
rtdebug!("sending task home");
|
||||
task.give_home(Sched(home_handle));
|
||||
Scheduler::send_task_home(task);
|
||||
return Some(this);
|
||||
} else {
|
||||
rtdebug!("running task here");
|
||||
task.give_home(Sched(home_handle));
|
||||
this.resume_task_immediately(task);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
AnySched if this.run_anything => {
|
||||
rtdebug!("running anysched task here");
|
||||
task.give_home(AnySched);
|
||||
this.resume_task_immediately(task);
|
||||
return None;
|
||||
}
|
||||
AnySched => {
|
||||
rtdebug!("sending task to friend");
|
||||
task.give_home(AnySched);
|
||||
this.send_to_friend(task);
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bundle the helpers together.
|
||||
fn do_work(~self) -> Option<~Scheduler> {
|
||||
let mut this = self;
|
||||
|
||||
rtdebug!("scheduler calling do work");
|
||||
match this.find_work() {
|
||||
Some(task) => {
|
||||
rtdebug!("found some work! processing the task");
|
||||
return this.process_task(task);
|
||||
}
|
||||
None => {
|
||||
rtdebug!("no work was found, returning the scheduler struct");
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -533,7 +597,7 @@ impl Scheduler {
|
|||
sched.enqueue_blocked_task(last_task);
|
||||
}
|
||||
};
|
||||
opt.map_consume(Local::put);
|
||||
opt.map_move(Local::put);
|
||||
}
|
||||
|
||||
// The primary function for changing contexts. In the current
|
||||
|
|
@ -711,7 +775,6 @@ impl Scheduler {
|
|||
GiveTask(task, f) => f.to_fn()(self, task)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The cases for the below function.
|
||||
|
|
@ -745,6 +808,8 @@ impl ClosureConverter for UnsafeTaskReceiver {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern mod extra;
|
||||
|
||||
use prelude::*;
|
||||
use rt::test::*;
|
||||
use unstable::run_in_bare_thread;
|
||||
|
|
@ -833,7 +898,7 @@ mod test {
|
|||
let mut sched = ~new_test_uv_sched();
|
||||
let sched_handle = sched.make_handle();
|
||||
|
||||
let mut task = ~do Task::new_root_homed(&mut sched.stack_pool,
|
||||
let mut task = ~do Task::new_root_homed(&mut sched.stack_pool, None,
|
||||
Sched(sched_handle)) {
|
||||
unsafe { *task_ran_ptr = true };
|
||||
assert!(Task::on_appropriate_sched());
|
||||
|
|
@ -862,12 +927,15 @@ mod test {
|
|||
do run_in_bare_thread {
|
||||
|
||||
let sleepers = SleeperList::new();
|
||||
let work_queue = WorkQueue::new();
|
||||
let normal_queue = WorkQueue::new();
|
||||
let special_queue = WorkQueue::new();
|
||||
let queues = ~[normal_queue.clone(), special_queue.clone()];
|
||||
|
||||
// Our normal scheduler
|
||||
let mut normal_sched = ~Scheduler::new(
|
||||
~UvEventLoop::new(),
|
||||
work_queue.clone(),
|
||||
normal_queue,
|
||||
queues.clone(),
|
||||
sleepers.clone());
|
||||
|
||||
let normal_handle = Cell::new(normal_sched.make_handle());
|
||||
|
|
@ -877,7 +945,8 @@ mod test {
|
|||
// Our special scheduler
|
||||
let mut special_sched = ~Scheduler::new_special(
|
||||
~UvEventLoop::new(),
|
||||
work_queue.clone(),
|
||||
special_queue.clone(),
|
||||
queues.clone(),
|
||||
sleepers.clone(),
|
||||
false,
|
||||
Some(friend_handle));
|
||||
|
|
@ -893,21 +962,21 @@ mod test {
|
|||
// 3) task not homed, sched requeues
|
||||
// 4) task not home, send home
|
||||
|
||||
let task1 = ~do Task::new_root_homed(&mut special_sched.stack_pool,
|
||||
let task1 = ~do Task::new_root_homed(&mut special_sched.stack_pool, None,
|
||||
Sched(t1_handle)) || {
|
||||
rtassert!(Task::on_appropriate_sched());
|
||||
};
|
||||
rtdebug!("task1 id: **%u**", borrow::to_uint(task1));
|
||||
|
||||
let task2 = ~do Task::new_root(&mut normal_sched.stack_pool) {
|
||||
let task2 = ~do Task::new_root(&mut normal_sched.stack_pool, None) {
|
||||
rtassert!(Task::on_appropriate_sched());
|
||||
};
|
||||
|
||||
let task3 = ~do Task::new_root(&mut normal_sched.stack_pool) {
|
||||
let task3 = ~do Task::new_root(&mut normal_sched.stack_pool, None) {
|
||||
rtassert!(Task::on_appropriate_sched());
|
||||
};
|
||||
|
||||
let task4 = ~do Task::new_root_homed(&mut special_sched.stack_pool,
|
||||
let task4 = ~do Task::new_root_homed(&mut special_sched.stack_pool, None,
|
||||
Sched(t4_handle)) {
|
||||
rtassert!(Task::on_appropriate_sched());
|
||||
};
|
||||
|
|
@ -923,7 +992,7 @@ mod test {
|
|||
let port = Cell::new(port);
|
||||
let chan = Cell::new(chan);
|
||||
|
||||
let normal_task = ~do Task::new_root(&mut normal_sched.stack_pool) {
|
||||
let normal_task = ~do Task::new_root(&mut normal_sched.stack_pool, None) {
|
||||
rtdebug!("*about to submit task2*");
|
||||
Scheduler::run_task(task2.take());
|
||||
rtdebug!("*about to submit task4*");
|
||||
|
|
@ -938,7 +1007,7 @@ mod test {
|
|||
|
||||
rtdebug!("normal task: %u", borrow::to_uint(normal_task));
|
||||
|
||||
let special_task = ~do Task::new_root(&mut special_sched.stack_pool) {
|
||||
let special_task = ~do Task::new_root(&mut special_sched.stack_pool, None) {
|
||||
rtdebug!("*about to submit task1*");
|
||||
Scheduler::run_task(task1.take());
|
||||
rtdebug!("*about to submit task3*");
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ mod test {
|
|||
fn select_stream() {
|
||||
use util;
|
||||
use comm::GenericChan;
|
||||
use iter::Times;
|
||||
|
||||
// Sends 10 buffered packets, and uses select to retrieve them all.
|
||||
// Puts the port in a different spot in the vector each time.
|
||||
|
|
@ -199,9 +200,7 @@ mod test {
|
|||
// get it back out
|
||||
util::swap(port.get_mut_ref(), &mut ports[index]);
|
||||
// NB. Not recv(), because optimistic_check randomly fails.
|
||||
let (data, new_port) = port.take_unwrap().recv_ready().unwrap();
|
||||
assert!(data == 31337);
|
||||
port = Some(new_port);
|
||||
assert!(port.get_ref().recv_ready().unwrap() == 31337);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -265,6 +264,7 @@ mod test {
|
|||
|
||||
fn select_racing_senders_helper(killable: bool, send_on_chans: ~[uint]) {
|
||||
use rt::test::spawntask_random;
|
||||
use iter::Times;
|
||||
|
||||
do run_in_newsched_task {
|
||||
// A bit of stress, since ordinarily this is just smoke and mirrors.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use libc::{c_void, uintptr_t};
|
|||
use ptr;
|
||||
use prelude::*;
|
||||
use option::{Option, Some, None};
|
||||
use rt::env;
|
||||
use rt::kill::Death;
|
||||
use rt::local::Local;
|
||||
use rt::logging::StdErrLogger;
|
||||
|
|
@ -85,12 +86,13 @@ impl Task {
|
|||
|
||||
// A helper to build a new task using the dynamically found
|
||||
// scheduler and task. Only works in GreenTask context.
|
||||
pub fn build_homed_child(f: ~fn(), home: SchedHome) -> ~Task {
|
||||
pub fn build_homed_child(stack_size: Option<uint>, f: ~fn(), home: SchedHome) -> ~Task {
|
||||
let f = Cell::new(f);
|
||||
let home = Cell::new(home);
|
||||
do Local::borrow::<Task, ~Task> |running_task| {
|
||||
let mut sched = running_task.sched.take_unwrap();
|
||||
let new_task = ~running_task.new_child_homed(&mut sched.stack_pool,
|
||||
stack_size,
|
||||
home.take(),
|
||||
f.take());
|
||||
running_task.sched = Some(sched);
|
||||
|
|
@ -98,25 +100,26 @@ impl Task {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_child(f: ~fn()) -> ~Task {
|
||||
Task::build_homed_child(f, AnySched)
|
||||
pub fn build_child(stack_size: Option<uint>, f: ~fn()) -> ~Task {
|
||||
Task::build_homed_child(stack_size, f, AnySched)
|
||||
}
|
||||
|
||||
pub fn build_homed_root(f: ~fn(), home: SchedHome) -> ~Task {
|
||||
pub fn build_homed_root(stack_size: Option<uint>, f: ~fn(), home: SchedHome) -> ~Task {
|
||||
let f = Cell::new(f);
|
||||
let home = Cell::new(home);
|
||||
do Local::borrow::<Task, ~Task> |running_task| {
|
||||
let mut sched = running_task.sched.take_unwrap();
|
||||
let new_task = ~Task::new_root_homed(&mut sched.stack_pool,
|
||||
home.take(),
|
||||
f.take());
|
||||
stack_size,
|
||||
home.take(),
|
||||
f.take());
|
||||
running_task.sched = Some(sched);
|
||||
new_task
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_root(f: ~fn()) -> ~Task {
|
||||
Task::build_homed_root(f, AnySched)
|
||||
pub fn build_root(stack_size: Option<uint>, f: ~fn()) -> ~Task {
|
||||
Task::build_homed_root(stack_size, f, AnySched)
|
||||
}
|
||||
|
||||
pub fn new_sched_task() -> Task {
|
||||
|
|
@ -137,17 +140,20 @@ impl Task {
|
|||
}
|
||||
|
||||
pub fn new_root(stack_pool: &mut StackPool,
|
||||
stack_size: Option<uint>,
|
||||
start: ~fn()) -> Task {
|
||||
Task::new_root_homed(stack_pool, AnySched, start)
|
||||
Task::new_root_homed(stack_pool, stack_size, AnySched, start)
|
||||
}
|
||||
|
||||
pub fn new_child(&mut self,
|
||||
stack_pool: &mut StackPool,
|
||||
stack_size: Option<uint>,
|
||||
start: ~fn()) -> Task {
|
||||
self.new_child_homed(stack_pool, AnySched, start)
|
||||
self.new_child_homed(stack_pool, stack_size, AnySched, start)
|
||||
}
|
||||
|
||||
pub fn new_root_homed(stack_pool: &mut StackPool,
|
||||
stack_size: Option<uint>,
|
||||
home: SchedHome,
|
||||
start: ~fn()) -> Task {
|
||||
Task {
|
||||
|
|
@ -160,7 +166,7 @@ impl Task {
|
|||
death: Death::new(),
|
||||
destroyed: false,
|
||||
name: None,
|
||||
coroutine: Some(Coroutine::new(stack_pool, start)),
|
||||
coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
|
||||
sched: None,
|
||||
task_type: GreenTask(Some(~home))
|
||||
}
|
||||
|
|
@ -168,6 +174,7 @@ impl Task {
|
|||
|
||||
pub fn new_child_homed(&mut self,
|
||||
stack_pool: &mut StackPool,
|
||||
stack_size: Option<uint>,
|
||||
home: SchedHome,
|
||||
start: ~fn()) -> Task {
|
||||
Task {
|
||||
|
|
@ -181,7 +188,7 @@ impl Task {
|
|||
death: self.death.new_child(),
|
||||
destroyed: false,
|
||||
name: None,
|
||||
coroutine: Some(Coroutine::new(stack_pool, start)),
|
||||
coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
|
||||
sched: None,
|
||||
task_type: GreenTask(Some(~home))
|
||||
}
|
||||
|
|
@ -325,11 +332,13 @@ impl Drop for Task {
|
|||
|
||||
impl Coroutine {
|
||||
|
||||
pub fn new(stack_pool: &mut StackPool, start: ~fn()) -> Coroutine {
|
||||
static MIN_STACK_SIZE: uint = 3000000; // XXX: Too much stack
|
||||
|
||||
pub fn new(stack_pool: &mut StackPool, stack_size: Option<uint>, start: ~fn()) -> Coroutine {
|
||||
let stack_size = match stack_size {
|
||||
Some(size) => size,
|
||||
None => env::min_stack()
|
||||
};
|
||||
let start = Coroutine::build_start_wrapper(start);
|
||||
let mut stack = stack_pool.take_segment(MIN_STACK_SIZE);
|
||||
let mut stack = stack_pool.take_segment(stack_size);
|
||||
let initial_context = Context::new(start, &mut stack);
|
||||
Coroutine {
|
||||
current_stack_segment: stack,
|
||||
|
|
@ -465,10 +474,10 @@ mod test {
|
|||
do run_in_newsched_task() {
|
||||
static key: local_data::Key<@~str> = &local_data::Key;
|
||||
local_data::set(key, @~"data");
|
||||
assert!(*local_data::get(key, |k| k.map(|&k| *k)).unwrap() == ~"data");
|
||||
assert!(*local_data::get(key, |k| k.map_move(|k| *k)).unwrap() == ~"data");
|
||||
static key2: local_data::Key<@~str> = &local_data::Key;
|
||||
local_data::set(key2, @~"data");
|
||||
assert!(*local_data::get(key2, |k| k.map(|&k| *k)).unwrap() == ~"data");
|
||||
assert!(*local_data::get(key2, |k| k.map_move(|k| *k)).unwrap() == ~"data");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use cell::Cell;
|
|||
use clone::Clone;
|
||||
use container::Container;
|
||||
use iterator::{Iterator, range};
|
||||
use vec::{OwnedVector, MutableVector};
|
||||
use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
|
||||
use vec::{OwnedVector, MutableVector, ImmutableVector};
|
||||
use rt::sched::Scheduler;
|
||||
use unstable::run_in_bare_thread;
|
||||
use rt::thread::Thread;
|
||||
|
|
@ -29,8 +29,12 @@ use result::{Result, Ok, Err};
|
|||
|
||||
pub fn new_test_uv_sched() -> Scheduler {
|
||||
|
||||
let queue = WorkQueue::new();
|
||||
let queues = ~[queue.clone()];
|
||||
|
||||
let mut sched = Scheduler::new(~UvEventLoop::new(),
|
||||
WorkQueue::new(),
|
||||
queue,
|
||||
queues,
|
||||
SleeperList::new());
|
||||
|
||||
// Don't wait for the Shutdown message
|
||||
|
|
@ -57,7 +61,7 @@ pub fn run_in_newsched_task_core(f: ~fn()) {
|
|||
exit_handle.take().send(Shutdown);
|
||||
rtassert!(exit_status);
|
||||
};
|
||||
let mut task = ~Task::new_root(&mut sched.stack_pool, f);
|
||||
let mut task = ~Task::new_root(&mut sched.stack_pool, None, f);
|
||||
task.death.on_exit = Some(on_exit);
|
||||
|
||||
sched.bootstrap(task);
|
||||
|
|
@ -164,15 +168,21 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
|
|||
};
|
||||
|
||||
let sleepers = SleeperList::new();
|
||||
let work_queue = WorkQueue::new();
|
||||
|
||||
let mut handles = ~[];
|
||||
let mut scheds = ~[];
|
||||
let mut work_queues = ~[];
|
||||
|
||||
for _ in range(0u, nthreads) {
|
||||
let work_queue = WorkQueue::new();
|
||||
work_queues.push(work_queue);
|
||||
}
|
||||
|
||||
for i in range(0u, nthreads) {
|
||||
let loop_ = ~UvEventLoop::new();
|
||||
let mut sched = ~Scheduler::new(loop_,
|
||||
work_queue.clone(),
|
||||
work_queues[i].clone(),
|
||||
work_queues.clone(),
|
||||
sleepers.clone());
|
||||
let handle = sched.make_handle();
|
||||
|
||||
|
|
@ -190,8 +200,7 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
|
|||
|
||||
rtassert!(exit_status);
|
||||
};
|
||||
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
|
||||
f.take());
|
||||
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, None, f.take());
|
||||
main_task.death.on_exit = Some(on_exit);
|
||||
|
||||
let mut threads = ~[];
|
||||
|
|
@ -209,7 +218,7 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
|
|||
|
||||
while !scheds.is_empty() {
|
||||
let mut sched = scheds.pop();
|
||||
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool) || {
|
||||
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
|
||||
rtdebug!("bootstrapping non-primary scheduler");
|
||||
};
|
||||
let bootstrap_task_cell = Cell::new(bootstrap_task);
|
||||
|
|
@ -232,12 +241,12 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
|
|||
|
||||
/// Test tasks will abort on failure instead of unwinding
|
||||
pub fn spawntask(f: ~fn()) {
|
||||
Scheduler::run_task(Task::build_child(f));
|
||||
Scheduler::run_task(Task::build_child(None, f));
|
||||
}
|
||||
|
||||
/// Create a new task and run it right now. Aborts on failure
|
||||
pub fn spawntask_later(f: ~fn()) {
|
||||
Scheduler::run_task_later(Task::build_child(f));
|
||||
Scheduler::run_task_later(Task::build_child(None, f));
|
||||
}
|
||||
|
||||
pub fn spawntask_random(f: ~fn()) {
|
||||
|
|
@ -259,7 +268,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
|
|||
let chan = Cell::new(chan);
|
||||
let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
|
||||
|
||||
let mut new_task = Task::build_root(f);
|
||||
let mut new_task = Task::build_root(None, f);
|
||||
new_task.death.on_exit = Some(on_exit);
|
||||
|
||||
Scheduler::run_task(new_task);
|
||||
|
|
@ -285,7 +294,7 @@ pub fn spawntask_thread(f: ~fn()) -> Thread {
|
|||
pub fn with_test_task(blk: ~fn(~Task) -> ~Task) {
|
||||
do run_in_bare_thread {
|
||||
let mut sched = ~new_test_uv_sched();
|
||||
let task = blk(~Task::new_root(&mut sched.stack_pool, ||{}));
|
||||
let task = blk(~Task::new_root(&mut sched.stack_pool, None, ||{}));
|
||||
cleanup_task(task);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -634,7 +634,6 @@ fn spawn_process_os(prog: &str, args: &[~str],
|
|||
|
||||
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
||||
use libc::funcs::bsd44::getdtablesize;
|
||||
use int;
|
||||
|
||||
mod rustrt {
|
||||
use libc::c_void;
|
||||
|
|
@ -667,10 +666,9 @@ fn spawn_process_os(prog: &str, args: &[~str],
|
|||
fail!("failure in dup3(err_fd, 2): %s", os::last_os_error());
|
||||
}
|
||||
// close all other fds
|
||||
do int::range_rev(getdtablesize() as int, 3) |fd| {
|
||||
for fd in range(3, getdtablesize()).invert() {
|
||||
close(fd as c_int);
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
do with_dirp(dir) |dirp| {
|
||||
if !dirp.is_null() && chdir(dirp) == -1 {
|
||||
|
|
@ -780,14 +778,14 @@ fn with_dirp<T>(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T {
|
|||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
priv fn free_handle(handle: *()) {
|
||||
fn free_handle(handle: *()) {
|
||||
unsafe {
|
||||
libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
priv fn free_handle(_handle: *()) {
|
||||
fn free_handle(_handle: *()) {
|
||||
// unix has no process handle object, just a pid
|
||||
}
|
||||
|
||||
|
|
@ -842,7 +840,7 @@ pub fn process_output(prog: &str, args: &[~str]) -> ProcessOutput {
|
|||
* operate on a none-existant process or, even worse, on a newer process
|
||||
* with the same id.
|
||||
*/
|
||||
priv fn waitpid(pid: pid_t) -> int {
|
||||
fn waitpid(pid: pid_t) -> int {
|
||||
return waitpid_os(pid);
|
||||
|
||||
#[cfg(windows)]
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ pub mod rand;
|
|||
pub mod run;
|
||||
pub mod sys;
|
||||
pub mod cast;
|
||||
pub mod fmt;
|
||||
pub mod repr;
|
||||
pub mod cleanup;
|
||||
pub mod reflect;
|
||||
|
|
@ -217,4 +218,6 @@ mod std {
|
|||
pub use unstable;
|
||||
pub use str;
|
||||
pub use os;
|
||||
pub use fmt;
|
||||
pub use to_bytes;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -855,7 +855,7 @@ pub fn count_bytes<'b>(s: &'b str, start: uint, n: uint) -> uint {
|
|||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc3629
|
||||
priv static UTF8_CHAR_WIDTH: [u8, ..256] = [
|
||||
static UTF8_CHAR_WIDTH: [u8, ..256] = [
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
|
|
@ -898,15 +898,15 @@ macro_rules! utf8_acc_cont_byte(
|
|||
)
|
||||
|
||||
// UTF-8 tags and ranges
|
||||
priv static TAG_CONT_U8: u8 = 128u8;
|
||||
priv static TAG_CONT: uint = 128u;
|
||||
priv static MAX_ONE_B: uint = 128u;
|
||||
priv static TAG_TWO_B: uint = 192u;
|
||||
priv static MAX_TWO_B: uint = 2048u;
|
||||
priv static TAG_THREE_B: uint = 224u;
|
||||
priv static MAX_THREE_B: uint = 65536u;
|
||||
priv static TAG_FOUR_B: uint = 240u;
|
||||
priv static MAX_UNICODE: uint = 1114112u;
|
||||
static TAG_CONT_U8: u8 = 128u8;
|
||||
static TAG_CONT: uint = 128u;
|
||||
static MAX_ONE_B: uint = 128u;
|
||||
static TAG_TWO_B: uint = 192u;
|
||||
static MAX_TWO_B: uint = 2048u;
|
||||
static TAG_THREE_B: uint = 224u;
|
||||
static MAX_THREE_B: uint = 65536u;
|
||||
static TAG_FOUR_B: uint = 240u;
|
||||
static MAX_UNICODE: uint = 1114112u;
|
||||
|
||||
/// Unsafe operations
|
||||
pub mod raw {
|
||||
|
|
@ -2085,7 +2085,7 @@ impl<'self> StrSlice<'self> for &'self str {
|
|||
} else {
|
||||
self.matches_index_iter(needle)
|
||||
.next()
|
||||
.map_consume(|(start, _end)| start)
|
||||
.map_move(|(start, _end)| start)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ pub fn to_ascii_lower(string: &str) -> ~str {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
priv fn map_bytes(string: &str, map: &'static [u8]) -> ~str {
|
||||
fn map_bytes(string: &str, map: &'static [u8]) -> ~str {
|
||||
let len = string.len();
|
||||
let mut result = str::with_capacity(len);
|
||||
unsafe {
|
||||
|
|
@ -329,7 +329,7 @@ pub fn eq_ignore_ascii_case(a: &str, b: &str) -> bool {
|
|||
|(byte_a, byte_b)| ASCII_LOWER_MAP[*byte_a] == ASCII_LOWER_MAP[*byte_b])
|
||||
}
|
||||
|
||||
priv static ASCII_LOWER_MAP: &'static [u8] = &[
|
||||
static ASCII_LOWER_MAP: &'static [u8] = &[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
|
|
@ -364,7 +364,7 @@ priv static ASCII_LOWER_MAP: &'static [u8] = &[
|
|||
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
||||
];
|
||||
|
||||
priv static ASCII_UPPER_MAP: &'static [u8] = &[
|
||||
static ASCII_UPPER_MAP: &'static [u8] = &[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ pub struct TaskOpts {
|
|||
indestructible: bool,
|
||||
notify_chan: Option<Chan<TaskResult>>,
|
||||
name: Option<~str>,
|
||||
sched: SchedOpts
|
||||
sched: SchedOpts,
|
||||
stack_size: Option<uint>
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,7 +198,8 @@ impl TaskBuilder {
|
|||
indestructible: self.opts.indestructible,
|
||||
notify_chan: notify_chan,
|
||||
name: name,
|
||||
sched: self.opts.sched
|
||||
sched: self.opts.sched,
|
||||
stack_size: self.opts.stack_size
|
||||
},
|
||||
gen_body: gen_body,
|
||||
can_not_copy: None,
|
||||
|
|
@ -351,7 +353,8 @@ impl TaskBuilder {
|
|||
indestructible: x.opts.indestructible,
|
||||
notify_chan: notify_chan,
|
||||
name: name,
|
||||
sched: x.opts.sched
|
||||
sched: x.opts.sched,
|
||||
stack_size: x.opts.stack_size
|
||||
};
|
||||
let f = match gen_body {
|
||||
Some(gen) => {
|
||||
|
|
@ -422,7 +425,8 @@ pub fn default_task_opts() -> TaskOpts {
|
|||
name: None,
|
||||
sched: SchedOpts {
|
||||
mode: DefaultScheduler,
|
||||
}
|
||||
},
|
||||
stack_size: None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -655,6 +659,7 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_kill_unkillable_task() {
|
||||
use rt::test::*;
|
||||
|
|
@ -675,6 +680,7 @@ fn test_kill_unkillable_task() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_kill_rekillable_task() {
|
||||
use rt::test::*;
|
||||
|
|
@ -716,6 +722,7 @@ fn test_cant_dup_task_builder() {
|
|||
#[cfg(test)]
|
||||
fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); }
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -734,6 +741,7 @@ fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
|
|||
po.recv();
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -741,6 +749,7 @@ fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
|
|||
do spawn_unlinked { fail!(); }
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -750,6 +759,7 @@ fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
|
|||
do 16.times { task::yield(); }
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_unlinked_sup_fail_down() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -762,6 +772,7 @@ fn test_spawn_unlinked_sup_fail_down() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -782,6 +793,7 @@ fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
|
|||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -798,6 +810,7 @@ fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
|
|||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -810,6 +823,7 @@ fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
|
|||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -822,6 +836,7 @@ fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
|
|||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -840,6 +855,7 @@ fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
|
|||
// A couple bonus linked failure tests - testing for failure propagation even
|
||||
// when the middle task exits successfully early before kill signals are sent.
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_failure_propagate_grandchild() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -856,6 +872,7 @@ fn test_spawn_failure_propagate_grandchild() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_failure_propagate_secondborn() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -872,6 +889,7 @@ fn test_spawn_failure_propagate_secondborn() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_failure_propagate_nephew_or_niece() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -888,6 +906,7 @@ fn test_spawn_failure_propagate_nephew_or_niece() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_linked_sup_propagate_sibling() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -1191,6 +1210,7 @@ fn test_avoid_copying_the_body_unlinked() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
#[ignore(cfg(windows))]
|
||||
#[should_fail]
|
||||
|
|
@ -1226,6 +1246,7 @@ fn test_unkillable() {
|
|||
po.recv();
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test]
|
||||
#[ignore(cfg(windows))]
|
||||
#[should_fail]
|
||||
|
|
@ -1292,6 +1313,7 @@ fn test_simple_newsched_spawn() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_watched() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
@ -1314,6 +1336,7 @@ fn test_spawn_watched() {
|
|||
}
|
||||
}
|
||||
|
||||
#[ignore(reason = "linked failure")]
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_indestructible() {
|
||||
use rt::test::run_in_newsched_task;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ use rt::kill::KillHandle;
|
|||
use rt::sched::Scheduler;
|
||||
use rt::uv::uvio::UvEventLoop;
|
||||
use rt::thread::Thread;
|
||||
use rt::work_queue::WorkQueue;
|
||||
|
||||
#[cfg(test)] use task::default_task_opts;
|
||||
#[cfg(test)] use comm;
|
||||
|
|
@ -500,7 +501,7 @@ impl RuntimeGlue {
|
|||
OldTask(ptr) => rt::rust_task_kill_other(ptr),
|
||||
NewTask(handle) => {
|
||||
let mut handle = handle;
|
||||
do handle.kill().map_consume |killed_task| {
|
||||
do handle.kill().map_move |killed_task| {
|
||||
let killed_task = Cell::new(killed_task);
|
||||
do Local::borrow::<Scheduler, ()> |sched| {
|
||||
sched.enqueue_task(killed_task.take());
|
||||
|
|
@ -682,7 +683,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
|||
// Child task runs this code.
|
||||
|
||||
// If child data is 'None', the enlist is vacuously successful.
|
||||
let enlist_success = do child_data.take().map_consume_default(true) |child_data| {
|
||||
let enlist_success = do child_data.take().map_move_default(true) |child_data| {
|
||||
let child_data = Cell::new(child_data); // :(
|
||||
do Local::borrow::<Task, bool> |me| {
|
||||
let (child_tg, ancestors, is_main) = child_data.take();
|
||||
|
|
@ -713,19 +714,25 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
|||
let mut task = unsafe {
|
||||
if opts.sched.mode != SingleThreaded {
|
||||
if opts.watched {
|
||||
Task::build_child(child_wrapper)
|
||||
Task::build_child(opts.stack_size, child_wrapper)
|
||||
} else {
|
||||
Task::build_root(child_wrapper)
|
||||
Task::build_root(opts.stack_size, child_wrapper)
|
||||
}
|
||||
} else {
|
||||
// Creating a 1:1 task:thread ...
|
||||
let sched = Local::unsafe_borrow::<Scheduler>();
|
||||
let sched_handle = (*sched).make_handle();
|
||||
|
||||
// Since this is a 1:1 scheduler we create a queue not in
|
||||
// the stealee set. The run_anything flag is set false
|
||||
// which will disable stealing.
|
||||
let work_queue = WorkQueue::new();
|
||||
|
||||
// Create a new scheduler to hold the new task
|
||||
let new_loop = ~UvEventLoop::new();
|
||||
let mut new_sched = ~Scheduler::new_special(new_loop,
|
||||
(*sched).work_queue.clone(),
|
||||
work_queue,
|
||||
(*sched).work_queues.clone(),
|
||||
(*sched).sleeper_list.clone(),
|
||||
false,
|
||||
Some(sched_handle));
|
||||
|
|
@ -736,16 +743,16 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
|||
|
||||
// Pin the new task to the new scheduler
|
||||
let new_task = if opts.watched {
|
||||
Task::build_homed_child(child_wrapper, Sched(new_sched_handle))
|
||||
Task::build_homed_child(opts.stack_size, child_wrapper, Sched(new_sched_handle))
|
||||
} else {
|
||||
Task::build_homed_root(child_wrapper, Sched(new_sched_handle))
|
||||
Task::build_homed_root(opts.stack_size, child_wrapper, Sched(new_sched_handle))
|
||||
};
|
||||
|
||||
// Create a task that will later be used to join with the new scheduler
|
||||
// thread when it is ready to terminate
|
||||
let (thread_port, thread_chan) = oneshot();
|
||||
let thread_port_cell = Cell::new(thread_port);
|
||||
let join_task = do Task::build_child() {
|
||||
let join_task = do Task::build_child(None) {
|
||||
rtdebug!("running join task");
|
||||
let thread_port = thread_port_cell.take();
|
||||
let thread: Thread = thread_port.recv();
|
||||
|
|
@ -762,8 +769,8 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
|||
let mut orig_sched_handle = orig_sched_handle_cell.take();
|
||||
let join_task = join_task_cell.take();
|
||||
|
||||
let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool) || {
|
||||
rtdebug!("bootstrapping a 1:1 scheduler");
|
||||
let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool, None) || {
|
||||
rtdebug!("boostrapping a 1:1 scheduler");
|
||||
};
|
||||
new_sched.bootstrap(bootstrap_task);
|
||||
|
||||
|
|
@ -854,7 +861,7 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
|
|||
// Even if the below code fails to kick the child off, we must
|
||||
// send Something on the notify channel.
|
||||
|
||||
let notifier = notify_chan.map_consume(|c| AutoNotify(c));
|
||||
let notifier = notify_chan.map_move(|c| AutoNotify(c));
|
||||
|
||||
if enlist_many(OldTask(child), &child_arc, &mut ancestors) {
|
||||
let group = @@mut Taskgroup(child_arc, ancestors, is_main, notifier);
|
||||
|
|
|
|||
|
|
@ -271,8 +271,8 @@ impl<T> TrieNode<T> {
|
|||
|
||||
impl<T> TrieNode<T> {
|
||||
fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool {
|
||||
for idx in range(0u, self.children.len()) {
|
||||
match self.children[idx] {
|
||||
for elt in self.children.iter() {
|
||||
match *elt {
|
||||
Internal(ref x) => if !x.each(|i,t| f(i,t)) { return false },
|
||||
External(k, ref v) => if !f(&k, v) { return false },
|
||||
Nothing => ()
|
||||
|
|
@ -282,13 +282,14 @@ impl<T> TrieNode<T> {
|
|||
}
|
||||
|
||||
fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool {
|
||||
do uint::range_rev(self.children.len(), 0) |idx| {
|
||||
match self.children[idx] {
|
||||
Internal(ref x) => x.each_reverse(|i,t| f(i,t)),
|
||||
External(k, ref v) => f(&k, v),
|
||||
Nothing => true
|
||||
for elt in self.children.rev_iter() {
|
||||
match *elt {
|
||||
Internal(ref x) => if !x.each_reverse(|i,t| f(i,t)) { return false },
|
||||
External(k, ref v) => if !f(&k, v) { return false },
|
||||
Nothing => ()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn mutate_values<'a>(&'a mut self, f: &fn(&uint, &mut T) -> bool) -> bool {
|
||||
|
|
@ -539,10 +540,9 @@ mod test_map {
|
|||
fn test_each_break() {
|
||||
let mut m = TrieMap::new();
|
||||
|
||||
do uint::range_rev(uint::max_value, uint::max_value - 10000) |x| {
|
||||
for x in range(uint::max_value - 10000, uint::max_value).invert() {
|
||||
m.insert(x, x / 2);
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
let mut n = uint::max_value - 10000;
|
||||
do m.each |k, v| {
|
||||
|
|
@ -580,10 +580,9 @@ mod test_map {
|
|||
fn test_each_reverse_break() {
|
||||
let mut m = TrieMap::new();
|
||||
|
||||
do uint::range_rev(uint::max_value, uint::max_value - 10000) |x| {
|
||||
for x in range(uint::max_value - 10000, uint::max_value).invert() {
|
||||
m.insert(x, x / 2);
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
let mut n = uint::max_value - 1;
|
||||
do m.each_reverse |k, v| {
|
||||
|
|
@ -634,10 +633,9 @@ mod test_map {
|
|||
let last = uint::max_value;
|
||||
|
||||
let mut map = TrieMap::new();
|
||||
do uint::range_rev(last, first) |x| {
|
||||
for x in range(first, last).invert() {
|
||||
map.insert(x, x / 2);
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
for (k, &v) in map.iter() {
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ pub fn start(main: *u8, argc: int, argv: **c_char,
|
|||
use os;
|
||||
|
||||
unsafe {
|
||||
let use_old_rt = os::getenv("RUST_NEWRT").is_none();
|
||||
let use_old_rt = os::getenv("RUST_OLDRT").is_some();
|
||||
if use_old_rt {
|
||||
return rust_start(main as *c_void, argc as c_int, argv,
|
||||
crate_map as *c_void) as int;
|
||||
|
|
|
|||
|
|
@ -1602,8 +1602,8 @@ impl<T:Clone> OwnedCopyableVector<T> for ~[T] {
|
|||
let new_len = self.len() + rhs.len();
|
||||
self.reserve(new_len);
|
||||
|
||||
for i in range(0u, rhs.len()) {
|
||||
self.push(unsafe { raw::get(rhs, i) })
|
||||
for elt in rhs.iter() {
|
||||
self.push((*elt).clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue