<commit_before>use architecture::*;
use goblin;
use loader::*;
use memory::backing::Memory;
use memory::MemoryPermissions;
use std::collections::BTreeSet;
use std::fs::File;
use std::io::Read;
use std::path::Path;

/// Loader for a single ELf file.
#[derive(Debug)]
pub struct Elf {
    base_address: u64,
    bytes: Vec<u8>,
    user_function_entries: Vec<u64>,
    architecture: Box<Architecture>,
}

impl Elf {
    /// Create a new Elf from the given bytes. This Elf will be rebased to the given
    /// base address.
    pub fn new(bytes: Vec<u8>, base_address: u64) -> Result<Elf> {
        let architecture = {
            let elf = goblin::elf::Elf::parse(&bytes).map_err(|_| "Not a valid elf")?;

            let architecture = if elf.header.e_machine == goblin::elf::header::EM_386 {
                Box::new(X86::new())
            } else if elf.header.e_machine == goblin::elf::header::EM_MIPS {
                match elf.header.endianness()? {
                    goblin::container::Endian::Big => Box::new(Mips::new()) as Box<Architecture>,
                    goblin::container::Endian::Little => {
                        Box::new(Mipsel::new()) as Box<Architecture>
                    }
                }
            } else if elf.header.e_machine == goblin::elf::header::EM_PPC {
                match elf.header.endianness()? {
                    goblin::container::Endian::Big => Box::new(Ppc::new()) as Box<Architecture>,
                    goblin::container::Endian::Little => bail!("PPC Little-Endian not supported"),
                }
            } else if elf.header.e_machine == goblin::elf::header::EM_X86_64 {
                Box::new(Amd64::new())
            } else {
                bail!("Unsupported Architecture");
            };

            architecture
        };

        Ok(Elf {
            base_address: base_address,
            bytes: bytes,
            user_function_entries: Vec::new(),
            architecture: architecture,
        })
    }

    /// Get the base address of this Elf where it has been loaded into loader
    /// memory.
    pub fn base_address(&self) -> u64 {
        self.base_address
    }

    /// Load an Elf from a file and use the given base address.
    pub fn from_file_with_base_address(filename: &Path, base_address: u64) -> Result<Elf> {
        let mut file = match File::open(filename) {
            Ok(file) => file,
            Err(e) => {
                return Err(format!("Error opening {}: {}", filename.to_str().unwrap(), e).into())
            }
        };
        let mut buf = Vec::new();
        file.read_to_end(&mut buf)?;
        Elf::new(buf, base_address)
    }

    /// Load an elf from a file and use the base address of 0.
    pub fn from_file(filename: &Path) -> Result<Elf> {
        Elf::from_file_with_base_address(filename, 0)
    }

    /// Allow the user to manually specify a function entry
    pub fn add_user_function(&mut self, address: u64) {
        self.user_function_entries.push(address);
    }

    /// Return the strings from the DT_NEEDED entries.
    pub fn dt_needed(&self) -> Result<Vec<String>> {
        let mut v = Vec::new();

        let elf = self.elf();
        if let Some(dynamic) = elf.dynamic {
            // We need that strtab, and we have to do this one manually.
            // Get the strtab address
            let mut strtab_address = None;
            for dyn in &dynamic.dyns {
                if dyn.d_tag == goblin::elf::dynamic::DT_STRTAB {
                    strtab_address = Some(dyn.d_val);
                    break;
                }
            }
            if strtab_address.is_none() {
                return Ok(v);
            }
            let strtab_address = strtab_address.unwrap();
            // We're going to make a pretty safe assumption that strtab is all
            // in one section
            for section_header in &elf.section_headers {
                if section_header.sh_addr > 0
                    && section_header.sh_addr <= strtab_address
                    && section_header.sh_addr + section_header.sh_size > strtab_address
                {
                    let start =
                        section_header.sh_offset + (strtab_address - section_header.sh_addr);
                    let size = section_header.sh_size - (start - section_header.sh_offset);
                    let start = start as usize;
                    let size = size as usize;
                    let strtab_bytes = self.bytes.get(start..(start + size)).unwrap();
                    let strtab = goblin::strtab::Strtab::new(&strtab_bytes, 0);
                    for dyn in dynamic.dyns {
                        if dyn.d_tag == goblin::elf::dynamic::DT_NEEDED {
                            let so_name = &strtab[dyn.d_val as usize];
                            v.push(so_name.to_string());
                        }
                    }
                    return Ok(v);
                }
            }
            // if we got here, we didn't return a vector (I think ;))
            panic!("Failed to get Dynamic strtab");
        }

        Ok(v)
    }

    /// Return the goblin::elf::Elf for this elf.
    pub fn elf(&self) -> goblin::elf::Elf {
        goblin::elf::Elf::parse(&self.bytes).unwrap()
    }

    /// Return all symbols exported from this Elf
    pub fn exported_symbols(&self) -> Vec<Symbol> {
        let mut v = Vec::new();
        let elf = self.elf();
        for sym in elf.dynsyms.iter() {
            if sym.st_value == 0 || sym.st_shndx == 0 {
                continue;
            }
            if sym.st_bind() == goblin::elf::sym::STB_GLOBAL
                || sym.st_bind() == goblin::elf::sym::STB_WEAK
            {
                v.push(Symbol::new(
                    &elf.dynstrtab[sym.st_name],
                    sym.st_value + self.base_address(),
                ));
            }
        }

        v
    }

    /// Return all symbols for this Elf
    pub fn symbols(&self) -> Vec<Symbol> {
        let elf = self.elf();
        let mut symbols = Vec::new();
        for sym in elf.dynsyms.iter() {
            if sym.st_value == 0 {
                continue;
            }
            symbols.push(Symbol::new(
                &elf.dynstrtab[sym.st_name],
                sym.st_value + self.base_address(),
            ));
        }

        for sym in elf.syms.iter() {
            if sym.st_value == 0 {
                continue;
            }
            symbols.push(Symbol::new(
                &elf.strtab[sym.st_name],
                sym.st_value + self.base_address(),
            ));
        }

        for rel in elf.pltrelocs.iter() {
            let sym = match elf.dynsyms.get(rel.r_sym) {
                Some(sym) => sym,
                None => continue,
            };

            let name = &elf.dynstrtab[sym.st_name];
            symbols.push(Symbol::new(name, rel.r_offset));
        }

        symbols.sort();
        symbols.dedup();
        symbols
    }
}

impl Loader for Elf {
    fn memory(&self) -> Result<Memory> {
        let elf = self.elf();
        let mut memory = Memory::new(self.architecture().endian());

        println!("BEGIN CALL TO MEMORY");
        for ph in elf.program_headers {
            if ph.p_type == goblin::elf::program_header::PT_LOAD {
                let file_range = (ph.p_offset as usize)..((ph.p_offset + ph.p_filesz) as usize);
                let mut bytes = self.bytes.get(file_range).ok_or("Malformed Elf")?.to_vec();

                if bytes.len() != ph.p_memsz as usize {
                    bytes.append(&mut vec![0; (ph.p_memsz - ph.p_filesz) as usize]);
                }

                let mut permissions = memory::MemoryPermissions::NONE;
                if ph.p_flags & goblin::elf::program_header::PF_R != 0 {
                    permissions |= MemoryPermissions::READ;
                }
                if ph.p_flags & goblin::elf::program_header::PF_W != 0 {
                    permissions |= MemoryPermissions::WRITE;
                }
                if ph.p_flags & goblin::elf::program_header::PF_X != 0 {
                    permissions |= MemoryPermissions::EXECUTE;
                }

                memory.set_memory(ph.p_vaddr + self.base_address, bytes, permissions);
            }
        }

        Ok(memory)
    }

    fn function_entries(&self) -> Result<Vec<FunctionEntry>> {
        let elf = self.elf();

        let mut function_entries = Vec::new();

        let mut functions_added: BTreeSet<u64> = BTreeSet::new();

        // dynamic symbols
        for sym in &elf.dynsyms {
            if sym.is_function() && sym.st_value != 0 && sym.st_shndx > 0 {
                let name = &elf.dynstrtab[sym.st_name];
                function_entries.push(FunctionEntry::new(
                    sym.st_value + self.base_address,
                    Some(name.to_string()),
                ));
                functions_added.insert(sym.st_value);
            }
        }

        // normal symbols
        for sym in &elf.syms {
            if sym.is_function() && sym.st_value != 0 && sym.st_shndx > 0 {
                let name = &elf.strtab[sym.st_name];
                function_entries.push(FunctionEntry::new(
                    sym.st_value + self.base_address,
                    Some(name.to_string()),
                ));
                functions_added.insert(sym.st_value);
            }
        }

        if !functions_added.contains(&elf.header.e_entry) {
            function_entries.push(FunctionEntry::new(
                elf.header.e_entry + self.base_address,
                None,
            ));
        }

        for user_function_entry in &self.user_function_entries {
            if functions_added
                .get(&(user_function_entry + self.base_address))
                .is_some()
            {
                continue;
            }

            function_entries.push(FunctionEntry::new(
                user_function_entry + self.base_address,
                Some(format!("user_function_{:x}", user_function_entry)),
            ));
        }

        Ok(function_entries)
    }

    fn program_entry(&self) -> u64 {
        self.elf().header.e_entry
    }

    fn architecture(&self) -> &Architecture {
        self.architecture.as_ref()
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn symbols(&self) -> Vec<Symbol> {
        self.symbols()
    }
}
<commit_msg>remove extraneous println<commit_after>use architecture::*;
use goblin;
use loader::*;
use memory::backing::Memory;
use memory::MemoryPermissions;
use std::collections::BTreeSet;
use std::fs::File;
use std::io::Read;
use std::path::Path;

/// Loader for a single ELf file.
#[derive(Debug)]
pub struct Elf {
    base_address: u64,
    bytes: Vec<u8>,
    user_function_entries: Vec<u64>,
    architecture: Box<Architecture>,
}

impl Elf {
    /// Create a new Elf from the given bytes. This Elf will be rebased to the given
    /// base address.
    pub fn new(bytes: Vec<u8>, base_address: u64) -> Result<Elf> {
        let architecture = {
            let elf = goblin::elf::Elf::parse(&bytes).map_err(|_| "Not a valid elf")?;

            let architecture = if elf.header.e_machine == goblin::elf::header::EM_386 {
                Box::new(X86::new())
            } else if elf.header.e_machine == goblin::elf::header::EM_MIPS {
                match elf.header.endianness()? {
                    goblin::container::Endian::Big => Box::new(Mips::new()) as Box<Architecture>,
                    goblin::container::Endian::Little => {
                        Box::new(Mipsel::new()) as Box<Architecture>
                    }
                }
            } else if elf.header.e_machine == goblin::elf::header::EM_PPC {
                match elf.header.endianness()? {
                    goblin::container::Endian::Big => Box::new(Ppc::new()) as Box<Architecture>,
                    goblin::container::Endian::Little => bail!("PPC Little-Endian not supported"),
                }
            } else if elf.header.e_machine == goblin::elf::header::EM_X86_64 {
                Box::new(Amd64::new())
            } else {
                bail!("Unsupported Architecture");
            };

            architecture
        };

        Ok(Elf {
            base_address: base_address,
            bytes: bytes,
            user_function_entries: Vec::new(),
            architecture: architecture,
        })
    }

