<commit_before>//!
//!	Code to read a sequence table from disk.
//!
extern crate bincode;

use std::io::prelude::*;
use std::io::SeekFrom;
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind;
use byteorder::{LittleEndian, ReadBytesExt};
use bincode::rustc_serialize::{decode_from, decode};
use flate2::{Decompress,Flush};

use super::SeqTableParams;
use super::write::SeqInfo;
use super::write::SeqBlock;

#[derive(Debug)]
pub struct SeqTable<R: Read + Seek> {
    pub params: SeqTableParams,
    block_length: u32,
    infotable: Vec<SeqInfo>,
    reader: R,
    dec_buffer: Vec<u8>,
    read_buffer: Vec<u8>,
    counts_offset: u64,
}

pub struct SequenceInfo {
    pub name: String,
    pub length: u32,
}

impl<R: Read + Seek> SeqTable<R> {
    pub fn open(mut reader: R) -> Result<SeqTable<R>> {
        try!(reader.seek(SeekFrom::Start(0)));
        // load version
        let version = try!(reader.read_u8());
        if version != super::TBL_VERSION {
            return Err(Error::new(ErrorKind::InvalidData, format!("Incompatible file version {}, expected {}.", version, super::TBL_VERSION)));
        }
        // load parameters
        let params = SeqTableParams {
            cut_length: try!(reader.read_u8()),
            plus_offset: try!(reader.read_u8()),
            minus_offset: try!(reader.read_u8()),
            read_length: try!(reader.read_u16::<LittleEndian>()),
        };
        // load block size
        let blen = try!(reader.read_u32::<LittleEndian>());
        // load info table offset
        let offset = try!(reader.read_u64::<LittleEndian>());
        // load buffer size
        let bufsize = try!(reader.read_u64::<LittleEndian>()) as usize;
        // load counts table offset
        let counts_offset = try!(reader.read_u64::<LittleEndian>());

        //println!("blen: {}, offset: {}, bufsize: {}", blen, offset, bufsize);

        // load info table
        try!(reader.seek(SeekFrom::Start(offset)));
        let infotable = (decode_from(&mut reader, bincode::SizeLimit::Infinite)).unwrap(); // TODO: fix

        Ok(SeqTable {
            params: params,
            block_length: blen,
            infotable: infotable,
            reader: reader,
            dec_buffer: vec![0u8; bufsize],
            read_buffer: vec![0u8; bufsize],
            counts_offset: counts_offset,
        })
    }

    pub fn get_sequence<'a>(&'a mut self, name: &str) -> Result<SeqReader<'a, R>> {
        // locate sequence info
        match self.infotable.iter().find(|&x| x.name == name) {
            Some(iref) => {
                match iref.blocks.get(0) {
                    Some(block) => {
                        try!(self.reader.seek(SeekFrom::Start(block.offset)));
                        Ok(SeqReader {
                            reader: &mut self.reader,
                            block_length: self.block_length,
                            info: &iref,
                            block: None,
                            block_idx: 0,
                            dec_buffer: &mut self.dec_buffer,
                            read_buffer: &mut self.read_buffer,
                        })
                    },
                    None => Err(Error::new(ErrorKind::NotFound, "sequence not found")),
                }

            },
            None => Err(Error::new(ErrorKind::NotFound, "sequence not found")),
        }
    }

    pub fn get_sequence_by_idx<'a>(&'a mut self, idx: usize) -> Result<SeqReader<'a, R>> {
        if self.infotable.len() <= idx {
            Err(Error::new(ErrorKind::NotFound, "sequence not found"))
        } else {
            match self.infotable[idx].blocks.get(0) {
                Some(block) => {
                    try!(self.reader.seek(SeekFrom::Start(block.offset)));
                    Ok(SeqReader {
                        reader: &mut self.reader,
                        block_length: self.block_length,
                        info: &self.infotable[idx],
                        block: None,
                        block_idx: 0,
                        dec_buffer: &mut self.dec_buffer,
                        read_buffer: &mut self.read_buffer,
                    })
                },
                None => Err(Error::new(ErrorKind::NotFound, "sequence not found")),
            }
        }
    }

    pub fn params(&self) -> &SeqTableParams {
        &self.params
    }

    pub fn equivalent(&self, _: &str, params: &SeqTableParams) -> bool {
        self.params.read_length == params.read_length &&
        self.params.cut_length == params.cut_length &&
        self.params.plus_offset == params.plus_offset &&
        self.params.minus_offset == params.minus_offset
    }

    pub fn sequences(&self) -> Vec<SequenceInfo> {
        self.infotable.iter().map(|info|
            SequenceInfo{ name: info.name.clone(), length: info.length }
        ).collect()
    }

    pub fn counts(&mut self) -> Result<Vec<(u64, u64, u64, u64)>> {
        try!(self.reader.seek(SeekFrom::Start(self.counts_offset)));
        let counts: Vec<(u64, u64, u64, u64)> = (decode_from(&mut self.reader, bincode::SizeLimit::Infinite)).unwrap();
        Ok(counts)
    }

    pub fn len(&self) -> usize {
        self.infotable.len()
    }
}

#[derive(Debug)]
pub struct SeqReader<'a, R: 'a + Read + Seek> {
    reader: &'a mut R,
    block_length: u32,
    info: &'a SeqInfo,
    block: Option<Vec<(u16, u16)>>,
    block_idx: usize,
    dec_buffer: &'a mut Vec<u8>,
    read_buffer: &'a mut Vec<u8>,
}

impl<'a, R: 'a + Read + Seek> SeqReader<'a, R> {

    fn read_block(&mut self, block_info: &SeqBlock) -> Result<Vec<(u16,u16)>> {

        // read from disk (compressed)
        let buf = &mut self.read_buffer[0..block_info.comp_size as usize];
        let mut decompressor = Decompress::new(false);

        try!(self.reader.seek(SeekFrom::Start(block_info.offset)));
        try!(self.reader.read(buf));

        // uncompress
        let bufout = &mut self.dec_buffer[0..block_info.enc_size as usize];
        decompressor.decompress(&buf, bufout, Flush::Finish).ok().expect("decompressing block");

        // decode back into vector
        let block: Vec<(u16, u16)> = decode(bufout).ok().expect("decode block");

        return Ok(block);
    }

    pub fn vir_get(&mut self, position: i32) -> Result<(u16, u16)> {
        if position < 0 { Ok((0, 0)) }
        else { self.get(position as u32) }
    }

    pub fn get(&mut self, position: u32) -> Result<(u16, u16)> {
        let idx = (position / self.block_length) as usize;

        //println!("get: pos: {} idx: {} blen: {} n_blocks: {} length: {}", position, idx, self.block_length, self.info.blocks.len(), self.info.length);

        let block = if self.block.is_some() && self.block_idx == idx {
            self.block.as_ref().unwrap()
        } else {
            match self.info.blocks.get(idx) {
                Some(block_info) => {
                    let aux = try!(self.read_block(block_info));
                    self.block = Some(aux);
                    self.block_idx = idx;
                    self.block.as_ref().unwrap()
                },
                None => return Err(Error::new(ErrorKind::AddrNotAvailable, "index out of range")),
            }
        };

        // get value
        let idx2 = position as usize - idx * self.block_length as usize;
        Ok(block[idx2])
    }
}
<commit_msg>Fix 16 to 32-bit transition on the reader side<commit_after>//!
//!	Code to read a sequence table from disk.
//!
extern crate bincode;

use std::io::prelude::*;
use std::io::SeekFrom;
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind;
use byteorder::{LittleEndian, ReadBytesExt};
use bincode::rustc_serialize::{decode_from, decode};
use flate2::{Decompress,Flush};

use super::SeqTableParams;
use super::write::SeqInfo;
use super::write::SeqBlock;

#[derive(Debug)]
pub struct SeqTable<R: Read + Seek> {
    pub params: SeqTableParams,
    block_length: u32,
    infotable: Vec<SeqInfo>,
    reader: R,
    dec_buffer: Vec<u8>,
    read_buffer: Vec<u8>,
    counts_offset: u64,
}

pub struct SequenceInfo {
    pub name: String,
    pub length: u32,
}

impl<R: Read + Seek> SeqTable<R> {
    pub fn open(mut reader: R) -> Result<SeqTable<R>> {
        try!(reader.seek(SeekFrom::Start(0)));
        // load version
        let version = try!(reader.read_u8());
        if version != super::TBL_VERSION {
            return Err(Error::new(ErrorKind::InvalidData, format!("Incompatible file version {}, expected {}.", version, super::TBL_VERSION)));
        }
        // load parameters
        let params = SeqTableParams {
            cut_length: try!(reader.read_u8()),
            plus_offset: try!(reader.read_u8()),
            minus_offset: try!(reader.read_u8()),
            read_length: try!(reader.read_u16::<LittleEndian>()),
        };
        // load block size
        let blen = try!(reader.read_u32::<LittleEndian>());
        // load info table offset
        let offset = try!(reader.read_u64::<LittleEndian>());
        // load buffer size
        let bufsize = try!(reader.read_u64::<LittleEndian>()) as usize;
        // load counts table offset
        let counts_offset = try!(reader.read_u64::<LittleEndian>());

        //println!("blen: {}, offset: {}, bufsize: {}", blen, offset, bufsize);

        // load info table
        try!(reader.seek(SeekFrom::Start(offset)));
        let infotable = (decode_from(&mut reader, bincode::SizeLimit::Infinite)).unwrap(); // TODO: fix

        Ok(SeqTable {
            params: params,
            block_length: blen,
            infotable: infotable,
            reader: reader,
            dec_buffer: vec![0u8; bufsize],
            read_buffer: vec![0u8; bufsize],
            counts_offset: counts_offset,
        })
    }

