<commit_before>extern crate winapi;
extern crate libc;
extern crate kernel32;

use std::ffi::CString;
use std::mem::transmute;
use kernel32::{LoadLibraryA, GetProcAddress, FreeLibrary, GetLastError};

#[repr(C)]
struct SimpleCapParams {
    buf: *mut libc::c_int,
    width: libc::c_uint,
    height: libc::c_uint,
}

#[derive(Copy, Clone)]
#[repr(C)]
struct DeviceNo(libc::c_uint);

#[allow(non_snake_case)]
pub struct Cameras {
    countCaptureDevices: extern fn() -> libc::c_int,
    initCapture: extern fn(DeviceNo, *const SimpleCapParams) -> libc::c_int,
    deinitCapture: extern fn(DeviceNo),
    doCapture: extern fn(DeviceNo),
    isCaptureDone: extern fn(DeviceNo) -> libc::c_int,
    getCaptureDeviceName: extern fn(DeviceNo, *mut libc::c_char, libc::c_int),
    ESCAPIVersion: extern fn() -> libc::c_uint,
    #[allow(dead_code)]
    getCapturePropertyValue: extern fn(DeviceNo, libc::c_int) -> libc::c_float,
    #[allow(dead_code)]
    getCapturePropertyAuto: extern fn(DeviceNo, libc::c_int) -> libc::c_int,
    #[allow(dead_code)]
    setCaptureProperty: extern fn(DeviceNo, libc::c_int, libc::c_float, libc::c_int),
    getCaptureErrorLine: extern fn(DeviceNo) -> libc::c_int,
    getCaptureErrorCode: extern fn(DeviceNo) -> libc::c_int,
    capdll: winapi::HMODULE,
}

impl Drop for Cameras {
    fn drop(&mut self) {
        unsafe {
            assert!(FreeLibrary(self.capdll) == 1, "failed to release escapi.dll");
        }
    }
}

pub struct Capture<'a, 'b: 'a> {
    device: &'a Device<'b>,
}

impl<'a, 'b> Capture<'a, 'b> {
    pub fn done(&self) -> bool {
        (self.device.cameras.isCaptureDone)(self.device.device_no) == 1
    }
}

impl<'a, 'b> Drop for Capture<'a, 'b> {
    fn drop(&mut self) {
        assert!(self.done(), "don't drop Capture objects before the capture is done");
    }
}

impl<'a> Device<'a> {
    pub fn do_capture<'b>(&'b mut self) -> Capture<'b, 'a> {
        (self.cameras.doCapture)(self.device_no);
        Capture {
            device: self
        }
    }
    pub fn name(&self) -> String {
        let mut v = vec![0u8; 100];
        (self.cameras.getCaptureDeviceName)(self.device_no, v.as_mut_ptr() as *mut i8, v.len() as i32);
        let null = v.iter().position(|&c| c == 0).expect("null termination character");
        v.truncate(null);
        String::from_utf8(v).expect("device name contains invalid utf8 characters")
    }
    pub fn error_code(&self) -> i32 {
        (self.cameras.getCaptureErrorCode)(self.device_no)
    }
    pub fn error_line(&self) -> i32 {
        (self.cameras.getCaptureErrorLine)(self.device_no)
    }
    pub fn pixels(&mut self) -> &[i32] {
        &self.buf
    }
}

pub struct Device<'a> {
    device_no: DeviceNo,
    buf: Box<[i32]>,
    #[allow(dead_code)]
    params: Box<SimpleCapParams>,
    cameras: &'a Cameras,
}

impl<'a> Drop for Device<'a> {
    fn drop(&mut self) {
        (self.cameras.deinitCapture)(self.device_no)
    }
}

impl Cameras {
    pub fn num_devices(&self) -> usize {
        (self.countCaptureDevices)() as usize
    }
    pub fn version(&self) -> u32 {
        (self.ESCAPIVersion)()
    }
    pub fn init(&self, index: u32, wdt: u32, hgt: u32) -> Result<Device, Error> {
        let mut data = vec![0; (wdt*hgt) as usize].into_boxed_slice();
        let params = Box::new(SimpleCapParams {
            width: wdt,
            height: hgt,
            buf: data.as_mut_ptr(),
        });
        if (self.initCapture)(DeviceNo(index), &*params) == 1 {
            Ok(Device {
                device_no: DeviceNo(index),
                cameras: &self,
                buf: data,
                params: params,
            })
        } else {
            Err(CouldNotOpenDevice(unsafe { GetLastError() }))
        }
    }
}

#[derive(Debug)]
pub enum Error {
    CouldNotLoadEscapiDLL(libc::c_ulong),
    CouldNotOpenDevice(libc::c_ulong),
}

use self::Error::*;

#[allow(non_snake_case)]
pub fn init() -> Result<Cameras, Error> {
    unsafe {
        let escapi_dll = CString::new("escapi.dll").unwrap();
        let capdll = LoadLibraryA(escapi_dll.as_ptr());
        if capdll == std::ptr::null_mut() {
            return Err(CouldNotLoadEscapiDLL(GetLastError()));
        }

        let countCaptureDevices = CString::new("countCaptureDevices").unwrap();
        let initCapture = CString::new("initCapture").unwrap();
        let deinitCapture = CString::new("deinitCapture").unwrap();
        let doCapture = CString::new("doCapture").unwrap();
        let isCaptureDone = CString::new("isCaptureDone").unwrap();
        let initCOM = CString::new("initCOM").unwrap();
        let getCaptureDeviceName = CString::new("getCaptureDeviceName").unwrap();
        let ESCAPIVersion = CString::new("ESCAPIVersion").unwrap();
        let getCapturePropertyValue = CString::new("getCapturePropertyValue").unwrap();
        let getCapturePropertyAuto = CString::new("getCapturePropertyAuto").unwrap();
        let setCaptureProperty = CString::new("setCaptureProperty").unwrap();
        let getCaptureErrorLine = CString::new("getCaptureErrorLine").unwrap();
        let getCaptureErrorCode = CString::new("getCaptureErrorCode").unwrap();

        transmute::<_, extern fn()>(GetProcAddress(capdll, initCOM.as_ptr()))();

        Ok(Cameras {
            countCaptureDevices: transmute(GetProcAddress(capdll, countCaptureDevices.as_ptr())),
            initCapture: transmute(GetProcAddress(capdll, initCapture.as_ptr())),
            deinitCapture: transmute(GetProcAddress(capdll, deinitCapture.as_ptr())),
            doCapture: transmute(GetProcAddress(capdll, doCapture.as_ptr())),
            isCaptureDone: transmute(GetProcAddress(capdll, isCaptureDone.as_ptr())),
            getCaptureDeviceName: transmute(GetProcAddress(capdll, getCaptureDeviceName.as_ptr())),
            ESCAPIVersion: transmute(GetProcAddress(capdll, ESCAPIVersion.as_ptr())),
            getCapturePropertyValue: transmute(GetProcAddress(capdll, getCapturePropertyValue.as_ptr())),
            getCapturePropertyAuto: transmute(GetProcAddress(capdll, getCapturePropertyAuto.as_ptr())),
            setCaptureProperty: transmute(GetProcAddress(capdll, setCaptureProperty.as_ptr())),
            getCaptureErrorLine: transmute(GetProcAddress(capdll, getCaptureErrorLine.as_ptr())),
            getCaptureErrorCode: transmute(GetProcAddress(capdll, getCaptureErrorCode.as_ptr())),
            capdll: capdll,
        })
    }
}
<commit_msg>bugfix<commit_after>extern crate winapi;
extern crate libc;
extern crate kernel32;

use std::ffi::CString;
use std::mem::transmute;
use kernel32::{LoadLibraryA, GetProcAddress, FreeLibrary, GetLastError};

#[repr(C)]
struct SimpleCapParams {
    buf: *mut libc::c_int,
    width: libc::c_uint,
    height: libc::c_uint,
}

#[derive(Copy, Clone)]
#[repr(C)]
struct DeviceNo(libc::c_uint);

#[allow(non_snake_case)]
pub struct Cameras {
    countCaptureDevices: extern fn() -> libc::c_int,
    initCapture: extern fn(DeviceNo, *const SimpleCapParams) -> libc::c_int,
    deinitCapture: extern fn(DeviceNo),
    doCapture: extern fn(DeviceNo) -> libc::c_int,
    isCaptureDone: extern fn(DeviceNo) -> libc::c_int,
    getCaptureDeviceName: extern fn(DeviceNo, *mut libc::c_char, libc::c_int),
    ESCAPIVersion: extern fn() -> libc::c_uint,
    #[allow(dead_code)]
    getCapturePropertyValue: extern fn(DeviceNo, libc::c_int) -> libc::c_float,
    #[allow(dead_code)]
    getCapturePropertyAuto: extern fn(DeviceNo, libc::c_int) -> libc::c_int,
    #[allow(dead_code)]
    setCaptureProperty: extern fn(DeviceNo, libc::c_int, libc::c_float, libc::c_int),
    getCaptureErrorLine: extern fn(DeviceNo) -> libc::c_int,
    getCaptureErrorCode: extern fn(DeviceNo) -> libc::c_int,
    capdll: winapi::HMODULE,
}

impl Drop for Cameras {
    fn drop(&mut self) {
        unsafe {
            assert!(FreeLibrary(self.capdll) == 1, "failed to release escapi.dll");
        }
    }
}

pub struct Capture<'a, 'b: 'a> {
    device: &'a Device<'b>,
}

impl<'a, 'b> Capture<'a, 'b> {
    pub fn done(&self) -> bool {
        (self.device.cameras.isCaptureDone)(self.device.device_no) == 1
    }
}

impl<'a, 'b> Drop for Capture<'a, 'b> {
    fn drop(&mut self) {
        assert!(self.done(), "don't drop Capture objects before the capture is done");
    }
}

impl<'a> Device<'a> {
    pub fn do_capture<'b>(&'b mut self) -> Capture<'b, 'a> {
        (self.cameras.doCapture)(self.device_no);
        Capture {
            device: self
        }
    }
    pub fn name(&self) -> String {
        let mut v = vec![0u8; 100];
        (self.cameras.getCaptureDeviceName)(self.device_no, v.as_mut_ptr() as *mut i8, v.len() as i32);
        let null = v.iter().position(|&c| c == 0).expect("null termination character");
        v.truncate(null);
        String::from_utf8(v).expect("device name contains invalid utf8 characters")
    }
    pub fn error_code(&self) -> i32 {
        (self.cameras.getCaptureErrorCode)(self.device_no)
    }
    pub fn error_line(&self) -> i32 {
        (self.cameras.getCaptureErrorLine)(self.device_no)
    }
    pub fn pixels(&mut self) -> &[i32] {
        &self.buf
    }
}

pub struct Device<'a> {
    device_no: DeviceNo,
    buf: Box<[i32]>,
    #[allow(dead_code)]
    params: Box<SimpleCapParams>,
    cameras: &'a Cameras,
}

impl<'a> Drop for Device<'a> {
    fn drop(&mut self) {
        (self.cameras.deinitCapture)(self.device_no)
    }
}

impl Cameras {
    pub fn num_devices(&self) -> usize {
        (self.countCaptureDevices)() as usize
    }
    pub fn version(&self) -> u32 {
        (self.ESCAPIVersion)()
    }
    pub fn init(&self, index: u32, wdt: u32, hgt: u32) -> Result<Device, Error> {
        let mut data = vec![0; (wdt*hgt) as usize].into_boxed_slice();
        let params = Box::new(SimpleCapParams {
            width: wdt,
            height: hgt,
            buf: data.as_mut_ptr(),
        });
        if (self.initCapture)(DeviceNo(index), &*params) == 1 {
            Ok(Device {
                device_no: DeviceNo(index),
                cameras: &self,
                buf: data,
                params: params,
            })
        } else {
            Err(CouldNotOpenDevice(unsafe { GetLastError() }))
        }
    }
}

#[derive(Debug)]
pub enum Error {
    CouldNotLoadEscapiDLL(libc::c_ulong),
    CouldNotOpenDevice(libc::c_ulong),
}

use self::Error::*;

