<commit_before>use super::partial_crosslink_record::PartialCrosslinkRecord;
use super::recent_proposer_record::RecentPropserRecord;
use super::rlp::{ RlpStream, Encodable };
use super::utils::types::*;

pub struct ActiveState {
    pub height: u64,
    pub randao: Sha256Digest,
    pub ffg_voter_bitfield: Bitfield,
    pub recent_attesters: Vec<u32>, // TODO: should be u24
    pub partial_crosslinks: Vec<PartialCrosslinkRecord>,
    pub total_skip_count: u64,
    pub recent_proposers: Vec<RecentPropserRecord>
}

impl ActiveState {
    pub fn new_for_height(height: u64) -> ActiveState {
        ActiveState {
            height: height,
            randao: Sha256Digest::random(),
            ffg_voter_bitfield: Vec::new(),
            recent_attesters: Vec::new(),
            partial_crosslinks: Vec::new(),
            total_skip_count: 0,
            recent_proposers: Vec::new()
        }
    }

    pub fn num_recent_proposers(&self) -> usize {
        self.recent_proposers.len()
    }

    pub fn num_recent_attesters(&self) -> usize {
        self.recent_attesters.len()
    }
}

/*
 * RLP Encoding
 */
impl Encodable for ActiveState {
    fn rlp_append(&self, s: &mut RlpStream) {
        s.append(&self.height);
        s.append(&self.randao);
        s.append(&self.ffg_voter_bitfield);
        // s.append(&self.recent_attesters);
        // s.append(&self.partial_crosslinks);
        s.append(&self.total_skip_count);
        // s.append(&self.recent_proposers);
    }
}

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

    #[test]
    fn test_new_for_height() {
        let h = 1;
        let a = ActiveState::new_for_height(h);
        assert_eq!(a.height, h);
    }

    #[test]
    fn test_num_recent_proposers() {
        let mut a = ActiveState::new_for_height(1);
        for _ in 1..5 {
            a.recent_proposers.push(RecentPropserRecord::new(
                    1,
                    Sha256Digest::random(),
                    2));
        }
        assert_eq!(a.num_recent_proposers(), 4)
    }

    #[test]
    fn test_num_recent_attesters() {
        let mut a = ActiveState::new_for_height(1);
        for _ in 1..5 {
            a.recent_attesters.push(1);
        }
        assert_eq!(a.num_recent_attesters(), 4)
    }
}
<commit_msg>Add RLP serialization for active_state<commit_after>use super::partial_crosslink_record::PartialCrosslinkRecord;
use super::recent_proposer_record::RecentPropserRecord;
use super::rlp::{ RlpStream, Encodable };
use super::utils::types::*;

pub struct ActiveState {
    pub height: u64,
    pub randao: Sha256Digest,
    pub ffg_voter_bitfield: Bitfield,
    pub recent_attesters: Vec<u32>, // TODO: should be u24
    pub partial_crosslinks: Vec<PartialCrosslinkRecord>,
    pub total_skip_count: u64,
    pub recent_proposers: Vec<RecentPropserRecord>
}

impl ActiveState {
    pub fn new_for_height(height: u64) -> ActiveState {
        ActiveState {
            height: height,
            randao: Sha256Digest::random(),
            ffg_voter_bitfield: Vec::new(),
            recent_attesters: Vec::new(),
            partial_crosslinks: Vec::new(),
            total_skip_count: 0,
            recent_proposers: Vec::new()
        }
    }

    pub fn num_recent_proposers(&self) -> usize {
        self.recent_proposers.len()
    }

    pub fn num_recent_attesters(&self) -> usize {
        self.recent_attesters.len()
    }
}

/*
 * RLP Encoding
 */
impl Encodable for ActiveState {
    fn rlp_append(&self, s: &mut RlpStream) {
        s.append(&self.height);
        s.append(&self.randao);
        s.append(&self.ffg_voter_bitfield);
        s.append_list(&self.recent_attesters);
        s.append_list(&self.partial_crosslinks);
        s.append(&self.total_skip_count);
        s.append_list(&self.recent_proposers);
    }
}

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

    #[test]
    fn test_new_for_height() {
        let h = 1;
        let a = ActiveState::new_for_height(h);
        assert_eq!(a.height, h);
    }

    #[test]
    fn test_num_recent_proposers() {
        let mut a = ActiveState::new_for_height(1);
        for _ in 1..5 {
            a.recent_proposers.push(RecentPropserRecord::new(
                    1,
                    Sha256Digest::random(),
                    2));
        }
        assert_eq!(a.num_recent_proposers(), 4)
    }

    #[test]
    fn test_num_recent_attesters() {
        let mut a = ActiveState::new_for_height(1);
        for _ in 1..5 {
            a.recent_attesters.push(1);
        }
        assert_eq!(a.num_recent_attesters(), 4)
    }

    #[test]
    fn test_serialization() {
        let a = ActiveState {
            height: 100,
            randao: Sha256Digest::zero(),
            ffg_voter_bitfield: Vec::new(),
            recent_attesters: Vec::new(),
            partial_crosslinks: Vec::new(),
            total_skip_count: 99,
            recent_proposers: Vec::new()
        };
        let e = rlp::encode(&a);
        assert_eq!(e.len(), 39);
        assert_eq!(e[0], 100);
        assert_eq!(e[1], 160);
        assert_eq!(e[2..34], [0; 32]);
        assert_eq!(e[34], 128);
        assert_eq!(e[35], 192);
        assert_eq!(e[36], 192);
        assert_eq!(e[37], 99);
        assert_eq!(e[38], 192);
    }
}
<|endoftext|>


<commit_before>use std::result;

use crate::common::*;
use crate::FallibleIterator;

/// These values correspond to the BinaryAnnotationOpcode enum from the
/// cvinfo.h
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum BinaryAnnotationOpcode {
    /// Link time pdb contains PADDINGs.
    ///
    /// These are represented with the 0 opcode which is in some PDB
    /// implementation called "invalid".
    Eof = 0,
    /// param : start offset
    CodeOffset = 1,
    /// param : nth separated code chunk (main code chunk == 0)
    ChangeCodeOffsetBase = 2,
    /// param : delta of offset
    ChangeCodeOffset = 3,
    /// param : length of code, default next start
    ChangeCodeLength = 4,
    /// param : fileId
    ChangeFile = 5,
    /// param : line offset (signed)
    ChangeLineOffset = 6,
    /// param : how many lines, default 1
    ChangeLineEndDelta = 7,
    /// param : either 1 (default, for statement)
    ///         or 0 (for expression)
    ChangeRangeKind = 8,
    /// param : start column number, 0 means no column info
    ChangeColumnStart = 9,
    /// param : end column number delta (signed)
    ChangeColumnEndDelta = 10,
    /// param : ((sourceDelta << 4) | CodeDelta)
    ChangeCodeOffsetAndLineOffset = 11,
    /// param : codeLength, codeOffset
    ChangeCodeLengthAndCodeOffset = 12,
    /// param : end column number
    ChangeColumnEnd = 13,
    /// A non valid value
    Invalid,
}

impl From<u32> for BinaryAnnotationOpcode {
    fn from(value: u32) -> Self {
        match value {
            0 => BinaryAnnotationOpcode::Eof,
            1 => BinaryAnnotationOpcode::CodeOffset,
            2 => BinaryAnnotationOpcode::ChangeCodeOffsetBase,
            3 => BinaryAnnotationOpcode::ChangeCodeOffset,
            4 => BinaryAnnotationOpcode::ChangeCodeLength,
            5 => BinaryAnnotationOpcode::ChangeFile,
            6 => BinaryAnnotationOpcode::ChangeLineOffset,
            7 => BinaryAnnotationOpcode::ChangeLineEndDelta,
            8 => BinaryAnnotationOpcode::ChangeRangeKind,
            9 => BinaryAnnotationOpcode::ChangeColumnStart,
            10 => BinaryAnnotationOpcode::ChangeColumnEndDelta,
            11 => BinaryAnnotationOpcode::ChangeCodeOffsetAndLineOffset,
            12 => BinaryAnnotationOpcode::ChangeCodeLengthAndCodeOffset,
            13 => BinaryAnnotationOpcode::ChangeColumnEnd,
            _ => BinaryAnnotationOpcode::Invalid,
        }
    }
}

/// Represents a parsed `BinaryAnnotation`.
///
/// Binary annotations are used by `S_INLINESITE` to encode opcodes for how to
/// evaluate the state changes for inline information.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BinaryAnnotation {
    CodeOffset(u32),
    ChangeCodeOffsetBase(u32),
    ChangeCodeOffset(u32),
    ChangeCodeLength(u32),
    ChangeFile(FileIndex),
    ChangeLineOffset(i32),
    ChangeLineEndDelta(u32),
    ChangeRangeKind(u32),
    ChangeColumnStart(u32),
    ChangeColumnEndDelta(i32),
    ChangeCodeOffsetAndLineOffset(u32, i32),
    ChangeCodeLengthAndCodeOffset(u32, u32),
    ChangeColumnEnd(u32),
}

impl BinaryAnnotation {
    /// Does this annotation emit a line info?
    pub fn emits_line_info(self) -> bool {
        match self {
            BinaryAnnotation::ChangeCodeOffset(..) => true,
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(..) => true,
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(..) => true,
            _ => false,
        }
    }
}

/// An iterator over binary annotations used by `S_INLINESITE`.
#[derive(Clone, Debug, Default)]
pub struct BinaryAnnotationsIter<'t> {
    buffer: ParseBuffer<'t>,
}

impl<'t> BinaryAnnotationsIter<'t> {
    fn uncompress_next(&mut self) -> Result<u32> {
        let b1 = u32::from(self.buffer.parse::<u8>()?);
        if (b1 & 0x80) == 0x00 {
            let value = b1;
            return Ok(value);
        }

        let b2 = u32::from(self.buffer.parse::<u8>()?);
        if (b1 & 0xc0) == 0x80 {
            let value = (b1 & 0x3f) << 8 | b2;
            return Ok(value);
        }

        let b3 = u32::from(self.buffer.parse::<u8>()?);
        let b4 = u32::from(self.buffer.parse::<u8>()?);
        if (b1 & 0xe0) == 0xc0 {
            let value = ((b1 & 0x1f) << 24) | (b2 << 16) | (b3 << 8) | b4;
            return Ok(value);
        }

        Err(Error::InvalidCompressedAnnotation)
    }
}

/// Resembles `DecodeSignedInt32`.
fn decode_signed_operand(value: u32) -> i32 {
    if value & 1 != 0 {
        -((value >> 1) as i32)
    } else {
        (value >> 1) as i32
    }
}

