<commit_before>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

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

    http://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.
==============================================================================*/

//! Log directories on local disk.

use log::{error, warn};
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufReader};
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

use crate::logdir::{EventFileBuf, Logdir, EVENT_FILE_BASENAME_INFIX};
use crate::types::Run;

/// A log directory on local disk.
pub struct DiskLogdir {
    root: PathBuf,
}

impl DiskLogdir {
    /// Creates a `DiskLogdir` with the given root directory.
    pub fn new(root: PathBuf) -> Self {
        DiskLogdir { root }
    }
}

impl Logdir for DiskLogdir {
    type File = BufReader<File>;

    fn discover(&self) -> io::Result<HashMap<Run, Vec<EventFileBuf>>> {
        let mut run_map: HashMap<Run, Vec<EventFileBuf>> = HashMap::new();
        let walker = WalkDir::new(&self.root)
            .sort_by(|a, b| a.file_name().cmp(b.file_name()))
            .follow_links(true);
        for walkdir_item in walker {
            let dirent = match walkdir_item {
                Ok(dirent) => dirent,
                Err(e) => {
                    error!("While walking log directory: {}", e);
                    continue;
                }
            };
            if !dirent.file_type().is_file() {
                continue;
            }
            let filename = dirent.file_name().to_string_lossy();
            if !filename.contains(EVENT_FILE_BASENAME_INFIX) {
                continue;
            }
            let run_dir = match dirent.path().parent() {
                Some(parent) => parent,
                None => {
                    // I don't know of any circumstance where this can happen, but I would believe
                    // that some weird filesystem can hit it, so just proceed.
                    warn!(
                        "Path {} is a file but has no parent",
                        dirent.path().display()
                    );
                    continue;
                }
            };
            let mut run_relpath = match run_dir.strip_prefix(&self.root) {
                Ok(rp) => rp.to_path_buf(),
                Err(_) => {
                    error!(
                        "Log directory {} is not a prefix of run directory {}",
                        &self.root.display(),
                        &run_dir.display(),
                    );
                    continue;
                }
            };
            // Render the root run as ".", not "".
            if run_relpath == Path::new("") {
                run_relpath.push(".");
            }
            let run = Run(run_relpath.display().to_string());
            run_map
                .entry(run)
                .or_default()
                .push(EventFileBuf(dirent.into_path()));
        }
        Ok(run_map)
    }

    fn open(&self, path: &EventFileBuf) -> io::Result<Self::File> {
        File::open(&path.0).map(BufReader::new)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::{Read, Write};

    fn test_in_logdir(logdir: &Path) -> std::io::Result<()> {
        let run_dir = logdir.join("train");
        std::fs::create_dir(&run_dir)?;
        {
            let mut f = File::create(run_dir.join("foo.tfevents.123"))?;
            f.write_all(b"hello")?;
            f.sync_all()?;
        }

        let disk_logdir = DiskLogdir::new(logdir.to_path_buf());
        let discoveries = disk_logdir.discover()?;
        assert_eq!(discoveries.len(), 1, "{:?}", discoveries);
        let train_event_files = &discoveries[&Run("train".to_string())];
        assert_eq!(train_event_files.len(), 1, "{:?}", train_event_files);

        let mut event_file = disk_logdir.open(&train_event_files[0])?;
        let mut contents = String::new();
        event_file.read_to_string(&mut contents)?;
        assert_eq!(contents, "hello");

        Ok(())
    }

    #[test]
    fn test_absolute() -> std::io::Result<()> {
        let tmpdir = tempfile::tempdir_in(".")?;
        let logdir = tmpdir.path();
        assert!(logdir.is_absolute(), "expected absolute: {:?}", logdir);
        test_in_logdir(logdir)
    }

    #[test]
    fn test_relative() -> std::io::Result<()> {
        let tmpdir = tempfile::tempdir_in(".")?;
        let logdir = tmpdir
            .path()
            .strip_prefix(std::env::current_dir()?)
            .expect("tmpdir not under $PWD");
        assert!(logdir.is_relative(), "expected relative: {:?}", logdir);
        test_in_logdir(logdir)
    }
}
<commit_msg>rust: downgrade logdir traversal errors (#4837)<commit_after>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

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

    http://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.
==============================================================================*/

//! Log directories on local disk.

use log::{error, info, warn};
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufReader};
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

use crate::logdir::{EventFileBuf, Logdir, EVENT_FILE_BASENAME_INFIX};
use crate::types::Run;

/// A log directory on local disk.
pub struct DiskLogdir {
    root: PathBuf,
}

impl DiskLogdir {
    /// Creates a `DiskLogdir` with the given root directory.
    pub fn new(root: PathBuf) -> Self {
        DiskLogdir { root }
    }
}

impl Logdir for DiskLogdir {
    type File = BufReader<File>;

    fn discover(&self) -> io::Result<HashMap<Run, Vec<EventFileBuf>>> {
        let mut run_map: HashMap<Run, Vec<EventFileBuf>> = HashMap::new();
        let walker = WalkDir::new(&self.root)
            .sort_by(|a, b| a.file_name().cmp(b.file_name()))
            .follow_links(true);
        for walkdir_item in walker {
            let dirent = match walkdir_item {
                Ok(dirent) => dirent,
                Err(e) => {
                    // TensorBoard traditionally doesn't complain loudly about non-existent
                    // directories, since the logdir may be created after TensorBoard starts.
                    if e.io_error()
                        .map_or(false, |e| e.kind() == io::ErrorKind::NotFound)
                    {
                        info!("While walking log directory: {}", e);
                    } else {
                        warn!("While walking log directory: {}", e);
                    }
                    continue;
                }
            };
            if !dirent.file_type().is_file() {
                continue;
            }
            let filename = dirent.file_name().to_string_lossy();
            if !filename.contains(EVENT_FILE_BASENAME_INFIX) {
                continue;
            }
            let run_dir = match dirent.path().parent() {
                Some(parent) => parent,
                None => {
                    // I don't know of any circumstance where this can happen, but I would believe
                    // that some weird filesystem can hit it, so just proceed.
                    warn!(
                        "Path {} is a file but has no parent",
                        dirent.path().display()
                    );
                    continue;
                }
            };
            let mut run_relpath = match run_dir.strip_prefix(&self.root) {
                Ok(rp) => rp.to_path_buf(),
                Err(_) => {
                    error!(
                        "Log directory {} is not a prefix of run directory {}",
                        &self.root.display(),
                        &run_dir.display(),
                    );
                    continue;
                }
            };
            // Render the root run as ".", not "".
            if run_relpath == Path::new("") {
                run_relpath.push(".");
            }
            let run = Run(run_relpath.display().to_string());
            run_map
                .entry(run)
                .or_default()
                .push(EventFileBuf(dirent.into_path()));
        }
        Ok(run_map)
    }

