From edc67817a312d2c7924ae333facb9294ff751008 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 23 Jan 2015 15:02:05 -0500 Subject: Improve libcore/cell.rs docs --- src/libcore/cell.rs | 294 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 209 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 7f73be9eb5f..202d860021e 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -10,39 +10,30 @@ //! Shareable mutable containers. //! -//! Values of the `Cell` and `RefCell` types may be mutated through -//! shared references (i.e. the common `&T` type), whereas most Rust -//! types can only be mutated through unique (`&mut T`) references. We -//! say that `Cell` and `RefCell` provide *interior mutability*, in -//! contrast with typical Rust types that exhibit *inherited -//! mutability*. +//! Values of the `Cell` and `RefCell` types may be mutated through shared references (i.e. +//! the common `&T` type), whereas most Rust types can only be mutated through unique (`&mut T`) +//! references. We say that `Cell` and `RefCell` provide 'interior mutability', in contrast +//! with typical Rust types that exhibit 'inherited mutability'. //! -//! Cell types come in two flavors: `Cell` and `RefCell`. `Cell` -//! provides `get` and `set` methods that change the -//! interior value with a single method call. `Cell` though is only -//! compatible with types that implement `Copy`. For other types, -//! one must use the `RefCell` type, acquiring a write lock before -//! mutating. +//! Cell types come in two flavors: `Cell` and `RefCell`. `Cell` provides `get` and `set` +//! methods that change the interior value with a single method call. `Cell` though is only +//! compatible with types that implement `Copy`. For other types, one must use the `RefCell` +//! type, acquiring a write lock before mutating. //! -//! `RefCell` uses Rust's lifetimes to implement *dynamic borrowing*, -//! a process whereby one can claim temporary, exclusive, mutable -//! access to the inner value. Borrows for `RefCell`s are tracked *at -//! runtime*, unlike Rust's native reference types which are entirely -//! tracked statically, at compile time. Because `RefCell` borrows are -//! dynamic it is possible to attempt to borrow a value that is -//! already mutably borrowed; when this happens it results in task -//! panic. +//! `RefCell` uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can +//! claim temporary, exclusive, mutable access to the inner value. Borrows for `RefCell`s are +//! tracked 'at runtime', unlike Rust's native reference types which are entirely tracked +//! statically, at compile time. Because `RefCell` borrows are dynamic it is possible to attempt +//! to borrow a value that is already mutably borrowed; when this happens it results in task panic. //! //! # When to choose interior mutability //! -//! The more common inherited mutability, where one must have unique -//! access to mutate a value, is one of the key language elements that -//! enables Rust to reason strongly about pointer aliasing, statically -//! preventing crash bugs. Because of that, inherited mutability is -//! preferred, and interior mutability is something of a last -//! resort. Since cell types enable mutation where it would otherwise -//! be disallowed though, there are occasions when interior -//! mutability might be appropriate, or even *must* be used, e.g. +//! The more common inherited mutability, where one must have unique access to mutate a value, is +//! one of the key language elements that enables Rust to reason strongly about pointer aliasing, +//! statically preventing crash bugs. Because of that, inherited mutability is preferred, and +//! interior mutability is something of a last resort. Since cell types enable mutation where it +//! would otherwise be disallowed though, there are occasions when interior mutability might be +//! appropriate, or even *must* be used, e.g. //! //! * Introducing inherited mutability roots to shared types. //! * Implementation details of logically-immutable methods. @@ -50,15 +41,13 @@ //! //! ## Introducing inherited mutability roots to shared types //! -//! Shared smart pointer types, including `Rc` and `Arc`, provide -//! containers that can be cloned and shared between multiple parties. -//! Because the contained values may be multiply-aliased, they can -//! only be borrowed as shared references, not mutable references. -//! Without cells it would be impossible to mutate data inside of -//! shared boxes at all! +//! Shared smart pointer types, including `Rc` and `Arc`, provide containers that can be +//! cloned and shared between multiple parties. Because the contained values may be +//! multiply-aliased, they can only be borrowed as shared references, not mutable references. +//! Without cells it would be impossible to mutate data inside of shared boxes at all! //! -//! It's very common then to put a `RefCell` inside shared pointer -//! types to reintroduce mutability: +//! It's very common then to put a `RefCell` inside shared pointer types to reintroduce +//! mutability: //! //! ``` //! use std::collections::HashMap; @@ -80,12 +69,10 @@ //! //! ## Implementation details of logically-immutable methods //! -//! Occasionally it may be desirable not to expose in an API that -//! there is mutation happening "under the hood". This may be because -//! logically the operation is immutable, but e.g. caching forces the -//! implementation to perform mutation; or because you must employ -//! mutation to implement a trait method that was originally defined -//! to take `&self`. +//! Occasionally it may be desirable not to expose in an API that there is mutation happening +//! "under the hood". This may be because logically the operation is immutable, but e.g. caching +//! forces the implementation to perform mutation; or because you must employ mutation to implement +//! a trait method that was originally defined to take `&self`. //! //! ``` //! use std::cell::RefCell; @@ -123,13 +110,11 @@ //! //! ## Mutating implementations of `clone` //! -//! This is simply a special - but common - case of the previous: -//! hiding mutability for operations that appear to be immutable. -//! The `clone` method is expected to not change the source value, and -//! is declared to take `&self`, not `&mut self`. Therefore any -//! mutation that happens in the `clone` method must use cell -//! types. For example, `Rc` maintains its reference counts within a -//! `Cell`. +//! This is simply a special - but common - case of the previous: hiding mutability for operations +//! that appear to be immutable. The `clone` method is expected to not change the source value, and +//! is declared to take `&self`, not `&mut self`. Therefore any mutation that happens in the +//! `clone` method must use cell types. For example, `Rc` maintains its reference counts within a +//! `Cell`. //! //! ``` //! use std::cell::Cell; @@ -153,10 +138,6 @@ //! } //! ``` //! -// FIXME: Explain difference between Cell and RefCell -// FIXME: Downsides to interior mutability -// FIXME: Can't be shared between threads. Dynamic borrows -// FIXME: Relationship to Atomic types and RWLock #![stable] @@ -169,6 +150,8 @@ use option::Option; use option::Option::{None, Some}; /// A mutable memory location that admits only `Copy` data. +/// +/// See the [module-level documentation](../index.html) for more. #[stable] pub struct Cell { value: UnsafeCell, @@ -176,6 +159,14 @@ pub struct Cell { impl Cell { /// Creates a new `Cell` containing the given value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::Cell; + /// + /// let c = Cell::new(5); + /// ``` #[stable] pub fn new(value: T) -> Cell { Cell { @@ -184,6 +175,16 @@ impl Cell { } /// Returns a copy of the contained value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::Cell; + /// + /// let c = Cell::new(5); + /// + /// let five = c.get(); + /// ``` #[inline] #[stable] pub fn get(&self) -> T { @@ -191,6 +192,16 @@ impl Cell { } /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::Cell; + /// + /// let c = Cell::new(5); + /// + /// c.set(10); + /// ``` #[inline] #[stable] pub fn set(&self, value: T) { @@ -201,9 +212,19 @@ impl Cell { /// Get a reference to the underlying `UnsafeCell`. /// - /// This can be used to circumvent `Cell`'s safety checks. + /// # Unsafety /// /// This function is `unsafe` because `UnsafeCell`'s field is public. + /// + /// # Examples + /// + /// ``` + /// use std::cell::Cell; + /// + /// let c = Cell::new(5); + /// + /// let uc = unsafe { c.as_unsafe_cell() }; + /// ``` #[inline] #[unstable] pub unsafe fn as_unsafe_cell<'a>(&'a self) -> &'a UnsafeCell { @@ -237,6 +258,8 @@ impl PartialEq for Cell { } /// A mutable memory location with dynamically checked borrow rules +/// +/// See the [module-level documentation](../index.html) for more. #[stable] pub struct RefCell { value: UnsafeCell, @@ -250,7 +273,15 @@ const UNUSED: BorrowFlag = 0; const WRITING: BorrowFlag = -1; impl RefCell { - /// Create a new `RefCell` containing `value` + /// Creates a new `RefCell` containing `value`. + /// + /// # Examples + /// + /// ``` + /// use std::cell::RefCell; + /// + /// let c = RefCell::new(5); + /// ``` #[stable] pub fn new(value: T) -> RefCell { RefCell { @@ -260,6 +291,16 @@ impl RefCell { } /// Consumes the `RefCell`, returning the wrapped value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::RefCell; + /// + /// let c = RefCell::new(5); + /// + /// let five = c.into_inner(); + /// ``` #[stable] pub fn into_inner(self) -> T { // Since this function takes `self` (the `RefCell`) by value, the @@ -285,12 +326,39 @@ impl RefCell { /// Immutably borrows the wrapped value. /// - /// The borrow lasts until the returned `Ref` exits scope. Multiple - /// immutable borrows can be taken out at the same time. + /// The borrow lasts until the returned `Ref` exits scope. Multiple immutable borrows can be + /// taken out at the same time. /// /// # Panics /// /// Panics if the value is currently mutably borrowed. + /// + /// # Examples + /// + /// ``` + /// use std::cell::RefCell; + /// + /// let c = RefCell::new(5); + /// + /// let borrowed_five = c.borrow(); + /// let borrowed_five2 = c.borrow(); + /// ``` + /// + /// An example of panic: + /// + /// ``` + /// use std::cell::RefCell; + /// use std::thread::Thread; + /// + /// let result = Thread::scoped(move || { + /// let c = RefCell::new(5); + /// let m = c.borrow_mut(); + /// + /// let b = c.borrow(); // this causes a panic + /// }).join(); + /// + /// assert!(result.is_err()); + /// ``` #[stable] pub fn borrow<'a>(&'a self) -> Ref<'a, T> { match self.try_borrow() { @@ -315,12 +383,38 @@ impl RefCell { /// Mutably borrows the wrapped value. /// - /// The borrow lasts until the returned `RefMut` exits scope. The value - /// cannot be borrowed while this borrow is active. + /// The borrow lasts until the returned `RefMut` exits scope. The value cannot be borrowed + /// while this borrow is active. /// /// # Panics /// /// Panics if the value is currently borrowed. + /// + /// # Examples + /// + /// ``` + /// use std::cell::RefCell; + /// + /// let c = RefCell::new(5); + /// + /// let borrowed_five = c.borrow_mut(); + /// ``` + /// + /// An example of panic: + /// + /// ``` + /// use std::cell::RefCell; + /// use std::thread::Thread; + /// + /// let result = Thread::scoped(move || { + /// let c = RefCell::new(5); + /// let m = c.borrow_mut(); + /// + /// let b = c.borrow_mut(); // this causes a panic + /// }).join(); + /// + /// assert!(result.is_err()); + /// ``` #[stable] pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> { match self.try_borrow_mut() { @@ -402,7 +496,9 @@ impl<'b> Clone for BorrowRef<'b> { } } -/// Wraps a borrowed reference to a value in a `RefCell` box. +/// A wrapper type for an immutably borrowed value from a `RefCell`. +/// +/// See the [module-level documentation](../index.html) for more. #[stable] pub struct Ref<'b, T:'b> { // FIXME #12808: strange name to try to avoid interfering with @@ -460,7 +556,9 @@ impl<'b> BorrowRefMut<'b> { } } -/// Wraps a mutable borrowed reference to a value in a `RefCell` box. +/// A wrapper type for a mutably borrowed value from a `RefCell`. +/// +/// See the [module-level documentation](../index.html) for more. #[stable] pub struct RefMut<'b, T:'b> { // FIXME #12808: strange name to try to avoid interfering with @@ -489,28 +587,25 @@ impl<'b, T> DerefMut for RefMut<'b, T> { /// The core primitive for interior mutability in Rust. /// -/// `UnsafeCell` type that wraps a type T and indicates unsafe interior -/// operations on the wrapped type. Types with an `UnsafeCell` field are -/// considered to have an *unsafe interior*. The `UnsafeCell` type is the only -/// legal way to obtain aliasable data that is considered mutable. In general, -/// transmuting an &T type into an &mut T is considered undefined behavior. +/// `UnsafeCell` is a type that wraps some `T` and indicates unsafe interior operations on the +/// wrapped type. Types with an `UnsafeCell` field are considered to have an 'unsafe interior'. +/// The `UnsafeCell` type is the only legal way to obtain aliasable data that is considered +/// mutable. In general, transmuting an `&T` type into an `&mut T` is considered undefined behavior. /// -/// Although it is possible to put an `UnsafeCell` into static item, it is -/// not permitted to take the address of the static item if the item is not -/// declared as mutable. This rule exists because immutable static items are -/// stored in read-only memory, and thus any attempt to mutate their interior -/// can cause segfaults. Immutable static items containing `UnsafeCell` -/// instances are still useful as read-only initializers, however, so we do not -/// forbid them altogether. +/// Although it is possible to put an `UnsafeCell` into static item, it is not permitted to take +/// the address of the static item if the item is not declared as mutable. This rule exists because +/// immutable static items are stored in read-only memory, and thus any attempt to mutate their +/// interior can cause segfaults. Immutable static items containing `UnsafeCell` instances are +/// still useful as read-only initializers, however, so we do not forbid them altogether. /// -/// Types like `Cell` and `RefCell` use this type to wrap their internal data. +/// Types like `Cell` and `RefCell` use this type to wrap their internal data. /// -/// `UnsafeCell` doesn't opt-out from any kind, instead, types with an -/// `UnsafeCell` interior are expected to opt-out from kinds themselves. +/// `UnsafeCell` doesn't opt-out from any marker traits, instead, types with an `UnsafeCell` +/// interior are expected to opt-out from those traits themselves. /// -/// # Example: +/// # Examples /// -/// ```rust +/// ``` /// use std::cell::UnsafeCell; /// use std::marker::Sync; /// @@ -521,9 +616,8 @@ impl<'b, T> DerefMut for RefMut<'b, T> { /// unsafe impl Sync for NotThreadSafe {} /// ``` /// -/// **NOTE:** `UnsafeCell` fields are public to allow static initializers. It -/// is not recommended to access its fields directly, `get` should be used -/// instead. +/// **NOTE:** `UnsafeCell`'s fields are public to allow static initializers. It is not +/// recommended to access its fields directly, `get` should be used instead. #[lang="unsafe"] #[stable] pub struct UnsafeCell { @@ -539,22 +633,52 @@ impl UnsafeCell { /// Construct a new instance of `UnsafeCell` which will wrap the specified /// value. /// - /// All access to the inner value through methods is `unsafe`, and it is - /// highly discouraged to access the fields directly. + /// All access to the inner value through methods is `unsafe`, and it is highly discouraged to + /// access the fields directly. + /// + /// # Examples + /// + /// ``` + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// ``` #[stable] pub fn new(value: T) -> UnsafeCell { UnsafeCell { value: value } } /// Gets a mutable pointer to the wrapped value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let five = uc.get(); + /// ``` #[inline] #[stable] pub fn get(&self) -> *mut T { &self.value as *const T as *mut T } /// Unwraps the value /// - /// This function is unsafe because there is no guarantee that this or other - /// tasks are currently inspecting the inner value. + /// # Unsafety + /// + /// This function is unsafe because there is no guarantee that this or other threads are + /// currently inspecting the inner value. + /// + /// # Examples + /// + /// ``` + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let five = unsafe { uc.into_inner() }; + /// ``` #[inline] #[stable] pub unsafe fn into_inner(self) -> T { self.value } -- cgit 1.4.1-3-g733a5 From 0736ad3c2abdd899eb625fc8bfdc632b876abe5a Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Sat, 24 Jan 2015 14:54:43 +0100 Subject: Spellfix for Debug trait Spellfix for `Debug` trait documentation. Change "most all types should implement this" to "all types should implement this". Same fix for deprecated `Show` trait. --- src/libcore/fmt/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 0e8d31a62ee..d4ffea3371e 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -231,7 +231,7 @@ impl<'a> Display for Arguments<'a> { } } -/// Format trait for the `:?` format. Useful for debugging, most all types +/// Format trait for the `:?` format. Useful for debugging, all types /// should implement this. #[deprecated = "renamed to Debug"] #[cfg(not(stage0))] @@ -240,7 +240,7 @@ pub trait Show { fn fmt(&self, &mut Formatter) -> Result; } -/// Format trait for the `:?` format. Useful for debugging, most all types +/// Format trait for the `:?` format. Useful for debugging, all types /// should implement this. #[unstable = "I/O and core have yet to be reconciled"] pub trait Debug { -- cgit 1.4.1-3-g733a5 From e7245252ccb5de3d8002a2cc3ecb25595ea23e90 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 25 Jan 2015 01:44:49 +0200 Subject: Cleanup check_cast. Fixes #21554 This also makes the cast error messages somewhat more uniform. --- src/librustc/middle/ty.rs | 1 - src/librustc_typeck/check/mod.rs | 186 +++++++++++---------- src/test/compile-fail/cast-from-nil.rs | 2 +- src/test/compile-fail/cast-to-nil.rs | 2 +- src/test/compile-fail/issue-10991.rs | 2 +- src/test/compile-fail/issue-21554.rs | 15 ++ .../compile-fail/typeck-cast-pointer-to-float.rs | 2 +- 7 files changed, 119 insertions(+), 91 deletions(-) create mode 100644 src/test/compile-fail/issue-21554.rs (limited to 'src') diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bae41b78c08..3e0801d6598 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3128,7 +3128,6 @@ pub fn type_is_scalar(ty: Ty) -> bool { ty_bool | ty_char | ty_int(_) | ty_float(_) | ty_uint(_) | ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) | ty_bare_fn(..) | ty_ptr(_) => true, - ty_tup(ref tys) if tys.is_empty() => true, _ => false } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a9f81d3a266..9489f0cc01b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -993,86 +993,65 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } -fn check_cast(fcx: &FnCtxt, - cast_expr: &ast::Expr, - e: &ast::Expr, - t: &ast::Ty) { - let id = cast_expr.id; - let span = cast_expr.span; - - // Find the type of `e`. Supply hints based on the type we are casting to, - // if appropriate. - let t_1 = fcx.to_ty(t); - let t_1 = structurally_resolved_type(fcx, span, t_1); - - check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1)); - - let t_e = fcx.expr_ty(e); - - debug!("t_1={}", fcx.infcx().ty_to_string(t_1)); - debug!("t_e={}", fcx.infcx().ty_to_string(t_e)); - - if ty::type_is_error(t_e) { - fcx.write_error(id); - return - } - - if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) { - let tstr = fcx.infcx().ty_to_string(t_1); - fcx.type_error_message(span, |actual| { - format!("cast to unsized type: `{}` as `{}`", actual, tstr) - }, t_e, None); - match t_e.sty { - ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => { - let mtstr = match mt { - ast::MutMutable => "mut ", - ast::MutImmutable => "" - }; - if ty::type_is_trait(t_1) { - span_help!(fcx.tcx().sess, t.span, "did you mean `&{}{}`?", mtstr, tstr); - } else { - span_help!(fcx.tcx().sess, span, - "consider using an implicit coercion to `&{}{}` instead", - mtstr, tstr); - } - } - ty::ty_uniq(..) => { - span_help!(fcx.tcx().sess, t.span, "did you mean `Box<{}>`?", tstr); - } - _ => { - span_help!(fcx.tcx().sess, e.span, - "consider using a box or reference as appropriate"); +fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + span: Span, + t_span: Span, + e_span: Span, + t_1: Ty<'tcx>, + t_e: Ty<'tcx>, + id: ast::NodeId) { + let tstr = fcx.infcx().ty_to_string(t_1); + fcx.type_error_message(span, |actual| { + format!("cast to unsized type: `{}` as `{}`", actual, tstr) + }, t_e, None); + match t_e.sty { + ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => { + let mtstr = match mt { + ast::MutMutable => "mut ", + ast::MutImmutable => "" + }; + if ty::type_is_trait(t_1) { + span_help!(fcx.tcx().sess, t_span, "did you mean `&{}{}`?", mtstr, tstr); + } else { + span_help!(fcx.tcx().sess, span, + "consider using an implicit coercion to `&{}{}` instead", + mtstr, tstr); } } - fcx.write_error(id); - return - } - - if ty::type_is_trait(t_1) { - // This will be looked up later on. - vtable::check_object_cast(fcx, cast_expr, e, t_1); - fcx.write_ty(id, t_1); - return + ty::ty_uniq(..) => { + span_help!(fcx.tcx().sess, t_span, "did you mean `Box<{}>`?", tstr); + } + _ => { + span_help!(fcx.tcx().sess, e_span, + "consider using a box or reference as appropriate"); + } } + fcx.write_error(id); +} - let t_1 = structurally_resolved_type(fcx, span, t_1); - let t_e = structurally_resolved_type(fcx, span, t_e); - if ty::type_is_nil(t_e) { - fcx.type_error_message(span, |actual| { - format!("cast from nil: `{}` as `{}`", - actual, - fcx.infcx().ty_to_string(t_1)) - }, t_e, None); - } else if ty::type_is_nil(t_1) { +fn check_cast_inner<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + span: Span, + t_1: Ty<'tcx>, + t_e: Ty<'tcx>, + e: &ast::Expr) { + fn cast_through_integer_err<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + span: Span, + t_1: Ty<'tcx>, + t_e: Ty<'tcx>) { fcx.type_error_message(span, |actual| { - format!("cast to nil: `{}` as `{}`", + format!("illegal cast; cast through an \ + integer first: `{}` as `{}`", actual, fcx.infcx().ty_to_string(t_1)) }, t_e, None); } let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e); + let t_e_is_scalar = ty::type_is_scalar(t_e); + let t_e_is_integral = ty::type_is_integral(t_e); + let t_e_is_float = ty::type_is_floating_point(t_e); + let t_e_is_c_enum = ty::type_is_c_like_enum(fcx.tcx(), t_e); let t_1_is_scalar = ty::type_is_scalar(t_1); let t_1_is_char = ty::type_is_char(t_1); @@ -1081,18 +1060,9 @@ fn check_cast(fcx: &FnCtxt, // casts to scalars other than `char` and `bare fn` are trivial let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn; + if t_e_is_bare_fn_item && t_1_is_bare_fn { demand::coerce(fcx, e.span, t_1, &*e); - } else if ty::type_is_c_like_enum(fcx.tcx(), t_e) && t_1_is_trivial { - if t_1_is_float || ty::type_is_unsafe_ptr(t_1) { - fcx.type_error_message(span, |actual| { - format!("illegal cast; cast through an \ - integer first: `{}` as `{}`", - actual, - fcx.infcx().ty_to_string(t_1)) - }, t_e, None); - } - // casts from C-like enums are allowed } else if t_1_is_char { let t_e = fcx.infcx().shallow_resolve(t_e); if t_e.sty != ty::ty_uint(ast::TyU8) { @@ -1104,6 +1074,16 @@ fn check_cast(fcx: &FnCtxt, } else if t_1.sty == ty::ty_bool { span_err!(fcx.tcx().sess, span, E0054, "cannot cast as `bool`, compare with zero instead"); + } else if t_1_is_float && (t_e_is_scalar || t_e_is_c_enum) && !( + t_e_is_integral || t_e_is_float || t_e.sty == ty::ty_bool) { + // Casts to float must go through an integer or boolean + cast_through_integer_err(fcx, span, t_1, t_e) + } else if t_e_is_c_enum && t_1_is_trivial { + if ty::type_is_unsafe_ptr(t_1) { + // ... and likewise with C enum -> *T + cast_through_integer_err(fcx, span, t_1, t_e) + } + // casts from C-like enums are allowed } else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) { fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool { @@ -1145,7 +1125,7 @@ fn check_cast(fcx: &FnCtxt, demand::coerce(fcx, e.span, t_1, &*e); } } - } else if !(ty::type_is_scalar(t_e) && t_1_is_trivial) { + } else if !(t_e_is_scalar && t_1_is_trivial) { /* If more type combinations should be supported than are supported here, then file an enhancement issue and @@ -1156,15 +1136,49 @@ fn check_cast(fcx: &FnCtxt, actual, fcx.infcx().ty_to_string(t_1)) }, t_e, None); - } else if ty::type_is_unsafe_ptr(t_e) && t_1_is_float { - fcx.type_error_message(span, |actual| { - format!("cannot cast from pointer to float directly: `{}` as `{}`; cast through an \ - integer first", - actual, - fcx.infcx().ty_to_string(t_1)) - }, t_e, None); } +} + +fn check_cast(fcx: &FnCtxt, + cast_expr: &ast::Expr, + e: &ast::Expr, + t: &ast::Ty) { + let id = cast_expr.id; + let span = cast_expr.span; + + // Find the type of `e`. Supply hints based on the type we are casting to, + // if appropriate. + let t_1 = fcx.to_ty(t); + let t_1 = structurally_resolved_type(fcx, span, t_1); + + check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1)); + + let t_e = fcx.expr_ty(e); + + debug!("t_1={}", fcx.infcx().ty_to_string(t_1)); + debug!("t_e={}", fcx.infcx().ty_to_string(t_e)); + + if ty::type_is_error(t_e) { + fcx.write_error(id); + return + } + + if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) { + report_cast_to_unsized_type(fcx, span, t.span, e.span, t_1, t_e, id); + return + } + + if ty::type_is_trait(t_1) { + // This will be looked up later on. + vtable::check_object_cast(fcx, cast_expr, e, t_1); + fcx.write_ty(id, t_1); + return + } + + let t_1 = structurally_resolved_type(fcx, span, t_1); + let t_e = structurally_resolved_type(fcx, span, t_e); + check_cast_inner(fcx, span, t_1, t_e, e); fcx.write_ty(id, t_1); } diff --git a/src/test/compile-fail/cast-from-nil.rs b/src/test/compile-fail/cast-from-nil.rs index 558a5478718..4c6dcaccc9a 100644 --- a/src/test/compile-fail/cast-from-nil.rs +++ b/src/test/compile-fail/cast-from-nil.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: cast from nil: `()` as `u32` +// error-pattern: non-scalar cast: `()` as `u32` fn main() { let u = (assert!(true) as u32); } diff --git a/src/test/compile-fail/cast-to-nil.rs b/src/test/compile-fail/cast-to-nil.rs index 1a5c0744f70..e5fd5bb33eb 100644 --- a/src/test/compile-fail/cast-to-nil.rs +++ b/src/test/compile-fail/cast-to-nil.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: cast to nil: `u32` as `()` +// error-pattern: non-scalar cast: `u32` as `()` fn main() { let u = 0u32 as (); } diff --git a/src/test/compile-fail/issue-10991.rs b/src/test/compile-fail/issue-10991.rs index 2913ddf395f..25060b94dcf 100644 --- a/src/test/compile-fail/issue-10991.rs +++ b/src/test/compile-fail/issue-10991.rs @@ -10,5 +10,5 @@ fn main() { let nil = (); - let _t = nil as usize; //~ ERROR: cast from nil: `()` as `usize` + let _t = nil as usize; //~ ERROR: non-scalar cast: `()` as `usize` } diff --git a/src/test/compile-fail/issue-21554.rs b/src/test/compile-fail/issue-21554.rs new file mode 100644 index 00000000000..a2cac55033c --- /dev/null +++ b/src/test/compile-fail/issue-21554.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Inches(i32); + +fn main() { + Inches as f32; //~ ERROR illegal cast; cast through an integer first +} diff --git a/src/test/compile-fail/typeck-cast-pointer-to-float.rs b/src/test/compile-fail/typeck-cast-pointer-to-float.rs index 22a0978ef7c..285a5dbee05 100644 --- a/src/test/compile-fail/typeck-cast-pointer-to-float.rs +++ b/src/test/compile-fail/typeck-cast-pointer-to-float.rs @@ -11,5 +11,5 @@ fn main() { let x : i16 = 22; ((&x) as *const i16) as f32; - //~^ ERROR: cannot cast from pointer to float directly: `*const i16` as `f32` + //~^ ERROR illegal cast; cast through an integer first: `*const i16` as `f32` } -- cgit 1.4.1-3-g733a5 From 0dac5685783631dc4ecf3e865229a3747fe81943 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 24 Jan 2015 22:43:11 -0800 Subject: syntax: Don't put quotes around filenames in codemap This ends up propagating all the way out to the output of dep-info which then makes Cargo think that files are not existent (it thinks the files have quotes in their name) when they in fact do. --- src/libsyntax/codemap.rs | 7 ++++--- src/libsyntax/ext/source_util.rs | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index a5e10f42750..e0d4f69a34c 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -364,9 +364,10 @@ impl CodeMap { }; // Append '\n' in case it's not already there. - // This is a workaround to prevent CodeMap.lookup_filemap_idx from accidentally - // overflowing into the next filemap in case the last byte of span is also the last - // byte of filemap, which leads to incorrect results from CodeMap.span_to_*. + // This is a workaround to prevent CodeMap.lookup_filemap_idx from + // accidentally overflowing into the next filemap in case the last byte + // of span is also the last byte of filemap, which leads to incorrect + // results from CodeMap.span_to_*. if src.len() > 0 && !src.ends_with("\n") { src.push('\n'); } diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index a74adbf4085..0d74af5caa4 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -135,7 +135,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let bytes = match File::open(&file).read_to_end() { Err(e) => { cx.span_err(sp, - &format!("couldn't read {:?}: {}", + &format!("couldn't read {}: {}", file.display(), e)[]); return DummyResult::expr(sp); @@ -146,7 +146,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) Ok(src) => { // Add this input file to the code map to make it available as // dependency information - let filename = format!("{:?}", file.display()); + let filename = format!("{}", file.display()); let interned = token::intern_and_get_ident(&src[]); cx.codemap().new_filemap(filename, src); @@ -154,7 +154,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } Err(_) => { cx.span_err(sp, - &format!("{:?} wasn't a utf-8 file", + &format!("{} wasn't a utf-8 file", file.display())[]); return DummyResult::expr(sp); } @@ -171,7 +171,7 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) match File::open(&file).read_to_end() { Err(e) => { cx.span_err(sp, - &format!("couldn't read {:?}: {}", file.display(), e)[]); + &format!("couldn't read {}: {}", file.display(), e)[]); return DummyResult::expr(sp); } Ok(bytes) => { -- cgit 1.4.1-3-g733a5 From bca25aeeb40b220d7330d71cd4906acafc32ebe7 Mon Sep 17 00:00:00 2001 From: Björn Steinbrink Date: Mon, 26 Jan 2015 09:45:16 +0100 Subject: Use more specific target CPUs on Darwin Macs don't come with anything older than a Yonah (32bit) or Core2 (64bit), so we can default to those targets. Clang does the same. --- src/librustc_back/target/i686_apple_darwin.rs | 1 + src/librustc_back/target/x86_64_apple_darwin.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/librustc_back/target/i686_apple_darwin.rs b/src/librustc_back/target/i686_apple_darwin.rs index 1b079323bf9..fcea900283d 100644 --- a/src/librustc_back/target/i686_apple_darwin.rs +++ b/src/librustc_back/target/i686_apple_darwin.rs @@ -12,6 +12,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::apple_base::opts(); + base.cpu = "yonah".to_string(); base.pre_link_args.push("-m32".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_apple_darwin.rs b/src/librustc_back/target/x86_64_apple_darwin.rs index f2abfd4564c..0b3b2bea62d 100644 --- a/src/librustc_back/target/x86_64_apple_darwin.rs +++ b/src/librustc_back/target/x86_64_apple_darwin.rs @@ -12,7 +12,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::apple_base::opts(); - base.cpu = "x86-64".to_string(); + base.cpu = "core2".to_string(); base.eliminate_frame_pointer = false; base.pre_link_args.push("-m64".to_string()); -- cgit 1.4.1-3-g733a5 From 296c74de96e2ca78289ee0a127cca3b7e58af734 Mon Sep 17 00:00:00 2001 From: Björn Steinbrink Date: Mon, 26 Jan 2015 09:54:19 +0100 Subject: Default to Pentium 4 as the x86 target CPU on Windows/Linux/DragonFly Limiting ourselves to a generic x86 instruction set doesn't seem useful. Both users running those systems on original i386 hardware might as well manually specify a target cpu ;-) Clang uses the same default. --- src/librustc_back/target/i686_pc_windows_gnu.rs | 1 + src/librustc_back/target/i686_unknown_dragonfly.rs | 1 + src/librustc_back/target/i686_unknown_linux_gnu.rs | 1 + 3 files changed, 3 insertions(+) (limited to 'src') diff --git a/src/librustc_back/target/i686_pc_windows_gnu.rs b/src/librustc_back/target/i686_pc_windows_gnu.rs index c2ab68ee052..249f2d440e6 100644 --- a/src/librustc_back/target/i686_pc_windows_gnu.rs +++ b/src/librustc_back/target/i686_pc_windows_gnu.rs @@ -12,6 +12,7 @@ use target::Target; pub fn target() -> Target { let mut options = super::windows_base::opts(); + options.cpu = "pentium4".to_string(); // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. diff --git a/src/librustc_back/target/i686_unknown_dragonfly.rs b/src/librustc_back/target/i686_unknown_dragonfly.rs index 7910eba7ea1..4450d8d6778 100644 --- a/src/librustc_back/target/i686_unknown_dragonfly.rs +++ b/src/librustc_back/target/i686_unknown_dragonfly.rs @@ -12,6 +12,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); + base.cpu = "pentium4".to_string(); base.pre_link_args.push("-m32".to_string()); Target { diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index c93a564fef5..f21f6adfb4c 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -12,6 +12,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::linux_base::opts(); + base.cpu = "pentium4".to_string(); base.pre_link_args.push("-m32".to_string()); Target { -- cgit 1.4.1-3-g733a5 From f72b1645103e12b581f7022b893c37b5fe41aef7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Jan 2015 16:27:48 -0800 Subject: std: Rename io to old_io In preparation for the I/O rejuvination of the standard library, this commit renames the current `io` module to `old_io` in order to make room for the new I/O modules. It is expected that the I/O RFCs will land incrementally over time instead of all at once, and this provides a fresh clean path for new modules to enter into as well as guaranteeing that all old infrastructure will remain in place for some time. As each `old_io` module is replaced it will be deprecated in-place for new structures in `std::{io, fs, net}` (as appropriate). This commit does *not* leave a reexport of `old_io as io` as the deprecation lint does not currently warn on this form of use. This is quite a large breaking change for all imports in existing code, but all functionality is retained precisely as-is and path statements simply need to be renamed from `io` to `old_io`. [breaking-change] --- src/libstd/io/buffered.rs | 697 ------------- src/libstd/io/comm_adapters.rs | 248 ----- src/libstd/io/extensions.rs | 562 ----------- src/libstd/io/fs.rs | 1571 ----------------------------- src/libstd/io/mem.rs | 757 -------------- src/libstd/io/mod.rs | 1939 ----------------------------------- src/libstd/io/net/addrinfo.rs | 137 --- src/libstd/io/net/ip.rs | 700 ------------- src/libstd/io/net/mod.rs | 46 - src/libstd/io/net/pipe.rs | 864 ---------------- src/libstd/io/net/tcp.rs | 1475 --------------------------- src/libstd/io/net/udp.rs | 457 --------- src/libstd/io/pipe.rs | 139 --- src/libstd/io/process.rs | 1231 ----------------------- src/libstd/io/result.rs | 129 --- src/libstd/io/stdio.rs | 566 ----------- src/libstd/io/tempfile.rs | 182 ---- src/libstd/io/test.rs | 175 ---- src/libstd/io/timer.rs | 481 --------- src/libstd/io/util.rs | 444 -------- src/libstd/lib.rs | 7 +- src/libstd/old_io/buffered.rs | 697 +++++++++++++ src/libstd/old_io/comm_adapters.rs | 248 +++++ src/libstd/old_io/extensions.rs | 562 +++++++++++ src/libstd/old_io/fs.rs | 1572 +++++++++++++++++++++++++++++ src/libstd/old_io/mem.rs | 757 ++++++++++++++ src/libstd/old_io/mod.rs | 1957 ++++++++++++++++++++++++++++++++++++ src/libstd/old_io/net/addrinfo.rs | 137 +++ src/libstd/old_io/net/ip.rs | 700 +++++++++++++ src/libstd/old_io/net/mod.rs | 46 + src/libstd/old_io/net/pipe.rs | 864 ++++++++++++++++ src/libstd/old_io/net/tcp.rs | 1475 +++++++++++++++++++++++++++ src/libstd/old_io/net/udp.rs | 457 +++++++++ src/libstd/old_io/pipe.rs | 139 +++ src/libstd/old_io/process.rs | 1231 +++++++++++++++++++++++ src/libstd/old_io/result.rs | 129 +++ src/libstd/old_io/stdio.rs | 566 +++++++++++ src/libstd/old_io/tempfile.rs | 182 ++++ src/libstd/old_io/test.rs | 175 ++++ src/libstd/old_io/timer.rs | 481 +++++++++ src/libstd/old_io/util.rs | 444 ++++++++ 41 files changed, 12823 insertions(+), 12803 deletions(-) delete mode 100644 src/libstd/io/buffered.rs delete mode 100644 src/libstd/io/comm_adapters.rs delete mode 100644 src/libstd/io/extensions.rs delete mode 100644 src/libstd/io/fs.rs delete mode 100644 src/libstd/io/mem.rs delete mode 100644 src/libstd/io/mod.rs delete mode 100644 src/libstd/io/net/addrinfo.rs delete mode 100644 src/libstd/io/net/ip.rs delete mode 100644 src/libstd/io/net/mod.rs delete mode 100644 src/libstd/io/net/pipe.rs delete mode 100644 src/libstd/io/net/tcp.rs delete mode 100644 src/libstd/io/net/udp.rs delete mode 100644 src/libstd/io/pipe.rs delete mode 100644 src/libstd/io/process.rs delete mode 100644 src/libstd/io/result.rs delete mode 100644 src/libstd/io/stdio.rs delete mode 100644 src/libstd/io/tempfile.rs delete mode 100644 src/libstd/io/test.rs delete mode 100644 src/libstd/io/timer.rs delete mode 100644 src/libstd/io/util.rs create mode 100644 src/libstd/old_io/buffered.rs create mode 100644 src/libstd/old_io/comm_adapters.rs create mode 100644 src/libstd/old_io/extensions.rs create mode 100644 src/libstd/old_io/fs.rs create mode 100644 src/libstd/old_io/mem.rs create mode 100644 src/libstd/old_io/mod.rs create mode 100644 src/libstd/old_io/net/addrinfo.rs create mode 100644 src/libstd/old_io/net/ip.rs create mode 100644 src/libstd/old_io/net/mod.rs create mode 100644 src/libstd/old_io/net/pipe.rs create mode 100644 src/libstd/old_io/net/tcp.rs create mode 100644 src/libstd/old_io/net/udp.rs create mode 100644 src/libstd/old_io/pipe.rs create mode 100644 src/libstd/old_io/process.rs create mode 100644 src/libstd/old_io/result.rs create mode 100644 src/libstd/old_io/stdio.rs create mode 100644 src/libstd/old_io/tempfile.rs create mode 100644 src/libstd/old_io/test.rs create mode 100644 src/libstd/old_io/timer.rs create mode 100644 src/libstd/old_io/util.rs (limited to 'src') diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs deleted file mode 100644 index 73c73209f00..00000000000 --- a/src/libstd/io/buffered.rs +++ /dev/null @@ -1,697 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// ignore-lexer-test FIXME #15883 - -//! Buffering wrappers for I/O traits - -use cmp; -use fmt; -use io::{Reader, Writer, Stream, Buffer, DEFAULT_BUF_SIZE, IoResult}; -use iter::{IteratorExt, ExactSizeIterator, repeat}; -use ops::Drop; -use option::Option; -use option::Option::{Some, None}; -use result::Result::Ok; -use slice::{SliceExt}; -use slice; -use vec::Vec; - -/// Wraps a Reader and buffers input from it -/// -/// It can be excessively inefficient to work directly with a `Reader`. For -/// example, every call to `read` on `TcpStream` results in a system call. A -/// `BufferedReader` performs large, infrequent reads on the underlying -/// `Reader` and maintains an in-memory buffer of the results. -/// -/// # Example -/// -/// ```rust -/// use std::io::{BufferedReader, File}; -/// -/// let file = File::open(&Path::new("message.txt")); -/// let mut reader = BufferedReader::new(file); -/// -/// let mut buf = [0; 100]; -/// match reader.read(&mut buf) { -/// Ok(nread) => println!("Read {} bytes", nread), -/// Err(e) => println!("error reading: {}", e) -/// } -/// ``` -pub struct BufferedReader { - inner: R, - buf: Vec, - pos: uint, - cap: uint, -} - -#[stable] -impl fmt::Debug for BufferedReader where R: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "BufferedReader {{ reader: {:?}, buffer: {}/{} }}", - self.inner, self.cap - self.pos, self.buf.len()) - } -} - -impl BufferedReader { - /// Creates a new `BufferedReader` with the specified buffer capacity - pub fn with_capacity(cap: uint, inner: R) -> BufferedReader { - BufferedReader { - inner: inner, - // We can't use the same trick here as we do for BufferedWriter, - // since this memory is visible to the inner Reader. - buf: repeat(0).take(cap).collect(), - pos: 0, - cap: 0, - } - } - - /// Creates a new `BufferedReader` with a default buffer capacity - pub fn new(inner: R) -> BufferedReader { - BufferedReader::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - /// Gets a reference to the underlying reader. - pub fn get_ref<'a>(&self) -> &R { &self.inner } - - /// Gets a mutable reference to the underlying reader. - /// - /// # Warning - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_mut(&mut self) -> &mut R { &mut self.inner } - - /// Unwraps this `BufferedReader`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> R { self.inner } -} - -impl Buffer for BufferedReader { - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { - if self.pos == self.cap { - self.cap = try!(self.inner.read(self.buf.as_mut_slice())); - self.pos = 0; - } - Ok(&self.buf[self.pos..self.cap]) - } - - fn consume(&mut self, amt: uint) { - self.pos += amt; - assert!(self.pos <= self.cap); - } -} - -impl Reader for BufferedReader { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - if self.pos == self.cap && buf.len() >= self.buf.capacity() { - return self.inner.read(buf); - } - let nread = { - let available = try!(self.fill_buf()); - let nread = cmp::min(available.len(), buf.len()); - slice::bytes::copy_memory(buf, &available[..nread]); - nread - }; - self.pos += nread; - Ok(nread) - } -} - -/// Wraps a Writer and buffers output to it -/// -/// It can be excessively inefficient to work directly with a `Writer`. For -/// example, every call to `write` on `TcpStream` results in a system call. A -/// `BufferedWriter` keeps an in memory buffer of data and writes it to the -/// underlying `Writer` in large, infrequent batches. -/// -/// This writer will be flushed when it is dropped. -/// -/// # Example -/// -/// ```rust -/// use std::io::{BufferedWriter, File}; -/// -/// let file = File::create(&Path::new("message.txt")).unwrap(); -/// let mut writer = BufferedWriter::new(file); -/// -/// writer.write_str("hello, world").unwrap(); -/// writer.flush().unwrap(); -/// ``` -pub struct BufferedWriter { - inner: Option, - buf: Vec, - pos: uint -} - -#[stable] -impl fmt::Debug for BufferedWriter where W: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "BufferedWriter {{ writer: {:?}, buffer: {}/{} }}", - self.inner.as_ref().unwrap(), self.pos, self.buf.len()) - } -} - -impl BufferedWriter { - /// Creates a new `BufferedWriter` with the specified buffer capacity - pub fn with_capacity(cap: uint, inner: W) -> BufferedWriter { - // It's *much* faster to create an uninitialized buffer than it is to - // fill everything in with 0. This buffer is entirely an implementation - // detail and is never exposed, so we're safe to not initialize - // everything up-front. This allows creation of BufferedWriter instances - // to be very cheap (large mallocs are not nearly as expensive as large - // callocs). - let mut buf = Vec::with_capacity(cap); - unsafe { buf.set_len(cap); } - BufferedWriter { - inner: Some(inner), - buf: buf, - pos: 0 - } - } - - /// Creates a new `BufferedWriter` with a default buffer capacity - pub fn new(inner: W) -> BufferedWriter { - BufferedWriter::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - fn flush_buf(&mut self) -> IoResult<()> { - if self.pos != 0 { - let ret = self.inner.as_mut().unwrap().write(&self.buf[..self.pos]); - self.pos = 0; - ret - } else { - Ok(()) - } - } - - /// Gets a reference to the underlying writer. - pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() } - - /// Gets a mutable reference to the underlying write. - /// - /// # Warning - /// - /// It is inadvisable to directly read from the underlying writer. - pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() } - - /// Unwraps this `BufferedWriter`, returning the underlying writer. - /// - /// The buffer is flushed before returning the writer. - pub fn into_inner(mut self) -> W { - // FIXME(#12628): is panicking the right thing to do if flushing panicks? - self.flush_buf().unwrap(); - self.inner.take().unwrap() - } -} - -impl Writer for BufferedWriter { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - if self.pos + buf.len() > self.buf.len() { - try!(self.flush_buf()); - } - - if buf.len() > self.buf.len() { - self.inner.as_mut().unwrap().write(buf) - } else { - let dst = &mut self.buf[self.pos..]; - slice::bytes::copy_memory(dst, buf); - self.pos += buf.len(); - Ok(()) - } - } - - fn flush(&mut self) -> IoResult<()> { - self.flush_buf().and_then(|()| self.inner.as_mut().unwrap().flush()) - } -} - -#[unsafe_destructor] -impl Drop for BufferedWriter { - fn drop(&mut self) { - if self.inner.is_some() { - // dtors should not panic, so we ignore a panicked flush - let _ = self.flush_buf(); - } - } -} - -/// Wraps a Writer and buffers output to it, flushing whenever a newline (`0x0a`, -/// `'\n'`) is detected. -/// -/// This writer will be flushed when it is dropped. -pub struct LineBufferedWriter { - inner: BufferedWriter, -} - -#[stable] -impl fmt::Debug for LineBufferedWriter where W: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "LineBufferedWriter {{ writer: {:?}, buffer: {}/{} }}", - self.inner.inner, self.inner.pos, self.inner.buf.len()) - } -} - -impl LineBufferedWriter { - /// Creates a new `LineBufferedWriter` - pub fn new(inner: W) -> LineBufferedWriter { - // Lines typically aren't that long, don't use a giant buffer - LineBufferedWriter { - inner: BufferedWriter::with_capacity(1024, inner) - } - } - - /// Gets a reference to the underlying writer. - /// - /// This type does not expose the ability to get a mutable reference to the - /// underlying reader because that could possibly corrupt the buffer. - pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() } - - /// Unwraps this `LineBufferedWriter`, returning the underlying writer. - /// - /// The internal buffer is flushed before returning the writer. - pub fn into_inner(self) -> W { self.inner.into_inner() } -} - -impl Writer for LineBufferedWriter { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - match buf.iter().rposition(|&b| b == b'\n') { - Some(i) => { - try!(self.inner.write(&buf[..i + 1])); - try!(self.inner.flush()); - try!(self.inner.write(&buf[i + 1..])); - Ok(()) - } - None => self.inner.write(buf), - } - } - - fn flush(&mut self) -> IoResult<()> { self.inner.flush() } -} - -struct InternalBufferedWriter(BufferedWriter); - -impl InternalBufferedWriter { - fn get_mut<'a>(&'a mut self) -> &'a mut BufferedWriter { - let InternalBufferedWriter(ref mut w) = *self; - return w; - } -} - -impl Reader for InternalBufferedWriter { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.get_mut().inner.as_mut().unwrap().read(buf) - } -} - -/// Wraps a Stream and buffers input and output to and from it. -/// -/// It can be excessively inefficient to work directly with a `Stream`. For -/// example, every call to `read` or `write` on `TcpStream` results in a system -/// call. A `BufferedStream` keeps in memory buffers of data, making large, -/// infrequent calls to `read` and `write` on the underlying `Stream`. -/// -/// The output half will be flushed when this stream is dropped. -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::{BufferedStream, File}; -/// -/// let file = File::open(&Path::new("message.txt")); -/// let mut stream = BufferedStream::new(file); -/// -/// stream.write("hello, world".as_bytes()); -/// stream.flush(); -/// -/// let mut buf = [0; 100]; -/// match stream.read(&mut buf) { -/// Ok(nread) => println!("Read {} bytes", nread), -/// Err(e) => println!("error reading: {}", e) -/// } -/// ``` -pub struct BufferedStream { - inner: BufferedReader> -} - -#[stable] -impl fmt::Debug for BufferedStream where S: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let reader = &self.inner; - let writer = &self.inner.inner.0; - write!(fmt, "BufferedStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}", - writer.inner, - writer.pos, writer.buf.len(), - reader.cap - reader.pos, reader.buf.len()) - } -} - -impl BufferedStream { - /// Creates a new buffered stream with explicitly listed capacities for the - /// reader/writer buffer. - pub fn with_capacities(reader_cap: uint, writer_cap: uint, inner: S) - -> BufferedStream { - let writer = BufferedWriter::with_capacity(writer_cap, inner); - let internal_writer = InternalBufferedWriter(writer); - let reader = BufferedReader::with_capacity(reader_cap, - internal_writer); - BufferedStream { inner: reader } - } - - /// Creates a new buffered stream with the default reader/writer buffer - /// capacities. - pub fn new(inner: S) -> BufferedStream { - BufferedStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE, - inner) - } - - /// Gets a reference to the underlying stream. - pub fn get_ref(&self) -> &S { - let InternalBufferedWriter(ref w) = self.inner.inner; - w.get_ref() - } - - /// Gets a mutable reference to the underlying stream. - /// - /// # Warning - /// - /// It is inadvisable to read directly from or write directly to the - /// underlying stream. - pub fn get_mut(&mut self) -> &mut S { - let InternalBufferedWriter(ref mut w) = self.inner.inner; - w.get_mut() - } - - /// Unwraps this `BufferedStream`, returning the underlying stream. - /// - /// The internal buffer is flushed before returning the stream. Any leftover - /// data in the read buffer is lost. - pub fn into_inner(self) -> S { - let InternalBufferedWriter(w) = self.inner.inner; - w.into_inner() - } -} - -impl Buffer for BufferedStream { - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { self.inner.fill_buf() } - fn consume(&mut self, amt: uint) { self.inner.consume(amt) } -} - -impl Reader for BufferedStream { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner.read(buf) - } -} - -impl Writer for BufferedStream { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner.inner.get_mut().write(buf) - } - fn flush(&mut self) -> IoResult<()> { - self.inner.inner.get_mut().flush() - } -} - -#[cfg(test)] -mod test { - extern crate test; - use io; - use prelude::v1::*; - use super::*; - use super::super::{IoResult, EndOfFile}; - use super::super::mem::MemReader; - use self::test::Bencher; - - /// A type, free to create, primarily intended for benchmarking creation of - /// wrappers that, just for construction, don't need a Reader/Writer that - /// does anything useful. Is equivalent to `/dev/null` in semantics. - #[derive(Clone,PartialEq,PartialOrd)] - pub struct NullStream; - - impl Reader for NullStream { - fn read(&mut self, _: &mut [u8]) -> io::IoResult { - Err(io::standard_error(io::EndOfFile)) - } - } - - impl Writer for NullStream { - fn write(&mut self, _: &[u8]) -> io::IoResult<()> { Ok(()) } - } - - /// A dummy reader intended at testing short-reads propagation. - pub struct ShortReader { - lengths: Vec, - } - - impl Reader for ShortReader { - fn read(&mut self, _: &mut [u8]) -> io::IoResult { - if self.lengths.is_empty() { - Err(io::standard_error(io::EndOfFile)) - } else { - Ok(self.lengths.remove(0)) - } - } - } - - #[test] - fn test_buffered_reader() { - let inner = MemReader::new(vec!(5, 6, 7, 0, 1, 2, 3, 4)); - let mut reader = BufferedReader::with_capacity(2, inner); - - let mut buf = [0, 0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(Ok(3), nread); - let b: &[_] = &[5, 6, 7]; - assert_eq!(buf, b); - - let mut buf = [0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(Ok(2), nread); - let b: &[_] = &[0, 1]; - assert_eq!(buf, b); - - let mut buf = [0]; - let nread = reader.read(&mut buf); - assert_eq!(Ok(1), nread); - let b: &[_] = &[2]; - assert_eq!(buf, b); - - let mut buf = [0, 0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(Ok(1), nread); - let b: &[_] = &[3, 0, 0]; - assert_eq!(buf, b); - - let nread = reader.read(&mut buf); - assert_eq!(Ok(1), nread); - let b: &[_] = &[4, 0, 0]; - assert_eq!(buf, b); - - assert!(reader.read(&mut buf).is_err()); - } - - #[test] - fn test_buffered_writer() { - let inner = Vec::new(); - let mut writer = BufferedWriter::with_capacity(2, inner); - - writer.write(&[0, 1]).unwrap(); - let b: &[_] = &[]; - assert_eq!(&writer.get_ref()[], b); - - writer.write(&[2]).unwrap(); - let b: &[_] = &[0, 1]; - assert_eq!(&writer.get_ref()[], b); - - writer.write(&[3]).unwrap(); - assert_eq!(&writer.get_ref()[], b); - - writer.flush().unwrap(); - let a: &[_] = &[0, 1, 2, 3]; - assert_eq!(a, &writer.get_ref()[]); - - writer.write(&[4]).unwrap(); - writer.write(&[5]).unwrap(); - assert_eq!(a, &writer.get_ref()[]); - - writer.write(&[6]).unwrap(); - let a: &[_] = &[0, 1, 2, 3, 4, 5]; - assert_eq!(a, &writer.get_ref()[]); - - writer.write(&[7, 8]).unwrap(); - let a: &[_] = &[0, 1, 2, 3, 4, 5, 6]; - assert_eq!(a, &writer.get_ref()[]); - - writer.write(&[9, 10, 11]).unwrap(); - let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; - assert_eq!(a, &writer.get_ref()[]); - - writer.flush().unwrap(); - assert_eq!(a, &writer.get_ref()[]); - } - - #[test] - fn test_buffered_writer_inner_flushes() { - let mut w = BufferedWriter::with_capacity(3, Vec::new()); - w.write(&[0, 1]).unwrap(); - let a: &[_] = &[]; - assert_eq!(a, &w.get_ref()[]); - let w = w.into_inner(); - let a: &[_] = &[0, 1]; - assert_eq!(a, &w[]); - } - - // This is just here to make sure that we don't infinite loop in the - // newtype struct autoderef weirdness - #[test] - fn test_buffered_stream() { - struct S; - - impl io::Writer for S { - fn write(&mut self, _: &[u8]) -> io::IoResult<()> { Ok(()) } - } - - impl io::Reader for S { - fn read(&mut self, _: &mut [u8]) -> io::IoResult { - Err(io::standard_error(io::EndOfFile)) - } - } - - let mut stream = BufferedStream::new(S); - let mut buf = []; - assert!(stream.read(&mut buf).is_err()); - stream.write(&buf).unwrap(); - stream.flush().unwrap(); - } - - #[test] - fn test_read_until() { - let inner = MemReader::new(vec!(0, 1, 2, 1, 0)); - let mut reader = BufferedReader::with_capacity(2, inner); - assert_eq!(reader.read_until(0), Ok(vec!(0))); - assert_eq!(reader.read_until(2), Ok(vec!(1, 2))); - assert_eq!(reader.read_until(1), Ok(vec!(1))); - assert_eq!(reader.read_until(8), Ok(vec!(0))); - assert!(reader.read_until(9).is_err()); - } - - #[test] - fn test_line_buffer() { - let mut writer = LineBufferedWriter::new(Vec::new()); - writer.write(&[0]).unwrap(); - let b: &[_] = &[]; - assert_eq!(&writer.get_ref()[], b); - writer.write(&[1]).unwrap(); - assert_eq!(&writer.get_ref()[], b); - writer.flush().unwrap(); - let b: &[_] = &[0, 1]; - assert_eq!(&writer.get_ref()[], b); - writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); - let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n']; - assert_eq!(&writer.get_ref()[], b); - writer.flush().unwrap(); - let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2]; - assert_eq!(&writer.get_ref()[], b); - writer.write(&[3, b'\n']).unwrap(); - let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']; - assert_eq!(&writer.get_ref()[], b); - } - - #[test] - fn test_read_line() { - let in_buf = MemReader::new(b"a\nb\nc".to_vec()); - let mut reader = BufferedReader::with_capacity(2, in_buf); - assert_eq!(reader.read_line(), Ok("a\n".to_string())); - assert_eq!(reader.read_line(), Ok("b\n".to_string())); - assert_eq!(reader.read_line(), Ok("c".to_string())); - assert!(reader.read_line().is_err()); - } - - #[test] - fn test_lines() { - let in_buf = MemReader::new(b"a\nb\nc".to_vec()); - let mut reader = BufferedReader::with_capacity(2, in_buf); - let mut it = reader.lines(); - assert_eq!(it.next(), Some(Ok("a\n".to_string()))); - assert_eq!(it.next(), Some(Ok("b\n".to_string()))); - assert_eq!(it.next(), Some(Ok("c".to_string()))); - assert_eq!(it.next(), None); - } - - #[test] - fn test_short_reads() { - let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]}; - let mut reader = BufferedReader::new(inner); - let mut buf = [0, 0]; - assert_eq!(reader.read(&mut buf), Ok(0)); - assert_eq!(reader.read(&mut buf), Ok(1)); - assert_eq!(reader.read(&mut buf), Ok(2)); - assert_eq!(reader.read(&mut buf), Ok(0)); - assert_eq!(reader.read(&mut buf), Ok(1)); - assert_eq!(reader.read(&mut buf), Ok(0)); - assert!(reader.read(&mut buf).is_err()); - } - - #[test] - fn read_char_buffered() { - let buf = [195u8, 159u8]; - let mut reader = BufferedReader::with_capacity(1, &buf[]); - assert_eq!(reader.read_char(), Ok('ß')); - } - - #[test] - fn test_chars() { - let buf = [195u8, 159u8, b'a']; - let mut reader = BufferedReader::with_capacity(1, &buf[]); - let mut it = reader.chars(); - assert_eq!(it.next(), Some(Ok('ß'))); - assert_eq!(it.next(), Some(Ok('a'))); - assert_eq!(it.next(), None); - } - - #[test] - #[should_fail] - fn dont_panic_in_drop_on_panicked_flush() { - struct FailFlushWriter; - - impl Writer for FailFlushWriter { - fn write(&mut self, _buf: &[u8]) -> IoResult<()> { Ok(()) } - fn flush(&mut self) -> IoResult<()> { Err(io::standard_error(EndOfFile)) } - } - - let writer = FailFlushWriter; - let _writer = BufferedWriter::new(writer); - - // If writer panics *again* due to the flush error then the process will abort. - panic!(); - } - - #[bench] - fn bench_buffered_reader(b: &mut Bencher) { - b.iter(|| { - BufferedReader::new(NullStream) - }); - } - - #[bench] - fn bench_buffered_writer(b: &mut Bencher) { - b.iter(|| { - BufferedWriter::new(NullStream) - }); - } - - #[bench] - fn bench_buffered_stream(b: &mut Bencher) { - b.iter(|| { - BufferedStream::new(NullStream); - }); - } -} diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs deleted file mode 100644 index 4649012d454..00000000000 --- a/src/libstd/io/comm_adapters.rs +++ /dev/null @@ -1,248 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use clone::Clone; -use cmp; -use sync::mpsc::{Sender, Receiver}; -use io; -use option::Option::{None, Some}; -use result::Result::{Ok, Err}; -use slice::{bytes, SliceExt}; -use super::{Buffer, Reader, Writer, IoResult}; -use vec::Vec; - -/// Allows reading from a rx. -/// -/// # Example -/// -/// ``` -/// use std::sync::mpsc::channel; -/// use std::io::ChanReader; -/// -/// let (tx, rx) = channel(); -/// # drop(tx); -/// let mut reader = ChanReader::new(rx); -/// -/// let mut buf = [0u8; 100]; -/// match reader.read(&mut buf) { -/// Ok(nread) => println!("Read {} bytes", nread), -/// Err(e) => println!("read error: {}", e), -/// } -/// ``` -pub struct ChanReader { - buf: Vec, // A buffer of bytes received but not consumed. - pos: uint, // How many of the buffered bytes have already be consumed. - rx: Receiver>, // The Receiver to pull data from. - closed: bool, // Whether the channel this Receiver connects to has been closed. -} - -impl ChanReader { - /// Wraps a `Port` in a `ChanReader` structure - pub fn new(rx: Receiver>) -> ChanReader { - ChanReader { - buf: Vec::new(), - pos: 0, - rx: rx, - closed: false, - } - } -} - -impl Buffer for ChanReader { - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { - if self.pos >= self.buf.len() { - self.pos = 0; - match self.rx.recv() { - Ok(bytes) => { - self.buf = bytes; - }, - Err(..) => { - self.closed = true; - self.buf = Vec::new(); - } - } - } - if self.closed { - Err(io::standard_error(io::EndOfFile)) - } else { - Ok(&self.buf[self.pos..]) - } - } - - fn consume(&mut self, amt: uint) { - self.pos += amt; - assert!(self.pos <= self.buf.len()); - } -} - -impl Reader for ChanReader { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - let mut num_read = 0; - loop { - let count = match self.fill_buf().ok() { - Some(src) => { - let dst = &mut buf[num_read..]; - let count = cmp::min(src.len(), dst.len()); - bytes::copy_memory(dst, &src[..count]); - count - }, - None => 0, - }; - self.consume(count); - num_read += count; - if num_read == buf.len() || self.closed { - break; - } - } - if self.closed && num_read == 0 { - Err(io::standard_error(io::EndOfFile)) - } else { - Ok(num_read) - } - } -} - -/// Allows writing to a tx. -/// -/// # Example -/// -/// ``` -/// # #![allow(unused_must_use)] -/// use std::sync::mpsc::channel; -/// use std::io::ChanWriter; -/// -/// let (tx, rx) = channel(); -/// # drop(rx); -/// let mut writer = ChanWriter::new(tx); -/// writer.write("hello, world".as_bytes()); -/// ``` -pub struct ChanWriter { - tx: Sender>, -} - -impl ChanWriter { - /// Wraps a channel in a `ChanWriter` structure - pub fn new(tx: Sender>) -> ChanWriter { - ChanWriter { tx: tx } - } -} - -#[stable] -impl Clone for ChanWriter { - fn clone(&self) -> ChanWriter { - ChanWriter { tx: self.tx.clone() } - } -} - -impl Writer for ChanWriter { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.tx.send(buf.to_vec()).map_err(|_| { - io::IoError { - kind: io::BrokenPipe, - desc: "Pipe closed", - detail: None - } - }) - } -} - - -#[cfg(test)] -mod test { - use prelude::v1::*; - - use sync::mpsc::channel; - use super::*; - use io; - use thread::Thread; - - #[test] - fn test_rx_reader() { - let (tx, rx) = channel(); - Thread::spawn(move|| { - tx.send(vec![1u8, 2u8]).unwrap(); - tx.send(vec![]).unwrap(); - tx.send(vec![3u8, 4u8]).unwrap(); - tx.send(vec![5u8, 6u8]).unwrap(); - tx.send(vec![7u8, 8u8]).unwrap(); - }); - - let mut reader = ChanReader::new(rx); - let mut buf = [0u8; 3]; - - assert_eq!(Ok(0), reader.read(&mut [])); - - assert_eq!(Ok(3), reader.read(&mut buf)); - let a: &[u8] = &[1,2,3]; - assert_eq!(a, buf); - - assert_eq!(Ok(3), reader.read(&mut buf)); - let a: &[u8] = &[4,5,6]; - assert_eq!(a, buf); - - assert_eq!(Ok(2), reader.read(&mut buf)); - let a: &[u8] = &[7,8,6]; - assert_eq!(a, buf); - - match reader.read(buf.as_mut_slice()) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::EndOfFile), - } - assert_eq!(a, buf); - - // Ensure it continues to panic in the same way. - match reader.read(buf.as_mut_slice()) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::EndOfFile), - } - assert_eq!(a, buf); - } - - #[test] - fn test_rx_buffer() { - let (tx, rx) = channel(); - Thread::spawn(move|| { - tx.send(b"he".to_vec()).unwrap(); - tx.send(b"llo wo".to_vec()).unwrap(); - tx.send(b"".to_vec()).unwrap(); - tx.send(b"rld\nhow ".to_vec()).unwrap(); - tx.send(b"are you?".to_vec()).unwrap(); - tx.send(b"".to_vec()).unwrap(); - }); - - let mut reader = ChanReader::new(rx); - - assert_eq!(Ok("hello world\n".to_string()), reader.read_line()); - assert_eq!(Ok("how are you?".to_string()), reader.read_line()); - match reader.read_line() { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::EndOfFile), - } - } - - #[test] - fn test_chan_writer() { - let (tx, rx) = channel(); - let mut writer = ChanWriter::new(tx); - writer.write_be_u32(42).unwrap(); - - let wanted = vec![0u8, 0u8, 0u8, 42u8]; - let got = match Thread::scoped(move|| { rx.recv().unwrap() }).join() { - Ok(got) => got, - Err(_) => panic!(), - }; - assert_eq!(wanted, got); - - match writer.write_u8(1) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::BrokenPipe), - } - } -} diff --git a/src/libstd/io/extensions.rs b/src/libstd/io/extensions.rs deleted file mode 100644 index af08eea210e..00000000000 --- a/src/libstd/io/extensions.rs +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Utility mixins that apply to all Readers and Writers - -#![allow(missing_docs)] - -// FIXME: Not sure how this should be structured -// FIXME: Iteration should probably be considered separately - -use io::{IoError, IoResult, Reader}; -use io; -use iter::Iterator; -use num::Int; -use ops::FnOnce; -use option::Option; -use option::Option::{Some, None}; -use ptr::PtrExt; -use result::Result::{Ok, Err}; -use slice::{SliceExt, AsSlice}; - -/// An iterator that reads a single byte on each iteration, -/// until `.read_byte()` returns `EndOfFile`. -/// -/// # Notes about the Iteration Protocol -/// -/// The `Bytes` may yield `None` and thus terminate -/// an iteration, but continue to yield elements if iteration -/// is attempted again. -/// -/// # Error -/// -/// Any error other than `EndOfFile` that is produced by the underlying Reader -/// is returned by the iterator and should be handled by the caller. -pub struct Bytes<'r, T:'r> { - reader: &'r mut T, -} - -impl<'r, R: Reader> Bytes<'r, R> { - /// Constructs a new byte iterator from the given Reader instance. - pub fn new(r: &'r mut R) -> Bytes<'r, R> { - Bytes { - reader: r, - } - } -} - -impl<'r, R: Reader> Iterator for Bytes<'r, R> { - type Item = IoResult; - - #[inline] - fn next(&mut self) -> Option> { - match self.reader.read_byte() { - Ok(x) => Some(Ok(x)), - Err(IoError { kind: io::EndOfFile, .. }) => None, - Err(e) => Some(Err(e)) - } - } -} - -/// Converts an 8-bit to 64-bit unsigned value to a little-endian byte -/// representation of the given size. If the size is not big enough to -/// represent the value, then the high-order bytes are truncated. -/// -/// Arguments: -/// -/// * `n`: The value to convert. -/// * `size`: The size of the value, in bytes. This must be 8 or less, or task -/// panic occurs. If this is less than 8, then a value of that -/// many bytes is produced. For example, if `size` is 4, then a -/// 32-bit byte representation is produced. -/// * `f`: A callback that receives the value. -/// -/// This function returns the value returned by the callback, for convenience. -pub fn u64_to_le_bytes(n: u64, size: uint, f: F) -> T where - F: FnOnce(&[u8]) -> T, -{ - use mem::transmute; - - // LLVM fails to properly optimize this when using shifts instead of the to_le* intrinsics - assert!(size <= 8u); - match size { - 1u => f(&[n as u8]), - 2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_le()) }), - 4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_le()) }), - 8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_le()) }), - _ => { - - let mut bytes = vec!(); - let mut i = size; - let mut n = n; - while i > 0u { - bytes.push((n & 255_u64) as u8); - n >>= 8; - i -= 1u; - } - f(bytes.as_slice()) - } - } -} - -/// Converts an 8-bit to 64-bit unsigned value to a big-endian byte -/// representation of the given size. If the size is not big enough to -/// represent the value, then the high-order bytes are truncated. -/// -/// Arguments: -/// -/// * `n`: The value to convert. -/// * `size`: The size of the value, in bytes. This must be 8 or less, or task -/// panic occurs. If this is less than 8, then a value of that -/// many bytes is produced. For example, if `size` is 4, then a -/// 32-bit byte representation is produced. -/// * `f`: A callback that receives the value. -/// -/// This function returns the value returned by the callback, for convenience. -pub fn u64_to_be_bytes(n: u64, size: uint, f: F) -> T where - F: FnOnce(&[u8]) -> T, -{ - use mem::transmute; - - // LLVM fails to properly optimize this when using shifts instead of the to_be* intrinsics - assert!(size <= 8u); - match size { - 1u => f(&[n as u8]), - 2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_be()) }), - 4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_be()) }), - 8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_be()) }), - _ => { - let mut bytes = vec!(); - let mut i = size; - while i > 0u { - let shift = (i - 1u) * 8u; - bytes.push((n >> shift) as u8); - i -= 1u; - } - f(bytes.as_slice()) - } - } -} - -/// Extracts an 8-bit to 64-bit unsigned big-endian value from the given byte -/// buffer and returns it as a 64-bit value. -/// -/// Arguments: -/// -/// * `data`: The buffer in which to extract the value. -/// * `start`: The offset at which to extract the value. -/// * `size`: The size of the value in bytes to extract. This must be 8 or -/// less, or task panic occurs. If this is less than 8, then only -/// that many bytes are parsed. For example, if `size` is 4, then a -/// 32-bit value is parsed. -pub fn u64_from_be_bytes(data: &[u8], start: uint, size: uint) -> u64 { - use ptr::{copy_nonoverlapping_memory}; - use slice::SliceExt; - - assert!(size <= 8u); - - if data.len() - start < size { - panic!("index out of bounds"); - } - - let mut buf = [0u8; 8]; - unsafe { - let ptr = data.as_ptr().offset(start as int); - let out = buf.as_mut_ptr(); - copy_nonoverlapping_memory(out.offset((8 - size) as int), ptr, size); - (*(out as *const u64)).to_be() - } -} - -#[cfg(test)] -mod test { - use prelude::v1::*; - use io; - use io::{MemReader, BytesReader}; - - struct InitialZeroByteReader { - count: int, - } - - impl Reader for InitialZeroByteReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - if self.count == 0 { - self.count = 1; - Ok(0) - } else { - buf[0] = 10; - Ok(1) - } - } - } - - struct EofReader; - - impl Reader for EofReader { - fn read(&mut self, _: &mut [u8]) -> io::IoResult { - Err(io::standard_error(io::EndOfFile)) - } - } - - struct ErroringReader; - - impl Reader for ErroringReader { - fn read(&mut self, _: &mut [u8]) -> io::IoResult { - Err(io::standard_error(io::InvalidInput)) - } - } - - struct PartialReader { - count: int, - } - - impl Reader for PartialReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - if self.count == 0 { - self.count = 1; - buf[0] = 10; - buf[1] = 11; - Ok(2) - } else { - buf[0] = 12; - buf[1] = 13; - Ok(2) - } - } - } - - struct ErroringLaterReader { - count: int, - } - - impl Reader for ErroringLaterReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - if self.count == 0 { - self.count = 1; - buf[0] = 10; - Ok(1) - } else { - Err(io::standard_error(io::InvalidInput)) - } - } - } - - struct ThreeChunkReader { - count: int, - } - - impl Reader for ThreeChunkReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - if self.count == 0 { - self.count = 1; - buf[0] = 10; - buf[1] = 11; - Ok(2) - } else if self.count == 1 { - self.count = 2; - buf[0] = 12; - buf[1] = 13; - Ok(2) - } else { - Err(io::standard_error(io::EndOfFile)) - } - } - } - - #[test] - fn read_byte() { - let mut reader = MemReader::new(vec!(10)); - let byte = reader.read_byte(); - assert!(byte == Ok(10)); - } - - #[test] - fn read_byte_0_bytes() { - let mut reader = InitialZeroByteReader { - count: 0, - }; - let byte = reader.read_byte(); - assert!(byte == Ok(10)); - } - - #[test] - fn read_byte_eof() { - let mut reader = EofReader; - let byte = reader.read_byte(); - assert!(byte.is_err()); - } - - #[test] - fn read_byte_error() { - let mut reader = ErroringReader; - let byte = reader.read_byte(); - assert!(byte.is_err()); - } - - #[test] - fn bytes_0_bytes() { - let mut reader = InitialZeroByteReader { - count: 0, - }; - let byte = reader.bytes().next(); - assert!(byte == Some(Ok(10))); - } - - #[test] - fn bytes_eof() { - let mut reader = EofReader; - let byte = reader.bytes().next(); - assert!(byte.is_none()); - } - - #[test] - fn bytes_error() { - let mut reader = ErroringReader; - let mut it = reader.bytes(); - let byte = it.next(); - assert!(byte.unwrap().is_err()); - } - - #[test] - fn read_bytes() { - let mut reader = MemReader::new(vec!(10, 11, 12, 13)); - let bytes = reader.read_exact(4).unwrap(); - assert!(bytes == vec!(10, 11, 12, 13)); - } - - #[test] - fn read_bytes_partial() { - let mut reader = PartialReader { - count: 0, - }; - let bytes = reader.read_exact(4).unwrap(); - assert!(bytes == vec!(10, 11, 12, 13)); - } - - #[test] - fn read_bytes_eof() { - let mut reader = MemReader::new(vec!(10, 11)); - assert!(reader.read_exact(4).is_err()); - } - - #[test] - fn push_at_least() { - let mut reader = MemReader::new(vec![10, 11, 12, 13]); - let mut buf = vec![8, 9]; - assert!(reader.push_at_least(4, 4, &mut buf).is_ok()); - assert!(buf == vec![8, 9, 10, 11, 12, 13]); - } - - #[test] - fn push_at_least_partial() { - let mut reader = PartialReader { - count: 0, - }; - let mut buf = vec![8, 9]; - assert!(reader.push_at_least(4, 4, &mut buf).is_ok()); - assert!(buf == vec![8, 9, 10, 11, 12, 13]); - } - - #[test] - fn push_at_least_eof() { - let mut reader = MemReader::new(vec![10, 11]); - let mut buf = vec![8, 9]; - assert!(reader.push_at_least(4, 4, &mut buf).is_err()); - assert!(buf == vec![8, 9, 10, 11]); - } - - #[test] - fn push_at_least_error() { - let mut reader = ErroringLaterReader { - count: 0, - }; - let mut buf = vec![8, 9]; - assert!(reader.push_at_least(4, 4, &mut buf).is_err()); - assert!(buf == vec![8, 9, 10]); - } - - #[test] - fn read_to_end() { - let mut reader = ThreeChunkReader { - count: 0, - }; - let buf = reader.read_to_end().unwrap(); - assert!(buf == vec!(10, 11, 12, 13)); - } - - #[test] - #[should_fail] - fn read_to_end_error() { - let mut reader = ThreeChunkReader { - count: 0, - }; - let buf = reader.read_to_end().unwrap(); - assert!(buf == vec!(10, 11)); - } - - #[test] - fn test_read_write_le_mem() { - let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX]; - - let mut writer = Vec::new(); - for i in uints.iter() { - writer.write_le_u64(*i).unwrap(); - } - - let mut reader = MemReader::new(writer); - for i in uints.iter() { - assert!(reader.read_le_u64().unwrap() == *i); - } - } - - - #[test] - fn test_read_write_be() { - let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX]; - - let mut writer = Vec::new(); - for i in uints.iter() { - writer.write_be_u64(*i).unwrap(); - } - - let mut reader = MemReader::new(writer); - for i in uints.iter() { - assert!(reader.read_be_u64().unwrap() == *i); - } - } - - #[test] - fn test_read_be_int_n() { - let ints = [::i32::MIN, -123456, -42, -5, 0, 1, ::i32::MAX]; - - let mut writer = Vec::new(); - for i in ints.iter() { - writer.write_be_i32(*i).unwrap(); - } - - let mut reader = MemReader::new(writer); - for i in ints.iter() { - // this tests that the sign extension is working - // (comparing the values as i32 would not test this) - assert!(reader.read_be_int_n(4).unwrap() == *i as i64); - } - } - - #[test] - fn test_read_f32() { - //big-endian floating-point 8.1250 - let buf = vec![0x41, 0x02, 0x00, 0x00]; - - let mut writer = Vec::new(); - writer.write(buf.as_slice()).unwrap(); - - let mut reader = MemReader::new(writer); - let f = reader.read_be_f32().unwrap(); - assert!(f == 8.1250); - } - - #[test] - fn test_read_write_f32() { - let f:f32 = 8.1250; - - let mut writer = Vec::new(); - writer.write_be_f32(f).unwrap(); - writer.write_le_f32(f).unwrap(); - - let mut reader = MemReader::new(writer); - assert!(reader.read_be_f32().unwrap() == 8.1250); - assert!(reader.read_le_f32().unwrap() == 8.1250); - } - - #[test] - fn test_u64_from_be_bytes() { - use super::u64_from_be_bytes; - - let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; - - // Aligned access - assert_eq!(u64_from_be_bytes(&buf, 0, 0), 0); - assert_eq!(u64_from_be_bytes(&buf, 0, 1), 0x01); - assert_eq!(u64_from_be_bytes(&buf, 0, 2), 0x0102); - assert_eq!(u64_from_be_bytes(&buf, 0, 3), 0x010203); - assert_eq!(u64_from_be_bytes(&buf, 0, 4), 0x01020304); - assert_eq!(u64_from_be_bytes(&buf, 0, 5), 0x0102030405); - assert_eq!(u64_from_be_bytes(&buf, 0, 6), 0x010203040506); - assert_eq!(u64_from_be_bytes(&buf, 0, 7), 0x01020304050607); - assert_eq!(u64_from_be_bytes(&buf, 0, 8), 0x0102030405060708); - - // Unaligned access - assert_eq!(u64_from_be_bytes(&buf, 1, 0), 0); - assert_eq!(u64_from_be_bytes(&buf, 1, 1), 0x02); - assert_eq!(u64_from_be_bytes(&buf, 1, 2), 0x0203); - assert_eq!(u64_from_be_bytes(&buf, 1, 3), 0x020304); - assert_eq!(u64_from_be_bytes(&buf, 1, 4), 0x02030405); - assert_eq!(u64_from_be_bytes(&buf, 1, 5), 0x0203040506); - assert_eq!(u64_from_be_bytes(&buf, 1, 6), 0x020304050607); - assert_eq!(u64_from_be_bytes(&buf, 1, 7), 0x02030405060708); - assert_eq!(u64_from_be_bytes(&buf, 1, 8), 0x0203040506070809); - } -} - -#[cfg(test)] -mod bench { - extern crate test; - - use prelude::v1::*; - use self::test::Bencher; - - // why is this a macro? wouldn't an inlined function work just as well? - macro_rules! u64_from_be_bytes_bench_impl { - ($b:expr, $size:expr, $stride:expr, $start_index:expr) => - ({ - use super::u64_from_be_bytes; - - let data = range(0u8, $stride*100+$start_index).collect::>(); - let mut sum = 0u64; - $b.iter(|| { - let mut i = $start_index; - while i < data.len() { - sum += u64_from_be_bytes(data.as_slice(), i, $size); - i += $stride; - } - }); - }) - } - - #[bench] - fn u64_from_be_bytes_4_aligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(b, 4, 4, 0); - } - - #[bench] - fn u64_from_be_bytes_4_unaligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(b, 4, 4, 1); - } - - #[bench] - fn u64_from_be_bytes_7_aligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(b, 7, 8, 0); - } - - #[bench] - fn u64_from_be_bytes_7_unaligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(b, 7, 8, 1); - } - - #[bench] - fn u64_from_be_bytes_8_aligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(b, 8, 8, 0); - } - - #[bench] - fn u64_from_be_bytes_8_unaligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(b, 8, 8, 1); - } -} diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs deleted file mode 100644 index cc36c5640d0..00000000000 --- a/src/libstd/io/fs.rs +++ /dev/null @@ -1,1571 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// ignore-lexer-test FIXME #15679 - -//! Synchronous File I/O -//! -//! This module provides a set of functions and traits for working -//! with regular files & directories on a filesystem. -//! -//! At the top-level of the module are a set of freestanding functions, associated -//! with various filesystem operations. They all operate on `Path` objects. -//! -//! All operations in this module, including those as part of `File` et al -//! block the task during execution. In the event of failure, all functions/methods -//! will return an `IoResult` type with an `Err` value. -//! -//! Also included in this module is an implementation block on the `Path` object -//! defined in `std::path::Path`. The impl adds useful methods about inspecting the -//! metadata of a file. This includes getting the `stat` information, reading off -//! particular bits of it, etc. -//! -//! # Example -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::io::fs::PathExtensions; -//! use std::io::{File, fs}; -//! -//! let path = Path::new("foo.txt"); -//! -//! // create the file, whether it exists or not -//! let mut file = File::create(&path); -//! file.write(b"foobar"); -//! # drop(file); -//! -//! // open the file in read-only mode -//! let mut file = File::open(&path); -//! file.read_to_end(); -//! -//! println!("{}", path.stat().unwrap().size); -//! # drop(file); -//! fs::unlink(&path); -//! ``` - -use clone::Clone; -use io::standard_error; -use io::{FilePermission, Write, Open, FileAccess, FileMode, FileType}; -use io::{IoResult, IoError, InvalidInput}; -use io::{FileStat, SeekStyle, Seek, Writer, Reader}; -use io::{Read, Truncate, ReadWrite, Append}; -use io::UpdateIoError; -use io; -use iter::{Iterator, Extend}; -use option::Option; -use option::Option::{Some, None}; -use path::{Path, GenericPath}; -use path; -use result::Result::{Err, Ok}; -use slice::SliceExt; -use string::String; -use vec::Vec; - -use sys::fs as fs_imp; -use sys_common; - -/// Unconstrained file access type that exposes read and write operations -/// -/// Can be constructed via `File::open()`, `File::create()`, and -/// `File::open_mode()`. -/// -/// # Error -/// -/// This type will return errors as an `IoResult` if operations are -/// attempted against it for which its underlying file descriptor was not -/// configured at creation time, via the `FileAccess` parameter to -/// `File::open_mode()`. -pub struct File { - fd: fs_imp::FileDesc, - path: Path, - last_nread: int, -} - -impl sys_common::AsInner for File { - fn as_inner(&self) -> &fs_imp::FileDesc { - &self.fd - } -} - -impl File { - /// Open a file at `path` in the mode specified by the `mode` and `access` - /// arguments - /// - /// # Example - /// - /// ```rust,should_fail - /// use std::io::{File, Open, ReadWrite}; - /// - /// let p = Path::new("/some/file/path.txt"); - /// - /// let file = match File::open_mode(&p, Open, ReadWrite) { - /// Ok(f) => f, - /// Err(e) => panic!("file error: {}", e), - /// }; - /// // do some stuff with that file - /// - /// // the file will be closed at the end of this block - /// ``` - /// - /// `FileMode` and `FileAccess` provide information about the permissions - /// context in which a given stream is created. More information about them - /// can be found in `std::io`'s docs. If a file is opened with `Write` - /// or `ReadWrite` access, then it will be created if it does not already - /// exist. - /// - /// Note that, with this function, a `File` is returned regardless of the - /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a - /// `File` opened as `Read` will return an error at runtime). - /// - /// # Error - /// - /// This function will return an error under a number of different - /// circumstances, to include but not limited to: - /// - /// * Opening a file that does not exist with `Read` access. - /// * Attempting to open a file with a `FileAccess` that the user lacks - /// permissions for - /// * Filesystem-level errors (full disk, etc) - pub fn open_mode(path: &Path, - mode: FileMode, - access: FileAccess) -> IoResult { - fs_imp::open(path, mode, access).and_then(|fd| { - // On *BSD systems, we can open a directory as a file and read from it: - // fd=open("/tmp", O_RDONLY); read(fd, buf, N); - // due to an old tradition before the introduction of opendir(3). - // We explicitly reject it because there are few use cases. - if cfg!(not(any(windows, target_os = "linux", target_os = "android"))) && - try!(fd.fstat()).kind == FileType::Directory { - Err(IoError { - kind: InvalidInput, - desc: "is a directory", - detail: None - }) - } else { - Ok(File { - path: path.clone(), - fd: fd, - last_nread: -1 - }) - } - }).update_err("couldn't open path as file", |e| { - format!("{}; path={}; mode={}; access={}", e, path.display(), - mode_string(mode), access_string(access)) - }) - } - - /// Attempts to open a file in read-only mode. This function is equivalent to - /// `File::open_mode(path, Open, Read)`, and will raise all of the same - /// errors that `File::open_mode` does. - /// - /// For more information, see the `File::open_mode` function. - /// - /// # Example - /// - /// ```rust - /// use std::io::File; - /// - /// let contents = File::open(&Path::new("foo.txt")).read_to_end(); - /// ``` - pub fn open(path: &Path) -> IoResult { - File::open_mode(path, Open, Read) - } - - /// Attempts to create a file in write-only mode. This function is - /// equivalent to `File::open_mode(path, Truncate, Write)`, and will - /// raise all of the same errors that `File::open_mode` does. - /// - /// For more information, see the `File::open_mode` function. - /// - /// # Example - /// - /// ```rust - /// # #![allow(unused_must_use)] - /// use std::io::File; - /// - /// let mut f = File::create(&Path::new("foo.txt")); - /// f.write(b"This is a sample file"); - /// # drop(f); - /// # ::std::io::fs::unlink(&Path::new("foo.txt")); - /// ``` - pub fn create(path: &Path) -> IoResult { - File::open_mode(path, Truncate, Write) - .update_desc("couldn't create file") - } - - /// Returns the original path that was used to open this file. - pub fn path<'a>(&'a self) -> &'a Path { - &self.path - } - - /// Synchronizes all modifications to this file to its permanent storage - /// device. This will flush any internal buffers necessary to perform this - /// operation. - pub fn fsync(&mut self) -> IoResult<()> { - self.fd.fsync() - .update_err("couldn't fsync file", - |e| format!("{}; path={}", e, self.path.display())) - } - - /// This function is similar to `fsync`, except that it may not synchronize - /// file metadata to the filesystem. This is intended for use cases that - /// must synchronize content, but don't need the metadata on disk. The goal - /// of this method is to reduce disk operations. - pub fn datasync(&mut self) -> IoResult<()> { - self.fd.datasync() - .update_err("couldn't datasync file", - |e| format!("{}; path={}", e, self.path.display())) - } - - /// Either truncates or extends the underlying file, updating the size of - /// this file to become `size`. This is equivalent to unix's `truncate` - /// function. - /// - /// If the `size` is less than the current file's size, then the file will - /// be shrunk. If it is greater than the current file's size, then the file - /// will be extended to `size` and have all of the intermediate data filled - /// in with 0s. - pub fn truncate(&mut self, size: i64) -> IoResult<()> { - self.fd.truncate(size) - .update_err("couldn't truncate file", |e| - format!("{}; path={}; size={}", e, self.path.display(), size)) - } - - /// Returns true if the stream has reached the end of the file. - /// - /// If true, then this file will no longer continue to return data via - /// `read`. - /// - /// Note that the operating system will not return an `EOF` indicator - /// until you have attempted to read past the end of the file, so if - /// you've read _exactly_ the number of bytes in the file, this will - /// return `false`, not `true`. - pub fn eof(&self) -> bool { - self.last_nread == 0 - } - - /// Queries information about the underlying file. - pub fn stat(&self) -> IoResult { - self.fd.fstat() - .update_err("couldn't fstat file", |e| - format!("{}; path={}", e, self.path.display())) - } -} - -/// Unlink a file from the underlying filesystem. -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::fs; -/// -/// let p = Path::new("/some/file/path.txt"); -/// fs::unlink(&p); -/// ``` -/// -/// Note that, just because an unlink call was successful, it is not -/// guaranteed that a file is immediately deleted (e.g. depending on -/// platform, other open file descriptors may prevent immediate removal) -/// -/// # Error -/// -/// This function will return an error if `path` points to a directory, if the -/// user lacks permissions to remove the file, or if some other filesystem-level -/// error occurs. -pub fn unlink(path: &Path) -> IoResult<()> { - fs_imp::unlink(path) - .update_err("couldn't unlink path", |e| - format!("{}; path={}", e, path.display())) -} - -/// Given a path, query the file system to get information about a file, -/// directory, etc. This function will traverse symlinks to query -/// information about the destination file. -/// -/// # Example -/// -/// ```rust -/// use std::io::fs; -/// -/// let p = Path::new("/some/file/path.txt"); -/// match fs::stat(&p) { -/// Ok(stat) => { /* ... */ } -/// Err(e) => { /* handle error */ } -/// } -/// ``` -/// -/// # Error -/// -/// This function will return an error if the user lacks the requisite permissions -/// to perform a `stat` call on the given `path` or if there is no entry in the -/// filesystem at the provided path. -pub fn stat(path: &Path) -> IoResult { - fs_imp::stat(path) - .update_err("couldn't stat path", |e| - format!("{}; path={}", e, path.display())) -} - -/// Perform the same operation as the `stat` function, except that this -/// function does not traverse through symlinks. This will return -/// information about the symlink file instead of the file that it points -/// to. -/// -/// # Error -/// -/// See `stat` -pub fn lstat(path: &Path) -> IoResult { - fs_imp::lstat(path) - .update_err("couldn't lstat path", |e| - format!("{}; path={}", e, path.display())) -} - -/// Rename a file or directory to a new name. -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::fs; -/// -/// fs::rename(&Path::new("foo"), &Path::new("bar")); -/// ``` -/// -/// # Error -/// -/// This function will return an error if the provided `from` doesn't exist, if -/// the process lacks permissions to view the contents, or if some other -/// intermittent I/O error occurs. -pub fn rename(from: &Path, to: &Path) -> IoResult<()> { - fs_imp::rename(from, to) - .update_err("couldn't rename path", |e| - format!("{}; from={:?}; to={:?}", e, from.display(), to.display())) -} - -/// Copies the contents of one file to another. This function will also -/// copy the permission bits of the original file to the destination file. -/// -/// Note that if `from` and `to` both point to the same file, then the file -/// will likely get truncated by this operation. -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::fs; -/// -/// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt")); -/// ``` -/// -/// # Error -/// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * The `from` path is not a file -/// * The `from` file does not exist -/// * The current process does not have the permission rights to access -/// `from` or write `to` -/// -/// Note that this copy is not atomic in that once the destination is -/// ensured to not exist, there is nothing preventing the destination from -/// being created and then destroyed by this operation. -pub fn copy(from: &Path, to: &Path) -> IoResult<()> { - fn update_err(result: IoResult, from: &Path, to: &Path) -> IoResult { - result.update_err("couldn't copy path", |e| { - format!("{}; from={:?}; to={:?}", e, from.display(), to.display()) - }) - } - - if !from.is_file() { - return update_err(Err(IoError { - kind: io::MismatchedFileTypeForOperation, - desc: "the source path is not an existing file", - detail: None - }), from, to) - } - - let mut reader = try!(File::open(from)); - let mut writer = try!(File::create(to)); - - try!(update_err(super::util::copy(&mut reader, &mut writer), from, to)); - - chmod(to, try!(update_err(from.stat(), from, to)).perm) -} - -/// Changes the permission mode bits found on a file or a directory. This -/// function takes a mask from the `io` module -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io; -/// use std::io::fs; -/// -/// fs::chmod(&Path::new("file.txt"), io::USER_FILE); -/// fs::chmod(&Path::new("file.txt"), io::USER_READ | io::USER_WRITE); -/// fs::chmod(&Path::new("dir"), io::USER_DIR); -/// fs::chmod(&Path::new("file.exe"), io::USER_EXEC); -/// ``` -/// -/// # Error -/// -/// This function will return an error if the provided `path` doesn't exist, if -/// the process lacks permissions to change the attributes of the file, or if -/// some other I/O error is encountered. -pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> { - fs_imp::chmod(path, mode.bits() as uint) - .update_err("couldn't chmod path", |e| - format!("{}; path={}; mode={:?}", e, path.display(), mode)) -} - -/// Change the user and group owners of a file at the specified path. -pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> { - fs_imp::chown(path, uid, gid) - .update_err("couldn't chown path", |e| - format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)) -} - -/// Creates a new hard link on the filesystem. The `dst` path will be a -/// link pointing to the `src` path. Note that systems often require these -/// two paths to both be located on the same filesystem. -pub fn link(src: &Path, dst: &Path) -> IoResult<()> { - fs_imp::link(src, dst) - .update_err("couldn't link path", |e| - format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display())) -} - -/// Creates a new symbolic link on the filesystem. The `dst` path will be a -/// symlink pointing to the `src` path. -pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { - fs_imp::symlink(src, dst) - .update_err("couldn't symlink path", |e| - format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display())) -} - -/// Reads a symlink, returning the file that the symlink points to. -/// -/// # Error -/// -/// This function will return an error on failure. Failure conditions include -/// reading a file that does not exist or reading a file that is not a symlink. -pub fn readlink(path: &Path) -> IoResult { - fs_imp::readlink(path) - .update_err("couldn't resolve symlink for path", |e| - format!("{}; path={}", e, path.display())) -} - -/// Create a new, empty directory at the provided path -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io; -/// use std::io::fs; -/// -/// let p = Path::new("/some/dir"); -/// fs::mkdir(&p, io::USER_RWX); -/// ``` -/// -/// # Error -/// -/// This function will return an error if the user lacks permissions to make a -/// new directory at the provided `path`, or if the directory already exists. -pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { - fs_imp::mkdir(path, mode.bits() as uint) - .update_err("couldn't create directory", |e| - format!("{}; path={}; mode={}", e, path.display(), mode)) -} - -/// Remove an existing, empty directory -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::fs; -/// -/// let p = Path::new("/some/dir"); -/// fs::rmdir(&p); -/// ``` -/// -/// # Error -/// -/// This function will return an error if the user lacks permissions to remove -/// the directory at the provided `path`, or if the directory isn't empty. -pub fn rmdir(path: &Path) -> IoResult<()> { - fs_imp::rmdir(path) - .update_err("couldn't remove directory", |e| - format!("{}; path={}", e, path.display())) -} - -/// Retrieve a vector containing all entries within a provided directory -/// -/// # Example -/// -/// ```rust -/// use std::io::fs::PathExtensions; -/// use std::io::fs; -/// use std::io; -/// -/// // one possible implementation of fs::walk_dir only visiting files -/// fn visit_dirs(dir: &Path, cb: &mut F) -> io::IoResult<()> where -/// F: FnMut(&Path), -/// { -/// if dir.is_dir() { -/// let contents = try!(fs::readdir(dir)); -/// for entry in contents.iter() { -/// if entry.is_dir() { -/// try!(visit_dirs(entry, cb)); -/// } else { -/// (*cb)(entry); -/// } -/// } -/// Ok(()) -/// } else { -/// Err(io::standard_error(io::InvalidInput)) -/// } -/// } -/// ``` -/// -/// # Error -/// -/// This function will return an error if the provided `path` doesn't exist, if -/// the process lacks permissions to view the contents or if the `path` points -/// at a non-directory file -pub fn readdir(path: &Path) -> IoResult> { - fs_imp::readdir(path) - .update_err("couldn't read directory", - |e| format!("{}; path={}", e, path.display())) -} - -/// Returns an iterator that will recursively walk the directory structure -/// rooted at `path`. The path given will not be iterated over, and this will -/// perform iteration in some top-down order. The contents of unreadable -/// subdirectories are ignored. -pub fn walk_dir(path: &Path) -> IoResult { - Ok(Directories { - stack: try!(readdir(path).update_err("couldn't walk directory", - |e| format!("{}; path={}", e, path.display()))) - }) -} - -/// An iterator that walks over a directory -#[derive(Clone)] -pub struct Directories { - stack: Vec, -} - -impl Iterator for Directories { - type Item = Path; - - fn next(&mut self) -> Option { - match self.stack.pop() { - Some(path) => { - if path.is_dir() { - match readdir(&path) { - Ok(dirs) => { self.stack.extend(dirs.into_iter()); } - Err(..) => {} - } - } - Some(path) - } - None => None - } - } -} - -/// Recursively create a directory and all of its parent components if they -/// are missing. -/// -/// # Error -/// -/// See `fs::mkdir`. -pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> { - // tjc: if directory exists but with different permissions, - // should we return false? - if path.is_dir() { - return Ok(()) - } - - let mut comps = path.components(); - let mut curpath = path.root_path().unwrap_or(Path::new(".")); - - for c in comps { - curpath.push(c); - - let result = mkdir(&curpath, mode) - .update_err("couldn't recursively mkdir", - |e| format!("{}; path={}", e, path.display())); - - match result { - Err(mkdir_err) => { - // already exists ? - if try!(stat(&curpath)).kind != FileType::Directory { - return Err(mkdir_err); - } - } - Ok(()) => () - } - } - - Ok(()) -} - -/// Removes a directory at this path, after removing all its contents. Use -/// carefully! -/// -/// # Error -/// -/// See `file::unlink` and `fs::readdir` -pub fn rmdir_recursive(path: &Path) -> IoResult<()> { - let mut rm_stack = Vec::new(); - rm_stack.push(path.clone()); - - fn rmdir_failed(err: &IoError, path: &Path) -> String { - format!("rmdir_recursive failed; path={}; cause={}", - path.display(), err) - } - - fn update_err(err: IoResult, path: &Path) -> IoResult { - err.update_err("couldn't recursively rmdir", - |e| rmdir_failed(e, path)) - } - - while !rm_stack.is_empty() { - let children = try!(readdir(rm_stack.last().unwrap()) - .update_detail(|e| rmdir_failed(e, path))); - - let mut has_child_dir = false; - - // delete all regular files in the way and push subdirs - // on the stack - for child in children.into_iter() { - // FIXME(#12795) we should use lstat in all cases - let child_type = match cfg!(windows) { - true => try!(update_err(stat(&child), path)), - false => try!(update_err(lstat(&child), path)) - }; - - if child_type.kind == FileType::Directory { - rm_stack.push(child); - has_child_dir = true; - } else { - // we can carry on safely if the file is already gone - // (eg: deleted by someone else since readdir) - match update_err(unlink(&child), path) { - Ok(()) => (), - Err(ref e) if e.kind == io::FileNotFound => (), - Err(e) => return Err(e) - } - } - } - - // if no subdir was found, let's pop and delete - if !has_child_dir { - let result = update_err(rmdir(&rm_stack.pop().unwrap()), path); - match result { - Ok(()) => (), - Err(ref e) if e.kind == io::FileNotFound => (), - Err(e) => return Err(e) - } - } - } - - Ok(()) -} - -/// Changes the timestamps for a file's last modification and access time. -/// The file at the path specified will have its last access time set to -/// `atime` and its modification time set to `mtime`. The times specified should -/// be in milliseconds. -// FIXME(#10301) these arguments should not be u64 -pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> { - fs_imp::utime(path, atime, mtime) - .update_err("couldn't change_file_times", |e| - format!("{}; path={}", e, path.display())) -} - -impl Reader for File { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - fn update_err(result: IoResult, file: &File) -> IoResult { - result.update_err("couldn't read file", - |e| format!("{}; path={}", - e, file.path.display())) - } - - let result = update_err(self.fd.read(buf), self); - - match result { - Ok(read) => { - self.last_nread = read as int; - match read { - 0 => update_err(Err(standard_error(io::EndOfFile)), self), - _ => Ok(read as uint) - } - }, - Err(e) => Err(e) - } - } -} - -impl Writer for File { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.fd.write(buf) - .update_err("couldn't write to file", - |e| format!("{}; path={}", e, self.path.display())) - } -} - -impl Seek for File { - fn tell(&self) -> IoResult { - self.fd.tell() - .update_err("couldn't retrieve file cursor (`tell`)", - |e| format!("{}; path={}", e, self.path.display())) - } - - fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { - let err = match self.fd.seek(pos, style) { - Ok(_) => { - // successful seek resets EOF indicator - self.last_nread = -1; - Ok(()) - } - Err(e) => Err(e), - }; - err.update_err("couldn't seek in file", - |e| format!("{}; path={}", e, self.path.display())) - } -} - -/// Utility methods for paths. -pub trait PathExtensions { - /// Get information on the file, directory, etc at this path. - /// - /// Consult the `fs::stat` documentation for more info. - /// - /// This call preserves identical runtime/error semantics with `file::stat`. - fn stat(&self) -> IoResult; - - /// Get information on the file, directory, etc at this path, not following - /// symlinks. - /// - /// Consult the `fs::lstat` documentation for more info. - /// - /// This call preserves identical runtime/error semantics with `file::lstat`. - fn lstat(&self) -> IoResult; - - /// Boolean value indicator whether the underlying file exists on the local - /// filesystem. Returns false in exactly the cases where `fs::stat` fails. - fn exists(&self) -> bool; - - /// Whether the underlying implementation (be it a file path, or something - /// else) points at a "regular file" on the FS. Will return false for paths - /// to non-existent locations or directories or other non-regular files - /// (named pipes, etc). Follows links when making this determination. - fn is_file(&self) -> bool; - - /// Whether the underlying implementation (be it a file path, or something - /// else) is pointing at a directory in the underlying FS. Will return - /// false for paths to non-existent locations or if the item is not a - /// directory (eg files, named pipes, etc). Follows links when making this - /// determination. - fn is_dir(&self) -> bool; -} - -impl PathExtensions for path::Path { - fn stat(&self) -> IoResult { stat(self) } - fn lstat(&self) -> IoResult { lstat(self) } - fn exists(&self) -> bool { - self.stat().is_ok() - } - fn is_file(&self) -> bool { - match self.stat() { - Ok(s) => s.kind == FileType::RegularFile, - Err(..) => false - } - } - fn is_dir(&self) -> bool { - match self.stat() { - Ok(s) => s.kind == FileType::Directory, - Err(..) => false - } - } -} - -fn mode_string(mode: FileMode) -> &'static str { - match mode { - super::Open => "open", - super::Append => "append", - super::Truncate => "truncate" - } -} - -fn access_string(access: FileAccess) -> &'static str { - match access { - super::Read => "read", - super::Write => "write", - super::ReadWrite => "readwrite" - } -} - -#[cfg(test)] -#[allow(unused_imports)] -#[allow(unused_variables)] -#[allow(unused_mut)] -mod test { - use prelude::v1::*; - use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite, FileType}; - use io; - use str; - use io::fs::*; - - macro_rules! check { ($e:expr) => ( - match $e { - Ok(t) => t, - Err(e) => panic!("{} failed with: {:?}", stringify!($e), e), - } - ) } - - macro_rules! error { ($e:expr, $s:expr) => ( - match $e { - Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), - Err(ref err) => assert!(err.to_string().contains($s.as_slice()), - format!("`{}` did not contain `{}`", err, $s)) - } - ) } - - pub struct TempDir(Path); - - impl TempDir { - fn join(&self, path: &str) -> Path { - let TempDir(ref p) = *self; - p.join(path) - } - - fn path<'a>(&'a self) -> &'a Path { - let TempDir(ref p) = *self; - p - } - } - - impl Drop for TempDir { - fn drop(&mut self) { - // Gee, seeing how we're testing the fs module I sure hope that we - // at least implement this correctly! - let TempDir(ref p) = *self; - check!(io::fs::rmdir_recursive(p)); - } - } - - pub fn tmpdir() -> TempDir { - use os; - use rand; - let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); - check!(io::fs::mkdir(&ret, io::USER_RWX)); - TempDir(ret) - } - - #[test] - fn file_test_io_smoke_test() { - let message = "it's alright. have a good time"; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test.txt"); - { - let mut write_stream = File::open_mode(filename, Open, ReadWrite); - check!(write_stream.write(message.as_bytes())); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - let mut read_buf = [0; 1028]; - let read_str = match check!(read_stream.read(&mut read_buf)) { - -1|0 => panic!("shouldn't happen"), - n => str::from_utf8(&read_buf[..n]).unwrap().to_string() - }; - assert_eq!(read_str.as_slice(), message); - } - check!(unlink(filename)); - } - - #[test] - fn invalid_path_raises() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_that_does_not_exist.txt"); - let result = File::open_mode(filename, Open, Read); - - error!(result, "couldn't open path as file"); - if cfg!(unix) { - error!(result, "no such file or directory"); - } - error!(result, format!("path={}; mode=open; access=read", filename.display())); - } - - #[test] - fn file_test_iounlinking_invalid_path_should_raise_condition() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); - - let result = unlink(filename); - - error!(result, "couldn't unlink path"); - if cfg!(unix) { - error!(result, "no such file or directory"); - } - error!(result, format!("path={}", filename.display())); - } - - #[test] - fn file_test_io_non_positional_read() { - let message: &str = "ten-four"; - let mut read_mem = [0; 8]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - check!(rw_stream.write(message.as_bytes())); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - { - let read_buf = read_mem.slice_mut(0, 4); - check!(read_stream.read(read_buf)); - } - { - let read_buf = read_mem.slice_mut(4, 8); - check!(read_stream.read(read_buf)); - } - } - check!(unlink(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert_eq!(read_str, message); - } - - #[test] - fn file_test_io_seek_and_tell_smoke_test() { - let message = "ten-four"; - let mut read_mem = [0; 4]; - let set_cursor = 4 as u64; - let mut tell_pos_pre_read; - let mut tell_pos_post_read; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - check!(rw_stream.write(message.as_bytes())); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - check!(read_stream.seek(set_cursor as i64, SeekSet)); - tell_pos_pre_read = check!(read_stream.tell()); - check!(read_stream.read(&mut read_mem)); - tell_pos_post_read = check!(read_stream.tell()); - } - check!(unlink(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert_eq!(read_str, message.slice(4, 8)); - assert_eq!(tell_pos_pre_read, set_cursor); - assert_eq!(tell_pos_post_read, message.len() as u64); - } - - #[test] - fn file_test_io_seek_and_write() { - let initial_msg = "food-is-yummy"; - let overwrite_msg = "-the-bar!!"; - let final_msg = "foo-the-bar!!"; - let seek_idx = 3i; - let mut read_mem = [0; 13]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - check!(rw_stream.write(initial_msg.as_bytes())); - check!(rw_stream.seek(seek_idx as i64, SeekSet)); - check!(rw_stream.write(overwrite_msg.as_bytes())); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - check!(read_stream.read(&mut read_mem)); - } - check!(unlink(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert!(read_str == final_msg); - } - - #[test] - fn file_test_io_seek_shakedown() { - use str; // 01234567890123 - let initial_msg = "qwer-asdf-zxcv"; - let chunk_one: &str = "qwer"; - let chunk_two: &str = "asdf"; - let chunk_three: &str = "zxcv"; - let mut read_mem = [0; 4]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - check!(rw_stream.write(initial_msg.as_bytes())); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - - check!(read_stream.seek(-4, SeekEnd)); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); - - check!(read_stream.seek(-9, SeekCur)); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); - - check!(read_stream.seek(0, SeekSet)); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); - } - check!(unlink(filename)); - } - - #[test] - fn file_test_stat_is_correct_on_is_file() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); - { - let mut fs = check!(File::open_mode(filename, Open, ReadWrite)); - let msg = "hw"; - fs.write(msg.as_bytes()).unwrap(); - - let fstat_res = check!(fs.stat()); - assert_eq!(fstat_res.kind, FileType::RegularFile); - } - let stat_res_fn = check!(stat(filename)); - assert_eq!(stat_res_fn.kind, FileType::RegularFile); - let stat_res_meth = check!(filename.stat()); - assert_eq!(stat_res_meth.kind, FileType::RegularFile); - check!(unlink(filename)); - } - - #[test] - fn file_test_stat_is_correct_on_is_dir() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_stat_correct_on_is_dir"); - check!(mkdir(filename, io::USER_RWX)); - let stat_res_fn = check!(stat(filename)); - assert!(stat_res_fn.kind == FileType::Directory); - let stat_res_meth = check!(filename.stat()); - assert!(stat_res_meth.kind == FileType::Directory); - check!(rmdir(filename)); - } - - #[test] - fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("fileinfo_false_on_dir"); - check!(mkdir(dir, io::USER_RWX)); - assert!(dir.is_file() == false); - check!(rmdir(dir)); - } - - #[test] - fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - let tmpdir = tmpdir(); - let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); - check!(File::create(file).write(b"foo")); - assert!(file.exists()); - check!(unlink(file)); - assert!(!file.exists()); - } - - #[test] - fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("before_and_after_dir"); - assert!(!dir.exists()); - check!(mkdir(dir, io::USER_RWX)); - assert!(dir.exists()); - assert!(dir.is_dir()); - check!(rmdir(dir)); - assert!(!dir.exists()); - } - - #[test] - fn file_test_directoryinfo_readdir() { - use str; - let tmpdir = tmpdir(); - let dir = &tmpdir.join("di_readdir"); - check!(mkdir(dir, io::USER_RWX)); - let prefix = "foo"; - for n in range(0i,3) { - let f = dir.join(format!("{}.txt", n)); - let mut w = check!(File::create(&f)); - let msg_str = format!("{}{}", prefix, n.to_string()); - let msg = msg_str.as_bytes(); - check!(w.write(msg)); - } - let files = check!(readdir(dir)); - let mut mem = [0u8; 4]; - for f in files.iter() { - { - let n = f.filestem_str(); - check!(File::open(f).read(&mut mem)); - let read_str = str::from_utf8(&mem).unwrap(); - let expected = match n { - None|Some("") => panic!("really shouldn't happen.."), - Some(n) => format!("{}{}", prefix, n), - }; - assert_eq!(expected.as_slice(), read_str); - } - check!(unlink(f)); - } - check!(rmdir(dir)); - } - - #[test] - fn file_test_walk_dir() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("walk_dir"); - check!(mkdir(dir, io::USER_RWX)); - - let dir1 = &dir.join("01/02/03"); - check!(mkdir_recursive(dir1, io::USER_RWX)); - check!(File::create(&dir1.join("04"))); - - let dir2 = &dir.join("11/12/13"); - check!(mkdir_recursive(dir2, io::USER_RWX)); - check!(File::create(&dir2.join("14"))); - - let mut files = check!(walk_dir(dir)); - let mut cur = [0u8; 2]; - for f in files { - let stem = f.filestem_str().unwrap(); - let root = stem.as_bytes()[0] - b'0'; - let name = stem.as_bytes()[1] - b'0'; - assert!(cur[root as uint] < name); - cur[root as uint] = name; - } - - check!(rmdir_recursive(dir)); - } - - #[test] - fn mkdir_path_already_exists_error() { - use io::{IoError, PathAlreadyExists}; - - let tmpdir = tmpdir(); - let dir = &tmpdir.join("mkdir_error_twice"); - check!(mkdir(dir, io::USER_RWX)); - match mkdir(dir, io::USER_RWX) { - Err(IoError{kind:PathAlreadyExists,..}) => (), - _ => assert!(false) - }; - } - - #[test] - fn recursive_mkdir() { - let tmpdir = tmpdir(); - let dir = tmpdir.join("d1/d2"); - check!(mkdir_recursive(&dir, io::USER_RWX)); - assert!(dir.is_dir()) - } - - #[test] - fn recursive_mkdir_failure() { - let tmpdir = tmpdir(); - let dir = tmpdir.join("d1"); - let file = dir.join("f1"); - - check!(mkdir_recursive(&dir, io::USER_RWX)); - check!(File::create(&file)); - - let result = mkdir_recursive(&file, io::USER_RWX); - - error!(result, "couldn't recursively mkdir"); - error!(result, "couldn't create directory"); - error!(result, "mode=0700"); - error!(result, format!("path={}", file.display())); - } - - #[test] - fn recursive_mkdir_slash() { - check!(mkdir_recursive(&Path::new("/"), io::USER_RWX)); - } - - // FIXME(#12795) depends on lstat to work on windows - #[cfg(not(windows))] - #[test] - fn recursive_rmdir() { - let tmpdir = tmpdir(); - let d1 = tmpdir.join("d1"); - let dt = d1.join("t"); - let dtt = dt.join("t"); - let d2 = tmpdir.join("d2"); - let canary = d2.join("do_not_delete"); - check!(mkdir_recursive(&dtt, io::USER_RWX)); - check!(mkdir_recursive(&d2, io::USER_RWX)); - check!(File::create(&canary).write(b"foo")); - check!(symlink(&d2, &dt.join("d2"))); - check!(rmdir_recursive(&d1)); - - assert!(!d1.is_dir()); - assert!(canary.exists()); - } - - #[test] - fn unicode_path_is_dir() { - assert!(Path::new(".").is_dir()); - assert!(!Path::new("test/stdtest/fs.rs").is_dir()); - - let tmpdir = tmpdir(); - - let mut dirpath = tmpdir.path().clone(); - dirpath.push(format!("test-가一ー你好")); - check!(mkdir(&dirpath, io::USER_RWX)); - assert!(dirpath.is_dir()); - - let mut filepath = dirpath; - filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); - check!(File::create(&filepath)); // ignore return; touch only - assert!(!filepath.is_dir()); - assert!(filepath.exists()); - } - - #[test] - fn unicode_path_exists() { - assert!(Path::new(".").exists()); - assert!(!Path::new("test/nonexistent-bogus-path").exists()); - - let tmpdir = tmpdir(); - let unicode = tmpdir.path(); - let unicode = unicode.join(format!("test-각丁ー再见")); - check!(mkdir(&unicode, io::USER_RWX)); - assert!(unicode.exists()); - assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); - } - - #[test] - fn copy_file_does_not_exist() { - let from = Path::new("test/nonexistent-bogus-path"); - let to = Path::new("test/other-bogus-path"); - - error!(copy(&from, &to), - format!("couldn't copy path (the source path is not an \ - existing file; from={:?}; to={:?})", - from.display(), to.display())); - - match copy(&from, &to) { - Ok(..) => panic!(), - Err(..) => { - assert!(!from.exists()); - assert!(!to.exists()); - } - } - } - - #[test] - fn copy_file_ok() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(File::create(&input).write(b"hello")); - check!(copy(&input, &out)); - let contents = check!(File::open(&out).read_to_end()); - assert_eq!(contents.as_slice(), b"hello"); - - assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm); - } - - #[test] - fn copy_file_dst_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - check!(File::create(&out)); - match copy(&out, tmpdir.path()) { - Ok(..) => panic!(), Err(..) => {} - } - } - - #[test] - fn copy_file_dst_exists() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in"); - let output = tmpdir.join("out"); - - check!(File::create(&input).write("foo".as_bytes())); - check!(File::create(&output).write("bar".as_bytes())); - check!(copy(&input, &output)); - - assert_eq!(check!(File::open(&output).read_to_end()), - b"foo".to_vec()); - } - - #[test] - fn copy_file_src_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - match copy(tmpdir.path(), &out) { - Ok(..) => panic!(), Err(..) => {} - } - assert!(!out.exists()); - } - - #[test] - fn copy_file_preserves_perm_bits() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(File::create(&input)); - check!(chmod(&input, io::USER_READ)); - check!(copy(&input, &out)); - assert!(!check!(out.stat()).perm.intersects(io::USER_WRITE)); - - check!(chmod(&input, io::USER_FILE)); - check!(chmod(&out, io::USER_FILE)); - } - - #[cfg(not(windows))] // FIXME(#10264) operation not permitted? - #[test] - fn symlinks_work() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(File::create(&input).write("foobar".as_bytes())); - check!(symlink(&input, &out)); - if cfg!(not(windows)) { - assert_eq!(check!(lstat(&out)).kind, FileType::Symlink); - assert_eq!(check!(out.lstat()).kind, FileType::Symlink); - } - assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); - assert_eq!(check!(File::open(&out).read_to_end()), - b"foobar".to_vec()); - } - - #[cfg(not(windows))] // apparently windows doesn't like symlinks - #[test] - fn symlink_noexist() { - let tmpdir = tmpdir(); - // symlinks can point to things that don't exist - check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar"))); - assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo")); - } - - #[test] - fn readlink_not_symlink() { - let tmpdir = tmpdir(); - match readlink(tmpdir.path()) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - } - - #[test] - fn links_work() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(File::create(&input).write("foobar".as_bytes())); - check!(link(&input, &out)); - if cfg!(not(windows)) { - assert_eq!(check!(lstat(&out)).kind, FileType::RegularFile); - assert_eq!(check!(out.lstat()).kind, FileType::RegularFile); - assert_eq!(check!(stat(&out)).unstable.nlink, 2); - assert_eq!(check!(out.stat()).unstable.nlink, 2); - } - assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); - assert_eq!(check!(stat(&out)).size, check!(input.stat()).size); - assert_eq!(check!(File::open(&out).read_to_end()), - b"foobar".to_vec()); - - // can't link to yourself - match link(&input, &input) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - // can't link to something that doesn't exist - match link(&tmpdir.join("foo"), &tmpdir.join("bar")) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - } - - #[test] - fn chmod_works() { - let tmpdir = tmpdir(); - let file = tmpdir.join("in.txt"); - - check!(File::create(&file)); - assert!(check!(stat(&file)).perm.contains(io::USER_WRITE)); - check!(chmod(&file, io::USER_READ)); - assert!(!check!(stat(&file)).perm.contains(io::USER_WRITE)); - - match chmod(&tmpdir.join("foo"), io::USER_RWX) { - Ok(..) => panic!("wanted a panic"), - Err(..) => {} - } - - check!(chmod(&file, io::USER_FILE)); - } - - #[test] - fn sync_doesnt_kill_anything() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite)); - check!(file.fsync()); - check!(file.datasync()); - check!(file.write(b"foo")); - check!(file.fsync()); - check!(file.datasync()); - drop(file); - } - - #[test] - fn truncate_works() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite)); - check!(file.write(b"foo")); - check!(file.fsync()); - - // Do some simple things with truncation - assert_eq!(check!(file.stat()).size, 3); - check!(file.truncate(10)); - assert_eq!(check!(file.stat()).size, 10); - check!(file.write(b"bar")); - check!(file.fsync()); - assert_eq!(check!(file.stat()).size, 10); - assert_eq!(check!(File::open(&path).read_to_end()), - b"foobar\0\0\0\0".to_vec()); - - // Truncate to a smaller length, don't seek, and then write something. - // Ensure that the intermediate zeroes are all filled in (we're seeked - // past the end of the file). - check!(file.truncate(2)); - assert_eq!(check!(file.stat()).size, 2); - check!(file.write(b"wut")); - check!(file.fsync()); - assert_eq!(check!(file.stat()).size, 9); - assert_eq!(check!(File::open(&path).read_to_end()), - b"fo\0\0\0\0wut".to_vec()); - drop(file); - } - - #[test] - fn open_flavors() { - let tmpdir = tmpdir(); - - match File::open_mode(&tmpdir.join("a"), io::Open, io::Read) { - Ok(..) => panic!(), Err(..) => {} - } - - // Perform each one twice to make sure that it succeeds the second time - // (where the file exists) - check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write)); - assert!(tmpdir.join("b").exists()); - check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write)); - - check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite)); - assert!(tmpdir.join("c").exists()); - check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite)); - - check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write)); - assert!(tmpdir.join("d").exists()); - check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write)); - - check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite)); - assert!(tmpdir.join("e").exists()); - check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite)); - - check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write)); - assert!(tmpdir.join("f").exists()); - check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write)); - - check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite)); - assert!(tmpdir.join("g").exists()); - check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite)); - - check!(File::create(&tmpdir.join("h")).write("foo".as_bytes())); - check!(File::open_mode(&tmpdir.join("h"), io::Open, io::Read)); - { - let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Open, - io::Read)); - match f.write("wut".as_bytes()) { - Ok(..) => panic!(), Err(..) => {} - } - } - assert!(check!(stat(&tmpdir.join("h"))).size == 3, - "write/stat failed"); - { - let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Append, - io::Write)); - check!(f.write("bar".as_bytes())); - } - assert!(check!(stat(&tmpdir.join("h"))).size == 6, - "append didn't append"); - { - let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Truncate, - io::Write)); - check!(f.write("bar".as_bytes())); - } - assert!(check!(stat(&tmpdir.join("h"))).size == 3, - "truncate didn't truncate"); - } - - #[test] - fn utime() { - let tmpdir = tmpdir(); - let path = tmpdir.join("a"); - check!(File::create(&path)); - // These numbers have to be bigger than the time in the day to account for timezones - // Windows in particular will fail in certain timezones with small enough values - check!(change_file_times(&path, 100000, 200000)); - assert_eq!(check!(path.stat()).accessed, 100000); - assert_eq!(check!(path.stat()).modified, 200000); - } - - #[test] - fn utime_noexist() { - let tmpdir = tmpdir(); - - match change_file_times(&tmpdir.join("a"), 100, 200) { - Ok(..) => panic!(), - Err(..) => {} - } - } - - #[test] - fn binary_file() { - use rand::{StdRng, Rng}; - - let mut bytes = [0; 1024]; - StdRng::new().ok().unwrap().fill_bytes(&mut bytes); - - let tmpdir = tmpdir(); - - check!(File::create(&tmpdir.join("test")).write(&bytes)); - let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); - assert!(actual == bytes.as_slice()); - } - - #[test] - fn unlink_readonly() { - let tmpdir = tmpdir(); - let path = tmpdir.join("file"); - check!(File::create(&path)); - check!(chmod(&path, io::USER_READ)); - check!(unlink(&path)); - } -} diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs deleted file mode 100644 index ec4191297ce..00000000000 --- a/src/libstd/io/mem.rs +++ /dev/null @@ -1,757 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// ignore-lexer-test FIXME #15679 - -//! Readers and Writers for in-memory buffers - -use cmp::min; -use option::Option::None; -use result::Result::{Err, Ok}; -use io; -use io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult}; -use slice::{self, AsSlice, SliceExt}; -use vec::Vec; - -const BUF_CAPACITY: uint = 128; - -fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult { - // compute offset as signed and clamp to prevent overflow - let pos = match seek { - io::SeekSet => 0, - io::SeekEnd => end, - io::SeekCur => cur, - } as i64; - - if offset + pos < 0 { - Err(IoError { - kind: io::InvalidInput, - desc: "invalid seek to a negative offset", - detail: None - }) - } else { - Ok((offset + pos) as u64) - } -} - -impl Writer for Vec { - #[inline] - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.push_all(buf); - Ok(()) - } -} - -/// Writes to an owned, growable byte vector -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::MemWriter; -/// -/// let mut w = MemWriter::new(); -/// w.write(&[0, 1, 2]); -/// -/// assert_eq!(w.into_inner(), vec!(0, 1, 2)); -/// ``` -#[deprecated = "use the Vec Writer implementation directly"] -#[derive(Clone)] -pub struct MemWriter { - buf: Vec, -} - -#[allow(deprecated)] -impl MemWriter { - /// Create a new `MemWriter`. - #[inline] - pub fn new() -> MemWriter { - MemWriter::with_capacity(BUF_CAPACITY) - } - /// Create a new `MemWriter`, allocating at least `n` bytes for - /// the internal buffer. - #[inline] - pub fn with_capacity(n: uint) -> MemWriter { - MemWriter::from_vec(Vec::with_capacity(n)) - } - /// Create a new `MemWriter` that will append to an existing `Vec`. - #[inline] - pub fn from_vec(buf: Vec) -> MemWriter { - MemWriter { buf: buf } - } - - /// Acquires an immutable reference to the underlying buffer of this - /// `MemWriter`. - #[inline] - pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() } - - /// Unwraps this `MemWriter`, returning the underlying buffer - #[inline] - pub fn into_inner(self) -> Vec { self.buf } -} - -impl Writer for MemWriter { - #[inline] - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.buf.push_all(buf); - Ok(()) - } -} - -/// Reads from an owned byte vector -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::MemReader; -/// -/// let mut r = MemReader::new(vec!(0, 1, 2)); -/// -/// assert_eq!(r.read_to_end().unwrap(), vec!(0, 1, 2)); -/// ``` -pub struct MemReader { - buf: Vec, - pos: uint -} - -impl MemReader { - /// Creates a new `MemReader` which will read the buffer given. The buffer - /// can be re-acquired through `unwrap` - #[inline] - pub fn new(buf: Vec) -> MemReader { - MemReader { - buf: buf, - pos: 0 - } - } - - /// Tests whether this reader has read all bytes in its buffer. - /// - /// If `true`, then this will no longer return bytes from `read`. - #[inline] - pub fn eof(&self) -> bool { self.pos >= self.buf.len() } - - /// Acquires an immutable reference to the underlying buffer of this - /// `MemReader`. - /// - /// No method is exposed for acquiring a mutable reference to the buffer - /// because it could corrupt the state of this `MemReader`. - #[inline] - pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() } - - /// Unwraps this `MemReader`, returning the underlying buffer - #[inline] - pub fn into_inner(self) -> Vec { self.buf } -} - -impl Reader for MemReader { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> IoResult { - if self.eof() { return Err(io::standard_error(io::EndOfFile)) } - - let write_len = min(buf.len(), self.buf.len() - self.pos); - { - let input = &self.buf[self.pos.. self.pos + write_len]; - let output = &mut buf[..write_len]; - assert_eq!(input.len(), output.len()); - slice::bytes::copy_memory(output, input); - } - self.pos += write_len; - assert!(self.pos <= self.buf.len()); - - return Ok(write_len); - } -} - -impl Seek for MemReader { - #[inline] - fn tell(&self) -> IoResult { Ok(self.pos as u64) } - - #[inline] - fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { - let new = try!(combine(style, self.pos, self.buf.len(), pos)); - self.pos = new as uint; - Ok(()) - } -} - -impl Buffer for MemReader { - #[inline] - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { - if self.pos < self.buf.len() { - Ok(&self.buf[self.pos..]) - } else { - Err(io::standard_error(io::EndOfFile)) - } - } - - #[inline] - fn consume(&mut self, amt: uint) { self.pos += amt; } -} - -impl<'a> Reader for &'a [u8] { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> IoResult { - if self.is_empty() { return Err(io::standard_error(io::EndOfFile)); } - - let write_len = min(buf.len(), self.len()); - { - let input = &self[..write_len]; - let output = &mut buf[.. write_len]; - slice::bytes::copy_memory(output, input); - } - - *self = &self[write_len..]; - - Ok(write_len) - } -} - -impl<'a> Buffer for &'a [u8] { - #[inline] - fn fill_buf(&mut self) -> IoResult<&[u8]> { - if self.is_empty() { - Err(io::standard_error(io::EndOfFile)) - } else { - Ok(*self) - } - } - - #[inline] - fn consume(&mut self, amt: uint) { - *self = &self[amt..]; - } -} - - -/// Writes to a fixed-size byte slice -/// -/// If a write will not fit in the buffer, it returns an error and does not -/// write any data. -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::BufWriter; -/// -/// let mut buf = [0; 4]; -/// { -/// let mut w = BufWriter::new(&mut buf); -/// w.write(&[0, 1, 2]); -/// } -/// assert!(buf == [0, 1, 2, 0]); -/// ``` -pub struct BufWriter<'a> { - buf: &'a mut [u8], - pos: uint -} - -impl<'a> BufWriter<'a> { - /// Creates a new `BufWriter` which will wrap the specified buffer. The - /// writer initially starts at position 0. - #[inline] - pub fn new(buf: &'a mut [u8]) -> BufWriter<'a> { - BufWriter { - buf: buf, - pos: 0 - } - } -} - -impl<'a> Writer for BufWriter<'a> { - #[inline] - fn write(&mut self, src: &[u8]) -> IoResult<()> { - let dst = &mut self.buf[self.pos..]; - let dst_len = dst.len(); - - if dst_len == 0 { - return Err(io::standard_error(io::EndOfFile)); - } - - let src_len = src.len(); - - if dst_len >= src_len { - slice::bytes::copy_memory(dst, src); - - self.pos += src_len; - - Ok(()) - } else { - slice::bytes::copy_memory(dst, &src[..dst_len]); - - self.pos += dst_len; - - Err(io::standard_error(io::ShortWrite(dst_len))) - } - } -} - -impl<'a> Seek for BufWriter<'a> { - #[inline] - fn tell(&self) -> IoResult { Ok(self.pos as u64) } - - #[inline] - fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { - let new = try!(combine(style, self.pos, self.buf.len(), pos)); - self.pos = min(new as uint, self.buf.len()); - Ok(()) - } -} - -/// Reads from a fixed-size byte slice -/// -/// # Example -/// -/// ```rust -/// # #![allow(unused_must_use)] -/// use std::io::BufReader; -/// -/// let buf = [0, 1, 2, 3]; -/// let mut r = BufReader::new(&buf); -/// -/// assert_eq!(r.read_to_end().unwrap(), vec![0, 1, 2, 3]); -/// ``` -pub struct BufReader<'a> { - buf: &'a [u8], - pos: uint -} - -impl<'a> BufReader<'a> { - /// Creates a new buffered reader which will read the specified buffer - #[inline] - pub fn new(buf: &'a [u8]) -> BufReader<'a> { - BufReader { - buf: buf, - pos: 0 - } - } - - /// Tests whether this reader has read all bytes in its buffer. - /// - /// If `true`, then this will no longer return bytes from `read`. - #[inline] - pub fn eof(&self) -> bool { self.pos >= self.buf.len() } -} - -impl<'a> Reader for BufReader<'a> { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> IoResult { - if self.eof() { return Err(io::standard_error(io::EndOfFile)) } - - let write_len = min(buf.len(), self.buf.len() - self.pos); - { - let input = &self.buf[self.pos.. self.pos + write_len]; - let output = &mut buf[..write_len]; - assert_eq!(input.len(), output.len()); - slice::bytes::copy_memory(output, input); - } - self.pos += write_len; - assert!(self.pos <= self.buf.len()); - - return Ok(write_len); - } -} - -impl<'a> Seek for BufReader<'a> { - #[inline] - fn tell(&self) -> IoResult { Ok(self.pos as u64) } - - #[inline] - fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { - let new = try!(combine(style, self.pos, self.buf.len(), pos)); - self.pos = new as uint; - Ok(()) - } -} - -impl<'a> Buffer for BufReader<'a> { - #[inline] - fn fill_buf(&mut self) -> IoResult<&[u8]> { - if self.pos < self.buf.len() { - Ok(&self.buf[self.pos..]) - } else { - Err(io::standard_error(io::EndOfFile)) - } - } - - #[inline] - fn consume(&mut self, amt: uint) { self.pos += amt; } -} - -#[cfg(test)] -mod test { - extern crate "test" as test_crate; - use io::{SeekSet, SeekCur, SeekEnd, Reader, Writer, Seek}; - use prelude::v1::{Ok, Err, range, Vec, Buffer, AsSlice, SliceExt}; - use prelude::v1::IteratorExt; - use io; - use iter::repeat; - use self::test_crate::Bencher; - use super::*; - - #[test] - fn test_vec_writer() { - let mut writer = Vec::new(); - writer.write(&[0]).unwrap(); - writer.write(&[1, 2, 3]).unwrap(); - writer.write(&[4, 5, 6, 7]).unwrap(); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - assert_eq!(writer.as_slice(), b); - } - - #[test] - fn test_mem_writer() { - let mut writer = MemWriter::new(); - writer.write(&[0]).unwrap(); - writer.write(&[1, 2, 3]).unwrap(); - writer.write(&[4, 5, 6, 7]).unwrap(); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - assert_eq!(writer.get_ref(), b); - } - - #[test] - fn test_buf_writer() { - let mut buf = [0 as u8; 9]; - { - let mut writer = BufWriter::new(&mut buf); - assert_eq!(writer.tell(), Ok(0)); - writer.write(&[0]).unwrap(); - assert_eq!(writer.tell(), Ok(1)); - writer.write(&[1, 2, 3]).unwrap(); - writer.write(&[4, 5, 6, 7]).unwrap(); - assert_eq!(writer.tell(), Ok(8)); - writer.write(&[]).unwrap(); - assert_eq!(writer.tell(), Ok(8)); - - assert_eq!(writer.write(&[8, 9]).err().unwrap().kind, io::ShortWrite(1)); - assert_eq!(writer.write(&[10]).err().unwrap().kind, io::EndOfFile); - } - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(buf, b); - } - - #[test] - fn test_buf_writer_seek() { - let mut buf = [0 as u8; 8]; - { - let mut writer = BufWriter::new(&mut buf); - assert_eq!(writer.tell(), Ok(0)); - writer.write(&[1]).unwrap(); - assert_eq!(writer.tell(), Ok(1)); - - writer.seek(2, SeekSet).unwrap(); - assert_eq!(writer.tell(), Ok(2)); - writer.write(&[2]).unwrap(); - assert_eq!(writer.tell(), Ok(3)); - - writer.seek(-2, SeekCur).unwrap(); - assert_eq!(writer.tell(), Ok(1)); - writer.write(&[3]).unwrap(); - assert_eq!(writer.tell(), Ok(2)); - - writer.seek(-1, SeekEnd).unwrap(); - assert_eq!(writer.tell(), Ok(7)); - writer.write(&[4]).unwrap(); - assert_eq!(writer.tell(), Ok(8)); - - } - let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; - assert_eq!(buf, b); - } - - #[test] - fn test_buf_writer_error() { - let mut buf = [0 as u8; 2]; - let mut writer = BufWriter::new(&mut buf); - writer.write(&[0]).unwrap(); - - match writer.write(&[0, 0]) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::ShortWrite(1)), - } - } - - #[test] - fn test_mem_reader() { - let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); - let mut buf = []; - assert_eq!(reader.read(&mut buf), Ok(0)); - assert_eq!(reader.tell(), Ok(0)); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf), Ok(1)); - assert_eq!(reader.tell(), Ok(1)); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf), Ok(4)); - assert_eq!(reader.tell(), Ok(5)); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf), Ok(3)); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert!(reader.read(&mut buf).is_err()); - let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); - assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); - assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); - assert!(reader.read(&mut buf).is_err()); - } - - #[test] - fn test_slice_reader() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let mut reader = &mut in_buf.as_slice(); - let mut buf = []; - assert_eq!(reader.read(&mut buf), Ok(0)); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf), Ok(1)); - assert_eq!(reader.len(), 7); - let b: &[_] = &[0]; - assert_eq!(buf.as_slice(), b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf), Ok(4)); - assert_eq!(reader.len(), 3); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf.as_slice(), b); - assert_eq!(reader.read(&mut buf), Ok(3)); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert!(reader.read(&mut buf).is_err()); - let mut reader = &mut in_buf.as_slice(); - assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); - assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); - assert!(reader.read(&mut buf).is_err()); - } - - #[test] - fn test_buf_reader() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let mut reader = BufReader::new(in_buf.as_slice()); - let mut buf = []; - assert_eq!(reader.read(&mut buf), Ok(0)); - assert_eq!(reader.tell(), Ok(0)); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf), Ok(1)); - assert_eq!(reader.tell(), Ok(1)); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf), Ok(4)); - assert_eq!(reader.tell(), Ok(5)); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf), Ok(3)); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert!(reader.read(&mut buf).is_err()); - let mut reader = BufReader::new(in_buf.as_slice()); - assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); - assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); - assert!(reader.read(&mut buf).is_err()); - } - - #[test] - fn test_read_char() { - let b = b"Vi\xE1\xBB\x87t"; - let mut r = BufReader::new(b); - assert_eq!(r.read_char(), Ok('V')); - assert_eq!(r.read_char(), Ok('i')); - assert_eq!(r.read_char(), Ok('ệ')); - assert_eq!(r.read_char(), Ok('t')); - assert!(r.read_char().is_err()); - } - - #[test] - fn test_read_bad_char() { - let b = b"\x80"; - let mut r = BufReader::new(b); - assert!(r.read_char().is_err()); - } - - #[test] - fn test_write_strings() { - let mut writer = MemWriter::new(); - writer.write_str("testing").unwrap(); - writer.write_line("testing").unwrap(); - writer.write_str("testing").unwrap(); - let mut r = BufReader::new(writer.get_ref()); - assert_eq!(r.read_to_string().unwrap(), "testingtesting\ntesting"); - } - - #[test] - fn test_write_char() { - let mut writer = MemWriter::new(); - writer.write_char('a').unwrap(); - writer.write_char('\n').unwrap(); - writer.write_char('ệ').unwrap(); - let mut r = BufReader::new(writer.get_ref()); - assert_eq!(r.read_to_string().unwrap(), "a\nệ"); - } - - #[test] - fn test_read_whole_string_bad() { - let buf = [0xff]; - let mut r = BufReader::new(&buf); - match r.read_to_string() { - Ok(..) => panic!(), - Err(..) => {} - } - } - - #[test] - fn seek_past_end() { - let buf = [0xff]; - let mut r = BufReader::new(&buf); - r.seek(10, SeekSet).unwrap(); - assert!(r.read(&mut []).is_err()); - - let mut r = MemReader::new(vec!(10)); - r.seek(10, SeekSet).unwrap(); - assert!(r.read(&mut []).is_err()); - - let mut buf = [0]; - let mut r = BufWriter::new(&mut buf); - r.seek(10, SeekSet).unwrap(); - assert!(r.write(&[3]).is_err()); - } - - #[test] - fn seek_before_0() { - let buf = [0xff]; - let mut r = BufReader::new(&buf); - assert!(r.seek(-1, SeekSet).is_err()); - - let mut r = MemReader::new(vec!(10)); - assert!(r.seek(-1, SeekSet).is_err()); - - let mut buf = [0]; - let mut r = BufWriter::new(&mut buf); - assert!(r.seek(-1, SeekSet).is_err()); - } - - #[test] - fn io_read_at_least() { - let mut r = MemReader::new(vec![1, 2, 3, 4, 5, 6, 7, 8]); - let mut buf = [0; 3]; - assert!(r.read_at_least(buf.len(), &mut buf).is_ok()); - let b: &[_] = &[1, 2, 3]; - assert_eq!(buf, b); - assert!(r.read_at_least(0, buf.slice_to_mut(0)).is_ok()); - assert_eq!(buf, b); - assert!(r.read_at_least(buf.len(), &mut buf).is_ok()); - let b: &[_] = &[4, 5, 6]; - assert_eq!(buf, b); - assert!(r.read_at_least(buf.len(), &mut buf).is_err()); - let b: &[_] = &[7, 8, 6]; - assert_eq!(buf, b); - } - - fn do_bench_mem_writer(b: &mut Bencher, times: uint, len: uint) { - let src: Vec = repeat(5).take(len).collect(); - - b.bytes = (times * len) as u64; - b.iter(|| { - let mut wr = MemWriter::new(); - for _ in range(0, times) { - wr.write(src.as_slice()).unwrap(); - } - - let v = wr.into_inner(); - assert_eq!(v.len(), times * len); - assert!(v.iter().all(|x| *x == 5)); - }); - } - - #[bench] - fn bench_mem_writer_001_0000(b: &mut Bencher) { - do_bench_mem_writer(b, 1, 0) - } - - #[bench] - fn bench_mem_writer_001_0010(b: &mut Bencher) { - do_bench_mem_writer(b, 1, 10) - } - - #[bench] - fn bench_mem_writer_001_0100(b: &mut Bencher) { - do_bench_mem_writer(b, 1, 100) - } - - #[bench] - fn bench_mem_writer_001_1000(b: &mut Bencher) { - do_bench_mem_writer(b, 1, 1000) - } - - #[bench] - fn bench_mem_writer_100_0000(b: &mut Bencher) { - do_bench_mem_writer(b, 100, 0) - } - - #[bench] - fn bench_mem_writer_100_0010(b: &mut Bencher) { - do_bench_mem_writer(b, 100, 10) - } - - #[bench] - fn bench_mem_writer_100_0100(b: &mut Bencher) { - do_bench_mem_writer(b, 100, 100) - } - - #[bench] - fn bench_mem_writer_100_1000(b: &mut Bencher) { - do_bench_mem_writer(b, 100, 1000) - } - - #[bench] - fn bench_mem_reader(b: &mut Bencher) { - b.iter(|| { - let buf = [5 as u8; 100].to_vec(); - { - let mut rdr = MemReader::new(buf); - for _i in range(0u, 10) { - let mut buf = [0 as u8; 10]; - rdr.read(&mut buf).unwrap(); - assert_eq!(buf.as_slice(), [5; 10].as_slice()); - } - } - }); - } - - #[bench] - fn bench_buf_writer(b: &mut Bencher) { - b.iter(|| { - let mut buf = [0 as u8; 100]; - { - let mut wr = BufWriter::new(&mut buf); - for _i in range(0u, 10) { - wr.write(&[5; 10]).unwrap(); - } - } - assert_eq!(buf.as_slice(), [5; 100].as_slice()); - }); - } - - #[bench] - fn bench_buf_reader(b: &mut Bencher) { - b.iter(|| { - let buf = [5 as u8; 100]; - { - let mut rdr = BufReader::new(&buf); - for _i in range(0u, 10) { - let mut buf = [0 as u8; 10]; - rdr.read(&mut buf).unwrap(); - assert_eq!(buf, [5; 10]); - } - } - }); - } -} diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs deleted file mode 100644 index f106e9464c5..00000000000 --- a/src/libstd/io/mod.rs +++ /dev/null @@ -1,1939 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// ignore-lexer-test FIXME #15883 - -// FIXME: cover these topics: -// path, reader, writer, stream, raii (close not needed), -// stdio, print!, println!, file access, process spawning, -// error handling - - -//! I/O, including files, networking, timers, and processes -//! -//! `std::io` provides Rust's basic I/O types, -//! for reading and writing to files, TCP, UDP, -//! and other types of sockets and pipes, -//! manipulating the file system, spawning processes. -//! -//! # Examples -//! -//! Some examples of obvious things you might want to do -//! -//! * Read lines from stdin -//! -//! ```rust -//! use std::io; -//! -//! for line in io::stdin().lock().lines() { -//! print!("{}", line.unwrap()); -//! } -//! ``` -//! -//! * Read a complete file -//! -//! ```rust -//! use std::io::File; -//! -//! let contents = File::open(&Path::new("message.txt")).read_to_end(); -//! ``` -//! -//! * Write a line to a file -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::io::File; -//! -//! let mut file = File::create(&Path::new("message.txt")); -//! file.write(b"hello, file!\n"); -//! # drop(file); -//! # ::std::io::fs::unlink(&Path::new("message.txt")); -//! ``` -//! -//! * Iterate over the lines of a file -//! -//! ```rust,no_run -//! use std::io::BufferedReader; -//! use std::io::File; -//! -//! let path = Path::new("message.txt"); -//! let mut file = BufferedReader::new(File::open(&path)); -//! for line in file.lines() { -//! print!("{}", line.unwrap()); -//! } -//! ``` -//! -//! * Pull the lines of a file into a vector of strings -//! -//! ```rust,no_run -//! use std::io::BufferedReader; -//! use std::io::File; -//! -//! let path = Path::new("message.txt"); -//! let mut file = BufferedReader::new(File::open(&path)); -//! let lines: Vec = file.lines().map(|x| x.unwrap()).collect(); -//! ``` -//! -//! * Make a simple TCP client connection and request -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::io::TcpStream; -//! -//! # // connection doesn't fail if a server is running on 8080 -//! # // locally, we still want to be type checking this code, so lets -//! # // just stop it running (#11576) -//! # if false { -//! let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap(); -//! socket.write(b"GET / HTTP/1.0\n\n"); -//! let response = socket.read_to_end(); -//! # } -//! ``` -//! -//! * Make a simple TCP server -//! -//! ```rust -//! # fn main() { } -//! # fn foo() { -//! # #![allow(dead_code)] -//! use std::io::{TcpListener, TcpStream}; -//! use std::io::{Acceptor, Listener}; -//! use std::thread::Thread; -//! -//! let listener = TcpListener::bind("127.0.0.1:80"); -//! -//! // bind the listener to the specified address -//! let mut acceptor = listener.listen(); -//! -//! fn handle_client(mut stream: TcpStream) { -//! // ... -//! # &mut stream; // silence unused mutability/variable warning -//! } -//! // accept connections and process them, spawning a new tasks for each one -//! for stream in acceptor.incoming() { -//! match stream { -//! Err(e) => { /* connection failed */ } -//! Ok(stream) => { -//! Thread::spawn(move|| { -//! // connection succeeded -//! handle_client(stream) -//! }); -//! } -//! } -//! } -//! -//! // close the socket server -//! drop(acceptor); -//! # } -//! ``` -//! -//! -//! # Error Handling -//! -//! I/O is an area where nearly every operation can result in unexpected -//! errors. Errors should be painfully visible when they happen, and handling them -//! should be easy to work with. It should be convenient to handle specific I/O -//! errors, and it should also be convenient to not deal with I/O errors. -//! -//! Rust's I/O employs a combination of techniques to reduce boilerplate -//! while still providing feedback about errors. The basic strategy: -//! -//! * All I/O operations return `IoResult` which is equivalent to -//! `Result`. The `Result` type is defined in the `std::result` -//! module. -//! * If the `Result` type goes unused, then the compiler will by default emit a -//! warning about the unused result. This is because `Result` has the -//! `#[must_use]` attribute. -//! * Common traits are implemented for `IoResult`, e.g. -//! `impl Reader for IoResult`, so that error values do not have -//! to be 'unwrapped' before use. -//! -//! These features combine in the API to allow for expressions like -//! `File::create(&Path::new("diary.txt")).write(b"Met a girl.\n")` -//! without having to worry about whether "diary.txt" exists or whether -//! the write succeeds. As written, if either `new` or `write_line` -//! encounters an error then the result of the entire expression will -//! be an error. -//! -//! If you wanted to handle the error though you might write: -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::io::File; -//! -//! match File::create(&Path::new("diary.txt")).write(b"Met a girl.\n") { -//! Ok(()) => (), // succeeded -//! Err(e) => println!("failed to write to my diary: {}", e), -//! } -//! -//! # ::std::io::fs::unlink(&Path::new("diary.txt")); -//! ``` -//! -//! So what actually happens if `create` encounters an error? -//! It's important to know that what `new` returns is not a `File` -//! but an `IoResult`. If the file does not open, then `new` will simply -//! return `Err(..)`. Because there is an implementation of `Writer` (the trait -//! required ultimately required for types to implement `write_line`) there is no -//! need to inspect or unwrap the `IoResult` and we simply call `write_line` -//! on it. If `new` returned an `Err(..)` then the followup call to `write_line` -//! will also return an error. -//! -//! ## `try!` -//! -//! Explicit pattern matching on `IoResult`s can get quite verbose, especially -//! when performing many I/O operations. Some examples (like those above) are -//! alleviated with extra methods implemented on `IoResult`, but others have more -//! complex interdependencies among each I/O operation. -//! -//! The `try!` macro from `std::macros` is provided as a method of early-return -//! inside `Result`-returning functions. It expands to an early-return on `Err` -//! and otherwise unwraps the contained `Ok` value. -//! -//! If you wanted to read several `u32`s from a file and return their product: -//! -//! ```rust -//! use std::io::{File, IoResult}; -//! -//! fn file_product(p: &Path) -> IoResult { -//! let mut f = File::open(p); -//! let x1 = try!(f.read_le_u32()); -//! let x2 = try!(f.read_le_u32()); -//! -//! Ok(x1 * x2) -//! } -//! -//! match file_product(&Path::new("numbers.bin")) { -//! Ok(x) => println!("{}", x), -//! Err(e) => println!("Failed to read numbers!") -//! } -//! ``` -//! -//! With `try!` in `file_product`, each `read_le_u32` need not be directly -//! concerned with error handling; instead its caller is responsible for -//! responding to errors that may occur while attempting to read the numbers. - -#![unstable] -#![deny(unused_must_use)] - -pub use self::SeekStyle::*; -pub use self::FileMode::*; -pub use self::FileAccess::*; -pub use self::IoErrorKind::*; - -use char::CharExt; -use default::Default; -use error::Error; -use fmt; -use int; -use iter::{Iterator, IteratorExt}; -use marker::Sized; -use mem::transmute; -use ops::FnOnce; -use option::Option; -use option::Option::{Some, None}; -use os; -use boxed::Box; -use result::Result; -use result::Result::{Ok, Err}; -use sys; -use slice::SliceExt; -use str::StrExt; -use str; -use string::String; -use uint; -use unicode; -use vec::Vec; - -// Reexports -pub use self::stdio::stdin; -pub use self::stdio::stdout; -pub use self::stdio::stderr; -pub use self::stdio::print; -pub use self::stdio::println; - -pub use self::fs::File; -pub use self::timer::Timer; -pub use self::net::ip::IpAddr; -pub use self::net::tcp::TcpListener; -pub use self::net::tcp::TcpStream; -pub use self::pipe::PipeStream; -pub use self::process::{Process, Command}; -pub use self::tempfile::TempDir; - -pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter}; -pub use self::buffered::{BufferedReader, BufferedWriter, BufferedStream, - LineBufferedWriter}; -pub use self::comm_adapters::{ChanReader, ChanWriter}; - -mod buffered; -mod comm_adapters; -mod mem; -mod result; -mod tempfile; -pub mod extensions; -pub mod fs; -pub mod net; -pub mod pipe; -pub mod process; -pub mod stdio; -pub mod timer; -pub mod util; - -#[macro_use] -pub mod test; - -/// The default buffer size for various I/O operations -// libuv recommends 64k buffers to maximize throughput -// https://groups.google.com/forum/#!topic/libuv/oQO1HJAIDdA -const DEFAULT_BUF_SIZE: uint = 1024 * 64; - -/// A convenient typedef of the return value of any I/O action. -pub type IoResult = Result; - -/// The type passed to I/O condition handlers to indicate error -/// -/// # FIXME -/// -/// Is something like this sufficient? It's kind of archaic -#[derive(PartialEq, Eq, Clone, Show)] -pub struct IoError { - /// An enumeration which can be matched against for determining the flavor - /// of error. - pub kind: IoErrorKind, - /// A human-readable description about the error - pub desc: &'static str, - /// Detailed information about this error, not always available - pub detail: Option -} - -impl IoError { - /// Convert an `errno` value into an `IoError`. - /// - /// If `detail` is `true`, the `detail` field of the `IoError` - /// struct is filled with an allocated string describing the error - /// in more detail, retrieved from the operating system. - pub fn from_errno(errno: uint, detail: bool) -> IoError { - let mut err = sys::decode_error(errno as i32); - if detail && err.kind == OtherIoError { - err.detail = Some(os::error_string(errno).chars() - .map(|c| c.to_lowercase()).collect()) - } - err - } - - /// Retrieve the last error to occur as a (detailed) IoError. - /// - /// This uses the OS `errno`, and so there should not be any task - /// descheduling or migration (other than that performed by the - /// operating system) between the call(s) for which errors are - /// being checked and the call of this function. - pub fn last_error() -> IoError { - IoError::from_errno(os::errno() as uint, true) - } -} - -#[stable] -impl fmt::Display for IoError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - IoError { kind: OtherIoError, desc: "unknown error", detail: Some(ref detail) } => - write!(fmt, "{}", detail), - IoError { detail: None, desc, .. } => - write!(fmt, "{}", desc), - IoError { detail: Some(ref detail), desc, .. } => - write!(fmt, "{} ({})", desc, detail) - } - } -} - -impl Error for IoError { - fn description(&self) -> &str { self.desc } -} - -/// A list specifying general categories of I/O error. -#[derive(Copy, PartialEq, Eq, Clone, Show)] -pub enum IoErrorKind { - /// Any I/O error not part of this list. - OtherIoError, - /// The operation could not complete because end of file was reached. - EndOfFile, - /// The file was not found. - FileNotFound, - /// The file permissions disallowed access to this file. - PermissionDenied, - /// A network connection failed for some reason not specified in this list. - ConnectionFailed, - /// The network operation failed because the network connection was closed. - Closed, - /// The connection was refused by the remote server. - ConnectionRefused, - /// The connection was reset by the remote server. - ConnectionReset, - /// The connection was aborted (terminated) by the remote server. - ConnectionAborted, - /// The network operation failed because it was not connected yet. - NotConnected, - /// The operation failed because a pipe was closed. - BrokenPipe, - /// A file already existed with that name. - PathAlreadyExists, - /// No file exists at that location. - PathDoesntExist, - /// The path did not specify the type of file that this operation required. For example, - /// attempting to copy a directory with the `fs::copy()` operation will fail with this error. - MismatchedFileTypeForOperation, - /// The operation temporarily failed (for example, because a signal was received), and retrying - /// may succeed. - ResourceUnavailable, - /// No I/O functionality is available for this task. - IoUnavailable, - /// A parameter was incorrect in a way that caused an I/O error not part of this list. - InvalidInput, - /// The I/O operation's timeout expired, causing it to be canceled. - TimedOut, - /// This write operation failed to write all of its data. - /// - /// Normally the write() method on a Writer guarantees that all of its data - /// has been written, but some operations may be terminated after only - /// partially writing some data. An example of this is a timed out write - /// which successfully wrote a known number of bytes, but bailed out after - /// doing so. - /// - /// The payload contained as part of this variant is the number of bytes - /// which are known to have been successfully written. - ShortWrite(uint), - /// The Reader returned 0 bytes from `read()` too many times. - NoProgress, -} - -/// A trait that lets you add a `detail` to an IoError easily -trait UpdateIoError { - /// Returns an IoError with updated description and detail - fn update_err(self, desc: &'static str, detail: D) -> Self where - D: FnOnce(&IoError) -> String; - - /// Returns an IoError with updated detail - fn update_detail(self, detail: D) -> Self where - D: FnOnce(&IoError) -> String; - - /// Returns an IoError with update description - fn update_desc(self, desc: &'static str) -> Self; -} - -impl UpdateIoError for IoResult { - fn update_err(self, desc: &'static str, detail: D) -> IoResult where - D: FnOnce(&IoError) -> String, - { - self.map_err(move |mut e| { - let detail = detail(&e); - e.desc = desc; - e.detail = Some(detail); - e - }) - } - - fn update_detail(self, detail: D) -> IoResult where - D: FnOnce(&IoError) -> String, - { - self.map_err(move |mut e| { e.detail = Some(detail(&e)); e }) - } - - fn update_desc(self, desc: &'static str) -> IoResult { - self.map_err(|mut e| { e.desc = desc; e }) - } -} - -static NO_PROGRESS_LIMIT: uint = 1000; - -/// A trait for objects which are byte-oriented streams. Readers are defined by -/// one method, `read`. This function will block until data is available, -/// filling in the provided buffer with any data read. -/// -/// Readers are intended to be composable with one another. Many objects -/// throughout the I/O and related libraries take and provide types which -/// implement the `Reader` trait. -pub trait Reader { - - // Only method which need to get implemented for this trait - - /// Read bytes, up to the length of `buf` and place them in `buf`. - /// Returns the number of bytes read. The number of bytes read may - /// be less than the number requested, even 0. Returns `Err` on EOF. - /// - /// # Error - /// - /// If an error occurs during this I/O operation, then it is returned as - /// `Err(IoError)`. Note that end-of-file is considered an error, and can be - /// inspected for in the error's `kind` field. Also note that reading 0 - /// bytes is not considered an error in all circumstances - /// - /// # Implementation Note - /// - /// When implementing this method on a new Reader, you are strongly encouraged - /// not to return 0 if you can avoid it. - fn read(&mut self, buf: &mut [u8]) -> IoResult; - - // Convenient helper methods based on the above methods - - /// Reads at least `min` bytes and places them in `buf`. - /// Returns the number of bytes read. - /// - /// This will continue to call `read` until at least `min` bytes have been - /// read. If `read` returns 0 too many times, `NoProgress` will be - /// returned. - /// - /// # Error - /// - /// If an error occurs at any point, that error is returned, and no further - /// bytes are read. - fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult { - if min > buf.len() { - return Err(IoError { - detail: Some(String::from_str("the buffer is too short")), - ..standard_error(InvalidInput) - }); - } - let mut read = 0; - while read < min { - let mut zeroes = 0; - loop { - match self.read(&mut buf[read..]) { - Ok(0) => { - zeroes += 1; - if zeroes >= NO_PROGRESS_LIMIT { - return Err(standard_error(NoProgress)); - } - } - Ok(n) => { - read += n; - break; - } - err@Err(_) => return err - } - } - } - Ok(read) - } - - /// Reads a single byte. Returns `Err` on EOF. - fn read_byte(&mut self) -> IoResult { - let mut buf = [0]; - try!(self.read_at_least(1, &mut buf)); - Ok(buf[0]) - } - - /// Reads up to `len` bytes and appends them to a vector. - /// Returns the number of bytes read. The number of bytes read may be - /// less than the number requested, even 0. Returns Err on EOF. - /// - /// # Error - /// - /// If an error occurs during this I/O operation, then it is returned - /// as `Err(IoError)`. See `read()` for more details. - fn push(&mut self, len: uint, buf: &mut Vec) -> IoResult { - let start_len = buf.len(); - buf.reserve(len); - - let n = { - let s = unsafe { slice_vec_capacity(buf, start_len, start_len + len) }; - try!(self.read(s)) - }; - unsafe { buf.set_len(start_len + n) }; - Ok(n) - } - - /// Reads at least `min` bytes, but no more than `len`, and appends them to - /// a vector. - /// Returns the number of bytes read. - /// - /// This will continue to call `read` until at least `min` bytes have been - /// read. If `read` returns 0 too many times, `NoProgress` will be - /// returned. - /// - /// # Error - /// - /// If an error occurs at any point, that error is returned, and no further - /// bytes are read. - fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec) -> IoResult { - if min > len { - return Err(IoError { - detail: Some(String::from_str("the buffer is too short")), - ..standard_error(InvalidInput) - }); - } - - let start_len = buf.len(); - buf.reserve(len); - - // we can't just use self.read_at_least(min, slice) because we need to push - // successful reads onto the vector before any returned errors. - - let mut read = 0; - while read < min { - read += { - let s = unsafe { slice_vec_capacity(buf, start_len + read, start_len + len) }; - try!(self.read_at_least(1, s)) - }; - unsafe { buf.set_len(start_len + read) }; - } - Ok(read) - } - - /// Reads exactly `len` bytes and gives you back a new vector of length - /// `len` - /// - /// # Error - /// - /// Fails with the same conditions as `read`. Additionally returns error - /// on EOF. Note that if an error is returned, then some number of bytes may - /// have already been consumed from the underlying reader, and they are lost - /// (not returned as part of the error). If this is unacceptable, then it is - /// recommended to use the `push_at_least` or `read` methods. - fn read_exact(&mut self, len: uint) -> IoResult> { - let mut buf = Vec::with_capacity(len); - match self.push_at_least(len, len, &mut buf) { - Ok(_) => Ok(buf), - Err(e) => Err(e), - } - } - - /// Reads all remaining bytes from the stream. - /// - /// # Error - /// - /// Returns any non-EOF error immediately. Previously read bytes are - /// discarded when an error is returned. - /// - /// When EOF is encountered, all bytes read up to that point are returned. - fn read_to_end(&mut self) -> IoResult> { - let mut buf = Vec::with_capacity(DEFAULT_BUF_SIZE); - loop { - match self.push_at_least(1, DEFAULT_BUF_SIZE, &mut buf) { - Ok(_) => {} - Err(ref e) if e.kind == EndOfFile => break, - Err(e) => return Err(e) - } - } - return Ok(buf); - } - - /// Reads all of the remaining bytes of this stream, interpreting them as a - /// UTF-8 encoded stream. The corresponding string is returned. - /// - /// # Error - /// - /// This function returns all of the same errors as `read_to_end` with an - /// additional error if the reader's contents are not a valid sequence of - /// UTF-8 bytes. - fn read_to_string(&mut self) -> IoResult { - self.read_to_end().and_then(|s| { - match String::from_utf8(s) { - Ok(s) => Ok(s), - Err(_) => Err(standard_error(InvalidInput)), - } - }) - } - - // Byte conversion helpers - - /// Reads `n` little-endian unsigned integer bytes. - /// - /// `n` must be between 1 and 8, inclusive. - fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult { - assert!(nbytes > 0 && nbytes <= 8); - - let mut val = 0u64; - let mut pos = 0; - let mut i = nbytes; - while i > 0 { - val += (try!(self.read_u8()) as u64) << pos; - pos += 8; - i -= 1; - } - Ok(val) - } - - /// Reads `n` little-endian signed integer bytes. - /// - /// `n` must be between 1 and 8, inclusive. - fn read_le_int_n(&mut self, nbytes: uint) -> IoResult { - self.read_le_uint_n(nbytes).map(|i| extend_sign(i, nbytes)) - } - - /// Reads `n` big-endian unsigned integer bytes. - /// - /// `n` must be between 1 and 8, inclusive. - fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult { - assert!(nbytes > 0 && nbytes <= 8); - - let mut val = 0u64; - let mut i = nbytes; - while i > 0 { - i -= 1; - val += (try!(self.read_u8()) as u64) << i * 8; - } - Ok(val) - } - - /// Reads `n` big-endian signed integer bytes. - /// - /// `n` must be between 1 and 8, inclusive. - fn read_be_int_n(&mut self, nbytes: uint) -> IoResult { - self.read_be_uint_n(nbytes).map(|i| extend_sign(i, nbytes)) - } - - /// Reads a little-endian unsigned integer. - /// - /// The number of bytes returned is system-dependent. - fn read_le_uint(&mut self) -> IoResult { - self.read_le_uint_n(uint::BYTES).map(|i| i as uint) - } - - /// Reads a little-endian integer. - /// - /// The number of bytes returned is system-dependent. - fn read_le_int(&mut self) -> IoResult { - self.read_le_int_n(int::BYTES).map(|i| i as int) - } - - /// Reads a big-endian unsigned integer. - /// - /// The number of bytes returned is system-dependent. - fn read_be_uint(&mut self) -> IoResult { - self.read_be_uint_n(uint::BYTES).map(|i| i as uint) - } - - /// Reads a big-endian integer. - /// - /// The number of bytes returned is system-dependent. - fn read_be_int(&mut self) -> IoResult { - self.read_be_int_n(int::BYTES).map(|i| i as int) - } - - /// Reads a big-endian `u64`. - /// - /// `u64`s are 8 bytes long. - fn read_be_u64(&mut self) -> IoResult { - self.read_be_uint_n(8) - } - - /// Reads a big-endian `u32`. - /// - /// `u32`s are 4 bytes long. - fn read_be_u32(&mut self) -> IoResult { - self.read_be_uint_n(4).map(|i| i as u32) - } - - /// Reads a big-endian `u16`. - /// - /// `u16`s are 2 bytes long. - fn read_be_u16(&mut self) -> IoResult { - self.read_be_uint_n(2).map(|i| i as u16) - } - - /// Reads a big-endian `i64`. - /// - /// `i64`s are 8 bytes long. - fn read_be_i64(&mut self) -> IoResult { - self.read_be_int_n(8) - } - - /// Reads a big-endian `i32`. - /// - /// `i32`s are 4 bytes long. - fn read_be_i32(&mut self) -> IoResult { - self.read_be_int_n(4).map(|i| i as i32) - } - - /// Reads a big-endian `i16`. - /// - /// `i16`s are 2 bytes long. - fn read_be_i16(&mut self) -> IoResult { - self.read_be_int_n(2).map(|i| i as i16) - } - - /// Reads a big-endian `f64`. - /// - /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. - fn read_be_f64(&mut self) -> IoResult { - self.read_be_u64().map(|i| unsafe { - transmute::(i) - }) - } - - /// Reads a big-endian `f32`. - /// - /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. - fn read_be_f32(&mut self) -> IoResult { - self.read_be_u32().map(|i| unsafe { - transmute::(i) - }) - } - - /// Reads a little-endian `u64`. - /// - /// `u64`s are 8 bytes long. - fn read_le_u64(&mut self) -> IoResult { - self.read_le_uint_n(8) - } - - /// Reads a little-endian `u32`. - /// - /// `u32`s are 4 bytes long. - fn read_le_u32(&mut self) -> IoResult { - self.read_le_uint_n(4).map(|i| i as u32) - } - - /// Reads a little-endian `u16`. - /// - /// `u16`s are 2 bytes long. - fn read_le_u16(&mut self) -> IoResult { - self.read_le_uint_n(2).map(|i| i as u16) - } - - /// Reads a little-endian `i64`. - /// - /// `i64`s are 8 bytes long. - fn read_le_i64(&mut self) -> IoResult { - self.read_le_int_n(8) - } - - /// Reads a little-endian `i32`. - /// - /// `i32`s are 4 bytes long. - fn read_le_i32(&mut self) -> IoResult { - self.read_le_int_n(4).map(|i| i as i32) - } - - /// Reads a little-endian `i16`. - /// - /// `i16`s are 2 bytes long. - fn read_le_i16(&mut self) -> IoResult { - self.read_le_int_n(2).map(|i| i as i16) - } - - /// Reads a little-endian `f64`. - /// - /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. - fn read_le_f64(&mut self) -> IoResult { - self.read_le_u64().map(|i| unsafe { - transmute::(i) - }) - } - - /// Reads a little-endian `f32`. - /// - /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. - fn read_le_f32(&mut self) -> IoResult { - self.read_le_u32().map(|i| unsafe { - transmute::(i) - }) - } - - /// Read a u8. - /// - /// `u8`s are 1 byte. - fn read_u8(&mut self) -> IoResult { - self.read_byte() - } - - /// Read an i8. - /// - /// `i8`s are 1 byte. - fn read_i8(&mut self) -> IoResult { - self.read_byte().map(|i| i as i8) - } -} - -/// A reader which can be converted to a RefReader. -pub trait ByRefReader { - /// Creates a wrapper around a mutable reference to the reader. - /// - /// This is useful to allow applying adaptors while still - /// retaining ownership of the original value. - fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self>; -} - -impl ByRefReader for T { - fn by_ref<'a>(&'a mut self) -> RefReader<'a, T> { - RefReader { inner: self } - } -} - -/// A reader which can be converted to bytes. -pub trait BytesReader { - /// Create an iterator that reads a single byte on - /// each iteration, until EOF. - /// - /// # Error - /// - /// Any error other than `EndOfFile` that is produced by the underlying Reader - /// is returned by the iterator and should be handled by the caller. - fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, Self>; -} - -impl BytesReader for T { - fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, T> { - extensions::Bytes::new(self) - } -} - -impl<'a> Reader for Box { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - let reader: &mut Reader = &mut **self; - reader.read(buf) - } -} - -impl<'a> Reader for &'a mut (Reader+'a) { - fn read(&mut self, buf: &mut [u8]) -> IoResult { (*self).read(buf) } -} - -/// Returns a slice of `v` between `start` and `end`. -/// -/// Similar to `slice()` except this function only bounds the slice on the -/// capacity of `v`, not the length. -/// -/// # Panics -/// -/// Panics when `start` or `end` point outside the capacity of `v`, or when -/// `start` > `end`. -// Private function here because we aren't sure if we want to expose this as -// API yet. If so, it should be a method on Vec. -unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec, start: uint, end: uint) -> &'a mut [T] { - use raw::Slice; - use ptr::PtrExt; - - assert!(start <= end); - assert!(end <= v.capacity()); - transmute(Slice { - data: v.as_ptr().offset(start as int), - len: end - start - }) -} - -/// A `RefReader` is a struct implementing `Reader` which contains a reference -/// to another reader. This is often useful when composing streams. -/// -/// # Examples -/// -/// ``` -/// use std::io; -/// use std::io::ByRefReader; -/// use std::io::util::LimitReader; -/// -/// fn process_input(r: R) {} -/// -/// let mut stream = io::stdin(); -/// -/// // Only allow the function to process at most one kilobyte of input -/// { -/// let stream = LimitReader::new(stream.by_ref(), 1024); -/// process_input(stream); -/// } -/// -/// // 'stream' is still available for use here -/// ``` -pub struct RefReader<'a, R:'a> { - /// The underlying reader which this is referencing - inner: &'a mut R -} - -impl<'a, R: Reader> Reader for RefReader<'a, R> { - fn read(&mut self, buf: &mut [u8]) -> IoResult { self.inner.read(buf) } -} - -impl<'a, R: Buffer> Buffer for RefReader<'a, R> { - fn fill_buf(&mut self) -> IoResult<&[u8]> { self.inner.fill_buf() } - fn consume(&mut self, amt: uint) { self.inner.consume(amt) } -} - -fn extend_sign(val: u64, nbytes: uint) -> i64 { - let shift = (8 - nbytes) * 8; - (val << shift) as i64 >> shift -} - -/// A trait for objects which are byte-oriented streams. Writers are defined by -/// one method, `write`. This function will block until the provided buffer of -/// bytes has been entirely written, and it will return any failures which occur. -/// -/// Another commonly overridden method is the `flush` method for writers such as -/// buffered writers. -/// -/// Writers are intended to be composable with one another. Many objects -/// throughout the I/O and related libraries take and provide types which -/// implement the `Writer` trait. -pub trait Writer { - /// Write the entirety of a given buffer - /// - /// # Errors - /// - /// If an error happens during the I/O operation, the error is returned as - /// `Err`. Note that it is considered an error if the entire buffer could - /// not be written, and if an error is returned then it is unknown how much - /// data (if any) was actually written. - fn write(&mut self, buf: &[u8]) -> IoResult<()>; - - /// Flush this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// This is by default a no-op and implementers of the `Writer` trait should - /// decide whether their stream needs to be buffered or not. - fn flush(&mut self) -> IoResult<()> { Ok(()) } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - /// - /// This method is primarily used to interface with the `format_args!` - /// macro, but it is rare that this should explicitly be called. The - /// `write!` macro should be favored to invoke this method instead. - /// - /// # Errors - /// - /// This function will return any I/O error reported while formatting. - fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> { - // Create a shim which translates a Writer to a fmt::Writer and saves - // off I/O errors. instead of discarding them - struct Adaptor<'a, T: ?Sized +'a> { - inner: &'a mut T, - error: IoResult<()>, - } - - impl<'a, T: ?Sized + Writer> fmt::Writer for Adaptor<'a, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adaptor { inner: self, error: Ok(()) }; - match fmt::write(&mut output, fmt) { - Ok(()) => Ok(()), - Err(..) => output.error - } - } - - - /// Write a rust string into this sink. - /// - /// The bytes written will be the UTF-8 encoded version of the input string. - /// If other encodings are desired, it is recommended to compose this stream - /// with another performing the conversion, or to use `write` with a - /// converted byte-array instead. - #[inline] - fn write_str(&mut self, s: &str) -> IoResult<()> { - self.write(s.as_bytes()) - } - - /// Writes a string into this sink, and then writes a literal newline (`\n`) - /// byte afterwards. Note that the writing of the newline is *not* atomic in - /// the sense that the call to `write` is invoked twice (once with the - /// string and once with a newline character). - /// - /// If other encodings or line ending flavors are desired, it is recommended - /// that the `write` method is used specifically instead. - #[inline] - fn write_line(&mut self, s: &str) -> IoResult<()> { - self.write_str(s).and_then(|()| self.write(&[b'\n'])) - } - - /// Write a single char, encoded as UTF-8. - #[inline] - fn write_char(&mut self, c: char) -> IoResult<()> { - let mut buf = [0u8; 4]; - let n = c.encode_utf8(buf.as_mut_slice()).unwrap_or(0); - self.write(&buf[..n]) - } - - /// Write the result of passing n through `int::to_str_bytes`. - #[inline] - fn write_int(&mut self, n: int) -> IoResult<()> { - write!(self, "{}", n) - } - - /// Write the result of passing n through `uint::to_str_bytes`. - #[inline] - fn write_uint(&mut self, n: uint) -> IoResult<()> { - write!(self, "{}", n) - } - - /// Write a little-endian uint (number of bytes depends on system). - #[inline] - fn write_le_uint(&mut self, n: uint) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, uint::BYTES, |v| self.write(v)) - } - - /// Write a little-endian int (number of bytes depends on system). - #[inline] - fn write_le_int(&mut self, n: int) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, int::BYTES, |v| self.write(v)) - } - - /// Write a big-endian uint (number of bytes depends on system). - #[inline] - fn write_be_uint(&mut self, n: uint) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, uint::BYTES, |v| self.write(v)) - } - - /// Write a big-endian int (number of bytes depends on system). - #[inline] - fn write_be_int(&mut self, n: int) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, int::BYTES, |v| self.write(v)) - } - - /// Write a big-endian u64 (8 bytes). - #[inline] - fn write_be_u64(&mut self, n: u64) -> IoResult<()> { - extensions::u64_to_be_bytes(n, 8u, |v| self.write(v)) - } - - /// Write a big-endian u32 (4 bytes). - #[inline] - fn write_be_u32(&mut self, n: u32) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a big-endian u16 (2 bytes). - #[inline] - fn write_be_u16(&mut self, n: u16) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a big-endian i64 (8 bytes). - #[inline] - fn write_be_i64(&mut self, n: i64) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) - } - - /// Write a big-endian i32 (4 bytes). - #[inline] - fn write_be_i32(&mut self, n: i32) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a big-endian i16 (2 bytes). - #[inline] - fn write_be_i16(&mut self, n: i16) -> IoResult<()> { - extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). - #[inline] - fn write_be_f64(&mut self, f: f64) -> IoResult<()> { - unsafe { - self.write_be_u64(transmute(f)) - } - } - - /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). - #[inline] - fn write_be_f32(&mut self, f: f32) -> IoResult<()> { - unsafe { - self.write_be_u32(transmute(f)) - } - } - - /// Write a little-endian u64 (8 bytes). - #[inline] - fn write_le_u64(&mut self, n: u64) -> IoResult<()> { - extensions::u64_to_le_bytes(n, 8u, |v| self.write(v)) - } - - /// Write a little-endian u32 (4 bytes). - #[inline] - fn write_le_u32(&mut self, n: u32) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a little-endian u16 (2 bytes). - #[inline] - fn write_le_u16(&mut self, n: u16) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a little-endian i64 (8 bytes). - #[inline] - fn write_le_i64(&mut self, n: i64) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) - } - - /// Write a little-endian i32 (4 bytes). - #[inline] - fn write_le_i32(&mut self, n: i32) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a little-endian i16 (2 bytes). - #[inline] - fn write_le_i16(&mut self, n: i16) -> IoResult<()> { - extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a little-endian IEEE754 double-precision floating-point - /// (8 bytes). - #[inline] - fn write_le_f64(&mut self, f: f64) -> IoResult<()> { - unsafe { - self.write_le_u64(transmute(f)) - } - } - - /// Write a little-endian IEEE754 single-precision floating-point - /// (4 bytes). - #[inline] - fn write_le_f32(&mut self, f: f32) -> IoResult<()> { - unsafe { - self.write_le_u32(transmute(f)) - } - } - - /// Write a u8 (1 byte). - #[inline] - fn write_u8(&mut self, n: u8) -> IoResult<()> { - self.write(&[n]) - } - - /// Write an i8 (1 byte). - #[inline] - fn write_i8(&mut self, n: i8) -> IoResult<()> { - self.write(&[n as u8]) - } -} - -/// A writer which can be converted to a RefWriter. -pub trait ByRefWriter { - /// Creates a wrapper around a mutable reference to the writer. - /// - /// This is useful to allow applying wrappers while still - /// retaining ownership of the original value. - #[inline] - fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self>; -} - -impl ByRefWriter for T { - fn by_ref<'a>(&'a mut self) -> RefWriter<'a, T> { - RefWriter { inner: self } - } -} - -impl<'a> Writer for Box { - #[inline] - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - (&mut **self).write(buf) - } - - #[inline] - fn flush(&mut self) -> IoResult<()> { - (&mut **self).flush() - } -} - -impl<'a> Writer for &'a mut (Writer+'a) { - #[inline] - fn write(&mut self, buf: &[u8]) -> IoResult<()> { (**self).write(buf) } - - #[inline] - fn flush(&mut self) -> IoResult<()> { (**self).flush() } -} - -/// A `RefWriter` is a struct implementing `Writer` which contains a reference -/// to another writer. This is often useful when composing streams. -/// -/// # Example -/// -/// ``` -/// use std::io::util::TeeReader; -/// use std::io::{stdin, ByRefWriter}; -/// -/// fn process_input(r: R) {} -/// -/// let mut output = Vec::new(); -/// -/// { -/// // Don't give ownership of 'output' to the 'tee'. Instead we keep a -/// // handle to it in the outer scope -/// let mut tee = TeeReader::new(stdin(), output.by_ref()); -/// process_input(tee); -/// } -/// -/// println!("input processed: {:?}", output); -/// ``` -pub struct RefWriter<'a, W:'a> { - /// The underlying writer which this is referencing - inner: &'a mut W -} - -impl<'a, W: Writer> Writer for RefWriter<'a, W> { - #[inline] - fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.inner.write(buf) } - - #[inline] - fn flush(&mut self) -> IoResult<()> { self.inner.flush() } -} - - -/// A Stream is a readable and a writable object. Data written is typically -/// received by the object which reads receive data from. -pub trait Stream: Reader + Writer { } - -impl Stream for T {} - -/// An iterator that reads a line on each iteration, -/// until `.read_line()` encounters `EndOfFile`. -/// -/// # Notes about the Iteration Protocol -/// -/// The `Lines` may yield `None` and thus terminate -/// an iteration, but continue to yield elements if iteration -/// is attempted again. -/// -/// # Error -/// -/// Any error other than `EndOfFile` that is produced by the underlying Reader -/// is returned by the iterator and should be handled by the caller. -pub struct Lines<'r, T:'r> { - buffer: &'r mut T, -} - -impl<'r, T: Buffer> Iterator for Lines<'r, T> { - type Item = IoResult; - - fn next(&mut self) -> Option> { - match self.buffer.read_line() { - Ok(x) => Some(Ok(x)), - Err(IoError { kind: EndOfFile, ..}) => None, - Err(y) => Some(Err(y)) - } - } -} - -/// An iterator that reads a utf8-encoded character on each iteration, -/// until `.read_char()` encounters `EndOfFile`. -/// -/// # Notes about the Iteration Protocol -/// -/// The `Chars` may yield `None` and thus terminate -/// an iteration, but continue to yield elements if iteration -/// is attempted again. -/// -/// # Error -/// -/// Any error other than `EndOfFile` that is produced by the underlying Reader -/// is returned by the iterator and should be handled by the caller. -pub struct Chars<'r, T:'r> { - buffer: &'r mut T -} - -impl<'r, T: Buffer> Iterator for Chars<'r, T> { - type Item = IoResult; - - fn next(&mut self) -> Option> { - match self.buffer.read_char() { - Ok(x) => Some(Ok(x)), - Err(IoError { kind: EndOfFile, ..}) => None, - Err(y) => Some(Err(y)) - } - } -} - -/// A Buffer is a type of reader which has some form of internal buffering to -/// allow certain kinds of reading operations to be more optimized than others. -/// This type extends the `Reader` trait with a few methods that are not -/// possible to reasonably implement with purely a read interface. -pub trait Buffer: Reader { - /// Fills the internal buffer of this object, returning the buffer contents. - /// Note that none of the contents will be "read" in the sense that later - /// calling `read` may return the same contents. - /// - /// The `consume` function must be called with the number of bytes that are - /// consumed from this buffer returned to ensure that the bytes are never - /// returned twice. - /// - /// # Error - /// - /// This function will return an I/O error if the underlying reader was - /// read, but returned an error. Note that it is not an error to return a - /// 0-length buffer. - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]>; - - /// Tells this buffer that `amt` bytes have been consumed from the buffer, - /// so they should no longer be returned in calls to `read`. - fn consume(&mut self, amt: uint); - - /// Reads the next line of input, interpreted as a sequence of UTF-8 - /// encoded Unicode codepoints. If a newline is encountered, then the - /// newline is contained in the returned string. - /// - /// # Example - /// - /// ```rust - /// use std::io::BufReader; - /// - /// let mut reader = BufReader::new(b"hello\nworld"); - /// assert_eq!("hello\n", &*reader.read_line().unwrap()); - /// ``` - /// - /// # Error - /// - /// This function has the same error semantics as `read_until`: - /// - /// * All non-EOF errors will be returned immediately - /// * If an error is returned previously consumed bytes are lost - /// * EOF is only returned if no bytes have been read - /// * Reach EOF may mean that the delimiter is not present in the return - /// value - /// - /// Additionally, this function can fail if the line of input read is not a - /// valid UTF-8 sequence of bytes. - fn read_line(&mut self) -> IoResult { - self.read_until(b'\n').and_then(|line| - match String::from_utf8(line) { - Ok(s) => Ok(s), - Err(_) => Err(standard_error(InvalidInput)), - } - ) - } - - /// Reads a sequence of bytes leading up to a specified delimiter. Once the - /// specified byte is encountered, reading ceases and the bytes up to and - /// including the delimiter are returned. - /// - /// # Error - /// - /// If any I/O error is encountered other than EOF, the error is immediately - /// returned. Note that this may discard bytes which have already been read, - /// and those bytes will *not* be returned. It is recommended to use other - /// methods if this case is worrying. - /// - /// If EOF is encountered, then this function will return EOF if 0 bytes - /// have been read, otherwise the pending byte buffer is returned. This - /// is the reason that the byte buffer returned may not always contain the - /// delimiter. - fn read_until(&mut self, byte: u8) -> IoResult> { - let mut res = Vec::new(); - - loop { - let (done, used) = { - let available = match self.fill_buf() { - Ok(n) => n, - Err(ref e) if res.len() > 0 && e.kind == EndOfFile => { - return Ok(res); - } - Err(e) => return Err(e) - }; - match available.iter().position(|&b| b == byte) { - Some(i) => { - res.push_all(&available[..i + 1]); - (true, i + 1) - } - None => { - res.push_all(available); - (false, available.len()) - } - } - }; - self.consume(used); - if done { - return Ok(res); - } - } - } - - /// Reads the next utf8-encoded character from the underlying stream. - /// - /// # Error - /// - /// If an I/O error occurs, or EOF, then this function will return `Err`. - /// This function will also return error if the stream does not contain a - /// valid utf-8 encoded codepoint as the next few bytes in the stream. - fn read_char(&mut self) -> IoResult { - let first_byte = try!(self.read_byte()); - let width = unicode::str::utf8_char_width(first_byte); - if width == 1 { return Ok(first_byte as char) } - if width == 0 { return Err(standard_error(InvalidInput)) } // not utf8 - let mut buf = [first_byte, 0, 0, 0]; - { - let mut start = 1; - while start < width { - match try!(self.read(&mut buf[start .. width])) { - n if n == width - start => break, - n if n < width - start => { start += n; } - _ => return Err(standard_error(InvalidInput)), - } - } - } - match str::from_utf8(&buf[..width]).ok() { - Some(s) => Ok(s.char_at(0)), - None => Err(standard_error(InvalidInput)) - } - } -} - -/// Extension methods for the Buffer trait which are included in the prelude. -pub trait BufferPrelude { - /// Create an iterator that reads a utf8-encoded character on each iteration - /// until EOF. - /// - /// # Error - /// - /// Any error other than `EndOfFile` that is produced by the underlying Reader - /// is returned by the iterator and should be handled by the caller. - fn chars<'r>(&'r mut self) -> Chars<'r, Self>; - - /// Create an iterator that reads a line on each iteration until EOF. - /// - /// # Error - /// - /// Any error other than `EndOfFile` that is produced by the underlying Reader - /// is returned by the iterator and should be handled by the caller. - fn lines<'r>(&'r mut self) -> Lines<'r, Self>; -} - -impl BufferPrelude for T { - fn chars<'r>(&'r mut self) -> Chars<'r, T> { - Chars { buffer: self } - } - - fn lines<'r>(&'r mut self) -> Lines<'r, T> { - Lines { buffer: self } - } -} - -/// When seeking, the resulting cursor is offset from a base by the offset given -/// to the `seek` function. The base used is specified by this enumeration. -#[derive(Copy)] -pub enum SeekStyle { - /// Seek from the beginning of the stream - SeekSet, - /// Seek from the end of the stream - SeekEnd, - /// Seek from the current position - SeekCur, -} - -/// An object implementing `Seek` internally has some form of cursor which can -/// be moved within a stream of bytes. The stream typically has a fixed size, -/// allowing seeking relative to either end. -pub trait Seek { - /// Return position of file cursor in the stream - fn tell(&self) -> IoResult; - - /// Seek to an offset in a stream - /// - /// A successful seek clears the EOF indicator. Seeking beyond EOF is - /// allowed, but seeking before position 0 is not allowed. - /// - /// # Errors - /// - /// * Seeking to a negative offset is considered an error - /// * Seeking past the end of the stream does not modify the underlying - /// stream, but the next write may cause the previous data to be filled in - /// with a bit pattern. - fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>; -} - -/// A listener is a value that can consume itself to start listening for -/// connections. -/// -/// Doing so produces some sort of Acceptor. -pub trait Listener> { - /// Spin up the listener and start queuing incoming connections - /// - /// # Error - /// - /// Returns `Err` if this listener could not be bound to listen for - /// connections. In all cases, this listener is consumed. - fn listen(self) -> IoResult; -} - -/// An acceptor is a value that presents incoming connections -pub trait Acceptor { - /// Wait for and accept an incoming connection - /// - /// # Error - /// - /// Returns `Err` if an I/O error is encountered. - fn accept(&mut self) -> IoResult; - - /// Create an iterator over incoming connection attempts. - /// - /// Note that I/O errors will be yielded by the iterator itself. - fn incoming<'r>(&'r mut self) -> IncomingConnections<'r, Self> { - IncomingConnections { inc: self } - } -} - -/// An infinite iterator over incoming connection attempts. -/// Calling `next` will block the task until a connection is attempted. -/// -/// Since connection attempts can continue forever, this iterator always returns -/// `Some`. The `Some` contains the `IoResult` representing whether the -/// connection attempt was successful. A successful connection will be wrapped -/// in `Ok`. A failed connection is represented as an `Err`. -pub struct IncomingConnections<'a, A: ?Sized +'a> { - inc: &'a mut A, -} - -#[old_impl_check] -impl<'a, T, A: ?Sized + Acceptor> Iterator for IncomingConnections<'a, A> { - type Item = IoResult; - - fn next(&mut self) -> Option> { - Some(self.inc.accept()) - } -} - -/// Creates a standard error for a commonly used flavor of error. The `detail` -/// field of the returned error will always be `None`. -/// -/// # Example -/// -/// ``` -/// use std::io; -/// -/// let eof = io::standard_error(io::EndOfFile); -/// let einval = io::standard_error(io::InvalidInput); -/// ``` -pub fn standard_error(kind: IoErrorKind) -> IoError { - let desc = match kind { - EndOfFile => "end of file", - IoUnavailable => "I/O is unavailable", - InvalidInput => "invalid input", - OtherIoError => "unknown I/O error", - FileNotFound => "file not found", - PermissionDenied => "permission denied", - ConnectionFailed => "connection failed", - Closed => "stream is closed", - ConnectionRefused => "connection refused", - ConnectionReset => "connection reset", - ConnectionAborted => "connection aborted", - NotConnected => "not connected", - BrokenPipe => "broken pipe", - PathAlreadyExists => "file already exists", - PathDoesntExist => "no such file", - MismatchedFileTypeForOperation => "mismatched file type", - ResourceUnavailable => "resource unavailable", - TimedOut => "operation timed out", - ShortWrite(..) => "short write", - NoProgress => "no progress", - }; - IoError { - kind: kind, - desc: desc, - detail: None, - } -} - -/// A mode specifies how a file should be opened or created. These modes are -/// passed to `File::open_mode` and are used to control where the file is -/// positioned when it is initially opened. -#[derive(Copy, Clone, PartialEq, Eq, Show)] -pub enum FileMode { - /// Opens a file positioned at the beginning. - Open, - /// Opens a file positioned at EOF. - Append, - /// Opens a file, truncating it if it already exists. - Truncate, -} - -/// Access permissions with which the file should be opened. `File`s -/// opened with `Read` will return an error if written to. -#[derive(Copy, Clone, PartialEq, Eq, Show)] -pub enum FileAccess { - /// Read-only access, requests to write will result in an error - Read, - /// Write-only access, requests to read will result in an error - Write, - /// Read-write access, no requests are denied by default - ReadWrite, -} - -/// Different kinds of files which can be identified by a call to stat -#[derive(Copy, PartialEq, Show, Hash, Clone)] -pub enum FileType { - /// This is a normal file, corresponding to `S_IFREG` - RegularFile, - - /// This file is a directory, corresponding to `S_IFDIR` - Directory, - - /// This file is a named pipe, corresponding to `S_IFIFO` - NamedPipe, - - /// This file is a block device, corresponding to `S_IFBLK` - BlockSpecial, - - /// This file is a symbolic link to another file, corresponding to `S_IFLNK` - Symlink, - - /// The type of this file is not recognized as one of the other categories - Unknown, -} - -/// A structure used to describe metadata information about a file. This -/// structure is created through the `stat` method on a `Path`. -/// -/// # Examples -/// -/// ```no_run -/// # #![allow(unstable)] -/// -/// use std::io::fs::PathExtensions; -/// -/// let info = match Path::new("foo.txt").stat() { -/// Ok(stat) => stat, -/// Err(e) => panic!("couldn't read foo.txt: {}", e), -/// }; -/// -/// println!("byte size: {}", info.size); -/// ``` -#[derive(Copy, Hash)] -pub struct FileStat { - /// The size of the file, in bytes - pub size: u64, - /// The kind of file this path points to (directory, file, pipe, etc.) - pub kind: FileType, - /// The file permissions currently on the file - pub perm: FilePermission, - - // FIXME(#10301): These time fields are pretty useless without an actual - // time representation, what are the milliseconds relative - // to? - - /// The time that the file was created at, in platform-dependent - /// milliseconds - pub created: u64, - /// The time that this file was last modified, in platform-dependent - /// milliseconds - pub modified: u64, - /// The time that this file was last accessed, in platform-dependent - /// milliseconds - pub accessed: u64, - - /// Information returned by stat() which is not guaranteed to be - /// platform-independent. This information may be useful on some platforms, - /// but it may have different meanings or no meaning at all on other - /// platforms. - /// - /// Usage of this field is discouraged, but if access is desired then the - /// fields are located here. - #[unstable] - pub unstable: UnstableFileStat, -} - -/// This structure represents all of the possible information which can be -/// returned from a `stat` syscall which is not contained in the `FileStat` -/// structure. This information is not necessarily platform independent, and may -/// have different meanings or no meaning at all on some platforms. -#[unstable] -#[derive(Copy, Hash)] -pub struct UnstableFileStat { - /// The ID of the device containing the file. - pub device: u64, - /// The file serial number. - pub inode: u64, - /// The device ID. - pub rdev: u64, - /// The number of hard links to this file. - pub nlink: u64, - /// The user ID of the file. - pub uid: u64, - /// The group ID of the file. - pub gid: u64, - /// The optimal block size for I/O. - pub blksize: u64, - /// The blocks allocated for this file. - pub blocks: u64, - /// User-defined flags for the file. - pub flags: u64, - /// The file generation number. - pub gen: u64, -} - - -bitflags! { - /// A set of permissions for a file or directory is represented by a set of - /// flags which are or'd together. - #[derive(Show)] - flags FilePermission: u32 { - const USER_READ = 0o400, - const USER_WRITE = 0o200, - const USER_EXECUTE = 0o100, - const GROUP_READ = 0o040, - const GROUP_WRITE = 0o020, - const GROUP_EXECUTE = 0o010, - const OTHER_READ = 0o004, - const OTHER_WRITE = 0o002, - const OTHER_EXECUTE = 0o001, - - const USER_RWX = USER_READ.bits | USER_WRITE.bits | USER_EXECUTE.bits, - const GROUP_RWX = GROUP_READ.bits | GROUP_WRITE.bits | GROUP_EXECUTE.bits, - const OTHER_RWX = OTHER_READ.bits | OTHER_WRITE.bits | OTHER_EXECUTE.bits, - - /// Permissions for user owned files, equivalent to 0644 on unix-like - /// systems. - const USER_FILE = USER_READ.bits | USER_WRITE.bits | GROUP_READ.bits | OTHER_READ.bits, - - /// Permissions for user owned directories, equivalent to 0755 on - /// unix-like systems. - const USER_DIR = USER_RWX.bits | GROUP_READ.bits | GROUP_EXECUTE.bits | - OTHER_READ.bits | OTHER_EXECUTE.bits, - - /// Permissions for user owned executables, equivalent to 0755 - /// on unix-like systems. - const USER_EXEC = USER_DIR.bits, - - /// All possible permissions enabled. - const ALL_PERMISSIONS = USER_RWX.bits | GROUP_RWX.bits | OTHER_RWX.bits, - } -} - - -#[stable] -impl Default for FilePermission { - #[stable] - #[inline] - fn default() -> FilePermission { FilePermission::empty() } -} - -#[stable] -impl fmt::Display for FilePermission { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:04o}", self.bits) - } -} - -#[cfg(test)] -mod tests { - use self::BadReaderBehavior::*; - use super::{IoResult, Reader, MemReader, NoProgress, InvalidInput, Writer}; - use prelude::v1::{Ok, Vec, Buffer, SliceExt}; - use uint; - - #[derive(Clone, PartialEq, Show)] - enum BadReaderBehavior { - GoodBehavior(uint), - BadBehavior(uint) - } - - struct BadReader { - r: T, - behavior: Vec, - } - - impl BadReader { - fn new(r: T, behavior: Vec) -> BadReader { - BadReader { behavior: behavior, r: r } - } - } - - impl Reader for BadReader { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - let BadReader { ref mut behavior, ref mut r } = *self; - loop { - if behavior.is_empty() { - // fall back on good - return r.read(buf); - } - match behavior.as_mut_slice()[0] { - GoodBehavior(0) => (), - GoodBehavior(ref mut x) => { - *x -= 1; - return r.read(buf); - } - BadBehavior(0) => (), - BadBehavior(ref mut x) => { - *x -= 1; - return Ok(0); - } - }; - behavior.remove(0); - } - } - } - - #[test] - fn test_read_at_least() { - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![GoodBehavior(uint::MAX)]); - let buf = &mut [0u8; 5]; - assert!(r.read_at_least(1, buf).unwrap() >= 1); - assert!(r.read_exact(5).unwrap().len() == 5); // read_exact uses read_at_least - assert!(r.read_at_least(0, buf).is_ok()); - - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(50), GoodBehavior(uint::MAX)]); - assert!(r.read_at_least(1, buf).unwrap() >= 1); - - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(1), GoodBehavior(1), - BadBehavior(50), GoodBehavior(uint::MAX)]); - assert!(r.read_at_least(1, buf).unwrap() >= 1); - assert!(r.read_at_least(1, buf).unwrap() >= 1); - - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(uint::MAX)]); - assert_eq!(r.read_at_least(1, buf).unwrap_err().kind, NoProgress); - - let mut r = MemReader::new(b"hello, world!".to_vec()); - assert_eq!(r.read_at_least(5, buf).unwrap(), 5); - assert_eq!(r.read_at_least(6, buf).unwrap_err().kind, InvalidInput); - } - - #[test] - fn test_push_at_least() { - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![GoodBehavior(uint::MAX)]); - let mut buf = Vec::new(); - assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); - assert!(r.push_at_least(0, 5, &mut buf).is_ok()); - - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(50), GoodBehavior(uint::MAX)]); - assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); - - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(1), GoodBehavior(1), - BadBehavior(50), GoodBehavior(uint::MAX)]); - assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); - assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); - - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(uint::MAX)]); - assert_eq!(r.push_at_least(1, 5, &mut buf).unwrap_err().kind, NoProgress); - - let mut r = MemReader::new(b"hello, world!".to_vec()); - assert_eq!(r.push_at_least(5, 1, &mut buf).unwrap_err().kind, InvalidInput); - } - - #[test] - fn test_show() { - use super::*; - - assert_eq!(format!("{}", USER_READ), "0400"); - assert_eq!(format!("{}", USER_FILE), "0644"); - assert_eq!(format!("{}", USER_EXEC), "0755"); - assert_eq!(format!("{}", USER_RWX), "0700"); - assert_eq!(format!("{}", GROUP_RWX), "0070"); - assert_eq!(format!("{}", OTHER_RWX), "0007"); - assert_eq!(format!("{}", ALL_PERMISSIONS), "0777"); - assert_eq!(format!("{}", USER_READ | USER_WRITE | OTHER_WRITE), "0602"); - } - - fn _ensure_buffer_is_object_safe(x: &T) -> &Buffer { - x as &Buffer - } -} diff --git a/src/libstd/io/net/addrinfo.rs b/src/libstd/io/net/addrinfo.rs deleted file mode 100644 index 7825a4e16e1..00000000000 --- a/src/libstd/io/net/addrinfo.rs +++ /dev/null @@ -1,137 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Synchronous DNS Resolution -//! -//! Contains the functionality to perform DNS resolution or reverse lookup, -//! in a style related to `getaddrinfo()` and `getnameinfo()`, respectively. - -#![allow(missing_docs)] - -pub use self::SocketType::*; -pub use self::Flag::*; -pub use self::Protocol::*; - -use iter::IteratorExt; -use io::{IoResult}; -use io::net::ip::{SocketAddr, IpAddr}; -use option::Option; -use option::Option::{Some, None}; -use string::String; -use sys; -use vec::Vec; - -/// Hints to the types of sockets that are desired when looking up hosts -#[derive(Copy, Show)] -pub enum SocketType { - Stream, Datagram, Raw -} - -/// Flags which can be or'd into the `flags` field of a `Hint`. These are used -/// to manipulate how a query is performed. -/// -/// The meaning of each of these flags can be found with `man -s 3 getaddrinfo` -#[derive(Copy, Show)] -pub enum Flag { - AddrConfig, - All, - CanonName, - NumericHost, - NumericServ, - Passive, - V4Mapped, -} - -/// A transport protocol associated with either a hint or a return value of -/// `lookup` -#[derive(Copy, Show)] -pub enum Protocol { - TCP, UDP -} - -/// This structure is used to provide hints when fetching addresses for a -/// remote host to control how the lookup is performed. -/// -/// For details on these fields, see their corresponding definitions via -/// `man -s 3 getaddrinfo` -#[derive(Copy, Show)] -pub struct Hint { - pub family: uint, - pub socktype: Option, - pub protocol: Option, - pub flags: uint, -} - -#[derive(Copy, Show)] -pub struct Info { - pub address: SocketAddr, - pub family: uint, - pub socktype: Option, - pub protocol: Option, - pub flags: uint, -} - -/// Easy name resolution. Given a hostname, returns the list of IP addresses for -/// that hostname. -pub fn get_host_addresses(host: &str) -> IoResult> { - lookup(Some(host), None, None).map(|a| a.into_iter().map(|i| i.address.ip).collect()) -} - -/// Reverse name resolution. Given an address, returns the corresponding -/// hostname. -pub fn get_address_name(addr: IpAddr) -> IoResult { - sys::addrinfo::get_address_name(addr) -} - -/// Full-fledged resolution. This function will perform a synchronous call to -/// getaddrinfo, controlled by the parameters -/// -/// # Arguments -/// -/// * hostname - an optional hostname to lookup against -/// * servname - an optional service name, listed in the system services -/// * hint - see the hint structure, and "man -s 3 getaddrinfo", for how this -/// controls lookup -/// -/// FIXME: this is not public because the `Hint` structure is not ready for public -/// consumption just yet. -#[allow(unused_variables)] -fn lookup(hostname: Option<&str>, servname: Option<&str>, hint: Option) - -> IoResult> { - sys::addrinfo::get_host_addresses(hostname, servname, hint) -} - -// Ignored on android since we cannot give tcp/ip -// permission without help of apk -#[cfg(all(test, not(target_os = "android")))] -mod test { - use prelude::v1::*; - use super::*; - use io::net::ip::*; - - #[test] - fn dns_smoke_test() { - let ipaddrs = get_host_addresses("localhost").unwrap(); - let mut found_local = false; - let local_addr = &Ipv4Addr(127, 0, 0, 1); - for addr in ipaddrs.iter() { - found_local = found_local || addr == local_addr; - } - assert!(found_local); - } - - #[ignore] - #[test] - fn issue_10663() { - // Something should happen here, but this certainly shouldn't cause - // everything to die. The actual outcome we don't care too much about. - get_host_addresses("example.com").unwrap(); - } -} diff --git a/src/libstd/io/net/ip.rs b/src/libstd/io/net/ip.rs deleted file mode 100644 index e4622781ae7..00000000000 --- a/src/libstd/io/net/ip.rs +++ /dev/null @@ -1,700 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Internet Protocol (IP) addresses. -//! -//! This module contains functions useful for parsing, formatting, and -//! manipulating IP addresses. - -#![allow(missing_docs)] - -pub use self::IpAddr::*; - -use boxed::Box; -use fmt; -use io::{self, IoResult, IoError}; -use io::net; -use iter::{Iterator, IteratorExt}; -use ops::{FnOnce, FnMut}; -use option::Option; -use option::Option::{None, Some}; -use result::Result::{Ok, Err}; -use slice::SliceExt; -use str::{FromStr, StrExt}; -use vec::Vec; - -pub type Port = u16; - -#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)] -pub enum IpAddr { - Ipv4Addr(u8, u8, u8, u8), - Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16) -} - -#[stable] -impl fmt::Display for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Ipv4Addr(a, b, c, d) => - write!(fmt, "{}.{}.{}.{}", a, b, c, d), - - // Ipv4 Compatible address - Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => { - write!(fmt, "::{}.{}.{}.{}", (g >> 8) as u8, g as u8, - (h >> 8) as u8, h as u8) - } - - // Ipv4-Mapped address - Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => { - write!(fmt, "::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8, - (h >> 8) as u8, h as u8) - } - - Ipv6Addr(a, b, c, d, e, f, g, h) => - write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - a, b, c, d, e, f, g, h) - } - } -} - -#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)] -pub struct SocketAddr { - pub ip: IpAddr, - pub port: Port, -} - -#[stable] -impl fmt::Display for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.ip { - Ipv4Addr(..) => write!(f, "{}:{}", self.ip, self.port), - Ipv6Addr(..) => write!(f, "[{}]:{}", self.ip, self.port), - } - } -} - -struct Parser<'a> { - // parsing as ASCII, so can use byte array - s: &'a [u8], - pos: uint, -} - -impl<'a> Parser<'a> { - fn new(s: &'a str) -> Parser<'a> { - Parser { - s: s.as_bytes(), - pos: 0, - } - } - - fn is_eof(&self) -> bool { - self.pos == self.s.len() - } - - // Commit only if parser returns Some - fn read_atomically(&mut self, cb: F) -> Option where - F: FnOnce(&mut Parser) -> Option, - { - let pos = self.pos; - let r = cb(self); - if r.is_none() { - self.pos = pos; - } - r - } - - // Commit only if parser read till EOF - fn read_till_eof(&mut self, cb: F) -> Option where - F: FnOnce(&mut Parser) -> Option, - { - self.read_atomically(move |p| { - match cb(p) { - Some(x) => if p.is_eof() {Some(x)} else {None}, - None => None, - } - }) - } - - // Return result of first successful parser - fn read_or(&mut self, parsers: &mut [Box Option>]) - -> Option { - for pf in parsers.iter_mut() { - match self.read_atomically(|p: &mut Parser| pf.call_mut((p,))) { - Some(r) => return Some(r), - None => {} - } - } - None - } - - // Apply 3 parsers sequentially - fn read_seq_3(&mut self, - pa: PA, - pb: PB, - pc: PC) - -> Option<(A, B, C)> where - PA: FnOnce(&mut Parser) -> Option, - PB: FnOnce(&mut Parser) -> Option, - PC: FnOnce(&mut Parser) -> Option, - { - self.read_atomically(move |p| { - let a = pa(p); - let b = if a.is_some() { pb(p) } else { None }; - let c = if b.is_some() { pc(p) } else { None }; - match (a, b, c) { - (Some(a), Some(b), Some(c)) => Some((a, b, c)), - _ => None - } - }) - } - - // Read next char - fn read_char(&mut self) -> Option { - if self.is_eof() { - None - } else { - let r = self.s[self.pos] as char; - self.pos += 1; - Some(r) - } - } - - // Return char and advance iff next char is equal to requested - fn read_given_char(&mut self, c: char) -> Option { - self.read_atomically(|p| { - match p.read_char() { - Some(next) if next == c => Some(next), - _ => None, - } - }) - } - - // Read digit - fn read_digit(&mut self, radix: u8) -> Option { - fn parse_digit(c: char, radix: u8) -> Option { - let c = c as u8; - // assuming radix is either 10 or 16 - if c >= b'0' && c <= b'9' { - Some(c - b'0') - } else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) { - Some(c - b'a' + 10) - } else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) { - Some(c - b'A' + 10) - } else { - None - } - } - - self.read_atomically(|p| { - p.read_char().and_then(|c| parse_digit(c, radix)) - }) - } - - fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option { - let mut r = 0u32; - let mut digit_count = 0; - loop { - match self.read_digit(radix) { - Some(d) => { - r = r * (radix as u32) + (d as u32); - digit_count += 1; - if digit_count > max_digits || r >= upto { - return None - } - } - None => { - if digit_count == 0 { - return None - } else { - return Some(r) - } - } - }; - } - } - - // Read number, failing if max_digits of number value exceeded - fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option { - self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto)) - } - - fn read_ipv4_addr_impl(&mut self) -> Option { - let mut bs = [0u8; 4]; - let mut i = 0; - while i < 4 { - if i != 0 && self.read_given_char('.').is_none() { - return None; - } - - let octet = self.read_number(10, 3, 0x100).map(|n| n as u8); - match octet { - Some(d) => bs[i] = d, - None => return None, - }; - i += 1; - } - Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3])) - } - - // Read IPv4 address - fn read_ipv4_addr(&mut self) -> Option { - self.read_atomically(|p| p.read_ipv4_addr_impl()) - } - - fn read_ipv6_addr_impl(&mut self) -> Option { - fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr { - assert!(head.len() + tail.len() <= 8); - let mut gs = [0u16; 8]; - gs.clone_from_slice(head); - gs[(8 - tail.len()) .. 8].clone_from_slice(tail); - Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) - } - - fn read_groups(p: &mut Parser, groups: &mut [u16; 8], limit: uint) -> (uint, bool) { - let mut i = 0; - while i < limit { - if i < limit - 1 { - let ipv4 = p.read_atomically(|p| { - if i == 0 || p.read_given_char(':').is_some() { - p.read_ipv4_addr() - } else { - None - } - }); - match ipv4 { - Some(Ipv4Addr(a, b, c, d)) => { - groups[i + 0] = ((a as u16) << 8) | (b as u16); - groups[i + 1] = ((c as u16) << 8) | (d as u16); - return (i + 2, true); - } - _ => {} - } - } - - let group = p.read_atomically(|p| { - if i == 0 || p.read_given_char(':').is_some() { - p.read_number(16, 4, 0x10000).map(|n| n as u16) - } else { - None - } - }); - match group { - Some(g) => groups[i] = g, - None => return (i, false) - } - i += 1; - } - (i, false) - } - - let mut head = [0u16; 8]; - let (head_size, head_ipv4) = read_groups(self, &mut head, 8); - - if head_size == 8 { - return Some(Ipv6Addr( - head[0], head[1], head[2], head[3], - head[4], head[5], head[6], head[7])) - } - - // IPv4 part is not allowed before `::` - if head_ipv4 { - return None - } - - // read `::` if previous code parsed less than 8 groups - if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() { - return None; - } - - let mut tail = [0u16; 8]; - let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size); - Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size])) - } - - fn read_ipv6_addr(&mut self) -> Option { - self.read_atomically(|p| p.read_ipv6_addr_impl()) - } - - fn read_ip_addr(&mut self) -> Option { - let ipv4_addr = |&mut: p: &mut Parser| p.read_ipv4_addr(); - let ipv6_addr = |&mut: p: &mut Parser| p.read_ipv6_addr(); - self.read_or(&mut [box ipv4_addr, box ipv6_addr]) - } - - fn read_socket_addr(&mut self) -> Option { - let ip_addr = |&: p: &mut Parser| { - let ipv4_p = |&mut: p: &mut Parser| p.read_ip_addr(); - let ipv6_p = |&mut: p: &mut Parser| { - let open_br = |&: p: &mut Parser| p.read_given_char('['); - let ip_addr = |&: p: &mut Parser| p.read_ipv6_addr(); - let clos_br = |&: p: &mut Parser| p.read_given_char(']'); - p.read_seq_3::(open_br, ip_addr, clos_br) - .map(|t| match t { (_, ip, _) => ip }) - }; - p.read_or(&mut [box ipv4_p, box ipv6_p]) - }; - let colon = |&: p: &mut Parser| p.read_given_char(':'); - let port = |&: p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16); - - // host, colon, port - self.read_seq_3::(ip_addr, colon, port) - .map(|t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } }) - } -} - -impl FromStr for IpAddr { - fn from_str(s: &str) -> Option { - Parser::new(s).read_till_eof(|p| p.read_ip_addr()) - } -} - -impl FromStr for SocketAddr { - fn from_str(s: &str) -> Option { - Parser::new(s).read_till_eof(|p| p.read_socket_addr()) - } -} - -/// A trait for objects which can be converted or resolved to one or more `SocketAddr` values. -/// -/// Implementing types minimally have to implement either `to_socket_addr` or `to_socket_addr_all` -/// method, and its trivial counterpart will be available automatically. -/// -/// This trait is used for generic address resolution when constructing network objects. -/// By default it is implemented for the following types: -/// -/// * `SocketAddr` - `to_socket_addr` is identity function. -/// -/// * `(IpAddr, u16)` - `to_socket_addr` constructs `SocketAddr` trivially. -/// -/// * `(&str, u16)` - the string should be either a string representation of an IP address -/// expected by `FromStr` implementation for `IpAddr` or a host name. -/// -/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding -/// to that IP address joined with the given port. -/// -/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses -/// for the host name, each joined with the given port. -/// -/// * `&str` - the string should be either a string representation of a `SocketAddr` as -/// expected by its `FromStr` implementation or a string like `:` pair -/// where `` is a `u16` value. -/// -/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding -/// to that socket address. -/// -/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses -/// for the host name, each joined with the port. -/// -/// -/// This trait allows constructing network objects like `TcpStream` or `UdpSocket` easily with -/// values of various types for the bind/connection address. It is needed because sometimes -/// one type is more appropriate than the other: for simple uses a string like `"localhost:12345"` -/// is much nicer than manual construction of the corresponding `SocketAddr`, but sometimes -/// `SocketAddr` value is *the* main source of the address, and converting it to some other type -/// (e.g. a string) just for it to be converted back to `SocketAddr` in constructor methods -/// is pointless. -/// -/// Some examples: -/// -/// ```rust,no_run -/// # #![allow(unused_must_use)] -/// -/// use std::io::{TcpStream, TcpListener}; -/// use std::io::net::udp::UdpSocket; -/// use std::io::net::ip::{Ipv4Addr, SocketAddr}; -/// -/// fn main() { -/// // The following lines are equivalent modulo possible "localhost" name resolution -/// // differences -/// let tcp_s = TcpStream::connect(SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 12345 }); -/// let tcp_s = TcpStream::connect((Ipv4Addr(127, 0, 0, 1), 12345u16)); -/// let tcp_s = TcpStream::connect(("127.0.0.1", 12345u16)); -/// let tcp_s = TcpStream::connect(("localhost", 12345u16)); -/// let tcp_s = TcpStream::connect("127.0.0.1:12345"); -/// let tcp_s = TcpStream::connect("localhost:12345"); -/// -/// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly -/// let tcp_l = TcpListener::bind("localhost:12345"); -/// -/// let mut udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)).unwrap(); -/// udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16)); -/// } -/// ``` -pub trait ToSocketAddr { - /// Converts this object to single socket address value. - /// - /// If more than one value is available, this method returns the first one. If no - /// values are available, this method returns an `IoError`. - /// - /// By default this method delegates to `to_socket_addr_all` method, taking the first - /// item from its result. - fn to_socket_addr(&self) -> IoResult { - self.to_socket_addr_all() - .and_then(|v| v.into_iter().next().ok_or_else(|| IoError { - kind: io::InvalidInput, - desc: "no address available", - detail: None - })) - } - - /// Converts this object to all available socket address values. - /// - /// Some values like host name string naturally correspond to multiple IP addresses. - /// This method tries to return all available addresses corresponding to this object. - /// - /// By default this method delegates to `to_socket_addr` method, creating a singleton - /// vector from its result. - #[inline] - fn to_socket_addr_all(&self) -> IoResult> { - self.to_socket_addr().map(|a| vec![a]) - } -} - -impl ToSocketAddr for SocketAddr { - #[inline] - fn to_socket_addr(&self) -> IoResult { Ok(*self) } -} - -impl ToSocketAddr for (IpAddr, u16) { - #[inline] - fn to_socket_addr(&self) -> IoResult { - let (ip, port) = *self; - Ok(SocketAddr { ip: ip, port: port }) - } -} - -fn resolve_socket_addr(s: &str, p: u16) -> IoResult> { - net::get_host_addresses(s) - .map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect()) -} - -fn parse_and_resolve_socket_addr(s: &str) -> IoResult> { - macro_rules! try_opt { - ($e:expr, $msg:expr) => ( - match $e { - Some(r) => r, - None => return Err(IoError { - kind: io::InvalidInput, - desc: $msg, - detail: None - }) - } - ) - } - - // split the string by ':' and convert the second part to u16 - let mut parts_iter = s.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); - let port: u16 = try_opt!(FromStr::from_str(port_str), "invalid port value"); - resolve_socket_addr(host, port) -} - -impl<'a> ToSocketAddr for (&'a str, u16) { - fn to_socket_addr_all(&self) -> IoResult> { - let (host, port) = *self; - - // try to parse the host as a regular IpAddr first - match FromStr::from_str(host) { - Some(addr) => return Ok(vec![SocketAddr { - ip: addr, - port: port - }]), - None => {} - } - - resolve_socket_addr(host, port) - } -} - -// accepts strings like 'localhost:12345' -impl<'a> ToSocketAddr for &'a str { - fn to_socket_addr(&self) -> IoResult { - // try to parse as a regular SocketAddr first - match FromStr::from_str(*self) { - Some(addr) => return Ok(addr), - None => {} - } - - parse_and_resolve_socket_addr(*self) - .and_then(|v| v.into_iter().next() - .ok_or_else(|| IoError { - kind: io::InvalidInput, - desc: "no address available", - detail: None - }) - ) - } - - fn to_socket_addr_all(&self) -> IoResult> { - // try to parse as a regular SocketAddr first - match FromStr::from_str(*self) { - Some(addr) => return Ok(vec![addr]), - None => {} - } - - parse_and_resolve_socket_addr(*self) - } -} - - -#[cfg(test)] -mod test { - use prelude::v1::*; - use super::*; - use str::FromStr; - - #[test] - fn test_from_str_ipv4() { - assert_eq!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1")); - assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255")); - assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0")); - - // out of range - let none: Option = FromStr::from_str("256.0.0.1"); - assert_eq!(None, none); - // too short - let none: Option = FromStr::from_str("255.0.0"); - assert_eq!(None, none); - // too long - let none: Option = FromStr::from_str("255.0.0.1.2"); - assert_eq!(None, none); - // no number between dots - let none: Option = FromStr::from_str("255.0..1"); - assert_eq!(None, none); - } - - #[test] - fn test_from_str_ipv6() { - assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0")); - assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1")); - - assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1")); - assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::")); - - assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), - FromStr::from_str("2a02:6b8::11:11")); - - // too long group - let none: Option = FromStr::from_str("::00000"); - assert_eq!(None, none); - // too short - let none: Option = FromStr::from_str("1:2:3:4:5:6:7"); - assert_eq!(None, none); - // too long - let none: Option = FromStr::from_str("1:2:3:4:5:6:7:8:9"); - assert_eq!(None, none); - // triple colon - let none: Option = FromStr::from_str("1:2:::6:7:8"); - assert_eq!(None, none); - // two double colons - let none: Option = FromStr::from_str("1:2::6::8"); - assert_eq!(None, none); - } - - #[test] - fn test_from_str_ipv4_in_ipv6() { - assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)), - FromStr::from_str("::192.0.2.33")); - assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), - FromStr::from_str("::FFFF:192.0.2.33")); - assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), - FromStr::from_str("64:ff9b::192.0.2.33")); - assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), - FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33")); - - // colon after v4 - let none: Option = FromStr::from_str("::127.0.0.1:"); - assert_eq!(None, none); - // not enough groups - let none: Option = FromStr::from_str("1.2.3.4.5:127.0.0.1"); - assert_eq!(None, none); - // too many groups - let none: Option = - FromStr::from_str("1.2.3.4.5:6:7:127.0.0.1"); - assert_eq!(None, none); - } - - #[test] - fn test_from_str_socket_addr() { - assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }), - FromStr::from_str("77.88.21.11:80")); - assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }), - FromStr::from_str("[2a02:6b8:0:1::1]:53")); - assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }), - FromStr::from_str("[::127.0.0.1]:22")); - - // without port - let none: Option = FromStr::from_str("127.0.0.1"); - assert_eq!(None, none); - // without port - let none: Option = FromStr::from_str("127.0.0.1:"); - assert_eq!(None, none); - // wrong brackets around v4 - let none: Option = FromStr::from_str("[127.0.0.1]:22"); - assert_eq!(None, none); - // port out of range - let none: Option = FromStr::from_str("127.0.0.1:123456"); - assert_eq!(None, none); - } - - #[test] - fn ipv6_addr_to_string() { - let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); - assert!(a1.to_string() == "::ffff:192.0.2.128" || - a1.to_string() == "::FFFF:192.0.2.128"); - assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(), - "8:9:a:b:c:d:e:f"); - } - - #[test] - fn to_socket_addr_socketaddr() { - let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 }; - assert_eq!(Ok(a), a.to_socket_addr()); - assert_eq!(Ok(vec![a]), a.to_socket_addr_all()); - } - - #[test] - fn to_socket_addr_ipaddr_u16() { - let a = Ipv4Addr(77, 88, 21, 11); - let p = 12345u16; - let e = SocketAddr { ip: a, port: p }; - assert_eq!(Ok(e), (a, p).to_socket_addr()); - assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all()); - } - - #[test] - fn to_socket_addr_str_u16() { - let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; - assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr()); - assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all()); - - let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; - assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr()); - assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all()); - - let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; - assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a)); - } - - #[test] - fn to_socket_addr_str() { - let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; - assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr()); - assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all()); - - let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; - assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr()); - assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all()); - - let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; - assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a)); - } -} diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs deleted file mode 100644 index 2056933e6df..00000000000 --- a/src/libstd/io/net/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Networking I/O - -use io::{IoError, IoResult, InvalidInput}; -use ops::FnMut; -use option::Option::None; -use result::Result::{Ok, Err}; -use self::ip::{SocketAddr, ToSocketAddr}; - -pub use self::addrinfo::get_host_addresses; - -pub mod addrinfo; -pub mod tcp; -pub mod udp; -pub mod ip; -pub mod pipe; - -fn with_addresses(addr: A, mut action: F) -> IoResult where - A: ToSocketAddr, - F: FnMut(SocketAddr) -> IoResult, -{ - const DEFAULT_ERROR: IoError = IoError { - kind: InvalidInput, - desc: "no addresses found for hostname", - detail: None - }; - - let addresses = try!(addr.to_socket_addr_all()); - let mut err = DEFAULT_ERROR; - for addr in addresses.into_iter() { - match action(addr) { - Ok(r) => return Ok(r), - Err(e) => err = e - } - } - Err(err) -} diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs deleted file mode 100644 index 61d164d21e3..00000000000 --- a/src/libstd/io/net/pipe.rs +++ /dev/null @@ -1,864 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Named pipes -//! -//! This module contains the ability to communicate over named pipes with -//! synchronous I/O. On windows, this corresponds to talking over a Named Pipe, -//! while on Unix it corresponds to UNIX domain sockets. -//! -//! These pipes are similar to TCP in the sense that you can have both a stream to a -//! server and a server itself. The server provided accepts other `UnixStream` -//! instances as clients. - -#![allow(missing_docs)] - -use prelude::v1::*; - -use ffi::CString; -use path::BytesContainer; -use io::{Listener, Acceptor, IoResult, TimedOut, standard_error}; -use sys::pipe::UnixAcceptor as UnixAcceptorImp; -use sys::pipe::UnixListener as UnixListenerImp; -use sys::pipe::UnixStream as UnixStreamImp; -use time::Duration; - -use sys_common; - -/// A stream which communicates over a named pipe. -pub struct UnixStream { - inner: UnixStreamImp, -} - -impl UnixStream { - - /// Connect to a pipe named by `path`. This will attempt to open a - /// connection to the underlying socket. - /// - /// The returned stream will be closed when the object falls out of scope. - /// - /// # Example - /// - /// ```rust - /// # #![allow(unused_must_use)] - /// use std::io::net::pipe::UnixStream; - /// - /// let server = Path::new("path/to/my/socket"); - /// let mut stream = UnixStream::connect(&server); - /// stream.write(&[1, 2, 3]); - /// ``` - pub fn connect(path: P) -> IoResult { - let path = CString::from_slice(path.container_as_bytes()); - UnixStreamImp::connect(&path, None) - .map(|inner| UnixStream { inner: inner }) - } - - /// Connect to a pipe named by `path`, timing out if the specified number of - /// milliseconds. - /// - /// This function is similar to `connect`, except that if `timeout` - /// elapses the function will return an error of kind `TimedOut`. - /// - /// If a `timeout` with zero or negative duration is specified then - /// the function returns `Err`, with the error kind set to `TimedOut`. - #[unstable = "the timeout argument is likely to change types"] - pub fn connect_timeout

(path: P, timeout: Duration) + -> IoResult + where P: BytesContainer { + if timeout <= Duration::milliseconds(0) { + return Err(standard_error(TimedOut)); + } + + let path = CString::from_slice(path.container_as_bytes()); + UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64)) + .map(|inner| UnixStream { inner: inner }) + } + + + /// Closes the reading half of this connection. + /// + /// This method will close the reading portion of this connection, causing + /// all pending and future reads to immediately return with an error. + /// + /// Note that this method affects all cloned handles associated with this + /// stream, not just this one handle. + pub fn close_read(&mut self) -> IoResult<()> { + self.inner.close_read() + } + + /// Closes the writing half of this connection. + /// + /// This method will close the writing portion of this connection, causing + /// all pending and future writes to immediately return with an error. + /// + /// Note that this method affects all cloned handles associated with this + /// stream, not just this one handle. + pub fn close_write(&mut self) -> IoResult<()> { + self.inner.close_write() + } + + /// Sets the read/write timeout for this socket. + /// + /// For more information, see `TcpStream::set_timeout` + #[unstable = "the timeout argument may change in type and value"] + pub fn set_timeout(&mut self, timeout_ms: Option) { + self.inner.set_timeout(timeout_ms) + } + + /// Sets the read timeout for this socket. + /// + /// For more information, see `TcpStream::set_timeout` + #[unstable = "the timeout argument may change in type and value"] + pub fn set_read_timeout(&mut self, timeout_ms: Option) { + self.inner.set_read_timeout(timeout_ms) + } + + /// Sets the write timeout for this socket. + /// + /// For more information, see `TcpStream::set_timeout` + #[unstable = "the timeout argument may change in type and value"] + pub fn set_write_timeout(&mut self, timeout_ms: Option) { + self.inner.set_write_timeout(timeout_ms) + } +} + +impl Clone for UnixStream { + fn clone(&self) -> UnixStream { + UnixStream { inner: self.inner.clone() } + } +} + +impl Reader for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner.read(buf) + } +} + +impl Writer for UnixStream { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.inner.write(buf) + } +} + +impl sys_common::AsInner for UnixStream { + fn as_inner(&self) -> &UnixStreamImp { + &self.inner + } +} + +/// A value that can listen for incoming named pipe connection requests. +pub struct UnixListener { + /// The internal, opaque runtime Unix listener. + inner: UnixListenerImp, +} + +impl UnixListener { + /// Creates a new listener, ready to receive incoming connections on the + /// specified socket. The server will be named by `path`. + /// + /// This listener will be closed when it falls out of scope. + /// + /// # Example + /// + /// ``` + /// # fn foo() { + /// use std::old_io::net::pipe::UnixListener; + /// use std::old_io::{Listener, Acceptor}; + /// + /// let server = Path::new("/path/to/my/socket"); + /// let stream = UnixListener::bind(&server); + /// for mut client in stream.listen().incoming() { + /// client.write(&[1, 2, 3, 4]); + /// } + /// # } + /// ``` + pub fn bind(path: P) -> IoResult { + let path = CString::from_slice(path.container_as_bytes()); + UnixListenerImp::bind(&path) + .map(|inner| UnixListener { inner: inner }) + } +} + +impl Listener for UnixListener { + fn listen(self) -> IoResult { + self.inner.listen() + .map(|inner| UnixAcceptor { inner: inner }) + } +} + +impl sys_common::AsInner for UnixListener { + fn as_inner(&self) -> &UnixListenerImp { + &self.inner + } +} + +/// A value that can accept named pipe connections, returned from `listen()`. +pub struct UnixAcceptor { + /// The internal, opaque runtime Unix acceptor. + inner: UnixAcceptorImp +} + +impl UnixAcceptor { + /// Sets a timeout for this acceptor, after which accept() will no longer + /// block indefinitely. + /// + /// The argument specified is the amount of time, in milliseconds, into the + /// future after which all invocations of accept() will not block (and any + /// pending invocation will return). A value of `None` will clear any + /// existing timeout. + /// + /// When using this method, it is likely necessary to reset the timeout as + /// appropriate, the timeout specified is specific to this object, not + /// specific to the next request. + #[unstable = "the name and arguments to this function are likely \ + to change"] + pub fn set_timeout(&mut self, timeout_ms: Option) { + self.inner.set_timeout(timeout_ms) + } + + /// Closes the accepting capabilities of this acceptor. + /// + /// This function has the same semantics as `TcpAcceptor::close_accept`, and + /// more information can be found in that documentation. + #[unstable] + pub fn close_accept(&mut self) -> IoResult<()> { + self.inner.close_accept() + } +} + +impl Acceptor for UnixAcceptor { + fn accept(&mut self) -> IoResult { + self.inner.accept().map(|s| { + UnixStream { inner: s } + }) + } +} + +impl Clone for UnixAcceptor { + /// Creates a new handle to this unix acceptor, allowing for simultaneous + /// accepts. + /// + /// The underlying unix acceptor will not be closed until all handles to the + /// acceptor have been deallocated. Incoming connections will be received on + /// at most once acceptor, the same connection will not be accepted twice. + /// + /// The `close_accept` method will shut down *all* acceptors cloned from the + /// same original acceptor, whereas the `set_timeout` method only affects + /// the selector that it is called on. + /// + /// This function is useful for creating a handle to invoke `close_accept` + /// on to wake up any other task blocked in `accept`. + fn clone(&self) -> UnixAcceptor { + UnixAcceptor { inner: self.inner.clone() } + } +} + +impl sys_common::AsInner for UnixAcceptor { + fn as_inner(&self) -> &UnixAcceptorImp { + &self.inner + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + + use old_io::fs::PathExtensions; + use old_io::{EndOfFile, TimedOut, ShortWrite, IoError, ConnectionReset}; + use old_io::{NotConnected, BrokenPipe, FileNotFound, InvalidInput, OtherIoError}; + use old_io::{PermissionDenied, Acceptor, Listener}; + use old_io::test::*; + use super::*; + use sync::mpsc::channel; + use thread::Thread; + use time::Duration; + + pub fn smalltest(server: F, client: G) + where F : FnOnce(UnixStream), F : Send, + G : FnOnce(UnixStream), G : Send + { + let path1 = next_test_unix(); + let path2 = path1.clone(); + + let mut acceptor = UnixListener::bind(&path1).listen(); + + let _t = Thread::spawn(move|| { + match UnixStream::connect(&path2) { + Ok(c) => client(c), + Err(e) => panic!("failed connect: {}", e), + } + }); + + match acceptor.accept() { + Ok(c) => server(c), + Err(e) => panic!("failed accept: {}", e), + } + } + + #[test] + fn bind_error() { + let path = "path/to/nowhere"; + match UnixListener::bind(&path) { + Ok(..) => panic!(), + Err(e) => { + assert!(e.kind == PermissionDenied || e.kind == FileNotFound || + e.kind == InvalidInput); + } + } + } + + #[test] + fn connect_error() { + let path = if cfg!(windows) { + r"\\.\pipe\this_should_not_exist_ever" + } else { + "path/to/nowhere" + }; + match UnixStream::connect(&path) { + Ok(..) => panic!(), + Err(e) => { + assert!(e.kind == FileNotFound || e.kind == OtherIoError); + } + } + } + + #[test] + fn smoke() { + smalltest(move |mut server| { + let mut buf = [0]; + server.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + }, move|mut client| { + client.write(&[99]).unwrap(); + }) + } + + #[cfg_attr(windows, ignore)] // FIXME(#12516) + #[test] + fn read_eof() { + smalltest(move|mut server| { + let mut buf = [0]; + assert!(server.read(&mut buf).is_err()); + assert!(server.read(&mut buf).is_err()); + }, move|_client| { + // drop the client + }) + } + + #[test] + fn write_begone() { + smalltest(move|mut server| { + let buf = [0]; + loop { + match server.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!(e.kind == BrokenPipe || + e.kind == NotConnected || + e.kind == ConnectionReset, + "unknown error {}", e); + break; + } + } + } + }, move|_client| { + // drop the client + }) + } + + #[test] + fn accept_lots() { + let times = 10; + let path1 = next_test_unix(); + let path2 = path1.clone(); + + let mut acceptor = match UnixListener::bind(&path1).listen() { + Ok(a) => a, + Err(e) => panic!("failed listen: {}", e), + }; + + let _t = Thread::spawn(move|| { + for _ in range(0u, times) { + let mut stream = UnixStream::connect(&path2); + match stream.write(&[100]) { + Ok(..) => {} + Err(e) => panic!("failed write: {}", e) + } + } + }); + + for _ in range(0, times) { + let mut client = acceptor.accept(); + let mut buf = [0]; + match client.read(&mut buf) { + Ok(..) => {} + Err(e) => panic!("failed read/accept: {}", e), + } + assert_eq!(buf[0], 100); + } + } + + #[cfg(unix)] + #[test] + fn path_exists() { + let path = next_test_unix(); + let _acceptor = UnixListener::bind(&path).listen(); + assert!(path.exists()); + } + + #[test] + fn unix_clone_smoke() { + let addr = next_test_unix(); + let mut acceptor = UnixListener::bind(&addr).listen(); + + let _t = Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr); + let mut buf = [0, 0]; + debug!("client reading"); + assert_eq!(s.read(&mut buf), Ok(1)); + assert_eq!(buf[0], 1); + debug!("client writing"); + s.write(&[2]).unwrap(); + debug!("client dropping"); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + rx1.recv().unwrap(); + debug!("writer writing"); + s2.write(&[1]).unwrap(); + debug!("writer done"); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + debug!("reader reading"); + assert_eq!(s1.read(&mut buf), Ok(1)); + debug!("reader done"); + rx2.recv().unwrap(); + } + + #[test] + fn unix_clone_two_read() { + let addr = next_test_unix(); + let mut acceptor = UnixListener::bind(&addr).listen(); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr); + s.write(&[1]).unwrap(); + rx.recv().unwrap(); + s.write(&[2]).unwrap(); + rx.recv().unwrap(); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (done, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + let mut buf = [0, 0]; + s2.read(&mut buf).unwrap(); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + s1.read(&mut buf).unwrap(); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + } + + #[test] + fn unix_clone_two_write() { + let addr = next_test_unix(); + let mut acceptor = UnixListener::bind(&addr).listen(); + + let _t = Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr); + let buf = &mut [0, 1]; + s.read(buf).unwrap(); + s.read(buf).unwrap(); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + s2.write(&[1]).unwrap(); + tx.send(()).unwrap(); + }); + s1.write(&[2]).unwrap(); + + rx.recv().unwrap(); + } + + #[cfg(not(windows))] + #[test] + fn drop_removes_listener_path() { + let path = next_test_unix(); + let l = UnixListener::bind(&path).unwrap(); + assert!(path.exists()); + drop(l); + assert!(!path.exists()); + } + + #[cfg(not(windows))] + #[test] + fn drop_removes_acceptor_path() { + let path = next_test_unix(); + let l = UnixListener::bind(&path).unwrap(); + assert!(path.exists()); + drop(l.listen().unwrap()); + assert!(!path.exists()); + } + + #[test] + fn accept_timeout() { + let addr = next_test_unix(); + let mut a = UnixListener::bind(&addr).unwrap().listen().unwrap(); + + a.set_timeout(Some(10)); + + // Make sure we time out once and future invocations also time out + let err = a.accept().err().unwrap(); + assert_eq!(err.kind, TimedOut); + let err = a.accept().err().unwrap(); + assert_eq!(err.kind, TimedOut); + + // Also make sure that even though the timeout is expired that we will + // continue to receive any pending connections. + let (tx, rx) = channel(); + let addr2 = addr.clone(); + let _t = Thread::spawn(move|| { + tx.send(UnixStream::connect(&addr2).unwrap()).unwrap(); + }); + let l = rx.recv().unwrap(); + for i in range(0u, 1001) { + match a.accept() { + Ok(..) => break, + Err(ref e) if e.kind == TimedOut => {} + Err(e) => panic!("error: {}", e), + } + ::thread::Thread::yield_now(); + if i == 1000 { panic!("should have a pending connection") } + } + drop(l); + + // Unset the timeout and make sure that this always blocks. + a.set_timeout(None); + let addr2 = addr.clone(); + let _t = Thread::spawn(move|| { + drop(UnixStream::connect(&addr2).unwrap()); + }); + a.accept().unwrap(); + } + + #[test] + fn connect_timeout_error() { + let addr = next_test_unix(); + assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_err()); + } + + #[test] + fn connect_timeout_success() { + let addr = next_test_unix(); + let _a = UnixListener::bind(&addr).unwrap().listen().unwrap(); + assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_ok()); + } + + #[test] + fn connect_timeout_zero() { + let addr = next_test_unix(); + let _a = UnixListener::bind(&addr).unwrap().listen().unwrap(); + assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(0)).is_err()); + } + + #[test] + fn connect_timeout_negative() { + let addr = next_test_unix(); + let _a = UnixListener::bind(&addr).unwrap().listen().unwrap(); + assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(-1)).is_err()); + } + + #[test] + fn close_readwrite_smoke() { + let addr = next_test_unix(); + let a = UnixListener::bind(&addr).listen().unwrap(); + let (_tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut a = a; + let _s = a.accept().unwrap(); + let _ = rx.recv(); + }); + + let mut b = [0]; + let mut s = UnixStream::connect(&addr).unwrap(); + let mut s2 = s.clone(); + + // closing should prevent reads/writes + s.close_write().unwrap(); + assert!(s.write(&[0]).is_err()); + s.close_read().unwrap(); + assert!(s.read(&mut b).is_err()); + + // closing should affect previous handles + assert!(s2.write(&[0]).is_err()); + assert!(s2.read(&mut b).is_err()); + + // closing should affect new handles + let mut s3 = s.clone(); + assert!(s3.write(&[0]).is_err()); + assert!(s3.read(&mut b).is_err()); + + // make sure these don't die + let _ = s2.close_read(); + let _ = s2.close_write(); + let _ = s3.close_read(); + let _ = s3.close_write(); + } + + #[test] + fn close_read_wakes_up() { + let addr = next_test_unix(); + let a = UnixListener::bind(&addr).listen().unwrap(); + let (_tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut a = a; + let _s = a.accept().unwrap(); + let _ = rx.recv(); + }); + + let mut s = UnixStream::connect(&addr).unwrap(); + let s2 = s.clone(); + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + assert!(s2.read(&mut [0]).is_err()); + tx.send(()).unwrap(); + }); + // this should wake up the child task + s.close_read().unwrap(); + + // this test will never finish if the child doesn't wake up + rx.recv().unwrap(); + } + + #[test] + fn readwrite_timeouts() { + let addr = next_test_unix(); + let mut a = UnixListener::bind(&addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr).unwrap(); + rx.recv().unwrap(); + assert!(s.write(&[0]).is_ok()); + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + s.set_timeout(Some(20)); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + + s.set_timeout(Some(20)); + for i in range(0u, 1001) { + match s.write(&[0; 128 * 1024]) { + Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, + Err(IoError { kind: TimedOut, .. }) => break, + Err(e) => panic!("{}", e), + } + if i == 1000 { panic!("should have filled up?!"); } + } + + // I'm not sure as to why, but apparently the write on windows always + // succeeds after the previous timeout. Who knows? + if !cfg!(windows) { + assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); + } + + tx.send(()).unwrap(); + s.set_timeout(None); + assert_eq!(s.read(&mut [0, 0]), Ok(1)); + } + + #[test] + fn read_timeouts() { + let addr = next_test_unix(); + let mut a = UnixListener::bind(&addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr).unwrap(); + rx.recv().unwrap(); + let mut amt = 0; + while amt < 100 * 128 * 1024 { + match s.read(&mut [0;128 * 1024]) { + Ok(n) => { amt += n; } + Err(e) => panic!("{}", e), + } + } + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + s.set_read_timeout(Some(20)); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + + tx.send(()).unwrap(); + for _ in range(0u, 100) { + assert!(s.write(&[0;128 * 1024]).is_ok()); + } + } + + #[test] + fn write_timeouts() { + let addr = next_test_unix(); + let mut a = UnixListener::bind(&addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr).unwrap(); + rx.recv().unwrap(); + assert!(s.write(&[0]).is_ok()); + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + s.set_write_timeout(Some(20)); + for i in range(0u, 1001) { + match s.write(&[0; 128 * 1024]) { + Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, + Err(IoError { kind: TimedOut, .. }) => break, + Err(e) => panic!("{}", e), + } + if i == 1000 { panic!("should have filled up?!"); } + } + + tx.send(()).unwrap(); + assert!(s.read(&mut [0]).is_ok()); + } + + #[test] + fn timeout_concurrent_read() { + let addr = next_test_unix(); + let mut a = UnixListener::bind(&addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = UnixStream::connect(&addr).unwrap(); + rx.recv().unwrap(); + assert!(s.write(&[0]).is_ok()); + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + let s2 = s.clone(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + assert!(s2.read(&mut [0]).is_ok()); + tx2.send(()).unwrap(); + }); + + s.set_read_timeout(Some(20)); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + tx.send(()).unwrap(); + + rx2.recv().unwrap(); + } + + #[cfg(not(windows))] + #[test] + fn clone_accept_smoke() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let mut a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let addr2 = addr.clone(); + let _t = Thread::spawn(move|| { + let _ = UnixStream::connect(&addr2); + }); + let _t = Thread::spawn(move|| { + let _ = UnixStream::connect(&addr); + }); + + assert!(a.accept().is_ok()); + drop(a); + assert!(a2.accept().is_ok()); + } + + #[cfg(not(windows))] // FIXME #17553 + #[test] + fn clone_accept_concurrent() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let a = l.listen().unwrap(); + let a2 = a.clone(); + + let (tx, rx) = channel(); + let tx2 = tx.clone(); + + let _t = Thread::spawn(move|| { + let mut a = a; + tx.send(a.accept()).unwrap() + }); + let _t = Thread::spawn(move|| { + let mut a = a2; + tx2.send(a.accept()).unwrap() + }); + + let addr2 = addr.clone(); + let _t = Thread::spawn(move|| { + let _ = UnixStream::connect(&addr2); + }); + let _t = Thread::spawn(move|| { + let _ = UnixStream::connect(&addr); + }); + + assert!(rx.recv().unwrap().is_ok()); + assert!(rx.recv().unwrap().is_ok()); + } + + #[test] + fn close_accept_smoke() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let mut a = l.listen().unwrap(); + + a.close_accept().unwrap(); + assert_eq!(a.accept().err().unwrap().kind, EndOfFile); + } + + #[test] + fn close_accept_concurrent() { + let addr = next_test_unix(); + let l = UnixListener::bind(&addr); + let a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut a = a; + tx.send(a.accept()).unwrap(); + }); + a2.close_accept().unwrap(); + + assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile); + } +} diff --git a/src/libstd/old_io/net/tcp.rs b/src/libstd/old_io/net/tcp.rs new file mode 100644 index 00000000000..62f3c02e98f --- /dev/null +++ b/src/libstd/old_io/net/tcp.rs @@ -0,0 +1,1475 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! TCP network connections +//! +//! This module contains the ability to open a TCP stream to a socket address, +//! as well as creating a socket server to accept incoming connections. The +//! destination and binding addresses can either be an IPv4 or IPv6 address. +//! +//! A TCP connection implements the `Reader` and `Writer` traits, while the TCP +//! listener (socket server) implements the `Listener` and `Acceptor` traits. + +use clone::Clone; +use old_io::IoResult; +use result::Result::Err; +use old_io::net::ip::{SocketAddr, ToSocketAddr}; +use old_io::{Reader, Writer, Listener, Acceptor}; +use old_io::{standard_error, TimedOut}; +use option::Option; +use option::Option::{None, Some}; +use time::Duration; + +use sys::tcp::TcpStream as TcpStreamImp; +use sys::tcp::TcpListener as TcpListenerImp; +use sys::tcp::TcpAcceptor as TcpAcceptorImp; + +use sys_common; + +/// A structure which represents a TCP stream between a local socket and a +/// remote socket. +/// +/// The socket will be closed when the value is dropped. +/// +/// # Example +/// +/// ```no_run +/// use std::old_io::TcpStream; +/// +/// { +/// let mut stream = TcpStream::connect("127.0.0.1:34254"); +/// +/// // ignore the Result +/// let _ = stream.write(&[1]); +/// +/// let mut buf = [0]; +/// let _ = stream.read(&mut buf); // ignore here too +/// } // the stream is closed here +/// ``` +pub struct TcpStream { + inner: TcpStreamImp, +} + +impl TcpStream { + fn new(s: TcpStreamImp) -> TcpStream { + TcpStream { inner: s } + } + + /// Open a TCP connection to a remote host. + /// + /// `addr` is an address of the remote host. Anything which implements `ToSocketAddr` + /// trait can be supplied for the address; see this trait documentation for + /// concrete examples. + pub fn connect(addr: A) -> IoResult { + super::with_addresses(addr, |addr| { + TcpStreamImp::connect(addr, None).map(TcpStream::new) + }) + } + + /// Creates a TCP connection to a remote socket address, timing out after + /// the specified duration. + /// + /// This is the same as the `connect` method, except that if the timeout + /// specified elapses before a connection is made an error will be + /// returned. The error's kind will be `TimedOut`. + /// + /// Same as the `connect` method, `addr` argument type can be anything which + /// implements `ToSocketAddr` trait. + /// + /// If a `timeout` with zero or negative duration is specified then + /// the function returns `Err`, with the error kind set to `TimedOut`. + #[unstable = "the timeout argument may eventually change types"] + pub fn connect_timeout(addr: A, + timeout: Duration) -> IoResult { + if timeout <= Duration::milliseconds(0) { + return Err(standard_error(TimedOut)); + } + + super::with_addresses(addr, |addr| { + TcpStreamImp::connect(addr, Some(timeout.num_milliseconds() as u64)) + .map(TcpStream::new) + }) + } + + /// Returns the socket address of the remote peer of this TCP connection. + pub fn peer_name(&mut self) -> IoResult { + self.inner.peer_name() + } + + /// Returns the socket address of the local half of this TCP connection. + pub fn socket_name(&mut self) -> IoResult { + self.inner.socket_name() + } + + /// Sets the nodelay flag on this connection to the boolean specified + #[unstable] + pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> { + self.inner.set_nodelay(nodelay) + } + + /// Sets the keepalive timeout to the timeout specified. + /// + /// If the value specified is `None`, then the keepalive flag is cleared on + /// this connection. Otherwise, the keepalive timeout will be set to the + /// specified time, in seconds. + #[unstable] + pub fn set_keepalive(&mut self, delay_in_seconds: Option) -> IoResult<()> { + self.inner.set_keepalive(delay_in_seconds) + } + + /// Closes the reading half of this connection. + /// + /// This method will close the reading portion of this connection, causing + /// all pending and future reads to immediately return with an error. + /// + /// # Example + /// + /// ```no_run + /// # #![allow(unused_must_use)] + /// use std::old_io::timer; + /// use std::old_io::TcpStream; + /// use std::time::Duration; + /// use std::thread::Thread; + /// + /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); + /// let stream2 = stream.clone(); + /// + /// let _t = Thread::spawn(move|| { + /// // close this stream after one second + /// timer::sleep(Duration::seconds(1)); + /// let mut stream = stream2; + /// stream.close_read(); + /// }); + /// + /// // wait for some data, will get canceled after one second + /// let mut buf = [0]; + /// stream.read(&mut buf); + /// ``` + /// + /// Note that this method affects all cloned handles associated with this + /// stream, not just this one handle. + pub fn close_read(&mut self) -> IoResult<()> { + self.inner.close_read() + } + + /// Closes the writing half of this connection. + /// + /// This method will close the writing portion of this connection, causing + /// all future writes to immediately return with an error. + /// + /// Note that this method affects all cloned handles associated with this + /// stream, not just this one handle. + pub fn close_write(&mut self) -> IoResult<()> { + self.inner.close_write() + } + + /// Sets a timeout, in milliseconds, for blocking operations on this stream. + /// + /// This function will set a timeout for all blocking operations (including + /// reads and writes) on this stream. The timeout specified is a relative + /// time, in milliseconds, into the future after which point operations will + /// time out. This means that the timeout must be reset periodically to keep + /// it from expiring. Specifying a value of `None` will clear the timeout + /// for this stream. + /// + /// The timeout on this stream is local to this stream only. Setting a + /// timeout does not affect any other cloned instances of this stream, nor + /// does the timeout propagated to cloned handles of this stream. Setting + /// this timeout will override any specific read or write timeouts + /// previously set for this stream. + /// + /// For clarification on the semantics of interrupting a read and a write, + /// take a look at `set_read_timeout` and `set_write_timeout`. + #[unstable = "the timeout argument may change in type and value"] + pub fn set_timeout(&mut self, timeout_ms: Option) { + self.inner.set_timeout(timeout_ms) + } + + /// Sets the timeout for read operations on this stream. + /// + /// See documentation in `set_timeout` for the semantics of this read time. + /// This will overwrite any previous read timeout set through either this + /// function or `set_timeout`. + /// + /// # Errors + /// + /// When this timeout expires, if there is no pending read operation, no + /// action is taken. Otherwise, the read operation will be scheduled to + /// promptly return. If a timeout error is returned, then no data was read + /// during the timeout period. + #[unstable = "the timeout argument may change in type and value"] + pub fn set_read_timeout(&mut self, timeout_ms: Option) { + self.inner.set_read_timeout(timeout_ms) + } + + /// Sets the timeout for write operations on this stream. + /// + /// See documentation in `set_timeout` for the semantics of this write time. + /// This will overwrite any previous write timeout set through either this + /// function or `set_timeout`. + /// + /// # Errors + /// + /// When this timeout expires, if there is no pending write operation, no + /// action is taken. Otherwise, the pending write operation will be + /// scheduled to promptly return. The actual state of the underlying stream + /// is not specified. + /// + /// The write operation may return an error of type `ShortWrite` which + /// indicates that the object is known to have written an exact number of + /// bytes successfully during the timeout period, and the remaining bytes + /// were never written. + /// + /// If the write operation returns `TimedOut`, then it the timeout primitive + /// does not know how many bytes were written as part of the timeout + /// operation. It may be the case that bytes continue to be written in an + /// asynchronous fashion after the call to write returns. + #[unstable = "the timeout argument may change in type and value"] + pub fn set_write_timeout(&mut self, timeout_ms: Option) { + self.inner.set_write_timeout(timeout_ms) + } +} + +impl Clone for TcpStream { + /// Creates a new handle to this TCP stream, allowing for simultaneous reads + /// and writes of this connection. + /// + /// The underlying TCP stream will not be closed until all handles to the + /// stream have been deallocated. All handles will also follow the same + /// stream, but two concurrent reads will not receive the same data. + /// Instead, the first read will receive the first packet received, and the + /// second read will receive the second packet. + fn clone(&self) -> TcpStream { + TcpStream { inner: self.inner.clone() } + } +} + +impl Reader for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner.read(buf) + } +} + +impl Writer for TcpStream { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.inner.write(buf) + } +} + +impl sys_common::AsInner for TcpStream { + fn as_inner(&self) -> &TcpStreamImp { + &self.inner + } +} + +/// A structure representing a socket server. This listener is used to create a +/// `TcpAcceptor` which can be used to accept sockets on a local port. +/// +/// # Examples +/// +/// ``` +/// # fn foo() { +/// use std::old_io::{TcpListener, TcpStream}; +/// use std::old_io::{Acceptor, Listener}; +/// use std::thread::Thread; +/// +/// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); +/// +/// // bind the listener to the specified address +/// let mut acceptor = listener.listen().unwrap(); +/// +/// fn handle_client(mut stream: TcpStream) { +/// // ... +/// # &mut stream; // silence unused mutability/variable warning +/// } +/// // accept connections and process them, spawning a new tasks for each one +/// for stream in acceptor.incoming() { +/// match stream { +/// Err(e) => { /* connection failed */ } +/// Ok(stream) => { +/// Thread::spawn(move|| { +/// // connection succeeded +/// handle_client(stream) +/// }); +/// } +/// } +/// } +/// +/// // close the socket server +/// drop(acceptor); +/// # } +/// ``` +pub struct TcpListener { + inner: TcpListenerImp, +} + +impl TcpListener { + /// Creates a new `TcpListener` which will be bound to the specified address. + /// This listener is not ready for accepting connections, `listen` must be called + /// on it before that's possible. + /// + /// Binding with a port number of 0 will request that the OS assigns a port + /// to this listener. The port allocated can be queried via the + /// `socket_name` function. + /// + /// The address type can be any implementer of `ToSocketAddr` trait. See its + /// documentation for concrete examples. + pub fn bind(addr: A) -> IoResult { + super::with_addresses(addr, |addr| { + TcpListenerImp::bind(addr).map(|inner| TcpListener { inner: inner }) + }) + } + + /// Returns the local socket address of this listener. + pub fn socket_name(&mut self) -> IoResult { + self.inner.socket_name() + } +} + +impl Listener for TcpListener { + fn listen(self) -> IoResult { + self.inner.listen(128).map(|a| TcpAcceptor { inner: a }) + } +} + +impl sys_common::AsInner for TcpListener { + fn as_inner(&self) -> &TcpListenerImp { + &self.inner + } +} + +/// The accepting half of a TCP socket server. This structure is created through +/// a `TcpListener`'s `listen` method, and this object can be used to accept new +/// `TcpStream` instances. +pub struct TcpAcceptor { + inner: TcpAcceptorImp, +} + +impl TcpAcceptor { + /// Prevents blocking on all future accepts after `ms` milliseconds have + /// elapsed. + /// + /// This function is used to set a deadline after which this acceptor will + /// time out accepting any connections. The argument is the relative + /// distance, in milliseconds, to a point in the future after which all + /// accepts will fail. + /// + /// If the argument specified is `None`, then any previously registered + /// timeout is cleared. + /// + /// A timeout of `0` can be used to "poll" this acceptor to see if it has + /// any pending connections. All pending connections will be accepted, + /// regardless of whether the timeout has expired or not (the accept will + /// not block in this case). + /// + /// # Example + /// + /// ```no_run + /// # #![allow(unstable)] + /// use std::old_io::TcpListener; + /// use std::old_io::{Listener, Acceptor, TimedOut}; + /// + /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); + /// + /// // After 100ms have passed, all accepts will fail + /// a.set_timeout(Some(100)); + /// + /// match a.accept() { + /// Ok(..) => println!("accepted a socket"), + /// Err(ref e) if e.kind == TimedOut => { println!("timed out!"); } + /// Err(e) => println!("err: {}", e), + /// } + /// + /// // Reset the timeout and try again + /// a.set_timeout(Some(100)); + /// let socket = a.accept(); + /// + /// // Clear the timeout and block indefinitely waiting for a connection + /// a.set_timeout(None); + /// let socket = a.accept(); + /// ``` + #[unstable = "the type of the argument and name of this function are \ + subject to change"] + pub fn set_timeout(&mut self, ms: Option) { self.inner.set_timeout(ms); } + + /// Closes the accepting capabilities of this acceptor. + /// + /// This function is similar to `TcpStream`'s `close_{read,write}` methods + /// in that it will affect *all* cloned handles of this acceptor's original + /// handle. + /// + /// Once this function succeeds, all future calls to `accept` will return + /// immediately with an error, preventing all future calls to accept. The + /// underlying socket will not be relinquished back to the OS until all + /// acceptors have been deallocated. + /// + /// This is useful for waking up a thread in an accept loop to indicate that + /// it should exit. + /// + /// # Example + /// + /// ``` + /// # #![allow(unstable)] + /// use std::old_io::{TcpListener, Listener, Acceptor, EndOfFile}; + /// use std::thread::Thread; + /// + /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); + /// let a2 = a.clone(); + /// + /// let _t = Thread::spawn(move|| { + /// let mut a2 = a2; + /// for socket in a2.incoming() { + /// match socket { + /// Ok(s) => { /* handle s */ } + /// Err(ref e) if e.kind == EndOfFile => break, // closed + /// Err(e) => panic!("unexpected error: {}", e), + /// } + /// } + /// }); + /// + /// # fn wait_for_sigint() {} + /// // Now that our accept loop is running, wait for the program to be + /// // requested to exit. + /// wait_for_sigint(); + /// + /// // Signal our accept loop to exit + /// assert!(a.close_accept().is_ok()); + /// ``` + #[unstable] + pub fn close_accept(&mut self) -> IoResult<()> { + self.inner.close_accept() + } +} + +impl Acceptor for TcpAcceptor { + fn accept(&mut self) -> IoResult { + self.inner.accept().map(TcpStream::new) + } +} + +impl Clone for TcpAcceptor { + /// Creates a new handle to this TCP acceptor, allowing for simultaneous + /// accepts. + /// + /// The underlying TCP acceptor will not be closed until all handles to the + /// acceptor have been deallocated. Incoming connections will be received on + /// at most once acceptor, the same connection will not be accepted twice. + /// + /// The `close_accept` method will shut down *all* acceptors cloned from the + /// same original acceptor, whereas the `set_timeout` method only affects + /// the selector that it is called on. + /// + /// This function is useful for creating a handle to invoke `close_accept` + /// on to wake up any other task blocked in `accept`. + fn clone(&self) -> TcpAcceptor { + TcpAcceptor { inner: self.inner.clone() } + } +} + +impl sys_common::AsInner for TcpAcceptor { + fn as_inner(&self) -> &TcpAcceptorImp { + &self.inner + } +} + +#[cfg(test)] +#[allow(unstable)] +mod test { + use prelude::v1::*; + + use sync::mpsc::channel; + use thread::Thread; + use old_io::net::tcp::*; + use old_io::net::ip::*; + use old_io::test::*; + use old_io::{EndOfFile, TimedOut, ShortWrite, IoError}; + use old_io::{ConnectionRefused, BrokenPipe, ConnectionAborted}; + use old_io::{ConnectionReset, NotConnected, PermissionDenied, OtherIoError}; + use old_io::{Acceptor, Listener}; + + // FIXME #11530 this fails on android because tests are run as root + #[cfg_attr(any(windows, target_os = "android"), ignore)] + #[test] + fn bind_error() { + match TcpListener::bind("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, PermissionDenied), + } + } + + #[test] + fn connect_error() { + match TcpStream::connect("0.0.0.0:1") { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, ConnectionRefused), + } + } + + #[test] + fn listen_ip4_localhost() { + let socket_addr = next_test_ip4(); + let listener = TcpListener::bind(socket_addr); + let mut acceptor = listener.listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("localhost", socket_addr.port)); + stream.write(&[144]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 144); + } + + #[test] + fn connect_localhost() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("localhost", addr.port)); + stream.write(&[64]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 64); + } + + #[test] + fn connect_ip4_loopback() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("127.0.0.1", addr.port)); + stream.write(&[44]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 44); + } + + #[test] + fn connect_ip6_loopback() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(("::1", addr.port)); + stream.write(&[66]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 66); + } + + #[test] + fn smoke_test_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + } + + #[test] + fn smoke_test_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + } + + #[test] + fn read_eof_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + } + + #[test] + fn read_eof_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + } + + #[test] + fn read_eof_twice_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + + match stream.read(&mut buf) { + Ok(..) => panic!(), + Err(ref e) => { + assert!(e.kind == NotConnected || e.kind == EndOfFile, + "unknown kind: {:?}", e.kind); + } + } + } + + #[test] + fn read_eof_twice_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let _stream = TcpStream::connect(addr); + // Close + }); + + let mut stream = acceptor.accept(); + let mut buf = [0]; + let nread = stream.read(&mut buf); + assert!(nread.is_err()); + + match stream.read(&mut buf) { + Ok(..) => panic!(), + Err(ref e) => { + assert!(e.kind == NotConnected || e.kind == EndOfFile, + "unknown kind: {:?}", e.kind); + } + } + } + + #[test] + fn write_close_ip4() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + drop(TcpStream::connect(addr)); + tx.send(()).unwrap(); + }); + + let mut stream = acceptor.accept(); + rx.recv().unwrap(); + let buf = [0]; + match stream.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!(e.kind == ConnectionReset || + e.kind == BrokenPipe || + e.kind == ConnectionAborted, + "unknown error: {}", e); + } + } + } + + #[test] + fn write_close_ip6() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + drop(TcpStream::connect(addr)); + tx.send(()).unwrap(); + }); + + let mut stream = acceptor.accept(); + rx.recv().unwrap(); + let buf = [0]; + match stream.write(&buf) { + Ok(..) => {} + Err(e) => { + assert!(e.kind == ConnectionReset || + e.kind == BrokenPipe || + e.kind == ConnectionAborted, + "unknown error: {}", e); + } + } + } + + #[test] + fn multiple_connect_serial_ip4() { + let addr = next_test_ip4(); + let max = 10u; + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + for _ in range(0, max) { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + } + }); + + for ref mut stream in acceptor.incoming().take(max) { + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert_eq!(buf[0], 99); + } + } + + #[test] + fn multiple_connect_serial_ip6() { + let addr = next_test_ip6(); + let max = 10u; + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + for _ in range(0, max) { + let mut stream = TcpStream::connect(addr); + stream.write(&[99]).unwrap(); + } + }); + + for ref mut stream in acceptor.incoming().take(max) { + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert_eq!(buf[0], 99); + } + } + + #[test] + fn multiple_connect_interleaved_greedy_schedule_ip4() { + let addr = next_test_ip4(); + static MAX: int = 10; + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == i as u8); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[i as u8]).unwrap(); + }); + } + } + + #[test] + fn multiple_connect_interleaved_greedy_schedule_ip6() { + let addr = next_test_ip6(); + static MAX: int = 10; + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == i as u8); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[i as u8]).unwrap(); + }); + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule_ip4() { + static MAX: int = 10; + let addr = next_test_ip4(); + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for stream in acceptor.incoming().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[99]).unwrap(); + }); + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule_ip6() { + static MAX: int = 10; + let addr = next_test_ip6(); + let acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + for stream in acceptor.incoming().take(MAX as uint) { + // Start another task to handle the connection + let _t = Thread::spawn(move|| { + let mut stream = stream; + let mut buf = [0]; + stream.read(&mut buf).unwrap(); + assert!(buf[0] == 99); + debug!("read"); + }); + } + }); + + connect(0, addr); + + fn connect(i: int, addr: SocketAddr) { + if i == MAX { return } + + let _t = Thread::spawn(move|| { + debug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + debug!("writing"); + stream.write(&[99]).unwrap(); + }); + } + } + + pub fn socket_name(addr: SocketAddr) { + let mut listener = TcpListener::bind(addr).unwrap(); + + // Make sure socket_name gives + // us the socket we binded to. + let so_name = listener.socket_name(); + assert!(so_name.is_ok()); + assert_eq!(addr, so_name.unwrap()); + } + + pub fn peer_name(addr: SocketAddr) { + let acceptor = TcpListener::bind(addr).listen(); + let _t = Thread::spawn(move|| { + let mut acceptor = acceptor; + acceptor.accept().unwrap(); + }); + + let stream = TcpStream::connect(addr); + + assert!(stream.is_ok()); + let mut stream = stream.unwrap(); + + // Make sure peer_name gives us the + // address/port of the peer we've + // connected to. + let peer_name = stream.peer_name(); + assert!(peer_name.is_ok()); + assert_eq!(addr, peer_name.unwrap()); + } + + #[test] + fn socket_and_peer_name_ip4() { + peer_name(next_test_ip4()); + socket_name(next_test_ip4()); + } + + #[test] + fn socket_and_peer_name_ip6() { + // FIXME: peer name is not consistent + //peer_name(next_test_ip6()); + socket_name(next_test_ip6()); + } + + #[test] + fn partial_read() { + let addr = next_test_ip4(); + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut srv = TcpListener::bind(addr).listen().unwrap(); + tx.send(()).unwrap(); + let mut cl = srv.accept().unwrap(); + cl.write(&[10]).unwrap(); + let mut b = [0]; + cl.read(&mut b).unwrap(); + tx.send(()).unwrap(); + }); + + rx.recv().unwrap(); + let mut c = TcpStream::connect(addr).unwrap(); + let mut b = [0; 10]; + assert_eq!(c.read(&mut b), Ok(1)); + c.write(&[1]).unwrap(); + rx.recv().unwrap(); + } + + #[test] + fn double_bind() { + let addr = next_test_ip4(); + let listener = TcpListener::bind(addr).unwrap().listen(); + assert!(listener.is_ok()); + match TcpListener::bind(addr).listen() { + Ok(..) => panic!(), + Err(e) => { + assert!(e.kind == ConnectionRefused || e.kind == OtherIoError, + "unknown error: {} {:?}", e, e.kind); + } + } + } + + #[test] + fn fast_rebind() { + let addr = next_test_ip4(); + let (tx, rx) = channel(); + + let _t = Thread::spawn(move|| { + rx.recv().unwrap(); + let _stream = TcpStream::connect(addr).unwrap(); + // Close + rx.recv().unwrap(); + }); + + { + let mut acceptor = TcpListener::bind(addr).listen(); + tx.send(()).unwrap(); + { + let _stream = acceptor.accept().unwrap(); + // Close client + tx.send(()).unwrap(); + } + // Close listener + } + let _listener = TcpListener::bind(addr); + } + + #[test] + fn tcp_clone_smoke() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut s = TcpStream::connect(addr); + let mut buf = [0, 0]; + assert_eq!(s.read(&mut buf), Ok(1)); + assert_eq!(buf[0], 1); + s.write(&[2]).unwrap(); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + rx1.recv().unwrap(); + s2.write(&[1]).unwrap(); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(s1.read(&mut buf), Ok(1)); + rx2.recv().unwrap(); + } + + #[test] + fn tcp_clone_two_read() { + let addr = next_test_ip6(); + let mut acceptor = TcpListener::bind(addr).listen(); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = Thread::spawn(move|| { + let mut s = TcpStream::connect(addr); + s.write(&[1]).unwrap(); + rx.recv().unwrap(); + s.write(&[2]).unwrap(); + rx.recv().unwrap(); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (done, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + let mut buf = [0, 0]; + s2.read(&mut buf).unwrap(); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + s1.read(&mut buf).unwrap(); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + } + + #[test] + fn tcp_clone_two_write() { + let addr = next_test_ip4(); + let mut acceptor = TcpListener::bind(addr).listen(); + + let _t = Thread::spawn(move|| { + let mut s = TcpStream::connect(addr); + let mut buf = [0, 1]; + s.read(&mut buf).unwrap(); + s.read(&mut buf).unwrap(); + }); + + let mut s1 = acceptor.accept().unwrap(); + let s2 = s1.clone(); + + let (done, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + s2.write(&[1]).unwrap(); + done.send(()).unwrap(); + }); + s1.write(&[2]).unwrap(); + + rx.recv().unwrap(); + } + + #[test] + fn shutdown_smoke() { + let addr = next_test_ip4(); + let a = TcpListener::bind(addr).unwrap().listen(); + let _t = Thread::spawn(move|| { + let mut a = a; + let mut c = a.accept().unwrap(); + assert_eq!(c.read_to_end(), Ok(vec!())); + c.write(&[1]).unwrap(); + }); + + let mut s = TcpStream::connect(addr).unwrap(); + assert!(s.inner.close_write().is_ok()); + assert!(s.write(&[1]).is_err()); + assert_eq!(s.read_to_end(), Ok(vec!(1))); + } + + #[test] + fn accept_timeout() { + let addr = next_test_ip4(); + let mut a = TcpListener::bind(addr).unwrap().listen().unwrap(); + + a.set_timeout(Some(10)); + + // Make sure we time out once and future invocations also time out + let err = a.accept().err().unwrap(); + assert_eq!(err.kind, TimedOut); + let err = a.accept().err().unwrap(); + assert_eq!(err.kind, TimedOut); + + // Also make sure that even though the timeout is expired that we will + // continue to receive any pending connections. + // + // FIXME: freebsd apparently never sees the pending connection, but + // testing manually always works. Need to investigate this + // flakiness. + if !cfg!(target_os = "freebsd") { + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + tx.send(TcpStream::connect(addr).unwrap()).unwrap(); + }); + let _l = rx.recv().unwrap(); + for i in range(0i, 1001) { + match a.accept() { + Ok(..) => break, + Err(ref e) if e.kind == TimedOut => {} + Err(e) => panic!("error: {}", e), + } + ::thread::Thread::yield_now(); + if i == 1000 { panic!("should have a pending connection") } + } + } + + // Unset the timeout and make sure that this always blocks. + a.set_timeout(None); + let _t = Thread::spawn(move|| { + drop(TcpStream::connect(addr).unwrap()); + }); + a.accept().unwrap(); + } + + #[test] + fn close_readwrite_smoke() { + let addr = next_test_ip4(); + let a = TcpListener::bind(addr).listen().unwrap(); + let (_tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut a = a; + let _s = a.accept().unwrap(); + let _ = rx.recv().unwrap(); + }); + + let mut b = [0]; + let mut s = TcpStream::connect(addr).unwrap(); + let mut s2 = s.clone(); + + // closing should prevent reads/writes + s.close_write().unwrap(); + assert!(s.write(&[0]).is_err()); + s.close_read().unwrap(); + assert!(s.read(&mut b).is_err()); + + // closing should affect previous handles + assert!(s2.write(&[0]).is_err()); + assert!(s2.read(&mut b).is_err()); + + // closing should affect new handles + let mut s3 = s.clone(); + assert!(s3.write(&[0]).is_err()); + assert!(s3.read(&mut b).is_err()); + + // make sure these don't die + let _ = s2.close_read(); + let _ = s2.close_write(); + let _ = s3.close_read(); + let _ = s3.close_write(); + } + + #[test] + fn close_read_wakes_up() { + let addr = next_test_ip4(); + let a = TcpListener::bind(addr).listen().unwrap(); + let (_tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut a = a; + let _s = a.accept().unwrap(); + let _ = rx.recv().unwrap(); + }); + + let mut s = TcpStream::connect(addr).unwrap(); + let s2 = s.clone(); + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + assert!(s2.read(&mut [0]).is_err()); + tx.send(()).unwrap(); + }); + // this should wake up the child task + s.close_read().unwrap(); + + // this test will never finish if the child doesn't wake up + rx.recv().unwrap(); + } + + #[test] + fn readwrite_timeouts() { + let addr = next_test_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + assert!(s.write(&[0]).is_ok()); + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + s.set_timeout(Some(20)); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + + s.set_timeout(Some(20)); + for i in range(0i, 1001) { + match s.write(&[0; 128 * 1024]) { + Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, + Err(IoError { kind: TimedOut, .. }) => break, + Err(e) => panic!("{}", e), + } + if i == 1000 { panic!("should have filled up?!"); } + } + assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); + + tx.send(()).unwrap(); + s.set_timeout(None); + assert_eq!(s.read(&mut [0, 0]), Ok(1)); + } + + #[test] + fn read_timeouts() { + let addr = next_test_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + let mut amt = 0; + while amt < 100 * 128 * 1024 { + match s.read(&mut [0;128 * 1024]) { + Ok(n) => { amt += n; } + Err(e) => panic!("{}", e), + } + } + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + s.set_read_timeout(Some(20)); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + + tx.send(()).unwrap(); + for _ in range(0i, 100) { + assert!(s.write(&[0;128 * 1024]).is_ok()); + } + } + + #[test] + fn write_timeouts() { + let addr = next_test_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + assert!(s.write(&[0]).is_ok()); + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + s.set_write_timeout(Some(20)); + for i in range(0i, 1001) { + match s.write(&[0; 128 * 1024]) { + Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, + Err(IoError { kind: TimedOut, .. }) => break, + Err(e) => panic!("{}", e), + } + if i == 1000 { panic!("should have filled up?!"); } + } + assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); + + tx.send(()).unwrap(); + assert!(s.read(&mut [0]).is_ok()); + } + + #[test] + fn timeout_concurrent_read() { + let addr = next_test_ip6(); + let mut a = TcpListener::bind(addr).listen().unwrap(); + let (tx, rx) = channel::<()>(); + Thread::spawn(move|| { + let mut s = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + assert_eq!(s.write(&[0]), Ok(())); + let _ = rx.recv(); + }); + + let mut s = a.accept().unwrap(); + let s2 = s.clone(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + let mut s2 = s2; + assert_eq!(s2.read(&mut [0]), Ok(1)); + tx2.send(()).unwrap(); + }); + + s.set_read_timeout(Some(20)); + assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); + tx.send(()).unwrap(); + + rx2.recv().unwrap(); + } + + #[test] + fn clone_while_reading() { + let addr = next_test_ip6(); + let listen = TcpListener::bind(addr); + let mut accept = listen.listen().unwrap(); + + // Enqueue a task to write to a socket + let (tx, rx) = channel(); + let (txdone, rxdone) = channel(); + let txdone2 = txdone.clone(); + let _t = Thread::spawn(move|| { + let mut tcp = TcpStream::connect(addr).unwrap(); + rx.recv().unwrap(); + tcp.write_u8(0).unwrap(); + txdone2.send(()).unwrap(); + }); + + // Spawn off a reading clone + let tcp = accept.accept().unwrap(); + let tcp2 = tcp.clone(); + let txdone3 = txdone.clone(); + let _t = Thread::spawn(move|| { + let mut tcp2 = tcp2; + tcp2.read_u8().unwrap(); + txdone3.send(()).unwrap(); + }); + + // Try to ensure that the reading clone is indeed reading + for _ in range(0i, 50) { + ::thread::Thread::yield_now(); + } + + // clone the handle again while it's reading, then let it finish the + // read. + let _ = tcp.clone(); + tx.send(()).unwrap(); + rxdone.recv().unwrap(); + rxdone.recv().unwrap(); + } + + #[test] + fn clone_accept_smoke() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr); + let mut a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + + assert!(a.accept().is_ok()); + assert!(a2.accept().is_ok()); + } + + #[test] + fn clone_accept_concurrent() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr); + let a = l.listen().unwrap(); + let a2 = a.clone(); + + let (tx, rx) = channel(); + let tx2 = tx.clone(); + + let _t = Thread::spawn(move|| { + let mut a = a; + tx.send(a.accept()).unwrap(); + }); + let _t = Thread::spawn(move|| { + let mut a = a2; + tx2.send(a.accept()).unwrap(); + }); + + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + let _t = Thread::spawn(move|| { + let _ = TcpStream::connect(addr); + }); + + assert!(rx.recv().unwrap().is_ok()); + assert!(rx.recv().unwrap().is_ok()); + } + + #[test] + fn close_accept_smoke() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr); + let mut a = l.listen().unwrap(); + + a.close_accept().unwrap(); + assert_eq!(a.accept().err().unwrap().kind, EndOfFile); + } + + #[test] + fn close_accept_concurrent() { + let addr = next_test_ip4(); + let l = TcpListener::bind(addr); + let a = l.listen().unwrap(); + let mut a2 = a.clone(); + + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut a = a; + tx.send(a.accept()).unwrap(); + }); + a2.close_accept().unwrap(); + + assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile); + } +} diff --git a/src/libstd/old_io/net/udp.rs b/src/libstd/old_io/net/udp.rs new file mode 100644 index 00000000000..d7fc760951e --- /dev/null +++ b/src/libstd/old_io/net/udp.rs @@ -0,0 +1,457 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! UDP (User Datagram Protocol) network connections. +//! +//! This module contains the ability to open a UDP stream to a socket address. +//! The destination and binding addresses can either be an IPv4 or IPv6 +//! address. There is no corresponding notion of a server because UDP is a +//! datagram protocol. + +use clone::Clone; +use old_io::net::ip::{SocketAddr, IpAddr, ToSocketAddr}; +use old_io::IoResult; +use option::Option; +use sys::udp::UdpSocket as UdpSocketImp; +use sys_common; + +/// A User Datagram Protocol socket. +/// +/// This is an implementation of a bound UDP socket. This supports both IPv4 and +/// IPv6 addresses, and there is no corresponding notion of a server because UDP +/// is a datagram protocol. +/// +/// # Example +/// +/// ```rust,no_run +/// # #![allow(unused_must_use)] +/// #![feature(slicing_syntax)] +/// +/// use std::old_io::net::udp::UdpSocket; +/// use std::old_io::net::ip::{Ipv4Addr, SocketAddr}; +/// fn main() { +/// let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 34254 }; +/// let mut socket = match UdpSocket::bind(addr) { +/// Ok(s) => s, +/// Err(e) => panic!("couldn't bind socket: {}", e), +/// }; +/// +/// let mut buf = [0; 10]; +/// match socket.recv_from(&mut buf) { +/// Ok((amt, src)) => { +/// // Send a reply to the socket we received data from +/// let buf = buf.slice_to_mut(amt); +/// buf.reverse(); +/// socket.send_to(buf, src); +/// } +/// Err(e) => println!("couldn't receive a datagram: {}", e) +/// } +/// drop(socket); // close the socket +/// } +/// ``` +pub struct UdpSocket { + inner: UdpSocketImp, +} + +impl UdpSocket { + /// Creates a UDP socket from the given address. + /// + /// Address type can be any implementor of `ToSocketAddr` trait. See its + /// documentation for concrete examples. + pub fn bind(addr: A) -> IoResult { + super::with_addresses(addr, |addr| { + UdpSocketImp::bind(addr).map(|s| UdpSocket { inner: s }) + }) + } + + /// Receives data from the socket. On success, returns the number of bytes + /// read and the address from whence the data came. + pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> { + self.inner.recv_from(buf) + } + + /// Sends data on the socket to the given address. Returns nothing on + /// success. + /// + /// Address type can be any implementer of `ToSocketAddr` trait. See its + /// documentation for concrete examples. + pub fn send_to(&mut self, buf: &[u8], addr: A) -> IoResult<()> { + super::with_addresses(addr, |addr| self.inner.send_to(buf, addr)) + } + + /// Returns the socket address that this socket was created from. + pub fn socket_name(&mut self) -> IoResult { + self.inner.socket_name() + } + + /// Joins a multicast IP address (becomes a member of it) + #[unstable] + pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> { + self.inner.join_multicast(multi) + } + + /// Leaves a multicast IP address (drops membership from it) + #[unstable] + pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> { + self.inner.leave_multicast(multi) + } + + /// Set the multicast loop flag to the specified value + /// + /// This lets multicast packets loop back to local sockets (if enabled) + #[unstable] + pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> { + self.inner.set_multicast_loop(on) + } + + /// Sets the multicast TTL + #[unstable] + pub fn set_multicast_ttl(&mut self, ttl: int) -> IoResult<()> { + self.inner.multicast_time_to_live(ttl) + } + + /// Sets this socket's TTL + #[unstable] + pub fn set_ttl(&mut self, ttl: int) -> IoResult<()> { + self.inner.time_to_live(ttl) + } + + /// Sets the broadcast flag on or off + #[unstable] + pub fn set_broadcast(&mut self, broadcast: bool) -> IoResult<()> { + self.inner.set_broadcast(broadcast) + } + + /// Sets the read/write timeout for this socket. + /// + /// For more information, see `TcpStream::set_timeout` + #[unstable = "the timeout argument may change in type and value"] + pub fn set_timeout(&mut self, timeout_ms: Option) { + self.inner.set_timeout(timeout_ms) + } + + /// Sets the read timeout for this socket. + /// + /// For more information, see `TcpStream::set_timeout` + #[unstable = "the timeout argument may change in type and value"] + pub fn set_read_timeout(&mut self, timeout_ms: Option) { + self.inner.set_read_timeout(timeout_ms) + } + + /// Sets the write timeout for this socket. + /// + /// For more information, see `TcpStream::set_timeout` + #[unstable = "the timeout argument may change in type and value"] + pub fn set_write_timeout(&mut self, timeout_ms: Option) { + self.inner.set_write_timeout(timeout_ms) + } +} + +impl Clone for UdpSocket { + /// Creates a new handle to this UDP socket, allowing for simultaneous + /// reads and writes of the socket. + /// + /// The underlying UDP socket will not be closed until all handles to the + /// socket have been deallocated. Two concurrent reads will not receive + /// the same data. Instead, the first read will receive the first packet + /// received, and the second read will receive the second packet. + fn clone(&self) -> UdpSocket { + UdpSocket { + inner: self.inner.clone(), + } + } +} + +impl sys_common::AsInner for UdpSocket { + fn as_inner(&self) -> &UdpSocketImp { + &self.inner + } +} + +#[cfg(test)] +#[allow(unstable)] +mod test { + use prelude::v1::*; + + use sync::mpsc::channel; + use old_io::net::ip::*; + use old_io::test::*; + use old_io::{IoError, TimedOut, PermissionDenied, ShortWrite}; + use super::*; + use thread::Thread; + + // FIXME #11530 this fails on android because tests are run as root + #[cfg_attr(any(windows, target_os = "android"), ignore)] + #[test] + fn bind_error() { + let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; + match UdpSocket::bind(addr) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, PermissionDenied), + } + } + + #[test] + fn socket_smoke_test_ip4() { + let server_ip = next_test_ip4(); + let client_ip = next_test_ip4(); + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + + let _t = Thread::spawn(move|| { + match UdpSocket::bind(client_ip) { + Ok(ref mut client) => { + rx1.recv().unwrap(); + client.send_to(&[99], server_ip).unwrap() + } + Err(..) => panic!() + } + tx2.send(()).unwrap(); + }); + + match UdpSocket::bind(server_ip) { + Ok(ref mut server) => { + tx1.send(()).unwrap(); + let mut buf = [0]; + match server.recv_from(&mut buf) { + Ok((nread, src)) => { + assert_eq!(nread, 1); + assert_eq!(buf[0], 99); + assert_eq!(src, client_ip); + } + Err(..) => panic!() + } + } + Err(..) => panic!() + } + rx2.recv().unwrap(); + } + + #[test] + fn socket_smoke_test_ip6() { + let server_ip = next_test_ip6(); + let client_ip = next_test_ip6(); + let (tx, rx) = channel::<()>(); + + let _t = Thread::spawn(move|| { + match UdpSocket::bind(client_ip) { + Ok(ref mut client) => { + rx.recv().unwrap(); + client.send_to(&[99], server_ip).unwrap() + } + Err(..) => panic!() + } + }); + + match UdpSocket::bind(server_ip) { + Ok(ref mut server) => { + tx.send(()).unwrap(); + let mut buf = [0]; + match server.recv_from(&mut buf) { + Ok((nread, src)) => { + assert_eq!(nread, 1); + assert_eq!(buf[0], 99); + assert_eq!(src, client_ip); + } + Err(..) => panic!() + } + } + Err(..) => panic!() + } + } + + pub fn socket_name(addr: SocketAddr) { + let server = UdpSocket::bind(addr); + + assert!(server.is_ok()); + let mut server = server.unwrap(); + + // Make sure socket_name gives + // us the socket we binded to. + let so_name = server.socket_name(); + assert!(so_name.is_ok()); + assert_eq!(addr, so_name.unwrap()); + } + + #[test] + fn socket_name_ip4() { + socket_name(next_test_ip4()); + } + + #[test] + fn socket_name_ip6() { + socket_name(next_test_ip6()); + } + + #[test] + fn udp_clone_smoke() { + let addr1 = next_test_ip4(); + let addr2 = next_test_ip4(); + let mut sock1 = UdpSocket::bind(addr1).unwrap(); + let sock2 = UdpSocket::bind(addr2).unwrap(); + + let _t = Thread::spawn(move|| { + let mut sock2 = sock2; + let mut buf = [0, 0]; + assert_eq!(sock2.recv_from(&mut buf), Ok((1, addr1))); + assert_eq!(buf[0], 1); + sock2.send_to(&[2], addr1).unwrap(); + }); + + let sock3 = sock1.clone(); + + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + let mut sock3 = sock3; + rx1.recv().unwrap(); + sock3.send_to(&[1], addr2).unwrap(); + tx2.send(()).unwrap(); + }); + tx1.send(()).unwrap(); + let mut buf = [0, 0]; + assert_eq!(sock1.recv_from(&mut buf), Ok((1, addr2))); + rx2.recv().unwrap(); + } + + #[test] + fn udp_clone_two_read() { + let addr1 = next_test_ip4(); + let addr2 = next_test_ip4(); + let mut sock1 = UdpSocket::bind(addr1).unwrap(); + let sock2 = UdpSocket::bind(addr2).unwrap(); + let (tx1, rx) = channel(); + let tx2 = tx1.clone(); + + let _t = Thread::spawn(move|| { + let mut sock2 = sock2; + sock2.send_to(&[1], addr1).unwrap(); + rx.recv().unwrap(); + sock2.send_to(&[2], addr1).unwrap(); + rx.recv().unwrap(); + }); + + let sock3 = sock1.clone(); + + let (done, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut sock3 = sock3; + let mut buf = [0, 0]; + sock3.recv_from(&mut buf).unwrap(); + tx2.send(()).unwrap(); + done.send(()).unwrap(); + }); + let mut buf = [0, 0]; + sock1.recv_from(&mut buf).unwrap(); + tx1.send(()).unwrap(); + + rx.recv().unwrap(); + } + + #[test] + fn udp_clone_two_write() { + let addr1 = next_test_ip4(); + let addr2 = next_test_ip4(); + let mut sock1 = UdpSocket::bind(addr1).unwrap(); + let sock2 = UdpSocket::bind(addr2).unwrap(); + + let (tx, rx) = channel(); + let (serv_tx, serv_rx) = channel(); + + let _t = Thread::spawn(move|| { + let mut sock2 = sock2; + let mut buf = [0, 1]; + + rx.recv().unwrap(); + match sock2.recv_from(&mut buf) { + Ok(..) => {} + Err(e) => panic!("failed receive: {}", e), + } + serv_tx.send(()).unwrap(); + }); + + let sock3 = sock1.clone(); + + let (done, rx) = channel(); + let tx2 = tx.clone(); + let _t = Thread::spawn(move|| { + let mut sock3 = sock3; + match sock3.send_to(&[1], addr2) { + Ok(..) => { let _ = tx2.send(()); } + Err(..) => {} + } + done.send(()).unwrap(); + }); + match sock1.send_to(&[2], addr2) { + Ok(..) => { let _ = tx.send(()); } + Err(..) => {} + } + drop(tx); + + rx.recv().unwrap(); + serv_rx.recv().unwrap(); + } + + #[cfg(not(windows))] // FIXME #17553 + #[test] + fn recv_from_timeout() { + let addr1 = next_test_ip4(); + let addr2 = next_test_ip4(); + let mut a = UdpSocket::bind(addr1).unwrap(); + let a2 = UdpSocket::bind(addr2).unwrap(); + + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + let _t = Thread::spawn(move|| { + let mut a = a2; + assert_eq!(a.recv_from(&mut [0]), Ok((1, addr1))); + assert_eq!(a.send_to(&[0], addr1), Ok(())); + rx.recv().unwrap(); + assert_eq!(a.send_to(&[0], addr1), Ok(())); + + tx2.send(()).unwrap(); + }); + + // Make sure that reads time out, but writes can continue + a.set_read_timeout(Some(20)); + assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut); + assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut); + assert_eq!(a.send_to(&[0], addr2), Ok(())); + + // Cloned handles should be able to block + let mut a2 = a.clone(); + assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2))); + + // Clearing the timeout should allow for receiving + a.set_timeout(None); + tx.send(()).unwrap(); + assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2))); + + // Make sure the child didn't die + rx2.recv().unwrap(); + } + + #[test] + fn send_to_timeout() { + let addr1 = next_test_ip4(); + let addr2 = next_test_ip4(); + let mut a = UdpSocket::bind(addr1).unwrap(); + let _b = UdpSocket::bind(addr2).unwrap(); + + a.set_write_timeout(Some(1000)); + for _ in range(0u, 100) { + match a.send_to(&[0;4*1024], addr2) { + Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, + Err(IoError { kind: TimedOut, .. }) => break, + Err(e) => panic!("other error: {}", e), + } + } + } +} diff --git a/src/libstd/old_io/pipe.rs b/src/libstd/old_io/pipe.rs new file mode 100644 index 00000000000..224f3bfd98c --- /dev/null +++ b/src/libstd/old_io/pipe.rs @@ -0,0 +1,139 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Synchronous, in-memory pipes. +//! +//! Currently these aren't particularly useful, there only exists bindings +//! enough so that pipes can be created to child processes. + +#![allow(missing_docs)] + +use prelude::v1::*; + +use old_io::IoResult; +use libc; +use sync::Arc; + +use sys_common; +use sys; +use sys::fs::FileDesc as FileDesc; + +/// A synchronous, in-memory pipe. +pub struct PipeStream { + inner: Arc +} + +pub struct PipePair { + pub reader: PipeStream, + pub writer: PipeStream, +} + +impl PipeStream { + /// Consumes a file descriptor to return a pipe stream that will have + /// synchronous, but non-blocking reads/writes. This is useful if the file + /// descriptor is acquired via means other than the standard methods. + /// + /// This operation consumes ownership of the file descriptor and it will be + /// closed once the object is deallocated. + /// + /// # Example + /// + /// ```{rust,no_run} + /// # #![allow(unused_must_use)] + /// extern crate libc; + /// + /// use std::old_io::pipe::PipeStream; + /// + /// fn main() { + /// let mut pipe = PipeStream::open(libc::STDERR_FILENO); + /// pipe.write(b"Hello, stderr!"); + /// } + /// ``` + pub fn open(fd: libc::c_int) -> IoResult { + Ok(PipeStream::from_filedesc(FileDesc::new(fd, true))) + } + + // FIXME: expose this some other way + /// Wrap a FileDesc directly, taking ownership. + #[doc(hidden)] + pub fn from_filedesc(fd: FileDesc) -> PipeStream { + PipeStream { inner: Arc::new(fd) } + } + + /// Creates a pair of in-memory OS pipes for a unidirectional communication + /// stream. + /// + /// The structure returned contains a reader and writer I/O object. Data + /// written to the writer can be read from the reader. + /// + /// # Errors + /// + /// This function can fail to succeed if the underlying OS has run out of + /// available resources to allocate a new pipe. + pub fn pair() -> IoResult { + let (reader, writer) = try!(unsafe { sys::os::pipe() }); + Ok(PipePair { + reader: PipeStream::from_filedesc(reader), + writer: PipeStream::from_filedesc(writer), + }) + } +} + +impl sys_common::AsInner for PipeStream { + fn as_inner(&self) -> &sys::fs::FileDesc { + &*self.inner + } +} + +impl Clone for PipeStream { + fn clone(&self) -> PipeStream { + PipeStream { inner: self.inner.clone() } + } +} + +impl Reader for PipeStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner.read(buf) + } +} + +impl Writer for PipeStream { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.inner.write(buf) + } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + + use sync::mpsc::channel; + use thread::Thread; + + #[test] + fn partial_read() { + use os; + use old_io::pipe::PipeStream; + + let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() }; + let out = PipeStream::open(writer); + let mut input = PipeStream::open(reader); + let (tx, rx) = channel(); + let _t = Thread::spawn(move|| { + let mut out = out; + out.write(&[10]).unwrap(); + rx.recv().unwrap(); // don't close the pipe until the other read has finished + }); + + let mut buf = [0; 10]; + input.read(&mut buf).unwrap(); + tx.send(()).unwrap(); + } +} diff --git a/src/libstd/old_io/process.rs b/src/libstd/old_io/process.rs new file mode 100644 index 00000000000..d7c263b3d1f --- /dev/null +++ b/src/libstd/old_io/process.rs @@ -0,0 +1,1231 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Bindings for executing child processes + +#![allow(unstable)] +#![allow(non_upper_case_globals)] + +pub use self::StdioContainer::*; +pub use self::ProcessExit::*; + +use prelude::v1::*; + +use collections::HashMap; +use ffi::CString; +use fmt; +use old_io::pipe::{PipeStream, PipePair}; +use old_io::{IoResult, IoError}; +use old_io; +use libc; +use os; +use path::BytesContainer; +use sync::mpsc::{channel, Receiver}; +use sys::fs::FileDesc; +use sys::process::Process as ProcessImp; +use sys; +use thread::Thread; + +#[cfg(windows)] use hash; +#[cfg(windows)] use str; + +/// Signal a process to exit, without forcibly killing it. Corresponds to +/// SIGTERM on unix platforms. +#[cfg(windows)] pub const PleaseExitSignal: int = 15; +/// Signal a process to exit immediately, forcibly killing it. Corresponds to +/// SIGKILL on unix platforms. +#[cfg(windows)] pub const MustDieSignal: int = 9; +/// Signal a process to exit, without forcibly killing it. Corresponds to +/// SIGTERM on unix platforms. +#[cfg(not(windows))] pub const PleaseExitSignal: int = libc::SIGTERM as int; +/// Signal a process to exit immediately, forcibly killing it. Corresponds to +/// SIGKILL on unix platforms. +#[cfg(not(windows))] pub const MustDieSignal: int = libc::SIGKILL as int; + +/// Representation of a running or exited child process. +/// +/// This structure is used to represent and manage child processes. A child +/// process is created via the `Command` struct, which configures the spawning +/// process and can itself be constructed using a builder-style interface. +/// +/// # Example +/// +/// ```should_fail +/// use std::old_io::Command; +/// +/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() { +/// Ok(child) => child, +/// Err(e) => panic!("failed to execute child: {}", e), +/// }; +/// +/// let contents = child.stdout.as_mut().unwrap().read_to_end(); +/// assert!(child.wait().unwrap().success()); +/// ``` +pub struct Process { + handle: ProcessImp, + forget: bool, + + /// None until wait() is called. + exit_code: Option, + + /// Manually delivered signal + exit_signal: Option, + + /// Deadline after which wait() will return + deadline: u64, + + /// Handle to the child's stdin, if the `stdin` field of this process's + /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. + pub stdin: Option, + + /// Handle to the child's stdout, if the `stdout` field of this process's + /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. + pub stdout: Option, + + /// Handle to the child's stderr, if the `stderr` field of this process's + /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. + pub stderr: Option, +} + +/// A representation of environment variable name +/// It compares case-insensitive on Windows and case-sensitive everywhere else. +#[cfg(not(windows))] +#[derive(Hash, PartialEq, Eq, Clone, Show)] +struct EnvKey(CString); + +#[doc(hidden)] +#[cfg(windows)] +#[derive(Eq, Clone, Show)] +struct EnvKey(CString); + +#[cfg(windows)] +impl hash::Hash for EnvKey { + fn hash(&self, state: &mut H) { + let &EnvKey(ref x) = self; + match str::from_utf8(x.as_bytes()) { + Ok(s) => for ch in s.chars() { + (ch as u8 as char).to_lowercase().hash(state); + }, + Err(..) => x.hash(state) + } + } +} + +#[cfg(windows)] +impl PartialEq for EnvKey { + fn eq(&self, other: &EnvKey) -> bool { + let &EnvKey(ref x) = self; + let &EnvKey(ref y) = other; + match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) { + (Ok(xs), Ok(ys)) => { + if xs.len() != ys.len() { + return false + } else { + for (xch, ych) in xs.chars().zip(ys.chars()) { + if xch.to_lowercase() != ych.to_lowercase() { + return false; + } + } + return true; + } + }, + // If either is not a valid utf8 string, just compare them byte-wise + _ => return x.eq(y) + } + } +} + +impl BytesContainer for EnvKey { + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + let &EnvKey(ref k) = self; + k.container_as_bytes() + } +} + +/// A HashMap representation of environment variables. +pub type EnvMap = HashMap; + +/// The `Command` type acts as a process builder, providing fine-grained control +/// over how a new process should be spawned. A default configuration can be +/// generated using `Command::new(program)`, where `program` gives a path to the +/// program to be executed. Additional builder methods allow the configuration +/// to be changed (for example, by adding arguments) prior to spawning: +/// +/// ``` +/// use std::old_io::Command; +/// +/// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() { +/// Ok(p) => p, +/// Err(e) => panic!("failed to execute process: {}", e), +/// }; +/// +/// let output = process.stdout.as_mut().unwrap().read_to_end(); +/// ``` +#[derive(Clone)] +pub struct Command { + // The internal data for the builder. Documented by the builder + // methods below, and serialized into rt::rtio::ProcessConfig. + program: CString, + args: Vec, + env: Option, + cwd: Option, + stdin: StdioContainer, + stdout: StdioContainer, + stderr: StdioContainer, + uid: Option, + gid: Option, + detach: bool, +} + +// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so +// we cannot usefully take BytesContainer arguments by reference (without forcing an +// additional & around &str). So we are instead temporarily adding an instance +// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path +// instance should be removed, and arguments bound by BytesContainer should be passed by +// reference. (Here: {new, arg, args, env}.) + +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * A readable pipe for stdin (file descriptor 0) + /// * A writeable pipe for stdout and stderr (file descriptors 1 and 2) + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + pub fn new(program: T) -> Command { + Command { + program: CString::from_slice(program.container_as_bytes()), + args: Vec::new(), + env: None, + cwd: None, + stdin: CreatePipe(true, false), + stdout: CreatePipe(false, true), + stderr: CreatePipe(false, true), + uid: None, + gid: None, + detach: false, + } + } + + /// Add an argument to pass to the program. + pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command { + self.args.push(CString::from_slice(arg.container_as_bytes())); + self + } + + /// Add multiple arguments to pass to the program. + pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command { + self.args.extend(args.iter().map(|arg| { + CString::from_slice(arg.container_as_bytes()) + })); + self + } + // Get a mutable borrow of the environment variable map for this `Command`. + fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { + match self.env { + Some(ref mut map) => map, + None => { + // if the env is currently just inheriting from the parent's, + // materialize the parent's env into a hashtable. + self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| { + (EnvKey(CString::from_slice(k.as_slice())), + CString::from_slice(v.as_slice())) + }).collect()); + self.env.as_mut().unwrap() + } + } + } + + /// Inserts or updates an environment variable mapping. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + pub fn env<'a, T, U>(&'a mut self, key: T, val: U) + -> &'a mut Command + where T: BytesContainer, U: BytesContainer { + let key = EnvKey(CString::from_slice(key.container_as_bytes())); + let val = CString::from_slice(val.container_as_bytes()); + self.get_env_map().insert(key, val); + self + } + + /// Removes an environment variable mapping. + pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command + where T: BytesContainer { + let key = EnvKey(CString::from_slice(key.container_as_bytes())); + self.get_env_map().remove(&key); + self + } + + /// Sets the entire environment map for the child process. + /// + /// If the given slice contains multiple instances of an environment + /// variable, the *rightmost* instance will determine the value. + pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)]) + -> &'a mut Command + where T: BytesContainer, U: BytesContainer { + self.env = Some(env.iter().map(|&(ref k, ref v)| { + (EnvKey(CString::from_slice(k.container_as_bytes())), + CString::from_slice(v.container_as_bytes())) + }).collect()); + self + } + + /// Set the working directory for the child process. + pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command { + self.cwd = Some(CString::from_slice(dir.as_vec())); + self + } + + /// Configuration for the child process's stdin handle (file descriptor 0). + /// Defaults to `CreatePipe(true, false)` so the input can be written to. + pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stdin = cfg; + self + } + + /// Configuration for the child process's stdout handle (file descriptor 1). + /// Defaults to `CreatePipe(false, true)` so the output can be collected. + pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stdout = cfg; + self + } + + /// Configuration for the child process's stderr handle (file descriptor 2). + /// Defaults to `CreatePipe(false, true)` so the output can be collected. + pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { + self.stderr = cfg; + self + } + + /// Sets the child process's user id. This translates to a `setuid` call in + /// the child process. Setting this value on windows will cause the spawn to + /// fail. Failure in the `setuid` call on unix will also cause the spawn to + /// fail. + pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command { + self.uid = Some(id); + self + } + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command { + self.gid = Some(id); + self + } + + /// Sets the child process to be spawned in a detached state. On unix, this + /// means that the child is the leader of a new process group. + pub fn detached<'a>(&'a mut self) -> &'a mut Command { + self.detach = true; + self + } + + /// Executes the command as a child process, which is returned. + pub fn spawn(&self) -> IoResult { + let (their_stdin, our_stdin) = try!(setup_io(self.stdin)); + let (their_stdout, our_stdout) = try!(setup_io(self.stdout)); + let (their_stderr, our_stderr) = try!(setup_io(self.stderr)); + + match ProcessImp::spawn(self, their_stdin, their_stdout, their_stderr) { + Err(e) => Err(e), + Ok(handle) => Ok(Process { + handle: handle, + forget: false, + exit_code: None, + exit_signal: None, + deadline: 0, + stdin: our_stdin, + stdout: our_stdout, + stderr: our_stderr, + }) + } + } + + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// # Example + /// + /// ``` + /// use std::old_io::Command; + /// + /// let output = match Command::new("cat").arg("foot.txt").output() { + /// Ok(output) => output, + /// Err(e) => panic!("failed to execute process: {}", e), + /// }; + /// + /// println!("status: {}", output.status); + /// println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice())); + /// println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice())); + /// ``` + pub fn output(&self) -> IoResult { + self.spawn().and_then(|p| p.wait_with_output()) + } + + /// Executes a command as a child process, waiting for it to finish and + /// collecting its exit status. + /// + /// # Example + /// + /// ``` + /// use std::old_io::Command; + /// + /// let status = match Command::new("ls").status() { + /// Ok(status) => status, + /// Err(e) => panic!("failed to execute process: {}", e), + /// }; + /// + /// println!("process exited with: {}", status); + /// ``` + pub fn status(&self) -> IoResult { + self.spawn().and_then(|mut p| p.wait()) + } +} + +impl fmt::Debug for Command { + /// Format the program and arguments of a Command for display. Any + /// non-utf8 data is lossily converted using the utf8 replacement + /// character. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes()))); + for arg in self.args.iter() { + try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes()))); + } + Ok(()) + } +} + +fn setup_io(io: StdioContainer) -> IoResult<(Option, Option)> { + let ours; + let theirs; + match io { + Ignored => { + theirs = None; + ours = None; + } + InheritFd(fd) => { + theirs = Some(PipeStream::from_filedesc(FileDesc::new(fd, false))); + ours = None; + } + CreatePipe(readable, _writable) => { + let PipePair { reader, writer } = try!(PipeStream::pair()); + if readable { + theirs = Some(reader); + ours = Some(writer); + } else { + theirs = Some(writer); + ours = Some(reader); + } + } + } + Ok((theirs, ours)) +} + +// Allow the sys module to get access to the Command state +impl sys::process::ProcessConfig for Command { + fn program(&self) -> &CString { + &self.program + } + fn args(&self) -> &[CString] { + self.args.as_slice() + } + fn env(&self) -> Option<&EnvMap> { + self.env.as_ref() + } + fn cwd(&self) -> Option<&CString> { + self.cwd.as_ref() + } + fn uid(&self) -> Option { + self.uid.clone() + } + fn gid(&self) -> Option { + self.gid.clone() + } + fn detach(&self) -> bool { + self.detach + } + +} + +/// The output of a finished process. +#[derive(PartialEq, Eq, Clone)] +pub struct ProcessOutput { + /// The status (exit code) of the process. + pub status: ProcessExit, + /// The data that the process wrote to stdout. + pub output: Vec, + /// The data that the process wrote to stderr. + pub error: Vec, +} + +/// Describes what to do with a standard io stream for a child process. +#[derive(Clone, Copy)] +pub enum StdioContainer { + /// This stream will be ignored. This is the equivalent of attaching the + /// stream to `/dev/null` + Ignored, + + /// The specified file descriptor is inherited for the stream which it is + /// specified for. Ownership of the file descriptor is *not* taken, so the + /// caller must clean it up. + InheritFd(libc::c_int), + + /// Creates a pipe for the specified file descriptor which will be created + /// when the process is spawned. + /// + /// The first boolean argument is whether the pipe is readable, and the + /// second is whether it is writable. These properties are from the view of + /// the *child* process, not the parent process. + CreatePipe(bool /* readable */, bool /* writable */), +} + +/// Describes the result of a process after it has terminated. +/// Note that Windows have no signals, so the result is usually ExitStatus. +#[derive(PartialEq, Eq, Clone, Copy, Show)] +pub enum ProcessExit { + /// Normal termination with an exit status. + ExitStatus(int), + + /// Termination by signal, with the signal number. + ExitSignal(int), +} + +#[stable] +impl fmt::Display for ProcessExit { + /// Format a ProcessExit enum, to nicely present the information. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ExitStatus(code) => write!(f, "exit code: {}", code), + ExitSignal(code) => write!(f, "signal: {}", code), + } + } +} + +impl ProcessExit { + /// Was termination successful? Signal termination not considered a success, + /// and success is defined as a zero exit status. + pub fn success(&self) -> bool { + return self.matches_exit_status(0); + } + + /// Checks whether this ProcessExit matches the given exit status. + /// Termination by signal will never match an exit code. + pub fn matches_exit_status(&self, wanted: int) -> bool { + *self == ExitStatus(wanted) + } +} + +impl Process { + /// Sends `signal` to another process in the system identified by `id`. + /// + /// Note that windows doesn't quite have the same model as unix, so some + /// unix signals are mapped to windows signals. Notably, unix termination + /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`. + /// + /// Additionally, a signal number of 0 can check for existence of the target + /// process. Note, though, that on some platforms signals will continue to + /// be successfully delivered if the child has exited, but not yet been + /// reaped. + pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> { + unsafe { ProcessImp::killpid(id, signal) } + } + + /// Returns the process id of this child process + pub fn id(&self) -> libc::pid_t { self.handle.id() } + + /// Sends the specified signal to the child process, returning whether the + /// signal could be delivered or not. + /// + /// Note that signal 0 is interpreted as a poll to check whether the child + /// process is still alive or not. If an error is returned, then the child + /// process has exited. + /// + /// On some unix platforms signals will continue to be received after a + /// child has exited but not yet been reaped. In order to report the status + /// of signal delivery correctly, unix implementations may invoke + /// `waitpid()` with `WNOHANG` in order to reap the child as necessary. + /// + /// # Errors + /// + /// If the signal delivery fails, the corresponding error is returned. + pub fn signal(&mut self, signal: int) -> IoResult<()> { + #[cfg(unix)] fn collect_status(p: &mut Process) { + // On Linux (and possibly other unices), a process that has exited will + // continue to accept signals because it is "defunct". The delivery of + // signals will only fail once the child has been reaped. For this + // reason, if the process hasn't exited yet, then we attempt to collect + // their status with WNOHANG. + if p.exit_code.is_none() { + match p.handle.try_wait() { + Some(code) => { p.exit_code = Some(code); } + None => {} + } + } + } + #[cfg(windows)] fn collect_status(_p: &mut Process) {} + + collect_status(self); + + // if the process has finished, and therefore had waitpid called, + // and we kill it, then on unix we might ending up killing a + // newer process that happens to have the same (re-used) id + if self.exit_code.is_some() { + return Err(IoError { + kind: old_io::InvalidInput, + desc: "invalid argument: can't kill an exited process", + detail: None, + }) + } + + // A successfully delivered signal that isn't 0 (just a poll for being + // alive) is recorded for windows (see wait()) + match unsafe { self.handle.kill(signal) } { + Ok(()) if signal == 0 => Ok(()), + Ok(()) => { self.exit_signal = Some(signal); Ok(()) } + Err(e) => Err(e), + } + + } + + /// Sends a signal to this child requesting that it exits. This is + /// equivalent to sending a SIGTERM on unix platforms. + pub fn signal_exit(&mut self) -> IoResult<()> { + self.signal(PleaseExitSignal) + } + + /// Sends a signal to this child forcing it to exit. This is equivalent to + /// sending a SIGKILL on unix platforms. + pub fn signal_kill(&mut self) -> IoResult<()> { + self.signal(MustDieSignal) + } + + /// Wait for the child to exit completely, returning the status that it + /// exited with. This function will continue to have the same return value + /// after it has been called at least once. + /// + /// The stdin handle to the child process will be closed before waiting. + /// + /// # Errors + /// + /// This function can fail if a timeout was previously specified via + /// `set_timeout` and the timeout expires before the child exits. + pub fn wait(&mut self) -> IoResult { + drop(self.stdin.take()); + match self.exit_code { + Some(code) => Ok(code), + None => { + let code = try!(self.handle.wait(self.deadline)); + // On windows, waitpid will never return a signal. If a signal + // was successfully delivered to the process, however, we can + // consider it as having died via a signal. + let code = match self.exit_signal { + None => code, + Some(signal) if cfg!(windows) => ExitSignal(signal), + Some(..) => code, + }; + self.exit_code = Some(code); + Ok(code) + } + } + } + + /// Sets a timeout, in milliseconds, for future calls to wait(). + /// + /// The argument specified is a relative distance into the future, in + /// milliseconds, after which any call to wait() will return immediately + /// with a timeout error, and all future calls to wait() will not block. + /// + /// A value of `None` will clear any previous timeout, and a value of `Some` + /// will override any previously set timeout. + /// + /// # Example + /// + /// ```no_run + /// # #![allow(unstable)] + /// use std::old_io::{Command, IoResult}; + /// use std::old_io::process::ProcessExit; + /// + /// fn run_gracefully(prog: &str) -> IoResult { + /// let mut p = try!(Command::new("long-running-process").spawn()); + /// + /// // give the process 10 seconds to finish completely + /// p.set_timeout(Some(10_000)); + /// match p.wait() { + /// Ok(status) => return Ok(status), + /// Err(..) => {} + /// } + /// + /// // Attempt to exit gracefully, but don't wait for it too long + /// try!(p.signal_exit()); + /// p.set_timeout(Some(1_000)); + /// match p.wait() { + /// Ok(status) => return Ok(status), + /// Err(..) => {} + /// } + /// + /// // Well, we did our best, forcefully kill the process + /// try!(p.signal_kill()); + /// p.set_timeout(None); + /// p.wait() + /// } + /// ``` + #[unstable = "the type of the timeout is likely to change"] + pub fn set_timeout(&mut self, timeout_ms: Option) { + self.deadline = timeout_ms.map(|i| i + sys::timer::now()).unwrap_or(0); + } + + /// Simultaneously wait for the child to exit and collect all remaining + /// output on the stdout/stderr handles, returning a `ProcessOutput` + /// instance. + /// + /// The stdin handle to the child is closed before waiting. + /// + /// # Errors + /// + /// This function can fail for any of the same reasons that `wait()` can + /// fail. + pub fn wait_with_output(mut self) -> IoResult { + drop(self.stdin.take()); + fn read(stream: Option) -> Receiver>> { + let (tx, rx) = channel(); + match stream { + Some(stream) => { + Thread::spawn(move |:| { + let mut stream = stream; + tx.send(stream.read_to_end()).unwrap(); + }); + } + None => tx.send(Ok(Vec::new())).unwrap() + } + rx + } + let stdout = read(self.stdout.take()); + let stderr = read(self.stderr.take()); + + let status = try!(self.wait()); + + Ok(ProcessOutput { + status: status, + output: stdout.recv().unwrap().unwrap_or(Vec::new()), + error: stderr.recv().unwrap().unwrap_or(Vec::new()), + }) + } + + /// Forgets this process, allowing it to outlive the parent + /// + /// This function will forcefully prevent calling `wait()` on the child + /// process in the destructor, allowing the child to outlive the + /// parent. Note that this operation can easily lead to leaking the + /// resources of the child process, so care must be taken when + /// invoking this method. + pub fn forget(mut self) { + self.forget = true; + } +} + +impl Drop for Process { + fn drop(&mut self) { + if self.forget { return } + + // Close all I/O before exiting to ensure that the child doesn't wait + // forever to print some text or something similar. + drop(self.stdin.take()); + drop(self.stdout.take()); + drop(self.stderr.take()); + + self.set_timeout(None); + let _ = self.wait().unwrap(); + } +} + +#[cfg(test)] +mod tests { + use old_io::{Truncate, Write, TimedOut, timer, process, FileNotFound}; + use prelude::v1::{Ok, Err, range, drop, Some, None, Vec}; + use prelude::v1::{Path, String, Reader, Writer, Clone}; + use prelude::v1::{SliceExt, Str, StrExt, AsSlice, ToString, GenericPath}; + use old_io::fs::PathExtensions; + use old_io::timer::*; + use rt::running_on_valgrind; + use str; + use super::{CreatePipe}; + use super::{InheritFd, Process, PleaseExitSignal, Command, ProcessOutput}; + use sync::mpsc::channel; + use thread::Thread; + use time::Duration; + + // FIXME(#10380) these tests should not all be ignored on android. + + #[cfg(not(target_os="android"))] + #[test] + fn smoke() { + let p = Command::new("true").spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.wait().unwrap().success()); + } + + #[cfg(not(target_os="android"))] + #[test] + fn smoke_failure() { + match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { + Ok(..) => panic!(), + Err(..) => {} + } + } + + #[cfg(not(target_os="android"))] + #[test] + fn exit_reported_right() { + let p = Command::new("false").spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.wait().unwrap().matches_exit_status(1)); + drop(p.wait().clone()); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn signal_reported_right() { + let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + match p.wait().unwrap() { + process::ExitSignal(1) => {}, + result => panic!("not terminated by signal 1 (instead, {})", result), + } + } + + pub fn read_all(input: &mut Reader) -> String { + input.read_to_string().unwrap() + } + + pub fn run_output(cmd: Command) -> String { + let p = cmd.spawn(); + assert!(p.is_ok()); + let mut p = p.unwrap(); + assert!(p.stdout.is_some()); + let ret = read_all(p.stdout.as_mut().unwrap() as &mut Reader); + assert!(p.wait().unwrap().success()); + return ret; + } + + #[cfg(not(target_os="android"))] + #[test] + fn stdout_works() { + let mut cmd = Command::new("echo"); + cmd.arg("foobar").stdout(CreatePipe(false, true)); + assert_eq!(run_output(cmd), "foobar\n"); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn set_cwd_works() { + let mut cmd = Command::new("/bin/sh"); + cmd.arg("-c").arg("pwd") + .cwd(&Path::new("/")) + .stdout(CreatePipe(false, true)); + assert_eq!(run_output(cmd), "/\n"); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn stdin_works() { + let mut p = Command::new("/bin/sh") + .arg("-c").arg("read line; echo $line") + .stdin(CreatePipe(true, false)) + .stdout(CreatePipe(false, true)) + .spawn().unwrap(); + p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); + drop(p.stdin.take()); + let out = read_all(p.stdout.as_mut().unwrap() as &mut Reader); + assert!(p.wait().unwrap().success()); + assert_eq!(out, "foobar\n"); + } + + #[cfg(not(target_os="android"))] + #[test] + fn detach_works() { + let mut p = Command::new("true").detached().spawn().unwrap(); + assert!(p.wait().unwrap().success()); + } + + #[cfg(windows)] + #[test] + fn uid_fails_on_windows() { + assert!(Command::new("test").uid(10).spawn().is_err()); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn uid_works() { + use libc; + let mut p = Command::new("/bin/sh") + .arg("-c").arg("true") + .uid(unsafe { libc::getuid() as uint }) + .gid(unsafe { libc::getgid() as uint }) + .spawn().unwrap(); + assert!(p.wait().unwrap().success()); + } + + #[cfg(all(unix, not(target_os="android")))] + #[test] + fn uid_to_root_fails() { + use libc; + + // if we're already root, this isn't a valid test. Most of the bots run + // as non-root though (android is an exception). + if unsafe { libc::getuid() == 0 } { return } + assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_process_status() { + let mut status = Command::new("false").status().unwrap(); + assert!(status.matches_exit_status(1)); + + status = Command::new("true").status().unwrap(); + assert!(status.success()); + } + + #[test] + fn test_process_output_fail_to_start() { + match Command::new("/no-binary-by-this-name-should-exist").output() { + Err(e) => assert_eq!(e.kind, FileNotFound), + Ok(..) => panic!() + } + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_process_output_output() { + let ProcessOutput {status, output, error} + = Command::new("echo").arg("hello").output().unwrap(); + let output_str = str::from_utf8(output.as_slice()).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + // FIXME #7224 + if !running_on_valgrind() { + assert_eq!(error, Vec::new()); + } + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_process_output_error() { + let ProcessOutput {status, output, error} + = Command::new("mkdir").arg(".").output().unwrap(); + + assert!(status.matches_exit_status(1)); + assert_eq!(output, Vec::new()); + assert!(!error.is_empty()); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_finish_once() { + let mut prog = Command::new("false").spawn().unwrap(); + assert!(prog.wait().unwrap().matches_exit_status(1)); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_finish_twice() { + let mut prog = Command::new("false").spawn().unwrap(); + assert!(prog.wait().unwrap().matches_exit_status(1)); + assert!(prog.wait().unwrap().matches_exit_status(1)); + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_wait_with_output_once() { + let prog = Command::new("echo").arg("hello").spawn().unwrap(); + let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap(); + let output_str = str::from_utf8(output.as_slice()).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + // FIXME #7224 + if !running_on_valgrind() { + assert_eq!(error, Vec::new()); + } + } + + #[cfg(all(unix, not(target_os="android")))] + pub fn pwd_cmd() -> Command { + Command::new("pwd") + } + #[cfg(target_os="android")] + pub fn pwd_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("pwd"); + cmd + } + + #[cfg(windows)] + pub fn pwd_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("cd"); + cmd + } + + #[test] + fn test_keep_current_working_dir() { + use os; + let prog = pwd_cmd().spawn().unwrap(); + + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + let parent_dir = os::getcwd().unwrap(); + let child_dir = Path::new(output.trim()); + + let parent_stat = parent_dir.stat().unwrap(); + let child_stat = child_dir.stat().unwrap(); + + assert_eq!(parent_stat.unstable.device, child_stat.unstable.device); + assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode); + } + + #[test] + fn test_change_working_directory() { + use os; + // test changing to the parent of os::getcwd() because we know + // the path exists (and os::getcwd() is not expected to be root) + let parent_dir = os::getcwd().unwrap().dir_path(); + let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap(); + + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + let child_dir = Path::new(output.trim()); + + let parent_stat = parent_dir.stat().unwrap(); + let child_stat = child_dir.stat().unwrap(); + + assert_eq!(parent_stat.unstable.device, child_stat.unstable.device); + assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode); + } + + #[cfg(all(unix, not(target_os="android")))] + pub fn env_cmd() -> Command { + Command::new("env") + } + #[cfg(target_os="android")] + pub fn env_cmd() -> Command { + let mut cmd = Command::new("/system/bin/sh"); + cmd.arg("-c").arg("set"); + cmd + } + + #[cfg(windows)] + pub fn env_cmd() -> Command { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("set"); + cmd + } + + #[cfg(not(target_os="android"))] + #[test] + fn test_inherit_env() { + use os; + if running_on_valgrind() { return; } + + let prog = env_cmd().spawn().unwrap(); + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + + let r = os::env(); + for &(ref k, ref v) in r.iter() { + // don't check windows magical empty-named variables + assert!(k.is_empty() || + output.contains(format!("{}={}", *k, *v).as_slice()), + "output doesn't contain `{}={}`\n{}", + k, v, output); + } + } + #[cfg(target_os="android")] + #[test] + fn test_inherit_env() { + use os; + if running_on_valgrind() { return; } + + let mut prog = env_cmd().spawn().unwrap(); + let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); + + let r = os::env(); + for &(ref k, ref v) in r.iter() { + // don't check android RANDOM variables + if *k != "RANDOM".to_string() { + assert!(output.contains(format!("{}={}", + *k, + *v).as_slice()) || + output.contains(format!("{}=\'{}\'", + *k, + *v).as_slice())); + } + } + } + + #[test] + fn test_override_env() { + use os; + let mut new_env = vec![("RUN_TEST_NEW_ENV", "123")]; + + // In some build environments (such as chrooted Nix builds), `env` can + // only be found in the explicitly-provided PATH env variable, not in + // default places such as /bin or /usr/bin. So we need to pass through + // PATH to our sub-process. + let path_val: String; + match os::getenv("PATH") { + None => {} + Some(val) => { + path_val = val; + new_env.push(("PATH", path_val.as_slice())) + } + } + + let prog = env_cmd().env_set_all(new_env.as_slice()).spawn().unwrap(); + let result = prog.wait_with_output().unwrap(); + let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); + + assert!(output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); + } + + #[test] + fn test_add_to_env() { + let prog = env_cmd().env("RUN_TEST_NEW_ENV", "123").spawn().unwrap(); + let result = prog.wait_with_output().unwrap(); + let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); + + assert!(output.contains("RUN_TEST_NEW_ENV=123"), + "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); + } + + #[cfg(unix)] + pub fn sleeper() -> Process { + Command::new("sleep").arg("1000").spawn().unwrap() + } + #[cfg(windows)] + pub fn sleeper() -> Process { + // There's a `timeout` command on windows, but it doesn't like having + // its output piped, so instead just ping ourselves a few times with + // gaps in between so we're sure this process is alive for awhile + Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap() + } + + #[test] + fn test_kill() { + let mut p = sleeper(); + Process::kill(p.id(), PleaseExitSignal).unwrap(); + assert!(!p.wait().unwrap().success()); + } + + #[test] + fn test_exists() { + let mut p = sleeper(); + assert!(Process::kill(p.id(), 0).is_ok()); + p.signal_kill().unwrap(); + assert!(!p.wait().unwrap().success()); + } + + #[test] + fn test_zero() { + let mut p = sleeper(); + p.signal_kill().unwrap(); + for _ in range(0i, 20) { + if p.signal(0).is_err() { + assert!(!p.wait().unwrap().success()); + return + } + timer::sleep(Duration::milliseconds(100)); + } + panic!("never saw the child go away"); + } + + #[test] + fn wait_timeout() { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + p.set_timeout(None); + assert!(p.wait().is_ok()); + } + + #[test] + fn wait_timeout2() { + let (tx, rx) = channel(); + let tx2 = tx.clone(); + let _t = Thread::spawn(move|| { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + tx.send(()).unwrap(); + }); + let _t = Thread::spawn(move|| { + let mut p = sleeper(); + p.set_timeout(Some(10)); + assert_eq!(p.wait().err().unwrap().kind, TimedOut); + p.signal_kill().unwrap(); + tx2.send(()).unwrap(); + }); + rx.recv().unwrap(); + rx.recv().unwrap(); + } + + #[test] + fn forget() { + let p = sleeper(); + let id = p.id(); + p.forget(); + assert!(Process::kill(id, 0).is_ok()); + assert!(Process::kill(id, PleaseExitSignal).is_ok()); + } + + #[test] + fn dont_close_fd_on_command_spawn() { + use sys::fs; + + let path = if cfg!(windows) { + Path::new("NUL") + } else { + Path::new("/dev/null") + }; + + let fdes = match fs::open(&path, Truncate, Write) { + Ok(f) => f, + Err(_) => panic!("failed to open file descriptor"), + }; + + let mut cmd = pwd_cmd(); + let _ = cmd.stdout(InheritFd(fdes.fd())); + assert!(cmd.status().unwrap().success()); + assert!(fdes.write("extra write\n".as_bytes()).is_ok()); + } + + #[test] + #[cfg(windows)] + fn env_map_keys_ci() { + use ffi::CString; + use super::EnvKey; + let mut cmd = Command::new(""); + cmd.env("path", "foo"); + cmd.env("Path", "bar"); + let env = &cmd.env.unwrap(); + let val = env.get(&EnvKey(CString::from_slice(b"PATH"))); + assert!(val.unwrap() == &CString::from_slice(b"bar")); + } +} diff --git a/src/libstd/old_io/result.rs b/src/libstd/old_io/result.rs new file mode 100644 index 00000000000..c1474650f1e --- /dev/null +++ b/src/libstd/old_io/result.rs @@ -0,0 +1,129 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementations of I/O traits for the IoResult type +//! +//! I/O constructors return option types to allow errors to be handled. +//! These implementations allow e.g. `IoResult` to be used +//! as a `Reader` without unwrapping the result first. + +use clone::Clone; +use result::Result::{Ok, Err}; +use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle, IoResult}; + +impl Writer for IoResult { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + match *self { + Ok(ref mut writer) => writer.write(buf), + Err(ref e) => Err((*e).clone()) + } + } + + fn flush(&mut self) -> IoResult<()> { + match *self { + Ok(ref mut writer) => writer.flush(), + Err(ref e) => Err(e.clone()), + } + } +} + +impl Reader for IoResult { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match *self { + Ok(ref mut reader) => reader.read(buf), + Err(ref e) => Err(e.clone()), + } + } +} + +impl Seek for IoResult { + fn tell(&self) -> IoResult { + match *self { + Ok(ref seeker) => seeker.tell(), + Err(ref e) => Err(e.clone()), + } + } + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { + match *self { + Ok(ref mut seeker) => seeker.seek(pos, style), + Err(ref e) => Err(e.clone()) + } + } +} + +impl, L: Listener> Listener for IoResult { + fn listen(self) -> IoResult { + match self { + Ok(listener) => listener.listen(), + Err(e) => Err(e), + } + } +} + +impl> Acceptor for IoResult { + fn accept(&mut self) -> IoResult { + match *self { + Ok(ref mut acceptor) => acceptor.accept(), + Err(ref e) => Err(e.clone()), + } + } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + use super::super::mem::*; + use io; + + #[test] + fn test_option_writer() { + let mut writer: io::IoResult> = Ok(Vec::new()); + writer.write(&[0, 1, 2]).unwrap(); + writer.flush().unwrap(); + assert_eq!(writer.unwrap(), vec!(0, 1, 2)); + } + + #[test] + fn test_option_writer_error() { + let mut writer: io::IoResult> = + Err(io::standard_error(io::EndOfFile)); + + match writer.write(&[0, 0, 0]) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, io::EndOfFile), + } + match writer.flush() { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, io::EndOfFile), + } + } + + #[test] + fn test_option_reader() { + let mut reader: io::IoResult = + Ok(MemReader::new(vec!(0, 1, 2, 3))); + let mut buf = [0, 0]; + reader.read(&mut buf).unwrap(); + let b: &[_] = &[0, 1]; + assert_eq!(buf.as_slice(), b); + } + + #[test] + fn test_option_reader_error() { + let mut reader: io::IoResult = + Err(io::standard_error(io::EndOfFile)); + let mut buf = []; + + match reader.read(&mut buf) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, io::EndOfFile), + } + } +} diff --git a/src/libstd/old_io/stdio.rs b/src/libstd/old_io/stdio.rs new file mode 100644 index 00000000000..04eedc758cd --- /dev/null +++ b/src/libstd/old_io/stdio.rs @@ -0,0 +1,566 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Non-blocking access to stdin, stdout, and stderr. +//! +//! This module provides bindings to the local event loop's TTY interface, using it +//! to offer synchronous but non-blocking versions of stdio. These handles can be +//! inspected for information about terminal dimensions or for related information +//! about the stream or terminal to which it is attached. +//! +//! # Example +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io; +//! +//! let mut out = old_io::stdout(); +//! out.write(b"Hello, world!"); +//! ``` + +use self::StdSource::*; + +use boxed::Box; +use cell::RefCell; +use clone::Clone; +use failure::LOCAL_STDERR; +use fmt; +use old_io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer, + standard_error, EndOfFile, LineBufferedWriter, BufferedReader}; +use marker::{Sync, Send}; +use libc; +use mem; +use option::Option; +use option::Option::{Some, None}; +use ops::{Deref, DerefMut, FnOnce}; +use ptr; +use result::Result::{Ok, Err}; +use rt; +use slice::SliceExt; +use str::StrExt; +use string::String; +use sys::{fs, tty}; +use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT}; +use uint; +use vec::Vec; + +// And so begins the tale of acquiring a uv handle to a stdio stream on all +// platforms in all situations. Our story begins by splitting the world into two +// categories, windows and unix. Then one day the creators of unix said let +// there be redirection! And henceforth there was redirection away from the +// console for standard I/O streams. +// +// After this day, the world split into four factions: +// +// 1. Unix with stdout on a terminal. +// 2. Unix with stdout redirected. +// 3. Windows with stdout on a terminal. +// 4. Windows with stdout redirected. +// +// Many years passed, and then one day the nation of libuv decided to unify this +// world. After months of toiling, uv created three ideas: TTY, Pipe, File. +// These three ideas propagated throughout the lands and the four great factions +// decided to settle among them. +// +// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon +// doing so, they even enhanced themselves further then their Pipe/File +// brethren, becoming the dominant powers. +// +// The group of 4, however, decided to work independently. They abandoned the +// common TTY belief throughout, and even abandoned the fledgling Pipe belief. +// The members of the 4th faction decided to only align themselves with File. +// +// tl;dr; TTY works on everything but when windows stdout is redirected, in that +// case pipe also doesn't work, but magically file does! +enum StdSource { + TTY(tty::TTY), + File(fs::FileDesc), +} + +fn src(fd: libc::c_int, _readable: bool, f: F) -> T where + F: FnOnce(StdSource) -> T, +{ + match tty::TTY::new(fd) { + Ok(tty) => f(TTY(tty)), + Err(_) => f(File(fs::FileDesc::new(fd, false))), + } +} + +thread_local! { + static LOCAL_STDOUT: RefCell>> = { + RefCell::new(None) + } +} + +struct RaceBox(BufferedReader); + +unsafe impl Send for RaceBox {} +unsafe impl Sync for RaceBox {} + +/// A synchronized wrapper around a buffered reader from stdin +#[derive(Clone)] +pub struct StdinReader { + inner: Arc>, +} + +unsafe impl Send for StdinReader {} +unsafe impl Sync for StdinReader {} + +/// A guard for exclusive access to `StdinReader`'s internal `BufferedReader`. +pub struct StdinReaderGuard<'a> { + inner: MutexGuard<'a, RaceBox>, +} + +impl<'a> Deref for StdinReaderGuard<'a> { + type Target = BufferedReader; + + fn deref(&self) -> &BufferedReader { + &self.inner.0 + } +} + +impl<'a> DerefMut for StdinReaderGuard<'a> { + fn deref_mut(&mut self) -> &mut BufferedReader { + &mut self.inner.0 + } +} + +impl StdinReader { + /// Locks the `StdinReader`, granting the calling thread exclusive access + /// to the underlying `BufferedReader`. + /// + /// This provides access to methods like `chars` and `lines`. + /// + /// # Examples + /// + /// ```rust + /// use std::io; + /// + /// for line in old_io::stdin().lock().lines() { + /// println!("{}", line.unwrap()); + /// } + /// ``` + pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> { + StdinReaderGuard { + inner: self.inner.lock().unwrap() + } + } + + /// Like `Buffer::read_line`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_line(&mut self) -> IoResult { + self.inner.lock().unwrap().0.read_line() + } + + /// Like `Buffer::read_until`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_until(&mut self, byte: u8) -> IoResult> { + self.inner.lock().unwrap().0.read_until(byte) + } + + /// Like `Buffer::read_char`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_char(&mut self) -> IoResult { + self.inner.lock().unwrap().0.read_char() + } +} + +impl Reader for StdinReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner.lock().unwrap().0.read(buf) + } + + // We have to manually delegate all of these because the default impls call + // read more than once and we don't want those calls to interleave (or + // incur the costs of repeated locking). + + fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult { + self.inner.lock().unwrap().0.read_at_least(min, buf) + } + + fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec) -> IoResult { + self.inner.lock().unwrap().0.push_at_least(min, len, buf) + } + + fn read_to_end(&mut self) -> IoResult> { + self.inner.lock().unwrap().0.read_to_end() + } + + fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult { + self.inner.lock().unwrap().0.read_le_uint_n(nbytes) + } + + fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult { + self.inner.lock().unwrap().0.read_be_uint_n(nbytes) + } +} + +/// Creates a new handle to the stdin of the current process. +/// +/// The returned handle is a wrapper around a global `BufferedReader` shared +/// by all threads. If buffered access is not desired, the `stdin_raw` function +/// is provided to provided unbuffered access to stdin. +/// +/// See `stdout()` for more notes about this function. +pub fn stdin() -> StdinReader { + // We're following the same strategy as kimundi's lazy_static library + static mut STDIN: *const StdinReader = 0 as *const StdinReader; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.call_once(|| { + // The default buffer capacity is 64k, but apparently windows doesn't like + // 64k reads on stdin. See #13304 for details, but the idea is that on + // windows we use a slightly smaller buffer that's been seen to be + // acceptable. + let stdin = if cfg!(windows) { + BufferedReader::with_capacity(8 * 1024, stdin_raw()) + } else { + BufferedReader::new(stdin_raw()) + }; + let stdin = StdinReader { + inner: Arc::new(Mutex::new(RaceBox(stdin))) + }; + STDIN = mem::transmute(box stdin); + + // Make sure to free it at exit + rt::at_exit(|| { + mem::transmute::<_, Box>(STDIN); + STDIN = ptr::null(); + }); + }); + + (*STDIN).clone() + } +} + +/// Creates a new non-blocking handle to the stdin of the current process. +/// +/// Unlike `stdin()`, the returned reader is *not* a buffered reader. +/// +/// See `stdout()` for more notes about this function. +pub fn stdin_raw() -> StdReader { + src(libc::STDIN_FILENO, true, |src| StdReader { inner: src }) +} + +/// Creates a line-buffered handle to the stdout of the current process. +/// +/// Note that this is a fairly expensive operation in that at least one memory +/// allocation is performed. Additionally, this must be called from a runtime +/// task context because the stream returned will be a non-blocking object using +/// the local scheduler to perform the I/O. +/// +/// Care should be taken when creating multiple handles to an output stream for +/// a single process. While usage is still safe, the output may be surprising if +/// no synchronization is performed to ensure a sane output. +pub fn stdout() -> LineBufferedWriter { + LineBufferedWriter::new(stdout_raw()) +} + +/// Creates an unbuffered handle to the stdout of the current process +/// +/// See notes in `stdout()` for more information. +pub fn stdout_raw() -> StdWriter { + src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src }) +} + +/// Creates a line-buffered handle to the stderr of the current process. +/// +/// See `stdout()` for notes about this function. +pub fn stderr() -> LineBufferedWriter { + LineBufferedWriter::new(stderr_raw()) +} + +/// Creates an unbuffered handle to the stderr of the current process +/// +/// See notes in `stdout()` for more information. +pub fn stderr_raw() -> StdWriter { + src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src }) +} + +/// Resets the task-local stdout handle to the specified writer +/// +/// This will replace the current task's stdout handle, returning the old +/// handle. All future calls to `print` and friends will emit their output to +/// this specified handle. +/// +/// Note that this does not need to be called for all new tasks; the default +/// output handle is to the process's stdout stream. +pub fn set_stdout(stdout: Box) -> Option> { + let mut new = Some(stdout); + LOCAL_STDOUT.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { + let _ = s.flush(); + Some(s) + }) +} + +/// Resets the task-local stderr handle to the specified writer +/// +/// This will replace the current task's stderr handle, returning the old +/// handle. Currently, the stderr handle is used for printing panic messages +/// during task panic. +/// +/// Note that this does not need to be called for all new tasks; the default +/// output handle is to the process's stderr stream. +pub fn set_stderr(stderr: Box) -> Option> { + let mut new = Some(stderr); + LOCAL_STDERR.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { + let _ = s.flush(); + Some(s) + }) +} + +// Helper to access the local task's stdout handle +// +// Note that this is not a safe function to expose because you can create an +// aliased pointer very easily: +// +// with_task_stdout(|io1| { +// with_task_stdout(|io2| { +// // io1 aliases io2 +// }) +// }) +fn with_task_stdout(f: F) where F: FnOnce(&mut Writer) -> IoResult<()> { + let mut my_stdout = LOCAL_STDOUT.with(|slot| { + slot.borrow_mut().take() + }).unwrap_or_else(|| { + box stdout() as Box + }); + let result = f(&mut *my_stdout); + let mut var = Some(my_stdout); + LOCAL_STDOUT.with(|slot| { + *slot.borrow_mut() = var.take(); + }); + match result { + Ok(()) => {} + Err(e) => panic!("failed printing to stdout: {:?}", e), + } +} + +/// Flushes the local task's stdout handle. +/// +/// By default, this stream is a line-buffering stream, so flushing may be +/// necessary to ensure that all output is printed to the screen (if there are +/// no newlines printed). +/// +/// Note that logging macros do not use this stream. Using the logging macros +/// will emit output to stderr, and while they are line buffered the log +/// messages are always terminated in a newline (no need to flush). +pub fn flush() { + with_task_stdout(|io| io.flush()) +} + +/// Prints a string to the stdout of the current process. No newline is emitted +/// after the string is printed. +pub fn print(s: &str) { + with_task_stdout(|io| io.write(s.as_bytes())) +} + +/// Prints a string to the stdout of the current process. A literal +/// `\n` character is printed to the console after the string. +pub fn println(s: &str) { + with_task_stdout(|io| { + io.write(s.as_bytes()).and_then(|()| io.write(&[b'\n'])) + }) +} + +/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible +/// with the `format_args!` macro. +pub fn print_args(fmt: fmt::Arguments) { + with_task_stdout(|io| write!(io, "{}", fmt)) +} + +/// Similar to `println`, but takes a `fmt::Arguments` structure to be +/// compatible with the `format_args!` macro. +pub fn println_args(fmt: fmt::Arguments) { + with_task_stdout(|io| writeln!(io, "{}", fmt)) +} + +/// Representation of a reader of a standard input stream +pub struct StdReader { + inner: StdSource +} + +impl StdReader { + /// Returns whether this stream is attached to a TTY instance or not. + pub fn isatty(&self) -> bool { + match self.inner { + TTY(..) => true, + File(..) => false, + } + } +} + +impl Reader for StdReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + let ret = match self.inner { + TTY(ref mut tty) => { + // Flush the task-local stdout so that weird issues like a + // print!'d prompt not being shown until after the user hits + // enter. + flush(); + tty.read(buf).map(|i| i as uint) + }, + File(ref mut file) => file.read(buf).map(|i| i as uint), + }; + match ret { + // When reading a piped stdin, libuv will return 0-length reads when + // stdin reaches EOF. For pretty much all other streams it will + // return an actual EOF error, but apparently for stdin it's a + // little different. Hence, here we convert a 0 length read to an + // end-of-file indicator so the caller knows to stop reading. + Ok(0) => { Err(standard_error(EndOfFile)) } + ret @ Ok(..) | ret @ Err(..) => ret, + } + } +} + +/// Representation of a writer to a standard output stream +pub struct StdWriter { + inner: StdSource +} + +unsafe impl Send for StdWriter {} +unsafe impl Sync for StdWriter {} + +impl StdWriter { + /// Gets the size of this output window, if possible. This is typically used + /// when the writer is attached to something like a terminal, this is used + /// to fetch the dimensions of the terminal. + /// + /// If successful, returns `Ok((width, height))`. + /// + /// # Error + /// + /// This function will return an error if the output stream is not actually + /// connected to a TTY instance, or if querying the TTY instance fails. + pub fn winsize(&mut self) -> IoResult<(int, int)> { + match self.inner { + TTY(ref mut tty) => { + tty.get_winsize() + } + File(..) => { + Err(IoError { + kind: OtherIoError, + desc: "stream is not a tty", + detail: None, + }) + } + } + } + + /// Controls whether this output stream is a "raw stream" or simply a normal + /// stream. + /// + /// # Error + /// + /// This function will return an error if the output stream is not actually + /// connected to a TTY instance, or if querying the TTY instance fails. + pub fn set_raw(&mut self, raw: bool) -> IoResult<()> { + match self.inner { + TTY(ref mut tty) => { + tty.set_raw(raw) + } + File(..) => { + Err(IoError { + kind: OtherIoError, + desc: "stream is not a tty", + detail: None, + }) + } + } + } + + /// Returns whether this stream is attached to a TTY instance or not. + pub fn isatty(&self) -> bool { + match self.inner { + TTY(..) => true, + File(..) => false, + } + } +} + +impl Writer for StdWriter { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + // As with stdin on windows, stdout often can't handle writes of large + // sizes. For an example, see #14940. For this reason, chunk the output + // buffer on windows, but on unix we can just write the whole buffer all + // at once. + // + // For some other references, it appears that this problem has been + // encountered by others [1] [2]. We choose the number 8KB just because + // libuv does the same. + // + // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232 + // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html + let max_size = if cfg!(windows) {8192} else {uint::MAX}; + for chunk in buf.chunks(max_size) { + try!(match self.inner { + TTY(ref mut tty) => tty.write(chunk), + File(ref mut file) => file.write(chunk), + }) + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + + use super::*; + use sync::mpsc::channel; + use thread::Thread; + + #[test] + fn smoke() { + // Just make sure we can acquire handles + stdin(); + stdout(); + stderr(); + } + + #[test] + fn capture_stdout() { + use old_io::{ChanReader, ChanWriter}; + + let (tx, rx) = channel(); + let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); + let _t = Thread::spawn(move|| { + set_stdout(box w); + println!("hello!"); + }); + assert_eq!(r.read_to_string().unwrap(), "hello!\n"); + } + + #[test] + fn capture_stderr() { + use old_io::{ChanReader, ChanWriter, Reader}; + + let (tx, rx) = channel(); + let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); + let _t = Thread::spawn(move || -> () { + set_stderr(box w); + panic!("my special message"); + }); + let s = r.read_to_string().unwrap(); + assert!(s.contains("my special message")); + } +} diff --git a/src/libstd/old_io/tempfile.rs b/src/libstd/old_io/tempfile.rs new file mode 100644 index 00000000000..029fef7c197 --- /dev/null +++ b/src/libstd/old_io/tempfile.rs @@ -0,0 +1,182 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Temporary files and directories + +use old_io::{fs, IoError, IoErrorKind, IoResult}; +use old_io; +use iter::{IteratorExt, range}; +use ops::Drop; +use option::Option; +use option::Option::{None, Some}; +use os; +use path::{Path, GenericPath}; +use rand::{Rng, thread_rng}; +use result::Result::{Ok, Err}; +use str::StrExt; +use string::String; + +/// A wrapper for a path to temporary directory implementing automatic +/// scope-based deletion. +/// +/// # Examples +/// +/// ```no_run +/// use std::old_io::TempDir; +/// +/// { +/// // create a temporary directory +/// let tmpdir = match TempDir::new("myprefix") { +/// Ok(dir) => dir, +/// Err(e) => panic!("couldn't create temporary directory: {}", e) +/// }; +/// +/// // get the path of the temporary directory without affecting the wrapper +/// let tmppath = tmpdir.path(); +/// +/// println!("The path of temporary directory is {}", tmppath.display()); +/// +/// // the temporary directory is automatically removed when tmpdir goes +/// // out of scope at the end of the block +/// } +/// { +/// // create a temporary directory, this time using a custom path +/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") { +/// Ok(dir) => dir, +/// Err(e) => panic!("couldn't create temporary directory: {}", e) +/// }; +/// +/// // get the path of the temporary directory and disable automatic deletion in the wrapper +/// let tmppath = tmpdir.into_inner(); +/// +/// println!("The path of the not-so-temporary directory is {}", tmppath.display()); +/// +/// // the temporary directory is not removed here +/// // because the directory is detached from the wrapper +/// } +/// { +/// // create a temporary directory +/// let tmpdir = match TempDir::new("myprefix") { +/// Ok(dir) => dir, +/// Err(e) => panic!("couldn't create temporary directory: {}", e) +/// }; +/// +/// // close the temporary directory manually and check the result +/// match tmpdir.close() { +/// Ok(_) => println!("success!"), +/// Err(e) => panic!("couldn't remove temporary directory: {}", e) +/// }; +/// } +/// ``` +pub struct TempDir { + path: Option, + disarmed: bool +} + +// How many times should we (re)try finding an unused random name? It should be +// enough that an attacker will run out of luck before we run out of patience. +const NUM_RETRIES: u32 = 1 << 31; +// How many characters should we include in a random file name? It needs to +// be enough to dissuade an attacker from trying to preemptively create names +// of that length, but not so huge that we unnecessarily drain the random number +// generator of entropy. +const NUM_RAND_CHARS: uint = 12; + +impl TempDir { + /// Attempts to make a temporary directory inside of `tmpdir` whose name + /// will have the prefix `prefix`. The directory will be automatically + /// deleted once the returned wrapper is destroyed. + /// + /// If no directory can be created, `Err` is returned. + pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult { + if !tmpdir.is_absolute() { + let abs_tmpdir = try!(os::make_absolute(tmpdir)); + return TempDir::new_in(&abs_tmpdir, prefix); + } + + let mut rng = thread_rng(); + for _ in range(0, NUM_RETRIES) { + let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect(); + let leaf = if prefix.len() > 0 { + format!("{}.{}", prefix, suffix) + } else { + // If we're given an empty string for a prefix, then creating a + // directory starting with "." would lead to it being + // semi-invisible on some systems. + suffix + }; + let path = tmpdir.join(leaf); + match fs::mkdir(&path, old_io::USER_RWX) { + Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }), + Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (), + Err(e) => return Err(e) + } + } + + return Err(IoError{ + kind: IoErrorKind::PathAlreadyExists, + desc:"Exhausted", + detail: None}); + } + + /// Attempts to make a temporary directory inside of `os::tmpdir()` whose + /// name will have the prefix `prefix`. The directory will be automatically + /// deleted once the returned wrapper is destroyed. + /// + /// If no directory can be created, `Err` is returned. + pub fn new(prefix: &str) -> IoResult { + TempDir::new_in(&os::tmpdir(), prefix) + } + + /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper. + /// This discards the wrapper so that the automatic deletion of the + /// temporary directory is prevented. + pub fn into_inner(self) -> Path { + let mut tmpdir = self; + tmpdir.path.take().unwrap() + } + + /// Access the wrapped `std::path::Path` to the temporary directory. + pub fn path<'a>(&'a self) -> &'a Path { + self.path.as_ref().unwrap() + } + + /// Close and remove the temporary directory + /// + /// Although `TempDir` removes the directory on drop, in the destructor + /// any errors are ignored. To detect errors cleaning up the temporary + /// directory, call `close` instead. + pub fn close(mut self) -> IoResult<()> { + self.cleanup_dir() + } + + fn cleanup_dir(&mut self) -> IoResult<()> { + assert!(!self.disarmed); + self.disarmed = true; + match self.path { + Some(ref p) => { + fs::rmdir_recursive(p) + } + None => Ok(()) + } + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + if !self.disarmed { + let _ = self.cleanup_dir(); + } + } +} + +// the tests for this module need to change the path using change_dir, +// and this doesn't play nicely with other tests so these unit tests are located +// in src/test/run-pass/tempfile.rs diff --git a/src/libstd/old_io/test.rs b/src/libstd/old_io/test.rs new file mode 100644 index 00000000000..f49e2397d42 --- /dev/null +++ b/src/libstd/old_io/test.rs @@ -0,0 +1,175 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Various utility functions useful for writing I/O tests + +use prelude::v1::*; + +use libc; +use os; +use std::old_io::net::ip::*; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +/// Get a port number, starting at 9600, for use in tests +pub fn next_test_port() -> u16 { + static NEXT_OFFSET: AtomicUsize = ATOMIC_USIZE_INIT; + base_port() + NEXT_OFFSET.fetch_add(1, Ordering::Relaxed) as u16 +} + +// iOS has a pretty long tmpdir path which causes pipe creation +// to like: invalid argument: path must be smaller than SUN_LEN +fn next_test_unix_socket() -> String { + static COUNT: AtomicUsize = ATOMIC_USIZE_INIT; + // base port and pid are an attempt to be unique between multiple + // test-runners of different configurations running on one + // buildbot, the count is to be unique within this executable. + format!("rust-test-unix-path-{}-{}-{}", + base_port(), + unsafe {libc::getpid()}, + COUNT.fetch_add(1, Ordering::Relaxed)) +} + +/// Get a temporary path which could be the location of a unix socket +#[cfg(not(target_os = "ios"))] +pub fn next_test_unix() -> Path { + let string = next_test_unix_socket(); + if cfg!(unix) { + os::tmpdir().join(string) + } else { + Path::new(format!("{}{}", r"\\.\pipe\", string)) + } +} + +/// Get a temporary path which could be the location of a unix socket +#[cfg(target_os = "ios")] +pub fn next_test_unix() -> Path { + Path::new(format!("/var/tmp/{}", next_test_unix_socket())) +} + +/// Get a unique IPv4 localhost:port pair starting at 9600 +pub fn next_test_ip4() -> SocketAddr { + SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() } +} + +/// Get a unique IPv6 localhost:port pair starting at 9600 +pub fn next_test_ip6() -> SocketAddr { + SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() } +} + +/* +XXX: Welcome to MegaHack City. + +The bots run multiple builds at the same time, and these builds +all want to use ports. This function figures out which workspace +it is running in and assigns a port range based on it. +*/ +fn base_port() -> u16 { + + let base = 9600u16; + let range = 1000u16; + + let bases = [ + ("32-opt", base + range * 1), + ("32-nopt", base + range * 2), + ("64-opt", base + range * 3), + ("64-nopt", base + range * 4), + ("64-opt-vg", base + range * 5), + ("all-opt", base + range * 6), + ("snap3", base + range * 7), + ("dist", base + range * 8) + ]; + + // FIXME (#9639): This needs to handle non-utf8 paths + let path = os::getcwd().unwrap(); + let path_s = path.as_str().unwrap(); + + let mut final_base = base; + + for &(dir, base) in bases.iter() { + if path_s.contains(dir) { + final_base = base; + break; + } + } + + return final_base; +} + +/// Raises the file descriptor limit when running tests if necessary +pub fn raise_fd_limit() { + unsafe { darwin_fd_limit::raise_fd_limit() } +} + +/// darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the rlimit +/// maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low for our +/// multithreaded scheduler testing, depending on the number of cores available. +/// +/// This fixes issue #7772. +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[allow(non_camel_case_types)] +mod darwin_fd_limit { + use libc; + type rlim_t = libc::uint64_t; + #[repr(C)] + struct rlimit { + rlim_cur: rlim_t, + rlim_max: rlim_t + } + extern { + // name probably doesn't need to be mut, but the C function doesn't specify const + fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint, + oldp: *mut libc::c_void, oldlenp: *mut libc::size_t, + newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int; + fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int; + fn setrlimit(resource: libc::c_int, rlp: *const rlimit) -> libc::c_int; + } + static CTL_KERN: libc::c_int = 1; + static KERN_MAXFILESPERPROC: libc::c_int = 29; + static RLIMIT_NOFILE: libc::c_int = 8; + + pub unsafe fn raise_fd_limit() { + // The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc + // sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value. + use ptr::null_mut; + use mem::size_of_val; + use os::last_os_error; + + // Fetch the kern.maxfilesperproc value + let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC]; + let mut maxfiles: libc::c_int = 0; + let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; + if sysctl(&mut mib[0], 2, &mut maxfiles as *mut libc::c_int as *mut libc::c_void, &mut size, + null_mut(), 0) != 0 { + let err = last_os_error(); + panic!("raise_fd_limit: error calling sysctl: {}", err); + } + + // Fetch the current resource limits + let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0}; + if getrlimit(RLIMIT_NOFILE, &mut rlim) != 0 { + let err = last_os_error(); + panic!("raise_fd_limit: error calling getrlimit: {}", err); + } + + // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit + rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max); + + // Set our newly-increased resource limit + if setrlimit(RLIMIT_NOFILE, &rlim) != 0 { + let err = last_os_error(); + panic!("raise_fd_limit: error calling setrlimit: {}", err); + } + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +mod darwin_fd_limit { + pub unsafe fn raise_fd_limit() {} +} diff --git a/src/libstd/old_io/timer.rs b/src/libstd/old_io/timer.rs new file mode 100644 index 00000000000..7e15c9ad7fc --- /dev/null +++ b/src/libstd/old_io/timer.rs @@ -0,0 +1,481 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Synchronous Timers +//! +//! This module exposes the functionality to create timers, block the current task, +//! and create receivers which will receive notifications after a period of time. + +// FIXME: These functions take Durations but only pass ms to the backend impls. + +use sync::mpsc::{Receiver, Sender, channel}; +use time::Duration; +use old_io::IoResult; +use sys::timer::Callback; +use sys::timer::Timer as TimerImp; + +/// A synchronous timer object +/// +/// Values of this type can be used to put the current task to sleep for a +/// period of time. Handles to this timer can also be created in the form of +/// receivers which will receive notifications over time. +/// +/// # Examples +/// +/// ``` +/// # fn foo() { +/// use std::old_io::Timer; +/// use std::time::Duration; +/// +/// let mut timer = Timer::new().unwrap(); +/// timer.sleep(Duration::milliseconds(10)); // block the task for awhile +/// +/// let timeout = timer.oneshot(Duration::milliseconds(10)); +/// // do some work +/// timeout.recv().unwrap(); // wait for the timeout to expire +/// +/// let periodic = timer.periodic(Duration::milliseconds(10)); +/// loop { +/// periodic.recv().unwrap(); +/// // this loop is only executed once every 10ms +/// } +/// # } +/// ``` +/// +/// If only sleeping is necessary, then a convenience API is provided through +/// the `old_io::timer` module. +/// +/// ``` +/// # fn foo() { +/// use std::old_io::timer; +/// use std::time::Duration; +/// +/// // Put this task to sleep for 5 seconds +/// timer::sleep(Duration::seconds(5)); +/// # } +/// ``` +pub struct Timer { + inner: TimerImp, +} + +struct TimerCallback { tx: Sender<()> } + +/// Sleep the current task for the specified duration. +/// +/// When provided a zero or negative `duration`, the function will +/// return immediately. +pub fn sleep(duration: Duration) { + let timer = Timer::new(); + let mut timer = timer.ok().expect("timer::sleep: could not create a Timer"); + + timer.sleep(duration) +} + +impl Timer { + /// Creates a new timer which can be used to put the current task to sleep + /// for a number of milliseconds, or to possibly create channels which will + /// get notified after an amount of time has passed. + pub fn new() -> IoResult { + TimerImp::new().map(|t| Timer { inner: t }) + } + + /// Blocks the current task for the specified duration. + /// + /// Note that this function will cause any other receivers for this timer to + /// be invalidated (the other end will be closed). + /// + /// When provided a zero or negative `duration`, the function will + /// return immediately. + pub fn sleep(&mut self, duration: Duration) { + // Short-circuit the timer backend for 0 duration + let ms = in_ms_u64(duration); + if ms == 0 { return } + self.inner.sleep(ms); + } + + /// Creates a oneshot receiver which will have a notification sent when + /// the specified duration has elapsed. + /// + /// This does *not* block the current task, but instead returns immediately. + /// + /// Note that this invalidates any previous receiver which has been created + /// by this timer, and that the returned receiver will be invalidated once + /// the timer is destroyed (when it falls out of scope). In particular, if + /// this is called in method-chaining style, the receiver will be + /// invalidated at the end of that statement, and all `recv` calls will + /// fail. + /// + /// # Example + /// + /// ```rust + /// use std::old_io::Timer; + /// use std::time::Duration; + /// + /// let mut timer = Timer::new().unwrap(); + /// let ten_milliseconds = timer.oneshot(Duration::milliseconds(10)); + /// + /// for _ in range(0u, 100) { /* do work */ } + /// + /// // blocks until 10 ms after the `oneshot` call + /// ten_milliseconds.recv().unwrap(); + /// ``` + /// + /// ```rust + /// use std::old_io::Timer; + /// use std::time::Duration; + /// + /// // Incorrect, method chaining-style: + /// let mut five_ms = Timer::new().unwrap().oneshot(Duration::milliseconds(5)); + /// // The timer object was destroyed, so this will always fail: + /// // five_ms.recv().unwrap() + /// ``` + /// + /// When provided a zero or negative `duration`, the message will + /// be sent immediately. + pub fn oneshot(&mut self, duration: Duration) -> Receiver<()> { + let (tx, rx) = channel(); + // Short-circuit the timer backend for 0 duration + if in_ms_u64(duration) != 0 { + self.inner.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx }); + } else { + tx.send(()).unwrap(); + } + return rx + } + + /// Creates a receiver which will have a continuous stream of notifications + /// being sent each time the specified duration has elapsed. + /// + /// This does *not* block the current task, but instead returns + /// immediately. The first notification will not be received immediately, + /// but rather after the first duration. + /// + /// Note that this invalidates any previous receiver which has been created + /// by this timer, and that the returned receiver will be invalidated once + /// the timer is destroyed (when it falls out of scope). In particular, if + /// this is called in method-chaining style, the receiver will be + /// invalidated at the end of that statement, and all `recv` calls will + /// fail. + /// + /// # Example + /// + /// ```rust + /// use std::old_io::Timer; + /// use std::time::Duration; + /// + /// let mut timer = Timer::new().unwrap(); + /// let ten_milliseconds = timer.periodic(Duration::milliseconds(10)); + /// + /// for _ in range(0u, 100) { /* do work */ } + /// + /// // blocks until 10 ms after the `periodic` call + /// ten_milliseconds.recv().unwrap(); + /// + /// for _ in range(0u, 100) { /* do work */ } + /// + /// // blocks until 20 ms after the `periodic` call (*not* 10ms after the + /// // previous `recv`) + /// ten_milliseconds.recv().unwrap(); + /// ``` + /// + /// ```rust + /// use std::old_io::Timer; + /// use std::time::Duration; + /// + /// // Incorrect, method chaining-style. + /// let mut five_ms = Timer::new().unwrap().periodic(Duration::milliseconds(5)); + /// // The timer object was destroyed, so this will always fail: + /// // five_ms.recv().unwrap() + /// ``` + /// + /// When provided a zero or negative `duration`, the messages will + /// be sent without delay. + pub fn periodic(&mut self, duration: Duration) -> Receiver<()> { + let ms = in_ms_u64(duration); + // FIXME: The backend implementations don't ever send a message + // if given a 0 ms duration. Temporarily using 1ms. It's + // not clear what use a 0ms period is anyway... + let ms = if ms == 0 { 1 } else { ms }; + let (tx, rx) = channel(); + self.inner.period(ms, box TimerCallback { tx: tx }); + return rx + } +} + +impl Callback for TimerCallback { + fn call(&mut self) { + let _ = self.tx.send(()); + } +} + +fn in_ms_u64(d: Duration) -> u64 { + let ms = d.num_milliseconds(); + if ms < 0 { return 0 }; + return ms as u64; +} + +#[cfg(test)] +mod test { + use super::Timer; + use thread::Thread; + use time::Duration; + + #[test] + fn test_timer_send() { + let mut timer = Timer::new().unwrap(); + Thread::spawn(move || timer.sleep(Duration::milliseconds(1))); + } + + #[test] + fn test_io_timer_sleep_simple() { + let mut timer = Timer::new().unwrap(); + timer.sleep(Duration::milliseconds(1)); + } + + #[test] + fn test_io_timer_sleep_oneshot() { + let mut timer = Timer::new().unwrap(); + timer.oneshot(Duration::milliseconds(1)).recv().unwrap(); + } + + #[test] + fn test_io_timer_sleep_oneshot_forget() { + let mut timer = Timer::new().unwrap(); + timer.oneshot(Duration::milliseconds(100000000)); + } + + #[test] + fn oneshot_twice() { + let mut timer = Timer::new().unwrap(); + let rx1 = timer.oneshot(Duration::milliseconds(10000)); + let rx = timer.oneshot(Duration::milliseconds(1)); + rx.recv().unwrap(); + assert!(rx1.recv().is_err()); + } + + #[test] + fn test_io_timer_oneshot_then_sleep() { + let mut timer = Timer::new().unwrap(); + let rx = timer.oneshot(Duration::milliseconds(100000000)); + timer.sleep(Duration::milliseconds(1)); // this should invalidate rx + + assert!(rx.recv().is_err()); + } + + #[test] + fn test_io_timer_sleep_periodic() { + let mut timer = Timer::new().unwrap(); + let rx = timer.periodic(Duration::milliseconds(1)); + rx.recv().unwrap(); + rx.recv().unwrap(); + rx.recv().unwrap(); + } + + #[test] + fn test_io_timer_sleep_periodic_forget() { + let mut timer = Timer::new().unwrap(); + timer.periodic(Duration::milliseconds(100000000)); + } + + #[test] + fn test_io_timer_sleep_standalone() { + super::sleep(Duration::milliseconds(1)) + } + + #[test] + fn oneshot() { + let mut timer = Timer::new().unwrap(); + + let rx = timer.oneshot(Duration::milliseconds(1)); + rx.recv().unwrap(); + assert!(rx.recv().is_err()); + + let rx = timer.oneshot(Duration::milliseconds(1)); + rx.recv().unwrap(); + assert!(rx.recv().is_err()); + } + + #[test] + fn test_override() { + let mut timer = Timer::new().unwrap(); + let orx = timer.oneshot(Duration::milliseconds(100)); + let prx = timer.periodic(Duration::milliseconds(100)); + timer.sleep(Duration::milliseconds(1)); + assert!(orx.recv().is_err()); + assert!(prx.recv().is_err()); + timer.oneshot(Duration::milliseconds(1)).recv().unwrap(); + } + + #[test] + fn period() { + let mut timer = Timer::new().unwrap(); + let rx = timer.periodic(Duration::milliseconds(1)); + rx.recv().unwrap(); + rx.recv().unwrap(); + let rx2 = timer.periodic(Duration::milliseconds(1)); + rx2.recv().unwrap(); + rx2.recv().unwrap(); + } + + #[test] + fn sleep() { + let mut timer = Timer::new().unwrap(); + timer.sleep(Duration::milliseconds(1)); + timer.sleep(Duration::milliseconds(1)); + } + + #[test] + #[should_fail] + fn oneshot_fail() { + let mut timer = Timer::new().unwrap(); + let _rx = timer.oneshot(Duration::milliseconds(1)); + panic!(); + } + + #[test] + #[should_fail] + fn period_fail() { + let mut timer = Timer::new().unwrap(); + let _rx = timer.periodic(Duration::milliseconds(1)); + panic!(); + } + + #[test] + #[should_fail] + fn normal_fail() { + let _timer = Timer::new().unwrap(); + panic!(); + } + + #[test] + fn closing_channel_during_drop_doesnt_kill_everything() { + // see issue #10375 + let mut timer = Timer::new().unwrap(); + let timer_rx = timer.periodic(Duration::milliseconds(1000)); + + Thread::spawn(move|| { + let _ = timer_rx.recv(); + }); + + // when we drop the TimerWatcher we're going to destroy the channel, + // which must wake up the task on the other end + } + + #[test] + fn reset_doesnt_switch_tasks() { + // similar test to the one above. + let mut timer = Timer::new().unwrap(); + let timer_rx = timer.periodic(Duration::milliseconds(1000)); + + Thread::spawn(move|| { + let _ = timer_rx.recv(); + }); + + timer.oneshot(Duration::milliseconds(1)); + } + + #[test] + fn reset_doesnt_switch_tasks2() { + // similar test to the one above. + let mut timer = Timer::new().unwrap(); + let timer_rx = timer.periodic(Duration::milliseconds(1000)); + + Thread::spawn(move|| { + let _ = timer_rx.recv(); + }); + + timer.sleep(Duration::milliseconds(1)); + } + + #[test] + fn sender_goes_away_oneshot() { + let rx = { + let mut timer = Timer::new().unwrap(); + timer.oneshot(Duration::milliseconds(1000)) + }; + assert!(rx.recv().is_err()); + } + + #[test] + fn sender_goes_away_period() { + let rx = { + let mut timer = Timer::new().unwrap(); + timer.periodic(Duration::milliseconds(1000)) + }; + assert!(rx.recv().is_err()); + } + + #[test] + fn receiver_goes_away_oneshot() { + let mut timer1 = Timer::new().unwrap(); + timer1.oneshot(Duration::milliseconds(1)); + let mut timer2 = Timer::new().unwrap(); + // while sleeping, the previous timer should fire and not have its + // callback do something terrible. + timer2.sleep(Duration::milliseconds(2)); + } + + #[test] + fn receiver_goes_away_period() { + let mut timer1 = Timer::new().unwrap(); + timer1.periodic(Duration::milliseconds(1)); + let mut timer2 = Timer::new().unwrap(); + // while sleeping, the previous timer should fire and not have its + // callback do something terrible. + timer2.sleep(Duration::milliseconds(2)); + } + + #[test] + fn sleep_zero() { + let mut timer = Timer::new().unwrap(); + timer.sleep(Duration::milliseconds(0)); + } + + #[test] + fn sleep_negative() { + let mut timer = Timer::new().unwrap(); + timer.sleep(Duration::milliseconds(-1000000)); + } + + #[test] + fn oneshot_zero() { + let mut timer = Timer::new().unwrap(); + let rx = timer.oneshot(Duration::milliseconds(0)); + rx.recv().unwrap(); + } + + #[test] + fn oneshot_negative() { + let mut timer = Timer::new().unwrap(); + let rx = timer.oneshot(Duration::milliseconds(-1000000)); + rx.recv().unwrap(); + } + + #[test] + fn periodic_zero() { + let mut timer = Timer::new().unwrap(); + let rx = timer.periodic(Duration::milliseconds(0)); + rx.recv().unwrap(); + rx.recv().unwrap(); + rx.recv().unwrap(); + rx.recv().unwrap(); + } + + #[test] + fn periodic_negative() { + let mut timer = Timer::new().unwrap(); + let rx = timer.periodic(Duration::milliseconds(-1000000)); + rx.recv().unwrap(); + rx.recv().unwrap(); + rx.recv().unwrap(); + rx.recv().unwrap(); + } + +} diff --git a/src/libstd/old_io/util.rs b/src/libstd/old_io/util.rs new file mode 100644 index 00000000000..f3f0b8dd663 --- /dev/null +++ b/src/libstd/old_io/util.rs @@ -0,0 +1,444 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Utility implementations of Reader and Writer + +use prelude::v1::*; +use cmp; +use old_io; +use slice::bytes::MutableByteVector; + +/// Wraps a `Reader`, limiting the number of bytes that can be read from it. +#[derive(Show)] +pub struct LimitReader { + limit: uint, + inner: R +} + +impl LimitReader { + /// Creates a new `LimitReader` + pub fn new(r: R, limit: uint) -> LimitReader { + LimitReader { limit: limit, inner: r } + } + + /// Consumes the `LimitReader`, returning the underlying `Reader`. + pub fn into_inner(self) -> R { self.inner } + + /// Returns the number of bytes that can be read before the `LimitReader` + /// will return EOF. + /// + /// # Note + /// + /// The reader may reach EOF after reading fewer bytes than indicated by + /// this method if the underlying reader reaches EOF. + pub fn limit(&self) -> uint { self.limit } +} + +impl Reader for LimitReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + if self.limit == 0 { + return Err(old_io::standard_error(old_io::EndOfFile)); + } + + let len = cmp::min(self.limit, buf.len()); + let res = self.inner.read(&mut buf[..len]); + match res { + Ok(len) => self.limit -= len, + _ => {} + } + res + } +} + +impl Buffer for LimitReader { + fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> { + let amt = try!(self.inner.fill_buf()); + let buf = &amt[..cmp::min(amt.len(), self.limit)]; + if buf.len() == 0 { + Err(old_io::standard_error(old_io::EndOfFile)) + } else { + Ok(buf) + } + } + + fn consume(&mut self, amt: uint) { + // Don't let callers reset the limit by passing an overlarge value + let amt = cmp::min(amt, self.limit); + self.limit -= amt; + self.inner.consume(amt); + } + +} + +/// A `Writer` which ignores bytes written to it, like /dev/null. +#[derive(Copy, Show)] +pub struct NullWriter; + +impl Writer for NullWriter { + #[inline] + fn write(&mut self, _buf: &[u8]) -> old_io::IoResult<()> { Ok(()) } +} + +/// A `Reader` which returns an infinite stream of 0 bytes, like /dev/zero. +#[derive(Copy, Show)] +pub struct ZeroReader; + +impl Reader for ZeroReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + buf.set_memory(0); + Ok(buf.len()) + } +} + +impl Buffer for ZeroReader { + fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> { + static DATA: [u8; 64] = [0; 64]; + Ok(DATA.as_slice()) + } + + fn consume(&mut self, _amt: uint) {} +} + +/// A `Reader` which is always at EOF, like /dev/null. +#[derive(Copy, Show)] +pub struct NullReader; + +impl Reader for NullReader { + #[inline] + fn read(&mut self, _buf: &mut [u8]) -> old_io::IoResult { + Err(old_io::standard_error(old_io::EndOfFile)) + } +} + +impl Buffer for NullReader { + fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> { + Err(old_io::standard_error(old_io::EndOfFile)) + } + fn consume(&mut self, _amt: uint) {} +} + +/// A `Writer` which multiplexes writes to a set of `Writer`s. +/// +/// The `Writer`s are delegated to in order. If any `Writer` returns an error, +/// that error is returned immediately and remaining `Writer`s are not called. +#[derive(Show)] +pub struct MultiWriter { + writers: Vec +} + +impl MultiWriter where W: Writer { + /// Creates a new `MultiWriter` + pub fn new(writers: Vec) -> MultiWriter { + MultiWriter { writers: writers } + } +} + +impl Writer for MultiWriter where W: Writer { + #[inline] + fn write(&mut self, buf: &[u8]) -> old_io::IoResult<()> { + for writer in self.writers.iter_mut() { + try!(writer.write(buf)); + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> old_io::IoResult<()> { + for writer in self.writers.iter_mut() { + try!(writer.flush()); + } + Ok(()) + } +} + +/// A `Reader` which chains input from multiple `Reader`s, reading each to +/// completion before moving onto the next. +#[derive(Clone, Show)] +pub struct ChainedReader { + readers: I, + cur_reader: Option, +} + +impl> ChainedReader { + /// Creates a new `ChainedReader` + pub fn new(mut readers: I) -> ChainedReader { + let r = readers.next(); + ChainedReader { readers: readers, cur_reader: r } + } +} + +impl> Reader for ChainedReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + loop { + let err = match self.cur_reader { + Some(ref mut r) => { + match r.read(buf) { + Ok(len) => return Ok(len), + Err(ref e) if e.kind == old_io::EndOfFile => None, + Err(e) => Some(e), + } + } + None => break + }; + self.cur_reader = self.readers.next(); + match err { + Some(e) => return Err(e), + None => {} + } + } + Err(old_io::standard_error(old_io::EndOfFile)) + } +} + +/// A `Reader` which forwards input from another `Reader`, passing it along to +/// a `Writer` as well. Similar to the `tee(1)` command. +#[derive(Show)] +pub struct TeeReader { + reader: R, + writer: W, +} + +impl TeeReader { + /// Creates a new `TeeReader` + pub fn new(r: R, w: W) -> TeeReader { + TeeReader { reader: r, writer: w } + } + + /// Consumes the `TeeReader`, returning the underlying `Reader` and + /// `Writer`. + pub fn into_inner(self) -> (R, W) { + let TeeReader { reader, writer } = self; + (reader, writer) + } +} + +impl Reader for TeeReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + self.reader.read(buf).and_then(|len| { + self.writer.write(&mut buf[..len]).map(|()| len) + }) + } +} + +/// Copies all data from a `Reader` to a `Writer`. +pub fn copy(r: &mut R, w: &mut W) -> old_io::IoResult<()> { + let mut buf = [0; super::DEFAULT_BUF_SIZE]; + loop { + let len = match r.read(&mut buf) { + Ok(len) => len, + Err(ref e) if e.kind == old_io::EndOfFile => return Ok(()), + Err(e) => return Err(e), + }; + try!(w.write(&buf[..len])); + } +} + +/// An adaptor converting an `Iterator` to a `Reader`. +#[derive(Clone, Show)] +pub struct IterReader { + iter: T, +} + +impl> IterReader { + /// Creates a new `IterReader` which will read from the specified + /// `Iterator`. + pub fn new(iter: T) -> IterReader { + IterReader { iter: iter } + } +} + +impl> Reader for IterReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + let mut len = 0; + for (slot, elt) in buf.iter_mut().zip(self.iter.by_ref()) { + *slot = elt; + len += 1; + } + if len == 0 && buf.len() != 0 { + Err(old_io::standard_error(old_io::EndOfFile)) + } else { + Ok(len) + } + } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + + use old_io::{MemReader, ByRefReader}; + use io; + use super::*; + + #[test] + fn test_limit_reader_unlimited() { + let mut r = MemReader::new(vec!(0, 1, 2)); + { + let mut r = LimitReader::new(r.by_ref(), 4); + assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap()); + } + } + + #[test] + fn test_limit_reader_limited() { + let mut r = MemReader::new(vec!(0, 1, 2)); + { + let mut r = LimitReader::new(r.by_ref(), 2); + assert_eq!(vec!(0, 1), r.read_to_end().unwrap()); + } + assert_eq!(vec!(2), r.read_to_end().unwrap()); + } + + #[test] + fn test_limit_reader_limit() { + let r = MemReader::new(vec!(0, 1, 2)); + let mut r = LimitReader::new(r, 3); + assert_eq!(3, r.limit()); + assert_eq!(0, r.read_byte().unwrap()); + assert_eq!(2, r.limit()); + assert_eq!(vec!(1, 2), r.read_to_end().unwrap()); + assert_eq!(0, r.limit()); + } + + #[test] + fn test_limit_reader_overlong_consume() { + let mut r = MemReader::new(vec![0, 1, 2, 3, 4, 5]); + let mut r = LimitReader::new(r.by_ref(), 1); + r.consume(2); + assert_eq!(vec![], r.read_to_end().unwrap()); + } + + #[test] + fn test_null_writer() { + let mut s = NullWriter; + let buf = vec![0, 0, 0]; + s.write(buf.as_slice()).unwrap(); + s.flush().unwrap(); + } + + #[test] + fn test_zero_reader() { + let mut s = ZeroReader; + let mut buf = vec![1, 2, 3]; + assert_eq!(s.read(buf.as_mut_slice()), Ok(3)); + assert_eq!(vec![0, 0, 0], buf); + } + + #[test] + fn test_null_reader() { + let mut r = NullReader; + let mut buf = vec![0]; + assert!(r.read(buf.as_mut_slice()).is_err()); + } + + #[test] + fn test_multi_writer() { + static mut writes: uint = 0; + static mut flushes: uint = 0; + + struct TestWriter; + impl Writer for TestWriter { + fn write(&mut self, _buf: &[u8]) -> old_io::IoResult<()> { + unsafe { writes += 1 } + Ok(()) + } + + fn flush(&mut self) -> old_io::IoResult<()> { + unsafe { flushes += 1 } + Ok(()) + } + } + + let mut multi = MultiWriter::new(vec!(box TestWriter as Box, + box TestWriter as Box)); + multi.write(&[1, 2, 3]).unwrap(); + assert_eq!(2, unsafe { writes }); + assert_eq!(0, unsafe { flushes }); + multi.flush().unwrap(); + assert_eq!(2, unsafe { writes }); + assert_eq!(2, unsafe { flushes }); + } + + #[test] + fn test_chained_reader() { + let rs = vec!(MemReader::new(vec!(0, 1)), MemReader::new(vec!()), + MemReader::new(vec!(2, 3))); + let mut r = ChainedReader::new(rs.into_iter()); + assert_eq!(vec!(0, 1, 2, 3), r.read_to_end().unwrap()); + } + + #[test] + fn test_tee_reader() { + let mut r = TeeReader::new(MemReader::new(vec!(0, 1, 2)), + Vec::new()); + assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap()); + let (_, w) = r.into_inner(); + assert_eq!(vec!(0, 1, 2), w); + } + + #[test] + fn test_copy() { + let mut r = MemReader::new(vec!(0, 1, 2, 3, 4)); + let mut w = Vec::new(); + copy(&mut r, &mut w).unwrap(); + assert_eq!(vec!(0, 1, 2, 3, 4), w); + } + + #[test] + fn limit_reader_buffer() { + let r = &mut b"0123456789\n0123456789\n"; + { + let mut r = LimitReader::new(r.by_ref(), 3); + assert_eq!(r.read_line(), Ok("012".to_string())); + assert_eq!(r.limit(), 0); + assert_eq!(r.read_line().err().unwrap().kind, old_io::EndOfFile); + } + { + let mut r = LimitReader::new(r.by_ref(), 9); + assert_eq!(r.read_line(), Ok("3456789\n".to_string())); + assert_eq!(r.limit(), 1); + assert_eq!(r.read_line(), Ok("0".to_string())); + } + { + let mut r = LimitReader::new(r.by_ref(), 100); + assert_eq!(r.read_char(), Ok('1')); + assert_eq!(r.limit(), 99); + assert_eq!(r.read_line(), Ok("23456789\n".to_string())); + } + } + + #[test] + fn test_iter_reader() { + let mut r = IterReader::new(range(0u8, 8)); + let mut buf = [0, 0, 0]; + let len = r.read(&mut buf).unwrap(); + assert_eq!(len, 3); + assert!(buf == [0, 1, 2]); + + let len = r.read(&mut buf).unwrap(); + assert_eq!(len, 3); + assert!(buf == [3, 4, 5]); + + let len = r.read(&mut buf).unwrap(); + assert_eq!(len, 2); + assert!(buf == [6, 7, 5]); + + assert_eq!(r.read(&mut buf).unwrap_err().kind, old_io::EndOfFile); + } + + #[test] + fn iter_reader_zero_length() { + let mut r = IterReader::new(range(0u8, 8)); + let mut buf = []; + assert_eq!(Ok(0), r.read(&mut buf)); + } +} -- cgit 1.4.1-3-g733a5 From 3a07f859b880bfe4dd6f095c959422d7c6b53831 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Jan 2015 16:31:00 -0800 Subject: Fallout of io => old_io --- src/compiletest/compiletest.rs | 6 +-- src/compiletest/errors.rs | 2 +- src/compiletest/header.rs | 2 +- src/compiletest/procsrv.rs | 2 +- src/compiletest/runtest.rs | 22 ++++----- src/doc/trpl/guessing-game.md | 54 +++++++++++----------- src/doc/trpl/standard-input.md | 22 ++++----- src/libcore/error.rs | 2 +- src/libcore/result.rs | 16 +++---- src/libgraphviz/lib.rs | 18 ++++---- src/liblog/lib.rs | 8 ++-- src/librbml/io.rs | 26 +++++------ src/librbml/lib.rs | 26 +++++------ src/librustc/metadata/decoder.rs | 10 ++-- src/librustc/metadata/filesearch.rs | 4 +- src/librustc/metadata/loader.rs | 6 +-- src/librustc/middle/astencode.rs | 2 +- src/librustc/middle/dataflow.rs | 10 ++-- .../middle/infer/region_inference/graphviz.rs | 4 +- src/librustc/middle/liveness.rs | 8 ++-- src/librustc_back/archive.rs | 19 ++++---- src/librustc_back/fs.rs | 22 ++++----- src/librustc_back/rpath.rs | 2 +- src/librustc_back/target/apple_ios_base.rs | 2 +- src/librustc_back/target/mod.rs | 4 +- src/librustc_driver/driver.rs | 8 ++-- src/librustc_driver/lib.rs | 16 +++---- src/librustc_driver/pretty.rs | 24 +++++----- src/librustc_trans/back/link.rs | 12 ++--- src/librustc_trans/back/write.rs | 10 ++-- src/librustc_trans/save/mod.rs | 4 +- src/librustdoc/externalfiles.rs | 10 ++-- src/librustdoc/html/highlight.rs | 4 +- src/librustdoc/html/layout.rs | 8 ++-- src/librustdoc/html/render.rs | 32 ++++++------- src/librustdoc/lib.rs | 6 +-- src/librustdoc/markdown.rs | 10 ++-- src/librustdoc/test.rs | 16 +++---- src/libserialize/json.rs | 18 ++++---- src/libstd/failure.rs | 4 +- src/libstd/fmt.rs | 14 +++--- src/libstd/macros.rs | 8 ++-- src/libstd/old_io/buffered.rs | 2 +- src/libstd/old_io/comm_adapters.rs | 2 +- src/libstd/old_io/extensions.rs | 2 +- src/libstd/old_io/fs.rs | 8 ++-- src/libstd/old_io/mem.rs | 2 +- src/libstd/old_io/result.rs | 20 ++++---- src/libstd/old_io/stdio.rs | 4 +- src/libstd/old_io/util.rs | 2 +- src/libstd/os.rs | 32 +++++++------ src/libstd/path/mod.rs | 2 +- src/libstd/path/posix.rs | 2 +- src/libstd/path/windows.rs | 2 +- src/libstd/prelude/v1.rs | 2 +- src/libstd/rand/mod.rs | 2 +- src/libstd/rand/os.rs | 6 +-- src/libstd/rand/reader.rs | 6 +-- src/libstd/sync/mpsc/mod.rs | 4 +- src/libstd/sys/common/backtrace.rs | 2 +- src/libstd/sys/common/mod.rs | 10 ++-- src/libstd/sys/common/net.rs | 12 ++--- src/libstd/sys/unix/backtrace.rs | 4 +- src/libstd/sys/unix/ext.rs | 22 ++++----- src/libstd/sys/unix/fs.rs | 20 ++++---- src/libstd/sys/unix/mod.rs | 40 ++++++++-------- src/libstd/sys/unix/os.rs | 8 ++-- src/libstd/sys/unix/pipe.rs | 4 +- src/libstd/sys/unix/process.rs | 4 +- src/libstd/sys/unix/tcp.rs | 4 +- src/libstd/sys/unix/timer.rs | 2 +- src/libstd/sys/unix/tty.rs | 4 +- src/libstd/thread.rs | 4 +- src/libsyntax/ast_map/mod.rs | 2 +- src/libsyntax/diagnostic.rs | 18 ++++---- src/libsyntax/ext/source_util.rs | 2 +- src/libsyntax/fold.rs | 4 +- src/libsyntax/parse/lexer/comments.rs | 4 +- src/libsyntax/parse/lexer/mod.rs | 2 +- src/libsyntax/parse/mod.rs | 2 +- src/libsyntax/parse/parser.rs | 2 +- src/libsyntax/print/pp.rs | 44 +++++++++--------- src/libsyntax/print/pprust.rs | 20 ++++---- src/libterm/lib.rs | 14 +++--- src/libterm/terminfo/mod.rs | 2 +- src/libterm/terminfo/parser/compiled.rs | 6 +-- src/libterm/terminfo/searcher.rs | 4 +- src/libtest/lib.rs | 49 ++++++++++---------- src/libtest/stats.rs | 4 +- src/rustbook/book.rs | 2 +- src/rustbook/build.rs | 8 ++-- src/rustbook/error.rs | 2 +- src/rustbook/term.rs | 2 +- src/rustbook/test.rs | 2 +- src/test/bench/core-std.rs | 4 +- src/test/bench/shootout-fasta-redux.rs | 2 +- src/test/bench/shootout-fasta.rs | 10 ++-- src/test/bench/shootout-k-nucleotide-pipes.rs | 2 +- src/test/bench/shootout-k-nucleotide.rs | 6 +-- src/test/bench/shootout-mandelbrot.rs | 8 ++-- src/test/bench/shootout-reverse-complement.rs | 4 +- src/test/bench/sudoku.rs | 12 ++--- .../cannot-mutate-captured-non-mut-var.rs | 2 +- src/test/compile-fail/issue-11374.rs | 4 +- src/test/compile-fail/lint-uppercase-variables.rs | 6 +-- src/test/debuginfo/function-arg-initialization.rs | 10 ++-- .../function-prologue-stepping-no-stack-check.rs | 10 ++-- src/test/debuginfo/issue13213.rs | 2 +- .../create_and_compile.rs | 2 +- src/test/run-make/unicode-input/multiple_files.rs | 2 +- src/test/run-make/unicode-input/span_length.rs | 2 +- src/test/run-pass-valgrind/cleanup-stdin.rs | 2 +- src/test/run-pass/backtrace.rs | 2 +- src/test/run-pass/capturing-logging.rs | 2 +- src/test/run-pass/closure-reform.rs | 2 +- src/test/run-pass/colorful-write-macros.rs | 2 +- src/test/run-pass/core-run-destroy.rs | 6 +-- src/test/run-pass/issue-10626.rs | 2 +- src/test/run-pass/issue-11881.rs | 4 +- src/test/run-pass/issue-12684.rs | 2 +- src/test/run-pass/issue-12699.rs | 2 +- src/test/run-pass/issue-13304.rs | 6 +-- src/test/run-pass/issue-14456.rs | 12 ++--- src/test/run-pass/issue-14901.rs | 2 +- src/test/run-pass/issue-14940.rs | 2 +- src/test/run-pass/issue-15149.rs | 2 +- src/test/run-pass/issue-16272.rs | 2 +- src/test/run-pass/issue-16671.rs | 2 +- src/test/run-pass/issue-17121.rs | 8 ++-- src/test/run-pass/issue-17322.rs | 4 +- src/test/run-pass/issue-18619.rs | 2 +- src/test/run-pass/issue-20091.rs | 2 +- src/test/run-pass/issue-20644.rs | 2 +- src/test/run-pass/issue-20797.rs | 6 +-- src/test/run-pass/issue-2904.rs | 6 +-- src/test/run-pass/issue-4333.rs | 4 +- src/test/run-pass/issue-4446.rs | 2 +- src/test/run-pass/issue-5988.rs | 4 +- src/test/run-pass/issue-8398.rs | 4 +- src/test/run-pass/issue-9396.rs | 2 +- src/test/run-pass/logging-separate-lines.rs | 2 +- .../method-mut-self-modifies-mut-slice-lvalue.rs | 2 +- .../run-pass/out-of-stack-new-thread-no-split.rs | 2 +- src/test/run-pass/out-of-stack.rs | 2 +- src/test/run-pass/process-remove-from-env.rs | 2 +- .../run-pass/process-spawn-with-unicode-params.rs | 12 ++--- src/test/run-pass/rename-directory.rs | 12 ++--- src/test/run-pass/running-with-no-runtime.rs | 2 +- src/test/run-pass/segfault-no-out-of-stack.rs | 2 +- src/test/run-pass/signal-exit-status.rs | 2 +- src/test/run-pass/sigpipe-should-be-ignored.rs | 4 +- src/test/run-pass/stat.rs | 4 +- src/test/run-pass/task-stderr.rs | 2 +- src/test/run-pass/tcp-accept-stress.rs | 2 +- src/test/run-pass/tcp-connect-timeouts.rs | 8 ++-- src/test/run-pass/tempfile.rs | 20 ++++---- src/test/run-pass/trait-coercion.rs | 4 +- src/test/run-pass/wait-forked-but-failed-child.rs | 2 +- 158 files changed, 607 insertions(+), 605 deletions(-) (limited to 'src') diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 4659af4416b..b95e956aca2 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -24,8 +24,8 @@ extern crate getopts; extern crate log; use std::os; -use std::io; -use std::io::fs; +use std::old_io; +use std::old_io::fs; use std::str::FromStr; use std::thunk::Thunk; use getopts::{optopt, optflag, reqopt}; @@ -237,7 +237,7 @@ pub fn run_tests(config: &Config) { // sadly osx needs some file descriptor limits raised for running tests in // parallel (especially when we have lots and lots of child processes). // For context, see #8904 - io::test::raise_fd_limit(); + old_io::test::raise_fd_limit(); // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary os::setenv("__COMPAT_LAYER", "RunAsInvoker"); diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index fc815d66a4d..868512c7943 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -9,7 +9,7 @@ // except according to those terms. use self::WhichLine::*; -use std::io::{BufferedReader, File}; +use std::old_io::{BufferedReader, File}; pub struct ExpectedError { pub line: uint, diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index d7af767688e..8458d880d05 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -223,7 +223,7 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { fn iter_header(testfile: &Path, mut it: F) -> bool where F: FnMut(&str) -> bool, { - use std::io::{BufferedReader, File}; + use std::old_io::{BufferedReader, File}; let mut rdr = BufferedReader::new(File::open(testfile).unwrap()); for ln in rdr.lines() { diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs index f3f860d470d..60b040cd4ac 100644 --- a/src/compiletest/procsrv.rs +++ b/src/compiletest/procsrv.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io::process::{ProcessExit, Command, Process, ProcessOutput}; +use std::old_io::process::{ProcessExit, Command, Process, ProcessOutput}; use std::dynamic_lib::DynamicLibrary; fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) { diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index e5a973e7501..b093a53eee9 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -23,14 +23,14 @@ use util; #[cfg(target_os = "windows")] use std::ascii::AsciiExt; -use std::io::File; -use std::io::fs::PathExtensions; -use std::io::fs; -use std::io::net::tcp; -use std::io::process::ProcessExit; -use std::io::process; -use std::io::timer; -use std::io; +use std::old_io::File; +use std::old_io::fs::PathExtensions; +use std::old_io::fs; +use std::old_io::net::tcp; +use std::old_io::process::ProcessExit; +use std::old_io::process; +use std::old_io::timer; +use std::old_io; use std::os; use std::iter::repeat; use std::str; @@ -619,7 +619,7 @@ fn find_rust_src_root(config: &Config) -> Option { } fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) { - use std::io::process::{Command, ProcessOutput}; + use std::old_io::process::{Command, ProcessOutput}; if config.lldb_python_dir.is_none() { fatal("Can't run LLDB test because LLDB's python path is not set."); @@ -764,7 +764,7 @@ struct DebuggerCommands { fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str) -> DebuggerCommands { - use std::io::{BufferedReader, File}; + use std::old_io::{BufferedReader, File}; let command_directive = format!("{}-command", debugger_prefix); let check_directive = format!("{}-check", debugger_prefix); @@ -1224,7 +1224,7 @@ fn compose_and_run_compiler( fn ensure_dir(path: &Path) { if path.is_dir() { return; } - fs::mkdir(path, io::USER_RWX).unwrap(); + fs::mkdir(path, old_io::USER_RWX).unwrap(); } fn compose_and_run(config: &Config, testfile: &Path, diff --git a/src/doc/trpl/guessing-game.md b/src/doc/trpl/guessing-game.md index 6f67c88f2c0..f01b62223ca 100644 --- a/src/doc/trpl/guessing-game.md +++ b/src/doc/trpl/guessing-game.md @@ -75,14 +75,14 @@ Let's get to it! The first thing we need to do for our guessing game is allow our player to input a guess. Put this in your `src/main.rs`: ```{rust,no_run} -use std::io; +use std::old_io; fn main() { println!("Guess the number!"); println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); @@ -121,7 +121,7 @@ explanatory text, and then an example. Let's try to modify our code to add in th `random` function and see what happens: ```{rust,ignore} -use std::io; +use std::old_io; use std::rand; fn main() { @@ -133,7 +133,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); @@ -180,7 +180,7 @@ This says "please give me a random `i32` value." We can change our code to use this hint: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; fn main() { @@ -192,7 +192,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); @@ -233,7 +233,7 @@ unsigned integer approach. If we want a random positive number, we should ask fo a random positive number. Our code looks like this now: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; fn main() { @@ -245,7 +245,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); @@ -276,7 +276,7 @@ two numbers. Let's add that in, along with a `match` statement to compare our guess to the secret number: ```{rust,ignore} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -289,7 +289,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); @@ -331,7 +331,7 @@ but we've given it unsigned integers. In this case, the fix is easy, because we wrote the `cmp` function! Let's change it to take `u32`s: ```{rust,ignore} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -344,7 +344,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); @@ -397,7 +397,7 @@ Anyway, we have a `String`, but we need a `u32`. What to do? Well, there's a function for that: ```{rust,ignore} -let input = io::stdin().read_line() +let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.parse(); @@ -429,7 +429,7 @@ let input_num: Option = "5".parse(); // input_num: Option Anyway, with us now converting our input to a number, our code looks like this: ```{rust,ignore} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -442,7 +442,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.parse(); @@ -479,7 +479,7 @@ need to unwrap the Option. If you remember from before, `match` is a great way to do that. Try this code: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -492,7 +492,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.parse(); @@ -546,7 +546,7 @@ method we can use defined on them: `trim()`. One small modification, and our code looks like this: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -559,7 +559,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.trim().parse(); @@ -620,7 +620,7 @@ As we already discussed, the `loop` keyword gives us an infinite loop. Let's add that in: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -635,7 +635,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.trim().parse(); @@ -696,7 +696,7 @@ Ha! `quit` actually quits. As does any other non-number input. Well, this is suboptimal to say the least. First, let's actually quit when you win the game: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -711,7 +711,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.trim().parse(); @@ -752,7 +752,7 @@ we don't want to quit, we just want to ignore it. Change that `return` to ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -767,7 +767,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.trim().parse(); @@ -831,7 +831,7 @@ think of what it is? That's right, we don't want to print out the secret number. It was good for testing, but it kind of ruins the game. Here's our final source: ```{rust,no_run} -use std::io; +use std::old_io; use std::rand; use std::cmp::Ordering; @@ -844,7 +844,7 @@ fn main() { println!("Please input your guess."); - let input = io::stdin().read_line() + let input = old_io::stdin().read_line() .ok() .expect("Failed to read line"); let input_num: Option = input.trim().parse(); diff --git a/src/doc/trpl/standard-input.md b/src/doc/trpl/standard-input.md index 7145139bba5..0c26fb2b44f 100644 --- a/src/doc/trpl/standard-input.md +++ b/src/doc/trpl/standard-input.md @@ -8,7 +8,7 @@ and then prints it back out: fn main() { println!("Type something!"); - let input = std::io::stdin().read_line().ok().expect("Failed to read line"); + let input = std::old_io::stdin().read_line().ok().expect("Failed to read line"); println!("{}", input); } @@ -17,7 +17,7 @@ fn main() { Let's go over these chunks, one by one: ```{rust,ignore} -std::io::stdin(); +std::old_io::stdin(); ``` This calls a function, `stdin()`, that lives inside the `std::io` module. As @@ -28,7 +28,7 @@ Since writing the fully qualified name all the time is annoying, we can use the `use` statement to import it in: ```{rust} -use std::io::stdin; +use std::old_io::stdin; stdin(); ``` @@ -37,20 +37,20 @@ However, it's considered better practice to not import individual functions, but to import the module, and only use one level of qualification: ```{rust} -use std::io; +use std::old_io; -io::stdin(); +old_io::stdin(); ``` Let's update our example to use this style: ```{rust,ignore} -use std::io; +use std::old_io; fn main() { println!("Type something!"); - let input = io::stdin().read_line().ok().expect("Failed to read line"); + let input = old_io::stdin().read_line().ok().expect("Failed to read line"); println!("{}", input); } @@ -121,12 +121,12 @@ For now, this gives you enough of a basic understanding to work with. Back to the code we were working on! Here's a refresher: ```{rust,ignore} -use std::io; +use std::old_io; fn main() { println!("Type something!"); - let input = io::stdin().read_line().ok().expect("Failed to read line"); + let input = old_io::stdin().read_line().ok().expect("Failed to read line"); println!("{}", input); } @@ -136,14 +136,14 @@ With long lines like this, Rust gives you some flexibility with the whitespace. We _could_ write the example like this: ```{rust,ignore} -use std::io; +use std::old_io; fn main() { println!("Type something!"); // here, we'll show the types at each step - let input = io::stdin() // std::io::stdio::StdinReader + let input = old_io::stdin() // std::old_io::stdio::StdinReader .read_line() // IoResult .ok() // Option .expect("Failed to read line"); // String diff --git a/src/libcore/error.rs b/src/libcore/error.rs index 9ff38028df9..7bb11fb5d92 100644 --- a/src/libcore/error.rs +++ b/src/libcore/error.rs @@ -49,7 +49,7 @@ //! //! ``` //! use std::error::FromError; -//! use std::io::{File, IoError}; +//! use std::old_io::{File, IoError}; //! use std::os::{MemoryMap, MapError}; //! use std::path::Path; //! diff --git a/src/libcore/result.rs b/src/libcore/result.rs index ab0f447f6c9..28463c0f04c 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -95,7 +95,7 @@ //! by the [`Writer`](../io/trait.Writer.html) trait: //! //! ``` -//! use std::io::IoError; +//! use std::old_io::IoError; //! //! trait Writer { //! fn write_line(&mut self, s: &str) -> Result<(), IoError>; @@ -110,7 +110,7 @@ //! something like this: //! //! ```{.ignore} -//! use std::io::{File, Open, Write}; +//! use std::old_io::{File, Open, Write}; //! //! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write); //! // If `write_line` errors, then we'll never know, because the return @@ -128,7 +128,7 @@ //! a marginally useful message indicating why: //! //! ```{.no_run} -//! use std::io::{File, Open, Write}; +//! use std::old_io::{File, Open, Write}; //! //! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write); //! file.write_line("important message").ok().expect("failed to write message"); @@ -138,7 +138,7 @@ //! You might also simply assert success: //! //! ```{.no_run} -//! # use std::io::{File, Open, Write}; +//! # use std::old_io::{File, Open, Write}; //! //! # let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write); //! assert!(file.write_line("important message").is_ok()); @@ -148,7 +148,7 @@ //! Or propagate the error up the call stack with `try!`: //! //! ``` -//! # use std::io::{File, Open, Write, IoError}; +//! # use std::old_io::{File, Open, Write, IoError}; //! fn write_message() -> Result<(), IoError> { //! let mut file = File::open_mode(&Path::new("valuable_data.txt"), Open, Write); //! try!(file.write_line("important message")); @@ -167,7 +167,7 @@ //! It replaces this: //! //! ``` -//! use std::io::{File, Open, Write, IoError}; +//! use std::old_io::{File, Open, Write, IoError}; //! //! struct Info { //! name: String, @@ -191,7 +191,7 @@ //! With this: //! //! ``` -//! use std::io::{File, Open, Write, IoError}; +//! use std::old_io::{File, Open, Write, IoError}; //! //! struct Info { //! name: String, @@ -444,7 +444,7 @@ impl Result { /// ignoring I/O and parse errors: /// /// ``` - /// use std::io::IoResult; + /// use std::old_io::IoResult; /// /// let mut buffer = &mut b"1\n2\n3\n4\n"; /// diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 0ed32b7bf4f..ebe2487215a 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -96,7 +96,7 @@ //! ```no_run //! # pub fn render_to(output: &mut W) { unimplemented!() } //! pub fn main() { -//! use std::io::File; +//! use std::old_io::File; //! let mut f = File::create(&Path::new("example1.dot")); //! render_to(&mut f) //! } @@ -188,7 +188,7 @@ //! ```no_run //! # pub fn render_to(output: &mut W) { unimplemented!() } //! pub fn main() { -//! use std::io::File; +//! use std::old_io::File; //! let mut f = File::create(&Path::new("example2.dot")); //! render_to(&mut f) //! } @@ -252,7 +252,7 @@ //! ```no_run //! # pub fn render_to(output: &mut W) { unimplemented!() } //! pub fn main() { -//! use std::io::File; +//! use std::old_io::File; //! let mut f = File::create(&Path::new("example3.dot")); //! render_to(&mut f) //! } @@ -279,7 +279,7 @@ use self::LabelText::*; use std::borrow::IntoCow; -use std::io; +use std::old_io; use std::string::CowString; use std::vec::CowVec; @@ -532,7 +532,7 @@ pub fn default_options() -> Vec { vec![] } /// (Simple wrapper around `render_opts` that passes a default set of options.) pub fn render<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>( g: &'a G, - w: &mut W) -> io::IoResult<()> { + w: &mut W) -> old_io::IoResult<()> { render_opts(g, w, &[]) } @@ -541,14 +541,14 @@ pub fn render<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>( g: &'a G, w: &mut W, - options: &[RenderOption]) -> io::IoResult<()> + options: &[RenderOption]) -> old_io::IoResult<()> { - fn writeln(w: &mut W, arg: &[&str]) -> io::IoResult<()> { + fn writeln(w: &mut W, arg: &[&str]) -> old_io::IoResult<()> { for &s in arg.iter() { try!(w.write_str(s)); } w.write_char('\n') } - fn indent(w: &mut W) -> io::IoResult<()> { + fn indent(w: &mut W) -> old_io::IoResult<()> { w.write_str(" ") } @@ -590,7 +590,7 @@ mod tests { use self::NodeLabels::*; use super::{Id, Labeller, Nodes, Edges, GraphWalk, render}; use super::LabelText::{self, LabelStr, EscStr}; - use std::io::IoResult; + use std::old_io::IoResult; use std::borrow::IntoCow; use std::iter::repeat; diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs index e7c5bc35f76..6712f153c09 100644 --- a/src/liblog/lib.rs +++ b/src/liblog/lib.rs @@ -174,8 +174,8 @@ use std::cell::RefCell; use std::fmt; -use std::io::LineBufferedWriter; -use std::io; +use std::old_io::LineBufferedWriter; +use std::old_io; use std::mem; use std::os; use std::ptr; @@ -232,7 +232,7 @@ pub trait Logger { } struct DefaultLogger { - handle: LineBufferedWriter, + handle: LineBufferedWriter, } /// Wraps the log level with fmt implementations. @@ -294,7 +294,7 @@ pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) { let mut logger = LOCAL_LOGGER.with(|s| { s.borrow_mut().take() }).unwrap_or_else(|| { - box DefaultLogger { handle: io::stderr() } as Box + box DefaultLogger { handle: old_io::stderr() } as Box }); logger.log(&LogRecord { level: LogLevel(level), diff --git a/src/librbml/io.rs b/src/librbml/io.rs index 9c746c69baa..d23f5c68359 100644 --- a/src/librbml/io.rs +++ b/src/librbml/io.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io::{IoError, IoResult, SeekStyle}; -use std::io; +use std::old_io::{IoError, IoResult, SeekStyle}; +use std::old_io; use std::slice; use std::iter::repeat; @@ -18,14 +18,14 @@ static BUF_CAPACITY: uint = 128; fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult { // compute offset as signed and clamp to prevent overflow let pos = match seek { - io::SeekSet => 0, - io::SeekEnd => end, - io::SeekCur => cur, + old_io::SeekSet => 0, + old_io::SeekEnd => end, + old_io::SeekCur => cur, } as i64; if offset + pos < 0 { Err(IoError { - kind: io::InvalidInput, + kind: old_io::InvalidInput, desc: "invalid seek to a negative offset", detail: None }) @@ -132,7 +132,7 @@ impl Seek for SeekableMemWriter { mod tests { extern crate test; use super::SeekableMemWriter; - use std::io; + use std::old_io; use std::iter::repeat; use test::Bencher; @@ -148,23 +148,23 @@ mod tests { let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; assert_eq!(writer.get_ref(), b); - writer.seek(0, io::SeekSet).unwrap(); + writer.seek(0, old_io::SeekSet).unwrap(); assert_eq!(writer.tell(), Ok(0)); writer.write(&[3, 4]).unwrap(); let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; assert_eq!(writer.get_ref(), b); - writer.seek(1, io::SeekCur).unwrap(); + writer.seek(1, old_io::SeekCur).unwrap(); writer.write(&[0, 1]).unwrap(); let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; assert_eq!(writer.get_ref(), b); - writer.seek(-1, io::SeekEnd).unwrap(); + writer.seek(-1, old_io::SeekEnd).unwrap(); writer.write(&[1, 2]).unwrap(); let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; assert_eq!(writer.get_ref(), b); - writer.seek(1, io::SeekEnd).unwrap(); + writer.seek(1, old_io::SeekEnd).unwrap(); writer.write(&[1]).unwrap(); let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; assert_eq!(writer.get_ref(), b); @@ -173,14 +173,14 @@ mod tests { #[test] fn seek_past_end() { let mut r = SeekableMemWriter::new(); - r.seek(10, io::SeekSet).unwrap(); + r.seek(10, old_io::SeekSet).unwrap(); assert!(r.write(&[3]).is_ok()); } #[test] fn seek_before_0() { let mut r = SeekableMemWriter::new(); - assert!(r.seek(-1, io::SeekSet).is_err()); + assert!(r.seek(-1, old_io::SeekSet).is_err()); } fn do_bench_seekable_mem_writer(b: &mut Bencher, times: uint, len: uint) { diff --git a/src/librbml/lib.rs b/src/librbml/lib.rs index 50fe56ff5c0..56944fac35e 100644 --- a/src/librbml/lib.rs +++ b/src/librbml/lib.rs @@ -111,7 +111,7 @@ pub enum EbmlEncoderTag { pub enum Error { IntTooBig(uint), Expected(String), - IoError(std::io::IoError), + IoError(std::old_io::IoError), ApplicationError(String) } @@ -127,7 +127,7 @@ pub mod reader { use std::char; use std::int; - use std::io::extensions::u64_from_be_bytes; + use std::old_io::extensions::u64_from_be_bytes; use std::mem::transmute; use std::num::Int; use std::option::Option; @@ -685,9 +685,9 @@ pub mod reader { pub mod writer { use std::clone::Clone; - use std::io::extensions::u64_to_be_bytes; - use std::io::{Writer, Seek}; - use std::io; + use std::old_io::extensions::u64_to_be_bytes; + use std::old_io::{Writer, Seek}; + use std::old_io; use std::mem; use super::{ EsVec, EsMap, EsEnum, EsVecLen, EsVecElt, EsMapLen, EsMapKey, @@ -698,7 +698,7 @@ pub mod writer { use serialize; - pub type EncodeResult = io::IoResult<()>; + pub type EncodeResult = old_io::IoResult<()>; // rbml writing pub struct Encoder<'a, W:'a> { @@ -714,8 +714,8 @@ pub mod writer { n as u8]), 4u => w.write(&[0x10u8 | ((n >> 24_u) as u8), (n >> 16_u) as u8, (n >> 8_u) as u8, n as u8]), - _ => Err(io::IoError { - kind: io::OtherIoError, + _ => Err(old_io::IoError { + kind: old_io::OtherIoError, desc: "int too big", detail: Some(format!("{}", n)) }) @@ -727,8 +727,8 @@ pub mod writer { if n < 0x4000_u { return write_sized_vuint(w, n, 2u); } if n < 0x200000_u { return write_sized_vuint(w, n, 3u); } if n < 0x10000000_u { return write_sized_vuint(w, n, 4u); } - Err(io::IoError { - kind: io::OtherIoError, + Err(old_io::IoError { + kind: old_io::OtherIoError, desc: "int too big", detail: Some(format!("{}", n)) }) @@ -766,10 +766,10 @@ pub mod writer { pub fn end_tag(&mut self) -> EncodeResult { let last_size_pos = self.size_positions.pop().unwrap(); let cur_pos = try!(self.writer.tell()); - try!(self.writer.seek(last_size_pos as i64, io::SeekSet)); + try!(self.writer.seek(last_size_pos as i64, old_io::SeekSet)); let size = cur_pos as uint - last_size_pos - 4; try!(write_sized_vuint(self.writer, size, 4u)); - let r = try!(self.writer.seek(cur_pos as i64, io::SeekSet)); + let r = try!(self.writer.seek(cur_pos as i64, old_io::SeekSet)); debug!("End tag (size = {:?})", size); Ok(r) @@ -883,7 +883,7 @@ pub mod writer { } impl<'a, W: Writer + Seek> serialize::Encoder for Encoder<'a, W> { - type Error = io::IoError; + type Error = old_io::IoError; fn emit_nil(&mut self) -> EncodeResult { Ok(()) diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 1197276b990..e6f76dedca9 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -34,8 +34,8 @@ use middle::astencode::vtable_decoder_helpers; use std::collections::HashMap; use std::hash::{self, Hash, SipHasher}; -use std::io::extensions::u64_from_be_bytes; -use std::io; +use std::old_io::extensions::u64_from_be_bytes; +use std::old_io; use std::num::FromPrimitive; use std::rc::Rc; use std::str; @@ -1178,7 +1178,7 @@ fn get_attributes(md: rbml::Doc) -> Vec { } fn list_crate_attributes(md: rbml::Doc, hash: &Svh, - out: &mut io::Writer) -> io::IoResult<()> { + out: &mut old_io::Writer) -> old_io::IoResult<()> { try!(write!(out, "=Crate Attributes ({})=\n", *hash)); let r = get_attributes(md); @@ -1223,7 +1223,7 @@ pub fn get_crate_deps(data: &[u8]) -> Vec { return deps; } -fn list_crate_deps(data: &[u8], out: &mut io::Writer) -> io::IoResult<()> { +fn list_crate_deps(data: &[u8], out: &mut old_io::Writer) -> old_io::IoResult<()> { try!(write!(out, "=External Dependencies=\n")); for dep in get_crate_deps(data).iter() { try!(write!(out, "{} {}-{}\n", dep.cnum, dep.name, dep.hash)); @@ -1262,7 +1262,7 @@ pub fn get_crate_name(data: &[u8]) -> String { maybe_get_crate_name(data).expect("no crate name in crate") } -pub fn list_crate_metadata(bytes: &[u8], out: &mut io::Writer) -> io::IoResult<()> { +pub fn list_crate_metadata(bytes: &[u8], out: &mut old_io::Writer) -> old_io::IoResult<()> { let hash = get_crate_hash(bytes); let md = rbml::Doc::new(bytes); try!(list_crate_attributes(md, &hash, out)); diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index ddee1f79d6a..26046cfb43d 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -13,8 +13,8 @@ pub use self::FileMatch::*; use std::collections::HashSet; -use std::io::fs::PathExtensions; -use std::io::fs; +use std::old_io::fs::PathExtensions; +use std::old_io::fs; use std::os; use util::fs as myfs; diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index b1043a4152c..3ee4017292c 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -231,8 +231,8 @@ use rustc_back::target::Target; use std::ffi::CString; use std::cmp; use std::collections::HashMap; -use std::io::fs::PathExtensions; -use std::io; +use std::old_io::fs::PathExtensions; +use std::old_io; use std::ptr; use std::slice; use std::time::Duration; @@ -796,7 +796,7 @@ pub fn read_meta_section_name(is_osx: bool) -> &'static str { // A diagnostic function for dumping crate metadata to an output stream pub fn list_file_metadata(is_osx: bool, path: &Path, - out: &mut io::Writer) -> io::IoResult<()> { + out: &mut old_io::Writer) -> old_io::IoResult<()> { match get_metadata_section(is_osx, path) { Ok(bytes) => decoder::list_crate_metadata(bytes.as_slice(), out), Err(msg) => { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index fcc2be985a5..091ef9d52eb 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -37,7 +37,7 @@ use syntax::parse::token; use syntax::ptr::P; use syntax; -use std::io::Seek; +use std::old_io::Seek; use std::rc::Rc; use rbml::io::SeekableMemWriter; diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index a1727869810..d3c843d1d50 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -19,7 +19,7 @@ pub use self::EntryOrExit::*; use middle::cfg; use middle::cfg::CFGIndex; use middle::ty; -use std::io; +use std::old_io; use std::uint; use std::iter::repeat; use syntax::ast; @@ -105,7 +105,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> { impl<'a, 'tcx, O:DataFlowOperator> pprust::PpAnn for DataFlowContext<'a, 'tcx, O> { fn pre(&self, ps: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { + node: pprust::AnnNode) -> old_io::IoResult<()> { let id = match node { pprust::NodeIdent(_) | pprust::NodeName(_) => 0, pprust::NodeExpr(expr) => expr.id, @@ -457,13 +457,13 @@ impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> { debug!("Dataflow result for {}:", self.analysis_name); debug!("{}", { - self.pretty_print_to(box io::stderr(), blk).unwrap(); + self.pretty_print_to(box old_io::stderr(), blk).unwrap(); "" }); } - fn pretty_print_to(&self, wr: Box, - blk: &ast::Block) -> io::IoResult<()> { + fn pretty_print_to(&self, wr: Box, + blk: &ast::Block) -> old_io::IoResult<()> { let mut ps = pprust::rust_printer_annotated(wr, self); try!(ps.cbox(pprust::indent_unit)); try!(ps.ibox(0u)); diff --git a/src/librustc/middle/infer/region_inference/graphviz.rs b/src/librustc/middle/infer/region_inference/graphviz.rs index f8f962cd86d..8803fe7cf38 100644 --- a/src/librustc/middle/infer/region_inference/graphviz.rs +++ b/src/librustc/middle/infer/region_inference/graphviz.rs @@ -26,7 +26,7 @@ use util::nodemap::{FnvHashMap, FnvHashSet}; use util::ppaux::Repr; use std::collections::hash_map::Entry::Vacant; -use std::io::{self, File}; +use std::old_io::{self, File}; use std::os; use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; use syntax::ast; @@ -217,7 +217,7 @@ pub type ConstraintMap<'tcx> = FnvHashMap>; fn dump_region_constraints_to<'a, 'tcx:'a >(tcx: &'a ty::ctxt<'tcx>, map: &ConstraintMap<'tcx>, - path: &str) -> io::IoResult<()> { + path: &str) -> old_io::IoResult<()> { debug!("dump_region_constraints map (len: {}) path: {}", map.len(), path); let g = ConstraintGraph::new(tcx, format!("region_constraints"), map); let mut f = File::create(&Path::new(path)); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 0131b9f1491..00fa6546b48 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -118,7 +118,7 @@ use middle::ty::ClosureTyper; use lint; use util::nodemap::NodeMap; -use std::{fmt, io, uint}; +use std::{fmt, old_io, uint}; use std::rc::Rc; use std::iter::repeat; use syntax::ast::{self, NodeId, Expr}; @@ -693,10 +693,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } fn write_vars(&self, - wr: &mut io::Writer, + wr: &mut old_io::Writer, ln: LiveNode, mut test: F) - -> io::IoResult<()> where + -> old_io::IoResult<()> where F: FnMut(uint) -> LiveNode, { let node_base_idx = self.idx(ln, Variable(0)); @@ -740,7 +740,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn ln_str(&self, ln: LiveNode) -> String { let mut wr = Vec::new(); { - let wr = &mut wr as &mut io::Writer; + let wr = &mut wr as &mut old_io::Writer; write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln)); self.write_vars(wr, ln, |idx| self.users[idx].reader); write!(wr, " writes"); diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs index fa754b4a301..ee8bc71668b 100644 --- a/src/librustc_back/archive.rs +++ b/src/librustc_back/archive.rs @@ -10,10 +10,10 @@ //! A helper class for dealing with static archives -use std::io::fs::PathExtensions; -use std::io::process::{Command, ProcessOutput}; -use std::io::{fs, TempDir}; -use std::io; +use std::old_io::fs::PathExtensions; +use std::old_io::process::{Command, ProcessOutput}; +use std::old_io::{fs, TempDir}; +use std::old_io; use std::os; use std::str; use syntax::diagnostic::Handler as ErrorHandler; @@ -172,7 +172,7 @@ impl<'a> ArchiveBuilder<'a> { /// Adds all of the contents of a native library to this archive. This will /// search in the relevant locations for a library named `name`. - pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> { + pub fn add_native_library(&mut self, name: &str) -> old_io::IoResult<()> { let location = find_library(name, &self.archive.slib_prefix[], &self.archive.slib_suffix[], @@ -187,7 +187,7 @@ impl<'a> ArchiveBuilder<'a> { /// This ignores adding the bytecode from the rlib, and if LTO is enabled /// then the object file also isn't added. pub fn add_rlib(&mut self, rlib: &Path, name: &str, - lto: bool) -> io::IoResult<()> { + lto: bool) -> old_io::IoResult<()> { // Ignoring obj file starting with the crate name // as simple comparison is not enough - there // might be also an extra name suffix @@ -205,7 +205,7 @@ impl<'a> ArchiveBuilder<'a> { } /// Adds an arbitrary file to this archive - pub fn add_file(&mut self, file: &Path) -> io::IoResult<()> { + pub fn add_file(&mut self, file: &Path) -> old_io::IoResult<()> { let filename = Path::new(file.filename().unwrap()); let new_file = self.work_dir.path().join(&filename); try!(fs::copy(file, &new_file)); @@ -274,8 +274,9 @@ impl<'a> ArchiveBuilder<'a> { self.archive } - fn add_archive(&mut self, archive: &Path, name: &str, mut skip: F) -> io::IoResult<()> where - F: FnMut(&str) -> bool, + fn add_archive(&mut self, archive: &Path, name: &str, + mut skip: F) -> old_io::IoResult<()> + where F: FnMut(&str) -> bool, { let loc = TempDir::new("rsar").unwrap(); diff --git a/src/librustc_back/fs.rs b/src/librustc_back/fs.rs index d7deb09985f..24f81b02478 100644 --- a/src/librustc_back/fs.rs +++ b/src/librustc_back/fs.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io; -use std::io::fs; +use std::old_io; +use std::old_io::fs; use std::os; /// Returns an absolute path in the filesystem that `path` points to. The /// returned path does not contain any symlinks in its hierarchy. -pub fn realpath(original: &Path) -> io::IoResult { +pub fn realpath(original: &Path) -> old_io::IoResult { static MAX_LINKS_FOLLOWED: uint = 256; let original = os::make_absolute(original).unwrap(); @@ -32,12 +32,12 @@ pub fn realpath(original: &Path) -> io::IoResult { loop { if followed == MAX_LINKS_FOLLOWED { - return Err(io::standard_error(io::InvalidInput)) + return Err(old_io::standard_error(old_io::InvalidInput)) } match fs::lstat(&result) { Err(..) => break, - Ok(ref stat) if stat.kind != io::FileType::Symlink => break, + Ok(ref stat) if stat.kind != old_io::FileType::Symlink => break, Ok(..) => { followed += 1; let path = try!(fs::readlink(&result)); @@ -53,10 +53,10 @@ pub fn realpath(original: &Path) -> io::IoResult { #[cfg(all(not(windows), test))] mod test { - use std::io; - use std::io::fs::{File, symlink, mkdir, mkdir_recursive}; + use std::old_io; + use std::old_io::fs::{File, symlink, mkdir, mkdir_recursive}; use super::realpath; - use std::io::TempDir; + use std::old_io::TempDir; #[test] fn realpath_works() { @@ -68,7 +68,7 @@ mod test { let linkdir = tmpdir.join("test3"); File::create(&file).unwrap(); - mkdir(&dir, io::USER_RWX).unwrap(); + mkdir(&dir, old_io::USER_RWX).unwrap(); symlink(&file, &link).unwrap(); symlink(&dir, &linkdir).unwrap(); @@ -91,8 +91,8 @@ mod test { let e = d.join("e"); let f = a.join("f"); - mkdir_recursive(&b, io::USER_RWX).unwrap(); - mkdir_recursive(&d, io::USER_RWX).unwrap(); + mkdir_recursive(&b, old_io::USER_RWX).unwrap(); + mkdir_recursive(&d, old_io::USER_RWX).unwrap(); File::create(&f).unwrap(); symlink(&Path::new("../d/e"), &c).unwrap(); symlink(&Path::new("../f"), &e).unwrap(); diff --git a/src/librustc_back/rpath.rs b/src/librustc_back/rpath.rs index d24fd6a5b3f..bafd5fbe944 100644 --- a/src/librustc_back/rpath.rs +++ b/src/librustc_back/rpath.rs @@ -11,7 +11,7 @@ use std::collections::HashSet; use std::os; -use std::io::IoError; +use std::old_io::IoError; use syntax::ast; pub struct RPathConfig where diff --git a/src/librustc_back/target/apple_ios_base.rs b/src/librustc_back/target/apple_ios_base.rs index ac133cabc3b..715bcc4f36d 100644 --- a/src/librustc_back/target/apple_ios_base.rs +++ b/src/librustc_back/target/apple_ios_base.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::io::{Command, IoError, OtherIoError}; +use std::old_io::{Command, IoError, OtherIoError}; use target::TargetOptions; use self::Arch::*; diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 4626f2dc483..36d83da725e 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -48,7 +48,7 @@ use serialize::json::Json; use syntax::{diagnostic, abi}; use std::default::Default; -use std::io::fs::PathExtensions; +use std::old_io::fs::PathExtensions; mod windows_base; mod linux_base; @@ -302,7 +302,7 @@ impl Target { /// JSON decoding. pub fn search(target: &str) -> Result { use std::os; - use std::io::File; + use std::old_io::File; use std::path::Path; use serialize::json; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 3fac5ba9674..36a9c0e16f0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -30,8 +30,8 @@ use rustc_privacy; use serialize::json; -use std::io; -use std::io::fs; +use std::old_io; +use std::old_io::fs; use std::os; use syntax::ast; use syntax::ast_map; @@ -787,14 +787,14 @@ fn write_out_deps(sess: &Session, _ => return, }; - let result = (|&:| -> io::IoResult<()> { + let result = (|&:| -> old_io::IoResult<()> { // Build a list of files used to compile the output and // write Makefile-compatible dependency rules let files: Vec = sess.codemap().files.borrow() .iter().filter(|fmap| fmap.is_real_file()) .map(|fmap| escape_dep_filename(&fmap.name[])) .collect(); - let mut file = try!(io::File::create(&deps_filename)); + let mut file = try!(old_io::File::create(&deps_filename)); for path in out_filenames.iter() { try!(write!(&mut file as &mut Writer, "{}: {}\n\n", path.display(), files.connect(" "))); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 0e940b85bd8..fdda8e737a9 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -64,7 +64,7 @@ use rustc::metadata::creader::CrateOrString::Str; use rustc::util::common::time; use std::cmp::Ordering::Equal; -use std::io; +use std::old_io; use std::iter::repeat; use std::os; use std::sync::mpsc::channel; @@ -133,7 +133,7 @@ fn run_compiler(args: &[String]) { 1u => { let ifile = &matches.free[0][]; if ifile == "-" { - let contents = io::stdin().read_to_end().unwrap(); + let contents = old_io::stdin().read_to_end().unwrap(); let src = String::from_utf8(contents).unwrap(); (Input::Str(src), None) } else { @@ -187,7 +187,7 @@ fn run_compiler(args: &[String]) { if r.contains(&("ls".to_string())) { match input { Input::File(ref ifile) => { - let mut stdout = io::stdout(); + let mut stdout = old_io::stdout(); list_metadata(&sess, &(*ifile), &mut stdout).unwrap(); } Input::Str(_) => { @@ -590,7 +590,7 @@ fn parse_crate_attrs(sess: &Session, input: &Input) -> } pub fn list_metadata(sess: &Session, path: &Path, - out: &mut io::Writer) -> io::IoResult<()> { + out: &mut old_io::Writer) -> old_io::IoResult<()> { metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, path, out) } @@ -603,8 +603,8 @@ pub fn monitor(f: F) { static STACK_SIZE: uint = 8 * 1024 * 1024; // 8MB let (tx, rx) = channel(); - let w = io::ChanWriter::new(tx); - let mut r = io::ChanReader::new(rx); + let w = old_io::ChanWriter::new(tx); + let mut r = old_io::ChanReader::new(rx); let mut cfg = thread::Builder::new().name("rustc".to_string()); @@ -614,7 +614,7 @@ pub fn monitor(f: F) { cfg = cfg.stack_size(STACK_SIZE); } - match cfg.scoped(move || { std::io::stdio::set_stderr(box w); f() }).join() { + match cfg.scoped(move || { std::old_io::stdio::set_stderr(box w); f() }).join() { Ok(()) => { /* fallthrough */ } Err(value) => { // Thread panicked without emitting a fatal diagnostic @@ -656,7 +656,7 @@ pub fn monitor(f: F) { // Panic so the process returns a failure code, but don't pollute the // output with some unnecessary panic messages, we've already // printed everything that we needed to. - io::stdio::set_stderr(box io::util::NullWriter); + old_io::stdio::set_stderr(box old_io::util::NullWriter); panic!(); } } diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 582e1032324..b09e9f14357 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -38,7 +38,7 @@ use syntax::ptr::P; use graphviz as dot; -use std::io::{self, MemReader}; +use std::old_io::{self, MemReader}; use std::option; use std::str::FromStr; @@ -208,7 +208,7 @@ impl<'ast> PrinterSupport<'ast> for IdentifiedAnnotation<'ast> { impl<'ast> pprust::PpAnn for IdentifiedAnnotation<'ast> { fn pre(&self, s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { + node: pprust::AnnNode) -> old_io::IoResult<()> { match node { pprust::NodeExpr(_) => s.popen(), _ => Ok(()) @@ -216,7 +216,7 @@ impl<'ast> pprust::PpAnn for IdentifiedAnnotation<'ast> { } fn post(&self, s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { + node: pprust::AnnNode) -> old_io::IoResult<()> { match node { pprust::NodeIdent(_) | pprust::NodeName(_) => Ok(()), @@ -259,7 +259,7 @@ impl<'ast> PrinterSupport<'ast> for HygieneAnnotation<'ast> { impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { fn post(&self, s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { + node: pprust::AnnNode) -> old_io::IoResult<()> { match node { pprust::NodeIdent(&ast::Ident { name: ast::Name(nm), ctxt }) => { try!(pp::space(&mut s.s)); @@ -294,7 +294,7 @@ impl<'tcx> PrinterSupport<'tcx> for TypedAnnotation<'tcx> { impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> { fn pre(&self, s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { + node: pprust::AnnNode) -> old_io::IoResult<()> { match node { pprust::NodeExpr(_) => s.popen(), _ => Ok(()) @@ -302,7 +302,7 @@ impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> { } fn post(&self, s: &mut pprust::State, - node: pprust::AnnNode) -> io::IoResult<()> { + node: pprust::AnnNode) -> old_io::IoResult<()> { let tcx = &self.analysis.ty_cx; match node { pprust::NodeExpr(expr) => { @@ -548,9 +548,9 @@ pub fn pretty_print_input(sess: Session, let mut rdr = MemReader::new(src); let out = match ofile { - None => box io::stdout() as Box, + None => box old_io::stdout() as Box, Some(p) => { - let r = io::File::create(&p); + let r = old_io::File::create(&p); match r { Ok(w) => box w as Box, Err(e) => panic!("print-print failed to open {} due to {}", @@ -643,11 +643,11 @@ pub fn pretty_print_input(sess: Session, }.unwrap() } -fn print_flowgraph(variants: Vec, +fn print_flowgraph(variants: Vec, analysis: ty::CrateAnalysis, code: blocks::Code, mode: PpFlowGraphMode, - mut out: W) -> io::IoResult<()> { + mut out: W) -> old_io::IoResult<()> { let ty_cx = &analysis.ty_cx; let cfg = match code { blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block), @@ -687,11 +687,11 @@ fn print_flowgraph(variants: Vec, } } - fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> { + fn expand_err_details(r: old_io::IoResult<()>) -> old_io::IoResult<()> { r.map_err(|ioerr| { let orig_detail = ioerr.detail.clone(); let m = "graphviz::render failed"; - io::IoError { + old_io::IoError { detail: Some(match orig_detail { None => m.to_string(), Some(d) => format!("{}: {}", m, d) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index eedfc940751..25fe1942579 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -27,9 +27,9 @@ use util::common::time; use util::ppaux; use util::sha2::{Digest, Sha256}; -use std::io::fs::PathExtensions; -use std::io::{fs, TempDir, Command}; -use std::io; +use std::old_io::fs::PathExtensions; +use std::old_io::{fs, TempDir, Command}; +use std::old_io; use std::mem; use std::str; use std::string::String; @@ -425,7 +425,7 @@ pub fn invalid_output_for_target(sess: &Session, fn is_writeable(p: &Path) -> bool { match p.stat() { Err(..) => true, - Ok(m) => m.perm & io::USER_WRITE == io::USER_WRITE + Ok(m) => m.perm & old_io::USER_WRITE == old_io::USER_WRITE } } @@ -671,7 +671,7 @@ fn link_rlib<'a>(sess: &'a Session, fn write_rlib_bytecode_object_v1(writer: &mut T, bc_data_deflated: &[u8]) - -> ::std::io::IoResult<()> { + -> ::std::old_io::IoResult<()> { let bc_data_deflated_size: u64 = bc_data_deflated.len() as u64; try! { writer.write(RLIB_BYTECODE_OBJECT_MAGIC) }; @@ -1201,7 +1201,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, // Fix up permissions of the copy, as fs::copy() preserves // permissions, but the original file may have been installed // by a package manager and may be read-only. - match fs::chmod(&dst, io::USER_READ | io::USER_WRITE) { + match fs::chmod(&dst, old_io::USER_READ | old_io::USER_WRITE) { Ok(..) => {} Err(e) => { sess.err(&format!("failed to chmod {} when preparing \ diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 5e48ce384be..370ea0c7b14 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -23,8 +23,8 @@ use syntax::diagnostic; use syntax::diagnostic::{Emitter, Handler, Level, mk_handler}; use std::ffi::{self, CString}; -use std::io::Command; -use std::io::fs; +use std::old_io::Command; +use std::old_io::fs; use std::iter::Unfold; use std::ptr; use std::str; @@ -728,9 +728,9 @@ pub fn run_passes(sess: &Session, println!("{:?}", &cmd); } - cmd.stdin(::std::io::process::Ignored) - .stdout(::std::io::process::InheritFd(1)) - .stderr(::std::io::process::InheritFd(2)); + cmd.stdin(::std::old_io::process::Ignored) + .stdout(::std::old_io::process::InheritFd(1)) + .stderr(::std::old_io::process::InheritFd(2)); match cmd.status() { Ok(status) => { if !status.success() { diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 71ca6a4db03..7e7176d661c 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -33,7 +33,7 @@ use middle::def; use middle::ty::{self, Ty}; use std::cell::Cell; -use std::io::{self, File, fs}; +use std::old_io::{self, File, fs}; use std::os; use syntax::ast_util::{self, PostExpansionMethod}; @@ -1532,7 +1532,7 @@ pub fn process_crate(sess: &Session, }, }; - match fs::mkdir_recursive(&root_path, io::USER_RWX) { + match fs::mkdir_recursive(&root_path, old_io::USER_RWX) { Err(e) => sess.err(&format!("Could not create directory {}: {}", root_path.display(), e)[]), _ => (), diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 157d2580ad9..79ca24a18d4 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::{io, str}; +use std::{old_io, str}; #[derive(Clone)] pub struct ExternalHtml{ @@ -33,8 +33,8 @@ impl ExternalHtml { } } -pub fn load_string(input: &Path) -> io::IoResult> { - let mut f = try!(io::File::open(input)); +pub fn load_string(input: &Path) -> old_io::IoResult> { + let mut f = try!(old_io::File::open(input)); let d = try!(f.read_to_end()); Ok(str::from_utf8(d.as_slice()).map(|s| s.to_string()).ok()) } @@ -45,12 +45,12 @@ macro_rules! load_or_return { let input = Path::new($input); match ::externalfiles::load_string(&input) { Err(e) => { - let _ = writeln!(&mut io::stderr(), + let _ = writeln!(&mut old_io::stderr(), "error reading `{}`: {}", input.display(), e); return $cant_read; } Ok(None) => { - let _ = writeln!(&mut io::stderr(), + let _ = writeln!(&mut old_io::stderr(), "error reading `{}`: not UTF-8", input.display()); return $not_utf8; } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 885017152de..38b191846f1 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -15,7 +15,7 @@ use html::escape::Escape; -use std::io; +use std::old_io; use syntax::parse::lexer; use syntax::parse::token; use syntax::parse; @@ -46,7 +46,7 @@ pub fn highlight(src: &str, class: Option<&str>, id: Option<&str>) -> String { /// source. fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader, class: Option<&str>, id: Option<&str>, - out: &mut Writer) -> io::IoResult<()> { + out: &mut Writer) -> old_io::IoResult<()> { use syntax::parse::lexer::Reader; try!(write!(out, "

 {
 }
 
 pub fn render(
-    dst: &mut io::Writer, layout: &Layout, page: &Page, sidebar: &S, t: &T)
-    -> io::IoResult<()>
+    dst: &mut old_io::Writer, layout: &Layout, page: &Page, sidebar: &S, t: &T)
+    -> old_io::IoResult<()>
 {
     write!(dst,
 r##"
@@ -159,7 +159,7 @@ r##"
     )
 }
 
-pub fn redirect(dst: &mut io::Writer, url: &str) -> io::IoResult<()> {
+pub fn redirect(dst: &mut old_io::Writer, url: &str) -> old_io::IoResult<()> {
     // 

(path: P, timeout: Duration) - -> IoResult - where P: BytesContainer { - if timeout <= Duration::milliseconds(0) { - return Err(standard_error(TimedOut)); - } - - let path = CString::from_slice(path.container_as_bytes()); - UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64)) - .map(|inner| UnixStream { inner: inner }) - } - - - /// Closes the reading half of this connection. - /// - /// This method will close the reading portion of this connection, causing - /// all pending and future reads to immediately return with an error. - /// - /// Note that this method affects all cloned handles associated with this - /// stream, not just this one handle. - pub fn close_read(&mut self) -> IoResult<()> { - self.inner.close_read() - } - - /// Closes the writing half of this connection. - /// - /// This method will close the writing portion of this connection, causing - /// all pending and future writes to immediately return with an error. - /// - /// Note that this method affects all cloned handles associated with this - /// stream, not just this one handle. - pub fn close_write(&mut self) -> IoResult<()> { - self.inner.close_write() - } - - /// Sets the read/write timeout for this socket. - /// - /// For more information, see `TcpStream::set_timeout` - #[unstable = "the timeout argument may change in type and value"] - pub fn set_timeout(&mut self, timeout_ms: Option) { - self.inner.set_timeout(timeout_ms) - } - - /// Sets the read timeout for this socket. - /// - /// For more information, see `TcpStream::set_timeout` - #[unstable = "the timeout argument may change in type and value"] - pub fn set_read_timeout(&mut self, timeout_ms: Option) { - self.inner.set_read_timeout(timeout_ms) - } - - /// Sets the write timeout for this socket. - /// - /// For more information, see `TcpStream::set_timeout` - #[unstable = "the timeout argument may change in type and value"] - pub fn set_write_timeout(&mut self, timeout_ms: Option) { - self.inner.set_write_timeout(timeout_ms) - } -} - -impl Clone for UnixStream { - fn clone(&self) -> UnixStream { - UnixStream { inner: self.inner.clone() } - } -} - -impl Reader for UnixStream { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner.read(buf) - } -} - -impl Writer for UnixStream { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner.write(buf) - } -} - -impl sys_common::AsInner for UnixStream { - fn as_inner(&self) -> &UnixStreamImp { - &self.inner - } -} - -/// A value that can listen for incoming named pipe connection requests. -pub struct UnixListener { - /// The internal, opaque runtime Unix listener. - inner: UnixListenerImp, -} - -impl UnixListener { - /// Creates a new listener, ready to receive incoming connections on the - /// specified socket. The server will be named by `path`. - /// - /// This listener will be closed when it falls out of scope. - /// - /// # Example - /// - /// ``` - /// # fn foo() { - /// use std::io::net::pipe::UnixListener; - /// use std::io::{Listener, Acceptor}; - /// - /// let server = Path::new("/path/to/my/socket"); - /// let stream = UnixListener::bind(&server); - /// for mut client in stream.listen().incoming() { - /// client.write(&[1, 2, 3, 4]); - /// } - /// # } - /// ``` - pub fn bind(path: P) -> IoResult { - let path = CString::from_slice(path.container_as_bytes()); - UnixListenerImp::bind(&path) - .map(|inner| UnixListener { inner: inner }) - } -} - -impl Listener for UnixListener { - fn listen(self) -> IoResult { - self.inner.listen() - .map(|inner| UnixAcceptor { inner: inner }) - } -} - -impl sys_common::AsInner for UnixListener { - fn as_inner(&self) -> &UnixListenerImp { - &self.inner - } -} - -/// A value that can accept named pipe connections, returned from `listen()`. -pub struct UnixAcceptor { - /// The internal, opaque runtime Unix acceptor. - inner: UnixAcceptorImp -} - -impl UnixAcceptor { - /// Sets a timeout for this acceptor, after which accept() will no longer - /// block indefinitely. - /// - /// The argument specified is the amount of time, in milliseconds, into the - /// future after which all invocations of accept() will not block (and any - /// pending invocation will return). A value of `None` will clear any - /// existing timeout. - /// - /// When using this method, it is likely necessary to reset the timeout as - /// appropriate, the timeout specified is specific to this object, not - /// specific to the next request. - #[unstable = "the name and arguments to this function are likely \ - to change"] - pub fn set_timeout(&mut self, timeout_ms: Option) { - self.inner.set_timeout(timeout_ms) - } - - /// Closes the accepting capabilities of this acceptor. - /// - /// This function has the same semantics as `TcpAcceptor::close_accept`, and - /// more information can be found in that documentation. - #[unstable] - pub fn close_accept(&mut self) -> IoResult<()> { - self.inner.close_accept() - } -} - -impl Acceptor for UnixAcceptor { - fn accept(&mut self) -> IoResult { - self.inner.accept().map(|s| { - UnixStream { inner: s } - }) - } -} - -impl Clone for UnixAcceptor { - /// Creates a new handle to this unix acceptor, allowing for simultaneous - /// accepts. - /// - /// The underlying unix acceptor will not be closed until all handles to the - /// acceptor have been deallocated. Incoming connections will be received on - /// at most once acceptor, the same connection will not be accepted twice. - /// - /// The `close_accept` method will shut down *all* acceptors cloned from the - /// same original acceptor, whereas the `set_timeout` method only affects - /// the selector that it is called on. - /// - /// This function is useful for creating a handle to invoke `close_accept` - /// on to wake up any other task blocked in `accept`. - fn clone(&self) -> UnixAcceptor { - UnixAcceptor { inner: self.inner.clone() } - } -} - -impl sys_common::AsInner for UnixAcceptor { - fn as_inner(&self) -> &UnixAcceptorImp { - &self.inner - } -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - - use io::fs::PathExtensions; - use io::{EndOfFile, TimedOut, ShortWrite, IoError, ConnectionReset}; - use io::{NotConnected, BrokenPipe, FileNotFound, InvalidInput, OtherIoError}; - use io::{PermissionDenied, Acceptor, Listener}; - use io::test::*; - use super::*; - use sync::mpsc::channel; - use thread::Thread; - use time::Duration; - - pub fn smalltest(server: F, client: G) - where F : FnOnce(UnixStream), F : Send, - G : FnOnce(UnixStream), G : Send - { - let path1 = next_test_unix(); - let path2 = path1.clone(); - - let mut acceptor = UnixListener::bind(&path1).listen(); - - let _t = Thread::spawn(move|| { - match UnixStream::connect(&path2) { - Ok(c) => client(c), - Err(e) => panic!("failed connect: {}", e), - } - }); - - match acceptor.accept() { - Ok(c) => server(c), - Err(e) => panic!("failed accept: {}", e), - } - } - - #[test] - fn bind_error() { - let path = "path/to/nowhere"; - match UnixListener::bind(&path) { - Ok(..) => panic!(), - Err(e) => { - assert!(e.kind == PermissionDenied || e.kind == FileNotFound || - e.kind == InvalidInput); - } - } - } - - #[test] - fn connect_error() { - let path = if cfg!(windows) { - r"\\.\pipe\this_should_not_exist_ever" - } else { - "path/to/nowhere" - }; - match UnixStream::connect(&path) { - Ok(..) => panic!(), - Err(e) => { - assert!(e.kind == FileNotFound || e.kind == OtherIoError); - } - } - } - - #[test] - fn smoke() { - smalltest(move |mut server| { - let mut buf = [0]; - server.read(&mut buf).unwrap(); - assert!(buf[0] == 99); - }, move|mut client| { - client.write(&[99]).unwrap(); - }) - } - - #[cfg_attr(windows, ignore)] // FIXME(#12516) - #[test] - fn read_eof() { - smalltest(move|mut server| { - let mut buf = [0]; - assert!(server.read(&mut buf).is_err()); - assert!(server.read(&mut buf).is_err()); - }, move|_client| { - // drop the client - }) - } - - #[test] - fn write_begone() { - smalltest(move|mut server| { - let buf = [0]; - loop { - match server.write(&buf) { - Ok(..) => {} - Err(e) => { - assert!(e.kind == BrokenPipe || - e.kind == NotConnected || - e.kind == ConnectionReset, - "unknown error {}", e); - break; - } - } - } - }, move|_client| { - // drop the client - }) - } - - #[test] - fn accept_lots() { - let times = 10; - let path1 = next_test_unix(); - let path2 = path1.clone(); - - let mut acceptor = match UnixListener::bind(&path1).listen() { - Ok(a) => a, - Err(e) => panic!("failed listen: {}", e), - }; - - let _t = Thread::spawn(move|| { - for _ in range(0u, times) { - let mut stream = UnixStream::connect(&path2); - match stream.write(&[100]) { - Ok(..) => {} - Err(e) => panic!("failed write: {}", e) - } - } - }); - - for _ in range(0, times) { - let mut client = acceptor.accept(); - let mut buf = [0]; - match client.read(&mut buf) { - Ok(..) => {} - Err(e) => panic!("failed read/accept: {}", e), - } - assert_eq!(buf[0], 100); - } - } - - #[cfg(unix)] - #[test] - fn path_exists() { - let path = next_test_unix(); - let _acceptor = UnixListener::bind(&path).listen(); - assert!(path.exists()); - } - - #[test] - fn unix_clone_smoke() { - let addr = next_test_unix(); - let mut acceptor = UnixListener::bind(&addr).listen(); - - let _t = Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr); - let mut buf = [0, 0]; - debug!("client reading"); - assert_eq!(s.read(&mut buf), Ok(1)); - assert_eq!(buf[0], 1); - debug!("client writing"); - s.write(&[2]).unwrap(); - debug!("client dropping"); - }); - - let mut s1 = acceptor.accept().unwrap(); - let s2 = s1.clone(); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - rx1.recv().unwrap(); - debug!("writer writing"); - s2.write(&[1]).unwrap(); - debug!("writer done"); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - debug!("reader reading"); - assert_eq!(s1.read(&mut buf), Ok(1)); - debug!("reader done"); - rx2.recv().unwrap(); - } - - #[test] - fn unix_clone_two_read() { - let addr = next_test_unix(); - let mut acceptor = UnixListener::bind(&addr).listen(); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr); - s.write(&[1]).unwrap(); - rx.recv().unwrap(); - s.write(&[2]).unwrap(); - rx.recv().unwrap(); - }); - - let mut s1 = acceptor.accept().unwrap(); - let s2 = s1.clone(); - - let (done, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - let mut buf = [0, 0]; - s2.read(&mut buf).unwrap(); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - s1.read(&mut buf).unwrap(); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - } - - #[test] - fn unix_clone_two_write() { - let addr = next_test_unix(); - let mut acceptor = UnixListener::bind(&addr).listen(); - - let _t = Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr); - let buf = &mut [0, 1]; - s.read(buf).unwrap(); - s.read(buf).unwrap(); - }); - - let mut s1 = acceptor.accept().unwrap(); - let s2 = s1.clone(); - - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - s2.write(&[1]).unwrap(); - tx.send(()).unwrap(); - }); - s1.write(&[2]).unwrap(); - - rx.recv().unwrap(); - } - - #[cfg(not(windows))] - #[test] - fn drop_removes_listener_path() { - let path = next_test_unix(); - let l = UnixListener::bind(&path).unwrap(); - assert!(path.exists()); - drop(l); - assert!(!path.exists()); - } - - #[cfg(not(windows))] - #[test] - fn drop_removes_acceptor_path() { - let path = next_test_unix(); - let l = UnixListener::bind(&path).unwrap(); - assert!(path.exists()); - drop(l.listen().unwrap()); - assert!(!path.exists()); - } - - #[test] - fn accept_timeout() { - let addr = next_test_unix(); - let mut a = UnixListener::bind(&addr).unwrap().listen().unwrap(); - - a.set_timeout(Some(10)); - - // Make sure we time out once and future invocations also time out - let err = a.accept().err().unwrap(); - assert_eq!(err.kind, TimedOut); - let err = a.accept().err().unwrap(); - assert_eq!(err.kind, TimedOut); - - // Also make sure that even though the timeout is expired that we will - // continue to receive any pending connections. - let (tx, rx) = channel(); - let addr2 = addr.clone(); - let _t = Thread::spawn(move|| { - tx.send(UnixStream::connect(&addr2).unwrap()).unwrap(); - }); - let l = rx.recv().unwrap(); - for i in range(0u, 1001) { - match a.accept() { - Ok(..) => break, - Err(ref e) if e.kind == TimedOut => {} - Err(e) => panic!("error: {}", e), - } - ::thread::Thread::yield_now(); - if i == 1000 { panic!("should have a pending connection") } - } - drop(l); - - // Unset the timeout and make sure that this always blocks. - a.set_timeout(None); - let addr2 = addr.clone(); - let _t = Thread::spawn(move|| { - drop(UnixStream::connect(&addr2).unwrap()); - }); - a.accept().unwrap(); - } - - #[test] - fn connect_timeout_error() { - let addr = next_test_unix(); - assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_err()); - } - - #[test] - fn connect_timeout_success() { - let addr = next_test_unix(); - let _a = UnixListener::bind(&addr).unwrap().listen().unwrap(); - assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_ok()); - } - - #[test] - fn connect_timeout_zero() { - let addr = next_test_unix(); - let _a = UnixListener::bind(&addr).unwrap().listen().unwrap(); - assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(0)).is_err()); - } - - #[test] - fn connect_timeout_negative() { - let addr = next_test_unix(); - let _a = UnixListener::bind(&addr).unwrap().listen().unwrap(); - assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(-1)).is_err()); - } - - #[test] - fn close_readwrite_smoke() { - let addr = next_test_unix(); - let a = UnixListener::bind(&addr).listen().unwrap(); - let (_tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut a = a; - let _s = a.accept().unwrap(); - let _ = rx.recv(); - }); - - let mut b = [0]; - let mut s = UnixStream::connect(&addr).unwrap(); - let mut s2 = s.clone(); - - // closing should prevent reads/writes - s.close_write().unwrap(); - assert!(s.write(&[0]).is_err()); - s.close_read().unwrap(); - assert!(s.read(&mut b).is_err()); - - // closing should affect previous handles - assert!(s2.write(&[0]).is_err()); - assert!(s2.read(&mut b).is_err()); - - // closing should affect new handles - let mut s3 = s.clone(); - assert!(s3.write(&[0]).is_err()); - assert!(s3.read(&mut b).is_err()); - - // make sure these don't die - let _ = s2.close_read(); - let _ = s2.close_write(); - let _ = s3.close_read(); - let _ = s3.close_write(); - } - - #[test] - fn close_read_wakes_up() { - let addr = next_test_unix(); - let a = UnixListener::bind(&addr).listen().unwrap(); - let (_tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut a = a; - let _s = a.accept().unwrap(); - let _ = rx.recv(); - }); - - let mut s = UnixStream::connect(&addr).unwrap(); - let s2 = s.clone(); - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - assert!(s2.read(&mut [0]).is_err()); - tx.send(()).unwrap(); - }); - // this should wake up the child task - s.close_read().unwrap(); - - // this test will never finish if the child doesn't wake up - rx.recv().unwrap(); - } - - #[test] - fn readwrite_timeouts() { - let addr = next_test_unix(); - let mut a = UnixListener::bind(&addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr).unwrap(); - rx.recv().unwrap(); - assert!(s.write(&[0]).is_ok()); - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - s.set_timeout(Some(20)); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - - s.set_timeout(Some(20)); - for i in range(0u, 1001) { - match s.write(&[0; 128 * 1024]) { - Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, - Err(IoError { kind: TimedOut, .. }) => break, - Err(e) => panic!("{}", e), - } - if i == 1000 { panic!("should have filled up?!"); } - } - - // I'm not sure as to why, but apparently the write on windows always - // succeeds after the previous timeout. Who knows? - if !cfg!(windows) { - assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); - } - - tx.send(()).unwrap(); - s.set_timeout(None); - assert_eq!(s.read(&mut [0, 0]), Ok(1)); - } - - #[test] - fn read_timeouts() { - let addr = next_test_unix(); - let mut a = UnixListener::bind(&addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr).unwrap(); - rx.recv().unwrap(); - let mut amt = 0; - while amt < 100 * 128 * 1024 { - match s.read(&mut [0;128 * 1024]) { - Ok(n) => { amt += n; } - Err(e) => panic!("{}", e), - } - } - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - s.set_read_timeout(Some(20)); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - - tx.send(()).unwrap(); - for _ in range(0u, 100) { - assert!(s.write(&[0;128 * 1024]).is_ok()); - } - } - - #[test] - fn write_timeouts() { - let addr = next_test_unix(); - let mut a = UnixListener::bind(&addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr).unwrap(); - rx.recv().unwrap(); - assert!(s.write(&[0]).is_ok()); - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - s.set_write_timeout(Some(20)); - for i in range(0u, 1001) { - match s.write(&[0; 128 * 1024]) { - Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, - Err(IoError { kind: TimedOut, .. }) => break, - Err(e) => panic!("{}", e), - } - if i == 1000 { panic!("should have filled up?!"); } - } - - tx.send(()).unwrap(); - assert!(s.read(&mut [0]).is_ok()); - } - - #[test] - fn timeout_concurrent_read() { - let addr = next_test_unix(); - let mut a = UnixListener::bind(&addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = UnixStream::connect(&addr).unwrap(); - rx.recv().unwrap(); - assert!(s.write(&[0]).is_ok()); - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - let s2 = s.clone(); - let (tx2, rx2) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - assert!(s2.read(&mut [0]).is_ok()); - tx2.send(()).unwrap(); - }); - - s.set_read_timeout(Some(20)); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - tx.send(()).unwrap(); - - rx2.recv().unwrap(); - } - - #[cfg(not(windows))] - #[test] - fn clone_accept_smoke() { - let addr = next_test_unix(); - let l = UnixListener::bind(&addr); - let mut a = l.listen().unwrap(); - let mut a2 = a.clone(); - - let addr2 = addr.clone(); - let _t = Thread::spawn(move|| { - let _ = UnixStream::connect(&addr2); - }); - let _t = Thread::spawn(move|| { - let _ = UnixStream::connect(&addr); - }); - - assert!(a.accept().is_ok()); - drop(a); - assert!(a2.accept().is_ok()); - } - - #[cfg(not(windows))] // FIXME #17553 - #[test] - fn clone_accept_concurrent() { - let addr = next_test_unix(); - let l = UnixListener::bind(&addr); - let a = l.listen().unwrap(); - let a2 = a.clone(); - - let (tx, rx) = channel(); - let tx2 = tx.clone(); - - let _t = Thread::spawn(move|| { - let mut a = a; - tx.send(a.accept()).unwrap() - }); - let _t = Thread::spawn(move|| { - let mut a = a2; - tx2.send(a.accept()).unwrap() - }); - - let addr2 = addr.clone(); - let _t = Thread::spawn(move|| { - let _ = UnixStream::connect(&addr2); - }); - let _t = Thread::spawn(move|| { - let _ = UnixStream::connect(&addr); - }); - - assert!(rx.recv().unwrap().is_ok()); - assert!(rx.recv().unwrap().is_ok()); - } - - #[test] - fn close_accept_smoke() { - let addr = next_test_unix(); - let l = UnixListener::bind(&addr); - let mut a = l.listen().unwrap(); - - a.close_accept().unwrap(); - assert_eq!(a.accept().err().unwrap().kind, EndOfFile); - } - - #[test] - fn close_accept_concurrent() { - let addr = next_test_unix(); - let l = UnixListener::bind(&addr); - let a = l.listen().unwrap(); - let mut a2 = a.clone(); - - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut a = a; - tx.send(a.accept()).unwrap(); - }); - a2.close_accept().unwrap(); - - assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile); - } -} diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs deleted file mode 100644 index 4978085fa4f..00000000000 --- a/src/libstd/io/net/tcp.rs +++ /dev/null @@ -1,1475 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! TCP network connections -//! -//! This module contains the ability to open a TCP stream to a socket address, -//! as well as creating a socket server to accept incoming connections. The -//! destination and binding addresses can either be an IPv4 or IPv6 address. -//! -//! A TCP connection implements the `Reader` and `Writer` traits, while the TCP -//! listener (socket server) implements the `Listener` and `Acceptor` traits. - -use clone::Clone; -use io::IoResult; -use result::Result::Err; -use io::net::ip::{SocketAddr, ToSocketAddr}; -use io::{Reader, Writer, Listener, Acceptor}; -use io::{standard_error, TimedOut}; -use option::Option; -use option::Option::{None, Some}; -use time::Duration; - -use sys::tcp::TcpStream as TcpStreamImp; -use sys::tcp::TcpListener as TcpListenerImp; -use sys::tcp::TcpAcceptor as TcpAcceptorImp; - -use sys_common; - -/// A structure which represents a TCP stream between a local socket and a -/// remote socket. -/// -/// The socket will be closed when the value is dropped. -/// -/// # Example -/// -/// ```no_run -/// use std::io::TcpStream; -/// -/// { -/// let mut stream = TcpStream::connect("127.0.0.1:34254"); -/// -/// // ignore the Result -/// let _ = stream.write(&[1]); -/// -/// let mut buf = [0]; -/// let _ = stream.read(&mut buf); // ignore here too -/// } // the stream is closed here -/// ``` -pub struct TcpStream { - inner: TcpStreamImp, -} - -impl TcpStream { - fn new(s: TcpStreamImp) -> TcpStream { - TcpStream { inner: s } - } - - /// Open a TCP connection to a remote host. - /// - /// `addr` is an address of the remote host. Anything which implements `ToSocketAddr` - /// trait can be supplied for the address; see this trait documentation for - /// concrete examples. - pub fn connect(addr: A) -> IoResult { - super::with_addresses(addr, |addr| { - TcpStreamImp::connect(addr, None).map(TcpStream::new) - }) - } - - /// Creates a TCP connection to a remote socket address, timing out after - /// the specified duration. - /// - /// This is the same as the `connect` method, except that if the timeout - /// specified elapses before a connection is made an error will be - /// returned. The error's kind will be `TimedOut`. - /// - /// Same as the `connect` method, `addr` argument type can be anything which - /// implements `ToSocketAddr` trait. - /// - /// If a `timeout` with zero or negative duration is specified then - /// the function returns `Err`, with the error kind set to `TimedOut`. - #[unstable = "the timeout argument may eventually change types"] - pub fn connect_timeout(addr: A, - timeout: Duration) -> IoResult { - if timeout <= Duration::milliseconds(0) { - return Err(standard_error(TimedOut)); - } - - super::with_addresses(addr, |addr| { - TcpStreamImp::connect(addr, Some(timeout.num_milliseconds() as u64)) - .map(TcpStream::new) - }) - } - - /// Returns the socket address of the remote peer of this TCP connection. - pub fn peer_name(&mut self) -> IoResult { - self.inner.peer_name() - } - - /// Returns the socket address of the local half of this TCP connection. - pub fn socket_name(&mut self) -> IoResult { - self.inner.socket_name() - } - - /// Sets the nodelay flag on this connection to the boolean specified - #[unstable] - pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> { - self.inner.set_nodelay(nodelay) - } - - /// Sets the keepalive timeout to the timeout specified. - /// - /// If the value specified is `None`, then the keepalive flag is cleared on - /// this connection. Otherwise, the keepalive timeout will be set to the - /// specified time, in seconds. - #[unstable] - pub fn set_keepalive(&mut self, delay_in_seconds: Option) -> IoResult<()> { - self.inner.set_keepalive(delay_in_seconds) - } - - /// Closes the reading half of this connection. - /// - /// This method will close the reading portion of this connection, causing - /// all pending and future reads to immediately return with an error. - /// - /// # Example - /// - /// ```no_run - /// # #![allow(unused_must_use)] - /// use std::io::timer; - /// use std::io::TcpStream; - /// use std::time::Duration; - /// use std::thread::Thread; - /// - /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); - /// let stream2 = stream.clone(); - /// - /// let _t = Thread::spawn(move|| { - /// // close this stream after one second - /// timer::sleep(Duration::seconds(1)); - /// let mut stream = stream2; - /// stream.close_read(); - /// }); - /// - /// // wait for some data, will get canceled after one second - /// let mut buf = [0]; - /// stream.read(&mut buf); - /// ``` - /// - /// Note that this method affects all cloned handles associated with this - /// stream, not just this one handle. - pub fn close_read(&mut self) -> IoResult<()> { - self.inner.close_read() - } - - /// Closes the writing half of this connection. - /// - /// This method will close the writing portion of this connection, causing - /// all future writes to immediately return with an error. - /// - /// Note that this method affects all cloned handles associated with this - /// stream, not just this one handle. - pub fn close_write(&mut self) -> IoResult<()> { - self.inner.close_write() - } - - /// Sets a timeout, in milliseconds, for blocking operations on this stream. - /// - /// This function will set a timeout for all blocking operations (including - /// reads and writes) on this stream. The timeout specified is a relative - /// time, in milliseconds, into the future after which point operations will - /// time out. This means that the timeout must be reset periodically to keep - /// it from expiring. Specifying a value of `None` will clear the timeout - /// for this stream. - /// - /// The timeout on this stream is local to this stream only. Setting a - /// timeout does not affect any other cloned instances of this stream, nor - /// does the timeout propagated to cloned handles of this stream. Setting - /// this timeout will override any specific read or write timeouts - /// previously set for this stream. - /// - /// For clarification on the semantics of interrupting a read and a write, - /// take a look at `set_read_timeout` and `set_write_timeout`. - #[unstable = "the timeout argument may change in type and value"] - pub fn set_timeout(&mut self, timeout_ms: Option) { - self.inner.set_timeout(timeout_ms) - } - - /// Sets the timeout for read operations on this stream. - /// - /// See documentation in `set_timeout` for the semantics of this read time. - /// This will overwrite any previous read timeout set through either this - /// function or `set_timeout`. - /// - /// # Errors - /// - /// When this timeout expires, if there is no pending read operation, no - /// action is taken. Otherwise, the read operation will be scheduled to - /// promptly return. If a timeout error is returned, then no data was read - /// during the timeout period. - #[unstable = "the timeout argument may change in type and value"] - pub fn set_read_timeout(&mut self, timeout_ms: Option) { - self.inner.set_read_timeout(timeout_ms) - } - - /// Sets the timeout for write operations on this stream. - /// - /// See documentation in `set_timeout` for the semantics of this write time. - /// This will overwrite any previous write timeout set through either this - /// function or `set_timeout`. - /// - /// # Errors - /// - /// When this timeout expires, if there is no pending write operation, no - /// action is taken. Otherwise, the pending write operation will be - /// scheduled to promptly return. The actual state of the underlying stream - /// is not specified. - /// - /// The write operation may return an error of type `ShortWrite` which - /// indicates that the object is known to have written an exact number of - /// bytes successfully during the timeout period, and the remaining bytes - /// were never written. - /// - /// If the write operation returns `TimedOut`, then it the timeout primitive - /// does not know how many bytes were written as part of the timeout - /// operation. It may be the case that bytes continue to be written in an - /// asynchronous fashion after the call to write returns. - #[unstable = "the timeout argument may change in type and value"] - pub fn set_write_timeout(&mut self, timeout_ms: Option) { - self.inner.set_write_timeout(timeout_ms) - } -} - -impl Clone for TcpStream { - /// Creates a new handle to this TCP stream, allowing for simultaneous reads - /// and writes of this connection. - /// - /// The underlying TCP stream will not be closed until all handles to the - /// stream have been deallocated. All handles will also follow the same - /// stream, but two concurrent reads will not receive the same data. - /// Instead, the first read will receive the first packet received, and the - /// second read will receive the second packet. - fn clone(&self) -> TcpStream { - TcpStream { inner: self.inner.clone() } - } -} - -impl Reader for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner.read(buf) - } -} - -impl Writer for TcpStream { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner.write(buf) - } -} - -impl sys_common::AsInner for TcpStream { - fn as_inner(&self) -> &TcpStreamImp { - &self.inner - } -} - -/// A structure representing a socket server. This listener is used to create a -/// `TcpAcceptor` which can be used to accept sockets on a local port. -/// -/// # Examples -/// -/// ``` -/// # fn foo() { -/// use std::io::{TcpListener, TcpStream}; -/// use std::io::{Acceptor, Listener}; -/// use std::thread::Thread; -/// -/// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); -/// -/// // bind the listener to the specified address -/// let mut acceptor = listener.listen().unwrap(); -/// -/// fn handle_client(mut stream: TcpStream) { -/// // ... -/// # &mut stream; // silence unused mutability/variable warning -/// } -/// // accept connections and process them, spawning a new tasks for each one -/// for stream in acceptor.incoming() { -/// match stream { -/// Err(e) => { /* connection failed */ } -/// Ok(stream) => { -/// Thread::spawn(move|| { -/// // connection succeeded -/// handle_client(stream) -/// }); -/// } -/// } -/// } -/// -/// // close the socket server -/// drop(acceptor); -/// # } -/// ``` -pub struct TcpListener { - inner: TcpListenerImp, -} - -impl TcpListener { - /// Creates a new `TcpListener` which will be bound to the specified address. - /// This listener is not ready for accepting connections, `listen` must be called - /// on it before that's possible. - /// - /// Binding with a port number of 0 will request that the OS assigns a port - /// to this listener. The port allocated can be queried via the - /// `socket_name` function. - /// - /// The address type can be any implementer of `ToSocketAddr` trait. See its - /// documentation for concrete examples. - pub fn bind(addr: A) -> IoResult { - super::with_addresses(addr, |addr| { - TcpListenerImp::bind(addr).map(|inner| TcpListener { inner: inner }) - }) - } - - /// Returns the local socket address of this listener. - pub fn socket_name(&mut self) -> IoResult { - self.inner.socket_name() - } -} - -impl Listener for TcpListener { - fn listen(self) -> IoResult { - self.inner.listen(128).map(|a| TcpAcceptor { inner: a }) - } -} - -impl sys_common::AsInner for TcpListener { - fn as_inner(&self) -> &TcpListenerImp { - &self.inner - } -} - -/// The accepting half of a TCP socket server. This structure is created through -/// a `TcpListener`'s `listen` method, and this object can be used to accept new -/// `TcpStream` instances. -pub struct TcpAcceptor { - inner: TcpAcceptorImp, -} - -impl TcpAcceptor { - /// Prevents blocking on all future accepts after `ms` milliseconds have - /// elapsed. - /// - /// This function is used to set a deadline after which this acceptor will - /// time out accepting any connections. The argument is the relative - /// distance, in milliseconds, to a point in the future after which all - /// accepts will fail. - /// - /// If the argument specified is `None`, then any previously registered - /// timeout is cleared. - /// - /// A timeout of `0` can be used to "poll" this acceptor to see if it has - /// any pending connections. All pending connections will be accepted, - /// regardless of whether the timeout has expired or not (the accept will - /// not block in this case). - /// - /// # Example - /// - /// ```no_run - /// # #![allow(unstable)] - /// use std::io::TcpListener; - /// use std::io::{Listener, Acceptor, TimedOut}; - /// - /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); - /// - /// // After 100ms have passed, all accepts will fail - /// a.set_timeout(Some(100)); - /// - /// match a.accept() { - /// Ok(..) => println!("accepted a socket"), - /// Err(ref e) if e.kind == TimedOut => { println!("timed out!"); } - /// Err(e) => println!("err: {}", e), - /// } - /// - /// // Reset the timeout and try again - /// a.set_timeout(Some(100)); - /// let socket = a.accept(); - /// - /// // Clear the timeout and block indefinitely waiting for a connection - /// a.set_timeout(None); - /// let socket = a.accept(); - /// ``` - #[unstable = "the type of the argument and name of this function are \ - subject to change"] - pub fn set_timeout(&mut self, ms: Option) { self.inner.set_timeout(ms); } - - /// Closes the accepting capabilities of this acceptor. - /// - /// This function is similar to `TcpStream`'s `close_{read,write}` methods - /// in that it will affect *all* cloned handles of this acceptor's original - /// handle. - /// - /// Once this function succeeds, all future calls to `accept` will return - /// immediately with an error, preventing all future calls to accept. The - /// underlying socket will not be relinquished back to the OS until all - /// acceptors have been deallocated. - /// - /// This is useful for waking up a thread in an accept loop to indicate that - /// it should exit. - /// - /// # Example - /// - /// ``` - /// # #![allow(unstable)] - /// use std::io::{TcpListener, Listener, Acceptor, EndOfFile}; - /// use std::thread::Thread; - /// - /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); - /// let a2 = a.clone(); - /// - /// let _t = Thread::spawn(move|| { - /// let mut a2 = a2; - /// for socket in a2.incoming() { - /// match socket { - /// Ok(s) => { /* handle s */ } - /// Err(ref e) if e.kind == EndOfFile => break, // closed - /// Err(e) => panic!("unexpected error: {}", e), - /// } - /// } - /// }); - /// - /// # fn wait_for_sigint() {} - /// // Now that our accept loop is running, wait for the program to be - /// // requested to exit. - /// wait_for_sigint(); - /// - /// // Signal our accept loop to exit - /// assert!(a.close_accept().is_ok()); - /// ``` - #[unstable] - pub fn close_accept(&mut self) -> IoResult<()> { - self.inner.close_accept() - } -} - -impl Acceptor for TcpAcceptor { - fn accept(&mut self) -> IoResult { - self.inner.accept().map(TcpStream::new) - } -} - -impl Clone for TcpAcceptor { - /// Creates a new handle to this TCP acceptor, allowing for simultaneous - /// accepts. - /// - /// The underlying TCP acceptor will not be closed until all handles to the - /// acceptor have been deallocated. Incoming connections will be received on - /// at most once acceptor, the same connection will not be accepted twice. - /// - /// The `close_accept` method will shut down *all* acceptors cloned from the - /// same original acceptor, whereas the `set_timeout` method only affects - /// the selector that it is called on. - /// - /// This function is useful for creating a handle to invoke `close_accept` - /// on to wake up any other task blocked in `accept`. - fn clone(&self) -> TcpAcceptor { - TcpAcceptor { inner: self.inner.clone() } - } -} - -impl sys_common::AsInner for TcpAcceptor { - fn as_inner(&self) -> &TcpAcceptorImp { - &self.inner - } -} - -#[cfg(test)] -#[allow(unstable)] -mod test { - use prelude::v1::*; - - use sync::mpsc::channel; - use thread::Thread; - use io::net::tcp::*; - use io::net::ip::*; - use io::test::*; - use io::{EndOfFile, TimedOut, ShortWrite, IoError}; - use io::{ConnectionRefused, BrokenPipe, ConnectionAborted}; - use io::{ConnectionReset, NotConnected, PermissionDenied, OtherIoError}; - use io::{Acceptor, Listener}; - - // FIXME #11530 this fails on android because tests are run as root - #[cfg_attr(any(windows, target_os = "android"), ignore)] - #[test] - fn bind_error() { - match TcpListener::bind("0.0.0.0:1") { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, PermissionDenied), - } - } - - #[test] - fn connect_error() { - match TcpStream::connect("0.0.0.0:1") { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, ConnectionRefused), - } - } - - #[test] - fn listen_ip4_localhost() { - let socket_addr = next_test_ip4(); - let listener = TcpListener::bind(socket_addr); - let mut acceptor = listener.listen(); - - let _t = Thread::spawn(move|| { - let mut stream = TcpStream::connect(("localhost", socket_addr.port)); - stream.write(&[144]).unwrap(); - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 144); - } - - #[test] - fn connect_localhost() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut stream = TcpStream::connect(("localhost", addr.port)); - stream.write(&[64]).unwrap(); - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 64); - } - - #[test] - fn connect_ip4_loopback() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut stream = TcpStream::connect(("127.0.0.1", addr.port)); - stream.write(&[44]).unwrap(); - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 44); - } - - #[test] - fn connect_ip6_loopback() { - let addr = next_test_ip6(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut stream = TcpStream::connect(("::1", addr.port)); - stream.write(&[66]).unwrap(); - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 66); - } - - #[test] - fn smoke_test_ip4() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut stream = TcpStream::connect(addr); - stream.write(&[99]).unwrap(); - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 99); - } - - #[test] - fn smoke_test_ip6() { - let addr = next_test_ip6(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut stream = TcpStream::connect(addr); - stream.write(&[99]).unwrap(); - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 99); - } - - #[test] - fn read_eof_ip4() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let _stream = TcpStream::connect(addr); - // Close - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(&mut buf); - assert!(nread.is_err()); - } - - #[test] - fn read_eof_ip6() { - let addr = next_test_ip6(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let _stream = TcpStream::connect(addr); - // Close - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(&mut buf); - assert!(nread.is_err()); - } - - #[test] - fn read_eof_twice_ip4() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let _stream = TcpStream::connect(addr); - // Close - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(&mut buf); - assert!(nread.is_err()); - - match stream.read(&mut buf) { - Ok(..) => panic!(), - Err(ref e) => { - assert!(e.kind == NotConnected || e.kind == EndOfFile, - "unknown kind: {:?}", e.kind); - } - } - } - - #[test] - fn read_eof_twice_ip6() { - let addr = next_test_ip6(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let _stream = TcpStream::connect(addr); - // Close - }); - - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(&mut buf); - assert!(nread.is_err()); - - match stream.read(&mut buf) { - Ok(..) => panic!(), - Err(ref e) => { - assert!(e.kind == NotConnected || e.kind == EndOfFile, - "unknown kind: {:?}", e.kind); - } - } - } - - #[test] - fn write_close_ip4() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - drop(TcpStream::connect(addr)); - tx.send(()).unwrap(); - }); - - let mut stream = acceptor.accept(); - rx.recv().unwrap(); - let buf = [0]; - match stream.write(&buf) { - Ok(..) => {} - Err(e) => { - assert!(e.kind == ConnectionReset || - e.kind == BrokenPipe || - e.kind == ConnectionAborted, - "unknown error: {}", e); - } - } - } - - #[test] - fn write_close_ip6() { - let addr = next_test_ip6(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - drop(TcpStream::connect(addr)); - tx.send(()).unwrap(); - }); - - let mut stream = acceptor.accept(); - rx.recv().unwrap(); - let buf = [0]; - match stream.write(&buf) { - Ok(..) => {} - Err(e) => { - assert!(e.kind == ConnectionReset || - e.kind == BrokenPipe || - e.kind == ConnectionAborted, - "unknown error: {}", e); - } - } - } - - #[test] - fn multiple_connect_serial_ip4() { - let addr = next_test_ip4(); - let max = 10u; - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - for _ in range(0, max) { - let mut stream = TcpStream::connect(addr); - stream.write(&[99]).unwrap(); - } - }); - - for ref mut stream in acceptor.incoming().take(max) { - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert_eq!(buf[0], 99); - } - } - - #[test] - fn multiple_connect_serial_ip6() { - let addr = next_test_ip6(); - let max = 10u; - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - for _ in range(0, max) { - let mut stream = TcpStream::connect(addr); - stream.write(&[99]).unwrap(); - } - }); - - for ref mut stream in acceptor.incoming().take(max) { - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert_eq!(buf[0], 99); - } - } - - #[test] - fn multiple_connect_interleaved_greedy_schedule_ip4() { - let addr = next_test_ip4(); - static MAX: int = 10; - let acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut acceptor = acceptor; - for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - // Start another task to handle the connection - let _t = Thread::spawn(move|| { - let mut stream = stream; - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == i as u8); - debug!("read"); - }); - } - }); - - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - let _t = Thread::spawn(move|| { - debug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - debug!("writing"); - stream.write(&[i as u8]).unwrap(); - }); - } - } - - #[test] - fn multiple_connect_interleaved_greedy_schedule_ip6() { - let addr = next_test_ip6(); - static MAX: int = 10; - let acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut acceptor = acceptor; - for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - // Start another task to handle the connection - let _t = Thread::spawn(move|| { - let mut stream = stream; - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == i as u8); - debug!("read"); - }); - } - }); - - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - let _t = Thread::spawn(move|| { - debug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - debug!("writing"); - stream.write(&[i as u8]).unwrap(); - }); - } - } - - #[test] - fn multiple_connect_interleaved_lazy_schedule_ip4() { - static MAX: int = 10; - let addr = next_test_ip4(); - let acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut acceptor = acceptor; - for stream in acceptor.incoming().take(MAX as uint) { - // Start another task to handle the connection - let _t = Thread::spawn(move|| { - let mut stream = stream; - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 99); - debug!("read"); - }); - } - }); - - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - let _t = Thread::spawn(move|| { - debug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - debug!("writing"); - stream.write(&[99]).unwrap(); - }); - } - } - - #[test] - fn multiple_connect_interleaved_lazy_schedule_ip6() { - static MAX: int = 10; - let addr = next_test_ip6(); - let acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut acceptor = acceptor; - for stream in acceptor.incoming().take(MAX as uint) { - // Start another task to handle the connection - let _t = Thread::spawn(move|| { - let mut stream = stream; - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - assert!(buf[0] == 99); - debug!("read"); - }); - } - }); - - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - let _t = Thread::spawn(move|| { - debug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - debug!("writing"); - stream.write(&[99]).unwrap(); - }); - } - } - - pub fn socket_name(addr: SocketAddr) { - let mut listener = TcpListener::bind(addr).unwrap(); - - // Make sure socket_name gives - // us the socket we binded to. - let so_name = listener.socket_name(); - assert!(so_name.is_ok()); - assert_eq!(addr, so_name.unwrap()); - } - - pub fn peer_name(addr: SocketAddr) { - let acceptor = TcpListener::bind(addr).listen(); - let _t = Thread::spawn(move|| { - let mut acceptor = acceptor; - acceptor.accept().unwrap(); - }); - - let stream = TcpStream::connect(addr); - - assert!(stream.is_ok()); - let mut stream = stream.unwrap(); - - // Make sure peer_name gives us the - // address/port of the peer we've - // connected to. - let peer_name = stream.peer_name(); - assert!(peer_name.is_ok()); - assert_eq!(addr, peer_name.unwrap()); - } - - #[test] - fn socket_and_peer_name_ip4() { - peer_name(next_test_ip4()); - socket_name(next_test_ip4()); - } - - #[test] - fn socket_and_peer_name_ip6() { - // FIXME: peer name is not consistent - //peer_name(next_test_ip6()); - socket_name(next_test_ip6()); - } - - #[test] - fn partial_read() { - let addr = next_test_ip4(); - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut srv = TcpListener::bind(addr).listen().unwrap(); - tx.send(()).unwrap(); - let mut cl = srv.accept().unwrap(); - cl.write(&[10]).unwrap(); - let mut b = [0]; - cl.read(&mut b).unwrap(); - tx.send(()).unwrap(); - }); - - rx.recv().unwrap(); - let mut c = TcpStream::connect(addr).unwrap(); - let mut b = [0; 10]; - assert_eq!(c.read(&mut b), Ok(1)); - c.write(&[1]).unwrap(); - rx.recv().unwrap(); - } - - #[test] - fn double_bind() { - let addr = next_test_ip4(); - let listener = TcpListener::bind(addr).unwrap().listen(); - assert!(listener.is_ok()); - match TcpListener::bind(addr).listen() { - Ok(..) => panic!(), - Err(e) => { - assert!(e.kind == ConnectionRefused || e.kind == OtherIoError, - "unknown error: {} {:?}", e, e.kind); - } - } - } - - #[test] - fn fast_rebind() { - let addr = next_test_ip4(); - let (tx, rx) = channel(); - - let _t = Thread::spawn(move|| { - rx.recv().unwrap(); - let _stream = TcpStream::connect(addr).unwrap(); - // Close - rx.recv().unwrap(); - }); - - { - let mut acceptor = TcpListener::bind(addr).listen(); - tx.send(()).unwrap(); - { - let _stream = acceptor.accept().unwrap(); - // Close client - tx.send(()).unwrap(); - } - // Close listener - } - let _listener = TcpListener::bind(addr); - } - - #[test] - fn tcp_clone_smoke() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut s = TcpStream::connect(addr); - let mut buf = [0, 0]; - assert_eq!(s.read(&mut buf), Ok(1)); - assert_eq!(buf[0], 1); - s.write(&[2]).unwrap(); - }); - - let mut s1 = acceptor.accept().unwrap(); - let s2 = s1.clone(); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - rx1.recv().unwrap(); - s2.write(&[1]).unwrap(); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - assert_eq!(s1.read(&mut buf), Ok(1)); - rx2.recv().unwrap(); - } - - #[test] - fn tcp_clone_two_read() { - let addr = next_test_ip6(); - let mut acceptor = TcpListener::bind(addr).listen(); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = Thread::spawn(move|| { - let mut s = TcpStream::connect(addr); - s.write(&[1]).unwrap(); - rx.recv().unwrap(); - s.write(&[2]).unwrap(); - rx.recv().unwrap(); - }); - - let mut s1 = acceptor.accept().unwrap(); - let s2 = s1.clone(); - - let (done, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - let mut buf = [0, 0]; - s2.read(&mut buf).unwrap(); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - s1.read(&mut buf).unwrap(); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - } - - #[test] - fn tcp_clone_two_write() { - let addr = next_test_ip4(); - let mut acceptor = TcpListener::bind(addr).listen(); - - let _t = Thread::spawn(move|| { - let mut s = TcpStream::connect(addr); - let mut buf = [0, 1]; - s.read(&mut buf).unwrap(); - s.read(&mut buf).unwrap(); - }); - - let mut s1 = acceptor.accept().unwrap(); - let s2 = s1.clone(); - - let (done, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - s2.write(&[1]).unwrap(); - done.send(()).unwrap(); - }); - s1.write(&[2]).unwrap(); - - rx.recv().unwrap(); - } - - #[test] - fn shutdown_smoke() { - let addr = next_test_ip4(); - let a = TcpListener::bind(addr).unwrap().listen(); - let _t = Thread::spawn(move|| { - let mut a = a; - let mut c = a.accept().unwrap(); - assert_eq!(c.read_to_end(), Ok(vec!())); - c.write(&[1]).unwrap(); - }); - - let mut s = TcpStream::connect(addr).unwrap(); - assert!(s.inner.close_write().is_ok()); - assert!(s.write(&[1]).is_err()); - assert_eq!(s.read_to_end(), Ok(vec!(1))); - } - - #[test] - fn accept_timeout() { - let addr = next_test_ip4(); - let mut a = TcpListener::bind(addr).unwrap().listen().unwrap(); - - a.set_timeout(Some(10)); - - // Make sure we time out once and future invocations also time out - let err = a.accept().err().unwrap(); - assert_eq!(err.kind, TimedOut); - let err = a.accept().err().unwrap(); - assert_eq!(err.kind, TimedOut); - - // Also make sure that even though the timeout is expired that we will - // continue to receive any pending connections. - // - // FIXME: freebsd apparently never sees the pending connection, but - // testing manually always works. Need to investigate this - // flakiness. - if !cfg!(target_os = "freebsd") { - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - tx.send(TcpStream::connect(addr).unwrap()).unwrap(); - }); - let _l = rx.recv().unwrap(); - for i in range(0i, 1001) { - match a.accept() { - Ok(..) => break, - Err(ref e) if e.kind == TimedOut => {} - Err(e) => panic!("error: {}", e), - } - ::thread::Thread::yield_now(); - if i == 1000 { panic!("should have a pending connection") } - } - } - - // Unset the timeout and make sure that this always blocks. - a.set_timeout(None); - let _t = Thread::spawn(move|| { - drop(TcpStream::connect(addr).unwrap()); - }); - a.accept().unwrap(); - } - - #[test] - fn close_readwrite_smoke() { - let addr = next_test_ip4(); - let a = TcpListener::bind(addr).listen().unwrap(); - let (_tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut a = a; - let _s = a.accept().unwrap(); - let _ = rx.recv().unwrap(); - }); - - let mut b = [0]; - let mut s = TcpStream::connect(addr).unwrap(); - let mut s2 = s.clone(); - - // closing should prevent reads/writes - s.close_write().unwrap(); - assert!(s.write(&[0]).is_err()); - s.close_read().unwrap(); - assert!(s.read(&mut b).is_err()); - - // closing should affect previous handles - assert!(s2.write(&[0]).is_err()); - assert!(s2.read(&mut b).is_err()); - - // closing should affect new handles - let mut s3 = s.clone(); - assert!(s3.write(&[0]).is_err()); - assert!(s3.read(&mut b).is_err()); - - // make sure these don't die - let _ = s2.close_read(); - let _ = s2.close_write(); - let _ = s3.close_read(); - let _ = s3.close_write(); - } - - #[test] - fn close_read_wakes_up() { - let addr = next_test_ip4(); - let a = TcpListener::bind(addr).listen().unwrap(); - let (_tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut a = a; - let _s = a.accept().unwrap(); - let _ = rx.recv().unwrap(); - }); - - let mut s = TcpStream::connect(addr).unwrap(); - let s2 = s.clone(); - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - assert!(s2.read(&mut [0]).is_err()); - tx.send(()).unwrap(); - }); - // this should wake up the child task - s.close_read().unwrap(); - - // this test will never finish if the child doesn't wake up - rx.recv().unwrap(); - } - - #[test] - fn readwrite_timeouts() { - let addr = next_test_ip6(); - let mut a = TcpListener::bind(addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = TcpStream::connect(addr).unwrap(); - rx.recv().unwrap(); - assert!(s.write(&[0]).is_ok()); - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - s.set_timeout(Some(20)); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - - s.set_timeout(Some(20)); - for i in range(0i, 1001) { - match s.write(&[0; 128 * 1024]) { - Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, - Err(IoError { kind: TimedOut, .. }) => break, - Err(e) => panic!("{}", e), - } - if i == 1000 { panic!("should have filled up?!"); } - } - assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); - - tx.send(()).unwrap(); - s.set_timeout(None); - assert_eq!(s.read(&mut [0, 0]), Ok(1)); - } - - #[test] - fn read_timeouts() { - let addr = next_test_ip6(); - let mut a = TcpListener::bind(addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = TcpStream::connect(addr).unwrap(); - rx.recv().unwrap(); - let mut amt = 0; - while amt < 100 * 128 * 1024 { - match s.read(&mut [0;128 * 1024]) { - Ok(n) => { amt += n; } - Err(e) => panic!("{}", e), - } - } - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - s.set_read_timeout(Some(20)); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - - tx.send(()).unwrap(); - for _ in range(0i, 100) { - assert!(s.write(&[0;128 * 1024]).is_ok()); - } - } - - #[test] - fn write_timeouts() { - let addr = next_test_ip6(); - let mut a = TcpListener::bind(addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = TcpStream::connect(addr).unwrap(); - rx.recv().unwrap(); - assert!(s.write(&[0]).is_ok()); - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - s.set_write_timeout(Some(20)); - for i in range(0i, 1001) { - match s.write(&[0; 128 * 1024]) { - Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, - Err(IoError { kind: TimedOut, .. }) => break, - Err(e) => panic!("{}", e), - } - if i == 1000 { panic!("should have filled up?!"); } - } - assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut); - - tx.send(()).unwrap(); - assert!(s.read(&mut [0]).is_ok()); - } - - #[test] - fn timeout_concurrent_read() { - let addr = next_test_ip6(); - let mut a = TcpListener::bind(addr).listen().unwrap(); - let (tx, rx) = channel::<()>(); - Thread::spawn(move|| { - let mut s = TcpStream::connect(addr).unwrap(); - rx.recv().unwrap(); - assert_eq!(s.write(&[0]), Ok(())); - let _ = rx.recv(); - }); - - let mut s = a.accept().unwrap(); - let s2 = s.clone(); - let (tx2, rx2) = channel(); - let _t = Thread::spawn(move|| { - let mut s2 = s2; - assert_eq!(s2.read(&mut [0]), Ok(1)); - tx2.send(()).unwrap(); - }); - - s.set_read_timeout(Some(20)); - assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut); - tx.send(()).unwrap(); - - rx2.recv().unwrap(); - } - - #[test] - fn clone_while_reading() { - let addr = next_test_ip6(); - let listen = TcpListener::bind(addr); - let mut accept = listen.listen().unwrap(); - - // Enqueue a task to write to a socket - let (tx, rx) = channel(); - let (txdone, rxdone) = channel(); - let txdone2 = txdone.clone(); - let _t = Thread::spawn(move|| { - let mut tcp = TcpStream::connect(addr).unwrap(); - rx.recv().unwrap(); - tcp.write_u8(0).unwrap(); - txdone2.send(()).unwrap(); - }); - - // Spawn off a reading clone - let tcp = accept.accept().unwrap(); - let tcp2 = tcp.clone(); - let txdone3 = txdone.clone(); - let _t = Thread::spawn(move|| { - let mut tcp2 = tcp2; - tcp2.read_u8().unwrap(); - txdone3.send(()).unwrap(); - }); - - // Try to ensure that the reading clone is indeed reading - for _ in range(0i, 50) { - ::thread::Thread::yield_now(); - } - - // clone the handle again while it's reading, then let it finish the - // read. - let _ = tcp.clone(); - tx.send(()).unwrap(); - rxdone.recv().unwrap(); - rxdone.recv().unwrap(); - } - - #[test] - fn clone_accept_smoke() { - let addr = next_test_ip4(); - let l = TcpListener::bind(addr); - let mut a = l.listen().unwrap(); - let mut a2 = a.clone(); - - let _t = Thread::spawn(move|| { - let _ = TcpStream::connect(addr); - }); - let _t = Thread::spawn(move|| { - let _ = TcpStream::connect(addr); - }); - - assert!(a.accept().is_ok()); - assert!(a2.accept().is_ok()); - } - - #[test] - fn clone_accept_concurrent() { - let addr = next_test_ip4(); - let l = TcpListener::bind(addr); - let a = l.listen().unwrap(); - let a2 = a.clone(); - - let (tx, rx) = channel(); - let tx2 = tx.clone(); - - let _t = Thread::spawn(move|| { - let mut a = a; - tx.send(a.accept()).unwrap(); - }); - let _t = Thread::spawn(move|| { - let mut a = a2; - tx2.send(a.accept()).unwrap(); - }); - - let _t = Thread::spawn(move|| { - let _ = TcpStream::connect(addr); - }); - let _t = Thread::spawn(move|| { - let _ = TcpStream::connect(addr); - }); - - assert!(rx.recv().unwrap().is_ok()); - assert!(rx.recv().unwrap().is_ok()); - } - - #[test] - fn close_accept_smoke() { - let addr = next_test_ip4(); - let l = TcpListener::bind(addr); - let mut a = l.listen().unwrap(); - - a.close_accept().unwrap(); - assert_eq!(a.accept().err().unwrap().kind, EndOfFile); - } - - #[test] - fn close_accept_concurrent() { - let addr = next_test_ip4(); - let l = TcpListener::bind(addr); - let a = l.listen().unwrap(); - let mut a2 = a.clone(); - - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut a = a; - tx.send(a.accept()).unwrap(); - }); - a2.close_accept().unwrap(); - - assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile); - } -} diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs deleted file mode 100644 index 8cdad3f528a..00000000000 --- a/src/libstd/io/net/udp.rs +++ /dev/null @@ -1,457 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! UDP (User Datagram Protocol) network connections. -//! -//! This module contains the ability to open a UDP stream to a socket address. -//! The destination and binding addresses can either be an IPv4 or IPv6 -//! address. There is no corresponding notion of a server because UDP is a -//! datagram protocol. - -use clone::Clone; -use io::net::ip::{SocketAddr, IpAddr, ToSocketAddr}; -use io::IoResult; -use option::Option; -use sys::udp::UdpSocket as UdpSocketImp; -use sys_common; - -/// A User Datagram Protocol socket. -/// -/// This is an implementation of a bound UDP socket. This supports both IPv4 and -/// IPv6 addresses, and there is no corresponding notion of a server because UDP -/// is a datagram protocol. -/// -/// # Example -/// -/// ```rust,no_run -/// # #![allow(unused_must_use)] -/// #![feature(slicing_syntax)] -/// -/// use std::io::net::udp::UdpSocket; -/// use std::io::net::ip::{Ipv4Addr, SocketAddr}; -/// fn main() { -/// let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 34254 }; -/// let mut socket = match UdpSocket::bind(addr) { -/// Ok(s) => s, -/// Err(e) => panic!("couldn't bind socket: {}", e), -/// }; -/// -/// let mut buf = [0; 10]; -/// match socket.recv_from(&mut buf) { -/// Ok((amt, src)) => { -/// // Send a reply to the socket we received data from -/// let buf = buf.slice_to_mut(amt); -/// buf.reverse(); -/// socket.send_to(buf, src); -/// } -/// Err(e) => println!("couldn't receive a datagram: {}", e) -/// } -/// drop(socket); // close the socket -/// } -/// ``` -pub struct UdpSocket { - inner: UdpSocketImp, -} - -impl UdpSocket { - /// Creates a UDP socket from the given address. - /// - /// Address type can be any implementor of `ToSocketAddr` trait. See its - /// documentation for concrete examples. - pub fn bind(addr: A) -> IoResult { - super::with_addresses(addr, |addr| { - UdpSocketImp::bind(addr).map(|s| UdpSocket { inner: s }) - }) - } - - /// Receives data from the socket. On success, returns the number of bytes - /// read and the address from whence the data came. - pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> { - self.inner.recv_from(buf) - } - - /// Sends data on the socket to the given address. Returns nothing on - /// success. - /// - /// Address type can be any implementer of `ToSocketAddr` trait. See its - /// documentation for concrete examples. - pub fn send_to(&mut self, buf: &[u8], addr: A) -> IoResult<()> { - super::with_addresses(addr, |addr| self.inner.send_to(buf, addr)) - } - - /// Returns the socket address that this socket was created from. - pub fn socket_name(&mut self) -> IoResult { - self.inner.socket_name() - } - - /// Joins a multicast IP address (becomes a member of it) - #[unstable] - pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> { - self.inner.join_multicast(multi) - } - - /// Leaves a multicast IP address (drops membership from it) - #[unstable] - pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> { - self.inner.leave_multicast(multi) - } - - /// Set the multicast loop flag to the specified value - /// - /// This lets multicast packets loop back to local sockets (if enabled) - #[unstable] - pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> { - self.inner.set_multicast_loop(on) - } - - /// Sets the multicast TTL - #[unstable] - pub fn set_multicast_ttl(&mut self, ttl: int) -> IoResult<()> { - self.inner.multicast_time_to_live(ttl) - } - - /// Sets this socket's TTL - #[unstable] - pub fn set_ttl(&mut self, ttl: int) -> IoResult<()> { - self.inner.time_to_live(ttl) - } - - /// Sets the broadcast flag on or off - #[unstable] - pub fn set_broadcast(&mut self, broadcast: bool) -> IoResult<()> { - self.inner.set_broadcast(broadcast) - } - - /// Sets the read/write timeout for this socket. - /// - /// For more information, see `TcpStream::set_timeout` - #[unstable = "the timeout argument may change in type and value"] - pub fn set_timeout(&mut self, timeout_ms: Option) { - self.inner.set_timeout(timeout_ms) - } - - /// Sets the read timeout for this socket. - /// - /// For more information, see `TcpStream::set_timeout` - #[unstable = "the timeout argument may change in type and value"] - pub fn set_read_timeout(&mut self, timeout_ms: Option) { - self.inner.set_read_timeout(timeout_ms) - } - - /// Sets the write timeout for this socket. - /// - /// For more information, see `TcpStream::set_timeout` - #[unstable = "the timeout argument may change in type and value"] - pub fn set_write_timeout(&mut self, timeout_ms: Option) { - self.inner.set_write_timeout(timeout_ms) - } -} - -impl Clone for UdpSocket { - /// Creates a new handle to this UDP socket, allowing for simultaneous - /// reads and writes of the socket. - /// - /// The underlying UDP socket will not be closed until all handles to the - /// socket have been deallocated. Two concurrent reads will not receive - /// the same data. Instead, the first read will receive the first packet - /// received, and the second read will receive the second packet. - fn clone(&self) -> UdpSocket { - UdpSocket { - inner: self.inner.clone(), - } - } -} - -impl sys_common::AsInner for UdpSocket { - fn as_inner(&self) -> &UdpSocketImp { - &self.inner - } -} - -#[cfg(test)] -#[allow(unstable)] -mod test { - use prelude::v1::*; - - use sync::mpsc::channel; - use io::net::ip::*; - use io::test::*; - use io::{IoError, TimedOut, PermissionDenied, ShortWrite}; - use super::*; - use thread::Thread; - - // FIXME #11530 this fails on android because tests are run as root - #[cfg_attr(any(windows, target_os = "android"), ignore)] - #[test] - fn bind_error() { - let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; - match UdpSocket::bind(addr) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, PermissionDenied), - } - } - - #[test] - fn socket_smoke_test_ip4() { - let server_ip = next_test_ip4(); - let client_ip = next_test_ip4(); - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - - let _t = Thread::spawn(move|| { - match UdpSocket::bind(client_ip) { - Ok(ref mut client) => { - rx1.recv().unwrap(); - client.send_to(&[99], server_ip).unwrap() - } - Err(..) => panic!() - } - tx2.send(()).unwrap(); - }); - - match UdpSocket::bind(server_ip) { - Ok(ref mut server) => { - tx1.send(()).unwrap(); - let mut buf = [0]; - match server.recv_from(&mut buf) { - Ok((nread, src)) => { - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - assert_eq!(src, client_ip); - } - Err(..) => panic!() - } - } - Err(..) => panic!() - } - rx2.recv().unwrap(); - } - - #[test] - fn socket_smoke_test_ip6() { - let server_ip = next_test_ip6(); - let client_ip = next_test_ip6(); - let (tx, rx) = channel::<()>(); - - let _t = Thread::spawn(move|| { - match UdpSocket::bind(client_ip) { - Ok(ref mut client) => { - rx.recv().unwrap(); - client.send_to(&[99], server_ip).unwrap() - } - Err(..) => panic!() - } - }); - - match UdpSocket::bind(server_ip) { - Ok(ref mut server) => { - tx.send(()).unwrap(); - let mut buf = [0]; - match server.recv_from(&mut buf) { - Ok((nread, src)) => { - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - assert_eq!(src, client_ip); - } - Err(..) => panic!() - } - } - Err(..) => panic!() - } - } - - pub fn socket_name(addr: SocketAddr) { - let server = UdpSocket::bind(addr); - - assert!(server.is_ok()); - let mut server = server.unwrap(); - - // Make sure socket_name gives - // us the socket we binded to. - let so_name = server.socket_name(); - assert!(so_name.is_ok()); - assert_eq!(addr, so_name.unwrap()); - } - - #[test] - fn socket_name_ip4() { - socket_name(next_test_ip4()); - } - - #[test] - fn socket_name_ip6() { - socket_name(next_test_ip6()); - } - - #[test] - fn udp_clone_smoke() { - let addr1 = next_test_ip4(); - let addr2 = next_test_ip4(); - let mut sock1 = UdpSocket::bind(addr1).unwrap(); - let sock2 = UdpSocket::bind(addr2).unwrap(); - - let _t = Thread::spawn(move|| { - let mut sock2 = sock2; - let mut buf = [0, 0]; - assert_eq!(sock2.recv_from(&mut buf), Ok((1, addr1))); - assert_eq!(buf[0], 1); - sock2.send_to(&[2], addr1).unwrap(); - }); - - let sock3 = sock1.clone(); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = Thread::spawn(move|| { - let mut sock3 = sock3; - rx1.recv().unwrap(); - sock3.send_to(&[1], addr2).unwrap(); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - assert_eq!(sock1.recv_from(&mut buf), Ok((1, addr2))); - rx2.recv().unwrap(); - } - - #[test] - fn udp_clone_two_read() { - let addr1 = next_test_ip4(); - let addr2 = next_test_ip4(); - let mut sock1 = UdpSocket::bind(addr1).unwrap(); - let sock2 = UdpSocket::bind(addr2).unwrap(); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = Thread::spawn(move|| { - let mut sock2 = sock2; - sock2.send_to(&[1], addr1).unwrap(); - rx.recv().unwrap(); - sock2.send_to(&[2], addr1).unwrap(); - rx.recv().unwrap(); - }); - - let sock3 = sock1.clone(); - - let (done, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut sock3 = sock3; - let mut buf = [0, 0]; - sock3.recv_from(&mut buf).unwrap(); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - sock1.recv_from(&mut buf).unwrap(); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - } - - #[test] - fn udp_clone_two_write() { - let addr1 = next_test_ip4(); - let addr2 = next_test_ip4(); - let mut sock1 = UdpSocket::bind(addr1).unwrap(); - let sock2 = UdpSocket::bind(addr2).unwrap(); - - let (tx, rx) = channel(); - let (serv_tx, serv_rx) = channel(); - - let _t = Thread::spawn(move|| { - let mut sock2 = sock2; - let mut buf = [0, 1]; - - rx.recv().unwrap(); - match sock2.recv_from(&mut buf) { - Ok(..) => {} - Err(e) => panic!("failed receive: {}", e), - } - serv_tx.send(()).unwrap(); - }); - - let sock3 = sock1.clone(); - - let (done, rx) = channel(); - let tx2 = tx.clone(); - let _t = Thread::spawn(move|| { - let mut sock3 = sock3; - match sock3.send_to(&[1], addr2) { - Ok(..) => { let _ = tx2.send(()); } - Err(..) => {} - } - done.send(()).unwrap(); - }); - match sock1.send_to(&[2], addr2) { - Ok(..) => { let _ = tx.send(()); } - Err(..) => {} - } - drop(tx); - - rx.recv().unwrap(); - serv_rx.recv().unwrap(); - } - - #[cfg(not(windows))] // FIXME #17553 - #[test] - fn recv_from_timeout() { - let addr1 = next_test_ip4(); - let addr2 = next_test_ip4(); - let mut a = UdpSocket::bind(addr1).unwrap(); - let a2 = UdpSocket::bind(addr2).unwrap(); - - let (tx, rx) = channel(); - let (tx2, rx2) = channel(); - let _t = Thread::spawn(move|| { - let mut a = a2; - assert_eq!(a.recv_from(&mut [0]), Ok((1, addr1))); - assert_eq!(a.send_to(&[0], addr1), Ok(())); - rx.recv().unwrap(); - assert_eq!(a.send_to(&[0], addr1), Ok(())); - - tx2.send(()).unwrap(); - }); - - // Make sure that reads time out, but writes can continue - a.set_read_timeout(Some(20)); - assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut); - assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut); - assert_eq!(a.send_to(&[0], addr2), Ok(())); - - // Cloned handles should be able to block - let mut a2 = a.clone(); - assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2))); - - // Clearing the timeout should allow for receiving - a.set_timeout(None); - tx.send(()).unwrap(); - assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2))); - - // Make sure the child didn't die - rx2.recv().unwrap(); - } - - #[test] - fn send_to_timeout() { - let addr1 = next_test_ip4(); - let addr2 = next_test_ip4(); - let mut a = UdpSocket::bind(addr1).unwrap(); - let _b = UdpSocket::bind(addr2).unwrap(); - - a.set_write_timeout(Some(1000)); - for _ in range(0u, 100) { - match a.send_to(&[0;4*1024], addr2) { - Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {}, - Err(IoError { kind: TimedOut, .. }) => break, - Err(e) => panic!("other error: {}", e), - } - } - } -} diff --git a/src/libstd/io/pipe.rs b/src/libstd/io/pipe.rs deleted file mode 100644 index 09dcafb0218..00000000000 --- a/src/libstd/io/pipe.rs +++ /dev/null @@ -1,139 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Synchronous, in-memory pipes. -//! -//! Currently these aren't particularly useful, there only exists bindings -//! enough so that pipes can be created to child processes. - -#![allow(missing_docs)] - -use prelude::v1::*; - -use io::IoResult; -use libc; -use sync::Arc; - -use sys_common; -use sys; -use sys::fs::FileDesc as FileDesc; - -/// A synchronous, in-memory pipe. -pub struct PipeStream { - inner: Arc -} - -pub struct PipePair { - pub reader: PipeStream, - pub writer: PipeStream, -} - -impl PipeStream { - /// Consumes a file descriptor to return a pipe stream that will have - /// synchronous, but non-blocking reads/writes. This is useful if the file - /// descriptor is acquired via means other than the standard methods. - /// - /// This operation consumes ownership of the file descriptor and it will be - /// closed once the object is deallocated. - /// - /// # Example - /// - /// ```{rust,no_run} - /// # #![allow(unused_must_use)] - /// extern crate libc; - /// - /// use std::io::pipe::PipeStream; - /// - /// fn main() { - /// let mut pipe = PipeStream::open(libc::STDERR_FILENO); - /// pipe.write(b"Hello, stderr!"); - /// } - /// ``` - pub fn open(fd: libc::c_int) -> IoResult { - Ok(PipeStream::from_filedesc(FileDesc::new(fd, true))) - } - - // FIXME: expose this some other way - /// Wrap a FileDesc directly, taking ownership. - #[doc(hidden)] - pub fn from_filedesc(fd: FileDesc) -> PipeStream { - PipeStream { inner: Arc::new(fd) } - } - - /// Creates a pair of in-memory OS pipes for a unidirectional communication - /// stream. - /// - /// The structure returned contains a reader and writer I/O object. Data - /// written to the writer can be read from the reader. - /// - /// # Errors - /// - /// This function can fail to succeed if the underlying OS has run out of - /// available resources to allocate a new pipe. - pub fn pair() -> IoResult { - let (reader, writer) = try!(unsafe { sys::os::pipe() }); - Ok(PipePair { - reader: PipeStream::from_filedesc(reader), - writer: PipeStream::from_filedesc(writer), - }) - } -} - -impl sys_common::AsInner for PipeStream { - fn as_inner(&self) -> &sys::fs::FileDesc { - &*self.inner - } -} - -impl Clone for PipeStream { - fn clone(&self) -> PipeStream { - PipeStream { inner: self.inner.clone() } - } -} - -impl Reader for PipeStream { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner.read(buf) - } -} - -impl Writer for PipeStream { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner.write(buf) - } -} - -#[cfg(test)] -mod test { - use prelude::v1::*; - - use sync::mpsc::channel; - use thread::Thread; - - #[test] - fn partial_read() { - use os; - use io::pipe::PipeStream; - - let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() }; - let out = PipeStream::open(writer); - let mut input = PipeStream::open(reader); - let (tx, rx) = channel(); - let _t = Thread::spawn(move|| { - let mut out = out; - out.write(&[10]).unwrap(); - rx.recv().unwrap(); // don't close the pipe until the other read has finished - }); - - let mut buf = [0; 10]; - input.read(&mut buf).unwrap(); - tx.send(()).unwrap(); - } -} diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs deleted file mode 100644 index c2f52f5c8a3..00000000000 --- a/src/libstd/io/process.rs +++ /dev/null @@ -1,1231 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Bindings for executing child processes - -#![allow(unstable)] -#![allow(non_upper_case_globals)] - -pub use self::StdioContainer::*; -pub use self::ProcessExit::*; - -use prelude::v1::*; - -use collections::HashMap; -use ffi::CString; -use fmt; -use io::pipe::{PipeStream, PipePair}; -use io::{IoResult, IoError}; -use io; -use libc; -use os; -use path::BytesContainer; -use sync::mpsc::{channel, Receiver}; -use sys::fs::FileDesc; -use sys::process::Process as ProcessImp; -use sys; -use thread::Thread; - -#[cfg(windows)] use hash; -#[cfg(windows)] use str; - -/// Signal a process to exit, without forcibly killing it. Corresponds to -/// SIGTERM on unix platforms. -#[cfg(windows)] pub const PleaseExitSignal: int = 15; -/// Signal a process to exit immediately, forcibly killing it. Corresponds to -/// SIGKILL on unix platforms. -#[cfg(windows)] pub const MustDieSignal: int = 9; -/// Signal a process to exit, without forcibly killing it. Corresponds to -/// SIGTERM on unix platforms. -#[cfg(not(windows))] pub const PleaseExitSignal: int = libc::SIGTERM as int; -/// Signal a process to exit immediately, forcibly killing it. Corresponds to -/// SIGKILL on unix platforms. -#[cfg(not(windows))] pub const MustDieSignal: int = libc::SIGKILL as int; - -/// Representation of a running or exited child process. -/// -/// This structure is used to represent and manage child processes. A child -/// process is created via the `Command` struct, which configures the spawning -/// process and can itself be constructed using a builder-style interface. -/// -/// # Example -/// -/// ```should_fail -/// use std::io::Command; -/// -/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() { -/// Ok(child) => child, -/// Err(e) => panic!("failed to execute child: {}", e), -/// }; -/// -/// let contents = child.stdout.as_mut().unwrap().read_to_end(); -/// assert!(child.wait().unwrap().success()); -/// ``` -pub struct Process { - handle: ProcessImp, - forget: bool, - - /// None until wait() is called. - exit_code: Option, - - /// Manually delivered signal - exit_signal: Option, - - /// Deadline after which wait() will return - deadline: u64, - - /// Handle to the child's stdin, if the `stdin` field of this process's - /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. - pub stdin: Option, - - /// Handle to the child's stdout, if the `stdout` field of this process's - /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. - pub stdout: Option, - - /// Handle to the child's stderr, if the `stderr` field of this process's - /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`. - pub stderr: Option, -} - -/// A representation of environment variable name -/// It compares case-insensitive on Windows and case-sensitive everywhere else. -#[cfg(not(windows))] -#[derive(Hash, PartialEq, Eq, Clone, Show)] -struct EnvKey(CString); - -#[doc(hidden)] -#[cfg(windows)] -#[derive(Eq, Clone, Show)] -struct EnvKey(CString); - -#[cfg(windows)] -impl hash::Hash for EnvKey { - fn hash(&self, state: &mut H) { - let &EnvKey(ref x) = self; - match str::from_utf8(x.as_bytes()) { - Ok(s) => for ch in s.chars() { - (ch as u8 as char).to_lowercase().hash(state); - }, - Err(..) => x.hash(state) - } - } -} - -#[cfg(windows)] -impl PartialEq for EnvKey { - fn eq(&self, other: &EnvKey) -> bool { - let &EnvKey(ref x) = self; - let &EnvKey(ref y) = other; - match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) { - (Ok(xs), Ok(ys)) => { - if xs.len() != ys.len() { - return false - } else { - for (xch, ych) in xs.chars().zip(ys.chars()) { - if xch.to_lowercase() != ych.to_lowercase() { - return false; - } - } - return true; - } - }, - // If either is not a valid utf8 string, just compare them byte-wise - _ => return x.eq(y) - } - } -} - -impl BytesContainer for EnvKey { - fn container_as_bytes<'a>(&'a self) -> &'a [u8] { - let &EnvKey(ref k) = self; - k.container_as_bytes() - } -} - -/// A HashMap representation of environment variables. -pub type EnvMap = HashMap; - -/// The `Command` type acts as a process builder, providing fine-grained control -/// over how a new process should be spawned. A default configuration can be -/// generated using `Command::new(program)`, where `program` gives a path to the -/// program to be executed. Additional builder methods allow the configuration -/// to be changed (for example, by adding arguments) prior to spawning: -/// -/// ``` -/// use std::io::Command; -/// -/// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() { -/// Ok(p) => p, -/// Err(e) => panic!("failed to execute process: {}", e), -/// }; -/// -/// let output = process.stdout.as_mut().unwrap().read_to_end(); -/// ``` -#[derive(Clone)] -pub struct Command { - // The internal data for the builder. Documented by the builder - // methods below, and serialized into rt::rtio::ProcessConfig. - program: CString, - args: Vec, - env: Option, - cwd: Option, - stdin: StdioContainer, - stdout: StdioContainer, - stderr: StdioContainer, - uid: Option, - gid: Option, - detach: bool, -} - -// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so -// we cannot usefully take BytesContainer arguments by reference (without forcing an -// additional & around &str). So we are instead temporarily adding an instance -// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path -// instance should be removed, and arguments bound by BytesContainer should be passed by -// reference. (Here: {new, arg, args, env}.) - -impl Command { - /// Constructs a new `Command` for launching the program at - /// path `program`, with the following default configuration: - /// - /// * No arguments to the program - /// * Inherit the current process's environment - /// * Inherit the current process's working directory - /// * A readable pipe for stdin (file descriptor 0) - /// * A writeable pipe for stdout and stderr (file descriptors 1 and 2) - /// - /// Builder methods are provided to change these defaults and - /// otherwise configure the process. - pub fn new(program: T) -> Command { - Command { - program: CString::from_slice(program.container_as_bytes()), - args: Vec::new(), - env: None, - cwd: None, - stdin: CreatePipe(true, false), - stdout: CreatePipe(false, true), - stderr: CreatePipe(false, true), - uid: None, - gid: None, - detach: false, - } - } - - /// Add an argument to pass to the program. - pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command { - self.args.push(CString::from_slice(arg.container_as_bytes())); - self - } - - /// Add multiple arguments to pass to the program. - pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command { - self.args.extend(args.iter().map(|arg| { - CString::from_slice(arg.container_as_bytes()) - })); - self - } - // Get a mutable borrow of the environment variable map for this `Command`. - fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { - match self.env { - Some(ref mut map) => map, - None => { - // if the env is currently just inheriting from the parent's, - // materialize the parent's env into a hashtable. - self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| { - (EnvKey(CString::from_slice(k.as_slice())), - CString::from_slice(v.as_slice())) - }).collect()); - self.env.as_mut().unwrap() - } - } - } - - /// Inserts or updates an environment variable mapping. - /// - /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, - /// and case-sensitive on all other platforms. - pub fn env<'a, T, U>(&'a mut self, key: T, val: U) - -> &'a mut Command - where T: BytesContainer, U: BytesContainer { - let key = EnvKey(CString::from_slice(key.container_as_bytes())); - let val = CString::from_slice(val.container_as_bytes()); - self.get_env_map().insert(key, val); - self - } - - /// Removes an environment variable mapping. - pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command - where T: BytesContainer { - let key = EnvKey(CString::from_slice(key.container_as_bytes())); - self.get_env_map().remove(&key); - self - } - - /// Sets the entire environment map for the child process. - /// - /// If the given slice contains multiple instances of an environment - /// variable, the *rightmost* instance will determine the value. - pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)]) - -> &'a mut Command - where T: BytesContainer, U: BytesContainer { - self.env = Some(env.iter().map(|&(ref k, ref v)| { - (EnvKey(CString::from_slice(k.container_as_bytes())), - CString::from_slice(v.container_as_bytes())) - }).collect()); - self - } - - /// Set the working directory for the child process. - pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command { - self.cwd = Some(CString::from_slice(dir.as_vec())); - self - } - - /// Configuration for the child process's stdin handle (file descriptor 0). - /// Defaults to `CreatePipe(true, false)` so the input can be written to. - pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { - self.stdin = cfg; - self - } - - /// Configuration for the child process's stdout handle (file descriptor 1). - /// Defaults to `CreatePipe(false, true)` so the output can be collected. - pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { - self.stdout = cfg; - self - } - - /// Configuration for the child process's stderr handle (file descriptor 2). - /// Defaults to `CreatePipe(false, true)` so the output can be collected. - pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command { - self.stderr = cfg; - self - } - - /// Sets the child process's user id. This translates to a `setuid` call in - /// the child process. Setting this value on windows will cause the spawn to - /// fail. Failure in the `setuid` call on unix will also cause the spawn to - /// fail. - pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command { - self.uid = Some(id); - self - } - - /// Similar to `uid`, but sets the group id of the child process. This has - /// the same semantics as the `uid` field. - pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command { - self.gid = Some(id); - self - } - - /// Sets the child process to be spawned in a detached state. On unix, this - /// means that the child is the leader of a new process group. - pub fn detached<'a>(&'a mut self) -> &'a mut Command { - self.detach = true; - self - } - - /// Executes the command as a child process, which is returned. - pub fn spawn(&self) -> IoResult { - let (their_stdin, our_stdin) = try!(setup_io(self.stdin)); - let (their_stdout, our_stdout) = try!(setup_io(self.stdout)); - let (their_stderr, our_stderr) = try!(setup_io(self.stderr)); - - match ProcessImp::spawn(self, their_stdin, their_stdout, their_stderr) { - Err(e) => Err(e), - Ok(handle) => Ok(Process { - handle: handle, - forget: false, - exit_code: None, - exit_signal: None, - deadline: 0, - stdin: our_stdin, - stdout: our_stdout, - stderr: our_stderr, - }) - } - } - - /// Executes the command as a child process, waiting for it to finish and - /// collecting all of its output. - /// - /// # Example - /// - /// ``` - /// use std::io::Command; - /// - /// let output = match Command::new("cat").arg("foot.txt").output() { - /// Ok(output) => output, - /// Err(e) => panic!("failed to execute process: {}", e), - /// }; - /// - /// println!("status: {}", output.status); - /// println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice())); - /// println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice())); - /// ``` - pub fn output(&self) -> IoResult { - self.spawn().and_then(|p| p.wait_with_output()) - } - - /// Executes a command as a child process, waiting for it to finish and - /// collecting its exit status. - /// - /// # Example - /// - /// ``` - /// use std::io::Command; - /// - /// let status = match Command::new("ls").status() { - /// Ok(status) => status, - /// Err(e) => panic!("failed to execute process: {}", e), - /// }; - /// - /// println!("process exited with: {}", status); - /// ``` - pub fn status(&self) -> IoResult { - self.spawn().and_then(|mut p| p.wait()) - } -} - -impl fmt::Debug for Command { - /// Format the program and arguments of a Command for display. Any - /// non-utf8 data is lossily converted using the utf8 replacement - /// character. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes()))); - for arg in self.args.iter() { - try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes()))); - } - Ok(()) - } -} - -fn setup_io(io: StdioContainer) -> IoResult<(Option, Option)> { - let ours; - let theirs; - match io { - Ignored => { - theirs = None; - ours = None; - } - InheritFd(fd) => { - theirs = Some(PipeStream::from_filedesc(FileDesc::new(fd, false))); - ours = None; - } - CreatePipe(readable, _writable) => { - let PipePair { reader, writer } = try!(PipeStream::pair()); - if readable { - theirs = Some(reader); - ours = Some(writer); - } else { - theirs = Some(writer); - ours = Some(reader); - } - } - } - Ok((theirs, ours)) -} - -// Allow the sys module to get access to the Command state -impl sys::process::ProcessConfig for Command { - fn program(&self) -> &CString { - &self.program - } - fn args(&self) -> &[CString] { - self.args.as_slice() - } - fn env(&self) -> Option<&EnvMap> { - self.env.as_ref() - } - fn cwd(&self) -> Option<&CString> { - self.cwd.as_ref() - } - fn uid(&self) -> Option { - self.uid.clone() - } - fn gid(&self) -> Option { - self.gid.clone() - } - fn detach(&self) -> bool { - self.detach - } - -} - -/// The output of a finished process. -#[derive(PartialEq, Eq, Clone)] -pub struct ProcessOutput { - /// The status (exit code) of the process. - pub status: ProcessExit, - /// The data that the process wrote to stdout. - pub output: Vec, - /// The data that the process wrote to stderr. - pub error: Vec, -} - -/// Describes what to do with a standard io stream for a child process. -#[derive(Clone, Copy)] -pub enum StdioContainer { - /// This stream will be ignored. This is the equivalent of attaching the - /// stream to `/dev/null` - Ignored, - - /// The specified file descriptor is inherited for the stream which it is - /// specified for. Ownership of the file descriptor is *not* taken, so the - /// caller must clean it up. - InheritFd(libc::c_int), - - /// Creates a pipe for the specified file descriptor which will be created - /// when the process is spawned. - /// - /// The first boolean argument is whether the pipe is readable, and the - /// second is whether it is writable. These properties are from the view of - /// the *child* process, not the parent process. - CreatePipe(bool /* readable */, bool /* writable */), -} - -/// Describes the result of a process after it has terminated. -/// Note that Windows have no signals, so the result is usually ExitStatus. -#[derive(PartialEq, Eq, Clone, Copy, Show)] -pub enum ProcessExit { - /// Normal termination with an exit status. - ExitStatus(int), - - /// Termination by signal, with the signal number. - ExitSignal(int), -} - -#[stable] -impl fmt::Display for ProcessExit { - /// Format a ProcessExit enum, to nicely present the information. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ExitStatus(code) => write!(f, "exit code: {}", code), - ExitSignal(code) => write!(f, "signal: {}", code), - } - } -} - -impl ProcessExit { - /// Was termination successful? Signal termination not considered a success, - /// and success is defined as a zero exit status. - pub fn success(&self) -> bool { - return self.matches_exit_status(0); - } - - /// Checks whether this ProcessExit matches the given exit status. - /// Termination by signal will never match an exit code. - pub fn matches_exit_status(&self, wanted: int) -> bool { - *self == ExitStatus(wanted) - } -} - -impl Process { - /// Sends `signal` to another process in the system identified by `id`. - /// - /// Note that windows doesn't quite have the same model as unix, so some - /// unix signals are mapped to windows signals. Notably, unix termination - /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`. - /// - /// Additionally, a signal number of 0 can check for existence of the target - /// process. Note, though, that on some platforms signals will continue to - /// be successfully delivered if the child has exited, but not yet been - /// reaped. - pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> { - unsafe { ProcessImp::killpid(id, signal) } - } - - /// Returns the process id of this child process - pub fn id(&self) -> libc::pid_t { self.handle.id() } - - /// Sends the specified signal to the child process, returning whether the - /// signal could be delivered or not. - /// - /// Note that signal 0 is interpreted as a poll to check whether the child - /// process is still alive or not. If an error is returned, then the child - /// process has exited. - /// - /// On some unix platforms signals will continue to be received after a - /// child has exited but not yet been reaped. In order to report the status - /// of signal delivery correctly, unix implementations may invoke - /// `waitpid()` with `WNOHANG` in order to reap the child as necessary. - /// - /// # Errors - /// - /// If the signal delivery fails, the corresponding error is returned. - pub fn signal(&mut self, signal: int) -> IoResult<()> { - #[cfg(unix)] fn collect_status(p: &mut Process) { - // On Linux (and possibly other unices), a process that has exited will - // continue to accept signals because it is "defunct". The delivery of - // signals will only fail once the child has been reaped. For this - // reason, if the process hasn't exited yet, then we attempt to collect - // their status with WNOHANG. - if p.exit_code.is_none() { - match p.handle.try_wait() { - Some(code) => { p.exit_code = Some(code); } - None => {} - } - } - } - #[cfg(windows)] fn collect_status(_p: &mut Process) {} - - collect_status(self); - - // if the process has finished, and therefore had waitpid called, - // and we kill it, then on unix we might ending up killing a - // newer process that happens to have the same (re-used) id - if self.exit_code.is_some() { - return Err(IoError { - kind: io::InvalidInput, - desc: "invalid argument: can't kill an exited process", - detail: None, - }) - } - - // A successfully delivered signal that isn't 0 (just a poll for being - // alive) is recorded for windows (see wait()) - match unsafe { self.handle.kill(signal) } { - Ok(()) if signal == 0 => Ok(()), - Ok(()) => { self.exit_signal = Some(signal); Ok(()) } - Err(e) => Err(e), - } - - } - - /// Sends a signal to this child requesting that it exits. This is - /// equivalent to sending a SIGTERM on unix platforms. - pub fn signal_exit(&mut self) -> IoResult<()> { - self.signal(PleaseExitSignal) - } - - /// Sends a signal to this child forcing it to exit. This is equivalent to - /// sending a SIGKILL on unix platforms. - pub fn signal_kill(&mut self) -> IoResult<()> { - self.signal(MustDieSignal) - } - - /// Wait for the child to exit completely, returning the status that it - /// exited with. This function will continue to have the same return value - /// after it has been called at least once. - /// - /// The stdin handle to the child process will be closed before waiting. - /// - /// # Errors - /// - /// This function can fail if a timeout was previously specified via - /// `set_timeout` and the timeout expires before the child exits. - pub fn wait(&mut self) -> IoResult { - drop(self.stdin.take()); - match self.exit_code { - Some(code) => Ok(code), - None => { - let code = try!(self.handle.wait(self.deadline)); - // On windows, waitpid will never return a signal. If a signal - // was successfully delivered to the process, however, we can - // consider it as having died via a signal. - let code = match self.exit_signal { - None => code, - Some(signal) if cfg!(windows) => ExitSignal(signal), - Some(..) => code, - }; - self.exit_code = Some(code); - Ok(code) - } - } - } - - /// Sets a timeout, in milliseconds, for future calls to wait(). - /// - /// The argument specified is a relative distance into the future, in - /// milliseconds, after which any call to wait() will return immediately - /// with a timeout error, and all future calls to wait() will not block. - /// - /// A value of `None` will clear any previous timeout, and a value of `Some` - /// will override any previously set timeout. - /// - /// # Example - /// - /// ```no_run - /// # #![allow(unstable)] - /// use std::io::{Command, IoResult}; - /// use std::io::process::ProcessExit; - /// - /// fn run_gracefully(prog: &str) -> IoResult { - /// let mut p = try!(Command::new("long-running-process").spawn()); - /// - /// // give the process 10 seconds to finish completely - /// p.set_timeout(Some(10_000)); - /// match p.wait() { - /// Ok(status) => return Ok(status), - /// Err(..) => {} - /// } - /// - /// // Attempt to exit gracefully, but don't wait for it too long - /// try!(p.signal_exit()); - /// p.set_timeout(Some(1_000)); - /// match p.wait() { - /// Ok(status) => return Ok(status), - /// Err(..) => {} - /// } - /// - /// // Well, we did our best, forcefully kill the process - /// try!(p.signal_kill()); - /// p.set_timeout(None); - /// p.wait() - /// } - /// ``` - #[unstable = "the type of the timeout is likely to change"] - pub fn set_timeout(&mut self, timeout_ms: Option) { - self.deadline = timeout_ms.map(|i| i + sys::timer::now()).unwrap_or(0); - } - - /// Simultaneously wait for the child to exit and collect all remaining - /// output on the stdout/stderr handles, returning a `ProcessOutput` - /// instance. - /// - /// The stdin handle to the child is closed before waiting. - /// - /// # Errors - /// - /// This function can fail for any of the same reasons that `wait()` can - /// fail. - pub fn wait_with_output(mut self) -> IoResult { - drop(self.stdin.take()); - fn read(stream: Option) -> Receiver>> { - let (tx, rx) = channel(); - match stream { - Some(stream) => { - Thread::spawn(move |:| { - let mut stream = stream; - tx.send(stream.read_to_end()).unwrap(); - }); - } - None => tx.send(Ok(Vec::new())).unwrap() - } - rx - } - let stdout = read(self.stdout.take()); - let stderr = read(self.stderr.take()); - - let status = try!(self.wait()); - - Ok(ProcessOutput { - status: status, - output: stdout.recv().unwrap().unwrap_or(Vec::new()), - error: stderr.recv().unwrap().unwrap_or(Vec::new()), - }) - } - - /// Forgets this process, allowing it to outlive the parent - /// - /// This function will forcefully prevent calling `wait()` on the child - /// process in the destructor, allowing the child to outlive the - /// parent. Note that this operation can easily lead to leaking the - /// resources of the child process, so care must be taken when - /// invoking this method. - pub fn forget(mut self) { - self.forget = true; - } -} - -impl Drop for Process { - fn drop(&mut self) { - if self.forget { return } - - // Close all I/O before exiting to ensure that the child doesn't wait - // forever to print some text or something similar. - drop(self.stdin.take()); - drop(self.stdout.take()); - drop(self.stderr.take()); - - self.set_timeout(None); - let _ = self.wait().unwrap(); - } -} - -#[cfg(test)] -mod tests { - use io::{Truncate, Write, TimedOut, timer, process, FileNotFound}; - use prelude::v1::{Ok, Err, range, drop, Some, None, Vec}; - use prelude::v1::{Path, String, Reader, Writer, Clone}; - use prelude::v1::{SliceExt, Str, StrExt, AsSlice, ToString, GenericPath}; - use io::fs::PathExtensions; - use io::timer::*; - use rt::running_on_valgrind; - use str; - use super::{CreatePipe}; - use super::{InheritFd, Process, PleaseExitSignal, Command, ProcessOutput}; - use sync::mpsc::channel; - use thread::Thread; - use time::Duration; - - // FIXME(#10380) these tests should not all be ignored on android. - - #[cfg(not(target_os="android"))] - #[test] - fn smoke() { - let p = Command::new("true").spawn(); - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.wait().unwrap().success()); - } - - #[cfg(not(target_os="android"))] - #[test] - fn smoke_failure() { - match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { - Ok(..) => panic!(), - Err(..) => {} - } - } - - #[cfg(not(target_os="android"))] - #[test] - fn exit_reported_right() { - let p = Command::new("false").spawn(); - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.wait().unwrap().matches_exit_status(1)); - drop(p.wait().clone()); - } - - #[cfg(all(unix, not(target_os="android")))] - #[test] - fn signal_reported_right() { - let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn(); - assert!(p.is_ok()); - let mut p = p.unwrap(); - match p.wait().unwrap() { - process::ExitSignal(1) => {}, - result => panic!("not terminated by signal 1 (instead, {})", result), - } - } - - pub fn read_all(input: &mut Reader) -> String { - input.read_to_string().unwrap() - } - - pub fn run_output(cmd: Command) -> String { - let p = cmd.spawn(); - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.stdout.is_some()); - let ret = read_all(p.stdout.as_mut().unwrap() as &mut Reader); - assert!(p.wait().unwrap().success()); - return ret; - } - - #[cfg(not(target_os="android"))] - #[test] - fn stdout_works() { - let mut cmd = Command::new("echo"); - cmd.arg("foobar").stdout(CreatePipe(false, true)); - assert_eq!(run_output(cmd), "foobar\n"); - } - - #[cfg(all(unix, not(target_os="android")))] - #[test] - fn set_cwd_works() { - let mut cmd = Command::new("/bin/sh"); - cmd.arg("-c").arg("pwd") - .cwd(&Path::new("/")) - .stdout(CreatePipe(false, true)); - assert_eq!(run_output(cmd), "/\n"); - } - - #[cfg(all(unix, not(target_os="android")))] - #[test] - fn stdin_works() { - let mut p = Command::new("/bin/sh") - .arg("-c").arg("read line; echo $line") - .stdin(CreatePipe(true, false)) - .stdout(CreatePipe(false, true)) - .spawn().unwrap(); - p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); - drop(p.stdin.take()); - let out = read_all(p.stdout.as_mut().unwrap() as &mut Reader); - assert!(p.wait().unwrap().success()); - assert_eq!(out, "foobar\n"); - } - - #[cfg(not(target_os="android"))] - #[test] - fn detach_works() { - let mut p = Command::new("true").detached().spawn().unwrap(); - assert!(p.wait().unwrap().success()); - } - - #[cfg(windows)] - #[test] - fn uid_fails_on_windows() { - assert!(Command::new("test").uid(10).spawn().is_err()); - } - - #[cfg(all(unix, not(target_os="android")))] - #[test] - fn uid_works() { - use libc; - let mut p = Command::new("/bin/sh") - .arg("-c").arg("true") - .uid(unsafe { libc::getuid() as uint }) - .gid(unsafe { libc::getgid() as uint }) - .spawn().unwrap(); - assert!(p.wait().unwrap().success()); - } - - #[cfg(all(unix, not(target_os="android")))] - #[test] - fn uid_to_root_fails() { - use libc; - - // if we're already root, this isn't a valid test. Most of the bots run - // as non-root though (android is an exception). - if unsafe { libc::getuid() == 0 } { return } - assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_process_status() { - let mut status = Command::new("false").status().unwrap(); - assert!(status.matches_exit_status(1)); - - status = Command::new("true").status().unwrap(); - assert!(status.success()); - } - - #[test] - fn test_process_output_fail_to_start() { - match Command::new("/no-binary-by-this-name-should-exist").output() { - Err(e) => assert_eq!(e.kind, FileNotFound), - Ok(..) => panic!() - } - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_process_output_output() { - let ProcessOutput {status, output, error} - = Command::new("echo").arg("hello").output().unwrap(); - let output_str = str::from_utf8(output.as_slice()).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello"); - // FIXME #7224 - if !running_on_valgrind() { - assert_eq!(error, Vec::new()); - } - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_process_output_error() { - let ProcessOutput {status, output, error} - = Command::new("mkdir").arg(".").output().unwrap(); - - assert!(status.matches_exit_status(1)); - assert_eq!(output, Vec::new()); - assert!(!error.is_empty()); - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_finish_once() { - let mut prog = Command::new("false").spawn().unwrap(); - assert!(prog.wait().unwrap().matches_exit_status(1)); - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_finish_twice() { - let mut prog = Command::new("false").spawn().unwrap(); - assert!(prog.wait().unwrap().matches_exit_status(1)); - assert!(prog.wait().unwrap().matches_exit_status(1)); - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_wait_with_output_once() { - let prog = Command::new("echo").arg("hello").spawn().unwrap(); - let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap(); - let output_str = str::from_utf8(output.as_slice()).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello"); - // FIXME #7224 - if !running_on_valgrind() { - assert_eq!(error, Vec::new()); - } - } - - #[cfg(all(unix, not(target_os="android")))] - pub fn pwd_cmd() -> Command { - Command::new("pwd") - } - #[cfg(target_os="android")] - pub fn pwd_cmd() -> Command { - let mut cmd = Command::new("/system/bin/sh"); - cmd.arg("-c").arg("pwd"); - cmd - } - - #[cfg(windows)] - pub fn pwd_cmd() -> Command { - let mut cmd = Command::new("cmd"); - cmd.arg("/c").arg("cd"); - cmd - } - - #[test] - fn test_keep_current_working_dir() { - use os; - let prog = pwd_cmd().spawn().unwrap(); - - let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); - let parent_dir = os::getcwd().unwrap(); - let child_dir = Path::new(output.trim()); - - let parent_stat = parent_dir.stat().unwrap(); - let child_stat = child_dir.stat().unwrap(); - - assert_eq!(parent_stat.unstable.device, child_stat.unstable.device); - assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode); - } - - #[test] - fn test_change_working_directory() { - use os; - // test changing to the parent of os::getcwd() because we know - // the path exists (and os::getcwd() is not expected to be root) - let parent_dir = os::getcwd().unwrap().dir_path(); - let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap(); - - let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); - let child_dir = Path::new(output.trim()); - - let parent_stat = parent_dir.stat().unwrap(); - let child_stat = child_dir.stat().unwrap(); - - assert_eq!(parent_stat.unstable.device, child_stat.unstable.device); - assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode); - } - - #[cfg(all(unix, not(target_os="android")))] - pub fn env_cmd() -> Command { - Command::new("env") - } - #[cfg(target_os="android")] - pub fn env_cmd() -> Command { - let mut cmd = Command::new("/system/bin/sh"); - cmd.arg("-c").arg("set"); - cmd - } - - #[cfg(windows)] - pub fn env_cmd() -> Command { - let mut cmd = Command::new("cmd"); - cmd.arg("/c").arg("set"); - cmd - } - - #[cfg(not(target_os="android"))] - #[test] - fn test_inherit_env() { - use os; - if running_on_valgrind() { return; } - - let prog = env_cmd().spawn().unwrap(); - let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); - - let r = os::env(); - for &(ref k, ref v) in r.iter() { - // don't check windows magical empty-named variables - assert!(k.is_empty() || - output.contains(format!("{}={}", *k, *v).as_slice()), - "output doesn't contain `{}={}`\n{}", - k, v, output); - } - } - #[cfg(target_os="android")] - #[test] - fn test_inherit_env() { - use os; - if running_on_valgrind() { return; } - - let mut prog = env_cmd().spawn().unwrap(); - let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); - - let r = os::env(); - for &(ref k, ref v) in r.iter() { - // don't check android RANDOM variables - if *k != "RANDOM".to_string() { - assert!(output.contains(format!("{}={}", - *k, - *v).as_slice()) || - output.contains(format!("{}=\'{}\'", - *k, - *v).as_slice())); - } - } - } - - #[test] - fn test_override_env() { - use os; - let mut new_env = vec![("RUN_TEST_NEW_ENV", "123")]; - - // In some build environments (such as chrooted Nix builds), `env` can - // only be found in the explicitly-provided PATH env variable, not in - // default places such as /bin or /usr/bin. So we need to pass through - // PATH to our sub-process. - let path_val: String; - match os::getenv("PATH") { - None => {} - Some(val) => { - path_val = val; - new_env.push(("PATH", path_val.as_slice())) - } - } - - let prog = env_cmd().env_set_all(new_env.as_slice()).spawn().unwrap(); - let result = prog.wait_with_output().unwrap(); - let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); - - assert!(output.contains("RUN_TEST_NEW_ENV=123"), - "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); - } - - #[test] - fn test_add_to_env() { - let prog = env_cmd().env("RUN_TEST_NEW_ENV", "123").spawn().unwrap(); - let result = prog.wait_with_output().unwrap(); - let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); - - assert!(output.contains("RUN_TEST_NEW_ENV=123"), - "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); - } - - #[cfg(unix)] - pub fn sleeper() -> Process { - Command::new("sleep").arg("1000").spawn().unwrap() - } - #[cfg(windows)] - pub fn sleeper() -> Process { - // There's a `timeout` command on windows, but it doesn't like having - // its output piped, so instead just ping ourselves a few times with - // gaps in between so we're sure this process is alive for awhile - Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap() - } - - #[test] - fn test_kill() { - let mut p = sleeper(); - Process::kill(p.id(), PleaseExitSignal).unwrap(); - assert!(!p.wait().unwrap().success()); - } - - #[test] - fn test_exists() { - let mut p = sleeper(); - assert!(Process::kill(p.id(), 0).is_ok()); - p.signal_kill().unwrap(); - assert!(!p.wait().unwrap().success()); - } - - #[test] - fn test_zero() { - let mut p = sleeper(); - p.signal_kill().unwrap(); - for _ in range(0i, 20) { - if p.signal(0).is_err() { - assert!(!p.wait().unwrap().success()); - return - } - timer::sleep(Duration::milliseconds(100)); - } - panic!("never saw the child go away"); - } - - #[test] - fn wait_timeout() { - let mut p = sleeper(); - p.set_timeout(Some(10)); - assert_eq!(p.wait().err().unwrap().kind, TimedOut); - assert_eq!(p.wait().err().unwrap().kind, TimedOut); - p.signal_kill().unwrap(); - p.set_timeout(None); - assert!(p.wait().is_ok()); - } - - #[test] - fn wait_timeout2() { - let (tx, rx) = channel(); - let tx2 = tx.clone(); - let _t = Thread::spawn(move|| { - let mut p = sleeper(); - p.set_timeout(Some(10)); - assert_eq!(p.wait().err().unwrap().kind, TimedOut); - p.signal_kill().unwrap(); - tx.send(()).unwrap(); - }); - let _t = Thread::spawn(move|| { - let mut p = sleeper(); - p.set_timeout(Some(10)); - assert_eq!(p.wait().err().unwrap().kind, TimedOut); - p.signal_kill().unwrap(); - tx2.send(()).unwrap(); - }); - rx.recv().unwrap(); - rx.recv().unwrap(); - } - - #[test] - fn forget() { - let p = sleeper(); - let id = p.id(); - p.forget(); - assert!(Process::kill(id, 0).is_ok()); - assert!(Process::kill(id, PleaseExitSignal).is_ok()); - } - - #[test] - fn dont_close_fd_on_command_spawn() { - use sys::fs; - - let path = if cfg!(windows) { - Path::new("NUL") - } else { - Path::new("/dev/null") - }; - - let fdes = match fs::open(&path, Truncate, Write) { - Ok(f) => f, - Err(_) => panic!("failed to open file descriptor"), - }; - - let mut cmd = pwd_cmd(); - let _ = cmd.stdout(InheritFd(fdes.fd())); - assert!(cmd.status().unwrap().success()); - assert!(fdes.write("extra write\n".as_bytes()).is_ok()); - } - - #[test] - #[cfg(windows)] - fn env_map_keys_ci() { - use ffi::CString; - use super::EnvKey; - let mut cmd = Command::new(""); - cmd.env("path", "foo"); - cmd.env("Path", "bar"); - let env = &cmd.env.unwrap(); - let val = env.get(&EnvKey(CString::from_slice(b"PATH"))); - assert!(val.unwrap() == &CString::from_slice(b"bar")); - } -} diff --git a/src/libstd/io/result.rs b/src/libstd/io/result.rs deleted file mode 100644 index c1474650f1e..00000000000 --- a/src/libstd/io/result.rs +++ /dev/null @@ -1,129 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementations of I/O traits for the IoResult type -//! -//! I/O constructors return option types to allow errors to be handled. -//! These implementations allow e.g. `IoResult` to be used -//! as a `Reader` without unwrapping the result first. - -use clone::Clone; -use result::Result::{Ok, Err}; -use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle, IoResult}; - -impl Writer for IoResult { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - match *self { - Ok(ref mut writer) => writer.write(buf), - Err(ref e) => Err((*e).clone()) - } - } - - fn flush(&mut self) -> IoResult<()> { - match *self { - Ok(ref mut writer) => writer.flush(), - Err(ref e) => Err(e.clone()), - } - } -} - -impl Reader for IoResult { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - match *self { - Ok(ref mut reader) => reader.read(buf), - Err(ref e) => Err(e.clone()), - } - } -} - -impl Seek for IoResult { - fn tell(&self) -> IoResult { - match *self { - Ok(ref seeker) => seeker.tell(), - Err(ref e) => Err(e.clone()), - } - } - fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { - match *self { - Ok(ref mut seeker) => seeker.seek(pos, style), - Err(ref e) => Err(e.clone()) - } - } -} - -impl, L: Listener> Listener for IoResult { - fn listen(self) -> IoResult { - match self { - Ok(listener) => listener.listen(), - Err(e) => Err(e), - } - } -} - -impl> Acceptor for IoResult { - fn accept(&mut self) -> IoResult { - match *self { - Ok(ref mut acceptor) => acceptor.accept(), - Err(ref e) => Err(e.clone()), - } - } -} - -#[cfg(test)] -mod test { - use prelude::v1::*; - use super::super::mem::*; - use io; - - #[test] - fn test_option_writer() { - let mut writer: io::IoResult> = Ok(Vec::new()); - writer.write(&[0, 1, 2]).unwrap(); - writer.flush().unwrap(); - assert_eq!(writer.unwrap(), vec!(0, 1, 2)); - } - - #[test] - fn test_option_writer_error() { - let mut writer: io::IoResult> = - Err(io::standard_error(io::EndOfFile)); - - match writer.write(&[0, 0, 0]) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::EndOfFile), - } - match writer.flush() { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::EndOfFile), - } - } - - #[test] - fn test_option_reader() { - let mut reader: io::IoResult = - Ok(MemReader::new(vec!(0, 1, 2, 3))); - let mut buf = [0, 0]; - reader.read(&mut buf).unwrap(); - let b: &[_] = &[0, 1]; - assert_eq!(buf.as_slice(), b); - } - - #[test] - fn test_option_reader_error() { - let mut reader: io::IoResult = - Err(io::standard_error(io::EndOfFile)); - let mut buf = []; - - match reader.read(&mut buf) { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::EndOfFile), - } - } -} diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs deleted file mode 100644 index a5664b9f013..00000000000 --- a/src/libstd/io/stdio.rs +++ /dev/null @@ -1,566 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Non-blocking access to stdin, stdout, and stderr. -//! -//! This module provides bindings to the local event loop's TTY interface, using it -//! to offer synchronous but non-blocking versions of stdio. These handles can be -//! inspected for information about terminal dimensions or for related information -//! about the stream or terminal to which it is attached. -//! -//! # Example -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::io; -//! -//! let mut out = io::stdout(); -//! out.write(b"Hello, world!"); -//! ``` - -use self::StdSource::*; - -use boxed::Box; -use cell::RefCell; -use clone::Clone; -use failure::LOCAL_STDERR; -use fmt; -use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer, - standard_error, EndOfFile, LineBufferedWriter, BufferedReader}; -use marker::{Sync, Send}; -use libc; -use mem; -use option::Option; -use option::Option::{Some, None}; -use ops::{Deref, DerefMut, FnOnce}; -use ptr; -use result::Result::{Ok, Err}; -use rt; -use slice::SliceExt; -use str::StrExt; -use string::String; -use sys::{fs, tty}; -use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT}; -use uint; -use vec::Vec; - -// And so begins the tale of acquiring a uv handle to a stdio stream on all -// platforms in all situations. Our story begins by splitting the world into two -// categories, windows and unix. Then one day the creators of unix said let -// there be redirection! And henceforth there was redirection away from the -// console for standard I/O streams. -// -// After this day, the world split into four factions: -// -// 1. Unix with stdout on a terminal. -// 2. Unix with stdout redirected. -// 3. Windows with stdout on a terminal. -// 4. Windows with stdout redirected. -// -// Many years passed, and then one day the nation of libuv decided to unify this -// world. After months of toiling, uv created three ideas: TTY, Pipe, File. -// These three ideas propagated throughout the lands and the four great factions -// decided to settle among them. -// -// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon -// doing so, they even enhanced themselves further then their Pipe/File -// brethren, becoming the dominant powers. -// -// The group of 4, however, decided to work independently. They abandoned the -// common TTY belief throughout, and even abandoned the fledgling Pipe belief. -// The members of the 4th faction decided to only align themselves with File. -// -// tl;dr; TTY works on everything but when windows stdout is redirected, in that -// case pipe also doesn't work, but magically file does! -enum StdSource { - TTY(tty::TTY), - File(fs::FileDesc), -} - -fn src(fd: libc::c_int, _readable: bool, f: F) -> T where - F: FnOnce(StdSource) -> T, -{ - match tty::TTY::new(fd) { - Ok(tty) => f(TTY(tty)), - Err(_) => f(File(fs::FileDesc::new(fd, false))), - } -} - -thread_local! { - static LOCAL_STDOUT: RefCell>> = { - RefCell::new(None) - } -} - -struct RaceBox(BufferedReader); - -unsafe impl Send for RaceBox {} -unsafe impl Sync for RaceBox {} - -/// A synchronized wrapper around a buffered reader from stdin -#[derive(Clone)] -pub struct StdinReader { - inner: Arc>, -} - -unsafe impl Send for StdinReader {} -unsafe impl Sync for StdinReader {} - -/// A guard for exclusive access to `StdinReader`'s internal `BufferedReader`. -pub struct StdinReaderGuard<'a> { - inner: MutexGuard<'a, RaceBox>, -} - -impl<'a> Deref for StdinReaderGuard<'a> { - type Target = BufferedReader; - - fn deref(&self) -> &BufferedReader { - &self.inner.0 - } -} - -impl<'a> DerefMut for StdinReaderGuard<'a> { - fn deref_mut(&mut self) -> &mut BufferedReader { - &mut self.inner.0 - } -} - -impl StdinReader { - /// Locks the `StdinReader`, granting the calling thread exclusive access - /// to the underlying `BufferedReader`. - /// - /// This provides access to methods like `chars` and `lines`. - /// - /// # Examples - /// - /// ```rust - /// use std::io; - /// - /// for line in io::stdin().lock().lines() { - /// println!("{}", line.unwrap()); - /// } - /// ``` - pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> { - StdinReaderGuard { - inner: self.inner.lock().unwrap() - } - } - - /// Like `Buffer::read_line`. - /// - /// The read is performed atomically - concurrent read calls in other - /// threads will not interleave with this one. - pub fn read_line(&mut self) -> IoResult { - self.inner.lock().unwrap().0.read_line() - } - - /// Like `Buffer::read_until`. - /// - /// The read is performed atomically - concurrent read calls in other - /// threads will not interleave with this one. - pub fn read_until(&mut self, byte: u8) -> IoResult> { - self.inner.lock().unwrap().0.read_until(byte) - } - - /// Like `Buffer::read_char`. - /// - /// The read is performed atomically - concurrent read calls in other - /// threads will not interleave with this one. - pub fn read_char(&mut self) -> IoResult { - self.inner.lock().unwrap().0.read_char() - } -} - -impl Reader for StdinReader { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner.lock().unwrap().0.read(buf) - } - - // We have to manually delegate all of these because the default impls call - // read more than once and we don't want those calls to interleave (or - // incur the costs of repeated locking). - - fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult { - self.inner.lock().unwrap().0.read_at_least(min, buf) - } - - fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec) -> IoResult { - self.inner.lock().unwrap().0.push_at_least(min, len, buf) - } - - fn read_to_end(&mut self) -> IoResult> { - self.inner.lock().unwrap().0.read_to_end() - } - - fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult { - self.inner.lock().unwrap().0.read_le_uint_n(nbytes) - } - - fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult { - self.inner.lock().unwrap().0.read_be_uint_n(nbytes) - } -} - -/// Creates a new handle to the stdin of the current process. -/// -/// The returned handle is a wrapper around a global `BufferedReader` shared -/// by all threads. If buffered access is not desired, the `stdin_raw` function -/// is provided to provided unbuffered access to stdin. -/// -/// See `stdout()` for more notes about this function. -pub fn stdin() -> StdinReader { - // We're following the same strategy as kimundi's lazy_static library - static mut STDIN: *const StdinReader = 0 as *const StdinReader; - static ONCE: Once = ONCE_INIT; - - unsafe { - ONCE.call_once(|| { - // The default buffer capacity is 64k, but apparently windows doesn't like - // 64k reads on stdin. See #13304 for details, but the idea is that on - // windows we use a slightly smaller buffer that's been seen to be - // acceptable. - let stdin = if cfg!(windows) { - BufferedReader::with_capacity(8 * 1024, stdin_raw()) - } else { - BufferedReader::new(stdin_raw()) - }; - let stdin = StdinReader { - inner: Arc::new(Mutex::new(RaceBox(stdin))) - }; - STDIN = mem::transmute(box stdin); - - // Make sure to free it at exit - rt::at_exit(|| { - mem::transmute::<_, Box>(STDIN); - STDIN = ptr::null(); - }); - }); - - (*STDIN).clone() - } -} - -/// Creates a new non-blocking handle to the stdin of the current process. -/// -/// Unlike `stdin()`, the returned reader is *not* a buffered reader. -/// -/// See `stdout()` for more notes about this function. -pub fn stdin_raw() -> StdReader { - src(libc::STDIN_FILENO, true, |src| StdReader { inner: src }) -} - -/// Creates a line-buffered handle to the stdout of the current process. -/// -/// Note that this is a fairly expensive operation in that at least one memory -/// allocation is performed. Additionally, this must be called from a runtime -/// task context because the stream returned will be a non-blocking object using -/// the local scheduler to perform the I/O. -/// -/// Care should be taken when creating multiple handles to an output stream for -/// a single process. While usage is still safe, the output may be surprising if -/// no synchronization is performed to ensure a sane output. -pub fn stdout() -> LineBufferedWriter { - LineBufferedWriter::new(stdout_raw()) -} - -/// Creates an unbuffered handle to the stdout of the current process -/// -/// See notes in `stdout()` for more information. -pub fn stdout_raw() -> StdWriter { - src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src }) -} - -/// Creates a line-buffered handle to the stderr of the current process. -/// -/// See `stdout()` for notes about this function. -pub fn stderr() -> LineBufferedWriter { - LineBufferedWriter::new(stderr_raw()) -} - -/// Creates an unbuffered handle to the stderr of the current process -/// -/// See notes in `stdout()` for more information. -pub fn stderr_raw() -> StdWriter { - src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src }) -} - -/// Resets the task-local stdout handle to the specified writer -/// -/// This will replace the current task's stdout handle, returning the old -/// handle. All future calls to `print` and friends will emit their output to -/// this specified handle. -/// -/// Note that this does not need to be called for all new tasks; the default -/// output handle is to the process's stdout stream. -pub fn set_stdout(stdout: Box) -> Option> { - let mut new = Some(stdout); - LOCAL_STDOUT.with(|slot| { - mem::replace(&mut *slot.borrow_mut(), new.take()) - }).and_then(|mut s| { - let _ = s.flush(); - Some(s) - }) -} - -/// Resets the task-local stderr handle to the specified writer -/// -/// This will replace the current task's stderr handle, returning the old -/// handle. Currently, the stderr handle is used for printing panic messages -/// during task panic. -/// -/// Note that this does not need to be called for all new tasks; the default -/// output handle is to the process's stderr stream. -pub fn set_stderr(stderr: Box) -> Option> { - let mut new = Some(stderr); - LOCAL_STDERR.with(|slot| { - mem::replace(&mut *slot.borrow_mut(), new.take()) - }).and_then(|mut s| { - let _ = s.flush(); - Some(s) - }) -} - -// Helper to access the local task's stdout handle -// -// Note that this is not a safe function to expose because you can create an -// aliased pointer very easily: -// -// with_task_stdout(|io1| { -// with_task_stdout(|io2| { -// // io1 aliases io2 -// }) -// }) -fn with_task_stdout(f: F) where F: FnOnce(&mut Writer) -> IoResult<()> { - let mut my_stdout = LOCAL_STDOUT.with(|slot| { - slot.borrow_mut().take() - }).unwrap_or_else(|| { - box stdout() as Box - }); - let result = f(&mut *my_stdout); - let mut var = Some(my_stdout); - LOCAL_STDOUT.with(|slot| { - *slot.borrow_mut() = var.take(); - }); - match result { - Ok(()) => {} - Err(e) => panic!("failed printing to stdout: {:?}", e), - } -} - -/// Flushes the local task's stdout handle. -/// -/// By default, this stream is a line-buffering stream, so flushing may be -/// necessary to ensure that all output is printed to the screen (if there are -/// no newlines printed). -/// -/// Note that logging macros do not use this stream. Using the logging macros -/// will emit output to stderr, and while they are line buffered the log -/// messages are always terminated in a newline (no need to flush). -pub fn flush() { - with_task_stdout(|io| io.flush()) -} - -/// Prints a string to the stdout of the current process. No newline is emitted -/// after the string is printed. -pub fn print(s: &str) { - with_task_stdout(|io| io.write(s.as_bytes())) -} - -/// Prints a string to the stdout of the current process. A literal -/// `\n` character is printed to the console after the string. -pub fn println(s: &str) { - with_task_stdout(|io| { - io.write(s.as_bytes()).and_then(|()| io.write(&[b'\n'])) - }) -} - -/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible -/// with the `format_args!` macro. -pub fn print_args(fmt: fmt::Arguments) { - with_task_stdout(|io| write!(io, "{}", fmt)) -} - -/// Similar to `println`, but takes a `fmt::Arguments` structure to be -/// compatible with the `format_args!` macro. -pub fn println_args(fmt: fmt::Arguments) { - with_task_stdout(|io| writeln!(io, "{}", fmt)) -} - -/// Representation of a reader of a standard input stream -pub struct StdReader { - inner: StdSource -} - -impl StdReader { - /// Returns whether this stream is attached to a TTY instance or not. - pub fn isatty(&self) -> bool { - match self.inner { - TTY(..) => true, - File(..) => false, - } - } -} - -impl Reader for StdReader { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - let ret = match self.inner { - TTY(ref mut tty) => { - // Flush the task-local stdout so that weird issues like a - // print!'d prompt not being shown until after the user hits - // enter. - flush(); - tty.read(buf).map(|i| i as uint) - }, - File(ref mut file) => file.read(buf).map(|i| i as uint), - }; - match ret { - // When reading a piped stdin, libuv will return 0-length reads when - // stdin reaches EOF. For pretty much all other streams it will - // return an actual EOF error, but apparently for stdin it's a - // little different. Hence, here we convert a 0 length read to an - // end-of-file indicator so the caller knows to stop reading. - Ok(0) => { Err(standard_error(EndOfFile)) } - ret @ Ok(..) | ret @ Err(..) => ret, - } - } -} - -/// Representation of a writer to a standard output stream -pub struct StdWriter { - inner: StdSource -} - -unsafe impl Send for StdWriter {} -unsafe impl Sync for StdWriter {} - -impl StdWriter { - /// Gets the size of this output window, if possible. This is typically used - /// when the writer is attached to something like a terminal, this is used - /// to fetch the dimensions of the terminal. - /// - /// If successful, returns `Ok((width, height))`. - /// - /// # Error - /// - /// This function will return an error if the output stream is not actually - /// connected to a TTY instance, or if querying the TTY instance fails. - pub fn winsize(&mut self) -> IoResult<(int, int)> { - match self.inner { - TTY(ref mut tty) => { - tty.get_winsize() - } - File(..) => { - Err(IoError { - kind: OtherIoError, - desc: "stream is not a tty", - detail: None, - }) - } - } - } - - /// Controls whether this output stream is a "raw stream" or simply a normal - /// stream. - /// - /// # Error - /// - /// This function will return an error if the output stream is not actually - /// connected to a TTY instance, or if querying the TTY instance fails. - pub fn set_raw(&mut self, raw: bool) -> IoResult<()> { - match self.inner { - TTY(ref mut tty) => { - tty.set_raw(raw) - } - File(..) => { - Err(IoError { - kind: OtherIoError, - desc: "stream is not a tty", - detail: None, - }) - } - } - } - - /// Returns whether this stream is attached to a TTY instance or not. - pub fn isatty(&self) -> bool { - match self.inner { - TTY(..) => true, - File(..) => false, - } - } -} - -impl Writer for StdWriter { - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - // As with stdin on windows, stdout often can't handle writes of large - // sizes. For an example, see #14940. For this reason, chunk the output - // buffer on windows, but on unix we can just write the whole buffer all - // at once. - // - // For some other references, it appears that this problem has been - // encountered by others [1] [2]. We choose the number 8KB just because - // libuv does the same. - // - // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232 - // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html - let max_size = if cfg!(windows) {8192} else {uint::MAX}; - for chunk in buf.chunks(max_size) { - try!(match self.inner { - TTY(ref mut tty) => tty.write(chunk), - File(ref mut file) => file.write(chunk), - }) - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use prelude::v1::*; - - use super::*; - use sync::mpsc::channel; - use thread::Thread; - - #[test] - fn smoke() { - // Just make sure we can acquire handles - stdin(); - stdout(); - stderr(); - } - - #[test] - fn capture_stdout() { - use io::{ChanReader, ChanWriter}; - - let (tx, rx) = channel(); - let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); - let _t = Thread::spawn(move|| { - set_stdout(box w); - println!("hello!"); - }); - assert_eq!(r.read_to_string().unwrap(), "hello!\n"); - } - - #[test] - fn capture_stderr() { - use io::{ChanReader, ChanWriter, Reader}; - - let (tx, rx) = channel(); - let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); - let _t = Thread::spawn(move || -> () { - set_stderr(box w); - panic!("my special message"); - }); - let s = r.read_to_string().unwrap(); - assert!(s.contains("my special message")); - } -} diff --git a/src/libstd/io/tempfile.rs b/src/libstd/io/tempfile.rs deleted file mode 100644 index 394686be814..00000000000 --- a/src/libstd/io/tempfile.rs +++ /dev/null @@ -1,182 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Temporary files and directories - -use io::{fs, IoError, IoErrorKind, IoResult}; -use io; -use iter::{IteratorExt, range}; -use ops::Drop; -use option::Option; -use option::Option::{None, Some}; -use os; -use path::{Path, GenericPath}; -use rand::{Rng, thread_rng}; -use result::Result::{Ok, Err}; -use str::StrExt; -use string::String; - -/// A wrapper for a path to temporary directory implementing automatic -/// scope-based deletion. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::TempDir; -/// -/// { -/// // create a temporary directory -/// let tmpdir = match TempDir::new("myprefix") { -/// Ok(dir) => dir, -/// Err(e) => panic!("couldn't create temporary directory: {}", e) -/// }; -/// -/// // get the path of the temporary directory without affecting the wrapper -/// let tmppath = tmpdir.path(); -/// -/// println!("The path of temporary directory is {}", tmppath.display()); -/// -/// // the temporary directory is automatically removed when tmpdir goes -/// // out of scope at the end of the block -/// } -/// { -/// // create a temporary directory, this time using a custom path -/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") { -/// Ok(dir) => dir, -/// Err(e) => panic!("couldn't create temporary directory: {}", e) -/// }; -/// -/// // get the path of the temporary directory and disable automatic deletion in the wrapper -/// let tmppath = tmpdir.into_inner(); -/// -/// println!("The path of the not-so-temporary directory is {}", tmppath.display()); -/// -/// // the temporary directory is not removed here -/// // because the directory is detached from the wrapper -/// } -/// { -/// // create a temporary directory -/// let tmpdir = match TempDir::new("myprefix") { -/// Ok(dir) => dir, -/// Err(e) => panic!("couldn't create temporary directory: {}", e) -/// }; -/// -/// // close the temporary directory manually and check the result -/// match tmpdir.close() { -/// Ok(_) => println!("success!"), -/// Err(e) => panic!("couldn't remove temporary directory: {}", e) -/// }; -/// } -/// ``` -pub struct TempDir { - path: Option, - disarmed: bool -} - -// How many times should we (re)try finding an unused random name? It should be -// enough that an attacker will run out of luck before we run out of patience. -const NUM_RETRIES: u32 = 1 << 31; -// How many characters should we include in a random file name? It needs to -// be enough to dissuade an attacker from trying to preemptively create names -// of that length, but not so huge that we unnecessarily drain the random number -// generator of entropy. -const NUM_RAND_CHARS: uint = 12; - -impl TempDir { - /// Attempts to make a temporary directory inside of `tmpdir` whose name - /// will have the prefix `prefix`. The directory will be automatically - /// deleted once the returned wrapper is destroyed. - /// - /// If no directory can be created, `Err` is returned. - pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult { - if !tmpdir.is_absolute() { - let abs_tmpdir = try!(os::make_absolute(tmpdir)); - return TempDir::new_in(&abs_tmpdir, prefix); - } - - let mut rng = thread_rng(); - for _ in range(0, NUM_RETRIES) { - let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect(); - let leaf = if prefix.len() > 0 { - format!("{}.{}", prefix, suffix) - } else { - // If we're given an empty string for a prefix, then creating a - // directory starting with "." would lead to it being - // semi-invisible on some systems. - suffix - }; - let path = tmpdir.join(leaf); - match fs::mkdir(&path, io::USER_RWX) { - Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }), - Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (), - Err(e) => return Err(e) - } - } - - return Err(IoError{ - kind: IoErrorKind::PathAlreadyExists, - desc:"Exhausted", - detail: None}); - } - - /// Attempts to make a temporary directory inside of `os::tmpdir()` whose - /// name will have the prefix `prefix`. The directory will be automatically - /// deleted once the returned wrapper is destroyed. - /// - /// If no directory can be created, `Err` is returned. - pub fn new(prefix: &str) -> IoResult { - TempDir::new_in(&os::tmpdir(), prefix) - } - - /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper. - /// This discards the wrapper so that the automatic deletion of the - /// temporary directory is prevented. - pub fn into_inner(self) -> Path { - let mut tmpdir = self; - tmpdir.path.take().unwrap() - } - - /// Access the wrapped `std::path::Path` to the temporary directory. - pub fn path<'a>(&'a self) -> &'a Path { - self.path.as_ref().unwrap() - } - - /// Close and remove the temporary directory - /// - /// Although `TempDir` removes the directory on drop, in the destructor - /// any errors are ignored. To detect errors cleaning up the temporary - /// directory, call `close` instead. - pub fn close(mut self) -> IoResult<()> { - self.cleanup_dir() - } - - fn cleanup_dir(&mut self) -> IoResult<()> { - assert!(!self.disarmed); - self.disarmed = true; - match self.path { - Some(ref p) => { - fs::rmdir_recursive(p) - } - None => Ok(()) - } - } -} - -impl Drop for TempDir { - fn drop(&mut self) { - if !self.disarmed { - let _ = self.cleanup_dir(); - } - } -} - -// the tests for this module need to change the path using change_dir, -// and this doesn't play nicely with other tests so these unit tests are located -// in src/test/run-pass/tempfile.rs diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs deleted file mode 100644 index 6de466eb20b..00000000000 --- a/src/libstd/io/test.rs +++ /dev/null @@ -1,175 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Various utility functions useful for writing I/O tests - -use prelude::v1::*; - -use libc; -use os; -use std::io::net::ip::*; -use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - -/// Get a port number, starting at 9600, for use in tests -pub fn next_test_port() -> u16 { - static NEXT_OFFSET: AtomicUsize = ATOMIC_USIZE_INIT; - base_port() + NEXT_OFFSET.fetch_add(1, Ordering::Relaxed) as u16 -} - -// iOS has a pretty long tmpdir path which causes pipe creation -// to like: invalid argument: path must be smaller than SUN_LEN -fn next_test_unix_socket() -> String { - static COUNT: AtomicUsize = ATOMIC_USIZE_INIT; - // base port and pid are an attempt to be unique between multiple - // test-runners of different configurations running on one - // buildbot, the count is to be unique within this executable. - format!("rust-test-unix-path-{}-{}-{}", - base_port(), - unsafe {libc::getpid()}, - COUNT.fetch_add(1, Ordering::Relaxed)) -} - -/// Get a temporary path which could be the location of a unix socket -#[cfg(not(target_os = "ios"))] -pub fn next_test_unix() -> Path { - let string = next_test_unix_socket(); - if cfg!(unix) { - os::tmpdir().join(string) - } else { - Path::new(format!("{}{}", r"\\.\pipe\", string)) - } -} - -/// Get a temporary path which could be the location of a unix socket -#[cfg(target_os = "ios")] -pub fn next_test_unix() -> Path { - Path::new(format!("/var/tmp/{}", next_test_unix_socket())) -} - -/// Get a unique IPv4 localhost:port pair starting at 9600 -pub fn next_test_ip4() -> SocketAddr { - SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() } -} - -/// Get a unique IPv6 localhost:port pair starting at 9600 -pub fn next_test_ip6() -> SocketAddr { - SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() } -} - -/* -XXX: Welcome to MegaHack City. - -The bots run multiple builds at the same time, and these builds -all want to use ports. This function figures out which workspace -it is running in and assigns a port range based on it. -*/ -fn base_port() -> u16 { - - let base = 9600u16; - let range = 1000u16; - - let bases = [ - ("32-opt", base + range * 1), - ("32-nopt", base + range * 2), - ("64-opt", base + range * 3), - ("64-nopt", base + range * 4), - ("64-opt-vg", base + range * 5), - ("all-opt", base + range * 6), - ("snap3", base + range * 7), - ("dist", base + range * 8) - ]; - - // FIXME (#9639): This needs to handle non-utf8 paths - let path = os::getcwd().unwrap(); - let path_s = path.as_str().unwrap(); - - let mut final_base = base; - - for &(dir, base) in bases.iter() { - if path_s.contains(dir) { - final_base = base; - break; - } - } - - return final_base; -} - -/// Raises the file descriptor limit when running tests if necessary -pub fn raise_fd_limit() { - unsafe { darwin_fd_limit::raise_fd_limit() } -} - -/// darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the rlimit -/// maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low for our -/// multithreaded scheduler testing, depending on the number of cores available. -/// -/// This fixes issue #7772. -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[allow(non_camel_case_types)] -mod darwin_fd_limit { - use libc; - type rlim_t = libc::uint64_t; - #[repr(C)] - struct rlimit { - rlim_cur: rlim_t, - rlim_max: rlim_t - } - extern { - // name probably doesn't need to be mut, but the C function doesn't specify const - fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint, - oldp: *mut libc::c_void, oldlenp: *mut libc::size_t, - newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int; - fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int; - fn setrlimit(resource: libc::c_int, rlp: *const rlimit) -> libc::c_int; - } - static CTL_KERN: libc::c_int = 1; - static KERN_MAXFILESPERPROC: libc::c_int = 29; - static RLIMIT_NOFILE: libc::c_int = 8; - - pub unsafe fn raise_fd_limit() { - // The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc - // sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value. - use ptr::null_mut; - use mem::size_of_val; - use os::last_os_error; - - // Fetch the kern.maxfilesperproc value - let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC]; - let mut maxfiles: libc::c_int = 0; - let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; - if sysctl(&mut mib[0], 2, &mut maxfiles as *mut libc::c_int as *mut libc::c_void, &mut size, - null_mut(), 0) != 0 { - let err = last_os_error(); - panic!("raise_fd_limit: error calling sysctl: {}", err); - } - - // Fetch the current resource limits - let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0}; - if getrlimit(RLIMIT_NOFILE, &mut rlim) != 0 { - let err = last_os_error(); - panic!("raise_fd_limit: error calling getrlimit: {}", err); - } - - // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit - rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max); - - // Set our newly-increased resource limit - if setrlimit(RLIMIT_NOFILE, &rlim) != 0 { - let err = last_os_error(); - panic!("raise_fd_limit: error calling setrlimit: {}", err); - } - } -} - -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -mod darwin_fd_limit { - pub unsafe fn raise_fd_limit() {} -} diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs deleted file mode 100644 index 68ae7d0ff20..00000000000 --- a/src/libstd/io/timer.rs +++ /dev/null @@ -1,481 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Synchronous Timers -//! -//! This module exposes the functionality to create timers, block the current task, -//! and create receivers which will receive notifications after a period of time. - -// FIXME: These functions take Durations but only pass ms to the backend impls. - -use sync::mpsc::{Receiver, Sender, channel}; -use time::Duration; -use io::IoResult; -use sys::timer::Callback; -use sys::timer::Timer as TimerImp; - -/// A synchronous timer object -/// -/// Values of this type can be used to put the current task to sleep for a -/// period of time. Handles to this timer can also be created in the form of -/// receivers which will receive notifications over time. -/// -/// # Examples -/// -/// ``` -/// # fn foo() { -/// use std::io::Timer; -/// use std::time::Duration; -/// -/// let mut timer = Timer::new().unwrap(); -/// timer.sleep(Duration::milliseconds(10)); // block the task for awhile -/// -/// let timeout = timer.oneshot(Duration::milliseconds(10)); -/// // do some work -/// timeout.recv().unwrap(); // wait for the timeout to expire -/// -/// let periodic = timer.periodic(Duration::milliseconds(10)); -/// loop { -/// periodic.recv().unwrap(); -/// // this loop is only executed once every 10ms -/// } -/// # } -/// ``` -/// -/// If only sleeping is necessary, then a convenience API is provided through -/// the `io::timer` module. -/// -/// ``` -/// # fn foo() { -/// use std::io::timer; -/// use std::time::Duration; -/// -/// // Put this task to sleep for 5 seconds -/// timer::sleep(Duration::seconds(5)); -/// # } -/// ``` -pub struct Timer { - inner: TimerImp, -} - -struct TimerCallback { tx: Sender<()> } - -/// Sleep the current task for the specified duration. -/// -/// When provided a zero or negative `duration`, the function will -/// return immediately. -pub fn sleep(duration: Duration) { - let timer = Timer::new(); - let mut timer = timer.ok().expect("timer::sleep: could not create a Timer"); - - timer.sleep(duration) -} - -impl Timer { - /// Creates a new timer which can be used to put the current task to sleep - /// for a number of milliseconds, or to possibly create channels which will - /// get notified after an amount of time has passed. - pub fn new() -> IoResult { - TimerImp::new().map(|t| Timer { inner: t }) - } - - /// Blocks the current task for the specified duration. - /// - /// Note that this function will cause any other receivers for this timer to - /// be invalidated (the other end will be closed). - /// - /// When provided a zero or negative `duration`, the function will - /// return immediately. - pub fn sleep(&mut self, duration: Duration) { - // Short-circuit the timer backend for 0 duration - let ms = in_ms_u64(duration); - if ms == 0 { return } - self.inner.sleep(ms); - } - - /// Creates a oneshot receiver which will have a notification sent when - /// the specified duration has elapsed. - /// - /// This does *not* block the current task, but instead returns immediately. - /// - /// Note that this invalidates any previous receiver which has been created - /// by this timer, and that the returned receiver will be invalidated once - /// the timer is destroyed (when it falls out of scope). In particular, if - /// this is called in method-chaining style, the receiver will be - /// invalidated at the end of that statement, and all `recv` calls will - /// fail. - /// - /// # Example - /// - /// ```rust - /// use std::io::Timer; - /// use std::time::Duration; - /// - /// let mut timer = Timer::new().unwrap(); - /// let ten_milliseconds = timer.oneshot(Duration::milliseconds(10)); - /// - /// for _ in range(0u, 100) { /* do work */ } - /// - /// // blocks until 10 ms after the `oneshot` call - /// ten_milliseconds.recv().unwrap(); - /// ``` - /// - /// ```rust - /// use std::io::Timer; - /// use std::time::Duration; - /// - /// // Incorrect, method chaining-style: - /// let mut five_ms = Timer::new().unwrap().oneshot(Duration::milliseconds(5)); - /// // The timer object was destroyed, so this will always fail: - /// // five_ms.recv().unwrap() - /// ``` - /// - /// When provided a zero or negative `duration`, the message will - /// be sent immediately. - pub fn oneshot(&mut self, duration: Duration) -> Receiver<()> { - let (tx, rx) = channel(); - // Short-circuit the timer backend for 0 duration - if in_ms_u64(duration) != 0 { - self.inner.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx }); - } else { - tx.send(()).unwrap(); - } - return rx - } - - /// Creates a receiver which will have a continuous stream of notifications - /// being sent each time the specified duration has elapsed. - /// - /// This does *not* block the current task, but instead returns - /// immediately. The first notification will not be received immediately, - /// but rather after the first duration. - /// - /// Note that this invalidates any previous receiver which has been created - /// by this timer, and that the returned receiver will be invalidated once - /// the timer is destroyed (when it falls out of scope). In particular, if - /// this is called in method-chaining style, the receiver will be - /// invalidated at the end of that statement, and all `recv` calls will - /// fail. - /// - /// # Example - /// - /// ```rust - /// use std::io::Timer; - /// use std::time::Duration; - /// - /// let mut timer = Timer::new().unwrap(); - /// let ten_milliseconds = timer.periodic(Duration::milliseconds(10)); - /// - /// for _ in range(0u, 100) { /* do work */ } - /// - /// // blocks until 10 ms after the `periodic` call - /// ten_milliseconds.recv().unwrap(); - /// - /// for _ in range(0u, 100) { /* do work */ } - /// - /// // blocks until 20 ms after the `periodic` call (*not* 10ms after the - /// // previous `recv`) - /// ten_milliseconds.recv().unwrap(); - /// ``` - /// - /// ```rust - /// use std::io::Timer; - /// use std::time::Duration; - /// - /// // Incorrect, method chaining-style. - /// let mut five_ms = Timer::new().unwrap().periodic(Duration::milliseconds(5)); - /// // The timer object was destroyed, so this will always fail: - /// // five_ms.recv().unwrap() - /// ``` - /// - /// When provided a zero or negative `duration`, the messages will - /// be sent without delay. - pub fn periodic(&mut self, duration: Duration) -> Receiver<()> { - let ms = in_ms_u64(duration); - // FIXME: The backend implementations don't ever send a message - // if given a 0 ms duration. Temporarily using 1ms. It's - // not clear what use a 0ms period is anyway... - let ms = if ms == 0 { 1 } else { ms }; - let (tx, rx) = channel(); - self.inner.period(ms, box TimerCallback { tx: tx }); - return rx - } -} - -impl Callback for TimerCallback { - fn call(&mut self) { - let _ = self.tx.send(()); - } -} - -fn in_ms_u64(d: Duration) -> u64 { - let ms = d.num_milliseconds(); - if ms < 0 { return 0 }; - return ms as u64; -} - -#[cfg(test)] -mod test { - use super::Timer; - use thread::Thread; - use time::Duration; - - #[test] - fn test_timer_send() { - let mut timer = Timer::new().unwrap(); - Thread::spawn(move || timer.sleep(Duration::milliseconds(1))); - } - - #[test] - fn test_io_timer_sleep_simple() { - let mut timer = Timer::new().unwrap(); - timer.sleep(Duration::milliseconds(1)); - } - - #[test] - fn test_io_timer_sleep_oneshot() { - let mut timer = Timer::new().unwrap(); - timer.oneshot(Duration::milliseconds(1)).recv().unwrap(); - } - - #[test] - fn test_io_timer_sleep_oneshot_forget() { - let mut timer = Timer::new().unwrap(); - timer.oneshot(Duration::milliseconds(100000000)); - } - - #[test] - fn oneshot_twice() { - let mut timer = Timer::new().unwrap(); - let rx1 = timer.oneshot(Duration::milliseconds(10000)); - let rx = timer.oneshot(Duration::milliseconds(1)); - rx.recv().unwrap(); - assert!(rx1.recv().is_err()); - } - - #[test] - fn test_io_timer_oneshot_then_sleep() { - let mut timer = Timer::new().unwrap(); - let rx = timer.oneshot(Duration::milliseconds(100000000)); - timer.sleep(Duration::milliseconds(1)); // this should invalidate rx - - assert!(rx.recv().is_err()); - } - - #[test] - fn test_io_timer_sleep_periodic() { - let mut timer = Timer::new().unwrap(); - let rx = timer.periodic(Duration::milliseconds(1)); - rx.recv().unwrap(); - rx.recv().unwrap(); - rx.recv().unwrap(); - } - - #[test] - fn test_io_timer_sleep_periodic_forget() { - let mut timer = Timer::new().unwrap(); - timer.periodic(Duration::milliseconds(100000000)); - } - - #[test] - fn test_io_timer_sleep_standalone() { - super::sleep(Duration::milliseconds(1)) - } - - #[test] - fn oneshot() { - let mut timer = Timer::new().unwrap(); - - let rx = timer.oneshot(Duration::milliseconds(1)); - rx.recv().unwrap(); - assert!(rx.recv().is_err()); - - let rx = timer.oneshot(Duration::milliseconds(1)); - rx.recv().unwrap(); - assert!(rx.recv().is_err()); - } - - #[test] - fn test_override() { - let mut timer = Timer::new().unwrap(); - let orx = timer.oneshot(Duration::milliseconds(100)); - let prx = timer.periodic(Duration::milliseconds(100)); - timer.sleep(Duration::milliseconds(1)); - assert!(orx.recv().is_err()); - assert!(prx.recv().is_err()); - timer.oneshot(Duration::milliseconds(1)).recv().unwrap(); - } - - #[test] - fn period() { - let mut timer = Timer::new().unwrap(); - let rx = timer.periodic(Duration::milliseconds(1)); - rx.recv().unwrap(); - rx.recv().unwrap(); - let rx2 = timer.periodic(Duration::milliseconds(1)); - rx2.recv().unwrap(); - rx2.recv().unwrap(); - } - - #[test] - fn sleep() { - let mut timer = Timer::new().unwrap(); - timer.sleep(Duration::milliseconds(1)); - timer.sleep(Duration::milliseconds(1)); - } - - #[test] - #[should_fail] - fn oneshot_fail() { - let mut timer = Timer::new().unwrap(); - let _rx = timer.oneshot(Duration::milliseconds(1)); - panic!(); - } - - #[test] - #[should_fail] - fn period_fail() { - let mut timer = Timer::new().unwrap(); - let _rx = timer.periodic(Duration::milliseconds(1)); - panic!(); - } - - #[test] - #[should_fail] - fn normal_fail() { - let _timer = Timer::new().unwrap(); - panic!(); - } - - #[test] - fn closing_channel_during_drop_doesnt_kill_everything() { - // see issue #10375 - let mut timer = Timer::new().unwrap(); - let timer_rx = timer.periodic(Duration::milliseconds(1000)); - - Thread::spawn(move|| { - let _ = timer_rx.recv(); - }); - - // when we drop the TimerWatcher we're going to destroy the channel, - // which must wake up the task on the other end - } - - #[test] - fn reset_doesnt_switch_tasks() { - // similar test to the one above. - let mut timer = Timer::new().unwrap(); - let timer_rx = timer.periodic(Duration::milliseconds(1000)); - - Thread::spawn(move|| { - let _ = timer_rx.recv(); - }); - - timer.oneshot(Duration::milliseconds(1)); - } - - #[test] - fn reset_doesnt_switch_tasks2() { - // similar test to the one above. - let mut timer = Timer::new().unwrap(); - let timer_rx = timer.periodic(Duration::milliseconds(1000)); - - Thread::spawn(move|| { - let _ = timer_rx.recv(); - }); - - timer.sleep(Duration::milliseconds(1)); - } - - #[test] - fn sender_goes_away_oneshot() { - let rx = { - let mut timer = Timer::new().unwrap(); - timer.oneshot(Duration::milliseconds(1000)) - }; - assert!(rx.recv().is_err()); - } - - #[test] - fn sender_goes_away_period() { - let rx = { - let mut timer = Timer::new().unwrap(); - timer.periodic(Duration::milliseconds(1000)) - }; - assert!(rx.recv().is_err()); - } - - #[test] - fn receiver_goes_away_oneshot() { - let mut timer1 = Timer::new().unwrap(); - timer1.oneshot(Duration::milliseconds(1)); - let mut timer2 = Timer::new().unwrap(); - // while sleeping, the previous timer should fire and not have its - // callback do something terrible. - timer2.sleep(Duration::milliseconds(2)); - } - - #[test] - fn receiver_goes_away_period() { - let mut timer1 = Timer::new().unwrap(); - timer1.periodic(Duration::milliseconds(1)); - let mut timer2 = Timer::new().unwrap(); - // while sleeping, the previous timer should fire and not have its - // callback do something terrible. - timer2.sleep(Duration::milliseconds(2)); - } - - #[test] - fn sleep_zero() { - let mut timer = Timer::new().unwrap(); - timer.sleep(Duration::milliseconds(0)); - } - - #[test] - fn sleep_negative() { - let mut timer = Timer::new().unwrap(); - timer.sleep(Duration::milliseconds(-1000000)); - } - - #[test] - fn oneshot_zero() { - let mut timer = Timer::new().unwrap(); - let rx = timer.oneshot(Duration::milliseconds(0)); - rx.recv().unwrap(); - } - - #[test] - fn oneshot_negative() { - let mut timer = Timer::new().unwrap(); - let rx = timer.oneshot(Duration::milliseconds(-1000000)); - rx.recv().unwrap(); - } - - #[test] - fn periodic_zero() { - let mut timer = Timer::new().unwrap(); - let rx = timer.periodic(Duration::milliseconds(0)); - rx.recv().unwrap(); - rx.recv().unwrap(); - rx.recv().unwrap(); - rx.recv().unwrap(); - } - - #[test] - fn periodic_negative() { - let mut timer = Timer::new().unwrap(); - let rx = timer.periodic(Duration::milliseconds(-1000000)); - rx.recv().unwrap(); - rx.recv().unwrap(); - rx.recv().unwrap(); - rx.recv().unwrap(); - } - -} diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs deleted file mode 100644 index e4bf38a9ef5..00000000000 --- a/src/libstd/io/util.rs +++ /dev/null @@ -1,444 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Utility implementations of Reader and Writer - -use prelude::v1::*; -use cmp; -use io; -use slice::bytes::MutableByteVector; - -/// Wraps a `Reader`, limiting the number of bytes that can be read from it. -#[derive(Show)] -pub struct LimitReader { - limit: uint, - inner: R -} - -impl LimitReader { - /// Creates a new `LimitReader` - pub fn new(r: R, limit: uint) -> LimitReader { - LimitReader { limit: limit, inner: r } - } - - /// Consumes the `LimitReader`, returning the underlying `Reader`. - pub fn into_inner(self) -> R { self.inner } - - /// Returns the number of bytes that can be read before the `LimitReader` - /// will return EOF. - /// - /// # Note - /// - /// The reader may reach EOF after reading fewer bytes than indicated by - /// this method if the underlying reader reaches EOF. - pub fn limit(&self) -> uint { self.limit } -} - -impl Reader for LimitReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - if self.limit == 0 { - return Err(io::standard_error(io::EndOfFile)); - } - - let len = cmp::min(self.limit, buf.len()); - let res = self.inner.read(&mut buf[..len]); - match res { - Ok(len) => self.limit -= len, - _ => {} - } - res - } -} - -impl Buffer for LimitReader { - fn fill_buf<'a>(&'a mut self) -> io::IoResult<&'a [u8]> { - let amt = try!(self.inner.fill_buf()); - let buf = &amt[..cmp::min(amt.len(), self.limit)]; - if buf.len() == 0 { - Err(io::standard_error(io::EndOfFile)) - } else { - Ok(buf) - } - } - - fn consume(&mut self, amt: uint) { - // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt, self.limit); - self.limit -= amt; - self.inner.consume(amt); - } - -} - -/// A `Writer` which ignores bytes written to it, like /dev/null. -#[derive(Copy, Show)] -pub struct NullWriter; - -impl Writer for NullWriter { - #[inline] - fn write(&mut self, _buf: &[u8]) -> io::IoResult<()> { Ok(()) } -} - -/// A `Reader` which returns an infinite stream of 0 bytes, like /dev/zero. -#[derive(Copy, Show)] -pub struct ZeroReader; - -impl Reader for ZeroReader { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - buf.set_memory(0); - Ok(buf.len()) - } -} - -impl Buffer for ZeroReader { - fn fill_buf<'a>(&'a mut self) -> io::IoResult<&'a [u8]> { - static DATA: [u8; 64] = [0; 64]; - Ok(DATA.as_slice()) - } - - fn consume(&mut self, _amt: uint) {} -} - -/// A `Reader` which is always at EOF, like /dev/null. -#[derive(Copy, Show)] -pub struct NullReader; - -impl Reader for NullReader { - #[inline] - fn read(&mut self, _buf: &mut [u8]) -> io::IoResult { - Err(io::standard_error(io::EndOfFile)) - } -} - -impl Buffer for NullReader { - fn fill_buf<'a>(&'a mut self) -> io::IoResult<&'a [u8]> { - Err(io::standard_error(io::EndOfFile)) - } - fn consume(&mut self, _amt: uint) {} -} - -/// A `Writer` which multiplexes writes to a set of `Writer`s. -/// -/// The `Writer`s are delegated to in order. If any `Writer` returns an error, -/// that error is returned immediately and remaining `Writer`s are not called. -#[derive(Show)] -pub struct MultiWriter { - writers: Vec -} - -impl MultiWriter where W: Writer { - /// Creates a new `MultiWriter` - pub fn new(writers: Vec) -> MultiWriter { - MultiWriter { writers: writers } - } -} - -impl Writer for MultiWriter where W: Writer { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::IoResult<()> { - for writer in self.writers.iter_mut() { - try!(writer.write(buf)); - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::IoResult<()> { - for writer in self.writers.iter_mut() { - try!(writer.flush()); - } - Ok(()) - } -} - -/// A `Reader` which chains input from multiple `Reader`s, reading each to -/// completion before moving onto the next. -#[derive(Clone, Show)] -pub struct ChainedReader { - readers: I, - cur_reader: Option, -} - -impl> ChainedReader { - /// Creates a new `ChainedReader` - pub fn new(mut readers: I) -> ChainedReader { - let r = readers.next(); - ChainedReader { readers: readers, cur_reader: r } - } -} - -impl> Reader for ChainedReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - loop { - let err = match self.cur_reader { - Some(ref mut r) => { - match r.read(buf) { - Ok(len) => return Ok(len), - Err(ref e) if e.kind == io::EndOfFile => None, - Err(e) => Some(e), - } - } - None => break - }; - self.cur_reader = self.readers.next(); - match err { - Some(e) => return Err(e), - None => {} - } - } - Err(io::standard_error(io::EndOfFile)) - } -} - -/// A `Reader` which forwards input from another `Reader`, passing it along to -/// a `Writer` as well. Similar to the `tee(1)` command. -#[derive(Show)] -pub struct TeeReader { - reader: R, - writer: W, -} - -impl TeeReader { - /// Creates a new `TeeReader` - pub fn new(r: R, w: W) -> TeeReader { - TeeReader { reader: r, writer: w } - } - - /// Consumes the `TeeReader`, returning the underlying `Reader` and - /// `Writer`. - pub fn into_inner(self) -> (R, W) { - let TeeReader { reader, writer } = self; - (reader, writer) - } -} - -impl Reader for TeeReader { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - self.reader.read(buf).and_then(|len| { - self.writer.write(&mut buf[..len]).map(|()| len) - }) - } -} - -/// Copies all data from a `Reader` to a `Writer`. -pub fn copy(r: &mut R, w: &mut W) -> io::IoResult<()> { - let mut buf = [0; super::DEFAULT_BUF_SIZE]; - loop { - let len = match r.read(&mut buf) { - Ok(len) => len, - Err(ref e) if e.kind == io::EndOfFile => return Ok(()), - Err(e) => return Err(e), - }; - try!(w.write(&buf[..len])); - } -} - -/// An adaptor converting an `Iterator` to a `Reader`. -#[derive(Clone, Show)] -pub struct IterReader { - iter: T, -} - -impl> IterReader { - /// Creates a new `IterReader` which will read from the specified - /// `Iterator`. - pub fn new(iter: T) -> IterReader { - IterReader { iter: iter } - } -} - -impl> Reader for IterReader { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - let mut len = 0; - for (slot, elt) in buf.iter_mut().zip(self.iter.by_ref()) { - *slot = elt; - len += 1; - } - if len == 0 && buf.len() != 0 { - Err(io::standard_error(io::EndOfFile)) - } else { - Ok(len) - } - } -} - -#[cfg(test)] -mod test { - use prelude::v1::*; - - use io::{MemReader, ByRefReader}; - use io; - use super::*; - - #[test] - fn test_limit_reader_unlimited() { - let mut r = MemReader::new(vec!(0, 1, 2)); - { - let mut r = LimitReader::new(r.by_ref(), 4); - assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap()); - } - } - - #[test] - fn test_limit_reader_limited() { - let mut r = MemReader::new(vec!(0, 1, 2)); - { - let mut r = LimitReader::new(r.by_ref(), 2); - assert_eq!(vec!(0, 1), r.read_to_end().unwrap()); - } - assert_eq!(vec!(2), r.read_to_end().unwrap()); - } - - #[test] - fn test_limit_reader_limit() { - let r = MemReader::new(vec!(0, 1, 2)); - let mut r = LimitReader::new(r, 3); - assert_eq!(3, r.limit()); - assert_eq!(0, r.read_byte().unwrap()); - assert_eq!(2, r.limit()); - assert_eq!(vec!(1, 2), r.read_to_end().unwrap()); - assert_eq!(0, r.limit()); - } - - #[test] - fn test_limit_reader_overlong_consume() { - let mut r = MemReader::new(vec![0, 1, 2, 3, 4, 5]); - let mut r = LimitReader::new(r.by_ref(), 1); - r.consume(2); - assert_eq!(vec![], r.read_to_end().unwrap()); - } - - #[test] - fn test_null_writer() { - let mut s = NullWriter; - let buf = vec![0, 0, 0]; - s.write(buf.as_slice()).unwrap(); - s.flush().unwrap(); - } - - #[test] - fn test_zero_reader() { - let mut s = ZeroReader; - let mut buf = vec![1, 2, 3]; - assert_eq!(s.read(buf.as_mut_slice()), Ok(3)); - assert_eq!(vec![0, 0, 0], buf); - } - - #[test] - fn test_null_reader() { - let mut r = NullReader; - let mut buf = vec![0]; - assert!(r.read(buf.as_mut_slice()).is_err()); - } - - #[test] - fn test_multi_writer() { - static mut writes: uint = 0; - static mut flushes: uint = 0; - - struct TestWriter; - impl Writer for TestWriter { - fn write(&mut self, _buf: &[u8]) -> io::IoResult<()> { - unsafe { writes += 1 } - Ok(()) - } - - fn flush(&mut self) -> io::IoResult<()> { - unsafe { flushes += 1 } - Ok(()) - } - } - - let mut multi = MultiWriter::new(vec!(box TestWriter as Box, - box TestWriter as Box)); - multi.write(&[1, 2, 3]).unwrap(); - assert_eq!(2, unsafe { writes }); - assert_eq!(0, unsafe { flushes }); - multi.flush().unwrap(); - assert_eq!(2, unsafe { writes }); - assert_eq!(2, unsafe { flushes }); - } - - #[test] - fn test_chained_reader() { - let rs = vec!(MemReader::new(vec!(0, 1)), MemReader::new(vec!()), - MemReader::new(vec!(2, 3))); - let mut r = ChainedReader::new(rs.into_iter()); - assert_eq!(vec!(0, 1, 2, 3), r.read_to_end().unwrap()); - } - - #[test] - fn test_tee_reader() { - let mut r = TeeReader::new(MemReader::new(vec!(0, 1, 2)), - Vec::new()); - assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap()); - let (_, w) = r.into_inner(); - assert_eq!(vec!(0, 1, 2), w); - } - - #[test] - fn test_copy() { - let mut r = MemReader::new(vec!(0, 1, 2, 3, 4)); - let mut w = Vec::new(); - copy(&mut r, &mut w).unwrap(); - assert_eq!(vec!(0, 1, 2, 3, 4), w); - } - - #[test] - fn limit_reader_buffer() { - let r = &mut b"0123456789\n0123456789\n"; - { - let mut r = LimitReader::new(r.by_ref(), 3); - assert_eq!(r.read_line(), Ok("012".to_string())); - assert_eq!(r.limit(), 0); - assert_eq!(r.read_line().err().unwrap().kind, io::EndOfFile); - } - { - let mut r = LimitReader::new(r.by_ref(), 9); - assert_eq!(r.read_line(), Ok("3456789\n".to_string())); - assert_eq!(r.limit(), 1); - assert_eq!(r.read_line(), Ok("0".to_string())); - } - { - let mut r = LimitReader::new(r.by_ref(), 100); - assert_eq!(r.read_char(), Ok('1')); - assert_eq!(r.limit(), 99); - assert_eq!(r.read_line(), Ok("23456789\n".to_string())); - } - } - - #[test] - fn test_iter_reader() { - let mut r = IterReader::new(range(0u8, 8)); - let mut buf = [0, 0, 0]; - let len = r.read(&mut buf).unwrap(); - assert_eq!(len, 3); - assert!(buf == [0, 1, 2]); - - let len = r.read(&mut buf).unwrap(); - assert_eq!(len, 3); - assert!(buf == [3, 4, 5]); - - let len = r.read(&mut buf).unwrap(); - assert_eq!(len, 2); - assert!(buf == [6, 7, 5]); - - assert_eq!(r.read(&mut buf).unwrap_err().kind, io::EndOfFile); - } - - #[test] - fn iter_reader_zero_length() { - let mut r = IterReader::new(range(0u8, 8)); - let mut buf = []; - assert_eq!(Ok(0), r.read(&mut buf)); - } -} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9bfc15f1438..119e7c50196 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -79,7 +79,8 @@ //! memory types, including [`atomic`](sync/atomic/index.html). //! //! Common types of I/O, including files, TCP, UDP, pipes, Unix domain sockets, -//! timers, and process spawning, are defined in the [`io`](io/index.html) module. +//! timers, and process spawning, are defined in the +//! [`old_io`](old_io/index.html) module. //! //! Rust's I/O and concurrency depends on a small runtime interface //! that lives, along with its support code, in mod [`rt`](rt/index.html). @@ -239,7 +240,7 @@ pub mod thread_local; pub mod dynamic_lib; pub mod ffi; pub mod fmt; -pub mod io; +pub mod old_io; pub mod os; pub mod path; pub mod rand; @@ -284,7 +285,7 @@ mod std { pub use sync; // used for select!() pub use error; // used for try!() pub use fmt; // used for any formatting strings - pub use io; // used for println!() + pub use old_io; // used for println!() pub use option; // used for bitflags!{} pub use rt; // used for panic!() pub use vec; // used for vec![] diff --git a/src/libstd/old_io/buffered.rs b/src/libstd/old_io/buffered.rs new file mode 100644 index 00000000000..c04af865af8 --- /dev/null +++ b/src/libstd/old_io/buffered.rs @@ -0,0 +1,697 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// ignore-lexer-test FIXME #15883 + +//! Buffering wrappers for I/O traits + +use cmp; +use fmt; +use old_io::{Reader, Writer, Stream, Buffer, DEFAULT_BUF_SIZE, IoResult}; +use iter::{IteratorExt, ExactSizeIterator, repeat}; +use ops::Drop; +use option::Option; +use option::Option::{Some, None}; +use result::Result::Ok; +use slice::{SliceExt}; +use slice; +use vec::Vec; + +/// Wraps a Reader and buffers input from it +/// +/// It can be excessively inefficient to work directly with a `Reader`. For +/// example, every call to `read` on `TcpStream` results in a system call. A +/// `BufferedReader` performs large, infrequent reads on the underlying +/// `Reader` and maintains an in-memory buffer of the results. +/// +/// # Example +/// +/// ```rust +/// use std::old_io::{BufferedReader, File}; +/// +/// let file = File::open(&Path::new("message.txt")); +/// let mut reader = BufferedReader::new(file); +/// +/// let mut buf = [0; 100]; +/// match reader.read(&mut buf) { +/// Ok(nread) => println!("Read {} bytes", nread), +/// Err(e) => println!("error reading: {}", e) +/// } +/// ``` +pub struct BufferedReader { + inner: R, + buf: Vec, + pos: uint, + cap: uint, +} + +#[stable] +impl fmt::Debug for BufferedReader where R: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "BufferedReader {{ reader: {:?}, buffer: {}/{} }}", + self.inner, self.cap - self.pos, self.buf.len()) + } +} + +impl BufferedReader { + /// Creates a new `BufferedReader` with the specified buffer capacity + pub fn with_capacity(cap: uint, inner: R) -> BufferedReader { + BufferedReader { + inner: inner, + // We can't use the same trick here as we do for BufferedWriter, + // since this memory is visible to the inner Reader. + buf: repeat(0).take(cap).collect(), + pos: 0, + cap: 0, + } + } + + /// Creates a new `BufferedReader` with a default buffer capacity + pub fn new(inner: R) -> BufferedReader { + BufferedReader::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Gets a reference to the underlying reader. + pub fn get_ref<'a>(&self) -> &R { &self.inner } + + /// Gets a mutable reference to the underlying reader. + /// + /// # Warning + /// + /// It is inadvisable to directly read from the underlying reader. + pub fn get_mut(&mut self) -> &mut R { &mut self.inner } + + /// Unwraps this `BufferedReader`, returning the underlying reader. + /// + /// Note that any leftover data in the internal buffer is lost. + pub fn into_inner(self) -> R { self.inner } +} + +impl Buffer for BufferedReader { + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { + if self.pos == self.cap { + self.cap = try!(self.inner.read(self.buf.as_mut_slice())); + self.pos = 0; + } + Ok(&self.buf[self.pos..self.cap]) + } + + fn consume(&mut self, amt: uint) { + self.pos += amt; + assert!(self.pos <= self.cap); + } +} + +impl Reader for BufferedReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + if self.pos == self.cap && buf.len() >= self.buf.capacity() { + return self.inner.read(buf); + } + let nread = { + let available = try!(self.fill_buf()); + let nread = cmp::min(available.len(), buf.len()); + slice::bytes::copy_memory(buf, &available[..nread]); + nread + }; + self.pos += nread; + Ok(nread) + } +} + +/// Wraps a Writer and buffers output to it +/// +/// It can be excessively inefficient to work directly with a `Writer`. For +/// example, every call to `write` on `TcpStream` results in a system call. A +/// `BufferedWriter` keeps an in memory buffer of data and writes it to the +/// underlying `Writer` in large, infrequent batches. +/// +/// This writer will be flushed when it is dropped. +/// +/// # Example +/// +/// ```rust +/// use std::old_io::{BufferedWriter, File}; +/// +/// let file = File::create(&Path::new("message.txt")).unwrap(); +/// let mut writer = BufferedWriter::new(file); +/// +/// writer.write_str("hello, world").unwrap(); +/// writer.flush().unwrap(); +/// ``` +pub struct BufferedWriter { + inner: Option, + buf: Vec, + pos: uint +} + +#[stable] +impl fmt::Debug for BufferedWriter where W: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "BufferedWriter {{ writer: {:?}, buffer: {}/{} }}", + self.inner.as_ref().unwrap(), self.pos, self.buf.len()) + } +} + +impl BufferedWriter { + /// Creates a new `BufferedWriter` with the specified buffer capacity + pub fn with_capacity(cap: uint, inner: W) -> BufferedWriter { + // It's *much* faster to create an uninitialized buffer than it is to + // fill everything in with 0. This buffer is entirely an implementation + // detail and is never exposed, so we're safe to not initialize + // everything up-front. This allows creation of BufferedWriter instances + // to be very cheap (large mallocs are not nearly as expensive as large + // callocs). + let mut buf = Vec::with_capacity(cap); + unsafe { buf.set_len(cap); } + BufferedWriter { + inner: Some(inner), + buf: buf, + pos: 0 + } + } + + /// Creates a new `BufferedWriter` with a default buffer capacity + pub fn new(inner: W) -> BufferedWriter { + BufferedWriter::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + fn flush_buf(&mut self) -> IoResult<()> { + if self.pos != 0 { + let ret = self.inner.as_mut().unwrap().write(&self.buf[..self.pos]); + self.pos = 0; + ret + } else { + Ok(()) + } + } + + /// Gets a reference to the underlying writer. + pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() } + + /// Gets a mutable reference to the underlying write. + /// + /// # Warning + /// + /// It is inadvisable to directly read from the underlying writer. + pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() } + + /// Unwraps this `BufferedWriter`, returning the underlying writer. + /// + /// The buffer is flushed before returning the writer. + pub fn into_inner(mut self) -> W { + // FIXME(#12628): is panicking the right thing to do if flushing panicks? + self.flush_buf().unwrap(); + self.inner.take().unwrap() + } +} + +impl Writer for BufferedWriter { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + if self.pos + buf.len() > self.buf.len() { + try!(self.flush_buf()); + } + + if buf.len() > self.buf.len() { + self.inner.as_mut().unwrap().write(buf) + } else { + let dst = &mut self.buf[self.pos..]; + slice::bytes::copy_memory(dst, buf); + self.pos += buf.len(); + Ok(()) + } + } + + fn flush(&mut self) -> IoResult<()> { + self.flush_buf().and_then(|()| self.inner.as_mut().unwrap().flush()) + } +} + +#[unsafe_destructor] +impl Drop for BufferedWriter { + fn drop(&mut self) { + if self.inner.is_some() { + // dtors should not panic, so we ignore a panicked flush + let _ = self.flush_buf(); + } + } +} + +/// Wraps a Writer and buffers output to it, flushing whenever a newline (`0x0a`, +/// `'\n'`) is detected. +/// +/// This writer will be flushed when it is dropped. +pub struct LineBufferedWriter { + inner: BufferedWriter, +} + +#[stable] +impl fmt::Debug for LineBufferedWriter where W: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "LineBufferedWriter {{ writer: {:?}, buffer: {}/{} }}", + self.inner.inner, self.inner.pos, self.inner.buf.len()) + } +} + +impl LineBufferedWriter { + /// Creates a new `LineBufferedWriter` + pub fn new(inner: W) -> LineBufferedWriter { + // Lines typically aren't that long, don't use a giant buffer + LineBufferedWriter { + inner: BufferedWriter::with_capacity(1024, inner) + } + } + + /// Gets a reference to the underlying writer. + /// + /// This type does not expose the ability to get a mutable reference to the + /// underlying reader because that could possibly corrupt the buffer. + pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() } + + /// Unwraps this `LineBufferedWriter`, returning the underlying writer. + /// + /// The internal buffer is flushed before returning the writer. + pub fn into_inner(self) -> W { self.inner.into_inner() } +} + +impl Writer for LineBufferedWriter { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + match buf.iter().rposition(|&b| b == b'\n') { + Some(i) => { + try!(self.inner.write(&buf[..i + 1])); + try!(self.inner.flush()); + try!(self.inner.write(&buf[i + 1..])); + Ok(()) + } + None => self.inner.write(buf), + } + } + + fn flush(&mut self) -> IoResult<()> { self.inner.flush() } +} + +struct InternalBufferedWriter(BufferedWriter); + +impl InternalBufferedWriter { + fn get_mut<'a>(&'a mut self) -> &'a mut BufferedWriter { + let InternalBufferedWriter(ref mut w) = *self; + return w; + } +} + +impl Reader for InternalBufferedWriter { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.get_mut().inner.as_mut().unwrap().read(buf) + } +} + +/// Wraps a Stream and buffers input and output to and from it. +/// +/// It can be excessively inefficient to work directly with a `Stream`. For +/// example, every call to `read` or `write` on `TcpStream` results in a system +/// call. A `BufferedStream` keeps in memory buffers of data, making large, +/// infrequent calls to `read` and `write` on the underlying `Stream`. +/// +/// The output half will be flushed when this stream is dropped. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::{BufferedStream, File}; +/// +/// let file = File::open(&Path::new("message.txt")); +/// let mut stream = BufferedStream::new(file); +/// +/// stream.write("hello, world".as_bytes()); +/// stream.flush(); +/// +/// let mut buf = [0; 100]; +/// match stream.read(&mut buf) { +/// Ok(nread) => println!("Read {} bytes", nread), +/// Err(e) => println!("error reading: {}", e) +/// } +/// ``` +pub struct BufferedStream { + inner: BufferedReader> +} + +#[stable] +impl fmt::Debug for BufferedStream where S: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let reader = &self.inner; + let writer = &self.inner.inner.0; + write!(fmt, "BufferedStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}", + writer.inner, + writer.pos, writer.buf.len(), + reader.cap - reader.pos, reader.buf.len()) + } +} + +impl BufferedStream { + /// Creates a new buffered stream with explicitly listed capacities for the + /// reader/writer buffer. + pub fn with_capacities(reader_cap: uint, writer_cap: uint, inner: S) + -> BufferedStream { + let writer = BufferedWriter::with_capacity(writer_cap, inner); + let internal_writer = InternalBufferedWriter(writer); + let reader = BufferedReader::with_capacity(reader_cap, + internal_writer); + BufferedStream { inner: reader } + } + + /// Creates a new buffered stream with the default reader/writer buffer + /// capacities. + pub fn new(inner: S) -> BufferedStream { + BufferedStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE, + inner) + } + + /// Gets a reference to the underlying stream. + pub fn get_ref(&self) -> &S { + let InternalBufferedWriter(ref w) = self.inner.inner; + w.get_ref() + } + + /// Gets a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read directly from or write directly to the + /// underlying stream. + pub fn get_mut(&mut self) -> &mut S { + let InternalBufferedWriter(ref mut w) = self.inner.inner; + w.get_mut() + } + + /// Unwraps this `BufferedStream`, returning the underlying stream. + /// + /// The internal buffer is flushed before returning the stream. Any leftover + /// data in the read buffer is lost. + pub fn into_inner(self) -> S { + let InternalBufferedWriter(w) = self.inner.inner; + w.into_inner() + } +} + +impl Buffer for BufferedStream { + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { self.inner.fill_buf() } + fn consume(&mut self, amt: uint) { self.inner.consume(amt) } +} + +impl Reader for BufferedStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner.read(buf) + } +} + +impl Writer for BufferedStream { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.inner.inner.get_mut().write(buf) + } + fn flush(&mut self) -> IoResult<()> { + self.inner.inner.get_mut().flush() + } +} + +#[cfg(test)] +mod test { + extern crate test; + use io; + use prelude::v1::*; + use super::*; + use super::super::{IoResult, EndOfFile}; + use super::super::mem::MemReader; + use self::test::Bencher; + + /// A type, free to create, primarily intended for benchmarking creation of + /// wrappers that, just for construction, don't need a Reader/Writer that + /// does anything useful. Is equivalent to `/dev/null` in semantics. + #[derive(Clone,PartialEq,PartialOrd)] + pub struct NullStream; + + impl Reader for NullStream { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + + impl Writer for NullStream { + fn write(&mut self, _: &[u8]) -> old_io::IoResult<()> { Ok(()) } + } + + /// A dummy reader intended at testing short-reads propagation. + pub struct ShortReader { + lengths: Vec, + } + + impl Reader for ShortReader { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult { + if self.lengths.is_empty() { + Err(old_io::standard_error(old_io::EndOfFile)) + } else { + Ok(self.lengths.remove(0)) + } + } + } + + #[test] + fn test_buffered_reader() { + let inner = MemReader::new(vec!(5, 6, 7, 0, 1, 2, 3, 4)); + let mut reader = BufferedReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(3), nread); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf, b); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(2), nread); + let b: &[_] = &[0, 1]; + assert_eq!(buf, b); + + let mut buf = [0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(1), nread); + let b: &[_] = &[2]; + assert_eq!(buf, b); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(Ok(1), nread); + let b: &[_] = &[3, 0, 0]; + assert_eq!(buf, b); + + let nread = reader.read(&mut buf); + assert_eq!(Ok(1), nread); + let b: &[_] = &[4, 0, 0]; + assert_eq!(buf, b); + + assert!(reader.read(&mut buf).is_err()); + } + + #[test] + fn test_buffered_writer() { + let inner = Vec::new(); + let mut writer = BufferedWriter::with_capacity(2, inner); + + writer.write(&[0, 1]).unwrap(); + let b: &[_] = &[]; + assert_eq!(&writer.get_ref()[], b); + + writer.write(&[2]).unwrap(); + let b: &[_] = &[0, 1]; + assert_eq!(&writer.get_ref()[], b); + + writer.write(&[3]).unwrap(); + assert_eq!(&writer.get_ref()[], b); + + writer.flush().unwrap(); + let a: &[_] = &[0, 1, 2, 3]; + assert_eq!(a, &writer.get_ref()[]); + + writer.write(&[4]).unwrap(); + writer.write(&[5]).unwrap(); + assert_eq!(a, &writer.get_ref()[]); + + writer.write(&[6]).unwrap(); + let a: &[_] = &[0, 1, 2, 3, 4, 5]; + assert_eq!(a, &writer.get_ref()[]); + + writer.write(&[7, 8]).unwrap(); + let a: &[_] = &[0, 1, 2, 3, 4, 5, 6]; + assert_eq!(a, &writer.get_ref()[]); + + writer.write(&[9, 10, 11]).unwrap(); + let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + assert_eq!(a, &writer.get_ref()[]); + + writer.flush().unwrap(); + assert_eq!(a, &writer.get_ref()[]); + } + + #[test] + fn test_buffered_writer_inner_flushes() { + let mut w = BufferedWriter::with_capacity(3, Vec::new()); + w.write(&[0, 1]).unwrap(); + let a: &[_] = &[]; + assert_eq!(a, &w.get_ref()[]); + let w = w.into_inner(); + let a: &[_] = &[0, 1]; + assert_eq!(a, &w[]); + } + + // This is just here to make sure that we don't infinite loop in the + // newtype struct autoderef weirdness + #[test] + fn test_buffered_stream() { + struct S; + + impl old_io::Writer for S { + fn write(&mut self, _: &[u8]) -> old_io::IoResult<()> { Ok(()) } + } + + impl old_io::Reader for S { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + + let mut stream = BufferedStream::new(S); + let mut buf = []; + assert!(stream.read(&mut buf).is_err()); + stream.write(&buf).unwrap(); + stream.flush().unwrap(); + } + + #[test] + fn test_read_until() { + let inner = MemReader::new(vec!(0, 1, 2, 1, 0)); + let mut reader = BufferedReader::with_capacity(2, inner); + assert_eq!(reader.read_until(0), Ok(vec!(0))); + assert_eq!(reader.read_until(2), Ok(vec!(1, 2))); + assert_eq!(reader.read_until(1), Ok(vec!(1))); + assert_eq!(reader.read_until(8), Ok(vec!(0))); + assert!(reader.read_until(9).is_err()); + } + + #[test] + fn test_line_buffer() { + let mut writer = LineBufferedWriter::new(Vec::new()); + writer.write(&[0]).unwrap(); + let b: &[_] = &[]; + assert_eq!(&writer.get_ref()[], b); + writer.write(&[1]).unwrap(); + assert_eq!(&writer.get_ref()[], b); + writer.flush().unwrap(); + let b: &[_] = &[0, 1]; + assert_eq!(&writer.get_ref()[], b); + writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); + let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n']; + assert_eq!(&writer.get_ref()[], b); + writer.flush().unwrap(); + let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2]; + assert_eq!(&writer.get_ref()[], b); + writer.write(&[3, b'\n']).unwrap(); + let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']; + assert_eq!(&writer.get_ref()[], b); + } + + #[test] + fn test_read_line() { + let in_buf = MemReader::new(b"a\nb\nc".to_vec()); + let mut reader = BufferedReader::with_capacity(2, in_buf); + assert_eq!(reader.read_line(), Ok("a\n".to_string())); + assert_eq!(reader.read_line(), Ok("b\n".to_string())); + assert_eq!(reader.read_line(), Ok("c".to_string())); + assert!(reader.read_line().is_err()); + } + + #[test] + fn test_lines() { + let in_buf = MemReader::new(b"a\nb\nc".to_vec()); + let mut reader = BufferedReader::with_capacity(2, in_buf); + let mut it = reader.lines(); + assert_eq!(it.next(), Some(Ok("a\n".to_string()))); + assert_eq!(it.next(), Some(Ok("b\n".to_string()))); + assert_eq!(it.next(), Some(Ok("c".to_string()))); + assert_eq!(it.next(), None); + } + + #[test] + fn test_short_reads() { + let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]}; + let mut reader = BufferedReader::new(inner); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.read(&mut buf), Ok(2)); + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.read(&mut buf), Ok(0)); + assert!(reader.read(&mut buf).is_err()); + } + + #[test] + fn read_char_buffered() { + let buf = [195u8, 159u8]; + let mut reader = BufferedReader::with_capacity(1, &buf[]); + assert_eq!(reader.read_char(), Ok('ß')); + } + + #[test] + fn test_chars() { + let buf = [195u8, 159u8, b'a']; + let mut reader = BufferedReader::with_capacity(1, &buf[]); + let mut it = reader.chars(); + assert_eq!(it.next(), Some(Ok('ß'))); + assert_eq!(it.next(), Some(Ok('a'))); + assert_eq!(it.next(), None); + } + + #[test] + #[should_fail] + fn dont_panic_in_drop_on_panicked_flush() { + struct FailFlushWriter; + + impl Writer for FailFlushWriter { + fn write(&mut self, _buf: &[u8]) -> IoResult<()> { Ok(()) } + fn flush(&mut self) -> IoResult<()> { Err(old_io::standard_error(EndOfFile)) } + } + + let writer = FailFlushWriter; + let _writer = BufferedWriter::new(writer); + + // If writer panics *again* due to the flush error then the process will abort. + panic!(); + } + + #[bench] + fn bench_buffered_reader(b: &mut Bencher) { + b.iter(|| { + BufferedReader::new(NullStream) + }); + } + + #[bench] + fn bench_buffered_writer(b: &mut Bencher) { + b.iter(|| { + BufferedWriter::new(NullStream) + }); + } + + #[bench] + fn bench_buffered_stream(b: &mut Bencher) { + b.iter(|| { + BufferedStream::new(NullStream); + }); + } +} diff --git a/src/libstd/old_io/comm_adapters.rs b/src/libstd/old_io/comm_adapters.rs new file mode 100644 index 00000000000..3c684e4cc6a --- /dev/null +++ b/src/libstd/old_io/comm_adapters.rs @@ -0,0 +1,248 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use clone::Clone; +use cmp; +use sync::mpsc::{Sender, Receiver}; +use old_io; +use option::Option::{None, Some}; +use result::Result::{Ok, Err}; +use slice::{bytes, SliceExt}; +use super::{Buffer, Reader, Writer, IoResult}; +use vec::Vec; + +/// Allows reading from a rx. +/// +/// # Example +/// +/// ``` +/// use std::sync::mpsc::channel; +/// use std::old_io::ChanReader; +/// +/// let (tx, rx) = channel(); +/// # drop(tx); +/// let mut reader = ChanReader::new(rx); +/// +/// let mut buf = [0u8; 100]; +/// match reader.read(&mut buf) { +/// Ok(nread) => println!("Read {} bytes", nread), +/// Err(e) => println!("read error: {}", e), +/// } +/// ``` +pub struct ChanReader { + buf: Vec, // A buffer of bytes received but not consumed. + pos: uint, // How many of the buffered bytes have already be consumed. + rx: Receiver>, // The Receiver to pull data from. + closed: bool, // Whether the channel this Receiver connects to has been closed. +} + +impl ChanReader { + /// Wraps a `Port` in a `ChanReader` structure + pub fn new(rx: Receiver>) -> ChanReader { + ChanReader { + buf: Vec::new(), + pos: 0, + rx: rx, + closed: false, + } + } +} + +impl Buffer for ChanReader { + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { + if self.pos >= self.buf.len() { + self.pos = 0; + match self.rx.recv() { + Ok(bytes) => { + self.buf = bytes; + }, + Err(..) => { + self.closed = true; + self.buf = Vec::new(); + } + } + } + if self.closed { + Err(old_io::standard_error(old_io::EndOfFile)) + } else { + Ok(&self.buf[self.pos..]) + } + } + + fn consume(&mut self, amt: uint) { + self.pos += amt; + assert!(self.pos <= self.buf.len()); + } +} + +impl Reader for ChanReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + let mut num_read = 0; + loop { + let count = match self.fill_buf().ok() { + Some(src) => { + let dst = &mut buf[num_read..]; + let count = cmp::min(src.len(), dst.len()); + bytes::copy_memory(dst, &src[..count]); + count + }, + None => 0, + }; + self.consume(count); + num_read += count; + if num_read == buf.len() || self.closed { + break; + } + } + if self.closed && num_read == 0 { + Err(old_io::standard_error(old_io::EndOfFile)) + } else { + Ok(num_read) + } + } +} + +/// Allows writing to a tx. +/// +/// # Example +/// +/// ``` +/// # #![allow(unused_must_use)] +/// use std::sync::mpsc::channel; +/// use std::old_io::ChanWriter; +/// +/// let (tx, rx) = channel(); +/// # drop(rx); +/// let mut writer = ChanWriter::new(tx); +/// writer.write("hello, world".as_bytes()); +/// ``` +pub struct ChanWriter { + tx: Sender>, +} + +impl ChanWriter { + /// Wraps a channel in a `ChanWriter` structure + pub fn new(tx: Sender>) -> ChanWriter { + ChanWriter { tx: tx } + } +} + +#[stable] +impl Clone for ChanWriter { + fn clone(&self) -> ChanWriter { + ChanWriter { tx: self.tx.clone() } + } +} + +impl Writer for ChanWriter { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.tx.send(buf.to_vec()).map_err(|_| { + old_io::IoError { + kind: old_io::BrokenPipe, + desc: "Pipe closed", + detail: None + } + }) + } +} + + +#[cfg(test)] +mod test { + use prelude::v1::*; + + use sync::mpsc::channel; + use super::*; + use io; + use thread::Thread; + + #[test] + fn test_rx_reader() { + let (tx, rx) = channel(); + Thread::spawn(move|| { + tx.send(vec![1u8, 2u8]).unwrap(); + tx.send(vec![]).unwrap(); + tx.send(vec![3u8, 4u8]).unwrap(); + tx.send(vec![5u8, 6u8]).unwrap(); + tx.send(vec![7u8, 8u8]).unwrap(); + }); + + let mut reader = ChanReader::new(rx); + let mut buf = [0u8; 3]; + + assert_eq!(Ok(0), reader.read(&mut [])); + + assert_eq!(Ok(3), reader.read(&mut buf)); + let a: &[u8] = &[1,2,3]; + assert_eq!(a, buf); + + assert_eq!(Ok(3), reader.read(&mut buf)); + let a: &[u8] = &[4,5,6]; + assert_eq!(a, buf); + + assert_eq!(Ok(2), reader.read(&mut buf)); + let a: &[u8] = &[7,8,6]; + assert_eq!(a, buf); + + match reader.read(buf.as_mut_slice()) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, old_io::EndOfFile), + } + assert_eq!(a, buf); + + // Ensure it continues to panic in the same way. + match reader.read(buf.as_mut_slice()) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, old_io::EndOfFile), + } + assert_eq!(a, buf); + } + + #[test] + fn test_rx_buffer() { + let (tx, rx) = channel(); + Thread::spawn(move|| { + tx.send(b"he".to_vec()).unwrap(); + tx.send(b"llo wo".to_vec()).unwrap(); + tx.send(b"".to_vec()).unwrap(); + tx.send(b"rld\nhow ".to_vec()).unwrap(); + tx.send(b"are you?".to_vec()).unwrap(); + tx.send(b"".to_vec()).unwrap(); + }); + + let mut reader = ChanReader::new(rx); + + assert_eq!(Ok("hello world\n".to_string()), reader.read_line()); + assert_eq!(Ok("how are you?".to_string()), reader.read_line()); + match reader.read_line() { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, old_io::EndOfFile), + } + } + + #[test] + fn test_chan_writer() { + let (tx, rx) = channel(); + let mut writer = ChanWriter::new(tx); + writer.write_be_u32(42).unwrap(); + + let wanted = vec![0u8, 0u8, 0u8, 42u8]; + let got = match Thread::scoped(move|| { rx.recv().unwrap() }).join() { + Ok(got) => got, + Err(_) => panic!(), + }; + assert_eq!(wanted, got); + + match writer.write_u8(1) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, old_io::BrokenPipe), + } + } +} diff --git a/src/libstd/old_io/extensions.rs b/src/libstd/old_io/extensions.rs new file mode 100644 index 00000000000..259242d0cd8 --- /dev/null +++ b/src/libstd/old_io/extensions.rs @@ -0,0 +1,562 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Utility mixins that apply to all Readers and Writers + +#![allow(missing_docs)] + +// FIXME: Not sure how this should be structured +// FIXME: Iteration should probably be considered separately + +use old_io::{IoError, IoResult, Reader}; +use old_io; +use iter::Iterator; +use num::Int; +use ops::FnOnce; +use option::Option; +use option::Option::{Some, None}; +use ptr::PtrExt; +use result::Result::{Ok, Err}; +use slice::{SliceExt, AsSlice}; + +/// An iterator that reads a single byte on each iteration, +/// until `.read_byte()` returns `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Bytes` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Error +/// +/// Any error other than `EndOfFile` that is produced by the underlying Reader +/// is returned by the iterator and should be handled by the caller. +pub struct Bytes<'r, T:'r> { + reader: &'r mut T, +} + +impl<'r, R: Reader> Bytes<'r, R> { + /// Constructs a new byte iterator from the given Reader instance. + pub fn new(r: &'r mut R) -> Bytes<'r, R> { + Bytes { + reader: r, + } + } +} + +impl<'r, R: Reader> Iterator for Bytes<'r, R> { + type Item = IoResult; + + #[inline] + fn next(&mut self) -> Option> { + match self.reader.read_byte() { + Ok(x) => Some(Ok(x)), + Err(IoError { kind: old_io::EndOfFile, .. }) => None, + Err(e) => Some(Err(e)) + } + } +} + +/// Converts an 8-bit to 64-bit unsigned value to a little-endian byte +/// representation of the given size. If the size is not big enough to +/// represent the value, then the high-order bytes are truncated. +/// +/// Arguments: +/// +/// * `n`: The value to convert. +/// * `size`: The size of the value, in bytes. This must be 8 or less, or task +/// panic occurs. If this is less than 8, then a value of that +/// many bytes is produced. For example, if `size` is 4, then a +/// 32-bit byte representation is produced. +/// * `f`: A callback that receives the value. +/// +/// This function returns the value returned by the callback, for convenience. +pub fn u64_to_le_bytes(n: u64, size: uint, f: F) -> T where + F: FnOnce(&[u8]) -> T, +{ + use mem::transmute; + + // LLVM fails to properly optimize this when using shifts instead of the to_le* intrinsics + assert!(size <= 8u); + match size { + 1u => f(&[n as u8]), + 2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_le()) }), + 4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_le()) }), + 8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_le()) }), + _ => { + + let mut bytes = vec!(); + let mut i = size; + let mut n = n; + while i > 0u { + bytes.push((n & 255_u64) as u8); + n >>= 8; + i -= 1u; + } + f(bytes.as_slice()) + } + } +} + +/// Converts an 8-bit to 64-bit unsigned value to a big-endian byte +/// representation of the given size. If the size is not big enough to +/// represent the value, then the high-order bytes are truncated. +/// +/// Arguments: +/// +/// * `n`: The value to convert. +/// * `size`: The size of the value, in bytes. This must be 8 or less, or task +/// panic occurs. If this is less than 8, then a value of that +/// many bytes is produced. For example, if `size` is 4, then a +/// 32-bit byte representation is produced. +/// * `f`: A callback that receives the value. +/// +/// This function returns the value returned by the callback, for convenience. +pub fn u64_to_be_bytes(n: u64, size: uint, f: F) -> T where + F: FnOnce(&[u8]) -> T, +{ + use mem::transmute; + + // LLVM fails to properly optimize this when using shifts instead of the to_be* intrinsics + assert!(size <= 8u); + match size { + 1u => f(&[n as u8]), + 2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_be()) }), + 4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_be()) }), + 8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_be()) }), + _ => { + let mut bytes = vec!(); + let mut i = size; + while i > 0u { + let shift = (i - 1u) * 8u; + bytes.push((n >> shift) as u8); + i -= 1u; + } + f(bytes.as_slice()) + } + } +} + +/// Extracts an 8-bit to 64-bit unsigned big-endian value from the given byte +/// buffer and returns it as a 64-bit value. +/// +/// Arguments: +/// +/// * `data`: The buffer in which to extract the value. +/// * `start`: The offset at which to extract the value. +/// * `size`: The size of the value in bytes to extract. This must be 8 or +/// less, or task panic occurs. If this is less than 8, then only +/// that many bytes are parsed. For example, if `size` is 4, then a +/// 32-bit value is parsed. +pub fn u64_from_be_bytes(data: &[u8], start: uint, size: uint) -> u64 { + use ptr::{copy_nonoverlapping_memory}; + use slice::SliceExt; + + assert!(size <= 8u); + + if data.len() - start < size { + panic!("index out of bounds"); + } + + let mut buf = [0u8; 8]; + unsafe { + let ptr = data.as_ptr().offset(start as int); + let out = buf.as_mut_ptr(); + copy_nonoverlapping_memory(out.offset((8 - size) as int), ptr, size); + (*(out as *const u64)).to_be() + } +} + +#[cfg(test)] +mod test { + use prelude::v1::*; + use io; + use old_io::{MemReader, BytesReader}; + + struct InitialZeroByteReader { + count: int, + } + + impl Reader for InitialZeroByteReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + if self.count == 0 { + self.count = 1; + Ok(0) + } else { + buf[0] = 10; + Ok(1) + } + } + } + + struct EofReader; + + impl Reader for EofReader { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + + struct ErroringReader; + + impl Reader for ErroringReader { + fn read(&mut self, _: &mut [u8]) -> old_io::IoResult { + Err(old_io::standard_error(old_io::InvalidInput)) + } + } + + struct PartialReader { + count: int, + } + + impl Reader for PartialReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + if self.count == 0 { + self.count = 1; + buf[0] = 10; + buf[1] = 11; + Ok(2) + } else { + buf[0] = 12; + buf[1] = 13; + Ok(2) + } + } + } + + struct ErroringLaterReader { + count: int, + } + + impl Reader for ErroringLaterReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + if self.count == 0 { + self.count = 1; + buf[0] = 10; + Ok(1) + } else { + Err(old_io::standard_error(old_io::InvalidInput)) + } + } + } + + struct ThreeChunkReader { + count: int, + } + + impl Reader for ThreeChunkReader { + fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult { + if self.count == 0 { + self.count = 1; + buf[0] = 10; + buf[1] = 11; + Ok(2) + } else if self.count == 1 { + self.count = 2; + buf[0] = 12; + buf[1] = 13; + Ok(2) + } else { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + } + + #[test] + fn read_byte() { + let mut reader = MemReader::new(vec!(10)); + let byte = reader.read_byte(); + assert!(byte == Ok(10)); + } + + #[test] + fn read_byte_0_bytes() { + let mut reader = InitialZeroByteReader { + count: 0, + }; + let byte = reader.read_byte(); + assert!(byte == Ok(10)); + } + + #[test] + fn read_byte_eof() { + let mut reader = EofReader; + let byte = reader.read_byte(); + assert!(byte.is_err()); + } + + #[test] + fn read_byte_error() { + let mut reader = ErroringReader; + let byte = reader.read_byte(); + assert!(byte.is_err()); + } + + #[test] + fn bytes_0_bytes() { + let mut reader = InitialZeroByteReader { + count: 0, + }; + let byte = reader.bytes().next(); + assert!(byte == Some(Ok(10))); + } + + #[test] + fn bytes_eof() { + let mut reader = EofReader; + let byte = reader.bytes().next(); + assert!(byte.is_none()); + } + + #[test] + fn bytes_error() { + let mut reader = ErroringReader; + let mut it = reader.bytes(); + let byte = it.next(); + assert!(byte.unwrap().is_err()); + } + + #[test] + fn read_bytes() { + let mut reader = MemReader::new(vec!(10, 11, 12, 13)); + let bytes = reader.read_exact(4).unwrap(); + assert!(bytes == vec!(10, 11, 12, 13)); + } + + #[test] + fn read_bytes_partial() { + let mut reader = PartialReader { + count: 0, + }; + let bytes = reader.read_exact(4).unwrap(); + assert!(bytes == vec!(10, 11, 12, 13)); + } + + #[test] + fn read_bytes_eof() { + let mut reader = MemReader::new(vec!(10, 11)); + assert!(reader.read_exact(4).is_err()); + } + + #[test] + fn push_at_least() { + let mut reader = MemReader::new(vec![10, 11, 12, 13]); + let mut buf = vec![8, 9]; + assert!(reader.push_at_least(4, 4, &mut buf).is_ok()); + assert!(buf == vec![8, 9, 10, 11, 12, 13]); + } + + #[test] + fn push_at_least_partial() { + let mut reader = PartialReader { + count: 0, + }; + let mut buf = vec![8, 9]; + assert!(reader.push_at_least(4, 4, &mut buf).is_ok()); + assert!(buf == vec![8, 9, 10, 11, 12, 13]); + } + + #[test] + fn push_at_least_eof() { + let mut reader = MemReader::new(vec![10, 11]); + let mut buf = vec![8, 9]; + assert!(reader.push_at_least(4, 4, &mut buf).is_err()); + assert!(buf == vec![8, 9, 10, 11]); + } + + #[test] + fn push_at_least_error() { + let mut reader = ErroringLaterReader { + count: 0, + }; + let mut buf = vec![8, 9]; + assert!(reader.push_at_least(4, 4, &mut buf).is_err()); + assert!(buf == vec![8, 9, 10]); + } + + #[test] + fn read_to_end() { + let mut reader = ThreeChunkReader { + count: 0, + }; + let buf = reader.read_to_end().unwrap(); + assert!(buf == vec!(10, 11, 12, 13)); + } + + #[test] + #[should_fail] + fn read_to_end_error() { + let mut reader = ThreeChunkReader { + count: 0, + }; + let buf = reader.read_to_end().unwrap(); + assert!(buf == vec!(10, 11)); + } + + #[test] + fn test_read_write_le_mem() { + let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX]; + + let mut writer = Vec::new(); + for i in uints.iter() { + writer.write_le_u64(*i).unwrap(); + } + + let mut reader = MemReader::new(writer); + for i in uints.iter() { + assert!(reader.read_le_u64().unwrap() == *i); + } + } + + + #[test] + fn test_read_write_be() { + let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX]; + + let mut writer = Vec::new(); + for i in uints.iter() { + writer.write_be_u64(*i).unwrap(); + } + + let mut reader = MemReader::new(writer); + for i in uints.iter() { + assert!(reader.read_be_u64().unwrap() == *i); + } + } + + #[test] + fn test_read_be_int_n() { + let ints = [::i32::MIN, -123456, -42, -5, 0, 1, ::i32::MAX]; + + let mut writer = Vec::new(); + for i in ints.iter() { + writer.write_be_i32(*i).unwrap(); + } + + let mut reader = MemReader::new(writer); + for i in ints.iter() { + // this tests that the sign extension is working + // (comparing the values as i32 would not test this) + assert!(reader.read_be_int_n(4).unwrap() == *i as i64); + } + } + + #[test] + fn test_read_f32() { + //big-endian floating-point 8.1250 + let buf = vec![0x41, 0x02, 0x00, 0x00]; + + let mut writer = Vec::new(); + writer.write(buf.as_slice()).unwrap(); + + let mut reader = MemReader::new(writer); + let f = reader.read_be_f32().unwrap(); + assert!(f == 8.1250); + } + + #[test] + fn test_read_write_f32() { + let f:f32 = 8.1250; + + let mut writer = Vec::new(); + writer.write_be_f32(f).unwrap(); + writer.write_le_f32(f).unwrap(); + + let mut reader = MemReader::new(writer); + assert!(reader.read_be_f32().unwrap() == 8.1250); + assert!(reader.read_le_f32().unwrap() == 8.1250); + } + + #[test] + fn test_u64_from_be_bytes() { + use super::u64_from_be_bytes; + + let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; + + // Aligned access + assert_eq!(u64_from_be_bytes(&buf, 0, 0), 0); + assert_eq!(u64_from_be_bytes(&buf, 0, 1), 0x01); + assert_eq!(u64_from_be_bytes(&buf, 0, 2), 0x0102); + assert_eq!(u64_from_be_bytes(&buf, 0, 3), 0x010203); + assert_eq!(u64_from_be_bytes(&buf, 0, 4), 0x01020304); + assert_eq!(u64_from_be_bytes(&buf, 0, 5), 0x0102030405); + assert_eq!(u64_from_be_bytes(&buf, 0, 6), 0x010203040506); + assert_eq!(u64_from_be_bytes(&buf, 0, 7), 0x01020304050607); + assert_eq!(u64_from_be_bytes(&buf, 0, 8), 0x0102030405060708); + + // Unaligned access + assert_eq!(u64_from_be_bytes(&buf, 1, 0), 0); + assert_eq!(u64_from_be_bytes(&buf, 1, 1), 0x02); + assert_eq!(u64_from_be_bytes(&buf, 1, 2), 0x0203); + assert_eq!(u64_from_be_bytes(&buf, 1, 3), 0x020304); + assert_eq!(u64_from_be_bytes(&buf, 1, 4), 0x02030405); + assert_eq!(u64_from_be_bytes(&buf, 1, 5), 0x0203040506); + assert_eq!(u64_from_be_bytes(&buf, 1, 6), 0x020304050607); + assert_eq!(u64_from_be_bytes(&buf, 1, 7), 0x02030405060708); + assert_eq!(u64_from_be_bytes(&buf, 1, 8), 0x0203040506070809); + } +} + +#[cfg(test)] +mod bench { + extern crate test; + + use prelude::v1::*; + use self::test::Bencher; + + // why is this a macro? wouldn't an inlined function work just as well? + macro_rules! u64_from_be_bytes_bench_impl { + ($b:expr, $size:expr, $stride:expr, $start_index:expr) => + ({ + use super::u64_from_be_bytes; + + let data = range(0u8, $stride*100+$start_index).collect::>(); + let mut sum = 0u64; + $b.iter(|| { + let mut i = $start_index; + while i < data.len() { + sum += u64_from_be_bytes(data.as_slice(), i, $size); + i += $stride; + } + }); + }) + } + + #[bench] + fn u64_from_be_bytes_4_aligned(b: &mut Bencher) { + u64_from_be_bytes_bench_impl!(b, 4, 4, 0); + } + + #[bench] + fn u64_from_be_bytes_4_unaligned(b: &mut Bencher) { + u64_from_be_bytes_bench_impl!(b, 4, 4, 1); + } + + #[bench] + fn u64_from_be_bytes_7_aligned(b: &mut Bencher) { + u64_from_be_bytes_bench_impl!(b, 7, 8, 0); + } + + #[bench] + fn u64_from_be_bytes_7_unaligned(b: &mut Bencher) { + u64_from_be_bytes_bench_impl!(b, 7, 8, 1); + } + + #[bench] + fn u64_from_be_bytes_8_aligned(b: &mut Bencher) { + u64_from_be_bytes_bench_impl!(b, 8, 8, 0); + } + + #[bench] + fn u64_from_be_bytes_8_unaligned(b: &mut Bencher) { + u64_from_be_bytes_bench_impl!(b, 8, 8, 1); + } +} diff --git a/src/libstd/old_io/fs.rs b/src/libstd/old_io/fs.rs new file mode 100644 index 00000000000..ee6dbafe688 --- /dev/null +++ b/src/libstd/old_io/fs.rs @@ -0,0 +1,1572 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// ignore-lexer-test FIXME #15679 + +//! Synchronous File I/O +//! +//! This module provides a set of functions and traits for working +//! with regular files & directories on a filesystem. +//! +//! At the top-level of the module are a set of freestanding functions, associated +//! with various filesystem operations. They all operate on `Path` objects. +//! +//! All operations in this module, including those as part of `File` et al block +//! the task during execution. In the event of failure, all functions/methods +//! will return an `IoResult` type with an `Err` value. +//! +//! Also included in this module is an implementation block on the `Path` object +//! defined in `std::path::Path`. The impl adds useful methods about inspecting +//! the metadata of a file. This includes getting the `stat` information, +//! reading off particular bits of it, etc. +//! +//! # Example +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::fs::PathExtensions; +//! use std::old_io::{File, fs}; +//! +//! let path = Path::new("foo.txt"); +//! +//! // create the file, whether it exists or not +//! let mut file = File::create(&path); +//! file.write(b"foobar"); +//! # drop(file); +//! +//! // open the file in read-only mode +//! let mut file = File::open(&path); +//! file.read_to_end(); +//! +//! println!("{}", path.stat().unwrap().size); +//! # drop(file); +//! fs::unlink(&path); +//! ``` + +use clone::Clone; +use old_io::standard_error; +use old_io::{FilePermission, Write, Open, FileAccess, FileMode, FileType}; +use old_io::{IoResult, IoError, InvalidInput}; +use old_io::{FileStat, SeekStyle, Seek, Writer, Reader}; +use old_io::{Read, Truncate, ReadWrite, Append}; +use old_io::UpdateIoError; +use old_io; +use iter::{Iterator, Extend}; +use option::Option; +use option::Option::{Some, None}; +use path::{Path, GenericPath}; +use path; +use result::Result::{Err, Ok}; +use slice::SliceExt; +use string::String; +use vec::Vec; + +use sys::fs as fs_imp; +use sys_common; + +/// Unconstrained file access type that exposes read and write operations +/// +/// Can be constructed via `File::open()`, `File::create()`, and +/// `File::open_mode()`. +/// +/// # Error +/// +/// This type will return errors as an `IoResult` if operations are +/// attempted against it for which its underlying file descriptor was not +/// configured at creation time, via the `FileAccess` parameter to +/// `File::open_mode()`. +pub struct File { + fd: fs_imp::FileDesc, + path: Path, + last_nread: int, +} + +impl sys_common::AsInner for File { + fn as_inner(&self) -> &fs_imp::FileDesc { + &self.fd + } +} + +impl File { + /// Open a file at `path` in the mode specified by the `mode` and `access` + /// arguments + /// + /// # Example + /// + /// ```rust,should_fail + /// use std::old_io::{File, Open, ReadWrite}; + /// + /// let p = Path::new("/some/file/path.txt"); + /// + /// let file = match File::open_mode(&p, Open, ReadWrite) { + /// Ok(f) => f, + /// Err(e) => panic!("file error: {}", e), + /// }; + /// // do some stuff with that file + /// + /// // the file will be closed at the end of this block + /// ``` + /// + /// `FileMode` and `FileAccess` provide information about the permissions + /// context in which a given stream is created. More information about them + /// can be found in `std::io`'s docs. If a file is opened with `Write` + /// or `ReadWrite` access, then it will be created if it does not already + /// exist. + /// + /// Note that, with this function, a `File` is returned regardless of the + /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a + /// `File` opened as `Read` will return an error at runtime). + /// + /// # Error + /// + /// This function will return an error under a number of different + /// circumstances, to include but not limited to: + /// + /// * Opening a file that does not exist with `Read` access. + /// * Attempting to open a file with a `FileAccess` that the user lacks + /// permissions for + /// * Filesystem-level errors (full disk, etc) + pub fn open_mode(path: &Path, + mode: FileMode, + access: FileAccess) -> IoResult { + fs_imp::open(path, mode, access).and_then(|fd| { + // On *BSD systems, we can open a directory as a file and read from it: + // fd=open("/tmp", O_RDONLY); read(fd, buf, N); + // due to an old tradition before the introduction of opendir(3). + // We explicitly reject it because there are few use cases. + if cfg!(not(any(windows, target_os = "linux", target_os = "android"))) && + try!(fd.fstat()).kind == FileType::Directory { + Err(IoError { + kind: InvalidInput, + desc: "is a directory", + detail: None + }) + } else { + Ok(File { + path: path.clone(), + fd: fd, + last_nread: -1 + }) + } + }).update_err("couldn't open path as file", |e| { + format!("{}; path={}; mode={}; access={}", e, path.display(), + mode_string(mode), access_string(access)) + }) + } + + /// Attempts to open a file in read-only mode. This function is equivalent to + /// `File::open_mode(path, Open, Read)`, and will raise all of the same + /// errors that `File::open_mode` does. + /// + /// For more information, see the `File::open_mode` function. + /// + /// # Example + /// + /// ```rust + /// use std::old_io::File; + /// + /// let contents = File::open(&Path::new("foo.txt")).read_to_end(); + /// ``` + pub fn open(path: &Path) -> IoResult { + File::open_mode(path, Open, Read) + } + + /// Attempts to create a file in write-only mode. This function is + /// equivalent to `File::open_mode(path, Truncate, Write)`, and will + /// raise all of the same errors that `File::open_mode` does. + /// + /// For more information, see the `File::open_mode` function. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused_must_use)] + /// use std::old_io::File; + /// + /// let mut f = File::create(&Path::new("foo.txt")); + /// f.write(b"This is a sample file"); + /// # drop(f); + /// # ::std::old_io::fs::unlink(&Path::new("foo.txt")); + /// ``` + pub fn create(path: &Path) -> IoResult { + File::open_mode(path, Truncate, Write) + .update_desc("couldn't create file") + } + + /// Returns the original path that was used to open this file. + pub fn path<'a>(&'a self) -> &'a Path { + &self.path + } + + /// Synchronizes all modifications to this file to its permanent storage + /// device. This will flush any internal buffers necessary to perform this + /// operation. + pub fn fsync(&mut self) -> IoResult<()> { + self.fd.fsync() + .update_err("couldn't fsync file", + |e| format!("{}; path={}", e, self.path.display())) + } + + /// This function is similar to `fsync`, except that it may not synchronize + /// file metadata to the filesystem. This is intended for use cases that + /// must synchronize content, but don't need the metadata on disk. The goal + /// of this method is to reduce disk operations. + pub fn datasync(&mut self) -> IoResult<()> { + self.fd.datasync() + .update_err("couldn't datasync file", + |e| format!("{}; path={}", e, self.path.display())) + } + + /// Either truncates or extends the underlying file, updating the size of + /// this file to become `size`. This is equivalent to unix's `truncate` + /// function. + /// + /// If the `size` is less than the current file's size, then the file will + /// be shrunk. If it is greater than the current file's size, then the file + /// will be extended to `size` and have all of the intermediate data filled + /// in with 0s. + pub fn truncate(&mut self, size: i64) -> IoResult<()> { + self.fd.truncate(size) + .update_err("couldn't truncate file", |e| + format!("{}; path={}; size={}", e, self.path.display(), size)) + } + + /// Returns true if the stream has reached the end of the file. + /// + /// If true, then this file will no longer continue to return data via + /// `read`. + /// + /// Note that the operating system will not return an `EOF` indicator + /// until you have attempted to read past the end of the file, so if + /// you've read _exactly_ the number of bytes in the file, this will + /// return `false`, not `true`. + pub fn eof(&self) -> bool { + self.last_nread == 0 + } + + /// Queries information about the underlying file. + pub fn stat(&self) -> IoResult { + self.fd.fstat() + .update_err("couldn't fstat file", |e| + format!("{}; path={}", e, self.path.display())) + } +} + +/// Unlink a file from the underlying filesystem. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/file/path.txt"); +/// fs::unlink(&p); +/// ``` +/// +/// Note that, just because an unlink call was successful, it is not +/// guaranteed that a file is immediately deleted (e.g. depending on +/// platform, other open file descriptors may prevent immediate removal) +/// +/// # Error +/// +/// This function will return an error if `path` points to a directory, if the +/// user lacks permissions to remove the file, or if some other filesystem-level +/// error occurs. +pub fn unlink(path: &Path) -> IoResult<()> { + fs_imp::unlink(path) + .update_err("couldn't unlink path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Given a path, query the file system to get information about a file, +/// directory, etc. This function will traverse symlinks to query +/// information about the destination file. +/// +/// # Example +/// +/// ```rust +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/file/path.txt"); +/// match fs::stat(&p) { +/// Ok(stat) => { /* ... */ } +/// Err(e) => { /* handle error */ } +/// } +/// ``` +/// +/// # Error +/// +/// This function will return an error if the user lacks the requisite permissions +/// to perform a `stat` call on the given `path` or if there is no entry in the +/// filesystem at the provided path. +pub fn stat(path: &Path) -> IoResult { + fs_imp::stat(path) + .update_err("couldn't stat path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Perform the same operation as the `stat` function, except that this +/// function does not traverse through symlinks. This will return +/// information about the symlink file instead of the file that it points +/// to. +/// +/// # Error +/// +/// See `stat` +pub fn lstat(path: &Path) -> IoResult { + fs_imp::lstat(path) + .update_err("couldn't lstat path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Rename a file or directory to a new name. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// fs::rename(&Path::new("foo"), &Path::new("bar")); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the provided `from` doesn't exist, if +/// the process lacks permissions to view the contents, or if some other +/// intermittent I/O error occurs. +pub fn rename(from: &Path, to: &Path) -> IoResult<()> { + fs_imp::rename(from, to) + .update_err("couldn't rename path", |e| + format!("{}; from={:?}; to={:?}", e, from.display(), to.display())) +} + +/// Copies the contents of one file to another. This function will also +/// copy the permission bits of the original file to the destination file. +/// +/// Note that if `from` and `to` both point to the same file, then the file +/// will likely get truncated by this operation. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt")); +/// ``` +/// +/// # Error +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * The `from` path is not a file +/// * The `from` file does not exist +/// * The current process does not have the permission rights to access +/// `from` or write `to` +/// +/// Note that this copy is not atomic in that once the destination is +/// ensured to not exist, there is nothing preventing the destination from +/// being created and then destroyed by this operation. +pub fn copy(from: &Path, to: &Path) -> IoResult<()> { + fn update_err(result: IoResult, from: &Path, to: &Path) -> IoResult { + result.update_err("couldn't copy path", |e| { + format!("{}; from={:?}; to={:?}", e, from.display(), to.display()) + }) + } + + if !from.is_file() { + return update_err(Err(IoError { + kind: old_io::MismatchedFileTypeForOperation, + desc: "the source path is not an existing file", + detail: None + }), from, to) + } + + let mut reader = try!(File::open(from)); + let mut writer = try!(File::create(to)); + + try!(update_err(super::util::copy(&mut reader, &mut writer), from, to)); + + chmod(to, try!(update_err(from.stat(), from, to)).perm) +} + +/// Changes the permission mode bits found on a file or a directory. This +/// function takes a mask from the `io` module +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::io; +/// use std::old_io::fs; +/// +/// fs::chmod(&Path::new("file.txt"), old_io::USER_FILE); +/// fs::chmod(&Path::new("file.txt"), old_io::USER_READ | old_io::USER_WRITE); +/// fs::chmod(&Path::new("dir"), old_io::USER_DIR); +/// fs::chmod(&Path::new("file.exe"), old_io::USER_EXEC); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the provided `path` doesn't exist, if +/// the process lacks permissions to change the attributes of the file, or if +/// some other I/O error is encountered. +pub fn chmod(path: &Path, mode: old_io::FilePermission) -> IoResult<()> { + fs_imp::chmod(path, mode.bits() as uint) + .update_err("couldn't chmod path", |e| + format!("{}; path={}; mode={:?}", e, path.display(), mode)) +} + +/// Change the user and group owners of a file at the specified path. +pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> { + fs_imp::chown(path, uid, gid) + .update_err("couldn't chown path", |e| + format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)) +} + +/// Creates a new hard link on the filesystem. The `dst` path will be a +/// link pointing to the `src` path. Note that systems often require these +/// two paths to both be located on the same filesystem. +pub fn link(src: &Path, dst: &Path) -> IoResult<()> { + fs_imp::link(src, dst) + .update_err("couldn't link path", |e| + format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display())) +} + +/// Creates a new symbolic link on the filesystem. The `dst` path will be a +/// symlink pointing to the `src` path. +pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { + fs_imp::symlink(src, dst) + .update_err("couldn't symlink path", |e| + format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display())) +} + +/// Reads a symlink, returning the file that the symlink points to. +/// +/// # Error +/// +/// This function will return an error on failure. Failure conditions include +/// reading a file that does not exist or reading a file that is not a symlink. +pub fn readlink(path: &Path) -> IoResult { + fs_imp::readlink(path) + .update_err("couldn't resolve symlink for path", |e| + format!("{}; path={}", e, path.display())) +} + +/// Create a new, empty directory at the provided path +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::io; +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/dir"); +/// fs::mkdir(&p, old_io::USER_RWX); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the user lacks permissions to make a +/// new directory at the provided `path`, or if the directory already exists. +pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { + fs_imp::mkdir(path, mode.bits() as uint) + .update_err("couldn't create directory", |e| + format!("{}; path={}; mode={}", e, path.display(), mode)) +} + +/// Remove an existing, empty directory +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::fs; +/// +/// let p = Path::new("/some/dir"); +/// fs::rmdir(&p); +/// ``` +/// +/// # Error +/// +/// This function will return an error if the user lacks permissions to remove +/// the directory at the provided `path`, or if the directory isn't empty. +pub fn rmdir(path: &Path) -> IoResult<()> { + fs_imp::rmdir(path) + .update_err("couldn't remove directory", |e| + format!("{}; path={}", e, path.display())) +} + +/// Retrieve a vector containing all entries within a provided directory +/// +/// # Example +/// +/// ```rust +/// use std::old_io::fs::PathExtensions; +/// use std::old_io::fs; +/// use std::io; +/// +/// // one possible implementation of fs::walk_dir only visiting files +/// fn visit_dirs(dir: &Path, cb: &mut F) -> old_io::IoResult<()> where +/// F: FnMut(&Path), +/// { +/// if dir.is_dir() { +/// let contents = try!(fs::readdir(dir)); +/// for entry in contents.iter() { +/// if entry.is_dir() { +/// try!(visit_dirs(entry, cb)); +/// } else { +/// (*cb)(entry); +/// } +/// } +/// Ok(()) +/// } else { +/// Err(old_io::standard_error(old_io::InvalidInput)) +/// } +/// } +/// ``` +/// +/// # Error +/// +/// This function will return an error if the provided `path` doesn't exist, if +/// the process lacks permissions to view the contents or if the `path` points +/// at a non-directory file +pub fn readdir(path: &Path) -> IoResult> { + fs_imp::readdir(path) + .update_err("couldn't read directory", + |e| format!("{}; path={}", e, path.display())) +} + +/// Returns an iterator that will recursively walk the directory structure +/// rooted at `path`. The path given will not be iterated over, and this will +/// perform iteration in some top-down order. The contents of unreadable +/// subdirectories are ignored. +pub fn walk_dir(path: &Path) -> IoResult { + Ok(Directories { + stack: try!(readdir(path).update_err("couldn't walk directory", + |e| format!("{}; path={}", e, path.display()))) + }) +} + +/// An iterator that walks over a directory +#[derive(Clone)] +pub struct Directories { + stack: Vec, +} + +impl Iterator for Directories { + type Item = Path; + + fn next(&mut self) -> Option { + match self.stack.pop() { + Some(path) => { + if path.is_dir() { + match readdir(&path) { + Ok(dirs) => { self.stack.extend(dirs.into_iter()); } + Err(..) => {} + } + } + Some(path) + } + None => None + } + } +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// # Error +/// +/// See `fs::mkdir`. +pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> { + // tjc: if directory exists but with different permissions, + // should we return false? + if path.is_dir() { + return Ok(()) + } + + let mut comps = path.components(); + let mut curpath = path.root_path().unwrap_or(Path::new(".")); + + for c in comps { + curpath.push(c); + + let result = mkdir(&curpath, mode) + .update_err("couldn't recursively mkdir", + |e| format!("{}; path={}", e, path.display())); + + match result { + Err(mkdir_err) => { + // already exists ? + if try!(stat(&curpath)).kind != FileType::Directory { + return Err(mkdir_err); + } + } + Ok(()) => () + } + } + + Ok(()) +} + +/// Removes a directory at this path, after removing all its contents. Use +/// carefully! +/// +/// # Error +/// +/// See `file::unlink` and `fs::readdir` +pub fn rmdir_recursive(path: &Path) -> IoResult<()> { + let mut rm_stack = Vec::new(); + rm_stack.push(path.clone()); + + fn rmdir_failed(err: &IoError, path: &Path) -> String { + format!("rmdir_recursive failed; path={}; cause={}", + path.display(), err) + } + + fn update_err(err: IoResult, path: &Path) -> IoResult { + err.update_err("couldn't recursively rmdir", + |e| rmdir_failed(e, path)) + } + + while !rm_stack.is_empty() { + let children = try!(readdir(rm_stack.last().unwrap()) + .update_detail(|e| rmdir_failed(e, path))); + + let mut has_child_dir = false; + + // delete all regular files in the way and push subdirs + // on the stack + for child in children.into_iter() { + // FIXME(#12795) we should use lstat in all cases + let child_type = match cfg!(windows) { + true => try!(update_err(stat(&child), path)), + false => try!(update_err(lstat(&child), path)) + }; + + if child_type.kind == FileType::Directory { + rm_stack.push(child); + has_child_dir = true; + } else { + // we can carry on safely if the file is already gone + // (eg: deleted by someone else since readdir) + match update_err(unlink(&child), path) { + Ok(()) => (), + Err(ref e) if e.kind == old_io::FileNotFound => (), + Err(e) => return Err(e) + } + } + } + + // if no subdir was found, let's pop and delete + if !has_child_dir { + let result = update_err(rmdir(&rm_stack.pop().unwrap()), path); + match result { + Ok(()) => (), + Err(ref e) if e.kind == old_io::FileNotFound => (), + Err(e) => return Err(e) + } + } + } + + Ok(()) +} + +/// Changes the timestamps for a file's last modification and access time. +/// The file at the path specified will have its last access time set to +/// `atime` and its modification time set to `mtime`. The times specified should +/// be in milliseconds. +// FIXME(#10301) these arguments should not be u64 +pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> { + fs_imp::utime(path, atime, mtime) + .update_err("couldn't change_file_times", |e| + format!("{}; path={}", e, path.display())) +} + +impl Reader for File { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + fn update_err(result: IoResult, file: &File) -> IoResult { + result.update_err("couldn't read file", + |e| format!("{}; path={}", + e, file.path.display())) + } + + let result = update_err(self.fd.read(buf), self); + + match result { + Ok(read) => { + self.last_nread = read as int; + match read { + 0 => update_err(Err(standard_error(old_io::EndOfFile)), self), + _ => Ok(read as uint) + } + }, + Err(e) => Err(e) + } + } +} + +impl Writer for File { + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.fd.write(buf) + .update_err("couldn't write to file", + |e| format!("{}; path={}", e, self.path.display())) + } +} + +impl Seek for File { + fn tell(&self) -> IoResult { + self.fd.tell() + .update_err("couldn't retrieve file cursor (`tell`)", + |e| format!("{}; path={}", e, self.path.display())) + } + + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { + let err = match self.fd.seek(pos, style) { + Ok(_) => { + // successful seek resets EOF indicator + self.last_nread = -1; + Ok(()) + } + Err(e) => Err(e), + }; + err.update_err("couldn't seek in file", + |e| format!("{}; path={}", e, self.path.display())) + } +} + +/// Utility methods for paths. +pub trait PathExtensions { + /// Get information on the file, directory, etc at this path. + /// + /// Consult the `fs::stat` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with `file::stat`. + fn stat(&self) -> IoResult; + + /// Get information on the file, directory, etc at this path, not following + /// symlinks. + /// + /// Consult the `fs::lstat` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with `file::lstat`. + fn lstat(&self) -> IoResult; + + /// Boolean value indicator whether the underlying file exists on the local + /// filesystem. Returns false in exactly the cases where `fs::stat` fails. + fn exists(&self) -> bool; + + /// Whether the underlying implementation (be it a file path, or something + /// else) points at a "regular file" on the FS. Will return false for paths + /// to non-existent locations or directories or other non-regular files + /// (named pipes, etc). Follows links when making this determination. + fn is_file(&self) -> bool; + + /// Whether the underlying implementation (be it a file path, or something + /// else) is pointing at a directory in the underlying FS. Will return + /// false for paths to non-existent locations or if the item is not a + /// directory (eg files, named pipes, etc). Follows links when making this + /// determination. + fn is_dir(&self) -> bool; +} + +impl PathExtensions for path::Path { + fn stat(&self) -> IoResult { stat(self) } + fn lstat(&self) -> IoResult { lstat(self) } + fn exists(&self) -> bool { + self.stat().is_ok() + } + fn is_file(&self) -> bool { + match self.stat() { + Ok(s) => s.kind == FileType::RegularFile, + Err(..) => false + } + } + fn is_dir(&self) -> bool { + match self.stat() { + Ok(s) => s.kind == FileType::Directory, + Err(..) => false + } + } +} + +fn mode_string(mode: FileMode) -> &'static str { + match mode { + super::Open => "open", + super::Append => "append", + super::Truncate => "truncate" + } +} + +fn access_string(access: FileAccess) -> &'static str { + match access { + super::Read => "read", + super::Write => "write", + super::ReadWrite => "readwrite" + } +} + +#[cfg(test)] +#[allow(unused_imports)] +#[allow(unused_variables)] +#[allow(unused_mut)] +mod test { + use prelude::v1::*; + use old_io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite, FileType}; + use io; + use str; + use old_io::fs::*; + + macro_rules! check { ($e:expr) => ( + match $e { + Ok(t) => t, + Err(e) => panic!("{} failed with: {:?}", stringify!($e), e), + } + ) } + + macro_rules! error { ($e:expr, $s:expr) => ( + match $e { + Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), + Err(ref err) => assert!(err.to_string().contains($s.as_slice()), + format!("`{}` did not contain `{}`", err, $s)) + } + ) } + + pub struct TempDir(Path); + + impl TempDir { + fn join(&self, path: &str) -> Path { + let TempDir(ref p) = *self; + p.join(path) + } + + fn path<'a>(&'a self) -> &'a Path { + let TempDir(ref p) = *self; + p + } + } + + impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + check!(old_io::fs::rmdir_recursive(p)); + } + } + + pub fn tmpdir() -> TempDir { + use os; + use rand; + let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); + check!(old_io::fs::mkdir(&ret, old_io::USER_RWX)); + TempDir(ret) + } + + #[test] + fn file_test_io_smoke_test() { + let message = "it's alright. have a good time"; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test.txt"); + { + let mut write_stream = File::open_mode(filename, Open, ReadWrite); + check!(write_stream.write(message.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + let mut read_buf = [0; 1028]; + let read_str = match check!(read_stream.read(&mut read_buf)) { + -1|0 => panic!("shouldn't happen"), + n => str::from_utf8(&read_buf[..n]).unwrap().to_string() + }; + assert_eq!(read_str.as_slice(), message); + } + check!(unlink(filename)); + } + + #[test] + fn invalid_path_raises() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_that_does_not_exist.txt"); + let result = File::open_mode(filename, Open, Read); + + error!(result, "couldn't open path as file"); + if cfg!(unix) { + error!(result, "no such file or directory"); + } + error!(result, format!("path={}; mode=open; access=read", filename.display())); + } + + #[test] + fn file_test_iounlinking_invalid_path_should_raise_condition() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); + + let result = unlink(filename); + + error!(result, "couldn't unlink path"); + if cfg!(unix) { + error!(result, "no such file or directory"); + } + error!(result, format!("path={}", filename.display())); + } + + #[test] + fn file_test_io_non_positional_read() { + let message: &str = "ten-four"; + let mut read_mem = [0; 8]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(message.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + { + let read_buf = read_mem.slice_mut(0, 4); + check!(read_stream.read(read_buf)); + } + { + let read_buf = read_mem.slice_mut(4, 8); + check!(read_stream.read(read_buf)); + } + } + check!(unlink(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert_eq!(read_str, message); + } + + #[test] + fn file_test_io_seek_and_tell_smoke_test() { + let message = "ten-four"; + let mut read_mem = [0; 4]; + let set_cursor = 4 as u64; + let mut tell_pos_pre_read; + let mut tell_pos_post_read; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(message.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + check!(read_stream.seek(set_cursor as i64, SeekSet)); + tell_pos_pre_read = check!(read_stream.tell()); + check!(read_stream.read(&mut read_mem)); + tell_pos_post_read = check!(read_stream.tell()); + } + check!(unlink(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert_eq!(read_str, message.slice(4, 8)); + assert_eq!(tell_pos_pre_read, set_cursor); + assert_eq!(tell_pos_post_read, message.len() as u64); + } + + #[test] + fn file_test_io_seek_and_write() { + let initial_msg = "food-is-yummy"; + let overwrite_msg = "-the-bar!!"; + let final_msg = "foo-the-bar!!"; + let seek_idx = 3i; + let mut read_mem = [0; 13]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(initial_msg.as_bytes())); + check!(rw_stream.seek(seek_idx as i64, SeekSet)); + check!(rw_stream.write(overwrite_msg.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + check!(read_stream.read(&mut read_mem)); + } + check!(unlink(filename)); + let read_str = str::from_utf8(&read_mem).unwrap(); + assert!(read_str == final_msg); + } + + #[test] + fn file_test_io_seek_shakedown() { + use str; // 01234567890123 + let initial_msg = "qwer-asdf-zxcv"; + let chunk_one: &str = "qwer"; + let chunk_two: &str = "asdf"; + let chunk_three: &str = "zxcv"; + let mut read_mem = [0; 4]; + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); + { + let mut rw_stream = File::open_mode(filename, Open, ReadWrite); + check!(rw_stream.write(initial_msg.as_bytes())); + } + { + let mut read_stream = File::open_mode(filename, Open, Read); + + check!(read_stream.seek(-4, SeekEnd)); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); + + check!(read_stream.seek(-9, SeekCur)); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); + + check!(read_stream.seek(0, SeekSet)); + check!(read_stream.read(&mut read_mem)); + assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); + } + check!(unlink(filename)); + } + + #[test] + fn file_test_stat_is_correct_on_is_file() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); + { + let mut fs = check!(File::open_mode(filename, Open, ReadWrite)); + let msg = "hw"; + fs.write(msg.as_bytes()).unwrap(); + + let fstat_res = check!(fs.stat()); + assert_eq!(fstat_res.kind, FileType::RegularFile); + } + let stat_res_fn = check!(stat(filename)); + assert_eq!(stat_res_fn.kind, FileType::RegularFile); + let stat_res_meth = check!(filename.stat()); + assert_eq!(stat_res_meth.kind, FileType::RegularFile); + check!(unlink(filename)); + } + + #[test] + fn file_test_stat_is_correct_on_is_dir() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_dir"); + check!(mkdir(filename, old_io::USER_RWX)); + let stat_res_fn = check!(stat(filename)); + assert!(stat_res_fn.kind == FileType::Directory); + let stat_res_meth = check!(filename.stat()); + assert!(stat_res_meth.kind == FileType::Directory); + check!(rmdir(filename)); + } + + #[test] + fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("fileinfo_false_on_dir"); + check!(mkdir(dir, old_io::USER_RWX)); + assert!(dir.is_file() == false); + check!(rmdir(dir)); + } + + #[test] + fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + let tmpdir = tmpdir(); + let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); + check!(File::create(file).write(b"foo")); + assert!(file.exists()); + check!(unlink(file)); + assert!(!file.exists()); + } + + #[test] + fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("before_and_after_dir"); + assert!(!dir.exists()); + check!(mkdir(dir, old_io::USER_RWX)); + assert!(dir.exists()); + assert!(dir.is_dir()); + check!(rmdir(dir)); + assert!(!dir.exists()); + } + + #[test] + fn file_test_directoryinfo_readdir() { + use str; + let tmpdir = tmpdir(); + let dir = &tmpdir.join("di_readdir"); + check!(mkdir(dir, old_io::USER_RWX)); + let prefix = "foo"; + for n in range(0i,3) { + let f = dir.join(format!("{}.txt", n)); + let mut w = check!(File::create(&f)); + let msg_str = format!("{}{}", prefix, n.to_string()); + let msg = msg_str.as_bytes(); + check!(w.write(msg)); + } + let files = check!(readdir(dir)); + let mut mem = [0u8; 4]; + for f in files.iter() { + { + let n = f.filestem_str(); + check!(File::open(f).read(&mut mem)); + let read_str = str::from_utf8(&mem).unwrap(); + let expected = match n { + None|Some("") => panic!("really shouldn't happen.."), + Some(n) => format!("{}{}", prefix, n), + }; + assert_eq!(expected.as_slice(), read_str); + } + check!(unlink(f)); + } + check!(rmdir(dir)); + } + + #[test] + fn file_test_walk_dir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("walk_dir"); + check!(mkdir(dir, old_io::USER_RWX)); + + let dir1 = &dir.join("01/02/03"); + check!(mkdir_recursive(dir1, old_io::USER_RWX)); + check!(File::create(&dir1.join("04"))); + + let dir2 = &dir.join("11/12/13"); + check!(mkdir_recursive(dir2, old_io::USER_RWX)); + check!(File::create(&dir2.join("14"))); + + let mut files = check!(walk_dir(dir)); + let mut cur = [0u8; 2]; + for f in files { + let stem = f.filestem_str().unwrap(); + let root = stem.as_bytes()[0] - b'0'; + let name = stem.as_bytes()[1] - b'0'; + assert!(cur[root as uint] < name); + cur[root as uint] = name; + } + + check!(rmdir_recursive(dir)); + } + + #[test] + fn mkdir_path_already_exists_error() { + use old_io::{IoError, PathAlreadyExists}; + + let tmpdir = tmpdir(); + let dir = &tmpdir.join("mkdir_error_twice"); + check!(mkdir(dir, old_io::USER_RWX)); + match mkdir(dir, old_io::USER_RWX) { + Err(IoError{kind:PathAlreadyExists,..}) => (), + _ => assert!(false) + }; + } + + #[test] + fn recursive_mkdir() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1/d2"); + check!(mkdir_recursive(&dir, old_io::USER_RWX)); + assert!(dir.is_dir()) + } + + #[test] + fn recursive_mkdir_failure() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1"); + let file = dir.join("f1"); + + check!(mkdir_recursive(&dir, old_io::USER_RWX)); + check!(File::create(&file)); + + let result = mkdir_recursive(&file, old_io::USER_RWX); + + error!(result, "couldn't recursively mkdir"); + error!(result, "couldn't create directory"); + error!(result, "mode=0700"); + error!(result, format!("path={}", file.display())); + } + + #[test] + fn recursive_mkdir_slash() { + check!(mkdir_recursive(&Path::new("/"), old_io::USER_RWX)); + } + + // FIXME(#12795) depends on lstat to work on windows + #[cfg(not(windows))] + #[test] + fn recursive_rmdir() { + let tmpdir = tmpdir(); + let d1 = tmpdir.join("d1"); + let dt = d1.join("t"); + let dtt = dt.join("t"); + let d2 = tmpdir.join("d2"); + let canary = d2.join("do_not_delete"); + check!(mkdir_recursive(&dtt, old_io::USER_RWX)); + check!(mkdir_recursive(&d2, old_io::USER_RWX)); + check!(File::create(&canary).write(b"foo")); + check!(symlink(&d2, &dt.join("d2"))); + check!(rmdir_recursive(&d1)); + + assert!(!d1.is_dir()); + assert!(canary.exists()); + } + + #[test] + fn unicode_path_is_dir() { + assert!(Path::new(".").is_dir()); + assert!(!Path::new("test/stdtest/fs.rs").is_dir()); + + let tmpdir = tmpdir(); + + let mut dirpath = tmpdir.path().clone(); + dirpath.push(format!("test-가一ー你好")); + check!(mkdir(&dirpath, old_io::USER_RWX)); + assert!(dirpath.is_dir()); + + let mut filepath = dirpath; + filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); + check!(File::create(&filepath)); // ignore return; touch only + assert!(!filepath.is_dir()); + assert!(filepath.exists()); + } + + #[test] + fn unicode_path_exists() { + assert!(Path::new(".").exists()); + assert!(!Path::new("test/nonexistent-bogus-path").exists()); + + let tmpdir = tmpdir(); + let unicode = tmpdir.path(); + let unicode = unicode.join(format!("test-각丁ー再见")); + check!(mkdir(&unicode, old_io::USER_RWX)); + assert!(unicode.exists()); + assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); + } + + #[test] + fn copy_file_does_not_exist() { + let from = Path::new("test/nonexistent-bogus-path"); + let to = Path::new("test/other-bogus-path"); + + error!(copy(&from, &to), + format!("couldn't copy path (the source path is not an \ + existing file; from={:?}; to={:?})", + from.display(), to.display())); + + match copy(&from, &to) { + Ok(..) => panic!(), + Err(..) => { + assert!(!from.exists()); + assert!(!to.exists()); + } + } + } + + #[test] + fn copy_file_ok() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input).write(b"hello")); + check!(copy(&input, &out)); + let contents = check!(File::open(&out).read_to_end()); + assert_eq!(contents.as_slice(), b"hello"); + + assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm); + } + + #[test] + fn copy_file_dst_dir() { + let tmpdir = tmpdir(); + let out = tmpdir.join("out"); + + check!(File::create(&out)); + match copy(&out, tmpdir.path()) { + Ok(..) => panic!(), Err(..) => {} + } + } + + #[test] + fn copy_file_dst_exists() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in"); + let output = tmpdir.join("out"); + + check!(File::create(&input).write("foo".as_bytes())); + check!(File::create(&output).write("bar".as_bytes())); + check!(copy(&input, &output)); + + assert_eq!(check!(File::open(&output).read_to_end()), + b"foo".to_vec()); + } + + #[test] + fn copy_file_src_dir() { + let tmpdir = tmpdir(); + let out = tmpdir.join("out"); + + match copy(tmpdir.path(), &out) { + Ok(..) => panic!(), Err(..) => {} + } + assert!(!out.exists()); + } + + #[test] + fn copy_file_preserves_perm_bits() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input)); + check!(chmod(&input, old_io::USER_READ)); + check!(copy(&input, &out)); + assert!(!check!(out.stat()).perm.intersects(old_io::USER_WRITE)); + + check!(chmod(&input, old_io::USER_FILE)); + check!(chmod(&out, old_io::USER_FILE)); + } + + #[cfg(not(windows))] // FIXME(#10264) operation not permitted? + #[test] + fn symlinks_work() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input).write("foobar".as_bytes())); + check!(symlink(&input, &out)); + if cfg!(not(windows)) { + assert_eq!(check!(lstat(&out)).kind, FileType::Symlink); + assert_eq!(check!(out.lstat()).kind, FileType::Symlink); + } + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(File::open(&out).read_to_end()), + b"foobar".to_vec()); + } + + #[cfg(not(windows))] // apparently windows doesn't like symlinks + #[test] + fn symlink_noexist() { + let tmpdir = tmpdir(); + // symlinks can point to things that don't exist + check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar"))); + assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo")); + } + + #[test] + fn readlink_not_symlink() { + let tmpdir = tmpdir(); + match readlink(tmpdir.path()) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + } + + #[test] + fn links_work() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + check!(File::create(&input).write("foobar".as_bytes())); + check!(link(&input, &out)); + if cfg!(not(windows)) { + assert_eq!(check!(lstat(&out)).kind, FileType::RegularFile); + assert_eq!(check!(out.lstat()).kind, FileType::RegularFile); + assert_eq!(check!(stat(&out)).unstable.nlink, 2); + assert_eq!(check!(out.stat()).unstable.nlink, 2); + } + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(stat(&out)).size, check!(input.stat()).size); + assert_eq!(check!(File::open(&out).read_to_end()), + b"foobar".to_vec()); + + // can't link to yourself + match link(&input, &input) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + // can't link to something that doesn't exist + match link(&tmpdir.join("foo"), &tmpdir.join("bar")) { + Ok(..) => panic!("wanted a failure"), + Err(..) => {} + } + } + + #[test] + fn chmod_works() { + let tmpdir = tmpdir(); + let file = tmpdir.join("in.txt"); + + check!(File::create(&file)); + assert!(check!(stat(&file)).perm.contains(old_io::USER_WRITE)); + check!(chmod(&file, old_io::USER_READ)); + assert!(!check!(stat(&file)).perm.contains(old_io::USER_WRITE)); + + match chmod(&tmpdir.join("foo"), old_io::USER_RWX) { + Ok(..) => panic!("wanted a panic"), + Err(..) => {} + } + + check!(chmod(&file, old_io::USER_FILE)); + } + + #[test] + fn sync_doesnt_kill_anything() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let mut file = check!(File::open_mode(&path, old_io::Open, old_io::ReadWrite)); + check!(file.fsync()); + check!(file.datasync()); + check!(file.write(b"foo")); + check!(file.fsync()); + check!(file.datasync()); + drop(file); + } + + #[test] + fn truncate_works() { + let tmpdir = tmpdir(); + let path = tmpdir.join("in.txt"); + + let mut file = check!(File::open_mode(&path, old_io::Open, old_io::ReadWrite)); + check!(file.write(b"foo")); + check!(file.fsync()); + + // Do some simple things with truncation + assert_eq!(check!(file.stat()).size, 3); + check!(file.truncate(10)); + assert_eq!(check!(file.stat()).size, 10); + check!(file.write(b"bar")); + check!(file.fsync()); + assert_eq!(check!(file.stat()).size, 10); + assert_eq!(check!(File::open(&path).read_to_end()), + b"foobar\0\0\0\0".to_vec()); + + // Truncate to a smaller length, don't seek, and then write something. + // Ensure that the intermediate zeroes are all filled in (we're seeked + // past the end of the file). + check!(file.truncate(2)); + assert_eq!(check!(file.stat()).size, 2); + check!(file.write(b"wut")); + check!(file.fsync()); + assert_eq!(check!(file.stat()).size, 9); + assert_eq!(check!(File::open(&path).read_to_end()), + b"fo\0\0\0\0wut".to_vec()); + drop(file); + } + + #[test] + fn open_flavors() { + let tmpdir = tmpdir(); + + match File::open_mode(&tmpdir.join("a"), old_io::Open, old_io::Read) { + Ok(..) => panic!(), Err(..) => {} + } + + // Perform each one twice to make sure that it succeeds the second time + // (where the file exists) + check!(File::open_mode(&tmpdir.join("b"), old_io::Open, old_io::Write)); + assert!(tmpdir.join("b").exists()); + check!(File::open_mode(&tmpdir.join("b"), old_io::Open, old_io::Write)); + + check!(File::open_mode(&tmpdir.join("c"), old_io::Open, old_io::ReadWrite)); + assert!(tmpdir.join("c").exists()); + check!(File::open_mode(&tmpdir.join("c"), old_io::Open, old_io::ReadWrite)); + + check!(File::open_mode(&tmpdir.join("d"), old_io::Append, old_io::Write)); + assert!(tmpdir.join("d").exists()); + check!(File::open_mode(&tmpdir.join("d"), old_io::Append, old_io::Write)); + + check!(File::open_mode(&tmpdir.join("e"), old_io::Append, old_io::ReadWrite)); + assert!(tmpdir.join("e").exists()); + check!(File::open_mode(&tmpdir.join("e"), old_io::Append, old_io::ReadWrite)); + + check!(File::open_mode(&tmpdir.join("f"), old_io::Truncate, old_io::Write)); + assert!(tmpdir.join("f").exists()); + check!(File::open_mode(&tmpdir.join("f"), old_io::Truncate, old_io::Write)); + + check!(File::open_mode(&tmpdir.join("g"), old_io::Truncate, old_io::ReadWrite)); + assert!(tmpdir.join("g").exists()); + check!(File::open_mode(&tmpdir.join("g"), old_io::Truncate, old_io::ReadWrite)); + + check!(File::create(&tmpdir.join("h")).write("foo".as_bytes())); + check!(File::open_mode(&tmpdir.join("h"), old_io::Open, old_io::Read)); + { + let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Open, + old_io::Read)); + match f.write("wut".as_bytes()) { + Ok(..) => panic!(), Err(..) => {} + } + } + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "write/stat failed"); + { + let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Append, + old_io::Write)); + check!(f.write("bar".as_bytes())); + } + assert!(check!(stat(&tmpdir.join("h"))).size == 6, + "append didn't append"); + { + let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Truncate, + old_io::Write)); + check!(f.write("bar".as_bytes())); + } + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "truncate didn't truncate"); + } + + #[test] + fn utime() { + let tmpdir = tmpdir(); + let path = tmpdir.join("a"); + check!(File::create(&path)); + // These numbers have to be bigger than the time in the day to account + // for timezones Windows in particular will fail in certain timezones + // with small enough values + check!(change_file_times(&path, 100000, 200000)); + assert_eq!(check!(path.stat()).accessed, 100000); + assert_eq!(check!(path.stat()).modified, 200000); + } + + #[test] + fn utime_noexist() { + let tmpdir = tmpdir(); + + match change_file_times(&tmpdir.join("a"), 100, 200) { + Ok(..) => panic!(), + Err(..) => {} + } + } + + #[test] + fn binary_file() { + use rand::{StdRng, Rng}; + + let mut bytes = [0; 1024]; + StdRng::new().ok().unwrap().fill_bytes(&mut bytes); + + let tmpdir = tmpdir(); + + check!(File::create(&tmpdir.join("test")).write(&bytes)); + let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); + assert!(actual == bytes.as_slice()); + } + + #[test] + fn unlink_readonly() { + let tmpdir = tmpdir(); + let path = tmpdir.join("file"); + check!(File::create(&path)); + check!(chmod(&path, old_io::USER_READ)); + check!(unlink(&path)); + } +} diff --git a/src/libstd/old_io/mem.rs b/src/libstd/old_io/mem.rs new file mode 100644 index 00000000000..3fc2a330fde --- /dev/null +++ b/src/libstd/old_io/mem.rs @@ -0,0 +1,757 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// ignore-lexer-test FIXME #15679 + +//! Readers and Writers for in-memory buffers + +use cmp::min; +use option::Option::None; +use result::Result::{Err, Ok}; +use old_io; +use old_io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult}; +use slice::{self, AsSlice, SliceExt}; +use vec::Vec; + +const BUF_CAPACITY: uint = 128; + +fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult { + // compute offset as signed and clamp to prevent overflow + let pos = match seek { + old_io::SeekSet => 0, + old_io::SeekEnd => end, + old_io::SeekCur => cur, + } as i64; + + if offset + pos < 0 { + Err(IoError { + kind: old_io::InvalidInput, + desc: "invalid seek to a negative offset", + detail: None + }) + } else { + Ok((offset + pos) as u64) + } +} + +impl Writer for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.push_all(buf); + Ok(()) + } +} + +/// Writes to an owned, growable byte vector +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::MemWriter; +/// +/// let mut w = MemWriter::new(); +/// w.write(&[0, 1, 2]); +/// +/// assert_eq!(w.into_inner(), vec!(0, 1, 2)); +/// ``` +#[deprecated = "use the Vec Writer implementation directly"] +#[derive(Clone)] +pub struct MemWriter { + buf: Vec, +} + +#[allow(deprecated)] +impl MemWriter { + /// Create a new `MemWriter`. + #[inline] + pub fn new() -> MemWriter { + MemWriter::with_capacity(BUF_CAPACITY) + } + /// Create a new `MemWriter`, allocating at least `n` bytes for + /// the internal buffer. + #[inline] + pub fn with_capacity(n: uint) -> MemWriter { + MemWriter::from_vec(Vec::with_capacity(n)) + } + /// Create a new `MemWriter` that will append to an existing `Vec`. + #[inline] + pub fn from_vec(buf: Vec) -> MemWriter { + MemWriter { buf: buf } + } + + /// Acquires an immutable reference to the underlying buffer of this + /// `MemWriter`. + #[inline] + pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() } + + /// Unwraps this `MemWriter`, returning the underlying buffer + #[inline] + pub fn into_inner(self) -> Vec { self.buf } +} + +impl Writer for MemWriter { + #[inline] + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.buf.push_all(buf); + Ok(()) + } +} + +/// Reads from an owned byte vector +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::MemReader; +/// +/// let mut r = MemReader::new(vec!(0, 1, 2)); +/// +/// assert_eq!(r.read_to_end().unwrap(), vec!(0, 1, 2)); +/// ``` +pub struct MemReader { + buf: Vec, + pos: uint +} + +impl MemReader { + /// Creates a new `MemReader` which will read the buffer given. The buffer + /// can be re-acquired through `unwrap` + #[inline] + pub fn new(buf: Vec) -> MemReader { + MemReader { + buf: buf, + pos: 0 + } + } + + /// Tests whether this reader has read all bytes in its buffer. + /// + /// If `true`, then this will no longer return bytes from `read`. + #[inline] + pub fn eof(&self) -> bool { self.pos >= self.buf.len() } + + /// Acquires an immutable reference to the underlying buffer of this + /// `MemReader`. + /// + /// No method is exposed for acquiring a mutable reference to the buffer + /// because it could corrupt the state of this `MemReader`. + #[inline] + pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() } + + /// Unwraps this `MemReader`, returning the underlying buffer + #[inline] + pub fn into_inner(self) -> Vec { self.buf } +} + +impl Reader for MemReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult { + if self.eof() { return Err(old_io::standard_error(old_io::EndOfFile)) } + + let write_len = min(buf.len(), self.buf.len() - self.pos); + { + let input = &self.buf[self.pos.. self.pos + write_len]; + let output = &mut buf[..write_len]; + assert_eq!(input.len(), output.len()); + slice::bytes::copy_memory(output, input); + } + self.pos += write_len; + assert!(self.pos <= self.buf.len()); + + return Ok(write_len); + } +} + +impl Seek for MemReader { + #[inline] + fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { + let new = try!(combine(style, self.pos, self.buf.len(), pos)); + self.pos = new as uint; + Ok(()) + } +} + +impl Buffer for MemReader { + #[inline] + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { + if self.pos < self.buf.len() { + Ok(&self.buf[self.pos..]) + } else { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + + #[inline] + fn consume(&mut self, amt: uint) { self.pos += amt; } +} + +impl<'a> Reader for &'a [u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult { + if self.is_empty() { return Err(old_io::standard_error(old_io::EndOfFile)); } + + let write_len = min(buf.len(), self.len()); + { + let input = &self[..write_len]; + let output = &mut buf[.. write_len]; + slice::bytes::copy_memory(output, input); + } + + *self = &self[write_len..]; + + Ok(write_len) + } +} + +impl<'a> Buffer for &'a [u8] { + #[inline] + fn fill_buf(&mut self) -> IoResult<&[u8]> { + if self.is_empty() { + Err(old_io::standard_error(old_io::EndOfFile)) + } else { + Ok(*self) + } + } + + #[inline] + fn consume(&mut self, amt: uint) { + *self = &self[amt..]; + } +} + + +/// Writes to a fixed-size byte slice +/// +/// If a write will not fit in the buffer, it returns an error and does not +/// write any data. +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::BufWriter; +/// +/// let mut buf = [0; 4]; +/// { +/// let mut w = BufWriter::new(&mut buf); +/// w.write(&[0, 1, 2]); +/// } +/// assert!(buf == [0, 1, 2, 0]); +/// ``` +pub struct BufWriter<'a> { + buf: &'a mut [u8], + pos: uint +} + +impl<'a> BufWriter<'a> { + /// Creates a new `BufWriter` which will wrap the specified buffer. The + /// writer initially starts at position 0. + #[inline] + pub fn new(buf: &'a mut [u8]) -> BufWriter<'a> { + BufWriter { + buf: buf, + pos: 0 + } + } +} + +impl<'a> Writer for BufWriter<'a> { + #[inline] + fn write(&mut self, src: &[u8]) -> IoResult<()> { + let dst = &mut self.buf[self.pos..]; + let dst_len = dst.len(); + + if dst_len == 0 { + return Err(old_io::standard_error(old_io::EndOfFile)); + } + + let src_len = src.len(); + + if dst_len >= src_len { + slice::bytes::copy_memory(dst, src); + + self.pos += src_len; + + Ok(()) + } else { + slice::bytes::copy_memory(dst, &src[..dst_len]); + + self.pos += dst_len; + + Err(old_io::standard_error(old_io::ShortWrite(dst_len))) + } + } +} + +impl<'a> Seek for BufWriter<'a> { + #[inline] + fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { + let new = try!(combine(style, self.pos, self.buf.len(), pos)); + self.pos = min(new as uint, self.buf.len()); + Ok(()) + } +} + +/// Reads from a fixed-size byte slice +/// +/// # Example +/// +/// ```rust +/// # #![allow(unused_must_use)] +/// use std::old_io::BufReader; +/// +/// let buf = [0, 1, 2, 3]; +/// let mut r = BufReader::new(&buf); +/// +/// assert_eq!(r.read_to_end().unwrap(), vec![0, 1, 2, 3]); +/// ``` +pub struct BufReader<'a> { + buf: &'a [u8], + pos: uint +} + +impl<'a> BufReader<'a> { + /// Creates a new buffered reader which will read the specified buffer + #[inline] + pub fn new(buf: &'a [u8]) -> BufReader<'a> { + BufReader { + buf: buf, + pos: 0 + } + } + + /// Tests whether this reader has read all bytes in its buffer. + /// + /// If `true`, then this will no longer return bytes from `read`. + #[inline] + pub fn eof(&self) -> bool { self.pos >= self.buf.len() } +} + +impl<'a> Reader for BufReader<'a> { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult { + if self.eof() { return Err(old_io::standard_error(old_io::EndOfFile)) } + + let write_len = min(buf.len(), self.buf.len() - self.pos); + { + let input = &self.buf[self.pos.. self.pos + write_len]; + let output = &mut buf[..write_len]; + assert_eq!(input.len(), output.len()); + slice::bytes::copy_memory(output, input); + } + self.pos += write_len; + assert!(self.pos <= self.buf.len()); + + return Ok(write_len); + } +} + +impl<'a> Seek for BufReader<'a> { + #[inline] + fn tell(&self) -> IoResult { Ok(self.pos as u64) } + + #[inline] + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { + let new = try!(combine(style, self.pos, self.buf.len(), pos)); + self.pos = new as uint; + Ok(()) + } +} + +impl<'a> Buffer for BufReader<'a> { + #[inline] + fn fill_buf(&mut self) -> IoResult<&[u8]> { + if self.pos < self.buf.len() { + Ok(&self.buf[self.pos..]) + } else { + Err(old_io::standard_error(old_io::EndOfFile)) + } + } + + #[inline] + fn consume(&mut self, amt: uint) { self.pos += amt; } +} + +#[cfg(test)] +mod test { + extern crate "test" as test_crate; + use old_io::{SeekSet, SeekCur, SeekEnd, Reader, Writer, Seek}; + use prelude::v1::{Ok, Err, range, Vec, Buffer, AsSlice, SliceExt}; + use prelude::v1::IteratorExt; + use io; + use iter::repeat; + use self::test_crate::Bencher; + use super::*; + + #[test] + fn test_vec_writer() { + let mut writer = Vec::new(); + writer.write(&[0]).unwrap(); + writer.write(&[1, 2, 3]).unwrap(); + writer.write(&[4, 5, 6, 7]).unwrap(); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.as_slice(), b); + } + + #[test] + fn test_mem_writer() { + let mut writer = MemWriter::new(); + writer.write(&[0]).unwrap(); + writer.write(&[1, 2, 3]).unwrap(); + writer.write(&[4, 5, 6, 7]).unwrap(); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.get_ref(), b); + } + + #[test] + fn test_buf_writer() { + let mut buf = [0 as u8; 9]; + { + let mut writer = BufWriter::new(&mut buf); + assert_eq!(writer.tell(), Ok(0)); + writer.write(&[0]).unwrap(); + assert_eq!(writer.tell(), Ok(1)); + writer.write(&[1, 2, 3]).unwrap(); + writer.write(&[4, 5, 6, 7]).unwrap(); + assert_eq!(writer.tell(), Ok(8)); + writer.write(&[]).unwrap(); + assert_eq!(writer.tell(), Ok(8)); + + assert_eq!(writer.write(&[8, 9]).err().unwrap().kind, old_io::ShortWrite(1)); + assert_eq!(writer.write(&[10]).err().unwrap().kind, old_io::EndOfFile); + } + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(buf, b); + } + + #[test] + fn test_buf_writer_seek() { + let mut buf = [0 as u8; 8]; + { + let mut writer = BufWriter::new(&mut buf); + assert_eq!(writer.tell(), Ok(0)); + writer.write(&[1]).unwrap(); + assert_eq!(writer.tell(), Ok(1)); + + writer.seek(2, SeekSet).unwrap(); + assert_eq!(writer.tell(), Ok(2)); + writer.write(&[2]).unwrap(); + assert_eq!(writer.tell(), Ok(3)); + + writer.seek(-2, SeekCur).unwrap(); + assert_eq!(writer.tell(), Ok(1)); + writer.write(&[3]).unwrap(); + assert_eq!(writer.tell(), Ok(2)); + + writer.seek(-1, SeekEnd).unwrap(); + assert_eq!(writer.tell(), Ok(7)); + writer.write(&[4]).unwrap(); + assert_eq!(writer.tell(), Ok(8)); + + } + let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; + assert_eq!(buf, b); + } + + #[test] + fn test_buf_writer_error() { + let mut buf = [0 as u8; 2]; + let mut writer = BufWriter::new(&mut buf); + writer.write(&[0]).unwrap(); + + match writer.write(&[0, 0]) { + Ok(..) => panic!(), + Err(e) => assert_eq!(e.kind, old_io::ShortWrite(1)), + } + } + + #[test] + fn test_mem_reader() { + let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.tell(), Ok(0)); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.tell(), Ok(1)); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.tell(), Ok(5)); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert!(reader.read(&mut buf).is_err()); + let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); + assert!(reader.read(&mut buf).is_err()); + } + + #[test] + fn test_slice_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = &mut in_buf.as_slice(); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(buf.as_slice(), b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf.as_slice(), b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert!(reader.read(&mut buf).is_err()); + let mut reader = &mut in_buf.as_slice(); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); + assert!(reader.read(&mut buf).is_err()); + } + + #[test] + fn test_buf_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = BufReader::new(in_buf.as_slice()); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + assert_eq!(reader.tell(), Ok(0)); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.tell(), Ok(1)); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.tell(), Ok(5)); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert!(reader.read(&mut buf).is_err()); + let mut reader = BufReader::new(in_buf.as_slice()); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); + assert!(reader.read(&mut buf).is_err()); + } + + #[test] + fn test_read_char() { + let b = b"Vi\xE1\xBB\x87t"; + let mut r = BufReader::new(b); + assert_eq!(r.read_char(), Ok('V')); + assert_eq!(r.read_char(), Ok('i')); + assert_eq!(r.read_char(), Ok('ệ')); + assert_eq!(r.read_char(), Ok('t')); + assert!(r.read_char().is_err()); + } + + #[test] + fn test_read_bad_char() { + let b = b"\x80"; + let mut r = BufReader::new(b); + assert!(r.read_char().is_err()); + } + + #[test] + fn test_write_strings() { + let mut writer = MemWriter::new(); + writer.write_str("testing").unwrap(); + writer.write_line("testing").unwrap(); + writer.write_str("testing").unwrap(); + let mut r = BufReader::new(writer.get_ref()); + assert_eq!(r.read_to_string().unwrap(), "testingtesting\ntesting"); + } + + #[test] + fn test_write_char() { + let mut writer = MemWriter::new(); + writer.write_char('a').unwrap(); + writer.write_char('\n').unwrap(); + writer.write_char('ệ').unwrap(); + let mut r = BufReader::new(writer.get_ref()); + assert_eq!(r.read_to_string().unwrap(), "a\nệ"); + } + + #[test] + fn test_read_whole_string_bad() { + let buf = [0xff]; + let mut r = BufReader::new(&buf); + match r.read_to_string() { + Ok(..) => panic!(), + Err(..) => {} + } + } + + #[test] + fn seek_past_end() { + let buf = [0xff]; + let mut r = BufReader::new(&buf); + r.seek(10, SeekSet).unwrap(); + assert!(r.read(&mut []).is_err()); + + let mut r = MemReader::new(vec!(10)); + r.seek(10, SeekSet).unwrap(); + assert!(r.read(&mut []).is_err()); + + let mut buf = [0]; + let mut r = BufWriter::new(&mut buf); + r.seek(10, SeekSet).unwrap(); + assert!(r.write(&[3]).is_err()); + } + + #[test] + fn seek_before_0() { + let buf = [0xff]; + let mut r = BufReader::new(&buf); + assert!(r.seek(-1, SeekSet).is_err()); + + let mut r = MemReader::new(vec!(10)); + assert!(r.seek(-1, SeekSet).is_err()); + + let mut buf = [0]; + let mut r = BufWriter::new(&mut buf); + assert!(r.seek(-1, SeekSet).is_err()); + } + + #[test] + fn io_read_at_least() { + let mut r = MemReader::new(vec![1, 2, 3, 4, 5, 6, 7, 8]); + let mut buf = [0; 3]; + assert!(r.read_at_least(buf.len(), &mut buf).is_ok()); + let b: &[_] = &[1, 2, 3]; + assert_eq!(buf, b); + assert!(r.read_at_least(0, buf.slice_to_mut(0)).is_ok()); + assert_eq!(buf, b); + assert!(r.read_at_least(buf.len(), &mut buf).is_ok()); + let b: &[_] = &[4, 5, 6]; + assert_eq!(buf, b); + assert!(r.read_at_least(buf.len(), &mut buf).is_err()); + let b: &[_] = &[7, 8, 6]; + assert_eq!(buf, b); + } + + fn do_bench_mem_writer(b: &mut Bencher, times: uint, len: uint) { + let src: Vec = repeat(5).take(len).collect(); + + b.bytes = (times * len) as u64; + b.iter(|| { + let mut wr = MemWriter::new(); + for _ in range(0, times) { + wr.write(src.as_slice()).unwrap(); + } + + let v = wr.into_inner(); + assert_eq!(v.len(), times * len); + assert!(v.iter().all(|x| *x == 5)); + }); + } + + #[bench] + fn bench_mem_writer_001_0000(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 0) + } + + #[bench] + fn bench_mem_writer_001_0010(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 10) + } + + #[bench] + fn bench_mem_writer_001_0100(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 100) + } + + #[bench] + fn bench_mem_writer_001_1000(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 1000) + } + + #[bench] + fn bench_mem_writer_100_0000(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 0) + } + + #[bench] + fn bench_mem_writer_100_0010(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 10) + } + + #[bench] + fn bench_mem_writer_100_0100(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 100) + } + + #[bench] + fn bench_mem_writer_100_1000(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 1000) + } + + #[bench] + fn bench_mem_reader(b: &mut Bencher) { + b.iter(|| { + let buf = [5 as u8; 100].to_vec(); + { + let mut rdr = MemReader::new(buf); + for _i in range(0u, 10) { + let mut buf = [0 as u8; 10]; + rdr.read(&mut buf).unwrap(); + assert_eq!(buf.as_slice(), [5; 10].as_slice()); + } + } + }); + } + + #[bench] + fn bench_buf_writer(b: &mut Bencher) { + b.iter(|| { + let mut buf = [0 as u8; 100]; + { + let mut wr = BufWriter::new(&mut buf); + for _i in range(0u, 10) { + wr.write(&[5; 10]).unwrap(); + } + } + assert_eq!(buf.as_slice(), [5; 100].as_slice()); + }); + } + + #[bench] + fn bench_buf_reader(b: &mut Bencher) { + b.iter(|| { + let buf = [5 as u8; 100]; + { + let mut rdr = BufReader::new(&buf); + for _i in range(0u, 10) { + let mut buf = [0 as u8; 10]; + rdr.read(&mut buf).unwrap(); + assert_eq!(buf, [5; 10]); + } + } + }); + } +} diff --git a/src/libstd/old_io/mod.rs b/src/libstd/old_io/mod.rs new file mode 100644 index 00000000000..7a8ed204ca5 --- /dev/null +++ b/src/libstd/old_io/mod.rs @@ -0,0 +1,1957 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// ignore-lexer-test FIXME #15883 + +// FIXME: cover these topics: +// path, reader, writer, stream, raii (close not needed), +// stdio, print!, println!, file access, process spawning, +// error handling + + +//! I/O, including files, networking, timers, and processes +//! +//! > **Warning**: This module is currently called `old_io` for a reason! The +//! > module is currently being redesigned in a number of RFCs. For more details +//! > follow the RFC repository in connection with [RFC 517][base] or follow +//! > some of these sub-RFCs +//! > +//! > * [String handling][osstr] +//! > * [Core I/O support][core] +//! > * [Deadlines][deadlines] +//! > * [std::env][env] +//! > * [std::process][process] +//! +//! [base]: https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md +//! [osstr]: https://github.com/rust-lang/rfcs/pull/575 +//! [core]: https://github.com/rust-lang/rfcs/pull/576 +//! [deadlines]: https://github.com/rust-lang/rfcs/pull/577 +//! [env]: https://github.com/rust-lang/rfcs/pull/578 +//! [process]: https://github.com/rust-lang/rfcs/pull/579 +//! +//! `std::io` provides Rust's basic I/O types, +//! for reading and writing to files, TCP, UDP, +//! and other types of sockets and pipes, +//! manipulating the file system, spawning processes. +//! +//! # Examples +//! +//! Some examples of obvious things you might want to do +//! +//! * Read lines from stdin +//! +//! ```rust +//! use std::old_io as io; +//! +//! for line in io::stdin().lock().lines() { +//! print!("{}", line.unwrap()); +//! } +//! ``` +//! +//! * Read a complete file +//! +//! ```rust +//! use std::old_io::File; +//! +//! let contents = File::open(&Path::new("message.txt")).read_to_end(); +//! ``` +//! +//! * Write a line to a file +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::File; +//! +//! let mut file = File::create(&Path::new("message.txt")); +//! file.write(b"hello, file!\n"); +//! # drop(file); +//! # ::std::old_io::fs::unlink(&Path::new("message.txt")); +//! ``` +//! +//! * Iterate over the lines of a file +//! +//! ```rust,no_run +//! use std::old_io::BufferedReader; +//! use std::old_io::File; +//! +//! let path = Path::new("message.txt"); +//! let mut file = BufferedReader::new(File::open(&path)); +//! for line in file.lines() { +//! print!("{}", line.unwrap()); +//! } +//! ``` +//! +//! * Pull the lines of a file into a vector of strings +//! +//! ```rust,no_run +//! use std::old_io::BufferedReader; +//! use std::old_io::File; +//! +//! let path = Path::new("message.txt"); +//! let mut file = BufferedReader::new(File::open(&path)); +//! let lines: Vec = file.lines().map(|x| x.unwrap()).collect(); +//! ``` +//! +//! * Make a simple TCP client connection and request +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::TcpStream; +//! +//! # // connection doesn't fail if a server is running on 8080 +//! # // locally, we still want to be type checking this code, so lets +//! # // just stop it running (#11576) +//! # if false { +//! let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap(); +//! socket.write(b"GET / HTTP/1.0\n\n"); +//! let response = socket.read_to_end(); +//! # } +//! ``` +//! +//! * Make a simple TCP server +//! +//! ```rust +//! # fn main() { } +//! # fn foo() { +//! # #![allow(dead_code)] +//! use std::old_io::{TcpListener, TcpStream}; +//! use std::old_io::{Acceptor, Listener}; +//! use std::thread::Thread; +//! +//! let listener = TcpListener::bind("127.0.0.1:80"); +//! +//! // bind the listener to the specified address +//! let mut acceptor = listener.listen(); +//! +//! fn handle_client(mut stream: TcpStream) { +//! // ... +//! # &mut stream; // silence unused mutability/variable warning +//! } +//! // accept connections and process them, spawning a new tasks for each one +//! for stream in acceptor.incoming() { +//! match stream { +//! Err(e) => { /* connection failed */ } +//! Ok(stream) => { +//! Thread::spawn(move|| { +//! // connection succeeded +//! handle_client(stream) +//! }); +//! } +//! } +//! } +//! +//! // close the socket server +//! drop(acceptor); +//! # } +//! ``` +//! +//! +//! # Error Handling +//! +//! I/O is an area where nearly every operation can result in unexpected +//! errors. Errors should be painfully visible when they happen, and handling them +//! should be easy to work with. It should be convenient to handle specific I/O +//! errors, and it should also be convenient to not deal with I/O errors. +//! +//! Rust's I/O employs a combination of techniques to reduce boilerplate +//! while still providing feedback about errors. The basic strategy: +//! +//! * All I/O operations return `IoResult` which is equivalent to +//! `Result`. The `Result` type is defined in the `std::result` +//! module. +//! * If the `Result` type goes unused, then the compiler will by default emit a +//! warning about the unused result. This is because `Result` has the +//! `#[must_use]` attribute. +//! * Common traits are implemented for `IoResult`, e.g. +//! `impl Reader for IoResult`, so that error values do not have +//! to be 'unwrapped' before use. +//! +//! These features combine in the API to allow for expressions like +//! `File::create(&Path::new("diary.txt")).write(b"Met a girl.\n")` +//! without having to worry about whether "diary.txt" exists or whether +//! the write succeeds. As written, if either `new` or `write_line` +//! encounters an error then the result of the entire expression will +//! be an error. +//! +//! If you wanted to handle the error though you might write: +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::old_io::File; +//! +//! match File::create(&Path::new("diary.txt")).write(b"Met a girl.\n") { +//! Ok(()) => (), // succeeded +//! Err(e) => println!("failed to write to my diary: {}", e), +//! } +//! +//! # ::std::old_io::fs::unlink(&Path::new("diary.txt")); +//! ``` +//! +//! So what actually happens if `create` encounters an error? +//! It's important to know that what `new` returns is not a `File` +//! but an `IoResult`. If the file does not open, then `new` will simply +//! return `Err(..)`. Because there is an implementation of `Writer` (the trait +//! required ultimately required for types to implement `write_line`) there is no +//! need to inspect or unwrap the `IoResult` and we simply call `write_line` +//! on it. If `new` returned an `Err(..)` then the followup call to `write_line` +//! will also return an error. +//! +//! ## `try!` +//! +//! Explicit pattern matching on `IoResult`s can get quite verbose, especially +//! when performing many I/O operations. Some examples (like those above) are +//! alleviated with extra methods implemented on `IoResult`, but others have more +//! complex interdependencies among each I/O operation. +//! +//! The `try!` macro from `std::macros` is provided as a method of early-return +//! inside `Result`-returning functions. It expands to an early-return on `Err` +//! and otherwise unwraps the contained `Ok` value. +//! +//! If you wanted to read several `u32`s from a file and return their product: +//! +//! ```rust +//! use std::old_io::{File, IoResult}; +//! +//! fn file_product(p: &Path) -> IoResult { +//! let mut f = File::open(p); +//! let x1 = try!(f.read_le_u32()); +//! let x2 = try!(f.read_le_u32()); +//! +//! Ok(x1 * x2) +//! } +//! +//! match file_product(&Path::new("numbers.bin")) { +//! Ok(x) => println!("{}", x), +//! Err(e) => println!("Failed to read numbers!") +//! } +//! ``` +//! +//! With `try!` in `file_product`, each `read_le_u32` need not be directly +//! concerned with error handling; instead its caller is responsible for +//! responding to errors that may occur while attempting to read the numbers. + +#![unstable] +#![deny(unused_must_use)] + +pub use self::SeekStyle::*; +pub use self::FileMode::*; +pub use self::FileAccess::*; +pub use self::IoErrorKind::*; + +use char::CharExt; +use default::Default; +use error::Error; +use fmt; +use int; +use iter::{Iterator, IteratorExt}; +use marker::Sized; +use mem::transmute; +use ops::FnOnce; +use option::Option; +use option::Option::{Some, None}; +use os; +use boxed::Box; +use result::Result; +use result::Result::{Ok, Err}; +use sys; +use slice::SliceExt; +use str::StrExt; +use str; +use string::String; +use uint; +use unicode; +use vec::Vec; + +// Reexports +pub use self::stdio::stdin; +pub use self::stdio::stdout; +pub use self::stdio::stderr; +pub use self::stdio::print; +pub use self::stdio::println; + +pub use self::fs::File; +pub use self::timer::Timer; +pub use self::net::ip::IpAddr; +pub use self::net::tcp::TcpListener; +pub use self::net::tcp::TcpStream; +pub use self::pipe::PipeStream; +pub use self::process::{Process, Command}; +pub use self::tempfile::TempDir; + +pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter}; +pub use self::buffered::{BufferedReader, BufferedWriter, BufferedStream, + LineBufferedWriter}; +pub use self::comm_adapters::{ChanReader, ChanWriter}; + +mod buffered; +mod comm_adapters; +mod mem; +mod result; +mod tempfile; +pub mod extensions; +pub mod fs; +pub mod net; +pub mod pipe; +pub mod process; +pub mod stdio; +pub mod timer; +pub mod util; + +#[macro_use] +pub mod test; + +/// The default buffer size for various I/O operations +// libuv recommends 64k buffers to maximize throughput +// https://groups.google.com/forum/#!topic/libuv/oQO1HJAIDdA +const DEFAULT_BUF_SIZE: uint = 1024 * 64; + +/// A convenient typedef of the return value of any I/O action. +pub type IoResult = Result; + +/// The type passed to I/O condition handlers to indicate error +/// +/// # FIXME +/// +/// Is something like this sufficient? It's kind of archaic +#[derive(PartialEq, Eq, Clone, Show)] +pub struct IoError { + /// An enumeration which can be matched against for determining the flavor + /// of error. + pub kind: IoErrorKind, + /// A human-readable description about the error + pub desc: &'static str, + /// Detailed information about this error, not always available + pub detail: Option +} + +impl IoError { + /// Convert an `errno` value into an `IoError`. + /// + /// If `detail` is `true`, the `detail` field of the `IoError` + /// struct is filled with an allocated string describing the error + /// in more detail, retrieved from the operating system. + pub fn from_errno(errno: uint, detail: bool) -> IoError { + let mut err = sys::decode_error(errno as i32); + if detail && err.kind == OtherIoError { + err.detail = Some(os::error_string(errno).chars() + .map(|c| c.to_lowercase()).collect()) + } + err + } + + /// Retrieve the last error to occur as a (detailed) IoError. + /// + /// This uses the OS `errno`, and so there should not be any task + /// descheduling or migration (other than that performed by the + /// operating system) between the call(s) for which errors are + /// being checked and the call of this function. + pub fn last_error() -> IoError { + IoError::from_errno(os::errno() as uint, true) + } +} + +#[stable] +impl fmt::Display for IoError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + IoError { kind: OtherIoError, desc: "unknown error", detail: Some(ref detail) } => + write!(fmt, "{}", detail), + IoError { detail: None, desc, .. } => + write!(fmt, "{}", desc), + IoError { detail: Some(ref detail), desc, .. } => + write!(fmt, "{} ({})", desc, detail) + } + } +} + +impl Error for IoError { + fn description(&self) -> &str { self.desc } +} + +/// A list specifying general categories of I/O error. +#[derive(Copy, PartialEq, Eq, Clone, Show)] +pub enum IoErrorKind { + /// Any I/O error not part of this list. + OtherIoError, + /// The operation could not complete because end of file was reached. + EndOfFile, + /// The file was not found. + FileNotFound, + /// The file permissions disallowed access to this file. + PermissionDenied, + /// A network connection failed for some reason not specified in this list. + ConnectionFailed, + /// The network operation failed because the network connection was closed. + Closed, + /// The connection was refused by the remote server. + ConnectionRefused, + /// The connection was reset by the remote server. + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + ConnectionAborted, + /// The network operation failed because it was not connected yet. + NotConnected, + /// The operation failed because a pipe was closed. + BrokenPipe, + /// A file already existed with that name. + PathAlreadyExists, + /// No file exists at that location. + PathDoesntExist, + /// The path did not specify the type of file that this operation required. For example, + /// attempting to copy a directory with the `fs::copy()` operation will fail with this error. + MismatchedFileTypeForOperation, + /// The operation temporarily failed (for example, because a signal was received), and retrying + /// may succeed. + ResourceUnavailable, + /// No I/O functionality is available for this task. + IoUnavailable, + /// A parameter was incorrect in a way that caused an I/O error not part of this list. + InvalidInput, + /// The I/O operation's timeout expired, causing it to be canceled. + TimedOut, + /// This write operation failed to write all of its data. + /// + /// Normally the write() method on a Writer guarantees that all of its data + /// has been written, but some operations may be terminated after only + /// partially writing some data. An example of this is a timed out write + /// which successfully wrote a known number of bytes, but bailed out after + /// doing so. + /// + /// The payload contained as part of this variant is the number of bytes + /// which are known to have been successfully written. + ShortWrite(uint), + /// The Reader returned 0 bytes from `read()` too many times. + NoProgress, +} + +/// A trait that lets you add a `detail` to an IoError easily +trait UpdateIoError { + /// Returns an IoError with updated description and detail + fn update_err(self, desc: &'static str, detail: D) -> Self where + D: FnOnce(&IoError) -> String; + + /// Returns an IoError with updated detail + fn update_detail(self, detail: D) -> Self where + D: FnOnce(&IoError) -> String; + + /// Returns an IoError with update description + fn update_desc(self, desc: &'static str) -> Self; +} + +impl UpdateIoError for IoResult { + fn update_err(self, desc: &'static str, detail: D) -> IoResult where + D: FnOnce(&IoError) -> String, + { + self.map_err(move |mut e| { + let detail = detail(&e); + e.desc = desc; + e.detail = Some(detail); + e + }) + } + + fn update_detail(self, detail: D) -> IoResult where + D: FnOnce(&IoError) -> String, + { + self.map_err(move |mut e| { e.detail = Some(detail(&e)); e }) + } + + fn update_desc(self, desc: &'static str) -> IoResult { + self.map_err(|mut e| { e.desc = desc; e }) + } +} + +static NO_PROGRESS_LIMIT: uint = 1000; + +/// A trait for objects which are byte-oriented streams. Readers are defined by +/// one method, `read`. This function will block until data is available, +/// filling in the provided buffer with any data read. +/// +/// Readers are intended to be composable with one another. Many objects +/// throughout the I/O and related libraries take and provide types which +/// implement the `Reader` trait. +pub trait Reader { + + // Only method which need to get implemented for this trait + + /// Read bytes, up to the length of `buf` and place them in `buf`. + /// Returns the number of bytes read. The number of bytes read may + /// be less than the number requested, even 0. Returns `Err` on EOF. + /// + /// # Error + /// + /// If an error occurs during this I/O operation, then it is returned as + /// `Err(IoError)`. Note that end-of-file is considered an error, and can be + /// inspected for in the error's `kind` field. Also note that reading 0 + /// bytes is not considered an error in all circumstances + /// + /// # Implementation Note + /// + /// When implementing this method on a new Reader, you are strongly encouraged + /// not to return 0 if you can avoid it. + fn read(&mut self, buf: &mut [u8]) -> IoResult; + + // Convenient helper methods based on the above methods + + /// Reads at least `min` bytes and places them in `buf`. + /// Returns the number of bytes read. + /// + /// This will continue to call `read` until at least `min` bytes have been + /// read. If `read` returns 0 too many times, `NoProgress` will be + /// returned. + /// + /// # Error + /// + /// If an error occurs at any point, that error is returned, and no further + /// bytes are read. + fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult { + if min > buf.len() { + return Err(IoError { + detail: Some(String::from_str("the buffer is too short")), + ..standard_error(InvalidInput) + }); + } + let mut read = 0; + while read < min { + let mut zeroes = 0; + loop { + match self.read(&mut buf[read..]) { + Ok(0) => { + zeroes += 1; + if zeroes >= NO_PROGRESS_LIMIT { + return Err(standard_error(NoProgress)); + } + } + Ok(n) => { + read += n; + break; + } + err@Err(_) => return err + } + } + } + Ok(read) + } + + /// Reads a single byte. Returns `Err` on EOF. + fn read_byte(&mut self) -> IoResult { + let mut buf = [0]; + try!(self.read_at_least(1, &mut buf)); + Ok(buf[0]) + } + + /// Reads up to `len` bytes and appends them to a vector. + /// Returns the number of bytes read. The number of bytes read may be + /// less than the number requested, even 0. Returns Err on EOF. + /// + /// # Error + /// + /// If an error occurs during this I/O operation, then it is returned + /// as `Err(IoError)`. See `read()` for more details. + fn push(&mut self, len: uint, buf: &mut Vec) -> IoResult { + let start_len = buf.len(); + buf.reserve(len); + + let n = { + let s = unsafe { slice_vec_capacity(buf, start_len, start_len + len) }; + try!(self.read(s)) + }; + unsafe { buf.set_len(start_len + n) }; + Ok(n) + } + + /// Reads at least `min` bytes, but no more than `len`, and appends them to + /// a vector. + /// Returns the number of bytes read. + /// + /// This will continue to call `read` until at least `min` bytes have been + /// read. If `read` returns 0 too many times, `NoProgress` will be + /// returned. + /// + /// # Error + /// + /// If an error occurs at any point, that error is returned, and no further + /// bytes are read. + fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec) -> IoResult { + if min > len { + return Err(IoError { + detail: Some(String::from_str("the buffer is too short")), + ..standard_error(InvalidInput) + }); + } + + let start_len = buf.len(); + buf.reserve(len); + + // we can't just use self.read_at_least(min, slice) because we need to push + // successful reads onto the vector before any returned errors. + + let mut read = 0; + while read < min { + read += { + let s = unsafe { slice_vec_capacity(buf, start_len + read, start_len + len) }; + try!(self.read_at_least(1, s)) + }; + unsafe { buf.set_len(start_len + read) }; + } + Ok(read) + } + + /// Reads exactly `len` bytes and gives you back a new vector of length + /// `len` + /// + /// # Error + /// + /// Fails with the same conditions as `read`. Additionally returns error + /// on EOF. Note that if an error is returned, then some number of bytes may + /// have already been consumed from the underlying reader, and they are lost + /// (not returned as part of the error). If this is unacceptable, then it is + /// recommended to use the `push_at_least` or `read` methods. + fn read_exact(&mut self, len: uint) -> IoResult> { + let mut buf = Vec::with_capacity(len); + match self.push_at_least(len, len, &mut buf) { + Ok(_) => Ok(buf), + Err(e) => Err(e), + } + } + + /// Reads all remaining bytes from the stream. + /// + /// # Error + /// + /// Returns any non-EOF error immediately. Previously read bytes are + /// discarded when an error is returned. + /// + /// When EOF is encountered, all bytes read up to that point are returned. + fn read_to_end(&mut self) -> IoResult> { + let mut buf = Vec::with_capacity(DEFAULT_BUF_SIZE); + loop { + match self.push_at_least(1, DEFAULT_BUF_SIZE, &mut buf) { + Ok(_) => {} + Err(ref e) if e.kind == EndOfFile => break, + Err(e) => return Err(e) + } + } + return Ok(buf); + } + + /// Reads all of the remaining bytes of this stream, interpreting them as a + /// UTF-8 encoded stream. The corresponding string is returned. + /// + /// # Error + /// + /// This function returns all of the same errors as `read_to_end` with an + /// additional error if the reader's contents are not a valid sequence of + /// UTF-8 bytes. + fn read_to_string(&mut self) -> IoResult { + self.read_to_end().and_then(|s| { + match String::from_utf8(s) { + Ok(s) => Ok(s), + Err(_) => Err(standard_error(InvalidInput)), + } + }) + } + + // Byte conversion helpers + + /// Reads `n` little-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64; + let mut pos = 0; + let mut i = nbytes; + while i > 0 { + val += (try!(self.read_u8()) as u64) << pos; + pos += 8; + i -= 1; + } + Ok(val) + } + + /// Reads `n` little-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_le_int_n(&mut self, nbytes: uint) -> IoResult { + self.read_le_uint_n(nbytes).map(|i| extend_sign(i, nbytes)) + } + + /// Reads `n` big-endian unsigned integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64; + let mut i = nbytes; + while i > 0 { + i -= 1; + val += (try!(self.read_u8()) as u64) << i * 8; + } + Ok(val) + } + + /// Reads `n` big-endian signed integer bytes. + /// + /// `n` must be between 1 and 8, inclusive. + fn read_be_int_n(&mut self, nbytes: uint) -> IoResult { + self.read_be_uint_n(nbytes).map(|i| extend_sign(i, nbytes)) + } + + /// Reads a little-endian unsigned integer. + /// + /// The number of bytes returned is system-dependent. + fn read_le_uint(&mut self) -> IoResult { + self.read_le_uint_n(uint::BYTES).map(|i| i as uint) + } + + /// Reads a little-endian integer. + /// + /// The number of bytes returned is system-dependent. + fn read_le_int(&mut self) -> IoResult { + self.read_le_int_n(int::BYTES).map(|i| i as int) + } + + /// Reads a big-endian unsigned integer. + /// + /// The number of bytes returned is system-dependent. + fn read_be_uint(&mut self) -> IoResult { + self.read_be_uint_n(uint::BYTES).map(|i| i as uint) + } + + /// Reads a big-endian integer. + /// + /// The number of bytes returned is system-dependent. + fn read_be_int(&mut self) -> IoResult { + self.read_be_int_n(int::BYTES).map(|i| i as int) + } + + /// Reads a big-endian `u64`. + /// + /// `u64`s are 8 bytes long. + fn read_be_u64(&mut self) -> IoResult { + self.read_be_uint_n(8) + } + + /// Reads a big-endian `u32`. + /// + /// `u32`s are 4 bytes long. + fn read_be_u32(&mut self) -> IoResult { + self.read_be_uint_n(4).map(|i| i as u32) + } + + /// Reads a big-endian `u16`. + /// + /// `u16`s are 2 bytes long. + fn read_be_u16(&mut self) -> IoResult { + self.read_be_uint_n(2).map(|i| i as u16) + } + + /// Reads a big-endian `i64`. + /// + /// `i64`s are 8 bytes long. + fn read_be_i64(&mut self) -> IoResult { + self.read_be_int_n(8) + } + + /// Reads a big-endian `i32`. + /// + /// `i32`s are 4 bytes long. + fn read_be_i32(&mut self) -> IoResult { + self.read_be_int_n(4).map(|i| i as i32) + } + + /// Reads a big-endian `i16`. + /// + /// `i16`s are 2 bytes long. + fn read_be_i16(&mut self) -> IoResult { + self.read_be_int_n(2).map(|i| i as i16) + } + + /// Reads a big-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + fn read_be_f64(&mut self) -> IoResult { + self.read_be_u64().map(|i| unsafe { + transmute::(i) + }) + } + + /// Reads a big-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + fn read_be_f32(&mut self) -> IoResult { + self.read_be_u32().map(|i| unsafe { + transmute::(i) + }) + } + + /// Reads a little-endian `u64`. + /// + /// `u64`s are 8 bytes long. + fn read_le_u64(&mut self) -> IoResult { + self.read_le_uint_n(8) + } + + /// Reads a little-endian `u32`. + /// + /// `u32`s are 4 bytes long. + fn read_le_u32(&mut self) -> IoResult { + self.read_le_uint_n(4).map(|i| i as u32) + } + + /// Reads a little-endian `u16`. + /// + /// `u16`s are 2 bytes long. + fn read_le_u16(&mut self) -> IoResult { + self.read_le_uint_n(2).map(|i| i as u16) + } + + /// Reads a little-endian `i64`. + /// + /// `i64`s are 8 bytes long. + fn read_le_i64(&mut self) -> IoResult { + self.read_le_int_n(8) + } + + /// Reads a little-endian `i32`. + /// + /// `i32`s are 4 bytes long. + fn read_le_i32(&mut self) -> IoResult { + self.read_le_int_n(4).map(|i| i as i32) + } + + /// Reads a little-endian `i16`. + /// + /// `i16`s are 2 bytes long. + fn read_le_i16(&mut self) -> IoResult { + self.read_le_int_n(2).map(|i| i as i16) + } + + /// Reads a little-endian `f64`. + /// + /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. + fn read_le_f64(&mut self) -> IoResult { + self.read_le_u64().map(|i| unsafe { + transmute::(i) + }) + } + + /// Reads a little-endian `f32`. + /// + /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. + fn read_le_f32(&mut self) -> IoResult { + self.read_le_u32().map(|i| unsafe { + transmute::(i) + }) + } + + /// Read a u8. + /// + /// `u8`s are 1 byte. + fn read_u8(&mut self) -> IoResult { + self.read_byte() + } + + /// Read an i8. + /// + /// `i8`s are 1 byte. + fn read_i8(&mut self) -> IoResult { + self.read_byte().map(|i| i as i8) + } +} + +/// A reader which can be converted to a RefReader. +pub trait ByRefReader { + /// Creates a wrapper around a mutable reference to the reader. + /// + /// This is useful to allow applying adaptors while still + /// retaining ownership of the original value. + fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self>; +} + +impl ByRefReader for T { + fn by_ref<'a>(&'a mut self) -> RefReader<'a, T> { + RefReader { inner: self } + } +} + +/// A reader which can be converted to bytes. +pub trait BytesReader { + /// Create an iterator that reads a single byte on + /// each iteration, until EOF. + /// + /// # Error + /// + /// Any error other than `EndOfFile` that is produced by the underlying Reader + /// is returned by the iterator and should be handled by the caller. + fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, Self>; +} + +impl BytesReader for T { + fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, T> { + extensions::Bytes::new(self) + } +} + +impl<'a> Reader for Box { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + let reader: &mut Reader = &mut **self; + reader.read(buf) + } +} + +impl<'a> Reader for &'a mut (Reader+'a) { + fn read(&mut self, buf: &mut [u8]) -> IoResult { (*self).read(buf) } +} + +/// Returns a slice of `v` between `start` and `end`. +/// +/// Similar to `slice()` except this function only bounds the slice on the +/// capacity of `v`, not the length. +/// +/// # Panics +/// +/// Panics when `start` or `end` point outside the capacity of `v`, or when +/// `start` > `end`. +// Private function here because we aren't sure if we want to expose this as +// API yet. If so, it should be a method on Vec. +unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec, start: uint, end: uint) -> &'a mut [T] { + use raw::Slice; + use ptr::PtrExt; + + assert!(start <= end); + assert!(end <= v.capacity()); + transmute(Slice { + data: v.as_ptr().offset(start as int), + len: end - start + }) +} + +/// A `RefReader` is a struct implementing `Reader` which contains a reference +/// to another reader. This is often useful when composing streams. +/// +/// # Examples +/// +/// ``` +/// use std::old_io as io; +/// use std::old_io::ByRefReader; +/// use std::old_io::util::LimitReader; +/// +/// fn process_input(r: R) {} +/// +/// let mut stream = io::stdin(); +/// +/// // Only allow the function to process at most one kilobyte of input +/// { +/// let stream = LimitReader::new(stream.by_ref(), 1024); +/// process_input(stream); +/// } +/// +/// // 'stream' is still available for use here +/// ``` +pub struct RefReader<'a, R:'a> { + /// The underlying reader which this is referencing + inner: &'a mut R +} + +impl<'a, R: Reader> Reader for RefReader<'a, R> { + fn read(&mut self, buf: &mut [u8]) -> IoResult { self.inner.read(buf) } +} + +impl<'a, R: Buffer> Buffer for RefReader<'a, R> { + fn fill_buf(&mut self) -> IoResult<&[u8]> { self.inner.fill_buf() } + fn consume(&mut self, amt: uint) { self.inner.consume(amt) } +} + +fn extend_sign(val: u64, nbytes: uint) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + +/// A trait for objects which are byte-oriented streams. Writers are defined by +/// one method, `write`. This function will block until the provided buffer of +/// bytes has been entirely written, and it will return any failures which occur. +/// +/// Another commonly overridden method is the `flush` method for writers such as +/// buffered writers. +/// +/// Writers are intended to be composable with one another. Many objects +/// throughout the I/O and related libraries take and provide types which +/// implement the `Writer` trait. +pub trait Writer { + /// Write the entirety of a given buffer + /// + /// # Errors + /// + /// If an error happens during the I/O operation, the error is returned as + /// `Err`. Note that it is considered an error if the entire buffer could + /// not be written, and if an error is returned then it is unknown how much + /// data (if any) was actually written. + fn write(&mut self, buf: &[u8]) -> IoResult<()>; + + /// Flush this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// This is by default a no-op and implementers of the `Writer` trait should + /// decide whether their stream needs to be buffered or not. + fn flush(&mut self) -> IoResult<()> { Ok(()) } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the `format_args!` + /// macro, but it is rare that this should explicitly be called. The + /// `write!` macro should be favored to invoke this method instead. + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> { + // Create a shim which translates a Writer to a fmt::Writer and saves + // off I/O errors. instead of discarding them + struct Adaptor<'a, T: ?Sized +'a> { + inner: &'a mut T, + error: IoResult<()>, + } + + impl<'a, T: ?Sized + Writer> fmt::Writer for Adaptor<'a, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adaptor { inner: self, error: Ok(()) }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => output.error + } + } + + + /// Write a rust string into this sink. + /// + /// The bytes written will be the UTF-8 encoded version of the input string. + /// If other encodings are desired, it is recommended to compose this stream + /// with another performing the conversion, or to use `write` with a + /// converted byte-array instead. + #[inline] + fn write_str(&mut self, s: &str) -> IoResult<()> { + self.write(s.as_bytes()) + } + + /// Writes a string into this sink, and then writes a literal newline (`\n`) + /// byte afterwards. Note that the writing of the newline is *not* atomic in + /// the sense that the call to `write` is invoked twice (once with the + /// string and once with a newline character). + /// + /// If other encodings or line ending flavors are desired, it is recommended + /// that the `write` method is used specifically instead. + #[inline] + fn write_line(&mut self, s: &str) -> IoResult<()> { + self.write_str(s).and_then(|()| self.write(&[b'\n'])) + } + + /// Write a single char, encoded as UTF-8. + #[inline] + fn write_char(&mut self, c: char) -> IoResult<()> { + let mut buf = [0u8; 4]; + let n = c.encode_utf8(buf.as_mut_slice()).unwrap_or(0); + self.write(&buf[..n]) + } + + /// Write the result of passing n through `int::to_str_bytes`. + #[inline] + fn write_int(&mut self, n: int) -> IoResult<()> { + write!(self, "{}", n) + } + + /// Write the result of passing n through `uint::to_str_bytes`. + #[inline] + fn write_uint(&mut self, n: uint) -> IoResult<()> { + write!(self, "{}", n) + } + + /// Write a little-endian uint (number of bytes depends on system). + #[inline] + fn write_le_uint(&mut self, n: uint) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, uint::BYTES, |v| self.write(v)) + } + + /// Write a little-endian int (number of bytes depends on system). + #[inline] + fn write_le_int(&mut self, n: int) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, int::BYTES, |v| self.write(v)) + } + + /// Write a big-endian uint (number of bytes depends on system). + #[inline] + fn write_be_uint(&mut self, n: uint) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, uint::BYTES, |v| self.write(v)) + } + + /// Write a big-endian int (number of bytes depends on system). + #[inline] + fn write_be_int(&mut self, n: int) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, int::BYTES, |v| self.write(v)) + } + + /// Write a big-endian u64 (8 bytes). + #[inline] + fn write_be_u64(&mut self, n: u64) -> IoResult<()> { + extensions::u64_to_be_bytes(n, 8u, |v| self.write(v)) + } + + /// Write a big-endian u32 (4 bytes). + #[inline] + fn write_be_u32(&mut self, n: u32) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + + /// Write a big-endian u16 (2 bytes). + #[inline] + fn write_be_u16(&mut self, n: u16) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + + /// Write a big-endian i64 (8 bytes). + #[inline] + fn write_be_i64(&mut self, n: i64) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) + } + + /// Write a big-endian i32 (4 bytes). + #[inline] + fn write_be_i32(&mut self, n: i32) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + + /// Write a big-endian i16 (2 bytes). + #[inline] + fn write_be_i16(&mut self, n: i16) -> IoResult<()> { + extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + + /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). + #[inline] + fn write_be_f64(&mut self, f: f64) -> IoResult<()> { + unsafe { + self.write_be_u64(transmute(f)) + } + } + + /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). + #[inline] + fn write_be_f32(&mut self, f: f32) -> IoResult<()> { + unsafe { + self.write_be_u32(transmute(f)) + } + } + + /// Write a little-endian u64 (8 bytes). + #[inline] + fn write_le_u64(&mut self, n: u64) -> IoResult<()> { + extensions::u64_to_le_bytes(n, 8u, |v| self.write(v)) + } + + /// Write a little-endian u32 (4 bytes). + #[inline] + fn write_le_u32(&mut self, n: u32) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + + /// Write a little-endian u16 (2 bytes). + #[inline] + fn write_le_u16(&mut self, n: u16) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + + /// Write a little-endian i64 (8 bytes). + #[inline] + fn write_le_i64(&mut self, n: i64) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) + } + + /// Write a little-endian i32 (4 bytes). + #[inline] + fn write_le_i32(&mut self, n: i32) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + + /// Write a little-endian i16 (2 bytes). + #[inline] + fn write_le_i16(&mut self, n: i16) -> IoResult<()> { + extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + + /// Write a little-endian IEEE754 double-precision floating-point + /// (8 bytes). + #[inline] + fn write_le_f64(&mut self, f: f64) -> IoResult<()> { + unsafe { + self.write_le_u64(transmute(f)) + } + } + + /// Write a little-endian IEEE754 single-precision floating-point + /// (4 bytes). + #[inline] + fn write_le_f32(&mut self, f: f32) -> IoResult<()> { + unsafe { + self.write_le_u32(transmute(f)) + } + } + + /// Write a u8 (1 byte). + #[inline] + fn write_u8(&mut self, n: u8) -> IoResult<()> { + self.write(&[n]) + } + + /// Write an i8 (1 byte). + #[inline] + fn write_i8(&mut self, n: i8) -> IoResult<()> { + self.write(&[n as u8]) + } +} + +/// A writer which can be converted to a RefWriter. +pub trait ByRefWriter { + /// Creates a wrapper around a mutable reference to the writer. + /// + /// This is useful to allow applying wrappers while still + /// retaining ownership of the original value. + #[inline] + fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self>; +} + +impl ByRefWriter for T { + fn by_ref<'a>(&'a mut self) -> RefWriter<'a, T> { + RefWriter { inner: self } + } +} + +impl<'a> Writer for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + (&mut **self).write(buf) + } + + #[inline] + fn flush(&mut self) -> IoResult<()> { + (&mut **self).flush() + } +} + +impl<'a> Writer for &'a mut (Writer+'a) { + #[inline] + fn write(&mut self, buf: &[u8]) -> IoResult<()> { (**self).write(buf) } + + #[inline] + fn flush(&mut self) -> IoResult<()> { (**self).flush() } +} + +/// A `RefWriter` is a struct implementing `Writer` which contains a reference +/// to another writer. This is often useful when composing streams. +/// +/// # Example +/// +/// ``` +/// use std::old_io::util::TeeReader; +/// use std::old_io::{stdin, ByRefWriter}; +/// +/// fn process_input(r: R) {} +/// +/// let mut output = Vec::new(); +/// +/// { +/// // Don't give ownership of 'output' to the 'tee'. Instead we keep a +/// // handle to it in the outer scope +/// let mut tee = TeeReader::new(stdin(), output.by_ref()); +/// process_input(tee); +/// } +/// +/// println!("input processed: {:?}", output); +/// ``` +pub struct RefWriter<'a, W:'a> { + /// The underlying writer which this is referencing + inner: &'a mut W +} + +impl<'a, W: Writer> Writer for RefWriter<'a, W> { + #[inline] + fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.inner.write(buf) } + + #[inline] + fn flush(&mut self) -> IoResult<()> { self.inner.flush() } +} + + +/// A Stream is a readable and a writable object. Data written is typically +/// received by the object which reads receive data from. +pub trait Stream: Reader + Writer { } + +impl Stream for T {} + +/// An iterator that reads a line on each iteration, +/// until `.read_line()` encounters `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Lines` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Error +/// +/// Any error other than `EndOfFile` that is produced by the underlying Reader +/// is returned by the iterator and should be handled by the caller. +pub struct Lines<'r, T:'r> { + buffer: &'r mut T, +} + +impl<'r, T: Buffer> Iterator for Lines<'r, T> { + type Item = IoResult; + + fn next(&mut self) -> Option> { + match self.buffer.read_line() { + Ok(x) => Some(Ok(x)), + Err(IoError { kind: EndOfFile, ..}) => None, + Err(y) => Some(Err(y)) + } + } +} + +/// An iterator that reads a utf8-encoded character on each iteration, +/// until `.read_char()` encounters `EndOfFile`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `Chars` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Error +/// +/// Any error other than `EndOfFile` that is produced by the underlying Reader +/// is returned by the iterator and should be handled by the caller. +pub struct Chars<'r, T:'r> { + buffer: &'r mut T +} + +impl<'r, T: Buffer> Iterator for Chars<'r, T> { + type Item = IoResult; + + fn next(&mut self) -> Option> { + match self.buffer.read_char() { + Ok(x) => Some(Ok(x)), + Err(IoError { kind: EndOfFile, ..}) => None, + Err(y) => Some(Err(y)) + } + } +} + +/// A Buffer is a type of reader which has some form of internal buffering to +/// allow certain kinds of reading operations to be more optimized than others. +/// This type extends the `Reader` trait with a few methods that are not +/// possible to reasonably implement with purely a read interface. +pub trait Buffer: Reader { + /// Fills the internal buffer of this object, returning the buffer contents. + /// Note that none of the contents will be "read" in the sense that later + /// calling `read` may return the same contents. + /// + /// The `consume` function must be called with the number of bytes that are + /// consumed from this buffer returned to ensure that the bytes are never + /// returned twice. + /// + /// # Error + /// + /// This function will return an I/O error if the underlying reader was + /// read, but returned an error. Note that it is not an error to return a + /// 0-length buffer. + fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]>; + + /// Tells this buffer that `amt` bytes have been consumed from the buffer, + /// so they should no longer be returned in calls to `read`. + fn consume(&mut self, amt: uint); + + /// Reads the next line of input, interpreted as a sequence of UTF-8 + /// encoded Unicode codepoints. If a newline is encountered, then the + /// newline is contained in the returned string. + /// + /// # Example + /// + /// ```rust + /// use std::old_io::BufReader; + /// + /// let mut reader = BufReader::new(b"hello\nworld"); + /// assert_eq!("hello\n", &*reader.read_line().unwrap()); + /// ``` + /// + /// # Error + /// + /// This function has the same error semantics as `read_until`: + /// + /// * All non-EOF errors will be returned immediately + /// * If an error is returned previously consumed bytes are lost + /// * EOF is only returned if no bytes have been read + /// * Reach EOF may mean that the delimiter is not present in the return + /// value + /// + /// Additionally, this function can fail if the line of input read is not a + /// valid UTF-8 sequence of bytes. + fn read_line(&mut self) -> IoResult { + self.read_until(b'\n').and_then(|line| + match String::from_utf8(line) { + Ok(s) => Ok(s), + Err(_) => Err(standard_error(InvalidInput)), + } + ) + } + + /// Reads a sequence of bytes leading up to a specified delimiter. Once the + /// specified byte is encountered, reading ceases and the bytes up to and + /// including the delimiter are returned. + /// + /// # Error + /// + /// If any I/O error is encountered other than EOF, the error is immediately + /// returned. Note that this may discard bytes which have already been read, + /// and those bytes will *not* be returned. It is recommended to use other + /// methods if this case is worrying. + /// + /// If EOF is encountered, then this function will return EOF if 0 bytes + /// have been read, otherwise the pending byte buffer is returned. This + /// is the reason that the byte buffer returned may not always contain the + /// delimiter. + fn read_until(&mut self, byte: u8) -> IoResult> { + let mut res = Vec::new(); + + loop { + let (done, used) = { + let available = match self.fill_buf() { + Ok(n) => n, + Err(ref e) if res.len() > 0 && e.kind == EndOfFile => { + return Ok(res); + } + Err(e) => return Err(e) + }; + match available.iter().position(|&b| b == byte) { + Some(i) => { + res.push_all(&available[..i + 1]); + (true, i + 1) + } + None => { + res.push_all(available); + (false, available.len()) + } + } + }; + self.consume(used); + if done { + return Ok(res); + } + } + } + + /// Reads the next utf8-encoded character from the underlying stream. + /// + /// # Error + /// + /// If an I/O error occurs, or EOF, then this function will return `Err`. + /// This function will also return error if the stream does not contain a + /// valid utf-8 encoded codepoint as the next few bytes in the stream. + fn read_char(&mut self) -> IoResult { + let first_byte = try!(self.read_byte()); + let width = unicode::str::utf8_char_width(first_byte); + if width == 1 { return Ok(first_byte as char) } + if width == 0 { return Err(standard_error(InvalidInput)) } // not utf8 + let mut buf = [first_byte, 0, 0, 0]; + { + let mut start = 1; + while start < width { + match try!(self.read(&mut buf[start .. width])) { + n if n == width - start => break, + n if n < width - start => { start += n; } + _ => return Err(standard_error(InvalidInput)), + } + } + } + match str::from_utf8(&buf[..width]).ok() { + Some(s) => Ok(s.char_at(0)), + None => Err(standard_error(InvalidInput)) + } + } +} + +/// Extension methods for the Buffer trait which are included in the prelude. +pub trait BufferPrelude { + /// Create an iterator that reads a utf8-encoded character on each iteration + /// until EOF. + /// + /// # Error + /// + /// Any error other than `EndOfFile` that is produced by the underlying Reader + /// is returned by the iterator and should be handled by the caller. + fn chars<'r>(&'r mut self) -> Chars<'r, Self>; + + /// Create an iterator that reads a line on each iteration until EOF. + /// + /// # Error + /// + /// Any error other than `EndOfFile` that is produced by the underlying Reader + /// is returned by the iterator and should be handled by the caller. + fn lines<'r>(&'r mut self) -> Lines<'r, Self>; +} + +impl BufferPrelude for T { + fn chars<'r>(&'r mut self) -> Chars<'r, T> { + Chars { buffer: self } + } + + fn lines<'r>(&'r mut self) -> Lines<'r, T> { + Lines { buffer: self } + } +} + +/// When seeking, the resulting cursor is offset from a base by the offset given +/// to the `seek` function. The base used is specified by this enumeration. +#[derive(Copy)] +pub enum SeekStyle { + /// Seek from the beginning of the stream + SeekSet, + /// Seek from the end of the stream + SeekEnd, + /// Seek from the current position + SeekCur, +} + +/// An object implementing `Seek` internally has some form of cursor which can +/// be moved within a stream of bytes. The stream typically has a fixed size, +/// allowing seeking relative to either end. +pub trait Seek { + /// Return position of file cursor in the stream + fn tell(&self) -> IoResult; + + /// Seek to an offset in a stream + /// + /// A successful seek clears the EOF indicator. Seeking beyond EOF is + /// allowed, but seeking before position 0 is not allowed. + /// + /// # Errors + /// + /// * Seeking to a negative offset is considered an error + /// * Seeking past the end of the stream does not modify the underlying + /// stream, but the next write may cause the previous data to be filled in + /// with a bit pattern. + fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>; +} + +/// A listener is a value that can consume itself to start listening for +/// connections. +/// +/// Doing so produces some sort of Acceptor. +pub trait Listener> { + /// Spin up the listener and start queuing incoming connections + /// + /// # Error + /// + /// Returns `Err` if this listener could not be bound to listen for + /// connections. In all cases, this listener is consumed. + fn listen(self) -> IoResult; +} + +/// An acceptor is a value that presents incoming connections +pub trait Acceptor { + /// Wait for and accept an incoming connection + /// + /// # Error + /// + /// Returns `Err` if an I/O error is encountered. + fn accept(&mut self) -> IoResult; + + /// Create an iterator over incoming connection attempts. + /// + /// Note that I/O errors will be yielded by the iterator itself. + fn incoming<'r>(&'r mut self) -> IncomingConnections<'r, Self> { + IncomingConnections { inc: self } + } +} + +/// An infinite iterator over incoming connection attempts. +/// Calling `next` will block the task until a connection is attempted. +/// +/// Since connection attempts can continue forever, this iterator always returns +/// `Some`. The `Some` contains the `IoResult` representing whether the +/// connection attempt was successful. A successful connection will be wrapped +/// in `Ok`. A failed connection is represented as an `Err`. +pub struct IncomingConnections<'a, A: ?Sized +'a> { + inc: &'a mut A, +} + +#[old_impl_check] +impl<'a, T, A: ?Sized + Acceptor> Iterator for IncomingConnections<'a, A> { + type Item = IoResult; + + fn next(&mut self) -> Option> { + Some(self.inc.accept()) + } +} + +/// Creates a standard error for a commonly used flavor of error. The `detail` +/// field of the returned error will always be `None`. +/// +/// # Example +/// +/// ``` +/// use std::old_io as io; +/// +/// let eof = io::standard_error(io::EndOfFile); +/// let einval = io::standard_error(io::InvalidInput); +/// ``` +pub fn standard_error(kind: IoErrorKind) -> IoError { + let desc = match kind { + EndOfFile => "end of file", + IoUnavailable => "I/O is unavailable", + InvalidInput => "invalid input", + OtherIoError => "unknown I/O error", + FileNotFound => "file not found", + PermissionDenied => "permission denied", + ConnectionFailed => "connection failed", + Closed => "stream is closed", + ConnectionRefused => "connection refused", + ConnectionReset => "connection reset", + ConnectionAborted => "connection aborted", + NotConnected => "not connected", + BrokenPipe => "broken pipe", + PathAlreadyExists => "file already exists", + PathDoesntExist => "no such file", + MismatchedFileTypeForOperation => "mismatched file type", + ResourceUnavailable => "resource unavailable", + TimedOut => "operation timed out", + ShortWrite(..) => "short write", + NoProgress => "no progress", + }; + IoError { + kind: kind, + desc: desc, + detail: None, + } +} + +/// A mode specifies how a file should be opened or created. These modes are +/// passed to `File::open_mode` and are used to control where the file is +/// positioned when it is initially opened. +#[derive(Copy, Clone, PartialEq, Eq, Show)] +pub enum FileMode { + /// Opens a file positioned at the beginning. + Open, + /// Opens a file positioned at EOF. + Append, + /// Opens a file, truncating it if it already exists. + Truncate, +} + +/// Access permissions with which the file should be opened. `File`s +/// opened with `Read` will return an error if written to. +#[derive(Copy, Clone, PartialEq, Eq, Show)] +pub enum FileAccess { + /// Read-only access, requests to write will result in an error + Read, + /// Write-only access, requests to read will result in an error + Write, + /// Read-write access, no requests are denied by default + ReadWrite, +} + +/// Different kinds of files which can be identified by a call to stat +#[derive(Copy, PartialEq, Show, Hash, Clone)] +pub enum FileType { + /// This is a normal file, corresponding to `S_IFREG` + RegularFile, + + /// This file is a directory, corresponding to `S_IFDIR` + Directory, + + /// This file is a named pipe, corresponding to `S_IFIFO` + NamedPipe, + + /// This file is a block device, corresponding to `S_IFBLK` + BlockSpecial, + + /// This file is a symbolic link to another file, corresponding to `S_IFLNK` + Symlink, + + /// The type of this file is not recognized as one of the other categories + Unknown, +} + +/// A structure used to describe metadata information about a file. This +/// structure is created through the `stat` method on a `Path`. +/// +/// # Examples +/// +/// ```no_run +/// # #![allow(unstable)] +/// +/// use std::old_io::fs::PathExtensions; +/// +/// let info = match Path::new("foo.txt").stat() { +/// Ok(stat) => stat, +/// Err(e) => panic!("couldn't read foo.txt: {}", e), +/// }; +/// +/// println!("byte size: {}", info.size); +/// ``` +#[derive(Copy, Hash)] +pub struct FileStat { + /// The size of the file, in bytes + pub size: u64, + /// The kind of file this path points to (directory, file, pipe, etc.) + pub kind: FileType, + /// The file permissions currently on the file + pub perm: FilePermission, + + // FIXME(#10301): These time fields are pretty useless without an actual + // time representation, what are the milliseconds relative + // to? + + /// The time that the file was created at, in platform-dependent + /// milliseconds + pub created: u64, + /// The time that this file was last modified, in platform-dependent + /// milliseconds + pub modified: u64, + /// The time that this file was last accessed, in platform-dependent + /// milliseconds + pub accessed: u64, + + /// Information returned by stat() which is not guaranteed to be + /// platform-independent. This information may be useful on some platforms, + /// but it may have different meanings or no meaning at all on other + /// platforms. + /// + /// Usage of this field is discouraged, but if access is desired then the + /// fields are located here. + #[unstable] + pub unstable: UnstableFileStat, +} + +/// This structure represents all of the possible information which can be +/// returned from a `stat` syscall which is not contained in the `FileStat` +/// structure. This information is not necessarily platform independent, and may +/// have different meanings or no meaning at all on some platforms. +#[unstable] +#[derive(Copy, Hash)] +pub struct UnstableFileStat { + /// The ID of the device containing the file. + pub device: u64, + /// The file serial number. + pub inode: u64, + /// The device ID. + pub rdev: u64, + /// The number of hard links to this file. + pub nlink: u64, + /// The user ID of the file. + pub uid: u64, + /// The group ID of the file. + pub gid: u64, + /// The optimal block size for I/O. + pub blksize: u64, + /// The blocks allocated for this file. + pub blocks: u64, + /// User-defined flags for the file. + pub flags: u64, + /// The file generation number. + pub gen: u64, +} + + +bitflags! { + /// A set of permissions for a file or directory is represented by a set of + /// flags which are or'd together. + #[derive(Show)] + flags FilePermission: u32 { + const USER_READ = 0o400, + const USER_WRITE = 0o200, + const USER_EXECUTE = 0o100, + const GROUP_READ = 0o040, + const GROUP_WRITE = 0o020, + const GROUP_EXECUTE = 0o010, + const OTHER_READ = 0o004, + const OTHER_WRITE = 0o002, + const OTHER_EXECUTE = 0o001, + + const USER_RWX = USER_READ.bits | USER_WRITE.bits | USER_EXECUTE.bits, + const GROUP_RWX = GROUP_READ.bits | GROUP_WRITE.bits | GROUP_EXECUTE.bits, + const OTHER_RWX = OTHER_READ.bits | OTHER_WRITE.bits | OTHER_EXECUTE.bits, + + /// Permissions for user owned files, equivalent to 0644 on unix-like + /// systems. + const USER_FILE = USER_READ.bits | USER_WRITE.bits | GROUP_READ.bits | OTHER_READ.bits, + + /// Permissions for user owned directories, equivalent to 0755 on + /// unix-like systems. + const USER_DIR = USER_RWX.bits | GROUP_READ.bits | GROUP_EXECUTE.bits | + OTHER_READ.bits | OTHER_EXECUTE.bits, + + /// Permissions for user owned executables, equivalent to 0755 + /// on unix-like systems. + const USER_EXEC = USER_DIR.bits, + + /// All possible permissions enabled. + const ALL_PERMISSIONS = USER_RWX.bits | GROUP_RWX.bits | OTHER_RWX.bits, + } +} + + +#[stable] +impl Default for FilePermission { + #[stable] + #[inline] + fn default() -> FilePermission { FilePermission::empty() } +} + +#[stable] +impl fmt::Display for FilePermission { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:04o}", self.bits) + } +} + +#[cfg(test)] +mod tests { + use self::BadReaderBehavior::*; + use super::{IoResult, Reader, MemReader, NoProgress, InvalidInput, Writer}; + use prelude::v1::{Ok, Vec, Buffer, SliceExt}; + use uint; + + #[derive(Clone, PartialEq, Show)] + enum BadReaderBehavior { + GoodBehavior(uint), + BadBehavior(uint) + } + + struct BadReader { + r: T, + behavior: Vec, + } + + impl BadReader { + fn new(r: T, behavior: Vec) -> BadReader { + BadReader { behavior: behavior, r: r } + } + } + + impl Reader for BadReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + let BadReader { ref mut behavior, ref mut r } = *self; + loop { + if behavior.is_empty() { + // fall back on good + return r.read(buf); + } + match behavior.as_mut_slice()[0] { + GoodBehavior(0) => (), + GoodBehavior(ref mut x) => { + *x -= 1; + return r.read(buf); + } + BadBehavior(0) => (), + BadBehavior(ref mut x) => { + *x -= 1; + return Ok(0); + } + }; + behavior.remove(0); + } + } + } + + #[test] + fn test_read_at_least() { + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![GoodBehavior(uint::MAX)]); + let buf = &mut [0u8; 5]; + assert!(r.read_at_least(1, buf).unwrap() >= 1); + assert!(r.read_exact(5).unwrap().len() == 5); // read_exact uses read_at_least + assert!(r.read_at_least(0, buf).is_ok()); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.read_at_least(1, buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(1), GoodBehavior(1), + BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.read_at_least(1, buf).unwrap() >= 1); + assert!(r.read_at_least(1, buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(uint::MAX)]); + assert_eq!(r.read_at_least(1, buf).unwrap_err().kind, NoProgress); + + let mut r = MemReader::new(b"hello, world!".to_vec()); + assert_eq!(r.read_at_least(5, buf).unwrap(), 5); + assert_eq!(r.read_at_least(6, buf).unwrap_err().kind, InvalidInput); + } + + #[test] + fn test_push_at_least() { + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![GoodBehavior(uint::MAX)]); + let mut buf = Vec::new(); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + assert!(r.push_at_least(0, 5, &mut buf).is_ok()); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(1), GoodBehavior(1), + BadBehavior(50), GoodBehavior(uint::MAX)]); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); + + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), + vec![BadBehavior(uint::MAX)]); + assert_eq!(r.push_at_least(1, 5, &mut buf).unwrap_err().kind, NoProgress); + + let mut r = MemReader::new(b"hello, world!".to_vec()); + assert_eq!(r.push_at_least(5, 1, &mut buf).unwrap_err().kind, InvalidInput); + } + + #[test] + fn test_show() { + use super::*; + + assert_eq!(format!("{}", USER_READ), "0400"); + assert_eq!(format!("{}", USER_FILE), "0644"); + assert_eq!(format!("{}", USER_EXEC), "0755"); + assert_eq!(format!("{}", USER_RWX), "0700"); + assert_eq!(format!("{}", GROUP_RWX), "0070"); + assert_eq!(format!("{}", OTHER_RWX), "0007"); + assert_eq!(format!("{}", ALL_PERMISSIONS), "0777"); + assert_eq!(format!("{}", USER_READ | USER_WRITE | OTHER_WRITE), "0602"); + } + + fn _ensure_buffer_is_object_safe(x: &T) -> &Buffer { + x as &Buffer + } +} diff --git a/src/libstd/old_io/net/addrinfo.rs b/src/libstd/old_io/net/addrinfo.rs new file mode 100644 index 00000000000..9800cc6829e --- /dev/null +++ b/src/libstd/old_io/net/addrinfo.rs @@ -0,0 +1,137 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Synchronous DNS Resolution +//! +//! Contains the functionality to perform DNS resolution or reverse lookup, +//! in a style related to `getaddrinfo()` and `getnameinfo()`, respectively. + +#![allow(missing_docs)] + +pub use self::SocketType::*; +pub use self::Flag::*; +pub use self::Protocol::*; + +use iter::IteratorExt; +use old_io::{IoResult}; +use old_io::net::ip::{SocketAddr, IpAddr}; +use option::Option; +use option::Option::{Some, None}; +use string::String; +use sys; +use vec::Vec; + +/// Hints to the types of sockets that are desired when looking up hosts +#[derive(Copy, Show)] +pub enum SocketType { + Stream, Datagram, Raw +} + +/// Flags which can be or'd into the `flags` field of a `Hint`. These are used +/// to manipulate how a query is performed. +/// +/// The meaning of each of these flags can be found with `man -s 3 getaddrinfo` +#[derive(Copy, Show)] +pub enum Flag { + AddrConfig, + All, + CanonName, + NumericHost, + NumericServ, + Passive, + V4Mapped, +} + +/// A transport protocol associated with either a hint or a return value of +/// `lookup` +#[derive(Copy, Show)] +pub enum Protocol { + TCP, UDP +} + +/// This structure is used to provide hints when fetching addresses for a +/// remote host to control how the lookup is performed. +/// +/// For details on these fields, see their corresponding definitions via +/// `man -s 3 getaddrinfo` +#[derive(Copy, Show)] +pub struct Hint { + pub family: uint, + pub socktype: Option, + pub protocol: Option, + pub flags: uint, +} + +#[derive(Copy, Show)] +pub struct Info { + pub address: SocketAddr, + pub family: uint, + pub socktype: Option, + pub protocol: Option, + pub flags: uint, +} + +/// Easy name resolution. Given a hostname, returns the list of IP addresses for +/// that hostname. +pub fn get_host_addresses(host: &str) -> IoResult> { + lookup(Some(host), None, None).map(|a| a.into_iter().map(|i| i.address.ip).collect()) +} + +/// Reverse name resolution. Given an address, returns the corresponding +/// hostname. +pub fn get_address_name(addr: IpAddr) -> IoResult { + sys::addrinfo::get_address_name(addr) +} + +/// Full-fledged resolution. This function will perform a synchronous call to +/// getaddrinfo, controlled by the parameters +/// +/// # Arguments +/// +/// * hostname - an optional hostname to lookup against +/// * servname - an optional service name, listed in the system services +/// * hint - see the hint structure, and "man -s 3 getaddrinfo", for how this +/// controls lookup +/// +/// FIXME: this is not public because the `Hint` structure is not ready for public +/// consumption just yet. +#[allow(unused_variables)] +fn lookup(hostname: Option<&str>, servname: Option<&str>, hint: Option) + -> IoResult> { + sys::addrinfo::get_host_addresses(hostname, servname, hint) +} + +// Ignored on android since we cannot give tcp/ip +// permission without help of apk +#[cfg(all(test, not(target_os = "android")))] +mod test { + use prelude::v1::*; + use super::*; + use old_io::net::ip::*; + + #[test] + fn dns_smoke_test() { + let ipaddrs = get_host_addresses("localhost").unwrap(); + let mut found_local = false; + let local_addr = &Ipv4Addr(127, 0, 0, 1); + for addr in ipaddrs.iter() { + found_local = found_local || addr == local_addr; + } + assert!(found_local); + } + + #[ignore] + #[test] + fn issue_10663() { + // Something should happen here, but this certainly shouldn't cause + // everything to die. The actual outcome we don't care too much about. + get_host_addresses("example.com").unwrap(); + } +} diff --git a/src/libstd/old_io/net/ip.rs b/src/libstd/old_io/net/ip.rs new file mode 100644 index 00000000000..26eb068b151 --- /dev/null +++ b/src/libstd/old_io/net/ip.rs @@ -0,0 +1,700 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Internet Protocol (IP) addresses. +//! +//! This module contains functions useful for parsing, formatting, and +//! manipulating IP addresses. + +#![allow(missing_docs)] + +pub use self::IpAddr::*; + +use boxed::Box; +use fmt; +use old_io::{self, IoResult, IoError}; +use old_io::net; +use iter::{Iterator, IteratorExt}; +use ops::{FnOnce, FnMut}; +use option::Option; +use option::Option::{None, Some}; +use result::Result::{Ok, Err}; +use slice::SliceExt; +use str::{FromStr, StrExt}; +use vec::Vec; + +pub type Port = u16; + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)] +pub enum IpAddr { + Ipv4Addr(u8, u8, u8, u8), + Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16) +} + +#[stable] +impl fmt::Display for IpAddr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Ipv4Addr(a, b, c, d) => + write!(fmt, "{}.{}.{}.{}", a, b, c, d), + + // Ipv4 Compatible address + Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => { + write!(fmt, "::{}.{}.{}.{}", (g >> 8) as u8, g as u8, + (h >> 8) as u8, h as u8) + } + + // Ipv4-Mapped address + Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => { + write!(fmt, "::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8, + (h >> 8) as u8, h as u8) + } + + Ipv6Addr(a, b, c, d, e, f, g, h) => + write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + a, b, c, d, e, f, g, h) + } + } +} + +#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)] +pub struct SocketAddr { + pub ip: IpAddr, + pub port: Port, +} + +#[stable] +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.ip { + Ipv4Addr(..) => write!(f, "{}:{}", self.ip, self.port), + Ipv6Addr(..) => write!(f, "[{}]:{}", self.ip, self.port), + } + } +} + +struct Parser<'a> { + // parsing as ASCII, so can use byte array + s: &'a [u8], + pos: uint, +} + +impl<'a> Parser<'a> { + fn new(s: &'a str) -> Parser<'a> { + Parser { + s: s.as_bytes(), + pos: 0, + } + } + + fn is_eof(&self) -> bool { + self.pos == self.s.len() + } + + // Commit only if parser returns Some + fn read_atomically(&mut self, cb: F) -> Option where + F: FnOnce(&mut Parser) -> Option, + { + let pos = self.pos; + let r = cb(self); + if r.is_none() { + self.pos = pos; + } + r + } + + // Commit only if parser read till EOF + fn read_till_eof(&mut self, cb: F) -> Option where + F: FnOnce(&mut Parser) -> Option, + { + self.read_atomically(move |p| { + match cb(p) { + Some(x) => if p.is_eof() {Some(x)} else {None}, + None => None, + } + }) + } + + // Return result of first successful parser + fn read_or(&mut self, parsers: &mut [Box Option>]) + -> Option { + for pf in parsers.iter_mut() { + match self.read_atomically(|p: &mut Parser| pf.call_mut((p,))) { + Some(r) => return Some(r), + None => {} + } + } + None + } + + // Apply 3 parsers sequentially + fn read_seq_3(&mut self, + pa: PA, + pb: PB, + pc: PC) + -> Option<(A, B, C)> where + PA: FnOnce(&mut Parser) -> Option, + PB: FnOnce(&mut Parser) -> Option, + PC: FnOnce(&mut Parser) -> Option, + { + self.read_atomically(move |p| { + let a = pa(p); + let b = if a.is_some() { pb(p) } else { None }; + let c = if b.is_some() { pc(p) } else { None }; + match (a, b, c) { + (Some(a), Some(b), Some(c)) => Some((a, b, c)), + _ => None + } + }) + } + + // Read next char + fn read_char(&mut self) -> Option { + if self.is_eof() { + None + } else { + let r = self.s[self.pos] as char; + self.pos += 1; + Some(r) + } + } + + // Return char and advance iff next char is equal to requested + fn read_given_char(&mut self, c: char) -> Option { + self.read_atomically(|p| { + match p.read_char() { + Some(next) if next == c => Some(next), + _ => None, + } + }) + } + + // Read digit + fn read_digit(&mut self, radix: u8) -> Option { + fn parse_digit(c: char, radix: u8) -> Option { + let c = c as u8; + // assuming radix is either 10 or 16 + if c >= b'0' && c <= b'9' { + Some(c - b'0') + } else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) { + Some(c - b'a' + 10) + } else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) { + Some(c - b'A' + 10) + } else { + None + } + } + + self.read_atomically(|p| { + p.read_char().and_then(|c| parse_digit(c, radix)) + }) + } + + fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option { + let mut r = 0u32; + let mut digit_count = 0; + loop { + match self.read_digit(radix) { + Some(d) => { + r = r * (radix as u32) + (d as u32); + digit_count += 1; + if digit_count > max_digits || r >= upto { + return None + } + } + None => { + if digit_count == 0 { + return None + } else { + return Some(r) + } + } + }; + } + } + + // Read number, failing if max_digits of number value exceeded + fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option { + self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto)) + } + + fn read_ipv4_addr_impl(&mut self) -> Option { + let mut bs = [0u8; 4]; + let mut i = 0; + while i < 4 { + if i != 0 && self.read_given_char('.').is_none() { + return None; + } + + let octet = self.read_number(10, 3, 0x100).map(|n| n as u8); + match octet { + Some(d) => bs[i] = d, + None => return None, + }; + i += 1; + } + Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3])) + } + + // Read IPv4 address + fn read_ipv4_addr(&mut self) -> Option { + self.read_atomically(|p| p.read_ipv4_addr_impl()) + } + + fn read_ipv6_addr_impl(&mut self) -> Option { + fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr { + assert!(head.len() + tail.len() <= 8); + let mut gs = [0u16; 8]; + gs.clone_from_slice(head); + gs[(8 - tail.len()) .. 8].clone_from_slice(tail); + Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) + } + + fn read_groups(p: &mut Parser, groups: &mut [u16; 8], limit: uint) -> (uint, bool) { + let mut i = 0; + while i < limit { + if i < limit - 1 { + let ipv4 = p.read_atomically(|p| { + if i == 0 || p.read_given_char(':').is_some() { + p.read_ipv4_addr() + } else { + None + } + }); + match ipv4 { + Some(Ipv4Addr(a, b, c, d)) => { + groups[i + 0] = ((a as u16) << 8) | (b as u16); + groups[i + 1] = ((c as u16) << 8) | (d as u16); + return (i + 2, true); + } + _ => {} + } + } + + let group = p.read_atomically(|p| { + if i == 0 || p.read_given_char(':').is_some() { + p.read_number(16, 4, 0x10000).map(|n| n as u16) + } else { + None + } + }); + match group { + Some(g) => groups[i] = g, + None => return (i, false) + } + i += 1; + } + (i, false) + } + + let mut head = [0u16; 8]; + let (head_size, head_ipv4) = read_groups(self, &mut head, 8); + + if head_size == 8 { + return Some(Ipv6Addr( + head[0], head[1], head[2], head[3], + head[4], head[5], head[6], head[7])) + } + + // IPv4 part is not allowed before `::` + if head_ipv4 { + return None + } + + // read `::` if previous code parsed less than 8 groups + if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() { + return None; + } + + let mut tail = [0u16; 8]; + let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size); + Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size])) + } + + fn read_ipv6_addr(&mut self) -> Option { + self.read_atomically(|p| p.read_ipv6_addr_impl()) + } + + fn read_ip_addr(&mut self) -> Option { + let ipv4_addr = |&mut: p: &mut Parser| p.read_ipv4_addr(); + let ipv6_addr = |&mut: p: &mut Parser| p.read_ipv6_addr(); + self.read_or(&mut [box ipv4_addr, box ipv6_addr]) + } + + fn read_socket_addr(&mut self) -> Option { + let ip_addr = |&: p: &mut Parser| { + let ipv4_p = |&mut: p: &mut Parser| p.read_ip_addr(); + let ipv6_p = |&mut: p: &mut Parser| { + let open_br = |&: p: &mut Parser| p.read_given_char('['); + let ip_addr = |&: p: &mut Parser| p.read_ipv6_addr(); + let clos_br = |&: p: &mut Parser| p.read_given_char(']'); + p.read_seq_3::(open_br, ip_addr, clos_br) + .map(|t| match t { (_, ip, _) => ip }) + }; + p.read_or(&mut [box ipv4_p, box ipv6_p]) + }; + let colon = |&: p: &mut Parser| p.read_given_char(':'); + let port = |&: p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16); + + // host, colon, port + self.read_seq_3::(ip_addr, colon, port) + .map(|t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } }) + } +} + +impl FromStr for IpAddr { + fn from_str(s: &str) -> Option { + Parser::new(s).read_till_eof(|p| p.read_ip_addr()) + } +} + +impl FromStr for SocketAddr { + fn from_str(s: &str) -> Option { + Parser::new(s).read_till_eof(|p| p.read_socket_addr()) + } +} + +/// A trait for objects which can be converted or resolved to one or more `SocketAddr` values. +/// +/// Implementing types minimally have to implement either `to_socket_addr` or `to_socket_addr_all` +/// method, and its trivial counterpart will be available automatically. +/// +/// This trait is used for generic address resolution when constructing network objects. +/// By default it is implemented for the following types: +/// +/// * `SocketAddr` - `to_socket_addr` is identity function. +/// +/// * `(IpAddr, u16)` - `to_socket_addr` constructs `SocketAddr` trivially. +/// +/// * `(&str, u16)` - the string should be either a string representation of an IP address +/// expected by `FromStr` implementation for `IpAddr` or a host name. +/// +/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding +/// to that IP address joined with the given port. +/// +/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses +/// for the host name, each joined with the given port. +/// +/// * `&str` - the string should be either a string representation of a `SocketAddr` as +/// expected by its `FromStr` implementation or a string like `:` pair +/// where `` is a `u16` value. +/// +/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding +/// to that socket address. +/// +/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses +/// for the host name, each joined with the port. +/// +/// +/// This trait allows constructing network objects like `TcpStream` or `UdpSocket` easily with +/// values of various types for the bind/connection address. It is needed because sometimes +/// one type is more appropriate than the other: for simple uses a string like `"localhost:12345"` +/// is much nicer than manual construction of the corresponding `SocketAddr`, but sometimes +/// `SocketAddr` value is *the* main source of the address, and converting it to some other type +/// (e.g. a string) just for it to be converted back to `SocketAddr` in constructor methods +/// is pointless. +/// +/// Some examples: +/// +/// ```rust,no_run +/// # #![allow(unused_must_use)] +/// +/// use std::old_io::{TcpStream, TcpListener}; +/// use std::old_io::net::udp::UdpSocket; +/// use std::old_io::net::ip::{Ipv4Addr, SocketAddr}; +/// +/// fn main() { +/// // The following lines are equivalent modulo possible "localhost" name resolution +/// // differences +/// let tcp_s = TcpStream::connect(SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 12345 }); +/// let tcp_s = TcpStream::connect((Ipv4Addr(127, 0, 0, 1), 12345u16)); +/// let tcp_s = TcpStream::connect(("127.0.0.1", 12345u16)); +/// let tcp_s = TcpStream::connect(("localhost", 12345u16)); +/// let tcp_s = TcpStream::connect("127.0.0.1:12345"); +/// let tcp_s = TcpStream::connect("localhost:12345"); +/// +/// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly +/// let tcp_l = TcpListener::bind("localhost:12345"); +/// +/// let mut udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)).unwrap(); +/// udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16)); +/// } +/// ``` +pub trait ToSocketAddr { + /// Converts this object to single socket address value. + /// + /// If more than one value is available, this method returns the first one. If no + /// values are available, this method returns an `IoError`. + /// + /// By default this method delegates to `to_socket_addr_all` method, taking the first + /// item from its result. + fn to_socket_addr(&self) -> IoResult { + self.to_socket_addr_all() + .and_then(|v| v.into_iter().next().ok_or_else(|| IoError { + kind: old_io::InvalidInput, + desc: "no address available", + detail: None + })) + } + + /// Converts this object to all available socket address values. + /// + /// Some values like host name string naturally correspond to multiple IP addresses. + /// This method tries to return all available addresses corresponding to this object. + /// + /// By default this method delegates to `to_socket_addr` method, creating a singleton + /// vector from its result. + #[inline] + fn to_socket_addr_all(&self) -> IoResult> { + self.to_socket_addr().map(|a| vec![a]) + } +} + +impl ToSocketAddr for SocketAddr { + #[inline] + fn to_socket_addr(&self) -> IoResult { Ok(*self) } +} + +impl ToSocketAddr for (IpAddr, u16) { + #[inline] + fn to_socket_addr(&self) -> IoResult { + let (ip, port) = *self; + Ok(SocketAddr { ip: ip, port: port }) + } +} + +fn resolve_socket_addr(s: &str, p: u16) -> IoResult> { + net::get_host_addresses(s) + .map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect()) +} + +fn parse_and_resolve_socket_addr(s: &str) -> IoResult> { + macro_rules! try_opt { + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(IoError { + kind: old_io::InvalidInput, + desc: $msg, + detail: None + }) + } + ) + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(FromStr::from_str(port_str), "invalid port value"); + resolve_socket_addr(host, port) +} + +impl<'a> ToSocketAddr for (&'a str, u16) { + fn to_socket_addr_all(&self) -> IoResult> { + let (host, port) = *self; + + // try to parse the host as a regular IpAddr first + match FromStr::from_str(host) { + Some(addr) => return Ok(vec![SocketAddr { + ip: addr, + port: port + }]), + None => {} + } + + resolve_socket_addr(host, port) + } +} + +// accepts strings like 'localhost:12345' +impl<'a> ToSocketAddr for &'a str { + fn to_socket_addr(&self) -> IoResult { + // try to parse as a regular SocketAddr first + match FromStr::from_str(*self) { + Some(addr) => return Ok(addr), + None => {} + } + + parse_and_resolve_socket_addr(*self) + .and_then(|v| v.into_iter().next() + .ok_or_else(|| IoError { + kind: old_io::InvalidInput, + desc: "no address available", + detail: None + }) + ) + } + + fn to_socket_addr_all(&self) -> IoResult> { + // try to parse as a regular SocketAddr first + match FromStr::from_str(*self) { + Some(addr) => return Ok(vec![addr]), + None => {} + } + + parse_and_resolve_socket_addr(*self) + } +} + + +#[cfg(test)] +mod test { + use prelude::v1::*; + use super::*; + use str::FromStr; + + #[test] + fn test_from_str_ipv4() { + assert_eq!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1")); + assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255")); + assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0")); + + // out of range + let none: Option = FromStr::from_str("256.0.0.1"); + assert_eq!(None, none); + // too short + let none: Option = FromStr::from_str("255.0.0"); + assert_eq!(None, none); + // too long + let none: Option = FromStr::from_str("255.0.0.1.2"); + assert_eq!(None, none); + // no number between dots + let none: Option = FromStr::from_str("255.0..1"); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_ipv6() { + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0")); + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1")); + + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1")); + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::")); + + assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), + FromStr::from_str("2a02:6b8::11:11")); + + // too long group + let none: Option = FromStr::from_str("::00000"); + assert_eq!(None, none); + // too short + let none: Option = FromStr::from_str("1:2:3:4:5:6:7"); + assert_eq!(None, none); + // too long + let none: Option = FromStr::from_str("1:2:3:4:5:6:7:8:9"); + assert_eq!(None, none); + // triple colon + let none: Option = FromStr::from_str("1:2:::6:7:8"); + assert_eq!(None, none); + // two double colons + let none: Option = FromStr::from_str("1:2::6::8"); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_ipv4_in_ipv6() { + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)), + FromStr::from_str("::192.0.2.33")); + assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), + FromStr::from_str("::FFFF:192.0.2.33")); + assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), + FromStr::from_str("64:ff9b::192.0.2.33")); + assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), + FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33")); + + // colon after v4 + let none: Option = FromStr::from_str("::127.0.0.1:"); + assert_eq!(None, none); + // not enough groups + let none: Option = FromStr::from_str("1.2.3.4.5:127.0.0.1"); + assert_eq!(None, none); + // too many groups + let none: Option = + FromStr::from_str("1.2.3.4.5:6:7:127.0.0.1"); + assert_eq!(None, none); + } + + #[test] + fn test_from_str_socket_addr() { + assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }), + FromStr::from_str("77.88.21.11:80")); + assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }), + FromStr::from_str("[2a02:6b8:0:1::1]:53")); + assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }), + FromStr::from_str("[::127.0.0.1]:22")); + + // without port + let none: Option = FromStr::from_str("127.0.0.1"); + assert_eq!(None, none); + // without port + let none: Option = FromStr::from_str("127.0.0.1:"); + assert_eq!(None, none); + // wrong brackets around v4 + let none: Option = FromStr::from_str("[127.0.0.1]:22"); + assert_eq!(None, none); + // port out of range + let none: Option = FromStr::from_str("127.0.0.1:123456"); + assert_eq!(None, none); + } + + #[test] + fn ipv6_addr_to_string() { + let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); + assert!(a1.to_string() == "::ffff:192.0.2.128" || + a1.to_string() == "::FFFF:192.0.2.128"); + assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(), + "8:9:a:b:c:d:e:f"); + } + + #[test] + fn to_socket_addr_socketaddr() { + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 }; + assert_eq!(Ok(a), a.to_socket_addr()); + assert_eq!(Ok(vec![a]), a.to_socket_addr_all()); + } + + #[test] + fn to_socket_addr_ipaddr_u16() { + let a = Ipv4Addr(77, 88, 21, 11); + let p = 12345u16; + let e = SocketAddr { ip: a, port: p }; + assert_eq!(Ok(e), (a, p).to_socket_addr()); + assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all()); + } + + #[test] + fn to_socket_addr_str_u16() { + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; + assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr()); + assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; + assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr()); + assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; + assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a)); + } + + #[test] + fn to_socket_addr_str() { + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; + assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr()); + assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; + assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr()); + assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all()); + + let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; + assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a)); + } +} diff --git a/src/libstd/old_io/net/mod.rs b/src/libstd/old_io/net/mod.rs new file mode 100644 index 00000000000..d8394aa8b6a --- /dev/null +++ b/src/libstd/old_io/net/mod.rs @@ -0,0 +1,46 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Networking I/O + +use old_io::{IoError, IoResult, InvalidInput}; +use ops::FnMut; +use option::Option::None; +use result::Result::{Ok, Err}; +use self::ip::{SocketAddr, ToSocketAddr}; + +pub use self::addrinfo::get_host_addresses; + +pub mod addrinfo; +pub mod tcp; +pub mod udp; +pub mod ip; +pub mod pipe; + +fn with_addresses(addr: A, mut action: F) -> IoResult where + A: ToSocketAddr, + F: FnMut(SocketAddr) -> IoResult, +{ + const DEFAULT_ERROR: IoError = IoError { + kind: InvalidInput, + desc: "no addresses found for hostname", + detail: None + }; + + let addresses = try!(addr.to_socket_addr_all()); + let mut err = DEFAULT_ERROR; + for addr in addresses.into_iter() { + match action(addr) { + Ok(r) => return Ok(r), + Err(e) => err = e + } + } + Err(err) +} diff --git a/src/libstd/old_io/net/pipe.rs b/src/libstd/old_io/net/pipe.rs new file mode 100644 index 00000000000..2ed6d8118d5 --- /dev/null +++ b/src/libstd/old_io/net/pipe.rs @@ -0,0 +1,864 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Named pipes +//! +//! This module contains the ability to communicate over named pipes with +//! synchronous I/O. On windows, this corresponds to talking over a Named Pipe, +//! while on Unix it corresponds to UNIX domain sockets. +//! +//! These pipes are similar to TCP in the sense that you can have both a stream to a +//! server and a server itself. The server provided accepts other `UnixStream` +//! instances as clients. + +#![allow(missing_docs)] + +use prelude::v1::*; + +use ffi::CString; +use path::BytesContainer; +use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error}; +use sys::pipe::UnixAcceptor as UnixAcceptorImp; +use sys::pipe::UnixListener as UnixListenerImp; +use sys::pipe::UnixStream as UnixStreamImp; +use time::Duration; + +use sys_common; + +/// A stream which communicates over a named pipe. +pub struct UnixStream { + inner: UnixStreamImp, +} + +impl UnixStream { + + /// Connect to a pipe named by `path`. This will attempt to open a + /// connection to the underlying socket. + /// + /// The returned stream will be closed when the object falls out of scope. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused_must_use)] + /// use std::old_io::net::pipe::UnixStream; + /// + /// let server = Path::new("path/to/my/socket"); + /// let mut stream = UnixStream::connect(&server); + /// stream.write(&[1, 2, 3]); + /// ``` + pub fn connect(path: P) -> IoResult { + let path = CString::from_slice(path.container_as_bytes()); + UnixStreamImp::connect(&path, None) + .map(|inner| UnixStream { inner: inner }) + } + + /// Connect to a pipe named by `path`, timing out if the specified number of + /// milliseconds. + /// + /// This function is similar to `connect`, except that if `timeout` + /// elapses the function will return an error of kind `TimedOut`. + /// + /// If a `timeout` with zero or negative duration is specified then + /// the function returns `Err`, with the error kind set to `TimedOut`. + #[unstable = "the timeout argument is likely to change types"] + pub fn connect_timeout