<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for inlining external documentation into the current AST.

use syntax::ast;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;

use rustc::metadata::csearch;
use rustc::metadata::decoder;
use rustc::middle::def;
use rustc::middle::ty;
use rustc::middle::subst;
use rustc::middle::stability;

use core;
use doctree;
use clean;

use super::Clean;

/// Attempt to inline the definition of a local node id into this AST.
///
/// This function will fetch the definition of the id specified, and if it is
/// from another crate it will attempt to inline the documentation from the
/// other crate into this crate.
///
/// This is primarily used for `pub use` statements which are, in general,
/// implementation details. Inlining the documentation should help provide a
/// better experience when reading the documentation in this use case.
///
/// The returned value is `None` if the `id` could not be inlined, and `Some`
/// of a vector of items if it was successfully expanded.
pub fn try_inline(id: ast::NodeId, into: Option<ast::Ident>)
                  -> Option<Vec<clean::Item>> {
    let cx = ::ctxtkey.get().unwrap();
    let tcx = match cx.maybe_typed {
        core::Typed(ref tycx) => tycx,
        core::NotTyped(_) => return None,
    };
    let def = match tcx.def_map.borrow().find(&id) {
        Some(def) => *def,
        None => return None,
    };
    let did = def.def_id();
    if ast_util::is_local(did) { return None }
    try_inline_def(&**cx, tcx, def).map(|vec| {
        vec.move_iter().map(|mut item| {
            match into {
                Some(into) if item.name.is_some() => {
                    item.name = Some(into.clean());
                }
                _ => {}
            }
            item
        }).collect()
    })
}

fn try_inline_def(cx: &core::DocContext,
                  tcx: &ty::ctxt,
                  def: def::Def) -> Option<Vec<clean::Item>> {
    let mut ret = Vec::new();
    let did = def.def_id();
    let inner = match def {
        def::DefTrait(did) => {
            record_extern_fqn(cx, did, clean::TypeTrait);
            clean::TraitItem(build_external_trait(tcx, did))
        }
        def::DefFn(did, style) => {
            // If this function is a tuple struct constructor, we just skip it
            if csearch::get_tuple_struct_definition_if_ctor(&tcx.sess.cstore,
                                                            did).is_some() {
                return None
            }
            record_extern_fqn(cx, did, clean::TypeFunction);
            clean::FunctionItem(build_external_function(tcx, did, style))
        }
        def::DefStruct(did) => {
            record_extern_fqn(cx, did, clean::TypeStruct);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            clean::StructItem(build_struct(tcx, did))
        }
        def::DefTy(did) => {
            record_extern_fqn(cx, did, clean::TypeEnum);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            build_type(tcx, did)
        }
        // Assume that the enum type is reexported next to the variant, and
        // variants don't show up in documentation specially.
        def::DefVariant(..) => return Some(Vec::new()),
        def::DefMod(did) => {
            record_extern_fqn(cx, did, clean::TypeModule);
            clean::ModuleItem(build_module(cx, tcx, did))
        }
        def::DefStatic(did, mtbl) => {
            record_extern_fqn(cx, did, clean::TypeStatic);
            clean::StaticItem(build_static(tcx, did, mtbl))
        }
        _ => return None,
    };
    let fqn = csearch::get_item_path(tcx, did);
    cx.inlined.borrow_mut().get_mut_ref().insert(did);
    ret.push(clean::Item {
        source: clean::Span::empty(),
        name: Some(fqn.last().unwrap().to_string()),
        attrs: load_attrs(tcx, did),
        inner: inner,
        visibility: Some(ast::Public),
        stability: stability::lookup(tcx, did).clean(),
        def_id: did,
    });
    Some(ret)
}

pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
    let mut attrs = Vec::new();
    csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
        attrs.extend(v.move_iter().map(|mut a| {
            // FIXME this isn't quite always true, it's just true about 99% of
            //       the time when dealing with documentation. For example,
            //       this would treat doc comments of the form `#[doc = "foo"]`
            //       incorrectly.
            if a.name().get() == "doc" && a.value_str().is_some() {
                a.node.is_sugared_doc = true;
            }
            a.clean()
        }));
    });
    attrs
}

/// Record an external fully qualified name in the external_paths cache.
///
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
pub fn record_extern_fqn(cx: &core::DocContext,
                         did: ast::DefId,
                         kind: clean::TypeKind) {
    match cx.maybe_typed {
        core::Typed(ref tcx) => {
            let fqn = csearch::get_item_path(tcx, did);
            let fqn = fqn.move_iter().map(|i| i.to_string()).collect();
            cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
        }
        core::NotTyped(..) => {}
    }
}

pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
    let def = ty::lookup_trait_def(tcx, did);
    let trait_items = ty::trait_items(tcx, did).clean();
    let provided = ty::provided_trait_methods(tcx, did);
    let mut items = trait_items.move_iter().map(|trait_item| {
        if provided.iter().any(|a| a.def_id == trait_item.def_id) {
            clean::ProvidedMethod(trait_item)
        } else {
            clean::RequiredMethod(trait_item)
        }
    });
    let supertraits = ty::trait_supertraits(tcx, did);
    let mut parents = supertraits.iter().map(|i| {
        match i.clean() {
            clean::TraitBound(ty) => ty,
            clean::RegionBound => unreachable!()
        }
    });

    clean::Trait {
        generics: (&def.generics, subst::TypeSpace).clean(),
        items: items.collect(),
        parents: parents.collect()
    }
}

fn build_external_function(tcx: &ty::ctxt,
                           did: ast::DefId,
                           style: ast::FnStyle) -> clean::Function {
    let t = ty::lookup_item_type(tcx, did);
    clean::Function {
        decl: match ty::get(t.ty).sty {
            ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
            _ => fail!("bad function"),
        },
        generics: (&t.generics, subst::FnSpace).clean(),
        fn_style: style,
    }
}

fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
    use syntax::parse::token::special_idents::unnamed_field;

    let t = ty::lookup_item_type(tcx, did);
    let fields = ty::lookup_struct_fields(tcx, did);

    clean::Struct {
        struct_type: match fields.as_slice() {
            [] => doctree::Unit,
            [ref f] if f.name == unnamed_field.name => doctree::Newtype,
            [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
            _ => doctree::Plain,
        },
        generics: (&t.generics, subst::TypeSpace).clean(),
        fields: fields.clean(),
        fields_stripped: false,
    }
}

fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
    let t = ty::lookup_item_type(tcx, did);
    match ty::get(t.ty).sty {
        ty::ty_enum(edid, _) if !csearch::is_typedef(&tcx.sess.cstore, did) => {
            return clean::EnumItem(clean::Enum {
                generics: (&t.generics, subst::TypeSpace).clean(),
                variants_stripped: false,
                variants: ty::enum_variants(tcx, edid).clean(),
            })
        }
        _ => {}
    }

    clean::TypedefItem(clean::Typedef {
        type_: t.ty.clean(),
        generics: (&t.generics, subst::TypeSpace).clean(),
    })
}

fn build_impls(cx: &core::DocContext,
               tcx: &ty::ctxt,
               did: ast::DefId) -> Vec<clean::Item> {
    ty::populate_implementations_for_type_if_necessary(tcx, did);
    let mut impls = Vec::new();

    match tcx.inherent_impls.borrow().find(&did) {
        None => {}
        Some(i) => {
            impls.extend(i.borrow().iter().map(|&did| { build_impl(cx, tcx, did) }));
        }
    }

    // If this is the first time we've inlined something from this crate, then
    // we inline *all* impls from the crate into this crate. Note that there's
    // currently no way for us to filter this based on type, and we likely need
    // many impls for a variety of reasons.
    //
    // Primarily, the impls will be used to populate the documentation for this
    // type being inlined, but impls can also be used when generating
    // documentation for primitives (no way to find those specifically).
    if cx.populated_crate_impls.borrow_mut().insert(did.krate) {
        csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
                                              did.krate,
                                              |def, _, _| {
            populate_impls(cx, tcx, def, &mut impls)
        });

        fn populate_impls(cx: &core::DocContext,
                          tcx: &ty::ctxt,
                          def: decoder::DefLike,
                          impls: &mut Vec<Option<clean::Item>>) {
            match def {
                decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)),
                decoder::DlDef(def::DefMod(did)) => {
                    csearch::each_child_of_item(&tcx.sess.cstore,
                                                did,
                                                |def, _, _| {
                        populate_impls(cx, tcx, def, impls)
                    })
                }
                _ => {}
            }
        }
    }

    impls.move_iter().filter_map(|a| a).collect()
}