    fn open(&self, path: &EventFileBuf) -> io::Result<Self::File> {
        File::open(&path.0).map(BufReader::new)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::{Read, Write};

    fn test_in_logdir(logdir: &Path) -> std::io::Result<()> {
        let run_dir = logdir.join("train");
        std::fs::create_dir(&run_dir)?;
        {
            let mut f = File::create(run_dir.join("foo.tfevents.123"))?;
            f.write_all(b"hello")?;
            f.sync_all()?;
        }

        let disk_logdir = DiskLogdir::new(logdir.to_path_buf());
        let discoveries = disk_logdir.discover()?;
        assert_eq!(discoveries.len(), 1, "{:?}", discoveries);
        let train_event_files = &discoveries[&Run("train".to_string())];
        assert_eq!(train_event_files.len(), 1, "{:?}", train_event_files);

        let mut event_file = disk_logdir.open(&train_event_files[0])?;
        let mut contents = String::new();
        event_file.read_to_string(&mut contents)?;
        assert_eq!(contents, "hello");

        Ok(())
    }

    #[test]
    fn test_absolute() -> std::io::Result<()> {
        let tmpdir = tempfile::tempdir_in(".")?;
        let logdir = tmpdir.path();
        assert!(logdir.is_absolute(), "expected absolute: {:?}", logdir);
        test_in_logdir(logdir)
    }

    #[test]
    fn test_relative() -> std::io::Result<()> {
        let tmpdir = tempfile::tempdir_in(".")?;
        let logdir = tmpdir
            .path()
            .strip_prefix(std::env::current_dir()?)
            .expect("tmpdir not under $PWD");
        assert!(logdir.is_relative(), "expected relative: {:?}", logdir);
        test_in_logdir(logdir)
    }
}
<|endoftext|>


<commit_before>
use std::fs::File;
use byteorder::{BigEndian, ReadBytesExt};
use std::io;
use std::io::{Error, ErrorKind, Seek, Read};

const HUNK_HEADER: u32 = 1011;
// hunk types
const HUNK_UNIT: u32 = 999;
const HUNK_NAME: u32 = 1000;
const HUNK_CODE: u32 = 1001;
const HUNK_DATA: u32 = 1002;
const HUNK_BSS: u32 = 1003;
const HUNK_RELOC32: u32 = 1004;
const HUNK_DEBUG: u32 = 1009;
const HUNK_SYMBOL: u32 = 1008;
const HUNK_END: u32 = 1010;

/*
const HUNK_ABSRELOC32: u32 = HUNK_RELOC32;
const HUNK_RELOC16: u32 = 1005;
const HUNK_RELRELOC16 :u32 = HUNK_RELOC16;
const HUNK_RELOC8: u32 = 1006;
const HUNK_RELRELOC8: u32 = HUNK_RELOC8;

const HUNK_OVERLAY: u32 = 1013;
const HUNK_BREAK: u32 = 1014;

const HUNK_DREL32: u32 = 1015;
const HUNK_DREL16: u32 = 1016;
const HUNK_DREL8: u32 = 1017;

const HUNK_LIB: u32 = 1018;
const HUNK_INDEX: u32 = 1019;
*/

/*
//
// Note: V37 LoadSeg uses 1015 (HUNK_DREL32) by mistake.  This will continue
// to be supported in future versions, since HUNK_DREL32 is illegal in load files
// anyways.  Future versions will support both 1015 and 1020, though anything
// that should be usable under V37 should use 1015.
//

const HUNK_RELOC32SHORT:u32 = 1020;

// see ext_xxx below.  New for V39 (note that LoadSeg only handles RELRELOC32).
const HUNK_RELRELOC32:u32 = 1021;
const HUNK_ABSRELOC16:u32 = 1022;

//
// Any hunks that have the HUNKB_ADVISORY bit set will be ignored if they
// aren't understood.  When ignored, they're treated like HUNK_DEBUG hunks.
// NOTE: this handling of HUNKB_ADVISORY started as of V39 dos.library!  If
// lading such executables is attempted under <V39 dos, it will fail with a
// bad hunk type.
//

const HUNKB_ADVISORY: u32 = 29;

// hunk_ext sub-type
const EXT_SYMB:u32 = 0; // table
const EXT_DEF: u32 = 1;	// relocatable definition
const EXT_ABS: u32 = 2;	// Absolute definition
const EXT_RES: u32 = 3;	// no longer supported
const EXT_REF32: u32 = 129;	// 32 bit absolute reference to symbol
const EXT_ABSREF32: u32 = EXT_REF32;
const EXT_COMMON: u32 = 130;	// 32 bit absolute reference to COMMON block
const EXT_ABSCOMMON: u32 = EXT_COMMON;
const EXT_REF16: u32 = 131;	// 16 bit PC-relative reference to symbol
const EXT_RELREF16: u32 = EXT_REF16;
const EXT_REF8: u32 = 132;	//  8 bit PC-relative reference to symbol
const EXT_RELREF8: u32 = EXT_REF8;
const EXT_DEXT32: u32 = 133; // 32 bit data relative reference
const EXT_DEXT16: u32 = 134; // 16 bit data relative reference
const EXT_DEXT8: u32 = 135; // 8 bit data relative reference

// These are to support some of the '020 and up modes that are rarely used
const EXT_RELREF32:u32 = 136;	// 32 bit PC-relative reference to symbol
const EXT_RELCOMMON:u32 = 137;	// 32 bit PC-relative reference to COMMON block

// for completeness... All 680x0's support this
const EXT_ABSREF16:u32 = 138;	// 16 bit absolute reference to symbol

// this only exists on '020's and above, in the (d8,An,Xn) address mode
const EXT_ABSREF8:u32 = 139; //8 bit absolute reference to symbol

*/

const HUNKF_CHIP: u32 = 1 << 30;
const HUNKF_FAST: u32 = 1 << 31;

pub struct HunkParser;

#[derive(Clone, Copy, Debug)]
enum HunkType {
    Code,
    Data,
    Bss,
}

pub struct RelocInfo32 {
    pub target: usize,
    pub data: Vec<u32>,
}

pub struct Hunk {
    mem_type: MemoryType,
    hunk_type: HunkType,
    alloc_size: usize,
    data_size: usize,
    code_data: Option<Vec<u8>>,
    reloc_32: Option<Vec<RelocInfo32>>,
}

#[derive(Clone, Copy, Debug)]
enum MemoryType {
    Any,
    Chip,
    Fast,
}

struct SizesTypes {
    mem_type: MemoryType,
    size: usize,
}

impl HunkParser {
    fn skip_hunk(file: &mut File, name: &str) -> io::Result<()> {
        println!("Skipping {}\n", name);
        let seek_offset = try!(file.read_u32::<BigEndian>());
        file.seek(io::SeekFrom::Current(seek_offset as i64)).map(|_|())
    }

    fn get_size_type(t: u32) -> (usize, MemoryType) {
        let size = (t & 0x0fffffff) * 4;
        let mem_t = t & 0xf0000000;
        let mem_type = match mem_t {
            HUNKF_CHIP => MemoryType::Chip,
            HUNKF_FAST => MemoryType::Fast,
            _ => MemoryType::Any,
        };

        (size as usize, mem_type)
    }

    fn parse_bss(hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        let (size, mem_type) = Self::get_size_type(try!(file.read_u32::<BigEndian>()));
        hunk.hunk_type = HunkType::Bss;
        hunk.data_size = size;
        hunk.mem_type = mem_type;
        Ok(())
    }

    fn parse_code_or_data(hunk_type: HunkType, hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        let (size, mem_type) = Self::get_size_type(try!(file.read_u32::<BigEndian>()));
        let mut code_data: Vec<u8> = vec![0; size];

        hunk.data_size = size;
        hunk.hunk_type = hunk_type;
        hunk.mem_type = mem_type;

        try!(file.read(&mut code_data));

        hunk.code_data = Some(code_data);

        Ok(())
    }

    fn parse_symbol(file: &mut File) -> io::Result<()> {
        let mut sym_len = try!(file.read_u32::<BigEndian>()) * 4;

        while sym_len > 0 {
            try!(file.seek(io::SeekFrom::Current((sym_len + 4) as i64)).map(|_| ()));
            sym_len = try!(file.read_u32::<BigEndian>()) * 4;
        }

        Ok(())
    }

    fn parse_reloc32(hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        let mut relocs = Vec::<RelocInfo32>::new();

        loop {
            let count = try!(file.read_u32::<BigEndian>()) as usize;

            if count == 0 {
                break;
            }

            let target = try!(file.read_u32::<BigEndian>()) as usize;

            let mut reloc = RelocInfo32 {
                target: target,
                data: Vec::<u32>::with_capacity(count),
            };

            for _ in 0..count {
                reloc.data.push(try!(file.read_u32::<BigEndian>()));
            }

            relocs.push(reloc);
        }

        hunk.reloc_32 = Some(relocs);

        Ok(())
    }

    fn fill_hunk(hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        loop {
            let hunk_type = try!(file.read_u32::<BigEndian>());

            match hunk_type {
                HUNK_UNIT => { try!(Self::skip_hunk(file, "HUNK_UNIT")) }
                HUNK_NAME => { try!(Self::skip_hunk(file, "HUNK_NAME")) }
                HUNK_DEBUG => { try!(Self::skip_hunk(file, "HUNK_DEBUG")) }
                HUNK_CODE => { try!(Self::parse_code_or_data(HunkType::Code, hunk, file)) }
                HUNK_DATA => { try!(Self::parse_code_or_data(HunkType::Data, hunk, file)) }
                HUNK_BSS => { try!(Self::parse_bss(hunk, file)) }
                HUNK_RELOC32 => { try!(Self::parse_reloc32(hunk, file)) }
                HUNK_SYMBOL => { try!(Self::parse_symbol(file)) }
                HUNK_END => {
                    return Ok(());
                }

                _ => {
                    return Err(Error::new(ErrorKind::Other, "Unsupported hunk"));
                }
            }
        }
    }

