<commit_before>//! The types modules describes all the structures to express FITS files.

use std::str::FromStr;
use std::fmt::{Display, Formatter, Error};

/// Representation of a FITS file.
#[derive(Debug, PartialEq)]
pub struct Fits<'a> {
    /// The primary header
    pub primary_header: PrimaryHeader<'a>
}

impl<'a> Fits<'a> {
    /// Create a Fits structure with a given primary header
    pub fn new(primary_header: PrimaryHeader<'a>) -> Fits<'a> {
        Fits { primary_header: primary_header }
    }
}

/// The primary header of a FITS file.
#[derive(Debug, PartialEq)]
pub struct PrimaryHeader<'a> {
    /// The keyword records of the primary header.
    pub keyword_records: Vec<KeywordRecord<'a>>,
}

impl<'a> PrimaryHeader<'a> {
    /// Create a PrimaryHeader with a given set of keyword_records
    pub fn new(keyword_records: Vec<KeywordRecord<'a>>) -> PrimaryHeader<'a> {
        PrimaryHeader { keyword_records: keyword_records }
    }
}

/// A keyword record contains information about a FITS header. It consists of a
/// keyword, the corresponding value and an optional comment.
#[derive(Debug, PartialEq)]
pub struct KeywordRecord<'a> {
    /// The keyword of this record.
    keyword: Keyword,
    /// The value of this record.
    value: &'a str,
    /// The comment of this record.
    comment: Option<&'a str>
}

impl<'a> KeywordRecord<'a> {
    /// Create a `KeywordRecord` from a specific `Keyword`.
    pub fn new(keyword: Keyword, value: &'a str, comment: Option<&'a str>) -> KeywordRecord<'a> {
        KeywordRecord { keyword: keyword, value: value, comment: comment }
    }
}

impl<'a> Display for KeywordRecord<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "{:?}= {}/{}", self.keyword, self.value, self.comment.unwrap_or(""))
    }
}

/// A unit struct that will act as a placeholder for blank records.
#[derive(Debug, PartialEq)]
pub struct BlankRecord;

/// The various keywords that can be found in headers.
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types, missing_docs)]
pub enum Keyword {
    SIMPLE,
    BITPIX,
    NAXIS,
    NAXISn(u8),
    EXTEND,
    NEXTEND,
    EXTNAME,
    EXTVER,
    ORIGIN,
    DATE,
    CREATOR,
    PROCVER,
    FILEVER,
    TIMVERSN,
    TELESCOP,
    INSTRUME,
    OBJECT,
    KEPLERID,
    CHANNEL,
    MODULE,
    OUTPUT,
    CAMPAIGN,
    DATA_REL,
    OBSMODE,
    MISSION,
    TTABLEID,
    RADESYS,
    RA_OBJ,
    DEC_OBJ,
    EQUINOX,
    PMRA,
    PMDEC,
    PMTOTAL,
    PARALLAX,
    GLON,
    GLAT,
    GMAG,
    RMAG,
    IMAG,
    ZMAG,
    JMAG,
    HMAG,
    KMAG,
    KEPMAG,
    GRCOLOR,
    JKCOLOR,
    GKCOLOR,
    TEFF,
    LOGG,
    FEH,
    EBMINUSV,
    AV,
    RADIUS,
    TMINDEX,
    CHECKSUM,
    DATASUM,
    END,
}

/// Problems that could occur when parsing a `str` are enumerated here.
#[derive(Debug)]
pub enum ParseKeywordError {
    /// When a str can not be recognized as a keyword, this error will be returned
    UnknownKeyword
}

impl FromStr for Keyword {
    type Err = ParseKeywordError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO parse NAXIS1 correctly
        match s.trim_right() {
            "SIMPLE" => Ok(Keyword::SIMPLE),
            "BITPIX" => Ok(Keyword::BITPIX),
            "NAXIS" => Ok(Keyword::NAXIS),
            "EXTEND" => Ok(Keyword::EXTEND),
            "NEXTEND" => Ok(Keyword::NEXTEND),
            "EXTNAME" => Ok(Keyword::EXTNAME),
            "EXTVER" => Ok(Keyword::EXTVER),
            "ORIGIN" => Ok(Keyword::ORIGIN),
            "DATE" => Ok(Keyword::DATE),
            "CREATOR" => Ok(Keyword::CREATOR),
            "PROCVER" => Ok(Keyword::PROCVER),
            "FILEVER" => Ok(Keyword::FILEVER),
            "TIMVERSN" => Ok(Keyword::TIMVERSN),
            "TELESCOP" => Ok(Keyword::TELESCOP),
            "INSTRUME" => Ok(Keyword::INSTRUME),
            "OBJECT" => Ok(Keyword::OBJECT),
            "KEPLERID" => Ok(Keyword::KEPLERID),
            "CHANNEL" => Ok(Keyword::CHANNEL),
            "MODULE" => Ok(Keyword::MODULE),
            "OUTPUT" => Ok(Keyword::OUTPUT),
            "CAMPAIGN" => Ok(Keyword::CAMPAIGN),
            "DATA_REL" => Ok(Keyword::DATA_REL),
            "OBSMODE" => Ok(Keyword::OBSMODE),
            "MISSION" => Ok(Keyword::MISSION),
            "TTABLEID" => Ok(Keyword::TTABLEID),
            "RADESYS" => Ok(Keyword::RADESYS),
            "RA_OBJ" => Ok(Keyword::RA_OBJ),
            "DEC_OBJ" => Ok(Keyword::DEC_OBJ),
            "EQUINOX" => Ok(Keyword::EQUINOX),
            "PMRA" => Ok(Keyword::PMRA),
            "PMDEC" => Ok(Keyword::PMDEC),
            "PMTOTAL" => Ok(Keyword::PMTOTAL),
            "PARALLAX" => Ok(Keyword::PARALLAX),
            "GLON" => Ok(Keyword::GLON),
            "GLAT" => Ok(Keyword::GLAT),
            "GMAG" => Ok(Keyword::GMAG),
            "RMAG" => Ok(Keyword::RMAG),
            "IMAG" => Ok(Keyword::IMAG),
            "ZMAG" => Ok(Keyword::ZMAG),
            "JMAG" => Ok(Keyword::JMAG),
            "HMAG" => Ok(Keyword::HMAG),
            "KMAG" => Ok(Keyword::KMAG),
            "KEPMAG" => Ok(Keyword::KEPMAG),
            "GRCOLOR" => Ok(Keyword::GRCOLOR),
            "JKCOLOR" => Ok(Keyword::JKCOLOR),
            "GKCOLOR" => Ok(Keyword::GKCOLOR),
            "TEFF" => Ok(Keyword::TEFF),
            "LOGG" => Ok(Keyword::LOGG),
            "FEH" => Ok(Keyword::FEH),
            "EBMINUSV" => Ok(Keyword::EBMINUSV),
            "AV" => Ok(Keyword::AV),
            "RADIUS" => Ok(Keyword::RADIUS),
            "TMINDEX" => Ok(Keyword::TMINDEX),
            "CHECKSUM" => Ok(Keyword::CHECKSUM),
            "DATASUM" => Ok(Keyword::DATASUM),
            "END" => Ok(Keyword::END),
            _ => Err(ParseKeywordError::UnknownKeyword)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;
    use super::*;

    #[test]
    fn fits_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            Fits { primary_header: PrimaryHeader::new(vec!()) },
            Fits::new(PrimaryHeader::new(vec!()))
        );
    }

    #[test]
    fn primary_header_constructed_from_the_new_function_shoul_eq_hand_construction() {
        assert_eq!(
            PrimaryHeader { keyword_records: vec!(
                KeywordRecord::new(Keyword::SIMPLE, "T", Option::None),
                KeywordRecord::new(Keyword::NEXTEND, "0", Option::Some("no extensions")),
            )},
            PrimaryHeader::new(vec!(
                KeywordRecord::new(Keyword::SIMPLE, "T", Option::None),
                KeywordRecord::new(Keyword::NEXTEND, "0", Option::Some("no extensions")),
            ))
        );
    }

    #[test]
    fn keyword_record_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            KeywordRecord { keyword: Keyword::ORIGIN, value: "", comment: Option::None },
            KeywordRecord::new(Keyword::ORIGIN, "", Option::None));
    }

    #[test]
    fn keywords_could_be_constructed_from_str() {
        let data = vec!(
            ("SIMPLE", Keyword::SIMPLE),
            ("BITPIX", Keyword::BITPIX),
            ("NAXIS", Keyword::NAXIS),
            ("EXTEND", Keyword::EXTEND),
            ("NEXTEND", Keyword::NEXTEND),
            ("EXTVER", Keyword::EXTVER),
            ("ORIGIN", Keyword::ORIGIN),
            ("DATE", Keyword::DATE),
            ("CREATOR", Keyword::CREATOR),
            ("PROCVER", Keyword::PROCVER),
            ("FILEVER", Keyword::FILEVER),
            ("TIMVERSN", Keyword::TIMVERSN),
            ("TELESCOP", Keyword::TELESCOP),
            ("INSTRUME", Keyword::INSTRUME),
            ("OBJECT", Keyword::OBJECT),
            ("KEPLERID", Keyword::KEPLERID),
            ("CHANNEL", Keyword::CHANNEL),
            ("MODULE", Keyword::MODULE),
            ("OUTPUT", Keyword::OUTPUT),
            ("CAMPAIGN", Keyword::CAMPAIGN),
            ("DATA_REL", Keyword::DATA_REL),
            ("OBSMODE", Keyword::OBSMODE),
            ("MISSION", Keyword::MISSION),
            ("TTABLEID", Keyword::TTABLEID),
            ("RADESYS", Keyword::RADESYS),
            ("RA_OBJ", Keyword::RA_OBJ),
            ("DEC_OBJ", Keyword::DEC_OBJ),
            ("EQUINOX", Keyword::EQUINOX),
            ("PMRA", Keyword::PMRA),
            ("PMDEC", Keyword::PMDEC),
            ("PMTOTAL", Keyword::PMTOTAL),
            ("PARALLAX", Keyword::PARALLAX),
            ("GLON", Keyword::GLON),
            ("GLAT", Keyword::GLAT),
            ("GMAG", Keyword::GMAG),
            ("RMAG", Keyword::RMAG),
            ("IMAG", Keyword::IMAG),
            ("ZMAG", Keyword::ZMAG),
            ("JMAG", Keyword::JMAG),
            ("HMAG", Keyword::HMAG),
            ("KMAG", Keyword::KMAG),
            ("KEPMAG", Keyword::KEPMAG),
            ("GRCOLOR", Keyword::GRCOLOR),
            ("JKCOLOR", Keyword::JKCOLOR),
            ("GKCOLOR", Keyword::GKCOLOR),
            ("TEFF", Keyword::TEFF),
            ("LOGG", Keyword::LOGG),
            ("FEH", Keyword::FEH),
            ("EBMINUSV", Keyword::EBMINUSV),
            ("AV", Keyword::AV),
            ("RADIUS", Keyword::RADIUS),
            ("TMINDEX", Keyword::TMINDEX),
            ("CHECKSUM", Keyword::CHECKSUM),
            ("DATASUM", Keyword::DATASUM),
            ("END", Keyword::END),
        );

        for (input, expected) in data {
            assert_eq!(Keyword::from_str(input).unwrap(), expected);
        }
    }

    #[test]
    fn should_also_parse_whitespace_keywords() {
        assert_eq!(Keyword::from_str("SIMPLE  ").unwrap(), Keyword::SIMPLE);
    }
}
<commit_msg>Create KeywordRecord Value type<commit_after>//! The types modules describes all the structures to express FITS files.