fn build_impl(cx: &core::DocContext,
              tcx: &ty::ctxt,
              did: ast::DefId) -> Option<clean::Item> {
    if !cx.inlined.borrow_mut().get_mut_ref().insert(did) {
        return None
    }

    let associated_trait = csearch::get_impl_trait(tcx, did);
    // If this is an impl for a #[doc(hidden)] trait, be sure to not inline it.
    match associated_trait {
        Some(ref t) => {
            let trait_attrs = load_attrs(tcx, t.def_id);
            if trait_attrs.iter().any(|a| is_doc_hidden(a)) {
                return None
            }
        }
        None => {}
    }

    let attrs = load_attrs(tcx, did);
    let ty = ty::lookup_item_type(tcx, did);
    let trait_items = csearch::get_impl_items(&tcx.sess.cstore, did)
            .iter()
            .filter_map(|did| {
        let did = did.def_id();
        let impl_item = ty::impl_or_trait_item(tcx, did);
        match impl_item {
            ty::MethodTraitItem(method) => {
                if method.vis != ast::Public && associated_trait.is_none() {
                    return None
                }
                let mut item = method.clean();
                item.inner = match item.inner.clone() {
                    clean::TyMethodItem(clean::TyMethod {
                        fn_style, decl, self_, generics
                    }) => {
                        clean::MethodItem(clean::Method {
                            fn_style: fn_style,
                            decl: decl,
                            self_: self_,
                            generics: generics,
                        })
                    }
                    _ => fail!("not a tymethod"),
                };
                Some(item)
            }
        }
    }).collect();
    return Some(clean::Item {
        inner: clean::ImplItem(clean::Impl {
            derived: clean::detect_derived(attrs.as_slice()),
            trait_: associated_trait.clean().map(|bound| {
                match bound {
                    clean::TraitBound(ty) => ty,
                    clean::RegionBound => unreachable!(),
                }
            }),
            for_: ty.ty.clean(),
            generics: (&ty.generics, subst::TypeSpace).clean(),
            items: trait_items,
        }),
        source: clean::Span::empty(),
        name: None,
        attrs: attrs,
        visibility: Some(ast::Inherited),
        stability: stability::lookup(tcx, did).clean(),
        def_id: did,
    });

    fn is_doc_hidden(a: &clean::Attribute) -> bool {
        match *a {
            clean::List(ref name, ref inner) if name.as_slice() == "doc" => {
                inner.iter().any(|a| {
                    match *a {
                        clean::Word(ref s) => s.as_slice() == "hidden",
                        _ => false,
                    }
                })
            }
            _ => false
        }
    }
}

fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
                did: ast::DefId) -> clean::Module {
    let mut items = Vec::new();
    fill_in(cx, tcx, did, &mut items);
    return clean::Module {
        items: items,
        is_crate: false,
    };

    // FIXME: this doesn't handle reexports inside the module itself.
    //        Should they be handled?
    fn fill_in(cx: &core::DocContext, tcx: &ty::ctxt, did: ast::DefId,
               items: &mut Vec<clean::Item>) {
        csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, vis| {
            match def {
                decoder::DlDef(def::DefForeignMod(did)) => {
                    fill_in(cx, tcx, did, items);
                }
                decoder::DlDef(def) if vis == ast::Public => {
                    match try_inline_def(cx, tcx, def) {
                        Some(i) => items.extend(i.move_iter()),
                        None => {}
                    }
                }
                decoder::DlDef(..) => {}
                // All impls were inlined above
                decoder::DlImpl(..) => {}
                decoder::DlField => fail!("unimplemented field"),
            }
        });
    }
}

fn build_static(tcx: &ty::ctxt,
                did: ast::DefId,
                mutable: bool) -> clean::Static {
    clean::Static {
        type_: ty::lookup_item_type(tcx, did).ty.clean(),
        mutability: if mutable {clean::Mutable} else {clean::Immutable},
        expr: "\n\n\n".to_string(), // trigger the "[definition]" links
    }
}
<commit_msg>auto merge of #16766 : kevinmehall/rust/issue-15976, r=alexcrichton<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for inlining external documentation into the current AST.

use syntax::ast;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;

use rustc::metadata::csearch;
use rustc::metadata::decoder;
use rustc::middle::def;
use rustc::middle::ty;
use rustc::middle::subst;
use rustc::middle::stability;

use core;
use doctree;
use clean;

use super::Clean;

/// Attempt to inline the definition of a local node id into this AST.
///
/// This function will fetch the definition of the id specified, and if it is
/// from another crate it will attempt to inline the documentation from the
/// other crate into this crate.
///
/// This is primarily used for `pub use` statements which are, in general,
/// implementation details. Inlining the documentation should help provide a
/// better experience when reading the documentation in this use case.
///
/// The returned value is `None` if the `id` could not be inlined, and `Some`
/// of a vector of items if it was successfully expanded.
pub fn try_inline(id: ast::NodeId, into: Option<ast::Ident>)
                  -> Option<Vec<clean::Item>> {
    let cx = ::ctxtkey.get().unwrap();
    let tcx = match cx.maybe_typed {
        core::Typed(ref tycx) => tycx,
        core::NotTyped(_) => return None,
    };
    let def = match tcx.def_map.borrow().find(&id) {
        Some(def) => *def,
        None => return None,
    };
    let did = def.def_id();
    if ast_util::is_local(did) { return None }
    try_inline_def(&**cx, tcx, def).map(|vec| {
        vec.move_iter().map(|mut item| {
            match into {
                Some(into) if item.name.is_some() => {
                    item.name = Some(into.clean());
                }
                _ => {}
            }
            item
        }).collect()
    })
}

fn try_inline_def(cx: &core::DocContext,
                  tcx: &ty::ctxt,
                  def: def::Def) -> Option<Vec<clean::Item>> {
    let mut ret = Vec::new();
    let did = def.def_id();
    let inner = match def {
        def::DefTrait(did) => {
            record_extern_fqn(cx, did, clean::TypeTrait);
            clean::TraitItem(build_external_trait(tcx, did))
        }
        def::DefFn(did, style) => {
            // If this function is a tuple struct constructor, we just skip it
            if csearch::get_tuple_struct_definition_if_ctor(&tcx.sess.cstore,
                                                            did).is_some() {
                return None
            }
            record_extern_fqn(cx, did, clean::TypeFunction);
            clean::FunctionItem(build_external_function(tcx, did, style))
        }
        def::DefStruct(did) => {
            record_extern_fqn(cx, did, clean::TypeStruct);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            clean::StructItem(build_struct(tcx, did))
        }
        def::DefTy(did) => {
            record_extern_fqn(cx, did, clean::TypeEnum);
            ret.extend(build_impls(cx, tcx, did).move_iter());
            build_type(tcx, did)
        }
        // Assume that the enum type is reexported next to the variant, and
        // variants don't show up in documentation specially.
        def::DefVariant(..) => return Some(Vec::new()),
        def::DefMod(did) => {
            record_extern_fqn(cx, did, clean::TypeModule);
            clean::ModuleItem(build_module(cx, tcx, did))
        }
        def::DefStatic(did, mtbl) => {
            record_extern_fqn(cx, did, clean::TypeStatic);
            clean::StaticItem(build_static(tcx, did, mtbl))
        }
        _ => return None,
    };
    let fqn = csearch::get_item_path(tcx, did);
    cx.inlined.borrow_mut().get_mut_ref().insert(did);
    ret.push(clean::Item {
        source: clean::Span::empty(),
        name: Some(fqn.last().unwrap().to_string()),
        attrs: load_attrs(tcx, did),
        inner: inner,
        visibility: Some(ast::Public),
        stability: stability::lookup(tcx, did).clean(),
        def_id: did,
    });
    Some(ret)
}

pub fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<clean::Attribute> {
    let mut attrs = Vec::new();
    csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
        attrs.extend(v.move_iter().map(|a| {
            a.clean()
        }));
    });
    attrs
}