impl<'t> FallibleIterator for BinaryAnnotationsIter<'t> {
    type Item = BinaryAnnotation;
    type Error = Error;

    fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
        if self.buffer.is_empty() {
            return Ok(None);
        }

        let op = self.uncompress_next()?;
        let annotation = match BinaryAnnotationOpcode::from(op) {
            BinaryAnnotationOpcode::Eof => {
                // This makes the end of the stream
                self.buffer = ParseBuffer::default();
                return Ok(None);
            }
            BinaryAnnotationOpcode::CodeOffset => {
                BinaryAnnotation::CodeOffset(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeCodeOffsetBase => {
                BinaryAnnotation::ChangeCodeOffsetBase(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeCodeOffset => {
                BinaryAnnotation::ChangeCodeOffset(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeCodeLength => {
                BinaryAnnotation::ChangeCodeLength(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeFile => {
                BinaryAnnotation::ChangeFile(FileIndex(self.uncompress_next()?))
            }
            BinaryAnnotationOpcode::ChangeLineOffset => {
                BinaryAnnotation::ChangeLineOffset(decode_signed_operand(self.uncompress_next()?))
            }
            BinaryAnnotationOpcode::ChangeLineEndDelta => {
                BinaryAnnotation::ChangeLineEndDelta(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeRangeKind => {
                BinaryAnnotation::ChangeRangeKind(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeColumnStart => {
                BinaryAnnotation::ChangeColumnStart(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeColumnEndDelta => BinaryAnnotation::ChangeColumnEndDelta(
                decode_signed_operand(self.uncompress_next()?),
            ),
            BinaryAnnotationOpcode::ChangeCodeOffsetAndLineOffset => {
                let operand = self.uncompress_next()?;
                BinaryAnnotation::ChangeCodeOffsetAndLineOffset(
                    operand & 0xf,
                    decode_signed_operand(operand >> 4),
                )
            }
            BinaryAnnotationOpcode::ChangeCodeLengthAndCodeOffset => {
                BinaryAnnotation::ChangeCodeLengthAndCodeOffset(
                    self.uncompress_next()?,
                    self.uncompress_next()?,
                )
            }
            BinaryAnnotationOpcode::ChangeColumnEnd => {
                BinaryAnnotation::ChangeColumnEnd(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::Invalid => {
                return Err(Error::UnknownBinaryAnnotation(op));
            }
        };

        Ok(Some(annotation))
    }
}

/// Binary annotations of a symbol.
///
/// The binary annotation mechanism supports recording a list of annotations in an instruction
/// stream. The X64 unwind code and the DWARF standard have a similar design.
///
/// Binary annotations are primarily used as line programs for inline function calls.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct BinaryAnnotations<'t> {
    data: &'t [u8],
}

impl<'t> BinaryAnnotations<'t> {
    /// Creates a new instance of binary annotations.
    pub(crate) fn new(data: &'t [u8]) -> Self {
        BinaryAnnotations { data }
    }

    /// Iterates through binary annotations.
    pub fn iter(&self) -> BinaryAnnotationsIter<'t> {
        BinaryAnnotationsIter {
            buffer: ParseBuffer::from(self.data),
        }
    }
}

#[test]
fn test_binary_annotation_iter() {
    let inp = b"\x0b\x03\x06\n\x03\x08\x06\x06\x03-\x06\x08\x03\x07\x0br\x06\x06\x0c\x03\x07\x06\x0f\x0c\x06\x05\x00\x00";
    let annotations = BinaryAnnotations::new(inp)
        .iter()
        .collect::<Vec<_>>()
        .unwrap();

    assert_eq!(
        annotations,
        vec![
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(3, 0),
            BinaryAnnotation::ChangeLineOffset(5),
            BinaryAnnotation::ChangeCodeOffset(8),
            BinaryAnnotation::ChangeLineOffset(3),
            BinaryAnnotation::ChangeCodeOffset(45),
            BinaryAnnotation::ChangeLineOffset(4),
            BinaryAnnotation::ChangeCodeOffset(7),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(2, -3),
            BinaryAnnotation::ChangeLineOffset(3),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(3, 7),
            BinaryAnnotation::ChangeLineOffset(-7),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(6, 5)
        ]
    );

    let inp = b"\x03P\x06\x0e\x03\x0c\x06\x04\x032\x06\x06\x03T\x0b#\x0b\\\x0bC\x0b/\x06\x04\x0c-\t\x03;\x06\x1d\x0c\x05\x06\x00\x00";
    let annotations = BinaryAnnotations::new(inp)
        .iter()
        .collect::<Vec<_>>()
        .unwrap();

    assert_eq!(
        annotations,
        vec![
            BinaryAnnotation::ChangeCodeOffset(80),
            BinaryAnnotation::ChangeLineOffset(7),
            BinaryAnnotation::ChangeCodeOffset(12),
            BinaryAnnotation::ChangeLineOffset(2),
            BinaryAnnotation::ChangeCodeOffset(50),
            BinaryAnnotation::ChangeLineOffset(3),
            BinaryAnnotation::ChangeCodeOffset(84),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(3, 1),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(12, -2),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(3, 2),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(15, 1),
            BinaryAnnotation::ChangeLineOffset(2),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(45, 9),
            BinaryAnnotation::ChangeCodeOffset(59),
            BinaryAnnotation::ChangeLineOffset(-14),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(5, 6),
        ]
    );
}
<commit_msg>Remove invalid annotation operator and add comments<commit_after>use crate::common::*;
use crate::FallibleIterator;

/// These values correspond to the BinaryAnnotationOpcode enum from the
/// cvinfo.h
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum BinaryAnnotationOpcode {
    /// Link time pdb contains PADDINGs.
    ///
    /// These are represented with the 0 opcode which is in some PDB
    /// implementation called "invalid".
    Eof = 0,
    /// param : start offset
    CodeOffset = 1,
    /// param : nth separated code chunk (main code chunk == 0)
    ChangeCodeOffsetBase = 2,
    /// param : delta of offset
    ChangeCodeOffset = 3,
    /// param : length of code, default next start
    ChangeCodeLength = 4,
    /// param : fileId
    ChangeFile = 5,
    /// param : line offset (signed)
    ChangeLineOffset = 6,
    /// param : how many lines, default 1
    ChangeLineEndDelta = 7,
    /// param : either 1 (default, for statement)
    ///         or 0 (for expression)
    ChangeRangeKind = 8,
    /// param : start column number, 0 means no column info
    ChangeColumnStart = 9,
    /// param : end column number delta (signed)
    ChangeColumnEndDelta = 10,
    /// param : ((sourceDelta << 4) | CodeDelta)
    ChangeCodeOffsetAndLineOffset = 11,
    /// param : codeLength, codeOffset
    ChangeCodeLengthAndCodeOffset = 12,
    /// param : end column number
    ChangeColumnEnd = 13,
}

impl BinaryAnnotationOpcode {
    fn parse(value: u32) -> Result<Self> {
        Ok(match value {
            0 => BinaryAnnotationOpcode::Eof,
            1 => BinaryAnnotationOpcode::CodeOffset,
            2 => BinaryAnnotationOpcode::ChangeCodeOffsetBase,
            3 => BinaryAnnotationOpcode::ChangeCodeOffset,
            4 => BinaryAnnotationOpcode::ChangeCodeLength,
            5 => BinaryAnnotationOpcode::ChangeFile,
            6 => BinaryAnnotationOpcode::ChangeLineOffset,
            7 => BinaryAnnotationOpcode::ChangeLineEndDelta,
            8 => BinaryAnnotationOpcode::ChangeRangeKind,
            9 => BinaryAnnotationOpcode::ChangeColumnStart,
            10 => BinaryAnnotationOpcode::ChangeColumnEndDelta,
            11 => BinaryAnnotationOpcode::ChangeCodeOffsetAndLineOffset,
            12 => BinaryAnnotationOpcode::ChangeCodeLengthAndCodeOffset,
            13 => BinaryAnnotationOpcode::ChangeColumnEnd,
            _ => return Err(Error::UnknownBinaryAnnotation(value)),
        })
    }
}

/// Represents a parsed `BinaryAnnotation`.
///
/// Binary annotations are used by `S_INLINESITE` to encode opcodes for how to
/// evaluate the state changes for inline information.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BinaryAnnotation {
    CodeOffset(u32),
    ChangeCodeOffsetBase(u32),
    ChangeCodeOffset(u32),
    ChangeCodeLength(u32),
    ChangeFile(FileIndex),
    ChangeLineOffset(i32),
    ChangeLineEndDelta(u32),
    ChangeRangeKind(u32),
    ChangeColumnStart(u32),
    ChangeColumnEndDelta(i32),
    ChangeCodeOffsetAndLineOffset(u32, i32),
    ChangeCodeLengthAndCodeOffset(u32, u32),
    ChangeColumnEnd(u32),
}

impl BinaryAnnotation {
    /// Does this annotation emit a line info?
    pub fn emits_line_info(self) -> bool {
        match self {
            BinaryAnnotation::ChangeCodeOffset(..) => true,
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(..) => true,
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(..) => true,
            _ => false,
        }
    }
}

/// An iterator over binary annotations used by `S_INLINESITE`.
#[derive(Clone, Debug, Default)]
pub struct BinaryAnnotationsIter<'t> {
    buffer: ParseBuffer<'t>,
}

impl<'t> BinaryAnnotationsIter<'t> {
    /// Parse a compact version of an unsigned integer.
    ///
    /// This implements `CVUncompressData`, which can decode numbers no larger than 0x1FFFFFFF. It
    /// seems that values compressed this way are only used for binary annotations at this point.
    fn uncompress_next(&mut self) -> Result<u32> {
        let b1 = u32::from(self.buffer.parse::<u8>()?);
        if (b1 & 0x80) == 0x00 {
            let value = b1;
            return Ok(value);
        }

        let b2 = u32::from(self.buffer.parse::<u8>()?);
        if (b1 & 0xc0) == 0x80 {
            let value = (b1 & 0x3f) << 8 | b2;
            return Ok(value);
        }

        let b3 = u32::from(self.buffer.parse::<u8>()?);
        let b4 = u32::from(self.buffer.parse::<u8>()?);
        if (b1 & 0xe0) == 0xc0 {
            let value = ((b1 & 0x1f) << 24) | (b2 << 16) | (b3 << 8) | b4;
            return Ok(value);
        }

        Err(Error::InvalidCompressedAnnotation)
    }
}

/// Resembles `DecodeSignedInt32`.
fn decode_signed_operand(value: u32) -> i32 {
    if value & 1 != 0 {
        -((value >> 1) as i32)
    } else {
        (value >> 1) as i32
    }
}

impl<'t> FallibleIterator for BinaryAnnotationsIter<'t> {
    type Item = BinaryAnnotation;
    type Error = Error;

    fn next(&mut self) -> Result<Option<Self::Item>> {
        if self.buffer.is_empty() {
            return Ok(None);
        }

        let op = self.uncompress_next()?;
        let annotation = match BinaryAnnotationOpcode::parse(op)? {
            BinaryAnnotationOpcode::Eof => {
                // This makes the end of the stream
                self.buffer = ParseBuffer::default();
                return Ok(None);
            }
            BinaryAnnotationOpcode::CodeOffset => {
                BinaryAnnotation::CodeOffset(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeCodeOffsetBase => {
                BinaryAnnotation::ChangeCodeOffsetBase(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeCodeOffset => {
                BinaryAnnotation::ChangeCodeOffset(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeCodeLength => {
                BinaryAnnotation::ChangeCodeLength(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeFile => {
                BinaryAnnotation::ChangeFile(FileIndex(self.uncompress_next()?))
            }
            BinaryAnnotationOpcode::ChangeLineOffset => {
                BinaryAnnotation::ChangeLineOffset(decode_signed_operand(self.uncompress_next()?))
            }
            BinaryAnnotationOpcode::ChangeLineEndDelta => {
                BinaryAnnotation::ChangeLineEndDelta(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeRangeKind => {
                BinaryAnnotation::ChangeRangeKind(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeColumnStart => {
                BinaryAnnotation::ChangeColumnStart(self.uncompress_next()?)
            }
            BinaryAnnotationOpcode::ChangeColumnEndDelta => BinaryAnnotation::ChangeColumnEndDelta(
                decode_signed_operand(self.uncompress_next()?),
            ),
            BinaryAnnotationOpcode::ChangeCodeOffsetAndLineOffset => {
                let operand = self.uncompress_next()?;
                BinaryAnnotation::ChangeCodeOffsetAndLineOffset(
                    operand & 0xf,
                    decode_signed_operand(operand >> 4),
                )
            }
            BinaryAnnotationOpcode::ChangeCodeLengthAndCodeOffset => {
                BinaryAnnotation::ChangeCodeLengthAndCodeOffset(
                    self.uncompress_next()?,
                    self.uncompress_next()?,
                )
            }
            BinaryAnnotationOpcode::ChangeColumnEnd => {
                BinaryAnnotation::ChangeColumnEnd(self.uncompress_next()?)
            }
        };

        Ok(Some(annotation))
    }
}

/// Binary annotations of a symbol.
///
/// The binary annotation mechanism supports recording a list of annotations in an instruction
/// stream. The X64 unwind code and the DWARF standard have a similar design.
///
/// Binary annotations are primarily used as line programs for inline function calls.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct BinaryAnnotations<'t> {
    data: &'t [u8],
}

impl<'t> BinaryAnnotations<'t> {
    /// Creates a new instance of binary annotations.
    pub(crate) fn new(data: &'t [u8]) -> Self {
        BinaryAnnotations { data }
    }

    /// Iterates through binary annotations.
    pub fn iter(&self) -> BinaryAnnotationsIter<'t> {
        BinaryAnnotationsIter {
            buffer: ParseBuffer::from(self.data),
        }
    }
}

#[test]
fn test_binary_annotation_iter() {
    let inp = b"\x0b\x03\x06\n\x03\x08\x06\x06\x03-\x06\x08\x03\x07\x0br\x06\x06\x0c\x03\x07\x06\x0f\x0c\x06\x05\x00\x00";
    let annotations = BinaryAnnotations::new(inp)
        .iter()
        .collect::<Vec<_>>()
        .unwrap();

    assert_eq!(
        annotations,
        vec![
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(3, 0),
            BinaryAnnotation::ChangeLineOffset(5),
            BinaryAnnotation::ChangeCodeOffset(8),
            BinaryAnnotation::ChangeLineOffset(3),
            BinaryAnnotation::ChangeCodeOffset(45),
            BinaryAnnotation::ChangeLineOffset(4),
            BinaryAnnotation::ChangeCodeOffset(7),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(2, -3),
            BinaryAnnotation::ChangeLineOffset(3),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(3, 7),
            BinaryAnnotation::ChangeLineOffset(-7),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(6, 5)
        ]
    );

    let inp = b"\x03P\x06\x0e\x03\x0c\x06\x04\x032\x06\x06\x03T\x0b#\x0b\\\x0bC\x0b/\x06\x04\x0c-\t\x03;\x06\x1d\x0c\x05\x06\x00\x00";
    let annotations = BinaryAnnotations::new(inp)
        .iter()
        .collect::<Vec<_>>()
        .unwrap();

    assert_eq!(
        annotations,
        vec![
            BinaryAnnotation::ChangeCodeOffset(80),
            BinaryAnnotation::ChangeLineOffset(7),
            BinaryAnnotation::ChangeCodeOffset(12),
            BinaryAnnotation::ChangeLineOffset(2),
            BinaryAnnotation::ChangeCodeOffset(50),
            BinaryAnnotation::ChangeLineOffset(3),
            BinaryAnnotation::ChangeCodeOffset(84),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(3, 1),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(12, -2),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(3, 2),
            BinaryAnnotation::ChangeCodeOffsetAndLineOffset(15, 1),
            BinaryAnnotation::ChangeLineOffset(2),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(45, 9),
            BinaryAnnotation::ChangeCodeOffset(59),
            BinaryAnnotation::ChangeLineOffset(-14),
            BinaryAnnotation::ChangeCodeLengthAndCodeOffset(5, 6),
        ]
    );
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;

use bincode::Options as _;
use core::str::FromStr;
use rust_decimal::Decimal;

macro_rules! bench_decimal_op {
    ($name:ident, $op:tt, $y:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            let x = Decimal::from_str("2.01").unwrap();
            let y = Decimal::from_str($y).unwrap();
            b.iter(|| {
                let result = x $op y;
                ::test::black_box(result);
            });
        }
    }
}

macro_rules! bench_fold_op {
    ($name:ident, $op:tt, $init:expr, $count:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            fn fold(values: &[Decimal]) -> Decimal {
                let mut acc: Decimal = $init.into();
                for value in values {
                    acc = acc $op value;
                }
                acc
            }

            let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect());
            b.iter(|| {
                let result = fold(&values);
                ::test::black_box(result);
            });
        }
    }
}

/* Add */
bench_decimal_op!(add_one, +, "1");
bench_decimal_op!(add_two, +, "2");
bench_decimal_op!(add_one_hundred, +, "100");
bench_decimal_op!(add_point_zero_one, +, "0.01");
bench_decimal_op!(add_negative_point_five, +, "-0.5");
bench_decimal_op!(add_pi, +, "3.1415926535897932384626433832");
bench_decimal_op!(add_negative_pi, +, "-3.1415926535897932384626433832");

bench_fold_op!(add_10k, +, 0, 10_000);

/* Sub */
bench_decimal_op!(sub_one, -, "1");
bench_decimal_op!(sub_two, -, "2");
bench_decimal_op!(sub_one_hundred, -, "100");
bench_decimal_op!(sub_point_zero_one, -, "0.01");
bench_decimal_op!(sub_negative_point_five, -, "-0.5");
bench_decimal_op!(sub_pi, -, "3.1415926535897932384626433832");
bench_decimal_op!(sub_negative_pi, -, "-3.1415926535897932384626433832");

bench_fold_op!(sub_10k, -, 5_000_000, 10_000);

/* Mul */
bench_decimal_op!(mul_one, *, "1");
bench_decimal_op!(mul_two, *, "2");
bench_decimal_op!(mul_one_hundred, *, "100");
bench_decimal_op!(mul_point_zero_one, *, "0.01");
bench_decimal_op!(mul_negative_point_five, *, "-0.5");
bench_decimal_op!(mul_pi, *, "3.1415926535897932384626433832");
bench_decimal_op!(mul_negative_pi, *, "-3.1415926535897932384626433832");

/* Div */
bench_decimal_op!(div_one, /, "1");
bench_decimal_op!(div_two, /, "2");
bench_decimal_op!(div_one_hundred, /, "100");
bench_decimal_op!(div_point_zero_one, /, "0.01");
bench_decimal_op!(div_negative_point_five, /, "-0.5");
bench_decimal_op!(div_pi, /, "3.1415926535897932384626433832");
bench_decimal_op!(div_negative_pi, /, "-3.1415926535897932384626433832");

bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);

/* Iteration */
struct DecimalIterator {
    count: usize,
}

impl DecimalIterator {
    fn new() -> DecimalIterator {
        DecimalIterator { count: 0 }
    }
}

impl Iterator for DecimalIterator {
    type Item = Decimal;

    fn next(&mut self) -> Option<Decimal> {
        self.count += 1;
        if self.count < 6 {
            Some(Decimal::new(314, 2))
        } else {
            None
        }
    }
}

#[bench]
fn iterator_individual(b: &mut ::test::Bencher) {
    b.iter(|| {
        let mut result = Decimal::new(0, 0);
        let iterator = DecimalIterator::new();
        for i in iterator {
            result += i;
        }
        ::test::black_box(result);
    });
}

#[bench]
fn iterator_sum(b: &mut ::test::Bencher) {
    b.iter(|| {
        let result: Decimal = DecimalIterator::new().sum();
        ::test::black_box(result);
    });
}

const SAMPLE_STRS: &[&str] = &[
    "3950.123456",
    "3950",
    "0.1",
    "0.01",
    "0.001",
    "0.0001",
    "0.00001",
    "0.000001",
    "1",
    "-100",
    "-123.456",
    "119996.25",
    "1000000",
    "9999999.99999",
    "12340.56789",
];

#[bench]
fn serialize_bincode(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for d in &decimals {
            let bytes = bincode::options().serialize(d).unwrap();
            test::black_box(bytes);
        }
    })
}

#[cfg(feature = "serde-str")]
#[bench]
fn deserialize_bincode(b: &mut test::Bencher) {
    let payloads: Vec<Vec<u8>> = SAMPLE_STRS
        .iter()
        .map(|s| bincode::options().serialize(&Decimal::from_str(s).unwrap()).unwrap())
        .collect();

    b.iter(|| {
        for payload in &payloads {
            let decimal: Decimal = bincode::options().deserialize(payload).unwrap();
            test::black_box(decimal);
        }
    })
}

#[bench]
fn decimal_from_str(b: &mut test::Bencher) {
    b.iter(|| {
        for s in SAMPLE_STRS {
            let result = Decimal::from_str(s).unwrap();
            test::black_box(result);
        }
    })
}

#[bench]
fn decimal_to_string(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for s in decimals.iter() {
            let string = s.to_string();
            test::black_box(string);
        }
    })
}

#[cfg(feature = "postgres")]
#[bench]
fn to_from_sql(b: &mut ::test::Bencher) {
    use bytes::BytesMut;
    use postgres::types::{FromSql, Kind, ToSql, Type};

    let samples: Vec<Decimal> = test::black_box(SAMPLE_STRS.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
    let t = Type::new("".into(), 0, Kind::Simple, "".into());
    let mut bytes: BytesMut = BytesMut::with_capacity(100).into();

    b.iter(|| {
        for _ in 0..100 {
            for sample in &samples {
                bytes.clear();
                sample.to_sql(&t, &mut bytes).unwrap();
                let result = Decimal::from_sql(&t, &bytes).unwrap();
                ::test::black_box(result);
            }
        }
    });
}

#[bench]
fn powi(b: &mut ::test::Bencher) {
    // These exponents have to be fairly small because multiplcation overflows easily
    let samples = &[
        (Decimal::from_str("36.7").unwrap(), 5),
        (Decimal::from_str("0.00000007").unwrap(), 5),
        (Decimal::from(2), 64),
        (Decimal::from_str("8819287.19276555").unwrap(), 3),
        (Decimal::from_str("-8819287.19276555").unwrap(), 3),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.0.powi(sample.1);
            ::test::black_box(result);
        }
    });
}

#[bench]
fn sqrt(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19276555").unwrap(),
        Decimal::from_str("-8819287.19276555").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.sqrt();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn exp(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8.19").unwrap(),
        Decimal::from_str("-8.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.exp();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_cdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_cdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_pdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_pdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn ln(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19").unwrap(),
        Decimal::from_str("-8819287.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.ln();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn erf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from(0),
        Decimal::from(1),
        Decimal::from_str("-0.98717").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from_str("0.1111").unwrap(),
        Decimal::from_str("0.4").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.erf();
            ::test::black_box(result);
        }
    });
}
<commit_msg>Added additional bench for division<commit_after>#![feature(test)]

extern crate test;

use bincode::Options as _;
use core::str::FromStr;
use rust_decimal::Decimal;

macro_rules! bench_decimal_op {
    ($name:ident, $op:tt, $x:expr, $y:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            let x = Decimal::from_str($x).unwrap();
            let y = Decimal::from_str($y).unwrap();
            b.iter(|| {
                let result = x $op y;
                ::test::black_box(result);
            });
        }
    }
}

macro_rules! bench_fold_op {
    ($name:ident, $op:tt, $init:expr, $count:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            fn fold(values: &[Decimal]) -> Decimal {
                let mut acc: Decimal = $init.into();
                for value in values {
                    acc = acc $op value;
                }
                acc
            }

            let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect());
            b.iter(|| {
                let result = fold(&values);
                ::test::black_box(result);
            });
        }
    }
}

/* Add */
bench_decimal_op!(add_one, +, "2.01", "1");
bench_decimal_op!(add_two, +, "2.01", "2");
bench_decimal_op!(add_one_hundred, +, "2.01", "100");
bench_decimal_op!(add_point_zero_one, +, "2.01", "0.01");
bench_decimal_op!(add_negative_point_five, +, "2.01", "-0.5");
bench_decimal_op!(add_pi, +, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(add_negative_pi, +, "2.01", "-3.1415926535897932384626433832");

bench_fold_op!(add_10k, +, 0, 10_000);

/* Sub */
bench_decimal_op!(sub_one, -, "2.01", "1");
bench_decimal_op!(sub_two, -, "2.01", "2");
bench_decimal_op!(sub_one_hundred, -, "2.01", "100");
bench_decimal_op!(sub_point_zero_one, -, "2.01", "0.01");
bench_decimal_op!(sub_negative_point_five, -, "2.01", "-0.5");
bench_decimal_op!(sub_pi, -, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(sub_negative_pi, -, "2.01", "-3.1415926535897932384626433832");

bench_fold_op!(sub_10k, -, 5_000_000, 10_000);

/* Mul */
bench_decimal_op!(mul_one, *, "2.01", "1");
bench_decimal_op!(mul_two, *, "2.01", "2");
bench_decimal_op!(mul_one_hundred, *, "2.01", "100");
bench_decimal_op!(mul_point_zero_one, *, "2.01", "0.01");
bench_decimal_op!(mul_negative_point_five, *, "2.01", "-0.5");
bench_decimal_op!(mul_pi, *, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(mul_negative_pi, *, "2.01", "-3.1415926535897932384626433832");

/* Div */
bench_decimal_op!(div_one, /, "2.01", "1");
bench_decimal_op!(div_two, /, "2.01", "2");
bench_decimal_op!(div_one_hundred, /, "2.01", "100");
bench_decimal_op!(div_point_zero_one, /, "2.01", "0.01");
bench_decimal_op!(div_negative_point_five, /, "2.01", "-0.5");
bench_decimal_op!(div_pi, /, "2.01", "3.1415926535897932384626433832");
bench_decimal_op!(div_negative_pi, /, "2.01", "-3.1415926535897932384626433832");
bench_decimal_op!(div_no_underflow, /, "1.02343545345", "0.35454343453");

bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);

/* Iteration */
struct DecimalIterator {
    count: usize,
}

impl DecimalIterator {
    fn new() -> DecimalIterator {
        DecimalIterator { count: 0 }
    }
}

impl Iterator for DecimalIterator {
    type Item = Decimal;

    fn next(&mut self) -> Option<Decimal> {
        self.count += 1;
        if self.count < 6 {
            Some(Decimal::new(314, 2))
        } else {
            None
        }
    }
}

#[bench]
fn iterator_individual(b: &mut ::test::Bencher) {
    b.iter(|| {
        let mut result = Decimal::new(0, 0);
        let iterator = DecimalIterator::new();
        for i in iterator {
            result += i;
        }
        ::test::black_box(result);
    });
}

#[bench]
fn iterator_sum(b: &mut ::test::Bencher) {
    b.iter(|| {
        let result: Decimal = DecimalIterator::new().sum();
        ::test::black_box(result);
    });
}

const SAMPLE_STRS: &[&str] = &[
    "3950.123456",
    "3950",
    "0.1",
    "0.01",
    "0.001",
    "0.0001",
    "0.00001",
    "0.000001",
    "1",
    "-100",
    "-123.456",
    "119996.25",
    "1000000",
    "9999999.99999",
    "12340.56789",
];

#[bench]
fn serialize_bincode(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for d in &decimals {
            let bytes = bincode::options().serialize(d).unwrap();
            test::black_box(bytes);
        }
    })
}

#[cfg(feature = "serde-str")]
#[bench]
fn deserialize_bincode(b: &mut test::Bencher) {
    let payloads: Vec<Vec<u8>> = SAMPLE_STRS
        .iter()
        .map(|s| bincode::options().serialize(&Decimal::from_str(s).unwrap()).unwrap())
        .collect();

    b.iter(|| {
        for payload in &payloads {
            let decimal: Decimal = bincode::options().deserialize(payload).unwrap();
            test::black_box(decimal);
        }
    })
}

#[bench]
fn decimal_from_str(b: &mut test::Bencher) {
    b.iter(|| {
        for s in SAMPLE_STRS {
            let result = Decimal::from_str(s).unwrap();
            test::black_box(result);
        }
    })
}

#[bench]
fn decimal_to_string(b: &mut test::Bencher) {
    let decimals: Vec<Decimal> = SAMPLE_STRS.iter().map(|s| Decimal::from_str(s).unwrap()).collect();

    b.iter(|| {
        for s in decimals.iter() {
            let string = s.to_string();
            test::black_box(string);
        }
    })
}

#[cfg(feature = "postgres")]
#[bench]
fn to_from_sql(b: &mut ::test::Bencher) {
    use bytes::BytesMut;
    use postgres::types::{FromSql, Kind, ToSql, Type};

    let samples: Vec<Decimal> = test::black_box(SAMPLE_STRS.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
    let t = Type::new("".into(), 0, Kind::Simple, "".into());
    let mut bytes: BytesMut = BytesMut::with_capacity(100).into();

    b.iter(|| {
        for _ in 0..100 {
            for sample in &samples {
                bytes.clear();
                sample.to_sql(&t, &mut bytes).unwrap();
                let result = Decimal::from_sql(&t, &bytes).unwrap();
                ::test::black_box(result);
            }
        }
    });
}

#[bench]
fn powi(b: &mut ::test::Bencher) {
    // These exponents have to be fairly small because multiplcation overflows easily
    let samples = &[
        (Decimal::from_str("36.7").unwrap(), 5),
        (Decimal::from_str("0.00000007").unwrap(), 5),
        (Decimal::from(2), 64),
        (Decimal::from_str("8819287.19276555").unwrap(), 3),
        (Decimal::from_str("-8819287.19276555").unwrap(), 3),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.0.powi(sample.1);
            ::test::black_box(result);
        }
    });
}

#[bench]
fn sqrt(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19276555").unwrap(),
        Decimal::from_str("-8819287.19276555").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.sqrt();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn exp(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8.19").unwrap(),
        Decimal::from_str("-8.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.exp();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_cdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_cdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_pdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_pdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn ln(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19").unwrap(),
        Decimal::from_str("-8819287.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.ln();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn erf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from(0),
        Decimal::from(1),
        Decimal::from_str("-0.98717").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from_str("0.1111").unwrap(),
        Decimal::from_str("0.4").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.erf();
            ::test::black_box(result);
        }
    });
}
<|endoftext|>


<commit_before>#![feature(test)]

#[macro_use]
extern crate generic_array;
extern crate test;
extern crate rand;
extern crate typenum;
extern crate spatial;

use rand::Rng;
use test::Bencher;
use spatial::{Point, Rect, MbrQuery, RStar, RStarTree};
use typenum::U3;

fn generate_tree_with_size(count: usize) -> RStarTree<f64, U3, Point<f64, U3>, usize>
{
    let mut tree_map = RStar::new_with_max(512);
    let mut rng = rand::thread_rng();
    for i in 0..count {
        tree_map.insert(Point::new(arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()]), i);
    }
    tree_map
}

#[bench]
fn insert_rng_bench_3d_10(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(10);
    });
}

#[bench]
fn insert_rng_bench_3d_100(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(100);
    });
}

#[bench]
fn insert_rng_bench_3d_1000(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(1000);
    });
}