    pub fn get_sequence<'a>(&'a mut self, name: &str) -> Result<SeqReader<'a, R>> {
        // locate sequence info
        match self.infotable.iter().find(|&x| x.name == name) {
            Some(iref) => {
                match iref.blocks.get(0) {
                    Some(block) => {
                        try!(self.reader.seek(SeekFrom::Start(block.offset)));
                        Ok(SeqReader {
                            reader: &mut self.reader,
                            block_length: self.block_length,
                            info: &iref,
                            block: None,
                            block_idx: 0,
                            dec_buffer: &mut self.dec_buffer,
                            read_buffer: &mut self.read_buffer,
                        })
                    },
                    None => Err(Error::new(ErrorKind::NotFound, "sequence not found")),
                }

            },
            None => Err(Error::new(ErrorKind::NotFound, "sequence not found")),
        }
    }

    pub fn get_sequence_by_idx<'a>(&'a mut self, idx: usize) -> Result<SeqReader<'a, R>> {
        if self.infotable.len() <= idx {
            Err(Error::new(ErrorKind::NotFound, "sequence not found"))
        } else {
            match self.infotable[idx].blocks.get(0) {
                Some(block) => {
                    try!(self.reader.seek(SeekFrom::Start(block.offset)));
                    Ok(SeqReader {
                        reader: &mut self.reader,
                        block_length: self.block_length,
                        info: &self.infotable[idx],
                        block: None,
                        block_idx: 0,
                        dec_buffer: &mut self.dec_buffer,
                        read_buffer: &mut self.read_buffer,
                    })
                },
                None => Err(Error::new(ErrorKind::NotFound, "sequence not found")),
            }
        }
    }

    pub fn params(&self) -> &SeqTableParams {
        &self.params
    }

    pub fn equivalent(&self, _: &str, params: &SeqTableParams) -> bool {
        self.params.read_length == params.read_length &&
        self.params.cut_length == params.cut_length &&
        self.params.plus_offset == params.plus_offset &&
        self.params.minus_offset == params.minus_offset
    }

    pub fn sequences(&self) -> Vec<SequenceInfo> {
        self.infotable.iter().map(|info|
            SequenceInfo{ name: info.name.clone(), length: info.length }
        ).collect()
    }

    pub fn counts(&mut self) -> Result<Vec<(u64, u64, u64, u64)>> {
        try!(self.reader.seek(SeekFrom::Start(self.counts_offset)));
        let counts: Vec<(u64, u64, u64, u64)> = (decode_from(&mut self.reader, bincode::SizeLimit::Infinite)).unwrap();
        Ok(counts)
    }

    pub fn len(&self) -> usize {
        self.infotable.len()
    }
}

#[derive(Debug)]
pub struct SeqReader<'a, R: 'a + Read + Seek> {
    reader: &'a mut R,
    block_length: u32,
    info: &'a SeqInfo,
    block: Option<Vec<(u32, u32)>>,
    block_idx: usize,
    dec_buffer: &'a mut Vec<u8>,
    read_buffer: &'a mut Vec<u8>,
}

impl<'a, R: 'a + Read + Seek> SeqReader<'a, R> {

    fn read_block(&mut self, block_info: &SeqBlock) -> Result<Vec<(u32,u32)>> {

        // read from disk (compressed)
        let buf = &mut self.read_buffer[0..block_info.comp_size as usize];
        let mut decompressor = Decompress::new(false);

        try!(self.reader.seek(SeekFrom::Start(block_info.offset)));
        try!(self.reader.read(buf));

        // uncompress
        let bufout = &mut self.dec_buffer[0..block_info.enc_size as usize];
        decompressor.decompress(&buf, bufout, Flush::Finish).ok().expect("decompressing block");

        // decode back into vector
        let block: Vec<(u32, u32)> = decode(bufout).ok().expect("decode block");

        return Ok(block);
    }

    pub fn vir_get(&mut self, position: i32) -> Result<(u32, u32)> {
        if position < 0 { Ok((0, 0)) }
        else { self.get(position as u32) }
    }

    pub fn get(&mut self, position: u32) -> Result<(u32, u32)> {
        let idx = (position / self.block_length) as usize;

        //println!("get: pos: {} idx: {} blen: {} n_blocks: {} length: {}", position, idx, self.block_length, self.info.blocks.len(), self.info.length);

        let block = if self.block.is_some() && self.block_idx == idx {
            self.block.as_ref().unwrap()
        } else {
            match self.info.blocks.get(idx) {
                Some(block_info) => {
                    let aux = try!(self.read_block(block_info));
                    self.block = Some(aux);
                    self.block_idx = idx;
                    self.block.as_ref().unwrap()
                },
                None => return Err(Error::new(ErrorKind::AddrNotAvailable, "index out of range")),
            }
        };

        // get value
        let idx2 = position as usize - idx * self.block_length as usize;
        Ok(block[idx2])
    }
}
<|endoftext|>


<commit_before><commit_msg>[fix] Don't try to use the generic ioctl encoding on unknown archs<commit_after><|endoftext|>


<commit_before>fn main() {
    let a = 0;
    {
        let b = &Some(a);
    }
    let c = 1;
}

// END RUST SOURCE
// START rustc.main.TypeckMir.before.mir
//     bb0: {
//         StorageLive(_1);
//         _1 = const 0i32;
//         FakeRead(ForLet, _1);
//         StorageLive(_3);
//         StorageLive(_4);
//         StorageLive(_5);
//         _5 = _1;
//         _4 = std::option::Option::<i32>::Some(move _5,);
//         StorageDead(_5);
//         _3 = &_4;
//         FakeRead(ForLet, _3);
//         _2 = ();
//         StorageDead(_4);
//         StorageDead(_3);
//         StorageLive(_6);
//         _6 = const 1i32;
//         FakeRead(ForLet, _6);
//         _0 = ();
//         StorageDead(_6);
//         StorageDead(_1);
//         return;
//      }
// END rustc.main.TypeckMir.before.mir
<commit_msg>Fix test<commit_after>fn main() {
    let a = 0;
    {
        let b = &Some(a);
    }
    let c = 1;
}

// END RUST SOURCE
// START rustc.main.nll.0.mir
//     bb0: {
//         StorageLive(_1);
//         _1 = const 0i32;
//         FakeRead(ForLet, _1);
//         StorageLive(_3);
//         StorageLive(_4);
//         StorageLive(_5);
//         _5 = _1;
//         _4 = std::option::Option::<i32>::Some(move _5,);
//         StorageDead(_5);
//         _3 = &_4;
//         FakeRead(ForLet, _3);
//         _2 = ();
//         StorageDead(_4);
//         StorageDead(_3);
//         StorageLive(_6);
//         _6 = const 1i32;
//         FakeRead(ForLet, _6);
//         _0 = ();
//         StorageDead(_6);
//         StorageDead(_1);
//         return;
//      }
// END rustc.main.nll.0.mir
<|endoftext|>


<commit_before>#![allow(ctypes)]
#![no_std]
#![no_main]
#![feature(globs)]
#![feature(asm)]
// Adding pub mod (another fix for this warning) increases footprint, so just
// disble it instead.
#![allow(visible_private_types)]

extern crate core;

use core::prelude::*;
use core::mem::*;

use aspace::AddressSpace;
use con::write;
use dlist::DList;
use process::Process;
use start32::MultiBootInfo;
//use start32::OrigMultiBootInfo;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;
use x86::idt;
pub use x86::idt::irq_entry;
pub use syscall::syscall;

mod aspace;
#[allow(dead_code)]
mod con;
mod dict;
mod dlist;
mod mboot;
mod mem;
mod process;
mod start32;
mod syscall;
pub mod util;
mod x86;

static log_assoc_procs : bool = false;
static log_page_fault : bool = false;
static log_switch : bool = false;
static mem_test : bool = false;

#[allow(dead_code)]
fn writeMBInfo(infop : *mboot::Info) {
    con::write("Multiboot info at ");
    con::writePtr(infop);
    con::putc('\n');

    let &info = unsafe { &*infop };
    con::write("Flags: ");
    con::writeHex(info.flags as uint);
    con::newline();

    if info.has(mboot::MemorySize) {
        con::writeUInt(info.mem_lower as uint);
        con::write("kB lower memory, ");
        con::writeUInt(info.mem_upper as uint);
        con::write("kB upper memory, ");
        con::writeUInt(((info.mem_lower + info.mem_upper + 1023) / 1024) as uint);
        con::write("MB total.\n");
    }
    if info.has(mboot::CommandLine) {
        let cmdline : *u8 = PhysAddr(info.cmdline as uint);
        con::write("Command line @");
        con::writePtr(cmdline);
        con::write(" (");
        con::writeHex(info.cmdline as uint);
        con::write(") \"");
        con::writeCStr(cmdline);
        con::write("\"\n");
    }
}