#[allow(non_snake_case)]
pub fn init() -> Result<Cameras, Error> {
    unsafe {
        let escapi_dll = CString::new("escapi.dll").unwrap();
        let capdll = LoadLibraryA(escapi_dll.as_ptr());
        if capdll == std::ptr::null_mut() {
            return Err(CouldNotLoadEscapiDLL(GetLastError()));
        }

        let countCaptureDevices = CString::new("countCaptureDevices").unwrap();
        let initCapture = CString::new("initCapture").unwrap();
        let deinitCapture = CString::new("deinitCapture").unwrap();
        let doCapture = CString::new("doCapture").unwrap();
        let isCaptureDone = CString::new("isCaptureDone").unwrap();
        let initCOM = CString::new("initCOM").unwrap();
        let getCaptureDeviceName = CString::new("getCaptureDeviceName").unwrap();
        let ESCAPIVersion = CString::new("ESCAPIVersion").unwrap();
        let getCapturePropertyValue = CString::new("getCapturePropertyValue").unwrap();
        let getCapturePropertyAuto = CString::new("getCapturePropertyAuto").unwrap();
        let setCaptureProperty = CString::new("setCaptureProperty").unwrap();
        let getCaptureErrorLine = CString::new("getCaptureErrorLine").unwrap();
        let getCaptureErrorCode = CString::new("getCaptureErrorCode").unwrap();

        transmute::<_, extern fn()>(GetProcAddress(capdll, initCOM.as_ptr()))();

        Ok(Cameras {
            countCaptureDevices: transmute(GetProcAddress(capdll, countCaptureDevices.as_ptr())),
            initCapture: transmute(GetProcAddress(capdll, initCapture.as_ptr())),
            deinitCapture: transmute(GetProcAddress(capdll, deinitCapture.as_ptr())),
            doCapture: transmute(GetProcAddress(capdll, doCapture.as_ptr())),
            isCaptureDone: transmute(GetProcAddress(capdll, isCaptureDone.as_ptr())),
            getCaptureDeviceName: transmute(GetProcAddress(capdll, getCaptureDeviceName.as_ptr())),
            ESCAPIVersion: transmute(GetProcAddress(capdll, ESCAPIVersion.as_ptr())),
            getCapturePropertyValue: transmute(GetProcAddress(capdll, getCapturePropertyValue.as_ptr())),
            getCapturePropertyAuto: transmute(GetProcAddress(capdll, getCapturePropertyAuto.as_ptr())),
            setCaptureProperty: transmute(GetProcAddress(capdll, setCaptureProperty.as_ptr())),
            getCaptureErrorLine: transmute(GetProcAddress(capdll, getCaptureErrorLine.as_ptr())),
            getCaptureErrorCode: transmute(GetProcAddress(capdll, getCaptureErrorCode.as_ptr())),
            capdll: capdll,
        })
    }
}
<|endoftext|>


<commit_before>#![feature(no_std)]
#![feature(core)]
#![no_std]

#![crate_name = "multiboot"]
#![crate_type = "lib"]

extern crate core;

#[cfg(test)]
extern crate std;

use core::mem::{transmute};
use core::ops::FnMut;
use core::raw;
use core::str;

/// Value that is in rax after multiboot jumps to our entry point
pub const SIGNATURE_RAX: u64 = 0x2BADB002;

#[derive(Debug, PartialEq, Eq)]
pub enum MemType {
    RAM = 1,
    Unusable = 2,
}

pub type PAddr = u64;
pub type VAddr = usize;

/// Multiboot struct clients mainly interact with
/// To create this use Multiboot::new()
pub struct Multiboot<'a> {
    header: &'a MultibootHeader,
    paddr_to_vaddr: fn(PAddr) -> VAddr,
}

/// Representation of Multiboot header according to specification.
#[derive(Debug)]
#[repr(packed)]
struct MultibootHeader {
    flags: u32,

    mem_lower: u32,
    mem_upper: u32,

    boot_device: u32,
    cmdline: u32,

    mods_count: u32,
    mods_addr: u32,

    elf_symbols: ElfSymbols,

    mmap_length: u32,
    mmap_addr: u32,
}

/// Multiboot format of the MMAP buffer.
/// Note that size is defined to be at -4 bytes.
#[derive(Debug)]
#[repr(packed)]
struct MemEntry {
    size: u32,
    base_addr: u64,
    length: u64,
    mtype: u32
}

#[derive(Debug)]
#[repr(packed)]
struct ElfSymbols {
    num: u32,
    size: u32,
    addr: u32,
    shndx: u32,
}

/// Multiboot module structure
#[derive(Debug)]
#[repr(packed)]
struct Module {
    /// Start address of module in memory.
    start: u32,
    /// End address of module in memory.
    end: u32,
    /// Name of module.
    string: u32,
    /// Must be zero.
    reserved: u32
}

/// Convert a C string into a [u8 slice and from there into a &'static str.
/// This unsafe block builds on assumption that multiboot strings are sane.
fn convert_safe_c_string(cstring: *const u8) -> &'static str {
    unsafe {
        let mut iter = cstring;
        while *iter != 0 {
            iter = iter.offset(1);
        }

        let slice = raw::Slice { data: cstring, len: iter as usize - cstring as usize };
        let byte_array: &'static [u8] = transmute(slice);
        str::from_utf8_unchecked(byte_array)
    }
}

impl<'a> Multiboot<'a> {

    /// Initializes the multiboot structure.
    ///
    /// # Arguments
    ///
    ///  * `mboot_ptr` - The physical address of the multiboot header. On qemu for example
    ///                  this is typically at 0x9500.
    ///  * `paddr_to_vaddr` - Translation of the physical addresses into kernel addresses.
    ///
    ///  `paddr_to_vaddr` translates physical it into a kernel accessible address.
    ///  The simplest paddr_to_vaddr function would for example be just the identity
    ///  function. But this may vary depending on how your page table layout looks like.
    ///
    pub fn new(mboot_ptr: u64, paddr_to_vaddr: fn(paddr: PAddr) -> VAddr) -> Multiboot<'a> {
        let header = paddr_to_vaddr(mboot_ptr);
        let mb: &MultibootHeader = unsafe { transmute::<VAddr, &MultibootHeader>(header) };

        Multiboot { header: mb, paddr_to_vaddr: paddr_to_vaddr }
    }

    pub fn has_mmap(&'a self) -> bool {
        self.header.flags & 0x1 > 0
    }

    /// Discover all memory regions in the multiboot memory map.
    ///
    /// # Arguments
    ///
    ///  * `discovery_callback` - Function to notify your memory system about regions.
    ///
    pub fn find_memory<F: FnMut(u64, u64, MemType)>(&'a self, mut discovery_callback: F) {
        if !self.has_mmap() {
            return
        }

        let paddr_to_vaddr = self.paddr_to_vaddr;

        let mut current = self.header.mmap_addr;
        let end = self.header.mmap_addr + self.header.mmap_length;
        while current < end
        {
            let memory_region: &MemEntry = unsafe { transmute::<VAddr, &MemEntry>(paddr_to_vaddr(current as u64)) };

            let mtype = match memory_region.mtype {
                1 => MemType::RAM,
                2 => MemType::Unusable,
                _ => MemType::Unusable
            };

            discovery_callback(memory_region.base_addr, memory_region.length, mtype);
            current += memory_region.size + 4;
        }
    }

    pub fn has_modules(&'a self) -> bool {
        self.header.flags & (1<<3) > 0
    }

    /// Discover all additional modules in multiboot.
    ///
    /// # Arguments
    ///
    ///  * `discovery_callback` - Function to notify your system about modules.
    ///
    pub fn find_modules<F: FnMut(&'static str, VAddr, VAddr)>(&'a self, mut discovery_callback: F) {
        if !self.has_modules() {
            return
        }

        let paddr_to_vaddr = self.paddr_to_vaddr;

        let module_start = paddr_to_vaddr(self.header.mods_addr as u64);
        let count: usize = self.header.mods_count as usize;
        for _ in 0..count {
            let current: &Module = unsafe { transmute::<VAddr, &Module>(module_start) };
            let path = unsafe { convert_safe_c_string(transmute::<VAddr, *const u8>(paddr_to_vaddr(current.string as u64))) };

            discovery_callback(path, paddr_to_vaddr(current.start as PAddr), paddr_to_vaddr(current.end as PAddr));
        }
    }
}
<commit_msg>Added feature raw to make travis happy.<commit_after>#![feature(no_std)]
#![feature(core)]
#![feature(raw)]
#![no_std]

#![crate_name = "multiboot"]
#![crate_type = "lib"]

extern crate core;

#[cfg(test)]
extern crate std;

use core::mem::{transmute};
use core::ops::FnMut;
use core::raw;
use core::str;

/// Value that is in rax after multiboot jumps to our entry point
pub const SIGNATURE_RAX: u64 = 0x2BADB002;

#[derive(Debug, PartialEq, Eq)]
pub enum MemType {
    RAM = 1,
    Unusable = 2,
}

pub type PAddr = u64;
pub type VAddr = usize;

/// Multiboot struct clients mainly interact with
/// To create this use Multiboot::new()
pub struct Multiboot<'a> {
    header: &'a MultibootHeader,
    paddr_to_vaddr: fn(PAddr) -> VAddr,
}

/// Representation of Multiboot header according to specification.
#[derive(Debug)]
#[repr(packed)]
struct MultibootHeader {
    flags: u32,

    mem_lower: u32,
    mem_upper: u32,

    boot_device: u32,
    cmdline: u32,

    mods_count: u32,
    mods_addr: u32,

    elf_symbols: ElfSymbols,

    mmap_length: u32,
    mmap_addr: u32,
}

/// Multiboot format of the MMAP buffer.
/// Note that size is defined to be at -4 bytes.
#[derive(Debug)]
#[repr(packed)]
struct MemEntry {
    size: u32,
    base_addr: u64,
    length: u64,
    mtype: u32
}

#[derive(Debug)]
#[repr(packed)]
struct ElfSymbols {
    num: u32,
    size: u32,
    addr: u32,
    shndx: u32,
}

/// Multiboot module structure
#[derive(Debug)]
#[repr(packed)]
struct Module {
    /// Start address of module in memory.
    start: u32,
    /// End address of module in memory.
    end: u32,
    /// Name of module.
    string: u32,
    /// Must be zero.
    reserved: u32
}

/// Convert a C string into a [u8 slice and from there into a &'static str.
/// This unsafe block builds on assumption that multiboot strings are sane.
fn convert_safe_c_string(cstring: *const u8) -> &'static str {
    unsafe {
        let mut iter = cstring;
        while *iter != 0 {
            iter = iter.offset(1);
        }

        let slice = raw::Slice { data: cstring, len: iter as usize - cstring as usize };
        let byte_array: &'static [u8] = transmute(slice);
        str::from_utf8_unchecked(byte_array)
    }
}

impl<'a> Multiboot<'a> {

    /// Initializes the multiboot structure.
    ///
    /// # Arguments
    ///
    ///  * `mboot_ptr` - The physical address of the multiboot header. On qemu for example
    ///                  this is typically at 0x9500.
    ///  * `paddr_to_vaddr` - Translation of the physical addresses into kernel addresses.
    ///
    ///  `paddr_to_vaddr` translates physical it into a kernel accessible address.
    ///  The simplest paddr_to_vaddr function would for example be just the identity
    ///  function. But this may vary depending on how your page table layout looks like.
    ///
    pub fn new(mboot_ptr: u64, paddr_to_vaddr: fn(paddr: PAddr) -> VAddr) -> Multiboot<'a> {
        let header = paddr_to_vaddr(mboot_ptr);
        let mb: &MultibootHeader = unsafe { transmute::<VAddr, &MultibootHeader>(header) };

