<commit_before>use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

use nix::unistd::Pid;
use scan_fmt::scan_fmt_some;
use serde::Deserialize;

use crate::linux::Jiffies;
use crate::procfs::{ParseStatError, ParseStateError, ProcFsError, Result};

/// The status of a `Process`
///
/// This represents much of the information in `/proc/[pid]/stat`, and is
/// commonly access via a [`Process`](../struct.Process.html)
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Stat {
    /// The process ID
    pub pid: Pid,
    /// The filename of the executable
    pub comm: String,
    /// The state of the process
    pub state: State,
    pub ppid: Pid,
    pub pgrp: i32,
    pub session: i32,
    pub tty_nr: i32,
    pub tpgid: i32,
    pub flags: u32,
    pub minflt: u64,
    pub cminflt: u64,
    pub majflt: u64,
    pub cmajflt: u64,
    pub utime: Jiffies,
    pub stime: Jiffies,
    pub cutime: Jiffies,
    pub cstime: Jiffies,
    pub priority: i64,
    pub nice: i64,
    pub num_threads: i64,
    pub starttime: u64,
    pub vsize: u64,
    pub rss: u64,
}

impl Stat {
    pub fn from_pid<P: fmt::Display>(pid: P) -> Result<Stat> {
        let path_str = format!("/proc/{}/stat", pid);
        let mut f = File::open(&path_str)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        s.parse()
    }
}

impl Default for Stat {
    fn default() -> Stat {
        Stat {
            pid: Pid::from_raw(0),
            comm: "init".to_owned(),
            state: "R".parse().unwrap(),
            ppid: Pid::from_raw(0),
            pgrp: 0,
            session: 0,
            tty_nr: 0,
            tpgid: 0,
            flags: 0,
            minflt: 0,
            cminflt: 0,
            majflt: 0,
            cmajflt: 0,
            utime: Jiffies::new(0),
            stime: Jiffies::new(0),
            cutime: Jiffies::new(0),
            cstime: Jiffies::new(0),
            priority: 0,
            nice: 0,
            num_threads: 0,
            starttime: 0,
            vsize: 0,
            rss: 0,
        }
    }
}

fn field<T>(val: Option<T>, field_name: &'static str, row: &str, position: u8) -> Result<T> {
    match val {
        Some(v) => Ok(v),
        None => {
            let row = row.to_string();
            return Err(ParseStatError {
                line: row,
                field_name,
                position,
            }
            .into());
        }
    }
}

impl FromStr for Stat {
    type Err = ProcFsError;
    /// Parse the results of /proc/[pid]/stat into a `Stat`
    fn from_str(s: &str) -> Result<Stat> {
        let (
            pid,
            comm,
            state,
            ppid,
            pgrp,
            session,
            tty_nr,
            tpgid,
            flags,
            minflt,
            cminflt,
            majflt,
            cmajflt,
            utime,
            stime,
            cutime,
            cstime,
            priority,
            nice,
            num_threads,
            starttime,
            vsize,
            rss,
        ) = scan_fmt_some!(
            s,
            "{d} {/\\((.*)\\) /}{} {} {} {} {} {} {d} {d} {d} {d} {d} {d} {d} {} {} \
             {} {} {} 0 {d} {d} {}",
            i32,    // pid
            String, // comm
            State,  // state
            i32,    // ppid
            i32,    // pgrp
            i32,    // session
            i32,    // tty_nr
            i32,    // tpgid
            u32,    // flags
            u64,    // minflt
            u64,    // cminflt
            u64,    // majflt
            u64,    // cmajflt
            u64,    // utime
            u64,    // stime
            i64,    // cutime (children usertime)
            i64,    // cstime
            i64,    // priority
            i64,    // nice
            i64,    // num_threads
            // itrealvalue (always 0)
            u64, // starttime FIXME: should be long long int
            u64, // vsize
            u64  /* rss */
        );
        Ok(Stat {
            pid: Pid::from_raw(field(pid, "pid", s, 0)?),
            comm: field(comm, "comm", s, 1)?,
            state: field(state, "state", s, 2)?,
            ppid: Pid::from_raw(field(ppid, "ppid", s, 3)?),
            pgrp: field(pgrp, "pgrp", s, 4)?,
            session: field(session, "session", s, 5)?,
            tty_nr: field(tty_nr, "tty_nr", s, 6)?,
            tpgid: field(tpgid, "tpgid", s, 7)?,
            flags: field(flags, "flags", s, 8)?,
            minflt: field(minflt, "minflt", s, 9)?,
            cminflt: field(cminflt, "cminflt", s, 10)?,
            majflt: field(majflt, "majflt", s, 11)?,
            cmajflt: field(cmajflt, "cmajflt", s, 12)?,
            utime: field(utime, "utime", s, 13)?.into(),
            stime: field(stime, "stime", s, 14)?.into(),
            cutime: field(cutime, "cutime", s, 15)?.into(),
            cstime: field(cstime, "cstime", s, 16)?.into(),
            priority: field(priority, "priority", s, 17)?,
            nice: field(nice, "nice", s, 18)?,
            num_threads: field(num_threads, "num_threads", s, 19)?,
            starttime: field(starttime, "starttime", s, 20)?,
            vsize: field(vsize, "vsize", s, 21)?,
            rss: field(rss, "rss", s, 22)?,
        })
    }
}

/// The state of the process
///
/// See `man 5 proc` for details
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub enum State {
    /// `R`: Currently using the CPU
    Running,
    /// `S`: Sleeping on an interruptible wait
    Sleeping,
    /// `D`: Sleeping on an uninterruptible or disk sleep
    UninterruptibleSleep,
    /// `W`: Paging
    Waiting,
    /// `T`: Traced or Stopped on a signal
    Stopped,
    /// `Z`: No parent process has reaped this
    Zombie,
}

impl FromStr for State {
    type Err = ProcFsError;

    /// Parse from either /proc/<pid>/stat or the command line
    ///
    /// StructOpt and scan_fmt use the same trait, so we need to handle both
    /// here.
    fn from_str(s: &str) -> Result<State> {
        use self::State::*;
        match s {
            "R" | "running" => Ok(Running),
            "S" | "sleeping" => Ok(Sleeping),
            "D" | "uninteruptible-sleep" => Ok(UninterruptibleSleep),
            "W" | "waiting" => Ok(Waiting),
            "T" | "stopped" => Ok(Stopped),
            "Z" | "zombie" => Ok(Zombie),
            _ => Err(ParseStateError {
                state: s.to_string(),
            }
            .into()),
        }
    }
}

#[cfg(test)]
mod test {
    mod cpu_stat {
        use super::super::*;

        #[test]
        fn test_parse_cpuline() {
            for (i, s) in [
                "529 ((sd-proc)) S 885 885 885 0 -1 107793 24 0 0 0 0 \
                 0 0 0 20 0 1 0 7777777 111111111 647 18848888888888888888 1 1 0 \
                 0 0 0 0 4096 0 0 0 0 17 15 0 0 0 0 0 0 0 0 0 0 0 0 0",
                "47 (migration/8) S 2 0 0 0 -1 66666668 0 0 0 0 0 14 0 0 -100 0 1 \
                 0 25 0 0 18848888888888888888 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 \
                 8 99 1 0 0 0 0 0 0 0 0 0 0 0",
                "122 (statsd /app/connection) S 103 103 181 0 -1 304 0626 0 \
                 0 0 605 198 0 0 20 0 10 0 71025 1230417920 11878 18848888888888888888 \
                 1 1 0 0 0 0 0 4096 16898 0 0 0 17 11 0 0 0 0 0 0 0 0 0 0 0 0 0",
            ]
            .iter()
            .enumerate()
            {
                s.parse::<Stat>().unwrap_or_else(|e| match e {
                    ProcFsError::ParseStatError(e) => panic!("line {}: {}", i, e),
                    x => panic!("unexpected error: {:?}", x),
                });
            }
        }
    }
}
<commit_msg>Add new linux process states<commit_after>use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;

use nix::unistd::Pid;
use scan_fmt::scan_fmt_some;
use serde::Deserialize;

use crate::linux::Jiffies;
use crate::procfs::{ParseStatError, ParseStateError, ProcFsError, Result};

/// The status of a `Process`
///
/// This represents much of the information in `/proc/[pid]/stat`, and is
/// commonly access via a [`Process`](../struct.Process.html)
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Stat {
    /// The process ID
    pub pid: Pid,
    /// The filename of the executable
    pub comm: String,
    /// The state of the process
    pub state: State,
    pub ppid: Pid,
    pub pgrp: i32,
    pub session: i32,
    pub tty_nr: i32,
    pub tpgid: i32,
    pub flags: u32,
    pub minflt: u64,
    pub cminflt: u64,
    pub majflt: u64,
    pub cmajflt: u64,
    pub utime: Jiffies,
    pub stime: Jiffies,
    pub cutime: Jiffies,
    pub cstime: Jiffies,
    pub priority: i64,
    pub nice: i64,
    pub num_threads: i64,
    pub starttime: u64,
    pub vsize: u64,
    pub rss: u64,
}

impl Stat {
    pub fn from_pid<P: fmt::Display>(pid: P) -> Result<Stat> {
        let path_str = format!("/proc/{}/stat", pid);
        let mut f = File::open(&path_str)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        s.parse()
    }
}

impl Default for Stat {
    fn default() -> Stat {
        Stat {
            pid: Pid::from_raw(0),
            comm: "init".to_owned(),
            state: "R".parse().unwrap(),
            ppid: Pid::from_raw(0),
            pgrp: 0,
            session: 0,
            tty_nr: 0,
            tpgid: 0,
            flags: 0,
            minflt: 0,
            cminflt: 0,
            majflt: 0,
            cmajflt: 0,
            utime: Jiffies::new(0),
            stime: Jiffies::new(0),
            cutime: Jiffies::new(0),
            cstime: Jiffies::new(0),
            priority: 0,
            nice: 0,
            num_threads: 0,
            starttime: 0,
            vsize: 0,
            rss: 0,
        }
    }
}

fn field<T>(val: Option<T>, field_name: &'static str, row: &str, position: u8) -> Result<T> {
    match val {
        Some(v) => Ok(v),
        None => {
            let row = row.to_string();
            return Err(ParseStatError {
                line: row,
                field_name,
                position,
            }
            .into());
        }
    }
}