use std::str::FromStr;
use std::fmt::{Display, Formatter, Error};

/// Representation of a FITS file.
#[derive(Debug, PartialEq)]
pub struct Fits<'a> {
    /// The primary header
    pub primary_header: PrimaryHeader<'a>
}

impl<'a> Fits<'a> {
    /// Create a Fits structure with a given primary header
    pub fn new(primary_header: PrimaryHeader<'a>) -> Fits<'a> {
        Fits { primary_header: primary_header }
    }
}

/// The primary header of a FITS file.
#[derive(Debug, PartialEq)]
pub struct PrimaryHeader<'a> {
    /// The keyword records of the primary header.
    pub keyword_records: Vec<KeywordRecord<'a>>,
}

impl<'a> PrimaryHeader<'a> {
    /// Create a PrimaryHeader with a given set of keyword_records
    pub fn new(keyword_records: Vec<KeywordRecord<'a>>) -> PrimaryHeader<'a> {
        PrimaryHeader { keyword_records: keyword_records }
    }
}

/// A keyword record contains information about a FITS header. It consists of a
/// keyword, the corresponding value and an optional comment.
#[derive(Debug, PartialEq)]
pub struct KeywordRecord<'a> {
    /// The keyword of this record.
    keyword: Keyword,
    /// The value of this record.
    value: &'a str,
    /// The comment of this record.
    comment: Option<&'a str>
}

impl<'a> KeywordRecord<'a> {
    /// Create a `KeywordRecord` from a specific `Keyword`.
    pub fn new(keyword: Keyword, value: &'a str, comment: Option<&'a str>) -> KeywordRecord<'a> {
        KeywordRecord { keyword: keyword, value: value, comment: comment }
    }
}

impl<'a> Display for KeywordRecord<'a> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "{:?}= {}/{}", self.keyword, self.value, self.comment.unwrap_or(""))
    }
}

/// The possible values of a KeywordRecord.
pub enum Value<'a> {
    /// A string enclosed in single quotes `'`.
    CharacterString(&'a str),
    /// A logical constant signified by either an uppercase `F` or an uppercase `T`.
    Logical(bool),
    /// An optionally signed decimal integer.
    Integer(i64),
    /// Fixed format real floating point number.
    Real(f64),
    /// Complex number represented by a real an imaginary component.
    Complex((f64, f64)),
    /// Not yet correctly parsed value // TODO Remove this value as soon as possible.
    Raw(&'a str),
}

/// A unit struct that will act as a placeholder for blank records.
#[derive(Debug, PartialEq)]
pub struct BlankRecord;

/// The various keywords that can be found in headers.
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types, missing_docs)]
pub enum Keyword {
    SIMPLE,
    BITPIX,
    NAXIS,
    NAXISn(u8),
    EXTEND,
    NEXTEND,
    EXTNAME,
    EXTVER,
    ORIGIN,
    DATE,
    CREATOR,
    PROCVER,
    FILEVER,
    TIMVERSN,
    TELESCOP,
    INSTRUME,
    OBJECT,
    KEPLERID,
    CHANNEL,
    MODULE,
    OUTPUT,
    CAMPAIGN,
    DATA_REL,
    OBSMODE,
    MISSION,
    TTABLEID,
    RADESYS,
    RA_OBJ,
    DEC_OBJ,
    EQUINOX,
    PMRA,
    PMDEC,
    PMTOTAL,
    PARALLAX,
    GLON,
    GLAT,
    GMAG,
    RMAG,
    IMAG,
    ZMAG,
    JMAG,
    HMAG,
    KMAG,
    KEPMAG,
    GRCOLOR,
    JKCOLOR,
    GKCOLOR,
    TEFF,
    LOGG,
    FEH,
    EBMINUSV,
    AV,
    RADIUS,
    TMINDEX,
    CHECKSUM,
    DATASUM,
    END,
}

/// Problems that could occur when parsing a `str` are enumerated here.
#[derive(Debug)]
pub enum ParseKeywordError {
    /// When a str can not be recognized as a keyword, this error will be returned
    UnknownKeyword
}

impl FromStr for Keyword {
    type Err = ParseKeywordError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO parse NAXIS1 correctly
        match s.trim_right() {
            "SIMPLE" => Ok(Keyword::SIMPLE),
            "BITPIX" => Ok(Keyword::BITPIX),
            "NAXIS" => Ok(Keyword::NAXIS),
            "EXTEND" => Ok(Keyword::EXTEND),
            "NEXTEND" => Ok(Keyword::NEXTEND),
            "EXTNAME" => Ok(Keyword::EXTNAME),
            "EXTVER" => Ok(Keyword::EXTVER),
            "ORIGIN" => Ok(Keyword::ORIGIN),
            "DATE" => Ok(Keyword::DATE),
            "CREATOR" => Ok(Keyword::CREATOR),
            "PROCVER" => Ok(Keyword::PROCVER),
            "FILEVER" => Ok(Keyword::FILEVER),
            "TIMVERSN" => Ok(Keyword::TIMVERSN),
            "TELESCOP" => Ok(Keyword::TELESCOP),
            "INSTRUME" => Ok(Keyword::INSTRUME),
            "OBJECT" => Ok(Keyword::OBJECT),
            "KEPLERID" => Ok(Keyword::KEPLERID),
            "CHANNEL" => Ok(Keyword::CHANNEL),
            "MODULE" => Ok(Keyword::MODULE),
            "OUTPUT" => Ok(Keyword::OUTPUT),
            "CAMPAIGN" => Ok(Keyword::CAMPAIGN),
            "DATA_REL" => Ok(Keyword::DATA_REL),
            "OBSMODE" => Ok(Keyword::OBSMODE),
            "MISSION" => Ok(Keyword::MISSION),
            "TTABLEID" => Ok(Keyword::TTABLEID),
            "RADESYS" => Ok(Keyword::RADESYS),
            "RA_OBJ" => Ok(Keyword::RA_OBJ),
            "DEC_OBJ" => Ok(Keyword::DEC_OBJ),
            "EQUINOX" => Ok(Keyword::EQUINOX),
            "PMRA" => Ok(Keyword::PMRA),
            "PMDEC" => Ok(Keyword::PMDEC),
            "PMTOTAL" => Ok(Keyword::PMTOTAL),
            "PARALLAX" => Ok(Keyword::PARALLAX),
            "GLON" => Ok(Keyword::GLON),
            "GLAT" => Ok(Keyword::GLAT),
            "GMAG" => Ok(Keyword::GMAG),
            "RMAG" => Ok(Keyword::RMAG),
            "IMAG" => Ok(Keyword::IMAG),
            "ZMAG" => Ok(Keyword::ZMAG),
            "JMAG" => Ok(Keyword::JMAG),
            "HMAG" => Ok(Keyword::HMAG),
            "KMAG" => Ok(Keyword::KMAG),
            "KEPMAG" => Ok(Keyword::KEPMAG),
            "GRCOLOR" => Ok(Keyword::GRCOLOR),
            "JKCOLOR" => Ok(Keyword::JKCOLOR),
            "GKCOLOR" => Ok(Keyword::GKCOLOR),
            "TEFF" => Ok(Keyword::TEFF),
            "LOGG" => Ok(Keyword::LOGG),
            "FEH" => Ok(Keyword::FEH),
            "EBMINUSV" => Ok(Keyword::EBMINUSV),
            "AV" => Ok(Keyword::AV),
            "RADIUS" => Ok(Keyword::RADIUS),
            "TMINDEX" => Ok(Keyword::TMINDEX),
            "CHECKSUM" => Ok(Keyword::CHECKSUM),
            "DATASUM" => Ok(Keyword::DATASUM),
            "END" => Ok(Keyword::END),
            _ => Err(ParseKeywordError::UnknownKeyword)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;
    use super::*;

    #[test]
    fn fits_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            Fits { primary_header: PrimaryHeader::new(vec!()) },
            Fits::new(PrimaryHeader::new(vec!()))
        );
    }

    #[test]
    fn primary_header_constructed_from_the_new_function_shoul_eq_hand_construction() {
        assert_eq!(
            PrimaryHeader { keyword_records: vec!(
                KeywordRecord::new(Keyword::SIMPLE, "T", Option::None),
                KeywordRecord::new(Keyword::NEXTEND, "0", Option::Some("no extensions")),
            )},
            PrimaryHeader::new(vec!(
                KeywordRecord::new(Keyword::SIMPLE, "T", Option::None),
                KeywordRecord::new(Keyword::NEXTEND, "0", Option::Some("no extensions")),
            ))
        );
    }

    #[test]
    fn keyword_record_constructed_from_the_new_function_should_eq_hand_construction() {
        assert_eq!(
            KeywordRecord { keyword: Keyword::ORIGIN, value: "", comment: Option::None },
            KeywordRecord::new(Keyword::ORIGIN, "", Option::None));
    }

    #[test]
    fn keywords_could_be_constructed_from_str() {
        let data = vec!(
            ("SIMPLE", Keyword::SIMPLE),
            ("BITPIX", Keyword::BITPIX),
            ("NAXIS", Keyword::NAXIS),
            ("EXTEND", Keyword::EXTEND),
            ("NEXTEND", Keyword::NEXTEND),
            ("EXTVER", Keyword::EXTVER),
            ("ORIGIN", Keyword::ORIGIN),
            ("DATE", Keyword::DATE),
            ("CREATOR", Keyword::CREATOR),
            ("PROCVER", Keyword::PROCVER),
            ("FILEVER", Keyword::FILEVER),
            ("TIMVERSN", Keyword::TIMVERSN),
            ("TELESCOP", Keyword::TELESCOP),
            ("INSTRUME", Keyword::INSTRUME),
            ("OBJECT", Keyword::OBJECT),
            ("KEPLERID", Keyword::KEPLERID),
            ("CHANNEL", Keyword::CHANNEL),
            ("MODULE", Keyword::MODULE),
            ("OUTPUT", Keyword::OUTPUT),
            ("CAMPAIGN", Keyword::CAMPAIGN),
            ("DATA_REL", Keyword::DATA_REL),
            ("OBSMODE", Keyword::OBSMODE),
            ("MISSION", Keyword::MISSION),
            ("TTABLEID", Keyword::TTABLEID),
            ("RADESYS", Keyword::RADESYS),
            ("RA_OBJ", Keyword::RA_OBJ),
            ("DEC_OBJ", Keyword::DEC_OBJ),
            ("EQUINOX", Keyword::EQUINOX),
            ("PMRA", Keyword::PMRA),
            ("PMDEC", Keyword::PMDEC),
            ("PMTOTAL", Keyword::PMTOTAL),
            ("PARALLAX", Keyword::PARALLAX),
            ("GLON", Keyword::GLON),
            ("GLAT", Keyword::GLAT),
            ("GMAG", Keyword::GMAG),
            ("RMAG", Keyword::RMAG),
            ("IMAG", Keyword::IMAG),
            ("ZMAG", Keyword::ZMAG),
            ("JMAG", Keyword::JMAG),
            ("HMAG", Keyword::HMAG),
            ("KMAG", Keyword::KMAG),
            ("KEPMAG", Keyword::KEPMAG),
            ("GRCOLOR", Keyword::GRCOLOR),
            ("JKCOLOR", Keyword::JKCOLOR),
            ("GKCOLOR", Keyword::GKCOLOR),
            ("TEFF", Keyword::TEFF),
            ("LOGG", Keyword::LOGG),
            ("FEH", Keyword::FEH),
            ("EBMINUSV", Keyword::EBMINUSV),
            ("AV", Keyword::AV),
            ("RADIUS", Keyword::RADIUS),
            ("TMINDEX", Keyword::TMINDEX),
            ("CHECKSUM", Keyword::CHECKSUM),
            ("DATASUM", Keyword::DATASUM),
            ("END", Keyword::END),
        );

        for (input, expected) in data {
            assert_eq!(Keyword::from_str(input).unwrap(), expected);
        }
    }

    #[test]
    fn should_also_parse_whitespace_keywords() {
        assert_eq!(Keyword::from_str("SIMPLE  ").unwrap(), Keyword::SIMPLE);
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::error::ERRNO_NOT_POSITIVE;
use crate::util::LazyUsize;
use crate::Error;
use core::num::NonZeroU32;
use core::ptr::NonNull;

cfg_if! {
    if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
        use libc::__errno as errno_location;
    } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] {
        use libc::__errno_location as errno_location;
    } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] {
        use libc::___errno as errno_location;
    } else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] {
        use libc::__error as errno_location;
    } else if #[cfg(target_os = "haiku")] {
        use libc::_errnop as errno_location;
    }
}