        Multiboot { header: mb, paddr_to_vaddr: paddr_to_vaddr }
    }

    pub fn has_mmap(&'a self) -> bool {
        self.header.flags & 0x1 > 0
    }

    /// Discover all memory regions in the multiboot memory map.
    ///
    /// # Arguments
    ///
    ///  * `discovery_callback` - Function to notify your memory system about regions.
    ///
    pub fn find_memory<F: FnMut(u64, u64, MemType)>(&'a self, mut discovery_callback: F) {
        if !self.has_mmap() {
            return
        }

        let paddr_to_vaddr = self.paddr_to_vaddr;

        let mut current = self.header.mmap_addr;
        let end = self.header.mmap_addr + self.header.mmap_length;
        while current < end
        {
            let memory_region: &MemEntry = unsafe { transmute::<VAddr, &MemEntry>(paddr_to_vaddr(current as u64)) };

            let mtype = match memory_region.mtype {
                1 => MemType::RAM,
                2 => MemType::Unusable,
                _ => MemType::Unusable
            };

            discovery_callback(memory_region.base_addr, memory_region.length, mtype);
            current += memory_region.size + 4;
        }
    }

    pub fn has_modules(&'a self) -> bool {
        self.header.flags & (1<<3) > 0
    }

    /// Discover all additional modules in multiboot.
    ///
    /// # Arguments
    ///
    ///  * `discovery_callback` - Function to notify your system about modules.
    ///
    pub fn find_modules<F: FnMut(&'static str, VAddr, VAddr)>(&'a self, mut discovery_callback: F) {
        if !self.has_modules() {
            return
        }

        let paddr_to_vaddr = self.paddr_to_vaddr;

        let module_start = paddr_to_vaddr(self.header.mods_addr as u64);
        let count: usize = self.header.mods_count as usize;
        for _ in 0..count {
            let current: &Module = unsafe { transmute::<VAddr, &Module>(module_start) };
            let path = unsafe { convert_safe_c_string(transmute::<VAddr, *const u8>(paddr_to_vaddr(current.string as u64))) };

            discovery_callback(path, paddr_to_vaddr(current.start as PAddr), paddr_to_vaddr(current.end as PAddr));
        }
    }
}
<|endoftext|>


<commit_before>//#![deny(missing_docs)]

//! # Capsize
//!
//! Capsize provides conversions between units of capacity,
//! similar in nature to [Duration](https://doc.rust-lang.org/std/time/duration/), which provides conversions between units of time.
//!
//! All conversions are represented as an `i64` by default.
//!
//! This crate also provides FromStr implementations that parse values "1k" into
//! their corresponding capacity in `i64` format in bytes.

use std::convert::Into;
use std::str::FromStr;

const BYTE: i64     = 1;
const KILOBYTE: i64 = BYTE     << 10;
const MEGABYTE: i64 = KILOBYTE << 10;
const GIGABYTE: i64 = MEGABYTE << 10;
const TERABYTE: i64 = GIGABYTE << 10;
const PETABYTE: i64 = TERABYTE << 10;
const EXABYTE: i64  = PETABYTE << 10;

macro_rules! map(
  { $($key:expr => $value:expr),+ } => {
    {
      let mut m = ::std::collections::HashMap::new();
      $(
        m.insert($key, $value);
        )+
        m
    }
  };
);

/// Capacity provides a simple way to convert
/// values to corresponding units of capacity
pub trait Capacity {

  /// size in bytes
  fn bytes(&self) -> i64;

  /// size in kilobytes
  fn kilobytes(&self) -> i64 {
    self.bytes().rotate_left(10)
  }

  /// size in megabytes
  fn megabytes(&self) -> i64 {
    self.kilobytes().rotate_left(10)
  }

  /// size in gigabytes
  fn gigabytes(&self) -> i64 {
    self.megabytes().rotate_left(10)
  }

  /// size in terabytes
  fn terabytes(&self) -> i64 {
    self.gigabytes().rotate_left(10)
  }

  /// size in petabytes
  fn petabytes(&self) -> i64 {
    self.terabytes().rotate_left(10)
  }

  /// size in exabytes
  fn exabytes(&self) -> i64 {
    self.petabytes().rotate_left(10)
  }

  /// size as a human readable string
  fn capacity(&self) -> String {
    match self.bytes() {
      small if small < KILOBYTE =>
        stringify!(small).to_owned(),
      large => {
        let units = vec![
          ('E', EXABYTE),
          ('P', PETABYTE),
          ('T', TERABYTE),
          ('G', GIGABYTE),
          ('M', MEGABYTE),
          ('K', KILOBYTE)
        ];
        for (suffix, size) in units {
          if large == size {
            return format!("1{}", suffix)
          } else if large > size {
            let sized = (large as f64) / (size as f64);
            let round = (sized * 100.0).round() / 100.0;
            return format!("{:.1}{}", round, suffix)
          }
        }
        unreachable!()
      }
    }
  }
}

impl Capacity for i64 {
  fn bytes(&self) -> i64 {
    *self
  }
}

impl Capacity for Bytes {
  fn bytes(&self) -> i64 {
    (*self).size
  }
}

/// Bytes is a simple type
/// to extract a capacity size, in bytes,
/// from a string value
#[derive(Debug)]
pub struct Bytes {
  size: i64
}

impl Into<Bytes> for i64 {
  fn into(self) -> Bytes {
    Bytes { size: self }
  }
}

impl FromStr for Bytes {
  type Err = String;
  fn from_str(s: &str) -> Result<Bytes, String> {
    let units = map!(
      'E' => EXABYTE,
      'P' => PETABYTE,
      'T' => TERABYTE,
      'G' => GIGABYTE,
      'M' => MEGABYTE,
      'K' => KILOBYTE
    );
    if s.len() > 1 {
      let last = s.chars().last().unwrap();
      match units.get(&last) {
        Some(unit) => {
          let init: String = s.chars().take(s.chars().count() - 1).collect();
          init.parse::<i64>().map(|lit| {
            Bytes { size: lit * unit }
          }).or_else(|_| Err(s.to_owned()))
        },
        _ => {
          Err(s.to_owned())
        }
      }
    } else {
      s.parse::<i64>().map(|lit| {
        Bytes { size: lit }
      }).or_else(|_| Err(s.to_owned()))
    }
  }
}

#[test]
fn test_bytes() {
  assert_eq!(1.bytes(), 1);
}

#[test]
fn test_kilobytes() {
  assert_eq!(1.kilobytes(), 1024)
}

#[test]
fn test_megabytes() {
  assert_eq!(1.megabytes(), 1048576)
}

#[test]
fn test_gigabytes() {
  assert_eq!(1.gigabytes(), 1073741824)
}

#[test]
fn test_terabytes() {
  assert_eq!(1.terabytes(), 1099511627776)
}

#[test]
fn test_petabytes() {
  assert_eq!(1.petabytes(), 1125899906842624)
}

#[test]
fn test_exabytes() {
  assert_eq!(1.exabytes(), 1152921504606846976)
}

#[test]
fn test_kilobytes_capactity() {
  let half = 1.kilobytes() / 2;
  assert_eq!((1.kilobytes() + half).capacity(), "1.5K".to_owned())
}

#[test]
fn test_megabytes_capactity() {
  let half = 1.megabytes() / 2;
  assert_eq!((1.megabytes() + half).capacity(), "1.5M".to_owned())
}

#[test]
fn test_gigabytes_capactity() {
  let half = 1.gigabytes() / 2;
  assert_eq!((1.gigabytes() + half).capacity(), "1.5G".to_owned())
}

#[test]
fn test_terabytes_capactity() {
  let half = 1.terabytes() / 2;
  assert_eq!((1.terabytes() + half).capacity(), "1.5T".to_owned())
}

#[test]
fn test_petabytes_capactity() {
  let half = 1.petabytes() / 2;
  assert_eq!((1.petabytes() + half).capacity(), "1.5P".to_owned())
}

#[test]
fn test_exabytes_capactity() {
  let half = 1.exabytes() / 2;
  assert_eq!((1.exabytes() + half).capacity(), "1.5E".to_owned())
}

#[test]
fn test_kilobytes_parse() {
  let cap: String = 1.kilobytes().capacity();
  let bytes = cap.parse::<Bytes>().ok().unwrap();
  assert_eq!(bytes.capacity(), cap)
}
<commit_msg>doc test and examples<commit_after>//#![deny(missing_docs)]

//! # Capsize
//!
//! Capsize provides conversions between units of capacity,
//! similar in nature to [Duration](https://doc.rust-lang.org/std/time/duration/), which provides conversions between units of time.
//!
//! All conversions are represented as an `i64` by default.
//!
//! This crate also provides FromStr implementations that parse values "1k" into
//! their corresponding capacity in `i64` format in bytes.
//!
//! # Examples
//!
//! ```rust
//! use capsize::Capacity;
//!
//! // convert to kilobytes to bytes
//! let bytes = 4.kilobytes();
//! assert_eq!(bytes, 4096);
//!
//! // resolve 4096 to the closest human readable form
//! assert_eq!(bytes.capacity(), "4.0K");
//! ```
use std::convert::Into;
use std::str::FromStr;

const BYTE: i64     = 1;
const KILOBYTE: i64 = BYTE     << 10;
const MEGABYTE: i64 = KILOBYTE << 10;
const GIGABYTE: i64 = MEGABYTE << 10;
const TERABYTE: i64 = GIGABYTE << 10;
const PETABYTE: i64 = TERABYTE << 10;
const EXABYTE: i64  = PETABYTE << 10;

macro_rules! map(
  { $($key:expr => $value:expr),+ } => {
    {
      let mut m = ::std::collections::HashMap::new();
      $(
        m.insert($key, $value);
        )+
        m
    }
  };
);

/// Capacity provides a simple way to convert
/// values to corresponding units of capacity
pub trait Capacity {

  /// size in bytes
  fn bytes(&self) -> i64;

  /// size in kilobytes
  fn kilobytes(&self) -> i64 {
    self.bytes().rotate_left(10)
  }

  /// size in megabytes
  fn megabytes(&self) -> i64 {
    self.kilobytes().rotate_left(10)
  }

  /// size in gigabytes
  fn gigabytes(&self) -> i64 {
    self.megabytes().rotate_left(10)
  }

  /// size in terabytes
  fn terabytes(&self) -> i64 {
    self.gigabytes().rotate_left(10)
  }

  /// size in petabytes
  fn petabytes(&self) -> i64 {
    self.terabytes().rotate_left(10)
  }

  /// size in exabytes
  fn exabytes(&self) -> i64 {
    self.petabytes().rotate_left(10)
  }

  /// size as a human readable string
  fn capacity(&self) -> String {
    match self.bytes() {
      small if small < KILOBYTE =>
        stringify!(small).to_owned(),
      large => {
        let units = vec![
          ('E', EXABYTE),
          ('P', PETABYTE),
          ('T', TERABYTE),
          ('G', GIGABYTE),
          ('M', MEGABYTE),
          ('K', KILOBYTE)
        ];
        for (suffix, size) in units {
          if large == size {
            return format!("1{}", suffix)
          } else if large > size {
            let sized = (large as f64) / (size as f64);
            let round = (sized * 100.0).round() / 100.0;
            return format!("{:.1}{}", round, suffix)
          }
        }
        unreachable!()
      }
    }
  }
}

impl Capacity for i64 {
  fn bytes(&self) -> i64 {
    *self
  }
}

impl Capacity for Bytes {
  fn bytes(&self) -> i64 {
    (*self).size
  }
}

/// Bytes is a simple type
/// to extract a capacity size, in bytes,
/// from a string value
#[derive(Debug)]
pub struct Bytes {
  size: i64
}

impl Into<Bytes> for i64 {
  fn into(self) -> Bytes {
    Bytes { size: self }
  }
}

impl FromStr for Bytes {
  type Err = String;
  fn from_str(s: &str) -> Result<Bytes, String> {
    let units = map!(
      'E' => EXABYTE,
      'P' => PETABYTE,
      'T' => TERABYTE,
      'G' => GIGABYTE,
      'M' => MEGABYTE,
      'K' => KILOBYTE
    );
    if s.len() > 1 {
      let last = s.chars().last().unwrap();
      match units.get(&last) {
        Some(unit) => {
          let init: String = s.chars().take(s.chars().count() - 1).collect();
          init.parse::<i64>().map(|lit| {
            Bytes { size: lit * unit }
          }).or_else(|_| Err(s.to_owned()))
        },
        _ => {
          Err(s.to_owned())
        }
      }
    } else {
      s.parse::<i64>().map(|lit| {
        Bytes { size: lit }
      }).or_else(|_| Err(s.to_owned()))
    }
  }
}