impl FromStr for Stat {
    type Err = ProcFsError;
    /// Parse the results of /proc/[pid]/stat into a `Stat`
    fn from_str(s: &str) -> Result<Stat> {
        let (
            pid,
            comm,
            state,
            ppid,
            pgrp,
            session,
            tty_nr,
            tpgid,
            flags,
            minflt,
            cminflt,
            majflt,
            cmajflt,
            utime,
            stime,
            cutime,
            cstime,
            priority,
            nice,
            num_threads,
            starttime,
            vsize,
            rss,
        ) = scan_fmt_some!(
            s,
            "{d} {/\\((.*)\\) /}{} {} {} {} {} {} {d} {d} {d} {d} {d} {d} {d} {} {} \
             {} {} {} 0 {d} {d} {}",
            i32,    // pid
            String, // comm
            State,  // state
            i32,    // ppid
            i32,    // pgrp
            i32,    // session
            i32,    // tty_nr
            i32,    // tpgid
            u32,    // flags
            u64,    // minflt
            u64,    // cminflt
            u64,    // majflt
            u64,    // cmajflt
            u64,    // utime
            u64,    // stime
            i64,    // cutime (children usertime)
            i64,    // cstime
            i64,    // priority
            i64,    // nice
            i64,    // num_threads
            // itrealvalue (always 0)
            u64, // starttime FIXME: should be long long int
            u64, // vsize
            u64  /* rss */
        );
        Ok(Stat {
            pid: Pid::from_raw(field(pid, "pid", s, 0)?),
            comm: field(comm, "comm", s, 1)?,
            state: field(state, "state", s, 2)?,
            ppid: Pid::from_raw(field(ppid, "ppid", s, 3)?),
            pgrp: field(pgrp, "pgrp", s, 4)?,
            session: field(session, "session", s, 5)?,
            tty_nr: field(tty_nr, "tty_nr", s, 6)?,
            tpgid: field(tpgid, "tpgid", s, 7)?,
            flags: field(flags, "flags", s, 8)?,
            minflt: field(minflt, "minflt", s, 9)?,
            cminflt: field(cminflt, "cminflt", s, 10)?,
            majflt: field(majflt, "majflt", s, 11)?,
            cmajflt: field(cmajflt, "cmajflt", s, 12)?,
            utime: field(utime, "utime", s, 13)?.into(),
            stime: field(stime, "stime", s, 14)?.into(),
            cutime: field(cutime, "cutime", s, 15)?.into(),
            cstime: field(cstime, "cstime", s, 16)?.into(),
            priority: field(priority, "priority", s, 17)?,
            nice: field(nice, "nice", s, 18)?,
            num_threads: field(num_threads, "num_threads", s, 19)?,
            starttime: field(starttime, "starttime", s, 20)?,
            vsize: field(vsize, "vsize", s, 21)?,
            rss: field(rss, "rss", s, 22)?,
        })
    }
}

/// The state of the process
///
/// See `man 5 proc` for details
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub enum State {
    /// `R`: Currently using the CPU
    Running,
    /// `S`: Sleeping on an interruptible wait
    Sleeping,
    /// `D`: Sleeping on an uninterruptible or disk sleep
    UninterruptibleSleep,
    /// `W`: Paging
    Waiting,
    /// `T`: Traced or Stopped on a signal
    Stopped,
    /// `Z`: No parent process has reaped this
    Zombie,
    /// `X`: Dead
    Dead,
    /// `P`: Parked
    Parked,
    /// `N`: NoLoad, a process that does not contribute to the load average
    NoLoad,
    /// `I`: Idle, an uninterruptable task which does not contribute to load
    ///
    /// Combination of `D` and `N`
    ///
    /// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=80ed87c8a9ca0cad7ca66cf3bbdfb17559a66dcf
    Idle,
}

impl FromStr for State {
    type Err = ProcFsError;

    /// Parse from either /proc/<pid>/stat or the command line
    ///
    /// StructOpt and scan_fmt use the same trait, so we need to handle both
    /// here.
    fn from_str(s: &str) -> Result<State> {
        use self::State::*;
        match s {
            "R" | "running" => Ok(Running),
            "S" | "sleeping" => Ok(Sleeping),
            "D" | "uninteruptible-sleep" => Ok(UninterruptibleSleep),
            "W" | "waiting" => Ok(Waiting),
            "T" | "stopped" => Ok(Stopped),
            "Z" | "zombie" => Ok(Zombie),
            "X" | "x" | "dead" => Ok(Dead),
            "P" | "parked" => Ok(Parked),
            "N" | "noload" => Ok(NoLoad),
            "I" | "idle" => Ok(Idle),
            _ => Err(ParseStateError {
                state: s.to_string(),
            }
            .into()),
        }
    }
}

#[cfg(test)]
mod test {
    mod cpu_stat {
        use super::super::*;

        #[test]
        fn test_parse_cpuline() {
            for (i, s) in [
                "529 ((sd-proc)) S 885 885 885 0 -1 107793 24 0 0 0 0 \
                 0 0 0 20 0 1 0 7777777 111111111 647 18848888888888888888 1 1 0 \
                 0 0 0 0 4096 0 0 0 0 17 15 0 0 0 0 0 0 0 0 0 0 0 0 0",
                "47 (migration/8) S 2 0 0 0 -1 66666668 0 0 0 0 0 14 0 0 -100 0 1 \
                 0 25 0 0 18848888888888888888 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 \
                 8 99 1 0 0 0 0 0 0 0 0 0 0 0",
                "122 (statsd /app/connection) S 103 103 181 0 -1 304 0626 0 \
                 0 0 605 198 0 0 20 0 10 0 71025 1230417920 11878 18848888888888888888 \
                 1 1 0 0 0 0 0 4096 16898 0 0 0 17 11 0 0 0 0 0 0 0 0 0 0 0 0 0",
            ]
            .iter()
            .enumerate()
            {
                s.parse::<Stat>().unwrap_or_else(|e| match e {
                    ProcFsError::ParseStatError(e) => panic!("line {}: {}", i, e),
                    x => panic!("unexpected error: {:?}", x),
                });
            }
        }
    }
}
<|endoftext|>


<commit_before>use gl;

use context::CommandContext;
use version::Version;
use version::Api;

use backend::Facade;
use CapabilitiesSource;

use std::fmt;
use std::collections::hash_map::{self, HashMap};

use GlObject;
use ProgramExt;
use Handle;
use RawUniformValue;

use program::{COMPILER_GLOBAL_LOCK, ProgramCreationInput, ProgramCreationError, Binary};
use program::GetBinaryError;

use program::reflection::{Uniform, UniformBlock, OutputPrimitives};
use program::reflection::{Attribute, TransformFeedbackBuffer};
use program::reflection::{SubroutineData, ShaderStage, SubroutineUniform};
use program::shader::build_shader;

use program::raw::RawProgram;

use vertex::VertexFormat;

/// A combination of shaders linked together.
pub struct Program {
    raw: RawProgram,
    outputs_srgb: bool,
    uses_point_size: bool,
}

impl Program {
    /// Builds a new program.
    pub fn new<'a, F, I>(facade: &F, input: I) -> Result<Program, ProgramCreationError>
                         where I: Into<ProgramCreationInput<'a>>, F: Facade
    {
        let input = input.into();

        let (raw, outputs_srgb, uses_point_size) = match input {
            ProgramCreationInput::SourceCode { vertex_shader, tessellation_control_shader,
                                               tessellation_evaluation_shader, geometry_shader,
                                               fragment_shader, transform_feedback_varyings,
                                               outputs_srgb, uses_point_size } =>
            {
                let mut has_geometry_shader = false;
                let mut has_tessellation_control_shader = false;
                let mut has_tessellation_evaluation_shader = false;

                let mut shaders = vec![
                    (vertex_shader, gl::VERTEX_SHADER),
                    (fragment_shader, gl::FRAGMENT_SHADER)
                ];

                if let Some(gs) = geometry_shader {
                    shaders.push((gs, gl::GEOMETRY_SHADER));
                    has_geometry_shader = true;
                }

                if let Some(ts) = tessellation_control_shader {
                    shaders.push((ts, gl::TESS_CONTROL_SHADER));
                    has_tessellation_control_shader = true;
                }

                if let Some(ts) = tessellation_evaluation_shader {
                    shaders.push((ts, gl::TESS_EVALUATION_SHADER));
                    has_tessellation_evaluation_shader = true;
                }

                // TODO: move somewhere else
                if transform_feedback_varyings.is_some() &&
                    (facade.get_context().get_version() >= &Version(Api::Gl, 3, 0) ||
                        !facade.get_context().get_extensions().gl_ext_transform_feedback)
                {
                    return Err(ProgramCreationError::TransformFeedbackNotSupported);
                }

                if uses_point_size && !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) {
                    return Err(ProgramCreationError::PointSizeNotSupported);
                }

                let _lock = COMPILER_GLOBAL_LOCK.lock();

                let shaders_store = {
                    let mut shaders_store = Vec::new();
                    for (src, ty) in shaders.into_iter() {
                        shaders_store.push(try!(build_shader(facade, ty, src)));
                    }
                    shaders_store
                };

                (try!(RawProgram::from_shaders(facade, &shaders_store, has_geometry_shader,
                                               has_tessellation_control_shader, has_tessellation_evaluation_shader,
                                               transform_feedback_varyings)),
                 outputs_srgb, uses_point_size)
            },

            ProgramCreationInput::Binary { data, outputs_srgb, uses_point_size } => {
                if uses_point_size && !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) {
                    return Err(ProgramCreationError::PointSizeNotSupported);
                }

                (try!(RawProgram::from_binary(facade, data)), outputs_srgb, uses_point_size)
            },
        };
        Ok(Program {
            raw: raw,
            outputs_srgb: outputs_srgb,
            uses_point_size: uses_point_size,
        })
    }

    /// Builds a new program from GLSL source code.
    ///
    /// A program is a group of shaders linked together.
    ///
    /// # Parameters
    ///
    /// - `vertex_shader`: Source code of the vertex shader.
    /// - `fragment_shader`: Source code of the fragment shader.
    /// - `geometry_shader`: Source code of the geometry shader.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # let display: glium::Display = unsafe { std::mem::uninitialized() };
    /// # let vertex_source = ""; let fragment_source = ""; let geometry_source = "";
    /// let program = glium::Program::from_source(&display, vertex_source, fragment_source,
    ///     Some(geometry_source));
    /// ```
    ///
    #[inline]
    pub fn from_source<'a, F>(facade: &F, vertex_shader: &'a str, fragment_shader: &'a str,
                              geometry_shader: Option<&'a str>)
                              -> Result<Program, ProgramCreationError> where F: Facade
    {
        Program::new(facade, ProgramCreationInput::SourceCode {
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            geometry_shader: geometry_shader,
            tessellation_control_shader: None,
            tessellation_evaluation_shader: None,
            transform_feedback_varyings: None,
            outputs_srgb: false,
            uses_point_size: false,
        })
    }

    /// Returns the program's compiled binary.
    ///
    /// You can store the result in a file, then reload it later. This avoids having to compile
    /// the source code every time.
    #[inline]
    pub fn get_binary(&self) -> Result<Binary, GetBinaryError> {
        self.raw.get_binary()
    }

    /// Returns the *location* of an output fragment, if it exists.
    ///
    /// The *location* is low-level information that is used internally by glium.
    /// You probably don't need to call this function.
    ///
    /// You can declare output fragments in your shaders by writing:
    ///
    /// ```notrust
    /// out vec4 foo;
    /// ```
    ///
    #[inline]
    pub fn get_frag_data_location(&self, name: &str) -> Option<u32> {
        self.raw.get_frag_data_location(name)
    }

    /// Returns informations about a uniform variable, if it exists.
    #[inline]
    pub fn get_uniform(&self, name: &str) -> Option<&Uniform> {
        self.raw.get_uniform(name)
    }

    /// Returns an iterator to the list of uniforms.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, uniform) in program.uniforms() {
    ///     println!("Name: {} - Type: {:?}", name, uniform.ty);
    /// }
    /// ```
    #[inline]
    pub fn uniforms(&self) -> hash_map::Iter<String, Uniform> {
        self.raw.uniforms()
    }

    /// Returns a list of uniform blocks.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, uniform) in program.get_uniform_blocks() {
    ///     println!("Name: {}", name);
    /// }
    /// ```
    #[inline]
    pub fn get_uniform_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_uniform_blocks()
    }

    /// Returns the list of transform feedback varyings.
    #[inline]
    pub fn get_transform_feedback_buffers(&self) -> &[TransformFeedbackBuffer] {
        self.raw.get_transform_feedback_buffers()
    }

    /// True if the transform feedback output of this program matches the specified `VertexFormat`
    /// and `stride`.
    ///
    /// The `stride` is the number of bytes between two vertices.
    #[inline]
    pub fn transform_feedback_matches(&self, format: &VertexFormat, stride: usize) -> bool {
        self.raw.transform_feedback_matches(format, stride)
    }

    /// Returns the type of geometry that transform feedback would generate, or `None` if it
    /// depends on the vertex/index data passed when drawing.
    ///
    /// This corresponds to `GL_GEOMETRY_OUTPUT_TYPE` or `GL_TESS_GEN_MODE`. If the program doesn't
    /// contain either a geometry shader or a tessellation evaluation shader, returns `None`.
    #[inline]
    pub fn get_output_primitives(&self) -> Option<OutputPrimitives> {
        self.raw.get_output_primitives()
    }

    /// Returns true if the program contains a tessellation stage.
    #[inline]
    pub fn has_tessellation_shaders(&self) -> bool {
        self.raw.has_tessellation_shaders()
    }

    /// Returns true if the program contains a tessellation control stage.
    #[inline]
    pub fn has_tessellation_control_shader(&self) -> bool {
        self.raw.has_tessellation_control_shader()
    }

    /// Returns true if the program contains a tessellation evaluation stage.
    #[inline]
    pub fn has_tessellation_evaluation_shader(&self) -> bool {
        self.raw.has_tessellation_evaluation_shader()
    }

    /// Returns true if the program contains a geometry shader.
    #[inline]
    pub fn has_geometry_shader(&self) -> bool {
        self.raw.has_geometry_shader()
    }

    /// Returns informations about an attribute, if it exists.
    #[inline]
    pub fn get_attribute(&self, name: &str) -> Option<&Attribute> {
        self.raw.get_attribute(name)
    }

    /// Returns an iterator to the list of attributes.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, attribute) in program.attributes() {
    ///     println!("Name: {} - Type: {:?}", name, attribute.ty);
    /// }
    /// ```
    #[inline]
    pub fn attributes(&self) -> hash_map::Iter<String, Attribute> {
        self.raw.attributes()
    }

    /// Returns true if the program has been configured to output sRGB instead of RGB.
    #[inline]
    pub fn has_srgb_output(&self) -> bool {
        self.outputs_srgb
    }

    /// Returns the list of shader storage blocks.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, uniform) in program.get_shader_storage_blocks() {
    ///     println!("Name: {}", name);
    /// }
    /// ```
    #[inline]
    pub fn get_shader_storage_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_shader_storage_blocks()
    }

    /// Returns the subroutine uniforms of this program.
    ///
    /// Since subroutine uniforms are unique per shader and *not* per program,
    /// the keys of the `HashMap` are in the format `("subroutine_name", ShaderStage)`.
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (&(ref name, shader), uniform) in program.get_subroutine_uniforms() {
    ///     println!("Name: {}", name);
    /// }
    /// ```
    #[inline]
    pub fn get_subroutine_uniforms(&self) -> &HashMap<(String, ShaderStage), SubroutineUniform> {
        &self.raw.get_subroutine_data().subroutine_uniforms
    }

    /// Returns true if the program has been configured to use the `gl_PointSize` variable.
    ///
    /// If the program uses `gl_PointSize` without having been configured appropriately, then
    /// setting the value of `gl_PointSize` will have no effect.
    #[inline]
    pub fn uses_point_size(&self) -> bool {
      self.uses_point_size
    }
}