    pub fn parse_file(filename: &str) -> io::Result<()> {
        //let mut index = 0;

        let mut file = try!(File::open(filename));

        let hunk_header = try!(file.read_u32::<BigEndian>());
        if hunk_header != HUNK_HEADER  {
            return Err(Error::new(ErrorKind::Other, "Unable to find correct HUNK_HEADER"));
        };

        // Skip header/string section
        try!(file.read_u32::<BigEndian>());

        let table_size = try!(file.read_u32::<BigEndian>()) as i32;
        let first_hunk = try!(file.read_u32::<BigEndian>()) as i32;
        let last_hunk = try!(file.read_u32::<BigEndian>()) as i32;

        if table_size < 0 || first_hunk < 0 || last_hunk < 0 {
            return Err(Error::new(ErrorKind::Other, "Invalid sizes for hunks"));
        }

        let hunk_count = (last_hunk - first_hunk + 1) as usize;

        let mut hunk_table = Vec::with_capacity(hunk_count);

        for _ in 0..hunk_count {
            let (size, mem_type) = Self::get_size_type(try!(file.read_u32::<BigEndian>()));
            hunk_table.push(SizesTypes {
                mem_type: mem_type,
                size: size
            });
        }

        let mut hunks = Vec::with_capacity(hunk_count);

        for i in 0..hunk_count {
            let mut hunk = Hunk {
                mem_type: hunk_table[i].mem_type,
                hunk_type: HunkType::Bss,
                alloc_size: hunk_table[i].size as usize,
                data_size: 0,
                code_data: None,
                reloc_32: None,
            };

            try!(Self::fill_hunk(&mut hunk, &mut file));

            hunks.push(hunk);
        }

        // dump info

        for hunk in hunks {
            println!("type {:?} - size {} - alloc_size {}", hunk.hunk_type, hunk.data_size, hunk.alloc_size);
        }

        //println!("b {}", hunk_header);
        Ok(())
    }
}
<commit_msg>Minor cleanup + rustfmt<commit_after>
use std::fs::File;
use byteorder::{BigEndian, ReadBytesExt};
use std::io;
use std::io::{Error, ErrorKind, Seek, Read};

const HUNK_HEADER: u32 = 1011;
// hunk types
const HUNK_UNIT: u32 = 999;
const HUNK_NAME: u32 = 1000;
const HUNK_CODE: u32 = 1001;
const HUNK_DATA: u32 = 1002;
const HUNK_BSS: u32 = 1003;
const HUNK_RELOC32: u32 = 1004;
const HUNK_DEBUG: u32 = 1009;
const HUNK_SYMBOL: u32 = 1008;
const HUNK_END: u32 = 1010;

/*
   const HUNK_ABSRELOC32: u32 = HUNK_RELOC32;
   const HUNK_RELOC16: u32 = 1005;
   const HUNK_RELRELOC16 :u32 = HUNK_RELOC16;
   const HUNK_RELOC8: u32 = 1006;
   const HUNK_RELRELOC8: u32 = HUNK_RELOC8;

   const HUNK_OVERLAY: u32 = 1013;
   const HUNK_BREAK: u32 = 1014;

   const HUNK_DREL32: u32 = 1015;
   const HUNK_DREL16: u32 = 1016;
   const HUNK_DREL8: u32 = 1017;

   const HUNK_LIB: u32 = 1018;
   const HUNK_INDEX: u32 = 1019;
   */

/*
//
// Note: V37 LoadSeg uses 1015 (HUNK_DREL32) by mistake.  This will continue
// to be supported in future versions, since HUNK_DREL32 is illegal in load files
// anyways.  Future versions will support both 1015 and 1020, though anything
// that should be usable under V37 should use 1015.
//

const HUNK_RELOC32SHORT:u32 = 1020;

// see ext_xxx below.  New for V39 (note that LoadSeg only handles RELRELOC32).
const HUNK_RELRELOC32:u32 = 1021;
const HUNK_ABSRELOC16:u32 = 1022;

//
// Any hunks that have the HUNKB_ADVISORY bit set will be ignored if they
// aren't understood.  When ignored, they're treated like HUNK_DEBUG hunks.
// NOTE: this handling of HUNKB_ADVISORY started as of V39 dos.library!  If
// lading such executables is attempted under <V39 dos, it will fail with a
// bad hunk type.
//

const HUNKB_ADVISORY: u32 = 29;

// hunk_ext sub-type
const EXT_SYMB:u32 = 0; // table
const EXT_DEF: u32 = 1;	// relocatable definition
const EXT_ABS: u32 = 2;	// Absolute definition
const EXT_RES: u32 = 3;	// no longer supported
const EXT_REF32: u32 = 129;	// 32 bit absolute reference to symbol
const EXT_ABSREF32: u32 = EXT_REF32;
const EXT_COMMON: u32 = 130;	// 32 bit absolute reference to COMMON block
const EXT_ABSCOMMON: u32 = EXT_COMMON;
const EXT_REF16: u32 = 131;	// 16 bit PC-relative reference to symbol
const EXT_RELREF16: u32 = EXT_REF16;
const EXT_REF8: u32 = 132;	//  8 bit PC-relative reference to symbol
const EXT_RELREF8: u32 = EXT_REF8;
const EXT_DEXT32: u32 = 133; // 32 bit data relative reference
const EXT_DEXT16: u32 = 134; // 16 bit data relative reference
const EXT_DEXT8: u32 = 135; // 8 bit data relative reference

// These are to support some of the '020 and up modes that are rarely used
const EXT_RELREF32:u32 = 136;	// 32 bit PC-relative reference to symbol
const EXT_RELCOMMON:u32 = 137;	// 32 bit PC-relative reference to COMMON block

// for completeness... All 680x0's support this
const EXT_ABSREF16:u32 = 138;	// 16 bit absolute reference to symbol

// this only exists on '020's and above, in the (d8,An,Xn) address mode
const EXT_ABSREF8:u32 = 139; //8 bit absolute reference to symbol

*/

const HUNKF_CHIP: u32 = 1 << 30;
const HUNKF_FAST: u32 = 1 << 31;

pub struct HunkParser;

#[derive(Clone, Copy, Debug)]
enum HunkType {
    Code,
    Data,
    Bss,
}

pub struct RelocInfo32 {
    pub target: usize,
    pub data: Vec<u32>,
}

pub struct Hunk {
    mem_type: MemoryType,
    hunk_type: HunkType,
    alloc_size: usize,
    data_size: usize,
    code_data: Option<Vec<u8>>,
    reloc_32: Option<Vec<RelocInfo32>>,
}

#[derive(Clone, Copy, Debug)]
enum MemoryType {
    Any,
    Chip,
    Fast,
}

struct SizesTypes {
    mem_type: MemoryType,
    size: usize,
}

impl HunkParser {
    fn skip_hunk(file: &mut File, name: &str) -> io::Result<()> {
        println!("Skipping {}\n", name);
        let seek_offset = try!(file.read_u32::<BigEndian>());
        file.seek(io::SeekFrom::Current(seek_offset as i64)).map(|_|())
    }

    fn get_size_type(t: u32) -> (usize, MemoryType) {
        let size = (t & 0x0fffffff) * 4;
        let mem_t = t & 0xf0000000;
        let mem_type = match mem_t {
            HUNKF_CHIP => MemoryType::Chip,
            HUNKF_FAST => MemoryType::Fast,
            _ => MemoryType::Any,
        };

        (size as usize, mem_type)
    }

    fn parse_bss(hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        let (size, mem_type) = Self::get_size_type(try!(file.read_u32::<BigEndian>()));
        hunk.hunk_type = HunkType::Bss;
        hunk.data_size = size;
        hunk.mem_type = mem_type;
        Ok(())
    }