pub fn generic_irq_handler(p : &mut Process, vec : u8) {
    write("IRQ! vec=");
    con::writeUInt(vec as uint);
    con::newline();
    cpu().queue(p);
}

pub fn page_fault(p : &mut Process, error : uint) -> ! {
    mod pf_errors {
        #![allow(dead_code)]
        // Error code flags.
        pub static PRESENT : uint = 1;
        pub static WRITE : uint = 2;
        pub static USER : uint = 4;
        pub static RSVD : uint = 8;
        pub static INSTR : uint = 16;
    }

    if log_page_fault {
        write("page fault ");
        con::writeHex(error);
        write(" cr2=");
        con::writePHex(x86::cr2());
        write(" rip=");
        con::writePHex(p.regs.rip as uint);
        write(" in process ");
        con::writeMutPtr(p);
        con::newline();
    }

    if (error & pf_errors::USER) == 0 {
        abort("kernel page fault\n");
    }

    let back = p.aspace().find_add_backing(x86::cr2() & !0xfff);
    // FIXME should return e.g. Option<> so we can detect failures better than
    // just aborting.
    p.aspace().add_pte(back.vaddr(), back.pte());

    cpu().queue(p);
    unsafe { cpu().run(); }
}

pub fn handler_NM() {
    abort("NM");
}

pub fn idle() -> ! {
    loop {
        write("idle\n");
        unsafe { asm!("swapgs; sti; hlt; cli; swapgs" :::: "volatile"); } }
}

pub struct PerCpu {
    selfp : *mut PerCpu,
    stack : *mut u8,
    process : Option<&'static mut Process>,
    //

    // End of assembly-fixed fields.
    memory : mem::PerCpu,
    runqueue : DList<Process>,
}

impl PerCpu {
    unsafe fn new() -> *mut PerCpu {
        let mut mem = mem::PerCpu::new();
        let p : *mut PerCpu = mem.alloc_frame_panic();
        let stack : *mut u8 = mem.alloc_frame_panic();
        *p = PerCpu {
            selfp : p,
            stack : stack,
            memory : mem,
            runqueue : DList::empty(),
            process : None
        };
        return p
    }

    unsafe fn start(&mut self) {
        setup_msrs(self.selfp as uint);
    }

    fn queue(&mut self, p: &mut Process) {
        if !p.is_queued() {
            p.set(process::Queued);
            self.runqueue.append(p);
        }
    }

    unsafe fn run(&mut self) -> ! {
        match self.runqueue.pop() {
            Some(p) => self.switch_to(&mut *p),
            None => idle()
        }
    }

    unsafe fn switch_to(&mut self, p: &mut Process) -> ! {
        if log_switch {
            write("switch_to ");
            con::writeMutPtr(p as *mut Process);
            con::newline();
        }
        p.unset(process::Queued);
        p.set(process::Running);
        self.process = transmute(p as *mut Process);
        // TODO Check fpu_process, see if we need to set/reset TS bit in cr0
        x86::set_cr3(p.cr3);
        extern "C" {
            fn fastret(p : &mut Process) -> !;
            fn slowret(p : &mut Process) -> !;
        }
        if p.is(process::FastRet) {
            p.unset(process::FastRet);
            if log_switch {
                write("fastret to ");
                con::writeHex(p.regs.rip as uint);
                con::newline();
            }
            fastret(p);
        } else {
            if log_switch {
                write("slowret to ");
                con::writeHex(p.regs.rip as uint);
                con::newline();
            }
            slowret(p);
        }
    }

    fn syscall_return(&mut self, p: &mut Process, rax : uint) -> ! {
        p.regs.rax = rax;
        unsafe { self.switch_to(p); }
    }

    #[inline(never)]
    unsafe fn get_process<'a>(&self) -> &'a mut Process {
        &mut **(&self.process as *Option<&'static mut Process> as *Option<&'a mut Process> as **mut Process)
    }

    fn leave_proc(&mut self) {
        unsafe {
            let p = self.get_process();
            p.unset(process::Running);
            //self.process = None;
        }
    }
}

// NB: One of the funky guarantees that Rust gives/requires is that there is
// at most one &mut reference to the same thing at any one time. This function
// can't quite guarantee that...
pub fn cpu() -> &mut PerCpu {
    unsafe {
        let mut ret = 0;
        asm!("movq %gs:($0), $0" : "=r"(ret) : "0"(ret));
        return &mut *(ret as *mut PerCpu);
    }
}

#[lang="exchange_malloc"]
#[inline(never)]
pub fn xmalloc(size : uint, _align: uint) -> *mut u8 {
    malloc(size)
}

pub fn malloc(size : uint) -> *mut u8 {
    if size > 4096 {
        abort("oversized malloc");
    }
    return cpu().memory.alloc_frame_panic();
}

#[inline(never)]
pub fn alloc<T>() -> *mut T {
    malloc(size_of::<T>()) as *mut T
}

pub fn free<T>(p : *mut T) {
    if p.is_not_null() {
        cpu().memory.free_frame(p as *mut u8);
    }
}

#[lang="exchange_free"]
pub fn xfree(p : *mut u8, _size: uint, _align: uint) {
    free(p);
}

unsafe fn setup_msrs(gs : uint) {
    use x86::msr::*;
    use x86::rflags;
    use x86::efer;
    use x86::seg;
    #[allow(dead_code)]
    extern {
        fn syscall_entry_stub();
        fn syscall_entry_compat();
    }

    wrmsr(STAR, (seg::user_code32_base << 16) | seg::code);
    wrmsr(LSTAR, syscall_entry_stub as uint);
    wrmsr(CSTAR, syscall_entry_compat as uint);
    // FIXME: We want to clear a lot more flags - Direction for instance.
    // FreeBSD sets PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D
    wrmsr(FMASK, rflags::IF | rflags::VM);
    wrmsr(EFER, rdmsr(EFER) | efer::SCE | efer::NXE);
    wrmsr(GSBASE, gs);
}

#[lang="eh_personality"]
fn dummy() {}

#[inline(never)]
fn new_proc_simple(start : uint, end_unaligned : uint) -> *mut Process {
    let end = (end_unaligned + 0xfff) & !0xfff;
    let start_page = start & !0xfff;
    let aspace : *mut AddressSpace = unsafe { transmute(box AddressSpace::new()) };
    let ret : *mut Process = unsafe { transmute(box Process::new(aspace)) };
    unsafe {
        (*ret).regs.rsp = 0x100000;
        (*ret).regs.rip = 0x100000 + (start & 0xfff);
    }

    unsafe {
        use aspace::mapflag::*;
        // Stack at 1MB - 4kB (not executable)
        (*aspace).mapcard_set(0x0ff000, 0, 0, Anon | R | W);
        (*aspace).mapcard_set(0x100000, 0, start_page - 0x100000, Phys | R | X);
        (*aspace).mapcard_set(0x100000 + (end - start_page), 0, 0, 0);
        /*match (*ret).aspace().mapcard_find(0x100000) {
            Some(c) => {
                write("0x100000: ");
                con::writePHex(c.vaddr());
                write(" -> paddr ");
                con::writePHex(c.paddr(0x100000));
                con::newline();
            },
            None => {
                abort("mapcard we added is not there anymore");
            }
        }*/
    }
    return ret;
}

fn assoc_procs(p : &mut Process, i : uint, q : &mut Process, j : uint) {
    if log_assoc_procs {
        con::writeMutPtr(p);
        con::putc(':');
        con::writeUInt(i);
        write(" <-> ");
        con::writeUInt(j);
        con::putc(':');
        con::writeMutPtr(q);
        con::newline();
    }
    p.assoc_handles(j, q, i);
}

fn init_modules(cpu : &mut PerCpu) {
    let &info = start32::MultiBootInfo();
    if !info.has(mboot::Modules) {
        return;
    }
    let mut head = DList::empty();
    let mut count = 0;
    for m in info.modules(start32::PhysAddr).iter() {
        write("Module ");
        con::writeHex(m.start as uint);
        write("..");
        con::writeHex(m.end as uint);
        write(": ");
        con::writeCStr(start32::PhysAddr(m.string as uint));
        con::newline();

        head.append(new_proc_simple(m.start as uint, m.end as uint));
        count += 1;
    }
    con::writeUInt(count);
    con::newline();
    // Now all processes are in our list. We need to remove them before it
    // gets possible to make them runnable.
    let mut i = 0;
    for p in head.iter() {
        i += 1;
        // start iterating at j = i + 1, and q = p.next...
        let mut j = 0;
        for q in head.iter() {
            j += 1;
            if i < j {
                assoc_procs(p, i, q, j);
            }
        }
    }
    while match head.pop() {
        Some(p) => { cpu.queue(unsafe { &mut *p }); true },
        None => false
    } {}
}