impl fmt::Debug for Program {
    #[inline]
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(formatter, "{:?}", self.raw)
    }
}

impl GlObject for Program {
    type Id = Handle;

    #[inline]
    fn get_id(&self) -> Handle {
        self.raw.get_id()
    }
}

impl ProgramExt for Program {
    fn use_program(&self, ctxt: &mut CommandContext) {
        // compatibility was checked at program creation
        if self.uses_point_size && !ctxt.state.enabled_program_point_size {
            unsafe { ctxt.gl.Enable(gl::PROGRAM_POINT_SIZE); }
        } else if !self.uses_point_size && ctxt.state.enabled_program_point_size {
            unsafe { ctxt.gl.Disable(gl::PROGRAM_POINT_SIZE); }
        }

        if ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.extensions.gl_arb_framebuffer_srgb ||
           ctxt.extensions.gl_ext_framebuffer_srgb || ctxt.extensions.gl_ext_srgb_write_control
        {
            if ctxt.state.enabled_framebuffer_srgb == self.outputs_srgb {
                ctxt.state.enabled_framebuffer_srgb = !self.outputs_srgb;

                if self.outputs_srgb {
                    unsafe { ctxt.gl.Disable(gl::FRAMEBUFFER_SRGB) };
                } else {
                    unsafe { ctxt.gl.Enable(gl::FRAMEBUFFER_SRGB) };
                }
            }
        }

        self.raw.use_program(ctxt)
    }

    #[inline]
    fn set_uniform(&self, ctxt: &mut CommandContext, uniform_location: gl::types::GLint,
                   value: &RawUniformValue)
    {
        self.raw.set_uniform(ctxt, uniform_location, value)
    }

    #[inline]
    fn set_uniform_block_binding(&self, ctxt: &mut CommandContext, block_location: gl::types::GLuint,
                                 value: gl::types::GLuint)
    {
        self.raw.set_uniform_block_binding(ctxt, block_location, value)
    }

    #[inline]
    fn set_shader_storage_block_binding(&self, ctxt: &mut CommandContext,
                                        block_location: gl::types::GLuint,
                                        value: gl::types::GLuint)
    {
        self.raw.set_shader_storage_block_binding(ctxt, block_location, value)
    }

    #[inline]
    fn set_subroutine_uniforms_for_stage(&self, ctxt: &mut CommandContext,
                                         stage: ShaderStage,
                                         indices: &[gl::types::GLuint])
    {
        self.raw.set_subroutine_uniforms_for_stage(ctxt, stage, indices);
    }

    #[inline]
    fn get_uniform(&self, name: &str) -> Option<&Uniform> {
        self.raw.get_uniform(name)
    }

    #[inline]
    fn get_uniform_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_uniform_blocks()
    }

    #[inline]
    fn get_shader_storage_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_shader_storage_blocks()
    }

    #[inline]
    fn get_subroutine_data(&self) -> &SubroutineData {
        self.raw.get_subroutine_data()
    }
}
<commit_msg>Fixed erroneous TransformFeedbackNotSupported error<commit_after>use gl;

use context::CommandContext;
use version::Version;
use version::Api;

use backend::Facade;
use CapabilitiesSource;

use std::fmt;
use std::collections::hash_map::{self, HashMap};

use GlObject;
use ProgramExt;
use Handle;
use RawUniformValue;

use program::{COMPILER_GLOBAL_LOCK, ProgramCreationInput, ProgramCreationError, Binary};
use program::GetBinaryError;

use program::reflection::{Uniform, UniformBlock, OutputPrimitives};
use program::reflection::{Attribute, TransformFeedbackBuffer};
use program::reflection::{SubroutineData, ShaderStage, SubroutineUniform};
use program::shader::build_shader;

use program::raw::RawProgram;

use vertex::VertexFormat;

/// A combination of shaders linked together.
pub struct Program {
    raw: RawProgram,
    outputs_srgb: bool,
    uses_point_size: bool,
}

impl Program {
    /// Builds a new program.
    pub fn new<'a, F, I>(facade: &F, input: I) -> Result<Program, ProgramCreationError>
                         where I: Into<ProgramCreationInput<'a>>, F: Facade
    {
        let input = input.into();

        let (raw, outputs_srgb, uses_point_size) = match input {
            ProgramCreationInput::SourceCode { vertex_shader, tessellation_control_shader,
                                               tessellation_evaluation_shader, geometry_shader,
                                               fragment_shader, transform_feedback_varyings,
                                               outputs_srgb, uses_point_size } =>
            {
                let mut has_geometry_shader = false;
                let mut has_tessellation_control_shader = false;
                let mut has_tessellation_evaluation_shader = false;

                let mut shaders = vec![
                    (vertex_shader, gl::VERTEX_SHADER),
                    (fragment_shader, gl::FRAGMENT_SHADER)
                ];

                if let Some(gs) = geometry_shader {
                    shaders.push((gs, gl::GEOMETRY_SHADER));
                    has_geometry_shader = true;
                }

                if let Some(ts) = tessellation_control_shader {
                    shaders.push((ts, gl::TESS_CONTROL_SHADER));
                    has_tessellation_control_shader = true;
                }

                if let Some(ts) = tessellation_evaluation_shader {
                    shaders.push((ts, gl::TESS_EVALUATION_SHADER));
                    has_tessellation_evaluation_shader = true;
                }

                // TODO: move somewhere else
                if transform_feedback_varyings.is_some() &&
                    !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) &&
                    !facade.get_context().get_extensions().gl_ext_transform_feedback
                {
                    return Err(ProgramCreationError::TransformFeedbackNotSupported);
                }

                if uses_point_size && !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) {
                    return Err(ProgramCreationError::PointSizeNotSupported);
                }

                let _lock = COMPILER_GLOBAL_LOCK.lock();

                let shaders_store = {
                    let mut shaders_store = Vec::new();
                    for (src, ty) in shaders.into_iter() {
                        shaders_store.push(try!(build_shader(facade, ty, src)));
                    }
                    shaders_store
                };

                (try!(RawProgram::from_shaders(facade, &shaders_store, has_geometry_shader,
                                               has_tessellation_control_shader, has_tessellation_evaluation_shader,
                                               transform_feedback_varyings)),
                 outputs_srgb, uses_point_size)
            },

            ProgramCreationInput::Binary { data, outputs_srgb, uses_point_size } => {
                if uses_point_size && !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) {
                    return Err(ProgramCreationError::PointSizeNotSupported);
                }