    fn parse_code_or_data(hunk_type: HunkType, hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        let (size, mem_type) = Self::get_size_type(try!(file.read_u32::<BigEndian>()));
        let mut code_data: Vec<u8> = vec![0; size];

        hunk.data_size = size;
        hunk.hunk_type = hunk_type;
        hunk.mem_type = mem_type;

        try!(file.read(&mut code_data));

        hunk.code_data = Some(code_data);

        Ok(())
    }

    fn parse_symbol(file: &mut File) -> io::Result<()> {
        let mut sym_len = try!(file.read_u32::<BigEndian>()) * 4;

        while sym_len > 0 {
            try!(file.seek(io::SeekFrom::Current((sym_len + 4) as i64)).map(|_| ()));
            sym_len = try!(file.read_u32::<BigEndian>()) * 4;
        }

        Ok(())
    }

    fn parse_reloc32(hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        let mut relocs = Vec::<RelocInfo32>::new();

        loop {
            let count = try!(file.read_u32::<BigEndian>()) as usize;

            if count == 0 {
                break;
            }

            let target = try!(file.read_u32::<BigEndian>()) as usize;

            let mut reloc = RelocInfo32 {
                target: target,
                data: Vec::<u32>::with_capacity(count),
            };

            for _ in 0..count {
                reloc.data.push(try!(file.read_u32::<BigEndian>()));
            }

            relocs.push(reloc);
        }

        hunk.reloc_32 = Some(relocs);

        Ok(())
    }

    fn fill_hunk(hunk: &mut Hunk, file: &mut File) -> io::Result<()> {
        loop {
            let hunk_type = try!(file.read_u32::<BigEndian>());

            match hunk_type {
                HUNK_UNIT => { try!(Self::skip_hunk(file, "HUNK_UNIT")) }
                HUNK_NAME => { try!(Self::skip_hunk(file, "HUNK_NAME")) }
                HUNK_DEBUG => { try!(Self::skip_hunk(file, "HUNK_DEBUG")) }
                HUNK_CODE => { try!(Self::parse_code_or_data(HunkType::Code, hunk, file)) }
                HUNK_DATA => { try!(Self::parse_code_or_data(HunkType::Data, hunk, file)) }
                HUNK_BSS => { try!(Self::parse_bss(hunk, file)) }
                HUNK_RELOC32 => { try!(Self::parse_reloc32(hunk, file)) }
                HUNK_SYMBOL => { try!(Self::parse_symbol(file)) }
                HUNK_END => {
                    return Ok(());
                }

                _ => {
                    return Err(Error::new(ErrorKind::Other, "Unsupported hunk"));
                }
            }
        }
    }

    pub fn parse_file(filename: &str) -> io::Result<()> {
        //let mut index = 0;

        let mut file = try!(File::open(filename));

        let hunk_header = try!(file.read_u32::<BigEndian>());
        if hunk_header != HUNK_HEADER  {
            return Err(Error::new(ErrorKind::Other, "Unable to find correct HUNK_HEADER"));
        };

        // Skip header/string section
        try!(file.read_u32::<BigEndian>());

        let table_size = try!(file.read_u32::<BigEndian>()) as i32;
        let first_hunk = try!(file.read_u32::<BigEndian>()) as i32;
        let last_hunk = try!(file.read_u32::<BigEndian>()) as i32;

        if table_size < 0 || first_hunk < 0 || last_hunk < 0 {
            return Err(Error::new(ErrorKind::Other, "Invalid sizes for hunks"));
        }

        let hunk_count = (last_hunk - first_hunk + 1) as usize;

        let mut hunk_table = Vec::with_capacity(hunk_count);

        for _ in 0..hunk_count {
            let (size, mem_type) = Self::get_size_type(try!(file.read_u32::<BigEndian>()));
            hunk_table.push(SizesTypes {
                mem_type: mem_type,
                size: size
            });
        }

        let mut hunks = Vec::with_capacity(hunk_count);

        for i in 0..hunk_count {
            let mut hunk = Hunk {
                mem_type: hunk_table[i].mem_type,
                    hunk_type: HunkType::Bss,
                    alloc_size: hunk_table[i].size as usize,
                    data_size: 0,
                    code_data: None,
                    reloc_32: None,
            };

            try!(Self::fill_hunk(&mut hunk, &mut file));

            hunks.push(hunk);
        }

        // dump info

        for hunk in hunks {
            println!("type {:?} - size {} - alloc_size {}", hunk.hunk_type, hunk.data_size, hunk.alloc_size);
        }

        //println!("b {}", hunk_header);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::{
    analysis::namespaces::{self, NsId},
    case::CaseExt,
    library::*,
};
use std::collections::HashMap;

#[derive(Clone, Debug, Default)]
pub struct Symbol {
    crate_name: Option<String>,
    owner_name: Option<String>,
    name: String,
}

impl Symbol {
    pub fn parent(&self) -> String {
        let mut ret = String::new();
        if let Some(ref s) = self.crate_name {
            if s == "gobject" {
                ret.push_str("glib::object");
            } else {
                ret.push_str(s);
            }
            ret.push_str("::");
        }
        if let Some(ref s) = self.owner_name {
            ret.push_str(s);
            ret.push_str("::");
        }
        ret
    }

    pub fn full_rust_name(&self) -> String {
        let mut ret = self.parent();
        ret.push_str(&self.name);
        ret
    }

    pub fn make_trait_method(&mut self, trait_name: &str) {
        self.owner_name = Some(trait_name.into());
    }

    pub fn crate_name(&self) -> Option<&String> {
        self.crate_name.as_ref()
    }

    pub fn owner_name(&self) -> Option<&str> {
        self.owner_name.as_deref()
    }

