<commit_before>use rustc_middle::mir;
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;

use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::unix::fs::EvalContextExt as _;
use shims::unix::linux::sync::futex;
use shims::unix::sync::EvalContextExt as _;
use shims::unix::thread::EvalContextExt as _;

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: Symbol,
        abi: Abi,
        args: &[OpTy<'tcx, Tag>],
        dest: &PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
        let this = self.eval_context_mut();

        match link_name.as_str() {
            // errno
            "__errno_location" => {
                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let errno_place = this.last_error_place()?;
                this.write_scalar(errno_place.to_ref(this).to_scalar()?, dest)?;
            }

            // File related shims (but also see "syscall" below for statx)
            // These symbols have different names on Linux and macOS, which is the only reason they are not
            // in the `posix` module.
            "close" => {
                let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.close(fd)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "opendir" => {
                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.opendir(name)?;
                this.write_scalar(result, dest)?;
            }
            "readdir64" => {
                let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.linux_readdir64(dirp)?;
                this.write_scalar(result, dest)?;
            }
            "ftruncate64" => {
                let [fd, length] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.ftruncate64(fd, length)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            // Linux-only
            "posix_fadvise" => {
                let [fd, offset, len, advice] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                this.read_scalar(fd)?.to_i32()?;
                this.read_scalar(offset)?.to_machine_isize(this)?;
                this.read_scalar(len)?.to_machine_isize(this)?;
                this.read_scalar(advice)?.to_i32()?;
                // fadvise is only informational, we can ignore it.
                this.write_null(dest)?;
            }
            "sync_file_range" => {
                let [fd, offset, nbytes, flags] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.sync_file_range(fd, offset, nbytes, flags)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Time related shims
            "clock_gettime" => {
                // This is a POSIX function but it has only been tested on linux.
                let [clk_id, tp] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.clock_gettime(clk_id, tp)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Threading
            "prctl" => {
                // prctl is variadic. (It is not documented like that in the manpage, but defined like that in the libc crate.)
                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
                let result = this.prctl(args)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_setclock" => {
                let [attr, clock_id] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.pthread_condattr_setclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_getclock" => {
                let [attr, clock_id] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.pthread_condattr_getclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Dynamically invoked syscalls
            "syscall" => {
                // We do not use `check_shim` here because `syscall` is variadic. The argument
                // count is checked bellow.
                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
                // The syscall variadic function is legal to call with more arguments than needed,
                // extra arguments are simply ignored. The important check is that when we use an
                // argument, we have to also check all arguments *before* it to ensure that they
                // have the right type.

                let sys_getrandom = this.eval_libc("SYS_getrandom")?.to_machine_usize(this)?;

                let sys_statx = this.eval_libc("SYS_statx")?.to_machine_usize(this)?;

                let sys_futex = this.eval_libc("SYS_futex")?.to_machine_usize(this)?;

                if args.is_empty() {
                    throw_ub_format!(
                        "incorrect number of arguments for syscall: got 0, expected at least 1"
                    );
                }
                match this.read_scalar(&args[0])?.to_machine_usize(this)? {
                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                    id if id == sys_getrandom => {
                        // The first argument is the syscall id, so skip over it.
                        if args.len() < 4 {
                            throw_ub_format!(
                                "incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4",
                                args.len()
                            );
                        }
                        getrandom(this, &args[1], &args[2], &args[3], dest)?;
                    }
                    // `statx` is used by `libstd` to retrieve metadata information on `linux`
                    // instead of using `stat`,`lstat` or `fstat` as on `macos`.
                    id if id == sys_statx => {
                        // The first argument is the syscall id, so skip over it.
                        if args.len() < 6 {
                            throw_ub_format!(
                                "incorrect number of arguments for `statx` syscall: got {}, expected at least 6",
                                args.len()
                            );
                        }
                        let result =
                            this.linux_statx(&args[1], &args[2], &args[3], &args[4], &args[5])?;
                        this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
                    }
                    // `futex` is used by some synchonization primitives.
                    id if id == sys_futex => {
                        futex(this, &args[1..], dest)?;
                    }
                    id => {
                        this.handle_unsupported(format!("can't execute syscall with ID {}", id))?;
                        return Ok(EmulateByNameResult::AlreadyJumped);
                    }
                }
            }

            // Miscelanneous
            "getrandom" => {
                let [ptr, len, flags] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                getrandom(this, ptr, len, flags, dest)?;
            }
            "sched_getaffinity" => {
                let [pid, cpusetsize, mask] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                this.read_scalar(pid)?.to_i32()?;
                this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
                this.deref_operand(mask)?;
                // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
                let einval = this.eval_libc("EINVAL")?;
                this.set_last_error(einval)?;
                this.write_scalar(Scalar::from_i32(-1), dest)?;
            }

            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
            // These shims are enabled only when the caller is in the standard library.
            "pthread_getattr_np" if this.frame_in_std() => {
                let [_thread, _attr] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                this.write_null(dest)?;
            }

            _ => return Ok(EmulateByNameResult::NotSupported),
        };

        Ok(EmulateByNameResult::NeedsJumping)
    }
}

// Shims the linux `getrandom` syscall.
fn getrandom<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    ptr: &OpTy<'tcx, Tag>,
    len: &OpTy<'tcx, Tag>,
    flags: &OpTy<'tcx, Tag>,
    dest: &PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    let ptr = this.read_pointer(ptr)?;
    let len = this.read_scalar(len)?.to_machine_usize(this)?;

    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
    // neither of which have any effect on our current PRNG.
    // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
    let _flags = this.read_scalar(flags)?.to_i32();

    this.gen_random(ptr, len)?;
    this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
    Ok(())
}
<commit_msg>Cargo fmt<commit_after>use rustc_middle::mir;
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;

use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::unix::fs::EvalContextExt as _;
use shims::unix::linux::sync::futex;
use shims::unix::sync::EvalContextExt as _;
use shims::unix::thread::EvalContextExt as _;

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: Symbol,
        abi: Abi,
        args: &[OpTy<'tcx, Tag>],
        dest: &PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
        let this = self.eval_context_mut();

        match link_name.as_str() {
            // errno
            "__errno_location" => {
                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let errno_place = this.last_error_place()?;
                this.write_scalar(errno_place.to_ref(this).to_scalar()?, dest)?;
            }

            // File related shims (but also see "syscall" below for statx)
            // These symbols have different names on Linux and macOS, which is the only reason they are not
            // in the `posix` module.
            "close" => {
                let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.close(fd)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "opendir" => {
                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.opendir(name)?;
                this.write_scalar(result, dest)?;
            }
            "readdir64" => {
                let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.linux_readdir64(dirp)?;
                this.write_scalar(result, dest)?;
            }
            "ftruncate64" => {
                let [fd, length] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.ftruncate64(fd, length)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            // Linux-only
            "posix_fadvise" => {
                let [fd, offset, len, advice] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                this.read_scalar(fd)?.to_i32()?;
                this.read_scalar(offset)?.to_machine_isize(this)?;
                this.read_scalar(len)?.to_machine_isize(this)?;
                this.read_scalar(advice)?.to_i32()?;
                // fadvise is only informational, we can ignore it.
                this.write_null(dest)?;
            }
            "sync_file_range" => {
                let [fd, offset, nbytes, flags] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.sync_file_range(fd, offset, nbytes, flags)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Time related shims
            "clock_gettime" => {
                // This is a POSIX function but it has only been tested on linux.
                let [clk_id, tp] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.clock_gettime(clk_id, tp)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Threading
            "prctl" => {
                // prctl is variadic. (It is not documented like that in the manpage, but defined like that in the libc crate.)
                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
                let result = this.prctl(args)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_setclock" => {
                let [attr, clock_id] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.pthread_condattr_setclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_getclock" => {
                let [attr, clock_id] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                let result = this.pthread_condattr_getclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Dynamically invoked syscalls
            "syscall" => {
                // We do not use `check_shim` here because `syscall` is variadic. The argument
                // count is checked bellow.
                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
                // The syscall variadic function is legal to call with more arguments than needed,
                // extra arguments are simply ignored. The important check is that when we use an
                // argument, we have to also check all arguments *before* it to ensure that they
                // have the right type.

                let sys_getrandom = this.eval_libc("SYS_getrandom")?.to_machine_usize(this)?;

                let sys_statx = this.eval_libc("SYS_statx")?.to_machine_usize(this)?;

                let sys_futex = this.eval_libc("SYS_futex")?.to_machine_usize(this)?;

                if args.is_empty() {
                    throw_ub_format!(
                        "incorrect number of arguments for syscall: got 0, expected at least 1"
                    );
                }
                match this.read_scalar(&args[0])?.to_machine_usize(this)? {
                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                    id if id == sys_getrandom => {
                        // The first argument is the syscall id, so skip over it.
                        if args.len() < 4 {
                            throw_ub_format!(
                                "incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4",
                                args.len()
                            );
                        }
                        getrandom(this, &args[1], &args[2], &args[3], dest)?;
                    }
                    // `statx` is used by `libstd` to retrieve metadata information on `linux`
                    // instead of using `stat`,`lstat` or `fstat` as on `macos`.
                    id if id == sys_statx => {
                        // The first argument is the syscall id, so skip over it.
                        if args.len() < 6 {
                            throw_ub_format!(
                                "incorrect number of arguments for `statx` syscall: got {}, expected at least 6",
                                args.len()
                            );
                        }
                        let result =
                            this.linux_statx(&args[1], &args[2], &args[3], &args[4], &args[5])?;
                        this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
                    }
                    // `futex` is used by some synchonization primitives.
                    id if id == sys_futex => {
                        futex(this, &args[1..], dest)?;
                    }
                    id => {
                        this.handle_unsupported(format!("can't execute syscall with ID {}", id))?;
                        return Ok(EmulateByNameResult::AlreadyJumped);
                    }
                }
            }

            // Miscelanneous
            "getrandom" => {
                let [ptr, len, flags] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                getrandom(this, ptr, len, flags, dest)?;
            }
            "sched_getaffinity" => {
                let [pid, cpusetsize, mask] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                this.read_scalar(pid)?.to_i32()?;
                this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
                this.deref_operand(mask)?;
                // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
                let einval = this.eval_libc("EINVAL")?;
                this.set_last_error(einval)?;
                this.write_scalar(Scalar::from_i32(-1), dest)?;
            }

            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
            // These shims are enabled only when the caller is in the standard library.
            "pthread_getattr_np" if this.frame_in_std() => {
                let [_thread, _attr] =
                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                this.write_null(dest)?;
            }

            _ => return Ok(EmulateByNameResult::NotSupported),
        };

        Ok(EmulateByNameResult::NeedsJumping)
    }
}

// Shims the linux `getrandom` syscall.
fn getrandom<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    ptr: &OpTy<'tcx, Tag>,
    len: &OpTy<'tcx, Tag>,
    flags: &OpTy<'tcx, Tag>,
    dest: &PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    let ptr = this.read_pointer(ptr)?;
    let len = this.read_scalar(len)?.to_machine_usize(this)?;

    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
    // neither of which have any effect on our current PRNG.
    // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
    let _flags = this.read_scalar(flags)?.to_i32();

    this.gen_random(ptr, len)?;
    this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
    Ok(())
}
<|endoftext|>


<commit_before>use crate::env::temp_dir;
use crate::fs::OpenOptions;
use crate::io;
use crate::io::Result;
use crate::io::SeekFrom;
use crate::io::{BufRead, Read, Seek, Write};
use crate::os::unix::io::AsRawFd;

#[test]
fn copy_specialization() -> Result<()> {
    use crate::io::{BufReader, BufWriter};

    let path = crate::env::temp_dir();
    let source_path = path.join("copy-spec.source");
    let sink_path = path.join("copy-spec.sink");

    let result: Result<()> = try {
        let mut source = crate::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(&source_path)?;
        source.write_all(b"abcdefghiklmnopqr")?;
        source.seek(SeekFrom::Start(8))?;
        let mut source = BufReader::with_capacity(8, source.take(5));
        source.fill_buf()?;
        assert_eq!(source.buffer(), b"iklmn");
        source.get_mut().set_limit(6);
        source.get_mut().get_mut().seek(SeekFrom::Start(1))?; // "bcdefg"
        let mut source = source.take(10); // "iklmnbcdef"

        let mut sink = crate::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(&sink_path)?;
        sink.write_all(b"000000")?;
        let mut sink = BufWriter::with_capacity(5, sink);
        sink.write_all(b"wxyz")?;
        assert_eq!(sink.buffer(), b"wxyz");

        let copied = crate::io::copy(&mut source, &mut sink)?;
        assert_eq!(copied, 10);
        assert_eq!(sink.buffer().len(), 0);

        let mut sink = sink.into_inner()?;
        sink.seek(SeekFrom::Start(0))?;
        let mut copied = Vec::new();
        sink.read_to_end(&mut copied)?;
        assert_eq!(&copied, b"000000wxyziklmnbcdef");
    };

    let rm1 = crate::fs::remove_file(source_path);
    let rm2 = crate::fs::remove_file(sink_path);

    result.and(rm1).and(rm2)
}

#[bench]
fn bench_file_to_file_copy(b: &mut test::Bencher) {
    const BYTES: usize = 128 * 1024;
    let src_path = temp_dir().join("file-copy-bench-src");
    let mut src = crate::fs::OpenOptions::new()
        .create(true)
        .truncate(true)
        .read(true)
        .write(true)
        .open(src_path)
        .unwrap();
    src.write(&vec![0u8; BYTES]).unwrap();

    let sink_path = temp_dir().join("file-copy-bench-sink");
    let mut sink = crate::fs::OpenOptions::new()
        .create(true)
        .truncate(true)
        .write(true)
        .open(sink_path)
        .unwrap();

    b.bytes = BYTES as u64;
    b.iter(|| {
        src.seek(SeekFrom::Start(0)).unwrap();
        sink.seek(SeekFrom::Start(0)).unwrap();
        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
    });
}

#[bench]
fn bench_file_to_socket_copy(b: &mut test::Bencher) {
    const BYTES: usize = 128 * 1024;
    let src_path = temp_dir().join("pipe-copy-bench-src");
    let mut src = OpenOptions::new()
        .create(true)
        .truncate(true)
        .read(true)
        .write(true)
        .open(src_path)
        .unwrap();
    src.write(&vec![0u8; BYTES]).unwrap();

    let sink_drainer = crate::net::TcpListener::bind("localhost:0").unwrap();
    let mut sink = crate::net::TcpStream::connect(sink_drainer.local_addr().unwrap()).unwrap();
    let mut sink_drainer = sink_drainer.accept().unwrap().0;

    crate::thread::spawn(move || {
        let mut sink_buf = vec![0u8; 1024 * 1024];
        loop {
            sink_drainer.read(&mut sink_buf[..]).unwrap();
        }
    });

    b.bytes = BYTES as u64;
    b.iter(|| {
        src.seek(SeekFrom::Start(0)).unwrap();
        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
    });
}

#[bench]
fn bench_file_to_uds_copy(b: &mut test::Bencher) {
    const BYTES: usize = 128 * 1024;
    let src_path = temp_dir().join("uds-copy-bench-src");
    let mut src = OpenOptions::new()
        .create(true)
        .truncate(true)
        .read(true)
        .write(true)
        .open(src_path)
        .unwrap();
    src.write(&vec![0u8; BYTES]).unwrap();

    let (mut sink, mut sink_drainer) = crate::os::unix::net::UnixStream::pair().unwrap();

    crate::thread::spawn(move || {
        let mut sink_buf = vec![0u8; 1024 * 1024];
        loop {
            sink_drainer.read(&mut sink_buf[..]).unwrap();
        }
    });

    b.bytes = BYTES as u64;
    b.iter(|| {
        src.seek(SeekFrom::Start(0)).unwrap();
        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
    });
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[bench]
fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) {
    use super::CopyResult;
    use crate::io::ErrorKind;
    use crate::process::{ChildStdin, ChildStdout};
    use crate::sys_common::FromInner;

    let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap();

    let mut read_end = ChildStdout::from_inner(read_end);
    let write_end = ChildStdin::from_inner(write_end);

    let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap();
    let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap();

    let local_end = crate::sync::Arc::new(acceptor.accept().unwrap().0);

    // the data flow in this benchmark:
    //
    //                      socket(tx)  local_source
    // remote_end (write)  +-------->   (splice to)
    //                                  write_end
    //                                     +
    //                                     |
    //                                     | pipe
    //                                     v
    //                                  read_end
    // remote_end (read)   <---------+  (splice to) *
    //                      socket(rx)  local_end
    //
    // * benchmark loop using io::copy

    crate::thread::spawn(move || {
        let mut sink_buf = vec![0u8; 1024 * 1024];
        remote_end.set_nonblocking(true).unwrap();
        loop {
            match remote_end.write(&mut sink_buf[..]) {
                Err(err) if err.kind() == ErrorKind::WouldBlock => {}
                Ok(_) => {}
                err => {
                    err.expect("write failed");
                }
            };
            match remote_end.read(&mut sink_buf[..]) {
                Err(err) if err.kind() == ErrorKind::WouldBlock => {}
                Ok(_) => {}
                err => {
                    err.expect("read failed");
                }
            };
        }
    });

    // check that splice works, otherwise the benchmark would hang
    let probe = super::sendfile_splice(
        super::SpliceMode::Splice,
        local_end.as_raw_fd(),
        write_end.as_raw_fd(),
        1,
    );

    match probe {
        CopyResult::Ended(Ok(1)) => {
            // splice works
        }
        _ => {
            eprintln!("splice failed, skipping benchmark");
            return;
        }
    }

    let local_source = local_end.clone();
    crate::thread::spawn(move || {
        loop {
            super::sendfile_splice(
                super::SpliceMode::Splice,
                local_source.as_raw_fd(),
                write_end.as_raw_fd(),
                u64::MAX,
            );
        }
    });

    const BYTES: usize = 128 * 1024;
    b.bytes = BYTES as u64;
    b.iter(|| {
        assert_eq!(
            BYTES as u64,
            io::copy(&mut (&mut read_end).take(BYTES as u64), &mut &*local_end).unwrap()
        );
    });
}
<commit_msg>Make the kernel_copy tests more robust/concurrent.<commit_after>use crate::fs::OpenOptions;
use crate::io;
use crate::io::Result;
use crate::io::SeekFrom;
use crate::io::{BufRead, Read, Seek, Write};
use crate::os::unix::io::AsRawFd;
use crate::sys_common::io::test::tmpdir;

#[test]
fn copy_specialization() -> Result<()> {
    use crate::io::{BufReader, BufWriter};

    let tmp_path = tmpdir();
    let source_path = tmp_path.join("copy-spec.source");
    let sink_path = tmp_path.join("copy-spec.sink");

    let result: Result<()> = try {
        let mut source = crate::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(&source_path)?;
        source.write_all(b"abcdefghiklmnopqr")?;
        source.seek(SeekFrom::Start(8))?;
        let mut source = BufReader::with_capacity(8, source.take(5));
        source.fill_buf()?;
        assert_eq!(source.buffer(), b"iklmn");
        source.get_mut().set_limit(6);
        source.get_mut().get_mut().seek(SeekFrom::Start(1))?; // "bcdefg"
        let mut source = source.take(10); // "iklmnbcdef"

        let mut sink = crate::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(&sink_path)?;
        sink.write_all(b"000000")?;
        let mut sink = BufWriter::with_capacity(5, sink);
        sink.write_all(b"wxyz")?;
        assert_eq!(sink.buffer(), b"wxyz");

        let copied = crate::io::copy(&mut source, &mut sink)?;
        assert_eq!(copied, 10);
        assert_eq!(sink.buffer().len(), 0);

        let mut sink = sink.into_inner()?;
        sink.seek(SeekFrom::Start(0))?;
        let mut copied = Vec::new();
        sink.read_to_end(&mut copied)?;
        assert_eq!(&copied, b"000000wxyziklmnbcdef");
    };

    let rm1 = crate::fs::remove_file(source_path);
    let rm2 = crate::fs::remove_file(sink_path);

    result.and(rm1).and(rm2)
}

#[bench]
fn bench_file_to_file_copy(b: &mut test::Bencher) {
    const BYTES: usize = 128 * 1024;
    let temp_path = tmpdir();
    let src_path = temp_path.join("file-copy-bench-src");
    let mut src = crate::fs::OpenOptions::new()
        .create(true)
        .truncate(true)
        .read(true)
        .write(true)
        .open(src_path)
        .unwrap();
    src.write(&vec![0u8; BYTES]).unwrap();

    let sink_path = temp_path.join("file-copy-bench-sink");
    let mut sink = crate::fs::OpenOptions::new()
        .create(true)
        .truncate(true)
        .write(true)
        .open(sink_path)
        .unwrap();

    b.bytes = BYTES as u64;
    b.iter(|| {
        src.seek(SeekFrom::Start(0)).unwrap();
        sink.seek(SeekFrom::Start(0)).unwrap();
        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
    });
}

#[bench]
fn bench_file_to_socket_copy(b: &mut test::Bencher) {
    const BYTES: usize = 128 * 1024;
    let temp_path = tmpdir();
    let src_path = temp_path.join("pipe-copy-bench-src");
    let mut src = OpenOptions::new()
        .create(true)
        .truncate(true)
        .read(true)
        .write(true)
        .open(src_path)
        .unwrap();
    src.write(&vec![0u8; BYTES]).unwrap();

    let sink_drainer = crate::net::TcpListener::bind("localhost:0").unwrap();
    let mut sink = crate::net::TcpStream::connect(sink_drainer.local_addr().unwrap()).unwrap();
    let mut sink_drainer = sink_drainer.accept().unwrap().0;

    crate::thread::spawn(move || {
        let mut sink_buf = vec![0u8; 1024 * 1024];
        loop {
            sink_drainer.read(&mut sink_buf[..]).unwrap();
        }
    });

    b.bytes = BYTES as u64;
    b.iter(|| {
        src.seek(SeekFrom::Start(0)).unwrap();
        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
    });
}

#[bench]
fn bench_file_to_uds_copy(b: &mut test::Bencher) {
    const BYTES: usize = 128 * 1024;
    let temp_path = tmpdir();
    let src_path = temp_path.join("uds-copy-bench-src");
    let mut src = OpenOptions::new()
        .create(true)
        .truncate(true)
        .read(true)
        .write(true)
        .open(src_path)
        .unwrap();
    src.write(&vec![0u8; BYTES]).unwrap();

    let (mut sink, mut sink_drainer) = crate::os::unix::net::UnixStream::pair().unwrap();

    crate::thread::spawn(move || {
        let mut sink_buf = vec![0u8; 1024 * 1024];
        loop {
            sink_drainer.read(&mut sink_buf[..]).unwrap();
        }
    });

    b.bytes = BYTES as u64;
    b.iter(|| {
        src.seek(SeekFrom::Start(0)).unwrap();
        assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
    });
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[bench]
fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) {
    use super::CopyResult;
    use crate::io::ErrorKind;
    use crate::process::{ChildStdin, ChildStdout};
    use crate::sys_common::FromInner;

    let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap();

    let mut read_end = ChildStdout::from_inner(read_end);
    let write_end = ChildStdin::from_inner(write_end);

    let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap();
    let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap();

    let local_end = crate::sync::Arc::new(acceptor.accept().unwrap().0);

    // the data flow in this benchmark:
    //
    //                      socket(tx)  local_source
    // remote_end (write)  +-------->   (splice to)
    //                                  write_end
    //                                     +
    //                                     |
    //                                     | pipe
    //                                     v
    //                                  read_end
    // remote_end (read)   <---------+  (splice to) *
    //                      socket(rx)  local_end
    //
    // * benchmark loop using io::copy

    crate::thread::spawn(move || {
        let mut sink_buf = vec![0u8; 1024 * 1024];
        remote_end.set_nonblocking(true).unwrap();
        loop {
            match remote_end.write(&mut sink_buf[..]) {
                Err(err) if err.kind() == ErrorKind::WouldBlock => {}
                Ok(_) => {}
                err => {
                    err.expect("write failed");
                }
            };
            match remote_end.read(&mut sink_buf[..]) {
                Err(err) if err.kind() == ErrorKind::WouldBlock => {}
                Ok(_) => {}
                err => {
                    err.expect("read failed");
                }
            };
        }
    });

    // check that splice works, otherwise the benchmark would hang
    let probe = super::sendfile_splice(
        super::SpliceMode::Splice,
        local_end.as_raw_fd(),
        write_end.as_raw_fd(),
        1,
    );

    match probe {
        CopyResult::Ended(Ok(1)) => {
            // splice works
        }
        _ => {
            eprintln!("splice failed, skipping benchmark");
            return;
        }
    }

    let local_source = local_end.clone();
    crate::thread::spawn(move || {
        loop {
            super::sendfile_splice(
                super::SpliceMode::Splice,
                local_source.as_raw_fd(),
                write_end.as_raw_fd(),
                u64::MAX,
            );
        }
    });

    const BYTES: usize = 128 * 1024;
    b.bytes = BYTES as u64;
    b.iter(|| {
        assert_eq!(
            BYTES as u64,
            io::copy(&mut (&mut read_end).take(BYTES as u64), &mut &*local_end).unwrap()
        );
    });
}
<|endoftext|>


<commit_before>// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers

// Copyright (c) 2012-2014 The Rust Project Developers
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in
//   the documentation and/or other materials provided with the
//   distribution.
//
// - Neither the name of "The Computer Language Benchmarks Game" nor
//   the name of "The Computer Language Shootout Benchmarks" nor the
//   names of its contributors may be used to endorse or promote
//   products derived from this software without specific prior
//   written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#![feature(macro_rules)]
#![feature(simd)]
#![allow(experimental)]

// ignore-pretty very bad with line comments

use std::io;
use std::os;
use std::simd::f64x2;
use std::sync::{Arc, Future};

const ITER: int = 50;
const LIMIT: f64 = 2.0;
const WORKERS: uint = 16;

#[inline(always)]
fn mandelbrot<W: io::Writer>(w: uint, mut out: W) -> io::IoResult<()> {
    assert!(WORKERS % 2 == 0);

    // Ensure w and h are multiples of 8.
    let w = (w + 7) / 8 * 8;
    let h = w;

    let chunk_size = h / WORKERS;

    // Account for remainders in workload division, e.g. 1000 / 16 = 62.5
    let last_chunk_size = if h % WORKERS != 0 {
        chunk_size + h % WORKERS
    } else {
        chunk_size
    };

    // precalc values
    let inverse_w_doubled = 2.0 / w as f64;
    let inverse_h_doubled = 2.0 / h as f64;
    let v_inverses = f64x2(inverse_w_doubled, inverse_h_doubled);
    let v_consts = f64x2(1.5, 1.0);

    // A lot of this code assumes this (so do other lang benchmarks)
    assert!(w == h);
    let mut precalc_r = Vec::with_capacity(w);
    let mut precalc_i = Vec::with_capacity(h);

    let precalc_futures = Vec::from_fn(WORKERS, |i| {
        Future::spawn(proc () {
            let mut rs = Vec::with_capacity(w / WORKERS);
            let mut is = Vec::with_capacity(w / WORKERS);

            let start = i * chunk_size;
            let end = if i == (WORKERS - 1) {
                start + last_chunk_size
            } else {
                (i + 1) * chunk_size
            };

            // This assumes w == h
            for x in range(start, end) {
                let xf = x as f64;
                let xy = f64x2(xf, xf);

                let f64x2(r, i) = xy * v_inverses - v_consts;
                rs.push(r);
                is.push(i);
            }

            (rs, is)
        })
    });

    for res in precalc_futures.into_iter() {
        let (rs, is) = res.unwrap();
        precalc_r.extend(rs.into_iter());
        precalc_i.extend(is.into_iter());
    }

    assert_eq!(precalc_r.len(), w);
    assert_eq!(precalc_i.len(), h);

    let arc_init_r = Arc::new(precalc_r);
    let arc_init_i = Arc::new(precalc_i);

    let data = Vec::from_fn(WORKERS, |i| {
        let vec_init_r = arc_init_r.clone();
        let vec_init_i = arc_init_i.clone();

        Future::spawn(proc () {
            let mut res: Vec<u8> = Vec::with_capacity((chunk_size * w) / 8);
            let init_r_slice = vec_init_r.as_slice();
            for &init_i in vec_init_i.slice(i * chunk_size, (i + 1) * chunk_size).iter() {
                write_line(init_i, init_r_slice, &mut res);
            }

            res
        })
    });

    try!(writeln!(&mut out as &mut Writer, "P4\n{} {}", w, h));
    for res in data.into_iter() {
        try!(out.write(res.unwrap().as_slice()));
    }
    out.flush()
}

fn write_line(init_i: f64, vec_init_r: &[f64], res: &mut Vec<u8>) {
    let v_init_i : f64x2 = f64x2(init_i, init_i);
    let v_2 : f64x2 = f64x2(2.0, 2.0);
    const LIMIT_SQUARED: f64 = LIMIT * LIMIT;

    for chunk_init_r in vec_init_r.chunks(8) {
        let mut cur_byte = 0xff;
        let mut i = 0;

        while i < 8 {
            let v_init_r = f64x2(chunk_init_r[i], chunk_init_r[i + 1]);
            let mut cur_r = v_init_r;
            let mut cur_i = v_init_i;
            let mut r_sq = v_init_r * v_init_r;
            let mut i_sq = v_init_i * v_init_i;

            let mut b = 0;
            for _ in range(0, ITER) {
                let r = cur_r;
                let i = cur_i;

                cur_i = v_2 * r * i + v_init_i;
                cur_r = r_sq - i_sq + v_init_r;

                let f64x2(bit1, bit2) = r_sq + i_sq;

                if bit1 > LIMIT_SQUARED {
                    b |= 2;
                    if b == 3 { break; }
                }

                if bit2 > LIMIT_SQUARED {
                    b |= 1;
                    if b == 3 { break; }
                }

                r_sq = cur_r * cur_r;
                i_sq = cur_i * cur_i;
            }

            cur_byte = (cur_byte << 2) + b;
            i += 2;
        }

        res.push(cur_byte^-1);
    }
}

fn main() {
    let args = os::args();
    let args = args.as_slice();
    let res = if args.len() < 2 {
        println!("Test mode: do not dump the image because it's not utf8, \
                  which interferes with the test runner.");
        mandelbrot(1000, io::util::NullWriter)
    } else {
        mandelbrot(from_str(args[1].as_slice()).unwrap(), io::stdout())
    };
    res.unwrap();
}
<commit_msg>auto merge of #17957 : TeXitoi/rust/fix-mandelbrot, r=alexcrichton<commit_after>// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers

// Copyright (c) 2012-2014 The Rust Project Developers
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in
//   the documentation and/or other materials provided with the
//   distribution.
//
// - Neither the name of "The Computer Language Benchmarks Game" nor
//   the name of "The Computer Language Shootout Benchmarks" nor the
//   names of its contributors may be used to endorse or promote
//   products derived from this software without specific prior
//   written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#![feature(macro_rules)]
#![feature(simd)]
#![allow(experimental)]

// ignore-pretty very bad with line comments

use std::io;
use std::os;
use std::simd::f64x2;
use std::sync::{Arc, Future};

const ITER: int = 50;
const LIMIT: f64 = 2.0;
const WORKERS: uint = 16;

#[inline(always)]
fn mandelbrot<W: io::Writer>(w: uint, mut out: W) -> io::IoResult<()> {
    assert!(WORKERS % 2 == 0);

    // Ensure w and h are multiples of 8.
    let w = (w + 7) / 8 * 8;
    let h = w;

    let chunk_size = h / WORKERS;

    // Account for remainders in workload division, e.g. 1000 / 16 = 62.5
    let last_chunk_size = if h % WORKERS != 0 {
        chunk_size + h % WORKERS
    } else {
        chunk_size
    };

    // precalc values
    let inverse_w_doubled = 2.0 / w as f64;
    let inverse_h_doubled = 2.0 / h as f64;
    let v_inverses = f64x2(inverse_w_doubled, inverse_h_doubled);
    let v_consts = f64x2(1.5, 1.0);

    // A lot of this code assumes this (so do other lang benchmarks)
    assert!(w == h);
    let mut precalc_r = Vec::with_capacity(w);
    let mut precalc_i = Vec::with_capacity(h);

    let precalc_futures = Vec::from_fn(WORKERS, |i| {
        Future::spawn(proc () {
            let mut rs = Vec::with_capacity(w / WORKERS);
            let mut is = Vec::with_capacity(w / WORKERS);

            let start = i * chunk_size;
            let end = if i == (WORKERS - 1) {
                start + last_chunk_size
            } else {
                (i + 1) * chunk_size
            };

            // This assumes w == h
            for x in range(start, end) {
                let xf = x as f64;
                let xy = f64x2(xf, xf);

                let f64x2(r, i) = xy * v_inverses - v_consts;
                rs.push(r);
                is.push(i);
            }

            (rs, is)
        })
    });

    for res in precalc_futures.into_iter() {
        let (rs, is) = res.unwrap();
        precalc_r.extend(rs.into_iter());
        precalc_i.extend(is.into_iter());
    }

    assert_eq!(precalc_r.len(), w);
    assert_eq!(precalc_i.len(), h);

    let arc_init_r = Arc::new(precalc_r);
    let arc_init_i = Arc::new(precalc_i);

    let data = Vec::from_fn(WORKERS, |i| {
        let vec_init_r = arc_init_r.clone();
        let vec_init_i = arc_init_i.clone();

        Future::spawn(proc () {
            let mut res: Vec<u8> = Vec::with_capacity((chunk_size * w) / 8);
            let init_r_slice = vec_init_r.as_slice();

            let start = i * chunk_size;
            let end = if i == (WORKERS - 1) {
                start + last_chunk_size
            } else {
                (i + 1) * chunk_size
            };

            for &init_i in vec_init_i.slice(start, end).iter() {
                write_line(init_i, init_r_slice, &mut res);
            }

            res
        })
    });

    try!(writeln!(&mut out as &mut Writer, "P4\n{} {}", w, h));
    for res in data.into_iter() {
        try!(out.write(res.unwrap().as_slice()));
    }
    out.flush()
}

fn write_line(init_i: f64, vec_init_r: &[f64], res: &mut Vec<u8>) {
    let v_init_i : f64x2 = f64x2(init_i, init_i);
    let v_2 : f64x2 = f64x2(2.0, 2.0);
    const LIMIT_SQUARED: f64 = LIMIT * LIMIT;

    for chunk_init_r in vec_init_r.chunks(8) {
        let mut cur_byte = 0xff;
        let mut i = 0;

        while i < 8 {
            let v_init_r = f64x2(chunk_init_r[i], chunk_init_r[i + 1]);
            let mut cur_r = v_init_r;
            let mut cur_i = v_init_i;
            let mut r_sq = v_init_r * v_init_r;
            let mut i_sq = v_init_i * v_init_i;

            let mut b = 0;
            for _ in range(0, ITER) {
                let r = cur_r;
                let i = cur_i;

                cur_i = v_2 * r * i + v_init_i;
                cur_r = r_sq - i_sq + v_init_r;

                let f64x2(bit1, bit2) = r_sq + i_sq;

                if bit1 > LIMIT_SQUARED {
                    b |= 2;
                    if b == 3 { break; }
                }

                if bit2 > LIMIT_SQUARED {
                    b |= 1;
                    if b == 3 { break; }
                }

                r_sq = cur_r * cur_r;
                i_sq = cur_i * cur_i;
            }

            cur_byte = (cur_byte << 2) + b;
            i += 2;
        }

        res.push(cur_byte^-1);
    }
}

fn main() {
    let args = os::args();
    let args = args.as_slice();
    let res = if args.len() < 2 {
        println!("Test mode: do not dump the image because it's not utf8, \
                  which interferes with the test runner.");
        mandelbrot(1000, io::util::NullWriter)
    } else {
        mandelbrot(from_str(args[1].as_slice()).unwrap(), io::stdout())
    };
    res.unwrap();
}
<|endoftext|>


<commit_before>use std::fmt;
use num::{Zero};
use std::ops::{Add, Sub};

extern crate num;
use num::{ToPrimitive, FromPrimitive, CheckedAdd, CheckedSub};

/// Type infrastructure that describes an architecture,
///  with details such as pointer size.
///
/// Useful for specifying the appropriate member types for pointers, etc.
/// That is, when a struct contains an RVA, its 32-bits or 64-bits depending on Arch.
///
/// Example:
///
/// ```
/// use lancelot::arch::{Arch, Arch32};
///
/// struct Section<A: Arch> {
///   addr: A::RVA,
///   buf:  Vec<u8>,
///   name: String,
/// }
///
/// let s: Section::<Arch32> = Section {
///   addr: 0x0,
///   buf: vec![],
///   name: "foo".to_string(),
/// };
/// ```
///
/// see: https://stackoverflow.com/q/55785858/87207
pub trait Arch {
    /// The type used for Virtual Addresses (which are unsigned).
    type VA:
        // trait Ord so that we can compare addresses.
        Ord
        // trait Add so that we can do VA + RVA -> VA
        //  technically, we should constrain Add to Add<RHS=RVA>
        //  but, signed + unsigned addition is not implemented.
        //  atm, we push this down to the user.
        //  TODO: provide helper routines to make this easy.
        + Add<Output=Self::VA>
        + Zero
        + FromPrimitive
        + ToPrimitive
        // trait Copy because this is just a number, so prefer copy semantics.
        + Copy
        ;

    /// The type used for Relative Virtual Addresses (signed).
    type RVA:
        // trait Ord so that we can compare offsets.
        Ord
        + Zero
        // trait Add so that we can do VA + RVA -> VA.
        //  see note above.
        + Add<Output=Self::RVA>
        + Sub<Output=Self::RVA>
        // trait Add so that we can do RVA + RVA -> RVA.
        + Add<Self::RVA, Output=Self::RVA>
        + CheckedAdd<Output=Self::RVA>
        + CheckedSub<Output=Self::RVA>
        // trait FromPrimitive so that we can convert from usize (vec length) to offset.
        + FromPrimitive
        + ToPrimitive
        // trait Copy because this is just a number, so prefer copy semantics.
        + Copy
        ;

    fn get_bits() -> u8;
}

/// 32-bit Intel architecture.
pub struct Arch32;
impl Arch for Arch32 {
    type VA = u32;
    type RVA = i32;
    fn get_bits() -> u8 {
        32
    }
}

impl fmt::Display for Arch32 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "x32")
    }
}

/// 64-bit Intel architecture.
pub struct Arch64;
impl Arch for Arch64 {
    type VA = u64;
    type RVA = i64;
    fn get_bits() -> u8 {
        64
    }
}

impl fmt::Display for Arch64 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "x64")
    }
}

