<commit_before>// Copyright 2015 Ted Mielczarek. See the COPYRIGHT
// file at the top-level directory of this distribution.

extern crate chrono;
extern crate failure;
extern crate memmap;
extern crate minidump;
extern crate minidump_common;

use chrono::prelude::*;
use memmap::Mmap;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use minidump::*;
use minidump::system_info::{CPU, OS};
use minidump_common::traits::Module;

fn get_test_minidump_path() -> PathBuf {
    let mut path = PathBuf::from(file!());
    path.pop();
    path.pop();
    path.push("testdata/test.dmp");
    path
}

fn read_test_minidump<'a>() -> Result<Minidump<'a, Mmap>, Error> {
    let path = get_test_minidump_path();
    Minidump::read_path(&path)
}

#[test]
fn test_minidump_read_path() {
    read_test_minidump().unwrap();
}

#[test]
fn test_minidump_read() {
    let path = get_test_minidump_path();
    let mut f = File::open(path).unwrap();
    let mut buf = vec![];
    f.read_to_end(&mut buf).unwrap();
    let _dump = Minidump::read(buf).unwrap();
}

#[test]
fn test_module_list() {
    let dump = read_test_minidump().unwrap();
    let module_list = dump.get_stream::<MinidumpModuleList>().unwrap();
    assert_eq!(
        module_list.module_at_address(0x400000).unwrap().code_file(),
        "c:\\test_app.exe"
    );
    let modules = module_list.iter().collect::<Vec<_>>();
    let module_files = modules.iter().map(|m| m.code_file()).collect::<Vec<_>>();
    assert_eq!(modules.len(), 13);
    assert_eq!(modules[0].base_address(), 0x400000);
    assert_eq!(modules[0].size(), 0x2d000);
    assert_eq!(modules[0].code_file(), "c:\\test_app.exe");
    assert_eq!(modules[0].code_identifier(), "45D35F6C2d000");
    assert_eq!(modules[0].debug_file().unwrap(), "c:\\test_app.pdb");
    assert_eq!(
        modules[0].debug_identifier().unwrap(),
        "5A9832E5287241C1838ED98914E9B7FF1"
    );
    assert!(modules[0].version().is_none());

    assert_eq!(modules[12].base_address(), 0x76bf0000);
    assert_eq!(modules[12].size(), 0xb000);
    assert_eq!(modules[12].code_file(), "C:\\WINDOWS\\system32\\psapi.dll");
    assert_eq!(modules[12].code_identifier(), "411096CAb000");
    assert_eq!(modules[12].debug_file().unwrap(), "psapi.pdb");
    assert_eq!(
        modules[12].debug_identifier().unwrap(),
        "A5C3A1F9689F43D8AD228A09293889702"
    );
    assert_eq!(modules[12].version().unwrap(), "5.1.2600.2180");

    assert_eq!(
        module_files,
        vec![
            r"c:\test_app.exe",
            r"C:\WINDOWS\system32\ntdll.dll",
            r"C:\WINDOWS\system32\kernel32.dll",
            r"C:\WINDOWS\system32\ole32.dll",
            r"C:\WINDOWS\system32\advapi32.dll",
            r"C:\WINDOWS\system32\rpcrt4.dll",
            r"C:\WINDOWS\system32\gdi32.dll",
            r"C:\WINDOWS\system32\user32.dll",
            r"C:\WINDOWS\system32\msvcrt.dll",
            r"C:\WINDOWS\system32\imm32.dll",
            r"C:\WINDOWS\system32\dbghelp.dll",
            r"C:\WINDOWS\system32\version.dll",
            r"C:\WINDOWS\system32\psapi.dll",
        ]
    );

    assert_eq!(
        module_list
            .by_addr()
            .map(|m| m.code_file())
            .collect::<Vec<_>>(),
        vec![
            r"c:\test_app.exe",
            r"C:\WINDOWS\system32\dbghelp.dll",
            r"C:\WINDOWS\system32\imm32.dll",
            r"C:\WINDOWS\system32\psapi.dll",
            r"C:\WINDOWS\system32\ole32.dll",
            r"C:\WINDOWS\system32\version.dll",
            r"C:\WINDOWS\system32\msvcrt.dll",
            r"C:\WINDOWS\system32\user32.dll",
            r"C:\WINDOWS\system32\advapi32.dll",
            r"C:\WINDOWS\system32\rpcrt4.dll",
            r"C:\WINDOWS\system32\gdi32.dll",
            r"C:\WINDOWS\system32\kernel32.dll",
            r"C:\WINDOWS\system32\ntdll.dll",
        ]
    );
}

#[test]
fn test_system_info() {
    let dump = read_test_minidump().unwrap();
    let system_info = dump.get_stream::<MinidumpSystemInfo>().unwrap();
    assert_eq!(system_info.os, OS::Windows);
    assert_eq!(system_info.cpu, CPU::X86);
}

#[test]
fn test_misc_info() {
    let dump = read_test_minidump().unwrap();
    let misc_info = dump.get_stream::<MinidumpMiscInfo>().unwrap();
    assert_eq!(misc_info.raw.process_id(), Some(3932));
    assert_eq!(misc_info.raw.process_create_time(), Some(0x45d35f73));
    assert_eq!(
        misc_info.process_create_time().unwrap(),
        Utc.ymd(2007, 02, 14).and_hms(19, 13, 55)
    );
}

#[test]
fn test_breakpad_info() {
    let dump = read_test_minidump().unwrap();
    let breakpad_info = dump.get_stream::<MinidumpBreakpadInfo>().unwrap();
    assert_eq!(breakpad_info.dump_thread_id.unwrap(), 0x11c0);
    assert_eq!(breakpad_info.requesting_thread_id.unwrap(), 0xbf4);
}

#[test]
fn test_exception() {
    let dump = read_test_minidump().unwrap();
    let exception = dump.get_stream::<MinidumpException>().unwrap();
    assert_eq!(exception.thread_id, 0xbf4);
    assert_eq!(exception.raw.exception_record.exception_code, 0xc0000005);
    if let Some(ref ctx) = exception.context {
        assert_eq!(ctx.get_instruction_pointer(), 0x40429e);
        assert_eq!(ctx.get_stack_pointer(), 0x12fe84);
        if let &MinidumpContext {
            raw: MinidumpRawContext::X86(ref raw),
            ref valid,
        } = ctx
        {
            assert_eq!(raw.eip, 0x40429e);
            assert_eq!(*valid, MinidumpContextValidity::All);
        } else {
            assert!(false, "Wrong context type");
        }
    } else {
        assert!(false, "Missing context");
    }
}

#[test]
fn test_thread_list() {
    let dump = read_test_minidump().unwrap();
    let thread_list = dump.get_stream::<MinidumpThreadList>().unwrap();
    let ref threads = thread_list.threads;
    assert_eq!(threads.len(), 2);
    assert_eq!(threads[0].raw.thread_id, 0xbf4);
    assert_eq!(threads[1].raw.thread_id, 0x11c0);
    let id = threads[1].raw.thread_id;
    assert_eq!(thread_list.get_thread(id).unwrap().raw.thread_id, id);
    if let Some(ref ctx) = threads[0].context {
        assert_eq!(ctx.get_instruction_pointer(), 0x7c90eb94);
        assert_eq!(ctx.get_stack_pointer(), 0x12f320);
        if let &MinidumpContext {
            raw: MinidumpRawContext::X86(ref raw),
            ref valid,
        } = ctx
        {
            assert_eq!(raw.eip, 0x7c90eb94);
            assert_eq!(*valid, MinidumpContextValidity::All);
        } else {
            assert!(false, "Wrong context type");
        }
    } else {
        assert!(false, "Missing context");
    }
    if let Some(ref stack) = threads[0].stack {
        // Try the beginning
        assert_eq!(stack.get_memory_at_address::<u8>(0x12f31c).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u16>(0x12f31c).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u32>(0x12f31c).unwrap(), 0);
        assert_eq!(
            stack.get_memory_at_address::<u64>(0x12f31c).unwrap(),
            0x7c90e9c000000000
        );
        // And the end
        assert_eq!(stack.get_memory_at_address::<u8>(0x12ffff).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u16>(0x12fffe).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u32>(0x12fffc).unwrap(), 0);
        assert_eq!(
            stack.get_memory_at_address::<u64>(0x12fff8).unwrap(),
            0x405443
        );
    } else {
        assert!(false, "Missing stack memory");
    }
}
<commit_msg>Add a test that reading an empty minidump fails properly<commit_after>// Copyright 2015 Ted Mielczarek. See the COPYRIGHT
// file at the top-level directory of this distribution.

extern crate chrono;
extern crate failure;
extern crate memmap;
extern crate minidump;
extern crate minidump_common;

use chrono::prelude::*;
use memmap::Mmap;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use minidump::*;
use minidump::system_info::{CPU, OS};
use minidump_common::traits::Module;

fn get_test_minidump_path() -> PathBuf {
    let mut path = PathBuf::from(file!());
    path.pop();
    path.pop();
    path.push("testdata/test.dmp");
    path
}