pub fn dump_runqueue(queue: &DList<Process>) {
    let mut count = 0;
    for _ in queue.iter() {
        count += 1;
    }
    con::write("runqueue: ");
    con::writeUInt(count);
    con::newline();
    for p in queue.iter() {
        p.dump();
    }
}

#[no_mangle]
pub unsafe fn start64() -> ! {
    con::init(MutPhysAddr(0xb8000), 80, 25);
    con::clear();
    write("Hello World!\n");
    //writeMBInfo(start32::MultiBootInfo());

    x86::lgdt(start32::Gdtr());
    x86::ltr(x86::seg::tss64);

    idt::init();

    mem::global.init(start32::MultiBootInfo(), start32::MemoryStart());
    write("Memory initialized. ");
    mem::global.stat();

    let pcpu = PerCpu::new();
    let ref mut cpu = *pcpu;
    cpu.start();
    if mem_test {
        cpu.memory.test();
        mem::global.stat();
    }

    init_modules(cpu);
    //dump_runqueue(&cpu.runqueue);

//  let mut i = 0;
//  loop {
//      con.writeUInt(i);
//      con.putc('\n');
//      i += 1;
//  }
    cpu.run();
}
<commit_msg>Some manual worker/wrappering<commit_after>#![allow(ctypes)]
#![no_std]
#![no_main]
#![feature(globs)]
#![feature(asm)]
// Adding pub mod (another fix for this warning) increases footprint, so just
// disble it instead.
#![allow(visible_private_types)]

extern crate core;

use core::prelude::*;
use core::mem::*;

use aspace::AddressSpace;
use con::write;
use dlist::DList;
use process::Process;
use start32::MultiBootInfo;
//use start32::OrigMultiBootInfo;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;
use x86::idt;
pub use x86::idt::irq_entry;
pub use syscall::syscall;

mod aspace;
#[allow(dead_code)]
mod con;
mod dict;
mod dlist;
mod mboot;
mod mem;
mod process;
mod start32;
mod syscall;
pub mod util;
mod x86;

static log_assoc_procs : bool = false;
static log_page_fault : bool = false;
static log_switch : bool = false;
static mem_test : bool = false;

#[allow(dead_code)]
fn writeMBInfo(infop : *mboot::Info) {
    con::write("Multiboot info at ");
    con::writePtr(infop);
    con::putc('\n');

    let &info = unsafe { &*infop };
    con::write("Flags: ");
    con::writeHex(info.flags as uint);
    con::newline();

    if info.has(mboot::MemorySize) {
        con::writeUInt(info.mem_lower as uint);
        con::write("kB lower memory, ");
        con::writeUInt(info.mem_upper as uint);
        con::write("kB upper memory, ");
        con::writeUInt(((info.mem_lower + info.mem_upper + 1023) / 1024) as uint);
        con::write("MB total.\n");
    }
    if info.has(mboot::CommandLine) {
        let cmdline : *u8 = PhysAddr(info.cmdline as uint);
        con::write("Command line @");
        con::writePtr(cmdline);
        con::write(" (");
        con::writeHex(info.cmdline as uint);
        con::write(") \"");
        con::writeCStr(cmdline);
        con::write("\"\n");
    }
}

pub fn generic_irq_handler(p : &mut Process, vec : u8) {
    write("IRQ! vec=");
    con::writeUInt(vec as uint);
    con::newline();
    cpu().queue(p);
}

pub fn page_fault(p : &mut Process, error : uint) -> ! {
    mod pf_errors {
        #![allow(dead_code)]
        // Error code flags.
        pub static PRESENT : uint = 1;
        pub static WRITE : uint = 2;
        pub static USER : uint = 4;
        pub static RSVD : uint = 8;
        pub static INSTR : uint = 16;
    }

    if log_page_fault {
        write("page fault ");
        con::writeHex(error);
        write(" cr2=");
        con::writePHex(x86::cr2());
        write(" rip=");
        con::writePHex(p.regs.rip as uint);
        write(" in process ");
        con::writeMutPtr(p);
        con::newline();
    }

    if (error & pf_errors::USER) == 0 {
        abort("kernel page fault\n");
    }

    let back = p.aspace().find_add_backing(x86::cr2() & !0xfff);
    // FIXME should return e.g. Option<> so we can detect failures better than
    // just aborting.
    p.aspace().add_pte(back.vaddr(), back.pte());

    cpu().queue(p);
    unsafe { cpu().run(); }
}

pub fn handler_NM() {
    abort("NM");
}

pub fn idle() -> ! {
    loop {
        write("idle\n");
        unsafe { asm!("swapgs; sti; hlt; cli; swapgs" :::: "volatile"); } }
}

pub struct PerCpu {
    selfp : *mut PerCpu,
    stack : *mut u8,
    process : Option<&'static mut Process>,
    //

    // End of assembly-fixed fields.
    memory : mem::PerCpu,
    runqueue : DList<Process>,
}

impl PerCpu {
    unsafe fn new() -> *mut PerCpu {
        let mut mem = mem::PerCpu::new();
        let p : *mut PerCpu = mem.alloc_frame_panic();
        let stack : *mut u8 = mem.alloc_frame_panic();
        *p = PerCpu {
            selfp : p,
            stack : stack,
            memory : mem,
            runqueue : DList::empty(),
            process : None
        };
        return p
    }

    unsafe fn start(&mut self) {
        setup_msrs(self.selfp as uint);
    }

    fn queue(&mut self, p: &mut Process) {
        if !p.is_queued() {
            p.set(process::Queued);
            self.runqueue.append(p);
        }
    }

    unsafe fn run(&mut self) -> ! {
        match self.runqueue.pop() {
            Some(p) => self.switch_to(&mut *p),
            None => idle()
        }
    }

    unsafe fn switch_to(&mut self, p: &mut Process) -> ! {
        if log_switch {
            write("switch_to ");
            con::writeMutPtr(p as *mut Process);
            con::newline();
        }
        p.unset(process::Queued);
        p.set(process::Running);
        self.process = transmute(p as *mut Process);
        // TODO Check fpu_process, see if we need to set/reset TS bit in cr0
        x86::set_cr3(p.cr3);
        extern "C" {
            fn fastret(p : &mut Process) -> !;
            fn slowret(p : &mut Process) -> !;
        }
        if p.is(process::FastRet) {
            p.unset(process::FastRet);
            if log_switch {
                write("fastret to ");
                con::writeHex(p.regs.rip as uint);
                con::newline();
            }
            fastret(p);
        } else {
            if log_switch {
                write("slowret to ");
                con::writeHex(p.regs.rip as uint);
                con::newline();
            }
            slowret(p);
        }
    }

    fn syscall_return(&mut self, p: &mut Process, rax : uint) -> ! {
        p.regs.rax = rax;
        unsafe { self.switch_to(p); }
    }

    #[inline(never)]
    unsafe fn get_process<'a>(&self) -> &'a mut Process {
        &mut **(&self.process as *Option<&'static mut Process> as *Option<&'a mut Process> as **mut Process)
    }

    fn leave_proc(&mut self) {
        unsafe {
            let p = self.get_process();
            p.unset(process::Running);
            //self.process = None;
        }
    }
}

// NB: One of the funky guarantees that Rust gives/requires is that there is
// at most one &mut reference to the same thing at any one time. This function
// can't quite guarantee that...
pub fn cpu() -> &mut PerCpu {
    unsafe {
        let mut ret = 0;
        asm!("movq %gs:($0), $0" : "=r"(ret) : "0"(ret));
        return &mut *(ret as *mut PerCpu);
    }
}

#[lang="exchange_malloc"]
#[inline(always)]
pub fn xmalloc(size : uint, _align: uint) -> *mut u8 {
    malloc(size)
}

pub fn malloc(size : uint) -> *mut u8 {
    if size > 4096 {
        abort("oversized malloc");
    }
    #[inline(never)]
    fn malloc_() -> *mut u8 {
        cpu().memory.alloc_frame_panic()
    }
    malloc_()
}

#[inline(always)]
pub fn alloc<T>() -> *mut T {
    malloc(size_of::<T>()) as *mut T
}

pub fn free<T>(p : *mut T) {
    #[inline(never)]
    fn free_(p: *mut u8) {
        cpu().memory.free_frame(p);
    }
    if p.is_not_null() {
        free_(p as *mut u8);
    }
}

#[lang="exchange_free"]
#[inline(always)]
pub fn xfree(p : *mut u8, _size: uint, _align: uint) {
    free(p);
}

unsafe fn setup_msrs(gs : uint) {
    use x86::msr::*;
    use x86::rflags;
    use x86::efer;
    use x86::seg;
    #[allow(dead_code)]
    extern {
        fn syscall_entry_stub();
        fn syscall_entry_compat();
    }

    wrmsr(STAR, (seg::user_code32_base << 16) | seg::code);
    wrmsr(LSTAR, syscall_entry_stub as uint);
    wrmsr(CSTAR, syscall_entry_compat as uint);
    // FIXME: We want to clear a lot more flags - Direction for instance.
    // FreeBSD sets PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D
    wrmsr(FMASK, rflags::IF | rflags::VM);
    wrmsr(EFER, rdmsr(EFER) | efer::SCE | efer::NXE);
    wrmsr(GSBASE, gs);
}