cfg_if! {
    if #[cfg(target_os = "vxworks")] {
        use libc::errnoGet as get_errno;
    } else if #[cfg(target_os = "dragonfly")] {
        // Until rust-lang/rust#29594 is stable, we cannot get the errno value
        // on DragonFlyBSD. So we just return an out-of-range errno.
        unsafe fn get_errno() -> libc::c_int { -1 }
    } else {
        unsafe fn get_errno() -> libc::c_int { *errno_location() }
    }
}

pub fn last_os_error() -> Error {
    let errno = unsafe { get_errno() };
    if errno > 0 {
        Error::from(NonZeroU32::new(errno as u32).unwrap())
    } else {
        ERRNO_NOT_POSITIVE
    }
}

// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
//   - should return -1 and set errno on failure
//   - should return the number of bytes written on success
pub fn sys_fill_exact(
    mut buf: &mut [u8],
    sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
) -> Result<(), Error> {
    while !buf.is_empty() {
        let res = sys_fill(buf);
        if res < 0 {
            let err = last_os_error();
            // We should try again if the call was interrupted.
            if err.raw_os_error() != Some(libc::EINTR) {
                return Err(err);
            }
        } else {
            // We don't check for EOF (ret = 0) as the data we are reading
            // should be an infinite stream of random bytes.
            buf = &mut buf[(res as usize)..];
        }
    }
    Ok(())
}

// A "weak" binding to a C function that may or may not be present at runtime.
// Used for supporting newer OS features while still building on older systems.
// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the
// weak! macro in libstd.
pub struct Weak {
    name: &'static str,
    addr: LazyUsize,
}

impl Weak {
    // Construct a binding to a C function with a given name. This function is
    // unsafe because `name` _must_ be null terminated.
    pub const unsafe fn new(name: &'static str) -> Self {
        Self {
            name,
            addr: LazyUsize::new(),
        }
    }

    // Return a function pointer if present at runtime. Otherwise, return null.
    pub fn ptr(&self) -> Option<NonNull<libc::c_void>> {
        let addr = self.addr.unsync_init(|| unsafe {
            libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize
        });
        NonNull::new(addr as *mut _)
    }
}

pub struct LazyFd(LazyUsize);

impl LazyFd {
    pub const fn new() -> Self {
        Self(LazyUsize::new())
    }

    // If init() returns Some(x), x should be nonnegative.
    pub fn init(&self, init: impl FnOnce() -> Option<libc::c_int>) -> Option<libc::c_int> {
        let fd = self.0.sync_init(
            || match init() {
                // OK as val >= 0 and val <= c_int::MAX < usize::MAX
                Some(val) => val as usize,
                None => LazyUsize::UNINIT,
            },
            || unsafe {
                // We are usually waiting on an open(2) syscall to complete,
                // which typically takes < 10us if the file is a device.
                // However, we might end up waiting much longer if the entropy
                // pool isn't initialized, but even in that case, this loop will
                // consume a negligible amount of CPU on most platforms.
                libc::usleep(10);
            },
        );
        match fd {
            LazyUsize::UNINIT => None,
            val => Some(val as libc::c_int),
        }
    }
}

cfg_if! {
    if #[cfg(any(target_os = "linux", target_os = "emscripten"))] {
        use libc::open64 as open;
    } else {
        use libc::open;
    }
}

// SAFETY: path must be null terminated, FD must be manually closed.
pub unsafe fn open_readonly(path: &str) -> Option<libc::c_int> {
    debug_assert!(path.as_bytes().last() == Some(&0));
    let fd = open(path.as_ptr() as *mut _, libc::O_RDONLY | libc::O_CLOEXEC);
    if fd < 0 {
        return None;
    }
    // O_CLOEXEC works on all Unix targets except for older Linux kernels (pre
    // 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set.
    #[cfg(target_os = "linux")]
    libc::ioctl(fd, libc::FIOCLEX);
    Some(fd)
}
<commit_msg>util_libc: open_readonly shoud return a Result<commit_after>// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::error::ERRNO_NOT_POSITIVE;
use crate::util::LazyUsize;
use crate::Error;
use core::num::NonZeroU32;
use core::ptr::NonNull;

cfg_if! {
    if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
        use libc::__errno as errno_location;
    } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] {
        use libc::__errno_location as errno_location;
    } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] {
        use libc::___errno as errno_location;
    } else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] {
        use libc::__error as errno_location;
    } else if #[cfg(target_os = "haiku")] {
        use libc::_errnop as errno_location;
    }
}

cfg_if! {
    if #[cfg(target_os = "vxworks")] {
        use libc::errnoGet as get_errno;
    } else if #[cfg(target_os = "dragonfly")] {
        // Until rust-lang/rust#29594 is stable, we cannot get the errno value
        // on DragonFlyBSD. So we just return an out-of-range errno.
        unsafe fn get_errno() -> libc::c_int { -1 }
    } else {
        unsafe fn get_errno() -> libc::c_int { *errno_location() }
    }
}

pub fn last_os_error() -> Error {
    let errno = unsafe { get_errno() };
    if errno > 0 {
        Error::from(NonZeroU32::new(errno as u32).unwrap())
    } else {
        ERRNO_NOT_POSITIVE
    }
}

// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
//   - should return -1 and set errno on failure
//   - should return the number of bytes written on success
pub fn sys_fill_exact(
    mut buf: &mut [u8],
    sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
) -> Result<(), Error> {
    while !buf.is_empty() {
        let res = sys_fill(buf);
        if res < 0 {
            let err = last_os_error();
            // We should try again if the call was interrupted.
            if err.raw_os_error() != Some(libc::EINTR) {
                return Err(err);
            }
        } else {
            // We don't check for EOF (ret = 0) as the data we are reading
            // should be an infinite stream of random bytes.
            buf = &mut buf[(res as usize)..];
        }
    }
    Ok(())
}

// A "weak" binding to a C function that may or may not be present at runtime.
// Used for supporting newer OS features while still building on older systems.
// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the
// weak! macro in libstd.
pub struct Weak {
    name: &'static str,
    addr: LazyUsize,
}

impl Weak {
    // Construct a binding to a C function with a given name. This function is
    // unsafe because `name` _must_ be null terminated.
    pub const unsafe fn new(name: &'static str) -> Self {
        Self {
            name,
            addr: LazyUsize::new(),
        }
    }

    // Return a function pointer if present at runtime. Otherwise, return null.
    pub fn ptr(&self) -> Option<NonNull<libc::c_void>> {
        let addr = self.addr.unsync_init(|| unsafe {
            libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize
        });
        NonNull::new(addr as *mut _)
    }
}

pub struct LazyFd(LazyUsize);

impl LazyFd {
    pub const fn new() -> Self {
        Self(LazyUsize::new())
    }

    // If init() returns Some(x), x should be nonnegative.
    pub fn init(&self, init: impl FnOnce() -> Option<libc::c_int>) -> Option<libc::c_int> {
        let fd = self.0.sync_init(
            || match init() {
                // OK as val >= 0 and val <= c_int::MAX < usize::MAX
                Some(val) => val as usize,
                None => LazyUsize::UNINIT,
            },
            || unsafe {
                // We are usually waiting on an open(2) syscall to complete,
                // which typically takes < 10us if the file is a device.
                // However, we might end up waiting much longer if the entropy
                // pool isn't initialized, but even in that case, this loop will
                // consume a negligible amount of CPU on most platforms.
                libc::usleep(10);
            },
        );
        match fd {
            LazyUsize::UNINIT => None,
            val => Some(val as libc::c_int),
        }
    }
}

cfg_if! {
    if #[cfg(any(target_os = "linux", target_os = "emscripten"))] {
        use libc::open64 as open;
    } else {
        use libc::open;
    }
}

// SAFETY: path must be null terminated, FD must be manually closed.
pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
    debug_assert!(path.as_bytes().last() == Some(&0));
    let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
    if fd < 0 {
        return Err(last_os_error());
    }
    // O_CLOEXEC works on all Unix targets except for older Linux kernels (pre
    // 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set.
    #[cfg(target_os = "linux")]
    libc::ioctl(fd, libc::FIOCLEX);
    Ok(fd)
}
<|endoftext|>


<commit_before>use std::ops::{Add, Mul};
use std::collections::HashMap;
use self::LpExpression::{AddExpr, MulExpr};

pub enum LpType {
    Binary,
    Integer,
    Continuous
}

#[derive(Debug, Clone, Copy)]
pub enum LpVariable {
    BinaryVariable {
        name: &'static str,
    },
    IntegerVariable {
        name: &'static str,
        lower_bound: Option<i32>,
        upper_bound: Option<i32>,
    },
    ContinuousVariable {
        name: &'static str,
        lower_bound: Option<i32>,
        upper_bound: Option<i32>,
    }
}

impl LpVariable {
    pub fn new(name: &'static str, var_type: LpType) -> LpVariable {
        match var_type {
            LpType::Binary => LpVariable::BinaryVariable { name: name },
            LpType::Integer => LpVariable::IntegerVariable { name: name, lower_bound: None, upper_bound: None },
            LpType::Continuous => LpVariable::ContinuousVariable { name: name, lower_bound: None, upper_bound: None }

        }
    }
    fn lower_bound(&self, lw: i32) -> LpVariable {
        match self {
            &LpVariable::BinaryVariable { name: ref n } =>
                LpVariable::BinaryVariable {
                    name: n
                },
            &LpVariable::IntegerVariable { name: ref n, lower_bound: _, upper_bound: u } =>
                LpVariable::IntegerVariable {
                    name: n,
                    lower_bound: Some(lw),
                    upper_bound: u
                },
            &LpVariable::ContinuousVariable { name: ref n, lower_bound: _, upper_bound: u } =>
                LpVariable::ContinuousVariable {
                    name: n,
                    lower_bound: Some(lw),
                    upper_bound: u
                }
        }
    }
    fn upper_bound(&self, up: i32) -> LpVariable {
        match self {
            &LpVariable::BinaryVariable { name: ref n } =>
                LpVariable::BinaryVariable {
                    name: n.clone()
                },
            &LpVariable::IntegerVariable { name: ref n, lower_bound: l, upper_bound: _ } =>
                LpVariable::IntegerVariable {
                    name: n.clone(),
                    lower_bound: l,
                    upper_bound: Some(up)
                },
            &LpVariable::ContinuousVariable { name: ref n, lower_bound: l, upper_bound: _ } =>
                LpVariable::ContinuousVariable {
                    name: n.clone(),
                    lower_bound: l,
                    upper_bound: Some(up)
                }
        }
    }
}