fn read_test_minidump<'a>() -> Result<Minidump<'a, Mmap>, Error> {
    let path = get_test_minidump_path();
    Minidump::read_path(&path)
}

#[test]
fn test_minidump_read_path() {
    read_test_minidump().unwrap();
}

#[test]
fn test_minidump_read() {
    let path = get_test_minidump_path();
    let mut f = File::open(path).unwrap();
    let mut buf = vec![];
    f.read_to_end(&mut buf).unwrap();
    let _dump = Minidump::read(buf).unwrap();
}

#[test]
fn test_module_list() {
    let dump = read_test_minidump().unwrap();
    let module_list = dump.get_stream::<MinidumpModuleList>().unwrap();
    assert_eq!(
        module_list.module_at_address(0x400000).unwrap().code_file(),
        "c:\\test_app.exe"
    );
    let modules = module_list.iter().collect::<Vec<_>>();
    let module_files = modules.iter().map(|m| m.code_file()).collect::<Vec<_>>();
    assert_eq!(modules.len(), 13);
    assert_eq!(modules[0].base_address(), 0x400000);
    assert_eq!(modules[0].size(), 0x2d000);
    assert_eq!(modules[0].code_file(), "c:\\test_app.exe");
    assert_eq!(modules[0].code_identifier(), "45D35F6C2d000");
    assert_eq!(modules[0].debug_file().unwrap(), "c:\\test_app.pdb");
    assert_eq!(
        modules[0].debug_identifier().unwrap(),
        "5A9832E5287241C1838ED98914E9B7FF1"
    );
    assert!(modules[0].version().is_none());

    assert_eq!(modules[12].base_address(), 0x76bf0000);
    assert_eq!(modules[12].size(), 0xb000);
    assert_eq!(modules[12].code_file(), "C:\\WINDOWS\\system32\\psapi.dll");
    assert_eq!(modules[12].code_identifier(), "411096CAb000");
    assert_eq!(modules[12].debug_file().unwrap(), "psapi.pdb");
    assert_eq!(
        modules[12].debug_identifier().unwrap(),
        "A5C3A1F9689F43D8AD228A09293889702"
    );
    assert_eq!(modules[12].version().unwrap(), "5.1.2600.2180");

    assert_eq!(
        module_files,
        vec![
            r"c:\test_app.exe",
            r"C:\WINDOWS\system32\ntdll.dll",
            r"C:\WINDOWS\system32\kernel32.dll",
            r"C:\WINDOWS\system32\ole32.dll",
            r"C:\WINDOWS\system32\advapi32.dll",
            r"C:\WINDOWS\system32\rpcrt4.dll",
            r"C:\WINDOWS\system32\gdi32.dll",
            r"C:\WINDOWS\system32\user32.dll",
            r"C:\WINDOWS\system32\msvcrt.dll",
            r"C:\WINDOWS\system32\imm32.dll",
            r"C:\WINDOWS\system32\dbghelp.dll",
            r"C:\WINDOWS\system32\version.dll",
            r"C:\WINDOWS\system32\psapi.dll",
        ]
    );

    assert_eq!(
        module_list
            .by_addr()
            .map(|m| m.code_file())
            .collect::<Vec<_>>(),
        vec![
            r"c:\test_app.exe",
            r"C:\WINDOWS\system32\dbghelp.dll",
            r"C:\WINDOWS\system32\imm32.dll",
            r"C:\WINDOWS\system32\psapi.dll",
            r"C:\WINDOWS\system32\ole32.dll",
            r"C:\WINDOWS\system32\version.dll",
            r"C:\WINDOWS\system32\msvcrt.dll",
            r"C:\WINDOWS\system32\user32.dll",
            r"C:\WINDOWS\system32\advapi32.dll",
            r"C:\WINDOWS\system32\rpcrt4.dll",
            r"C:\WINDOWS\system32\gdi32.dll",
            r"C:\WINDOWS\system32\kernel32.dll",
            r"C:\WINDOWS\system32\ntdll.dll",
        ]
    );
}

#[test]
fn test_system_info() {
    let dump = read_test_minidump().unwrap();
    let system_info = dump.get_stream::<MinidumpSystemInfo>().unwrap();
    assert_eq!(system_info.os, OS::Windows);
    assert_eq!(system_info.cpu, CPU::X86);
}

#[test]
fn test_misc_info() {
    let dump = read_test_minidump().unwrap();
    let misc_info = dump.get_stream::<MinidumpMiscInfo>().unwrap();
    assert_eq!(misc_info.raw.process_id(), Some(3932));
    assert_eq!(misc_info.raw.process_create_time(), Some(0x45d35f73));
    assert_eq!(
        misc_info.process_create_time().unwrap(),
        Utc.ymd(2007, 02, 14).and_hms(19, 13, 55)
    );
}

#[test]
fn test_breakpad_info() {
    let dump = read_test_minidump().unwrap();
    let breakpad_info = dump.get_stream::<MinidumpBreakpadInfo>().unwrap();
    assert_eq!(breakpad_info.dump_thread_id.unwrap(), 0x11c0);
    assert_eq!(breakpad_info.requesting_thread_id.unwrap(), 0xbf4);
}

#[test]
fn test_exception() {
    let dump = read_test_minidump().unwrap();
    let exception = dump.get_stream::<MinidumpException>().unwrap();
    assert_eq!(exception.thread_id, 0xbf4);
    assert_eq!(exception.raw.exception_record.exception_code, 0xc0000005);
    if let Some(ref ctx) = exception.context {
        assert_eq!(ctx.get_instruction_pointer(), 0x40429e);
        assert_eq!(ctx.get_stack_pointer(), 0x12fe84);
        if let &MinidumpContext {
            raw: MinidumpRawContext::X86(ref raw),
            ref valid,
        } = ctx
        {
            assert_eq!(raw.eip, 0x40429e);
            assert_eq!(*valid, MinidumpContextValidity::All);
        } else {
            assert!(false, "Wrong context type");
        }
    } else {
        assert!(false, "Missing context");
    }
}

#[test]
fn test_thread_list() {
    let dump = read_test_minidump().unwrap();
    let thread_list = dump.get_stream::<MinidumpThreadList>().unwrap();
    let ref threads = thread_list.threads;
    assert_eq!(threads.len(), 2);
    assert_eq!(threads[0].raw.thread_id, 0xbf4);
    assert_eq!(threads[1].raw.thread_id, 0x11c0);
    let id = threads[1].raw.thread_id;
    assert_eq!(thread_list.get_thread(id).unwrap().raw.thread_id, id);
    if let Some(ref ctx) = threads[0].context {
        assert_eq!(ctx.get_instruction_pointer(), 0x7c90eb94);
        assert_eq!(ctx.get_stack_pointer(), 0x12f320);
        if let &MinidumpContext {
            raw: MinidumpRawContext::X86(ref raw),
            ref valid,
        } = ctx
        {
            assert_eq!(raw.eip, 0x7c90eb94);
            assert_eq!(*valid, MinidumpContextValidity::All);
        } else {
            assert!(false, "Wrong context type");
        }
    } else {
        assert!(false, "Missing context");
    }
    if let Some(ref stack) = threads[0].stack {
        // Try the beginning
        assert_eq!(stack.get_memory_at_address::<u8>(0x12f31c).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u16>(0x12f31c).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u32>(0x12f31c).unwrap(), 0);
        assert_eq!(
            stack.get_memory_at_address::<u64>(0x12f31c).unwrap(),
            0x7c90e9c000000000
        );
        // And the end
        assert_eq!(stack.get_memory_at_address::<u8>(0x12ffff).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u16>(0x12fffe).unwrap(), 0);
        assert_eq!(stack.get_memory_at_address::<u32>(0x12fffc).unwrap(), 0);
        assert_eq!(
            stack.get_memory_at_address::<u64>(0x12fff8).unwrap(),
            0x405443
        );
    } else {
        assert!(false, "Missing stack memory");
    }
}

#[test]
fn test_empty_minidump() {
    match Minidump::read(&b""[..]) {
        Ok(_) => panic!("Should have failed to read minidump"),
        Err(e) => assert_eq!(e, Error::MissingHeader),
    }
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ByteOrder};
use std::cell::RefCell;
use std::rc::Rc;

use crate::audio_interface::AudioInterface;
use crate::command_processor::CommandProcessor;
use crate::cpu::instruction::Instruction;
use crate::cpu::mmu::Mmu;
use crate::cpu::msr::Msr;
use crate::dsp_interface::DspInterface;
use crate::dvd_interface::DvdInterface;
use crate::exi::Exi;
use crate::gp_fifo::GPFifo;
use crate::memory::Ram;
//use crate::memory_interface::MemoryInterface;
use crate::pixel_engine::PixelEngine;
use crate::processor_interface::ProcessorInterface;
use crate::serial_interface::SerialInterface;
use crate::video_interface::VideoInterface;

const BOOTROM_SIZE: usize = 0x0020_0000; // 2 MB

