<commit_before>#![feature(rustc_private, never_type, decl_macro)]
#![allow(intra_doc_link_resolution_failure)]

extern crate log;
extern crate rustc;
extern crate rustc_allocator;
extern crate rustc_codegen_ssa;
extern crate rustc_codegen_utils;
extern crate rustc_data_structures;
extern crate rustc_fs_util;
extern crate rustc_incremental;
extern crate rustc_mir;
extern crate rustc_target;
extern crate syntax;

use std::any::Any;
use std::fs::File;
use std::sync::mpsc;

use rustc::dep_graph::DepGraph;
use rustc::middle::cstore::MetadataLoader;
use rustc::session::{
    config::{OutputFilenames, OutputType},
    CompileIncomplete,
};
use rustc::ty::query::Providers;
use rustc::mir::mono::{Linkage as RLinkage, Visibility};
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::CrateInfo;
use rustc_codegen_utils::codegen_backend::CodegenBackend;
use rustc_codegen_utils::link::out_filename;

use cranelift::codegen::settings;
use cranelift_faerie::*;

use crate::constant::ConstantCx;
use crate::prelude::*;

mod abi;
mod allocator;
mod analyze;
mod archive;
mod base;
mod common;
mod constant;
mod intrinsics;
mod link;
mod link_copied;
mod main_shim;
mod metadata;
mod pretty_clif;
mod trap;
mod unimpl;
mod vtable;

mod prelude {
    pub use std::any::Any;
    pub use std::collections::{HashMap, HashSet};

    pub use syntax::ast::{FloatTy, IntTy, UintTy};
    pub use syntax::source_map::DUMMY_SP;

    pub use rustc::bug;
    pub use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
    pub use rustc::mir::{self, interpret::AllocId, *};
    pub use rustc::session::{
        config::{CrateType, Lto},
        Session,
    };
    pub use rustc::ty::layout::{self, Abi, LayoutOf, Scalar, Size, TyLayout, VariantIdx};
    pub use rustc::ty::{
        self, subst::Substs, FnSig, Instance, InstanceDef, ParamEnv, PolyFnSig, Ty, TyCtxt,
        TypeAndMut, TypeFoldable,
    };
    pub use rustc_data_structures::{
        fx::{FxHashMap, FxHashSet},
        indexed_vec::Idx,
        sync::Lrc,
    };
    pub use rustc_mir::monomorphize::{collector, MonoItem};

    pub use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
    pub use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleKind};
    pub use rustc_codegen_ssa::traits::*;

    pub use cranelift::codegen::ir::{
        condcodes::IntCC, function::Function, ExternalName, FuncRef, Inst, StackSlot,
    };
    pub use cranelift::codegen::isa::CallConv;
    pub use cranelift::codegen::Context;
    pub use cranelift::prelude::*;
    pub use cranelift_module::{Backend, DataContext, DataId, FuncId, Linkage, Module};
    pub use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

    pub use crate::abi::*;
    pub use crate::base::{trans_operand, trans_place};
    pub use crate::common::*;
    pub use crate::trap::*;
    pub use crate::unimpl::{unimpl, with_unimpl_span};
    pub use crate::Caches;
}

pub struct Caches<'tcx> {
    pub context: Context,
    pub vtables: HashMap<(Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>), DataId>,
}

impl<'tcx> Caches<'tcx> {
    fn new() -> Self {
        Caches {
            context: Context::new(),
            vtables: HashMap::new(),
        }
    }
}

struct CraneliftCodegenBackend;

impl CodegenBackend for CraneliftCodegenBackend {
    fn init(&self, sess: &Session) {
        for cty in sess.opts.crate_types.iter() {
            match *cty {
                CrateType::Rlib | CrateType::Dylib | CrateType::Executable => {}
                _ => {
                    sess.err(&format!(
                        "Rustc codegen cranelift doesn't support output type {}",
                        cty
                    ));
                }
            }
        }
        match sess.lto() {
            Lto::Fat | Lto::Thin | Lto::ThinLocal => {
                sess.warn("Rustc codegen cranelift doesn't support lto");
            }
            Lto::No => {}
        }
        if sess.opts.cg.rpath {
            sess.err("rpath is not yet supported");
        }
        if sess.opts.debugging_opts.pgo_gen.is_some() {
            sess.err("pgo is not supported");
        }
    }

    fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync> {
        Box::new(crate::metadata::CraneliftMetadataLoader)
    }

    fn provide(&self, providers: &mut Providers) {
        rustc_codegen_utils::symbol_names::provide(providers);
        rustc_codegen_ssa::back::symbol_export::provide(providers);

        providers.target_features_whitelist = |_tcx, _cnum| Lrc::new(Default::default());
    }
    fn provide_extern(&self, providers: &mut Providers) {
        rustc_codegen_ssa::back::symbol_export::provide_extern(providers);
    }

    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        _rx: mpsc::Receiver<Box<dyn Any + Send>>,
    ) -> Box<dyn Any> {
        if !tcx.sess.crate_types.get().contains(&CrateType::Executable)
            && std::env::var("SHOULD_RUN").is_ok()
        {
            tcx.sess
                .err("Can't JIT run non executable (SHOULD_RUN env var is set)");
        }

        tcx.sess.abort_if_errors();

        let metadata = tcx.encode_metadata();

        // TODO: move to the end of this function when compiling libcore doesn't have unimplemented stuff anymore
        save_incremental(tcx);
        tcx.sess.warn("Saved incremental data");

        let mut log = if cfg!(debug_assertions) {
            Some(File::create(concat!(env!("CARGO_MANIFEST_DIR"), "/target/out/log.txt")).unwrap())
        } else {
            None
        };

        if std::env::var("SHOULD_RUN").is_ok() {
            let mut jit_module: Module<SimpleJITBackend> = Module::new(SimpleJITBuilder::new());
            assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type());

            let sig = Signature {
                params: vec![
                    AbiParam::new(jit_module.target_config().pointer_type()),
                    AbiParam::new(jit_module.target_config().pointer_type()),
                ],
                returns: vec![AbiParam::new(
                    jit_module.target_config().pointer_type(), /*isize*/
                )],
                call_conv: CallConv::SystemV,
            };
            let main_func_id = jit_module
                .declare_function("main", Linkage::Import, &sig)
                .unwrap();

            codegen_cgus(tcx, &mut jit_module, &mut log);
            crate::allocator::codegen(tcx.sess, &mut jit_module);
            jit_module.finalize_definitions();

            tcx.sess.abort_if_errors();
            println!("Compiled everything");
            println!("Rustc codegen cranelift will JIT run the executable, because the SHOULD_RUN env var is set");

            let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id);
            println!("🎉 Finalized everything");

            let f: extern "C" fn(isize, *const *const u8) -> isize =
                unsafe { ::std::mem::transmute(finalized_main) };
            let res = f(0, 0 as *const _);
            tcx.sess.warn(&format!("🚀 main returned {}", res));

            jit_module.finish();
            ::std::process::exit(0);
        } else {
            let new_module = |name: String| {
                let module: Module<FaerieBackend> = Module::new(
                    FaerieBuilder::new(
                        build_isa(tcx.sess),
                        name + ".o",
                        FaerieTrapCollection::Disabled,
                        FaerieBuilder::default_libcall_names(),
                    )
                    .unwrap(),
                );
                assert_eq!(
                    pointer_ty(tcx),
                    module.target_config().pointer_type()
                );
                module
            };

            let mut faerie_module = new_module("some_file".to_string());

            codegen_cgus(tcx, &mut faerie_module, &mut log);
            crate::allocator::codegen(tcx.sess, &mut faerie_module);

            tcx.sess.abort_if_errors();

            let emit_module = |name: &str, kind: ModuleKind, mut module: Module<FaerieBackend>| {
                module.finalize_definitions();
                let artifact = module.finish().artifact;

                let tmp_file = tcx
                    .output_filenames(LOCAL_CRATE)
                    .temp_path(OutputType::Object, Some(name));
                let obj = artifact.emit().unwrap();
                std::fs::write(&tmp_file, obj).unwrap();
                CompiledModule {
                    name: name.to_string(),
                    kind,
                    object: Some(tmp_file),
                    bytecode: None,
                    bytecode_compressed: None,
                }
            };

            return Box::new(CodegenResults {
                crate_name: tcx.crate_name(LOCAL_CRATE),
                modules: vec![emit_module("dummy_name", ModuleKind::Regular, faerie_module)],
                allocator_module: None,
                metadata_module: CompiledModule {
                    name: "dummy_metadata".to_string(),
                    kind: ModuleKind::Metadata,
                    object: None,
                    bytecode: None,
                    bytecode_compressed: None,
                },
                crate_hash: tcx.crate_hash(LOCAL_CRATE),
                metadata,
                windows_subsystem: None, // Windows is not yet supported
                linker_info: LinkerInfo::new(tcx),
                crate_info: CrateInfo::new(tcx),
            });
        }
    }

    fn join_codegen_and_link(
        &self,
        res: Box<dyn Any>,
        sess: &Session,
        _dep_graph: &DepGraph,
        outputs: &OutputFilenames,
    ) -> Result<(), CompileIncomplete> {
        let res = *res
            .downcast::<CodegenResults>()
            .expect("Expected CraneliftCodegenBackend's CodegenResult, found Box<Any>");

        for &crate_type in sess.opts.crate_types.iter() {
            let output_name = out_filename(sess, crate_type, &outputs, &res.crate_name.as_str());
            match crate_type {
                CrateType::Rlib => link::link_rlib(sess, &res, output_name),
                CrateType::Dylib | CrateType::Executable => {
                    link::link_natively(sess, crate_type, &res, &output_name);
                }
                _ => sess.fatal(&format!("Unsupported crate type: {:?}", crate_type)),
            }
        }
        Ok(())
    }
}