#[derive(Debug)]
pub enum LpExpression {
    MulExpr(i32, LpVariable),
    AddExpr(Vec<LpExpression>)
}

// i32 * LpVar
impl Mul<LpVariable> for i32 {
    type Output = LpExpression;
    fn mul(self, _rhs: LpVariable) -> LpExpression {
        LpExpression::MulExpr(self, _rhs)
    }
}

// LpVar + LpVar
impl Add for LpVariable {
    type Output = LpExpression;
    fn add(self, _rhs: LpVariable) -> LpExpression {
        let mut v = Vec::new();
        v.push(MulExpr(1, self));
        v.push(MulExpr(1, _rhs));
        AddExpr(v)
    }
}

// LpExpr + LpVar
impl Add<LpVariable> for LpExpression {
    type Output = LpExpression;
    fn add(self, _rhs: LpVariable) -> LpExpression {
        match self {
            AddExpr(mut v) =>
                {
                    v.push(MulExpr(1, _rhs));
                    AddExpr(v)
                },
            MulExpr(_, _) =>
                {
                    let mut v = Vec::new();
                    v.push(self);
                    v.push(MulExpr(1, _rhs));
                    AddExpr(v)
                }
        }
    }
}

impl Add for LpExpression {
    type Output = LpExpression;
    fn add(self, _rhs: LpExpression) -> LpExpression {
        match self {
            AddExpr(mut v) =>
                {
                    v.push(_rhs);
                    AddExpr(v)
                },
            MulExpr(_, _) =>
                {
                    let mut v = Vec::new();
                    v.push(self);
                    v.push(_rhs);
                    AddExpr(v)
                }
        }
    }
}
<commit_msg>Expressions and Constraints features<commit_after>use std::ops::{Add, Mul};
use self::LpExpression::{AddExpr, MulExpr};

pub enum LpType {
    Binary,
    Integer,
    Continuous
}

#[derive(Debug, Clone, Copy)]
pub enum LpVariable {
    BinaryVariable {
        name: &'static str,
    },
    IntegerVariable {
        name: &'static str,
        lower_bound: Option<i32>,
        upper_bound: Option<i32>,
    },
    ContinuousVariable {
        name: &'static str,
        lower_bound: Option<i32>,
        upper_bound: Option<i32>,
    }
}

impl LpVariable {
    pub fn new(name: &'static str, var_type: LpType) -> LpVariable {
        match var_type {
            LpType::Binary => LpVariable::BinaryVariable { name: name },
            LpType::Integer => LpVariable::IntegerVariable { name: name, lower_bound: None, upper_bound: None },
            LpType::Continuous => LpVariable::ContinuousVariable { name: name, lower_bound: None, upper_bound: None }

        }
    }
    fn lower_bound(&self, lw: i32) -> LpVariable {
        match self {
            &LpVariable::BinaryVariable { name: ref n } =>
                LpVariable::BinaryVariable {
                    name: n
                },
            &LpVariable::IntegerVariable { name: ref n, lower_bound: _, upper_bound: u } =>
                LpVariable::IntegerVariable {
                    name: n,
                    lower_bound: Some(lw),
                    upper_bound: u
                },
            &LpVariable::ContinuousVariable { name: ref n, lower_bound: _, upper_bound: u } =>
                LpVariable::ContinuousVariable {
                    name: n,
                    lower_bound: Some(lw),
                    upper_bound: u
                }
        }
    }
    fn upper_bound(&self, up: i32) -> LpVariable {
        match self {
            &LpVariable::BinaryVariable { name: ref n } =>
                LpVariable::BinaryVariable {
                    name: n.clone()
                },
            &LpVariable::IntegerVariable { name: ref n, lower_bound: l, upper_bound: _ } =>
                LpVariable::IntegerVariable {
                    name: n.clone(),
                    lower_bound: l,
                    upper_bound: Some(up)
                },
            &LpVariable::ContinuousVariable { name: ref n, lower_bound: l, upper_bound: _ } =>
                LpVariable::ContinuousVariable {
                    name: n.clone(),
                    lower_bound: l,
                    upper_bound: Some(up)
                }
        }
    }
}

#[derive(Debug, Clone)]
pub enum LpExpression {
    MulExpr(i32, LpVariable),
    AddExpr(Vec<LpExpression>)
}

pub trait LpOperations<T> {
    fn lt(&self, lhs_expr: T) -> LpConstraint;
    fn le(&self, lhs_expr: T) -> LpConstraint;
    fn gt(&self, lhs_expr: T) -> LpConstraint;
    fn ge(&self, lhs_expr: T) -> LpConstraint;
    fn eq(&self, lhs_expr: T) -> LpConstraint;
}

// <LpExr> op <LpExpr>
impl LpOperations<LpExpression> for LpExpression {
    fn lt(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::Less, lhs_expr)
    }
    fn le(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::LessOrEqual, lhs_expr)
    }
    fn gt(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::Greater, lhs_expr)
    }
    fn ge(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::GreaterOrEqual, lhs_expr)
    }
    fn eq( &self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::Equal, lhs_expr)
    }
}

// <LpExr> op <LpVar>
impl LpOperations<LpVariable> for LpExpression {
    fn lt(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::Less, MulExpr(1, lhs_expr))
    }
    fn le(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::LessOrEqual, MulExpr(1, lhs_expr))
    }
    fn gt(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::Greater, MulExpr(1, lhs_expr))
    }
    fn ge(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::GreaterOrEqual, MulExpr(1, lhs_expr))
    }
    fn eq( &self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(self.clone(), Constraint::Equal, MulExpr(1, lhs_expr))
    }
}

// <LpVar> op <LpVar>
impl LpOperations<LpVariable> for LpVariable {
    fn lt(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::Less, MulExpr(1, lhs_expr))
    }
    fn le(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::LessOrEqual, MulExpr(1, lhs_expr))
    }
    fn gt(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::Greater, MulExpr(1, lhs_expr))
    }
    fn ge(&self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::GreaterOrEqual, MulExpr(1, lhs_expr))
    }
    fn eq( &self, lhs_expr: LpVariable) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::Equal, MulExpr(1, lhs_expr))
    }
}

// <LpVar> op <LpExpr>
impl LpOperations<LpExpression> for LpVariable {
    fn lt(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::Less, lhs_expr)
    }
    fn le(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::LessOrEqual, lhs_expr)
    }
    fn gt(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::Greater, lhs_expr)
    }
    fn ge(&self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::GreaterOrEqual, lhs_expr)
    }
    fn eq( &self, lhs_expr: LpExpression) -> LpConstraint {
        LpConstraint(MulExpr(1, self.clone()), Constraint::Equal, lhs_expr)
    }
}

// i32 * LpVar
impl Mul<LpVariable> for i32 {
    type Output = LpExpression;
    fn mul(self, _rhs: LpVariable) -> LpExpression {
        LpExpression::MulExpr(self, _rhs)
    }
}

// LpVar + LpVar
impl Add for LpVariable {
    type Output = LpExpression;
    fn add(self, _rhs: LpVariable) -> LpExpression {
        let mut v = Vec::new();
        v.push(MulExpr(1, self));
        v.push(MulExpr(1, _rhs));
        AddExpr(v)
    }
}

// LpExpr + LpVar
impl Add<LpVariable> for LpExpression {
    type Output = LpExpression;
    fn add(self, _rhs: LpVariable) -> LpExpression {
        match self {
            AddExpr(mut v) =>
                {
                    v.push(MulExpr(1, _rhs));
                    AddExpr(v)
                },
            MulExpr(_, _) =>
                {
                    let mut v = Vec::new();
                    v.push(self);
                    v.push(MulExpr(1, _rhs));
                    AddExpr(v)
                }
        }
    }
}

impl Add for LpExpression {
    type Output = LpExpression;
    fn add(self, _rhs: LpExpression) -> LpExpression {
        match self {
            AddExpr(mut v) =>
                {
                    v.push(_rhs);
                    AddExpr(v)
                },
            MulExpr(_, _) =>
                {
                    let mut v = Vec::new();
                    v.push(self);
                    v.push(_rhs);
                    AddExpr(v)
                }
        }
    }
}

#[derive(Debug)]
pub enum Constraint {
    Greater,
    Less,
    GreaterOrEqual,
    LessOrEqual,
    Equal
}

#[derive(Debug)]
pub struct LpConstraint(LpExpression, Constraint, LpExpression);

impl LpConstraint{
}

<|endoftext|>


<commit_before>// VslIdent (vxid) is encoded as 30 bit long u where 31'st and 32'nd bits are used to tell
// client and backend requests apart; value of 0 is reserved for non-request related
// records.
// VslIdent is assigned in VXID_Get from per worker pool of 32768 IDs. When worker runs out
// additional set of IDs is allocated to it.
// So in practice we have 1Gi IDs available per client/backend request before they wrap
// around.
//
// We need to constrain memory usage + make sure that super old records are not keept
// forever if this constraing is not reached.
//
// To constrain memory we just set a limit of how many slots are available at any given
// time. To constraint time we can count VSL records in u64 or smaller wrapping integer
// so we can tell if two VslIdent's are from two different times.

use linked_hash_map::{self, LinkedHashMap};
use std::num::Wrapping;
use std::fmt::Debug;
use super::VslIdent;
use fnv::FnvHasher;
use std::hash::BuildHasherDefault;

pub type Epoch = u32;

// How many VslIdent recorts to keep in the map
const MAX_SLOTS: u32 = 16_000;
// How many inserts old a record can be before we drop it on remove
const MAX_EPOCH_DIFF: Epoch = 1_000_000;
// How many objects to nuke when we need to nuke as factor of MAX_SLOTS
const NUKE_FACTOR: f32 = 0.01;

#[derive(Debug)]
pub struct VslStore<T> {
    map: LinkedHashMap<VslIdent, (Wrapping<Epoch>, T), BuildHasherDefault<FnvHasher>>,
    slots_free: u32,
    nuke_count: u32,
    epoch: Wrapping<Epoch>,
    max_epoch_diff: Wrapping<Epoch>,
}

impl<T> VslStore<T> {
    pub fn new() -> VslStore<T> {
        VslStore::with_max_slots_and_epoch_diff(MAX_SLOTS, MAX_EPOCH_DIFF)
    }

    pub fn with_max_slots_and_epoch_diff(max_slots: u32, max_epoch_diff: Epoch) -> VslStore<T> {
        VslStore {
            map: LinkedHashMap::default(),
            slots_free: max_slots,
            nuke_count: (max_slots as f32 * NUKE_FACTOR).ceil() as u32,
            epoch: Wrapping(0),
            max_epoch_diff: Wrapping(max_epoch_diff),
        }
    }

    pub fn insert(&mut self, ident: VslIdent, value: T) {
        if self.slots_free < 1 {
            self.nuke();
        }
        assert!(self.slots_free >= 1);

        self.epoch = self.epoch + Wrapping(1);

        if self.map.insert(ident, (self.epoch, value)).is_none() {
            // TODO: CEst: 11.59 !?!?!?
            self.slots_free = self.slots_free - 1;
        }
    }

    pub fn get_mut(&mut self, ident: &VslIdent) -> Option<&mut T> where T: Debug {
        //TODO: need to check expiration somehow
        self.map.get_refresh(ident).map(|&mut (_, ref mut t)| t)
    }