#[derive(Debug)]
pub enum Address {
    Ram,
    EmbeddedFramebuffer,
    CommandProcessor(u32),
    PixelEngine(u32),
    VideoInterface(u32),
    ProcessorInterface(u32),
    MemoryInterface(u32),
    DspInterface(u32),
    DvdInterface(u32),
    SerialInterface(u32),
    ExpansionInterface(u32, u32),
    AudioInterface(u32),
    GPFifo,
    Bootrom(u32),
}

fn map(address: u32) -> Address {
    match address {
        0x0000_0000...0x017F_FFFF => Address::Ram,
        0x0800_0000...0x0BFF_FFFF => Address::EmbeddedFramebuffer,
        0x0C00_0000...0x0C00_0FFF => Address::CommandProcessor(address - 0x0C00_0000),
        0x0C00_1000...0x0C00_1FFF => Address::PixelEngine(address - 0x0C00_1000),
        0x0C00_2000...0x0C00_2FFF => Address::VideoInterface(address - 0x0C00_2000),
        0x0C00_3000...0x0C00_3FFF => Address::ProcessorInterface(address - 0x0C00_3000),
        0x0C00_4000...0x0C00_4FFF => Address::MemoryInterface(address - 0x0C00_4000),
        0x0C00_5000...0x0C00_5200 => Address::DspInterface(address - 0x0C00_5000),
        0x0C00_6000...0x0C00_63FF => Address::DvdInterface(address - 0x0C00_6000),
        0x0C00_6400...0x0C00_67FF => Address::SerialInterface(address - 0x0C00_6400),
        0x0C00_6800...0x0C00_6938 => {
            let channel = (address - 0x0C00_6800) / 0x14;
            let register = (address - 0x0C00_6800) % 0x14;
            Address::ExpansionInterface(channel, register)
        }
        0x0C00_6C00...0x0C00_6C20 => Address::AudioInterface(address - 0x0C00_6C00),
        0x0C00_8000 => Address::GPFifo,
        0xFFF0_0000...0xFFFF_FFFF => Address::Bootrom(address - 0xFFF0_0000),
        _ => panic!("Unrecognized physical address: {:#x}", address),
    }
}

pub struct Interconnect {
    ai: AudioInterface,
    pub bootrom: Rc<RefCell<Vec<u8>>>,
    cp: CommandProcessor,
    dsp: DspInterface,
    dvd: DvdInterface,
    exi: Exi,
    pub gp: GPFifo,
    pub mmu: Mmu,
    //mi: MemoryInterface,
    pe: PixelEngine,
    pi: ProcessorInterface,
    ram: Ram,
    si: SerialInterface,
    vi: VideoInterface,
}

impl Default for Interconnect {
    fn default() -> Self {
        let bootrom = Rc::new(RefCell::new(Vec::with_capacity(BOOTROM_SIZE)));

        Interconnect {
            ai: AudioInterface::default(),
            cp: CommandProcessor::new(),
            dsp: DspInterface::default(),
            dvd: DvdInterface::default(),
            exi: Exi::new(bootrom.clone()),
            bootrom,
            gp: GPFifo::default(),
            mmu: Mmu::default(),
            //mi: MemoryInterface::new(),
            pe: PixelEngine::default(),
            pi: ProcessorInterface::default(),
            ram: Ram::default(),
            si: SerialInterface::default(),
            vi: VideoInterface::default(),
        }
    }
}