/// Checked arithmetic across RVA and usize.
///
/// We'd like RVA to be trait `Add<usize, Output=RVA>`.
/// However, this cannot be the case when `<RVA=u32>`
///  as we then have `i32 + u32` which can overflow `i32`.
/// So, we have to do checked arithmetic.
///
/// Example:
///
/// ```
/// use lancelot::arch::*;
///
/// // returns Some if the operation does not wrap:
/// assert_eq!(true, rva_add_usize::<Arch32>(0x0, 0x1).is_some());
/// assert_eq!(0x1,  rva_add_usize::<Arch32>(0x0, 0x1).unwrap());
///
/// // returns None if the operation wraps.
/// assert_eq!(true, rva_add_usize::<Arch32>(0x0, 0xFFFFFFFF).is_none());
/// ```
pub fn rva_add_usize<A: Arch>(base: A::RVA, offset: usize) -> Option<A::RVA> {
    if let Some(v) = A::RVA::from_usize(offset) {
        base.checked_add(&v)
    } else {
        None
    }
}

/// Checked arithmetic across RVA and usize.
///
/// We'd like RVA to be trait `Sub<usize, Output=RVA>`.
/// However, this cannot be the case when `<RVA=u32>`
///  as we then have `i32 - u32` which can underflow `i32`.
/// So, we have to do checked arithmetic.
///
/// Example:
///
/// ```
/// use std::i32;
/// use lancelot::arch::*;
///
/// // returns Some if the operation does not wrap:
/// assert_eq!(true, rva_sub_usize::<Arch32>(0x1, 0x0).is_some());
/// assert_eq!(0x1,  rva_sub_usize::<Arch32>(0x1, 0x0).unwrap());
///
/// // returns None if the operation wraps.
/// assert_eq!(true, rva_sub_usize::<Arch32>(std::i32::MAX, 0xFFFFFFFF).is_none());
/// ```
pub fn rva_sub_usize<A: Arch>(base: A::RVA, offset: usize) -> Option<A::RVA> {
    if let Some(v) = A::RVA::from_usize(offset) {
        base.checked_sub(&v)
    } else {
        None
    }
}