    pub fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug)]
pub struct Info {
    symbols: Vec<Symbol>,
    c_name_index: HashMap<String, u32>,
    tid_index: HashMap<Option<TypeId>, u32>,
}

pub fn run(library: &Library, namespaces: &namespaces::Info) -> Info {
    let mut info = Info {
        symbols: Vec::new(),
        c_name_index: HashMap::new(),
        tid_index: HashMap::new(),
    };

    info.insert(
        "NULL",
        Symbol {
            name: "None".into(),
            ..Default::default()
        },
        None,
    );
    info.insert(
        "FALSE",
        Symbol {
            name: "false".into(),
            ..Default::default()
        },
        None,
    );
    info.insert(
        "TRUE",
        Symbol {
            name: "true".into(),
            ..Default::default()
        },
        None,
    );

    for (ns_id, ns) in library.namespaces.iter().enumerate() {
        let ns_id = ns_id as NsId;
        if ns_id == namespaces::INTERNAL {
            continue;
        }

        let crate_name = if ns_id == namespaces::MAIN {
            None
        } else {
            Some(&namespaces[ns_id].crate_name)
        };

        for (pos, typ) in ns.types.iter().map(|t| t.as_ref().unwrap()).enumerate() {
            let symbol = Symbol {
                crate_name: crate_name.cloned(),
                name: typ.get_name(),
                ..Default::default()
            };
            let tid = TypeId {
                ns_id,
                id: pos as u32,
            };

            match *typ {
                Type::Alias(Alias {
                    ref c_identifier, ..
                }) => {
                    info.insert(c_identifier, symbol, Some(tid));
                }
                Type::Enumeration(Enumeration {
                    ref name,
                    ref c_type,
                    ref members,
                    ref functions,
                    ..
                })
                | Type::Bitfield(Bitfield {
                    ref name,
                    ref c_type,
                    ref members,
                    ref functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for member in members {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: member.name.to_camel(),
                        };
                        info.insert(&member.c_identifier, symbol, Some(tid));
                    }
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                Type::Record(Record {
                    ref name,
                    ref c_type,
                    ref functions,
                    ..
                })
                | Type::Class(Class {
                    ref name,
                    ref c_type,
                    ref functions,
                    ..
                })
                | Type::Interface(Interface {
                    ref name,
                    ref c_type,
                    ref functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                _ => {}
            }
        }
    }

    info
}

impl Info {
    pub fn by_c_name(&self, name: &str) -> Option<&Symbol> {
        self.c_name_index
            .get(name)
            .map(|&id| &self.symbols[id as usize])
    }

    pub fn by_c_name_mut(&mut self, name: &str) -> Option<&mut Symbol> {
        if let Some(&id) = self.c_name_index.get(name) {
            Some(&mut self.symbols[id as usize])
        } else {
            None
        }
    }

    pub fn by_tid(&self, tid: TypeId) -> Option<&Symbol> {
        self.tid_index
            .get(&Some(tid))
            .map(|&id| &self.symbols[id as usize])
    }

    fn insert(&mut self, name: &str, symbol: Symbol, tid: Option<TypeId>) {
        let id = self.symbols.len();
        self.symbols.push(symbol);
        self.c_name_index.insert(name.to_owned(), id as u32);
        if tid.is_some() {
            self.tid_index.insert(tid, id as u32);
        }
    }
}
<commit_msg>doc: Link trait functions in prelude<commit_after>use crate::{
    analysis::namespaces::{self, NsId},
    case::CaseExt,
    library::*,
};
use std::collections::HashMap;

#[derive(Clone, Debug, Default)]
pub struct Symbol {
    crate_name: Option<String>,
    module_name: Option<String>,
    owner_name: Option<String>,
    name: String,
}

impl Symbol {
    pub fn parent(&self) -> String {
        let mut ret = String::new();
        if Some("gobject") == self.crate_name() {
            ret.push_str("glib::object::");
        } else {
            if let Some(ref s) = self.crate_name {
                ret.push_str(s);
                ret.push_str("::");
            }
            if let Some(ref module) = self.module_name {
                ret.push_str(module);
                ret.push_str("::");
            }
        }
        if let Some(ref s) = self.owner_name {
            ret.push_str(s);
            ret.push_str("::");
        }
        ret
    }

    pub fn full_rust_name(&self) -> String {
        let mut ret = self.parent();
        ret.push_str(&self.name);
        ret
    }

    pub fn make_trait_method(&mut self, trait_name: &str) {
        if self.module_name.replace("prelude".to_string()).is_some() {
            // .expect_none is not stabilized yet
            panic!("{:?} already had a module name set!", self)
        }
        self.owner_name = Some(trait_name.into());
    }

    pub fn crate_name(&self) -> Option<&str> {
        self.crate_name.as_deref()
    }

    pub fn owner_name(&self) -> Option<&str> {
        self.owner_name.as_deref()
    }

    pub fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug)]
pub struct Info {
    symbols: Vec<Symbol>,
    c_name_index: HashMap<String, u32>,
    tid_index: HashMap<Option<TypeId>, u32>,
}

pub fn run(library: &Library, namespaces: &namespaces::Info) -> Info {
    let mut info = Info {
        symbols: Vec::new(),
        c_name_index: HashMap::new(),
        tid_index: HashMap::new(),
    };

    info.insert(
        "NULL",
        Symbol {
            name: "None".into(),
            ..Default::default()
        },
        None,
    );
    info.insert(
        "FALSE",
        Symbol {
            name: "false".into(),
            ..Default::default()
        },
        None,
    );
    info.insert(
        "TRUE",
        Symbol {
            name: "true".into(),
            ..Default::default()
        },
        None,
    );

    for (ns_id, ns) in library.namespaces.iter().enumerate() {
        let ns_id = ns_id as NsId;
        if ns_id == namespaces::INTERNAL {
            continue;
        }

        let crate_name = if ns_id == namespaces::MAIN {
            None
        } else {
            Some(&namespaces[ns_id].crate_name)
        };

        for (pos, typ) in ns.types.iter().map(|t| t.as_ref().unwrap()).enumerate() {
            let symbol = Symbol {
                crate_name: crate_name.cloned(),
                name: typ.get_name(),
                ..Default::default()
            };
            let tid = TypeId {
                ns_id,
                id: pos as u32,
            };

            match *typ {
                Type::Alias(Alias {
                    ref c_identifier, ..
                }) => {
                    info.insert(c_identifier, symbol, Some(tid));
                }
                Type::Enumeration(Enumeration {
                    ref name,
                    ref c_type,
                    ref members,
                    ref functions,
                    ..
                })
                | Type::Bitfield(Bitfield {
                    ref name,
                    ref c_type,
                    ref members,
                    ref functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for member in members {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: member.name.to_camel(),
                            ..Default::default()
                        };
                        info.insert(&member.c_identifier, symbol, Some(tid));
                    }
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                            ..Default::default()
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                Type::Record(Record {
                    ref name,
                    ref c_type,
                    ref functions,
                    ..
                })
                | Type::Class(Class {
                    ref name,
                    ref c_type,
                    ref functions,
                    ..
                })
                | Type::Interface(Interface {
                    ref name,
                    ref c_type,
                    ref functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                            ..Default::default()
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                _ => {}
            }
        }
    }

    info
}

impl Info {
    pub fn by_c_name(&self, name: &str) -> Option<&Symbol> {
        self.c_name_index
            .get(name)
            .map(|&id| &self.symbols[id as usize])
    }

    pub fn by_c_name_mut(&mut self, name: &str) -> Option<&mut Symbol> {
        if let Some(&id) = self.c_name_index.get(name) {
            Some(&mut self.symbols[id as usize])
        } else {
            None
        }
    }

    pub fn by_tid(&self, tid: TypeId) -> Option<&Symbol> {
        self.tid_index
            .get(&Some(tid))
            .map(|&id| &self.symbols[id as usize])
    }

    fn insert(&mut self, name: &str, symbol: Symbol, tid: Option<TypeId>) {
        let id = self.symbols.len();
        self.symbols.push(symbol);
        self.c_name_index.insert(name.to_owned(), id as u32);
        if tid.is_some() {
            self.tid_index.insert(tid, id as u32);
        }
    }
}
<|endoftext|>


<commit_before>use bvh::*;
use bounding_hierarchy::BHShape;
use aabb::AABB;
use std::collections::HashSet;

// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB
// that is updated from the outside, perhaps by passing not only the indices of the changed
// shapes, but also their new AABBs into optimize().

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children and transitive children of the given node having correct AABBs.
    ///
    /// Returns Some(index_of_node) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_surface_area = 0f32;

        // TODO Re-implement without mutability
        let mut parent_index: usize = 0;

        #[derive(Debug, Copy, Clone)]
        struct NodeData {
            index: usize,
            aabb: AABB,
        }

        macro_rules! get_children_node_data {
            ($node_index:expr) => ({
                let node = &nodes[$node_index];
                match *node {
                    BVHNode::Node { child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                        Some((NodeData{ index: child_l, aabb: child_l_aabb},
                            NodeData{ index: child_r, aabb: child_r_aabb}))
                    }
                    BVHNode::Leaf { .. } => {
                        None
                    }
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                }
            });
        }

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_surface_area.
        let (child_l, child_r) = match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                let left_children = match get_children_node_data!(child_l) {
                    Some(x) => x,
                    _ => unreachable!(),
                };
                let right_children = match get_children_node_data!(child_r) {
                    Some(x) => x,
                    _ => unreachable!(),
                };

                // Update the AABBs saved for the children
                // since at least one of them changed
                let aabb_l = left_children.0.aabb.join(&left_children.1.aabb);
                let aabb_r = right_children.0.aabb.join(&right_children.1.aabb);

                parent_index = parent;
                best_surface_area = child_l_aabb.surface_area() + child_r_aabb.surface_area();
                (NodeData {
                     index: child_l,
                     aabb: aabb_l,
                 },
                 NodeData {
                     index: child_r,
                     aabb: aabb_r,
                 })
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        };

        // Stores the Rotation that would result in the surface area best_surface_area,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;

        macro_rules! consider_rotation {
            ($a:expr, $b:expr, $sa:expr) => {
                if $sa < best_surface_area {
                    best_surface_area = $sa;
                    best_rotation = Some(($a.index, $b.index));
                }
            };
        }

        // Get indices and AABBs of all grandchildren
        let left_children_nodes = {
            let node_data: NodeData = child_l;
            let index = node_data.index;
            get_children_node_data!(index)
        };
        let right_children_nodes = {
            let node_data: NodeData = child_r;
            let index = node_data.index;
            get_children_node_data!(index)
        };

        // Child to grandchild rotations
        if let Some((child_rl, child_rr)) = right_children_nodes {
            consider_rotation!(child_l,
                               child_rl,
                               child_rl.aabb.surface_area() +
                               child_l.aabb.join(&child_rr.aabb).surface_area());
            consider_rotation!(child_l,
                               child_rr,
                               child_rr.aabb.surface_area() +
                               child_l.aabb.join(&child_rl.aabb).surface_area());
        }
        if let Some((child_ll, child_lr)) = left_children_nodes {
            consider_rotation!(child_r,
                               child_ll,
                               child_ll.aabb.surface_area() +
                               child_r.aabb.join(&child_lr.aabb).surface_area());
            consider_rotation!(child_r,
                               child_lr,
                               child_lr.aabb.surface_area() +
                               child_r.aabb.join(&child_ll.aabb).surface_area());

            // Grandchild to grandchild rotations
            if let Some((child_rl, child_rr)) = right_children_nodes {
                consider_rotation!(child_ll,
                                   child_rl,
                                   child_rl.aabb.join(&child_lr.aabb).surface_area() +
                                   child_ll.aabb.join(&child_rr.aabb).surface_area());
                consider_rotation!(child_ll,
                                   child_rr,
                                   child_ll.aabb.join(&child_rl.aabb).surface_area() +
                                   child_lr.aabb.join(&child_rr.aabb).surface_area());
            }
        }

        let new_refit_node_index = if parent_index > 0 {
            Some(parent_index)
        } else {
            None
        };

        if let Some(rotation) = best_rotation {
            BVH::rotate(nodes, rotation.0, rotation.1);

            // TODO Update all changed node AABBs

            // Return parent node's index for upcoming refitting,
            // since this node just changed its AABB
            new_refit_node_index
        } else {
            // Update this node's AABBs (child_l_aabb, child_r_aabb)
            // according to the children nodes' AABBs.
            let mut node = &mut nodes[node_index];
            match node {
                &mut BVHNode::Node { ref mut child_l_aabb, ref mut child_r_aabb, .. } => {
                    *child_l_aabb = child_l.aabb;
                    *child_r_aabb = child_r.aabb;
                }
                _ => unreachable!(),
            }

            // Even with no rotation being useful for this node, a parent node's rotation
            // could be beneficial, so queue the parent sometimes.
            // TODO Return None most of the time, randomly
            // (see https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307)
            new_refit_node_index
        }
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice).
    fn rotate(nodes: &mut Vec<BVHNode>, node_a_index: usize, node_b_index: usize) {
        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        // Get parent indices
        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        // Get info about the nodes being a left or right child
        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        // Perform the switch
        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<commit_msg>Fix test imports<commit_after>use bvh::*;
use bounding_hierarchy::BHShape;
use aabb::AABB;
use std::collections::HashSet;

// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB
// that is updated from the outside, perhaps by passing not only the indices of the changed
// shapes, but also their new AABBs into optimize().

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children and transitive children of the given node having correct AABBs.
    ///
    /// Returns Some(index_of_node) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_surface_area = 0f32;

        // TODO Re-implement without mutability
        let mut parent_index: usize = 0;

        #[derive(Debug, Copy, Clone)]
        struct NodeData {
            index: usize,
            aabb: AABB,
        }

        macro_rules! get_children_node_data {
            ($node_index:expr) => ({
                let node = &nodes[$node_index];
                match *node {
                    BVHNode::Node { child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                        Some((NodeData{ index: child_l, aabb: child_l_aabb},
                            NodeData{ index: child_r, aabb: child_r_aabb}))
                    }
                    BVHNode::Leaf { .. } => {
                        None
                    }
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                }
            });
        }

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_surface_area.
        let (child_l, child_r) = match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                let left_children = match get_children_node_data!(child_l) {
                    Some(x) => x,
                    _ => unreachable!(),
                };
                let right_children = match get_children_node_data!(child_r) {
                    Some(x) => x,
                    _ => unreachable!(),
                };

                // Update the AABBs saved for the children
                // since at least one of them changed
                let aabb_l = left_children.0.aabb.join(&left_children.1.aabb);
                let aabb_r = right_children.0.aabb.join(&right_children.1.aabb);

                parent_index = parent;
                best_surface_area = child_l_aabb.surface_area() + child_r_aabb.surface_area();
                (NodeData {
                     index: child_l,
                     aabb: aabb_l,
                 },
                 NodeData {
                     index: child_r,
                     aabb: aabb_r,
                 })
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        };

        // Stores the Rotation that would result in the surface area best_surface_area,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;

        macro_rules! consider_rotation {
            ($a:expr, $b:expr, $sa:expr) => {
                if $sa < best_surface_area {
                    best_surface_area = $sa;
                    best_rotation = Some(($a.index, $b.index));
                }
            };
        }

        // Get indices and AABBs of all grandchildren
        let left_children_nodes = {
            let node_data: NodeData = child_l;
            let index = node_data.index;
            get_children_node_data!(index)
        };
        let right_children_nodes = {
            let node_data: NodeData = child_r;
            let index = node_data.index;
            get_children_node_data!(index)
        };

        // Child to grandchild rotations
        if let Some((child_rl, child_rr)) = right_children_nodes {
            consider_rotation!(child_l,
                               child_rl,
                               child_rl.aabb.surface_area() +
                               child_l.aabb.join(&child_rr.aabb).surface_area());
            consider_rotation!(child_l,
                               child_rr,
                               child_rr.aabb.surface_area() +
                               child_l.aabb.join(&child_rl.aabb).surface_area());
        }
        if let Some((child_ll, child_lr)) = left_children_nodes {
            consider_rotation!(child_r,
                               child_ll,
                               child_ll.aabb.surface_area() +
                               child_r.aabb.join(&child_lr.aabb).surface_area());
            consider_rotation!(child_r,
                               child_lr,
                               child_lr.aabb.surface_area() +
                               child_r.aabb.join(&child_ll.aabb).surface_area());

            // Grandchild to grandchild rotations
            if let Some((child_rl, child_rr)) = right_children_nodes {
                consider_rotation!(child_ll,
                                   child_rl,
                                   child_rl.aabb.join(&child_lr.aabb).surface_area() +
                                   child_ll.aabb.join(&child_rr.aabb).surface_area());
                consider_rotation!(child_ll,
                                   child_rr,
                                   child_ll.aabb.join(&child_rl.aabb).surface_area() +
                                   child_lr.aabb.join(&child_rr.aabb).surface_area());
            }
        }

        let new_refit_node_index = if parent_index > 0 {
            Some(parent_index)
        } else {
            None
        };

        if let Some(rotation) = best_rotation {
            BVH::rotate(nodes, rotation.0, rotation.1);

            // TODO Update all changed node AABBs

            // Return parent node's index for upcoming refitting,
            // since this node just changed its AABB
            new_refit_node_index
        } else {
            // Update this node's AABBs (child_l_aabb, child_r_aabb)
            // according to the children nodes' AABBs.
            let mut node = &mut nodes[node_index];
            match node {
                &mut BVHNode::Node { ref mut child_l_aabb, ref mut child_r_aabb, .. } => {
                    *child_l_aabb = child_l.aabb;
                    *child_r_aabb = child_r.aabb;
                }
                _ => unreachable!(),
            }

            // Even with no rotation being useful for this node, a parent node's rotation
            // could be beneficial, so queue the parent sometimes.
            // TODO Return None most of the time, randomly
            // (see https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307)
            new_refit_node_index
        }
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice).
    fn rotate(nodes: &mut Vec<BVHNode>, node_a_index: usize, node_b_index: usize) {
        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        // Get parent indices
        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        // Get info about the nodes being a left or right child
        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        // Perform the switch
        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;
    use std::collections::HashSet;
    use testbase::build_some_bh;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<|endoftext|>


<commit_before>use crate::ast::types::{Block, Definition};
use crate::ast::{AstChildren, AstNode};

pub trait ItemOwner: AstNode {
    fn items(&self) -> AstChildren<Definition> {
        self.child::<Block>().children()
    }

    fn items_of<T: AstNode>(&self) -> AstChildren<T> {
        self.child::<Block>().children()
    }
}

pub trait NameOwner: AstNode {}

#[cfg(test)]
mod tests {
    use crate::ast::SourceFile;

    use super::*;

    fn parse_item<T: AstNode>(text: &str) -> T {
        SourceFile::parse(text)
            .tree()
            .items_of::<T>()
            .nth(0)
            .unwrap()
    }

    //    #[test]
    //    fn variable_as_name_owner() {
    //        test_name_owner::<VariableDef>("type t;", "t");
    //    }
    //
    //    #[test]
    //    fn variable_with_initializer_as_name_owner() {
    //        test_name_owner::<VariableDef>("type_attribute t = v;", "t");
    //    }
    //
    //    #[test]
    //    fn macro_as_name_owner() {
    //        test_name_owner::<MacroDef>("macro abc() {}", "abc");
    //    }
    //
    //    #[test]
    //    fn block_as_name_owner() {
    //        test_name_owner::<ContainerDef>("block abc {}", "abc");
    //    }
}
<commit_msg>Fix NameOwner AST tests<commit_after>use secsp_parser::syntax::{SyntaxElement, SyntaxKind};

use crate::ast::types::{Block, Definition};
use crate::ast::{AstChildren, AstNode};
use rowan::SmolStr;

pub trait ItemOwner: AstNode {
    fn items(&self) -> AstChildren<Definition> {
        self.child::<Block>().children()
    }

    fn items_of<T: AstNode>(&self) -> AstChildren<T> {
        self.child::<Block>().children()
    }
}

pub trait NameOwner: AstNode {
    fn name(&self) -> Option<String> {
        self.syntax()
            .children_with_tokens()
            .find_map(|child| match child {
                SyntaxElement::Token(tok) => {
                    if tok.kind() == SyntaxKind::TOK_NAME {
                        Some(tok.text().to_string())
                    } else {
                        None
                    }
                }
                _ => None,
            })
    }

}
#[cfg(test)]
mod tests {
    use crate::ast::{SourceFile, VariableDef, MacroDef, ContainerDef};

    use super::*;

    fn test_name_owner<T: NameOwner>(text: &str, name: &str) {
        let m = parse_item::<T>(text);
        let macro_name = m.name().expect("couldn't find name on node");

        assert_eq!(name, macro_name);
    }

    fn parse_item<T: AstNode>(text: &str) -> T {
        SourceFile::parse(text)
            .tree()
            .items_of::<T>()
            .nth(0)
            .unwrap()
    }

    #[test]
    fn variable_as_name_owner() {
        test_name_owner::<VariableDef>("type t;", "t");
    }

    #[test]
    fn variable_with_initializer_as_name_owner() {
        test_name_owner::<VariableDef>("type_attribute t = v;", "t");
    }

    #[test]
    fn macro_as_name_owner() {
        test_name_owner::<MacroDef>("macro abc() {}", "abc");
    }

    #[test]
    fn block_as_name_owner() {
        test_name_owner::<ContainerDef>("block abc {}", "abc");
    }
}
<|endoftext|>


<commit_before>use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};

use TomlError = toml::Error;

pub trait CargoError {
    fn description(&self) -> String;
    fn detail(&self) -> Option<String> { None }
    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
    fn is_human(&self) -> bool { false }

    fn to_error<E: FromError<Self>>(self) -> E {
        FromError::from_error(self)
    }

    fn box_error(self) -> Box<CargoError> {
        box self as Box<CargoError>
    }

    fn concrete(&self) -> ConcreteCargoError {
        ConcreteCargoError {
            description: self.description(),
            detail: self.detail(),
            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
            is_human: self.is_human()
        }
    }

    fn with_cause<E: CargoError>(self, cause: E) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.cause = Some(cause.box_error());
        box concrete as Box<CargoError>
    }

    fn mark_human(self) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.is_human = true;
        box concrete as Box<CargoError>
    }
}

pub trait FromError<E> {
    fn from_error(error: E) -> Self;
}

impl<E: CargoError> FromError<E> for Box<CargoError> {
    fn from_error(error: E) -> Box<CargoError> {
        error.box_error()
    }
}

macro_rules! from_error (
    ($ty:ty) => {
        impl FromError<$ty> for $ty {
            fn from_error(error: $ty) -> $ty {
                error
            }
        }
    }
)

impl Show for Box<CargoError> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));
        Ok(())
    }
}