fn build_isa(sess: &Session) -> Box<isa::TargetIsa + 'static> {
    use rustc::session::config::OptLevel;

    let mut flags_builder = settings::builder();
    flags_builder.enable("is_pic").unwrap();
    flags_builder.set("enable_verifier", if cfg!(debug_assertions) {
        "true"
    } else {
        "false"
    }).unwrap();

    match sess.opts.optimize {
        OptLevel::No => {
            flags_builder.set("opt_level", "fastest").unwrap();
        }
        OptLevel::Less | OptLevel::Default => {}
        OptLevel::Aggressive => {
            flags_builder.set("opt_level", "best").unwrap();
        }
        OptLevel::Size | OptLevel::SizeMin => {
            sess.warn("Optimizing for size is not supported. Just ignoring the request");
        }
    }

    let flags = settings::Flags::new(flags_builder);
    cranelift::codegen::isa::lookup(sess.target.target.llvm_target.parse().unwrap())
        .unwrap()
        .finish(flags)
}

fn codegen_cgus<'a, 'tcx: 'a>(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    module: &mut Module<impl Backend + 'static>,
    log: &mut Option<File>,
) {
    let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
    let mono_items = cgus
        .iter()
        .map(|cgu| cgu.items().iter())
        .flatten()
        .map(|(&mono_item, &(linkage, vis))| (mono_item, (linkage, vis)))
        .collect::<FxHashMap<_, (_, _)>>();

    codegen_mono_items(tcx, module, log, mono_items);

    crate::main_shim::maybe_create_entry_wrapper(tcx, module);
}

fn codegen_mono_items<'a, 'tcx: 'a>(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    module: &mut Module<impl Backend + 'static>,
    log: &mut Option<File>,
    mono_items: FxHashMap<MonoItem<'tcx>, (RLinkage, Visibility)>,
) {
    time("codegen mono items", move || {
        let mut caches = Caches::new();
        let mut ccx = ConstantCx::default();

        for (mono_item, (_linkage, _vis)) in mono_items {
            unimpl::try_unimpl(tcx, log, || {
                base::trans_mono_item(tcx, module, &mut caches, &mut ccx, mono_item);
            });
        }

        ccx.finalize(tcx, module);
    });
}

fn time<R>(name: &str, f: impl FnOnce() -> R) -> R {
    println!("[{}] start", name);
    let before = ::std::time::Instant::now();
    let res = f();
    let after = ::std::time::Instant::now();
    println!("[{}] end time: {:?}", name, after - before);
    res
}

fn save_incremental<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
    rustc_incremental::assert_dep_graph(tcx);
    rustc_incremental::save_dep_graph(tcx);
    rustc_incremental::finalize_session_directory(tcx.sess, tcx.crate_hash(LOCAL_CRATE));
}

/// This is the entrypoint for a hot plugged rustc_codegen_cranelift
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
    Box::new(CraneliftCodegenBackend)
}
<commit_msg>Move emit_module a bit up<commit_after>#![feature(rustc_private, never_type, decl_macro)]
#![allow(intra_doc_link_resolution_failure)]

extern crate log;
extern crate rustc;
extern crate rustc_allocator;
extern crate rustc_codegen_ssa;
extern crate rustc_codegen_utils;
extern crate rustc_data_structures;
extern crate rustc_fs_util;
extern crate rustc_incremental;
extern crate rustc_mir;
extern crate rustc_target;
extern crate syntax;

use std::any::Any;
use std::fs::File;
use std::sync::mpsc;

use rustc::dep_graph::DepGraph;
use rustc::middle::cstore::MetadataLoader;
use rustc::session::{
    config::{OutputFilenames, OutputType},
    CompileIncomplete,
};
use rustc::ty::query::Providers;
use rustc::mir::mono::{Linkage as RLinkage, Visibility};
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::CrateInfo;
use rustc_codegen_utils::codegen_backend::CodegenBackend;
use rustc_codegen_utils::link::out_filename;

use cranelift::codegen::settings;
use cranelift_faerie::*;

use crate::constant::ConstantCx;
use crate::prelude::*;

mod abi;
mod allocator;
mod analyze;
mod archive;
mod base;
mod common;
mod constant;
mod intrinsics;
mod link;
mod link_copied;
mod main_shim;
mod metadata;
mod pretty_clif;
mod trap;
mod unimpl;
mod vtable;

mod prelude {
    pub use std::any::Any;
    pub use std::collections::{HashMap, HashSet};

    pub use syntax::ast::{FloatTy, IntTy, UintTy};
    pub use syntax::source_map::DUMMY_SP;

    pub use rustc::bug;
    pub use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
    pub use rustc::mir::{self, interpret::AllocId, *};
    pub use rustc::session::{
        config::{CrateType, Lto},
        Session,
    };
    pub use rustc::ty::layout::{self, Abi, LayoutOf, Scalar, Size, TyLayout, VariantIdx};
    pub use rustc::ty::{
        self, subst::Substs, FnSig, Instance, InstanceDef, ParamEnv, PolyFnSig, Ty, TyCtxt,
        TypeAndMut, TypeFoldable,
    };
    pub use rustc_data_structures::{
        fx::{FxHashMap, FxHashSet},
        indexed_vec::Idx,
        sync::Lrc,
    };
    pub use rustc_mir::monomorphize::{collector, MonoItem};

    pub use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
    pub use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleKind};
    pub use rustc_codegen_ssa::traits::*;

    pub use cranelift::codegen::ir::{
        condcodes::IntCC, function::Function, ExternalName, FuncRef, Inst, StackSlot,
    };
    pub use cranelift::codegen::isa::CallConv;
    pub use cranelift::codegen::Context;
    pub use cranelift::prelude::*;
    pub use cranelift_module::{Backend, DataContext, DataId, FuncId, Linkage, Module};
    pub use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

    pub use crate::abi::*;
    pub use crate::base::{trans_operand, trans_place};
    pub use crate::common::*;
    pub use crate::trap::*;
    pub use crate::unimpl::{unimpl, with_unimpl_span};
    pub use crate::Caches;
}

pub struct Caches<'tcx> {
    pub context: Context,
    pub vtables: HashMap<(Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>), DataId>,
}

impl<'tcx> Caches<'tcx> {
    fn new() -> Self {
        Caches {
            context: Context::new(),
            vtables: HashMap::new(),
        }
    }
}

struct CraneliftCodegenBackend;

impl CodegenBackend for CraneliftCodegenBackend {
    fn init(&self, sess: &Session) {
        for cty in sess.opts.crate_types.iter() {
            match *cty {
                CrateType::Rlib | CrateType::Dylib | CrateType::Executable => {}
                _ => {
                    sess.err(&format!(
                        "Rustc codegen cranelift doesn't support output type {}",
                        cty
                    ));
                }
            }
        }
        match sess.lto() {
            Lto::Fat | Lto::Thin | Lto::ThinLocal => {
                sess.warn("Rustc codegen cranelift doesn't support lto");
            }
            Lto::No => {}
        }
        if sess.opts.cg.rpath {
            sess.err("rpath is not yet supported");
        }
        if sess.opts.debugging_opts.pgo_gen.is_some() {
            sess.err("pgo is not supported");
        }
    }

    fn metadata_loader(&self) -> Box<dyn MetadataLoader + Sync> {
        Box::new(crate::metadata::CraneliftMetadataLoader)
    }

    fn provide(&self, providers: &mut Providers) {
        rustc_codegen_utils::symbol_names::provide(providers);
        rustc_codegen_ssa::back::symbol_export::provide(providers);

        providers.target_features_whitelist = |_tcx, _cnum| Lrc::new(Default::default());
    }
    fn provide_extern(&self, providers: &mut Providers) {
        rustc_codegen_ssa::back::symbol_export::provide_extern(providers);
    }

    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        _rx: mpsc::Receiver<Box<dyn Any + Send>>,
    ) -> Box<dyn Any> {
        if !tcx.sess.crate_types.get().contains(&CrateType::Executable)
            && std::env::var("SHOULD_RUN").is_ok()
        {
            tcx.sess
                .err("Can't JIT run non executable (SHOULD_RUN env var is set)");
        }

        tcx.sess.abort_if_errors();

        let metadata = tcx.encode_metadata();

        // TODO: move to the end of this function when compiling libcore doesn't have unimplemented stuff anymore
        save_incremental(tcx);
        tcx.sess.warn("Saved incremental data");

        let mut log = if cfg!(debug_assertions) {
            Some(File::create(concat!(env!("CARGO_MANIFEST_DIR"), "/target/out/log.txt")).unwrap())
        } else {
            None
        };

        if std::env::var("SHOULD_RUN").is_ok() {
            let mut jit_module: Module<SimpleJITBackend> = Module::new(SimpleJITBuilder::new());
            assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type());

            let sig = Signature {
                params: vec![
                    AbiParam::new(jit_module.target_config().pointer_type()),
                    AbiParam::new(jit_module.target_config().pointer_type()),
                ],
                returns: vec![AbiParam::new(
                    jit_module.target_config().pointer_type(), /*isize*/
                )],
                call_conv: CallConv::SystemV,
            };
            let main_func_id = jit_module
                .declare_function("main", Linkage::Import, &sig)
                .unwrap();

            codegen_cgus(tcx, &mut jit_module, &mut log);
            crate::allocator::codegen(tcx.sess, &mut jit_module);
            jit_module.finalize_definitions();

            tcx.sess.abort_if_errors();
            println!("Compiled everything");
            println!("Rustc codegen cranelift will JIT run the executable, because the SHOULD_RUN env var is set");

            let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id);
            println!("🎉 Finalized everything");

            let f: extern "C" fn(isize, *const *const u8) -> isize =
                unsafe { ::std::mem::transmute(finalized_main) };
            let res = f(0, 0 as *const _);
            tcx.sess.warn(&format!("🚀 main returned {}", res));

            jit_module.finish();
            ::std::process::exit(0);
        } else {
            let new_module = |name: String| {
                let module: Module<FaerieBackend> = Module::new(
                    FaerieBuilder::new(
                        build_isa(tcx.sess),
                        name + ".o",
                        FaerieTrapCollection::Disabled,
                        FaerieBuilder::default_libcall_names(),
                    )
                    .unwrap(),
                );
                assert_eq!(
                    pointer_ty(tcx),
                    module.target_config().pointer_type()
                );
                module
            };

            let emit_module = |name: &str, kind: ModuleKind, mut module: Module<FaerieBackend>| {
                module.finalize_definitions();
                let artifact = module.finish().artifact;

                let tmp_file = tcx
                    .output_filenames(LOCAL_CRATE)
                    .temp_path(OutputType::Object, Some(name));
                let obj = artifact.emit().unwrap();
                std::fs::write(&tmp_file, obj).unwrap();
                CompiledModule {
                    name: name.to_string(),
                    kind,
                    object: Some(tmp_file),
                    bytecode: None,
                    bytecode_compressed: None,
                }
            };

            let mut faerie_module = new_module("some_file".to_string());

            codegen_cgus(tcx, &mut faerie_module, &mut log);
            crate::allocator::codegen(tcx.sess, &mut faerie_module);

            tcx.sess.abort_if_errors();

            return Box::new(CodegenResults {
                crate_name: tcx.crate_name(LOCAL_CRATE),
                modules: vec![emit_module("dummy_name", ModuleKind::Regular, faerie_module)],
                allocator_module: None,
                metadata_module: CompiledModule {
                    name: "dummy_metadata".to_string(),
                    kind: ModuleKind::Metadata,
                    object: None,
                    bytecode: None,
                    bytecode_compressed: None,
                },
                crate_hash: tcx.crate_hash(LOCAL_CRATE),
                metadata,
                windows_subsystem: None, // Windows is not yet supported
                linker_info: LinkerInfo::new(tcx),
                crate_info: CrateInfo::new(tcx),
            });
        }
    }

    fn join_codegen_and_link(
        &self,
        res: Box<dyn Any>,
        sess: &Session,
        _dep_graph: &DepGraph,
        outputs: &OutputFilenames,
    ) -> Result<(), CompileIncomplete> {
        let res = *res
            .downcast::<CodegenResults>()
            .expect("Expected CraneliftCodegenBackend's CodegenResult, found Box<Any>");

        for &crate_type in sess.opts.crate_types.iter() {
            let output_name = out_filename(sess, crate_type, &outputs, &res.crate_name.as_str());
            match crate_type {
                CrateType::Rlib => link::link_rlib(sess, &res, output_name),
                CrateType::Dylib | CrateType::Executable => {
                    link::link_natively(sess, crate_type, &res, &output_name);
                }
                _ => sess.fatal(&format!("Unsupported crate type: {:?}", crate_type)),
            }
        }
        Ok(())
    }
}