#[bench]
fn insert_rng_bench_3d_10000(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(10000);
    });
}

fn search_rng_bench_3d(b: &mut Bencher, size: usize) {
    let tree_map = generate_tree_with_size(size);
    let mut rng = rand::thread_rng();
    b.iter( || {
        let x_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        let y_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        tree_map.iter_query(MbrQuery::Overlaps(Rect::from_corners(x_array,y_array)))
            .count();
    });
}

fn remove_rng_bench_3d(b: &mut Bencher, size: usize) {
    let mut tree_map = generate_tree_with_size(size);
    let mut rng = rand::thread_rng();
    b.iter( || {
        let x_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        let y_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        let removed = tree_map.remove(MbrQuery::Overlaps(Rect::from_corners(x_array,y_array)));
        for(lshape, item) in removed {
            tree_map.insert(lshape, item);
        }
    });
}

#[bench]
fn search_bench_3d_10(b: &mut Bencher) {
    search_rng_bench_3d(b, 10);

}

#[bench]
fn search_bench_3d_100(b: &mut Bencher) {
    search_rng_bench_3d(b, 100);
}

#[bench]
fn search_bench_3d_1000(b: &mut Bencher) {
    search_rng_bench_3d(b, 1000);
}

#[bench]
fn search_bench_3d_10000(b: &mut Bencher) {
    search_rng_bench_3d(b, 10000);
}