<commit_msg>arch: mark RVA as hashable<commit_after>use std::fmt;
use num::{Zero};
use std::ops::{Add, Sub};
use std::hash::Hash;

extern crate num;
use num::{ToPrimitive, FromPrimitive, CheckedAdd, CheckedSub};

/// Type infrastructure that describes an architecture,
///  with details such as pointer size.
///
/// Useful for specifying the appropriate member types for pointers, etc.
/// That is, when a struct contains an RVA, its 32-bits or 64-bits depending on Arch.
///
/// Example:
///
/// ```
/// use lancelot::arch::{Arch, Arch32};
///
/// struct Section<A: Arch> {
///   addr: A::RVA,
///   buf:  Vec<u8>,
///   name: String,
/// }
///
/// let s: Section::<Arch32> = Section {
///   addr: 0x0,
///   buf: vec![],
///   name: "foo".to_string(),
/// };
/// ```
///
/// see: https://stackoverflow.com/q/55785858/87207
pub trait Arch {
    /// The type used for Virtual Addresses (which are unsigned).
    type VA:
        // trait Ord so that we can compare addresses.
        Ord
        // trait Add so that we can do VA + RVA -> VA
        //  technically, we should constrain Add to Add<RHS=RVA>
        //  but, signed + unsigned addition is not implemented.
        //  atm, we push this down to the user.
        //  TODO: provide helper routines to make this easy.
        + Add<Output=Self::VA>
        + Zero
        + FromPrimitive
        + ToPrimitive
        // trait Copy because this is just a number, so prefer copy semantics.
        + Copy
        ;