/// Record an external fully qualified name in the external_paths cache.
///
/// These names are used later on by HTML rendering to generate things like
/// source links back to the original item.
pub fn record_extern_fqn(cx: &core::DocContext,
                         did: ast::DefId,
                         kind: clean::TypeKind) {
    match cx.maybe_typed {
        core::Typed(ref tcx) => {
            let fqn = csearch::get_item_path(tcx, did);
            let fqn = fqn.move_iter().map(|i| i.to_string()).collect();
            cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
        }
        core::NotTyped(..) => {}
    }
}

pub fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> clean::Trait {
    let def = ty::lookup_trait_def(tcx, did);
    let trait_items = ty::trait_items(tcx, did).clean();
    let provided = ty::provided_trait_methods(tcx, did);
    let mut items = trait_items.move_iter().map(|trait_item| {
        if provided.iter().any(|a| a.def_id == trait_item.def_id) {
            clean::ProvidedMethod(trait_item)
        } else {
            clean::RequiredMethod(trait_item)
        }
    });
    let supertraits = ty::trait_supertraits(tcx, did);
    let mut parents = supertraits.iter().map(|i| {
        match i.clean() {
            clean::TraitBound(ty) => ty,
            clean::RegionBound => unreachable!()
        }
    });

    clean::Trait {
        generics: (&def.generics, subst::TypeSpace).clean(),
        items: items.collect(),
        parents: parents.collect()
    }
}

fn build_external_function(tcx: &ty::ctxt,
                           did: ast::DefId,
                           style: ast::FnStyle) -> clean::Function {
    let t = ty::lookup_item_type(tcx, did);
    clean::Function {
        decl: match ty::get(t.ty).sty {
            ty::ty_bare_fn(ref f) => (did, &f.sig).clean(),
            _ => fail!("bad function"),
        },
        generics: (&t.generics, subst::FnSpace).clean(),
        fn_style: style,
    }
}

fn build_struct(tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
    use syntax::parse::token::special_idents::unnamed_field;

    let t = ty::lookup_item_type(tcx, did);
    let fields = ty::lookup_struct_fields(tcx, did);

    clean::Struct {
        struct_type: match fields.as_slice() {
            [] => doctree::Unit,
            [ref f] if f.name == unnamed_field.name => doctree::Newtype,
            [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
            _ => doctree::Plain,
        },
        generics: (&t.generics, subst::TypeSpace).clean(),
        fields: fields.clean(),
        fields_stripped: false,
    }
}

fn build_type(tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
    let t = ty::lookup_item_type(tcx, did);
    match ty::get(t.ty).sty {
        ty::ty_enum(edid, _) if !csearch::is_typedef(&tcx.sess.cstore, did) => {
            return clean::EnumItem(clean::Enum {
                generics: (&t.generics, subst::TypeSpace).clean(),
                variants_stripped: false,
                variants: ty::enum_variants(tcx, edid).clean(),
            })
        }
        _ => {}
    }

    clean::TypedefItem(clean::Typedef {
        type_: t.ty.clean(),
        generics: (&t.generics, subst::TypeSpace).clean(),
    })
}

fn build_impls(cx: &core::DocContext,
               tcx: &ty::ctxt,
               did: ast::DefId) -> Vec<clean::Item> {
    ty::populate_implementations_for_type_if_necessary(tcx, did);
    let mut impls = Vec::new();

    match tcx.inherent_impls.borrow().find(&did) {
        None => {}
        Some(i) => {
            impls.extend(i.borrow().iter().map(|&did| { build_impl(cx, tcx, did) }));
        }
    }

    // If this is the first time we've inlined something from this crate, then
    // we inline *all* impls from the crate into this crate. Note that there's
    // currently no way for us to filter this based on type, and we likely need
    // many impls for a variety of reasons.
    //
    // Primarily, the impls will be used to populate the documentation for this
    // type being inlined, but impls can also be used when generating
    // documentation for primitives (no way to find those specifically).
    if cx.populated_crate_impls.borrow_mut().insert(did.krate) {
        csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
                                              did.krate,
                                              |def, _, _| {
            populate_impls(cx, tcx, def, &mut impls)
        });

        fn populate_impls(cx: &core::DocContext,
                          tcx: &ty::ctxt,
                          def: decoder::DefLike,
                          impls: &mut Vec<Option<clean::Item>>) {
            match def {
                decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)),
                decoder::DlDef(def::DefMod(did)) => {
                    csearch::each_child_of_item(&tcx.sess.cstore,
                                                did,
                                                |def, _, _| {
                        populate_impls(cx, tcx, def, impls)
                    })
                }
                _ => {}
            }
        }
    }

    impls.move_iter().filter_map(|a| a).collect()
}

fn build_impl(cx: &core::DocContext,
              tcx: &ty::ctxt,
              did: ast::DefId) -> Option<clean::Item> {
    if !cx.inlined.borrow_mut().get_mut_ref().insert(did) {
        return None
    }

    let associated_trait = csearch::get_impl_trait(tcx, did);
    // If this is an impl for a #[doc(hidden)] trait, be sure to not inline it.
    match associated_trait {
        Some(ref t) => {
            let trait_attrs = load_attrs(tcx, t.def_id);
            if trait_attrs.iter().any(|a| is_doc_hidden(a)) {
                return None
            }
        }
        None => {}
    }

    let attrs = load_attrs(tcx, did);
    let ty = ty::lookup_item_type(tcx, did);
    let trait_items = csearch::get_impl_items(&tcx.sess.cstore, did)
            .iter()
            .filter_map(|did| {
        let did = did.def_id();
        let impl_item = ty::impl_or_trait_item(tcx, did);
        match impl_item {
            ty::MethodTraitItem(method) => {
                if method.vis != ast::Public && associated_trait.is_none() {
                    return None
                }
                let mut item = method.clean();
                item.inner = match item.inner.clone() {
                    clean::TyMethodItem(clean::TyMethod {
                        fn_style, decl, self_, generics
                    }) => {
                        clean::MethodItem(clean::Method {
                            fn_style: fn_style,
                            decl: decl,
                            self_: self_,
                            generics: generics,
                        })
                    }
                    _ => fail!("not a tymethod"),
                };
                Some(item)
            }
        }
    }).collect();
    return Some(clean::Item {
        inner: clean::ImplItem(clean::Impl {
            derived: clean::detect_derived(attrs.as_slice()),
            trait_: associated_trait.clean().map(|bound| {
                match bound {
                    clean::TraitBound(ty) => ty,
                    clean::RegionBound => unreachable!(),
                }
            }),
            for_: ty.ty.clean(),
            generics: (&ty.generics, subst::TypeSpace).clean(),
            items: trait_items,
        }),
        source: clean::Span::empty(),
        name: None,
        attrs: attrs,
        visibility: Some(ast::Inherited),
        stability: stability::lookup(tcx, did).clean(),
        def_id: did,
    });

    fn is_doc_hidden(a: &clean::Attribute) -> bool {
        match *a {
            clean::List(ref name, ref inner) if name.as_slice() == "doc" => {
                inner.iter().any(|a| {
                    match *a {
                        clean::Word(ref s) => s.as_slice() == "hidden",
                        _ => false,
                    }
                })
            }
            _ => false
        }
    }
}

fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
                did: ast::DefId) -> clean::Module {
    let mut items = Vec::new();
    fill_in(cx, tcx, did, &mut items);
    return clean::Module {
        items: items,
        is_crate: false,
    };

    // FIXME: this doesn't handle reexports inside the module itself.
    //        Should they be handled?
    fn fill_in(cx: &core::DocContext, tcx: &ty::ctxt, did: ast::DefId,
               items: &mut Vec<clean::Item>) {
        csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, vis| {
            match def {
                decoder::DlDef(def::DefForeignMod(did)) => {
                    fill_in(cx, tcx, did, items);
                }
                decoder::DlDef(def) if vis == ast::Public => {
                    match try_inline_def(cx, tcx, def) {
                        Some(i) => items.extend(i.move_iter()),
                        None => {}
                    }
                }
                decoder::DlDef(..) => {}
                // All impls were inlined above
                decoder::DlImpl(..) => {}
                decoder::DlField => fail!("unimplemented field"),
            }
        });
    }
}