#[bench]
fn remove_bench_3d_10(b: &mut Bencher) {
    remove_rng_bench_3d(b, 10);

}

#[bench]
fn remove_bench_3d_100(b: &mut Bencher) {
    remove_rng_bench_3d(b, 100);
}

#[bench]
fn remove_bench_3d_1000(b: &mut Bencher) {
    remove_rng_bench_3d(b, 1000);
}

#[bench]
fn remove_bench_3d_10000(b: &mut Bencher) {
    remove_rng_bench_3d(b, 10000);
}<commit_msg>set size to 1024.<commit_after>#![feature(test)]

#[macro_use]
extern crate generic_array;
extern crate test;
extern crate rand;
extern crate typenum;
extern crate spatial;

use rand::Rng;
use test::Bencher;
use spatial::{Point, Rect, MbrQuery, RStar, RStarTree};
use typenum::U3;

fn generate_tree_with_size(count: usize) -> RStarTree<f64, U3, Point<f64, U3>, usize>
{
    let mut tree_map = RStar::new_with_max(1024);
    let mut rng = rand::thread_rng();
    for i in 0..count {
        tree_map.insert(Point::new(arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()]), i);
    }
    tree_map
}

#[bench]
fn insert_rng_bench_3d_10(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(10);
    });
}