fn build_isa(sess: &Session) -> Box<isa::TargetIsa + 'static> {
    use rustc::session::config::OptLevel;

    let mut flags_builder = settings::builder();
    flags_builder.enable("is_pic").unwrap();
    flags_builder.set("enable_verifier", if cfg!(debug_assertions) {
        "true"
    } else {
        "false"
    }).unwrap();

    match sess.opts.optimize {
        OptLevel::No => {
            flags_builder.set("opt_level", "fastest").unwrap();
        }
        OptLevel::Less | OptLevel::Default => {}
        OptLevel::Aggressive => {
            flags_builder.set("opt_level", "best").unwrap();
        }
        OptLevel::Size | OptLevel::SizeMin => {
            sess.warn("Optimizing for size is not supported. Just ignoring the request");
        }
    }

    let flags = settings::Flags::new(flags_builder);
    cranelift::codegen::isa::lookup(sess.target.target.llvm_target.parse().unwrap())
        .unwrap()
        .finish(flags)
}

fn codegen_cgus<'a, 'tcx: 'a>(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    module: &mut Module<impl Backend + 'static>,
    log: &mut Option<File>,
) {
    let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
    let mono_items = cgus
        .iter()
        .map(|cgu| cgu.items().iter())
        .flatten()
        .map(|(&mono_item, &(linkage, vis))| (mono_item, (linkage, vis)))
        .collect::<FxHashMap<_, (_, _)>>();

    codegen_mono_items(tcx, module, log, mono_items);

    crate::main_shim::maybe_create_entry_wrapper(tcx, module);
}

fn codegen_mono_items<'a, 'tcx: 'a>(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    module: &mut Module<impl Backend + 'static>,
    log: &mut Option<File>,
    mono_items: FxHashMap<MonoItem<'tcx>, (RLinkage, Visibility)>,
) {
    time("codegen mono items", move || {
        let mut caches = Caches::new();
        let mut ccx = ConstantCx::default();

        for (mono_item, (_linkage, _vis)) in mono_items {
            unimpl::try_unimpl(tcx, log, || {
                base::trans_mono_item(tcx, module, &mut caches, &mut ccx, mono_item);
            });
        }

        ccx.finalize(tcx, module);
    });
}

fn time<R>(name: &str, f: impl FnOnce() -> R) -> R {
    println!("[{}] start", name);
    let before = ::std::time::Instant::now();
    let res = f();
    let after = ::std::time::Instant::now();
    println!("[{}] end time: {:?}", name, after - before);
    res
}

fn save_incremental<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
    rustc_incremental::assert_dep_graph(tcx);
    rustc_incremental::save_dep_graph(tcx);
    rustc_incremental::finalize_session_directory(tcx.sess, tcx.crate_hash(LOCAL_CRATE));
}

/// This is the entrypoint for a hot plugged rustc_codegen_cranelift
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
    Box::new(CraneliftCodegenBackend)
}
<|endoftext|>


<commit_before>//! corepack is a no_std support for messagepack in serde.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.
#![feature(inclusive_range)]
#![feature(inclusive_range_syntax)]
#![feature(unboxed_closures)]
#![feature(collections)]
#![feature(alloc)]
#![feature(range_contains)]
#![feature(const_fn)]
#![feature(box_syntax)]
#![allow(overflowing_literals)]
// always test with libstd turned on
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#[cfg(all(not(feature = "std"), not(test)))]
extern crate core as std;
extern crate serde;
extern crate byteorder;
#[cfg(test)]
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate collections;
extern crate alloc;

use collections::Vec;

pub use ser::Serializer;
pub use de::Deserializer;

pub mod error;
pub mod read;

mod defs;
mod seq_serializer;
mod map_serializer;
mod variant_visitor;
mod ext_visitor;
mod seq_visitor;

mod ser;
mod de;

/// Parse V out of a stream of bytes.
pub fn from_iter<I, V>(mut iter: I) -> Result<V, error::Error>
    where I: Iterator<Item = u8>,
          V: serde::de::DeserializeOwned
{
    let mut de = Deserializer::new(read::CopyRead::new(|buf: &mut [u8]| {
        for i in 0..buf.len() {
            if let Some(byte) = iter.next() {
                buf[i] = byte;
            } else {
                return Err(error::Error::EndOfStream);
            }
        }

        Ok(())
    }));

    V::deserialize(&mut de)
}

/// Parse V out of a slice of bytes.
pub fn from_bytes<'a, V>(bytes: &'a [u8]) -> Result<V, error::Error>
    where V: serde::Deserialize<'a>
{
    let mut position: usize = 0;

    let mut de = Deserializer::new(read::BorrowRead::new(|len: usize| if position + len > bytes.len() {
        Err(error::Error::EndOfStream)
    } else {
        let result = &bytes[position..position + len];

        position += len;

        Ok(result)
    }));

    V::deserialize(&mut de)
}

/// Serialize V into a byte buffer.
pub fn to_bytes<V>(value: V) -> Result<Vec<u8>, error::Error>
    where V: serde::Serialize
{
    let mut bytes = vec![];

    {
        let mut ser = Serializer::new(|buf| {
            bytes.extend_from_slice(buf);
            Ok(())
        });

        try!(value.serialize(&mut ser));
    }

    Ok(bytes)
}

#[cfg(test)]
mod test {
    use serde::{Serialize, Deserialize};
    use std::fmt::Debug;

    #[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
    enum T {
        A(usize),
        B,
        C(i8, i8),
        D { a: isize, b: String },
    }

    fn test_through<T>(expected: T)
        where for<'a> T: Serialize + Deserialize<'a> + PartialEq + Debug
    {
        let x = ::to_bytes(&expected).expect("Failed to serialize");

        let actual = ::from_bytes(&x).expect("Failed to deserialize");

        assert_eq!(expected, actual);
    }

    #[test]
    fn test_str() {
        test_through(format!("Hello World!"))
    }

    #[test]
    fn test_enum() {
        test_through(T::B)
    }

    #[test]
    fn test_enum_newtype() {
        test_through(T::A(42))
    }

    #[test]
    fn test_enum_tuple() {
        test_through(T::C(-3, 22))
    }

    #[test]
    fn test_enum_struct() {
        test_through(T::D {
            a: 9001,
            b: "Hello world!".into(),
        })
    }

    #[test]
    fn test_option() {
        test_through(Some(7))
    }

    #[test]
    fn test_unit_option() {
        test_through(Some(()))
    }

    #[test]
    fn test_char() {
        test_through('b')
    }
}
<commit_msg>fix(test): DeserializeOwned is easier to read<commit_after>//! corepack is a no_std support for messagepack in serde.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.
#![feature(inclusive_range)]
#![feature(inclusive_range_syntax)]
#![feature(unboxed_closures)]
#![feature(collections)]
#![feature(alloc)]
#![feature(range_contains)]
#![feature(const_fn)]
#![feature(box_syntax)]
#![allow(overflowing_literals)]
// always test with libstd turned on
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#[cfg(all(not(feature = "std"), not(test)))]
extern crate core as std;
extern crate serde;
extern crate byteorder;
#[cfg(test)]
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate collections;
extern crate alloc;

use collections::Vec;

pub use ser::Serializer;
pub use de::Deserializer;

pub mod error;
pub mod read;

mod defs;
mod seq_serializer;
mod map_serializer;
mod variant_visitor;
mod ext_visitor;
mod seq_visitor;

mod ser;
mod de;

/// Parse V out of a stream of bytes.
pub fn from_iter<I, V>(mut iter: I) -> Result<V, error::Error>
    where I: Iterator<Item = u8>,
          V: serde::de::DeserializeOwned
{
    let mut de = Deserializer::new(read::CopyRead::new(|buf: &mut [u8]| {
        for i in 0..buf.len() {
            if let Some(byte) = iter.next() {
                buf[i] = byte;
            } else {
                return Err(error::Error::EndOfStream);
            }
        }

        Ok(())
    }));

    V::deserialize(&mut de)
}

