<commit_before>//! PPU rendering code
//!
//! # Terminology
//!
//! * **Tile**/**Character**: A tile consists of 8x8 or 16x16 pixels worth of color indices and is
//!   what makes up both sprites and backgrounds. Sprites are always made up of 8x8 tiles, while
//!   backgrounds can use 16x16 pixel tiles by setting the appropriate bit in `BGMODE ($2105)`.
//! * **Background**: A background is a big, connected layer of tiles that can (only) be moved as a
//!   unit. Depending on the background mode, between 1 and 4 BG layers can be used, each with
//!   between 4 and 256 colors.
//! * **Sprite**: A sprite is an independently movable object up to 64x64 pixels in size. Sprites
//!   always use 8x8 tiles and 16 colors, while each sprite can use a different palette. Sprites can
//!   not be rotated (rotating objects in games are either prerendered or make use of BG mode 7,
//!   which allows full matrix transformations on the layer).
//! * **Name table**/**Character table**/**Character map**: These tables store tile data: Maps of
//!   color indices to use for the tile's pixels. Sprites and all background modes except mode 7
//!   store the color indices in "bitplanes", which makes their decoding tricky: A bitplane stores 1
//!   bit for every pixel of the tile. Two bitplanes are interleaved: Bitplane 0 is stored in the
//!   low byte, while bitplane 1 is stored in the high byte.
//! * **Tile map**: Backgrounds consist of 1, 2 or 4 tile maps, each storing data for 32x32 tiles.
//!   For each tile on the background, the tile map stores the tile's priority and H/V flip bits,
//!   the palette, and the tile number. If 16x16 tiles are enabled, each entry in the tile map
//!   stores data for a 16x16 tile consisting of 4 8x8 tiles: `TILE`, `TILE+1`, `TILE+16` and
//!   `TILE+17`, where `TILE` is the stored tile number.

use super::{Ppu, Rgb};

/// Rendering
impl Ppu {
    /// Get the configured sprite size in pixels. If `size_toggle` is `false`, gets the size of
    /// small sprites, otherwise gets the size of large sprites (OAM size bit set).
    pub fn obj_size(&self, size_toggle: bool) -> (u8, u8) {
        match self.obsel >> 5 & 0b111 {
            0b000 => if !size_toggle {(8,8)} else {(16,16)},
            0b001 => if !size_toggle {(8,8)} else {(32,32)},
            0b010 => if !size_toggle {(8,8)} else {(64,64)},
            0b011 => if !size_toggle {(16,16)} else {(32,32)},
            0b100 => if !size_toggle {(16,16)} else {(64,64)},
            0b101 => if !size_toggle {(32,32)} else {(64,64)},
            // FIXME Figure out if we want to support these:
            0b110 => if !size_toggle {(16,32)} else {(32,64)},
            0b111 => if !size_toggle {(16,32)} else {(32,32)},
            _ => unreachable!(),
        }
    }

    /// Returns the active BG mode (0-7).
    pub fn bg_mode(&self) -> u8 { self.bgmode & 0b111 }

    /// Looks up a color index in the CGRAM
    pub fn lookup_color(&self, color: u8) -> Rgb {
        // FIXME Is this correct?
        // 16-bit big endian value! (high byte, high address first)
        // -bbbbbgg gggrrrrr
        let lo = self.cgram[color as u16 * 2] as u16;
        let hi = self.cgram[color as u16 * 2 + 1] as u16;

        let val = (hi << 8) | lo;
        let b = (val & 0x7c00) >> 10;
        let g = (val & 0x03e0) >> 5;
        let r = val & 0x001f;
        Rgb { r: (r as u8) << 3, g: (g as u8) << 3, b: (b as u8) << 3 }
    }

    /// Returns the backdrop color used as a default color (with color math applied, if enabled).
    fn backdrop_color(&self) -> Rgb {
        // TODO: Color math
        self.lookup_color(0)
    }