    pub fn remove(&mut self, ident: &VslIdent) -> Option<T> where T: Debug {
        let opt = self.map.remove(ident);
        if let Some((epoch, t)) = opt {
            self.slots_free = self.slots_free + 1;
            if self.is_expired(epoch) {
                warn!("Dropping old record; current epoch {}, record epoch {}, ident: {}: {:?}", self.epoch, epoch, ident, t);
                return None
            }
            return Some(t)
        }
        None
    }

    pub fn values(&self) -> Values<T> {
        Values(self.map.values())
    }

    fn is_expired(&self, entry_epoch: Wrapping<Epoch>) -> bool {
        self.epoch - entry_epoch > self.max_epoch_diff
    }

    fn nuke(&mut self) {
        warn!("Nuking up to {} oldest records", self.nuke_count); //TODO: CEst: 1.92

        for _ in 0..self.nuke_count {
            if self.map.pop_front().is_none() {
                break;
            }
            self.slots_free = self.slots_free + 1;
        }
    }

    #[cfg(test)]
    pub fn get(&self, ident: &VslIdent) -> Option<&T> {
        self.map.get(ident).map(t)
    }

    #[cfg(test)]
    pub fn oldest(&self) -> Option<(&VslIdent, &T)> {
        self.map.front().map(|(i, v)| (i, t(v)))
    }
}

fn t<T>(v: &(Wrapping<Epoch>, T)) -> &T {
    &v.1
}

pub struct Values<'a, T: 'a>(linked_hash_map::Values<'a, VslIdent, (Wrapping<Epoch>, T)>);

impl<'a, T> Iterator for Values<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(t)
    }
}

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

    #[test]
    fn old_elements_should_be_removed_on_overflow() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(10, 100);

        for i in 0..1024 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 1024 - 10);
    }

    #[test]
    fn old_elements_should_be_removed_on_overflow_with_remove() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(10, 100);

        for i in 0..1024 {
            s.insert(i, i);
            if i >= 4 && i % 4 == 0 {
                s.remove(&(i - 2));
            }
        }

        assert_eq!(*s.oldest().unwrap().0, 1024 - 10 - 2);
    }

    #[test]
    fn old_elements_should_not_be_retruned_by_remove() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(1024, 100);

        for i in 0..1024 {
            s.insert(i, i);
        }

        assert!(s.remove(&0).is_none());
        assert!(s.remove(&100).is_none());
        assert!(s.remove(&(1023 - 101)).is_none());
        assert!(s.remove(&(1023 - 100)).is_some());
    }
}
<commit_msg>expire store entries on get_mut<commit_after>// VslIdent (vxid) is encoded as 30 bit long u where 31'st and 32'nd bits are used to tell
// client and backend requests apart; value of 0 is reserved for non-request related
// records.
// VslIdent is assigned in VXID_Get from per worker pool of 32768 IDs. When worker runs out
// additional set of IDs is allocated to it.
// So in practice we have 1Gi IDs available per client/backend request before they wrap
// around.
//
// We need to constrain memory usage + make sure that super old records are not keept
// forever if this constraing is not reached.
//
// To constrain memory we just set a limit of how many slots are available at any given
// time. To constraint time we can count VSL records in u64 or smaller wrapping integer
// so we can tell if two VslIdent's are from two different times.

use linked_hash_map::{self, LinkedHashMap};
use std::num::Wrapping;
use std::fmt::Debug;
use super::VslIdent;
use fnv::FnvHasher;
use std::hash::BuildHasherDefault;

pub type Epoch = u32;

// How many VslIdent recorts to keep in the map
const MAX_SLOTS: u32 = 16_000;
// How many inserts old a record can be before we drop it on remove
const MAX_EPOCH_DIFF: Epoch = 1_000_000;
// How many objects to nuke when we need to nuke as factor of MAX_SLOTS
const NUKE_FACTOR: f32 = 0.01;

#[derive(Debug)]
pub struct VslStore<T> {
    map: LinkedHashMap<VslIdent, (Wrapping<Epoch>, T), BuildHasherDefault<FnvHasher>>,
    expired: Vec<VslIdent>,
    slots_free: u32,
    nuke_count: u32,
    epoch: Wrapping<Epoch>,
    max_epoch_diff: Wrapping<Epoch>,
}

impl<T> VslStore<T> {
    pub fn new() -> VslStore<T> {
        VslStore::with_max_slots_and_epoch_diff(MAX_SLOTS, MAX_EPOCH_DIFF)
    }

    pub fn with_max_slots_and_epoch_diff(max_slots: u32, max_epoch_diff: Epoch) -> VslStore<T> {
        VslStore {
            map: LinkedHashMap::default(),
            expired: Vec::new(),
            slots_free: max_slots,
            nuke_count: (max_slots as f32 * NUKE_FACTOR).ceil() as u32,
            epoch: Wrapping(0),
            max_epoch_diff: Wrapping(max_epoch_diff),
        }
    }

    pub fn insert(&mut self, ident: VslIdent, value: T) {
        self.expire();

        if self.slots_free < 1 {
            self.nuke();
        }
        assert!(self.slots_free >= 1);

        self.epoch = self.epoch + Wrapping(1);

        if self.map.insert(ident, (self.epoch, value)).is_none() {
            // TODO: CEst: 11.59 !?!?!?
            self.slots_free = self.slots_free - 1;
        }
    }

    pub fn get_mut(&mut self, ident: &VslIdent) -> Option<&mut T> where T: Debug {
        let opt = self.map.get_refresh(ident);
        if let Some(&mut (epoch, ref mut t)) = opt {
            if self.epoch - epoch > self.max_epoch_diff {
                warn!("Adding old record to expirity list; current epoch {}, record epoch {}, ident: {}: {:?}", self.epoch, epoch, ident, t);
                self.expired.push(ident.clone());
                return None
            }
            return Some(t)
        }
        None
    }

    pub fn remove(&mut self, ident: &VslIdent) -> Option<T> where T: Debug {
        let opt = self.map.remove(ident);
        if let Some((epoch, t)) = opt {
            self.slots_free = self.slots_free + 1;
            if self.is_expired(epoch) {
                warn!("Dropping old record; current epoch {}, record epoch {}, ident: {}: {:?}", self.epoch, epoch, ident, t);
                return None
            }
            return Some(t)
        }
        None
    }

    pub fn values(&self) -> Values<T> {
        Values(self.map.values())
    }

    fn is_expired(&self, entry_epoch: Wrapping<Epoch>) -> bool {
        self.epoch - entry_epoch > self.max_epoch_diff
    }

    fn expire(&mut self) {
        for expired_ident in self.expired.drain(..) {
            self.map.remove(&expired_ident);
        }
    }

    fn nuke(&mut self) {
        warn!("Nuking up to {} oldest records", self.nuke_count); //TODO: CEst: 1.92

        for _ in 0..self.nuke_count {
            if self.map.pop_front().is_none() {
                break;
            }
            self.slots_free = self.slots_free + 1;
        }
    }

    #[cfg(test)]
    pub fn get(&self, ident: &VslIdent) -> Option<&T> {
        self.map.get(ident).map(t)
    }

    #[cfg(test)]
    pub fn oldest(&self) -> Option<(&VslIdent, &T)> {
        self.map.front().map(|(i, v)| (i, t(v)))
    }
}

fn t<T>(v: &(Wrapping<Epoch>, T)) -> &T {
    &v.1
}

pub struct Values<'a, T: 'a>(linked_hash_map::Values<'a, VslIdent, (Wrapping<Epoch>, T)>);

impl<'a, T> Iterator for Values<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(t)
    }
}

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

    #[test]
    fn old_elements_should_be_removed_on_overflow() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(10, 100);

        for i in 0..1024 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 1024 - 10);
    }

    #[test]
    fn old_elements_should_be_removed_on_overflow_with_remove() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(10, 100);

        for i in 0..1024 {
            s.insert(i, i);
            if i >= 4 && i % 4 == 0 {
                s.remove(&(i - 2));
            }
        }

        assert_eq!(*s.oldest().unwrap().0, 1024 - 10 - 2);
    }

    #[test]
    fn old_elements_should_not_be_retruned_by_remove() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(1024, 100);

        for i in 0..1024 {
            s.insert(i, i);
        }

        assert!(s.remove(&0).is_none());
        assert!(s.remove(&100).is_none());
        assert!(s.remove(&(1023 - 101)).is_none());
        assert!(s.remove(&(1023 - 100)).is_some());
    }

    #[test]
    fn old_elements_should_not_be_retruned_by_get_mut() {
        let mut s = VslStore::with_max_slots_and_epoch_diff(1024, 100);

        for i in 0..1024 {
            s.insert(i, i);
        }

        assert!(s.get_mut(&0).is_none());
        assert!(s.get_mut(&100).is_none());
        assert!(s.get_mut(&(1023 - 101)).is_none());
        assert!(s.get_mut(&(1023 - 100)).is_some());
    }
}
<|endoftext|>


<commit_before>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) edef <edef@edef.eu>,
//               whitequark <whitequark@whitequark.org>
// See the LICENSE file included in this distribution.
#![feature(thread_local)]
extern crate simd;
extern crate fringe;

use fringe::Context;

#[thread_local]
static mut ctx_slot: *mut Context<fringe::OsStack> = 0 as *mut Context<_>;

#[test]
fn context() {
  unsafe extern "C" fn adder(arg: usize) -> ! {
    println!("it's alive! arg: {}", arg);
    let arg = Context::swap(ctx_slot, ctx_slot, arg + 1);
    println!("still alive! arg: {}", arg);
    Context::swap(ctx_slot, ctx_slot, arg + 1);
    panic!("i should be dead");
  }

  unsafe {
    let stack = fringe::OsStack::new(4 << 20).unwrap();
    let mut ctx = Context::new(stack, adder);
    ctx_slot = &mut ctx;

    let ret = Context::swap(ctx_slot, ctx_slot, 10);
    assert_eq!(ret, 11);
    let ret = Context::swap(ctx_slot, ctx_slot, 50);
    assert_eq!(ret, 51);
  }
}

#[test]
fn simd() {
  unsafe extern "C" fn permuter(arg: usize) -> ! {
    // This will crash if the stack is not aligned properly.
    let x = simd::i32x4::splat(arg as i32);
    let y = x * x;
    println!("simd result: {:?}", y);
    Context::swap(ctx_slot, ctx_slot, 0);
    // And try again after a context switch.
    let x = simd::i32x4::splat(arg as i32);
    let y = x * x;
    println!("simd result: {:?}", y);
    Context::swap(ctx_slot, ctx_slot, 0);
    panic!("i should be dead");
  }

  unsafe {
    let stack = fringe::OsStack::new(4 << 20).unwrap();
    let mut ctx = Context::new(stack, permuter);
    ctx_slot = &mut ctx;

    Context::swap(ctx_slot, ctx_slot, 10);
    Context::swap(ctx_slot, ctx_slot, 20);
  }
}
<commit_msg>rename simd -> context_simd<commit_after>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) edef <edef@edef.eu>,
//               whitequark <whitequark@whitequark.org>
// See the LICENSE file included in this distribution.
#![feature(thread_local)]
extern crate simd;
extern crate fringe;

use fringe::Context;

#[thread_local]
static mut ctx_slot: *mut Context<fringe::OsStack> = 0 as *mut Context<_>;