/// Parse V out of a slice of bytes.
pub fn from_bytes<'a, V>(bytes: &'a [u8]) -> Result<V, error::Error>
    where V: serde::Deserialize<'a>
{
    let mut position: usize = 0;

    let mut de = Deserializer::new(read::BorrowRead::new(|len: usize| if position + len > bytes.len() {
        Err(error::Error::EndOfStream)
    } else {
        let result = &bytes[position..position + len];

        position += len;

        Ok(result)
    }));

    V::deserialize(&mut de)
}

/// Serialize V into a byte buffer.
pub fn to_bytes<V>(value: V) -> Result<Vec<u8>, error::Error>
    where V: serde::Serialize
{
    let mut bytes = vec![];

    {
        let mut ser = Serializer::new(|buf| {
            bytes.extend_from_slice(buf);
            Ok(())
        });

        try!(value.serialize(&mut ser));
    }

    Ok(bytes)
}

#[cfg(test)]
mod test {
    use serde::Serialize;
    use serde::de::DeserializeOwned;
    use std::fmt::Debug;

    #[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
    enum T {
        A(usize),
        B,
        C(i8, i8),
        D { a: isize, b: String },
    }

    fn test_through<T>(expected: T)
        where T: Serialize + DeserializeOwned + PartialEq + Debug
    {
        let x = ::to_bytes(&expected).expect("Failed to serialize");

        let actual = ::from_bytes(&x).expect("Failed to deserialize");

        assert_eq!(expected, actual);
    }

    #[test]
    fn test_str() {
        test_through(format!("Hello World!"))
    }

    #[test]
    fn test_enum() {
        test_through(T::B)
    }

    #[test]
    fn test_enum_newtype() {
        test_through(T::A(42))
    }

    #[test]
    fn test_enum_tuple() {
        test_through(T::C(-3, 22))
    }

    #[test]
    fn test_enum_struct() {
        test_through(T::D {
            a: 9001,
            b: "Hello world!".into(),
        })
    }

    #[test]
    fn test_option() {
        test_through(Some(7))
    }

    #[test]
    fn test_unit_option() {
        test_through(Some(()))
    }

    #[test]
    fn test_char() {
        test_through('b')
    }
}
<|endoftext|>


<commit_before>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! Open the given URL in the default web browser.
//!
//! ```no_run
//! open::that("http://rust-lang.org").unwrap();
//! ```
//!
//! Alternatively, specify the program to be used to open the path or URL.
//!
//! ```no_run
//! open::with("http://rust-lang.org", "firefox").unwrap();
//! ```
//!
//! # Notes
//!
//! As an operating system program is used, the open operation can fail.
//! Therefore, you are advised to at least check the result and behave
//! accordingly, e.g. by letting the user know that the open operation failed.
//!
//! ```no_run
//! let path = "http://rust-lang.org";
//!
//! match open::that(path) {
//!     Ok(()) => println!("Opened '{}' successfully.", path),
//!     Err(err) => eprintln!("An error occurred when opening '{}': {}", path, err),
//! }
//! ```

#[cfg(target_os = "windows")]
use windows as os;

#[cfg(target_os = "macos")]
use macos as os;

#[cfg(target_os = "ios")]
use ios as os;

#[cfg(target_os = "haiku")]
use haiku as os;

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
use unix as os;

#[cfg(not(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris",
    target_os = "ios",
    target_os = "macos",
    target_os = "windows",
    target_os = "haiku"
)))]
compile_error!("open is not supported on this platform");

use std::{
    ffi::OsStr,
    io,
    process::{Command, Stdio},
    thread,
};

/// Open path with the default application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
///
/// match open::that(path) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
    os::that(path)
}

/// Open path with the given application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
/// let app = "firefox";
///
/// match open::with(path, app) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
    os::with(path, app)
}

/// Open path with the default application in a new thread, which is useful if
/// the program ends up to be blocking. Otherwise, prefer [`that()`].
///
/// See documentation of [`that()`] for more details.
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<io::Result<()>> {
    let path = path.as_ref().to_os_string();
    thread::spawn(|| that(path))
}

/// Open path with the given application in a new thread.
///
/// See documentation of [`with()`] for more details.
pub fn with_in_background<T: AsRef<OsStr>>(
    path: T,
    app: impl Into<String>,
) -> thread::JoinHandle<io::Result<()>> {
    let path = path.as_ref().to_os_string();
    let app = app.into();
    thread::spawn(|| with(path, app))
}

trait IntoResult<T> {
    fn into_result(self) -> T;
}

impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
    fn into_result(self) -> io::Result<()> {
        match self {
            Ok(status) if status.success() => Ok(()),
            Ok(status) => Err(io::Error::new(
                io::ErrorKind::Other,
                format!("Launcher failed with {:?}", status),
            )),
            Err(err) => Err(err),
        }
    }
}

#[cfg(windows)]
impl IntoResult<io::Result<()>> for std::os::raw::c_int {
    fn into_result(self) -> io::Result<()> {
        match self {
            i if i > 32 => Ok(()),
            _ => Err(io::Error::last_os_error()),
        }
    }
}

trait CommandExt {
    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
}

impl CommandExt for Command {
    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
        let mut process = self
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()?;

        process.wait()
    }
}

#[cfg(windows)]
mod windows;

#[cfg(target_os = "macos")]
mod macos;

#[cfg(target_os = "ios")]
mod ios;

#[cfg(target_os = "haiku")]
mod haiku;

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
mod unix;
<commit_msg>fix: deprecate `that_in_background()` as `that()` is definitely non-blocking now.<commit_after>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! Open the given URL in the default web browser.
//!
//! ```no_run
//! open::that("http://rust-lang.org").unwrap();
//! ```
//!
//! Alternatively, specify the program to be used to open the path or URL.
//!
//! ```no_run
//! open::with("http://rust-lang.org", "firefox").unwrap();
//! ```
//!
//! # Notes
//!
//! As an operating system program is used, the open operation can fail.
//! Therefore, you are advised to at least check the result and behave
//! accordingly, e.g. by letting the user know that the open operation failed.
//!
//! ```no_run
//! let path = "http://rust-lang.org";
//!
//! match open::that(path) {
//!     Ok(()) => println!("Opened '{}' successfully.", path),
//!     Err(err) => eprintln!("An error occurred when opening '{}': {}", path, err),
//! }
//! ```

#[cfg(target_os = "windows")]
use windows as os;

#[cfg(target_os = "macos")]
use macos as os;

#[cfg(target_os = "ios")]
use ios as os;

#[cfg(target_os = "haiku")]
use haiku as os;

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
use unix as os;

#[cfg(not(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris",
    target_os = "ios",
    target_os = "macos",
    target_os = "windows",
    target_os = "haiku"
)))]
compile_error!("open is not supported on this platform");

use std::{
    ffi::OsStr,
    io,
    process::{Command, Stdio},
    thread,
};

/// Open path with the default application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
///
/// match open::that(path) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
    os::that(path)
}

/// Open path with the given application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
/// let app = "firefox";
///
/// match open::with(path, app) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
    os::with(path, app)
}

/// Open path with the default application in a new thread.
///
/// See documentation of [`that()`] for more details.
#[deprecated = "Use `that()` as it is non-blocking while making error handling easy."]
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<io::Result<()>> {
    let path = path.as_ref().to_os_string();
    thread::spawn(|| that(path))
}

/// Open path with the given application in a new thread, which is useful if
/// the program ends up to be blocking. Otherwise, prefer [`with()`] for
/// straightforward error handling.
///
/// See documentation of [`with()`] for more details.
pub fn with_in_background<T: AsRef<OsStr>>(
    path: T,
    app: impl Into<String>,
) -> thread::JoinHandle<io::Result<()>> {
    let path = path.as_ref().to_os_string();
    let app = app.into();
    thread::spawn(|| with(path, app))
}

trait IntoResult<T> {
    fn into_result(self) -> T;
}

impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
    fn into_result(self) -> io::Result<()> {
        match self {
            Ok(status) if status.success() => Ok(()),
            Ok(status) => Err(io::Error::new(
                io::ErrorKind::Other,
                format!("Launcher failed with {:?}", status),
            )),
            Err(err) => Err(err),
        }
    }
}

#[cfg(windows)]
impl IntoResult<io::Result<()>> for std::os::raw::c_int {
    fn into_result(self) -> io::Result<()> {
        match self {
            i if i > 32 => Ok(()),
            _ => Err(io::Error::last_os_error()),
        }
    }
}

trait CommandExt {
    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
}

impl CommandExt for Command {
    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
        let mut process = self
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()?;

        process.wait()
    }
}

#[cfg(windows)]
mod windows;

#[cfg(target_os = "macos")]
mod macos;

#[cfg(target_os = "ios")]
mod ios;

#[cfg(target_os = "haiku")]
mod haiku;

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
mod unix;
<|endoftext|>