#[lang="eh_personality"]
fn dummy() {}

#[inline(never)]
fn new_proc_simple(start : uint, end_unaligned : uint) -> *mut Process {
    let end = (end_unaligned + 0xfff) & !0xfff;
    let start_page = start & !0xfff;
    let aspace : *mut AddressSpace = unsafe { transmute(box AddressSpace::new()) };
    let ret : *mut Process = unsafe { transmute(box Process::new(aspace)) };
    unsafe {
        (*ret).regs.rsp = 0x100000;
        (*ret).regs.rip = 0x100000 + (start & 0xfff);
    }

    unsafe {
        use aspace::mapflag::*;
        // Stack at 1MB - 4kB (not executable)
        (*aspace).mapcard_set(0x0ff000, 0, 0, Anon | R | W);
        (*aspace).mapcard_set(0x100000, 0, start_page - 0x100000, Phys | R | X);
        (*aspace).mapcard_set(0x100000 + (end - start_page), 0, 0, 0);
        /*match (*ret).aspace().mapcard_find(0x100000) {
            Some(c) => {
                write("0x100000: ");
                con::writePHex(c.vaddr());
                write(" -> paddr ");
                con::writePHex(c.paddr(0x100000));
                con::newline();
            },
            None => {
                abort("mapcard we added is not there anymore");
            }
        }*/
    }
    return ret;
}

fn assoc_procs(p : &mut Process, i : uint, q : &mut Process, j : uint) {
    if log_assoc_procs {
        con::writeMutPtr(p);
        con::putc(':');
        con::writeUInt(i);
        write(" <-> ");
        con::writeUInt(j);
        con::putc(':');
        con::writeMutPtr(q);
        con::newline();
    }
    p.assoc_handles(j, q, i);
}

fn init_modules(cpu : &mut PerCpu) {
    let &info = start32::MultiBootInfo();
    if !info.has(mboot::Modules) {
        return;
    }
    let mut head = DList::empty();
    let mut count = 0;
    for m in info.modules(start32::PhysAddr).iter() {
        write("Module ");
        con::writeHex(m.start as uint);
        write("..");
        con::writeHex(m.end as uint);
        write(": ");
        con::writeCStr(start32::PhysAddr(m.string as uint));
        con::newline();

        head.append(new_proc_simple(m.start as uint, m.end as uint));
        count += 1;
    }
    con::writeUInt(count);
    con::newline();
    // Now all processes are in our list. We need to remove them before it
    // gets possible to make them runnable.
    let mut i = 0;
    for p in head.iter() {
        i += 1;
        // start iterating at j = i + 1, and q = p.next...
        let mut j = 0;
        for q in head.iter() {
            j += 1;
            if i < j {
                assoc_procs(p, i, q, j);
            }
        }
    }
    while match head.pop() {
        Some(p) => { cpu.queue(unsafe { &mut *p }); true },
        None => false
    } {}
}

pub fn dump_runqueue(queue: &DList<Process>) {
    let mut count = 0;
    for _ in queue.iter() {
        count += 1;
    }
    con::write("runqueue: ");
    con::writeUInt(count);
    con::newline();
    for p in queue.iter() {
        p.dump();
    }
}

#[no_mangle]
pub unsafe fn start64() -> ! {
    con::init(MutPhysAddr(0xb8000), 80, 25);
    con::clear();
    write("Hello World!\n");
    //writeMBInfo(start32::MultiBootInfo());

    x86::lgdt(start32::Gdtr());
    x86::ltr(x86::seg::tss64);

    idt::init();

    mem::global.init(start32::MultiBootInfo(), start32::MemoryStart());
    write("Memory initialized. ");
    mem::global.stat();

    let pcpu = PerCpu::new();
    let ref mut cpu = *pcpu;
    cpu.start();
    if mem_test {
        cpu.memory.test();
        mem::global.stat();
    }

    init_modules(cpu);
    //dump_runqueue(&cpu.runqueue);

//  let mut i = 0;
//  loop {
//      con.writeUInt(i);
//      con.putc('\n');
//      i += 1;
//  }
    cpu.run();
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;

use crate::conversion::{
    convert_error::{ConvertErrorWithContext, ErrorContext},
    error_reporter::report_any_error,
    parse::type_converter::Annotated,
};
use crate::{
    conversion::{
        api::{ApiDetail, ParseResults, TypedefKind, UnanalyzedApi},
        ConvertError,
    },
    types::Namespace,
    types::QualifiedName,
};
use autocxx_parser::TypeConfig;
use syn::{parse_quote, Fields, Item, Type, TypePath, UseTree};

use super::{super::utilities::generate_utilities, type_converter::TypeConverter};

use super::parse_foreign_mod::ParseForeignMod;

/// Parses a bindgen mod in order to understand the APIs within it.
pub(crate) struct ParseBindgen<'a> {
    type_config: &'a TypeConfig,
    results: ParseResults<'a>,
    /// Here we track the last struct which bindgen told us about.
    /// Any subsequent "extern 'C'" blocks are methods belonging to that type,
    /// even if the 'this' is actually recorded as void in the
    /// function signature.
    latest_virtual_this_type: Option<QualifiedName>,
}

impl<'a> ParseBindgen<'a> {
    pub(crate) fn new(type_config: &'a TypeConfig) -> Self {
        ParseBindgen {
            type_config,
            results: ParseResults {
                apis: Vec::new(),
                type_converter: TypeConverter::new(type_config),
            },
            latest_virtual_this_type: None,
        }
    }