impl Interconnect {
    pub fn read_instruction(&self, msr: &Msr, cia: u32) -> Instruction {
        let addr = self.mmu.translate_instr_address(msr, cia);

        let val = match map(addr) {
            Address::Ram => self.ram.read_u32(addr),
            Address::Bootrom(offset) => {
                BigEndian::read_u32(&self.bootrom.borrow()[offset as usize..])
            }
            _ => panic!(
                "read_instruction not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        };

        Instruction(val)
    }

    pub fn read_u8(&self, msr: &Msr, addr: u32) -> u8 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u8(addr),
            Address::Bootrom(offset) => self.bootrom.borrow()[offset as usize],
            _ => panic!(
                "read_u8 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn read_u16(&mut self, msr: &Msr, addr: u32) -> u16 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u16(addr),
            Address::VideoInterface(offset) => self.vi.read_u16(offset, &self.ram),
            Address::DspInterface(offset) => self.dsp.read_u16(offset),
            Address::Bootrom(offset) => {
                BigEndian::read_u16(&self.bootrom.borrow()[offset as usize..])
            }
            Address::PixelEngine(offset) => self.pe.read_u16(offset),
            _ => panic!(
                "read_u16 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn read_u32(&self, msr: &Msr, addr: u32) -> u32 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u32(addr),
            Address::ProcessorInterface(offset) => self.pi.read_u32(offset),
            Address::SerialInterface(offset) => self.si.read_u32(offset),
            Address::ExpansionInterface(channel, register) => self.exi.read(channel, register),
            Address::Bootrom(offset) => {
                BigEndian::read_u32(&self.bootrom.borrow()[offset as usize..])
            }
            Address::AudioInterface(offset) => self.ai.read_u32(offset),
            _ => panic!(
                "read_u32 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn read_u64(&self, msr: &Msr, addr: u32) -> u64 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u64(addr),
            Address::Bootrom(offset) => {
                BigEndian::read_u64(&self.bootrom.borrow()[offset as usize..])
            }
            _ => panic!(
                "read_u64 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u8(&mut self, msr: &Msr, addr: u32, val: u8) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u8(addr, val),
            Address::GPFifo => self
                .gp
                .write_u8(val, &mut self.cp, &mut self.pi, &mut self.ram),
            _ => panic!(
                "write_u8 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u16(&mut self, msr: &Msr, addr: u32, val: u16) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u16(addr, val),
            Address::VideoInterface(offset) => self.vi.write_u16(offset, val),
            Address::MemoryInterface(_) => println!("FixMe: memory interface"),
            Address::DspInterface(offset) => self.dsp.write_u16(offset, val),
            Address::CommandProcessor(offset) => self.cp.write_u16(offset, val),
            Address::PixelEngine(offset) => self.pe.write_u16(offset, val),
            _ => panic!(
                "write_u16 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u32(&mut self, msr: &Msr, addr: u32, val: u32) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u32(addr, val),
            Address::VideoInterface(offset) => self.vi.write_u32(offset, val),
            Address::ProcessorInterface(offset) => self.pi.write_u32(offset, val),
            Address::DspInterface(offset) => {
                self.dsp.write_u16(offset, (val >> 16) as u16);
                self.dsp.write_u16(offset + 2, val as u16);
            }
            Address::CommandProcessor(offset) => self.cp.write_u32(offset, val),
            Address::DvdInterface(offset) => self.dvd.write_u32(offset, val),
            Address::SerialInterface(offset) => self.si.write_u32(offset, val),
            Address::ExpansionInterface(channel, register) => {
                self.exi.write(channel, register, val, &mut self.ram)
            }
            Address::AudioInterface(offset) => self.ai.write_u32(offset, val),
            Address::GPFifo => self
                .gp
                .write_u32(val, &mut self.cp, &mut self.pi, &mut self.ram),
            _ => panic!(
                "write_u32 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u64(&mut self, msr: &Msr, addr: u32, val: u64) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u64(addr, val),
            Address::GPFifo => self
                .gp
                .write_u64(val, &mut self.cp, &mut self.pi, &mut self.ram),
            _ => panic!(
                "write_u64 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write(&mut self, msr: &Msr, addr: u32, data: &[u8]) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_dma(addr, data),
            _ => panic!(
                "write_u64 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }
}
<commit_msg>Initialize bootrom Vec with zeroes<commit_after>use byteorder::{BigEndian, ByteOrder};
use std::cell::RefCell;
use std::rc::Rc;

use crate::audio_interface::AudioInterface;
use crate::command_processor::CommandProcessor;
use crate::cpu::instruction::Instruction;
use crate::cpu::mmu::Mmu;
use crate::cpu::msr::Msr;
use crate::dsp_interface::DspInterface;
use crate::dvd_interface::DvdInterface;
use crate::exi::Exi;
use crate::gp_fifo::GPFifo;
use crate::memory::Ram;
//use crate::memory_interface::MemoryInterface;
use crate::pixel_engine::PixelEngine;
use crate::processor_interface::ProcessorInterface;
use crate::serial_interface::SerialInterface;
use crate::video_interface::VideoInterface;

const BOOTROM_SIZE: usize = 0x0020_0000; // 2 MB

#[derive(Debug)]
pub enum Address {
    Ram,
    EmbeddedFramebuffer,
    CommandProcessor(u32),
    PixelEngine(u32),
    VideoInterface(u32),
    ProcessorInterface(u32),
    MemoryInterface(u32),
    DspInterface(u32),
    DvdInterface(u32),
    SerialInterface(u32),
    ExpansionInterface(u32, u32),
    AudioInterface(u32),
    GPFifo,
    Bootrom(u32),
}

fn map(address: u32) -> Address {
    match address {
        0x0000_0000...0x017F_FFFF => Address::Ram,
        0x0800_0000...0x0BFF_FFFF => Address::EmbeddedFramebuffer,
        0x0C00_0000...0x0C00_0FFF => Address::CommandProcessor(address - 0x0C00_0000),
        0x0C00_1000...0x0C00_1FFF => Address::PixelEngine(address - 0x0C00_1000),
        0x0C00_2000...0x0C00_2FFF => Address::VideoInterface(address - 0x0C00_2000),
        0x0C00_3000...0x0C00_3FFF => Address::ProcessorInterface(address - 0x0C00_3000),
        0x0C00_4000...0x0C00_4FFF => Address::MemoryInterface(address - 0x0C00_4000),
        0x0C00_5000...0x0C00_5200 => Address::DspInterface(address - 0x0C00_5000),
        0x0C00_6000...0x0C00_63FF => Address::DvdInterface(address - 0x0C00_6000),
        0x0C00_6400...0x0C00_67FF => Address::SerialInterface(address - 0x0C00_6400),
        0x0C00_6800...0x0C00_6938 => {
            let channel = (address - 0x0C00_6800) / 0x14;
            let register = (address - 0x0C00_6800) % 0x14;
            Address::ExpansionInterface(channel, register)
        }
        0x0C00_6C00...0x0C00_6C20 => Address::AudioInterface(address - 0x0C00_6C00),
        0x0C00_8000 => Address::GPFifo,
        0xFFF0_0000...0xFFFF_FFFF => Address::Bootrom(address - 0xFFF0_0000),
        _ => panic!("Unrecognized physical address: {:#x}", address),
    }
}

pub struct Interconnect {
    ai: AudioInterface,
    pub bootrom: Rc<RefCell<Vec<u8>>>,
    cp: CommandProcessor,
    dsp: DspInterface,
    dvd: DvdInterface,
    exi: Exi,
    pub gp: GPFifo,
    pub mmu: Mmu,
    //mi: MemoryInterface,
    pe: PixelEngine,
    pi: ProcessorInterface,
    ram: Ram,
    si: SerialInterface,
    vi: VideoInterface,
}

impl Default for Interconnect {
    fn default() -> Self {
        let bootrom = Rc::new(RefCell::new(vec![0; BOOTROM_SIZE]));

        Interconnect {
            ai: AudioInterface::default(),
            cp: CommandProcessor::new(),
            dsp: DspInterface::default(),
            dvd: DvdInterface::default(),
            exi: Exi::new(bootrom.clone()),
            bootrom,
            gp: GPFifo::default(),
            mmu: Mmu::default(),
            //mi: MemoryInterface::new(),
            pe: PixelEngine::default(),
            pi: ProcessorInterface::default(),
            ram: Ram::default(),
            si: SerialInterface::default(),
            vi: VideoInterface::default(),
        }
    }
}

impl Interconnect {
    pub fn read_instruction(&self, msr: &Msr, cia: u32) -> Instruction {
        let addr = self.mmu.translate_instr_address(msr, cia);

        let val = match map(addr) {
            Address::Ram => self.ram.read_u32(addr),
            Address::Bootrom(offset) => {
                BigEndian::read_u32(&self.bootrom.borrow()[offset as usize..])
            }
            _ => panic!(
                "read_instruction not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        };

        Instruction(val)
    }

    pub fn read_u8(&self, msr: &Msr, addr: u32) -> u8 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u8(addr),
            Address::Bootrom(offset) => self.bootrom.borrow()[offset as usize],
            _ => panic!(
                "read_u8 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn read_u16(&mut self, msr: &Msr, addr: u32) -> u16 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u16(addr),
            Address::VideoInterface(offset) => self.vi.read_u16(offset, &self.ram),
            Address::DspInterface(offset) => self.dsp.read_u16(offset),
            Address::Bootrom(offset) => {
                BigEndian::read_u16(&self.bootrom.borrow()[offset as usize..])
            }
            Address::PixelEngine(offset) => self.pe.read_u16(offset),
            _ => panic!(
                "read_u16 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn read_u32(&self, msr: &Msr, addr: u32) -> u32 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u32(addr),
            Address::ProcessorInterface(offset) => self.pi.read_u32(offset),
            Address::SerialInterface(offset) => self.si.read_u32(offset),
            Address::ExpansionInterface(channel, register) => self.exi.read(channel, register),
            Address::Bootrom(offset) => {
                BigEndian::read_u32(&self.bootrom.borrow()[offset as usize..])
            }
            Address::AudioInterface(offset) => self.ai.read_u32(offset),
            _ => panic!(
                "read_u32 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn read_u64(&self, msr: &Msr, addr: u32) -> u64 {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.read_u64(addr),
            Address::Bootrom(offset) => {
                BigEndian::read_u64(&self.bootrom.borrow()[offset as usize..])
            }
            _ => panic!(
                "read_u64 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u8(&mut self, msr: &Msr, addr: u32, val: u8) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u8(addr, val),
            Address::GPFifo => self
                .gp
                .write_u8(val, &mut self.cp, &mut self.pi, &mut self.ram),
            _ => panic!(
                "write_u8 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u16(&mut self, msr: &Msr, addr: u32, val: u16) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u16(addr, val),
            Address::VideoInterface(offset) => self.vi.write_u16(offset, val),
            Address::MemoryInterface(_) => println!("FixMe: memory interface"),
            Address::DspInterface(offset) => self.dsp.write_u16(offset, val),
            Address::CommandProcessor(offset) => self.cp.write_u16(offset, val),
            Address::PixelEngine(offset) => self.pe.write_u16(offset, val),
            _ => panic!(
                "write_u16 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u32(&mut self, msr: &Msr, addr: u32, val: u32) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u32(addr, val),
            Address::VideoInterface(offset) => self.vi.write_u32(offset, val),
            Address::ProcessorInterface(offset) => self.pi.write_u32(offset, val),
            Address::DspInterface(offset) => {
                self.dsp.write_u16(offset, (val >> 16) as u16);
                self.dsp.write_u16(offset + 2, val as u16);
            }
            Address::CommandProcessor(offset) => self.cp.write_u32(offset, val),
            Address::DvdInterface(offset) => self.dvd.write_u32(offset, val),
            Address::SerialInterface(offset) => self.si.write_u32(offset, val),
            Address::ExpansionInterface(channel, register) => {
                self.exi.write(channel, register, val, &mut self.ram)
            }
            Address::AudioInterface(offset) => self.ai.write_u32(offset, val),
            Address::GPFifo => self
                .gp
                .write_u32(val, &mut self.cp, &mut self.pi, &mut self.ram),
            _ => panic!(
                "write_u32 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write_u64(&mut self, msr: &Msr, addr: u32, val: u64) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_u64(addr, val),
            Address::GPFifo => self
                .gp
                .write_u64(val, &mut self.cp, &mut self.pi, &mut self.ram),
            _ => panic!(
                "write_u64 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }

    pub fn write(&mut self, msr: &Msr, addr: u32, data: &[u8]) {
        let addr = self.mmu.translate_data_address(msr, addr);

        match map(addr) {
            Address::Ram => self.ram.write_dma(addr, data),
            _ => panic!(
                "write_u64 not implemented for {:#?} address {:#x}",
                map(addr),
                addr
            ),
        }
    }
}
<|endoftext|>


<commit_before>use super::*;

use buffer::*;

#[test]
fn output_zero() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![0], output);
}

#[test]
fn inc_by_one() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(1), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![1], output);
}

#[test]
fn inc_by_two() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(2), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![2], output);
}

#[test]
fn inc_by_one_and_by_one() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(1), Inc(1), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![2], output);
}

#[test]
fn inc_wrapping() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(255), Inc(1), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![0], output);
}

#[test]
fn dec_by_one() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(2), Dec(1), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![1], output);
}

#[test]
fn dec_by_two() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(2), Dec(2), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![0], output);
}

#[test]
fn dec_by_one_and_by_one() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Inc(3), Dec(1), Dec(1), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![1], output);
}

#[test]
fn dec_wrapping() {
    let mut input = IBuffer::from_str("");
    let mut output = Vec::new();
    Program { instructions: vec![Dec(1), Inc(2), Output] }.interp(&mut input, &mut output);
    assert_eq!(vec![1], output);
}
<commit_msg>simplified test<commit_after>use super::*;

use buffer::*;

fn execute_and_check(program: &[Instruction], input: &str, exp_output: &[u8]) {
    let mut input = IBuffer::from_str(input);
    let mut output = Vec::new();
    Program { instructions: program.to_vec() }.interp(&mut input, &mut output);
    assert_eq!(exp_output, output.as_slice());
}

#[test]
fn output_zero() {
    execute_and_check(&[Output], "", &[0]);
}

#[test]
fn inc_by_one() {
    execute_and_check(&[Inc(1), Output], "", &[1]);
}

#[test]
fn inc_by_two() {
    execute_and_check(&[Inc(2), Output], "", &[2]);
}

#[test]
fn inc_by_one_and_by_one() {
    execute_and_check(&[Inc(1), Inc(1), Output], "", &[2]);
}

#[test]
fn inc_wrapping() {
    execute_and_check(&[Inc(255), Inc(1), Output], "", &[0]);
}

#[test]
fn dec_by_one() {
    execute_and_check(&[Inc(2), Dec(1), Output], "", &[1]);
}

#[test]
fn dec_by_two() {
    execute_and_check(&[Inc(2), Dec(2), Output], "", &[0]);
}

#[test]
fn dec_by_one_and_by_one() {
    execute_and_check(&[Inc(3), Dec(1), Dec(1), Output], "", &[1]);
}

#[test]
fn dec_wrapping() {
    execute_and_check(&[Dec(1), Inc(2), Output], "", &[1]);
}

#[test]
fn move_right() {
    execute_and_check(&[Inc(2), MoveRight(1), Inc(1), Output], "", &[1]);
}
<|endoftext|>


<commit_before>//! Define table formatting utilities

use std::io::{Write, Error};

use encode_unicode::Utf8Char;

use super::utils::NEWLINE;

/// Alignment for cell's content
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum Alignment {
    /// Align left
    LEFT,
    /// Align in the center
    CENTER,
    /// Align right
    RIGHT,
}

/// Position of a line separator in a table
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum LinePosition {
    /// Table's border on top
    Top,
    /// Line separator between the titles row,
    /// and the first data row
    Title,
    /// Line separator between data rows
    Intern,
    /// Bottom table's border
    Bottom,
}

/// Position of a column separator in a row
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum ColumnPosition {
    /// Left table's border
    Left,
    /// Internal column separators
    Intern,
    /// Rigth table's border
    Right,
}

/// Contains the character used for printing a line separator
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
pub struct LineSeparator {
    /// Line separator
    line: char,
    /// Internal junction separator
    junc: char,
    /// Left junction separator
    ljunc: char,
    /// Right junction separator
    rjunc: char,
}

impl LineSeparator {
    /// Create a new line separator instance where `line` is the character used to separate 2 lines
    /// and `junc` is the one used for junctions between columns and lines
    pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
        LineSeparator {
            line: line,
            junc: junc,
            ljunc: ljunc,
            rjunc: rjunc,
        }
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    #[deprecated(since = "0.6.7", note = "function will be removed. See [issue #57](https://github.com/phsym/prettytable-rs/pull/57).")]
    pub fn print<T: Write + ?Sized>(&self,
                                    out: &mut T,
                                    col_width: &[usize],
                                    colsep: bool,
                                    lborder: bool,
                                    rborder: bool)
                                    -> Result<(), Error> {
        self._print(out, col_width, (1, 1), colsep, lborder, rborder)
    }

    fn _print<T: Write + ?Sized>(&self,
                                 out: &mut T,
                                 col_width: &[usize],
                                 padding: (usize, usize),
                                 colsep: bool,
                                 lborder: bool,
                                 rborder: bool)
                                 -> Result<(), Error> {
        if lborder {
            out.write_all(Utf8Char::from(self.ljunc).as_bytes())?;
        }
        let mut iter = col_width.into_iter().peekable();
        while let Some(width) = iter.next() {
            for _ in 0..width + padding.0 + padding.1 {
                out.write_all(Utf8Char::from(self.line).as_bytes())?;
            }
            if colsep && iter.peek().is_some() {
                out.write_all(Utf8Char::from(self.junc).as_bytes())?;
            }
        }
        if rborder {
            out.write_all(Utf8Char::from(self.rjunc).as_bytes())?;
        }
        out.write_all(NEWLINE)
    }
}

impl Default for LineSeparator {
    fn default() -> Self {
        LineSeparator::new('-', '+', '+', '+')
    }
}

/// Contains the table formatting rules
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
pub struct TableFormat {
    /// Optional column separator character
    csep: Option<char>,
    /// Optional left border character
    lborder: Option<char>,
    /// Optional right border character
    rborder: Option<char>,
    /// Optional internal line separator
    lsep: Option<LineSeparator>,
    /// Optional title line separator
    tsep: Option<LineSeparator>,
    /// Optional top line separator
    top_sep: Option<LineSeparator>,
    /// Optional bottom line separator
    bottom_sep: Option<LineSeparator>,
    /// Left padding
    pad_left: usize,
    /// Right padding
    pad_right: usize,
    /// Global indentation when rendering the table
    indent: usize,
}

impl TableFormat {
    /// Create a new empty TableFormat.
    pub fn new() -> TableFormat {
        TableFormat {
            csep: None,
            lborder: None,
            rborder: None,
            lsep: None,
            tsep: None,
            top_sep: None,
            bottom_sep: None,
            pad_left: 0,
            pad_right: 0,
            indent: 0,
        }
    }

    /// Return a tuple with left and right padding
    pub fn get_padding(&self) -> (usize, usize) {
        (self.pad_left, self.pad_right)
    }

    /// Set left and right padding
    pub fn padding(&mut self, left: usize, right: usize) {
        self.pad_left = left;
        self.pad_right = right;
    }

    /// Set the character used for internal column separation
    pub fn column_separator(&mut self, separator: char) {
        self.csep = Some(separator);
    }

    /// Set the character used for table borders
    pub fn borders(&mut self, border: char) {
        self.lborder = Some(border);
        self.rborder = Some(border);
    }

    /// Set a line separator
    pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) {
        *match what {
             LinePosition::Top => &mut self.top_sep,
             LinePosition::Bottom => &mut self.bottom_sep,
             LinePosition::Title => &mut self.tsep,
             LinePosition::Intern => &mut self.lsep,
         } = Some(separator);
    }

    /// Set format for multiple kind of line separator
    pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) {
        for pos in what {
            self.separator(*pos, separator);
        }
    }

    fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
        match pos {
            LinePosition::Intern => &self.lsep,
            LinePosition::Top => &self.top_sep,
            LinePosition::Bottom => &self.bottom_sep,
            LinePosition::Title => {
                match &self.tsep {
                    s @ &Some(_) => s,
                    &None => &self.lsep,
                }
            }
        }
    }

    /// Set global indentation in spaces used when rendering a table
    pub fn indent(&mut self, spaces: usize) {
        self.indent = spaces;
    }

    /// Get global indentation in spaces used when rendering a table
    pub fn get_indent(&self) -> usize {
        self.indent
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    pub fn print_line_separator<T: Write + ?Sized>(&self,
                                                   out: &mut T,
                                                   col_width: &[usize],
                                                   pos: LinePosition)
                                                   -> Result<(), Error> {
        match *self.get_sep_for_line(pos) {
            Some(ref l) => {
                //TODO: Wrap this into dedicated function one day
                out.write_all(&vec![b' '; self.get_indent()])?;
                l._print(out,
                         col_width,
                         self.get_padding(),
                         self.csep.is_some(),
                         self.lborder.is_some(),
                         self.rborder.is_some())
            }
            None => Ok(()),
        }
    }

    /// Returns the character used to separate columns.
    /// `pos` specify if the separator is left/right final or internal to the table
    pub fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
        match pos {
            ColumnPosition::Left => self.lborder,
            ColumnPosition::Intern => self.csep,
            ColumnPosition::Right => self.rborder,
        }
    }

    /// Print a column separator or a table border
    pub fn print_column_separator<T: Write + ?Sized>(&self,
                                                     out: &mut T,
                                                     pos: ColumnPosition)
                                                     -> Result<(), Error> {
        match self.get_column_separator(pos) {
            Some(s) => out.write_all(Utf8Char::from(s).as_bytes()),
            None => Ok(()),
        }
    }
}

impl Default for TableFormat {
    fn default() -> Self {
        TableFormat::new()
    }
}

/// A builder to create a `TableFormat`
pub struct FormatBuilder {
    format: Box<TableFormat>,
}

impl FormatBuilder {
    /// Creates a new builder
    pub fn new() -> FormatBuilder {
        FormatBuilder { format: Box::new(TableFormat::new()) }
    }

    /// Set left and right padding
    pub fn padding(mut self, left: usize, right: usize) -> Self {
        self.format.padding(left, right);
        self
    }

    /// Set the character used for internal column separation
    pub fn column_separator(mut self, separator: char) -> Self {
        self.format.column_separator(separator);
        self
    }

    /// Set the character used for table borders
    pub fn borders(mut self, border: char) -> Self {
        self.format.borders(border);
        self
    }

    /// Set a line separator format
    pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self {
        self.format.separator(what, separator);
        self
    }

    /// Set separator format for multiple kind of line separators
    pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self {
        self.format.separators(what, separator);
        self
    }

    /// Set global indentation in spaces used when rendering a table
    pub fn indent(mut self, spaces: usize) -> Self {
        self.format.indent(spaces);
        self
    }

    /// Return the generated `TableFormat`
    pub fn build(&self) -> TableFormat {
        *self.format
    }
}

impl Into<TableFormat> for FormatBuilder {
    fn into(self) -> TableFormat {
        *self.format
    }
}

impl From<TableFormat> for FormatBuilder {
    fn from(fmt: TableFormat) -> Self {
        FormatBuilder { format: Box::new(fmt) }
    }
}

/// Predifined formats. Those constants are lazily evaluated when
/// the corresponding struct is dereferenced
pub mod consts {
    use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition};

    lazy_static! {
        /// A line separator made of `-` and `+`
        static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+');
        /// A line separator made of `=` and `+`
        static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+');

        /// Default table format
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +====+====+
        /// | a  | b  |
        /// +----+----+
        /// | d  | c  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Similar to `FORMAT_DEFAULT` but without special separator after title line
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// +----+----+
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line separator, but with title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line or title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// No column separator
        ///
        /// # Example
        /// ```text
        /// --------
        ///  T1  T2
        /// ========
        ///  a   b
        /// --------
        ///  d   c
        /// --------
        /// ```
        pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new()
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for printing a table without any separators (only alignment)
        ///
        /// # Example
        /// ```text
        ///  T1  T2
        ///  a   b
        ///  d   c
        /// ```
        pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for a table with only external borders and title separator
        ///
        /// # Example
        /// ```text
        /// +--------+
        /// | T1  T2 |
        /// +========+
        /// | a   b  |
        /// | c   d  |
        /// +--------+
        /// ```
        pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .borders('|')
                                                                    .build();

        /// A table with no external border
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ====+====
        ///  a  | b
        /// ----+----
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();

        /// A table with no external border and no line separation
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ----+----
        ///  a  | b
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();
    }
}
<commit_msg>Added the possibility to style the left and right border separately.<commit_after>//! Define table formatting utilities

use std::io::{Write, Error};

use encode_unicode::Utf8Char;

use super::utils::NEWLINE;

/// Alignment for cell's content
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum Alignment {
    /// Align left
    LEFT,
    /// Align in the center
    CENTER,
    /// Align right
    RIGHT,
}

/// Position of a line separator in a table
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum LinePosition {
    /// Table's border on top
    Top,
    /// Line separator between the titles row,
    /// and the first data row
    Title,
    /// Line separator between data rows
    Intern,
    /// Bottom table's border
    Bottom,
}

/// Position of a column separator in a row
#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
pub enum ColumnPosition {
    /// Left table's border
    Left,
    /// Internal column separators
    Intern,
    /// Rigth table's border
    Right,
}

/// Contains the character used for printing a line separator
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
pub struct LineSeparator {
    /// Line separator
    line: char,
    /// Internal junction separator
    junc: char,
    /// Left junction separator
    ljunc: char,
    /// Right junction separator
    rjunc: char,
}

impl LineSeparator {
    /// Create a new line separator instance where `line` is the character used to separate 2 lines
    /// and `junc` is the one used for junctions between columns and lines
    pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
        LineSeparator {
            line: line,
            junc: junc,
            ljunc: ljunc,
            rjunc: rjunc,
        }
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    #[deprecated(since = "0.6.7", note = "function will be removed. See [issue #57](https://github.com/phsym/prettytable-rs/pull/57).")]
    pub fn print<T: Write + ?Sized>(&self,
                                    out: &mut T,
                                    col_width: &[usize],
                                    colsep: bool,
                                    lborder: bool,
                                    rborder: bool)
                                    -> Result<(), Error> {
        self._print(out, col_width, (1, 1), colsep, lborder, rborder)
    }