#[test]
fn context() {
  unsafe extern "C" fn adder(arg: usize) -> ! {
    println!("it's alive! arg: {}", arg);
    let arg = Context::swap(ctx_slot, ctx_slot, arg + 1);
    println!("still alive! arg: {}", arg);
    Context::swap(ctx_slot, ctx_slot, arg + 1);
    panic!("i should be dead");
  }

  unsafe {
    let stack = fringe::OsStack::new(4 << 20).unwrap();
    let mut ctx = Context::new(stack, adder);
    ctx_slot = &mut ctx;

    let ret = Context::swap(ctx_slot, ctx_slot, 10);
    assert_eq!(ret, 11);
    let ret = Context::swap(ctx_slot, ctx_slot, 50);
    assert_eq!(ret, 51);
  }
}

#[test]
fn context_simd() {
  unsafe extern "C" fn permuter(arg: usize) -> ! {
    // This will crash if the stack is not aligned properly.
    let x = simd::i32x4::splat(arg as i32);
    let y = x * x;
    println!("simd result: {:?}", y);
    Context::swap(ctx_slot, ctx_slot, 0);
    // And try again after a context switch.
    let x = simd::i32x4::splat(arg as i32);
    let y = x * x;
    println!("simd result: {:?}", y);
    Context::swap(ctx_slot, ctx_slot, 0);
    panic!("i should be dead");
  }

  unsafe {
    let stack = fringe::OsStack::new(4 << 20).unwrap();
    let mut ctx = Context::new(stack, permuter);
    ctx_slot = &mut ctx;

    Context::swap(ctx_slot, ctx_slot, 10);
    Context::swap(ctx_slot, ctx_slot, 20);
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use crate::frame::*;
use crate::tiling::*;
use crate::util::*;
use rust_hawktracer::*;

#[derive(Debug, Default, Clone)]
pub struct ActivityMask {
  variances: Vec<f64>,
  // Width and height of the original frame that is masked
  width: usize,
  height: usize,
  // Side of unit (square) activity block in log2
  granularity: usize,
}

impl ActivityMask {
  #[hawktracer(activity_mask_from_plane)]
  pub fn from_plane<T: Pixel>(luma_plane: &Plane<T>) -> ActivityMask {
    let PlaneConfig { width, height, .. } = luma_plane.cfg;

    let granularity = 3;

    let aligned_luma = Rect {
      x: 0_isize,
      y: 0_isize,
      width: (width >> granularity) << granularity,
      height: (height >> granularity) << granularity,
    };
    let luma = PlaneRegion::new(luma_plane, aligned_luma);

    let mut variances =
      Vec::with_capacity((height >> granularity) * (width >> granularity));

    for y in 0..height >> granularity {
      for x in 0..width >> granularity {
        let block_rect = Area::Rect {
          x: (x << granularity) as isize,
          y: (y << granularity) as isize,
          width: 8,
          height: 8,
        };

        let block = luma.subregion(block_rect);

        let mean: f64 = block
          .rows_iter()
          .flatten()
          .map(|&pix| {
            let pix: i16 = CastFromPrimitive::cast_from(pix);
            pix as f64
          })
          .sum::<f64>()
          / 64.0_f64;
        let variance: f64 = block
          .rows_iter()
          .flatten()
          .map(|&pix| {
            let pix: i16 = CastFromPrimitive::cast_from(pix);
            (pix as f64 - mean).powi(2)
          })
          .sum::<f64>();
        variances.push(variance);
      }
    }
    ActivityMask { variances, width, height, granularity }
  }

  pub fn variance_at(&self, x: usize, y: usize) -> Option<f64> {
    let (dec_width, dec_height) =
      (self.width >> self.granularity, self.height >> self.granularity);
    if x > dec_width || y > dec_height {
      None
    } else {
      Some(*self.variances.get(x + dec_width * y).unwrap())
    }
  }

  pub fn mean_activity_of(&self, rect: Rect) -> Option<f64> {
    let Rect { x, y, width, height } = rect;
    let (x, y) = (x as usize, y as usize);
    let granularity = self.granularity;
    let (dec_x, dec_y) = (x >> granularity, y >> granularity);
    let (dec_width, dec_height) =
      (width >> granularity, height >> granularity);

    if x > self.width
      || y > self.height
      || (x + width) > self.width
      || (y + height) > self.height
      || dec_width == 0
      || dec_height == 0
    {
      // Region lies out of the frame or is smaller than 8x8 on some axis
      None
    } else {
      let activity = self
        .variances
        .chunks_exact(self.width >> granularity)
        .skip(dec_y)
        .take(dec_height)
        .map(|row| row.iter().skip(dec_x).take(dec_width).sum::<f64>())
        .sum::<f64>()
        / (dec_width as f64 * dec_height as f64);

      Some(activity.cbrt().sqrt())
    }
  }
}
<commit_msg>Drop unused ActivityMask methods<commit_after>// Copyright (c) 2017-2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use crate::frame::*;
use crate::tiling::*;
use crate::util::*;
use rust_hawktracer::*;

#[derive(Debug, Default, Clone)]
pub struct ActivityMask {
  variances: Vec<f64>,
  // Width and height of the original frame that is masked
  width: usize,
  height: usize,
  // Side of unit (square) activity block in log2
  granularity: usize,
}

impl ActivityMask {
  #[hawktracer(activity_mask_from_plane)]
  pub fn from_plane<T: Pixel>(luma_plane: &Plane<T>) -> ActivityMask {
    let PlaneConfig { width, height, .. } = luma_plane.cfg;

    let granularity = 3;

    let aligned_luma = Rect {
      x: 0_isize,
      y: 0_isize,
      width: (width >> granularity) << granularity,
      height: (height >> granularity) << granularity,
    };
    let luma = PlaneRegion::new(luma_plane, aligned_luma);

    let mut variances =
      Vec::with_capacity((height >> granularity) * (width >> granularity));

    for y in 0..height >> granularity {
      for x in 0..width >> granularity {
        let block_rect = Area::Rect {
          x: (x << granularity) as isize,
          y: (y << granularity) as isize,
          width: 8,
          height: 8,
        };

        let block = luma.subregion(block_rect);

        let mean: f64 = block
          .rows_iter()
          .flatten()
          .map(|&pix| {
            let pix: i16 = CastFromPrimitive::cast_from(pix);
            pix as f64
          })
          .sum::<f64>()
          / 64.0_f64;
        let variance: f64 = block
          .rows_iter()
          .flatten()
          .map(|&pix| {
            let pix: i16 = CastFromPrimitive::cast_from(pix);
            (pix as f64 - mean).powi(2)
          })
          .sum::<f64>();
        variances.push(variance);
      }
    }
    ActivityMask { variances, width, height, granularity }
  }
}
<|endoftext|>


<commit_before>// TODO: data xrefs

use super::*;
use zydis;
use byteorder::{ByteOrder, LittleEndian};
use std::mem;

fn analyze_operand_xrefs(
    // used to determine valid addresses
    layout: &ModuleLayout,
    // used to detect RIP-relative addressing
    pc: zydis::enums::register::Register,
    // used to compute relative addresses
    rva: Rva,
    // used to compute relative addresses
    insn: &zydis::ffi::DecodedInstruction,
    // the operand to inspect
    op: &zydis::ffi::DecodedOperand,
) -> Result<Option<Rva>, Error> {
    match op.ty {
        zydis::enums::OperandType::Unused => Err(Error::NotImplemented("xref from unused register")),
        zydis::enums::OperandType::Register => {
            // like: CALL rbx
            // TODO: for now, don't index unresolved indirect branches
            Ok(None)
        }
        zydis::enums::OperandType::Memory => {
            // like: .text:0000000180001041 FF 15 D1 78 07 00      call    cs:__imp_RtlVirtualUnwind_0
            //           0x0000000000001041:                       call    [0x0000000000079980]
            if pc == op.mem.base && op.mem.disp.has_displacement && op.mem.scale == 0 {
                // RIP-relative
                // this is the default encoding on x64.
                // tools like IDA automatically compute and display the target.
                // CALL [RIP + 0x401000]
                if let zydis::enums::register::Register::NONE = op.mem.index {
                    let target =
                        (rva as i64
                        // TODO: cast from rva (u64) to i64 is lossy.
                        + op.mem.disp.displacement
                        + i64::from(insn.length)) as Rva;

                    if layout.is_rva_valid(target) {
                        Ok(Some(target))
                    } else {
                        // TODO: record this anomaly somewhere.
                        warn!("problem: invalid xref target: memory not in sections");
                        Ok(None)
                    }
                } else {
                    // unsupported
                    // like: CALL [RIP + 4*RCX + 0x401000] ??
                    println!("CALL [RIP + 4*RCX + 0x401000] ??");
                    Err(Error::NotImplemented("xref from RIP-relative, non-zero index memory"))
                }
            } else if op.mem.base == zydis::enums::register::Register::NONE &&
                      op.mem.disp.has_displacement &&
                      op.mem.scale == 0 {
                // like: .text:00401078 FF 15 40 80 40 00    call   DWORD PTR ds:0x408040
                // "mem": {
                //    "ty": "Register",
                //    "segment": "DS",
                //    "base": "NONE",
                //    "index": "NONE",
                //    "scale": 0,
                //    "disp": {
                //        "has_displacement": true,
                //        "displacement": 4227136
                //    }
                // },

                let target = op.mem.disp.displacement as Va;
                match layout.va2rva(target) {
                    Ok(target) => {
                        debug!("found RVA 0x{:x} from VA 0x{:x} using base address 0x{:x}",
                            target, op.mem.disp.displacement, layout.base_address);
                        Ok(Some(target))
                    }
                    Err(_) => {
                        warn!("problem: VA 0x{:x} not mapped using base address 0x{:x}", target, layout.base_address);
                        Ok(None)
                    }
                }
            } else {
                // like: CALL [rbx]
                // like: CALL [rbx + 0x10]
                // TODO: for now, don't index unresolved indirect branches
                Ok(None)
            }
        }
        zydis::enums::OperandType::Pointer => {
            println!("TODO: operand: pointer");
            Err(Error::NotImplemented("xref from pointer"))
        }
        zydis::enums::OperandType::Immediate => {
            if !op.imm.is_relative {
                println!("TODO: absolute immediate operand");
                Err(Error::NotImplemented("xref from absolute immediate"))
            } else {
                let imm = if op.imm.is_signed {
                    u64_i64(op.imm.value)
                } else {
                    op.imm.value as i64
                };

                // TODO: cast from rva (u64) to i64 is lossy.
                let dst = (rva as i64 + imm + i64::from(insn.length)) as Rva;
                if layout.is_rva_valid(dst) {
                    Ok(Some(dst))
                } else {
                    // TODO: record this anomaly somewhere.
                    debug!("problem: invalid xref target: relative immediate not in sections");
                    Ok(None)
                }
            }
        }
    }
}

pub fn analyze_insn_xrefs(
    layout: &ModuleLayout,
    pc: zydis::enums::register::Register,
    rva: Rva,
    insn: &zydis::ffi::DecodedInstruction,
) -> Result<Vec<Xref>, Error> {
    match insn.mnemonic {
        // see InstructionCategory
        // syscall, sysexit, sysret
        // vmcall, vmmcall
        zydis::enums::mnemonic::Mnemonic::CALL => {
            // a CALL always has an operand, so assume this is ok.
            let op = Workspace::get_first_operand(insn).unwrap();

            let fallthrough = Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::Fallthrough,
            };

            match analyze_operand_xrefs(layout, pc, rva, insn, op)? {
                // TODO: fallthrough is not guaranteed if the function is noret
                Some(dst) => Ok(vec![Xref { src: rva, dst: dst, typ: XrefType::Call }, fallthrough]),
                None => Ok(vec![fallthrough]),
            }
        }
        zydis::enums::mnemonic::Mnemonic::RET
        | zydis::enums::mnemonic::Mnemonic::IRET
        | zydis::enums::mnemonic::Mnemonic::IRETD
        | zydis::enums::mnemonic::Mnemonic::IRETQ => Ok(vec![]),
        zydis::enums::mnemonic::Mnemonic::JMP => {
            // a JMP always has an operand, so assume this is ok.
            let op = Workspace::get_first_operand(insn).unwrap();

            match analyze_operand_xrefs(layout, pc, rva, insn, op)? {
                Some(dst) => Ok(vec![Xref { src: rva, dst: dst, typ: XrefType::UnconditionalJump }]),
                None => Ok(vec![]),
            }
        }
        zydis::enums::mnemonic::Mnemonic::JB
        | zydis::enums::mnemonic::Mnemonic::JBE
        | zydis::enums::mnemonic::Mnemonic::JCXZ
        | zydis::enums::mnemonic::Mnemonic::JECXZ
        | zydis::enums::mnemonic::Mnemonic::JKNZD
        | zydis::enums::mnemonic::Mnemonic::JKZD
        | zydis::enums::mnemonic::Mnemonic::JL
        | zydis::enums::mnemonic::Mnemonic::JLE
        | zydis::enums::mnemonic::Mnemonic::JNB
        | zydis::enums::mnemonic::Mnemonic::JNBE
        | zydis::enums::mnemonic::Mnemonic::JNL
        | zydis::enums::mnemonic::Mnemonic::JNLE
        | zydis::enums::mnemonic::Mnemonic::JNO
        | zydis::enums::mnemonic::Mnemonic::JNP
        | zydis::enums::mnemonic::Mnemonic::JNS
        | zydis::enums::mnemonic::Mnemonic::JNZ
        | zydis::enums::mnemonic::Mnemonic::JO
        | zydis::enums::mnemonic::Mnemonic::JP
        | zydis::enums::mnemonic::Mnemonic::JRCXZ
        | zydis::enums::mnemonic::Mnemonic::JS
        | zydis::enums::mnemonic::Mnemonic::JZ => {
            // a J* always has an operand, so assume this is ok.
            let op = Workspace::get_first_operand(insn).unwrap();

            let fallthrough = Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::Fallthrough,
            };

            match analyze_operand_xrefs(layout, pc, rva, insn, op)? {
                Some(dst) => Ok(vec![
                    Xref { src: rva, dst: dst, typ: XrefType::ConditionalJump, },
                    fallthrough,
                ]),
                None => Ok(vec![fallthrough]),
            }
        }
        zydis::enums::mnemonic::Mnemonic::CMOVB
        | zydis::enums::mnemonic::Mnemonic::CMOVBE
        | zydis::enums::mnemonic::Mnemonic::CMOVL
        | zydis::enums::mnemonic::Mnemonic::CMOVLE
        | zydis::enums::mnemonic::Mnemonic::CMOVNB
        | zydis::enums::mnemonic::Mnemonic::CMOVNBE
        | zydis::enums::mnemonic::Mnemonic::CMOVNL
        | zydis::enums::mnemonic::Mnemonic::CMOVNLE
        | zydis::enums::mnemonic::Mnemonic::CMOVNO
        | zydis::enums::mnemonic::Mnemonic::CMOVNP
        | zydis::enums::mnemonic::Mnemonic::CMOVNS
        | zydis::enums::mnemonic::Mnemonic::CMOVNZ
        | zydis::enums::mnemonic::Mnemonic::CMOVO
        | zydis::enums::mnemonic::Mnemonic::CMOVP
        | zydis::enums::mnemonic::Mnemonic::CMOVS
        | zydis::enums::mnemonic::Mnemonic::CMOVZ => Ok(vec![
            Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::Fallthrough
            },
            Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::ConditionalMove
            },
        ]),
        _ => Ok(vec![Xref {
            src: rva,
            dst: rva + u64::from(insn.length),
            typ: XrefType::Fallthrough,
        }]),
    }
}