#[bench]
fn insert_rng_bench_3d_100(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(100);
    });
}

#[bench]
fn insert_rng_bench_3d_1000(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(1000);
    });
}

#[bench]
fn insert_rng_bench_3d_10000(b: &mut Bencher) {
    b.iter( || {
        generate_tree_with_size(10000);
    });
}

fn search_rng_bench_3d(b: &mut Bencher, size: usize) {
    let tree_map = generate_tree_with_size(size);
    let mut rng = rand::thread_rng();
    b.iter( || {
        let x_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        let y_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        tree_map.iter_query(MbrQuery::Overlaps(Rect::from_corners(x_array,y_array)))
            .count();
    });
}

fn remove_rng_bench_3d(b: &mut Bencher, size: usize) {
    let mut tree_map = generate_tree_with_size(size);
    let mut rng = rand::thread_rng();
    b.iter( || {
        let x_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        let y_array = arr![f64; rng.next_f64(), rng.next_f64(), rng.next_f64()];
        let removed = tree_map.remove(MbrQuery::Overlaps(Rect::from_corners(x_array,y_array)));
        for(lshape, item) in removed {
            tree_map.insert(lshape, item);
        }
    });
}

#[bench]
fn search_bench_3d_10(b: &mut Bencher) {
    search_rng_bench_3d(b, 10);

}

#[bench]
fn search_bench_3d_100(b: &mut Bencher) {
    search_rng_bench_3d(b, 100);
}

#[bench]
fn search_bench_3d_1000(b: &mut Bencher) {
    search_rng_bench_3d(b, 1000);
}

#[bench]
fn search_bench_3d_10000(b: &mut Bencher) {
    search_rng_bench_3d(b, 10000);
}

#[bench]
fn remove_bench_3d_10(b: &mut Bencher) {
    remove_rng_bench_3d(b, 10);

}

#[bench]
fn remove_bench_3d_100(b: &mut Bencher) {
    remove_rng_bench_3d(b, 100);
}

#[bench]
fn remove_bench_3d_1000(b: &mut Bencher) {
    remove_rng_bench_3d(b, 1000);
}

#[bench]
fn remove_bench_3d_10000(b: &mut Bencher) {
    remove_rng_bench_3d(b, 10000);
}<|endoftext|>


<commit_before>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::trie::*;
use adapton::engine::*;

// The code that we want to compare/measure under naive versus DCG engines:
fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
    v.into_iter().fold(t, |acc, i| TrieIntro::extend(name_of_usize(i), acc, i))
}

fn bench_naive_add_dups() {
    init_naive();
    let mut naive_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    for i in vec![1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3].iter() {
        v.push(*i);
        naive_input = doit(v.clone(), naive_input.clone())
    }
}

fn bench_dcg_add_dups() {
    init_dcg();
    let mut dcg_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    for i in vec![1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3].iter() {
        v.push(*i);
        dcg_input = doit(v.clone(), dcg_input.clone())
    }
}

#[bench]
fn benchmark_naive_trie(b: &mut Bencher) {
    b.bench_n(1, |b| b.iter(|| bench_naive_add_dups()))
}

#[bench]
fn benchmark_dcg_trie(b: &mut Bencher) {
    b.bench_n(1, |b| b.iter(|| bench_dcg_add_dups()))
}
<commit_msg>Benchmark more than 1 iteration<commit_after>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::trie::*;
use adapton::engine::*;

// The code that we want to compare/measure under naive versus DCG engines:
fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
    v.into_iter().fold(t, |acc, i| TrieIntro::extend(name_of_usize(i), acc, i))
}

fn bench_naive_add_dups() {
    init_naive();
    let mut naive_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    for i in vec![1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3].iter() {
        v.push(*i);
        naive_input = doit(v.clone(), naive_input.clone())
    }
}

fn bench_dcg_add_dups() {
    init_dcg();
    let mut dcg_input: Trie<usize> = SetIntro::empty();
    let mut v = Vec::new();

    for i in vec![1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3].iter() {
        v.push(*i);
        dcg_input = doit(v.clone(), dcg_input.clone())
    }
}

#[bench]
fn benchmark_naive_trie(b: &mut Bencher) {
    b.iter(|| bench_naive_add_dups())
}

#[bench]
fn benchmark_dcg_trie(b: &mut Bencher) {
    b.iter(|| bench_dcg_add_dups())
}
<|endoftext|>


<commit_before>use super::super::parse_ast as past;
use super::nodes::*;
use super::scope::{ClosureScope, FuncScope, ModuleScope, Scope, ScopeLike};
use super::typ::{Generic, Type, Variable};
use super::{unify, Builtins, Closable, RecursionTracker, TypeError, TypeResult};

pub fn translate_module(pmodule: past::Module) -> TypeResult<Module> {
    let scope = ModuleScope::new().into_scope();

    let mut statements = vec![];
    for pstatement in pmodule.statements.into_iter() {
        let statement = match pstatement {
            past::ModuleStatement::Func(pfunc) => {
                ModuleStatement::Func(translate_func(&pfunc, scope.clone())?)
            }
            _ => unreachable!(),
        };
        statements.push(statement);
        // NOTE: Closing should already be done within the translation of every
        //   module-level statement:
        // statements.push(statement.close(&mut RecursionTracker::new(), scope.clone())?);
    }

    // Now that we've translated the whole module we can close all of the
    // statements to ensure all types are bound.
    // let mut closed_statements = vec![];
    // for statement in statements.into_iter() {
    //     closed_statements.push(statement.close(&mut RecursionTracker::new())?);
    // }

    Ok(Module { statements })
}