impl CargoError for Box<CargoError> {
    fn description(&self) -> String {
        (*self).description()
    }

    fn detail(&self) -> Option<String> {
        (*self).detail()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        (*self).cause()
    }

    fn is_human(&self) -> bool {
        (*self).is_human()
    }

    fn box_error(self) -> Box<CargoError> {
        self
    }
}

pub type CargoResult<T> = Result<T, Box<CargoError>>;

pub trait BoxError<T> {
    fn box_error(self) -> CargoResult<T>;
}

pub trait ChainError<T> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T> ;
}

impl<T, E: CargoError> BoxError<T> for Result<T, E> {
    fn box_error(self) -> CargoResult<T> {
        self.map_err(|err| err.box_error())
    }
}

impl<T, E: CargoError> ChainError<T> for Result<T, E> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T>  {
        self.map_err(|err| {
            callback().with_cause(err)
        })
    }
}

impl CargoError for IoError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(IoError)

impl CargoError for TomlError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(TomlError)

pub struct ProcessError {
    pub msg: String,
    pub command: String,
    pub exit: Option<ProcessExit>,
    pub output: Option<ProcessOutput>,
    pub detail: Option<String>,
    pub cause: Option<Box<CargoError>>
}

from_error!(ProcessError)

impl Show for ProcessError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        write!(f, "{} (status={})", self.msg, exit)
    }
}