fn build_static(tcx: &ty::ctxt,
                did: ast::DefId,
                mutable: bool) -> clean::Static {
    clean::Static {
        type_: ty::lookup_item_type(tcx, did).ty.clean(),
        mutability: if mutable {clean::Mutable} else {clean::Immutable},
        expr: "\n\n\n".to_string(), // trigger the "[definition]" links
    }
}
<|endoftext|>


<commit_before>use crate::clean;
use crate::docfs::PathError;
use crate::error::Error;
use crate::fold::DocFolder;
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::layout;
use crate::html::render::{SharedContext, BASIC_KEYWORDS};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use std::ffi::OsStr;
use std::fs;
use std::path::{Component, Path, PathBuf};

crate fn render(
    dst: &Path,
    scx: &mut SharedContext<'_>,
    krate: clean::Crate,
) -> Result<clean::Crate, Error> {
    info!("emitting source files");
    let dst = dst.join("src").join(&*krate.name.as_str());
    scx.ensure_dir(&dst)?;
    let mut folder = SourceCollector { dst, scx };
    Ok(folder.fold_crate(krate))
}

/// Helper struct to render all source code to HTML pages
struct SourceCollector<'a, 'tcx> {
    scx: &'a mut SharedContext<'tcx>,

    /// Root destination to place all HTML output into
    dst: PathBuf,
}

impl DocFolder for SourceCollector<'_, '_> {
    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
        // If we're not rendering sources, there's nothing to do.
        // If we're including source files, and we haven't seen this file yet,
        // then we need to render it out to the filesystem.
        if self.scx.include_sources
            // skip all synthetic "files"
            && item.span.filename(self.sess()).is_real()
            // skip non-local files
            && item.span.cnum(self.sess()) == LOCAL_CRATE
        {
            let filename = item.span.filename(self.sess());
            // If it turns out that we couldn't read this file, then we probably
            // can't read any of the files (generating html output from json or
            // something like that), so just don't include sources for the
            // entire crate. The other option is maintaining this mapping on a
            // per-file basis, but that's probably not worth it...
            self.scx.include_sources = match self.emit_source(&filename) {
                Ok(()) => true,
                Err(e) => {
                    println!(
                        "warning: source code was requested to be rendered, \
                         but processing `{}` had an error: {}",
                        filename, e
                    );
                    println!("         skipping rendering of source code");
                    false
                }
            };
        }
        // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
        // we could return None here without having to walk the rest of the crate.
        Some(self.fold_item_recur(item))
    }
}

impl SourceCollector<'_, 'tcx> {
    fn sess(&self) -> &'tcx Session {
        &self.scx.tcx.sess
    }

    /// Renders the given filename into its corresponding HTML source file.
    fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
        let p = match *filename {
            FileName::Real(ref file) => file.local_path().to_path_buf(),
            _ => return Ok(()),
        };
        if self.scx.local_sources.contains_key(&*p) {
            // We've already emitted this source
            return Ok(());
        }

        let contents = match fs::read_to_string(&p) {
            Ok(contents) => contents,
            Err(e) => {
                return Err(Error::new(e, &p));
            }
        };

        // Remove the utf-8 BOM if any
        let contents = if contents.starts_with('\u{feff}') { &contents[3..] } else { &contents };

        // Create the intermediate directories
        let mut cur = self.dst.clone();
        let mut root_path = String::from("../../");
        let mut href = String::new();
        clean_path(&self.scx.src_root, &p, false, |component| {
            cur.push(component);
            root_path.push_str("../");
            href.push_str(&component.to_string_lossy());
            href.push('/');
        });
        self.scx.ensure_dir(&cur)?;

        let src_fname = p.file_name().expect("source has no filename").to_os_string();
        let mut fname = src_fname.clone();
        fname.push(".html");
        cur.push(&fname);
        href.push_str(&fname.to_string_lossy());

        let title = format!("{} - source", src_fname.to_string_lossy());
        let desc = format!("Source of the Rust file `{}`.", filename);
        let page = layout::Page {
            title: &title,
            css_class: "source",
            root_path: &root_path,
            static_root_path: self.scx.static_root_path.as_deref(),
            description: &desc,
            keywords: BASIC_KEYWORDS,
            resource_suffix: &self.scx.resource_suffix,
            extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
            static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
        };
        let v = layout::render(
            &self.scx.layout,
            &page,
            "",
            |buf: &mut _| print_src(buf, contents, self.scx.edition),
            &self.scx.style_files,
        );
        self.scx.fs.write(&cur, v.as_bytes())?;
        self.scx.local_sources.insert(p, href);
        Ok(())
    }
}

/// Takes a path to a source file and cleans the path to it. This canonicalizes
/// things like ".." to components which preserve the "top down" hierarchy of a
/// static HTML tree. Each component in the cleaned path will be passed as an
/// argument to `f`. The very last component of the path (ie the file name) will
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
crate fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
where
    F: FnMut(&OsStr),
{
    // make it relative, if possible
    let p = p.strip_prefix(src_root).unwrap_or(p);

    let mut iter = p.components().peekable();

    while let Some(c) = iter.next() {
        if !keep_filename && iter.peek().is_none() {
            break;
        }

        match c {
            Component::ParentDir => f("up".as_ref()),
            Component::Normal(c) => f(c),
            _ => continue,
        }
    }
}

/// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side.
fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
    let lines = s.lines().count();
    let mut cols = 0;
    let mut tmp = lines;
    while tmp > 0 {
        cols += 1;
        tmp /= 10;
    }
    buf.write_str("
");
    for i in 1..=lines {
        write!(buf, "{0:1$}\n", i, cols);
    }
    buf.write_str("
");
    highlight::render_with_highlighting(s, buf, None, None, None, edition);
}
<commit_msg>Rollup merge of #82993 - camelid:source-use-diag, r=jyn514<commit_after>use crate::clean;
use crate::docfs::PathError;
use crate::error::Error;
use crate::fold::DocFolder;
use crate::html::format::Buffer;
use crate::html::highlight;
use crate::html::layout;
use crate::html::render::{SharedContext, BASIC_KEYWORDS};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use std::ffi::OsStr;
use std::fs;
use std::path::{Component, Path, PathBuf};

crate fn render(
    dst: &Path,
    scx: &mut SharedContext<'_>,
    krate: clean::Crate,
) -> Result<clean::Crate, Error> {
    info!("emitting source files");
    let dst = dst.join("src").join(&*krate.name.as_str());
    scx.ensure_dir(&dst)?;
    let mut folder = SourceCollector { dst, scx };
    Ok(folder.fold_crate(krate))
}

/// Helper struct to render all source code to HTML pages
struct SourceCollector<'a, 'tcx> {
    scx: &'a mut SharedContext<'tcx>,

    /// Root destination to place all HTML output into
    dst: PathBuf,
}

impl DocFolder for SourceCollector<'_, '_> {
    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
        // If we're not rendering sources, there's nothing to do.
        // If we're including source files, and we haven't seen this file yet,
        // then we need to render it out to the filesystem.
        if self.scx.include_sources
            // skip all synthetic "files"
            && item.span.filename(self.sess()).is_real()
            // skip non-local files
            && item.span.cnum(self.sess()) == LOCAL_CRATE
        {
            let filename = item.span.filename(self.sess());
            // If it turns out that we couldn't read this file, then we probably
            // can't read any of the files (generating html output from json or
            // something like that), so just don't include sources for the
            // entire crate. The other option is maintaining this mapping on a
            // per-file basis, but that's probably not worth it...
            self.scx.include_sources = match self.emit_source(&filename) {
                Ok(()) => true,
                Err(e) => {
                    self.scx.tcx.sess.span_err(
                        item.span.inner(),
                        &format!("failed to render source code for `{}`: {}", filename, e),
                    );
                    false
                }
            };
        }
        // FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
        // we could return None here without having to walk the rest of the crate.
        Some(self.fold_item_recur(item))
    }
}