    /// Get the base address of this Elf where it has been loaded into loader
    /// memory.
    pub fn base_address(&self) -> u64 {
        self.base_address
    }

    /// Load an Elf from a file and use the given base address.
    pub fn from_file_with_base_address(filename: &Path, base_address: u64) -> Result<Elf> {
        let mut file = match File::open(filename) {
            Ok(file) => file,
            Err(e) => {
                return Err(format!("Error opening {}: {}", filename.to_str().unwrap(), e).into())
            }
        };
        let mut buf = Vec::new();
        file.read_to_end(&mut buf)?;
        Elf::new(buf, base_address)
    }

    /// Load an elf from a file and use the base address of 0.
    pub fn from_file(filename: &Path) -> Result<Elf> {
        Elf::from_file_with_base_address(filename, 0)
    }

    /// Allow the user to manually specify a function entry
    pub fn add_user_function(&mut self, address: u64) {
        self.user_function_entries.push(address);
    }

    /// Return the strings from the DT_NEEDED entries.
    pub fn dt_needed(&self) -> Result<Vec<String>> {
        let mut v = Vec::new();

        let elf = self.elf();
        if let Some(dynamic) = elf.dynamic {
            // We need that strtab, and we have to do this one manually.
            // Get the strtab address
            let mut strtab_address = None;
            for dyn in &dynamic.dyns {
                if dyn.d_tag == goblin::elf::dynamic::DT_STRTAB {
                    strtab_address = Some(dyn.d_val);
                    break;
                }
            }
            if strtab_address.is_none() {
                return Ok(v);
            }
            let strtab_address = strtab_address.unwrap();
            // We're going to make a pretty safe assumption that strtab is all
            // in one section
            for section_header in &elf.section_headers {
                if section_header.sh_addr > 0
                    && section_header.sh_addr <= strtab_address
                    && section_header.sh_addr + section_header.sh_size > strtab_address
                {
                    let start =
                        section_header.sh_offset + (strtab_address - section_header.sh_addr);
                    let size = section_header.sh_size - (start - section_header.sh_offset);
                    let start = start as usize;
                    let size = size as usize;
                    let strtab_bytes = self.bytes.get(start..(start + size)).unwrap();
                    let strtab = goblin::strtab::Strtab::new(&strtab_bytes, 0);
                    for dyn in dynamic.dyns {
                        if dyn.d_tag == goblin::elf::dynamic::DT_NEEDED {
                            let so_name = &strtab[dyn.d_val as usize];
                            v.push(so_name.to_string());
                        }
                    }
                    return Ok(v);
                }
            }
            // if we got here, we didn't return a vector (I think ;))
            panic!("Failed to get Dynamic strtab");
        }

        Ok(v)
    }

    /// Return the goblin::elf::Elf for this elf.
    pub fn elf(&self) -> goblin::elf::Elf {
        goblin::elf::Elf::parse(&self.bytes).unwrap()
    }

    /// Return all symbols exported from this Elf
    pub fn exported_symbols(&self) -> Vec<Symbol> {
        let mut v = Vec::new();
        let elf = self.elf();
        for sym in elf.dynsyms.iter() {
            if sym.st_value == 0 || sym.st_shndx == 0 {
                continue;
            }
            if sym.st_bind() == goblin::elf::sym::STB_GLOBAL
                || sym.st_bind() == goblin::elf::sym::STB_WEAK
            {
                v.push(Symbol::new(
                    &elf.dynstrtab[sym.st_name],
                    sym.st_value + self.base_address(),
                ));
            }
        }

        v
    }

    /// Return all symbols for this Elf
    pub fn symbols(&self) -> Vec<Symbol> {
        let elf = self.elf();
        let mut symbols = Vec::new();
        for sym in elf.dynsyms.iter() {
            if sym.st_value == 0 {
                continue;
            }
            symbols.push(Symbol::new(
                &elf.dynstrtab[sym.st_name],
                sym.st_value + self.base_address(),
            ));
        }

        for sym in elf.syms.iter() {
            if sym.st_value == 0 {
                continue;
            }
            symbols.push(Symbol::new(
                &elf.strtab[sym.st_name],
                sym.st_value + self.base_address(),
            ));
        }

        for rel in elf.pltrelocs.iter() {
            let sym = match elf.dynsyms.get(rel.r_sym) {
                Some(sym) => sym,
                None => continue,
            };

            let name = &elf.dynstrtab[sym.st_name];
            symbols.push(Symbol::new(name, rel.r_offset));
        }

        symbols.sort();
        symbols.dedup();
        symbols
    }
}

impl Loader for Elf {
    fn memory(&self) -> Result<Memory> {
        let elf = self.elf();
        let mut memory = Memory::new(self.architecture().endian());

        for ph in elf.program_headers {
            if ph.p_type == goblin::elf::program_header::PT_LOAD {
                let file_range = (ph.p_offset as usize)..((ph.p_offset + ph.p_filesz) as usize);
                let mut bytes = self.bytes.get(file_range).ok_or("Malformed Elf")?.to_vec();

                if bytes.len() != ph.p_memsz as usize {
                    bytes.append(&mut vec![0; (ph.p_memsz - ph.p_filesz) as usize]);
                }

                let mut permissions = memory::MemoryPermissions::NONE;
                if ph.p_flags & goblin::elf::program_header::PF_R != 0 {
                    permissions |= MemoryPermissions::READ;
                }
                if ph.p_flags & goblin::elf::program_header::PF_W != 0 {
                    permissions |= MemoryPermissions::WRITE;
                }
                if ph.p_flags & goblin::elf::program_header::PF_X != 0 {
                    permissions |= MemoryPermissions::EXECUTE;
                }

                memory.set_memory(ph.p_vaddr + self.base_address, bytes, permissions);
            }
        }

        Ok(memory)
    }

    fn function_entries(&self) -> Result<Vec<FunctionEntry>> {
        let elf = self.elf();

        let mut function_entries = Vec::new();

        let mut functions_added: BTreeSet<u64> = BTreeSet::new();

        // dynamic symbols
        for sym in &elf.dynsyms {
            if sym.is_function() && sym.st_value != 0 && sym.st_shndx > 0 {
                let name = &elf.dynstrtab[sym.st_name];
                function_entries.push(FunctionEntry::new(
                    sym.st_value + self.base_address,
                    Some(name.to_string()),
                ));
                functions_added.insert(sym.st_value);
            }
        }

        // normal symbols
        for sym in &elf.syms {
            if sym.is_function() && sym.st_value != 0 && sym.st_shndx > 0 {
                let name = &elf.strtab[sym.st_name];
                function_entries.push(FunctionEntry::new(
                    sym.st_value + self.base_address,
                    Some(name.to_string()),
                ));
                functions_added.insert(sym.st_value);
            }
        }

        if !functions_added.contains(&elf.header.e_entry) {
            function_entries.push(FunctionEntry::new(
                elf.header.e_entry + self.base_address,
                None,
            ));
        }

        for user_function_entry in &self.user_function_entries {
            if functions_added
                .get(&(user_function_entry + self.base_address))
                .is_some()
            {
                continue;
            }

            function_entries.push(FunctionEntry::new(
                user_function_entry + self.base_address,
                Some(format!("user_function_{:x}", user_function_entry)),
            ));
        }

        Ok(function_entries)
    }

    fn program_entry(&self) -> u64 {
        self.elf().header.e_entry
    }

    fn architecture(&self) -> &Architecture {
        self.architecture.as_ref()
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn symbols(&self) -> Vec<Symbol> {
        self.symbols()
    }
}
<|endoftext|>


<commit_before>use crate::sites::*;
use std::collections::BTreeSet;

pub const LUT4_PIN_MAP : &[(&str, &str)] = &[
    ("A", "A"),
    ("B", "B"),
    ("C", "C"),
    ("D", "D"),
    ("Z", "F"),
];

// TODO: need to add an extra pip to use LUT->DI path
const FD1P3BX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("PD", "LSR"),
    ("Q", "Q"),
];
const FD1P3DX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("CD", "LSR"),
    ("Q", "Q"),
];
const FD1P3IX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("CD", "LSR"),
    ("Q", "Q"),
];
const FD1P3JX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("PD", "LSR"),
    ("Q", "Q"),
];

const IB_PIN_MAP : &[(&str, &str)] = &[
    ("I", "B"),
    ("O", "O"),
];

const OB_PIN_MAP : &[(&str, &str)] = &[
    ("I", "I"),
    ("O", "B"),
];

// TODO: add back DFFs once we have some constraints set up

const BEL_CELL_TYPES : &[(&str, &[&str])] = &[
    ("OXIDE_COMB", &["LUT4"]),
    ("OXIDE_FF", &["FD1P3BX", "FD1P3DX", "FD1P3IX", "FD1P3JX"]),
    ("SEIO33_CORE", &["IB", "OB"]),
    ("SEIO18_CORE", &["IB", "OB"]),
    ("DCC", &["DCC"]),
    ("OSC_CORE", &["OSC_CORE"]),
    ("OXIDE_EBR", &["DP16K_MODE", "PDP16K_MODE", "PDPSC16K_MODE"]),
];

fn conv_map(map: &[(&str, &str)]) -> Vec<(String, String)> {
    map.iter().map(|(c, b)| (c.to_string(), b.to_string())).collect()
}

fn auto_map(site: &Site, bel: &SiteBel) -> Vec<(String, String)> {
    bel.pins.iter().map(|p| &site.bel_pins[*p]).map(|p| (p.pin_name.to_string(), p.pin_name.to_string())).collect()
}