impl CargoError for ProcessError {
    fn description(&self) -> String {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        format!("{} (status={})", self.msg, exit)
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }
}

pub struct ConcreteCargoError {
    description: String,
    detail: Option<String>,
    cause: Option<Box<CargoError>>,
    is_human: bool
}

impl Show for ConcreteCargoError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl CargoError for ConcreteCargoError {
    fn description(&self) -> String {
        self.description.clone()
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }

    fn is_human(&self) -> bool {
        self.is_human
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub struct CliError {
    pub error: Box<CargoError>,
    pub exit_code: uint
}

impl CliError {
    pub fn new<S: Str>(error: S, code: uint) -> CliError {
        let error = human(error.as_slice().to_str());
        CliError::from_boxed(error, code)
    }

    pub fn from_error<E: CargoError + 'static>(error: E, code: uint) -> CliError {
        let error = box error as Box<CargoError>;
        CliError::from_boxed(error, code)
    }

    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
        let error = if error.is_human() {
            error
        } else {
            human("An unknown error occurred").with_cause(error)
        };

        CliError { error: error, exit_code: code }
    }
}

pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
    ProcessError {
        msg: msg.as_slice().to_str(),
        command: command.to_str(),
        exit: status.map(|o| o.clone()),
        output: output.map(|o| o.clone()),
        detail: None,
        cause: None
    }
}

pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: Some(detail.as_slice().to_str()),
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn internal<S1: Str>(error: S1) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn human<S: Str>(error: S) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: true
    } as Box<CargoError>
}
<commit_msg>Optimized mark_human for ConcreteCargoError<commit_after>use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};

use TomlError = toml::Error;

pub trait CargoError {
    fn description(&self) -> String;
    fn detail(&self) -> Option<String> { None }
    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
    fn is_human(&self) -> bool { false }

    fn to_error<E: FromError<Self>>(self) -> E {
        FromError::from_error(self)
    }

    fn box_error(self) -> Box<CargoError> {
        box self as Box<CargoError>
    }

    fn concrete(&self) -> ConcreteCargoError {
        ConcreteCargoError {
            description: self.description(),
            detail: self.detail(),
            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
            is_human: self.is_human()
        }
    }

    fn with_cause<E: CargoError>(self, cause: E) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.cause = Some(cause.box_error());
        box concrete as Box<CargoError>
    }

    fn mark_human(self) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.is_human = true;
        box concrete as Box<CargoError>
    }
}

pub trait FromError<E> {
    fn from_error(error: E) -> Self;
}

impl<E: CargoError> FromError<E> for Box<CargoError> {
    fn from_error(error: E) -> Box<CargoError> {
        error.box_error()
    }
}

macro_rules! from_error (
    ($ty:ty) => {
        impl FromError<$ty> for $ty {
            fn from_error(error: $ty) -> $ty {
                error
            }
        }
    }
)

impl Show for Box<CargoError> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));
        Ok(())
    }
}