fn translate_func(pfunc: &past::Func, scope: Scope) -> TypeResult<Func> {
    let name = pfunc.name.name.clone();
    // The scope that the function's arguments and body will be evaluated in.
    let func_scope = FuncScope::new(Some(scope.clone())).into_scope();

    // Build the `FuncArgument` nodes ahead of time so that they have types
    // in place.
    let arguments_nodes = pfunc
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            // TODO: Support argument type definitions.
            typ: Type::new_unbound(func_scope.clone()),
        })
        .collect::<Vec<_>>();

    for argument_node in arguments_nodes.iter() {
        func_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }
    // Build a forward declaration for the recursive call.
    let retrn = Type::new_unbound(func_scope.clone());
    let typ = Type::new_func(
        Some(name.clone()),
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );
    // Add the function to its defining scope.
    // TODO: This should be an `add_static` once functions are static.
    scope.add_local(&name, typ.clone())?;

    let body = match &pfunc.body {
        past::FuncBody::Block(block) => {
            FuncBody::Block(translate_block(block, func_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, &implicit_retrn, func_scope.clone())?;

    Ok(Func {
        name: name.clone(),
        arguments: arguments_nodes,
        body,
        scope: func_scope.clone(),
        typ,
    }.close(&mut RecursionTracker::new(), func_scope)?)
}

fn translate_block(pblock: &past::Block, scope: Scope) -> TypeResult<Block> {
    let mut statements = vec![];
    for pstatement in &pblock.statements {
        let statement = match pstatement {
            past::BlockStatement::CommentLine(_) => continue,
            past::BlockStatement::Func(pfunc) => {
                BlockStatement::Func(translate_func(pfunc, scope.clone())?)
            }
            past::BlockStatement::Expression(pexpression) => {
                BlockStatement::Expression(translate_expression(&pexpression, scope.clone())?)
            }
            past::BlockStatement::Var(pvar) => {
                BlockStatement::Var(translate_var(pvar, scope.clone())?)
            }
        };
        statements.push(statement);
    }
    let typ = if statements.is_empty() {
        Type::new_empty_tuple(scope)
    } else {
        statements.last().unwrap().typ()
    };
    Ok(Block {
        statements,
        span: pblock.span.clone(),
        typ,
    })
}

fn translate_var(pvar: &past::Var, scope: Scope) -> TypeResult<Var> {
    let typ = Type::new_unbound(scope.clone());
    scope.add_local(&pvar.name.name, typ.clone())?;
    let initializer = match &pvar.initializer {
        Some(expression) => {
            let initializer = translate_expression(expression, scope.clone())?;
            unify(&typ, initializer.typ(), scope)?;
            Some(initializer)
        }
        None => None,
    };
    Ok(Var {
        name: pvar.name.clone(),
        initializer,
        typ,
    })
}

fn translate_expression(pexpression: &past::Expression, scope: Scope) -> TypeResult<Expression> {
    Ok(match pexpression {
        past::Expression::Closure(pclosure) => {
            Expression::Closure(translate_closure(pclosure, scope)?)
        }
        past::Expression::Identifier(pidentifier) => {
            let name = pidentifier.name.clone();
            let resolution = scope
                .get_local(&name.name)
                .map_err(|err| err.with_span(pidentifier.name.span.clone()))?;
            let typ = resolution.typ();
            Expression::Identifier(Identifier {
                name,
                resolution,
                typ,
            })
        }
        past::Expression::Infix(pinfix) => {
            let lhs = translate_expression(&*pinfix.lhs, scope.clone())?;
            let rhs = translate_expression(&*pinfix.rhs, scope.clone())?;
            // Left- and right-hand sides must be the same in an infix operation.
            unify(lhs.typ(), rhs.typ(), scope)?;
            let typ = rhs.typ().clone();
            Expression::Infix(Infix {
                lhs: Box::new(lhs),
                op: pinfix.op.clone(),
                rhs: Box::new(rhs),
                typ,
            })
        }
        past::Expression::LiteralInt(pliteral) => {
            let class = Builtins::get("Int");
            Expression::LiteralInt(LiteralInt {
                value: pliteral.value,
                typ: Type::new_object(class, scope),
            })
        }
        past::Expression::PostfixCall(pcall) => {
            Expression::PostfixCall(translate_postfix_call(pcall, scope)?)
        }
        past::Expression::PostfixProperty(pproperty) => {
            Expression::PostfixProperty(translate_postfix_property(pproperty, scope)?)
        }
        _ => unreachable!("Cannot translate {:?}", pexpression),
    })
}

fn translate_closure(pclosure: &past::Closure, scope: Scope) -> TypeResult<Closure> {
    let closure_scope = ClosureScope::new(Some(scope.clone())).into_scope();

    let arguments_nodes = pclosure
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            typ: Type::new_unbound(closure_scope.clone()),
        })
        .collect::<Vec<_>>();

    let retrn = Type::new_unbound(closure_scope.clone());

    for argument_node in arguments_nodes.iter() {
        closure_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }

    let body = match &*pclosure.body {
        past::ClosureBody::Block(block) => {
            ClosureBody::Block(translate_block(block, closure_scope.clone())?)
        }
        past::ClosureBody::Expression(expression) => {
            ClosureBody::Expression(translate_expression(expression, closure_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, implicit_retrn, closure_scope.clone())?;

    let typ = Type::new_func(
        None,
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );

    Ok(Closure {
        arguments: arguments_nodes,
        body: Box::new(body),
        scope: closure_scope.clone(),
        typ,
    }
    .close(&mut RecursionTracker::new(), closure_scope)?)
}

fn translate_postfix_call(pcall: &past::PostfixCall, scope: Scope) -> TypeResult<PostfixCall> {
    let target = translate_expression(&*pcall.target, scope.clone())?;

    let mut arguments = vec![];
    for argument in pcall.arguments.iter() {
        arguments.push(translate_expression(argument, scope.clone())?);
    }

    let retrn = {
        // If the target is already a callable then unify directly instead of
        // through a constraint.
        if let Some((target_arguments, target_retrn)) = target.typ().maybe_callable() {
            let call_arguments = arguments
                .iter()
                .map(|argument| argument.typ().clone())
                .collect::<Vec<_>>();
            if target_arguments.len() != call_arguments.len() {
                return Err(TypeError::ArgumentLengthMismatch {
                    expected: target_arguments,
                    got: call_arguments,
                });
            }
            let mut tracker = RecursionTracker::new();
            for (target_argument, call_argument) in
                target_arguments.iter().zip(call_arguments.iter())
            {
                let open_target_argument =
                    target_argument.open_duplicate(&mut tracker, scope.clone());
                unify(call_argument, &open_target_argument, scope.clone())?;
            }
            let call_retrn = Type::new_unbound(scope.clone());
            let open_target_retrn = target_retrn.open_duplicate(&mut tracker, scope.clone());
            unify(&call_retrn, &open_target_retrn, scope)?;
            call_retrn

        // Otherwise build a callable generic constraint as an intermediary
        // and unify through that.
        } else {
            // The return type of the callable.
            let retrn = Type::new_unbound(scope.clone());

            let mut generic = Generic::new(scope.clone());
            generic.add_callable_constraint(
                arguments
                    .iter()
                    .map(|argument| argument.typ().clone())
                    .collect::<Vec<_>>(),
                retrn.clone(),
            );
            // Unify to ensure target supports being called with the arguments and
            // return types.
            let intermediary = Type::new_variable(Variable::Generic {
                scope: scope.clone(),
                generic,
            });
            unify(target.typ(), &intermediary, scope)
                .map_err(|err| err.with_span(pcall.span.clone()))?;
            retrn
        }
    };

    Ok(PostfixCall {
        target: Box::new(target),
        arguments,
        typ: retrn,
    })
}

fn translate_postfix_property(
    pproperty: &past::PostfixProperty,
    scope: Scope,
) -> TypeResult<PostfixProperty> {
    let target = translate_expression(&*pproperty.target, scope.clone())?;

    // The ultimate type of getting the target's property.
    let typ = Type::new_unbound(scope.clone());

    // Set up a variable generic with a property constraint.
    let mut generic = Generic::new(scope.clone());
    generic.add_property_constraint(pproperty.property.name.clone(), typ.clone());
    // Apply unification to ensure the target supports having the
    // given property.
    let intermediary = Type::new_variable(Variable::Generic {
        scope: scope.clone(),
        generic,
    });
    unify(target.typ(), &intermediary, scope)
        .map_err(|err| err.with_span(pproperty.span.clone()))?;

    Ok(PostfixProperty {
        target: Box::new(target),
        property: pproperty.property.clone(),
        typ,
    })
}

#[cfg(test)]
mod tests {
    use super::super::super::parse_ast as past;
    use super::super::super::parser::{Location, Span, Token, Word};
    use super::super::scope::{ClosureScope, ScopeLike};
    use super::super::{Builtins, Type, TypeError, Variable};
    use super::{translate_expression, translate_func};

    fn word<S: AsRef<str>>(name: S) -> Word {
        Word {
            name: name.as_ref().to_string(),
            span: Span::unknown(),
        }
    }

    #[test]
    fn test_translate_infix() -> Result<(), TypeError> {
        // Test simple addition of two integer literals.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 1,
                span: Span::unknown(),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        translate_expression(&pexpression, ClosureScope::new(None).into_scope()).map(|_| ())?;

        // Add an unbound variable with an integer literal; check that the
        // variable is substituted to an integer.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::Identifier(past::Identifier {
                name: word("foo"),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        let scope = ClosureScope::new(None).into_scope();
        let foo = Type::new_unbound(scope.clone());
        scope.add_local("foo", foo.clone())?;
        translate_expression(&pexpression, scope.clone()).map(|_| ())?;
        assert_eq!(
            foo,
            Type::new_substitute(Type::new_object(Builtins::get("Int"), scope.clone()), scope)
        );

        Ok(())
    }

    #[test]
    fn test_translate_func() -> Result<(), TypeError> {
        let pclosure = past::Closure {
            arguments: vec![],
            body: Box::new(past::ClosureBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![past::BlockStatement::Expression(
                    past::Expression::Identifier(past::Identifier { name: word("bar") }),
                )],
                span: Span::unknown(),
            })),
            span: Span::unknown(),
        };

        let pfunc = past::Func {
            name: word("foo"),
            arguments: vec![word("bar")],
            body: past::FuncBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![
                    past::BlockStatement::Expression(past::Expression::Closure(pclosure)),
                    past::BlockStatement::Expression(past::Expression::Infix(past::Infix {
                        lhs: Box::new(past::Expression::Identifier(past::Identifier {
                            name: word("bar"),
                        })),
                        op: Token::Plus(Location::unknown()),
                        rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                            value: 1,
                            span: Span::unknown(),
                        })),
                    })),
                ],
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let scope = ClosureScope::new(None).into_scope();
        let func = translate_func(&pfunc, scope)?;

        println!("{:?}", func);

        Ok(())
    }
}
<commit_msg>Support translation (skipping) of comment lines in modules<commit_after>use super::super::parse_ast as past;
use super::nodes::*;
use super::scope::{ClosureScope, FuncScope, ModuleScope, Scope, ScopeLike};
use super::typ::{Generic, Type, Variable};
use super::{unify, Builtins, Closable, RecursionTracker, TypeError, TypeResult};

pub fn translate_module(pmodule: past::Module) -> TypeResult<Module> {
    let scope = ModuleScope::new().into_scope();

    let mut statements = vec![];
    for pstatement in pmodule.statements.into_iter() {
        let statement = match pstatement {
            past::ModuleStatement::Func(pfunc) => {
                ModuleStatement::Func(translate_func(&pfunc, scope.clone())?)
            }
            past::ModuleStatement::CommentLine(_) => continue,
            _ => unreachable!(),
        };
        statements.push(statement);
        // NOTE: Closing should already be done within the translation of every
        //   module-level statement:
        // statements.push(statement.close(&mut RecursionTracker::new(), scope.clone())?);
    }

    // Now that we've translated the whole module we can close all of the
    // statements to ensure all types are bound.
    // let mut closed_statements = vec![];
    // for statement in statements.into_iter() {
    //     closed_statements.push(statement.close(&mut RecursionTracker::new())?);
    // }

    Ok(Module { statements })
}