fn offset_bus(pin: &str, prefix: &str, rep: &str, offset: i32) -> String {
    let idx = pin[prefix.len()..].parse::<i32>().unwrap();
    format!("{}{}", rep, idx + offset)
}

fn bram_map(cell_type: &str, site: &Site, bel: &SiteBel) -> Vec<(String, String)> {
    let mut result = Vec::new();
    // PDP16K is a 1:1 map
    for bel_pin in bel.pins.iter().map(|p| &site.bel_pins[*p].pin_name) {
        let mut cell_pin = bel_pin.into();
        match cell_type {
            "DP16K_MODE" => { /* all pins pass through */ }
            "PDPSC16K_MODE" | "PDP16K_MODE" => {
                if bel_pin.starts_with("ADA") { cell_pin = bel_pin.replace("ADA", "ADW") }
                else if bel_pin.starts_with("ADB") { cell_pin = bel_pin.replace("ADB", "ADR") }
                else if bel_pin.starts_with("CSA") { cell_pin = bel_pin.replace("CSA", "CSW") }
                else if bel_pin.starts_with("CSB") { cell_pin = bel_pin.replace("CSB", "CSR") }
                else if bel_pin == "CLKA" { cell_pin = if cell_type == "PDPSC16K_MODE" { "CLK" } else { "CLKW" }.into() }
                else if bel_pin == "CLKB" { cell_pin = if cell_type == "PDPSC16K_MODE" { "CLK" } else { "CLKR" }.into() }
                else if bel_pin == "CEA" { cell_pin = "CEW".into() }
                else if bel_pin == "CEB" { cell_pin = "CER".into() }
                else if bel_pin == "RSTA" || bel_pin == "RSTB" { cell_pin = "RST".into() }
                else if bel_pin.starts_with("DIA") { cell_pin = bel_pin.replace("DIA", "DI") }
                else if bel_pin.starts_with("DIB") { cell_pin = offset_bus(bel_pin, "DIA", "DI", 18) }
                else if bel_pin.starts_with("DOA") { cell_pin = offset_bus(bel_pin, "DOA", "DO", 18) }
                else if bel_pin.starts_with("DOB") { cell_pin = bel_pin.replace("DOB", "DO") }
            },
            _ => unimplemented!(),
        }
        result.push((cell_pin, bel_pin.to_string()))
    }
    result
}

fn get_map_for_cell_bel(cell_type: &str, site: &Site, bel: &SiteBel) -> Vec<(String, String)> {
    match cell_type {
        "LUT4" => conv_map(LUT4_PIN_MAP),
        "FD1P3BX" => conv_map(FD1P3BX_PIN_MAP),
        "FD1P3DX" => conv_map(FD1P3DX_PIN_MAP),
        "FD1P3IX" => conv_map(FD1P3IX_PIN_MAP),
        "FD1P3JX" => conv_map(FD1P3JX_PIN_MAP),
        "IB" => conv_map(IB_PIN_MAP),
        "OB" => conv_map(OB_PIN_MAP),
        "DCC" => auto_map(site, bel),
        "OSC_CORE" => auto_map(site, bel),
        "PDPSC16K_MODE" | "PDP16K_MODE" | "DP16K_MODE" => bram_map(cell_type, site, bel),
        _ => unimplemented!(),
    }
}

#[derive(Clone)]
pub struct PinMap {
    pub cell_type: String,
    pub bels: Vec<String>,
    pub pin_map: Vec<(String, String)>,
}

pub fn get_pin_maps(site: &Site) -> Vec<PinMap> {
    let mut map = Vec::new();
    let unique_bel_types : BTreeSet<String>
        = site.bels.iter().filter_map(|b| if b.bel_class == SiteBelClass::BEL { Some(b.bel_type.to_string()) } else { None }).collect();
    for bel_type in unique_bel_types.iter() {
        if let Some((_, cell_types)) = BEL_CELL_TYPES.iter().find(|(bt, _)| bt == bel_type) {
            for cell_type in cell_types.iter() {
                map.push(PinMap {
                    cell_type: cell_type.to_string(),
                    bels: site.bels.iter().filter_map(|b| if &b.bel_type == bel_type { Some(b.name.to_string()) } else { None }).collect(),
                    pin_map: get_map_for_cell_bel(cell_type, site, site.bels.iter().find(|b| &b.bel_type == bel_type).unwrap()),
                });
            }
        }
    }
    return map;
}
<commit_msg>interchange: Add constant BRAM pins<commit_after>use crate::sites::*;
use std::collections::BTreeSet;

pub const LUT4_PIN_MAP : &[(&str, &str)] = &[
    ("A", "A"),
    ("B", "B"),
    ("C", "C"),
    ("D", "D"),
    ("Z", "F"),
];

// TODO: need to add an extra pip to use LUT->DI path
const FD1P3BX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("PD", "LSR"),
    ("Q", "Q"),
];
const FD1P3DX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("CD", "LSR"),
    ("Q", "Q"),
];
const FD1P3IX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("CD", "LSR"),
    ("Q", "Q"),
];
const FD1P3JX_PIN_MAP : &[(&str, &str)] = &[
    ("D", "M"),
    ("CK", "CLK"),
    ("SP", "CE"),
    ("PD", "LSR"),
    ("Q", "Q"),
];

const IB_PIN_MAP : &[(&str, &str)] = &[
    ("I", "B"),
    ("O", "O"),
];

const OB_PIN_MAP : &[(&str, &str)] = &[
    ("I", "I"),
    ("O", "B"),
];

// TODO: add back DFFs once we have some constraints set up

const BEL_CELL_TYPES : &[(&str, &[&str])] = &[
    ("OXIDE_COMB", &["LUT4"]),
    ("OXIDE_FF", &["FD1P3BX", "FD1P3DX", "FD1P3IX", "FD1P3JX"]),
    ("SEIO33_CORE", &["IB", "OB"]),
    ("SEIO18_CORE", &["IB", "OB"]),
    ("DCC", &["DCC"]),
    ("OSC_CORE", &["OSC_CORE"]),
    ("OXIDE_EBR", &["DP16K_MODE", "PDP16K_MODE", "PDPSC16K_MODE"]),
];

fn conv_map(map: &[(&str, &str)]) -> Vec<(String, String)> {
    map.iter().map(|(c, b)| (c.to_string(), b.to_string())).collect()
}

fn auto_map(site: &Site, bel: &SiteBel) -> Vec<(String, String)> {
    bel.pins.iter().map(|p| &site.bel_pins[*p]).map(|p| (p.pin_name.to_string(), p.pin_name.to_string())).collect()
}

fn offset_bus(pin: &str, prefix: &str, rep: &str, offset: i32) -> String {
    let idx = pin[prefix.len()..].parse::<i32>().unwrap();
    format!("{}{}", rep, idx + offset)
}

fn bram_map(cell_type: &str, site: &Site, bel: &SiteBel) -> Vec<(String, String)> {
    let mut result = Vec::new();
    // PDP16K is a 1:1 map
    for bel_pin in bel.pins.iter().map(|p| &site.bel_pins[*p].pin_name) {
        let mut cell_pin = bel_pin.into();
        match cell_type {
            "DP16K_MODE" => { /* all pins pass through */ }
            "PDPSC16K_MODE" | "PDP16K_MODE" => {
                if bel_pin.starts_with("ADA") { cell_pin = bel_pin.replace("ADA", "ADW") }
                else if bel_pin.starts_with("ADB") { cell_pin = bel_pin.replace("ADB", "ADR") }
                else if bel_pin.starts_with("CSA") { cell_pin = bel_pin.replace("CSA", "CSW") }
                else if bel_pin.starts_with("CSB") { cell_pin = bel_pin.replace("CSB", "CSR") }
                else if bel_pin == "CLKA" { cell_pin = if cell_type == "PDPSC16K_MODE" { "CLK" } else { "CLKW" }.into() }
                else if bel_pin == "CLKB" { cell_pin = if cell_type == "PDPSC16K_MODE" { "CLK" } else { "CLKR" }.into() }
                else if bel_pin == "CEA" { cell_pin = "CEW".into() }
                else if bel_pin == "CEB" { cell_pin = "CER".into() }
                else if bel_pin == "RSTA" || bel_pin == "RSTB" { cell_pin = "RST".into() }
                else if bel_pin.starts_with("DIA") { cell_pin = bel_pin.replace("DIA", "DI") }
                else if bel_pin.starts_with("DIB") { cell_pin = offset_bus(bel_pin, "DIA", "DI", 18) }
                else if bel_pin.starts_with("DOA") { cell_pin = offset_bus(bel_pin, "DOA", "DO", 18) }
                else if bel_pin.starts_with("DOB") { cell_pin = bel_pin.replace("DOB", "DO") }
            },
            _ => unimplemented!(),
        }
        result.push((cell_pin, bel_pin.to_string()))
    }
    result.extend(conv_map(&[
        ("VCC", "DWS0"),
        ("VCC", "DWS1"),
        ("VCC", "DWS2"),
        ("VCC", "DWS3"),
        ("VCC", "DWS4"),
    ]));
    result
}

fn get_map_for_cell_bel(cell_type: &str, site: &Site, bel: &SiteBel) -> Vec<(String, String)> {
    match cell_type {
        "LUT4" => conv_map(LUT4_PIN_MAP),
        "FD1P3BX" => conv_map(FD1P3BX_PIN_MAP),
        "FD1P3DX" => conv_map(FD1P3DX_PIN_MAP),
        "FD1P3IX" => conv_map(FD1P3IX_PIN_MAP),
        "FD1P3JX" => conv_map(FD1P3JX_PIN_MAP),
        "IB" => conv_map(IB_PIN_MAP),
        "OB" => conv_map(OB_PIN_MAP),
        "DCC" => auto_map(site, bel),
        "OSC_CORE" => auto_map(site, bel),
        "PDPSC16K_MODE" | "PDP16K_MODE" | "DP16K_MODE" => bram_map(cell_type, site, bel),
        _ => unimplemented!(),
    }
}

#[derive(Clone)]
pub struct PinMap {
    pub cell_type: String,
    pub bels: Vec<String>,
    pub pin_map: Vec<(String, String)>,
}