<commit_before>//! This crate allows you to specify an offset for reads and writes, without changing the current
//! position in a file. This is similar to [`pread()` and `pwrite()`][pread] in C.
//!
//! The major advantages of this type of I/O are:
//!
//! * You don't need to seek before doing a random-access read or write, which is convenient.
//! * Reads don't modify the file at all, so don't require mutability.
//!
//! [pread]: http://man7.org/linux/man-pages/man2/pread.2.html
//!
//! # Examples
//!
//! Read the fifth 512-byte sector of a file:
//!
//! ```no_run
//! # use std::io;
//! use std::fs::File;
//! use positioned_io::ReadAt;
//!
//! # fn foo() -> io::Result<()> {
//! // Note that file does not need to be mut!
//! let file = File::open("foo.data")?;
//! let mut buf = vec![0; 512];
//! let bytes_read = file.read_at(2048, &mut buf)?;
//! # Ok(())
//! # }
//! ```
//!
//! Write an integer to the middle of a file:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! use std::fs::OpenOptions;
//! use positioned_io::WriteAt;
//! use byteorder::{ByteOrder, LittleEndian};
//!
//! # fn foo() -> io::Result<()> {
//! // Put the integer in a buffer.
//! let mut buf = vec![0; 4];
//! LittleEndian::write_u32(&mut buf, 1234);
//!
//! // Write it to the file.
//! let mut file = OpenOptions::new().write(true).open("foo.data")?;
//! file.write_all_at(1 << 20, &buf)?;
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Or, more simply:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use std::fs::OpenOptions;
//! # use byteorder::LittleEndian;
//! // Extend files with writing integers at offsets.
//! use positioned_io::WriteBytesAtExt;
//!
//! # fn foo() -> io::Result<()> {
//! let mut file = OpenOptions::new().write(true).open("foo.data")?;
//! file.write_u32_at::<LittleEndian>(1 << 20, 1234)?;
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Read from anything else that supports ReadAt, like a byte array:
//!
//! ```rust
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use byteorder::BigEndian;
//! use positioned_io::ReadBytesAtExt;
//!
//! # fn foo() -> io::Result<()> {
//! let buf = [0, 5, 254, 212, 0, 3];
//! let n = buf.as_ref().read_i16_at::<BigEndian>(2)?;
//! assert_eq!(n, -300);
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```

#![doc(html_root_url = "https://docs.rs/positioned-io/0.2.2")]

#![warn(missing_debug_implementations)]
#![warn(bare_trait_objects)]

mod byteorder;
pub use byteorder::{ReadBytesAtExt, WriteBytesAtExt};
pub use byteorder::byteio::ByteIo;

mod cursor;
pub use cursor::{Cursor, SizeCursor};

mod slice;
pub use slice::Slice;

extern crate byteorder as extbyteorder;

use std::fs::File;
use std::io::{Error, ErrorKind, Result};

/// Trait for reading bytes at an offset.
///
/// Implementations should be able to read bytes without changing any sort of
/// read position. Self should not change at all. Buffering reads is unlikely
/// to be useful, since each time `read_at()` is called, the position may be
/// completely different.
///
/// # Examples
///
/// Read the fifth 512-byte sector of a file:
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use std::fs::File;
/// use positioned_io::ReadAt;
///
/// let file = File::open("tests/pi.txt")?;
/// let mut buf = [0; 512];
///
/// // read up to 512 bytes
/// let bytes_read = file.read_at(2048, &mut buf)?;
/// #     assert!(buf.starts_with(b"4"));
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```
pub trait ReadAt {
    /// Reads bytes from an offset in this source into a buffer, returning how
    /// many bytes were read.
    ///
    /// This function may yield fewer bytes than the size of `buf`, if it was
    /// interrupted or hit the "end of file".
    ///
    /// See [`Read::read()`](https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read)
    /// for details.
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize>;

    /// Reads the exact number of bytes required to fill `buf` from an offset.
    ///
    /// Errors if the "end of file" is encountered before filling the buffer.
    ///
    /// See [`Read::read_exact()`](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact)
    /// for details.
    fn read_exact_at(&self, mut pos: u64, mut buf: &mut [u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.read_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        if !buf.is_empty() {
            Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
        } else {
            Ok(())
        }
    }
}

/// Trait for writing bytes at an offset.
///
/// Implementations should be able to write bytes at an offset, without
/// changing any sort of write position. Self should not change at all.
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use std::fs::OpenOptions;
/// use positioned_io::WriteAt;
///
/// let mut file = OpenOptions::new().write(true).open("tests/pi.txt")?;
///
/// // write some bytes
/// let bytes_written = file.write_at(2, b"1415926535897932384626433")?;
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```
pub trait WriteAt {
    /// Writes bytes from a buffer to an offset, returning the number of bytes
    /// written.
    ///
    /// This function may write fewer bytes than the size of `buf`, for example
    /// if it is interrupted.
    ///
    /// See [`Write::write()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write)
    /// for details.
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> Result<usize>;

    /// Writes a complete buffer at an offset.
    ///
    /// Errors if it could not write the entire buffer.
    ///
    /// See [`Write::write_all()`](https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all)
    /// for details.
    fn write_all_at(&mut self, mut pos: u64, mut buf: &[u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.write_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }

    /// Flush this writer, ensuring that any intermediately buffered data
    /// reaches its destination.
    ///
    /// This should rarely do anything, since buffering is not very useful for
    /// positioned writes.
    fn flush(&mut self) -> Result<()>;
}

/// Trait to get the size of an I/O object.
///
/// Implementing this for a ReadAt or WriteAt makes it easier for users to predict whether they
/// will read past end-of-file. However, it may not be possible to implement for certain readers
/// or writers that have unknown size.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// # use std::fs::File;
/// use positioned_io::Size;
///
/// # fn foo() -> io::Result<()> {
/// let file = File::open("foo.txt")?;
/// let size = file.size()?;
/// assert_eq!(size, Some(22));
///
/// // Special files probably don't have a size.
/// let file = File::open("/dev/stdin")?;
/// let size = file.size()?;
/// assert_eq!(size, None);
/// # Ok(())
/// # }
/// ```
pub trait Size {
    /// Get the size of this object, in bytes.
    ///
    /// This function may return Ok(None) if the size is unknown, for example if a file is a pipe.
    ///
    /// If a positive value is returned, it should be the value such that reading at greater
    /// offsets always yields end-of-file.
    fn size(&self) -> Result<Option<u64>>;
}

impl Size for File {
    fn size(&self) -> Result<Option<u64>> {
        let md = self.metadata()?;
        if md.is_file() {
            Ok(Some(md.len()))
        } else {
            Ok(None)
        }
    }
}

// Implementation for Unix files.
#[cfg(unix)]
mod unix;

// Implementation for Windows files.
#[cfg(windows)]
mod windows;

// Implementation for arrays, vectors.
mod array;
mod vec;
mod refs;

#[cfg(test)]
mod tests {
    use super::*;

    struct _AssertObjectSafe1(Box<dyn ReadAt>);
    struct _AssertObjectSafe2(Box<dyn WriteAt>);
    struct _AssertObjectSafe3(Box<dyn Size>);
}
<commit_msg>improve Size docs<commit_after>//! This crate allows you to specify an offset for reads and writes, without changing the current
//! position in a file. This is similar to [`pread()` and `pwrite()`][pread] in C.
//!
//! The major advantages of this type of I/O are:
//!
//! * You don't need to seek before doing a random-access read or write, which is convenient.
//! * Reads don't modify the file at all, so don't require mutability.
//!
//! [pread]: http://man7.org/linux/man-pages/man2/pread.2.html
//!
//! # Examples
//!
//! Read the fifth 512-byte sector of a file:
//!
//! ```no_run
//! # use std::io;
//! use std::fs::File;
//! use positioned_io::ReadAt;
//!
//! # fn foo() -> io::Result<()> {
//! // Note that file does not need to be mut!
//! let file = File::open("foo.data")?;
//! let mut buf = vec![0; 512];
//! let bytes_read = file.read_at(2048, &mut buf)?;
//! # Ok(())
//! # }
//! ```
//!
//! Write an integer to the middle of a file:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! use std::fs::OpenOptions;
//! use positioned_io::WriteAt;
//! use byteorder::{ByteOrder, LittleEndian};
//!
//! # fn foo() -> io::Result<()> {
//! // Put the integer in a buffer.
//! let mut buf = vec![0; 4];
//! LittleEndian::write_u32(&mut buf, 1234);
//!
//! // Write it to the file.
//! let mut file = OpenOptions::new().write(true).open("foo.data")?;
//! file.write_all_at(1 << 20, &buf)?;
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Or, more simply:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use std::fs::OpenOptions;
//! # use byteorder::LittleEndian;
//! // Extend files with writing integers at offsets.
//! use positioned_io::WriteBytesAtExt;
//!
//! # fn foo() -> io::Result<()> {
//! let mut file = OpenOptions::new().write(true).open("foo.data")?;
//! file.write_u32_at::<LittleEndian>(1 << 20, 1234)?;
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Read from anything else that supports ReadAt, like a byte array:
//!
//! ```rust
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use byteorder::BigEndian;
//! use positioned_io::ReadBytesAtExt;
//!
//! # fn foo() -> io::Result<()> {
//! let buf = [0, 5, 254, 212, 0, 3];
//! let n = buf.as_ref().read_i16_at::<BigEndian>(2)?;
//! assert_eq!(n, -300);
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```

#![doc(html_root_url = "https://docs.rs/positioned-io/0.2.2")]

#![warn(missing_debug_implementations)]
#![warn(bare_trait_objects)]

mod byteorder;
pub use byteorder::{ReadBytesAtExt, WriteBytesAtExt};
pub use byteorder::byteio::ByteIo;

mod cursor;
pub use cursor::{Cursor, SizeCursor};

mod slice;
pub use slice::Slice;

extern crate byteorder as extbyteorder;

use std::fs::File;
use std::io::{Error, ErrorKind, Result};

/// Trait for reading bytes at an offset.
///
/// Implementations should be able to read bytes without changing any sort of
/// read position. Self should not change at all. Buffering reads is unlikely
/// to be useful, since each time `read_at()` is called, the position may be
/// completely different.
///
/// # Examples
///
/// Read the fifth 512-byte sector of a file:
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use std::fs::File;
/// use positioned_io::ReadAt;
///
/// let file = File::open("tests/pi.txt")?;
/// let mut buf = [0; 512];
///
/// // read up to 512 bytes
/// let bytes_read = file.read_at(2048, &mut buf)?;
/// #     assert!(buf.starts_with(b"4"));
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```
pub trait ReadAt {
    /// Reads bytes from an offset in this source into a buffer, returning how
    /// many bytes were read.
    ///
    /// This function may yield fewer bytes than the size of `buf`, if it was
    /// interrupted or hit the "end of file".
    ///
    /// See [`Read::read()`](https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read)
    /// for details.
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize>;