                (try!(RawProgram::from_binary(facade, data)), outputs_srgb, uses_point_size)
            },
        };
        Ok(Program {
            raw: raw,
            outputs_srgb: outputs_srgb,
            uses_point_size: uses_point_size,
        })
    }

    /// Builds a new program from GLSL source code.
    ///
    /// A program is a group of shaders linked together.
    ///
    /// # Parameters
    ///
    /// - `vertex_shader`: Source code of the vertex shader.
    /// - `fragment_shader`: Source code of the fragment shader.
    /// - `geometry_shader`: Source code of the geometry shader.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # let display: glium::Display = unsafe { std::mem::uninitialized() };
    /// # let vertex_source = ""; let fragment_source = ""; let geometry_source = "";
    /// let program = glium::Program::from_source(&display, vertex_source, fragment_source,
    ///     Some(geometry_source));
    /// ```
    ///
    #[inline]
    pub fn from_source<'a, F>(facade: &F, vertex_shader: &'a str, fragment_shader: &'a str,
                              geometry_shader: Option<&'a str>)
                              -> Result<Program, ProgramCreationError> where F: Facade
    {
        Program::new(facade, ProgramCreationInput::SourceCode {
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            geometry_shader: geometry_shader,
            tessellation_control_shader: None,
            tessellation_evaluation_shader: None,
            transform_feedback_varyings: None,
            outputs_srgb: false,
            uses_point_size: false,
        })
    }

    /// Returns the program's compiled binary.
    ///
    /// You can store the result in a file, then reload it later. This avoids having to compile
    /// the source code every time.
    #[inline]
    pub fn get_binary(&self) -> Result<Binary, GetBinaryError> {
        self.raw.get_binary()
    }

    /// Returns the *location* of an output fragment, if it exists.
    ///
    /// The *location* is low-level information that is used internally by glium.
    /// You probably don't need to call this function.
    ///
    /// You can declare output fragments in your shaders by writing:
    ///
    /// ```notrust
    /// out vec4 foo;
    /// ```
    ///
    #[inline]
    pub fn get_frag_data_location(&self, name: &str) -> Option<u32> {
        self.raw.get_frag_data_location(name)
    }

    /// Returns informations about a uniform variable, if it exists.
    #[inline]
    pub fn get_uniform(&self, name: &str) -> Option<&Uniform> {
        self.raw.get_uniform(name)
    }

    /// Returns an iterator to the list of uniforms.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, uniform) in program.uniforms() {
    ///     println!("Name: {} - Type: {:?}", name, uniform.ty);
    /// }
    /// ```
    #[inline]
    pub fn uniforms(&self) -> hash_map::Iter<String, Uniform> {
        self.raw.uniforms()
    }

    /// Returns a list of uniform blocks.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, uniform) in program.get_uniform_blocks() {
    ///     println!("Name: {}", name);
    /// }
    /// ```
    #[inline]
    pub fn get_uniform_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_uniform_blocks()
    }

    /// Returns the list of transform feedback varyings.
    #[inline]
    pub fn get_transform_feedback_buffers(&self) -> &[TransformFeedbackBuffer] {
        self.raw.get_transform_feedback_buffers()
    }

    /// True if the transform feedback output of this program matches the specified `VertexFormat`
    /// and `stride`.
    ///
    /// The `stride` is the number of bytes between two vertices.
    #[inline]
    pub fn transform_feedback_matches(&self, format: &VertexFormat, stride: usize) -> bool {
        self.raw.transform_feedback_matches(format, stride)
    }

    /// Returns the type of geometry that transform feedback would generate, or `None` if it
    /// depends on the vertex/index data passed when drawing.
    ///
    /// This corresponds to `GL_GEOMETRY_OUTPUT_TYPE` or `GL_TESS_GEN_MODE`. If the program doesn't
    /// contain either a geometry shader or a tessellation evaluation shader, returns `None`.
    #[inline]
    pub fn get_output_primitives(&self) -> Option<OutputPrimitives> {
        self.raw.get_output_primitives()
    }

    /// Returns true if the program contains a tessellation stage.
    #[inline]
    pub fn has_tessellation_shaders(&self) -> bool {
        self.raw.has_tessellation_shaders()
    }

    /// Returns true if the program contains a tessellation control stage.
    #[inline]
    pub fn has_tessellation_control_shader(&self) -> bool {
        self.raw.has_tessellation_control_shader()
    }

    /// Returns true if the program contains a tessellation evaluation stage.
    #[inline]
    pub fn has_tessellation_evaluation_shader(&self) -> bool {
        self.raw.has_tessellation_evaluation_shader()
    }

    /// Returns true if the program contains a geometry shader.
    #[inline]
    pub fn has_geometry_shader(&self) -> bool {
        self.raw.has_geometry_shader()
    }

    /// Returns informations about an attribute, if it exists.
    #[inline]
    pub fn get_attribute(&self, name: &str) -> Option<&Attribute> {
        self.raw.get_attribute(name)
    }

    /// Returns an iterator to the list of attributes.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, attribute) in program.attributes() {
    ///     println!("Name: {} - Type: {:?}", name, attribute.ty);
    /// }
    /// ```
    #[inline]
    pub fn attributes(&self) -> hash_map::Iter<String, Attribute> {
        self.raw.attributes()
    }

    /// Returns true if the program has been configured to output sRGB instead of RGB.
    #[inline]
    pub fn has_srgb_output(&self) -> bool {
        self.outputs_srgb
    }

    /// Returns the list of shader storage blocks.
    ///
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (name, uniform) in program.get_shader_storage_blocks() {
    ///     println!("Name: {}", name);
    /// }
    /// ```
    #[inline]
    pub fn get_shader_storage_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_shader_storage_blocks()
    }

    /// Returns the subroutine uniforms of this program.
    ///
    /// Since subroutine uniforms are unique per shader and *not* per program,
    /// the keys of the `HashMap` are in the format `("subroutine_name", ShaderStage)`.
    /// ## Example
    ///
    /// ```no_run
    /// # let program: glium::Program = unsafe { std::mem::uninitialized() };
    /// for (&(ref name, shader), uniform) in program.get_subroutine_uniforms() {
    ///     println!("Name: {}", name);
    /// }
    /// ```
    #[inline]
    pub fn get_subroutine_uniforms(&self) -> &HashMap<(String, ShaderStage), SubroutineUniform> {
        &self.raw.get_subroutine_data().subroutine_uniforms
    }

    /// Returns true if the program has been configured to use the `gl_PointSize` variable.
    ///
    /// If the program uses `gl_PointSize` without having been configured appropriately, then
    /// setting the value of `gl_PointSize` will have no effect.
    #[inline]
    pub fn uses_point_size(&self) -> bool {
      self.uses_point_size
    }
}

impl fmt::Debug for Program {
    #[inline]
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(formatter, "{:?}", self.raw)
    }
}

impl GlObject for Program {
    type Id = Handle;

    #[inline]
    fn get_id(&self) -> Handle {
        self.raw.get_id()
    }
}

impl ProgramExt for Program {
    fn use_program(&self, ctxt: &mut CommandContext) {
        // compatibility was checked at program creation
        if self.uses_point_size && !ctxt.state.enabled_program_point_size {
            unsafe { ctxt.gl.Enable(gl::PROGRAM_POINT_SIZE); }
        } else if !self.uses_point_size && ctxt.state.enabled_program_point_size {
            unsafe { ctxt.gl.Disable(gl::PROGRAM_POINT_SIZE); }
        }

        if ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.extensions.gl_arb_framebuffer_srgb ||
           ctxt.extensions.gl_ext_framebuffer_srgb || ctxt.extensions.gl_ext_srgb_write_control
        {
            if ctxt.state.enabled_framebuffer_srgb == self.outputs_srgb {
                ctxt.state.enabled_framebuffer_srgb = !self.outputs_srgb;

                if self.outputs_srgb {
                    unsafe { ctxt.gl.Disable(gl::FRAMEBUFFER_SRGB) };
                } else {
                    unsafe { ctxt.gl.Enable(gl::FRAMEBUFFER_SRGB) };
                }
            }
        }

        self.raw.use_program(ctxt)
    }

    #[inline]
    fn set_uniform(&self, ctxt: &mut CommandContext, uniform_location: gl::types::GLint,
                   value: &RawUniformValue)
    {
        self.raw.set_uniform(ctxt, uniform_location, value)
    }

    #[inline]
    fn set_uniform_block_binding(&self, ctxt: &mut CommandContext, block_location: gl::types::GLuint,
                                 value: gl::types::GLuint)
    {
        self.raw.set_uniform_block_binding(ctxt, block_location, value)
    }

    #[inline]
    fn set_shader_storage_block_binding(&self, ctxt: &mut CommandContext,
                                        block_location: gl::types::GLuint,
                                        value: gl::types::GLuint)
    {
        self.raw.set_shader_storage_block_binding(ctxt, block_location, value)
    }

    #[inline]
    fn set_subroutine_uniforms_for_stage(&self, ctxt: &mut CommandContext,
                                         stage: ShaderStage,
                                         indices: &[gl::types::GLuint])
    {
        self.raw.set_subroutine_uniforms_for_stage(ctxt, stage, indices);
    }

    #[inline]
    fn get_uniform(&self, name: &str) -> Option<&Uniform> {
        self.raw.get_uniform(name)
    }

    #[inline]
    fn get_uniform_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_uniform_blocks()
    }

    #[inline]
    fn get_shader_storage_blocks(&self) -> &HashMap<String, UniformBlock> {
        self.raw.get_shader_storage_blocks()
    }

    #[inline]
    fn get_subroutine_data(&self) -> &SubroutineData {
        self.raw.get_subroutine_data()
    }
}
<|endoftext|>


<commit_before>extern crate time;

use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Serialize, Deserialize, Debug)]
pub enum Event {
    BeginPeriod,
    EndPeriod,
    BeginSession,
    EndSession,
    Pause,
    Proceed,
    Meta { text: String },
    Commit { hash: u64 },
    Branch { name: String },
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
    pub id: u64,
    pub user: String,
    events: Vec<Event>,
}

impl Session {
    pub fn new() -> Session {
        let now = SystemTime::now();
        let seconds = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
        Session {
            id: seconds,
            user: "Rafael".to_string(),
            events: Vec::<Event>::new(),
        }
    }
    pub fn append_event(&mut self, e: Event) {
        match e {
            Event::BeginPeriod => self.events.push(e),
            Event::EndPeriod => self.events.push(e),
            Event::BeginSession => self.events.push(e),
            Event::EndSession => self.events.push(e),
            Event::Pause => self.events.push(e),
            Event::Proceed => self.events.push(e),
            Event::Meta { .. } => self.events.push(e),
            Event::Commit { .. } => self.events.push(e),
            Event::Branch { .. } => self.events.push(e),
        }
    }
}
<commit_msg>add period struct and impl<commit_after>extern crate time;

use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Serialize, Deserialize, Debug)]
pub enum Event {
    BeginPeriod,
    EndPeriod,
    BeginSession,
    EndSession,
    Pause,
    Proceed,
    Meta { text: String },
    Commit { hash: u64 },
    Branch { name: String },
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
    pub id: u64,
    // is this field necessary?
    pub user: String,
    events: Vec<Event>,
}