impl CargoError for Box<CargoError> {
    fn description(&self) -> String {
        (*self).description()
    }

    fn detail(&self) -> Option<String> {
        (*self).detail()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        (*self).cause()
    }

    fn is_human(&self) -> bool {
        (*self).is_human()
    }

    fn box_error(self) -> Box<CargoError> {
        self
    }
}

pub type CargoResult<T> = Result<T, Box<CargoError>>;

pub trait BoxError<T> {
    fn box_error(self) -> CargoResult<T>;
}

pub trait ChainError<T> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T> ;
}

impl<T, E: CargoError> BoxError<T> for Result<T, E> {
    fn box_error(self) -> CargoResult<T> {
        self.map_err(|err| err.box_error())
    }
}

impl<T, E: CargoError> ChainError<T> for Result<T, E> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T>  {
        self.map_err(|err| {
            callback().with_cause(err)
        })
    }
}

impl CargoError for IoError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(IoError)

impl CargoError for TomlError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(TomlError)

pub struct ProcessError {
    pub msg: String,
    pub command: String,
    pub exit: Option<ProcessExit>,
    pub output: Option<ProcessOutput>,
    pub detail: Option<String>,
    pub cause: Option<Box<CargoError>>
}

from_error!(ProcessError)

impl Show for ProcessError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        write!(f, "{} (status={})", self.msg, exit)
    }
}

impl CargoError for ProcessError {
    fn description(&self) -> String {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        format!("{} (status={})", self.msg, exit)
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }
}

pub struct ConcreteCargoError {
    description: String,
    detail: Option<String>,
    cause: Option<Box<CargoError>>,
    is_human: bool
}

impl Show for ConcreteCargoError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl CargoError for ConcreteCargoError {
    fn description(&self) -> String {
        self.description.clone()
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn with_cause<E: CargoError>(mut self, err: E) -> Box<CargoError> {
        self.cause = Some(err.box_error());
        box self as Box<CargoError>
    }

    fn mark_human(mut self) -> Box<CargoError> {
        self.is_human = true;
        box self as Box<CargoError>
    }

    fn is_human(&self) -> bool {
        self.is_human
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub struct CliError {
    pub error: Box<CargoError>,
    pub exit_code: uint
}

impl CliError {
    pub fn new<S: Str>(error: S, code: uint) -> CliError {
        let error = human(error.as_slice().to_str());
        CliError::from_boxed(error, code)
    }

    pub fn from_error<E: CargoError + 'static>(error: E, code: uint) -> CliError {
        let error = box error as Box<CargoError>;
        CliError::from_boxed(error, code)
    }

    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
        let error = if error.is_human() {
            error
        } else {
            human("An unknown error occurred").with_cause(error)
        };

        CliError { error: error, exit_code: code }
    }
}

pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
    ProcessError {
        msg: msg.as_slice().to_str(),
        command: command.to_str(),
        exit: status.map(|o| o.clone()),
        output: output.map(|o| o.clone()),
        detail: None,
        cause: None
    }
}

pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: Some(detail.as_slice().to_str()),
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn internal<S1: Str>(error: S1) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn human<S: Str>(error: S) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: true
    } as Box<CargoError>
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use env::Env;
use file_saver::save_to_file;
use version::Version;

pub fn generate(env: &Env) {
    println!("generating sys build script for {}", env.config.library_name);

    let path = env.config.target_path.join("build.rs");

    println!("Generating file {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_build_script(w, env));
}

fn generate_build_script(w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}", "extern crate pkg_config;\n"));

    let ns = env.namespaces.main();
    try!(writeln!(w, "const LIBRARY_NAME: &'static str = \"{}\";", ns.crate_name));
    try!(writeln!(w, "const PACKAGE_NAME: &'static str = \"{}\";",
                  ns.package_name.as_ref().unwrap()));
    try!(writeln!(w, "const VERSIONS: &'static [Version] = &["));
    let versions = ns.versions.iter()
        .filter(|v| **v >= env.config.min_cfg_version);
    for &Version(major, minor, patch) in versions {
        try!(writeln!(w, "\tVersion({}, {}, {}),", major, minor, patch));
    }
    try!(writeln!(w, "];"));

    writeln!(w, "{}",
r##"
fn main() {
    let lib = pkg_config::find_library(PACKAGE_NAME)
        .unwrap_or_else(|e| panic!("{}", e));
    let version = Version::new(&lib.version);
    let mut cfgs = Vec::new();
    for v in VERSIONS.iter().filter(|&&v| v <= version) {
        let cfg = v.to_cfg();
        println!("cargo:rustc-cfg={}", cfg);
        cfgs.push(cfg);
    }
    println!("cargo:cfg={}", cfgs.join(" "));
}

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Version(pub u16, pub u16, pub u16);

impl Version {
    fn new(s: &str) -> Version {
        let mut parts = s.splitn(4, '.')
            .map(|s| s.parse())
            .take_while(Result::is_ok)
            .map(Result::unwrap);
        Version(parts.next().unwrap_or(0),
            parts.next().unwrap_or(0), parts.next().unwrap_or(0))
    }

    fn to_cfg(&self) -> String {
        match *self {
            Version(major, minor, 0) => format!("{}_{}_{}", LIBRARY_NAME, major, minor),
            Version(major, minor, patch) =>
                format!("{}_{}_{}_{}", LIBRARY_NAME, major, minor, patch),
        }
    }
}
"##)
}
<commit_msg>Build script: add GTK_LIB_DIR override and change to version features<commit_after>use std::io::{Result, Write};

use env::Env;
use file_saver::save_to_file;
use regex::Regex;

pub fn generate(env: &Env) {
    println!("generating sys build script for {}", env.config.library_name);

    let path = env.config.target_path.join("build.rs");

    println!("Generating file {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_build_script(w, env));
}

fn generate_build_script(w: &mut Write, env: &Env) -> Result<()> {
    try!(write!(w, "{}",
r##"extern crate pkg_config;
use std::env;
use std::io::prelude::*;
use std::io;
use std::process;

fn main() {
    if let Err(s) = find() {
        let _ = writeln!(io::stderr(), "{}", s);
        process::exit(1);
    }
}

fn find() -> Result<(), String> {
"##));

    let ns = env.namespaces.main();
    let regex = Regex::new(r"^lib(.+)\.so.*$").unwrap();
    let shared_libs: Vec<String> = ns.shared_libs.iter()
        .map(|s| regex.replace(s, "\"$1\""))
        .collect();

    try!(writeln!(w, "\tlet package_name = \"{}\";", ns.package_name.as_ref().unwrap()));
    try!(writeln!(w, "\tlet shared_libs = [{}];", shared_libs.join(", ")));
    try!(write!(w, "\tlet expected_version =\n\t\t"));
    let versions = ns.versions.iter()
        .filter(|v| **v >= env.config.min_cfg_version)
        .skip(1)
        .collect::<Vec<_>>();
    for v in versions.iter().rev() {
        try!(write!(w, "if cfg!({}) {{\n\t\t\t\"{}\"\n\t\t}} else ", v.to_cfg(), v));
    }
    try!(writeln!(w, "{{\n\t\t\t\"{}\"\n\t\t}};", env.config.min_cfg_version));

    writeln!(w, "{}",
r##"
    if let Ok(lib_dir) = env::var("GTK_LIB_DIR") {
        for lib_ in shared_libs.iter() {
            println!("cargo:rustc-link-lib=dylib={}", lib_);
        }
        println!("cargo:rustc-link-search=native={}", lib_dir);
        Ok(())
    } else {
        let lib = try!(pkg_config::find_library(package_name));
        if Version::new(&lib.version) >= Version::new(expected_version) {
            Ok(())
        } else {
            Err(format!("Installed `{}` version `{}` lower than `{}` requested by cargo features",
                        package_name, lib.version, expected_version))
        }
    }
}

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Version(pub u16, pub u16, pub u16);

impl Version {
    fn new(s: &str) -> Version {
        let mut parts = s.splitn(4, '.')
            .map(|s| s.parse())
            .take_while(Result::is_ok)
            .map(Result::unwrap);
        Version(parts.next().unwrap_or(0),
            parts.next().unwrap_or(0), parts.next().unwrap_or(0))
    }
}
"##)
}
<|endoftext|>