#[test]
fn test_bytes() {
  assert_eq!(1.bytes(), 1);
}

#[test]
fn test_kilobytes() {
  assert_eq!(1.kilobytes(), 1024)
}

#[test]
fn test_megabytes() {
  assert_eq!(1.megabytes(), 1048576)
}

#[test]
fn test_gigabytes() {
  assert_eq!(1.gigabytes(), 1073741824)
}

#[test]
fn test_terabytes() {
  assert_eq!(1.terabytes(), 1099511627776)
}

#[test]
fn test_petabytes() {
  assert_eq!(1.petabytes(), 1125899906842624)
}

#[test]
fn test_exabytes() {
  assert_eq!(1.exabytes(), 1152921504606846976)
}

#[test]
fn test_kilobytes_capactity() {
  let half = 1.kilobytes() / 2;
  assert_eq!((1.kilobytes() + half).capacity(), "1.5K".to_owned())
}

#[test]
fn test_megabytes_capactity() {
  let half = 1.megabytes() / 2;
  assert_eq!((1.megabytes() + half).capacity(), "1.5M".to_owned())
}

#[test]
fn test_gigabytes_capactity() {
  let half = 1.gigabytes() / 2;
  assert_eq!((1.gigabytes() + half).capacity(), "1.5G".to_owned())
}

#[test]
fn test_terabytes_capactity() {
  let half = 1.terabytes() / 2;
  assert_eq!((1.terabytes() + half).capacity(), "1.5T".to_owned())
}

#[test]
fn test_petabytes_capactity() {
  let half = 1.petabytes() / 2;
  assert_eq!((1.petabytes() + half).capacity(), "1.5P".to_owned())
}

#[test]
fn test_exabytes_capactity() {
  let half = 1.exabytes() / 2;
  assert_eq!((1.exabytes() + half).capacity(), "1.5E".to_owned())
}