impl SourceCollector<'_, 'tcx> {
    fn sess(&self) -> &'tcx Session {
        &self.scx.tcx.sess
    }

    /// Renders the given filename into its corresponding HTML source file.
    fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
        let p = match *filename {
            FileName::Real(ref file) => file.local_path().to_path_buf(),
            _ => return Ok(()),
        };
        if self.scx.local_sources.contains_key(&*p) {
            // We've already emitted this source
            return Ok(());
        }

        let contents = match fs::read_to_string(&p) {
            Ok(contents) => contents,
            Err(e) => {
                return Err(Error::new(e, &p));
            }
        };

        // Remove the utf-8 BOM if any
        let contents = if contents.starts_with('\u{feff}') { &contents[3..] } else { &contents };

        // Create the intermediate directories
        let mut cur = self.dst.clone();
        let mut root_path = String::from("../../");
        let mut href = String::new();
        clean_path(&self.scx.src_root, &p, false, |component| {
            cur.push(component);
            root_path.push_str("../");
            href.push_str(&component.to_string_lossy());
            href.push('/');
        });
        self.scx.ensure_dir(&cur)?;

        let src_fname = p.file_name().expect("source has no filename").to_os_string();
        let mut fname = src_fname.clone();
        fname.push(".html");
        cur.push(&fname);
        href.push_str(&fname.to_string_lossy());

        let title = format!("{} - source", src_fname.to_string_lossy());
        let desc = format!("Source of the Rust file `{}`.", filename);
        let page = layout::Page {
            title: &title,
            css_class: "source",
            root_path: &root_path,
            static_root_path: self.scx.static_root_path.as_deref(),
            description: &desc,
            keywords: BASIC_KEYWORDS,
            resource_suffix: &self.scx.resource_suffix,
            extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
            static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
        };
        let v = layout::render(
            &self.scx.layout,
            &page,
            "",
            |buf: &mut _| print_src(buf, contents, self.scx.edition),
            &self.scx.style_files,
        );
        self.scx.fs.write(&cur, v.as_bytes())?;
        self.scx.local_sources.insert(p, href);
        Ok(())
    }
}

/// Takes a path to a source file and cleans the path to it. This canonicalizes
/// things like ".." to components which preserve the "top down" hierarchy of a
/// static HTML tree. Each component in the cleaned path will be passed as an
/// argument to `f`. The very last component of the path (ie the file name) will
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
crate fn clean_path<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F)
where
    F: FnMut(&OsStr),
{
    // make it relative, if possible
    let p = p.strip_prefix(src_root).unwrap_or(p);

    let mut iter = p.components().peekable();

    while let Some(c) = iter.next() {
        if !keep_filename && iter.peek().is_none() {
            break;
        }

        match c {
            Component::ParentDir => f("up".as_ref()),
            Component::Normal(c) => f(c),
            _ => continue,
        }
    }
}

/// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side.
fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
    let lines = s.lines().count();
    let mut cols = 0;
    let mut tmp = lines;
    while tmp > 0 {
        cols += 1;
        tmp /= 10;
    }
    buf.write_str("
");
    for i in 1..=lines {
        write!(buf, "{0:1$}\n", i, cols);
    }
    buf.write_str("
");
    highlight::render_with_highlighting(s, buf, None, None, None, edition);
}
<|endoftext|>


<commit_before>#![feature(generator_trait)]
#![no_std]

pub use core::*;

pub mod future {
    pub use core::future::Future;
    use core::ops::Generator;
    use core::pin::Pin;
    use core::task::Poll;

    pub fn poll_with_tls_context<F>(f: Pin<&mut F>) -> Poll<F::Output>
    where
        F: Future,
    {
        crate::executor::poll(f)
    }

    pub fn from_generator<G: Generator<Yield = ()>>(
        generator: G,
    ) -> impl Future<Output = G::Return> {
        crate::executor::from_generator(generator)
    }
}

pub mod executor {
    use core::future::Future;
    use core::ops::Generator;
    use core::ops::GeneratorState;
    use core::pin::Pin;
    use core::ptr;
    use core::task::Context;
    use core::task::Poll;
    use core::task::RawWaker;
    use core::task::RawWakerVTable;
    use core::task::Waker;

    const DUMMY_WAKER_VTABLE: RawWakerVTable =
        RawWakerVTable::new(get_waker, do_nothing, do_nothing, do_nothing);
    const DUMMY_WAKER: RawWaker = RawWaker::new(ptr::null(), &DUMMY_WAKER_VTABLE);

    extern "Rust" {
        #[link_name = "libtock::syscalls::yieldk"]
        fn yieldk();
    }

    pub fn block_on<T>(mut future: impl Future<Output = T>) -> T {
        let waker = unsafe { Waker::from_raw(DUMMY_WAKER) };
        let mut context = Context::from_waker(&waker);

        loop {
            let pinned_future = unsafe { Pin::new_unchecked(&mut future) };
            let result = pinned_future.poll(&mut context);
            match result {
                Poll::Pending => unsafe { yieldk() },
                Poll::Ready(value) => {
                    return value;
                }
            }
        }
    }

    pub(crate) fn poll<F: Future>(pinned_future: Pin<&mut F>) -> Poll<F::Output> {
        let waker = unsafe { Waker::from_raw(DUMMY_WAKER) };
        let mut context = Context::from_waker(&waker);
        pinned_future.poll(&mut context)
    }

    pub(crate) fn from_generator<G: Generator<Yield = ()>>(
        generator: G,
    ) -> impl Future<Output = G::Return> {
        GeneratorFuture { generator }
    }

    struct GeneratorFuture<G> {
        generator: G,
    }

    impl<G: Generator<Yield = ()>> Future for GeneratorFuture<G> {
        type Output = G::Return;

        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
            let pin = unsafe { Pin::new_unchecked(&mut Pin::into_inner_unchecked(self).generator) };
            match pin.resume() {
                GeneratorState::Yielded(()) => Poll::Pending,
                GeneratorState::Complete(out) => Poll::Ready(out),
            }
        }
    }

    const fn get_waker(_x: *const ()) -> RawWaker {
        DUMMY_WAKER
    }

    const fn do_nothing(_x: *const ()) {}
}
<commit_msg>Make RawWakerVTable static<commit_after>#![feature(generator_trait)]
#![no_std]

pub use core::*;

pub mod future {
    pub use core::future::Future;
    use core::ops::Generator;
    use core::pin::Pin;
    use core::task::Poll;

    pub fn poll_with_tls_context<F>(f: Pin<&mut F>) -> Poll<F::Output>
    where
        F: Future,
    {
        crate::executor::poll(f)
    }

    pub fn from_generator<G: Generator<Yield = ()>>(
        generator: G,
    ) -> impl Future<Output = G::Return> {
        crate::executor::from_generator(generator)
    }
}

pub mod executor {
    use core::future::Future;
    use core::ops::Generator;
    use core::ops::GeneratorState;
    use core::pin::Pin;
    use core::ptr;
    use core::task::Context;
    use core::task::Poll;
    use core::task::RawWaker;
    use core::task::RawWakerVTable;
    use core::task::Waker;

    static DUMMY_WAKER_VTABLE: RawWakerVTable =
        RawWakerVTable::new(clone, do_nothing, do_nothing, do_nothing);

    extern "Rust" {
        #[link_name = "libtock::syscalls::yieldk"]
        fn yieldk();
    }

    pub fn block_on<T>(mut future: impl Future<Output = T>) -> T {
        let waker = unsafe { Waker::from_raw(get_dummy_waker()) };
        let mut context = Context::from_waker(&waker);

        loop {
            let pinned_future = unsafe { Pin::new_unchecked(&mut future) };
            let result = pinned_future.poll(&mut context);
            match result {
                Poll::Pending => unsafe { yieldk() },
                Poll::Ready(value) => {
                    return value;
                }
            }
        }
    }

    pub(crate) fn poll<F: Future>(pinned_future: Pin<&mut F>) -> Poll<F::Output> {
        let waker = unsafe { Waker::from_raw(get_dummy_waker()) };
        let mut context = Context::from_waker(&waker);
        pinned_future.poll(&mut context)
    }

    pub(crate) fn from_generator<G: Generator<Yield = ()>>(
        generator: G,
    ) -> impl Future<Output = G::Return> {
        GeneratorFuture { generator }
    }

    struct GeneratorFuture<G> {
        generator: G,
    }