pub fn find_insns(ws: &Workspace, predicate: fn(&Instruction) -> bool) -> Result<Vec<Rva>, Error> {
    let mut ret: Vec<Rva> = vec![];

        // empirically, doing this in parallel (par_extend/par_bridge) is not worth it.
        // the predicates are likely too simple,
        // so the majority of time is spent synchronizing.
        ret.extend(
            ws
            .iter_insns()
            .filter(|insn| predicate(insn))
            .map(|insn| match insn {
                Instruction::Valid {loc, ..} => loc.addr,
                Instruction::Invalid {loc, ..} => loc.addr,
            })
        );

    Ok(ret)
}

pub fn find_roots(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    find_insns(ws, |insn| match insn {
        Instruction::Valid{loc, ..} => loc.xrefs.to.is_empty(),
        _ => false,
    })
}

pub fn find_call_targets(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    find_insns(ws, |insn| match insn {
        Instruction::Valid{loc, ..} => loc.xrefs.to.iter().any(|xref| match xref.typ {
            XrefType::Call => true,
            _ => false,
        }),
        _ => false,
    })
}

pub fn find_branch_targets(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    find_insns(ws, |insn| match insn {
        Instruction::Valid{loc, ..} => loc.xrefs.to.iter().any(|xref| match xref.typ {
            XrefType::UnconditionalJump => true,
            XrefType::ConditionalJump => true,
            _ => false,
        }),
        _ => false,
    })
}

fn get_section_by_name<'a>(ws: &'a Workspace, name: &[u8]) -> Result<Option<&'a Section>, Error> {
    match ws.get_obj()? {
        Object::PE(pe) => {
            match pe.sections.iter()
                .enumerate()
                .map(|(index, section)| (index, section.name))
                .find(|(_, sec_name)| sec_name == name) {
                    Some((index, _)) => Ok(Some(&ws.sections[index])),
                    None => Ok(None),
            }
        },
        _ => Err(Error::NotImplemented("analyzer for non-PE module"))
    }
}

// pub fn find_runtime_functions
//  on pe64, entries in .pdata are struct RUNTIME_FUNCTION
//
//  00000000 FunctionStart   dd ?                    ; offset rva
//  00000004 FunctionEnd     dd ?                    ; offset rva pastend
//  00000008 UnwindInfo      dd ?                    ; offset rva
//
// ref: https://stackoverflow.com/a/9794688/87207
//
// for k32, the first three entries should be:
//  - 1010
//  - 1060
//  - 1140
pub fn find_runtime_functions(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    match ws.get_obj()? {
        Object::PE(_) => {
            let mut ret: Vec<Rva> = vec![];

            if let Some(sec) = get_section_by_name(ws, b".pdata\x00\x00")? {
                let layout = ws.get_layout()?;
                for runtime_function in sec.buf.chunks_exact(mem::size_of::<u32>() * 3) {
                    let rva = LittleEndian::read_u32(&runtime_function) as Rva;
                    if rva == 0 {
                        // must be end of (valid) .pdata section
                        break;
                    } else if layout.is_rva_valid(rva) {
                        ret.push(rva);
                    } else {
                        warn!("malformed .pdata section: entry points outside of image");
                        return Ok(vec![]);
                    }
                }
            }

            Ok(ret)
        },
        _ => Err(Error::NotImplemented("analyzer for non-PE module"))
    }
}

// pub fn find_fixups

// pub fn find_ptrs
//  relies on the image being loaded at the base address
//  also relies on loc vs insn to be figured out.

/// ```
/// use lancelot::*;
/// use lancelot::rsrc::*;
/// use lancelot::analysis::*;
/// let ws = get_workspace(Rsrc::TINY);
/// assert_eq!(0, find_entrypoints(&ws).unwrap().len());
///
/// //let ws = get_workspace(Rsrc::K32);
/// //assert_eq!(1630, find_entrypoints(&ws).unwrap().len());
/// ```
pub fn find_entrypoints(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    match ws.get_obj()? {
        Object::PE(pe) => {
            let mut ret: Vec<Rva> = vec![];
            if let Some(opt) = pe.header.optional_header {
                ret.push(opt.standard_fields.address_of_entry_point);
                ret.extend(
                    pe.exports.iter().map(|export| export.rva as Rva)
                );
            } else {
                info!("no optional header")
            }

            Ok(ret)
        },
        _ => Err(Error::NotImplemented("analyzer for non-PE module"))
    }
}

// pub fn find_sigs

// pub fn find_impossible_paths
// like:
//   MOV
//   MOV
//   INVALID
<commit_msg>analysis: xref: handle ptr VA as long as its the zero segment<commit_after>// TODO: data xrefs

use super::*;
use zydis;
use byteorder::{ByteOrder, LittleEndian};
use std::mem;

fn analyze_operand_xrefs(
    // used to determine valid addresses
    layout: &ModuleLayout,
    // used to detect RIP-relative addressing
    pc: zydis::enums::register::Register,
    // used to compute relative addresses
    rva: Rva,
    // used to compute relative addresses
    insn: &zydis::ffi::DecodedInstruction,
    // the operand to inspect
    op: &zydis::ffi::DecodedOperand,
) -> Result<Option<Rva>, Error> {
    match op.ty {
        zydis::enums::OperandType::Unused => Err(Error::NotImplemented("xref from unused register")),
        zydis::enums::OperandType::Register => {
            // like: CALL rbx
            // TODO: for now, don't index unresolved indirect branches
            Ok(None)
        }
        zydis::enums::OperandType::Memory => {
            // like: .text:0000000180001041 FF 15 D1 78 07 00      call    cs:__imp_RtlVirtualUnwind_0
            //           0x0000000000001041:                       call    [0x0000000000079980]
            if pc == op.mem.base && op.mem.disp.has_displacement && op.mem.scale == 0 {
                // RIP-relative
                // this is the default encoding on x64.
                // tools like IDA automatically compute and display the target.
                // CALL [RIP + 0x401000]
                if let zydis::enums::register::Register::NONE = op.mem.index {
                    let target =
                        (rva as i64
                        // TODO: cast from rva (u64) to i64 is lossy.
                        + op.mem.disp.displacement
                        + i64::from(insn.length)) as Rva;

                    if layout.is_rva_valid(target) {
                        Ok(Some(target))
                    } else {
                        // TODO: record this anomaly somewhere.
                        warn!("problem: invalid xref target: memory not in sections");
                        Ok(None)
                    }
                } else {
                    // unsupported
                    // like: CALL [RIP + 4*RCX + 0x401000] ??
                    println!("CALL [RIP + 4*RCX + 0x401000] ??");
                    Err(Error::NotImplemented("xref from RIP-relative, non-zero index memory"))
                }
            } else if op.mem.base == zydis::enums::register::Register::NONE &&
                      op.mem.disp.has_displacement &&
                      op.mem.scale == 0 {
                // like: .text:00401078 FF 15 40 80 40 00    call   DWORD PTR ds:0x408040
                // "mem": {
                //    "ty": "Register",
                //    "segment": "DS",
                //    "base": "NONE",
                //    "index": "NONE",
                //    "scale": 0,
                //    "disp": {
                //        "has_displacement": true,
                //        "displacement": 4227136
                //    }
                // },

                let target = op.mem.disp.displacement as Va;
                match layout.va2rva(target) {
                    Ok(target) => {
                        debug!("found RVA 0x{:x} from VA 0x{:x} using base address 0x{:x}",
                            target, op.mem.disp.displacement, layout.base_address);
                        Ok(Some(target))
                    }
                    Err(_) => {
                        warn!("problem: VA 0x{:x} not mapped using base address 0x{:x}", target, layout.base_address);
                        Ok(None)
                    }
                }
            } else {
                // like: CALL [rbx]
                // like: CALL [rbx + 0x10]
                // TODO: for now, don't index unresolved indirect branches
                Ok(None)
            }
        }
        zydis::enums::OperandType::Pointer => {
            // like: EA 33 D2 B9 60 80 40  jmp  far ptr 4080h:60B9D233h
            // "ptr": {
            //    "segment": 16512,
            //    "offset": 1622790707
            // },
            if op.ptr.segment == 0x0 {
                // i guess we can treat this like a memory reference???
                let target = op.ptr.offset as Va;
                match layout.va2rva(target) {
                    Ok(target) => {
                        debug!("found RVA 0x{:x} from VA 0x{:x} using base address 0x{:x}",
                            target, op.mem.disp.displacement, layout.base_address);
                        Ok(Some(target))
                    }
                    Err(_) => {
                        warn!("problem: 0x{:x}: VA 0x{:x} not mapped using base address 0x{:x}",
                              rva, target, layout.base_address);
                        Ok(None)
                    }
                }
            } else {
                warn!("problem: 0x{:x}: pointer using non-zero segment: 0x{:x}", rva, op.ptr.segment);
                Ok(None)
            }
        }
        zydis::enums::OperandType::Immediate => {
            if !op.imm.is_relative {
                println!("TODO: absolute immediate operand");
                Err(Error::NotImplemented("xref from absolute immediate"))
            } else {
                let imm = if op.imm.is_signed {
                    u64_i64(op.imm.value)
                } else {
                    op.imm.value as i64
                };

                // TODO: cast from rva (u64) to i64 is lossy.
                let dst = (rva as i64 + imm + i64::from(insn.length)) as Rva;
                if layout.is_rva_valid(dst) {
                    Ok(Some(dst))
                } else {
                    // TODO: record this anomaly somewhere.
                    debug!("problem: invalid xref target: relative immediate not in sections");
                    Ok(None)
                }
            }
        }
    }
}