    /// Parses items found in the `bindgen` output and returns a set of
    /// `Api`s together with some other data.
    pub(crate) fn parse_items(
        mut self,
        items: Vec<Item>,
        exclude_utilities: bool,
    ) -> Result<ParseResults<'a>, ConvertError> {
        let items = Self::find_items_in_root(items)?;
        if !exclude_utilities {
            generate_utilities(&mut self.results.apis);
        }
        let root_ns = Namespace::new();
        self.parse_mod_items(items, root_ns);
        self.confirm_all_generate_directives_obeyed()?;
        Ok(self.results)
    }

    fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
        for item in items {
            match item {
                Item::Mod(root_mod) => {
                    // With namespaces enabled, bindgen always puts everything
                    // in a mod called 'root'. We don't want to pass that
                    // onto cxx, so jump right into it.
                    assert!(root_mod.ident == "root");
                    if let Some((_, items)) = root_mod.content {
                        return Ok(items);
                    }
                }
                _ => return Err(ConvertError::UnexpectedOuterItem),
            }
        }
        Ok(Vec::new())
    }

    /// Interpret the bindgen-generated .rs for a particular
    /// mod, which corresponds to a C++ namespace.
    fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
        // This object maintains some state specific to this namespace, i.e.
        // this particular mod.
        let mut mod_converter = ParseForeignMod::new(ns.clone());
        let mut more_apis = Vec::new();
        for item in items {
            report_any_error(&ns, &mut more_apis, || {
                self.parse_item(item, &mut mod_converter, &ns)
            });
        }
        self.results.apis.append(&mut more_apis);
        mod_converter.finished(&mut self.results.apis);
    }

    fn parse_item(
        &mut self,
        item: Item,
        mod_converter: &mut ParseForeignMod,
        ns: &Namespace,
    ) -> Result<(), ConvertErrorWithContext> {
        match item {
            Item::ForeignMod(fm) => {
                mod_converter
                    .convert_foreign_mod_items(fm.items, self.latest_virtual_this_type.clone());
                Ok(())
            }
            Item::Struct(s) => {
                if s.ident.to_string().ends_with("__bindgen_vtable") {
                    return Ok(());
                }
                let tyname = QualifiedName::new(ns, s.ident.clone());
                let is_forward_declaration = Self::spot_forward_declaration(&s.fields);
                // cxx::bridge can't cope with type aliases to generic
                // types at the moment.
                self.parse_type(
                    tyname.clone(),
                    is_forward_declaration,
                    HashSet::new(),
                    Some(Item::Struct(s)),
                );
                self.latest_virtual_this_type = Some(tyname);
                Ok(())
            }
            Item::Enum(e) => {
                let tyname = QualifiedName::new(ns, e.ident.clone());
                self.parse_type(tyname, false, HashSet::new(), Some(Item::Enum(e)));
                Ok(())
            }
            Item::Impl(imp) => {
                // We *mostly* ignore all impl blocks generated by bindgen.
                // Methods also appear in 'extern "C"' blocks which
                // we will convert instead. At that time we'll also construct
                // synthetic impl blocks.
                // We do however record which methods were spotted, since
                // we have no other way of working out which functions are
                // static methods vs plain functions.
                mod_converter.convert_impl_items(imp);
                Ok(())
            }
            Item::Mod(itm) => {
                if let Some((_, items)) = itm.content {
                    let new_ns = ns.push(itm.ident.to_string());
                    self.parse_mod_items(items, new_ns);
                }
                Ok(())
            }
            Item::Use(use_item) => {
                let mut segs = Vec::new();
                let mut tree = &use_item.tree;
                loop {
                    match tree {
                        UseTree::Path(up) => {
                            segs.push(up.ident.clone());
                            tree = &up.tree;
                        }
                        UseTree::Name(un) if un.ident == "root" => break, // we do not add this to any API since we generate equivalent
                        // use statements in our codegen phase.
                        UseTree::Rename(urn) => {
                            let old_id = &urn.ident;
                            let new_id = &urn.rename;
                            let new_tyname = QualifiedName::new(ns, new_id.clone());
                            if segs.remove(0) != "self" {
                                panic!("Path didn't start with self");
                            }
                            if segs.remove(0) != "super" {
                                panic!("Path didn't start with self::super");
                            }
                            // This is similar to the path encountered within 'tree'
                            // but without the self::super prefix which is unhelpful
                            // in our output mod, because we prefer relative paths
                            // (we're nested in another mod)
                            let old_path: TypePath = parse_quote! {
                                #(#segs)::* :: #old_id
                            };
                            let old_tyname = QualifiedName::from_type_path(&old_path);
                            if new_tyname == old_tyname {
                                return Err(ConvertErrorWithContext(
                                    ConvertError::InfinitelyRecursiveTypedef(new_tyname),
                                    Some(ErrorContext::Item(new_id.clone())),
                                ));
                            }
                            self.results
                                .type_converter
                                .insert_typedef(new_tyname, Type::Path(old_path.clone()));
                            let mut deps = HashSet::new();
                            deps.insert(old_tyname);
                            self.results.apis.push(UnanalyzedApi {
                                name: QualifiedName::new(ns, new_id.clone()),
                                deps,
                                detail: ApiDetail::Typedef {
                                    payload: TypedefKind::Use(parse_quote! {
                                        pub use #old_path as #new_id;
                                    }),
                                },
                            });
                            break;
                        }
                        _ => {
                            return Err(ConvertErrorWithContext(
                                ConvertError::UnexpectedUseStatement(segs.into_iter().last()),
                                None,
                            ))
                        }
                    }
                }
                Ok(())
            }
            Item::Const(const_item) => {
                // The following puts this constant into
                // the global namespace which is bug
                // https://github.com/google/autocxx/issues/133
                self.results.apis.push(UnanalyzedApi {
                    name: QualifiedName::new(ns, const_item.ident.clone()),
                    deps: HashSet::new(),
                    detail: ApiDetail::Const { const_item },
                });
                Ok(())
            }
            Item::Type(mut ity) => {
                let tyname = QualifiedName::new(ns, ity.ident.clone());
                let type_conversion_results =
                    self.results
                        .type_converter
                        .convert_type(*ity.ty, ns, false, &HashSet::new());
                match type_conversion_results {
                    Err(ConvertError::OpaqueTypeFound) => {
                        self.add_opaque_type(tyname);
                        Ok(())
                    }
                    Err(err) => Err(ConvertErrorWithContext(
                        err,
                        Some(ErrorContext::Item(ity.ident.clone())),
                    )),
                    Ok(Annotated {
                        ty: syn::Type::Path(ref typ),
                        ..
                    }) if QualifiedName::from_type_path(typ) == tyname => {
                        Err(ConvertErrorWithContext(
                            ConvertError::InfinitelyRecursiveTypedef(tyname),
                            Some(ErrorContext::Item(ity.ident)),
                        ))
                    }
                    Ok(mut final_type) => {
                        ity.ty = Box::new(final_type.ty.clone());
                        self.results
                            .type_converter
                            .insert_typedef(tyname, final_type.ty);
                        self.results.apis.append(&mut final_type.extra_apis);
                        self.results.apis.push(UnanalyzedApi {
                            name: QualifiedName::new(ns, ity.ident.clone()),
                            deps: final_type.types_encountered,
                            detail: ApiDetail::Typedef {
                                payload: TypedefKind::Type(ity),
                            },
                        });
                        Ok(())
                    }
                }
            }
            _ => Err(ConvertErrorWithContext(
                ConvertError::UnexpectedItemInMod,
                None,
            )),
        }
    }

    fn spot_forward_declaration(s: &Fields) -> bool {
        s.iter()
            .filter_map(|f| f.ident.as_ref())
            .any(|id| id == "_unused")
    }

    fn add_opaque_type(&mut self, name: QualifiedName) {
        self.results.apis.push(UnanalyzedApi {
            name,
            deps: HashSet::new(),
            detail: ApiDetail::OpaqueTypedef,
        });
    }

    /// Record the Api for a type, e.g. enum or struct.
    /// Code generated includes the bindgen entry itself,
    /// various entries for the cxx::bridge to ensure cxx
    /// is aware of the type, and 'use' statements for the final
    /// output mod hierarchy. All are stored in the Api which
    /// this adds.
    fn parse_type(
        &mut self,
        name: QualifiedName,
        is_forward_declaration: bool,
        deps: HashSet<QualifiedName>,
        bindgen_mod_item: Option<Item>,
    ) {
        if self.type_config.is_on_blocklist(&name.to_cpp_name()) {
            return;
        }
        let api = UnanalyzedApi {
            name: name.clone(),
            deps,
            detail: if is_forward_declaration {
                ApiDetail::ForwardDeclaration
            } else {
                ApiDetail::Type {
                    bindgen_mod_item,
                    analysis: (),
                }
            },
        };
        self.results.apis.push(api);
        self.results.type_converter.push(name);
    }

    fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
        let api_names: HashSet<_> = self
            .results
            .apis
            .iter()
            .map(|api| api.typename().to_cpp_name())
            .collect();
        for generate_directive in self.type_config.allowlist() {
            if !api_names.contains(generate_directive) {
                return Err(ConvertError::DidNotGenerateAnything(
                    generate_directive.into(),
                ));
            }
        }
        Ok(())
    }
}
<commit_msg>Remove outdated note about #133<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;

use crate::conversion::{
    convert_error::{ConvertErrorWithContext, ErrorContext},
    error_reporter::report_any_error,
    parse::type_converter::Annotated,
};
use crate::{
    conversion::{
        api::{ApiDetail, ParseResults, TypedefKind, UnanalyzedApi},
        ConvertError,
    },
    types::Namespace,
    types::QualifiedName,
};
use autocxx_parser::TypeConfig;
use syn::{parse_quote, Fields, Item, Type, TypePath, UseTree};

use super::{super::utilities::generate_utilities, type_converter::TypeConverter};

use super::parse_foreign_mod::ParseForeignMod;

/// Parses a bindgen mod in order to understand the APIs within it.
pub(crate) struct ParseBindgen<'a> {
    type_config: &'a TypeConfig,
    results: ParseResults<'a>,
    /// Here we track the last struct which bindgen told us about.
    /// Any subsequent "extern 'C'" blocks are methods belonging to that type,
    /// even if the 'this' is actually recorded as void in the
    /// function signature.
    latest_virtual_this_type: Option<QualifiedName>,
}

impl<'a> ParseBindgen<'a> {
    pub(crate) fn new(type_config: &'a TypeConfig) -> Self {
        ParseBindgen {
            type_config,
            results: ParseResults {
                apis: Vec::new(),
                type_converter: TypeConverter::new(type_config),
            },
            latest_virtual_this_type: None,
        }
    }