    impl<G: Generator<Yield = ()>> Future for GeneratorFuture<G> {
        type Output = G::Return;

        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
            let pin = unsafe { Pin::new_unchecked(&mut Pin::into_inner_unchecked(self).generator) };
            match pin.resume() {
                GeneratorState::Yielded(()) => Poll::Pending,
                GeneratorState::Complete(out) => Poll::Ready(out),
            }
        }
    }

    fn clone(_x: *const ()) -> RawWaker {
        get_dummy_waker()
    }

    fn get_dummy_waker() -> RawWaker {
        RawWaker::new(ptr::null(), &DUMMY_WAKER_VTABLE)
    }

    fn do_nothing(_x: *const ()) {}
}
<|endoftext|>


<commit_before>const CODE_MAX_SIZE: usize = 1024 * 64;
const CODE_MAX_DATA: usize = 1024 * 32;

const OP_VAL_INCR: u8 = 1;
const OP_VAL_DECR: u8 = 2;
const OP_PNT_INCR: u8 = 3;
const OP_PNT_DECR: u8 = 4;
const OP_LOOP_START: u8 = 5;
const OP_LOOP_END: u8 = 6;
const OP_OUT: u8 = 7;
const OP_IN: u8 = 8;
const OP_END: u8 = 9;

#[derive(Copy, Clone)]
pub struct Operation {
    op: u8,
    ex: u32
}

pub struct Program {
    pc: usize,
    ptr: usize,
    data: [u32; CODE_MAX_DATA],
    code: [Operation; CODE_MAX_SIZE]
}

impl Program {
    pub fn new() -> Program
    {
        Program {
            pc: 0,
            ptr: 0,
            data: [0u32; CODE_MAX_DATA],
            code: [Operation { op: 0u8, ex: 0u32}; CODE_MAX_SIZE]
        }
    }

    pub fn set(&mut self, location: usize, operation: Operation)
    {
        self.code[location] = operation;
    }

    pub fn get(&mut self, location: usize) -> Operation
    {
        self.code[location]
    }

    fn reset(&mut self)
    {
        self.pc = 0;
        self.ptr = 0;
        self.data = [0u32; CODE_MAX_DATA];
    }

    pub fn run(&mut self)
    {
        self.reset();
        while self.code[self.pc].op != OP_END {
            match self.code[self.pc].op {
                OP_VAL_INCR => self.data[self.ptr] += 1,
                OP_VAL_DECR => if self.data[self.ptr] > 0 { self.data[self.ptr] -= 1 },
                OP_PNT_INCR => self.ptr += 1,
                OP_PNT_DECR => if self.ptr > 0 { self.ptr -= 1 },
                OP_LOOP_START => {
                    if self.data[self.ptr] == 0 {
                        self.pc = self.code[self.pc].ex as usize;
                    }
                },
                OP_LOOP_END => {
                    if self.data[self.ptr] > 0 {
                        self.pc = self.code[self.pc].ex as usize;
                    }
                },
                OP_OUT => {
                    print!("{}", self.data[self.ptr] as u8 as char);
                }
                _ => {}
            }

            self.pc += 1;
        }
    }

    pub fn debug(&mut self)
    {
        self.reset();
        while self.code[self.pc].op != OP_END {
            match self.code[self.pc].op {
                OP_VAL_INCR => print!("{}: \t+\n", self.pc),
                OP_VAL_DECR => print!("{}: \t-\n", self.pc),
                OP_PNT_INCR => print!("{}: \t>\n", self.pc),
                OP_PNT_DECR => print!("{}: \t<\n", self.pc),
                OP_LOOP_START => {
                    print!("{}: \t[ \t{} \t{}\n", self.pc, self.data[self.ptr], self.code[self.pc].ex);
                },
                OP_LOOP_END => {
                    print!("{}: \t] \t{} \t{}\n", self.pc, self.data[self.ptr], self.code[self.pc].ex);
                },
                OP_OUT => {
                    print!("{}: \t.\n", self.pc)
                }
                _ => {}
            }

            self.pc += 1;
        }
    }
}

pub struct BrainfuckParser;
impl BrainfuckParser {
    pub fn parse(codestring: &str, program: &mut Program)
    {
        // Current program pointer
        let mut pc = 0usize;

        // Stack(Vector) used for loops
        let mut stack: Vec<usize> = Vec::new();

        for c in codestring.chars() {
            match c {
                '+' => program.set(pc, Operation { op: OP_VAL_INCR, ex: 1 }),
                '-' => program.set(pc, Operation { op: OP_VAL_DECR, ex: 1 }),
                '>' => program.set(pc, Operation { op: OP_PNT_INCR, ex: 1 }),
                '<' => program.set(pc, Operation { op: OP_PNT_DECR, ex: 1 }),
                '[' => {
                    program.set(pc, Operation { op: OP_LOOP_START, ex: 0 });
                    stack.push(pc);
                    },
                ']' => {
                    let pc_jmp = match stack.pop() {
                        None => break, // empty
                        Some(x) => x,
                    };
                    program.set(pc, Operation { op: OP_LOOP_END, ex: pc_jmp as u32 });
                    let mut op = program.get(pc_jmp);
                    op.ex = pc as u32;
                    program.set(pc_jmp, op);
                    },
                '.' => program.set(pc, Operation { op: OP_OUT, ex: 0 }),
                ',' => program.set(pc, Operation { op: OP_IN, ex: 0 }),
                _ => {
                    if pc > 0 {
                        pc = pc - 1
                    }
                },
            };

            pc = pc + 1;
        }
        program.set(pc, Operation { op: OP_END, ex: 0});
    }

}
<commit_msg>+ Fixed stack overflow on init<commit_after>use std::iter::repeat;

const CODE_MAX_SIZE: usize = 1024 * 64;
const CODE_MAX_DATA: usize = 1024 * 32;

const OP_VAL_INCR: u8 = 1;
const OP_VAL_DECR: u8 = 2;
const OP_PNT_INCR: u8 = 3;
const OP_PNT_DECR: u8 = 4;
const OP_LOOP_START: u8 = 5;
const OP_LOOP_END: u8 = 6;
const OP_OUT: u8 = 7;
const OP_IN: u8 = 8;
const OP_END: u8 = 9;

#[derive(Copy, Clone)]
pub struct Operation {
    op: u8,
    ex: u32
}

pub struct Program {
    pc: usize,
    ptr: usize,
    data: Vec<u32>,
    code: Vec<Operation>
}

impl Program {
    pub fn new() -> Program
    {
        Program {
            pc: 0,
            ptr: 0,
            data: Vec::new(), // Init empty, because it is reset before run
            code: repeat(Operation { op: 0u8, ex: 0u32}).take(CODE_MAX_SIZE).collect()
        }
    }

    pub fn set(&mut self, location: usize, operation: Operation)
    {
        self.code[location] = operation;
    }

    pub fn get(&mut self, location: usize) -> Operation
    {
        self.code[location]
    }

    fn reset(&mut self)
    {
        self.pc = 0;
        self.ptr = 0;
        self.data = repeat(0u32).take(CODE_MAX_DATA).collect();
    }

    pub fn run(&mut self)
    {
        self.reset();
        while self.code[self.pc].op != OP_END {
            match self.code[self.pc].op {
                OP_VAL_INCR => self.data[self.ptr] += 1,
                OP_VAL_DECR => if self.data[self.ptr] > 0 { self.data[self.ptr] -= 1 },
                OP_PNT_INCR => self.ptr += 1,
                OP_PNT_DECR => if self.ptr > 0 { self.ptr -= 1 },
                OP_LOOP_START => {
                    if self.data[self.ptr] == 0 {
                        self.pc = self.code[self.pc].ex as usize;
                    }
                },
                OP_LOOP_END => {
                    if self.data[self.ptr] > 0 {
                        self.pc = self.code[self.pc].ex as usize;
                    }
                },
                OP_OUT => {
                    print!("{}", self.data[self.ptr] as u8 as char);
                }
                _ => {}
            }

            self.pc += 1;
        }
    }