    /// The type used for Relative Virtual Addresses (signed).
    type RVA:
        // trait Ord so that we can compare offsets.
        Ord
        + Zero
        + Hash
        + fmt::LowerHex
        // trait Add so that we can do VA + RVA -> VA.
        //  see note above.
        + Add<Output=Self::RVA>
        + Sub<Output=Self::RVA>
        // trait Add so that we can do RVA + RVA -> RVA.
        + Add<Self::RVA, Output=Self::RVA>
        + CheckedAdd<Output=Self::RVA>
        + CheckedSub<Output=Self::RVA>
        // trait FromPrimitive so that we can convert from usize (vec length) to offset.
        + FromPrimitive
        + ToPrimitive
        // trait Copy because this is just a number, so prefer copy semantics.
        + Copy
        ;

    fn get_bits() -> u8;
}

/// 32-bit Intel architecture.
pub struct Arch32;
impl Arch for Arch32 {
    type VA = u32;
    type RVA = i32;
    fn get_bits() -> u8 {
        32
    }
}

impl fmt::Display for Arch32 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "x32")
    }
}

/// 64-bit Intel architecture.
pub struct Arch64;
impl Arch for Arch64 {
    type VA = u64;
    type RVA = i64;
    fn get_bits() -> u8 {
        64
    }
}