    /// Parses items found in the `bindgen` output and returns a set of
    /// `Api`s together with some other data.
    pub(crate) fn parse_items(
        mut self,
        items: Vec<Item>,
        exclude_utilities: bool,
    ) -> Result<ParseResults<'a>, ConvertError> {
        let items = Self::find_items_in_root(items)?;
        if !exclude_utilities {
            generate_utilities(&mut self.results.apis);
        }
        let root_ns = Namespace::new();
        self.parse_mod_items(items, root_ns);
        self.confirm_all_generate_directives_obeyed()?;
        Ok(self.results)
    }

    fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
        for item in items {
            match item {
                Item::Mod(root_mod) => {
                    // With namespaces enabled, bindgen always puts everything
                    // in a mod called 'root'. We don't want to pass that
                    // onto cxx, so jump right into it.
                    assert!(root_mod.ident == "root");
                    if let Some((_, items)) = root_mod.content {
                        return Ok(items);
                    }
                }
                _ => return Err(ConvertError::UnexpectedOuterItem),
            }
        }
        Ok(Vec::new())
    }

    /// Interpret the bindgen-generated .rs for a particular
    /// mod, which corresponds to a C++ namespace.
    fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
        // This object maintains some state specific to this namespace, i.e.
        // this particular mod.
        let mut mod_converter = ParseForeignMod::new(ns.clone());
        let mut more_apis = Vec::new();
        for item in items {
            report_any_error(&ns, &mut more_apis, || {
                self.parse_item(item, &mut mod_converter, &ns)
            });
        }
        self.results.apis.append(&mut more_apis);
        mod_converter.finished(&mut self.results.apis);
    }

    fn parse_item(
        &mut self,
        item: Item,
        mod_converter: &mut ParseForeignMod,
        ns: &Namespace,
    ) -> Result<(), ConvertErrorWithContext> {
        match item {
            Item::ForeignMod(fm) => {
                mod_converter
                    .convert_foreign_mod_items(fm.items, self.latest_virtual_this_type.clone());
                Ok(())
            }
            Item::Struct(s) => {
                if s.ident.to_string().ends_with("__bindgen_vtable") {
                    return Ok(());
                }
                let tyname = QualifiedName::new(ns, s.ident.clone());
                let is_forward_declaration = Self::spot_forward_declaration(&s.fields);
                // cxx::bridge can't cope with type aliases to generic
                // types at the moment.
                self.parse_type(
                    tyname.clone(),
                    is_forward_declaration,
                    HashSet::new(),
                    Some(Item::Struct(s)),
                );
                self.latest_virtual_this_type = Some(tyname);
                Ok(())
            }
            Item::Enum(e) => {
                let tyname = QualifiedName::new(ns, e.ident.clone());
                self.parse_type(tyname, false, HashSet::new(), Some(Item::Enum(e)));
                Ok(())
            }
            Item::Impl(imp) => {
                // We *mostly* ignore all impl blocks generated by bindgen.
                // Methods also appear in 'extern "C"' blocks which
                // we will convert instead. At that time we'll also construct
                // synthetic impl blocks.
                // We do however record which methods were spotted, since
                // we have no other way of working out which functions are
                // static methods vs plain functions.
                mod_converter.convert_impl_items(imp);
                Ok(())
            }
            Item::Mod(itm) => {
                if let Some((_, items)) = itm.content {
                    let new_ns = ns.push(itm.ident.to_string());
                    self.parse_mod_items(items, new_ns);
                }
                Ok(())
            }
            Item::Use(use_item) => {
                let mut segs = Vec::new();
                let mut tree = &use_item.tree;
                loop {
                    match tree {
                        UseTree::Path(up) => {
                            segs.push(up.ident.clone());
                            tree = &up.tree;
                        }
                        UseTree::Name(un) if un.ident == "root" => break, // we do not add this to any API since we generate equivalent
                        // use statements in our codegen phase.
                        UseTree::Rename(urn) => {
                            let old_id = &urn.ident;
                            let new_id = &urn.rename;
                            let new_tyname = QualifiedName::new(ns, new_id.clone());
                            if segs.remove(0) != "self" {
                                panic!("Path didn't start with self");
                            }
                            if segs.remove(0) != "super" {
                                panic!("Path didn't start with self::super");
                            }
                            // This is similar to the path encountered within 'tree'
                            // but without the self::super prefix which is unhelpful
                            // in our output mod, because we prefer relative paths
                            // (we're nested in another mod)
                            let old_path: TypePath = parse_quote! {
                                #(#segs)::* :: #old_id
                            };
                            let old_tyname = QualifiedName::from_type_path(&old_path);
                            if new_tyname == old_tyname {
                                return Err(ConvertErrorWithContext(
                                    ConvertError::InfinitelyRecursiveTypedef(new_tyname),
                                    Some(ErrorContext::Item(new_id.clone())),
                                ));
                            }
                            self.results
                                .type_converter
                                .insert_typedef(new_tyname, Type::Path(old_path.clone()));
                            let mut deps = HashSet::new();
                            deps.insert(old_tyname);
                            self.results.apis.push(UnanalyzedApi {
                                name: QualifiedName::new(ns, new_id.clone()),
                                deps,
                                detail: ApiDetail::Typedef {
                                    payload: TypedefKind::Use(parse_quote! {
                                        pub use #old_path as #new_id;
                                    }),
                                },
                            });
                            break;
                        }
                        _ => {
                            return Err(ConvertErrorWithContext(
                                ConvertError::UnexpectedUseStatement(segs.into_iter().last()),
                                None,
                            ))
                        }
                    }
                }
                Ok(())
            }
            Item::Const(const_item) => {
                self.results.apis.push(UnanalyzedApi {
                    name: QualifiedName::new(ns, const_item.ident.clone()),
                    deps: HashSet::new(),
                    detail: ApiDetail::Const { const_item },
                });
                Ok(())
            }
            Item::Type(mut ity) => {
                let tyname = QualifiedName::new(ns, ity.ident.clone());
                let type_conversion_results =
                    self.results
                        .type_converter
                        .convert_type(*ity.ty, ns, false, &HashSet::new());
                match type_conversion_results {
                    Err(ConvertError::OpaqueTypeFound) => {
                        self.add_opaque_type(tyname);
                        Ok(())
                    }
                    Err(err) => Err(ConvertErrorWithContext(
                        err,
                        Some(ErrorContext::Item(ity.ident.clone())),
                    )),
                    Ok(Annotated {
                        ty: syn::Type::Path(ref typ),
                        ..
                    }) if QualifiedName::from_type_path(typ) == tyname => {
                        Err(ConvertErrorWithContext(
                            ConvertError::InfinitelyRecursiveTypedef(tyname),
                            Some(ErrorContext::Item(ity.ident)),
                        ))
                    }
                    Ok(mut final_type) => {
                        ity.ty = Box::new(final_type.ty.clone());
                        self.results
                            .type_converter
                            .insert_typedef(tyname, final_type.ty);
                        self.results.apis.append(&mut final_type.extra_apis);
                        self.results.apis.push(UnanalyzedApi {
                            name: QualifiedName::new(ns, ity.ident.clone()),
                            deps: final_type.types_encountered,
                            detail: ApiDetail::Typedef {
                                payload: TypedefKind::Type(ity),
                            },
                        });
                        Ok(())
                    }
                }
            }
            _ => Err(ConvertErrorWithContext(
                ConvertError::UnexpectedItemInMod,
                None,
            )),
        }
    }

    fn spot_forward_declaration(s: &Fields) -> bool {
        s.iter()
            .filter_map(|f| f.ident.as_ref())
            .any(|id| id == "_unused")
    }

    fn add_opaque_type(&mut self, name: QualifiedName) {
        self.results.apis.push(UnanalyzedApi {
            name,
            deps: HashSet::new(),
            detail: ApiDetail::OpaqueTypedef,
        });
    }

    /// Record the Api for a type, e.g. enum or struct.
    /// Code generated includes the bindgen entry itself,
    /// various entries for the cxx::bridge to ensure cxx
    /// is aware of the type, and 'use' statements for the final
    /// output mod hierarchy. All are stored in the Api which
    /// this adds.
    fn parse_type(
        &mut self,
        name: QualifiedName,
        is_forward_declaration: bool,
        deps: HashSet<QualifiedName>,
        bindgen_mod_item: Option<Item>,
    ) {
        if self.type_config.is_on_blocklist(&name.to_cpp_name()) {
            return;
        }
        let api = UnanalyzedApi {
            name: name.clone(),
            deps,
            detail: if is_forward_declaration {
                ApiDetail::ForwardDeclaration
            } else {
                ApiDetail::Type {
                    bindgen_mod_item,
                    analysis: (),
                }
            },
        };
        self.results.apis.push(api);
        self.results.type_converter.push(name);
    }

    fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
        let api_names: HashSet<_> = self
            .results
            .apis
            .iter()
            .map(|api| api.typename().to_cpp_name())
            .collect();
        for generate_directive in self.type_config.allowlist() {
            if !api_names.contains(generate_directive) {
                return Err(ConvertError::DidNotGenerateAnything(
                    generate_directive.into(),
                ));
            }
        }
        Ok(())
    }
}
<|endoftext|>


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

// ignore-arm
// ignore-aarch64
// ignore-wasm
// ignore-emscripten
// ignore-mips
// ignore-mips64
// gate-test-sse4a_target_feature
// gate-test-powerpc_target_feature
// gate-test-avx512_target_feature
// gate-test-tbm_target_feature
// gate-test-arm_target_feature
// gate-test-aarch64_target_feature
// gate-test-hexagon_target_feature
// gate-test-mips_target_feature
// gate-test-mmx_target_feature
// min-llvm-version 6.0

#[target_feature(enable = "avx512bw")]
//~^ ERROR: currently unstable
unsafe fn foo() {
}

fn main() {}
<commit_msg>add feature-gate test<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-arm
// ignore-aarch64
// ignore-wasm
// ignore-emscripten
// ignore-mips
// ignore-mips64
// gate-test-sse4a_target_feature
// gate-test-powerpc_target_feature
// gate-test-avx512_target_feature
// gate-test-tbm_target_feature
// gate-test-arm_target_feature
// gate-test-aarch64_target_feature
// gate-test-hexagon_target_feature
// gate-test-mips_target_feature
// gate-test-mmx_target_feature
// gate-test-wasm_target_feature
// min-llvm-version 6.0

#[target_feature(enable = "avx512bw")]
//~^ ERROR: currently unstable
unsafe fn foo() {
}

fn main() {}
<|endoftext|>


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

// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
// Consider unify the read2() in libstd, cargo and this to prevent further code duplication.

pub use self::imp::read2;

#[cfg(not(any(unix, windows)))]
mod imp {
    use std::io::{self, Read};
    use std::process::{ChildStdout, ChildStderr};

    pub fn read2(out_pipe: ChildStdout,
                 err_pipe: ChildStderr,
                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
        let mut buffer = Vec::new();
        out_pipe.read_to_end(&mut buffer)?;
        data(true, &mut buffer, true);
        buffer.clear();
        err_pipe.read_to_end(&mut buffer)?;
        data(false, &mut buffer, true);
        Ok(())
    }
}