    /// Reads the exact number of bytes required to fill `buf` from an offset.
    ///
    /// Errors if the "end of file" is encountered before filling the buffer.
    ///
    /// See [`Read::read_exact()`](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact)
    /// for details.
    fn read_exact_at(&self, mut pos: u64, mut buf: &mut [u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.read_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        if !buf.is_empty() {
            Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
        } else {
            Ok(())
        }
    }
}

/// Trait for writing bytes at an offset.
///
/// Implementations should be able to write bytes at an offset, without
/// changing any sort of write position. Self should not change at all.
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use std::fs::OpenOptions;
/// use positioned_io::WriteAt;
///
/// let mut file = OpenOptions::new().write(true).open("tests/pi.txt")?;
///
/// // write some bytes
/// let bytes_written = file.write_at(2, b"1415926535897932384626433")?;
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```
pub trait WriteAt {
    /// Writes bytes from a buffer to an offset, returning the number of bytes
    /// written.
    ///
    /// This function may write fewer bytes than the size of `buf`, for example
    /// if it is interrupted.
    ///
    /// See [`Write::write()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write)
    /// for details.
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> Result<usize>;

    /// Writes a complete buffer at an offset.
    ///
    /// Errors if it could not write the entire buffer.
    ///
    /// See [`Write::write_all()`](https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all)
    /// for details.
    fn write_all_at(&mut self, mut pos: u64, mut buf: &[u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.write_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }

    /// Flush this writer, ensuring that any intermediately buffered data
    /// reaches its destination.
    ///
    /// This should rarely do anything, since buffering is not very useful for
    /// positioned writes.
    fn flush(&mut self) -> Result<()>;
}

/// Trait to get the size in bytes of an I/O object.
///
/// Implementing this for a types with `ReadAt` or `WriteAt` makes it easier
/// for users to predict whether they will read past end-of-file. However, it
/// may not be possible to implement for certain readers or writers that have
/// unknown size.
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// use std::fs::File;
/// use positioned_io::Size;
///
/// let file = File::open("tests/pi.txt")?;
/// let size = file.size()?;
/// assert_eq!(size, Some(1000002));
///
/// // some special files do not have a known size
/// let file = File::open("/dev/stdin")?;
/// let size = file.size()?;
/// assert_eq!(size, None);
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #    try_main().unwrap();
/// # }
/// ```
pub trait Size {
    /// Get the size of this object, in bytes.
    ///
    /// This function may return `Ok(None)` if the size is unknown, for example
    /// for pipes.
    fn size(&self) -> Result<Option<u64>>;
}

impl Size for File {
    fn size(&self) -> Result<Option<u64>> {
        let md = self.metadata()?;
        if md.is_file() {
            Ok(Some(md.len()))
        } else {
            Ok(None)
        }
    }
}

// Implementation for Unix files.
#[cfg(unix)]
mod unix;

// Implementation for Windows files.
#[cfg(windows)]
mod windows;

// Implementation for arrays, vectors.
mod array;
mod vec;
mod refs;

#[cfg(test)]
mod tests {
    use super::*;

    struct _AssertObjectSafe1(Box<dyn ReadAt>);
    struct _AssertObjectSafe2(Box<dyn WriteAt>);
    struct _AssertObjectSafe3(Box<dyn Size>);
}
<|endoftext|>


<commit_before>// Supress warnings generated by bindgen: https://github.com/rust-lang/rust-bindgen/issues/1651
#![allow(deref_nullptr)]
#![allow(unknown_lints)]

//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! Default options:
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("disk.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```
//!
//! Custom options:
//!
//! ```rust
//! # use loopdev::LoopControl;
//! # let lc = LoopControl::open().unwrap();
//! # let ld = lc.next_free().unwrap();
//! #
//! ld.with()
//!     .part_scan(true)
//!     .offset(512 * 1024 * 1024) // 512 MiB
//!     .size_limit(1024 * 1024 * 1024) // 1GiB
//!     .attach("disk.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

#[cfg(feature = "direct_io")]
use bindings::LOOP_SET_DIRECT_IO;
use crate::bindings::{
    loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD, LOOP_SET_STATUS64,
    LO_FLAGS_AUTOCLEAR, LO_FLAGS_READ_ONLY,
};
use libc::{c_int, ioctl};
use std::{
    default::Default,
    fs::{File, OpenOptions},
    io,
    os::unix::prelude::*,
    path::{Path, PathBuf},
};

#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[allow(non_snake_case)]
mod bindings {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
type IoctlRequest = libc::c_ulong;
#[cfg(any(target_os = "android", target_env = "musl"))]
type IoctlRequest = libc::c_int;

const LOOP_CONTROL: &str = "/dev/loop-control";
#[cfg(not(target_os = "android"))]
const LOOP_PREFIX: &str = "/dev/loop";
#[cfg(target_os = "android")]
const LOOP_PREFIX: &str = "/dev/block/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let dev_num = ioctl_to_error(unsafe {
            ioctl(
                self.dev_file.as_raw_fd() as c_int,
                LOOP_CTL_GET_FREE as IoctlRequest,
            )
        })?;
        LoopDevice::open(&format!("{}{}", LOOP_PREFIX, dev_num))
    }
}

impl AsRawFd for LoopControl {
    fn as_raw_fd(&self) -> RawFd {
        self.dev_file.as_raw_fd()
    }
}

impl IntoRawFd for LoopControl {
    fn into_raw_fd(self) -> RawFd {
        self.dev_file.into_raw_fd()
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl IntoRawFd for LoopDevice {
    fn into_raw_fd(self) -> RawFd {
        self.device.into_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file with given options.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let mut ld = LoopDevice::open("/dev/loop3").unwrap();
    /// ld.with().part_scan(true).attach("disk.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn with(&self) -> AttachOptions<'_> {
        AttachOptions {
            device: self,
            info: Default::default(),
            #[cfg(feature = "direct_io")]
            direct_io: false,
        }
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("disk.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        let info = loop_info64 {
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file with loop_info64.
    fn attach_with_loop_info(
        &self, // TODO should be mut? - but changing it is a breaking change
        backing_file: impl AsRef<Path>,
        info: loop_info64,
    ) -> io::Result<()> {
        let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;
        let bf = OpenOptions::new()
            .read(true)
            .write(write_access)
            .open(backing_file)?;
        self.attach_fd_with_loop_info(bf, info)
    }

    /// Attach the loop device to a fd with loop_info.
    fn attach_fd_with_loop_info(&self, bf: impl AsRawFd, info: loop_info64) -> io::Result<()> {
        // Attach the file
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD as IoctlRequest,
                bf.as_raw_fd() as c_int,
            )
        })?;

        let result = unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64 as IoctlRequest,
                &info,
            )
        };
        match ioctl_to_error(result) {
            Err(err) => {
                // Ignore the error to preserve the original error
                let _ = self.detach();
                Err(err)
            }
            Ok(_) => Ok(()),
        }
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Get the device major number
    pub fn major(&self) -> io::Result<u32> {
        self.device
            .metadata()
            .map(|m| unsafe { libc::major(m.rdev()) as u32 })
    }

    /// Get the device major number
    pub fn minor(&self) -> io::Result<u32> {
        self.device
            .metadata()
            .map(|m| unsafe { libc::minor(m.rdev()) as u32 })
    }

    /// Detach a loop device from its backing file.
    ///
    /// Note that the device won't fully detach until a short delay after the underling device file
    /// gets closed. This happens when LoopDev goes out of scope so you should ensure the LoopDev
    /// lives for a short a time as possible.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// # ld.attach_file("disk.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_CLR_FD as IoctlRequest,
                0,
            )
        })?;
        Ok(())
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY as IoctlRequest,
                0,
            )
        })?;
        Ok(())
    }

    // Enable or disable direct I/O for the backing file.
    #[cfg(feature = "direct_io")]
    pub fn set_direct_io(&self, direct_io: bool) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_DIRECT_IO as IoctlRequest,
                if direct_io { 1 } else { 0 },
            )
        })?;
        Ok(())
    }
}

/// Used to set options when attaching a device. Created with [LoopDevice::with()].
///
/// # Examples
///
/// Enable partition scanning on attach:
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop6").unwrap();
/// ld.with()
///     .part_scan(true)
///     .attach("disk.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
///
/// A 1MiB slice of the file located at 1KiB into the file.
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop7").unwrap();
/// ld.with()
///     .offset(1024*1024)
///     .size_limit(1024*1024*1024)
///     .attach("disk.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
pub struct AttachOptions<'d> {
    device: &'d LoopDevice,
    info: loop_info64,
    #[cfg(feature = "direct_io")]
    direct_io: bool,
}

impl AttachOptions<'_> {
    /// Offset in bytes from the start of the backing file the data will start at.
    pub fn offset(mut self, offset: u64) -> Self {
        self.info.lo_offset = offset;
        self
    }

    /// Maximum size of the data in bytes.
    pub fn size_limit(mut self, size_limit: u64) -> Self {
        self.info.lo_sizelimit = size_limit;
        self
    }

    /// Set read only flag
    pub fn read_only(mut self, read_only: bool) -> Self {
        if read_only {
            self.info.lo_flags |= LO_FLAGS_READ_ONLY;
        } else {
            self.info.lo_flags &= !LO_FLAGS_READ_ONLY;
        }
        self
    }

    /// Set autoclear flag
    pub fn autoclear(mut self, read_only: bool) -> Self {
        if read_only {
            self.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
        } else {
            self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR;
        }
        self
    }

    // Enable or disable direct I/O for the backing file.
    #[cfg(feature = "direct_io")]
    pub fn set_direct_io(mut self, direct_io: bool) -> Self {
        self.direct_io = direct_io;
        self
    }

    /// Force the kernel to scan the partition table on a newly created loop device. Note that the
    /// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
    pub fn part_scan(mut self, enable: bool) -> Self {
        if enable {
            self.info.lo_flags |= 1 << 4;
        } else {
            self.info.lo_flags &= u32::max_value() - (1 << 4);
        }
        self
    }

    /// Attach the loop device to a file with the set options.
    pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
        self.device.attach_with_loop_info(backing_file, self.info)?;
        #[cfg(feature = "direct_io")]
        if self.direct_io {
            self.device.set_direct_io(self.direct_io)?;
        }
        Ok(())
    }

    /// Attach the loop device to an fd
    pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> {
        self.device
            .attach_fd_with_loop_info(backing_file_fd, self.info)?;
        #[cfg(feature = "direct_io")]
        if self.direct_io {
            self.device.set_direct_io(self.direct_io)?;
        }
        Ok(())
    }
}