    fn _print<T: Write + ?Sized>(&self,
                                 out: &mut T,
                                 col_width: &[usize],
                                 padding: (usize, usize),
                                 colsep: bool,
                                 lborder: bool,
                                 rborder: bool)
                                 -> Result<(), Error> {
        if lborder {
            out.write_all(Utf8Char::from(self.ljunc).as_bytes())?;
        }
        let mut iter = col_width.into_iter().peekable();
        while let Some(width) = iter.next() {
            for _ in 0..width + padding.0 + padding.1 {
                out.write_all(Utf8Char::from(self.line).as_bytes())?;
            }
            if colsep && iter.peek().is_some() {
                out.write_all(Utf8Char::from(self.junc).as_bytes())?;
            }
        }
        if rborder {
            out.write_all(Utf8Char::from(self.rjunc).as_bytes())?;
        }
        out.write_all(NEWLINE)
    }
}

impl Default for LineSeparator {
    fn default() -> Self {
        LineSeparator::new('-', '+', '+', '+')
    }
}

/// Contains the table formatting rules
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
pub struct TableFormat {
    /// Optional column separator character
    csep: Option<char>,
    /// Optional left border character
    lborder: Option<char>,
    /// Optional right border character
    rborder: Option<char>,
    /// Optional internal line separator
    lsep: Option<LineSeparator>,
    /// Optional title line separator
    tsep: Option<LineSeparator>,
    /// Optional top line separator
    top_sep: Option<LineSeparator>,
    /// Optional bottom line separator
    bottom_sep: Option<LineSeparator>,
    /// Left padding
    pad_left: usize,
    /// Right padding
    pad_right: usize,
    /// Global indentation when rendering the table
    indent: usize,
}

impl TableFormat {
    /// Create a new empty TableFormat.
    pub fn new() -> TableFormat {
        TableFormat {
            csep: None,
            lborder: None,
            rborder: None,
            lsep: None,
            tsep: None,
            top_sep: None,
            bottom_sep: None,
            pad_left: 0,
            pad_right: 0,
            indent: 0,
        }
    }