fn translate_func(pfunc: &past::Func, scope: Scope) -> TypeResult<Func> {
    let name = pfunc.name.name.clone();
    // The scope that the function's arguments and body will be evaluated in.
    let func_scope = FuncScope::new(Some(scope.clone())).into_scope();

    // Build the `FuncArgument` nodes ahead of time so that they have types
    // in place.
    let arguments_nodes = pfunc
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            // TODO: Support argument type definitions.
            typ: Type::new_unbound(func_scope.clone()),
        })
        .collect::<Vec<_>>();

    for argument_node in arguments_nodes.iter() {
        func_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }
    // Build a forward declaration for the recursive call.
    let retrn = Type::new_unbound(func_scope.clone());
    let typ = Type::new_func(
        Some(name.clone()),
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );
    // Add the function to its defining scope.
    // TODO: This should be an `add_static` once functions are static.
    scope.add_local(&name, typ.clone())?;

    let body = match &pfunc.body {
        past::FuncBody::Block(block) => {
            FuncBody::Block(translate_block(block, func_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, &implicit_retrn, func_scope.clone())?;

    Ok(Func {
        name: name.clone(),
        arguments: arguments_nodes,
        body,
        scope: func_scope.clone(),
        typ,
    }.close(&mut RecursionTracker::new(), func_scope)?)
}

fn translate_block(pblock: &past::Block, scope: Scope) -> TypeResult<Block> {
    let mut statements = vec![];
    for pstatement in &pblock.statements {
        let statement = match pstatement {
            past::BlockStatement::CommentLine(_) => continue,
            past::BlockStatement::Func(pfunc) => {
                BlockStatement::Func(translate_func(pfunc, scope.clone())?)
            }
            past::BlockStatement::Expression(pexpression) => {
                BlockStatement::Expression(translate_expression(&pexpression, scope.clone())?)
            }
            past::BlockStatement::Var(pvar) => {
                BlockStatement::Var(translate_var(pvar, scope.clone())?)
            }
        };
        statements.push(statement);
    }
    let typ = if statements.is_empty() {
        Type::new_empty_tuple(scope)
    } else {
        statements.last().unwrap().typ()
    };
    Ok(Block {
        statements,
        span: pblock.span.clone(),
        typ,
    })
}

fn translate_var(pvar: &past::Var, scope: Scope) -> TypeResult<Var> {
    let typ = Type::new_unbound(scope.clone());
    scope.add_local(&pvar.name.name, typ.clone())?;
    let initializer = match &pvar.initializer {
        Some(expression) => {
            let initializer = translate_expression(expression, scope.clone())?;
            unify(&typ, initializer.typ(), scope)?;
            Some(initializer)
        }
        None => None,
    };
    Ok(Var {
        name: pvar.name.clone(),
        initializer,
        typ,
    })
}

fn translate_expression(pexpression: &past::Expression, scope: Scope) -> TypeResult<Expression> {
    Ok(match pexpression {
        past::Expression::Closure(pclosure) => {
            Expression::Closure(translate_closure(pclosure, scope)?)
        }
        past::Expression::Identifier(pidentifier) => {
            let name = pidentifier.name.clone();
            let resolution = scope
                .get_local(&name.name)
                .map_err(|err| err.with_span(pidentifier.name.span.clone()))?;
            let typ = resolution.typ();
            Expression::Identifier(Identifier {
                name,
                resolution,
                typ,
            })
        }
        past::Expression::Infix(pinfix) => {
            let lhs = translate_expression(&*pinfix.lhs, scope.clone())?;
            let rhs = translate_expression(&*pinfix.rhs, scope.clone())?;
            // Left- and right-hand sides must be the same in an infix operation.
            unify(lhs.typ(), rhs.typ(), scope)?;
            let typ = rhs.typ().clone();
            Expression::Infix(Infix {
                lhs: Box::new(lhs),
                op: pinfix.op.clone(),
                rhs: Box::new(rhs),
                typ,
            })
        }
        past::Expression::LiteralInt(pliteral) => {
            let class = Builtins::get("Int");
            Expression::LiteralInt(LiteralInt {
                value: pliteral.value,
                typ: Type::new_object(class, scope),
            })
        }
        past::Expression::PostfixCall(pcall) => {
            Expression::PostfixCall(translate_postfix_call(pcall, scope)?)
        }
        past::Expression::PostfixProperty(pproperty) => {
            Expression::PostfixProperty(translate_postfix_property(pproperty, scope)?)
        }
        _ => unreachable!("Cannot translate {:?}", pexpression),
    })
}

fn translate_closure(pclosure: &past::Closure, scope: Scope) -> TypeResult<Closure> {
    let closure_scope = ClosureScope::new(Some(scope.clone())).into_scope();

    let arguments_nodes = pclosure
        .arguments
        .iter()
        .map(|argument| FuncArgument {
            name: argument.name.clone(),
            typ: Type::new_unbound(closure_scope.clone()),
        })
        .collect::<Vec<_>>();

    let retrn = Type::new_unbound(closure_scope.clone());

    for argument_node in arguments_nodes.iter() {
        closure_scope.add_local(&argument_node.name, argument_node.typ.clone())?;
    }

    let body = match &*pclosure.body {
        past::ClosureBody::Block(block) => {
            ClosureBody::Block(translate_block(block, closure_scope.clone())?)
        }
        past::ClosureBody::Expression(expression) => {
            ClosureBody::Expression(translate_expression(expression, closure_scope.clone())?)
        }
    };

    let implicit_retrn = body.typ();
    unify(&retrn, implicit_retrn, closure_scope.clone())?;

    let typ = Type::new_func(
        None,
        arguments_nodes
            .iter()
            .map(|argument| argument.typ.clone())
            .collect::<Vec<_>>(),
        retrn.clone(),
        scope.clone(),
    );

    Ok(Closure {
        arguments: arguments_nodes,
        body: Box::new(body),
        scope: closure_scope.clone(),
        typ,
    }
    .close(&mut RecursionTracker::new(), closure_scope)?)
}

fn translate_postfix_call(pcall: &past::PostfixCall, scope: Scope) -> TypeResult<PostfixCall> {
    let target = translate_expression(&*pcall.target, scope.clone())?;

    let mut arguments = vec![];
    for argument in pcall.arguments.iter() {
        arguments.push(translate_expression(argument, scope.clone())?);
    }

    let retrn = {
        // If the target is already a callable then unify directly instead of
        // through a constraint.
        if let Some((target_arguments, target_retrn)) = target.typ().maybe_callable() {
            let call_arguments = arguments
                .iter()
                .map(|argument| argument.typ().clone())
                .collect::<Vec<_>>();
            if target_arguments.len() != call_arguments.len() {
                return Err(TypeError::ArgumentLengthMismatch {
                    expected: target_arguments,
                    got: call_arguments,
                });
            }
            let mut tracker = RecursionTracker::new();
            for (target_argument, call_argument) in
                target_arguments.iter().zip(call_arguments.iter())
            {
                let open_target_argument =
                    target_argument.open_duplicate(&mut tracker, scope.clone());
                unify(call_argument, &open_target_argument, scope.clone())?;
            }
            let call_retrn = Type::new_unbound(scope.clone());
            let open_target_retrn = target_retrn.open_duplicate(&mut tracker, scope.clone());
            unify(&call_retrn, &open_target_retrn, scope)?;
            call_retrn

        // Otherwise build a callable generic constraint as an intermediary
        // and unify through that.
        } else {
            // The return type of the callable.
            let retrn = Type::new_unbound(scope.clone());

            let mut generic = Generic::new(scope.clone());
            generic.add_callable_constraint(
                arguments
                    .iter()
                    .map(|argument| argument.typ().clone())
                    .collect::<Vec<_>>(),
                retrn.clone(),
            );
            // Unify to ensure target supports being called with the arguments and
            // return types.
            let intermediary = Type::new_variable(Variable::Generic {
                scope: scope.clone(),
                generic,
            });
            unify(target.typ(), &intermediary, scope)
                .map_err(|err| err.with_span(pcall.span.clone()))?;
            retrn
        }
    };

    Ok(PostfixCall {
        target: Box::new(target),
        arguments,
        typ: retrn,
    })
}

fn translate_postfix_property(
    pproperty: &past::PostfixProperty,
    scope: Scope,
) -> TypeResult<PostfixProperty> {
    let target = translate_expression(&*pproperty.target, scope.clone())?;

    // The ultimate type of getting the target's property.
    let typ = Type::new_unbound(scope.clone());

    // Set up a variable generic with a property constraint.
    let mut generic = Generic::new(scope.clone());
    generic.add_property_constraint(pproperty.property.name.clone(), typ.clone());
    // Apply unification to ensure the target supports having the
    // given property.
    let intermediary = Type::new_variable(Variable::Generic {
        scope: scope.clone(),
        generic,
    });
    unify(target.typ(), &intermediary, scope)
        .map_err(|err| err.with_span(pproperty.span.clone()))?;

    Ok(PostfixProperty {
        target: Box::new(target),
        property: pproperty.property.clone(),
        typ,
    })
}

#[cfg(test)]
mod tests {
    use super::super::super::parse_ast as past;
    use super::super::super::parser::{Location, Span, Token, Word};
    use super::super::scope::{ClosureScope, ScopeLike};
    use super::super::{Builtins, Type, TypeError, Variable};
    use super::{translate_expression, translate_func};

    fn word<S: AsRef<str>>(name: S) -> Word {
        Word {
            name: name.as_ref().to_string(),
            span: Span::unknown(),
        }
    }

    #[test]
    fn test_translate_infix() -> Result<(), TypeError> {
        // Test simple addition of two integer literals.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 1,
                span: Span::unknown(),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        translate_expression(&pexpression, ClosureScope::new(None).into_scope()).map(|_| ())?;

        // Add an unbound variable with an integer literal; check that the
        // variable is substituted to an integer.
        let pexpression = past::Expression::Infix(past::Infix {
            lhs: Box::new(past::Expression::Identifier(past::Identifier {
                name: word("foo"),
            })),
            op: Token::Plus(Location::unknown()),
            rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                value: 2,
                span: Span::unknown(),
            })),
        });
        let scope = ClosureScope::new(None).into_scope();
        let foo = Type::new_unbound(scope.clone());
        scope.add_local("foo", foo.clone())?;
        translate_expression(&pexpression, scope.clone()).map(|_| ())?;
        assert_eq!(
            foo,
            Type::new_substitute(Type::new_object(Builtins::get("Int"), scope.clone()), scope)
        );

        Ok(())
    }

    #[test]
    fn test_translate_func() -> Result<(), TypeError> {
        let pclosure = past::Closure {
            arguments: vec![],
            body: Box::new(past::ClosureBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![past::BlockStatement::Expression(
                    past::Expression::Identifier(past::Identifier { name: word("bar") }),
                )],
                span: Span::unknown(),
            })),
            span: Span::unknown(),
        };

        let pfunc = past::Func {
            name: word("foo"),
            arguments: vec![word("bar")],
            body: past::FuncBody::Block(past::Block {
                // statements: vec![past::BlockStatement::Expression(past::Expression::Infix(
                //     past::Infix {
                //         lhs: Box::new(past::Expression::Identifier(past::Identifier {
                //             name: word("bar"),
                //         })),
                //         op: Token::Plus(Location::unknown()),
                //         rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                //             value: 1,
                //             span: Span::unknown(),
                //         })),
                //     },
                // ))],
                statements: vec![
                    past::BlockStatement::Expression(past::Expression::Closure(pclosure)),
                    past::BlockStatement::Expression(past::Expression::Infix(past::Infix {
                        lhs: Box::new(past::Expression::Identifier(past::Identifier {
                            name: word("bar"),
                        })),
                        op: Token::Plus(Location::unknown()),
                        rhs: Box::new(past::Expression::LiteralInt(past::LiteralInt {
                            value: 1,
                            span: Span::unknown(),
                        })),
                    })),
                ],
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let scope = ClosureScope::new(None).into_scope();
        let func = translate_func(&pfunc, scope)?;

        println!("{:?}", func);

        Ok(())
    }
}
<|endoftext|>


<commit_before>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
*/

use ::types::Vector3D;

/// The Particle type hold basic data about a particle in the system. It is self
/// contained, so that it will be easy to send data between parrallels
/// processes.
#[derive(Clone, Debug)]
pub struct Particle {
    /// Particle name
    name: String,
    /// Particle mass
    mass: f64,
    /// Particle positions
    position: Vector3D,
    /// Particle velocity, if needed
    velocity: Vector3D,
}

impl Particle {
    pub fn new<S: Into<String>>(name: S) -> Particle {
        // TODO: get the mass here
        Particle{name: name.into(),
                 mass: 1.0,
                 position: Vector3D::new(0.0, 0.0, 0.0),
                 velocity: Vector3D::new(0.0, 0.0, 0.0)}
    }

    pub fn name<'a>(&'a self) -> &'a str {
        &self.name
    }

    pub fn set_name<S>(&mut self, name: S) where S: Into<String> {
        self.name = name.into();
    }

    pub fn mass(&self) -> f64 {
        self.mass
    }
    pub fn set_mass(&mut self, mass: f64) {
        self.mass = mass;
    }

    pub fn position<'a>(&'a self) -> &'a Vector3D {
        &self.position
    }
    pub fn set_position(&mut self, pos: Vector3D) {
        self.position = pos;
    }
    pub fn add_position(&mut self, new_pos: Vector3D) {
        self.position = self.position + new_pos;
    }

    pub fn velocity<'a>(&'a self) -> &'a Vector3D {
        &self.velocity
    }
    pub fn set_velocity(&mut self, vel: Vector3D) {
        self.velocity = vel;
    }
    pub fn add_velocity(&mut self, new_vel: Vector3D) {
        self.velocity = self.velocity + new_vel;
    }
}

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

    #[test]
    fn name() {
        let mut part = Particle::new("O");
        assert_eq!(part.name(), "O");

        part.set_name("H");
        assert_eq!(part.name(), "H");

        let name = "Zn";
        part.set_name(name);
        assert_eq!(part.name(), "Zn");
    }

    #[test]
    fn mass() {
        let mut part = Particle::new("O");
        part.set_mass(16.0);
        assert_eq!(part.mass(), 16.0);
    }

    #[test]
    fn coordinates() {
        let mut part = Particle::new("O");
        assert_eq!(part.position(), &Vector3D::new(0.0, 0.0, 0.0));

        part.set_position(Vector3D::new(1.0, 2.0, 3.0));
        assert_eq!(part.position(), &Vector3D::new(1.0, 2.0, 3.0));

        part.set_velocity(Vector3D::new(1.0, 2.0, 3.0));
        assert_eq!(part.velocity(), &Vector3D::new(1.0, 2.0, 3.0));
    }
}
<commit_msg>Add kind field in Particle struct<commit_after>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
*/