    /// Main rendering entry point. Renders the current pixel and returns its color. Assumes that
    /// we're not in any blank mode.
    pub fn render_pixel(&mut self) -> Rgb {
        if self.x == 0 && self.scanline == 0 {
            trace!("New frame. BG mode {}, layers enabled: {:05b}, sprites are {:?} or {:?}",
                self.bg_mode(),
                self.tm & 0x1f,
                self.obj_size(false),
                self.obj_size(true));
        }

        if self.x == 0 {
            // Entered new scanline.
            self.collect_sprite_data_for_scanline();
        }

        macro_rules! e {
            ( $e:expr ) => ( $e );
        }

        // This macro gets the current pixel from a tile with given priority in the given layer.
        // If the pixel is non-transparent, it will return its RGB value (after applying color
        // math). If it is transparent, it will do nothing (ie. the code following this macro is
        // executed).
        macro_rules! try_layer {
            ( Sprites with priority $prio:tt ) => {
                if let Some(rgb) = self.maybe_draw_sprite_pixel(e!($prio)) {
                    return rgb
                }
            };
            ( BG $bg:tt tiles with priority $prio:tt ) => {
                if let Some(rgb) = self.lookup_bg_color(e!($bg), e!($prio)) {
                    return rgb
                }
            };
        }

        match self.bg_mode() {
            0 => {
                // I love macros <3
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(BG 2 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(BG 2 tiles with priority 0);
                try_layer!(Sprites with priority 1);
                try_layer!(BG 3 tiles with priority 1);
                try_layer!(BG 4 tiles with priority 1);
                try_layer!(Sprites with priority 0);
                try_layer!(BG 3 tiles with priority 0);
                try_layer!(BG 4 tiles with priority 0);
                self.backdrop_color()
            }
            1 => {
                if self.bgmode & 0x08 != 0 { try_layer!(BG 3 tiles with priority 1) }
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(BG 2 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(BG 2 tiles with priority 0);
                try_layer!(Sprites with priority 1);
                if self.bgmode & 0x08 == 0 { try_layer!(BG 3 tiles with priority 1) }
                try_layer!(Sprites with priority 0);
                try_layer!(BG 3 tiles with priority 0);
                self.backdrop_color()
            }
            2 ... 5 => {
                // FIXME Do the background priorities differ here?
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(BG 2 tiles with priority 1);
                try_layer!(Sprites with priority 1);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(Sprites with priority 0);
                try_layer!(BG 2 tiles with priority 0);
                self.backdrop_color()
            }
            6 => {
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(Sprites with priority 1);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(Sprites with priority 0);
                self.backdrop_color()
            }
            7 => panic!("NYI: BG mode 7"),
            _ => unreachable!(),
        }
    }

    /// Reads character data for a pixel and returns the palette index stored in the bitplanes.
    ///
    /// # Parameters
    /// * `bitplane_count`: Number of bitplanes (must be even)
    /// * `start_addr`: Address of the first bitplane (or the first 2)
    /// * `tile_size`: 8 or 16
    /// * `(x, y)`: Offset inside the tile (`0-7` or `0-15`, depending on the tile size)
    pub fn read_chr_entry(&self,
                      bitplane_count: u8,
                      start_addr: u16,
                      tile_size: u8,
                      (x, y): (u8, u8)) -> u8 {
        // 2 bitplanes are stored interleaved with each other, so there can only be an even number
        debug_assert!(bitplane_count & 1 == 0, "odd bitplane count");
        debug_assert!(x <= 7 || (x <= 15 && tile_size == 16), "invalid x value: {}", x);
        debug_assert!(y <= 7 || (y <= 15 && tile_size == 16), "invalid y value: {}", y);
        debug_assert!(tile_size == 8, "non-8x8 tiles unsupported"); // FIXME support 16x16 tiles
        let bitplane_pairs = bitplane_count >> 1;

        let mut palette_index = 0u8;
        for i in 0..bitplane_pairs {
            let bitplane_bits = self.read_2_bitplanes(
                start_addr + i as u16 * 16, // 16 Bytes per pair of bitplanes
                (x, y));
            palette_index = palette_index | (bitplane_bits << (2 * i));
        }

        palette_index
    }

    /// Reads 2 bits of the given coordinate within the bitplane's tile from 2 interleaved
    /// bitplanes.
    ///
    /// # Parameters
    /// * `bitplanes_start`: Start address of the bitplanes
    /// * `(x_off, y_off)`: Offset into the tile (`0-7`)
    fn read_2_bitplanes(&self, bitplanes_start: u16, (x_off, y_off): (u8, u8)) -> u8 {
        // FIXME Handle flipped tiles somewhere in here (or not in here)
        // Bit 0 in low bytes, bit 1 in high bytes
        let lo = self.vram[bitplanes_start + y_off as u16 * 2];
        let hi = self.vram[bitplanes_start + y_off as u16 * 2 + 1];
        // X values in a byte: 01234567
        let bit0 = (lo >> (7 - x_off)) & 1;
        let bit1 = (hi >> (7 - x_off)) & 1;

        (bit1 << 1) | bit0
    }
}
<commit_msg>PPU: Stretch color range on CGRAM lookup<commit_after>//! PPU rendering code
//!
//! # Terminology
//!
//! * **Tile**/**Character**: A tile consists of 8x8 or 16x16 pixels worth of color indices and is
//!   what makes up both sprites and backgrounds. Sprites are always made up of 8x8 tiles, while
//!   backgrounds can use 16x16 pixel tiles by setting the appropriate bit in `BGMODE ($2105)`.
//! * **Background**: A background is a big, connected layer of tiles that can (only) be moved as a
//!   unit. Depending on the background mode, between 1 and 4 BG layers can be used, each with
//!   between 4 and 256 colors.
//! * **Sprite**: A sprite is an independently movable object up to 64x64 pixels in size. Sprites
//!   always use 8x8 tiles and 16 colors, while each sprite can use a different palette. Sprites can
//!   not be rotated (rotating objects in games are either prerendered or make use of BG mode 7,
//!   which allows full matrix transformations on the layer).
//! * **Name table**/**Character table**/**Character map**: These tables store tile data: Maps of
//!   color indices to use for the tile's pixels. Sprites and all background modes except mode 7
//!   store the color indices in "bitplanes", which makes their decoding tricky: A bitplane stores 1
//!   bit for every pixel of the tile. Two bitplanes are interleaved: Bitplane 0 is stored in the
//!   low byte, while bitplane 1 is stored in the high byte.
//! * **Tile map**: Backgrounds consist of 1, 2 or 4 tile maps, each storing data for 32x32 tiles.
//!   For each tile on the background, the tile map stores the tile's priority and H/V flip bits,
//!   the palette, and the tile number. If 16x16 tiles are enabled, each entry in the tile map
//!   stores data for a 16x16 tile consisting of 4 8x8 tiles: `TILE`, `TILE+1`, `TILE+16` and
//!   `TILE+17`, where `TILE` is the stored tile number.

use super::{Ppu, Rgb};

/// Rendering
impl Ppu {
    /// Get the configured sprite size in pixels. If `size_toggle` is `false`, gets the size of
    /// small sprites, otherwise gets the size of large sprites (OAM size bit set).
    pub fn obj_size(&self, size_toggle: bool) -> (u8, u8) {
        match self.obsel >> 5 & 0b111 {
            0b000 => if !size_toggle {(8,8)} else {(16,16)},
            0b001 => if !size_toggle {(8,8)} else {(32,32)},
            0b010 => if !size_toggle {(8,8)} else {(64,64)},
            0b011 => if !size_toggle {(16,16)} else {(32,32)},
            0b100 => if !size_toggle {(16,16)} else {(64,64)},
            0b101 => if !size_toggle {(32,32)} else {(64,64)},
            // FIXME Figure out if we want to support these:
            0b110 => if !size_toggle {(16,32)} else {(32,64)},
            0b111 => if !size_toggle {(16,32)} else {(32,32)},
            _ => unreachable!(),
        }
    }

    /// Returns the active BG mode (0-7).
    pub fn bg_mode(&self) -> u8 { self.bgmode & 0b111 }

    /// Looks up a color index in the CGRAM and converts the stored 15-bit BGR color to 24-bit RGB
    /// (the 15-bit range is stretched to 24 bits).
    pub fn lookup_color(&self, color: u8) -> Rgb {
        // FIXME Is this correct?
        // 16-bit big endian value! (high byte, high address first)
        // -bbbbbgg gggrrrrr
        let lo = self.cgram[color as u16 * 2] as u16;
        let hi = self.cgram[color as u16 * 2 + 1] as u16;
        let val = (hi << 8) | lo;

        // Extract components
        let b = (val & 0x7c00) >> 10;
        let g = (val & 0x03e0) >> 5;
        let r = val & 0x001f;

        // Convert to RGB
        let mut rgb = Rgb { r: (r as u8) << 3, g: (g as u8) << 3, b: (b as u8) << 3 };

        // Adjust color range
        rgb.r += rgb.r / 32;
        rgb.g += rgb.g / 32;
        rgb.b += rgb.b / 32;

        rgb
    }

    /// Returns the backdrop color used as a default color (with color math applied, if enabled).
    fn backdrop_color(&self) -> Rgb {
        // TODO: Color math
        self.lookup_color(0)
    }

    /// Main rendering entry point. Renders the current pixel and returns its color. Assumes that
    /// we're not in any blank mode.
    pub fn render_pixel(&mut self) -> Rgb {
        if self.x == 0 && self.scanline == 0 {
            trace!("New frame. BG mode {}, layers enabled: {:05b}, sprites are {:?} or {:?}",
                self.bg_mode(),
                self.tm & 0x1f,
                self.obj_size(false),
                self.obj_size(true));
        }

        if self.x == 0 {
            // Entered new scanline.
            self.collect_sprite_data_for_scanline();
        }

        macro_rules! e {
            ( $e:expr ) => ( $e );
        }

        // This macro gets the current pixel from a tile with given priority in the given layer.
        // If the pixel is non-transparent, it will return its RGB value (after applying color
        // math). If it is transparent, it will do nothing (ie. the code following this macro is
        // executed).
        macro_rules! try_layer {
            ( Sprites with priority $prio:tt ) => {
                if let Some(rgb) = self.maybe_draw_sprite_pixel(e!($prio)) {
                    return rgb
                }
            };
            ( BG $bg:tt tiles with priority $prio:tt ) => {
                if let Some(rgb) = self.lookup_bg_color(e!($bg), e!($prio)) {
                    return rgb
                }
            };
        }

        match self.bg_mode() {
            0 => {
                // I love macros <3
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(BG 2 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(BG 2 tiles with priority 0);
                try_layer!(Sprites with priority 1);
                try_layer!(BG 3 tiles with priority 1);
                try_layer!(BG 4 tiles with priority 1);
                try_layer!(Sprites with priority 0);
                try_layer!(BG 3 tiles with priority 0);
                try_layer!(BG 4 tiles with priority 0);
                self.backdrop_color()
            }
            1 => {
                if self.bgmode & 0x08 != 0 { try_layer!(BG 3 tiles with priority 1) }
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(BG 2 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(BG 2 tiles with priority 0);
                try_layer!(Sprites with priority 1);
                if self.bgmode & 0x08 == 0 { try_layer!(BG 3 tiles with priority 1) }
                try_layer!(Sprites with priority 0);
                try_layer!(BG 3 tiles with priority 0);
                self.backdrop_color()
            }
            2 ... 5 => {
                // FIXME Do the background priorities differ here?
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(BG 2 tiles with priority 1);
                try_layer!(Sprites with priority 1);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(Sprites with priority 0);
                try_layer!(BG 2 tiles with priority 0);
                self.backdrop_color()
            }
            6 => {
                try_layer!(Sprites with priority 3);
                try_layer!(BG 1 tiles with priority 1);
                try_layer!(Sprites with priority 2);
                try_layer!(Sprites with priority 1);
                try_layer!(BG 1 tiles with priority 0);
                try_layer!(Sprites with priority 0);
                self.backdrop_color()
            }
            7 => panic!("NYI: BG mode 7"),
            _ => unreachable!(),
        }
    }

    /// Reads character data for a pixel and returns the palette index stored in the bitplanes.
    ///
    /// # Parameters
    /// * `bitplane_count`: Number of bitplanes (must be even)
    /// * `start_addr`: Address of the first bitplane (or the first 2)
    /// * `tile_size`: 8 or 16
    /// * `(x, y)`: Offset inside the tile (`0-7` or `0-15`, depending on the tile size)
    pub fn read_chr_entry(&self,
                      bitplane_count: u8,
                      start_addr: u16,
                      tile_size: u8,
                      (x, y): (u8, u8)) -> u8 {
        // 2 bitplanes are stored interleaved with each other, so there can only be an even number
        debug_assert!(bitplane_count & 1 == 0, "odd bitplane count");
        debug_assert!(x <= 7 || (x <= 15 && tile_size == 16), "invalid x value: {}", x);
        debug_assert!(y <= 7 || (y <= 15 && tile_size == 16), "invalid y value: {}", y);
        debug_assert!(tile_size == 8, "non-8x8 tiles unsupported"); // FIXME support 16x16 tiles
        let bitplane_pairs = bitplane_count >> 1;

        let mut palette_index = 0u8;
        for i in 0..bitplane_pairs {
            let bitplane_bits = self.read_2_bitplanes(
                start_addr + i as u16 * 16, // 16 Bytes per pair of bitplanes
                (x, y));
            palette_index = palette_index | (bitplane_bits << (2 * i));
        }

        palette_index
    }

    /// Reads 2 bits of the given coordinate within the bitplane's tile from 2 interleaved
    /// bitplanes.
    ///
    /// # Parameters
    /// * `bitplanes_start`: Start address of the bitplanes
    /// * `(x_off, y_off)`: Offset into the tile (`0-7`)
    fn read_2_bitplanes(&self, bitplanes_start: u16, (x_off, y_off): (u8, u8)) -> u8 {
        // FIXME Handle flipped tiles somewhere in here (or not in here)
        // Bit 0 in low bytes, bit 1 in high bytes
        let lo = self.vram[bitplanes_start + y_off as u16 * 2];
        let hi = self.vram[bitplanes_start + y_off as u16 * 2 + 1];
        // X values in a byte: 01234567
        let bit0 = (lo >> (7 - x_off)) & 1;
        let bit1 = (hi >> (7 - x_off)) & 1;

        (bit1 << 1) | bit0
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate rusqlite;
#[macro_use]

extern crate rustc_serialize;
extern crate uuid;

mod loader;
mod db;
mod dbFsDirType;
mod dbFsDir;
mod dbFsFile;
mod dbProvider;
mod dbJob;
mod dbJobProvide;
mod dbJobDepend;
mod dbVariableName;
mod dbJobRequireVariable;
mod dbVariablePair;
mod dbJobRequireVariablePair;
mod json_loader_elephant;
mod cli_clap;
mod jobs_load;
mod elephant;
mod dbSession;
mod dbEnviroment;

use uuid::Uuid;

fn main() {
    let session_uuid = Uuid::new_v4();
    let session_uuid_string = session_uuid.simple().to_string();
    let conn = db::connect();
    db::create_tables(&conn);
    let some_value = 10;
    let clap_matches = cli_clap::cli_clap(&some_value);
    loader::deligate(&conn, &clap_matches);
    let pk_session = elephant::elephant_session(&conn, &session_uuid_string);
    loader::enviroment(&conn, pk_session, &clap_matches);
    jobs_load::load(&conn);
}
<commit_msg>Add mod autoconf;<commit_after>extern crate clap;
extern crate rusqlite;
#[macro_use]

extern crate rustc_serialize;
extern crate uuid;

mod loader;
mod db;
mod dbFsDirType;
mod dbFsDir;
mod dbFsFile;
mod dbProvider;
mod dbJob;
mod dbJobProvide;
mod dbJobDepend;
mod dbVariableName;
mod dbJobRequireVariable;
mod dbVariablePair;
mod dbJobRequireVariablePair;
mod json_loader_elephant;
mod cli_clap;
mod jobs_load;
mod elephant;
mod dbSession;
mod dbEnviroment;
mod autoconf;

use uuid::Uuid;

fn main() {
    let session_uuid = Uuid::new_v4();
    let session_uuid_string = session_uuid.simple().to_string();
    let conn = db::connect();
    db::create_tables(&conn);
    let some_value = 10;
    let clap_matches = cli_clap::cli_clap(&some_value);
    loader::deligate(&conn, &clap_matches);
    let pk_session = elephant::elephant_session(&conn, &session_uuid_string);
    loader::enviroment(&conn, pk_session, &clap_matches);
    jobs_load::load(&conn);
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;
use openssl::crypto::hash::Hasher;
use openssl::crypto::hash::HashType::SHA256;
use serialize::hex::ToHex;
use request::Header;

pub struct SigV4 {
    headers: Vec<Header>,
    path: Option<String>,
    method: Option<String>,
    query: Option<String>,
    payload: Option<String>,
}

impl SigV4 {
    pub fn new() -> SigV4{
        SigV4 {
            headers: Vec::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
        }
    }

    pub fn header(mut self, header: Header) -> SigV4 {
        self.headers.push(header);
        self
    }

    pub fn path(mut self, path: String) -> SigV4 {
        self.path = Some(path);
        self
    }

    pub fn method(mut self, method: String) -> SigV4 {
        self.method = Some(method);
        self
    }

    pub fn query(mut self, query: String) -> SigV4 {
        self.query = Some(query);
        self
    }

    pub fn payload(mut self, payload: String) -> SigV4 {
        self.payload = Some(payload);
        self
    }

    pub fn signature(self) -> String {
        self.method.unwrap()
    }

    fn hashed_payload(self) -> String {
        let val = match self.payload {
            Some(x) => x,
            None => "".to_string(),
        };
        let mut h = Hasher::new(SHA256);
        h.update(val.as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn signed_headers(mut self) -> String {
        self.sort_headers();
        let mut h = String::new();

        for header in self.headers.iter() {
            let key = header.key.to_ascii_lower();
            if key == "authorization" {
                continue;
            }
            h.push_str(key.as_slice());
            h.push(';')
        }
        h.trim_right_chars(';').to_string()
    }

    fn canonical_headers(mut self) -> String {
        self.sort_headers();
        let mut h = String::new();

        for header in self.headers.iter() {
            let key = header.key.to_ascii_lower();
            if key == "authorization" {
                continue;
            }
            h.push_str(format!("{}:{}\n", key, canonical_value(&header.value)).as_slice());
        }
        h
    }

    fn sort_headers(&mut self) {
        self.headers.sort_by(|a,b|
                        a.key.to_ascii_lower().cmp(&b.key.to_ascii_lower()))
    }
}

fn canonical_value(val: &String) -> String {
    if val.starts_with("\""){
        val.to_string()
    } else {
        val.replace("  ", " ").trim().to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::SigV4;
    use request::Header;

#[test]
    fn test_new_sigv4() {
        let sig = SigV4::new();
        assert_eq!(sig.headers.len(), 0)
    }

#[test]
    fn test_add_header() {
        let h = Header{ key: "test".to_string(),
        value: "a string".to_string()};

        let sig = SigV4::new().header(h);
        assert_eq!(sig.headers[0].value.as_slice(), "a string")
    }

#[test]
    fn test_signature() {
        let sig = SigV4::new().method("GET".to_string()).path("/".to_string());
        assert_eq!(sig.signature().as_slice(), "GET")
    }

#[test]
    fn test_signed_headers() {
        let h = Header{ key: "test".to_string(),
        value: "a string".to_string()};
        let h2 = Header{ key: "Content-Type".to_string(),
        value: "application/x-www-form-urlencoded; charset=utf-8".to_string()};
        let h3 = Header{ key: "X-Amz-Date".to_string(),
        value: "20120228T030031Z".to_string()};
        let h4 = Header{ key: "Authorization".to_string(),
        value: "none".to_string()};

        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.signed_headers().as_slice(), "content-type;test;x-amz-date")
    }

#[test]
    fn test_hashed_payload() {
        let sig = SigV4::new().
            payload("Action=ListUsers&Version=2010-05-08".to_string());
        assert_eq!(sig.hashed_payload(),
        "b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

#[test]
    fn test_empty_payload() {
        let sig = SigV4::new();
        assert_eq!(sig.hashed_payload(),
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_headers() {
        let h = Header{ key: "Xyz".to_string(), value: "1".to_string() };
        let h2 = Header{ key: "Abc".to_string(), value: "2".to_string() };
        let h3 = Header{ key: "Mno".to_string(), value: "3".to_string() };
        let h4 = Header{ key: "Authorization".to_string(), value: "4".to_string() };
        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:2\nmno:3\nxyz:1\n")
    }

    #[test]
    fn test_prune_whitespace() {
        let h = Header{ key: "Abc".to_string(), value: "a  b  c".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:a b c\n")
    }

    #[test]
    fn test_no_prune_quoted() {
        let h = Header{ key: "Abc".to_string(), value: "\"a  b  c\"".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:\"a  b  c\"\n")
    }
}
<commit_msg>indent test sigils<commit_after>use std::ascii::AsciiExt;
use openssl::crypto::hash::Hasher;
use openssl::crypto::hash::HashType::SHA256;
use serialize::hex::ToHex;
use request::Header;

pub struct SigV4 {
    headers: Vec<Header>,
    path: Option<String>,
    method: Option<String>,
    query: Option<String>,
    payload: Option<String>,
}

impl SigV4 {
    pub fn new() -> SigV4{
        SigV4 {
            headers: Vec::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
        }
    }

    pub fn header(mut self, header: Header) -> SigV4 {
        self.headers.push(header);
        self
    }

    pub fn path(mut self, path: String) -> SigV4 {
        self.path = Some(path);
        self
    }

    pub fn method(mut self, method: String) -> SigV4 {
        self.method = Some(method);
        self
    }

    pub fn query(mut self, query: String) -> SigV4 {
        self.query = Some(query);
        self
    }

    pub fn payload(mut self, payload: String) -> SigV4 {
        self.payload = Some(payload);
        self
    }

    pub fn signature(self) -> String {
        self.method.unwrap()
    }

    fn hashed_payload(self) -> String {
        let val = match self.payload {
            Some(x) => x,
            None => "".to_string(),
        };
        let mut h = Hasher::new(SHA256);
        h.update(val.as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn signed_headers(mut self) -> String {
        self.sort_headers();
        let mut h = String::new();

        for header in self.headers.iter() {
            let key = header.key.to_ascii_lower();
            if key == "authorization" {
                continue;
            }
            h.push_str(key.as_slice());
            h.push(';')
        }
        h.trim_right_chars(';').to_string()
    }

    fn canonical_headers(mut self) -> String {
        self.sort_headers();
        let mut h = String::new();

        for header in self.headers.iter() {
            let key = header.key.to_ascii_lower();
            if key == "authorization" {
                continue;
            }
            h.push_str(format!("{}:{}\n", key, canonical_value(&header.value)).as_slice());
        }
        h
    }

    fn sort_headers(&mut self) {
        self.headers.sort_by(|a,b|
                        a.key.to_ascii_lower().cmp(&b.key.to_ascii_lower()))
    }
}

fn canonical_value(val: &String) -> String {
    if val.starts_with("\""){
        val.to_string()
    } else {
        val.replace("  ", " ").trim().to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::SigV4;
    use request::Header;

    #[test]
    fn test_new_sigv4() {
        let sig = SigV4::new();
        assert_eq!(sig.headers.len(), 0)
    }

    #[test]
    fn test_add_header() {
        let h = Header{ key: "test".to_string(),
        value: "a string".to_string()};

        let sig = SigV4::new().header(h);
        assert_eq!(sig.headers[0].value.as_slice(), "a string")
    }

    #[test]
    fn test_signature() {
        let sig = SigV4::new().method("GET".to_string()).path("/".to_string());
        assert_eq!(sig.signature().as_slice(), "GET")
    }

    #[test]
    fn test_signed_headers() {
        let h = Header{ key: "test".to_string(),
        value: "a string".to_string()};
        let h2 = Header{ key: "Content-Type".to_string(),
        value: "application/x-www-form-urlencoded; charset=utf-8".to_string()};
        let h3 = Header{ key: "X-Amz-Date".to_string(),
        value: "20120228T030031Z".to_string()};
        let h4 = Header{ key: "Authorization".to_string(),
        value: "none".to_string()};

        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.signed_headers().as_slice(), "content-type;test;x-amz-date")
    }

    #[test]
    fn test_hashed_payload() {
        let sig = SigV4::new().
            payload("Action=ListUsers&Version=2010-05-08".to_string());
        assert_eq!(sig.hashed_payload(),
        "b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

    #[test]
    fn test_empty_payload() {
        let sig = SigV4::new();
        assert_eq!(sig.hashed_payload(),
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_headers() {
        let h = Header{ key: "Xyz".to_string(), value: "1".to_string() };
        let h2 = Header{ key: "Abc".to_string(), value: "2".to_string() };
        let h3 = Header{ key: "Mno".to_string(), value: "3".to_string() };
        let h4 = Header{ key: "Authorization".to_string(), value: "4".to_string() };
        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:2\nmno:3\nxyz:1\n")
    }

    #[test]
    fn test_prune_whitespace() {
        let h = Header{ key: "Abc".to_string(), value: "a  b  c".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:a b c\n")
    }

    #[test]
    fn test_no_prune_quoted() {
        let h = Header{ key: "Abc".to_string(), value: "\"a  b  c\"".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:\"a  b  c\"\n")
    }
}
<|endoftext|>


<commit_before>use bls::verify_proof_of_possession;
use types::{ValidatorRecord, ValidatorRegistration, ValidatorStatus};

/// The size of a validators deposit in GWei.
pub const DEPOSIT_GWEI: u64 = 32_000_000_000;

/// Inducts validators into a `CrystallizedState`.
pub struct ValidatorInductor {
    pub current_slot: u64,
    pub shard_count: u16,
    validators: Vec<ValidatorRecord>,
    empty_validator_start: usize,
}

#[derive(Debug, PartialEq, Clone)]
pub enum ValidatorInductionError {
    InvalidShard,
    InvaidProofOfPossession,
}

impl ValidatorInductor {
    pub fn new(current_slot: u64, shard_count: u16, validators: Vec<ValidatorRecord>) -> Self {
        Self {
            current_slot,
            shard_count,
            validators,
            empty_validator_start: 0,
        }
    }

    /// Attempt to induct a validator into the CrystallizedState.
    ///
    /// Returns an error if the registration is invalid, otherwise returns the index of the
    /// validator in `CrystallizedState.validators`.
    pub fn induct(
        &mut self,
        rego: &ValidatorRegistration,
        status: ValidatorStatus,
    ) -> Result<usize, ValidatorInductionError> {
        let v = self.process_registration(rego, status)?;
        Ok(self.add_validator(v))
    }

    /// Verify a `ValidatorRegistration` and return a `ValidatorRecord` if valid.
    fn process_registration(
        &self,
        r: &ValidatorRegistration,
        status: ValidatorStatus,
    ) -> Result<ValidatorRecord, ValidatorInductionError> {
        /*
         * Ensure withdrawal shard is not too high.
         */
        if r.withdrawal_shard > self.shard_count {
            return Err(ValidatorInductionError::InvalidShard);
        }

        /*
         * Prove validator has knowledge of their secret key.
         */
        if !verify_proof_of_possession(&r.proof_of_possession, &r.pubkey) {
            return Err(ValidatorInductionError::InvaidProofOfPossession);
        }

        Ok(ValidatorRecord {
            pubkey: r.pubkey.clone(),
            withdrawal_shard: r.withdrawal_shard,
            withdrawal_address: r.withdrawal_address,
            randao_commitment: r.randao_commitment,
            randao_last_change: self.current_slot,
            balance: DEPOSIT_GWEI,
            status: status,
            exit_slot: 0,
        })
    }

    /// Returns the index of the first `ValidatorRecord` in the `CrystallizedState` where
    /// `validator.status == Withdrawn`. If no such record exists, `None` is returned.
    fn first_withdrawn_validator(&mut self) -> Option<usize> {
        for i in self.empty_validator_start..self.validators.len() {
            if self.validators[i].status == ValidatorStatus::Withdrawn {
                self.empty_validator_start = i + 1;
                return Some(i);
            }
        }
        None
    }

    /// Adds a `ValidatorRecord` to the `CrystallizedState` by replacing first validator where
    /// `validator.status == Withdraw`. If no such withdrawn validator exists, adds the new
    /// validator to the end of the list.
    fn add_validator(&mut self, v: ValidatorRecord) -> usize {
        match self.first_withdrawn_validator() {
            Some(i) => {
                self.validators[i] = v;
                i
            }
            None => {
                self.validators.push(v);
                self.validators.len() - 1
            }
        }
    }

    pub fn to_vec(self) -> Vec<ValidatorRecord> {
        self.validators
    }
}

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

    use bls::{Keypair, Signature};
    use hashing::proof_of_possession_hash;
    use types::{Address, Hash256};

    fn registration_equals_record(reg: &ValidatorRegistration, rec: &ValidatorRecord) -> bool {
        (reg.pubkey == rec.pubkey)
            & (reg.withdrawal_shard == rec.withdrawal_shard)
            & (reg.withdrawal_address == rec.withdrawal_address)
            & (reg.randao_commitment == rec.randao_commitment)
            & (verify_proof_of_possession(&reg.proof_of_possession, &rec.pubkey))
    }

    /// Generate a proof of possession for some keypair.
    fn get_proof_of_possession(kp: &Keypair) -> Signature {
        let pop_message = proof_of_possession_hash(&kp.pk.as_bytes());
        Signature::new_hashed(&pop_message, &kp.sk)
    }

    /// Generate a basic working ValidatorRegistration for use in tests.
    fn get_registration() -> ValidatorRegistration {
        let kp = Keypair::random();
        ValidatorRegistration {
            pubkey: kp.pk.clone(),
            withdrawal_shard: 0,
            withdrawal_address: Address::zero(),
            randao_commitment: Hash256::zero(),
            proof_of_possession: get_proof_of_possession(&kp),
        }
    }

    #[test]
    fn test_validator_inductor_valid_empty_validators() {
        let validators = vec![];

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 0);
        assert!(registration_equals_record(&r, &validators[0]));
        assert_eq!(validators.len(), 1);
    }

    #[test]
    fn test_validator_inductor_status() {
        let validators = vec![];

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let _ = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let _ = inductor.induct(&r, ValidatorStatus::Active);
        let validators = inductor.to_vec();

        assert!(validators[0].status == ValidatorStatus::PendingActivation);
        assert!(validators[1].status == ValidatorStatus::Active);
        assert_eq!(validators.len(), 2);
    }

    #[test]
    fn test_validator_inductor_valid_all_active_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Active;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 5);
        assert!(registration_equals_record(&r, &validators[5]));
        assert_eq!(validators.len(), 6);
    }

    #[test]
    fn test_validator_inductor_valid_all_second_validator_withdrawn() {
        let mut validators = vec![];
        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Active;
        validators.push(v);
        for _ in 0..4 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 1);
        assert!(registration_equals_record(&r, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_valid_all_withdrawn_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        /*
         * Ensure the first validator gets the 0'th slot
         */
        let r = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 0);
        assert!(registration_equals_record(&r, &validators[0]));

        /*
         * Ensure the second validator gets the 1'st slot
         */
        let r_two = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r_two, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 1);
        assert!(registration_equals_record(&r_two, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_shard_too_high() {
        let validators = vec![];

        let mut r = get_registration();
        r.withdrawal_shard = 1025;

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result, Err(ValidatorInductionError::InvalidShard));
        assert_eq!(validators.len(), 0);
    }

    #[test]
    fn test_validator_inductor_shard_proof_of_possession_failure() {
        let validators = vec![];

        let mut r = get_registration();
        let kp = Keypair::random();
        r.proof_of_possession = get_proof_of_possession(&kp);

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(
            result,
            Err(ValidatorInductionError::InvaidProofOfPossession)
        );
        assert_eq!(validators.len(), 0);
    }
}
<commit_msg>first pass at updating inductor with deposit structs - lots of compilation errors<commit_after>use bls::verify_proof_of_possession;
use types::{ValidatorRecord, Deposit, ValidatorStatus, BeaconState};

/// The size of a validators deposit in GWei.
pub const DEPOSIT_GWEI: u64 = 32_000_000_000;

/// Inducts validators into a `CrystallizedState`.
pub struct ValidatorInductor {
    pub current_slot: u64,
    pub shard_count: u16,
    beacon_state: BeaconState,
    empty_validator_start: usize,
}

#[derive(Debug, PartialEq, Clone)]
pub enum ValidatorInductionError {
    InvalidShard,
    InvaidProofOfPossession,
}

impl ValidatorInductor {
    pub fn new(current_slot: u64, shard_count: u16, beacon_state: BeaconState) -> Self {
        Self {
            current_slot,
            shard_count,
            beacon_state,
            empty_validator_start: 0,
        }
    }

    /// Attempt to induct a validator into the CrystallizedState.
    ///
    /// Returns an error if the registration is invalid, otherwise returns the index of the
    /// validator in `CrystallizedState.validators`.
    pub fn induct(
        &mut self,
        deposit: &Deposit,
        status: ValidatorStatus,
    ) -> Result<usize, ValidatorInductionError> {
        let v = self.process_registration(deposit, status)?;
        Ok(self.add_validator(v))
    }

    /// Verify a `ValidatorRegistration` and return a `ValidatorRecord` if valid.
    fn process_deposit(
        &self,
        deposit: &Deposit,
        status: ValidatorStatus,
    ) -> Result<ValidatorRecord, ValidatorInductionError> {
        let deposit_input = &deposit.deposit_data.deposit_input;
        /*
         * Ensure withdrawal shard is not too high.
         */
        /*
        if r.withdrawal_shard > self.shard_count {
            return Err(ValidatorInductionError::InvalidShard);
        }
        */

        /*
         * Prove validator has knowledge of their secret key.
         */
        if !verify_proof_of_possession(&deposit_input.proof_of_possession, &deposit_input.pubkey) {
            return Err(ValidatorInductionError::InvaidProofOfPossession);
        }

        Ok(ValidatorRecord {
            pubkey: deposit_input.pubkey.clone(),
            withdrawal_credentials: deposit_input.withdrawal_credentials,
            randao_commitment: deposit_input.randao_commitment,
            // TODO: revisit this
            randao_layers: 0,
            balance: DEPOSIT_GWEI,
            status: status,
            latest_status_change_slot: self.beacon_state.validator_registry_latest_change_slot,
            exit_count: self.beacon_state.validator_registry_exit_count
        })
    }

    /// Returns the index of the first `ValidatorRecord` in the `CrystallizedState` where
    /// `validator.status == Withdrawn`. If no such record exists, `None` is returned.
    fn first_withdrawn_validator(&mut self) -> Option<usize> {
        let validators = self.beacon_state.validator_registry;
        for i in self.empty_validator_start..validators.len() {
            if validators[i].status == ValidatorStatus::Withdrawn {
                self.empty_validator_start = i + 1;
                return Some(i);
            }
        }
        None
    }

    /// Adds a `ValidatorRecord` to the `CrystallizedState` by replacing first validator where
    /// `validator.status == Withdraw`. If no such withdrawn validator exists, adds the new
    /// validator to the end of the list.
    fn add_validator(&mut self, v: ValidatorRecord) -> usize {
        match self.first_withdrawn_validator() {
            Some(i) => {
                self.beacon_state.validator_registry[i] = v;
                i
            }
            None => {
                self.beacon_state.validator_registry.push(v);
                self.beacon_state.validator_registry.len() - 1
            }
        }
    }

    pub fn to_vec(self) -> Vec<ValidatorRecord> {
        self.beacon_state.validator_registry
    }
}

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

    use bls::{Keypair, Signature};
    use hashing::proof_of_possession_hash;
    use types::{Address, Hash256};

    /*
    fn registration_equals_record(reg: &ValidatorRegistration, rec: &ValidatorRecord) -> bool {
        (reg.pubkey == rec.pubkey)
            & (reg.withdrawal_shard == rec.withdrawal_shard)
            & (reg.withdrawal_address == rec.withdrawal_address)
            & (reg.randao_commitment == rec.randao_commitment)
            & (verify_proof_of_possession(&reg.proof_of_possession, &rec.pubkey))
    }
    */

    /// Generate a proof of possession for some keypair.
    fn get_proof_of_possession(kp: &Keypair) -> Signature {
        let pop_message = proof_of_possession_hash(&kp.pk.as_bytes());
        Signature::new_hashed(&pop_message, &kp.sk)
    }

    /// Generate a basic working ValidatorRegistration for use in tests.
    fn get_registration() -> ValidatorRegistration {
        let kp = Keypair::random();
        ValidatorRegistration {
            pubkey: kp.pk.clone(),
            withdrawal_shard: 0,
            withdrawal_address: Address::zero(),
            randao_commitment: Hash256::zero(),
            proof_of_possession: get_proof_of_possession(&kp),
        }
    }

    #[test]
    fn test_validator_inductor_valid_empty_validators() {
        let validators = vec![];

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 0);
        //assert!(registration_equals_record(&r, &validators[0]));
        assert_eq!(validators.len(), 1);
    }

    #[test]
    fn test_validator_inductor_status() {
        let validators = vec![];

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let _ = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let _ = inductor.induct(&r, ValidatorStatus::Active);
        let validators = inductor.to_vec();

        assert!(validators[0].status == ValidatorStatus::PendingActivation);
        assert!(validators[1].status == ValidatorStatus::Active);
        assert_eq!(validators.len(), 2);
    }

    #[test]
    fn test_validator_inductor_valid_all_active_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Active;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 5);
        //assert!(registration_equals_record(&r, &validators[5]));
        assert_eq!(validators.len(), 6);
    }

    #[test]
    fn test_validator_inductor_valid_all_second_validator_withdrawn() {
        let mut validators = vec![];
        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Active;
        validators.push(v);
        for _ in 0..4 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 1);
        //assert!(registration_equals_record(&r, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_valid_all_withdrawn_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        /*
         * Ensure the first validator gets the 0'th slot
         */
        let r = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 0);
        //assert!(registration_equals_record(&r, &validators[0]));

        /*
         * Ensure the second validator gets the 1'st slot
         */
        let r_two = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r_two, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 1);
        //assert!(registration_equals_record(&r_two, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_shard_too_high() {
        let validators = vec![];

        let mut r = get_registration();
        r.withdrawal_shard = 1025;

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result, Err(ValidatorInductionError::InvalidShard));
        assert_eq!(validators.len(), 0);
    }

    #[test]
    fn test_validator_inductor_shard_proof_of_possession_failure() {
        let validators = vec![];

        let mut r = get_registration();
        let kp = Keypair::random();
        r.proof_of_possession = get_proof_of_possession(&kp);

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(
            result,
            Err(ValidatorInductionError::InvaidProofOfPossession)
        );
        assert_eq!(validators.len(), 0);
    }
}
<|endoftext|>


<commit_before>use clap::{App, Arg};
use env_logger::{Builder, Env};
use std::{fs::File, io::prelude::*};
use test_case::TestCase;
use yaml_rust::YamlLoader;

mod beacon_chain_harness;
mod test_case;
mod validator_harness;

use validator_harness::ValidatorHarness;

fn main() {
    let matches = App::new("Lighthouse Test Harness Runner")
        .version("0.0.1")
        .author("Sigma Prime <contact@sigmaprime.io>")
        .about("Runs `test_harness` using a YAML test_case.")
        .arg(
            Arg::with_name("yaml")
                .long("yaml")
                .value_name("FILE")
                .help("YAML file test_case.")
                .required(true),
        )
        .get_matches();

    Builder::from_env(Env::default().default_filter_or("debug")).init();

    if let Some(yaml_file) = matches.value_of("yaml") {
        let docs = {
            let mut file = File::open(yaml_file).unwrap();

            let mut yaml_str = String::new();
            file.read_to_string(&mut yaml_str).unwrap();

            YamlLoader::load_from_str(&yaml_str).unwrap()
        };

        for doc in &docs {
            // For each `test_cases` YAML in the document, build a `TestCase`, execute it and
            // assert that the execution result matches the test_case description.
            //
            // In effect, for each `test_case` a new `BeaconChainHarness` is created from genesis
            // and a new `BeaconChain` is built as per the test_case.
            //
            // After the `BeaconChain` has been built out as per the test_case, a dump of all blocks
            // and states in the chain is obtained and checked against the `results` specified in
            // the `test_case`.
            //
            // If any of the expectations in the results are not met, the process
            // panics with a message.
            for test_case in doc["test_cases"].as_vec().unwrap() {
                let test_case = TestCase::from_yaml(test_case);
                test_case.assert_result_valid(test_case.execute())
            }
        }
    }
}
<commit_msg>Add CLI option for log-level to test_harness<commit_after>use clap::{App, Arg};
use env_logger::{Builder, Env};
use std::{fs::File, io::prelude::*};
use test_case::TestCase;
use yaml_rust::YamlLoader;

mod beacon_chain_harness;
mod test_case;
mod validator_harness;

use validator_harness::ValidatorHarness;

fn main() {
    let matches = App::new("Lighthouse Test Harness Runner")
        .version("0.0.1")
        .author("Sigma Prime <contact@sigmaprime.io>")
        .about("Runs `test_harness` using a YAML test_case.")
        .arg(
            Arg::with_name("yaml")
                .long("yaml")
                .value_name("FILE")
                .help("YAML file test_case.")
                .required(true),
        )
        .arg(
            Arg::with_name("log")
                .long("log-level")
                .value_name("LOG_LEVEL")
                .help("Logging level.")
                .possible_values(&["error", "warn", "info", "debug", "trace"])
                .default_value("debug")
                .required(true),
        )
        .get_matches();

    if let Some(log_level) = matches.value_of("log") {
        Builder::from_env(Env::default().default_filter_or(log_level)).init();
    }

    if let Some(yaml_file) = matches.value_of("yaml") {
        let docs = {
            let mut file = File::open(yaml_file).unwrap();

            let mut yaml_str = String::new();
            file.read_to_string(&mut yaml_str).unwrap();

            YamlLoader::load_from_str(&yaml_str).unwrap()
        };

        for doc in &docs {
            // For each `test_cases` YAML in the document, build a `TestCase`, execute it and
            // assert that the execution result matches the test_case description.
            //
            // In effect, for each `test_case` a new `BeaconChainHarness` is created from genesis
            // and a new `BeaconChain` is built as per the test_case.
            //
            // After the `BeaconChain` has been built out as per the test_case, a dump of all blocks
            // and states in the chain is obtained and checked against the `results` specified in
            // the `test_case`.
            //
            // If any of the expectations in the results are not met, the process
            // panics with a message.
            for test_case in doc["test_cases"].as_vec().unwrap() {
                let test_case = TestCase::from_yaml(test_case);
                test_case.assert_result_valid(test_case.execute())
            }
        }
    }
}
<|endoftext|>


<commit_before>/* 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/. */

#![feature(box_syntax)]
#![feature(conservative_impl_trait)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(fnbox)]
#![feature(mpsc_select)]
#![feature(nonzero)]
#![feature(on_unimplemented)]
#![feature(optin_builtin_traits)]
#![feature(plugin)]
#![feature(slice_patterns)]
#![feature(stmt_expr_attributes)]
#![feature(question_mark)]
#![feature(try_borrow)]
#![feature(try_from)]

#![deny(unsafe_code)]
#![allow(non_snake_case)]

#![doc = "The script crate contains all matters DOM."]

#![plugin(heapsize_plugin)]
#![plugin(phf_macros)]
#![plugin(plugins)]

extern crate angle;
extern crate app_units;
extern crate audio_video_metadata;
#[allow(unused_extern_crates)]
#[macro_use]
extern crate bitflags;
extern crate canvas_traits;
extern crate caseless;
extern crate cookie as cookie_rs;
extern crate core;
#[macro_use]
extern crate cssparser;
extern crate devtools_traits;
extern crate encoding;
extern crate euclid;
extern crate fnv;
extern crate gfx_traits;
extern crate heapsize;
extern crate html5ever;
extern crate hyper;
extern crate hyper_serde;
extern crate image;
extern crate ipc_channel;
#[macro_use]
extern crate js;
extern crate libc;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mime;
extern crate mime_guess;
extern crate msg;
extern crate net_traits;
extern crate num_traits;
extern crate offscreen_gl_context;
extern crate open;
extern crate parking_lot;
extern crate phf;
#[macro_use]
extern crate profile_traits;
extern crate rand;
extern crate range;
extern crate ref_slice;
extern crate regex;
extern crate rustc_serialize;
extern crate script_layout_interface;
extern crate script_traits;
extern crate selectors;
extern crate serde;
extern crate smallvec;
#[macro_use(atom, ns)] extern crate string_cache;
#[macro_use]
extern crate style;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
extern crate tinyfiledialogs;
extern crate url;
#[macro_use]
extern crate util;
extern crate uuid;
extern crate webrender_traits;
extern crate websocket;
extern crate xml5ever;

pub mod bluetooth_blacklist;
mod body;
pub mod clipboard_provider;
mod devtools;
pub mod document_loader;
#[macro_use]
pub mod dom;
pub mod fetch;
pub mod layout_wrapper;
mod mem;
mod network_listener;
pub mod origin;
pub mod parse;
pub mod script_runtime;
#[allow(unsafe_code)]
pub mod script_thread;
mod serviceworker_manager;
mod task_source;
pub mod textinput;
mod timers;
mod unpremultiplytable;
mod webdriver_handlers;

use dom::bindings::codegen::RegisterBindings;
use dom::bindings::proxyhandler;
use script_traits::SWManagerSenders;
use serviceworker_manager::ServiceWorkerManager;

#[cfg(target_os = "linux")]
#[allow(unsafe_code)]
fn perform_platform_specific_initialization() {
    use std::mem;
    // 4096 is default max on many linux systems
    const MAX_FILE_LIMIT: libc::rlim_t = 4096;

    // Bump up our number of file descriptors to save us from impending doom caused by an onslaught
    // of iframes.
    unsafe {
        let mut rlim: libc::rlimit = mem::uninitialized();
        match libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) {
            0 => {
                if rlim.rlim_cur >= MAX_FILE_LIMIT {
                    // we have more than enough
                    return;
                }

                rlim.rlim_cur = match rlim.rlim_max {
                    libc::RLIM_INFINITY => MAX_FILE_LIMIT,
                    _ => {
                        if rlim.rlim_max < MAX_FILE_LIMIT {
                            rlim.rlim_max
                        } else {
                            MAX_FILE_LIMIT
                        }
                    }
                };
                match libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) {
                    0 => (),
                    _ => warn!("Failed to set file count limit"),
                };
            },
            _ => warn!("Failed to get file count limit"),
        };
    }
}

#[cfg(not(target_os = "linux"))]
fn perform_platform_specific_initialization() {}

#[allow(unsafe_code)]
pub fn init(sw_senders: SWManagerSenders) {
    unsafe {
        proxyhandler::init();
    }

    // Spawn the service worker manager passing the constellation sender
    ServiceWorkerManager::spawn_manager(sw_senders);

    // Create the global vtables used by the (generated) DOM
    // bindings to implement JS proxies.
    RegisterBindings::RegisterProxyHandlers();

    perform_platform_specific_initialization();
}
<commit_msg>Reorder features in script crate<commit_after>/* 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/. */

#![feature(box_syntax)]
#![feature(conservative_impl_trait)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(fnbox)]
#![feature(mpsc_select)]
#![feature(nonzero)]
#![feature(on_unimplemented)]
#![feature(optin_builtin_traits)]
#![feature(plugin)]
#![feature(question_mark)]
#![feature(slice_patterns)]
#![feature(stmt_expr_attributes)]
#![feature(try_borrow)]
#![feature(try_from)]

#![deny(unsafe_code)]
#![allow(non_snake_case)]

#![doc = "The script crate contains all matters DOM."]

#![plugin(heapsize_plugin)]
#![plugin(phf_macros)]
#![plugin(plugins)]

extern crate angle;
extern crate app_units;
extern crate audio_video_metadata;
#[allow(unused_extern_crates)]
#[macro_use]
extern crate bitflags;
extern crate canvas_traits;
extern crate caseless;
extern crate cookie as cookie_rs;
extern crate core;
#[macro_use]
extern crate cssparser;
extern crate devtools_traits;
extern crate encoding;
extern crate euclid;
extern crate fnv;
extern crate gfx_traits;
extern crate heapsize;
extern crate html5ever;
extern crate hyper;
extern crate hyper_serde;
extern crate image;
extern crate ipc_channel;
#[macro_use]
extern crate js;
extern crate libc;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mime;
extern crate mime_guess;
extern crate msg;
extern crate net_traits;
extern crate num_traits;
extern crate offscreen_gl_context;
extern crate open;
extern crate parking_lot;
extern crate phf;
#[macro_use]
extern crate profile_traits;
extern crate rand;
extern crate range;
extern crate ref_slice;
extern crate regex;
extern crate rustc_serialize;
extern crate script_layout_interface;
extern crate script_traits;
extern crate selectors;
extern crate serde;
extern crate smallvec;
#[macro_use(atom, ns)] extern crate string_cache;
#[macro_use]
extern crate style;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
extern crate tinyfiledialogs;
extern crate url;
#[macro_use]
extern crate util;
extern crate uuid;
extern crate webrender_traits;
extern crate websocket;
extern crate xml5ever;

pub mod bluetooth_blacklist;
mod body;
pub mod clipboard_provider;
mod devtools;
pub mod document_loader;
#[macro_use]
pub mod dom;
pub mod fetch;
pub mod layout_wrapper;
mod mem;
mod network_listener;
pub mod origin;
pub mod parse;
pub mod script_runtime;
#[allow(unsafe_code)]
pub mod script_thread;
mod serviceworker_manager;
mod task_source;
pub mod textinput;
mod timers;
mod unpremultiplytable;
mod webdriver_handlers;

use dom::bindings::codegen::RegisterBindings;
use dom::bindings::proxyhandler;
use script_traits::SWManagerSenders;
use serviceworker_manager::ServiceWorkerManager;

#[cfg(target_os = "linux")]
#[allow(unsafe_code)]
fn perform_platform_specific_initialization() {
    use std::mem;
    // 4096 is default max on many linux systems
    const MAX_FILE_LIMIT: libc::rlim_t = 4096;

    // Bump up our number of file descriptors to save us from impending doom caused by an onslaught
    // of iframes.
    unsafe {
        let mut rlim: libc::rlimit = mem::uninitialized();
        match libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) {
            0 => {
                if rlim.rlim_cur >= MAX_FILE_LIMIT {
                    // we have more than enough
                    return;
                }

                rlim.rlim_cur = match rlim.rlim_max {
                    libc::RLIM_INFINITY => MAX_FILE_LIMIT,
                    _ => {
                        if rlim.rlim_max < MAX_FILE_LIMIT {
                            rlim.rlim_max
                        } else {
                            MAX_FILE_LIMIT
                        }
                    }
                };
                match libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) {
                    0 => (),
                    _ => warn!("Failed to set file count limit"),
                };
            },
            _ => warn!("Failed to get file count limit"),
        };
    }
}

#[cfg(not(target_os = "linux"))]
fn perform_platform_specific_initialization() {}

#[allow(unsafe_code)]
pub fn init(sw_senders: SWManagerSenders) {
    unsafe {
        proxyhandler::init();
    }

    // Spawn the service worker manager passing the constellation sender
    ServiceWorkerManager::spawn_manager(sw_senders);

    // Create the global vtables used by the (generated) DOM
    // bindings to implement JS proxies.
    RegisterBindings::RegisterProxyHandlers();

    perform_platform_specific_initialization();
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use crossbeam::sync::MsQueue;
use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;
use std::thread;
use std::vec::IntoIter as VecIntoIter;

use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolAlloc;
use command_buffer::pool::CommandPoolBuilderAlloc;
use command_buffer::pool::UnsafeCommandPool;
use command_buffer::pool::UnsafeCommandPoolAlloc;
use instance::QueueFamily;

use OomError;
use VulkanObject;
use device::Device;
use device::DeviceOwned;

/// Standard implementation of a command pool.
///
/// Will use one Vulkan pool per thread in order to avoid locking. Will try to reuse command
/// buffers. Command buffers can't be moved between threads during the building process, but
/// finished command buffers can.
pub struct StandardCommandPool {
    // The device.
    device: Arc<Device>,

    // Identifier of the queue family.
    queue_family: u32,

    // For each thread, we store thread-specific info.
    per_thread: Mutex<FnvHashMap<thread::ThreadId, Weak<StandardCommandPoolPerThread>>>,
}

unsafe impl Send for StandardCommandPool {
}
unsafe impl Sync for StandardCommandPool {
}

struct StandardCommandPoolPerThread {
    // The Vulkan pool of this thread.
    pool: Mutex<UnsafeCommandPool>,
    // List of existing primary command buffers that are available for reuse.
    available_primary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
    // List of existing secondary command buffers that are available for reuse.
    available_secondary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
}

impl StandardCommandPool {
    /// Builds a new pool.
    ///
    /// # Panic
    ///
    /// - Panics if the device and the queue family don't belong to the same physical device.
    ///
    pub fn new(device: Arc<Device>, queue_family: QueueFamily) -> StandardCommandPool {
        assert_eq!(device.physical_device().internal_object(),
                   queue_family.physical_device().internal_object());

        StandardCommandPool {
            device: device,
            queue_family: queue_family.id(),
            per_thread: Mutex::new(Default::default()),
        }
    }
}

unsafe impl CommandPool for Arc<StandardCommandPool> {
    type Iter = VecIntoIter<StandardCommandPoolBuilder>;
    type Builder = StandardCommandPoolBuilder;
    type Alloc = StandardCommandPoolAlloc;

    fn alloc(&self, secondary: bool, count: u32) -> Result<Self::Iter, OomError> {
        // Find the correct `StandardCommandPoolPerThread` structure.
        let mut hashmap = self.per_thread.lock().unwrap();
        // TODO: meh for iterating everything every time
        hashmap.retain(|_, w| w.upgrade().is_some());

        // Get an appropriate `Arc<StandardCommandPoolPerThread>`.
        let per_thread = match hashmap.entry(thread::current().id()) {
            Entry::Occupied(mut entry) => {
                if let Some(entry) = entry.get().upgrade() {
                    entry
                } else {
                    let new_pool =
                        UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                    let pt = Arc::new(StandardCommandPoolPerThread {
                                          pool: Mutex::new(new_pool),
                                          available_primary_command_buffers: MsQueue::new(),
                                          available_secondary_command_buffers: MsQueue::new(),
                                      });

                    entry.insert(Arc::downgrade(&pt));
                    pt
                }
            },
            Entry::Vacant(entry) => {
                let new_pool =
                    UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                let pt = Arc::new(StandardCommandPoolPerThread {
                                      pool: Mutex::new(new_pool),
                                      available_primary_command_buffers: MsQueue::new(),
                                      available_secondary_command_buffers: MsQueue::new(),
                                  });

                entry.insert(Arc::downgrade(&pt));
                pt
            },
        };

        // The final output.
        let mut output = Vec::with_capacity(count as usize);

        // First, pick from already-existing command buffers.
        {
            let existing = if secondary {
                &per_thread.available_secondary_command_buffers
            } else {
                &per_thread.available_primary_command_buffers
            };

            for _ in 0 .. count as usize {
                if let Some(cmd) = existing.try_pop() {
                    output.push(StandardCommandPoolBuilder {
                        inner: StandardCommandPoolAlloc {
                            cmd: Some(cmd),
                            pool: per_thread.clone(),
                            secondary: secondary,
                            device: self.device.clone(),
                        },
                        dummy_avoid_send_sync: PhantomData,
                    });
                } else {
                    break;
                }
            }
        };

        // Then allocate the rest.
        if output.len() < count as usize {
            let pool_lock = per_thread.pool.lock().unwrap();
            let num_new = count as usize - output.len();

            for cmd in pool_lock.alloc_command_buffers(secondary, num_new)? {
                output.push(StandardCommandPoolBuilder {
                    inner: StandardCommandPoolAlloc {
                        cmd: Some(cmd),
                        pool: per_thread.clone(),
                        secondary: secondary,
                        device: self.device.clone(),
                    },
                    dummy_avoid_send_sync: PhantomData,
                });
            }
        }

        // Final output.
        Ok(output.into_iter())
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.device
            .physical_device()
            .queue_family_by_id(self.queue_family)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPool {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        &self.device
    }
}

/// Command buffer allocated from a `StandardCommandPool` and that is currently being built.
pub struct StandardCommandPoolBuilder {
    // The only difference between a `StandardCommandPoolBuilder` and a `StandardCommandPoolAlloc`
    // is that the former must not implement `Send` and `Sync`. Therefore we just share the structs.
    inner: StandardCommandPoolAlloc,
    // Unimplemented `Send` and `Sync` from the builder.
    dummy_avoid_send_sync: PhantomData<*const u8>,
}

unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
    type Alloc = StandardCommandPoolAlloc;

    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.inner.inner()
    }

    #[inline]
    fn into_alloc(self) -> Self::Alloc {
        self.inner
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.inner.queue_family()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolBuilder {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

/// Command buffer allocated from a `StandardCommandPool`.
pub struct StandardCommandPoolAlloc {
    // The actual command buffer. Must always be `Some`. Value extracted in the destructor.
    cmd: Option<UnsafeCommandPoolAlloc>,
    // We hold a reference to the command pool for our destructor.
    pool: Arc<StandardCommandPoolPerThread>,
    // True if secondary command buffer.
    secondary: bool,
    // The device we belong to. Necessary because of the `DeviceOwned` trait implementation.
    device: Arc<Device>,
}

unsafe impl Send for StandardCommandPoolAlloc {
}
unsafe impl Sync for StandardCommandPoolAlloc {
}

unsafe impl CommandPoolAlloc for StandardCommandPoolAlloc {
    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.cmd.as_ref().unwrap()
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        let queue_family_id = self.pool.pool.lock().unwrap().queue_family().id();

        self.device
            .physical_device()
            .queue_family_by_id(queue_family_id)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolAlloc {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        // Note that we could grab the device from `self.pool`. Unfortunately this requires a mutex
        // lock, so it isn't compatible with the API of `DeviceOwned`.
        &self.device
    }
}

impl Drop for StandardCommandPoolAlloc {
    fn drop(&mut self) {
        let cmd = self.cmd.take().unwrap();

        if self.secondary {
            self.pool.available_secondary_command_buffers.push(cmd);
        } else {
            self.pool.available_primary_command_buffers.push(cmd);
        }
    }
}

#[cfg(test)]
mod tests {
    use command_buffer::pool::CommandPool;
    use command_buffer::pool::CommandPoolBuilderAlloc;
    use device::Device;
    use VulkanObject;

    #[test]
    fn reuse_command_buffers() {
        let (device, _) = gfx_dev_and_queue!();
        let queue_family = device.physical_device().queue_families().next().unwrap();

        let pool = Device::standard_command_pool(&device, queue_family);

        let cb = pool.alloc(false, 1).unwrap().next().unwrap();
        let raw = cb.inner().internal_object();
        drop(cb);

        let cb2 = pool.alloc(false, 1).unwrap().next().unwrap();
        assert_eq!(raw, cb2.inner().internal_object());
    }
}
<commit_msg>Improvements for the StandardCommandPool (#800)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use crossbeam::sync::MsQueue;
use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::Weak;
use std::thread;
use std::vec::IntoIter as VecIntoIter;

use command_buffer::pool::CommandPool;
use command_buffer::pool::CommandPoolAlloc;
use command_buffer::pool::CommandPoolBuilderAlloc;
use command_buffer::pool::UnsafeCommandPool;
use command_buffer::pool::UnsafeCommandPoolAlloc;
use instance::QueueFamily;

use OomError;
use VulkanObject;
use device::Device;
use device::DeviceOwned;

/// Standard implementation of a command pool.
///
/// It is guaranteed that the allocated command buffers keep the `Arc<StandardCommandPool>` alive.
/// This is desirable so that we can store a `Weak<StandardCommandPool>`.
///
/// Will use one Vulkan pool per thread in order to avoid locking. Will try to reuse command
/// buffers. Command buffers can't be moved between threads during the building process, but
/// finished command buffers can.
pub struct StandardCommandPool {
    // The device.
    device: Arc<Device>,

    // Identifier of the queue family.
    queue_family: u32,

    // For each thread, we store thread-specific info.
    per_thread: Mutex<FnvHashMap<thread::ThreadId, Weak<StandardCommandPoolPerThread>>>,
}

unsafe impl Send for StandardCommandPool {
}
unsafe impl Sync for StandardCommandPool {
}

struct StandardCommandPoolPerThread {
    // The Vulkan pool of this thread.
    pool: Mutex<UnsafeCommandPool>,
    // List of existing primary command buffers that are available for reuse.
    available_primary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
    // List of existing secondary command buffers that are available for reuse.
    available_secondary_command_buffers: MsQueue<UnsafeCommandPoolAlloc>,
}

impl StandardCommandPool {
    /// Builds a new pool.
    ///
    /// # Panic
    ///
    /// - Panics if the device and the queue family don't belong to the same physical device.
    ///
    pub fn new(device: Arc<Device>, queue_family: QueueFamily) -> StandardCommandPool {
        assert_eq!(device.physical_device().internal_object(),
                   queue_family.physical_device().internal_object());

        StandardCommandPool {
            device: device,
            queue_family: queue_family.id(),
            per_thread: Mutex::new(Default::default()),
        }
    }
}

unsafe impl CommandPool for Arc<StandardCommandPool> {
    type Iter = VecIntoIter<StandardCommandPoolBuilder>;
    type Builder = StandardCommandPoolBuilder;
    type Alloc = StandardCommandPoolAlloc;

    fn alloc(&self, secondary: bool, count: u32) -> Result<Self::Iter, OomError> {
        // Find the correct `StandardCommandPoolPerThread` structure.
        let mut hashmap = self.per_thread.lock().unwrap();
        // TODO: meh for iterating everything every time
        hashmap.retain(|_, w| w.upgrade().is_some());

        // Get an appropriate `Arc<StandardCommandPoolPerThread>`.
        let per_thread = match hashmap.entry(thread::current().id()) {
            Entry::Occupied(mut entry) => {
                // The `unwrap()` can't fail, since we retained only valid members earlier.
                entry.get().upgrade().unwrap()
            },
            Entry::Vacant(entry) => {
                let new_pool =
                    UnsafeCommandPool::new(self.device.clone(), self.queue_family(), false, true)?;
                let pt = Arc::new(StandardCommandPoolPerThread {
                                      pool: Mutex::new(new_pool),
                                      available_primary_command_buffers: MsQueue::new(),
                                      available_secondary_command_buffers: MsQueue::new(),
                                  });

                entry.insert(Arc::downgrade(&pt));
                pt
            },
        };

        // The final output.
        let mut output = Vec::with_capacity(count as usize);

        // First, pick from already-existing command buffers.
        {
            let existing = if secondary {
                &per_thread.available_secondary_command_buffers
            } else {
                &per_thread.available_primary_command_buffers
            };

            for _ in 0 .. count as usize {
                if let Some(cmd) = existing.try_pop() {
                    output.push(StandardCommandPoolBuilder {
                        inner: StandardCommandPoolAlloc {
                            cmd: Some(cmd),
                            pool: per_thread.clone(),
                            pool_parent: self.clone(),
                            secondary: secondary,
                            device: self.device.clone(),
                        },
                        dummy_avoid_send_sync: PhantomData,
                    });
                } else {
                    break;
                }
            }
        };

        // Then allocate the rest.
        if output.len() < count as usize {
            let pool_lock = per_thread.pool.lock().unwrap();
            let num_new = count as usize - output.len();

            for cmd in pool_lock.alloc_command_buffers(secondary, num_new)? {
                output.push(StandardCommandPoolBuilder {
                    inner: StandardCommandPoolAlloc {
                        cmd: Some(cmd),
                        pool: per_thread.clone(),
                        pool_parent: self.clone(),
                        secondary: secondary,
                        device: self.device.clone(),
                    },
                    dummy_avoid_send_sync: PhantomData,
                });
            }
        }

        // Final output.
        Ok(output.into_iter())
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.device
            .physical_device()
            .queue_family_by_id(self.queue_family)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPool {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        &self.device
    }
}

/// Command buffer allocated from a `StandardCommandPool` and that is currently being built.
pub struct StandardCommandPoolBuilder {
    // The only difference between a `StandardCommandPoolBuilder` and a `StandardCommandPoolAlloc`
    // is that the former must not implement `Send` and `Sync`. Therefore we just share the structs.
    inner: StandardCommandPoolAlloc,
    // Unimplemented `Send` and `Sync` from the builder.
    dummy_avoid_send_sync: PhantomData<*const u8>,
}

unsafe impl CommandPoolBuilderAlloc for StandardCommandPoolBuilder {
    type Alloc = StandardCommandPoolAlloc;

    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.inner.inner()
    }

    #[inline]
    fn into_alloc(self) -> Self::Alloc {
        self.inner
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        self.inner.queue_family()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolBuilder {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }
}

/// Command buffer allocated from a `StandardCommandPool`.
pub struct StandardCommandPoolAlloc {
    // The actual command buffer. Must always be `Some`. Value extracted in the destructor.
    cmd: Option<UnsafeCommandPoolAlloc>,
    // We hold a reference to the command pool for our destructor.
    pool: Arc<StandardCommandPoolPerThread>,
    // Keep alive the `StandardCommandPool`, otherwise it would be destroyed.
    pool_parent: Arc<StandardCommandPool>,
    // True if secondary command buffer.
    secondary: bool,
    // The device we belong to. Necessary because of the `DeviceOwned` trait implementation.
    device: Arc<Device>,
}

unsafe impl Send for StandardCommandPoolAlloc {
}
unsafe impl Sync for StandardCommandPoolAlloc {
}

unsafe impl CommandPoolAlloc for StandardCommandPoolAlloc {
    #[inline]
    fn inner(&self) -> &UnsafeCommandPoolAlloc {
        self.cmd.as_ref().unwrap()
    }

    #[inline]
    fn queue_family(&self) -> QueueFamily {
        let queue_family_id = self.pool.pool.lock().unwrap().queue_family().id();

        self.device
            .physical_device()
            .queue_family_by_id(queue_family_id)
            .unwrap()
    }
}

unsafe impl DeviceOwned for StandardCommandPoolAlloc {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        // Note that we could grab the device from `self.pool`. Unfortunately this requires a mutex
        // lock, so it isn't compatible with the API of `DeviceOwned`.
        &self.device
    }
}

impl Drop for StandardCommandPoolAlloc {
    fn drop(&mut self) {
        let cmd = self.cmd.take().unwrap();

        if self.secondary {
            self.pool.available_secondary_command_buffers.push(cmd);
        } else {
            self.pool.available_primary_command_buffers.push(cmd);
        }
    }
}

#[cfg(test)]
mod tests {
    use command_buffer::pool::CommandPool;
    use command_buffer::pool::CommandPoolBuilderAlloc;
    use command_buffer::pool::StandardCommandPool;
    use device::Device;
    use std::sync::Arc;
    use VulkanObject;

    #[test]
    fn reuse_command_buffers() {
        let (device, _) = gfx_dev_and_queue!();
        let queue_family = device.physical_device().queue_families().next().unwrap();

        let pool = Device::standard_command_pool(&device, queue_family);

        let cb = pool.alloc(false, 1).unwrap().next().unwrap();
        let raw = cb.inner().internal_object();
        drop(cb);

        let cb2 = pool.alloc(false, 1).unwrap().next().unwrap();
        assert_eq!(raw, cb2.inner().internal_object());
    }

    #[test]
    fn pool_kept_alive_by_allocs() {
        let (device, queue) = gfx_dev_and_queue!();

        let pool = Arc::new(StandardCommandPool::new(device, queue.family()));
        let pool_weak = Arc::downgrade(&pool);

        let cb = pool.alloc(false, 1).unwrap().next().unwrap();
        drop(pool);
        assert!(pool_weak.upgrade().is_some());

        drop(cb);
        assert!(pool_weak.upgrade().is_none());
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![crate_type="staticlib"]
#![no_std]

//! Sample application for BlueNRG communication over SPI in X-NUCLEO-IDB04A1
//! extension board for NUCLEO-L152RE

#[macro_use] #[plugin]
extern crate core;
extern crate zinc;

use core::intrinsics::abort;
// "curious module hack" - a workaround for `fmt` module being looked in `std`
// by the `write!` macro
mod std {
  pub use core::fmt;
}

//TODO(kvark): temporary `u8 -> str` conversion until #235 is resolved
fn map_byte(s: u8) -> (&'static str, &'static str) {
  fn map_hex(h: u8) -> &'static str {
      match h {
        0x0 => "0",
        0x1 => "1",
        0x2 => "2",
        0x3 => "3",
        0x4 => "4",
        0x5 => "5",
        0x6 => "6",
        0x7 => "7",
        0x8 => "8",
        0x9 => "9",
        0xA => "A",
        0xB => "B",
        0xC => "C",
        0xD => "D",
        0xE => "E",
        0xF => "F",
        _ => "",
      }
  }
  (map_hex(s>>4), map_hex(s&0xF))
}

#[no_mangle]
pub unsafe fn main() {
  use core::fmt::Writer;
  use core::result::Result;
  use zinc::drivers::bluenrg;
  use zinc::hal;
  use zinc::hal::pin::Gpio;
  use zinc::hal::stm32l1::{init, pin, spi, usart};

  zinc::hal::mem_init::init_stack();
  zinc::hal::mem_init::init_data();

  let sys_clock = init::ClockConfig::new_default();
  sys_clock.setup();

  let _usart_tx = pin::Pin::new(pin::Port::PortA, 2,
    pin::Mode::AltFunction(
      pin::AltMode::AfUsart1_Usart2_Usart3,
      pin::OutputType::OutPushPull,
      pin::Speed::VeryLow),
    pin::PullType::PullNone);

  let mut uart = usart::Usart::new(usart::UsartPeripheral::Usart2, 38400, usart::WordLen::WordLen8bits,
    hal::uart::Parity::Disabled, usart::StopBit::StopBit1bit, &sys_clock);
  let _ = write!(&mut uart, "BlueNRG test app for STM32L1\n");

  let _spi_clock = pin::Pin::new(pin::Port::PortB, 3,
    pin::Mode::AltFunction(pin::AltMode::AfSpi1_Spi2, pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullDown);

  let _spi_in = pin::Pin::new(pin::Port::PortA, 6,
    pin::Mode::AltFunction(pin::AltMode::AfSpi1_Spi2, pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullNone);

  let _spi_out = pin::Pin::new(pin::Port::PortA, 7,
    pin::Mode::AltFunction(pin::AltMode::AfSpi1_Spi2, pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullNone);

  let spi_csn = pin::Pin::new(pin::Port::PortA, 1,
    pin::Mode::GpioOut(pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullUp);
  spi_csn.set_high();

  let spi = spi::Spi::new(spi::Peripheral::Spi1, spi::Direction::FullDuplex,
    spi::Role::Master, spi::DataSize::U8, spi::DataFormat::MsbFirst, 1).
    unwrap_or_else(|_| {
      let _ = write!(&mut uart, "SPI failed to initialize");
      abort()
    });

  let bnrg_reset = pin::Pin::new(pin::Port::PortA, 8,
    pin::Mode::GpioOut(pin::OutputType::OutPushPull, pin::Speed::VeryLow),
    pin::PullType::PullUp);

  bnrg_reset.set_low();
  let _ = write!(&mut uart, "SPI created, status = {}\n",
    map_byte(spi.get_status()));
  bnrg_reset.set_high();

  let blue = bluenrg::BlueNrg::new(spi_csn, spi);

  let _ = match blue.wakeup(100) {
    Result::Ok((size_write, size_read)) => write!(&mut uart,
      "BlueNRG is ready, write size = {}, read size = {}\n",
      map_byte(size_write as u8), map_byte(size_read as u8)),
    Result::Err(bluenrg::Error::Sleeping) => write!(&mut uart,
      "BlueNRG is sleeping\n"),
    Result::Err(bluenrg::Error::Allocating) => write!(&mut uart,
      "BlueNRG is allocating buffers\n"),
    Result::Err(bluenrg::Error::Unknown(status)) => write!(&mut uart,
      "BlueNRG unknown status = {}\n", map_byte(status)),
    Result::Err(bluenrg::Error::BufferSize(_)) => write!(&mut uart, ""),
  };

  loop {}
}
<commit_msg>"Fixed" bluenrg demo to compile.<commit_after>#![feature(plugin)]
#![crate_type="staticlib"]
#![no_std]

//! Sample application for BlueNRG communication over SPI in X-NUCLEO-IDB04A1
//! extension board for NUCLEO-L152RE

#[macro_use] #[plugin]
extern crate core;
extern crate zinc;

use core::intrinsics::abort;
// "curious module hack" - a workaround for `fmt` module being looked in `std`
// by the `write!` macro
mod std {
  pub use core::fmt;
}

//TODO(kvark): temporary `u8 -> str` conversion until #235 is resolved
fn map_byte(s: u8) -> (&'static str, &'static str) {
  fn map_hex(h: u8) -> &'static str {
      match h {
        0x0 => "0",
        0x1 => "1",
        0x2 => "2",
        0x3 => "3",
        0x4 => "4",
        0x5 => "5",
        0x6 => "6",
        0x7 => "7",
        0x8 => "8",
        0x9 => "9",
        0xA => "A",
        0xB => "B",
        0xC => "C",
        0xD => "D",
        0xE => "E",
        0xF => "F",
        _ => "",
      }
  }
  (map_hex(s>>4), map_hex(s&0xF))
}

#[no_mangle]
pub unsafe fn main() {
  use core::fmt::Writer;
  use core::result::Result;
  use zinc::drivers::bluenrg;
  use zinc::hal;
  use zinc::hal::pin::Gpio;
  use zinc::hal::stm32l1::{init, pin, spi, usart};

  zinc::hal::mem_init::init_stack();
  zinc::hal::mem_init::init_data();

  let sys_clock = init::ClockConfig::new_default();
  sys_clock.setup();

  let _usart_tx = pin::Pin::new(pin::Port::PortA, 2,
    pin::Mode::AltFunction(
      pin::AltMode::AfUsart1_Usart2_Usart3,
      pin::OutputType::OutPushPull,
      pin::Speed::VeryLow),
    pin::PullType::PullNone);

  let mut uart = usart::Usart::new(usart::UsartPeripheral::Usart2, 38400, usart::WordLen::WordLen8bits,
    hal::uart::Parity::Disabled, usart::StopBit::StopBit1bit, &sys_clock);
  let _ = write!(&mut uart, "BlueNRG test app for STM32L1\n");

  let _spi_clock = pin::Pin::new(pin::Port::PortB, 3,
    pin::Mode::AltFunction(pin::AltMode::AfSpi1_Spi2, pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullDown);

  let _spi_in = pin::Pin::new(pin::Port::PortA, 6,
    pin::Mode::AltFunction(pin::AltMode::AfSpi1_Spi2, pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullNone);

  let _spi_out = pin::Pin::new(pin::Port::PortA, 7,
    pin::Mode::AltFunction(pin::AltMode::AfSpi1_Spi2, pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullNone);

  let spi_csn = pin::Pin::new(pin::Port::PortA, 1,
    pin::Mode::GpioOut(pin::OutputType::OutPushPull, pin::Speed::Medium),
    pin::PullType::PullUp);
  spi_csn.set_high();

  let spi = spi::Spi::new(spi::Peripheral::Spi1, spi::Direction::FullDuplex,
    spi::Role::Master, spi::DataSize::U8, spi::DataFormat::MsbFirst, 1).
    unwrap_or_else(|_| {
      let _ = write!(&mut uart, "SPI failed to initialize");
      abort()
    });

  let bnrg_reset = pin::Pin::new(pin::Port::PortA, 8,
    pin::Mode::GpioOut(pin::OutputType::OutPushPull, pin::Speed::VeryLow),
    pin::PullType::PullUp);

  bnrg_reset.set_low();
  let status_s = map_byte(spi.get_status());
  let _ = write!(&mut uart, "SPI created, status = {}{}\n", status_s.0, status_s.1);
  bnrg_reset.set_high();

  let blue = bluenrg::BlueNrg::new(spi_csn, spi);

  match blue.wakeup(100) {
    Result::Ok((size_write, size_read)) => {
      let size_write_s = map_byte(size_write as u8);
      let size_read_s = map_byte(size_read as u8);
      write!(&mut uart,
        "BlueNRG is ready, write size = {}{}, read size = {}{}\n",
        size_write_s.0, size_write_s.1, size_read_s.0, size_read_s.1);
    },
    Result::Err(bluenrg::Error::Sleeping) => {
      write!(&mut uart, "BlueNRG is sleeping\n");
    },
    Result::Err(bluenrg::Error::Allocating) => {
      write!(&mut uart, "BlueNRG is allocating buffers\n");
    },
    Result::Err(bluenrg::Error::Unknown(status)) => {
      let status_s = map_byte(status);
      write!(&mut uart,
        "BlueNRG unknown status = {}{}\n", status_s.0, status_s.1);
    },
    Result::Err(bluenrg::Error::BufferSize(_)) => { write!(&mut uart, ""); }
  };

  loop {}
}
<|endoftext|>