    /// Return a tuple with left and right padding
    pub fn get_padding(&self) -> (usize, usize) {
        (self.pad_left, self.pad_right)
    }

    /// Set left and right padding
    pub fn padding(&mut self, left: usize, right: usize) {
        self.pad_left = left;
        self.pad_right = right;
    }

    /// Set the character used for internal column separation
    pub fn column_separator(&mut self, separator: char) {
        self.csep = Some(separator);
    }

    /// Set the character used for table borders
    pub fn borders(&mut self, border: char) {
        self.lborder = Some(border);
        self.rborder = Some(border);
    }

    /// Set the character used for left table border
    pub fn left_border(&mut self, border: char) {
        self.lborder = Some(border);
    }

    /// Set the character used for right table border
    pub fn right_border(&mut self, border: char) {
        self.rborder = Some(border);
    }

    /// Set a line separator
    pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) {
        *match what {
             LinePosition::Top => &mut self.top_sep,
             LinePosition::Bottom => &mut self.bottom_sep,
             LinePosition::Title => &mut self.tsep,
             LinePosition::Intern => &mut self.lsep,
         } = Some(separator);
    }

    /// Set format for multiple kind of line separator
    pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) {
        for pos in what {
            self.separator(*pos, separator);
        }
    }

    fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
        match pos {
            LinePosition::Intern => &self.lsep,
            LinePosition::Top => &self.top_sep,
            LinePosition::Bottom => &self.bottom_sep,
            LinePosition::Title => {
                match &self.tsep {
                    s @ &Some(_) => s,
                    &None => &self.lsep,
                }
            }
        }
    }

    /// Set global indentation in spaces used when rendering a table
    pub fn indent(&mut self, spaces: usize) {
        self.indent = spaces;
    }

    /// Get global indentation in spaces used when rendering a table
    pub fn get_indent(&self) -> usize {
        self.indent
    }

    /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column
    pub fn print_line_separator<T: Write + ?Sized>(&self,
                                                   out: &mut T,
                                                   col_width: &[usize],
                                                   pos: LinePosition)
                                                   -> Result<(), Error> {
        match *self.get_sep_for_line(pos) {
            Some(ref l) => {
                //TODO: Wrap this into dedicated function one day
                out.write_all(&vec![b' '; self.get_indent()])?;
                l._print(out,
                         col_width,
                         self.get_padding(),
                         self.csep.is_some(),
                         self.lborder.is_some(),
                         self.rborder.is_some())
            }
            None => Ok(()),
        }
    }

    /// Returns the character used to separate columns.
    /// `pos` specify if the separator is left/right final or internal to the table
    pub fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
        match pos {
            ColumnPosition::Left => self.lborder,
            ColumnPosition::Intern => self.csep,
            ColumnPosition::Right => self.rborder,
        }
    }

    /// Print a column separator or a table border
    pub fn print_column_separator<T: Write + ?Sized>(&self,
                                                     out: &mut T,
                                                     pos: ColumnPosition)
                                                     -> Result<(), Error> {
        match self.get_column_separator(pos) {
            Some(s) => out.write_all(Utf8Char::from(s).as_bytes()),
            None => Ok(()),
        }
    }
}

impl Default for TableFormat {
    fn default() -> Self {
        TableFormat::new()
    }
}

/// A builder to create a `TableFormat`
pub struct FormatBuilder {
    format: Box<TableFormat>,
}

impl FormatBuilder {
    /// Creates a new builder
    pub fn new() -> FormatBuilder {
        FormatBuilder { format: Box::new(TableFormat::new()) }
    }

    /// Set left and right padding
    pub fn padding(mut self, left: usize, right: usize) -> Self {
        self.format.padding(left, right);
        self
    }

    /// Set the character used for internal column separation
    pub fn column_separator(mut self, separator: char) -> Self {
        self.format.column_separator(separator);
        self
    }

    /// Set the character used for table borders
    pub fn borders(mut self, border: char) -> Self {
        self.format.borders(border);
        self
    }

    /// Set the character used for left table border
    pub fn left_border(mut self, border: char) -> Self {
        self.format.left_border(border);
        self
    }

    /// Set the character used for right table border
    pub fn right_border(mut self, border: char) -> Self {
        self.format.right_border(border);
        self
    }