impl fmt::Display for Arch64 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "x64")
    }
}

/// Checked arithmetic across RVA and usize.
///
/// We'd like RVA to be trait `Add<usize, Output=RVA>`.
/// However, this cannot be the case when `<RVA=u32>`
///  as we then have `i32 + u32` which can overflow `i32`.
/// So, we have to do checked arithmetic.
///
/// Example:
///
/// ```
/// use lancelot::arch::*;
///
/// // returns Some if the operation does not wrap:
/// assert_eq!(true, rva_add_usize::<Arch32>(0x0, 0x1).is_some());
/// assert_eq!(0x1,  rva_add_usize::<Arch32>(0x0, 0x1).unwrap());
///
/// // returns None if the operation wraps.
/// assert_eq!(true, rva_add_usize::<Arch32>(0x0, 0xFFFFFFFF).is_none());
/// ```
pub fn rva_add_usize<A: Arch>(base: A::RVA, offset: usize) -> Option<A::RVA> {
    if let Some(v) = A::RVA::from_usize(offset) {
        base.checked_add(&v)
    } else {
        None
    }
}

/// Checked arithmetic across RVA and usize.
///
/// We'd like RVA to be trait `Sub<usize, Output=RVA>`.
/// However, this cannot be the case when `<RVA=u32>`
///  as we then have `i32 - u32` which can underflow `i32`.
/// So, we have to do checked arithmetic.
///
/// Example:
///
/// ```
/// use std::i32;
/// use lancelot::arch::*;
///
/// // returns Some if the operation does not wrap:
/// assert_eq!(true, rva_sub_usize::<Arch32>(0x1, 0x0).is_some());
/// assert_eq!(0x1,  rva_sub_usize::<Arch32>(0x1, 0x0).unwrap());
///
/// // returns None if the operation wraps.
/// assert_eq!(true, rva_sub_usize::<Arch32>(std::i32::MAX, 0xFFFFFFFF).is_none());
/// ```
pub fn rva_sub_usize<A: Arch>(base: A::RVA, offset: usize) -> Option<A::RVA> {
    if let Some(v) = A::RVA::from_usize(offset) {
        base.checked_sub(&v)
    } else {
        None
    }
}

<|endoftext|>


<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use serde_json;
use std;
use v1::{Extras, Root};
use v1::root::extensions::{KhrBinaryGltf, KhrBinaryGltfHeader};

/// Error encountered when loading a glTF asset
#[derive(Debug)]
pub enum ImportError {
    /// Failure when deserializing a .gltf metadata file
    Deserialize(serde_json::error::Error),

    /// A glTF extension required by the asset has not been enabled by the user
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library
    ExtensionUnsupported(String),

    /// The .gltf data is invalid
    Invalid(String),

    /// Standard input / output error
    Io(std::io::Error),

    /// The glTF version of the asset is incompatible with this function
    IncompatibleVersion(String),
}

/// Imports a binary glTF 1.0 asset
fn import_binary_gltf<S, E>(mut stream: S) -> Result<Root<E>, ImportError>
    where S: std::io::Read, E: Extras
{
    use self::ImportError::*;

    #[cfg(not(feature = "KHR_binary_glTF"))]
    return Err(ExtensionDisabled("KHR_binary_glTF"));

    let header: KhrBinaryGltfHeader = unsafe {
        let mut buffer = [0u8; 16];
        stream.read_exact(&mut buffer[..])?;
        std::mem::transmute_copy(&buffer)
    };

    if header.version != 1 {
        let message = format!("KHR_binary_glTF version: {}", header.version);
        return Err(IncompatibleVersion(message));
    }

    if header.content_format != 0 {
        let message = format!("KHR_binary_header contentFormat: {}",
                              header.content_format);
        return Err(Invalid(message));
    }

    let mut content = Vec::with_capacity(header.content_length as usize);
    unsafe {
        content.set_len(header.content_length as usize);
    }
    stream.read_exact(&mut content[..])?;

    let body_length = header.length - header.content_length - 20;
    let mut body = Vec::with_capacity(body_length as usize);
    unsafe {
        body.set_len(body_length as usize);
    }
    stream.read_exact(&mut body[..])?;

    let mut root: Root<E> = serde_json::from_slice(&content)?;
    root.extensions.khr_binary_gltf = Some(KhrBinaryGltf {
        body: body,
        content: content,
        header: header,
    });

    Ok(root)
}

/// Imports a standard (plain text JSON) glTF 1.0 asset
fn import_standard_gltf<E>(data: Vec<u8>) -> Result<Root<E>, ImportError>
    where E: Extras
{
    let root: Root<E> = serde_json::from_slice(&data)?;

    Ok(root)
}

/// Imports a glTF 1.0 asset
pub fn import<P, E>(path: P) -> Result<Root<E>, ImportError>
    where P: AsRef<std::path::Path>, E: Extras
{
    use std::io::Read;

    let mut file = std::fs::File::open(path)?;

    let mut buffer = Vec::with_capacity(4);
    unsafe {
        buffer.set_len(4);
    }
    file.read_exact(&mut buffer)?;

    if buffer.starts_with(b"glTF") {
        import_binary_gltf(file)
    } else {
        file.read_to_end(&mut buffer)?;
        import_standard_gltf(buffer)
    }
}

impl From<serde_json::Error> for ImportError {
    fn from(err: serde_json::Error) -> ImportError {
        ImportError::Deserialize(err)
    }
}

impl From<std::io::Error> for ImportError {
    fn from(err: std::io::Error) -> ImportError {
        ImportError::Io(err)
    }
}
<commit_msg>Fixed an incorrect error message<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use serde_json;
use std;
use v1::{Extras, Root};
use v1::root::extensions::{KhrBinaryGltf, KhrBinaryGltfHeader};

/// Error encountered when loading a glTF asset
#[derive(Debug)]
pub enum ImportError {
    /// Failure when deserializing a .gltf metadata file
    Deserialize(serde_json::error::Error),

    /// A glTF extension required by the asset has not been enabled by the user
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library
    ExtensionUnsupported(String),

    /// The .gltf data is invalid
    Invalid(String),

    /// Standard input / output error
    Io(std::io::Error),

    /// The glTF version of the asset is incompatible with this function
    IncompatibleVersion(String),
}

/// Imports a binary glTF 1.0 asset
fn import_binary_gltf<S, E>(mut stream: S) -> Result<Root<E>, ImportError>
    where S: std::io::Read, E: Extras
{
    use self::ImportError::*;

    #[cfg(not(feature = "KHR_binary_glTF"))]
    return Err(ExtensionDisabled("KHR_binary_glTF"));

    let header: KhrBinaryGltfHeader = unsafe {
        let mut buffer = [0u8; 16];
        stream.read_exact(&mut buffer[..])?;
        std::mem::transmute_copy(&buffer)
    };

    if header.version != 1 {
        let message = format!("KHR_binary_glTF version: {}", header.version);
        return Err(IncompatibleVersion(message));
    }

    if header.content_format != 0 {
        let message = format!("KHR_binary_glTF contentFormat: {}",
                              header.content_format);
        return Err(Invalid(message));
    }

    let mut content = Vec::with_capacity(header.content_length as usize);
    unsafe {
        content.set_len(header.content_length as usize);
    }
    stream.read_exact(&mut content[..])?;

    let body_length = header.length - header.content_length - 20;
    let mut body = Vec::with_capacity(body_length as usize);
    unsafe {
        body.set_len(body_length as usize);
    }
    stream.read_exact(&mut body[..])?;

    let mut root: Root<E> = serde_json::from_slice(&content)?;
    root.extensions.khr_binary_gltf = Some(KhrBinaryGltf {
        body: body,
        content: content,
        header: header,
    });

    Ok(root)
}

/// Imports a standard (plain text JSON) glTF 1.0 asset
fn import_standard_gltf<E>(data: Vec<u8>) -> Result<Root<E>, ImportError>
    where E: Extras
{
    let root: Root<E> = serde_json::from_slice(&data)?;

    Ok(root)
}

/// Imports a glTF 1.0 asset
pub fn import<P, E>(path: P) -> Result<Root<E>, ImportError>
    where P: AsRef<std::path::Path>, E: Extras
{
    use std::io::Read;

    let mut file = std::fs::File::open(path)?;

    let mut buffer = Vec::with_capacity(4);
    unsafe {
        buffer.set_len(4);
    }
    file.read_exact(&mut buffer)?;

    if buffer.starts_with(b"glTF") {
        import_binary_gltf(file)
    } else {
        file.read_to_end(&mut buffer)?;
        import_standard_gltf(buffer)
    }
}

impl From<serde_json::Error> for ImportError {
    fn from(err: serde_json::Error) -> ImportError {
        ImportError::Deserialize(err)
    }
}

impl From<std::io::Error> for ImportError {
    fn from(err: std::io::Error) -> ImportError {
        ImportError::Io(err)
    }
}
<|endoftext|>


<commit_before><commit_msg>refactor: updates deprecation notices for completions functions<commit_after><|endoftext|>


<commit_before>#[feature(managed_boxes)];
#[feature(globs)];
#[feature(macro_rules)];
#[feature(log_syntax)];

extern crate syntax;
extern crate rustc;
extern crate getopts;
extern crate serialize;
extern crate collections;

use std::io::println;

use rustc::{driver};
use rustc::metadata::creader::Loader;

use std::{os,local_data};
use std::cell::RefCell;
use collections::{HashMap,HashSet};
use std::path::Path;

use syntax::{parse,ast,codemap};
use syntax::codemap::Pos;
use find_ast_node::{safe_node_id_to_type,get_node_info_str,find_node_tree_loc_at_byte_pos,ToJsonStr,ToJsonStrFc,AstNodeAccessors,get_node_source};
use jumptodefmap::{lookup_def_at_text_file_pos_str, make_jdm, def_info_from_node_id,
    lookup_def_at_text_file_pos, dump_json};

use rfindctx::{RFindCtx,ctxtkey};
pub use codemaput::{ZTextFilePos,ZTextFilePosLen,get_span_str,ToZTextFilePos,ZIndexFilePos,ToZIndexFilePos};
use rsfind::{SDM_LineCol,SDM_Source,SDM_GeditCmd};
use crosscratemap::CrossCrateMap;