fn ioctl_to_error(ret: i32) -> io::Result<i32> {
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}
<commit_msg>Fix the AttachOptions.part_scan function (#50)<commit_after>// Supress warnings generated by bindgen: https://github.com/rust-lang/rust-bindgen/issues/1651
#![allow(deref_nullptr)]
#![allow(unknown_lints)]

//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! Default options:
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("disk.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```
//!
//! Custom options:
//!
//! ```rust
//! # use loopdev::LoopControl;
//! # let lc = LoopControl::open().unwrap();
//! # let ld = lc.next_free().unwrap();
//! #
//! ld.with()
//!     .part_scan(true)
//!     .offset(512 * 1024 * 1024) // 512 MiB
//!     .size_limit(1024 * 1024 * 1024) // 1GiB
//!     .attach("disk.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

#[cfg(feature = "direct_io")]
use bindings::LOOP_SET_DIRECT_IO;
use crate::bindings::{
    loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD, LOOP_SET_STATUS64,
    LO_FLAGS_AUTOCLEAR, LO_FLAGS_PARTSCAN, LO_FLAGS_READ_ONLY,
};
use libc::{c_int, ioctl};
use std::{
    default::Default,
    fs::{File, OpenOptions},
    io,
    os::unix::prelude::*,
    path::{Path, PathBuf},
};

#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[allow(non_snake_case)]
mod bindings {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
type IoctlRequest = libc::c_ulong;
#[cfg(any(target_os = "android", target_env = "musl"))]
type IoctlRequest = libc::c_int;

const LOOP_CONTROL: &str = "/dev/loop-control";
#[cfg(not(target_os = "android"))]
const LOOP_PREFIX: &str = "/dev/loop";
#[cfg(target_os = "android")]
const LOOP_PREFIX: &str = "/dev/block/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let dev_num = ioctl_to_error(unsafe {
            ioctl(
                self.dev_file.as_raw_fd() as c_int,
                LOOP_CTL_GET_FREE as IoctlRequest,
            )
        })?;
        LoopDevice::open(&format!("{}{}", LOOP_PREFIX, dev_num))
    }
}

impl AsRawFd for LoopControl {
    fn as_raw_fd(&self) -> RawFd {
        self.dev_file.as_raw_fd()
    }
}

impl IntoRawFd for LoopControl {
    fn into_raw_fd(self) -> RawFd {
        self.dev_file.into_raw_fd()
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl IntoRawFd for LoopDevice {
    fn into_raw_fd(self) -> RawFd {
        self.device.into_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file with given options.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let mut ld = LoopDevice::open("/dev/loop3").unwrap();
    /// ld.with().part_scan(true).attach("disk.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn with(&self) -> AttachOptions<'_> {
        AttachOptions {
            device: self,
            info: Default::default(),
            #[cfg(feature = "direct_io")]
            direct_io: false,
        }
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("disk.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        let info = loop_info64 {
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file with loop_info64.
    fn attach_with_loop_info(
        &self, // TODO should be mut? - but changing it is a breaking change
        backing_file: impl AsRef<Path>,
        info: loop_info64,
    ) -> io::Result<()> {
        let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;
        let bf = OpenOptions::new()
            .read(true)
            .write(write_access)
            .open(backing_file)?;
        self.attach_fd_with_loop_info(bf, info)
    }

    /// Attach the loop device to a fd with loop_info.
    fn attach_fd_with_loop_info(&self, bf: impl AsRawFd, info: loop_info64) -> io::Result<()> {
        // Attach the file
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD as IoctlRequest,
                bf.as_raw_fd() as c_int,
            )
        })?;

        let result = unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64 as IoctlRequest,
                &info,
            )
        };
        match ioctl_to_error(result) {
            Err(err) => {
                // Ignore the error to preserve the original error
                let _ = self.detach();
                Err(err)
            }
            Ok(_) => Ok(()),
        }
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Get the device major number
    pub fn major(&self) -> io::Result<u32> {
        self.device
            .metadata()
            .map(|m| unsafe { libc::major(m.rdev()) as u32 })
    }

    /// Get the device major number
    pub fn minor(&self) -> io::Result<u32> {
        self.device
            .metadata()
            .map(|m| unsafe { libc::minor(m.rdev()) as u32 })
    }

    /// Detach a loop device from its backing file.
    ///
    /// Note that the device won't fully detach until a short delay after the underling device file
    /// gets closed. This happens when LoopDev goes out of scope so you should ensure the LoopDev
    /// lives for a short a time as possible.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// # ld.attach_file("disk.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_CLR_FD as IoctlRequest,
                0,
            )
        })?;
        Ok(())
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY as IoctlRequest,
                0,
            )
        })?;
        Ok(())
    }

    // Enable or disable direct I/O for the backing file.
    #[cfg(feature = "direct_io")]
    pub fn set_direct_io(&self, direct_io: bool) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_DIRECT_IO as IoctlRequest,
                if direct_io { 1 } else { 0 },
            )
        })?;
        Ok(())
    }
}

/// Used to set options when attaching a device. Created with [LoopDevice::with()].
///
/// # Examples
///
/// Enable partition scanning on attach:
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop6").unwrap();
/// ld.with()
///     .part_scan(true)
///     .attach("disk.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
///
/// A 1MiB slice of the file located at 1KiB into the file.
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop7").unwrap();
/// ld.with()
///     .offset(1024*1024)
///     .size_limit(1024*1024*1024)
///     .attach("disk.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
pub struct AttachOptions<'d> {
    device: &'d LoopDevice,
    info: loop_info64,
    #[cfg(feature = "direct_io")]
    direct_io: bool,
}

impl AttachOptions<'_> {
    /// Offset in bytes from the start of the backing file the data will start at.
    pub fn offset(mut self, offset: u64) -> Self {
        self.info.lo_offset = offset;
        self
    }

    /// Maximum size of the data in bytes.
    pub fn size_limit(mut self, size_limit: u64) -> Self {
        self.info.lo_sizelimit = size_limit;
        self
    }

    /// Set read only flag
    pub fn read_only(mut self, read_only: bool) -> Self {
        if read_only {
            self.info.lo_flags |= LO_FLAGS_READ_ONLY;
        } else {
            self.info.lo_flags &= !LO_FLAGS_READ_ONLY;
        }
        self
    }

    /// Set autoclear flag
    pub fn autoclear(mut self, read_only: bool) -> Self {
        if read_only {
            self.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
        } else {
            self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR;
        }
        self
    }

    // Enable or disable direct I/O for the backing file.
    #[cfg(feature = "direct_io")]
    pub fn set_direct_io(mut self, direct_io: bool) -> Self {
        self.direct_io = direct_io;
        self
    }

    /// Force the kernel to scan the partition table on a newly created loop device. Note that the
    /// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
    pub fn part_scan(mut self, enable: bool) -> Self {
        if enable {
            self.info.lo_flags |= LO_FLAGS_PARTSCAN;
        } else {
            self.info.lo_flags &= !LO_FLAGS_PARTSCAN;
        }
        self
    }

    /// Attach the loop device to a file with the set options.
    pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
        self.device.attach_with_loop_info(backing_file, self.info)?;
        #[cfg(feature = "direct_io")]
        if self.direct_io {
            self.device.set_direct_io(self.direct_io)?;
        }
        Ok(())
    }

    /// Attach the loop device to an fd
    pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> {
        self.device
            .attach_fd_with_loop_info(backing_file_fd, self.info)?;
        #[cfg(feature = "direct_io")]
        if self.direct_io {
            self.device.set_direct_io(self.direct_io)?;
        }
        Ok(())
    }
}

fn ioctl_to_error(ret: i32) -> io::Result<i32> {
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}
<|endoftext|>


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

//! Operations on ASCII strings and characters

#![cfg_attr(feature = "unstable", feature(ascii))]

mod ascii;
mod ascii_string;
mod ascii_str;

use std::mem;
use std::borrow::Borrow;
use std::ascii::AsciiExt;

pub use ascii::Ascii;
pub use ascii_string::AsciiString;
pub use ascii_str::AsciiStr;

/// Trait for converting into an ascii type.
pub trait AsciiCast<'a>: AsciiExt {
    type Target;

    /// Convert to an ascii type, return Err(()) on non-ASCII input.
    #[inline]
    fn to_ascii(&'a self) -> Result<Self::Target, ()> {
        if self.is_ascii() {
            Ok(unsafe { self.to_ascii_nocheck() })
        } else {
            Err(())
        }
    }

    /// Convert to an ascii type, not doing any range asserts
    unsafe fn to_ascii_nocheck(&'a self) -> Self::Target;
}

/// Trait for copyless casting to an ascii vector.
pub trait OwnedAsciiCast<T: ?Sized> : Sized
    where Self: Borrow<T>,
          T: AsciiExt<Owned=Self>
{
    /// Take ownership and cast to an ascii vector. On non-ASCII input return ownership of data
    /// that was attempted to cast to ascii in `Err(Self)`.
    #[inline]
    fn into_ascii(self) -> Result<AsciiString, Self> {
        if self.borrow().is_ascii() {
            Ok(unsafe { self.into_ascii_nocheck() })
        } else {
            Err(self)
        }
    }

    /// Take ownership and cast to an ascii vector.
    /// Does not perform validation checks.
    unsafe fn into_ascii_nocheck(self) -> AsciiString;
}

impl OwnedAsciiCast<str> for String {
    #[inline]
    unsafe fn into_ascii_nocheck(self) -> AsciiString {
        self.into_bytes().into_ascii_nocheck()
    }
}

impl OwnedAsciiCast<[u8]> for Vec<u8> {
    #[inline]
    unsafe fn into_ascii_nocheck(self) -> AsciiString {
        let v = Vec::from_raw_parts(self.as_ptr() as *mut Ascii,
                                    self.len(),
                                    self.capacity());

        // We forget `self` to avoid freeing it at the end of the scope
        // Otherwise, the returned `Vec` would point to freed memory
        mem::forget(self);
        AsciiString { vec: v }
    }
}
<commit_msg>Fix privacy issue due to splitting<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// ignore-lexer-test FIXME #15679