pub fn get_pin_maps(site: &Site) -> Vec<PinMap> {
    let mut map = Vec::new();
    let unique_bel_types : BTreeSet<String>
        = site.bels.iter().filter_map(|b| if b.bel_class == SiteBelClass::BEL { Some(b.bel_type.to_string()) } else { None }).collect();
    for bel_type in unique_bel_types.iter() {
        if let Some((_, cell_types)) = BEL_CELL_TYPES.iter().find(|(bt, _)| bt == bel_type) {
            for cell_type in cell_types.iter() {
                map.push(PinMap {
                    cell_type: cell_type.to_string(),
                    bels: site.bels.iter().filter_map(|b| if &b.bel_type == bel_type { Some(b.name.to_string()) } else { None }).collect(),
                    pin_map: get_map_for_cell_bel(cell_type, site, site.bels.iter().find(|b| &b.bel_type == bel_type).unwrap()),
                });
            }
        }
    }
    return map;
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate image;
extern crate rayon;
extern crate twox_hash;

extern crate utils;
extern crate errors;

use std::path::{Path, PathBuf};
use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use std::collections::hash_map::Entry as HEntry;
use std::fs::{self, File};

use regex::Regex;
use image::{GenericImage, FilterType};
use image::jpeg::JPEGEncoder;
use rayon::prelude::*;
use twox_hash::XxHash;

use utils::fs as ufs;
use errors::{Result, ResultExt};

static RESIZED_SUBDIR: &'static str = "_resized_images";
lazy_static!{
    pub static ref RESIZED_FILENAME: Regex = Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.]jpg"#).unwrap();
}

/// Describes the precise kind of a resize operation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResizeOp {
    /// A simple scale operation that doesn't take aspect ratio into account
    Scale(u32, u32),
    /// Scales the image to a specified width with height computed such that aspect ratio is preserved
    FitWidth(u32),
    /// Scales the image to a specified height with width computed such that aspect ratio is preserved
    FitHeight(u32),
    /// Scales the image such that it fits within the specified width and height preserving aspect ratio.
    /// Either dimension may end up being smaller, but never larger than specified.
    Fit(u32, u32),
    /// Scales the image such that it fills the specified width and height. Output will always have the exact dimensions specified.
    /// The part of the image that doesn't fit in the thumbnail due to differing aspect ratio will be cropped away, if any.
    Fill(u32, u32),
}

impl ResizeOp {
    pub fn from_args(op: &str, width: Option<u32>, height: Option<u32>) -> Result<ResizeOp> {
        use ResizeOp::*;

        // Validate args:
        match op {
            "fit_width" => if width.is_none() { return Err(format!("op=\"fit_width\" requires a `width` argument").into()) },
            "fit_height" => if height.is_none() { return Err(format!("op=\"fit_height\" requires a `height` argument").into()) },
            "scale" | "fit" | "fill" => if width.is_none() || height.is_none() {
                return Err(format!("op={} requires a `width` and `height` argument", op).into())
            },
            _ => return Err(format!("Invalid image resize operation: {}", op).into())
        };

        Ok(match op {
            "scale" => Scale(width.unwrap(), height.unwrap()),
            "fit_width" => FitWidth(width.unwrap()),
            "fit_height" => FitHeight(height.unwrap()),
            "fit" => Fit(width.unwrap(), height.unwrap()),
            "fill" => Fill(width.unwrap(), height.unwrap()),
            _ => unreachable!(),
        })
    }

    pub fn width(self) -> Option<u32> {
        use ResizeOp::*;

        match self {
            Scale(w, _) => Some(w),
            FitWidth(w) => Some(w),
            FitHeight(_) => None,
            Fit(w, _) => Some(w),
            Fill(w, _) => Some(w),
        }
    }

    pub fn height(self) -> Option<u32> {
        use ResizeOp::*;

        match self {
            Scale(_, h) => Some(h),
            FitWidth(_) => None,
            FitHeight(h) => Some(h),
            Fit(_, h) => Some(h),
            Fill(_, h) => Some(h),
        }
    }
}

impl From<ResizeOp> for u8 {
    fn from(op: ResizeOp) -> u8 {
        use ResizeOp::*;

        match op {
            Scale(_, _) => 1,
            FitWidth(_) => 2,
            FitHeight(_) => 3,
            Fit(_, _) => 4,
            Fill(_, _) => 5,
        }
    }
}

impl Hash for ResizeOp {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        hasher.write_u8(u8::from(*self));
        if let Some(w) = self.width()  { hasher.write_u32(w); }
        if let Some(h) = self.height() { hasher.write_u32(h); }
    }
}

/// Holds all data needed to perform a resize operation
#[derive(Debug, PartialEq, Eq)]
pub struct ImageOp {
    source: String,
    op: ResizeOp,
    quality: u8,
    /// Hash of the above parameters
    hash: u64,
    collision: Option<u32>,
}

impl ImageOp {
    pub fn new(source: String, op: ResizeOp, quality: u8) -> ImageOp {
        let mut hasher = XxHash::with_seed(0);
        hasher.write(source.as_ref());
        op.hash(&mut hasher);
        hasher.write_u8(quality);
        let hash = hasher.finish();

        ImageOp { source, op, quality, hash, collision: None }
    }

    pub fn from_args(source: String, op: &str, width: Option<u32>, height: Option<u32>, quality: u8) -> Result<ImageOp> {
        let op = ResizeOp::from_args(op, width, height)?;
        Ok(Self::new(source, op, quality))
    }

    fn num_collisions(&self) -> u32 { self.collision.unwrap_or(0) }

    fn perform(&self, content_path: &Path, target_path: &Path) -> Result<()> {
        use ResizeOp::*;

        let src_path = content_path.join(&self.source);
        if !ufs::file_stale(&src_path, target_path) {
            return Ok(())
        }

        let mut img = image::open(&src_path)?;
        let (img_w, img_h) = img.dimensions();

        const RESIZE_FILTER: FilterType = FilterType::Gaussian;
        const RATIO_EPSILLION: f32 = 0.1;

        let img = match self.op {
            Scale(w, h) => img.resize_exact(w, h, RESIZE_FILTER),
            FitWidth(w) => img.resize(w, u32::max_value(), RESIZE_FILTER),
            FitHeight(h) => img.resize(u32::max_value(), h, RESIZE_FILTER),
            Fit(w, h) => img.resize(w, h, RESIZE_FILTER),
            Fill(w, h) => {
                let factor_w = img_w as f32 / w as f32;
                let factor_h = img_h as f32 / h as f32;

                if (factor_w - factor_h).abs() <= RATIO_EPSILLION {
                    // If the horizontal and vertical factor is very similar, that means the aspect is similar enough
                    // that there's not much point in cropping, so just perform a simple scale in this case.
                    img.resize_exact(w, h, RESIZE_FILTER)
                } else {
                    // We perform the fill such that a crop is performed first and then resize_exact can be used,
                    // which should be cheaper than resizing and then cropping (smaller number of pixels to resize).

                    let (crop_w, crop_h) = if factor_w < factor_h {
                        (img_w, (factor_w * h as f32).round() as u32)
                    } else {
                        ((factor_h * w as f32).round() as u32, img_h)
                    };

                    let (offset_w, offset_h) = if factor_w < factor_h {
                        (0, (img_h - crop_h) / 2)
                    } else {
                        ((img_w - crop_w) / 2, 0)
                    };

                    img.crop(offset_w, offset_h, crop_w, crop_h).resize_exact(w, h, RESIZE_FILTER)
                }
            },
        };

        let mut f = File::create(target_path)?;
        let mut enc = JPEGEncoder::new_with_quality(&mut f, self.quality);
        let (img_w, img_h) = img.dimensions();
        enc.encode(&img.raw_pixels(), img_w, img_h, img.color())?;
        Ok(())
    }
}

/// A strcture into which image operations can be enqueued and then performed.
/// All output is written in a subdirectory in `static_path`,
/// taking care of file stale status based on timestamps and possible hash collisions.
#[derive(Debug)]
pub struct Processor {
    content_path: PathBuf,
    resized_path: PathBuf,
    resized_url: String,
    /// A map of a ImageOps by their stored hash.
    /// Note that this cannot be a HashSet, because hashest handles collisions and we don't want that,
    /// we need to be aware of and handle collisions ourselves.
    img_ops: HashMap<u64, ImageOp>,
    /// Hash collisions go here:
    img_ops_collisions: Vec<ImageOp>,
}

impl Processor {
    pub fn new(content_path: PathBuf, static_path: &Path, base_url: &str) -> Processor {
        Processor {
            content_path,
            resized_path: static_path.join(RESIZED_SUBDIR),
            resized_url: Self::resized_url(base_url),
            img_ops: HashMap::new(),
            img_ops_collisions: Vec::new(),
        }
    }

    fn resized_url(base_url: &str) -> String {
        if base_url.ends_with('/') {
             format!("{}{}", base_url, RESIZED_SUBDIR)
        } else {
             format!("{}/{}", base_url, RESIZED_SUBDIR)
        }
    }

    pub fn set_base_url(&mut self, base_url: &str) {
        self.resized_url = Self::resized_url(base_url);
    }

    pub fn source_exists(&self, source: &str) -> bool {
        self.content_path.join(source).exists()
    }

    pub fn num_img_ops(&self) -> usize {
        self.img_ops.len() + self.img_ops_collisions.len()
    }

    fn insert_with_collisions(&mut self, mut img_op: ImageOp) -> u32 {
        match self.img_ops.entry(img_op.hash) {
            HEntry::Occupied(entry) => if *entry.get() == img_op { return 0; },
            HEntry::Vacant(entry) => {
                entry.insert(img_op);
                return 0;
            },
        }

        // If we get here, that means a hash collision.
        // This is detected when there is an ImageOp with the same hash in the `img_ops` map but which is not equal to this one.
        // To deal with this, all collisions get a (random) sequential ID number.

        // First try to look up this ImageOp in `img_ops_collisions`, maybe we've already seen the same ImageOp
        let mut num = 1;
        for op in self.img_ops_collisions.iter().filter(|op| op.hash == img_op.hash) {
            if *op == img_op {
                // This is a colliding ImageOp, but we've already seen the very same one, so just return its ID
                return num;
            } else {
                num += 1;
            }
        }

        // If we get here, that means this is a new colliding ImageOp and `num` has the next free ID
        if num == 1 {
            self.img_ops.get_mut(&img_op.hash).unwrap().collision = Some(0);
        }
        img_op.collision = Some(num);
        self.img_ops_collisions.push(img_op);
        num
    }