#[cfg(unix)]
mod imp {
    use std::io::prelude::*;
    use std::io;
    use std::mem;
    use std::os::unix::prelude::*;
    use std::process::{ChildStdout, ChildStderr};
    use libc;

    pub fn read2(mut out_pipe: ChildStdout,
                 mut err_pipe: ChildStderr,
                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
        unsafe {
            libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
            libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
        }

        let mut out_done = false;
        let mut err_done = false;
        let mut out = Vec::new();
        let mut err = Vec::new();

        let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
        fds[0].fd = out_pipe.as_raw_fd();
        fds[0].events = libc::POLLIN;
        fds[1].fd = err_pipe.as_raw_fd();
        fds[1].events = libc::POLLIN;
        loop {
            // wait for either pipe to become readable using `select`
            let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) };
            if r == -1 {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::Interrupted {
                    continue
                }
                return Err(err)
            }

            // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
            // EAGAIN. If we hit EOF, then this will happen because the underlying
            // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
            // this case we flip the other fd back into blocking mode and read
            // whatever's leftover on that file descriptor.
            let handle = |res: io::Result<_>| {
                match res {
                    Ok(_) => Ok(true),
                    Err(e) => {
                        if e.kind() == io::ErrorKind::WouldBlock {
                            Ok(false)
                        } else {
                            Err(e)
                        }
                    }
                }
            };
            if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
                out_done = true;
            }
            data(true, &mut out, out_done);
            if !err_done && fds[1].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
                err_done = true;
            }
            data(false, &mut err, err_done);

            if out_done && err_done {
                return Ok(())
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate miow;
    extern crate winapi;

    use std::io;
    use std::os::windows::prelude::*;
    use std::process::{ChildStdout, ChildStderr};
    use std::slice;

    use self::miow::iocp::{CompletionPort, CompletionStatus};
    use self::miow::pipe::NamedPipe;
    use self::miow::Overlapped;
    use self::winapi::ERROR_BROKEN_PIPE;

    struct Pipe<'a> {
        dst: &'a mut Vec<u8>,
        overlapped: Overlapped,
        pipe: NamedPipe,
        done: bool,
    }

    pub fn read2(out_pipe: ChildStdout,
                 err_pipe: ChildStderr,
                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
        let mut out = Vec::new();
        let mut err = Vec::new();

        let port = CompletionPort::new(1)?;
        port.add_handle(0, &out_pipe)?;
        port.add_handle(1, &err_pipe)?;

        unsafe {
            let mut out_pipe = Pipe::new(out_pipe, &mut out);
            let mut err_pipe = Pipe::new(err_pipe, &mut err);

            out_pipe.read()?;
            err_pipe.read()?;

            let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];

            while !out_pipe.done || !err_pipe.done {
                for status in port.get_many(&mut status, None)? {
                    if status.token() == 0 {
                        out_pipe.complete(status);
                        data(true, out_pipe.dst, out_pipe.done);
                        out_pipe.read()?;
                    } else {
                        err_pipe.complete(status);
                        data(false, err_pipe.dst, err_pipe.done);
                        err_pipe.read()?;
                    }
                }
            }

            Ok(())
        }
    }

    impl<'a> Pipe<'a> {
        unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
            Pipe {
                dst: dst,
                pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
                overlapped: Overlapped::zero(),
                done: false,
            }
        }

        unsafe fn read(&mut self) -> io::Result<()> {
            let dst = slice_to_end(self.dst);
            match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
                Ok(_) => Ok(()),
                Err(e) => {
                    if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
                        self.done = true;
                        Ok(())
                    } else {
                        Err(e)
                    }
                }
            }
        }

        unsafe fn complete(&mut self, status: &CompletionStatus) {
            let prev = self.dst.len();
            self.dst.set_len(prev + status.bytes_transferred() as usize);
            if status.bytes_transferred() == 0 {
                self.done = true;
            }
        }
    }

    unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
        if v.capacity() == 0 {
            v.reserve(16);
        }
        if v.capacity() == v.len() {
            v.reserve(1);
        }
        slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
                                  v.capacity() - v.len())
    }
}
<commit_msg>Rollup merge of #48165 - alexcrichton:update-read2, r=Mark-Simulacrum<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
// Consider unify the read2() in libstd, cargo and this to prevent further code duplication.

pub use self::imp::read2;

#[cfg(not(any(unix, windows)))]
mod imp {
    use std::io::{self, Read};
    use std::process::{ChildStdout, ChildStderr};

    pub fn read2(out_pipe: ChildStdout,
                 err_pipe: ChildStderr,
                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
        let mut buffer = Vec::new();
        out_pipe.read_to_end(&mut buffer)?;
        data(true, &mut buffer, true);
        buffer.clear();
        err_pipe.read_to_end(&mut buffer)?;
        data(false, &mut buffer, true);
        Ok(())
    }
}

#[cfg(unix)]
mod imp {
    use std::io::prelude::*;
    use std::io;
    use std::mem;
    use std::os::unix::prelude::*;
    use std::process::{ChildStdout, ChildStderr};
    use libc;

    pub fn read2(mut out_pipe: ChildStdout,
                 mut err_pipe: ChildStderr,
                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
        unsafe {
            libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
            libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
        }

        let mut out_done = false;
        let mut err_done = false;
        let mut out = Vec::new();
        let mut err = Vec::new();

        let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
        fds[0].fd = out_pipe.as_raw_fd();
        fds[0].events = libc::POLLIN;
        fds[1].fd = err_pipe.as_raw_fd();
        fds[1].events = libc::POLLIN;
        let mut nfds = 2;
        let mut errfd = 1;

        while nfds > 0 {
            // wait for either pipe to become readable using `select`
            let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
            if r == -1 {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::Interrupted {
                    continue
                }
                return Err(err)
            }

            // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
            // EAGAIN. If we hit EOF, then this will happen because the underlying
            // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
            // this case we flip the other fd back into blocking mode and read
            // whatever's leftover on that file descriptor.
            let handle = |res: io::Result<_>| {
                match res {
                    Ok(_) => Ok(true),
                    Err(e) => {
                        if e.kind() == io::ErrorKind::WouldBlock {
                            Ok(false)
                        } else {
                            Err(e)
                        }
                    }
                }
            };
            if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
                err_done = true;
                nfds -= 1;
            }
            data(false, &mut err, err_done);
            if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
                out_done = true;
                fds[0].fd = err_pipe.as_raw_fd();
                errfd = 0;
                nfds -= 1;
            }
            data(true, &mut out, out_done);
        }
        Ok(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate miow;
    extern crate winapi;

    use std::io;
    use std::os::windows::prelude::*;
    use std::process::{ChildStdout, ChildStderr};
    use std::slice;

    use self::miow::iocp::{CompletionPort, CompletionStatus};
    use self::miow::pipe::NamedPipe;
    use self::miow::Overlapped;
    use self::winapi::ERROR_BROKEN_PIPE;

    struct Pipe<'a> {
        dst: &'a mut Vec<u8>,
        overlapped: Overlapped,
        pipe: NamedPipe,
        done: bool,
    }

    pub fn read2(out_pipe: ChildStdout,
                 err_pipe: ChildStderr,
                 data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
        let mut out = Vec::new();
        let mut err = Vec::new();

        let port = CompletionPort::new(1)?;
        port.add_handle(0, &out_pipe)?;
        port.add_handle(1, &err_pipe)?;

        unsafe {
            let mut out_pipe = Pipe::new(out_pipe, &mut out);
            let mut err_pipe = Pipe::new(err_pipe, &mut err);

            out_pipe.read()?;
            err_pipe.read()?;

            let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];

            while !out_pipe.done || !err_pipe.done {
                for status in port.get_many(&mut status, None)? {
                    if status.token() == 0 {
                        out_pipe.complete(status);
                        data(true, out_pipe.dst, out_pipe.done);
                        out_pipe.read()?;
                    } else {
                        err_pipe.complete(status);
                        data(false, err_pipe.dst, err_pipe.done);
                        err_pipe.read()?;
                    }
                }
            }

            Ok(())
        }
    }

    impl<'a> Pipe<'a> {
        unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
            Pipe {
                dst: dst,
                pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
                overlapped: Overlapped::zero(),
                done: false,
            }
        }

        unsafe fn read(&mut self) -> io::Result<()> {
            let dst = slice_to_end(self.dst);
            match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
                Ok(_) => Ok(()),
                Err(e) => {
                    if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
                        self.done = true;
                        Ok(())
                    } else {
                        Err(e)
                    }
                }
            }
        }

        unsafe fn complete(&mut self, status: &CompletionStatus) {
            let prev = self.dst.len();
            self.dst.set_len(prev + status.bytes_transferred() as usize);
            if status.bytes_transferred() == 0 {
                self.done = true;
            }
        }
    }

    unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
        if v.capacity() == 0 {
            v.reserve(16);
        }
        if v.capacity() == v.len() {
            v.reserve(1);
        }
        slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
                                  v.capacity() - v.len())
    }
}
<|endoftext|>