//! Operations on ASCII strings and characters

#![cfg_attr(feature = "unstable", feature(ascii))]

mod ascii;
mod ascii_string;
mod ascii_str;

use std::mem;
use std::borrow::Borrow;
use std::ascii::AsciiExt;

pub use ascii::Ascii;
pub use ascii_string::AsciiString;
pub use ascii_str::AsciiStr;

/// Trait for converting into an ascii type.
pub trait AsciiCast<'a>: AsciiExt {
    type Target;

    /// Convert to an ascii type, return Err(()) on non-ASCII input.
    #[inline]
    fn to_ascii(&'a self) -> Result<Self::Target, ()> {
        if self.is_ascii() {
            Ok(unsafe { self.to_ascii_nocheck() })
        } else {
            Err(())
        }
    }

    /// Convert to an ascii type, not doing any range asserts
    unsafe fn to_ascii_nocheck(&'a self) -> Self::Target;
}

/// Trait for copyless casting to an ascii vector.
pub trait OwnedAsciiCast<T: ?Sized> : Sized
    where Self: Borrow<T>,
          T: AsciiExt<Owned=Self>
{
    /// Take ownership and cast to an ascii vector. On non-ASCII input return ownership of data
    /// that was attempted to cast to ascii in `Err(Self)`.
    #[inline]
    fn into_ascii(self) -> Result<AsciiString, Self> {
        if self.borrow().is_ascii() {
            Ok(unsafe { self.into_ascii_nocheck() })
        } else {
            Err(self)
        }
    }

    /// Take ownership and cast to an ascii vector.
    /// Does not perform validation checks.
    unsafe fn into_ascii_nocheck(self) -> AsciiString;
}

impl OwnedAsciiCast<str> for String {
    #[inline]
    unsafe fn into_ascii_nocheck(self) -> AsciiString {
        self.into_bytes().into_ascii_nocheck()
    }
}

impl OwnedAsciiCast<[u8]> for Vec<u8> {
    #[inline]
    unsafe fn into_ascii_nocheck(self) -> AsciiString {
        AsciiString::from_bytes_unchecked(self)
    }
}
<|endoftext|>


<commit_before>//! Iterator-based `linspace` and `logspace` functions

#![deny(missing_docs, warnings)]
#![feature(associated_types, macro_rules, phase)]

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[phase(plugin)]
extern crate quickcheck_macros;

use std::num::{Float, self};

#[cfg(test)]
mod test;

/// Iterator that yields equally spaced numbers in the linear scale
#[derive(Copy)]
pub struct Linspace<T: Float> {
    start: T,
    state: uint,
    step: T,
    stop: uint,
}

impl<T> DoubleEndedIterator for Linspace<T> where T: Float {
    fn next_back(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            self.stop -= 1;
            Some(self.start + self.step * num::cast(self.stop).unwrap())
        }
    }
}

impl<T> Iterator for Linspace<T> where T: Float {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            let next = self.start + self.step * num::cast(self.state).unwrap();
            self.state += 1;
            Some(next)
        }
    }

    fn size_hint(&self) -> (uint, Option<uint>) {
        let exact = self.stop - self.state;
        (exact, Some(exact))
    }
}

/// Iterator that yields equally spaced numbers in the logarithmic scale
#[derive(Copy)]
pub struct Logspace<T: Float> {
    start: T,
    state: uint,
    step: T,
    stop: uint,
}

impl<T> DoubleEndedIterator for Logspace<T> where T: Float {
    fn next_back(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            self.stop -= 1;
            Some((self.start + self.step * num::cast(self.stop).unwrap()).exp())
        }
    }
}

impl<T> Iterator for Logspace<T> where T: Float {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            let next = self.start + self.step * num::cast(self.state).unwrap();
            self.state += 1;
            Some(next.exp())
        }
    }

    fn size_hint(&self) -> (uint, Option<uint>) {
        let exact = self.stop - self.state;
        (exact, Some(exact))
    }
}

/// Returns an iterator that yields `n` evenly spaced numbers over the `[start, end]` interval
///
/// # Panics
///
/// Panics if `end` < `start`
///
/// # Examples
///
/// **Note** These assertions will likely fail because of rounding errors. (In real applications
/// you shouldn't directly use equality between floats, but instead check that the absolute
/// difference is within some tolerance)
///
/// ``` ignore
/// assert_eq!(vec![2., 2.25, 2.5, 2.75, 3.], linspace(2., 3., 5).collect())
/// assert_eq!(vec![3., 2.75, 2.5, 2.25, 2.], linspace(2., 3., 5).rev().collect())
/// ```
pub fn linspace<T>(start: T, end: T, n: uint) -> Linspace<T> where T: Float {
    assert!(start <= end);

    let step = if n < 2 {
        // NB The value of `step` doesn't matter in these cases
        Float::zero()
    } else {
        (end - start) / num::cast(n - 1).unwrap()
    };

    Linspace {
        start: start,
        state: 0,
        step: step,
        stop: n,
    }
}

/// Logarithmic version of `linspace`
///
/// # Panics
///
/// Panics if `start` or `end` is a non-positive number, or if `end` < `start`
///
/// # Examples
///
/// **Note** These assertions will likely fail because of rounding errors. (In real applications
/// you shouldn't directly use equality between floats, but instead check that the absolute
/// difference is within some tolerance)
///
/// ``` ignore
/// assert_eq!(vec![0.1, 1., 10., 100.], logspace(0.1, 100., 4).collect())
/// assert_eq!(vec![100., 10., 1., 0.1], logspace(0.1, 100., 4).rev().collect())
/// ```
pub fn logspace<T>(start: T, end: T, n: uint) -> Logspace<T> where T: Float {
    assert!(start > Float::zero() && end > Float::zero() && start <= end);

    let (start, end) = (start.ln(), end.ln());

    let step = if n < 2 {
        // NB The value of `step` doesn't matter in these cases
        Float::zero()
    } else {
        (end - start) / num::cast(n - 1).unwrap()
    };

    Logspace {
        start: start,
        state: 0,
        step: step,
        stop: n,
    }
}
<commit_msg>fix: update to rust nightly<commit_after>//! Iterator-based `linspace` and `logspace` functions

#![deny(missing_docs, warnings)]
#![feature(plugin)]

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[plugin]
extern crate quickcheck_macros;

use std::num::{Float, self};

#[cfg(test)]
mod test;

/// Iterator that yields equally spaced numbers in the linear scale
#[derive(Copy)]
pub struct Linspace<T: Float> {
    start: T,
    state: uint,
    step: T,
    stop: uint,
}

impl<T> DoubleEndedIterator for Linspace<T> where T: Float {
    fn next_back(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            self.stop -= 1;
            Some(self.start + self.step * num::cast(self.stop).unwrap())
        }
    }
}

impl<T> Iterator for Linspace<T> where T: Float {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            let next = self.start + self.step * num::cast(self.state).unwrap();
            self.state += 1;
            Some(next)
        }
    }

    fn size_hint(&self) -> (uint, Option<uint>) {
        let exact = self.stop - self.state;
        (exact, Some(exact))
    }
}

/// Iterator that yields equally spaced numbers in the logarithmic scale
#[derive(Copy)]
pub struct Logspace<T: Float> {
    start: T,
    state: uint,
    step: T,
    stop: uint,
}

impl<T> DoubleEndedIterator for Logspace<T> where T: Float {
    fn next_back(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            self.stop -= 1;
            Some((self.start + self.step * num::cast(self.stop).unwrap()).exp())
        }
    }
}

impl<T> Iterator for Logspace<T> where T: Float {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.state == self.stop {
            None
        } else {
            let next = self.start + self.step * num::cast(self.state).unwrap();
            self.state += 1;
            Some(next.exp())
        }
    }

    fn size_hint(&self) -> (uint, Option<uint>) {
        let exact = self.stop - self.state;
        (exact, Some(exact))
    }
}

/// Returns an iterator that yields `n` evenly spaced numbers over the `[start, end]` interval
///
/// # Panics
///
/// Panics if `end` < `start`
///
/// # Examples
///
/// **Note** These assertions will likely fail because of rounding errors. (In real applications
/// you shouldn't directly use equality between floats, but instead check that the absolute
/// difference is within some tolerance)
///
/// ``` ignore
/// assert_eq!(vec![2., 2.25, 2.5, 2.75, 3.], linspace(2., 3., 5).collect())
/// assert_eq!(vec![3., 2.75, 2.5, 2.25, 2.], linspace(2., 3., 5).rev().collect())
/// ```
pub fn linspace<T>(start: T, end: T, n: uint) -> Linspace<T> where T: Float {
    assert!(start <= end);

    let step = if n < 2 {
        // NB The value of `step` doesn't matter in these cases
        Float::zero()
    } else {
        (end - start) / num::cast(n - 1).unwrap()
    };

    Linspace {
        start: start,
        state: 0,
        step: step,
        stop: n,
    }
}

/// Logarithmic version of `linspace`
///
/// # Panics
///
/// Panics if `start` or `end` is a non-positive number, or if `end` < `start`
///
/// # Examples
///
/// **Note** These assertions will likely fail because of rounding errors. (In real applications
/// you shouldn't directly use equality between floats, but instead check that the absolute
/// difference is within some tolerance)
///
/// ``` ignore
/// assert_eq!(vec![0.1, 1., 10., 100.], logspace(0.1, 100., 4).collect())
/// assert_eq!(vec![100., 10., 1., 0.1], logspace(0.1, 100., 4).rev().collect())
/// ```
pub fn logspace<T>(start: T, end: T, n: uint) -> Logspace<T> where T: Float {
    assert!(start > Float::zero() && end > Float::zero() && start <= end);

    let (start, end) = (start.ln(), end.ln());

    let step = if n < 2 {
        // NB The value of `step` doesn't matter in these cases
        Float::zero()
    } else {
        (end - start) / num::cast(n - 1).unwrap()
    };

    Logspace {
        start: start,
        state: 0,
        step: step,
        stop: n,
    }
}
<|endoftext|>