    fn op_filename(hash: u64, num_collisions: u32) -> String {
        // Please keep this in sync with RESIZED_FILENAME
        assert!(num_collisions < 256, "Unexpectedly large number of collisions: {}", num_collisions);
        format!("{:016x}{:02x}.jpg", hash, num_collisions)
    }

    fn op_url(&self, hash: u64, num_collisions: u32) -> String {
        format!("{}/{}", &self.resized_url, Self::op_filename(hash, num_collisions))
    }

    pub fn insert(&mut self, img_op: ImageOp) -> String {
        let hash = img_op.hash;
        let num_collisions = self.insert_with_collisions(img_op);
        self.op_url(hash, num_collisions)
    }

    pub fn prune(&self) -> Result<()> {
        ufs::ensure_directory_exists(&self.resized_path)?;
        let entries = fs::read_dir(&self.resized_path)?;
        for entry in entries {
            let entry_path = entry?.path();
            if entry_path.is_file() {
                let filename = entry_path.file_name().unwrap().to_string_lossy();
                if let Some(capts) = RESIZED_FILENAME.captures(filename.as_ref()) {
                    let hash = u64::from_str_radix(capts.get(1).unwrap().as_str(), 16).unwrap();
                    let num_collisions = u32::from_str_radix(capts.get(2).unwrap().as_str(), 16).unwrap();
                    if num_collisions > 0 || !self.img_ops.contains_key(&hash) {
                        fs::remove_file(&entry_path)?;
                    }
                }
            }
        }
        Ok(())
    }

    pub fn do_process(&mut self) -> Result<()> {
        self.img_ops.par_iter().map(|(hash, op)| {
            let target = self.resized_path.join(Self::op_filename(*hash, op.num_collisions()));
            op.perform(&self.content_path, &target)
                .chain_err(|| format!("Failed to process image: {}", op.source))
        })
        .fold(|| Ok(()), Result::and)
        .reduce(|| Ok(()), Result::and)
    }
}

/// Looks at file's extension and returns whether it's a supported image format
pub fn file_is_img<P: AsRef<Path>>(p: P) -> bool {
    p.as_ref().extension().and_then(|s| s.to_str()).map(|ext| {
        match ext.to_lowercase().as_str() {
            "jpg" | "jpeg" => true,
            "png" => true,
            "gif" => true,
            "bmp" => true,
            _ => false,
        }
    }).unwrap_or(false)
}
<commit_msg>imageproc: Cleanup and comments in hash collision resolution<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate image;
extern crate rayon;
extern crate twox_hash;

extern crate utils;
extern crate errors;

use std::path::{Path, PathBuf};
use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use std::collections::hash_map::Entry as HEntry;
use std::fs::{self, File};

use regex::Regex;
use image::{GenericImage, FilterType};
use image::jpeg::JPEGEncoder;
use rayon::prelude::*;
use twox_hash::XxHash;

use utils::fs as ufs;
use errors::{Result, ResultExt};

static RESIZED_SUBDIR: &'static str = "_resized_images";
lazy_static!{
    pub static ref RESIZED_FILENAME: Regex = Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.]jpg"#).unwrap();
}

/// Describes the precise kind of a resize operation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResizeOp {
    /// A simple scale operation that doesn't take aspect ratio into account
    Scale(u32, u32),
    /// Scales the image to a specified width with height computed such that aspect ratio is preserved
    FitWidth(u32),
    /// Scales the image to a specified height with width computed such that aspect ratio is preserved
    FitHeight(u32),
    /// Scales the image such that it fits within the specified width and height preserving aspect ratio.
    /// Either dimension may end up being smaller, but never larger than specified.
    Fit(u32, u32),
    /// Scales the image such that it fills the specified width and height. Output will always have the exact dimensions specified.
    /// The part of the image that doesn't fit in the thumbnail due to differing aspect ratio will be cropped away, if any.
    Fill(u32, u32),
}

impl ResizeOp {
    pub fn from_args(op: &str, width: Option<u32>, height: Option<u32>) -> Result<ResizeOp> {
        use ResizeOp::*;

        // Validate args:
        match op {
            "fit_width" => if width.is_none() { return Err(format!("op=\"fit_width\" requires a `width` argument").into()) },
            "fit_height" => if height.is_none() { return Err(format!("op=\"fit_height\" requires a `height` argument").into()) },
            "scale" | "fit" | "fill" => if width.is_none() || height.is_none() {
                return Err(format!("op={} requires a `width` and `height` argument", op).into())
            },
            _ => return Err(format!("Invalid image resize operation: {}", op).into())
        };

        Ok(match op {
            "scale" => Scale(width.unwrap(), height.unwrap()),
            "fit_width" => FitWidth(width.unwrap()),
            "fit_height" => FitHeight(height.unwrap()),
            "fit" => Fit(width.unwrap(), height.unwrap()),
            "fill" => Fill(width.unwrap(), height.unwrap()),
            _ => unreachable!(),
        })
    }

    pub fn width(self) -> Option<u32> {
        use ResizeOp::*;

        match self {
            Scale(w, _) => Some(w),
            FitWidth(w) => Some(w),
            FitHeight(_) => None,
            Fit(w, _) => Some(w),
            Fill(w, _) => Some(w),
        }
    }

    pub fn height(self) -> Option<u32> {
        use ResizeOp::*;

        match self {
            Scale(_, h) => Some(h),
            FitWidth(_) => None,
            FitHeight(h) => Some(h),
            Fit(_, h) => Some(h),
            Fill(_, h) => Some(h),
        }
    }
}

impl From<ResizeOp> for u8 {
    fn from(op: ResizeOp) -> u8 {
        use ResizeOp::*;

        match op {
            Scale(_, _) => 1,
            FitWidth(_) => 2,
            FitHeight(_) => 3,
            Fit(_, _) => 4,
            Fill(_, _) => 5,
        }
    }
}

impl Hash for ResizeOp {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        hasher.write_u8(u8::from(*self));
        if let Some(w) = self.width()  { hasher.write_u32(w); }
        if let Some(h) = self.height() { hasher.write_u32(h); }
    }
}

/// Holds all data needed to perform a resize operation
#[derive(Debug, PartialEq, Eq)]
pub struct ImageOp {
    source: String,
    op: ResizeOp,
    quality: u8,
    /// Hash of the above parameters
    hash: u64,
    /// If there is a hash collision with another ImageOp, this contains a sequential ID > 1
    /// identifying the collision in the order as encountered (which is essentially random).
    /// Therefore, ImageOps with collisions (ie. collision_id > 0) are always considered out of date.
    collision_id: u32,
}

impl ImageOp {
    pub fn new(source: String, op: ResizeOp, quality: u8) -> ImageOp {
        let mut hasher = XxHash::with_seed(0);
        hasher.write(source.as_ref());
        op.hash(&mut hasher);
        hasher.write_u8(quality);
        let hash = hasher.finish();

        ImageOp { source, op, quality, hash, collision_id: 0 }
    }

    pub fn from_args(source: String, op: &str, width: Option<u32>, height: Option<u32>, quality: u8) -> Result<ImageOp> {
        let op = ResizeOp::from_args(op, width, height)?;
        Ok(Self::new(source, op, quality))
    }

    fn perform(&self, content_path: &Path, target_path: &Path) -> Result<()> {
        use ResizeOp::*;

        let src_path = content_path.join(&self.source);
        if !ufs::file_stale(&src_path, target_path) {
            return Ok(())
        }

        let mut img = image::open(&src_path)?;
        let (img_w, img_h) = img.dimensions();

        const RESIZE_FILTER: FilterType = FilterType::Gaussian;
        const RATIO_EPSILLION: f32 = 0.1;

        let img = match self.op {
            Scale(w, h) => img.resize_exact(w, h, RESIZE_FILTER),
            FitWidth(w) => img.resize(w, u32::max_value(), RESIZE_FILTER),
            FitHeight(h) => img.resize(u32::max_value(), h, RESIZE_FILTER),
            Fit(w, h) => img.resize(w, h, RESIZE_FILTER),
            Fill(w, h) => {
                let factor_w = img_w as f32 / w as f32;
                let factor_h = img_h as f32 / h as f32;

                if (factor_w - factor_h).abs() <= RATIO_EPSILLION {
                    // If the horizontal and vertical factor is very similar, that means the aspect is similar enough
                    // that there's not much point in cropping, so just perform a simple scale in this case.
                    img.resize_exact(w, h, RESIZE_FILTER)
                } else {
                    // We perform the fill such that a crop is performed first and then resize_exact can be used,
                    // which should be cheaper than resizing and then cropping (smaller number of pixels to resize).

                    let (crop_w, crop_h) = if factor_w < factor_h {
                        (img_w, (factor_w * h as f32).round() as u32)
                    } else {
                        ((factor_h * w as f32).round() as u32, img_h)
                    };

                    let (offset_w, offset_h) = if factor_w < factor_h {
                        (0, (img_h - crop_h) / 2)
                    } else {
                        ((img_w - crop_w) / 2, 0)
                    };

                    img.crop(offset_w, offset_h, crop_w, crop_h).resize_exact(w, h, RESIZE_FILTER)
                }
            },
        };

        let mut f = File::create(target_path)?;
        let mut enc = JPEGEncoder::new_with_quality(&mut f, self.quality);
        let (img_w, img_h) = img.dimensions();
        enc.encode(&img.raw_pixels(), img_w, img_h, img.color())?;
        Ok(())
    }
}

/// A strcture into which image operations can be enqueued and then performed.
/// All output is written in a subdirectory in `static_path`,
/// taking care of file stale status based on timestamps and possible hash collisions.
#[derive(Debug)]
pub struct Processor {
    content_path: PathBuf,
    resized_path: PathBuf,
    resized_url: String,
    /// A map of a ImageOps by their stored hash.
    /// Note that this cannot be a HashSet, because hashest handles collisions and we don't want that,
    /// we need to be aware of and handle collisions ourselves.
    img_ops: HashMap<u64, ImageOp>,
    /// Hash collisions go here:
    img_ops_collisions: Vec<ImageOp>,
}

impl Processor {
    pub fn new(content_path: PathBuf, static_path: &Path, base_url: &str) -> Processor {
        Processor {
            content_path,
            resized_path: static_path.join(RESIZED_SUBDIR),
            resized_url: Self::resized_url(base_url),
            img_ops: HashMap::new(),
            img_ops_collisions: Vec::new(),
        }
    }

    fn resized_url(base_url: &str) -> String {
        if base_url.ends_with('/') {
             format!("{}{}", base_url, RESIZED_SUBDIR)
        } else {
             format!("{}/{}", base_url, RESIZED_SUBDIR)
        }
    }