impl Session {
    pub fn new() -> Session {
        let now = SystemTime::now();
        let seconds = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
        Session {
            id: seconds,
            user: "Rafael".to_string(),
            events: Vec::<Event>::new(),
        }
    }
    pub fn push_event(&mut self, e: Event) {
        match e {
            Event::BeginPeriod => self.events.push(e),
            Event::EndPeriod => self.events.push(e),
            Event::BeginSession => self.events.push(e),
            Event::EndSession => self.events.push(e),
            Event::Pause => self.events.push(e),
            Event::Proceed => self.events.push(e),
            Event::Meta { .. } => self.events.push(e),
            Event::Commit { .. } => self.events.push(e),
            Event::Branch { .. } => self.events.push(e),
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct Period {
    id: u64,
    // unused field is pointless
    user: String,
    sessions: Vec<Session>,
}

impl Period {
    pub fn new() -> Period {
        let now = SystemTime::now();
        let seconds = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
        Period {
            id: seconds,
            user: "Rafael".to_string(),
            sessions: Vec::<Session>::new(),
        }
    }

    pub fn push_session(&mut self, s: Session) {
        // Checking for valid session here
        self.sessions.push(s);
    }
}
<|endoftext|>


<commit_before>use linear::Matrix4;

pub type Transform = Matrix4<f32>;
/// Class of types that can yield transformation matrices.
pub trait Transformable {
  fn transform(&self) -> Transform;
}

impl Transformable for Matrix4<f32> {
  fn transform(&self) -> Transform {
    self.clone()
  }
}
<commit_msg>Newline.<commit_after>use linear::Matrix4;

pub type Transform = Matrix4<f32>;

/// Class of types that can yield transformation matrices.
pub trait Transformable {
  fn transform(&self) -> Transform;
}

impl Transformable for Matrix4<f32> {
  fn transform(&self) -> Transform {
    self.clone()
  }
}
<|endoftext|>


<commit_before>use std::io::{Write, Seek, SeekFrom};
use std::str::from_utf8;
use std::mem;
use std::slice;
use std::cell::RefCell;
use std::path::Path;
use std::borrow::Cow;
use std::collections::HashMap;

use uuid::Uuid;
use memmap::{Mmap, Protection};

use super::{Result, ErrorKind};
use super::sdk::SdkInfo;
use super::dsym::{Object, Variant};

// stored information:
//
//      object name + arch -> uuid
//      uuid -> stored variant
//      stored variant + addr -> symbol
//      symbol -> [object name id, symbol name id, symbol addr]
//      object name id -> object name
//      symbol name id -> symbol name
//

enum Backing<'a> {
    Buf(Cow<'a, [u8]>),
    Mmap(Mmap),
}

#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
struct MemDbHeader {
    pub version: u32,
    pub variants_start: u32,
    pub variants_count: u32,
    pub uuids_start: u32,
    pub uuids_count: u32,
    pub tagged_object_names_start: u32,
    pub tagged_object_names_count: u32,
    pub object_names_start: u32,
    pub object_names_count: u32,
    pub symbols_start: u32,
    pub symbols_count: u32,
}

#[repr(C, packed)]
struct StoredSlice {
    pub offset: u32,
    pub length: u32,
}

#[repr(C, packed)]
struct IndexItem {
    addr_low: u32,
    addr_high: u16,
    src_id: u16,
    sym_id: u32,
}

pub struct MemDb<'a> {
    backing: Backing<'a>
}

pub struct Symbol<'a> {
    object_name: &'a str,
    symbol: &'a str,
    addr: u64,
}

pub struct MemDbBuilder<W> {
    writer: RefCell<W>,
    info: SdkInfo,
    symbols: Vec<String>,
    symbols_map: HashMap<String, u32>,
    object_names: Vec<String>,
    object_names_map: HashMap<String, u16>,
    object_uuid_mapping: Vec<String>,
    variant_uuids: Vec<Uuid>,
    variants: Vec<Vec<IndexItem>>,
}

impl IndexItem {
    pub fn addr(&self) -> u64 {
        ((self.addr_high as u64) << 32) | (self.addr_low as u64)
    }
}

impl<W: Write + Seek> MemDbBuilder<W> {

    pub fn new(writer: W, info: &SdkInfo) -> Result<MemDbBuilder<W>> {
        let rv = MemDbBuilder {
            writer: RefCell::new(writer),
            info: info.clone(),
            symbols: vec![],
            symbols_map: HashMap::new(),
            object_names: vec![],
            object_names_map: HashMap::new(),
            object_uuid_mapping: vec![],
            variant_uuids: vec![],
            variants: vec![],
        };
        let header = MemDbHeader { ..Default::default() };
        rv.write(&header)?;
        Ok(rv)
    }

    fn write_bytes(&self, x: &[u8]) -> Result<usize> {
        self.writer.borrow_mut().write_all(x)?;
        Ok(x.len())
    }

    fn write<T>(&self, x: &T) -> Result<usize> {
        unsafe {
            let bytes : *const u8 = mem::transmute(x);
            let size = mem::size_of_val(x);
            self.writer.borrow_mut().write_all(slice::from_raw_parts(bytes, size))?;
            Ok(size)
        }
    }

    fn seek(&self, new_pos: usize) -> Result<()> {
        self.writer.borrow_mut().seek(SeekFrom::Start(new_pos as u64))?;
        Ok(())
    }

    fn tell(&self) -> Result<usize> {
        Ok(self.writer.borrow_mut().seek(SeekFrom::Current(0))? as usize)
    }

    fn add_symbol(&mut self, sym: &str) -> u32 {
        if let Some(&sym_id) = self.symbols_map.get(sym) {
            return sym_id;
        }
        let symbol_count = self.symbols.len() as u32;
        self.symbols.push(sym.to_string());
        self.symbols_map.insert(sym.to_string(), symbol_count);
        symbol_count
    }

    fn add_object_name(&mut self, src: &str) -> u16 {
        if let Some(&src_id) = self.object_names_map.get(src) {
            return src_id;
        }
        let object_count = self.object_names.len() as u16;
        self.object_names.push(src.to_string());
        self.object_names_map.insert(src.to_string(), object_count);
        object_count
    }

    pub fn write_object(&mut self, obj: &Object, filename: Option<&str>) -> Result<()> {
        for variant in obj.variants() {
            self.write_object_variant(&obj, &variant, filename)?;
        }
        Ok(())
    }

    pub fn write_object_variant(&mut self, obj: &Object, var: &Variant,
                                filename: Option<&str>) -> Result<()> {
        let mut symbols = obj.symbols(var.arch())?;
        let src = var.name().or(filename).unwrap();
        let src_id = self.add_object_name(src);

        // build symbol index
        let mut index = vec![];
        for (addr, sym) in symbols.iter() {
            let sym_id = self.add_symbol(sym);
            index.push(IndexItem {
                addr_low: (addr & 0xffffffff) as u32,
                addr_high: ((addr >> 32) &0xffff) as u16,
                src_id: src_id,
                sym_id: sym_id,
            });
        }
        index.sort_by_key(|item| item.addr());

        // register variant and uuid as well as object name and arch
        self.variant_uuids.push(var.uuid().unwrap());
        self.variants.push(index);
        self.object_uuid_mapping.push(format!("{}:{}", src, var.arch()));

        Ok(())
    }

    fn make_string_slices(&self, strings: &[String]) -> Result<Vec<StoredSlice>> {
        let mut slices = vec![];
        for string in strings {
            let offset = self.tell()?;
            let len = self.write_bytes(string.as_bytes())?;
            slices.push(StoredSlice {
                offset: offset as u32,
                length: len as u32
            });
        }
        Ok(slices)
    }

    fn write_slices(&self, slices: &[StoredSlice], start: &mut u32, len: &mut u32) -> Result<()> {
        *start = self.tell()? as u32;
        for item in slices.iter() {
            self.write(item)?;
        }
        *len = slices.len() as u32;
        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        let mut header = MemDbHeader { ..Default::default() };

        // start by writing out the index of the variants and record the slices.
        let mut slices = vec![];
        for variant in self.variants.iter() {
            let offset = self.tell()?;
            for index_item in variant {
                self.write(index_item)?;
            }
            slices.push(StoredSlice {
                offset: offset as u32,
                length: (self.tell()? - offset) as u32
            });
        }
        self.write_slices(&slices[..], &mut header.variants_start,
                          &mut header.variants_count)?;

        // next write out the UUIDs.  Since these are fixed length we do not
        // need to use slices here.
        header.uuids_start = self.tell()? as u32;
        header.uuids_count = self.variant_uuids.len() as u32;
        for uuid in self.variant_uuids.iter() {
            self.write_bytes(uuid.as_bytes())?;
        }

        // next we write out the name + arch -> uuid index mapping
        let slices = self.make_string_slices(&self.object_uuid_mapping[..])?;
        self.write_slices(&slices[..], &mut header.tagged_object_names_start,
                          &mut header.tagged_object_names_count)?;

        // now write out all the object name sources
        let slices = self.make_string_slices(&self.object_names[..])?;
        self.write_slices(&slices[..], &mut header.object_names_start,
                          &mut header.tagged_object_names_count)?;

        // now write out all the symbols
        let slices = self.make_string_slices(&self.symbols[..])?;
        self.write_slices(&slices[..], &mut header.symbols_start,
                          &mut header.symbols_count)?;

        // write the updated header
        self.seek(0)?;
        self.write(&header)?;

        Ok(())
    }
}

fn verify_version<'a>(rv: MemDb<'a>) -> Result<MemDb<'a>> {
    if rv.header()?.version != 1 {
        Err(ErrorKind::UnsupportedMemDbVersion.into())
    } else {
        Ok(rv)
    }
}

impl<'a> MemDb<'a> {