pub mod rf_common;
pub mod find_ast_node;
pub mod text_formatting;
pub mod ioutil;
pub mod htmlwriter;
pub mod rust2html;
pub mod codemaput;
pub mod rfindctx;
pub mod rsfind;
pub mod crosscratemap;
pub mod rfserver;
pub mod util;
pub mod rf_ast_ut;
pub mod jumptodefmap;
pub mod timer;

pub macro_rules! tlogi{
    ($($a:expr),*)=>(println!((file!()+":"+line!().to_str()+": " $(+$a.to_str())*) ))
}
pub macro_rules! logi{
    ($($a:expr),*)=>(println(""$(+$a.to_str())*) )
}
macro_rules! dump{ ($($a:expr),*)=>
    (   {   let mut txt=~"";
            $( { txt=txt.append(
                 format!("{:s}={:?}",stringify!($a),$a)+",")
                }
            );*;
            logi!(txt);
        }
    )
}

pub macro_rules! if_some {
    ($b:ident in $a:expr then $c:expr)=>(
        match $a {
            Some($b)=>$c,
            None=>{}
        }
    );
    ($b:ident in $a:expr then $c:expr _else $d:expr)=>(
        match $a {
            Some($b)=>$c,
            None=>{$d}
        }
    );
}

fn main() {

    use getopts::*;

    //test_default_arg(1,(2,3));
    //test_default_arg(1);

    let args = os::args();

    let opts = ~[
        optmulti("L", "", "Path to search for libraries", "<lib path>"),
        optflag("d", "", "Debug for this tool"),
        optflag("r", "", "Dump .rfx `crosscratemap` containing ast nods and jump definitions"),
        optflag("j", "", "Dump json map of the ast nodes & definitions"),
        optflag("h", "help", "Print this help menu"),
        optflag("i", "", "Run interactive server"),
        optflag("g", "", "Format output as gedit `filepos +line filename` (use with -f)"),
        optflag("w", "", "Dump as html"),
        optflag("f", "", "Return definition reference of symbol at given position"),
        optopt("x", "", "Where to look for html of external crates", "RUST_SRC"),
        optopt("o", "output-dir", "Directory to output the html / rfx files to", "OUTDIR")
    ];

    let matches = getopts(args.tail(), opts).unwrap();
    let libs1 = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
    let libs= if libs1.len() > 0 {
        libs1
    } else {
        match os::getenv(&"RUST_LIBS") {
            Some(x) => ~[Path::new(x)],
            None => ~[]
        }
    };

    if matches.opt_present("h") {
        println!(" filename.rs [-L<lib path>] : create linked html pages for sources in crate");
        println!(" -r filename.rs [-L<library path>]  : dump .rfx 'crosscratemap'  containing ast nodes and jump definitions");
        println!(" -x where to look for html of external crates -  :eg rustfind filename mysource.rs -x ~/rust/src\n");
        println!(" -o OUTDIR - Director to output the html / rfx files to");
        println!(" cratename.rs anotherfile.rs:line:col:");
        println!("    - load cratename.rs; look for definition at anotherfile.rs:line:col");
        println!("    - where anotherfile.rs is assumed to be a module of the crate");
        println!(" -f filename.rs:line:col : return definition reference of symbol at given position");
        println!(" -i filename.rs [-L<lib path>] : interactive mode");
        println!(" -g format output as gedit filepos +line filename");
        println!(" debug opts:-\n");
        println!(" -j filename.rs [-L<library path>]  : dump JSON map of the ast nodes & defintions");
        println!(" -d filename.rs [-L<lib path>] : debug for this tool");
        println!(" set RUST_LIBS for a default library search path");

        //println!("{}", getopts::usage("Summary of available options:", opts));
    };
    if matches.free.len()>0 {
        let mut done=false;
        let filename=util::get_filename_only(matches.free[0]);
        let dc = @get_ast_and_resolve(&Path::new(filename), libs.move_iter().collect());
        local_data::set(ctxtkey, dc);

        if matches.opt_present("d") {
            debug_test(dc);
            done=true;
        } else if matches.opt_present("j"){
            dump_json(dc);
        }
        let mut i=0;
        let lib_html_path=if matches.opt_present("x"){ matches.opt_str("x").unwrap()} else {~""};
        if matches.opt_present("f") {
            while i<matches.free.len() {
                let mode=if matches.opt_present("g"){SDM_GeditCmd} else {SDM_Source};
                println!("{}", lookup_def_at_text_file_pos_str(dc,matches.free[i],mode).unwrap_or(~"no def found"));
                i+=1;
                done=true;
            }
        }
        if matches.opt_present("i") {
            rfserver::run_server(dc);
            done=true;
        }
        let mut html_options = rust2html::Options::default();
        if matches.opt_present("o") {
            let out_dir = matches.opt_str("o").unwrap();
            let mut out_path = Path::new("./");
            out_path.push(out_dir);
            html_options.output_dir = out_path;
            println!("Outputting to `{}`", html_options.output_dir.display());
        }
        if matches.opt_present("r") {
            println!("Writing .rfx ast nodes/cross-crate-map:-");
            write_source_as_html_and_rfx(dc,lib_html_path, &html_options, false);
            println!("Writing .rfx .. done");
            done=true;
        }

        // Dump as html..
        if matches.opt_present("w") || !(done) {
            println!("Creating HTML pages from source & .rfx:-");
            write_source_as_html_and_rfx(dc,lib_html_path, &html_options, true);
            println!("Creating HTML pages from source.. done");
        }
    }
}

/// tags: crate,ast,parse resolve
/// Parses, resolves the given crate

fn get_ast_and_resolve(
    cpath: &std::path::Path,
    libs: HashSet<std::path::Path>)
    -> RFindCtx {

    let parsesess = parse::new_parse_sess();
    let sessopts = @driver::session::Options {
        maybe_sysroot: Some(@std::os::self_exe_path().unwrap()),
        addl_lib_search_paths:  @RefCell::new(libs),
        ..  (*rustc::driver::session::basic_options()).clone()
    };
    // Currently unused
//  let quiet = true;

    let diagnostic_handler = syntax::diagnostic::default_handler(); // TODO add back the blank emitter here ...
    let span_diagnostic_handler =
        syntax::diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);

    let sess = driver::driver::build_session_(sessopts,
                                              Some(cpath.clone()),
                                              parsesess.cm,
                                              span_diagnostic_handler);
    let input=driver::driver::FileInput(cpath.clone());
    let cfg = driver::driver::build_configuration(sess); //was, @"", &input);

    let crate1 = driver::driver::phase_1_parse_input(sess,cfg.clone(),&input);
    let loader = &mut Loader::new(sess);
    let (crate2, ast_map) = driver::driver::phase_2_configure_and_expand(sess, loader, crate1);

    let ca = driver::driver::phase_3_run_analysis_passes(sess, &crate2, ast_map);
    RFindCtx { crate_: @crate2, tycx: ca.ty_cx, sess: sess, ca:ca }
}

fn debug_test(dc:&RFindCtx) {

    // TODO: parse commandline source locations,convert to codemap locations
    //dump!(ctxt.tycx);
    logi!("==== Get tables of node-spans,def_maps,JumpToDefTable..===")
    let (node_info_map,node_def_node,jdm)=jumptodefmap::make_jdm(dc);
    println(node_info_map.to_json_str(dc));

    logi!("==== Node Definition mappings...===")
    println(node_def_node.to_json_str());

    logi!("==== JumpToDef Table ===");
    println(jdm.to_json_str());

    logi!("==== dump def table.===");
    rf_ast_ut::dump_ctxt_def_map(dc.tycx);

    logi!("==== Test node search by location...===");

    // Step a test 'cursor' src_pos through the given source file..
    let mut test_cursor=15 as uint;

    while test_cursor<500 {
        let loc = rfindctx::get_source_loc(dc,codemap::BytePos(test_cursor as u32));

        logi!(~"\n=====Find AST node at: ",loc.file.name,":",loc.line,":",loc.col.to_uint().to_str(),":"," =========");

        let nodetloc = find_node_tree_loc_at_byte_pos(dc.crate_,codemap::BytePos(test_cursor as u32));
        let node_info =  get_node_info_str(dc,&nodetloc);
        dump!(node_info);
        println("node ast loc:"+(nodetloc.map(|x| { x.get_id().to_str() })).to_str());

        if_some!(id in nodetloc.last().get_ref().ty_node_id() then {
            logi!("source=",get_node_source(dc.tycx, &node_info_map,ast::DefId{krate:0,node:id}));
            if_some!(t in safe_node_id_to_type(dc.tycx, id) then {
                println!("typeinfo: {:?}",
                    {let ntt= rustc::middle::ty::get(t); ntt});
                dump!(id,dc.tycx.def_map.borrow().get().find(&id));
                });
            let (def_id,opt_info)= def_info_from_node_id(dc,&node_info_map,id);
            if_some!(info in opt_info then{
                logi!("src node=",id," def node=",def_id,
                    " span=",format!("{:?}",info.span));
                logi!("def source=", get_node_source(dc.tycx, &node_info_map, def_id));
            })
        })

        test_cursor+=20;
    }

    // test byte pos from file...
    logi!("====test file:pos source lookup====");
    dump!(ZTextFilePosLen::new("test_input.rs",3-1,0,10).get_str(dc.tycx));
    dump!(ZTextFilePosLen::new("test_input.rs",9-1,0,10).get_str(dc.tycx));
    dump!(ZTextFilePosLen::new("test_input2.rs",4-1,0,10).get_str(dc.tycx));
    dump!(ZTextFilePosLen::new("test_input2.rs",11-1,0,10).get_str(dc.tycx));
    let ospan=ZTextFilePosLen::new("test_input2.rs", 10-1,0,32).to_byte_pos(dc.tycx);
    if_some!(x in ospan then {
        let (lo,hi)=x;
        logi!(get_span_str(dc.tycx, &codemap::Span{lo:lo,hi:hi,expn_info:None} ));
    });
    dump!(codemaput::zget_file_line_str(dc.tycx,"test_input2.rs",5-1));
    dump!(codemaput::zget_file_line_str(dc.tycx,"test_input.rs",9-1));

    logi!("\n====test full file:pos lookup====");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",8-1,21),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input2.rs",3-1,12),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",10-1,8),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",13-1,16),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",11-1,10),SDM_Source));println!("");

}

pub fn write_source_as_html_and_rfx(dc:&RFindCtx,lib_html_path:&str,opts: &rust2html::Options, write_html:bool) {

    let mut xcm:~CrossCrateMap=~HashMap::new();
    dc.tycx.cstore.iter_crate_data(|i,md| {
//      dump!(i, md.name,md.data.len(),md.cnum);
        println!("loading cross crate data {} {}", i, md.name);
        let xcm_sub=crosscratemap::read_cross_crate_map(dc, i as int, "lib"+md.name+&"/lib.rfx",lib_html_path);
        for (k,v) in xcm_sub.iter() {xcm.insert(*k,(*v).clone());}
    });

    let (info_map,def_map,jump_map) = make_jdm(dc);
    crosscratemap::write_cross_crate_map(dc,lib_html_path, &info_map,def_map,jump_map);
    if write_html {
        rust2html::write_source_as_html_sub(dc,&info_map,jump_map,xcm,lib_html_path,opts);
    }
}