    pub fn set_base_url(&mut self, base_url: &str) {
        self.resized_url = Self::resized_url(base_url);
    }

    pub fn source_exists(&self, source: &str) -> bool {
        self.content_path.join(source).exists()
    }

    pub fn num_img_ops(&self) -> usize {
        self.img_ops.len() + self.img_ops_collisions.len()
    }

    fn insert_with_collisions(&mut self, mut img_op: ImageOp) -> u32 {
        match self.img_ops.entry(img_op.hash) {
            HEntry::Occupied(entry) => if *entry.get() == img_op { return 0; },
            HEntry::Vacant(entry) => {
                entry.insert(img_op);
                return 0;
            },
        }

        // If we get here, that means a hash collision.
        // This is detected when there is an ImageOp with the same hash in the `img_ops` map but which is not equal to this one.
        // To deal with this, all collisions get a (random) sequential ID number.

        // First try to look up this ImageOp in `img_ops_collisions`, maybe we've already seen the same ImageOp.
        // At the same time, count IDs to figure out the next free one.
        // Start with the ID of 2, because we'll need to use 1 for the ImageOp already present in the map:
        let mut collision_id = 2;
        for op in self.img_ops_collisions.iter().filter(|op| op.hash == img_op.hash) {
            if *op == img_op {
                // This is a colliding ImageOp, but we've already seen an equal one (not just by hash, but by content too),
                // so just return its ID:
                return collision_id;
            } else {
                collision_id += 1;
            }
        }

        // If we get here, that means this is a new colliding ImageOp and `collision_id` is the next free ID
        if collision_id == 2 {
            // This is the first collision found with this hash, update the ID of the matching ImageOp in the map.
            self.img_ops.get_mut(&img_op.hash).unwrap().collision_id = 1;
        }
        img_op.collision_id = collision_id;
        self.img_ops_collisions.push(img_op);
        collision_id
    }

    fn op_filename(hash: u64, collision_id: u32) -> String {
        // Please keep this in sync with RESIZED_FILENAME
        assert!(collision_id < 256, "Unexpectedly large number of collisions: {}", collision_id);
        format!("{:016x}{:02x}.jpg", hash, collision_id)
    }

    fn op_url(&self, hash: u64, collision_id: u32) -> String {
        format!("{}/{}", &self.resized_url, Self::op_filename(hash, collision_id))
    }

    pub fn insert(&mut self, img_op: ImageOp) -> String {
        let hash = img_op.hash;
        let collision_id = self.insert_with_collisions(img_op);
        self.op_url(hash, collision_id)
    }

    pub fn prune(&self) -> Result<()> {
        ufs::ensure_directory_exists(&self.resized_path)?;
        let entries = fs::read_dir(&self.resized_path)?;
        for entry in entries {
            let entry_path = entry?.path();
            if entry_path.is_file() {
                let filename = entry_path.file_name().unwrap().to_string_lossy();
                if let Some(capts) = RESIZED_FILENAME.captures(filename.as_ref()) {
                    let hash = u64::from_str_radix(capts.get(1).unwrap().as_str(), 16).unwrap();
                    let collision_id = u32::from_str_radix(capts.get(2).unwrap().as_str(), 16).unwrap();
                    if collision_id > 0 || !self.img_ops.contains_key(&hash) {
                        fs::remove_file(&entry_path)?;
                    }
                }
            }
        }
        Ok(())
    }

    pub fn do_process(&mut self) -> Result<()> {
        self.img_ops.par_iter().map(|(hash, op)| {
            let target = self.resized_path.join(Self::op_filename(*hash, op.collision_id));
            op.perform(&self.content_path, &target)
                .chain_err(|| format!("Failed to process image: {}", op.source))
        })
        .fold(|| Ok(()), Result::and)
        .reduce(|| Ok(()), Result::and)
    }
}

/// Looks at file's extension and returns whether it's a supported image format
pub fn file_is_img<P: AsRef<Path>>(p: P) -> bool {
    p.as_ref().extension().and_then(|s| s.to_str()).map(|ext| {
        match ext.to_lowercase().as_str() {
            "jpg" | "jpeg" => true,
            "png" => true,
            "gif" => true,
            "bmp" => true,
            _ => false,
        }
    }).unwrap_or(false)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::XRViewBinding;
use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethods};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::vrframedata::create_typed_array;
use crate::dom::xrsession::XRSession;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSContext, JSObject};
use std::ptr::NonNull;
use webvr_traits::WebVRFrameData;

#[dom_struct]
pub struct XRView {
    reflector_: Reflector,
    session: Dom<XRSession>,
    eye: XREye,
    proj: Heap<*mut JSObject>,
    view: Heap<*mut JSObject>,
}

impl XRView {
    fn new_inherited(session: &XRSession, eye: XREye) -> XRView {
        XRView {
            reflector_: Reflector::new(),
            session: Dom::from_ref(session),
            eye,
            proj: Heap::default(),
            view: Heap::default(),
        }
    }

    pub fn new(
        global: &GlobalScope,
        session: &XRSession,
        eye: XREye,
        data: &WebVRFrameData,
    ) -> DomRoot<XRView> {
        let ret = reflect_dom_object(
            Box::new(XRView::new_inherited(session, eye)),
            global,
            XRViewBinding::Wrap,
        );

        let (proj, view) = if eye == XREye::Left {
            (&data.left_projection_matrix, &data.left_view_matrix)
        } else {
            (&data.right_projection_matrix, &data.right_view_matrix)
        };

        let cx = global.get_cx();
        create_typed_array(cx, proj, &ret.proj);
        create_typed_array(cx, view, &ret.view);
        ret
    }

    pub fn session(&self) -> &XRSession {
        &self.session
    }
}

impl XRViewMethods for XRView {
    /// https://immersive-web.github.io/webxr/#dom-xrview-eye
    fn Eye(&self) -> XREye {
        self.eye
    }

    #[allow(unsafe_code)]
    /// https://immersive-web.github.io/webxr/#dom-xrview-projectionmatrix
    unsafe fn ProjectionMatrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        NonNull::new_unchecked(self.proj.get())
    }

    #[allow(unsafe_code)]
    /// https://immersive-web.github.io/webxr/#dom-xrview-projectionmatrix
    unsafe fn ViewMatrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        NonNull::new_unchecked(self.view.get())
    }
}
<commit_msg>NonNull::new()<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::XRViewBinding;
use crate::dom::bindings::codegen::Bindings::XRViewBinding::{XREye, XRViewMethods};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::vrframedata::create_typed_array;
use crate::dom::xrsession::XRSession;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSContext, JSObject};
use std::ptr::NonNull;
use webvr_traits::WebVRFrameData;

#[dom_struct]
pub struct XRView {
    reflector_: Reflector,
    session: Dom<XRSession>,
    eye: XREye,
    proj: Heap<*mut JSObject>,
    view: Heap<*mut JSObject>,
}

impl XRView {
    fn new_inherited(session: &XRSession, eye: XREye) -> XRView {
        XRView {
            reflector_: Reflector::new(),
            session: Dom::from_ref(session),
            eye,
            proj: Heap::default(),
            view: Heap::default(),
        }
    }

    pub fn new(
        global: &GlobalScope,
        session: &XRSession,
        eye: XREye,
        data: &WebVRFrameData,
    ) -> DomRoot<XRView> {
        let ret = reflect_dom_object(
            Box::new(XRView::new_inherited(session, eye)),
            global,
            XRViewBinding::Wrap,
        );

        let (proj, view) = if eye == XREye::Left {
            (&data.left_projection_matrix, &data.left_view_matrix)
        } else {
            (&data.right_projection_matrix, &data.right_view_matrix)
        };

        let cx = global.get_cx();
        create_typed_array(cx, proj, &ret.proj);
        create_typed_array(cx, view, &ret.view);
        ret
    }

    pub fn session(&self) -> &XRSession {
        &self.session
    }
}

impl XRViewMethods for XRView {
    /// https://immersive-web.github.io/webxr/#dom-xrview-eye
    fn Eye(&self) -> XREye {
        self.eye
    }

    #[allow(unsafe_code)]
    /// https://immersive-web.github.io/webxr/#dom-xrview-projectionmatrix
    unsafe fn ProjectionMatrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        NonNull::new(self.proj.get()).unwrap()
    }

    #[allow(unsafe_code)]
    /// https://immersive-web.github.io/webxr/#dom-xrview-projectionmatrix
    unsafe fn ViewMatrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        NonNull::new(self.view.get()).unwrap()
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use serialize::{Decodable, json};
use core::misc::read_file;

pub struct Config {
    json: ~json::Object,
}

fn decode<A: Decodable<json::Decoder, json::Error>>(json_obj: json::Json) -> A {
    let mut decoder = json::Decoder::new(json_obj);
    let decoded: A = Decodable::decode(&mut decoder).unwrap();
    decoded
}

impl Config {
    pub fn new(path: &Path) -> Config {
        let json = match json::from_str(read_file(path)) {
            Ok(json::Object(obj)) => obj,
            _ => fail!("Config error"),
        };
        Config {
            json: json,
        }
    }