    /// Set a line separator format
    pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self {
        self.format.separator(what, separator);
        self
    }

    /// Set separator format for multiple kind of line separators
    pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self {
        self.format.separators(what, separator);
        self
    }

    /// Set global indentation in spaces used when rendering a table
    pub fn indent(mut self, spaces: usize) -> Self {
        self.format.indent(spaces);
        self
    }

    /// Return the generated `TableFormat`
    pub fn build(&self) -> TableFormat {
        *self.format
    }
}

impl Into<TableFormat> for FormatBuilder {
    fn into(self) -> TableFormat {
        *self.format
    }
}

impl From<TableFormat> for FormatBuilder {
    fn from(fmt: TableFormat) -> Self {
        FormatBuilder { format: Box::new(fmt) }
    }
}

/// Predifined formats. Those constants are lazily evaluated when
/// the corresponding struct is dereferenced
pub mod consts {
    use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition};

    lazy_static! {
        /// A line separator made of `-` and `+`
        static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+');
        /// A line separator made of `=` and `+`
        static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+');

        /// Default table format
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +====+====+
        /// | a  | b  |
        /// +----+----+
        /// | d  | c  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Similar to `FORMAT_DEFAULT` but without special separator after title line
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// +----+----+
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line separator, but with title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// +----+----+
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// With no line or title separator
        ///
        /// # Example
        /// ```text
        /// +----+----+
        /// | T1 | T2 |
        /// | a  | b  |
        /// | c  | d  |
        /// +----+----+
        /// ```
        pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new()
                                                                    .column_separator('|')
                                                                    .borders('|')
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// No column separator
        ///
        /// # Example
        /// ```text
        /// --------
        ///  T1  T2
        /// ========
        ///  a   b
        /// --------
        ///  d   c
        /// --------
        /// ```
        pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new()
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for printing a table without any separators (only alignment)
        ///
        /// # Example
        /// ```text
        ///  T1  T2
        ///  a   b
        ///  d   c
        /// ```
        pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .build();

        /// Format for a table with only external borders and title separator
        ///
        /// # Example
        /// ```text
        /// +--------+
        /// | T1  T2 |
        /// +========+
        /// | a   b  |
        /// | c   d  |
        /// +--------+
        /// ```
        pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Top, *MINUS_PLUS_SEP)
                                                                    .borders('|')
                                                                    .build();

        /// A table with no external border
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ====+====
        ///  a  | b
        /// ----+----
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
                                                                    .separator(LinePosition::Title, *EQU_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();

        /// A table with no external border and no line separation
        ///
        /// # Example
        /// ```text
        ///  T1 | T2
        /// ----+----
        ///  a  | b
        ///  c  | d
        /// ```
        pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new()
                                                                    .padding(1, 1)
                                                                    .separator(LinePosition::Title, *MINUS_PLUS_SEP)
                                                                    .column_separator('|')
                                                                    .build();
    }
}
<|endoftext|>