pub fn analyze_insn_xrefs(
    layout: &ModuleLayout,
    pc: zydis::enums::register::Register,
    rva: Rva,
    insn: &zydis::ffi::DecodedInstruction,
) -> Result<Vec<Xref>, Error> {
    match insn.mnemonic {
        // see InstructionCategory
        // syscall, sysexit, sysret
        // vmcall, vmmcall
        zydis::enums::mnemonic::Mnemonic::CALL => {
            // a CALL always has an operand, so assume this is ok.
            let op = Workspace::get_first_operand(insn).unwrap();

            let fallthrough = Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::Fallthrough,
            };

            match analyze_operand_xrefs(layout, pc, rva, insn, op)? {
                // TODO: fallthrough is not guaranteed if the function is noret
                Some(dst) => Ok(vec![Xref { src: rva, dst: dst, typ: XrefType::Call }, fallthrough]),
                None => Ok(vec![fallthrough]),
            }
        }
        zydis::enums::mnemonic::Mnemonic::RET
        | zydis::enums::mnemonic::Mnemonic::IRET
        | zydis::enums::mnemonic::Mnemonic::IRETD
        | zydis::enums::mnemonic::Mnemonic::IRETQ => Ok(vec![]),
        zydis::enums::mnemonic::Mnemonic::JMP => {
            // a JMP always has an operand, so assume this is ok.
            let op = Workspace::get_first_operand(insn).unwrap();

            match analyze_operand_xrefs(layout, pc, rva, insn, op)? {
                Some(dst) => Ok(vec![Xref { src: rva, dst: dst, typ: XrefType::UnconditionalJump }]),
                None => Ok(vec![]),
            }
        }
        zydis::enums::mnemonic::Mnemonic::JB
        | zydis::enums::mnemonic::Mnemonic::JBE
        | zydis::enums::mnemonic::Mnemonic::JCXZ
        | zydis::enums::mnemonic::Mnemonic::JECXZ
        | zydis::enums::mnemonic::Mnemonic::JKNZD
        | zydis::enums::mnemonic::Mnemonic::JKZD
        | zydis::enums::mnemonic::Mnemonic::JL
        | zydis::enums::mnemonic::Mnemonic::JLE
        | zydis::enums::mnemonic::Mnemonic::JNB
        | zydis::enums::mnemonic::Mnemonic::JNBE
        | zydis::enums::mnemonic::Mnemonic::JNL
        | zydis::enums::mnemonic::Mnemonic::JNLE
        | zydis::enums::mnemonic::Mnemonic::JNO
        | zydis::enums::mnemonic::Mnemonic::JNP
        | zydis::enums::mnemonic::Mnemonic::JNS
        | zydis::enums::mnemonic::Mnemonic::JNZ
        | zydis::enums::mnemonic::Mnemonic::JO
        | zydis::enums::mnemonic::Mnemonic::JP
        | zydis::enums::mnemonic::Mnemonic::JRCXZ
        | zydis::enums::mnemonic::Mnemonic::JS
        | zydis::enums::mnemonic::Mnemonic::JZ => {
            // a J* always has an operand, so assume this is ok.
            let op = Workspace::get_first_operand(insn).unwrap();

            let fallthrough = Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::Fallthrough,
            };

            match analyze_operand_xrefs(layout, pc, rva, insn, op)? {
                Some(dst) => Ok(vec![
                    Xref { src: rva, dst: dst, typ: XrefType::ConditionalJump, },
                    fallthrough,
                ]),
                None => Ok(vec![fallthrough]),
            }
        }
        zydis::enums::mnemonic::Mnemonic::CMOVB
        | zydis::enums::mnemonic::Mnemonic::CMOVBE
        | zydis::enums::mnemonic::Mnemonic::CMOVL
        | zydis::enums::mnemonic::Mnemonic::CMOVLE
        | zydis::enums::mnemonic::Mnemonic::CMOVNB
        | zydis::enums::mnemonic::Mnemonic::CMOVNBE
        | zydis::enums::mnemonic::Mnemonic::CMOVNL
        | zydis::enums::mnemonic::Mnemonic::CMOVNLE
        | zydis::enums::mnemonic::Mnemonic::CMOVNO
        | zydis::enums::mnemonic::Mnemonic::CMOVNP
        | zydis::enums::mnemonic::Mnemonic::CMOVNS
        | zydis::enums::mnemonic::Mnemonic::CMOVNZ
        | zydis::enums::mnemonic::Mnemonic::CMOVO
        | zydis::enums::mnemonic::Mnemonic::CMOVP
        | zydis::enums::mnemonic::Mnemonic::CMOVS
        | zydis::enums::mnemonic::Mnemonic::CMOVZ => Ok(vec![
            Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::Fallthrough
            },
            Xref {
                src: rva,
                dst: rva + u64::from(insn.length),
                typ: XrefType::ConditionalMove
            },
        ]),
        _ => Ok(vec![Xref {
            src: rva,
            dst: rva + u64::from(insn.length),
            typ: XrefType::Fallthrough,
        }]),
    }
}

pub fn find_insns(ws: &Workspace, predicate: fn(&Instruction) -> bool) -> Result<Vec<Rva>, Error> {
    let mut ret: Vec<Rva> = vec![];

        // empirically, doing this in parallel (par_extend/par_bridge) is not worth it.
        // the predicates are likely too simple,
        // so the majority of time is spent synchronizing.
        ret.extend(
            ws
            .iter_insns()
            .filter(|insn| predicate(insn))
            .map(|insn| match insn {
                Instruction::Valid {loc, ..} => loc.addr,
                Instruction::Invalid {loc, ..} => loc.addr,
            })
        );

    Ok(ret)
}

pub fn find_roots(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    find_insns(ws, |insn| match insn {
        Instruction::Valid{loc, ..} => loc.xrefs.to.is_empty(),
        _ => false,
    })
}

pub fn find_call_targets(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    find_insns(ws, |insn| match insn {
        Instruction::Valid{loc, ..} => loc.xrefs.to.iter().any(|xref| match xref.typ {
            XrefType::Call => true,
            _ => false,
        }),
        _ => false,
    })
}

pub fn find_branch_targets(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    find_insns(ws, |insn| match insn {
        Instruction::Valid{loc, ..} => loc.xrefs.to.iter().any(|xref| match xref.typ {
            XrefType::UnconditionalJump => true,
            XrefType::ConditionalJump => true,
            _ => false,
        }),
        _ => false,
    })
}

fn get_section_by_name<'a>(ws: &'a Workspace, name: &[u8]) -> Result<Option<&'a Section>, Error> {
    match ws.get_obj()? {
        Object::PE(pe) => {
            match pe.sections.iter()
                .enumerate()
                .map(|(index, section)| (index, section.name))
                .find(|(_, sec_name)| sec_name == name) {
                    Some((index, _)) => Ok(Some(&ws.sections[index])),
                    None => Ok(None),
            }
        },
        _ => Err(Error::NotImplemented("analyzer for non-PE module"))
    }
}

// pub fn find_runtime_functions
//  on pe64, entries in .pdata are struct RUNTIME_FUNCTION
//
//  00000000 FunctionStart   dd ?                    ; offset rva
//  00000004 FunctionEnd     dd ?                    ; offset rva pastend
//  00000008 UnwindInfo      dd ?                    ; offset rva
//
// ref: https://stackoverflow.com/a/9794688/87207
//
// for k32, the first three entries should be:
//  - 1010
//  - 1060
//  - 1140
pub fn find_runtime_functions(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    match ws.get_obj()? {
        Object::PE(_) => {
            let mut ret: Vec<Rva> = vec![];

            if let Some(sec) = get_section_by_name(ws, b".pdata\x00\x00")? {
                let layout = ws.get_layout()?;
                for runtime_function in sec.buf.chunks_exact(mem::size_of::<u32>() * 3) {
                    let rva = LittleEndian::read_u32(&runtime_function) as Rva;
                    if rva == 0 {
                        // must be end of (valid) .pdata section
                        break;
                    } else if layout.is_rva_valid(rva) {
                        ret.push(rva);
                    } else {
                        warn!("malformed .pdata section: entry points outside of image");
                        return Ok(vec![]);
                    }
                }
            }

            Ok(ret)
        },
        _ => Err(Error::NotImplemented("analyzer for non-PE module"))
    }
}

// pub fn find_fixups

// pub fn find_ptrs
//  relies on the image being loaded at the base address
//  also relies on loc vs insn to be figured out.

/// ```
/// use lancelot::*;
/// use lancelot::rsrc::*;
/// use lancelot::analysis::*;
/// let ws = get_workspace(Rsrc::TINY);
/// assert_eq!(0, find_entrypoints(&ws).unwrap().len());
///
/// //let ws = get_workspace(Rsrc::K32);
/// //assert_eq!(1630, find_entrypoints(&ws).unwrap().len());
/// ```
pub fn find_entrypoints(ws: &Workspace) -> Result<Vec<Rva>, Error> {
    match ws.get_obj()? {
        Object::PE(pe) => {
            let mut ret: Vec<Rva> = vec![];
            if let Some(opt) = pe.header.optional_header {
                ret.push(opt.standard_fields.address_of_entry_point);
                ret.extend(
                    pe.exports.iter().map(|export| export.rva as Rva)
                );
            } else {
                info!("no optional header")
            }

            Ok(ret)
        },
        _ => Err(Error::NotImplemented("analyzer for non-PE module"))
    }
}

// pub fn find_sigs

// pub fn find_impossible_paths
// like:
//   MOV
//   MOV
//   INVALID
<|endoftext|>