#[test]
fn test_kilobytes_parse() {
  let cap: String = 1.kilobytes().capacity();
  let bytes = cap.parse::<Bytes>().ok().unwrap();
  assert_eq!(bytes.capacity(), cap)
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/rayon/0.9")]
#![allow(non_camel_case_types)] // I prefer to use ALL_CAPS for type parameters
#![deny(missing_debug_implementations)]
#![cfg_attr(test, feature(conservative_impl_trait))]
#![cfg_attr(test, feature(i128_type))]

//! Data-parallelism library that is easy to convert sequential computations into parallel.
//!
//! Rayon is lightweight and convenient for application to existing code.  It guarantees
//! data-race free executions, and takes advantage of parallelism when sensible, based
//! on work-load at runtime.  There are two categories of rayon workloads: parallel
//! iterators and multi-branched recursion (`join` method).
//!
//! # Parallel Iterators
//!
//! Parallel iterators are formed using [`par_iter`], [`par_iter_mut`], and [`into_par_iter`]
//! functions to iterate by shared reference, mutable reference, or by value respectively.
//! These iterators are chained with computations that can take the
//! shape of `map` or `for_each` as an example.  This solves [embarrassingly]
//! parallel tasks that are completely independent of one another.
//!
//! [`par_iter`]: iter/trait.IntoParallelRefIterator.html#tymethod.par_iter
//! [`par_iter_mut`]: iter/trait.IntoParallelRefMutIterator.html#tymethod.par_iter_mut
//! [`into_par_iter`]: iter/trait.IntoParallelIterator.html#tymethod.into_par_iter
//! [embarrassingly]: https://en.wikipedia.org/wiki/Embarrassingly_parallel
//!
//! # Examples
//!
//! Here a string is encrypted using ROT13 leveraging parallelism.  Once all the
//! threads are complete, they are collected into a string.
//!
//! ```
//! extern crate rayon;
//! use rayon::prelude::*;
//! # fn main() {
//! let mut chars: Vec<char> = "A man, a plan, a canal - Panama!".chars().collect();
//! let encrypted: String = chars.into_par_iter().map(|c| {
//!        match c {
//!            'A' ... 'M' | 'a' ... 'm' => ((c as u8) + 13) as char,
//!            'N' ... 'Z' | 'n' ... 'z' => ((c as u8) - 13) as char,
//!            _ => c
//!        }
//!    }).collect();
//!    assert_eq!(encrypted, "N zna, n cyna, n pnany - Cnanzn!");
//! # }
//! ```
//!
//! # Divide and conquer with `join`
//!
//! [`join`] takes two closures and runs them in parallel if doing so will improve
//! execution time.  Parallel Iterators are implemented using [`join`] with
//! work-stealing.  Given two tasks that safely run in parallel, one task is queued
//! and another starts processing.  If idle threads exist, they begin execution on
//! the queued work.
//!
//! [`join`]: fn.join.html
//!
//! # Examples
//!
//! ```rust,ignore
//! join(|| do_something(), || do_something_else())
//! ```
//!
//! ```rust
//! fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
//!    if v.len() > 1 {
//!        let mid = partition(v);
//!        let (lo, hi) = v.split_at_mut(mid);
//!        rayon::join(|| quick_sort(lo),
//!                    || quick_sort(hi));
//!    }
//! }
//! # fn main() { }
//! # fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize { 0 }
//! ```
//!
//! # Rayon Types
//!
//! Rayon traits are bundled into [`rayon::prelude::*`].  To get access to parallel
//! implementations on various standard types include `use rayon::prelude::*;`
//!
//! These implementations will give you access to `par_iter` with parallel
//! implementations of iterative functions including [`map`], [`for_each`], [`filter`],
//! [`fold`], and [more].
//!
//! [`rayon::prelude::*`]: prelude/index.html
//! [`map`]: iter/trait.ParallelIterator.html#method.map
//! [`for_each`]: iter/trait.ParallelIterator.html#method.for_each
//! [`filter`]: iter/trait.ParallelIterator.html#method.filter
//! [`fold`]: iter/trait.ParallelIterator.html#method.fold
//! [more]: iter/trait.ParallelIterator.html#provided-methods
//!
//! # Crate Layout
//!
//! Rayon extends many of the types found in the standard library with
//! parallel iterator implementations. The modules in the `rayon`
//! crate mirror [`std`] itself: so, e.g., the `option` module in
//! Rayon contains parallel iterators for the `Option` type, which is
//! found in [the `option` module of `std`]. Similarly, the
//! `collections` module in Rayon offers parallel iterator types for
//! [the `collections` from `std`]. You will rarely need to access
//! these submodules unless you need to name iterator types
//! explicitly.
//!
//! [the `option` module of `std`]: https://doc.rust-lang.org/std/option/index.html
//! [the `collections` from `std`]: https://doc.rust-lang.org/std/collections/index.html
//! [`std`]: https://doc.rust-lang.org/std/

extern crate rayon_core;
extern crate either;

#[cfg(test)]
extern crate rand;

#[macro_use]
mod delegate;

#[macro_use]
mod private;

mod split_producer;

pub mod collections;
pub mod iter;
pub mod option;
pub mod prelude;
pub mod range;
pub mod result;
pub mod slice;
pub mod str;
pub mod vec;

mod par_either;
mod test;

pub use rayon_core::current_num_threads;
pub use rayon_core::Configuration;
pub use rayon_core::initialize;
pub use rayon_core::ThreadPool;
pub use rayon_core::{join, join_context};
pub use rayon_core::FnContext;
pub use rayon_core::{scope, Scope};
pub use rayon_core::spawn;
<commit_msg>`deny(missing_docs)` from this point forward<commit_after>#![doc(html_root_url = "https://docs.rs/rayon/0.9")]
#![allow(non_camel_case_types)] // I prefer to use ALL_CAPS for type parameters
#![deny(missing_debug_implementations)]
#![cfg_attr(test, feature(conservative_impl_trait))]
#![cfg_attr(test, feature(i128_type))]
#![deny(missing_docs)]

//! Data-parallelism library that is easy to convert sequential computations into parallel.
//!
//! Rayon is lightweight and convenient for application to existing code.  It guarantees
//! data-race free executions, and takes advantage of parallelism when sensible, based
//! on work-load at runtime.  There are two categories of rayon workloads: parallel
//! iterators and multi-branched recursion (`join` method).
//!
//! # Parallel Iterators
//!
//! Parallel iterators are formed using [`par_iter`], [`par_iter_mut`], and [`into_par_iter`]
//! functions to iterate by shared reference, mutable reference, or by value respectively.
//! These iterators are chained with computations that can take the
//! shape of `map` or `for_each` as an example.  This solves [embarrassingly]
//! parallel tasks that are completely independent of one another.
//!
//! [`par_iter`]: iter/trait.IntoParallelRefIterator.html#tymethod.par_iter
//! [`par_iter_mut`]: iter/trait.IntoParallelRefMutIterator.html#tymethod.par_iter_mut
//! [`into_par_iter`]: iter/trait.IntoParallelIterator.html#tymethod.into_par_iter
//! [embarrassingly]: https://en.wikipedia.org/wiki/Embarrassingly_parallel
//!
//! # Examples
//!
//! Here a string is encrypted using ROT13 leveraging parallelism.  Once all the
//! threads are complete, they are collected into a string.
//!
//! ```
//! extern crate rayon;
//! use rayon::prelude::*;
//! # fn main() {
//! let mut chars: Vec<char> = "A man, a plan, a canal - Panama!".chars().collect();
//! let encrypted: String = chars.into_par_iter().map(|c| {
//!        match c {
//!            'A' ... 'M' | 'a' ... 'm' => ((c as u8) + 13) as char,
//!            'N' ... 'Z' | 'n' ... 'z' => ((c as u8) - 13) as char,
//!            _ => c
//!        }
//!    }).collect();
//!    assert_eq!(encrypted, "N zna, n cyna, n pnany - Cnanzn!");
//! # }
//! ```
//!
//! # Divide and conquer with `join`
//!
//! [`join`] takes two closures and runs them in parallel if doing so will improve
//! execution time.  Parallel Iterators are implemented using [`join`] with
//! work-stealing.  Given two tasks that safely run in parallel, one task is queued
//! and another starts processing.  If idle threads exist, they begin execution on
//! the queued work.
//!
//! [`join`]: fn.join.html
//!
//! # Examples
//!
//! ```rust,ignore
//! join(|| do_something(), || do_something_else())
//! ```
//!
//! ```rust
//! fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
//!    if v.len() > 1 {
//!        let mid = partition(v);
//!        let (lo, hi) = v.split_at_mut(mid);
//!        rayon::join(|| quick_sort(lo),
//!                    || quick_sort(hi));
//!    }
//! }
//! # fn main() { }
//! # fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize { 0 }
//! ```
//!
//! # Rayon Types
//!
//! Rayon traits are bundled into [`rayon::prelude::*`].  To get access to parallel
//! implementations on various standard types include `use rayon::prelude::*;`
//!
//! These implementations will give you access to `par_iter` with parallel
//! implementations of iterative functions including [`map`], [`for_each`], [`filter`],
//! [`fold`], and [more].
//!
//! [`rayon::prelude::*`]: prelude/index.html
//! [`map`]: iter/trait.ParallelIterator.html#method.map
//! [`for_each`]: iter/trait.ParallelIterator.html#method.for_each
//! [`filter`]: iter/trait.ParallelIterator.html#method.filter
//! [`fold`]: iter/trait.ParallelIterator.html#method.fold
//! [more]: iter/trait.ParallelIterator.html#provided-methods
//!
//! # Crate Layout
//!
//! Rayon extends many of the types found in the standard library with
//! parallel iterator implementations. The modules in the `rayon`
//! crate mirror [`std`] itself: so, e.g., the `option` module in
//! Rayon contains parallel iterators for the `Option` type, which is
//! found in [the `option` module of `std`]. Similarly, the
//! `collections` module in Rayon offers parallel iterator types for
//! [the `collections` from `std`]. You will rarely need to access
//! these submodules unless you need to name iterator types
//! explicitly.
//!
//! [the `option` module of `std`]: https://doc.rust-lang.org/std/option/index.html
//! [the `collections` from `std`]: https://doc.rust-lang.org/std/collections/index.html
//! [`std`]: https://doc.rust-lang.org/std/

extern crate rayon_core;
extern crate either;

#[cfg(test)]
extern crate rand;

#[macro_use]
mod delegate;

#[macro_use]
mod private;

mod split_producer;

pub mod collections;
pub mod iter;
pub mod option;
pub mod prelude;
pub mod range;
pub mod result;
pub mod slice;
pub mod str;
pub mod vec;

mod par_either;
mod test;

pub use rayon_core::current_num_threads;
pub use rayon_core::Configuration;
pub use rayon_core::initialize;
pub use rayon_core::ThreadPool;
pub use rayon_core::{join, join_context};
pub use rayon_core::FnContext;
pub use rayon_core::{scope, Scope};
pub use rayon_core::spawn;
<|endoftext|>


<commit_before>extern crate log;
extern crate simplelog;

use goblin::pe::PE;
use goblin::Object;
use log::{debug, error, info, trace};
use rayon::prelude::*;
use std::env;
use std::fs;
use std::io::prelude::*;
use zydis;

// TODO: this really shouldn't be public. only for testing.
// TODO: what is the `cfg(doctest)` setting?
//#[cfg(test)]
pub mod rsrc;

pub struct Config {
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 2 {
            return Err("not enough arguments");
        }

        let filename = args[1].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

#[derive(Debug)]
pub enum Error {
    FileAccess,
    FileFormat,
    NotImplemented,
    InvalidRva,
}

/// Round the given value up to the next multiple of the given base.
///
/// # Panics
///
///   - Base `b` must be at least `2`.
///
/// # Examples
///
/// ```
/// use lancelot::*;
/// assert_eq!(align(0, 2), 0);
/// assert_eq!(align(1, 2), 2);
/// assert_eq!(align(2, 2), 2);
/// assert_eq!(align(3, 2), 4);
/// assert_eq!(align(4, 2), 4);
/// ```
pub fn align(i: usize, b: usize) -> usize {
    if b < 2 {
        panic!("base `b` must be at least: 2");
    }
    let rem = i % b;
    if rem == 0 {
        i
    } else {
        i + (b - rem)
    }
}

pub fn hexdump_ascii(b: u8) -> char {
    if b.is_ascii_graphic() || b == b' ' {
        b as char
    } else {
        '.'
    }
}

pub fn hexdump(buf: &[u8], offset: usize) -> String {
    // 01234567:  00 01 02 03 04 05 06 07  ...............
    // <prefix>   <hex col>                <ascii col>

    let padding = "  ";

    let padding_size = 2;
    let hex_col_size = 3;
    let ascii_col_size = 1;
    let prefix_size = 8 + 1;
    let newline_size = 1;
    let line_size = prefix_size
        + padding_size
        + 16 * hex_col_size
        + padding_size
        + 16 * ascii_col_size
        + newline_size;
    let line_count = align(buf.len(), 0x10) / 0x10;

    let mut ret = String::with_capacity(line_count * line_size);

    let mut line = String::with_capacity(line_size);
    let mut remaining_count = buf.len();
    for line_index in 0..line_count {
        let line_elem_count = 0x10.min(remaining_count);
        let padding_elem_count = 0x10 - line_elem_count;

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        // ^^^^^^^^^
        line.push_str(format!("{:08x}:", offset + 0x10 * line_index).as_str());

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //          ^^
        line.push_str(padding);

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //            ^^^
        for elem in &buf[line_index..line_index + line_elem_count] {
            line.push_str(format!("{:02x} ", elem).as_str());
        }
        for _ in 0..padding_elem_count {
            line.push_str("   ");
        }

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //                                   ^^
        line.push_str(padding);

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //                                     ^
        for elem in &buf[line_index..line_index + line_elem_count] {
            line.push(hexdump_ascii(*elem))
        }
        for _ in 0..padding_elem_count {
            line.push(' ');
        }
        line.push_str(padding);

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //                                                    ^
        line.push('\n');

        ret.push_str(line.as_str());
        line.truncate(0x0);
        remaining_count -= line_elem_count;
    }

    ret
}

fn foo(ws: &Workspace) -> Result<(), Error> {
    if let Object::PE(pe) = ws.get_obj().unwrap() {
        info!("module: {}", pe.name.unwrap_or("(unknown)"));

        info!("bitness: {}", if pe.is_64 { "64" } else { "32" });
        info!("image base: 0x{:x}", pe.image_base);
        info!("entry rva: 0x{:x}", pe.entry);

        // like:
        //
        //     sections:
        //       - .text
        //         raw size:     0x18aa00
        //         virtual size: 0x18a9a8
        info!("sections:");
        for section in ws.sections.iter() {
            info!("  - {}", section.name);
            info!("    virtual address: 0x{:x}", section.addr);
            info!("    virtual size: 0x{:x}", section.buf.len());

            info!(
                "\n{}",
                hexdump(&section.buf[..0x1C], pe.image_base + section.addr as usize)
            );

            let decoder =
                zydis::Decoder::new(zydis::MachineMode::Long64, zydis::AddressWidth::_64).unwrap();
            let insns: Vec<_> = section
                .buf
                .par_windows(0x10)
                .map(|ibuf| decoder.decode(ibuf))
                .collect();

            info!("total instructions: {}", insns.len());

            info!(
                "successful disassembles: {}",
                insns.par_iter().filter(|insn| insn.is_ok()).count()
            );

            info!(
                "valid instructions: {}",
                insns
                    .par_iter()
                    .filter(|insn| match insn {
                        Ok(Some(_)) => true,
                        _ => false,
                    })
                    .count()
            );
        }
    }

    Ok(())
}

pub fn read_file(filename: &str) -> Result<Vec<u8>, Error> {
    debug!("read_file: {:?}", filename);

    let mut buf = Vec::new();
    {
        debug!("reading file: {}", filename);
        let mut f = match fs::File::open(filename) {
            Ok(f) => f,
            Err(_) => {
                error!("failed to open file: {}", filename);
                return Err(Error::FileAccess);
            }
        };
        let bytes_read = match f.read_to_end(&mut buf) {
            Ok(c) => c,
            Err(_) => {
                error!("failed to read entire file: {}", filename);
                return Err(Error::FileAccess);
            }
        };
        debug!("read {} bytes", bytes_read);
        if bytes_read < 0x10 {
            error!("file too small: {}", filename);
            return Err(Error::FileFormat);
        }
    }

    Ok(buf)
}

type Rva = u64;
type Va = u64;

pub struct Section {
    pub name: String,
    pub addr: Rva,
    pub buf: Vec<u8>,
    pub insns: Vec<Option<zydis::ffi::DecodedInstruction>>,
}

pub struct Workspace {
    pub filename: String,
    pub buf: Vec<u8>,
    pub sections: Vec<Section>,
}

impl Workspace {
    /// Parse the given file into its object.
    ///
    /// # Errors
    ///   - `Error::FileFormat`: when not able to be parsed by Goblin.
    ///
    /// # Examples
    ///
    /// ```
    /// use goblin::Object;
    /// use matches::matches;
    /// use lancelot::rsrc::*;
    /// let ws = get_workspace(Rsrc::K32);
    /// assert!(matches!(ws.get_obj().unwrap(), Object::PE(_)));
    /// ```
    ///
    /// you might be tempted to maintain a method `get_pe`,
    /// however, i don't think this is a good idea:
    /// its fragile because the file type may not be PE.
    /// therefore, force clients to be explicit:
    ///
    /// ```
    /// use goblin::Object;
    /// use lancelot::rsrc::*;
    /// let ws = get_workspace(Rsrc::K32);
    /// if let Object::PE(_) = ws.get_obj().unwrap() {
    ///     // everyone is happy!
    /// }
    /// ```
    ///
    /// TODO: demonstrate `Error::FileFormat`.
    pub fn get_obj(&self) -> Result<Object, Error> {
        let obj = match Object::parse(&self.buf) {
            Ok(o) => o,
            Err(e) => {
                error!("failed to parse file: {} error: {:?}", self.filename, e);
                return Err(Error::FileFormat);
            }
        };

        match obj {
            Object::Unknown(_) => {
                error!(
                    "unknown file format, magic: | {:02X} {:02X} | '{}{}' ",
                    self.buf[0],
                    self.buf[1],
                    hexdump_ascii(self.buf[0]),
                    hexdump_ascii(self.buf[1])
                );

                Err(Error::FileFormat)
            }
            _ => Ok(obj),
        }
    }

    pub fn get_pe(&self) -> Result<PE, Error> {
        match self.get_obj()? {
            Object::PE(pe) => {
                return Ok(pe);
            }
            _ => {
                error!("not a PE file");
                return Err(Error::ValueError);
            }
        }
    }

    /// Construct a workspace from the module with the given contents.
    ///
    /// # Errors
    ///   - `Error::FileFormat`: when not able to be parsed by Goblin.
    ///   - `Error::NotImplemented`: when not a PE file.
    ///
    /// # Examples
    ///
    /// ```
    /// use lancelot::*;
    /// use lancelot::rsrc::*;
    /// let buf = get_buf(Rsrc::K32);
    /// let ws = Workspace::from_buf("kernel32.dll", buf).unwrap();
    /// ```
    ///
    /// TODO: demonstrate ELF file behavior.
    /// TODO: demonstrate MachO file behavior.
    /// TODO: demonstrate unknown file behavior.
    pub fn from_buf(filename: &str, buf: Vec<u8>) -> Result<Workspace, Error> {
        let mut ws = Workspace {
            filename: filename.to_string(),
            buf: buf.clone(),
            sections: vec![],
        };

        match ws.get_obj()? {
            Object::PE(pe) => {
                info!("found PE file");

                let machine;
                let mode;
                if pe.is_64 {
                    machine = zydis::MachineMode::Long64;
                    mode = zydis::AddressWidth::_64;
                } else {
                    // TODO: not sure what `LongCompat32` means, vs `Legacy32`.
                    machine = zydis::MachineMode::LongCompat32;
                    mode = zydis::AddressWidth::_32;
                }
                // TODO: save off decoder into workspace.
                let decoder = zydis::Decoder::new(machine, mode).unwrap();

                // TODO: load PE header, too

                ws.sections
                    .extend(pe.sections.iter().map(|section| -> Section {
                        // TODO: i'm sure this can be abused.
                        // TODO: add tests for weird section names.
                        let name = String::from_utf8_lossy(&section.name[..])
                            .into_owned()
                            .trim_end_matches("\u{0}")
                            .trim_end()
                            .to_string();

                        // TODO: figure out if we will work with usize, or u64, or what,
                        // then assert usize is ok.
                        // ref: `usize::max_value()`
                        let mut secbuf = vec![0; align(section.virtual_size as usize, 0x200)];

                        {
                            let secsize = section.size_of_raw_data as usize;
                            let rawbuf = &mut secbuf[..secsize];
                            let pstart = section.pointer_to_raw_data as usize;
                            rawbuf.copy_from_slice(&buf[pstart..pstart + secsize]);
                        }

                        let insns: Vec<_> = secbuf
                            .par_windows(0x10)
                            .map(|ibuf| match decoder.decode(ibuf) {
                                Ok(Some(insn)) => Some(insn),
                                _ => None,
                            })
                            .collect();

                        info!("loaded section: {}", name);
                        Section {
                            name: name,
                            addr: section.virtual_address as Rva,
                            buf: secbuf,
                            insns: insns,
                        }
                    }));
            }
            Object::Elf(_) => {
                return Err(Error::NotImplemented);
            }
            Object::Mach(_) => {
                return Err(Error::NotImplemented);
            }
            Object::Archive(_) => {
                return Err(Error::NotImplemented);
            }
            Object::Unknown(_) => {
                return Err(Error::NotImplemented);
            }
        }

        Ok(ws)
    }

    /// Construct a workspace from the module at given a file path.
    ///
    /// ```
    /// use lancelot::*;
    /// use lancelot::rsrc::*;
    /// let path = get_path(Rsrc::K32);
    /// // This test resource file is mangled. Needs to be fixed before parsing.
    /// // Otherwise, the following would work:
    /// // let ws = Workspace::from_file(&path).unwrap();
    /// ```
    pub fn from_file(filename: &str) -> Result<Workspace, Error> {
        let buf = read_file(filename)?;
        Workspace::from_buf(filename, buf)
    }
}

pub fn run(args: &Config) -> Result<(), Error> {
    debug!("filename: {:?}", args.filename);
    let ws = Workspace::from_file(&args.filename)?;

    if let Object::PE(_) = ws.get_obj()? {
        foo(&ws).expect("failed to foo");
    }

    Ok(())
}
<commit_msg>workspace: add routines for fetching section and insn by rva<commit_after>extern crate log;
extern crate simplelog;

use goblin::Object;
use log::{debug, error, info, trace};
use rayon::prelude::*;
use std::env;
use std::fs;
use std::io::prelude::*;
use zydis;

// TODO: this really shouldn't be public. only for testing.
// TODO: what is the `cfg(doctest)` setting?
//#[cfg(test)]
pub mod rsrc;

pub struct Config {
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 2 {
            return Err("not enough arguments");
        }

        let filename = args[1].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

#[derive(Debug)]
pub enum Error {
    FileAccess,
    FileFormat,
    NotImplemented,
    InvalidRva,
}

/// Round the given value up to the next multiple of the given base.
///
/// # Panics
///
///   - Base `b` must be at least `2`.
///
/// # Examples
///
/// ```
/// use lancelot::*;
/// assert_eq!(align(0, 2), 0);
/// assert_eq!(align(1, 2), 2);
/// assert_eq!(align(2, 2), 2);
/// assert_eq!(align(3, 2), 4);
/// assert_eq!(align(4, 2), 4);
/// ```
pub fn align(i: usize, b: usize) -> usize {
    if b < 2 {
        panic!("base `b` must be at least: 2");
    }
    let rem = i % b;
    if rem == 0 {
        i
    } else {
        i + (b - rem)
    }
}

pub fn hexdump_ascii(b: u8) -> char {
    if b.is_ascii_graphic() || b == b' ' {
        b as char
    } else {
        '.'
    }
}

pub fn hexdump(buf: &[u8], offset: usize) -> String {
    // 01234567:  00 01 02 03 04 05 06 07  ...............
    // <prefix>   <hex col>                <ascii col>

    let padding = "  ";

    let padding_size = 2;
    let hex_col_size = 3;
    let ascii_col_size = 1;
    let prefix_size = 8 + 1;
    let newline_size = 1;
    let line_size = prefix_size
        + padding_size
        + 16 * hex_col_size
        + padding_size
        + 16 * ascii_col_size
        + newline_size;
    let line_count = align(buf.len(), 0x10) / 0x10;

    let mut ret = String::with_capacity(line_count * line_size);

    let mut line = String::with_capacity(line_size);
    let mut remaining_count = buf.len();
    for line_index in 0..line_count {
        let line_elem_count = 0x10.min(remaining_count);
        let padding_elem_count = 0x10 - line_elem_count;

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        // ^^^^^^^^^
        line.push_str(format!("{:08x}:", offset + 0x10 * line_index).as_str());

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //          ^^
        line.push_str(padding);

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //            ^^^
        for elem in &buf[line_index..line_index + line_elem_count] {
            line.push_str(format!("{:02x} ", elem).as_str());
        }
        for _ in 0..padding_elem_count {
            line.push_str("   ");
        }

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //                                   ^^
        line.push_str(padding);

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //                                     ^
        for elem in &buf[line_index..line_index + line_elem_count] {
            line.push(hexdump_ascii(*elem))
        }
        for _ in 0..padding_elem_count {
            line.push(' ');
        }
        line.push_str(padding);

        // 01234567:  00 01 02 03 04 05 06 07  ...............
        //                                                    ^
        line.push('\n');

        ret.push_str(line.as_str());
        line.truncate(0x0);
        remaining_count -= line_elem_count;
    }

    ret
}

pub fn read_file(filename: &str) -> Result<Vec<u8>, Error> {
    debug!("read_file: {:?}", filename);

    let mut buf = Vec::new();
    {
        debug!("reading file: {}", filename);
        let mut f = match fs::File::open(filename) {
            Ok(f) => f,
            Err(_) => {
                error!("failed to open file: {}", filename);
                return Err(Error::FileAccess);
            }
        };
        let bytes_read = match f.read_to_end(&mut buf) {
            Ok(c) => c,
            Err(_) => {
                error!("failed to read entire file: {}", filename);
                return Err(Error::FileAccess);
            }
        };
        debug!("read {} bytes", bytes_read);
        if bytes_read < 0x10 {
            error!("file too small: {}", filename);
            return Err(Error::FileFormat);
        }
    }

    Ok(buf)
}

type Rva = u64;
type Va = u64;

pub struct Section {
    pub name: String,
    pub addr: Rva,
    pub buf: Vec<u8>,
    pub insns: Vec<Option<zydis::ffi::DecodedInstruction>>,
}

impl Section {
    pub fn contains(self: &Section, rva: Rva) -> bool {
        if rva < self.addr {
            return false;
        }
        if rva >= self.addr + self.buf.len() as Rva {
            return false;
        }
        return true;
    }
}

pub struct Workspace {
    pub filename: String,
    pub buf: Vec<u8>,
    pub sections: Vec<Section>,
}

impl Workspace {
    /// Parse the given file into its object.
    ///
    /// # Errors
    ///   - `Error::FileFormat`: when not able to be parsed by Goblin.
    ///
    /// # Examples
    ///
    /// ```
    /// use goblin::Object;
    /// use matches::matches;
    /// use lancelot::rsrc::*;
    /// let ws = get_workspace(Rsrc::K32);
    /// assert!(matches!(ws.get_obj().unwrap(), Object::PE(_)));
    /// ```
    ///
    /// you might be tempted to maintain a method `get_pe`,
    /// however, i don't think this is a good idea:
    /// its fragile because the file type may not be PE.
    /// therefore, force clients to be explicit:
    ///
    /// ```
    /// use goblin::Object;
    /// use lancelot::rsrc::*;
    /// let ws = get_workspace(Rsrc::K32);
    /// if let Object::PE(_) = ws.get_obj().unwrap() {
    ///     // everyone is happy!
    /// }
    /// ```
    ///
    /// TODO: demonstrate `Error::FileFormat`.
    pub fn get_obj(&self) -> Result<Object, Error> {
        let obj = match Object::parse(&self.buf) {
            Ok(o) => o,
            Err(e) => {
                error!("failed to parse file: {} error: {:?}", self.filename, e);
                return Err(Error::FileFormat);
            }
        };

        match obj {
            Object::Unknown(_) => {
                error!(
                    "unknown file format, magic: | {:02X} {:02X} | '{}{}' ",
                    self.buf[0],
                    self.buf[1],
                    hexdump_ascii(self.buf[0]),
                    hexdump_ascii(self.buf[1])
                );

                Err(Error::FileFormat)
            }
            _ => Ok(obj),
        }
    }

    /// Fetch the section that contains the given address.
    ///
    /// # Examples
    ///
    /// ```
    /// use lancelot::*;
    /// use lancelot::rsrc::*;
    /// let ws = rsrc::get_workspace(rsrc::Rsrc::K32);
    /// assert_eq!(ws.get_section(0x130C0).expect("section").name, ".text");
    /// ```
    pub fn get_section(self: &Workspace, rva: Rva) -> Result<&Section, Error> {
        let sec = self.sections.iter().filter(|sec| sec.contains(rva)).next();
        match sec {
            None => Err(Error::InvalidRva),
            Some(sec) => Ok(sec),
        }
    }

    pub fn get_insn(
        self: &Workspace,
        rva: Rva,
    ) -> Result<Option<&zydis::ffi::DecodedInstruction>, Error> {
        let sec = self.get_section(rva)?;
        let insn = &(sec.insns[(rva - sec.addr) as usize]);

        // jump through hoops to get an Option<&insn> (versus Option<insn>)
        match insn {
            None => Ok(None),
            Some(insn) => Ok(Some(&insn)),
        }
    }

    /// Construct a workspace from the module with the given contents.
    ///
    /// # Errors
    ///   - `Error::FileFormat`: when not able to be parsed by Goblin.
    ///   - `Error::NotImplemented`: when not a PE file.
    ///
    /// # Examples
    ///
    /// ```
    /// use lancelot::*;
    /// use lancelot::rsrc::*;
    /// let buf = get_buf(Rsrc::K32);
    /// let ws = Workspace::from_buf("kernel32.dll", buf).unwrap();
    /// ```
    ///
    /// TODO: demonstrate ELF file behavior.
    /// TODO: demonstrate MachO file behavior.
    /// TODO: demonstrate unknown file behavior.
    pub fn from_buf(filename: &str, buf: Vec<u8>) -> Result<Workspace, Error> {
        let mut ws = Workspace {
            filename: filename.to_string(),
            buf: buf.clone(),
            sections: vec![],
        };

        match ws.get_obj()? {
            Object::PE(pe) => {
                info!("found PE file");

                let machine;
                let mode;
                if pe.is_64 {
                    machine = zydis::MachineMode::Long64;
                    mode = zydis::AddressWidth::_64;
                } else {
                    // TODO: not sure what `LongCompat32` means, vs `Legacy32`.
                    machine = zydis::MachineMode::LongCompat32;
                    mode = zydis::AddressWidth::_32;
                }
                // TODO: save off decoder into workspace.
                let decoder = zydis::Decoder::new(machine, mode).unwrap();

                // TODO: load PE header, too

                ws.sections
                    .extend(pe.sections.iter().map(|section| -> Section {
                        // TODO: i'm sure this can be abused.
                        // TODO: add tests for weird section names.
                        let name = String::from_utf8_lossy(&section.name[..])
                            .into_owned()
                            .trim_end_matches("\u{0}")
                            .trim_end()
                            .to_string();

                        // TODO: figure out if we will work with usize, or u64, or what,
                        // then assert usize is ok.
                        // ref: `usize::max_value()`
                        let mut secbuf = vec![0; align(section.virtual_size as usize, 0x200)];

                        {
                            let secsize = section.size_of_raw_data as usize;
                            let rawbuf = &mut secbuf[..secsize];
                            let pstart = section.pointer_to_raw_data as usize;
                            rawbuf.copy_from_slice(&buf[pstart..pstart + secsize]);
                        }

                        let insns: Vec<_> = secbuf
                            .par_windows(0x10)
                            .map(|ibuf| match decoder.decode(ibuf) {
                                Ok(Some(insn)) => Some(insn),
                                _ => None,
                            })
                            .collect();

                        info!("loaded section: {}", name);
                        Section {
                            name: name,
                            addr: section.virtual_address as Rva,
                            buf: secbuf,
                            insns: insns,
                        }
                    }));
            }
            Object::Elf(_) => {
                return Err(Error::NotImplemented);
            }
            Object::Mach(_) => {
                return Err(Error::NotImplemented);
            }
            Object::Archive(_) => {
                return Err(Error::NotImplemented);
            }
            Object::Unknown(_) => {
                return Err(Error::NotImplemented);
            }
        }

        Ok(ws)
    }

    /// Construct a workspace from the module at given a file path.
    ///
    /// ```
    /// use lancelot::*;
    /// use lancelot::rsrc::*;
    /// let path = get_path(Rsrc::K32);
    /// // This test resource file is mangled. Needs to be fixed before parsing.
    /// // Otherwise, the following would work:
    /// // let ws = Workspace::from_file(&path).unwrap();
    /// ```
    pub fn from_file(filename: &str) -> Result<Workspace, Error> {
        let buf = read_file(filename)?;
        Workspace::from_buf(filename, buf)
    }
}

pub fn run(args: &Config) -> Result<(), Error> {
    debug!("filename: {:?}", args.filename);
    let ws = Workspace::from_file(&args.filename)?;

    if let Object::PE(pe) = ws.get_obj()? {
        let formatter = zydis::Formatter::new(zydis::FormatterStyle::Intel).expect("formatter");

        let mut buffer = [0u8; 200];
        let mut buffer = zydis::OutputBuffer::new(&mut buffer[..]);

        let oep: Rva = pe
            .header
            .optional_header
            .unwrap()
            .standard_fields
            .address_of_entry_point;

        let insn = ws.get_insn(oep).expect("insn").expect("valid insn");
        formatter
            .format_instruction(&insn, &mut buffer, Some(oep as u64), None)
            .expect("format");
        println!("0x{:016X}: {}", oep, buffer);
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! Rust bindings for the [OpenEXR](http://openexr.com) C++ library.
//!
//! OpenEXR is a bitmap image file format that can store high dynamic range
//! (HDR) images along with other arbitrary per-pixel data. It is used heavily
//! in the VFX and 3D animation industries.
//!
//! Although this wrapper's API differs a little from the C++ library, it tries
//! not to differ wildly.  Therefore the [C++ OpenEXR documentation]
//! (https://github.com/openexr/openexr/tree/develop/OpenEXR/doc) is still
//! useful as an introduction and rough reference.  Moreover, the file format
//! itself is also documented there.
//!
//! # Overview
//!
//! There are three primary parts to this crate:
//!
//! * The various [input](input/index.html) and [output](output/index.html)
//!   types.  These are used for reading and writing OpenEXR files.  They
//!   utilize the Header and FrameBuffer(Mut) types, listed below.
//!
//! * [`Header`](struct.Header.html): this is used for querying and specifying
//!   the properties of an OpenEXR file (such as resolution, channels, etc.), for
//!   reading and writing respectively.
//!
//! * [`FrameBuffer`](frame_buffer/struct.FrameBuffer.html) and
//!   [`FrameBufferMut`](frame_buffer/struct.FrameBufferMut.html):
//!   these are intermediaries that tell the OpenEXR APIs how to interpret
//!   your in-memory image data.  Rather than passing your image data to the
//!   API's directly, you construct FrameBuffers that that point at and describe
//!   it, and then you pass those FrameBuffers.
//!
//! # Examples
//!
//! Writing a scanline floating point RGB file.
//!
//! ```no_run
//! # use openexr::{FrameBuffer, Header, ScanlineOutputFile, PixelType};
//! #
//! // Pixel data for a 256x256 floating point RGB image.
//! let pixel_data = vec![(0.82f32, 1.78f32, 0.21f32); 256 * 256];
//!
//! // Create a file to write to.  The `Header` determines the properties of the
//! // file, like resolution and what channels it has.
//! let mut file = std::fs::File::create("output_file.exr").unwrap();
//! let mut output_file = ScanlineOutputFile::new(
//!     &mut file,
//!     Header::new()
//!         .set_resolution(256, 256)
//!         .add_channel("R", PixelType::FLOAT)
//!         .add_channel("G", PixelType::FLOAT)
//!         .add_channel("B", PixelType::FLOAT)).unwrap();
//!
//! // Create a `FrameBuffer` that points at our pixel data and describes it as
//! // RGB data.
//! let mut fb = FrameBuffer::new(256, 256);
//! fb.insert_channels(&["R", "G", "B"], &pixel_data);
//!
//! // Write pixel data to the file.
//! output_file.write_pixels(&fb).unwrap();
//! ```
//!
//! Reading a floating point RGB file.
//!
//! ```no_run
//! # use openexr::{FrameBufferMut, InputFile, PixelType};
//!
//! // Open the EXR file.
//! let mut file = std::fs::File::open("input_file.exr").unwrap();
//! let mut input_file = InputFile::new(&mut file).unwrap();
//!
//! // Get the image dimensions, so we know how large of a buffer to make.
//! let (width, height) = {
//!     let window = input_file.header().data_window();
//!     (window.max.x - window.min.x + 1, window.max.y - window.min.y + 1)
//! };
//!
//! // Buffer to read pixel data into.
//! let mut pixel_data = vec![(0.0f32, 0.0f32, 0.0f32); (width*height) as usize];
//!
//! // New scope because `FrameBuffer` mutably borrows `pixel_data`, so we need
//! // it to go out of scope before we can access our `pixel_data` again.
//! {
//!     // Create `FrameBufferMut` that points at our pixel data and describes
//!     // it as RGB data.
//!     let mut fb = FrameBufferMut::new(width as usize, height as usize);
//!     fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
//!
//!     // Read pixel data from the file.
//!     input_file.read_pixels(&mut fb).unwrap();
//! }
//! ```

#![warn(missing_docs)]

extern crate half;
extern crate libc;
extern crate openexr_sys;

mod cexr_type_aliases;
mod error;
pub mod frame_buffer;
pub mod input;
pub mod output;
mod stream_io;

use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use openexr_sys::*;

pub use cexr_type_aliases::*;
pub use error::*;
pub use frame_buffer::*;
pub use input::*;
pub use output::*;

// TODO: move Header to its own module once we can use
// `pub(crate)` on struct fields (should be in Rust 1.18).

/// Represents an OpenEXR file header.
///
/// The file header describes the properties of the image, such as image
/// resolution, the channels it contains, custom attributes, etc.  It is used
/// both for fetching information about a loaded EXR file and for defining the
/// properties of a file to be written.
///
/// # Examples
///
/// Creating a header for a file that will be written:
///
/// ```
/// use openexr::{Header, PixelType};
///
/// Header::new()
///     .set_resolution(1920, 1080)
///     .add_channel("R", PixelType::FLOAT)
///     .add_channel("G", PixelType::FLOAT)
///     .add_channel("B", PixelType::FLOAT);
/// ```
pub struct Header {
    handle: *mut CEXR_Header,
    owned: bool,
    _phantom: PhantomData<CEXR_Header>,
}

impl Header {
    /// Creates a new header.
    pub fn new() -> Self {
        // Create underlying C header
        let header = {
            let display_window = Box2i {
                min: CEXR_V2i { x: 0, y: 0 },
                max: CEXR_V2i { x: 1, y: 1 },
            };
            let data_window = display_window;
            let pixel_aspect_ratio = 1.0;
            let screen_window_center = CEXR_V2f { x: 0.0, y: 0.0 };
            let screen_window_width = 1.0;
            let line_order = LineOrder::INCREASING_Y;
            let compression = Compression::PIZ_COMPRESSION;
            let header = unsafe {
                CEXR_Header_new(&display_window,
                                &data_window,
                                pixel_aspect_ratio,
                                &screen_window_center,
                                screen_window_width,
                                line_order,
                                compression)
            };
            header
        };

        Self {
            handle: header,
            owned: true,
            _phantom: PhantomData,
        }
    }

    /// Sets the resolution.
    ///
    /// This is really just a shortcut for setting both the display window
    /// and data window to `(0, 0), (width-1, height-1)`.
    pub fn set_resolution(&mut self, width: u32, height: u32) -> &mut Self {
        let window = Box2i {
            min: CEXR_V2i { x: 0, y: 0 },
            max: CEXR_V2i {
                x: width as i32 - 1,
                y: height as i32 - 1,
            },
        };
        unsafe {
            CEXR_Header_set_display_window(self.handle, window);
        }
        unsafe {
            CEXR_Header_set_data_window(self.handle, window);
        }
        self
    }

    /// Sets the display window.
    ///
    /// For simple use-cases, it's better to use `set_resolution()` instead.
    pub fn set_display_window(&mut self, window: Box2i) -> &mut Self {
        unsafe {
            CEXR_Header_set_display_window(self.handle, window);
        }
        self
    }

    /// Sets the data window.
    ///
    /// For simple use-cases, it's better to use `set_resolution()` instead.
    pub fn set_data_window(&mut self, window: Box2i) -> &mut Self {
        unsafe {
            CEXR_Header_set_data_window(self.handle, window);
        }
        self
    }

    /// Sets the pixel aspect ratio.
    pub fn set_pixel_aspect_ratio(&mut self, aspect_ratio: f32) -> &mut Self {
        unsafe {
            CEXR_Header_set_pixel_aspect_ratio(self.handle, aspect_ratio);
        }
        self
    }

    /// Sets the screen window center.
    pub fn set_screen_window_center(&mut self, center: (f32, f32)) -> &mut Self {
        unsafe {
            CEXR_Header_set_screen_window_center(self.handle,
                                                 CEXR_V2f {
                                                     x: center.0,
                                                     y: center.1,
                                                 });
        }
        self
    }

    /// Sets the screen window width.
    pub fn set_screen_window_width(&mut self, width: f32) -> &mut Self {
        unsafe {
            CEXR_Header_set_screen_window_width(self.handle, width);
        }
        self
    }

    /// Sets the line order.
    pub fn set_line_order(&mut self, line_order: LineOrder) -> &mut Self {
        unsafe {
            CEXR_Header_set_line_order(self.handle, line_order);
        }
        self
    }

    /// Sets the compression mode.
    pub fn set_compression(&mut self, compression: Compression) -> &mut Self {
        unsafe {
            CEXR_Header_set_compression(self.handle, compression);
        }
        self
    }

    /// Adds a channel.
    ///
    /// This is a simplified version of `add_channel_detailed()`, using some reasonable
    /// defaults for the details.  Specifically: sampling is set to (1, 1)
    /// and p_linear is set to true.
    pub fn add_channel(&mut self, name: &str, pixel_type: PixelType) -> &mut Self {
        self.add_channel_detailed(name,
                                  Channel {
                                      pixel_type: pixel_type,
                                      x_sampling: 1,
                                      y_sampling: 1,
                                      p_linear: true,
                                  })
    }

    /// Adds a channel, specifying full details.
    pub fn add_channel_detailed(&mut self, name: &str, channel: Channel) -> &mut Self {
        let cname = CString::new(name.as_bytes()).unwrap();
        unsafe { CEXR_Header_insert_channel(self.handle, cname.as_ptr(), channel) };
        self
    }

    /// Access to the data window.
    pub fn data_window(&self) -> &Box2i {
        unsafe { &*CEXR_Header_data_window(self.handle) }
    }

    /// Access to the display window.
    pub fn display_window(&self) -> &Box2i {
        unsafe { &*CEXR_Header_display_window(self.handle) }
    }

    /// Returns an iterator over the channels in the header.
    pub fn channels<'a>(&'a self) -> ChannelIter<'a> {
        ChannelIter {
            iterator: unsafe { CEXR_Header_channel_list_iter(self.handle) },
            _phantom_1: PhantomData,
            _phantom_2: PhantomData,
        }
    }

    /// Access channels by name.
    pub fn get_channel<'a>(&'a self, name: &str) -> Option<&'a Channel> {
        let c_name = CString::new(name.as_bytes()).unwrap();
        let mut error_out = std::ptr::null();
        let mut out = std::ptr::null();
        if unsafe {
               CEXR_Header_get_channel(self.handle, c_name.as_ptr(), &mut out, &mut error_out)
           } == 0 {
            Some(unsafe { &(*out) })
        } else {
            None
        }
    }
}

impl Drop for Header {
    fn drop(&mut self) {
        if self.owned {
            unsafe { CEXR_Header_delete(self.handle) };
        }
    }
}

/// An iterator over the channels in a `Header`.
///
/// Yields a tuple of the name and description of each channel.
pub struct ChannelIter<'a> {
    iterator: *mut CEXR_ChannelListIter,
    _phantom_1: PhantomData<CEXR_ChannelListIter>,
    _phantom_2: PhantomData<&'a Header>,
}

impl<'a> Drop for ChannelIter<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_ChannelListIter_delete(self.iterator) };
    }
}