    pub fn debug(&mut self)
    {
        self.reset();
        while self.code[self.pc].op != OP_END {
            match self.code[self.pc].op {
                OP_VAL_INCR => print!("{}: \t+\n", self.pc),
                OP_VAL_DECR => print!("{}: \t-\n", self.pc),
                OP_PNT_INCR => print!("{}: \t>\n", self.pc),
                OP_PNT_DECR => print!("{}: \t<\n", self.pc),
                OP_LOOP_START => {
                    print!("{}: \t[ \t{} \t{}\n", self.pc, self.data[self.ptr], self.code[self.pc].ex);
                },
                OP_LOOP_END => {
                    print!("{}: \t] \t{} \t{}\n", self.pc, self.data[self.ptr], self.code[self.pc].ex);
                },
                OP_OUT => {
                    print!("{}: \t.\n", self.pc)
                }
                _ => {}
            }

            self.pc += 1;
        }
    }
}

pub struct BrainfuckParser;
impl BrainfuckParser {
    pub fn parse(codestring: &str, program: &mut Program)
    {
        // Current program pointer
        let mut pc = 0usize;

        // Stack(Vector) used for loops
        let mut stack: Vec<usize> = Vec::new();

        for c in codestring.chars() {
            match c {
                '+' => program.set(pc, Operation { op: OP_VAL_INCR, ex: 1 }),
                '-' => program.set(pc, Operation { op: OP_VAL_DECR, ex: 1 }),
                '>' => program.set(pc, Operation { op: OP_PNT_INCR, ex: 1 }),
                '<' => program.set(pc, Operation { op: OP_PNT_DECR, ex: 1 }),
                '[' => {
                    program.set(pc, Operation { op: OP_LOOP_START, ex: 0 });
                    stack.push(pc);
                    },
                ']' => {
                    let pc_jmp = match stack.pop() {
                        None => break, // empty
                        Some(x) => x,
                    };
                    program.set(pc, Operation { op: OP_LOOP_END, ex: pc_jmp as u32 });
                    let mut op = program.get(pc_jmp);
                    op.ex = pc as u32;
                    program.set(pc_jmp, op);
                    },
                '.' => program.set(pc, Operation { op: OP_OUT, ex: 0 }),
                ',' => program.set(pc, Operation { op: OP_IN, ex: 0 }),
                _ => {
                    if pc > 0 {
                        pc = pc - 1
                    }
                },
            };

            pc = pc + 1;
        }
        program.set(pc, Operation { op: OP_END, ex: 0});
    }

}
<|endoftext|>


<commit_before>use super::internal::*;
use super::*;
use std::usize;

/// `Interleave` is an iterator that interleaves elements of iterators
/// `i` and `j` in one continuous iterator. This struct is created by
/// the [`interleave()`] method on [`IndexedParallelIterator`]
///
/// [`interleave()`]: trait.IndexedParallelIterator.html#method.interleave
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    i: I,
    j: J,
    flag: bool,
}

/// Create a new `Interleave` iterator
///
/// NB: a free fn because it is NOT part of the end-user API.
pub fn new<I, J>(i: I, j: J) -> Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    let flag = false;
    Interleave { i, j, flag }
}

impl<I, J> ParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    type Item = I::Item;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: Consumer<I::Item>
    {
        bridge(self, consumer)
    }

    fn opt_len(&mut self) -> Option<usize> {
        Some(self.len())
    }
}

impl<I, J> IndexedParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>,
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self, consumer)
    }

    fn len(&mut self) -> usize {
        self.i.len() + self.j.len()
    }

    fn with_producer<CB>(mut self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        let (i_len, j_len) = (self.i.len(), self.j.len());
        return self.i.with_producer(CallbackI {
            callback: callback,
            i_len: i_len,
            j_len: j_len,
            j: self.j
        });

        struct CallbackI<CB, J> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            j: J
        }

        impl<CB, J> ProducerCallback<J::Item> for CallbackI<CB, J>
            where J: IndexedParallelIterator,
                  CB: ProducerCallback<J::Item>
        {
            type Output = CB::Output;

            fn callback<I>(self, i_producer: I) -> Self::Output
                where I: Producer<Item = J::Item>
            {
                self.j.with_producer(CallbackJ {
                    i_producer: i_producer,
                    i_len: self.i_len,
                    j_len: self.j_len,
                    callback: self.callback
                })
            }
        }

        struct CallbackJ<CB, I> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            i_producer: I
        }

        impl<CB, I> ProducerCallback<I::Item> for CallbackJ<CB, I>
            where I: Producer,
                  CB: ProducerCallback<I::Item>
        {
            type Output = CB::Output;

            fn callback<J>(self, j_producer: J) -> Self::Output
                where J: Producer<Item = I::Item>
            {
                let producer = InterleaveProducer::new(self.i_producer, j_producer, self.i_len, self.j_len, false);
                self.callback.callback(producer)
            }
        }
    }
}

pub struct InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    i: I,
    j: J,
    i_len: usize,
    j_len: usize,
    flag: bool,
}

impl<I, J> InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    fn new(i: I, j: J, i_len: usize, j_len: usize, flag: bool) -> InterleaveProducer<I, J> {
        InterleaveProducer { i, j, i_len, j_len, flag }
    }
}

impl<I, J> Producer for InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    type Item = I::Item;
    type IntoIter = InterleaveSeq<I::IntoIter, J::IntoIter>;

    fn into_iter(self) -> Self::IntoIter {
        InterleaveSeq::new(self.i.into_iter(), self.j.into_iter())
    }

    fn min_len(&self) -> usize {
        cmp::max(self.i.min_len(), self.j.min_len())
    }

    fn max_len(&self) -> usize {
        cmp::min(self.i.max_len(), self.j.max_len())
    }

    fn split_at(self, index: usize) -> (Self, Self) {
        // Switch on state
        let even = index%2 == 0;
        let idx = index >> 1;

        // We know 0 < index <= self.i_len + self.j_len
        // Find a, b satisfying the following conditions:
        // (1) 0 < a <= self.i_len
        // (2) 0 < b <= self.j_len
        // (3) a + b == index
        // assert!(index <= self.i_len + self.j_len);

        let odd_offset = |flag| if flag { 0 } else { 1 };

        // desired split
        let (i_idx, j_idx) = (idx + odd_offset(even || self.flag),
                              idx + odd_offset(even || !self.flag));

        let (i_split, j_split) = if self.i_len >= i_idx && self.j_len >= j_idx {
            (i_idx, j_idx)
        } else if self.i_len >= i_idx {
            // j too short
            (index - self.j_len, self.j_len)
        } else {
            // i too short
            (self.i_len, index - self.i_len)
        };

        let trailing_flag = even == self.flag;
        let (i_left, i_right) = self.i.split_at(i_split);
        let (j_left, j_right) = self.j.split_at(j_split);

        (InterleaveProducer::new(
            i_left,
            j_left,
            i_split,
            j_split,
            self.flag
        ), InterleaveProducer::new(
            i_right,
            j_right,
            self.i_len - i_split,
            self.j_len - j_split,
            trailing_flag
        ))
    }
}

/// Wrapper for Interleave to implement DoubleEndedIterator and
/// ExactSizeIterator
pub struct InterleaveSeq<I, J> {
    i: I,
    j: J,
    flag: bool
}

impl<I, J> InterleaveSeq<I, J> {
    fn new(i: I, j: J) -> InterleaveSeq<I, J>
        where I: DoubleEndedIterator + ExactSizeIterator,
              J: DoubleEndedIterator<Item = I::Item> + ExactSizeIterator<Item = I::Item>
    {
        InterleaveSeq { i: i, j: j, flag: false }
    }
}

impl<I, J> Iterator for InterleaveSeq<I, J>
    where I: Iterator,
          J: Iterator<Item = I::Item>
{
    type Item = I::Item;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.flag = !self.flag;
        if self.flag {
            match self.i.next() {
                None => self.j.next(),
                r => r
            }
        } else {
            match self.j.next() {
                None => self.i.next(),
                r => r
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (ih, jh) = (self.i.size_hint(), self.j.size_hint());
        let min = ih.0.checked_add(jh.0).unwrap_or(usize::MAX);
        let max = match (ih.1, jh.1) {
            (Some(x), Some(y)) => x.checked_add(y),
            _=> None
        };
        (min, max)
    }
}

// The implementation for DoubleEndedIterator requires
// ExactSizeIterator to provide `next_back()`. The last element will
// come from the iterator that runs out last (ie has the most elements
// in it). If the iterators have the same number of elements, then the
// last iterator will provide the last element.
impl<I, J> DoubleEndedIterator for InterleaveSeq<I, J>
    where I: DoubleEndedIterator + ExactSizeIterator,
          J: DoubleEndedIterator<Item = I::Item> + ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn next_back(&mut self) -> Option<I::Item> {
        if self.i.len() <= self.j.len() {
            self.j.next_back()
        } else {
            self.i.next_back()
        }
    }
}