    pub fn from_cow(cow: Cow<'a, [u8]>) -> Result<MemDb<'a>> {
        verify_version(MemDb {
            backing: Backing::Buf(cow),
        })
    }

    pub fn from_slice(buffer: &'a [u8]) -> Result<MemDb<'a>> {
        MemDb::from_cow(Cow::Borrowed(buffer))
    }

    pub fn from_vec(buffer: Vec<u8>) -> Result<MemDb<'a>> {
        MemDb::from_cow(Cow::Owned(buffer))
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MemDb<'a>> {
        let mmap = Mmap::open_path(path, Protection::Read)?;
        verify_version(MemDb {
            backing: Backing::Mmap(mmap),
        })
    }

    #[inline(always)]
    pub fn buffer(&self) -> &[u8] {
        match self.backing {
            Backing::Buf(ref buf) => buf,
            Backing::Mmap(ref mmap) => unsafe { mmap.as_slice() }
        }
    }

    fn get_data(&self, start: usize, len: usize) -> Result<&[u8]> {
        let buffer = self.buffer();
        let end = start.wrapping_add(len);
        if end < start || end > buffer.len() {
            Err(ErrorKind::BadMemDb.into())
        } else {
            Ok(&buffer[start..end])
        }
    }

    fn get_slice<T>(&self, offset: usize, count: usize) -> Result<&[T]> {
        let size = mem::size_of::<T>();
        Ok(unsafe {
            slice::from_raw_parts(
                mem::transmute(self.get_data(offset, count * size)?.as_ptr()),
                count
            )
        })
    }

    #[inline(always)]
    fn header(&self) -> Result<&MemDbHeader> {
        unsafe {
            Ok(mem::transmute(self.get_data(0, mem::size_of::<MemDbHeader>())?.as_ptr()))
        }
    }

    #[inline(always)]
    fn uuids(&self) -> Result<&[Uuid]> {
        let head = self.header()?;
        self.get_slice(head.uuids_start as usize, head.uuids_count as usize)
    }

    #[inline(always)]
    fn variants(&self) -> Result<&[StoredSlice]> {
        let head = self.header()?;
        self.get_slice(head.variants_start as usize, head.variants_count as usize)
    }

    #[inline(always)]
    fn get_index(&self, uuid: &Uuid) -> Result<Option<&[IndexItem]>> {
        let uuids = self.uuids()?;
        for (idx, item_uuid) in uuids.iter().enumerate() {
            if item_uuid == uuid {
                let variant_slice = &self.variants()?[idx];
                unsafe {
                    let data = self.get_data(variant_slice.offset as usize,
                                             variant_slice.length as usize)?;
                    let count = variant_slice.length as usize / mem::size_of::<IndexItem>();
                    return Ok(Some(slice::from_raw_parts(
                        mem::transmute(data.as_ptr()),
                        count
                    )));
                }
            }
        }
        Ok(None)
    }

    #[inline(always)]
    fn symbols(&self) -> Result<&[StoredSlice]> {
        let head = self.header()?;
        self.get_slice(head.symbols_start as usize, head.symbols_count as usize)
    }

    #[inline(always)]
    fn object_names(&self) -> Result<&[StoredSlice]> {
        let head = self.header()?;
        self.get_slice(head.object_names_start as usize, head.object_names_count as usize)
    }

    #[inline(always)]
    fn get_string(&self, slice: &StoredSlice) -> Result<&str> {
        let bytes = self.get_data(slice.offset as usize, slice.length as usize)?;
        Ok(from_utf8(bytes)?)
    }

    pub fn lookup_by_uuid(&'a self, uuid: &Uuid, addr: u64)
        -> Result<Option<Symbol<'a>>>
    {
        let index = match self.get_index(uuid)? {
            Some(idx) => idx,
            None => { return Ok(None); }
        };
        let mut low = 0;
        let mut high = index.len();

        while low < high {
            let mid = (low + high) / 2;
            let ii = &index[mid as usize];
            if addr < ii.addr() {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        if low > 0 && low <= index.len() {
            Ok(Some(self.index_item_to_symbol(&index[low - 1])?))
        } else {
            Ok(None)
        }
    }

    /*
    pub fn lookup_by_object_name<'a>(&'a self, object_name: &str, arch: &str, addr: u64)
        -> Option<Symbol<'a>>
    {
        self.find_uuid(object_name, arch).and_then(|uuid| {
            self.lookup_by_uuid(uuid, addr)
        })
    }
    */

    fn get_object_name(&'a self, src_id: u16) -> Result<&'a str> {
        self.get_string(&self.object_names()?[src_id as usize])
    }

    fn get_symbol(&'a self, sym_id: u32) -> Result<&'a str> {
        self.get_string(&self.symbols()?[sym_id as usize])
    }

    fn index_item_to_symbol(&'a self, ii: &IndexItem) -> Result<Symbol<'a>> {
        Ok(Symbol {
            object_name: self.get_object_name(ii.src_id)?,
            symbol: self.get_symbol(ii.sym_id)?,
            addr: ii.addr(),
        })
    }
}
<commit_msg>Corrected various small issues<commit_after>use std::io::{Write, Seek, SeekFrom};
use std::str::from_utf8;
use std::mem;
use std::slice;
use std::cell::RefCell;
use std::path::Path;
use std::borrow::Cow;
use std::collections::HashMap;

use uuid::Uuid;
use memmap::{Mmap, Protection};

use super::{Result, ErrorKind};
use super::sdk::SdkInfo;
use super::dsym::{Object, Variant};

// stored information:
//
//      object name + arch -> uuid
//      uuid -> stored variant
//      stored variant + addr -> symbol
//      symbol -> [object name id, symbol name id, symbol addr]
//      object name id -> object name
//      symbol name id -> symbol name
//

enum Backing<'a> {
    Buf(Cow<'a, [u8]>),
    Mmap(Mmap),
}

#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
struct MemDbHeader {
    pub version: u32,
    pub variants_start: u32,
    pub variants_count: u32,
    pub uuids_start: u32,
    pub uuids_count: u32,
    pub tagged_object_names_start: u32,
    pub tagged_object_names_count: u32,
    pub object_names_start: u32,
    pub object_names_count: u32,
    pub symbols_start: u32,
    pub symbols_count: u32,
}

#[repr(C, packed)]
struct StoredSlice {
    pub offset: u32,
    pub length: u32,
}

#[repr(C, packed)]
#[derive(Debug)]
struct IndexItem {
    addr_low: u32,
    addr_high: u16,
    src_id: u16,
    sym_id: u32,
}

pub struct MemDb<'a> {
    backing: Backing<'a>
}

#[derive(Debug)]
pub struct Symbol<'a> {
    object_name: &'a str,
    symbol: &'a str,
    addr: u64,
}

pub struct MemDbBuilder<W> {
    writer: RefCell<W>,
    info: SdkInfo,
    symbols: Vec<String>,
    symbols_map: HashMap<String, u32>,
    object_names: Vec<String>,
    object_names_map: HashMap<String, u16>,
    object_uuid_mapping: Vec<String>,
    variant_uuids: Vec<Uuid>,
    variants: Vec<Vec<IndexItem>>,
}

impl IndexItem {
    pub fn addr(&self) -> u64 {
        ((self.addr_high as u64) << 32) | (self.addr_low as u64)
    }
}

impl<W: Write + Seek> MemDbBuilder<W> {

    pub fn new(writer: W, info: &SdkInfo) -> Result<MemDbBuilder<W>> {
        let rv = MemDbBuilder {
            writer: RefCell::new(writer),
            info: info.clone(),
            symbols: vec![],
            symbols_map: HashMap::new(),
            object_names: vec![],
            object_names_map: HashMap::new(),
            object_uuid_mapping: vec![],
            variant_uuids: vec![],
            variants: vec![],
        };
        let header = MemDbHeader { ..Default::default() };
        rv.write(&header)?;
        Ok(rv)
    }

    fn write_bytes(&self, x: &[u8]) -> Result<usize> {
        self.writer.borrow_mut().write_all(x)?;
        Ok(x.len())
    }

    fn write<T>(&self, x: &T) -> Result<usize> {
        unsafe {
            let bytes : *const u8 = mem::transmute(x);
            let size = mem::size_of_val(x);
            self.writer.borrow_mut().write_all(slice::from_raw_parts(bytes, size))?;
            Ok(size)
        }
    }

    fn seek(&self, new_pos: usize) -> Result<()> {
        self.writer.borrow_mut().seek(SeekFrom::Start(new_pos as u64))?;
        Ok(())
    }

    fn tell(&self) -> Result<usize> {
        Ok(self.writer.borrow_mut().seek(SeekFrom::Current(0))? as usize)
    }

    fn add_symbol(&mut self, sym: &str) -> u32 {
        if let Some(&sym_id) = self.symbols_map.get(sym) {
            return sym_id;
        }
        let symbol_count = self.symbols.len() as u32;
        self.symbols.push(sym.to_string());
        self.symbols_map.insert(sym.to_string(), symbol_count);
        symbol_count
    }

    fn add_object_name(&mut self, src: &str) -> u16 {
        if let Some(&src_id) = self.object_names_map.get(src) {
            return src_id;
        }
        let object_count = self.object_names.len() as u16;
        self.object_names.push(src.to_string());
        self.object_names_map.insert(src.to_string(), object_count);
        object_count
    }

    pub fn write_object(&mut self, obj: &Object, filename: Option<&str>) -> Result<()> {
        for variant in obj.variants() {
            self.write_object_variant(&obj, &variant, filename)?;
        }
        Ok(())
    }

    pub fn write_object_variant(&mut self, obj: &Object, var: &Variant,
                                filename: Option<&str>) -> Result<()> {
        let mut symbols = obj.symbols(var.arch())?;
        let src = var.name().or(filename).unwrap();
        let src_id = self.add_object_name(src);

        // build symbol index
        let mut index = vec![];
        for (addr, sym) in symbols.iter() {
            let sym_id = self.add_symbol(sym);
            index.push(IndexItem {
                addr_low: (addr & 0xffffffff) as u32,
                addr_high: ((addr >> 32) &0xffff) as u16,
                src_id: src_id,
                sym_id: sym_id,
            });
        }
        index.sort_by_key(|item| item.addr());

        // register variant and uuid as well as object name and arch
        self.variant_uuids.push(var.uuid().unwrap());
        self.variants.push(index);
        self.object_uuid_mapping.push(format!("{}:{}", src, var.arch()));

        Ok(())
    }

    fn make_string_slices(&self, strings: &[String]) -> Result<Vec<StoredSlice>> {
        let mut slices = vec![];
        for string in strings {
            let offset = self.tell()?;
            let len = self.write_bytes(string.as_bytes())?;
            slices.push(StoredSlice {
                offset: offset as u32,
                length: len as u32
            });
        }
        Ok(slices)
    }

    fn write_slices(&self, slices: &[StoredSlice], start: &mut u32, len: &mut u32) -> Result<()> {
        *start = self.tell()? as u32;
        for item in slices.iter() {
            self.write(item)?;
        }
        *len = slices.len() as u32;
        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        let mut header = MemDbHeader { ..Default::default() };
        header.version = 1;

        // start by writing out the index of the variants and record the slices.
        let mut slices = vec![];
        for variant in self.variants.iter() {
            let offset = self.tell()?;
            for index_item in variant {
                self.write(index_item)?;
            }
            slices.push(StoredSlice {
                offset: offset as u32,
                length: (self.tell()? - offset) as u32
            });
        }
        self.write_slices(&slices[..], &mut header.variants_start,
                          &mut header.variants_count)?;

        // next write out the UUIDs.  Since these are fixed length we do not
        // need to use slices here.
        header.uuids_start = self.tell()? as u32;
        header.uuids_count = self.variant_uuids.len() as u32;
        for uuid in self.variant_uuids.iter() {
            self.write_bytes(uuid.as_bytes())?;
        }

        // next we write out the name + arch -> uuid index mapping
        let slices = self.make_string_slices(&self.object_uuid_mapping[..])?;
        self.write_slices(&slices[..], &mut header.tagged_object_names_start,
                          &mut header.tagged_object_names_count)?;

        // now write out all the object name sources
        let slices = self.make_string_slices(&self.object_names[..])?;
        self.write_slices(&slices[..], &mut header.object_names_start,
                          &mut header.object_names_count)?;

        // now write out all the symbols
        let slices = self.make_string_slices(&self.symbols[..])?;
        self.write_slices(&slices[..], &mut header.symbols_start,
                          &mut header.symbols_count)?;

        // write the updated header
        self.seek(0)?;
        self.write(&header)?;

        Ok(())
    }
}

fn verify_version<'a>(rv: MemDb<'a>) -> Result<MemDb<'a>> {
    if rv.header()?.version != 1 {
        Err(ErrorKind::UnsupportedMemDbVersion.into())
    } else {
        Ok(rv)
    }
}

impl<'a> MemDb<'a> {

    pub fn from_cow(cow: Cow<'a, [u8]>) -> Result<MemDb<'a>> {
        verify_version(MemDb {
            backing: Backing::Buf(cow),
        })
    }

    pub fn from_slice(buffer: &'a [u8]) -> Result<MemDb<'a>> {
        MemDb::from_cow(Cow::Borrowed(buffer))
    }

    pub fn from_vec(buffer: Vec<u8>) -> Result<MemDb<'a>> {
        MemDb::from_cow(Cow::Owned(buffer))
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MemDb<'a>> {
        let mmap = Mmap::open_path(path, Protection::Read)?;
        verify_version(MemDb {
            backing: Backing::Mmap(mmap),
        })
    }

    #[inline(always)]
    pub fn buffer(&self) -> &[u8] {
        match self.backing {
            Backing::Buf(ref buf) => buf,
            Backing::Mmap(ref mmap) => unsafe { mmap.as_slice() }
        }
    }

    fn get_data(&self, start: usize, len: usize) -> Result<&[u8]> {
        let buffer = self.buffer();
        let end = start.wrapping_add(len);
        if end < start || end > buffer.len() {
            Err(ErrorKind::BadMemDb.into())
        } else {
            Ok(&buffer[start..end])
        }
    }

    fn get_slice<T>(&self, offset: usize, count: usize) -> Result<&[T]> {
        let size = mem::size_of::<T>();
        Ok(unsafe {
            slice::from_raw_parts(
                mem::transmute(self.get_data(offset, count * size)?.as_ptr()),
                count
            )
        })
    }

    #[inline(always)]
    fn header(&self) -> Result<&MemDbHeader> {
        unsafe {
            Ok(mem::transmute(self.get_data(0, mem::size_of::<MemDbHeader>())?.as_ptr()))
        }
    }

    #[inline(always)]
    fn uuids(&self) -> Result<&[Uuid]> {
        let head = self.header()?;
        self.get_slice(head.uuids_start as usize, head.uuids_count as usize)
    }

    #[inline(always)]
    fn variants(&self) -> Result<&[StoredSlice]> {
        let head = self.header()?;
        self.get_slice(head.variants_start as usize, head.variants_count as usize)
    }

    #[inline(always)]
    fn get_index(&self, uuid: &Uuid) -> Result<Option<&[IndexItem]>> {
        let uuids = self.uuids()?;
        for (idx, item_uuid) in uuids.iter().enumerate() {
            if item_uuid == uuid {
                let variant_slice = &self.variants()?[idx];
                unsafe {
                    let data = self.get_data(variant_slice.offset as usize,
                                             variant_slice.length as usize)?;
                    let count = variant_slice.length as usize / mem::size_of::<IndexItem>();
                    return Ok(Some(slice::from_raw_parts(
                        mem::transmute(data.as_ptr()),
                        count
                    )));
                }
            }
        }
        Ok(None)
    }

    #[inline(always)]
    fn symbols(&self) -> Result<&[StoredSlice]> {
        let head = self.header()?;
        self.get_slice(head.symbols_start as usize, head.symbols_count as usize)
    }

    #[inline(always)]
    fn object_names(&self) -> Result<&[StoredSlice]> {
        let head = self.header()?;
        self.get_slice(head.object_names_start as usize, head.object_names_count as usize)
    }

    #[inline(always)]
    fn get_string(&self, slice: &StoredSlice) -> Result<&str> {
        let bytes = self.get_data(slice.offset as usize, slice.length as usize)?;
        Ok(from_utf8(bytes)?)
    }

    pub fn lookup_by_uuid(&'a self, uuid: &Uuid, addr: u64)
        -> Result<Option<Symbol<'a>>>
    {
        let index = match self.get_index(uuid)? {
            Some(idx) => idx,
            None => { return Ok(None); }
        };
        let mut low = 0;
        let mut high = index.len();

        while low < high {
            let mid = (low + high) / 2;
            let ii = &index[mid as usize];
            if addr < ii.addr() {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        if low > 0 && low <= index.len() {
            Ok(Some(self.index_item_to_symbol(&index[low - 1])?))
        } else {
            Ok(None)
        }
    }

    /*
    pub fn lookup_by_object_name<'a>(&'a self, object_name: &str, arch: &str, addr: u64)
        -> Option<Symbol<'a>>
    {
        self.find_uuid(object_name, arch).and_then(|uuid| {
            self.lookup_by_uuid(uuid, addr)
        })
    }
    */

    fn get_object_name(&'a self, src_id: u16) -> Result<&'a str> {
        self.get_string(&self.object_names()?[src_id as usize])
    }

    fn get_symbol(&'a self, sym_id: u32) -> Result<&'a str> {
        self.get_string(&self.symbols()?[sym_id as usize])
    }

    fn index_item_to_symbol(&'a self, ii: &IndexItem) -> Result<Symbol<'a>> {
        Ok(Symbol {
            object_name: self.get_object_name(ii.src_id)?,
            symbol: self.get_symbol(ii.sym_id)?,
            addr: ii.addr(),
        })
    }
}
<|endoftext|>


<commit_before>/// Model represents the global states needed in FZF.
/// It will also define how the states will be shown on the terminal

use std::sync::{Arc, RwLock};
use item::{Item, MatchedItem, MatchedRange};
use ncurses::*;
use std::cmp;
use std::cell::RefCell;
use std::collections::HashSet;
use orderedvec::OrderedVec;
use curses::*;

pub struct Model {
    pub query: String,
    query_cursor: i32,  // > qu<query_cursor>ery
    num_matched: u64,
    num_total: u64,
    pub items: Arc<RwLock<Vec<Item>>>, // all items
    selected_indics: HashSet<usize>,
    pub matched_items: RefCell<OrderedVec<MatchedItem>>,
    processed_percentage: u64,
    item_cursor: usize, // the index of matched item currently highlighted.
    line_cursor: usize, // line No.
    item_start_pos: usize, // for screen scroll.
    max_y: i32,
    max_x: i32,
    curses: Curses,
}

impl Model {
    pub fn new(curses: Curses) -> Self {
        let (max_y, max_x) = curses.get_maxyx();

        Model {
            query: String::new(),
            query_cursor: 0,
            num_matched: 0,
            num_total: 0,
            items: Arc::new(RwLock::new(Vec::new())),
            selected_indics: HashSet::new(),
            matched_items: RefCell::new(OrderedVec::new()),
            processed_percentage: 100,
            item_cursor: 0,
            line_cursor: (max_y - 3) as usize,
            item_start_pos: 0,
            max_y: max_y,
            max_x: max_x,
            curses: curses,
        }
    }

    pub fn output(&self) {
        let mut selected = self.selected_indics.iter().collect::<Vec<&usize>>();
        selected.sort();
        let items = self.items.read().unwrap();
        for index in selected {
            println!("{}", items[*index].text);
        }
    }

    pub fn toggle_select(&mut self, selected: Option<bool>) {
        let mut matched_items = self.matched_items.borrow_mut();
        let matched = matched_items.get(self.item_cursor);
        if matched == None {
            return;
        }

        let index = matched.unwrap().index;
        match selected {
            Some(true) => {
                let _ = self.selected_indics.insert(index);
            }
            Some(false) => {
                let _ = self.selected_indics.remove(&index);
            }
            None => {
                if self.selected_indics.contains(&index) {
                    let _ = self.selected_indics.remove(&index);
                } else {
                    let _ = self.selected_indics.insert(index);
                }
            }
        }
    }

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

    pub fn update_query(&mut self, query: String, cursor: i32) {
        self.query = query;
        self.query_cursor = cursor;
    }

    pub fn update_process_info(&mut self, matched: u64, total: u64, processed: u64) {
        self.num_matched = matched;
        self.num_total = total;
        self.processed_percentage = (processed+1)*100/(total+1);
    }

    pub fn push_item(&mut self, item: MatchedItem) {
        self.matched_items.borrow_mut().push(item);
    }

    pub fn clear_items(&mut self) {
        self.matched_items.borrow_mut().clear();
    }

    pub fn move_line_cursor(&mut self, diff: i32) {

        let y = self.line_cursor as i32 + diff;
        let item_y = cmp::max(0, self.item_cursor as i32 - diff);
        let screen_height = (self.max_y - 3) as usize;

        match y {
            y if y < 0 => {
                self.line_cursor = 0;
                self.item_cursor = cmp::min(item_y as usize, self.matched_items.borrow().len()-1);
                self.item_start_pos = self.item_cursor - screen_height;
            }

            y if y > screen_height as i32 => {
                self.line_cursor = screen_height;
                self.item_cursor = cmp::max(0, item_y as usize);
                self.item_start_pos = self.item_cursor;
            }

            y => {
                self.line_cursor = y as usize;
                self.item_cursor = item_y as usize;
            }
        }
    }

    pub fn print_query(&self) {
        // > query
        mv(self.max_y-1, 0);
        addstr("> ");
        addstr(&self.query);
        mv(self.max_y-1, self.query_cursor+2);
    }

    pub fn print_info(&self) {
        mv(self.max_y-2, 0);
        addstr(format!("  {}/{}{}", self.num_matched, self.num_total,
                       if self.processed_percentage == 100 {"".to_string()} else {format!("({}%)", self.processed_percentage)}
                       ).as_str());
    }

    fn print_item(&self, matched: &MatchedItem, is_current: bool) {
        let items = self.items.read().unwrap();
        let ref item = items[matched.index];

        let is_selected = self.selected_indics.contains(&matched.index);

        if is_selected {
            self.curses.cprint(COLOR_SELECTED, true, ">");
        } else {
            self.curses.cprint(if is_current {COLOR_CURRENT} else {COLOR_NORMAL}, false, " ");
        }

        match matched.matched_range {
            Some(MatchedRange::Chars(ref matched_indics)) => {
                let mut matched_indics_iter = matched_indics.iter().peekable();
                for (idx, ch) in item.text.chars().enumerate() {
                    if let Some(&&index) = matched_indics_iter.peek() {
                        if idx == index {
                            self.curses.caddch(COLOR_MATCHED, is_current, ch);
                            let _ = matched_indics_iter.next();
                        } else {
                            self.curses.caddch(if is_current {COLOR_CURRENT} else {COLOR_NORMAL}, is_current, ch)
                        }
                    } else {
                        self.curses.caddch(if is_current {COLOR_CURRENT} else {COLOR_NORMAL}, is_current, ch)
                    }
                    if idx >= (self.max_x - 3) as usize {
                        break;
                    }
                }
            }
            Some(MatchedRange::Range(_, _)) => {
                // pass
            }
            None => {
                // pass
            }
        }

    }

    pub fn print_items(&self) {
        let mut matched_items = self.matched_items.borrow_mut();

        let mut y = self.max_y - 3;
        let mut i = self.item_start_pos;
        while let Some(matched) = matched_items.get(i) {
            i+=1;

            mv(y, 0);
            let is_current_line = y == self.line_cursor as i32;

            let mut label = if is_current_line {">"} else {" "};

            self.curses.cprint(COLOR_CURSOR, true, label);

            self.print_item(matched, is_current_line);

            y -= 1;
            if y < 0 {
                break;
            }
        }
    }

    pub fn refresh(&self) {
        refresh();
    }

    pub fn display(&self) {
        erase();
        self.print_items();
        self.print_info();
        self.print_query();
    }

    // the terminal resizes, so we need to recalculate the margins.
    pub fn resize(&mut self) {
        clear();
        endwin();
        refresh();
        let mut max_y = 0;
        let mut max_x = 0;
        getmaxyx(stdscr, &mut max_y, &mut max_x);
        self.max_y = max_y;
        self.max_x = max_x;
    }
}
<commit_msg>[model] horizontal scroll<commit_after>/// Model represents the global states needed in FZF.
/// It will also define how the states will be shown on the terminal

use std::sync::{Arc, RwLock};
use item::{Item, MatchedItem, MatchedRange};
use ncurses::*;
use std::cmp::{min, max};
use std::cell::RefCell;
use std::collections::HashSet;
use orderedvec::OrderedVec;
use curses::*;

pub struct Model {
    pub query: String,
    query_cursor: i32,  // > qu<query_cursor>ery

    num_matched: u64,
    num_total: u64,
    pub items: Arc<RwLock<Vec<Item>>>, // all items
    selected_indics: HashSet<usize>,
    pub matched_items: RefCell<OrderedVec<MatchedItem>>,
    processed_percentage: u64,

    item_cursor: usize, // the index of matched item currently highlighted.
    line_cursor: usize, // line No.
    item_start_pos: usize, // for screen scroll.
    max_y: i32,
    max_x: i32,

    curses: Curses,
}

impl Model {
    pub fn new(curses: Curses) -> Self {
        let (max_y, max_x) = curses.get_maxyx();

        Model {
            query: String::new(),
            query_cursor: 0,
            num_matched: 0,
            num_total: 0,
            items: Arc::new(RwLock::new(Vec::new())),
            selected_indics: HashSet::new(),
            matched_items: RefCell::new(OrderedVec::new()),
            processed_percentage: 100,
            item_cursor: 0,
            line_cursor: (max_y - 3) as usize,
            item_start_pos: 0,
            max_y: max_y,
            max_x: max_x,
            curses: curses,
        }
    }

    pub fn output(&self) {
        let mut selected = self.selected_indics.iter().collect::<Vec<&usize>>();
        selected.sort();
        let items = self.items.read().unwrap();
        for index in selected {
            println!("{}", items[*index].text);
        }
    }

    pub fn toggle_select(&mut self, selected: Option<bool>) {
        let mut matched_items = self.matched_items.borrow_mut();
        let matched = matched_items.get(self.item_cursor);
        if matched == None {
            return;
        }

        let index = matched.unwrap().index;
        match selected {
            Some(true) => {
                let _ = self.selected_indics.insert(index);
            }
            Some(false) => {
                let _ = self.selected_indics.remove(&index);
            }
            None => {
                if self.selected_indics.contains(&index) {
                    let _ = self.selected_indics.remove(&index);
                } else {
                    let _ = self.selected_indics.insert(index);
                }
            }
        }
    }

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

    pub fn update_query(&mut self, query: String, cursor: i32) {
        self.query = query;
        self.query_cursor = cursor;
    }

    pub fn update_process_info(&mut self, matched: u64, total: u64, processed: u64) {
        self.num_matched = matched;
        self.num_total = total;
        self.processed_percentage = (processed+1)*100/(total+1);
    }

    pub fn push_item(&mut self, item: MatchedItem) {
        self.matched_items.borrow_mut().push(item);
    }

    pub fn clear_items(&mut self) {
        self.matched_items.borrow_mut().clear();
    }

    pub fn move_line_cursor(&mut self, diff: i32) {

        let y = self.line_cursor as i32 + diff;
        let item_y = max(0, self.item_cursor as i32 - diff);
        let screen_height = (self.max_y - 3) as usize;

        match y {
            y if y < 0 => {
                self.line_cursor = 0;
                self.item_cursor = min(item_y as usize, self.matched_items.borrow().len()-1);
                self.item_start_pos = self.item_cursor - screen_height;
            }

            y if y > screen_height as i32 => {
                self.line_cursor = screen_height;
                self.item_cursor = max(0, item_y as usize);
                self.item_start_pos = self.item_cursor;
            }

            y => {
                self.line_cursor = y as usize;
                self.item_cursor = item_y as usize;
            }
        }
    }

    pub fn print_query(&self) {
        // > query
        mv(self.max_y-1, 0);
        addstr("> ");
        addstr(&self.query);
        mv(self.max_y-1, self.query_cursor+2);
    }

    pub fn print_info(&self) {
        mv(self.max_y-2, 0);
        addstr(format!("  {}/{}{}", self.num_matched, self.num_total,
                       if self.processed_percentage == 100 {"".to_string()} else {format!("({}%)", self.processed_percentage)}
                       ).as_str());
    }

    fn print_item(&self, matched: &MatchedItem, is_current: bool) {
        let items = self.items.read().unwrap();
        let ref item = items[matched.index];

        let is_selected = self.selected_indics.contains(&matched.index);

        if is_selected {
            self.curses.cprint(COLOR_SELECTED, true, ">");
        } else {
            self.curses.cprint(if is_current {COLOR_CURRENT} else {COLOR_NORMAL}, false, " ");
        }

        match matched.matched_range {
            Some(MatchedRange::Chars(ref matched_indics)) => {
                let matched_end_pos = if matched_indics.len() > 0 {matched_indics[matched_indics.len()-1]} else {item.text.len()-1};
                let (text, mut idx) = reshape_string(&item.text.chars().collect::<Vec<char>>(), (self.max_x-3) as usize, 0, matched_end_pos);
                let mut matched_indics_iter = matched_indics.iter().peekable();

                // skip indics
                while let Some(&&index) = matched_indics_iter.peek() {
                    if idx > index {
                        let _ = matched_indics_iter.next();
                    } else {
                        break;
                    }
                }

                for &ch in text.iter() {
                    if let Some(&&index) = matched_indics_iter.peek() {
                        if idx == index {
                            self.curses.caddch(COLOR_MATCHED, is_current, ch);
                            let _ = matched_indics_iter.next();
                        } else {
                            self.curses.caddch(if is_current {COLOR_CURRENT} else {COLOR_NORMAL}, is_current, ch)
                        }
                    } else {
                        self.curses.caddch(if is_current {COLOR_CURRENT} else {COLOR_NORMAL}, is_current, ch)
                    }
                    idx += 1;
                }
            }
            Some(MatchedRange::Range(_, _)) => {
                // pass
            }
            None => {
                // pass
            }
        }

    }

    pub fn print_items(&self) {
        let mut matched_items = self.matched_items.borrow_mut();

        let mut y = self.max_y - 3;
        let mut i = self.item_start_pos;
        while let Some(matched) = matched_items.get(i) {
            i+=1;

            mv(y, 0);
            let is_current_line = y == self.line_cursor as i32;

            let mut label = if is_current_line {">"} else {" "};

            self.curses.cprint(COLOR_CURSOR, true, label);

            self.print_item(matched, is_current_line);

            y -= 1;
            if y < 0 {
                break;
            }
        }
    }

    pub fn refresh(&self) {
        refresh();
    }

    pub fn display(&self) {
        erase();
        self.print_items();
        self.print_info();
        self.print_query();
    }

    // the terminal resizes, so we need to recalculate the margins.
    pub fn resize(&mut self) {
        clear();
        endwin();
        refresh();
        let mut max_y = 0;
        let mut max_x = 0;
        getmaxyx(stdscr, &mut max_y, &mut max_x);
        self.max_y = max_y;
        self.max_x = max_x;
    }
}

// helper functions

// wide character will take two unit
fn display_width(text: &[char]) -> usize {
    text.iter()
        .map(|c| {if c.len_utf8() > 1 {2} else {1}})
        .fold(0, |acc, n| acc + n)
}

// calculate from left to right, stop when the width exceeds
fn left_fixed(text: &[char], width: usize) -> usize {
    if width <= 0 {
        return 0;
    }

    let mut w = 0;
    for (idx, &c) in text.iter().enumerate() {
        w += if c.len_utf8() > 1 {2} else {1};
        if w > width {
            return idx-1;
        }
    }
    return text.len()-1;
}

fn right_fixed(text: &[char], width: usize) -> usize {
    if width <= 0 {
        return text.len()-1;
    }

    let mut w = 0;
    for (idx, &c) in text.iter().enumerate().rev() {
        w += if c.len_utf8() > 1 {2} else {1};
        if w > width {
            return idx+1;
        }
    }
    return 0;

}

// return a string and its left position in original string
// matched_end_pos is char-wise
fn reshape_string(text: &Vec<char>,
                  container_width: usize,
                  text_start_pos: usize,
                  matched_end_pos: usize) -> (Vec<char>, usize) {
    let full_width = display_width(&text[text_start_pos..]);

    if full_width <= container_width {
        return (text[text_start_pos..].iter().map(|x| *x).collect(), text_start_pos);
    }

    let mut ret = Vec::new();
    let mut ret_pos = 0;

    // trim right, so that 'String' -> 'Str..'
    let right_pos = 1 + max(matched_end_pos, text_start_pos + left_fixed(&text[text_start_pos..], container_width-2));
    let mut left_pos = text_start_pos + right_fixed(&text[text_start_pos..right_pos], container_width-2);
    ret_pos = left_pos;

    if left_pos > text_start_pos {
        left_pos = text_start_pos + right_fixed(&text[text_start_pos..right_pos], container_width-4);
        ret.push('.'); ret.push('.');
        ret_pos = left_pos - 2;
    }

    // so we should print [left_pos..(right_pos+1)]
    for ch in text[left_pos..right_pos].iter() {
        ret.push(*ch);
    }
    ret.push('.'); ret.push('.');
    (ret, ret_pos)
}

#[cfg(test)]
mod test {
    #[test]
    fn test_display_width() {
        assert_eq!(super::display_width(&"abcdefg".to_string().chars().collect::<Vec<char>>()), 7);
        assert_eq!(super::display_width(&"This is 中国".to_string().chars().collect::<Vec<char>>()), 12);
    }

    #[test]
    fn test_left_fixed() {
        assert_eq!(super::left_fixed(&"a中cdef".to_string().chars().collect::<Vec<char>>(), 5), 3);
        assert_eq!(super::left_fixed(&"a中".to_string().chars().collect::<Vec<char>>(), 5), 1);
        assert_eq!(super::left_fixed(&"a中".to_string().chars().collect::<Vec<char>>(), 0), 0);
    }

    #[test]
    fn test_right_fixed() {
        assert_eq!(super::right_fixed(&"a中cdef".to_string().chars().collect::<Vec<char>>(), 5), 2);
        assert_eq!(super::right_fixed(&"a中".to_string().chars().collect::<Vec<char>>(), 5), 0);
        assert_eq!(super::right_fixed(&"a中".to_string().chars().collect::<Vec<char>>(), 0), 1);
    }

    #[test]
    fn test_reshape_string() {
        assert_eq!(super::reshape_string(&"0123456789".to_string().chars().collect::<Vec<char>>(),
                                         6, 1, 7),
                   ("..67..".to_string().chars().collect::<Vec<char>>(), 4));

        assert_eq!(super::reshape_string(&"0123456789".to_string().chars().collect::<Vec<char>>(),
                                         12, 1, 7),
                   ("123456789".to_string().chars().collect::<Vec<char>>(), 1));

        assert_eq!(super::reshape_string(&"0123456789".to_string().chars().collect::<Vec<char>>(),
                                         6, 0, 6),
                   ("..56..".to_string().chars().collect::<Vec<char>>(), 3));

        assert_eq!(super::reshape_string(&"0123456789".to_string().chars().collect::<Vec<char>>(),
                                         8, 0, 4),
                   ("012345..".to_string().chars().collect::<Vec<char>>(), 0));

        assert_eq!(super::reshape_string(&"0123456789".to_string().chars().collect::<Vec<char>>(),
                                         10, 0, 4),
                   ("0123456789".to_string().chars().collect::<Vec<char>>(), 0));
    }

}
<|endoftext|>