    pub fn get<A: Decodable<json::Decoder, json::Error>>(&self, name: &str) -> A {
        let owned_name_str = name.into_owned();
        decode(match self.json.find(&owned_name_str) {
            Some(val) => val.clone(),
            None => fail!("No field '{}", name),
        })
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Conf: Print error if possible<commit_after>// See LICENSE file for copyright and license details.

use serialize::{Decodable, json};
use core::misc::read_file;

pub struct Config {
    json: ~json::Object,
}

fn decode<A: Decodable<json::Decoder, json::Error>>(json_obj: json::Json) -> A {
    let mut decoder = json::Decoder::new(json_obj);
    let decoded: A = Decodable::decode(&mut decoder).unwrap();
    decoded
}

impl Config {
    pub fn new(path: &Path) -> Config {
        let json = match json::from_str(read_file(path)) {
            Ok(json::Object(obj)) => obj,
            Err(msg) => fail!("Config parsing error: {}", msg),
            _ => fail!("Unknown config parsing error"),
        };
        Config {
            json: json,
        }
    }

    pub fn get<A: Decodable<json::Decoder, json::Error>>(&self, name: &str) -> A {
        let owned_name_str = name.into_owned();
        decode(match self.json.find(&owned_name_str) {
            Some(val) => val.clone(),
            None => fail!("No field '{}", name),
        })
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>//! Connector with tokio-curl backend.

use std::fmt;
use std::sync::Arc;
use std::rc::Rc;

use antidote::Mutex;
use curl::easy::{Easy, List};
use futures::Future;
use futures::future::result;
use tokio_core::reactor::Handle;
use tokio_curl::Session;

use errors::Error;
use future::{TelegramFuture, NewTelegramFuture};

use telegram_bot_raw::{HttpRequest, HttpResponse, Method, Body};

use super::_base::Connector;

/// This connector uses `tokio-curl` backend.
pub struct CurlConnector {
    inner: Rc<Session>
}

impl fmt::Debug for CurlConnector {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        "curl connector".fmt(formatter)
    }
}

impl CurlConnector {
    pub fn new(handle: &Handle) -> Self {
        CurlConnector {
            inner: Rc::new(Session::new(handle.clone()))
        }
    }

    fn create_request(&self, token: &str, request: HttpRequest) -> Result<(Easy, Arc<Mutex<Vec<u8>>>), Error> {
        let mut handle = Easy::new();

        let url = request.url.url(token);
        handle.url(&url)?;

        match request.method {
            Method::Get => handle.get(true)?,
            Method::Post => handle.post(true)?,
        }

        match request.body {
            Body::Empty => (),
            Body::Json(body) => {
                handle.post_fields_copy(&body)?;

                let mut headers = List::new();
                headers.append(&format!("Content-Type: application/json"))?;
                handle.http_headers(headers)?;
            }
            body => panic!("Unknown body type {:?}", body)
        }

        let result = Arc::new(Mutex::new(Vec::new()));
        let write_result = result.clone();

        handle.write_function(move |data| {
            write_result.lock().extend_from_slice(data);
            Ok(data.len())
        })?;

        Ok((handle, result))
    }
}

impl Connector for CurlConnector {
    fn request(&self, token: &str, req: HttpRequest) -> TelegramFuture<HttpResponse> {
        let request = result(self.create_request(token, req));

        let session = self.inner.clone();
        let request = request.and_then(move |(handle, result)| {
            session.perform(handle).map_err(From::from).join(Ok(result))
        });

        let future = request.and_then(move |(_, result)| {
            let mut swap = Vec::new();
            let mut guard = result.lock();
            let prev: &mut Vec<u8> = &mut guard;
            ::std::mem::swap(prev, &mut swap);
            Ok(HttpResponse {
                body: Some(swap)
            })
        });

        TelegramFuture::new(Box::new(future))
    }
}

/// Returns default curl connector.
pub fn default_connector(handle: &Handle) -> Result<Box<Connector>, Error> {
    let connector = CurlConnector::new(handle);
    Ok(Box::new(connector))
}
<commit_msg>curl connector<commit_after>//! Connector with tokio-curl backend.

use std::fmt;
use std::sync::Arc;
use std::rc::Rc;

use antidote::Mutex;
use curl::easy::{Easy, Form, List};
use futures::Future;
use futures::future::result;
use tokio_core::reactor::Handle;
use tokio_curl::Session;

use errors::Error;
use future::{NewTelegramFuture, TelegramFuture};

use telegram_bot_raw::{Body, HttpRequest, HttpResponse, Method};

use super::_base::Connector;

/// This connector uses `tokio-curl` backend.
pub struct CurlConnector {
    inner: Rc<Session>,
}

impl fmt::Debug for CurlConnector {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        "curl connector".fmt(formatter)
    }
}

impl CurlConnector {
    pub fn new(handle: &Handle) -> Self {
        CurlConnector {
            inner: Rc::new(Session::new(handle.clone())),
        }
    }

    fn create_request(
        &self,
        token: &str,
        request: HttpRequest,
    ) -> Result<(Easy, Arc<Mutex<Vec<u8>>>), Error> {
        let mut handle = Easy::new();

        let url = request.url.url(token);
        handle.url(&url)?;

        match request.method {
            Method::Get => handle.get(true)?,
            Method::Post => handle.post(true)?,
        }

        match request.body {
            Body::Empty => (),
            Body::Json(body) => {
                handle.post_fields_copy(&body)?;

                let mut headers = List::new();
                headers.append(&format!("Content-Type: application/json"))?;
                handle.http_headers(headers)?;
            }
            Body::Multipart(parts) => {
                use telegram_bot_raw::MultipartValue;
                let mut form = Form::new();
                for (k, v) in parts.into_iter() {
                    match v {
                        MultipartValue::Text(text) => {
                            let mut part = form.part(&k);
                            part.contents(text.as_bytes());
                            part.add().unwrap();
                        }
                        MultipartValue::File { filename, path } => {
                            let mut part = form.part(&k);
                            part.buffer(filename.as_str(), path.into_bytes());
                            part.add().unwrap();
                        }
                    }
                }
                handle.httppost(form).unwrap();
            }
            body => panic!("Unknown body type {:?}", body),
        }

        let result = Arc::new(Mutex::new(Vec::new()));
        let write_result = result.clone();

        handle.write_function(move |data| {
            write_result.lock().extend_from_slice(data);
            Ok(data.len())
        })?;

        Ok((handle, result))
    }
}

impl Connector for CurlConnector {
    fn request(&self, token: &str, req: HttpRequest) -> TelegramFuture<HttpResponse> {
        let request = result(self.create_request(token, req));

        let session = self.inner.clone();
        let request = request.and_then(move |(handle, result)| {
            session.perform(handle).map_err(From::from).join(Ok(result))
        });

        let future = request.and_then(move |(_, result)| {
            let mut swap = Vec::new();
            let mut guard = result.lock();
            let prev: &mut Vec<u8> = &mut guard;
            ::std::mem::swap(prev, &mut swap);
            Ok(HttpResponse { body: Some(swap) })
        });

        TelegramFuture::new(Box::new(future))
    }
}

/// Returns default curl connector.
pub fn default_connector(handle: &Handle) -> Result<Box<Connector>, Error> {
    let connector = CurlConnector::new(handle);
    Ok(Box::new(connector))
}
<|endoftext|>


<commit_before>use header::EntryHeader;
use content::EntryContent;
use store::StoreId;

/**
 * An Entry of the store
 *
 * Contains location, header and content part.
 */
#[derive(Debug, Clone)]
pub struct Entry {
    location: StoreId,
    header: EntryHeader,
    content: EntryContent,
}

<commit_msg>Auto merge of #132 - matthiasbeyer:libimagstore/entry-getter, r=matthiasbeyer<commit_after>use header::EntryHeader;
use content::EntryContent;
use store::StoreId;

/**
 * An Entry of the store
 *
 * Contains location, header and content part.
 */
#[derive(Debug, Clone)]
pub struct Entry {
    location: StoreId,
    header: EntryHeader,
    content: EntryContent,
}

impl Entry {

    pub fn get_location(&self) -> &StoreId {
        &self.location
    }

    pub fn get_header(&self) -> &EntryHeader {
        &self.header
    }

    pub fn get_header_mut(&mut self) -> &mut EntryHeader {
        &mut self.header
    }

    pub fn get_content(&self) -> &EntryContent {
        &self.content
    }

    pub fn get_content_mut(&mut self) -> &mut EntryContent {
        &mut self.content
    }

}

<|endoftext|>


<commit_before>use chrono::naive::date::NaiveDate as ChronoNaiveDate;

use parse::Parse;

pub struct Date {
    year: i32,
    month: u32,
    day: u32,
}

impl Date {

    fn new(year: i32, month: u32, day: u32) -> Date {
        unimplemented!()
    }

}

impl Into<ChronoNaiveDate> for Date {

    fn into(self) -> ChronoNaiveDate {
        ChronoNaiveDate::from_ymd(self.year, self.month, self.day)
    }

}

impl Parse for Date {

    fn parse(s: &str) -> Option<Date> {
        unimplemented!()
    }

}

<commit_msg>Impl Parse::parse for Date<commit_after>use chrono::naive::date::NaiveDate as ChronoNaiveDate;

use parse::Parse;

pub struct Date {
    year: i32,
    month: u32,
    day: u32,
}

impl Date {

    fn new(year: i32, month: u32, day: u32) -> Date {
        unimplemented!()
    }

}

impl Into<ChronoNaiveDate> for Date {

    fn into(self) -> ChronoNaiveDate {
        ChronoNaiveDate::from_ymd(self.year, self.month, self.day)
    }

}

impl Parse for Date {

    /// Parse the date part of the full string into a Date object
    fn parse(s: &str) -> Option<Date> {
        use std::str::FromStr;
        use regex::Regex;
        use parse::time_parse_regex;

        lazy_static! {
            static ref R: Regex = Regex::new(time_parse_regex()).unwrap();
        }

        R.captures(s)
            .and_then(|capts| {
                let year  = capts.name("Y").and_then(|o| FromStr::from_str(o).ok());
                let month = capts.name("M").and_then(|o| FromStr::from_str(o).ok());
                let day   = capts.name("D").and_then(|o| FromStr::from_str(o).ok());

                if year.is_none() {
                    debug!("No year");
                    return None;
                }
                if month.is_none() {
                    debug!("No month");
                    return None;
                }
                if day.is_none() {
                    debug!("No day");
                    return None;
                }

                Some(Date::new(year.unwrap(), month.unwrap(), day.unwrap()))
            })

    }

}

#[cfg(test)]
mod test {
    use super::Date;
    use parse::Parse;

    #[test]
    fn test_valid() {
        let s = "2016-02-01";
        let d = Date::parse(s);

        assert!(d.is_some());
        let d = d.unwrap();

        assert_eq!(2016, d.year());
        assert_eq!(2, d.month());
        assert_eq!(1, d.day());
    }

    #[test]
    fn test_invalid() {
        assert!(Date::parse("2016-021-01").is_none());
        assert!(Date::parse("2016-02-012").is_none());
        assert!(Date::parse("2016-02-0").is_none());
        assert!(Date::parse("2016-0-02").is_none());
        assert!(Date::parse("2016-02").is_none());
        assert!(Date::parse("2016-2").is_none());
    }

}

<|endoftext|>


<commit_before>//! Utility functions for interacting with serviced

use alloc::borrow::ToOwned;
use alloc::vec::Vec;
use hashbrown::HashSet;

use crate::ipc::protocol::service::{Registration, ServiceName};

pub fn register(name: &str, oneshot: bool) {
    crate::ipc::deliver("serviced/register", &Registration {
        name: ServiceName(name.to_owned()),
        oneshot,
    })
    .unwrap();
}

pub fn wait_for_one(name: &str) {
    let mut hs = HashSet::new();
    hs.insert(ServiceName(name.to_owned()));
    crate::ipc::deliver("serviced/waitfor/any", &hs).unwrap();
}
<commit_msg>libd7: wait for multiple services<commit_after>//! Utility functions for interacting with serviced

use alloc::borrow::ToOwned;
use hashbrown::HashSet;

use crate::ipc::protocol::service::{Registration, ServiceName};

pub fn register(name: &str, oneshot: bool) {
    crate::ipc::deliver("serviced/register", &Registration {
        name: ServiceName(name.to_owned()),
        oneshot,
    })
    .unwrap();
}

pub fn wait_for_one(name: &str) {
    let mut hs = HashSet::new();
    hs.insert(ServiceName(name.to_owned()));
    crate::ipc::deliver("serviced/waitfor/any", &hs).unwrap();
}

pub fn wait_for_any<T>(names: T) where
    T: IntoIterator,
    T::Item: AsRef<str>,
{
    let mut hs = HashSet::new();
    for name in names.into_iter() {
        hs.insert(ServiceName(name.as_ref().to_owned()));
    }
    crate::ipc::deliver("serviced/waitfor/any", &hs).unwrap();
}

pub fn wait_for_all<T>(names: T) where
    T: IntoIterator,
    T::Item: AsRef<str>,
{
    let mut hs = HashSet::new();
    for name in names.into_iter() {
        hs.insert(ServiceName(name.as_ref().to_owned()));
    }
    crate::ipc::deliver("serviced/waitfor/all", &hs).unwrap();
}
<|endoftext|>


<commit_before>//! `StructureSpawn` data description.
use crate::data::RoomName;

with_update_struct! {
    /// A struct describing a creep currently spawning (used as part of the update for a StructureSpawn).
    #[derive(serde_derive::Deserialize, Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct SpawningCreep {
        /// The name of this creep, unique per player.
        pub name: String,
        /// The total number of game ticks needed to spawn this creep.
        #[serde(rename = "needTime")]
        pub total_time: u32,
        /// The number of game ticks left before this creep is spawned.
        pub remaining_time: u32,
    }

    /// The update structure for a spawning creep.
    #[derive(serde_derive::Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SpawningCreepUpdate { ... }
}

with_structure_fields_and_update_struct! {
    /// A spawn structure - a structure which can create creeps.
    #[derive(Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct StructureSpawn {
        /// The user ID of the owner of this structure.
        pub user: String,
        /// Whether or not this structure is non-functional due to a degraded controller.
        #[serde(default, rename = "off")]
        pub disabled: bool,
        /// The current amount of energy held in this structure.
        pub energy: i32,
        /// The maximum amount of energy that can be held in this structure.
        pub energy_capacity: i32,
        /// Whether or not an attack on this structure will send an email to the owner automatically.
        pub notify_when_attacked: bool,
        /// The name of this spawn, unique per player.
        pub name: String,
        /// The creep that's currently spawning, if any.
        pub spawning: Option<SpawningCreep>,
    }