<commit_msg>Update the help message printing<commit_after>#[feature(managed_boxes)];
#[feature(globs)];
#[feature(macro_rules)];
#[feature(log_syntax)];

extern crate syntax;
extern crate rustc;
extern crate getopts;
extern crate serialize;
extern crate collections;

use std::io::println;

use rustc::{driver};
use rustc::metadata::creader::Loader;

use std::{os,local_data};
use std::cell::RefCell;
use collections::{HashMap,HashSet};
use std::path::Path;

use getopts::{optmulti, optopt, optflag, getopts};

use syntax::{parse,ast,codemap};
use syntax::codemap::Pos;
use find_ast_node::{safe_node_id_to_type,get_node_info_str,find_node_tree_loc_at_byte_pos,ToJsonStr,ToJsonStrFc,AstNodeAccessors,get_node_source};
use jumptodefmap::{lookup_def_at_text_file_pos_str, make_jdm, def_info_from_node_id,
    lookup_def_at_text_file_pos, dump_json};

use rfindctx::{RFindCtx,ctxtkey};
pub use codemaput::{ZTextFilePos,ZTextFilePosLen,get_span_str,ToZTextFilePos,ZIndexFilePos,ToZIndexFilePos};
use rsfind::{SDM_LineCol,SDM_Source,SDM_GeditCmd};
use crosscratemap::CrossCrateMap;

pub mod rf_common;
pub mod find_ast_node;
pub mod text_formatting;
pub mod ioutil;
pub mod htmlwriter;
pub mod rust2html;
pub mod codemaput;
pub mod rfindctx;
pub mod rsfind;
pub mod crosscratemap;
pub mod rfserver;
pub mod util;
pub mod rf_ast_ut;
pub mod jumptodefmap;
pub mod timer;

pub macro_rules! tlogi{
    ($($a:expr),*)=>(println!((file!()+":"+line!().to_str()+": " $(+$a.to_str())*) ))
}
pub macro_rules! logi{
    ($($a:expr),*)=>(println(""$(+$a.to_str())*) )
}
macro_rules! dump{ ($($a:expr),*)=>
    (   {   let mut txt=~"";
            $( { txt=txt.append(
                 format!("{:s}={:?}",stringify!($a),$a)+",")
                }
            );*;
            logi!(txt);
        }
    )
}

pub macro_rules! if_some {
    ($b:ident in $a:expr then $c:expr)=>(
        match $a {
            Some($b)=>$c,
            None=>{}
        }
    );
    ($b:ident in $a:expr then $c:expr _else $d:expr)=>(
        match $a {
            Some($b)=>$c,
            None=>{$d}
        }
    );
}

fn optgroups () -> ~[getopts::OptGroup] {
    ~[
        optmulti("L", "", "Path to search for libraries", "<lib path>"),
        optflag("d", "", "Debug for this tool (DEBUG)"),
        optflag("r", "", "Dump .rfx `crosscratemap` containing ast nodes and jump definitions"),
        optflag("j", "", "Dump json map of the ast nodes & definitions (DEBUG)"),
        optflag("h", "help", "Print this help menu and exit"),
        optflag("i", "", "Run interactive server"),
        optflag("g", "", "Format output as gedit `filepos +line filename` (use with -f)"),
        optflag("w", "", "Dump as html"),
        optflag("f", "", "Return definition reference of symbol at given position"),
        optopt("x", "", "Path to html of external crates", "RUST_SRC"),
        optopt("o", "", "Directory to output the html / rfx files to", "OUTDIR")
    ]
}

fn usage(binary: &str) {
    let message = format!("Usage: {} [OPTIONS] INPUT", binary);
    println!("{}", getopts::usage(message, optgroups()));

    println!(" cratename.rs anotherfile.rs:line:col:");
    println!("    - load cratename.rs; look for definition at anotherfile.rs:line:col");
    println!("    - where anotherfile.rs is assumed to be a module of the crate");
    println!(" Set RUST_LIBS for a default library search path");
}

fn main() {
    let mut args = os::args();
    let binary = args.shift().unwrap();

    let opts = optgroups();
	let matches = getopts(args, opts).unwrap();
    let libs1 = matches.opt_strs("L").map(|s| Path::new(s.as_slice()));
    let libs= if libs1.len() > 0 {
        libs1
    } else {
        match os::getenv(&"RUST_LIBS") {
            Some(x) => ~[Path::new(x)],
            None => ~[]
        }
    };

    if matches.opt_present("h") || matches.opt_present("help") {
        usage(binary);
        return;
    };
    if matches.free.len()>0 {
        let mut done=false;
        let filename=util::get_filename_only(matches.free[0]);
        let dc = @get_ast_and_resolve(&Path::new(filename), libs.move_iter().collect());
        local_data::set(ctxtkey, dc);

        if matches.opt_present("d") {
            debug_test(dc);
            done=true;
        } else if matches.opt_present("j"){
            dump_json(dc);
        }
        let mut i=0;
        let lib_html_path=if matches.opt_present("x"){ matches.opt_str("x").unwrap()} else {~""};
        if matches.opt_present("f") {
            while i<matches.free.len() {
                let mode=if matches.opt_present("g"){SDM_GeditCmd} else {SDM_Source};
                println!("{}", lookup_def_at_text_file_pos_str(dc,matches.free[i],mode).unwrap_or(~"no def found"));
                i+=1;
                done=true;
            }
        }
        if matches.opt_present("i") {
            rfserver::run_server(dc);
            done=true;
        }
        let mut html_options = rust2html::Options::default();
        if matches.opt_present("o") {
            let out_dir = matches.opt_str("o").unwrap();
            let mut out_path = Path::new("./");
            out_path.push(out_dir);
            html_options.output_dir = out_path;
            println!("Outputting to `{}`", html_options.output_dir.display());
        }
        if matches.opt_present("r") {
            println!("Writing .rfx ast nodes/cross-crate-map:-");
            write_source_as_html_and_rfx(dc,lib_html_path, &html_options, false);
            println!("Writing .rfx .. done");
            done=true;
        }

        // Dump as html..
        if matches.opt_present("w") || !(done) {
            println!("Creating HTML pages from source & .rfx:-");
            write_source_as_html_and_rfx(dc,lib_html_path, &html_options, true);
            println!("Creating HTML pages from source.. done");
        }
	} else {
        usage(binary);
        return;
    }
}

/// tags: crate,ast,parse resolve
/// Parses, resolves the given crate

fn get_ast_and_resolve(
    cpath: &std::path::Path,
    libs: HashSet<std::path::Path>)
    -> RFindCtx {

    let parsesess = parse::new_parse_sess();
    let sessopts = @driver::session::Options {
        maybe_sysroot: Some(@std::os::self_exe_path().unwrap()),
        addl_lib_search_paths:  @RefCell::new(libs),
        ..  (*rustc::driver::session::basic_options()).clone()
    };
    // Currently unused
//  let quiet = true;

    let diagnostic_handler = syntax::diagnostic::default_handler(); // TODO add back the blank emitter here ...
    let span_diagnostic_handler =
        syntax::diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);

    let sess = driver::driver::build_session_(sessopts,
                                              Some(cpath.clone()),
                                              parsesess.cm,
                                              span_diagnostic_handler);
    let input=driver::driver::FileInput(cpath.clone());
    let cfg = driver::driver::build_configuration(sess); //was, @"", &input);

    let crate1 = driver::driver::phase_1_parse_input(sess,cfg.clone(),&input);
    let loader = &mut Loader::new(sess);
    let (crate2, ast_map) = driver::driver::phase_2_configure_and_expand(sess, loader, crate1);

    let ca = driver::driver::phase_3_run_analysis_passes(sess, &crate2, ast_map);
    RFindCtx { crate_: @crate2, tycx: ca.ty_cx, sess: sess, ca:ca }
}

fn debug_test(dc:&RFindCtx) {

    // TODO: parse commandline source locations,convert to codemap locations
    //dump!(ctxt.tycx);
    logi!("==== Get tables of node-spans,def_maps,JumpToDefTable..===")
    let (node_info_map,node_def_node,jdm)=jumptodefmap::make_jdm(dc);
    println(node_info_map.to_json_str(dc));

    logi!("==== Node Definition mappings...===")
    println(node_def_node.to_json_str());

    logi!("==== JumpToDef Table ===");
    println(jdm.to_json_str());

    logi!("==== dump def table.===");
    rf_ast_ut::dump_ctxt_def_map(dc.tycx);

    logi!("==== Test node search by location...===");

    // Step a test 'cursor' src_pos through the given source file..
    let mut test_cursor=15 as uint;

    while test_cursor<500 {
        let loc = rfindctx::get_source_loc(dc,codemap::BytePos(test_cursor as u32));

        logi!(~"\n=====Find AST node at: ",loc.file.name,":",loc.line,":",loc.col.to_uint().to_str(),":"," =========");

        let nodetloc = find_node_tree_loc_at_byte_pos(dc.crate_,codemap::BytePos(test_cursor as u32));
        let node_info =  get_node_info_str(dc,&nodetloc);
        dump!(node_info);
        println("node ast loc:"+(nodetloc.map(|x| { x.get_id().to_str() })).to_str());

        if_some!(id in nodetloc.last().get_ref().ty_node_id() then {
            logi!("source=",get_node_source(dc.tycx, &node_info_map,ast::DefId{krate:0,node:id}));
            if_some!(t in safe_node_id_to_type(dc.tycx, id) then {
                println!("typeinfo: {:?}",
                    {let ntt= rustc::middle::ty::get(t); ntt});
                dump!(id,dc.tycx.def_map.borrow().get().find(&id));
                });
            let (def_id,opt_info)= def_info_from_node_id(dc,&node_info_map,id);
            if_some!(info in opt_info then{
                logi!("src node=",id," def node=",def_id,
                    " span=",format!("{:?}",info.span));
                logi!("def source=", get_node_source(dc.tycx, &node_info_map, def_id));
            })
        })

        test_cursor+=20;
    }

    // test byte pos from file...
    logi!("====test file:pos source lookup====");
    dump!(ZTextFilePosLen::new("test_input.rs",3-1,0,10).get_str(dc.tycx));
    dump!(ZTextFilePosLen::new("test_input.rs",9-1,0,10).get_str(dc.tycx));
    dump!(ZTextFilePosLen::new("test_input2.rs",4-1,0,10).get_str(dc.tycx));
    dump!(ZTextFilePosLen::new("test_input2.rs",11-1,0,10).get_str(dc.tycx));
    let ospan=ZTextFilePosLen::new("test_input2.rs", 10-1,0,32).to_byte_pos(dc.tycx);
    if_some!(x in ospan then {
        let (lo,hi)=x;
        logi!(get_span_str(dc.tycx, &codemap::Span{lo:lo,hi:hi,expn_info:None} ));
    });
    dump!(codemaput::zget_file_line_str(dc.tycx,"test_input2.rs",5-1));
    dump!(codemaput::zget_file_line_str(dc.tycx,"test_input.rs",9-1));

    logi!("\n====test full file:pos lookup====");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",8-1,21),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input2.rs",3-1,12),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",10-1,8),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",13-1,16),SDM_Source));println!("");
    dump!(lookup_def_at_text_file_pos(dc, &ZTextFilePos::new("test_input.rs",11-1,10),SDM_Source));println!("");

}