impl<'a> Iterator for ChannelIter<'a> {
    type Item = Result<(&'a str, Channel)>;
    fn next(&mut self) -> Option<Result<(&'a str, Channel)>> {
        let mut name = unsafe { std::mem::uninitialized() };
        let mut channel = unsafe { std::mem::uninitialized() };
        if unsafe { CEXR_ChannelListIter_next(self.iterator, &mut name, &mut channel) } {
            // TODO: use CStr::from_bytes_with_nul() instead to avoid memory unsafety
            // if the string is not nul terminated.
            let cname = unsafe { CStr::from_ptr(name) };
            let str_name = cname.to_str();
            if let Ok(n) = str_name {
                Some(Ok((n, channel)))
            } else {
                Some(Err(Error::Generic(format!("Invalid channel name: {:?}", cname))))
            }
        } else {
            None
        }
    }
}
<commit_msg>Some more phrasing tweaks.<commit_after>//! Rust bindings for the [OpenEXR](http://openexr.com) C++ library.
//!
//! OpenEXR is a bitmap image file format that can store high dynamic range
//! (HDR) images along with other arbitrary per-pixel data. It is used heavily
//! in the VFX and 3D animation industries.
//!
//! Although this wrapper's API differs a little from the C++ library, it tries
//! not to differ wildly.  Therefore the [C++ OpenEXR documentation]
//! (https://github.com/openexr/openexr/tree/develop/OpenEXR/doc) is still
//! useful as an introduction and rough reference.  Moreover, the file format
//! itself is also documented there.
//!
//! # Overview
//!
//! There are three primary parts to this crate:
//!
//! * The various [input](input/index.html) and [output](output/index.html)
//!   types.  These are used for reading and writing OpenEXR files.  They
//!   utilize the Header and FrameBuffer(Mut) types, listed below.
//!
//! * [`Header`](struct.Header.html): this is used for querying and specifying
//!   the properties of an OpenEXR file (such as resolution, channels, etc.), for
//!   reading and writing respectively.
//!
//! * [`FrameBuffer`](frame_buffer/struct.FrameBuffer.html) and
//!   [`FrameBufferMut`](frame_buffer/struct.FrameBufferMut.html):
//!   these are intermediaries that tell the OpenEXR APIs how to interpret
//!   your in-memory image data.  Rather than passing your image data to the
//!   APIs directly, you construct a FrameBuffer that that points at and
//!   describes it, and then you pass that FrameBuffer.
//!
//! # Examples
//!
//! Writing a scanline floating point RGB file.
//!
//! ```no_run
//! # use openexr::{FrameBuffer, Header, ScanlineOutputFile, PixelType};
//! #
//! // Pixel data for a 256x256 floating point RGB image.
//! let pixel_data = vec![(0.82f32, 1.78f32, 0.21f32); 256 * 256];
//!
//! // Create a file to write to.  The `Header` determines the properties of the
//! // file, like resolution and what channels it has.
//! let mut file = std::fs::File::create("output_file.exr").unwrap();
//! let mut output_file = ScanlineOutputFile::new(
//!     &mut file,
//!     Header::new()
//!         .set_resolution(256, 256)
//!         .add_channel("R", PixelType::FLOAT)
//!         .add_channel("G", PixelType::FLOAT)
//!         .add_channel("B", PixelType::FLOAT)).unwrap();
//!
//! // Create a `FrameBuffer` that points at our pixel data and describes it as
//! // RGB data.
//! let mut fb = FrameBuffer::new(256, 256);
//! fb.insert_channels(&["R", "G", "B"], &pixel_data);
//!
//! // Write pixel data to the file.
//! output_file.write_pixels(&fb).unwrap();
//! ```
//!
//! Reading a floating point RGB file.
//!
//! ```no_run
//! # use openexr::{FrameBufferMut, InputFile, PixelType};
//!
//! // Open the EXR file.
//! let mut file = std::fs::File::open("input_file.exr").unwrap();
//! let mut input_file = InputFile::new(&mut file).unwrap();
//!
//! // Get the image dimensions, so we know how large of a buffer to make.
//! let (width, height) = {
//!     let window = input_file.header().data_window();
//!     (window.max.x - window.min.x + 1, window.max.y - window.min.y + 1)
//! };
//!
//! // Buffer to read pixel data into.
//! let mut pixel_data = vec![(0.0f32, 0.0f32, 0.0f32); (width*height) as usize];
//!
//! // New scope because `FrameBuffer` mutably borrows `pixel_data`, so we need
//! // it to go out of scope before we can access our `pixel_data` again.
//! {
//!     // Create `FrameBufferMut` that points at our pixel data and describes
//!     // it as RGB data.
//!     let mut fb = FrameBufferMut::new(width as usize, height as usize);
//!     fb.insert_channels(&[("R", 0.0), ("G", 0.0), ("B", 0.0)], &mut pixel_data);
//!
//!     // Read pixel data from the file.
//!     input_file.read_pixels(&mut fb).unwrap();
//! }
//! ```

#![warn(missing_docs)]

extern crate half;
extern crate libc;
extern crate openexr_sys;

mod cexr_type_aliases;
mod error;
pub mod frame_buffer;
pub mod input;
pub mod output;
mod stream_io;

use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use openexr_sys::*;

pub use cexr_type_aliases::*;
pub use error::*;
pub use frame_buffer::*;
pub use input::*;
pub use output::*;

// TODO: move Header to its own module once we can use
// `pub(crate)` on struct fields (should be in Rust 1.18).

/// Represents an OpenEXR file header.
///
/// The file header describes the properties of the image, such as image
/// resolution, the channels it contains, custom attributes, etc.  It is used
/// both for fetching information about a loaded EXR file and for defining the
/// properties of a file to be written.
///
/// # Examples
///
/// Creating a header for a file that will be written:
///
/// ```
/// use openexr::{Header, PixelType};
///
/// Header::new()
///     .set_resolution(1920, 1080)
///     .add_channel("R", PixelType::FLOAT)
///     .add_channel("G", PixelType::FLOAT)
///     .add_channel("B", PixelType::FLOAT);
/// ```
pub struct Header {
    handle: *mut CEXR_Header,
    owned: bool,
    _phantom: PhantomData<CEXR_Header>,
}

impl Header {
    /// Creates a new header.
    pub fn new() -> Self {
        // Create underlying C header
        let header = {
            let display_window = Box2i {
                min: CEXR_V2i { x: 0, y: 0 },
                max: CEXR_V2i { x: 1, y: 1 },
            };
            let data_window = display_window;
            let pixel_aspect_ratio = 1.0;
            let screen_window_center = CEXR_V2f { x: 0.0, y: 0.0 };
            let screen_window_width = 1.0;
            let line_order = LineOrder::INCREASING_Y;
            let compression = Compression::PIZ_COMPRESSION;
            let header = unsafe {
                CEXR_Header_new(&display_window,
                                &data_window,
                                pixel_aspect_ratio,
                                &screen_window_center,
                                screen_window_width,
                                line_order,
                                compression)
            };
            header
        };

        Self {
            handle: header,
            owned: true,
            _phantom: PhantomData,
        }
    }