    /// The update structure for a mineral object.
    #[derive(Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct StructureSpawnUpdate {
        - user: String,
        #[serde(rename = "off")]
        - disabled: bool,
        - energy: i32,
        - energy_capacity: i32,
        - notify_when_attacked: bool,
        - name: String,
        - spawning: Option<SpawningCreep>,
    }
}

#[cfg(test)]
mod test {
    use serde_json;

    use serde::Deserialize;

    use crate::data::RoomName;

    use super::{SpawningCreep, StructureSpawn};

    #[test]
    fn parse_spawn_and_update() {
        let json = json!({
            "_id": "58a23b6c4370e6302d758099",
            "energy": 300,
            "energyCapacity": 300,
            "hits": 5000,
            "hitsMax": 5000,
            "name": "Spawn36",
            "notifyWhenAttacked": true,
            "off": false,
            "room": "E4S61",
            "spawning": {
                "name": "5599",
                "needTime": 126,
                "remainingTime": 5,
            },
            "type": "spawn",
            "user": "57874d42d0ae911e3bd15bbc",
            "x": 24,
            "y": 6,
        });

        let mut obj = StructureSpawn::deserialize(json).unwrap();

        assert_eq!(
            obj,
            StructureSpawn {
                id: "58a23b6c4370e6302d758099".to_owned(),
                room: RoomName::new("E4S61").unwrap(),
                x: 24,
                y: 6,
                energy: 300,
                energy_capacity: 300,
                hits: 5000,
                hits_max: 5000,
                name: "Spawn36".to_owned(),
                notify_when_attacked: true,
                disabled: false,
                spawning: Some(SpawningCreep {
                    name: "5599".to_owned(),
                    total_time: 126,
                    remaining_time: 5,
                }),
                user: "57874d42d0ae911e3bd15bbc".to_owned(),
            }
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 4,
                },
            }))
            .unwrap(),
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 3,
                },
            }))
            .unwrap(),
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 2,
                },
            }))
            .unwrap(),
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 1,
                },
            }))
            .unwrap(),
        );

        assert_eq!(
            obj.spawning,
            Some(SpawningCreep {
                name: "5599".to_owned(),
                total_time: 126,
                remaining_time: 1,
            })
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": null,
            }))
            .unwrap(),
        );

        assert_eq!(obj.spawning, None);
    }
}
<commit_msg>Add new failing StructureSpawn test<commit_after>//! `StructureSpawn` data description.
use crate::data::RoomName;

with_update_struct! {
    /// A struct describing a creep currently spawning (used as part of the update for a StructureSpawn).
    #[derive(serde_derive::Deserialize, Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct SpawningCreep {
        /// The name of this creep, unique per player.
        pub name: String,
        /// The total number of game ticks needed to spawn this creep.
        #[serde(rename = "needTime")]
        pub total_time: u32,
        /// The number of game ticks left before this creep is spawned.
        pub remaining_time: u32,
    }

    /// The update structure for a spawning creep.
    #[derive(serde_derive::Deserialize, Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SpawningCreepUpdate { ... }
}

with_structure_fields_and_update_struct! {
    /// A spawn structure - a structure which can create creeps.
    #[derive(Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct StructureSpawn {
        /// The user ID of the owner of this structure.
        pub user: String,
        /// Whether or not this structure is non-functional due to a degraded controller.
        #[serde(default, rename = "off")]
        pub disabled: bool,
        /// The current amount of energy held in this structure.
        pub energy: i32,
        /// The maximum amount of energy that can be held in this structure.
        pub energy_capacity: i32,
        /// Whether or not an attack on this structure will send an email to the owner automatically.
        pub notify_when_attacked: bool,
        /// The name of this spawn, unique per player.
        pub name: String,
        /// The creep that's currently spawning, if any.
        pub spawning: Option<SpawningCreep>,
    }

    /// The update structure for a mineral object.
    #[derive(Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct StructureSpawnUpdate {
        - user: String,
        #[serde(rename = "off")]
        - disabled: bool,
        - energy: i32,
        - energy_capacity: i32,
        - notify_when_attacked: bool,
        - name: String,
        - spawning: Option<SpawningCreep>,
    }
}

#[cfg(test)]
mod test {
    use serde_json;

    use serde::Deserialize;

    use crate::data::RoomName;

    use super::{SpawningCreep, StructureSpawn};

    #[test]
    fn parse_empty_spawn() {
        let json = json!({
            "_id": "5f0236153187fd5e3dfa814a",
            "hits": 5000,
            "hitsMax": 5000,
            "name": "Spawn1",
            "notifyWhenAttacked": true,
            "off": false,
            "room": "W41N48",
            "spawning": null,
            "store": {
                "energy": 300
            },
            "storeCapacityResource": {
                "energy": 300
            },
            "type": "spawn",
            "user": "57874d42d0ae911e3bd15bbc",
            "x": 26,
            "y": 28
        });

        let mut obj = StructureSpawn::deserialize(json).unwrap();

        assert_eq!(
            obj,
            StructureSpawn {
                id: "5f0236153187fd5e3dfa814a".to_owned(),
                room: RoomName::new("W31N48").unwrap(),
                x: 26,
                y: 28,
                energy: 300,
                energy_capacity: 300,
                hits: 5000,
                hits_max: 5000,
                name: "Spawn1".to_owned(),
                notify_when_attacked: true,
                disabled: false,
                spawning: None,
                user: "57874d42d0ae911e3bd15bbc".to_owned(),
            }
        );
    }

    #[test]
    fn parse_spawn_and_update() {
        let json = json!({
            "_id": "58a23b6c4370e6302d758099",
            "energy": 300,
            "energyCapacity": 300,
            "hits": 5000,
            "hitsMax": 5000,
            "name": "Spawn36",
            "notifyWhenAttacked": true,
            "off": false,
            "room": "E4S61",
            "spawning": {
                "name": "5599",
                "needTime": 126,
                "remainingTime": 5,
            },
            "type": "spawn",
            "user": "57874d42d0ae911e3bd15bbc",
            "x": 24,
            "y": 6,
        });

        let mut obj = StructureSpawn::deserialize(json).unwrap();

        assert_eq!(
            obj,
            StructureSpawn {
                id: "58a23b6c4370e6302d758099".to_owned(),
                room: RoomName::new("E4S61").unwrap(),
                x: 24,
                y: 6,
                energy: 300,
                energy_capacity: 300,
                hits: 5000,
                hits_max: 5000,
                name: "Spawn36".to_owned(),
                notify_when_attacked: true,
                disabled: false,
                spawning: Some(SpawningCreep {
                    name: "5599".to_owned(),
                    total_time: 126,
                    remaining_time: 5,
                }),
                user: "57874d42d0ae911e3bd15bbc".to_owned(),
            }
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 4,
                },
            }))
            .unwrap(),
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 3,
                },
            }))
            .unwrap(),
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 2,
                },
            }))
            .unwrap(),
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": {
                    "remainingTime": 1,
                },
            }))
            .unwrap(),
        );

        assert_eq!(
            obj.spawning,
            Some(SpawningCreep {
                name: "5599".to_owned(),
                total_time: 126,
                remaining_time: 1,
            })
        );

        obj.update(
            serde_json::from_value(json!({
                "spawning": null,
            }))
            .unwrap(),
        );

        assert_eq!(obj.spawning, None);
    }
}
<|endoftext|>