<commit_before>use luminance::{Dim2, Equation, Factor, Flat, Framebuffer, Mode, Pipe, Pipeline, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use std::cell::RefCell;

use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_window(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_upper: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_upper: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_upper: left_upper
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

/// A renderer responsible of rendering shapes.
pub struct Renderer {
  framebuffer: Framebuffer<Flat, Dim2, Texture2D, ()>,
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  tri_vert_nb: RefCell<usize>,
  quads: RefCell<Tess>,
  quad_vert_nb: RefCell<usize>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  disc_vert_nb: RefCell<usize>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Renderer {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let fb = Framebuffer::new((w, h), 0).unwrap();

    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3), None);

    let quads = Tess::new(Mode::TriangleStrip, TessVertices::Reserve::<Vert>(max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Renderer {
      framebuffer: fb,
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      tri_vert_nb: RefCell::new(0),
      quads: RefCell::new(quads),
      quad_vert_nb: RefCell::new(0),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      disc_vert_nb: RefCell::new(0),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  // Dispatch the supported shape.
  fn dispatch(&self, tris: &mut Tess, quads: &mut Tess, discs: &mut Tess, input: &RenderInput) {
    let mut tris = tris.as_slice_mut().unwrap();
    let mut tri_i = 0;
    let mut quads = quads.as_slice_mut().unwrap();
    let mut quad_i = 0;
    let mut discs = discs.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, d];

      for &v in &abcd {
        quads[quad_i] = v.to_clip_space(&self.unit_converter);
        quad_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    *self.tri_vert_nb.borrow_mut() = tri_i;
    *self.quad_vert_nb.borrow_mut() = quad_i;
    *self.disc_vert_nb.borrow_mut() = disc_i;
  }

  pub fn render(&self, input: RenderInput) -> &Texture2D {
    self.dispatch(&mut self.tris.borrow_mut(), &mut self.quads.borrow_mut(), &mut self.discs.borrow_mut(), &input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, *self.tri_vert_nb.borrow());
    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, *self.disc_vert_nb.borrow());
    let text_quad = TessRender::one_whole(&self.text_quad);

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_upper.pos),
        TEXT_SIZE.alter(self.unit_converter.from_window(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_upper.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_render_cmds: Vec<_> = input.texts.iter().enumerate().map(|(i, _)| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, vec![
            Pipe::new(text_quad.clone())
          ]))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    Pipeline::new(&self.framebuffer, [0., 0., 0., 0.], &[], &[], vec![
      // render triangles
      Pipe::new(ShadingCommand::new(&self.tri_program.borrow(), vec![
        Pipe::new(RenderCommand::new(None, true, vec![
          Pipe::new(tris)
        ]))
      ])),
      // render discs
      Pipe::empty()
        .uniforms(&disc_uniforms)
        .unwrap(ShadingCommand::new(&self.disc_program.borrow(), vec![
          Pipe::new(RenderCommand::new(None, true, vec![
            Pipe::new(discs)
          ]))
        ])),
      // render texts
      Pipe::new(ShadingCommand::new(&self.text_program.borrow(), text_render_cmds))
    ]).run();

    &self.framebuffer.color_slot
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    UnitConverter {
      twice_rw: 2. / (w as f32),
      twice_rh: 2. / (h as f32)
    }
  }

  /// Convert from *window space* coordinates.
  pub fn from_window(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    assert!(x_ >= -1. && x_ <= 1., "x={}", x_);

    let y_ = y * self.twice_rh;
    assert!(y_ >= -1. && y_ <= 1., "y={}", y_);

    [x_, y_]
  }
}
<commit_msg>Fixed disc support.<commit_after>use luminance::{Dim2, Equation, Factor, Flat, Framebuffer, Mode, Pipe, Pipeline, RenderCommand, ShadingCommand, Tess, TessRender, TessVertices, Uniform, Unit, Vertex, VertexFormat};
use std::cell::RefCell;

use resource::Res;
use scene::Scene;
use shader::Program;
use text::TextTexture;
use texture::{RGBA32F, Texture};

/// Vertex used in overlay’s objects. Position coordinates are in *window space*.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Vert {
  /// Position.
  pub pos: [f32; 3],
  /// Color.
  pub color: [f32; 4]
}

impl Vert {
  pub fn new(pos: [f32; 3], color: [f32; 4]) -> Self {
    Vert {
      pos: pos,
      color: color
    }
  }

  /// Convert the vertex’ position to *clip space*.
  ///
  /// > Note: the depth coordinate is not affected.
  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    let converted = converter.from_window(self.pos[0], self.pos[1]);
    let pos_ = [converted[0], converted[1], self.pos[2]];

    Vert {
      pos: pos_,
      color: self.color
    }
  }
}

impl Vertex for Vert {
  fn vertex_format() -> VertexFormat {
    <([f32; 3], [f32; 4]) as Vertex>::vertex_format()
  }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Triangle(pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Quad(pub Vert, pub Vert, pub Vert, pub Vert);

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Disc {
  center: Vert,
  radius: f32
}

impl Disc {
  pub fn new(center: Vert, radius: f32) -> Self {
    Disc {
      center: center,
      radius: radius
    }
  }

  fn to_clip_space(&self, converter: &UnitConverter) -> Self {
    Disc {
      center: self.center.to_clip_space(converter),
      radius: self.radius * converter.rw
    }
  }
}

impl Vertex for Disc {
  fn vertex_format() -> VertexFormat {
    <(Vert, f32) as Vertex>::vertex_format()
  }
}

pub type Texture2D = Texture<Flat, Dim2, RGBA32F>;

pub struct Text<'a> {
  text_texture: &'a TextTexture,
  left_upper: Vert
}

impl<'a> Text<'a> {
  pub fn new(text_texture: &'a TextTexture, left_upper: Vert) -> Self {
    Text {
      text_texture: text_texture,
      left_upper: left_upper
    }
  }
}

const DISC_SCREEN_RATIO: &'static Uniform<f32> = &Uniform::new(0);

const TEXT_SAMPLER: &'static Uniform<Unit> = &Uniform::new(0);
const TEXT_POS: &'static Uniform<[f32; 3]> = &Uniform::new(1);
const TEXT_SIZE: &'static Uniform<[f32; 2]> = &Uniform::new(2);
const TEXT_SCALE: &'static Uniform<f32> = &Uniform::new(3);
const TEXT_COLOR: &'static Uniform<[f32; 4]> = &Uniform::new(4);

/// A renderer responsible of rendering shapes.
pub struct Renderer {
  framebuffer: Framebuffer<Flat, Dim2, Texture2D, ()>,
  ratio: f32,
  tri_program: Res<Program>,
  tris: RefCell<Tess>,
  tri_vert_nb: RefCell<usize>,
  quads: RefCell<Tess>,
  quad_vert_nb: RefCell<usize>,
  disc_program: Res<Program>,
  discs: RefCell<Tess>,
  disc_vert_nb: RefCell<usize>,
  text_program: Res<Program>,
  text_quad: Tess,
  unit_converter: UnitConverter,
}

impl Renderer {
  pub fn new(w: u32, h: u32, max_tris: usize, max_quads: usize, max_discs: usize, scene: &mut Scene) -> Self {
    let fb = Framebuffer::new((w, h), 0).unwrap();

    let tri_program = scene.get("spectra/overlay/triangle.glsl", vec![]).unwrap();
    let tris = Tess::new(Mode::Triangle, TessVertices::Reserve::<Vert>(max_tris * 3), None);

    let quads = Tess::new(Mode::TriangleStrip, TessVertices::Reserve::<Vert>(max_quads * 4), None);

    let disc_program = scene.get("spectra/overlay/disc.glsl", vec![DISC_SCREEN_RATIO.sem("ratio")]).unwrap();
    let discs = Tess::new(Mode::Point, TessVertices::Reserve::<Disc>(max_discs), None);

    let text_program = scene.get("spectra/overlay/text.glsl", vec![
      TEXT_SAMPLER.sem("text_texture"),
      TEXT_POS.sem("pos"),
      TEXT_SIZE.sem("size"),
      TEXT_SCALE.sem("scale"),
      TEXT_COLOR.sem("color")
    ]).unwrap();

    let text_quad = Tess::attributeless(Mode::TriangleStrip, 4);

    Renderer {
      framebuffer: fb,
      ratio: w as f32 / h as f32,
      tri_program: tri_program,
      tris: RefCell::new(tris),
      tri_vert_nb: RefCell::new(0),
      quads: RefCell::new(quads),
      quad_vert_nb: RefCell::new(0),
      disc_program: disc_program,
      discs: RefCell::new(discs),
      disc_vert_nb: RefCell::new(0),
      text_program: text_program,
      text_quad: text_quad,
      unit_converter: UnitConverter::new(w, h)
    }
  }

  // Dispatch the supported shape.
  fn dispatch(&self, tris: &mut Tess, quads: &mut Tess, discs: &mut Tess, input: &RenderInput) {
    let mut tris = tris.as_slice_mut().unwrap();
    let mut tri_i = 0;
    let mut quads = quads.as_slice_mut().unwrap();
    let mut quad_i = 0;
    let mut discs = discs.as_slice_mut().unwrap();
    let mut disc_i = 0;

    for &Triangle(a, b, c) in input.triangles {
      let abc = [a, b, c];

      for &v in &abc {
        tris[tri_i] = v.to_clip_space(&self.unit_converter);
        tri_i += 1;
      }
    }

    for &Quad(a, b, c, d) in input.quads {
      let abcd = [a, b, c, d];

      for &v in &abcd {
        quads[quad_i] = v.to_clip_space(&self.unit_converter);
        quad_i += 1;
      }
    }

    for disc in input.discs {
      discs[disc_i] = disc.to_clip_space(&self.unit_converter);
      disc_i += 1;
    }

    *self.tri_vert_nb.borrow_mut() = tri_i;
    *self.quad_vert_nb.borrow_mut() = quad_i;
    *self.disc_vert_nb.borrow_mut() = disc_i;
  }

  pub fn render(&self, input: RenderInput) -> &Texture2D {
    self.dispatch(&mut self.tris.borrow_mut(), &mut self.quads.borrow_mut(), &mut self.discs.borrow_mut(), &input);

    let tris_ref = self.tris.borrow();
    let tris = TessRender::one_sub(&tris_ref, *self.tri_vert_nb.borrow());
    let discs_ref = self.discs.borrow();
    let discs = TessRender::one_sub(&discs_ref, *self.disc_vert_nb.borrow());
    let text_quad = TessRender::one_whole(&self.text_quad);

    // FIXME: no alloc?
    let text_uniforms: Vec<_> = input.texts.map(|(texts, text_scale)| texts.iter().map(|text| {
      let (tex_w, tex_h) = text.text_texture.size();

      [
        TEXT_SAMPLER.alter(Unit::new(0)),
        TEXT_POS.alter(text.left_upper.pos),
        TEXT_SIZE.alter(self.unit_converter.from_window(tex_w as f32, tex_h as f32)),
        TEXT_SCALE.alter(text_scale),
        TEXT_COLOR.alter(text.left_upper.color),
      ]
    }).collect()).unwrap_or(Vec::new());

    let text_textures: Vec<_> = input.texts.map(|(texts, _)| texts.iter().map(|text| [&***text.text_texture]).collect()).unwrap_or(Vec::new());

    let text_render_cmds: Vec<_> = input.texts.iter().enumerate().map(|(i, _)| {
        let blending = (Equation::Additive, Factor::One, Factor::SrcAlphaComplement);
        Pipe::empty()
          .uniforms(&text_uniforms[i])
          .textures(&text_textures[i])
          .unwrap(RenderCommand::new(Some(blending), true, vec![
            Pipe::new(text_quad.clone())
          ]))
      }).collect();

    let disc_uniforms = [
      DISC_SCREEN_RATIO.alter(self.ratio)
    ];

    Pipeline::new(&self.framebuffer, [0., 0., 0., 0.], &[], &[], vec![
      // render triangles
      Pipe::new(ShadingCommand::new(&self.tri_program.borrow(), vec![
        Pipe::new(RenderCommand::new(None, true, vec![
          Pipe::new(tris)
        ]))
      ])),
      // render discs
      Pipe::empty()
        .uniforms(&disc_uniforms)
        .unwrap(ShadingCommand::new(&self.disc_program.borrow(), vec![
          Pipe::new(RenderCommand::new(None, true, vec![
            Pipe::new(discs)
          ]))
        ])),
      // render texts
      Pipe::new(ShadingCommand::new(&self.text_program.borrow(), text_render_cmds))
    ]).run();

    &self.framebuffer.color_slot
  }
}

#[derive(Clone)]
pub struct RenderInput<'a, 'b> where 'b: 'a {
  triangles: &'a [Triangle],
  quads: &'a [Quad],
  discs: &'a [Disc],
  texts: Option<(&'a [Text<'b>], f32)>
}

impl<'a, 'b> RenderInput<'a, 'b> where 'b: 'a {
  pub fn new() -> Self {
    RenderInput {
      triangles: &[],
      quads: &[],
      discs: &[],
      texts: None,
    }
  }

  pub fn triangles(self, triangles: &'a [Triangle]) -> Self {
    RenderInput {
      triangles: triangles,
      ..self
    }
  }

  pub fn quads(self, quads: &'a [Quad]) -> Self {
    RenderInput {
      quads: quads,
      ..self
    }
  }

  pub fn discs(self, discs: &'a [Disc]) -> Self {
    RenderInput {
      discs: discs,
      ..self
    }
  }

  pub fn texts(self, texts: &'a [Text<'b>], scale: f32) -> Self {
    RenderInput {
      texts: Some((texts, scale.max(0.))),
      ..self
    }
  }
}

/// A *unit converter* is used to position stuff in overlay in a universal and screen independent
/// way.
///
/// The idea is that you provide should handle *window space* coordinates only. This type will then
/// help compute normalized coordinates the renderer can understand.
///
/// For the record, *window space* coordinates are defined as below – given a viewport size of
/// *w × h*:
///
/// ^ y = h
/// |
/// |
/// |    x = w
/// O------>
///
/// The formula used to convert from *window space* to what we call *clip space* is:
///
/// > x' = 2x / w - 1
/// > y' = 2y / h - 1
#[derive(Copy, Clone, Debug, PartialEq)]
struct UnitConverter {
  rw: f32,
  twice_rw: f32,
  twice_rh: f32
}

impl UnitConverter {
  /// Build a unit converter by giving it the size of the viewport.
  pub fn new(w: u32, h: u32) -> Self {
    let rw = 1. / (w as f32);

    UnitConverter {
      rw: rw,
      twice_rw: rw * 0.5,
      twice_rh: h as f32 * 0.5
    }
  }

  /// Convert from *window space* coordinates.
  pub fn from_window(&self, x: f32, y: f32) -> [f32; 2] {
    let x_ = x * self.twice_rw - 1.;
    assert!(x_ >= -1. && x_ <= 1., "x={}", x_);

    let y_ = y * self.twice_rh - 1.;
    assert!(y_ >= -1. && y_ <= 1., "y={}", y_);

    [x_, y_]
  }
}
<|endoftext|>