    /// Sets the resolution.
    ///
    /// This is really just a shortcut for setting both the display window
    /// and data window to `(0, 0), (width-1, height-1)`.
    pub fn set_resolution(&mut self, width: u32, height: u32) -> &mut Self {
        let window = Box2i {
            min: CEXR_V2i { x: 0, y: 0 },
            max: CEXR_V2i {
                x: width as i32 - 1,
                y: height as i32 - 1,
            },
        };
        unsafe {
            CEXR_Header_set_display_window(self.handle, window);
        }
        unsafe {
            CEXR_Header_set_data_window(self.handle, window);
        }
        self
    }

    /// Sets the display window.
    ///
    /// For simple use-cases, it's better to use `set_resolution()` instead.
    pub fn set_display_window(&mut self, window: Box2i) -> &mut Self {
        unsafe {
            CEXR_Header_set_display_window(self.handle, window);
        }
        self
    }

    /// Sets the data window.
    ///
    /// For simple use-cases, it's better to use `set_resolution()` instead.
    pub fn set_data_window(&mut self, window: Box2i) -> &mut Self {
        unsafe {
            CEXR_Header_set_data_window(self.handle, window);
        }
        self
    }

    /// Sets the pixel aspect ratio.
    pub fn set_pixel_aspect_ratio(&mut self, aspect_ratio: f32) -> &mut Self {
        unsafe {
            CEXR_Header_set_pixel_aspect_ratio(self.handle, aspect_ratio);
        }
        self
    }