pub fn write_source_as_html_and_rfx(dc:&RFindCtx,lib_html_path:&str,opts: &rust2html::Options, write_html:bool) {

    let mut xcm:~CrossCrateMap=~HashMap::new();
    dc.tycx.cstore.iter_crate_data(|i,md| {
//      dump!(i, md.name,md.data.len(),md.cnum);
        println!("loading cross crate data {} {}", i, md.name);
        let xcm_sub=crosscratemap::read_cross_crate_map(dc, i as int, "lib"+md.name+&"/lib.rfx",lib_html_path);
        for (k,v) in xcm_sub.iter() {xcm.insert(*k,(*v).clone());}
    });

    let (info_map,def_map,jump_map) = make_jdm(dc);
    crosscratemap::write_cross_crate_map(dc,lib_html_path, &info_map,def_map,jump_map);
    if write_html {
        rust2html::write_source_as_html_sub(dc,&info_map,jump_map,xcm,lib_html_path,opts);
    }
}

<|endoftext|>


<commit_before>#[link(name = "simplify", vers = "0.0.6")];
extern mod extra;
use extra::json::*;
use extra::treemap::TreeSet;
use std::vec;
#[deriving(Clone, Eq)]
pub struct Point {
	x: float,
	y: float
}
impl ToJson for Point {
	fn to_json(&self) -> Json { List(~[self.x.to_json(),self.y.to_json()]) }
}
impl Sub<Point,Point> for Point {
	#[inline]
	fn sub(&self, other: &Point) -> Point { Point {x:self.x-other.x,y:self.y-other.y} }
}
impl Add<Point,Point> for Point {
	#[inline]
	fn add(&self, other: &Point) -> Point { Point { x:self.x+other.x,y:self.y+other.y }}
}
impl Mul<Point,Point> for Point {
	#[inline]
	fn mul(&self, other: &Point) -> Point { Point { x:self.x*other.x,y:self.y*other.y }}
}

impl Point {
	fn sum(self) -> float { self.x+self.y }
	fn sqsum(self) -> float { self.x * self.x + self.y * self.y}
	fn sub(self, other: float) -> Point { Point { x:self.x - other , y:self.y - other }}
	fn mul(self, other: float) -> Point { Point { x:self.x * other, y:self.y * other }}
	fn add(self, other: float) -> Point {  Point {x:self.x + other, y:self.y + other }}
}
type Pair = (uint, uint);
fn calcStuff(p:Point,p1:Point,d1:Point)->float {
	let top = ((p - p1) * d1).sum();
	let bottom =  d1.sqsum();
	if bottom == 0.0 {
	    0.0
	}else{
	    top/bottom
	}
}
fn getSquareSegmentDistance(p: Point, p1: Point, p2: Point) -> float {
	let d1 = p2-p1;
	let d2 = match d1{
		Point {x:0.0,_} | Point {y:0.0,_}=> {p1}
		_=>{
			let t = calcStuff(p,p1,d1);
			if t>1.0 {
			    p2
			}else if t>0.0{
			    d1.mul(t)+p1
			}else{
			    p1
			}
		}
	};
	(p-d2).sqsum()
}

fn simplifyRadialDistance(points:~[Point], sqTolerance:float) -> ~[Point]{
	let mut it = points.iter();
	it.next();
	let mut prevPoint : Point = points[0u];
	let mut newPoints : ~[Point] = ~[prevPoint];
	let &last = points.last();
	for &point in it{
		if (point - prevPoint).sqsum() > sqTolerance {
			newPoints.push(point);
			prevPoint = point;
		}
	}
	if (prevPoint!= last) {
		newPoints.push(last);
	}
	newPoints
}

fn simplifyDouglasPeucker(points : ~[Point], tolerance : float) -> ~[Point]{
	let len = points.len();
	let mut markers = TreeSet::new();
	let mut stack : ~[Pair] = ~[];
	markers.insert(0u);
	markers.insert(len-1u);
	let mut pair:Pair = (0u,len-1u);
	loop {
        let first = pair.first();
        let second = pair.second();
        let (first_pt, second_pt) = (points[first], points[second]);
        let mut index = 0;
        let mut max_sq_dist = 0.0f;
        let i = first + 1u;
        for (i, &point_i) in points.slice_from(i)
            .iter()
            .enumerate()
            .map(|(new_i, point)| (i + new_i, point))
            .take_while(|&(i, _)| i < second) {
            let sq_dist = getSquareSegmentDistance(point_i, first_pt, second_pt);
            if (sq_dist > max_sq_dist) {
                index = i;
                max_sq_dist = sq_dist;
            }
        }
        if max_sq_dist > tolerance {
            markers.insert(index);
            stack.push((first,index));
            stack.push((index,second));
        }
        match stack.pop_opt() {
            Some(p)=>pair=p,
            None=>break
        };
    }
	vec::from_fn(markers.len(),|k| points[k])
}

pub fn simplify(points : ~[Point], sqTolerance : float, hq:bool) -> ~[Point]{
	let tolerance = sqTolerance*sqTolerance;
	let pts:~[Point] = if hq {
	    points
	} else {
	    simplifyRadialDistance(points,tolerance)
	};
	simplifyDouglasPeucker(pts,tolerance)
}
<commit_msg>spawn<commit_after>#[link(name = "simplify", vers = "0.0.6")];
extern mod extra;
use extra::json::Json;
use extra::treemap::TreeSet;
use std::vec;
use extra::arc::Arc;
use extra::future::Future;
#[deriving(Clone, Eq)]
pub struct Point {
	x: float,
	y: float
}

impl ToJson for Point {
	fn to_json(&self) -> Json { List(~[self.x.to_json(),self.y.to_json()]) }
}
impl Sub<Point,Point> for Point {
	#[inline]
	fn sub(&self, other: &Point) -> Point { Point {x:self.x-other.x,y:self.y-other.y} }
}
impl Add<Point,Point> for Point {
	#[inline]
	fn add(&self, other: &Point) -> Point { Point { x:self.x+other.x,y:self.y+other.y }}
}
impl Mul<Point,Point> for Point {
	#[inline]
	fn mul(&self, other: &Point) -> Point { Point { x:self.x*other.x,y:self.y*other.y }}
}

impl Point {
	fn sum(self) -> float { self.x+self.y }
	fn sqsum(self) -> float { self.x * self.x + self.y * self.y}
	fn sub(self, other: float) -> Point { Point { x:self.x - other , y:self.y - other }}
	fn mul(self, other: float) -> Point { Point { x:self.x * other, y:self.y * other }}
	fn add(self, other: float) -> Point {  Point {x:self.x + other, y:self.y + other }}
}
type Pair = (uint, uint);

fn calcStuff(p:Point,p1:Point,d1:Point)->float {
	let top = ((p - p1) * d1).sum();
	let bottom =  d1.sqsum();
	if bottom == 0.0 {
	    0.0
	}else{
	    top/bottom
	}
}
fn getSquareSegmentDistance(p: Point, p1: Point, p2: Point) -> float {
	let d1 = p2-p1;
	let d2 = match d1{
		Point {x:0.0,_} | Point {y:0.0,_}=> {p1}
		_=>{
			let t = calcStuff(p,p1,d1);
			if t>1.0 {
			    p2
			}else if t>0.0{
			    d1.mul(t)+p1
			}else{
			    p1
			}
		}
	};
	(p-d2).sqsum()
}

fn simplifyRadialDistance(points:~[Point], sqTolerance:float) -> ~[Point]{
	let mut it = points.iter();
	it.next();
	let mut prevPoint : Point = points[0u];
	let mut newPoints : ~[Point] = ~[prevPoint];
	let &last = points.last();
	for &point in it{
		if (point - prevPoint).sqsum() > sqTolerance {
			newPoints.push(point);
			prevPoint = point;
		}
	}
	if (prevPoint!= last) {
		newPoints.push(last);
	}
	newPoints
}
struct retThing {
    max_sq_dist : float,
    first: uint,
    second:uint,
    index:uint
}
fn iterFunc (pair:Pair,pointsArc:Arc<~[Point]>)->retThing{
    let points = pointsArc.get();
    let first = pair.first();
    let second = pair.second();
    let (first_pt, second_pt) = (points[first], points[second]);
    let mut index = 0u;
    let mut max_sq_dist = 0.0f64;
    let i = first + 1u;
    for (i, &point_i) in points.slice_from(i)
        .iter()
        .enumerate()
        .map(|(new_i, point)| (i + new_i, point))
        .take_while(|&(i, _)| i < second) {
        let sq_dist = getSquareSegmentDistance(point_i, first_pt, second_pt);
        if (sq_dist > max_sq_dist) {
            index = i;
            max_sq_dist = sq_dist;
        }
    }
    return retThing{
        max_sq_dist:max_sq_dist,
        first:first,
        second:second,
        index:index
    }
}
fn simplifyDouglasPeucker(opoints : ~[Point], tolerance : float) -> ~[Point]{
    let arcPoints = Arc::new(opoints);
	let points = arcPoints.get();
	let len = points.len();
	let mut markers = TreeSet::new();
	let mut stack : ~[Pair] = ~[];
	markers.insert(0u);
	markers.insert(len-1u);
	let mut pair:Pair = (0u,len-1u);
	loop {
        let mut prelimrslt = Future::spawn(|| iterFunc(pair,arcPoints.clone()));
        let rslt = prelimrslt.get();
        if rslt.max_sq_dist > tolerance {
            markers.insert(rslt.index);
            stack.push((rslt.first,rslt.index));
            stack.push((rslt.index,rslt.second));
        }
        match stack.pop_opt() {
            Some(p)=>pair=p,
            None=>break
        };
    }
	vec::from_fn(markers.len(),|k| points[k])
}

pub fn simplify(points : ~[Point], sqTolerance : float, hq:bool) -> ~[Point]{
	let tolerance = sqTolerance*sqTolerance;
	let pts:~[Point] = if hq {
	    points
	} else {
	    simplifyRadialDistance(points,tolerance)
	};
	simplifyDouglasPeucker(pts,tolerance)
}
<|endoftext|>