impl<I, J> ExactSizeIterator for InterleaveSeq<I, J>
    where I: ExactSizeIterator,
          J: ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn len(&self) -> usize {
        self.i.len() + self.j.len()
    }
}
<commit_msg>interleave: Don't use shorthand field initialization<commit_after>use super::internal::*;
use super::*;
use std::usize;

/// `Interleave` is an iterator that interleaves elements of iterators
/// `i` and `j` in one continuous iterator. This struct is created by
/// the [`interleave()`] method on [`IndexedParallelIterator`]
///
/// [`interleave()`]: trait.IndexedParallelIterator.html#method.interleave
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    i: I,
    j: J,
    flag: bool,
}

/// Create a new `Interleave` iterator
///
/// NB: a free fn because it is NOT part of the end-user API.
pub fn new<I, J>(i: I, j: J) -> Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    Interleave { i: i, j: j, flag: false }
}

impl<I, J> ParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>
{
    type Item = I::Item;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: Consumer<I::Item>
    {
        bridge(self, consumer)
    }

    fn opt_len(&mut self) -> Option<usize> {
        Some(self.len())
    }
}

impl<I, J> IndexedParallelIterator for Interleave<I, J>
    where I: IndexedParallelIterator,
          J: IndexedParallelIterator<Item = I::Item>,
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self, consumer)
    }

    fn len(&mut self) -> usize {
        self.i.len() + self.j.len()
    }

    fn with_producer<CB>(mut self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        let (i_len, j_len) = (self.i.len(), self.j.len());
        return self.i.with_producer(CallbackI {
            callback: callback,
            i_len: i_len,
            j_len: j_len,
            j: self.j
        });

        struct CallbackI<CB, J> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            j: J
        }

        impl<CB, J> ProducerCallback<J::Item> for CallbackI<CB, J>
            where J: IndexedParallelIterator,
                  CB: ProducerCallback<J::Item>
        {
            type Output = CB::Output;

            fn callback<I>(self, i_producer: I) -> Self::Output
                where I: Producer<Item = J::Item>
            {
                self.j.with_producer(CallbackJ {
                    i_producer: i_producer,
                    i_len: self.i_len,
                    j_len: self.j_len,
                    callback: self.callback
                })
            }
        }

        struct CallbackJ<CB, I> {
            callback: CB,
            i_len: usize,
            j_len: usize,
            i_producer: I
        }

        impl<CB, I> ProducerCallback<I::Item> for CallbackJ<CB, I>
            where I: Producer,
                  CB: ProducerCallback<I::Item>
        {
            type Output = CB::Output;

            fn callback<J>(self, j_producer: J) -> Self::Output
                where J: Producer<Item = I::Item>
            {
                let producer = InterleaveProducer::new(self.i_producer, j_producer, self.i_len, self.j_len, false);
                self.callback.callback(producer)
            }
        }
    }
}

pub struct InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    i: I,
    j: J,
    i_len: usize,
    j_len: usize,
    flag: bool,
}

impl<I, J> InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    fn new(i: I, j: J, i_len: usize, j_len: usize, flag: bool) -> InterleaveProducer<I, J> {
        InterleaveProducer { i: i, j: j, i_len: i_len, j_len: j_len, flag: flag }
    }
}

impl<I, J> Producer for InterleaveProducer<I, J>
    where I: Producer,
          J: Producer<Item = I::Item>
{
    type Item = I::Item;
    type IntoIter = InterleaveSeq<I::IntoIter, J::IntoIter>;

    fn into_iter(self) -> Self::IntoIter {
        InterleaveSeq::new(self.i.into_iter(), self.j.into_iter())
    }

    fn min_len(&self) -> usize {
        cmp::max(self.i.min_len(), self.j.min_len())
    }

    fn max_len(&self) -> usize {
        cmp::min(self.i.max_len(), self.j.max_len())
    }

    fn split_at(self, index: usize) -> (Self, Self) {
        // Switch on state
        let even = index%2 == 0;
        let idx = index >> 1;

        // We know 0 < index <= self.i_len + self.j_len
        // Find a, b satisfying the following conditions:
        // (1) 0 < a <= self.i_len
        // (2) 0 < b <= self.j_len
        // (3) a + b == index
        // assert!(index <= self.i_len + self.j_len);

        let odd_offset = |flag| if flag { 0 } else { 1 };

        // desired split
        let (i_idx, j_idx) = (idx + odd_offset(even || self.flag),
                              idx + odd_offset(even || !self.flag));

        let (i_split, j_split) = if self.i_len >= i_idx && self.j_len >= j_idx {
            (i_idx, j_idx)
        } else if self.i_len >= i_idx {
            // j too short
            (index - self.j_len, self.j_len)
        } else {
            // i too short
            (self.i_len, index - self.i_len)
        };

        let trailing_flag = even == self.flag;
        let (i_left, i_right) = self.i.split_at(i_split);
        let (j_left, j_right) = self.j.split_at(j_split);

        (InterleaveProducer::new(
            i_left,
            j_left,
            i_split,
            j_split,
            self.flag
        ), InterleaveProducer::new(
            i_right,
            j_right,
            self.i_len - i_split,
            self.j_len - j_split,
            trailing_flag
        ))
    }
}

/// Wrapper for Interleave to implement DoubleEndedIterator and
/// ExactSizeIterator
pub struct InterleaveSeq<I, J> {
    i: I,
    j: J,
    flag: bool
}

impl<I, J> InterleaveSeq<I, J> {
    fn new(i: I, j: J) -> InterleaveSeq<I, J>
        where I: DoubleEndedIterator + ExactSizeIterator,
              J: DoubleEndedIterator<Item = I::Item> + ExactSizeIterator<Item = I::Item>
    {
        InterleaveSeq { i: i, j: j, flag: false }
    }
}

impl<I, J> Iterator for InterleaveSeq<I, J>
    where I: Iterator,
          J: Iterator<Item = I::Item>
{
    type Item = I::Item;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.flag = !self.flag;
        if self.flag {
            match self.i.next() {
                None => self.j.next(),
                r => r
            }
        } else {
            match self.j.next() {
                None => self.i.next(),
                r => r
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (ih, jh) = (self.i.size_hint(), self.j.size_hint());
        let min = ih.0.checked_add(jh.0).unwrap_or(usize::MAX);
        let max = match (ih.1, jh.1) {
            (Some(x), Some(y)) => x.checked_add(y),
            _=> None
        };
        (min, max)
    }
}

// The implementation for DoubleEndedIterator requires
// ExactSizeIterator to provide `next_back()`. The last element will
// come from the iterator that runs out last (ie has the most elements
// in it). If the iterators have the same number of elements, then the
// last iterator will provide the last element.
impl<I, J> DoubleEndedIterator for InterleaveSeq<I, J>
    where I: DoubleEndedIterator + ExactSizeIterator,
          J: DoubleEndedIterator<Item = I::Item> + ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn next_back(&mut self) -> Option<I::Item> {
        if self.i.len() <= self.j.len() {
            self.j.next_back()
        } else {
            self.i.next_back()
        }
    }
}

impl<I, J> ExactSizeIterator for InterleaveSeq<I, J>
    where I: ExactSizeIterator,
          J: ExactSizeIterator<Item = I::Item>
{
    #[inline]
    fn len(&self) -> usize {
        self.i.len() + self.j.len()
    }
}
<|endoftext|>


<commit_before>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).unwrap().st_ino;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "WRITE".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "READ".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: u64) -> Option<(String, String)> {
        let file = File::open("/proc/locks").unwrap();
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: u64 = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()))
            }
        }
        None
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<commit_msg>Try to get tests working on 32 bits<commit_after>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).unwrap().st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "WRITE".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "READ".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").unwrap();
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()))
            }
        }
        None
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<|endoftext|>