    /// Sets the screen window center.
    pub fn set_screen_window_center(&mut self, center: (f32, f32)) -> &mut Self {
        unsafe {
            CEXR_Header_set_screen_window_center(self.handle,
                                                 CEXR_V2f {
                                                     x: center.0,
                                                     y: center.1,
                                                 });
        }
        self
    }

    /// Sets the screen window width.
    pub fn set_screen_window_width(&mut self, width: f32) -> &mut Self {
        unsafe {
            CEXR_Header_set_screen_window_width(self.handle, width);
        }
        self
    }

    /// Sets the line order.
    pub fn set_line_order(&mut self, line_order: LineOrder) -> &mut Self {
        unsafe {
            CEXR_Header_set_line_order(self.handle, line_order);
        }
        self
    }

    /// Sets the compression mode.
    pub fn set_compression(&mut self, compression: Compression) -> &mut Self {
        unsafe {
            CEXR_Header_set_compression(self.handle, compression);
        }
        self
    }

    /// Adds a channel.
    ///
    /// This is a simplified version of `add_channel_detailed()`, using some reasonable
    /// defaults for the details.  Specifically: sampling is set to (1, 1)
    /// and p_linear is set to true.
    pub fn add_channel(&mut self, name: &str, pixel_type: PixelType) -> &mut Self {
        self.add_channel_detailed(name,
                                  Channel {
                                      pixel_type: pixel_type,
                                      x_sampling: 1,
                                      y_sampling: 1,
                                      p_linear: true,
                                  })
    }

    /// Adds a channel, specifying full details.
    pub fn add_channel_detailed(&mut self, name: &str, channel: Channel) -> &mut Self {
        let cname = CString::new(name.as_bytes()).unwrap();
        unsafe { CEXR_Header_insert_channel(self.handle, cname.as_ptr(), channel) };
        self
    }

    /// Access to the data window.
    pub fn data_window(&self) -> &Box2i {
        unsafe { &*CEXR_Header_data_window(self.handle) }
    }

    /// Access to the display window.
    pub fn display_window(&self) -> &Box2i {
        unsafe { &*CEXR_Header_display_window(self.handle) }
    }

    /// Returns an iterator over the channels in the header.
    pub fn channels<'a>(&'a self) -> ChannelIter<'a> {
        ChannelIter {
            iterator: unsafe { CEXR_Header_channel_list_iter(self.handle) },
            _phantom_1: PhantomData,
            _phantom_2: PhantomData,
        }
    }

    /// Access channels by name.
    pub fn get_channel<'a>(&'a self, name: &str) -> Option<&'a Channel> {
        let c_name = CString::new(name.as_bytes()).unwrap();
        let mut error_out = std::ptr::null();
        let mut out = std::ptr::null();
        if unsafe {
               CEXR_Header_get_channel(self.handle, c_name.as_ptr(), &mut out, &mut error_out)
           } == 0 {
            Some(unsafe { &(*out) })
        } else {
            None
        }
    }
}

impl Drop for Header {
    fn drop(&mut self) {
        if self.owned {
            unsafe { CEXR_Header_delete(self.handle) };
        }
    }
}

/// An iterator over the channels in a `Header`.
///
/// Yields a tuple of the name and description of each channel.
pub struct ChannelIter<'a> {
    iterator: *mut CEXR_ChannelListIter,
    _phantom_1: PhantomData<CEXR_ChannelListIter>,
    _phantom_2: PhantomData<&'a Header>,
}

impl<'a> Drop for ChannelIter<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_ChannelListIter_delete(self.iterator) };
    }
}

impl<'a> Iterator for ChannelIter<'a> {
    type Item = Result<(&'a str, Channel)>;
    fn next(&mut self) -> Option<Result<(&'a str, Channel)>> {
        let mut name = unsafe { std::mem::uninitialized() };
        let mut channel = unsafe { std::mem::uninitialized() };
        if unsafe { CEXR_ChannelListIter_next(self.iterator, &mut name, &mut channel) } {
            // TODO: use CStr::from_bytes_with_nul() instead to avoid memory unsafety
            // if the string is not nul terminated.
            let cname = unsafe { CStr::from_ptr(name) };
            let str_name = cname.to_str();
            if let Ok(n) = str_name {
                Some(Ok((n, channel)))
            } else {
                Some(Err(Error::Generic(format!("Invalid channel name: {:?}", cname))))
            }
        } else {
            None
        }
    }
}
<|endoftext|>