use ::types::Vector3D;

/// The Particle type hold basic data about a particle in the system. It is self
/// contained, so that it will be easy to send data between parrallels
/// processes.
#[derive(Clone, Debug)]
pub struct Particle {
    /// Particle name
    name: String,
    /// Particle kind, an index for potentials lookup
    kind: usize,
    /// Particle mass
    mass: f64,
    /// Particle positions
    position: Vector3D,
    /// Particle velocity, if needed
    velocity: Vector3D,
}

impl Particle {
    pub fn new<S: Into<String>>(name: S) -> Particle {
        // TODO: get the mass here
        Particle{name: name.into(),
                 mass: 1.0,
                 kind: usize::max_value(),
                 position: Vector3D::new(0.0, 0.0, 0.0),
                 velocity: Vector3D::new(0.0, 0.0, 0.0)}
    }

    pub fn name<'a>(&'a self) -> &'a str {
        &self.name
    }

    pub fn set_name<S>(&mut self, name: S) where S: Into<String> {
        self.name = name.into();
    }

    pub fn kind(&self) -> usize {
        self.kind
    }

    pub fn set_kind(&mut self, kind: usize) {
        self.kind = kind;
    }

    pub fn mass(&self) -> f64 {
        self.mass
    }
    pub fn set_mass(&mut self, mass: f64) {
        self.mass = mass;
    }

    pub fn position<'a>(&'a self) -> &'a Vector3D {
        &self.position
    }
    pub fn set_position(&mut self, pos: Vector3D) {
        self.position = pos;
    }
    pub fn add_position(&mut self, new_pos: Vector3D) {
        self.position = self.position + new_pos;
    }

    pub fn velocity<'a>(&'a self) -> &'a Vector3D {
        &self.velocity
    }
    pub fn set_velocity(&mut self, vel: Vector3D) {
        self.velocity = vel;
    }
    pub fn add_velocity(&mut self, new_vel: Vector3D) {
        self.velocity = self.velocity + new_vel;
    }
}

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

    #[test]
    fn name() {
        let mut part = Particle::new("O");
        assert_eq!(part.name(), "O");

        part.set_name("H");
        assert_eq!(part.name(), "H");

        let name = "Zn";
        part.set_name(name);
        assert_eq!(part.name(), "Zn");
    }

    #[test]
    fn mass() {
        let mut part = Particle::new("O");
        part.set_mass(16.0);
        assert_eq!(part.mass(), 16.0);
    }

    #[test]
    fn kind() {
        let mut part = Particle::new("O");
        part.set_kind(42);
        assert_eq!(part.kind(), 42);
    }

    #[test]
    fn coordinates() {
        let mut part = Particle::new("O");
        assert_eq!(part.position(), &Vector3D::new(0.0, 0.0, 0.0));

        part.set_position(Vector3D::new(1.0, 2.0, 3.0));
        assert_eq!(part.position(), &Vector3D::new(1.0, 2.0, 3.0));

        part.set_velocity(Vector3D::new(1.0, 2.0, 3.0));
        assert_eq!(part.velocity(), &Vector3D::new(1.0, 2.0, 3.0));
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::collections::VecDeque;
use super::super::ffi;
use super::ensure_thread_init;

pub struct MonitorID(pub u32);

pub fn get_available_monitors() -> VecDeque<MonitorID> {
    ensure_thread_init();
    let nb_monitors = unsafe {
        let display = ffi::XOpenDisplay(ptr::null());
        if display.is_null() {
            panic!("get_available_monitors failed");
        }
        let nb_monitors = ffi::XScreenCount(display);
        ffi::XCloseDisplay(display);
        nb_monitors
    };

    let mut monitors = VecDeque::new();
    monitors.extend(range(0, nb_monitors).map(|i| MonitorID(i as u32)));
    monitors
}

pub fn get_primary_monitor() -> MonitorID {
    ensure_thread_init();
    let primary_monitor = unsafe {
        let display = ffi::XOpenDisplay(ptr::null());
        if display.is_null() {
            panic!("get_available_monitors failed");
        }
        let primary_monitor = ffi::XDefaultScreen(display);
        ffi::XCloseDisplay(display);
        primary_monitor
    };

    MonitorID(primary_monitor as u32)
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        let MonitorID(screen_num) = *self;
        Some(format!("Monitor #{}", screen_num))
    }

    pub fn get_native_identifier(&self) -> NativeMonitorID {
        let MonitorID(screen_num) = *self;
        NativeMonitorID::Numeric(screen_num)
    }

    pub fn get_dimensions(&self) -> (u32, u32) {
        let dimensions = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            let MonitorID(screen_num) = *self;
            let screen = ffi::XScreenOfDisplay(display, screen_num as i32);
            let width = ffi::XWidthOfScreen(screen);
            let height = ffi::XHeightOfScreen(screen);
            ffi::XCloseDisplay(display);
            (width as u32, height as u32)
        };

        dimensions
    }
}

<commit_msg>fix X11 build issue due to missing import<commit_after>use std::ptr;
use std::collections::VecDeque;
use super::super::ffi;
use super::ensure_thread_init;
use window::NativeMonitorID;

pub struct MonitorID(pub u32);

pub fn get_available_monitors() -> VecDeque<MonitorID> {
    ensure_thread_init();
    let nb_monitors = unsafe {
        let display = ffi::XOpenDisplay(ptr::null());
        if display.is_null() {
            panic!("get_available_monitors failed");
        }
        let nb_monitors = ffi::XScreenCount(display);
        ffi::XCloseDisplay(display);
        nb_monitors
    };

    let mut monitors = VecDeque::new();
    monitors.extend(range(0, nb_monitors).map(|i| MonitorID(i as u32)));
    monitors
}

pub fn get_primary_monitor() -> MonitorID {
    ensure_thread_init();
    let primary_monitor = unsafe {
        let display = ffi::XOpenDisplay(ptr::null());
        if display.is_null() {
            panic!("get_available_monitors failed");
        }
        let primary_monitor = ffi::XDefaultScreen(display);
        ffi::XCloseDisplay(display);
        primary_monitor
    };

    MonitorID(primary_monitor as u32)
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        let MonitorID(screen_num) = *self;
        Some(format!("Monitor #{}", screen_num))
    }

    pub fn get_native_identifier(&self) -> NativeMonitorID {
        let MonitorID(screen_num) = *self;
        NativeMonitorID::Numeric(screen_num)
    }

    pub fn get_dimensions(&self) -> (u32, u32) {
        let dimensions = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            let MonitorID(screen_num) = *self;
            let screen = ffi::XScreenOfDisplay(display, screen_num as i32);
            let width = ffi::XWidthOfScreen(screen);
            let height = ffi::XHeightOfScreen(screen);
            ffi::XCloseDisplay(display);
            (width as u32, height as u32)
        };

        dimensions
    }
}

<|endoftext|>