<commit_before>use bifrost::raft::client::RaftClient;
use bifrost::raft::state_machine::master::ExecError;
use bifrost_hasher::hash_str;

use dovahkiin::types::Type;
use parking_lot::{RwLock, RwLockReadGuard};
use std::collections::HashMap;
use std::mem;

use super::types;
use core::borrow::Borrow;
use std::string::String;
use std::sync::Arc;

use futures::prelude::*;
use futures::FutureExt;
use std::ops::Deref;

pub mod sm;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Schema {
    pub id: u32,
    pub name: String,
    pub key_field: Option<Vec<u64>>,
    pub str_key_field: Option<Vec<String>>,
    pub fields: Field,
    pub static_bound: usize,
    pub is_dynamic: bool,
    pub is_scannable: bool,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum IndexType {
    Ranged,
    Hashed,
    Vectorized,
    Statistics,
}

impl Schema {
    pub fn new(
        name: &str,
        key_field: Option<Vec<String>>,
        mut fields: Field,
        is_dynamic: bool,
        is_scannable: bool,
    ) -> Schema {
        let mut bound = 0;
        fields.assign_offsets(&mut bound);
        trace!("Schema {:?} has bound {}", fields, bound);
        Schema {
            id: 0,
            name: name.to_string(),
            key_field: match key_field {
                None => None,
                Some(ref keys) => Some(keys.iter().map(|f| hash_str(f)).collect()), // field list into field ids
            },
            str_key_field: key_field,
            static_bound: bound,
            fields,
            is_dynamic,
            is_scannable,
        }
    }
    pub fn new_with_id(
        id: u32,
        name: &str,
        key_field: Option<Vec<String>>,
        fields: Field,
        dynamic: bool,
        scannable: bool,
    ) -> Schema {
        let mut schema = Schema::new(name, key_field, fields, dynamic, scannable);
        schema.id = id;
        schema
    }
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Field {
    pub data_type: Type,
    pub nullable: bool,
    pub is_array: bool,
    pub sub_fields: Option<Vec<Field>>,
    pub name: String,
    pub name_id: u64,
    pub indices: Vec<IndexType>,
    pub offset: Option<usize>,
}

impl Field {
    pub fn new(
        name: &str,
        data_type: Type,
        nullable: bool,
        is_array: bool,
        sub_fields: Option<Vec<Field>>,
        indices: Vec<IndexType>,
    ) -> Field {
        Field {
            name: name.to_string(),
            name_id: types::key_hash(name),
            data_type,
            nullable,
            is_array,
            sub_fields,
            indices,
            offset: None,
        }
    }
    fn assign_offsets(&mut self, offset: &mut usize) {
        const POINTER_SIZE: usize = mem::size_of::<u32>();
        self.offset = Some(*offset);
        let is_field_var = self.is_var();
        if self.nullable && !is_field_var {
            *offset += 1;
        }
        if self.is_array {
            // u32 as indication of the offset to the actual data
            *offset += POINTER_SIZE;
        } else if let Some(ref mut subs) = self.sub_fields {
            subs.iter_mut().for_each(|f| f.assign_offsets(offset));
        } else {
            if !is_field_var {
                *offset += types::size_of_type(self.data_type);
            } else {
                *offset += POINTER_SIZE;
            }
        }
        trace!(
            "Assigned field {} to {:?}, now at {}, var {}, offset moved {}",
            self.name,
            self.offset,
            offset,
            is_field_var,
            *offset - self.offset.unwrap()
        );
    }
    pub fn is_var(&self) -> bool {
        self.is_array || !types::fixed_size(self.data_type)
    }
}

pub struct SchemasMap {
    schema_map: HashMap<u32, Schema>,
    name_map: HashMap<String, u32>,
    id_counter: u32,
}

pub struct LocalSchemasCache {
    map: Arc<RwLock<SchemasMap>>,
}

impl LocalSchemasCache {
    pub async fn new(
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<LocalSchemasCache, ExecError> {
        info!("Initializing local schema cache");
        let map = Arc::new(RwLock::new(SchemasMap::new()));
        let m1 = map.clone();
        let m2 = map.clone();
        let sm = sm::client::SMClient::new(sm::generate_sm_id(group), raft_client);
        let sm_data = sm.get_all().await?;
        {
            let mut map = map.write();
            debug!("Importing {} schemas from cluster", sm_data.len());
            for schema in sm_data {
                trace!("Importing schema {}", schema.name);
                map.new_schema(schema);
            }
        }
        debug!("Subscribing schema events...");
        let _ = sm
            .on_schema_added(move |schema| {
                debug!("Add schema {} from subscription", schema.id);
                let mut m1 = m1.write();
                m1.new_schema(schema);
                future::ready(()).boxed()
            })
            .await?;
        let _ = sm
            .on_schema_deleted(move |schema| {
                let mut m2 = m2.write();
                m2.del_schema(&schema).unwrap();
                future::ready(()).boxed()
            })
            .await?;
        let schemas = LocalSchemasCache { map };
        info!("Local schema initialization completed");
        return Ok(schemas);
    }
    pub fn new_local(_group: &str) -> Self {
        let map = Arc::new(RwLock::new(SchemasMap::new()));
        LocalSchemasCache { map }
    }
    pub fn get(&self, id: &u32) -> Option<ReadingSchema> {
        let m = self.map.read();
        let so = m.get(id).map(|s| s as *const Schema);
        so.map(|s| ReadingSchema {
            _owner: m,
            reference: s,
        })
    }
    pub fn new_schema(&self, schema: Schema) {
        // for debug only
        let mut m = self.map.write();
        m.new_schema(schema)
    }
    pub fn name_to_id(&self, name: &str) -> Option<u32> {
        let m = self.map.read();
        m.name_to_id(name)
    }
}

impl SchemasMap {
    pub fn new() -> SchemasMap {
        SchemasMap {
            schema_map: HashMap::new(),
            name_map: HashMap::new(),
            id_counter: 0,
        }
    }
    pub fn new_schema(&mut self, schema: Schema) {
        let name = schema.name.clone();
        let id = schema.id;
        self.schema_map.insert(id, schema);
        self.name_map.insert(name, id);
    }
    pub fn del_schema(&mut self, name: &str) -> Result<(), ()> {
        if let Some(id) = self.name_to_id(name) {
            self.schema_map.remove(&id);
        }
        self.name_map.remove(&name.to_string());
        Ok(())
    }
    pub fn get_by_name(&self, name: &str) -> Option<&Schema> {
        if let Some(id) = self.name_to_id(name) {
            return self.get(&id);
        }
        return None;
    }
    pub fn get(&self, id: &u32) -> Option<&Schema> {
        if let Some(schema) = self.schema_map.get(id) {
            return Some(schema);
        }
        return None;
    }
    pub fn name_to_id(&self, name: &str) -> Option<u32> {
        self.name_map.get(&name.to_string()).cloned()
    }
    fn next_id(&mut self) -> u32 {
        self.id_counter += 1;
        while self.schema_map.contains_key(&self.id_counter) {
            self.id_counter += 1;
        }
        self.id_counter
    }
    fn get_all(&self) -> Vec<Schema> {
        self.schema_map
            .values()
            .map(|s_ref| {
                let arc = s_ref.clone();
                let r: &Schema = arc.borrow();
                r.clone()
            })
            .collect()
    }
    fn load_from_list(&mut self, data: Vec<Schema>) {
        for schema in data {
            let id = schema.id;
            self.name_map.insert(schema.name.clone(), id);
            self.schema_map.insert(id, schema);
        }
    }
}

pub struct ReadingRef<O, T: ?Sized> {
    _owner: O,
    reference: *const T,
}

pub type ReadingSchema<'a> = ReadingRef<RwLockReadGuard<'a, SchemasMap>, Schema>;

impl<O, T: ?Sized> Deref for ReadingRef<O, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.reference }
    }
}
<commit_msg>selection shortcut path<commit_after>use bifrost::raft::client::RaftClient;
use bifrost::raft::state_machine::master::ExecError;
use bifrost_hasher::hash_str;

use dovahkiin::types::Type;
use parking_lot::{RwLock, RwLockReadGuard};
use std::collections::HashMap;
use std::mem;

use super::types;
use core::borrow::Borrow;
use std::string::String;
use std::sync::Arc;

use futures::prelude::*;
use futures::FutureExt;
use std::ops::Deref;

pub mod sm;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Schema {
    pub id: u32,
    pub name: String,
    pub key_field: Option<Vec<u64>>,
    pub str_key_field: Option<Vec<String>>,
    pub id_index: HashMap<u64, Vec<usize>>,
    pub fields: Field,
    pub static_bound: usize,
    pub is_dynamic: bool,
    pub is_scannable: bool,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum IndexType {
    Ranged,
    Hashed,
    Vectorized,
    Statistics,
}

impl Schema {
    pub fn new(
        name: &str,
        key_field: Option<Vec<String>>,
        mut fields: Field,
        is_dynamic: bool,
        is_scannable: bool,
    ) -> Schema {
        let mut bound = 0;
        let mut id_index = HashMap::new();
        fields.assign_offsets(&mut bound, &mut id_index, String::new(), vec![]);
        trace!("Schema {:?} has bound {}", fields, bound);
        Schema {
            id: 0,
            name: name.to_string(),
            key_field: match key_field {
                None => None,
                Some(ref keys) => Some(keys.iter().map(|f| hash_str(f)).collect()), // field list into field ids
            },
            str_key_field: key_field,
            static_bound: bound,
            fields,
            is_dynamic,
            is_scannable,
            id_index,
        }
    }
    pub fn new_with_id(
        id: u32,
        name: &str,
        key_field: Option<Vec<String>>,
        fields: Field,
        dynamic: bool,
        scannable: bool,
    ) -> Schema {
        let mut schema = Schema::new(name, key_field, fields, dynamic, scannable);
        schema.id = id;
        schema
    }
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Field {
    pub data_type: Type,
    pub nullable: bool,
    pub is_array: bool,
    pub sub_fields: Option<Vec<Field>>,
    pub name: String,
    pub name_id: u64,
    pub indices: Vec<IndexType>,
    pub offset: Option<usize>,
}

impl Field {
    pub fn new(
        name: &str,
        data_type: Type,
        nullable: bool,
        is_array: bool,
        sub_fields: Option<Vec<Field>>,
        indices: Vec<IndexType>,
    ) -> Field {
        Field {
            name: name.to_string(),
            name_id: types::key_hash(name),
            data_type,
            nullable,
            is_array,
            sub_fields,
            indices,
            offset: None,
        }
    }
    fn assign_offsets(
        &mut self,
        offset: &mut usize,
        id_index: &mut HashMap<u64, Vec<usize>>,
        name_path: String,
        field_path: Vec<usize>,
    ) {
        const POINTER_SIZE: usize = mem::size_of::<u32>();
        self.offset = Some(*offset);
        let is_field_var = self.is_var();
        let name_path_hash = hash_str(&name_path);
        if self.nullable && !is_field_var {
            *offset += 1;
        }
        if self.is_array {
            // u32 as indication of the offset to the actual data
            *offset += POINTER_SIZE;
        } else if let Some(ref mut subs) = self.sub_fields {
            let format_name = if name_path.is_empty() {
                name_path
            } else {
                format!("{}|", name_path)
            };
            subs.iter_mut().enumerate().for_each(|(i, f)| {
                let mut new_path = field_path.clone();
                new_path.push(i);
                let new_name_path = format!("{}{}", format_name, f.name);
                f.assign_offsets(offset, id_index, new_name_path, new_path);
            });
        } else {
            if !is_field_var {
                *offset += types::size_of_type(self.data_type);
            } else {
                *offset += POINTER_SIZE;
            }
        }
        if !field_path.is_empty() {
            id_index.insert(name_path_hash, field_path);
        }
        trace!(
            "Assigned field {} to {:?}, now at {}, var {}, offset moved {}",
            self.name,
            self.offset,
            offset,
            is_field_var,
            *offset - self.offset.unwrap()
        );
    }
    pub fn is_var(&self) -> bool {
        self.is_array || !types::fixed_size(self.data_type)
    }
}

pub struct SchemasMap {
    schema_map: HashMap<u32, Schema>,
    name_map: HashMap<String, u32>,
    id_counter: u32,
}

pub struct LocalSchemasCache {
    map: Arc<RwLock<SchemasMap>>,
}

impl LocalSchemasCache {
    pub async fn new(
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<LocalSchemasCache, ExecError> {
        info!("Initializing local schema cache");
        let map = Arc::new(RwLock::new(SchemasMap::new()));
        let m1 = map.clone();
        let m2 = map.clone();
        let sm = sm::client::SMClient::new(sm::generate_sm_id(group), raft_client);
        let sm_data = sm.get_all().await?;
        {
            let mut map = map.write();
            debug!("Importing {} schemas from cluster", sm_data.len());
            for schema in sm_data {
                trace!("Importing schema {}", schema.name);
                map.new_schema(schema);
            }
        }
        debug!("Subscribing schema events...");
        let _ = sm
            .on_schema_added(move |schema| {
                debug!("Add schema {} from subscription", schema.id);
                let mut m1 = m1.write();
                m1.new_schema(schema);
                future::ready(()).boxed()
            })
            .await?;
        let _ = sm
            .on_schema_deleted(move |schema| {
                let mut m2 = m2.write();
                m2.del_schema(&schema).unwrap();
                future::ready(()).boxed()
            })
            .await?;
        let schemas = LocalSchemasCache { map };
        info!("Local schema initialization completed");
        return Ok(schemas);
    }
    pub fn new_local(_group: &str) -> Self {
        let map = Arc::new(RwLock::new(SchemasMap::new()));
        LocalSchemasCache { map }
    }
    pub fn get(&self, id: &u32) -> Option<ReadingSchema> {
        let m = self.map.read();
        let so = m.get(id).map(|s| s as *const Schema);
        so.map(|s| ReadingSchema {
            _owner: m,
            reference: s,
        })
    }
    pub fn new_schema(&self, schema: Schema) {
        // for debug only
        let mut m = self.map.write();
        m.new_schema(schema)
    }
    pub fn name_to_id(&self, name: &str) -> Option<u32> {
        let m = self.map.read();
        m.name_to_id(name)
    }
}

impl SchemasMap {
    pub fn new() -> SchemasMap {
        SchemasMap {
            schema_map: HashMap::new(),
            name_map: HashMap::new(),
            id_counter: 0,
        }
    }
    pub fn new_schema(&mut self, schema: Schema) {
        let name = schema.name.clone();
        let id = schema.id;
        self.schema_map.insert(id, schema);
        self.name_map.insert(name, id);
    }
    pub fn del_schema(&mut self, name: &str) -> Result<(), ()> {
        if let Some(id) = self.name_to_id(name) {
            self.schema_map.remove(&id);
        }
        self.name_map.remove(&name.to_string());
        Ok(())
    }
    pub fn get_by_name(&self, name: &str) -> Option<&Schema> {
        if let Some(id) = self.name_to_id(name) {
            return self.get(&id);
        }
        return None;
    }
    pub fn get(&self, id: &u32) -> Option<&Schema> {
        if let Some(schema) = self.schema_map.get(id) {
            return Some(schema);
        }
        return None;
    }
    pub fn name_to_id(&self, name: &str) -> Option<u32> {
        self.name_map.get(&name.to_string()).cloned()
    }
    fn next_id(&mut self) -> u32 {
        self.id_counter += 1;
        while self.schema_map.contains_key(&self.id_counter) {
            self.id_counter += 1;
        }
        self.id_counter
    }
    fn get_all(&self) -> Vec<Schema> {
        self.schema_map
            .values()
            .map(|s_ref| {
                let arc = s_ref.clone();
                let r: &Schema = arc.borrow();
                r.clone()
            })
            .collect()
    }
    fn load_from_list(&mut self, data: Vec<Schema>) {
        for schema in data {
            let id = schema.id;
            self.name_map.insert(schema.name.clone(), id);
            self.schema_map.insert(id, schema);
        }
    }
}

pub struct ReadingRef<O, T: ?Sized> {
    _owner: O,
    reference: *const T,
}

pub type ReadingSchema<'a> = ReadingRef<RwLockReadGuard<'a, SchemasMap>, Schema>;

impl<O, T: ?Sized> Deref for ReadingRef<O, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.reference }
    }
}
<|endoftext|>


<commit_before>use calx::color::*;
use calx::{Rgba, Kernel, KernelTerrain};
use calx::backend::{Image};
use content::{TerrainType, Brush};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

impl Angle {
    /// Return the angle of the vertical surface, if any, in degrees.
    pub fn degree(&self) -> Option<f32> {
        match *self {
            Angle::Up => None,
            Angle::North => Some(0.0),
            Angle::XWallBack => Some(30.0),
            Angle::Northeast => Some(60.0),
            Angle::East => Some(90.0),
            Angle::Southeast => Some(120.0),
            Angle::YWall => Some(150.0),
            Angle::South => Some(180.0),
            Angle::XWall => Some(210.0),
            Angle::Southwest => Some(240.0),
            Angle::West => Some(270.0),
            Angle::Northwest => Some(300.0),
            Angle::YWallBack => Some(330.0),
        }
    }
}

/// Generate draw instructions for a terrain cell.
///
/// Params to the draw function: Draw layer, brush, brush frame, main
/// color, border color.
pub fn render<F>(k: &Kernel<TerrainType>, mut draw: F)
    where F: FnMut(Image, Angle, Rgba, Rgba)
{
    use content::Brush::*;
    use self::Angle::*;

    enum T {
        Floor(Brush, Rgba),
        Floor2(Brush, Rgba, Rgba),
        Prop(Brush, Rgba),
        Prop2(Brush, Rgba, Rgba),
        Wall(Brush, Rgba),
        Wall2(Brush, Rgba, Rgba),
        Block(Brush, Rgba),
        Block2(Brush, Rgba, Rgba),
    }

    fn process<C: KernelTerrain, F>(
        k: &Kernel<C>, draw: &mut F, kind: T)
        where F: FnMut(Image, Angle, Rgba, Rgba)
        {
            // NB: Black-#000 foreground color prohibits recoloring of the
            // tile in FOV view, only use for special blocks.
            match kind {
                T::Floor(brush, color) => process(k, draw, T::Floor2(brush, color, BLACK)),
                T::Prop(brush, color) => process(k, draw, T::Prop2(brush, color, BLACK)),
                T::Wall(brush, color) => process(k, draw, T::Wall2(brush, color, BLACK)),
                T::Block(brush, color) => process(k, draw, T::Block2(brush, color, BLACK)),

                T::Floor2(brush, color, back) => {
                    draw(brush.get(0), Up, color, back);
                }
                T::Prop2(brush, color, back) => {
                    draw(brush.get(0), South, color, back);
                }
                T::Wall2(brush, color, back) => {
                    let extends = k.wall_extends();
                    if extends[0] {
                        draw(brush.get(2), XWall, color, back);
                    } else {
                        draw(brush.get(0), XWall, color, back);
                    }
                    if extends[1] {
                        draw(brush.get(3), YWall, color, back);
                    } else {
                        draw(brush.get(1), YWall, color, back);
                    }
                }
                T::Block2(brush, color, back) => {
                    // This part gets a little tricky. Basic idea is that
                    // there's an inner pointy-top hex core and the block hull
                    // will snap to that instead of the outer flat-top hex
                    // edge if neither adjacent face to the outer hex vertex
                    // is connected to another block.
                    //
                    // Based on how the sprites split up, the processing is
                    // done in four vertical segments.

                    let faces = k.block_faces();

                    // Do we snap to the outer vertices?
                    let ne_vertex = !faces[0] || !faces[1];
                    let e_vertex  = !faces[1] || !faces[2];
                    let se_vertex = !faces[2] || !faces[3];
                    let sw_vertex = !faces[3] || !faces[4];
                    let w_vertex  = !faces[4] || !faces[5];
                    let nw_vertex = !faces[5] || !faces[0];

                    // Segment 2, middle left
                    {
                        if faces[0] {
                            if nw_vertex && ne_vertex {
                                draw(BlockRear.get(1), North, color, BLACK);
                            } else if nw_vertex {
                                draw(BlockRear.get(9), XWallBack, color, BLACK);
                            } else {
                                draw(BlockRear.get(5), YWallBack, color, BLACK);
                            }
                        }
                        if faces[3] {
                            if sw_vertex && se_vertex {
                                draw(brush.get(1), South, color, back);
                            } else if sw_vertex {
                                draw(brush.get(5), YWall, color, back);
                            } else {
                                draw(brush.get(9), XWall, color, back);
                            }
                        }
                    }

                    // Segment 3, middle right
                    {
                        if faces[0] {
                            if ne_vertex && nw_vertex {
                                draw(BlockRear.get(2), North, color, BLACK);
                            } else if ne_vertex {
                                draw(BlockRear.get(6), YWallBack, color, BLACK);
                            } else {
                                draw(BlockRear.get(10), XWallBack, color, BLACK);
                            }
                        }
                        if faces[3] {
                            if se_vertex && sw_vertex {
                                draw(brush.get(2), South, color, back);
                            } else if se_vertex {
                                draw(brush.get(10), XWall, color, back);
                            } else {
                                draw(brush.get(6), YWall, color, back);
                            }
                        }
                    }

                    // The side segments need to come after the middle
                    // segments so that the vertical edges can overwrite the
                    // middle segment pixels.

                    // Segment 1, left edge
                    {
                        if w_vertex {
                            if faces[5] {
                                if nw_vertex {
                                    draw(BlockRear.get(0), Northwest, color, BLACK);
                                } else {
                                    draw(BlockRear.get(4), YWallBack, color, BLACK);
                                }
                            }

                            if faces[4] {
                                if sw_vertex {
                                    draw(brush.get(0), Southwest, color, back);
                                } else {
                                    draw(brush.get(8), XWall, color, back);
                                }
                            }
                        } else {
                            // Draw the left vertical line.
                            draw(BlockVertical.get(2), West, color, BLACK);
                            if !faces[0] {
                                draw(BlockVertical.get(0), West, color, BLACK);
                            }
                            if !faces[3] {
                                draw(BlockVertical.get(4), West, color, BLACK);
                            }
                        }
                    }

                    // Segment 4, right edge
                    {
                        if e_vertex {
                            if faces[1] {
                                if ne_vertex {
                                    draw(BlockRear.get(3), Northeast, color, BLACK);
                                } else {
                                    draw(BlockRear.get(11), XWallBack, color, BLACK);
                                }
                            }

                            if faces[2] {
                                if se_vertex {
                                    draw(brush.get(3), Southeast, color, back);
                                } else {
                                    draw(brush.get(7), YWall, color, back);
                                }
                            }
                        } else {
                            // Draw the right vertical line.
                            draw(BlockVertical.get(3), East, color, BLACK);
                            if !faces[0] {
                                draw(BlockVertical.get(1), East, color, BLACK);
                            }
                            if !faces[3] {
                                draw(BlockVertical.get(5), East, color, BLACK);
                            }
                        }
                    }
                }
            }
        }

    for i in match k.center {
        TerrainType::Void => vec![T::Floor(BlankFloor, MAGENTA)],
        TerrainType::Floor => vec![T::Floor(Floor, SLATEGRAY)],
        TerrainType::Water => vec![T::Floor(Water, ROYALBLUE)],
        TerrainType::Shallows => vec![T::Floor(Shallows, CORNFLOWERBLUE)],
        TerrainType::Magma => vec![T::Floor2(Water, YELLOW, DARKRED)],
        TerrainType::Downstairs => vec![T::Floor(StairsDown, SLATEGRAY)],
        TerrainType::Wall => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickWall, LIGHTSLATEGRAY),
        ],
        TerrainType::RockWall => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(RockWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Rock => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Block(BlockRock, DARKGOLDENROD),
        ],
        TerrainType::Tree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
            T::Prop(TreeFoliage, GREEN),
        ],
        TerrainType::Grass => vec![T::Floor(Floor, DARKGREEN)],
        TerrainType::Grass2 => vec![ T::Floor(Grass, DARKGREEN)],
        TerrainType::Stalagmite => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stalagmite, DARKGOLDENROD),
        ],
        TerrainType::Door => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
            T::Wall(DoorWall, SADDLEBROWN),
        ],
        TerrainType::OpenDoor => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Window => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickWindowWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Table => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Fence => vec![
            // The floor type beneath the fence tile is visible, make it grass
            // if there's grass behind the fence. Otherwise make it regular
            // floor.
            if k.n == TerrainType::Grass || k.ne == TerrainType::Grass || k.nw == TerrainType::Grass {
                T::Floor(Grass, GREEN)
            } else {
                T::Floor(Floor, SLATEGRAY)
            },
            T::Wall(FenceWall, DARKGOLDENROD),
        ],
        TerrainType::Bars => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BarsWall, GAINSBORO),
        ],
        TerrainType::Fountain => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Altar => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Fountain, GAINSBORO),
        ],
        TerrainType::Barrel => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Barrell, DARKGOLDENROD),
        ],
        TerrainType::Grave => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Grave, SLATEGRAY),
        ],
        TerrainType::Stone => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stone, SLATEGRAY),
        ],
        TerrainType::Menhir => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Menhir, SLATEGRAY),
        ],
        TerrainType::DeadTree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
        ],
    }.into_iter() {
        process(k, &mut draw, i);
    }
}
<commit_msg>Fix FOV visual for stairs down tile<commit_after>use calx::color::*;
use calx::{Rgba, Kernel, KernelTerrain};
use calx::backend::{Image};
use content::{TerrainType, Brush};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

impl Angle {
    /// Return the angle of the vertical surface, if any, in degrees.
    pub fn degree(&self) -> Option<f32> {
        match *self {
            Angle::Up => None,
            Angle::North => Some(0.0),
            Angle::XWallBack => Some(30.0),
            Angle::Northeast => Some(60.0),
            Angle::East => Some(90.0),
            Angle::Southeast => Some(120.0),
            Angle::YWall => Some(150.0),
            Angle::South => Some(180.0),
            Angle::XWall => Some(210.0),
            Angle::Southwest => Some(240.0),
            Angle::West => Some(270.0),
            Angle::Northwest => Some(300.0),
            Angle::YWallBack => Some(330.0),
        }
    }
}

/// Generate draw instructions for a terrain cell.
///
/// Params to the draw function: Draw layer, brush, brush frame, main
/// color, border color.
pub fn render<F>(k: &Kernel<TerrainType>, mut draw: F)
    where F: FnMut(Image, Angle, Rgba, Rgba)
{
    use content::Brush::*;
    use self::Angle::*;

    enum T {
        Floor(Brush, Rgba),
        Floor2(Brush, Rgba, Rgba),
        Prop(Brush, Rgba),
        Prop2(Brush, Rgba, Rgba),
        Wall(Brush, Rgba),
        Wall2(Brush, Rgba, Rgba),
        Block(Brush, Rgba),
        Block2(Brush, Rgba, Rgba),
    }

    fn process<C: KernelTerrain, F>(
        k: &Kernel<C>, draw: &mut F, kind: T)
        where F: FnMut(Image, Angle, Rgba, Rgba)
        {
            // NB: Black-#000 foreground color prohibits recoloring of the
            // tile in FOV view, only use for special blocks.
            match kind {
                T::Floor(brush, color) => process(k, draw, T::Floor2(brush, color, BLACK)),
                T::Prop(brush, color) => process(k, draw, T::Prop2(brush, color, BLACK)),
                T::Wall(brush, color) => process(k, draw, T::Wall2(brush, color, BLACK)),
                T::Block(brush, color) => process(k, draw, T::Block2(brush, color, BLACK)),

                T::Floor2(brush, color, back) => {
                    draw(brush.get(0), Up, color, back);
                }
                T::Prop2(brush, color, back) => {
                    draw(brush.get(0), South, color, back);
                }
                T::Wall2(brush, color, back) => {
                    let extends = k.wall_extends();
                    if extends[0] {
                        draw(brush.get(2), XWall, color, back);
                    } else {
                        draw(brush.get(0), XWall, color, back);
                    }
                    if extends[1] {
                        draw(brush.get(3), YWall, color, back);
                    } else {
                        draw(brush.get(1), YWall, color, back);
                    }
                }
                T::Block2(brush, color, back) => {
                    // This part gets a little tricky. Basic idea is that
                    // there's an inner pointy-top hex core and the block hull
                    // will snap to that instead of the outer flat-top hex
                    // edge if neither adjacent face to the outer hex vertex
                    // is connected to another block.
                    //
                    // Based on how the sprites split up, the processing is
                    // done in four vertical segments.

                    let faces = k.block_faces();

                    // Do we snap to the outer vertices?
                    let ne_vertex = !faces[0] || !faces[1];
                    let e_vertex  = !faces[1] || !faces[2];
                    let se_vertex = !faces[2] || !faces[3];
                    let sw_vertex = !faces[3] || !faces[4];
                    let w_vertex  = !faces[4] || !faces[5];
                    let nw_vertex = !faces[5] || !faces[0];

                    // Segment 2, middle left
                    {
                        if faces[0] {
                            if nw_vertex && ne_vertex {
                                draw(BlockRear.get(1), North, color, BLACK);
                            } else if nw_vertex {
                                draw(BlockRear.get(9), XWallBack, color, BLACK);
                            } else {
                                draw(BlockRear.get(5), YWallBack, color, BLACK);
                            }
                        }
                        if faces[3] {
                            if sw_vertex && se_vertex {
                                draw(brush.get(1), South, color, back);
                            } else if sw_vertex {
                                draw(brush.get(5), YWall, color, back);
                            } else {
                                draw(brush.get(9), XWall, color, back);
                            }
                        }
                    }

                    // Segment 3, middle right
                    {
                        if faces[0] {
                            if ne_vertex && nw_vertex {
                                draw(BlockRear.get(2), North, color, BLACK);
                            } else if ne_vertex {
                                draw(BlockRear.get(6), YWallBack, color, BLACK);
                            } else {
                                draw(BlockRear.get(10), XWallBack, color, BLACK);
                            }
                        }
                        if faces[3] {
                            if se_vertex && sw_vertex {
                                draw(brush.get(2), South, color, back);
                            } else if se_vertex {
                                draw(brush.get(10), XWall, color, back);
                            } else {
                                draw(brush.get(6), YWall, color, back);
                            }
                        }
                    }

                    // The side segments need to come after the middle
                    // segments so that the vertical edges can overwrite the
                    // middle segment pixels.

                    // Segment 1, left edge
                    {
                        if w_vertex {
                            if faces[5] {
                                if nw_vertex {
                                    draw(BlockRear.get(0), Northwest, color, BLACK);
                                } else {
                                    draw(BlockRear.get(4), YWallBack, color, BLACK);
                                }
                            }

                            if faces[4] {
                                if sw_vertex {
                                    draw(brush.get(0), Southwest, color, back);
                                } else {
                                    draw(brush.get(8), XWall, color, back);
                                }
                            }
                        } else {
                            // Draw the left vertical line.
                            draw(BlockVertical.get(2), West, color, BLACK);
                            if !faces[0] {
                                draw(BlockVertical.get(0), West, color, BLACK);
                            }
                            if !faces[3] {
                                draw(BlockVertical.get(4), West, color, BLACK);
                            }
                        }
                    }

                    // Segment 4, right edge
                    {
                        if e_vertex {
                            if faces[1] {
                                if ne_vertex {
                                    draw(BlockRear.get(3), Northeast, color, BLACK);
                                } else {
                                    draw(BlockRear.get(11), XWallBack, color, BLACK);
                                }
                            }

                            if faces[2] {
                                if se_vertex {
                                    draw(brush.get(3), Southeast, color, back);
                                } else {
                                    draw(brush.get(7), YWall, color, back);
                                }
                            }
                        } else {
                            // Draw the right vertical line.
                            draw(BlockVertical.get(3), East, color, BLACK);
                            if !faces[0] {
                                draw(BlockVertical.get(1), East, color, BLACK);
                            }
                            if !faces[3] {
                                draw(BlockVertical.get(5), East, color, BLACK);
                            }
                        }
                    }
                }
            }
        }

    for i in match k.center {
        TerrainType::Void => vec![T::Floor(BlankFloor, MAGENTA)],
        TerrainType::Floor => vec![T::Floor(Floor, SLATEGRAY)],
        TerrainType::Water => vec![T::Floor(Water, ROYALBLUE)],
        TerrainType::Shallows => vec![T::Floor(Shallows, CORNFLOWERBLUE)],
        TerrainType::Magma => vec![T::Floor2(Water, YELLOW, DARKRED)],
        TerrainType::Downstairs => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(StairsDown, SLATEGRAY)],
        TerrainType::Wall => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickWall, LIGHTSLATEGRAY),
        ],
        TerrainType::RockWall => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(RockWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Rock => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Block(BlockRock, DARKGOLDENROD),
        ],
        TerrainType::Tree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
            T::Prop(TreeFoliage, GREEN),
        ],
        TerrainType::Grass => vec![T::Floor(Floor, DARKGREEN)],
        TerrainType::Grass2 => vec![ T::Floor(Grass, DARKGREEN)],
        TerrainType::Stalagmite => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stalagmite, DARKGOLDENROD),
        ],
        TerrainType::Door => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
            T::Wall(DoorWall, SADDLEBROWN),
        ],
        TerrainType::OpenDoor => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Window => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickWindowWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Table => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Fence => vec![
            // The floor type beneath the fence tile is visible, make it grass
            // if there's grass behind the fence. Otherwise make it regular
            // floor.
            if k.n == TerrainType::Grass || k.ne == TerrainType::Grass || k.nw == TerrainType::Grass {
                T::Floor(Grass, GREEN)
            } else {
                T::Floor(Floor, SLATEGRAY)
            },
            T::Wall(FenceWall, DARKGOLDENROD),
        ],
        TerrainType::Bars => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BarsWall, GAINSBORO),
        ],
        TerrainType::Fountain => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Altar => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Fountain, GAINSBORO),
        ],
        TerrainType::Barrel => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Barrell, DARKGOLDENROD),
        ],
        TerrainType::Grave => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Grave, SLATEGRAY),
        ],
        TerrainType::Stone => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stone, SLATEGRAY),
        ],
        TerrainType::Menhir => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Menhir, SLATEGRAY),
        ],
        TerrainType::DeadTree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
        ],
    }.into_iter() {
        process(k, &mut draw, i);
    }
}
<|endoftext|>


<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

use std::fmt;
use std::result;
use std::str::FromStr;

use str_error;
use Result;

// like Into trait but works from a ref avoiding consumption or expensive clone
pub trait IntoSexp {
    fn into_sexp(&self) -> Sexp;
}

#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

/*

Following the KiCad file formats specification chapter 4.4 - Identifiers and Strings:
A string may not contain an actual newline or carriage return, but it may use an escape sequence to encode a
newline, such as \n.
If a string has any of the following conditions, then it must be quoted with a leading and trailing double quote
character, otherwise it is acceptable to not quote the string:
1. has one or more of the following 4 separator bytes: ASCII space, tab, '(', or ')'.
2. has one or more of the following bytes: '%', '{', or '}'.
3. has a length of zero bytes, and you need a place holder for the field, then use "".
4. includes a byte of '-', and this byte is not in the first position of the string.
*/

pub fn encode_string(s:&str) -> String {
    fn rule_4(s:&str) -> bool {
        s.contains('-') && s.len() > 1 && s.as_bytes()[0] != 45
    }
    if s.contains('(') || s.contains(' ') || s.contains(')') || s.contains('\t') || s.contains('{') || s.contains('}') || s.contains('}') || s.contains('%') || s.is_empty() || rule_4(s) {
        format!("\"{}\"", s)
    } else {
        String::from(s)
    }
}

impl Sexp {

    pub fn new_empty() -> Sexp {
        Sexp::Empty
    }

    pub fn new_string<T>(s:T) -> Sexp where T:fmt::Display {
        Sexp::String(format!("{}", s))
    }

    pub fn new_list(v:Vec<Sexp>) -> Sexp {
        Sexp::List(v)
    }

    pub fn new_named<T>(name:&str, value:T) -> Sexp where T:fmt::Display {
        let mut v = vec![];
        v.push(Sexp::new_string(name));
        v.push(Sexp::new_string(value));
        Sexp::List(v)
    }

    pub fn new_named_sexp<T>(name:&str, value:T) -> Sexp where T:IntoSexp {
        let mut v = vec![];
        v.push(Sexp::new_string(name));
        v.push(value.into_sexp());
        Sexp::List(v)
    }

    pub fn from<T:IntoSexp>(t:&T) -> Sexp {
        t.into_sexp()
    }

    pub fn list(&self) -> Result<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => str_error(format!("not a list: {}", self))
        }
    }

    pub fn string(&self) -> Result<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            _ => str_error(format!("not a string: {}", self))
        }
    }

    pub fn f(&self) -> Result<f64> {
        let s = try!(self.string());
        match f64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing float".to_string())
        }
    }

    pub fn i(&self) -> Result<i64> {
        let s = try!(self.string());
        match i64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing int".to_string())
        }
    }

    pub fn list_name(&self) -> Result<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list {} doesn't start with {}, but with {}", self, s, st))
        };
        Ok(&v[1..])
    }

    pub fn named_value(&self, s:&str) -> Result<&Sexp> {
        let v = try!(self.list());
        if v.len() != 2 {
            return str_error(format!("list {} is not a named_value", s))
        }
        let l = try!(self.slice_atom(s));
        Ok(&l[0])
    }

    pub fn named_value_i(&self, s:&str) -> Result<i64> {
        try!(self.named_value(s)).i()
    }

    pub fn named_value_f(&self, s:&str) -> Result<f64> {
        try!(self.named_value(s)).f()
    }

    pub fn named_value_string(&self, s:&str) -> Result<&String> {
        try!(self.named_value(s)).string()
    }

    pub fn slice_atom_num(&self, s:&str, num:usize) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list doesn't start with {}, but with {}", s, st))
        };
        if v.len() != (num+1) {
            return str_error(format!("list ({}) doesn't have {} elements but {}", s, num, v.len()-1))
        }
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                write!(f, "{}", encode_string(s))
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                let l = v.len();
                for (i,x) in v.iter().enumerate() {
                    if i < l-1 {
                        try!(write!(f, "{} ", x));
                    } else {
                        try!(write!(f, "{}", x));
                    }
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}
<commit_msg>borrow only<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

use std::fmt;
use std::result;
use std::str::FromStr;

use str_error;
use Result;

// like Into trait but works from a ref avoiding consumption or expensive clone
pub trait IntoSexp {
    fn into_sexp(&self) -> Sexp;
}

#[derive(Debug, Clone)]
pub enum Sexp {
    String(String),
    List(Vec<Sexp>),
    Empty,
}

/*

Following the KiCad file formats specification chapter 4.4 - Identifiers and Strings:
A string may not contain an actual newline or carriage return, but it may use an escape sequence to encode a
newline, such as \n.
If a string has any of the following conditions, then it must be quoted with a leading and trailing double quote
character, otherwise it is acceptable to not quote the string:
1. has one or more of the following 4 separator bytes: ASCII space, tab, '(', or ')'.
2. has one or more of the following bytes: '%', '{', or '}'.
3. has a length of zero bytes, and you need a place holder for the field, then use "".
4. includes a byte of '-', and this byte is not in the first position of the string.
*/

pub fn encode_string(s:&str) -> String {
    fn rule_4(s:&str) -> bool {
        s.contains('-') && s.len() > 1 && s.as_bytes()[0] != 45
    }
    if s.contains('(') || s.contains(' ') || s.contains(')') || s.contains('\t') || s.contains('{') || s.contains('}') || s.contains('}') || s.contains('%') || s.is_empty() || rule_4(s) {
        format!("\"{}\"", s)
    } else {
        String::from(s)
    }
}

impl Sexp {

    pub fn new_empty() -> Sexp {
        Sexp::Empty
    }

    pub fn new_string<T>(s:T) -> Sexp where T:fmt::Display {
        Sexp::String(format!("{}", s))
    }

    pub fn new_list(v:Vec<Sexp>) -> Sexp {
        Sexp::List(v)
    }

    pub fn new_named<T>(name:&str, value:T) -> Sexp where T:fmt::Display {
        let mut v = vec![];
        v.push(Sexp::new_string(name));
        v.push(Sexp::new_string(value));
        Sexp::List(v)
    }

    pub fn new_named_sexp<T>(name:&str, value:&T) -> Sexp where T:IntoSexp {
        let mut v = vec![];
        v.push(Sexp::new_string(name));
        v.push(value.into_sexp());
        Sexp::List(v)
    }

    pub fn from<T:IntoSexp>(t:&T) -> Sexp {
        t.into_sexp()
    }

    pub fn list(&self) -> Result<&Vec<Sexp> > {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => str_error(format!("not a list: {}", self))
        }
    }

    pub fn string(&self) -> Result<&String> {
        match *self {
            Sexp::String(ref s) => Ok(s),
            _ => str_error(format!("not a string: {}", self))
        }
    }

    pub fn f(&self) -> Result<f64> {
        let s = try!(self.string());
        match f64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing float".to_string())
        }
    }

    pub fn i(&self) -> Result<i64> {
        let s = try!(self.string());
        match i64::from_str(s) {
            Ok(f) => Ok(f),
            _ => str_error("Error parsing int".to_string())
        }
    }

    pub fn list_name(&self) -> Result<&String> {
        let l = try!(self.list());
        let l = &l[..];
        let a = try!(l[0].string());
        Ok(a)
    }

    pub fn slice_atom(&self, s:&str) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list {} doesn't start with {}, but with {}", self, s, st))
        };
        Ok(&v[1..])
    }

    pub fn named_value(&self, s:&str) -> Result<&Sexp> {
        let v = try!(self.list());
        if v.len() != 2 {
            return str_error(format!("list {} is not a named_value", s))
        }
        let l = try!(self.slice_atom(s));
        Ok(&l[0])
    }

    pub fn named_value_i(&self, s:&str) -> Result<i64> {
        try!(self.named_value(s)).i()
    }

    pub fn named_value_f(&self, s:&str) -> Result<f64> {
        try!(self.named_value(s)).f()
    }

    pub fn named_value_string(&self, s:&str) -> Result<&String> {
        try!(self.named_value(s)).string()
    }

    pub fn slice_atom_num(&self, s:&str, num:usize) -> Result<&[Sexp]> {
        let v = try!(self.list());
        let v2 =&v[..];
        let st = try!(v2[0].string());
        if st != s {
            return str_error(format!("list doesn't start with {}, but with {}", s, st))
        };
        if v.len() != (num+1) {
            return str_error(format!("list ({}) doesn't have {} elements but {}", s, num, v.len()-1))
        }
        Ok(&v[1..])
    }
}

impl fmt::Display for Sexp {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        match *self {
            Sexp::String(ref s) => {
                write!(f, "{}", encode_string(s))
            },
            Sexp::List(ref v) => {
                try!(write!(f, "("));
                let l = v.len();
                for (i,x) in v.iter().enumerate() {
                    if i < l-1 {
                        try!(write!(f, "{} ", x));
                    } else {
                        try!(write!(f, "{}", x));
                    }
                }
                write!(f, ")")
            },
            Sexp::Empty => Ok(())
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BinaryHeap;
use std::cmp::{PartialOrd, Ord, PartialEq, Eq, Ordering};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::{Duration, Instant};

use thunk::Thunk;

enum JobType {
    Once(Thunk<'static>),
    FixedRate {
        f: Box<FnMut() + Send + 'static>,
        rate: Duration,
    },
}

struct Job {
    type_: JobType,
    time: Instant,
}

impl PartialOrd for Job {
    fn partial_cmp(&self, other: &Job) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Job {
    fn cmp(&self, other: &Job) -> Ordering {
        // reverse because BinaryHeap's a max heap
        self.time.cmp(&other.time).reverse()
    }
}

impl PartialEq for Job {
    fn eq(&self, other: &Job) -> bool {
        self.time == other.time
    }
}

impl Eq for Job {}

struct InnerPool {
    queue: BinaryHeap<Job>,
    shutdown: bool,
}

struct SharedPool {
    inner: Mutex<InnerPool>,
    cvar: Condvar,
}

impl SharedPool {
    fn run(&self, job: Job) {
        let mut inner = self.inner.lock().unwrap();

        // Calls from the pool itself will never hit this, but calls from workers might
        if inner.shutdown {
            return;
        }

        match inner.queue.peek() {
            None => self.cvar.notify_all(),
            Some(e) if e.time > job.time => self.cvar.notify_all(),
            _ => {}
        };
        inner.queue.push(job);
    }
}

pub struct ScheduledThreadPool {
    shared: Arc<SharedPool>,
}

impl Drop for ScheduledThreadPool {
    fn drop(&mut self) {
        self.shared.inner.lock().unwrap().shutdown = true;
        self.shared.cvar.notify_all();
    }
}

impl ScheduledThreadPool {
    pub fn new(size: usize) -> ScheduledThreadPool {
        assert!(size > 0, "size must be positive");

        let inner = InnerPool {
            queue: BinaryHeap::new(),
            shutdown: false,
        };

        let shared = SharedPool {
            inner: Mutex::new(inner),
            cvar: Condvar::new(),
        };

        let pool = ScheduledThreadPool { shared: Arc::new(shared) };

        for i in 0..size {
            Worker::start(i, pool.shared.clone());
        }

        pool
    }

    #[allow(dead_code)]
    pub fn run<F>(&self, job: F)
        where F: FnOnce() + Send + 'static
    {
        self.run_after(Duration::from_secs(0), job)
    }

    pub fn run_after<F>(&self, dur: Duration, job: F)
        where F: FnOnce() + Send + 'static
    {
        let job = Job {
            type_: JobType::Once(Thunk::new(job)),
            time: Instant::now() + dur,
        };
        self.shared.run(job)
    }

    pub fn run_at_fixed_rate<F>(&self, rate: Duration, f: F)
        where F: FnMut() + Send + 'static
    {
        let job = Job {
            type_: JobType::FixedRate {
                f: Box::new(f),
                rate: rate,
            },
            time: Instant::now() + rate,
        };
        self.shared.run(job)
    }
}

struct Worker {
    i: usize,
    shared: Arc<SharedPool>,
}

impl Drop for Worker {
    fn drop(&mut self) {
        // Start up a new worker if this one's going away due to a panic from a job
        if thread::panicking() {
            Worker::start(self.i, self.shared.clone());
        }
    }
}

impl Worker {
    fn start(i: usize, shared: Arc<SharedPool>) {
        let mut worker = Worker {
            i: i,
            shared: shared,
        };
        thread::Builder::new()
            .name(format!("ScheduledThreadPool worker {}", i))
            .spawn(move || worker.run())
            .unwrap();
    }

    fn run(&mut self) {
        loop {
            match self.get_job() {
                Some(job) => self.run_job(job),
                None => break,
            }
        }
    }

    fn get_job(&self) -> Option<Job> {
        enum Need {
            Wait,
            WaitTimeout(Duration),
        }

        let mut inner = self.shared.inner.lock().unwrap();
        loop {
            let now = Instant::now();

            let need = match inner.queue.peek() {
                None if inner.shutdown => return None,
                None => Need::Wait,
                Some(e) if e.time <= now => break,
                Some(e) => Need::WaitTimeout(e.time - now),
            };

            inner = match need {
                Need::Wait => self.shared.cvar.wait(inner).unwrap(),
                Need::WaitTimeout(t) => self.shared.cvar.wait_timeout(inner, t).unwrap().0,
            };
        }

        Some(inner.queue.pop().unwrap())
    }

    fn run_job(&self, job: Job) {
        match job.type_ {
            JobType::Once(f) => f.invoke(()),
            JobType::FixedRate { mut f, rate } => {
                f();
                let new_job = Job {
                    type_: JobType::FixedRate { f: f, rate: rate },
                    time: job.time + rate,
                };
                self.shared.run(new_job)
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::sync::mpsc::channel;
    use std::sync::{Arc, Barrier};
    use std::time::Duration;

    use super::ScheduledThreadPool;

    const TEST_TASKS: usize = 4;

    #[test]
    fn test_works() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);

        let (tx, rx) = channel();
        for _ in 0..TEST_TASKS {
            let tx = tx.clone();
            pool.run(move || {
                tx.send(1usize).unwrap();
            });
        }

        assert_eq!(rx.iter().take(TEST_TASKS).fold(0, |a, b| a + b), TEST_TASKS);
    }

    #[test]
    #[should_panic(expected = "size must be positive")]
    fn test_zero_tasks_panic() {
        ScheduledThreadPool::new(0);
    }

    #[test]
    fn test_recovery_from_subtask_panic() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);

        // Panic all the existing threads.
        let waiter = Arc::new(Barrier::new(TEST_TASKS as usize));
        for _ in 0..TEST_TASKS {
            let waiter = waiter.clone();
            pool.run(move || -> () {
                waiter.wait();
                panic!();
            });
        }

        // Ensure new threads were spawned to compensate.
        let (tx, rx) = channel();
        let waiter = Arc::new(Barrier::new(TEST_TASKS as usize));
        for _ in 0..TEST_TASKS {
            let tx = tx.clone();
            let waiter = waiter.clone();
            pool.run(move || {
                waiter.wait();
                tx.send(1usize).unwrap();
            });
        }

        assert_eq!(rx.iter().take(TEST_TASKS).fold(0, |a, b| a + b), TEST_TASKS);
    }

    #[test]
    fn test_run_after() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);
        let (tx, rx) = channel();

        let tx1 = tx.clone();
        pool.run_after(Duration::from_secs(1), move || tx1.send(1usize).unwrap());
        pool.run_after(Duration::from_millis(500), move || tx.send(2usize).unwrap());

        assert_eq!(2, rx.recv().unwrap());
        assert_eq!(1, rx.recv().unwrap());
    }

    #[test]
    fn test_jobs_complete_after_drop() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);
        let (tx, rx) = channel();

        let tx1 = tx.clone();
        pool.run_after(Duration::from_secs(1), move || tx1.send(1usize).unwrap());
        pool.run_after(Duration::from_millis(500), move || tx.send(2usize).unwrap());

        drop(pool);

        assert_eq!(2, rx.recv().unwrap());
        assert_eq!(1, rx.recv().unwrap());
    }

    #[test]
    fn test_fixed_delay_jobs_stop_after_drop() {
        let pool = Arc::new(ScheduledThreadPool::new(TEST_TASKS));
        let (tx, rx) = channel();
        let (tx2, rx2) = channel();

        let mut pool2 = Some(pool.clone());
        let mut i = 0i32;
        pool.run_at_fixed_rate(Duration::from_millis(500), move || {
            i += 1;
            tx.send(i).unwrap();
            rx2.recv().unwrap();
            if i == 2 {
                drop(pool2.take().unwrap());
            }
        });
        drop(pool);

        assert_eq!(Ok(1), rx.recv());
        tx2.send(()).unwrap();
        assert_eq!(Ok(2), rx.recv());
        tx2.send(()).unwrap();
        assert!(rx.recv().is_err());
    }
}
<commit_msg>Make worker thread names better<commit_after>use std::collections::BinaryHeap;
use std::cmp::{PartialOrd, Ord, PartialEq, Eq, Ordering};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::{Duration, Instant};

use thunk::Thunk;

enum JobType {
    Once(Thunk<'static>),
    FixedRate {
        f: Box<FnMut() + Send + 'static>,
        rate: Duration,
    },
}

struct Job {
    type_: JobType,
    time: Instant,
}

impl PartialOrd for Job {
    fn partial_cmp(&self, other: &Job) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Job {
    fn cmp(&self, other: &Job) -> Ordering {
        // reverse because BinaryHeap's a max heap
        self.time.cmp(&other.time).reverse()
    }
}

impl PartialEq for Job {
    fn eq(&self, other: &Job) -> bool {
        self.time == other.time
    }
}

impl Eq for Job {}

struct InnerPool {
    queue: BinaryHeap<Job>,
    shutdown: bool,
}

struct SharedPool {
    inner: Mutex<InnerPool>,
    cvar: Condvar,
}

impl SharedPool {
    fn run(&self, job: Job) {
        let mut inner = self.inner.lock().unwrap();

        // Calls from the pool itself will never hit this, but calls from workers might
        if inner.shutdown {
            return;
        }

        match inner.queue.peek() {
            None => self.cvar.notify_all(),
            Some(e) if e.time > job.time => self.cvar.notify_all(),
            _ => {}
        };
        inner.queue.push(job);
    }
}

pub struct ScheduledThreadPool {
    shared: Arc<SharedPool>,
}

impl Drop for ScheduledThreadPool {
    fn drop(&mut self) {
        self.shared.inner.lock().unwrap().shutdown = true;
        self.shared.cvar.notify_all();
    }
}

impl ScheduledThreadPool {
    pub fn new(size: usize) -> ScheduledThreadPool {
        assert!(size > 0, "size must be positive");

        let inner = InnerPool {
            queue: BinaryHeap::new(),
            shutdown: false,
        };

        let shared = SharedPool {
            inner: Mutex::new(inner),
            cvar: Condvar::new(),
        };

        let pool = ScheduledThreadPool { shared: Arc::new(shared) };

        for i in 0..size {
            Worker::start(i, pool.shared.clone());
        }

        pool
    }

    #[allow(dead_code)]
    pub fn run<F>(&self, job: F)
        where F: FnOnce() + Send + 'static
    {
        self.run_after(Duration::from_secs(0), job)
    }

    pub fn run_after<F>(&self, dur: Duration, job: F)
        where F: FnOnce() + Send + 'static
    {
        let job = Job {
            type_: JobType::Once(Thunk::new(job)),
            time: Instant::now() + dur,
        };
        self.shared.run(job)
    }

    pub fn run_at_fixed_rate<F>(&self, rate: Duration, f: F)
        where F: FnMut() + Send + 'static
    {
        let job = Job {
            type_: JobType::FixedRate {
                f: Box::new(f),
                rate: rate,
            },
            time: Instant::now() + rate,
        };
        self.shared.run(job)
    }
}

struct Worker {
    i: usize,
    shared: Arc<SharedPool>,
}

impl Drop for Worker {
    fn drop(&mut self) {
        // Start up a new worker if this one's going away due to a panic from a job
        if thread::panicking() {
            Worker::start(self.i, self.shared.clone());
        }
    }
}

impl Worker {
    fn start(i: usize, shared: Arc<SharedPool>) {
        let mut worker = Worker {
            i: i,
            shared: shared,
        };
        thread::Builder::new()
            .name(format!("r2d2-worker-{}", i))
            .spawn(move || worker.run())
            .unwrap();
    }

    fn run(&mut self) {
        loop {
            match self.get_job() {
                Some(job) => self.run_job(job),
                None => break,
            }
        }
    }

    fn get_job(&self) -> Option<Job> {
        enum Need {
            Wait,
            WaitTimeout(Duration),
        }

        let mut inner = self.shared.inner.lock().unwrap();
        loop {
            let now = Instant::now();

            let need = match inner.queue.peek() {
                None if inner.shutdown => return None,
                None => Need::Wait,
                Some(e) if e.time <= now => break,
                Some(e) => Need::WaitTimeout(e.time - now),
            };

            inner = match need {
                Need::Wait => self.shared.cvar.wait(inner).unwrap(),
                Need::WaitTimeout(t) => self.shared.cvar.wait_timeout(inner, t).unwrap().0,
            };
        }

        Some(inner.queue.pop().unwrap())
    }

    fn run_job(&self, job: Job) {
        match job.type_ {
            JobType::Once(f) => f.invoke(()),
            JobType::FixedRate { mut f, rate } => {
                f();
                let new_job = Job {
                    type_: JobType::FixedRate { f: f, rate: rate },
                    time: job.time + rate,
                };
                self.shared.run(new_job)
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::sync::mpsc::channel;
    use std::sync::{Arc, Barrier};
    use std::time::Duration;

    use super::ScheduledThreadPool;

    const TEST_TASKS: usize = 4;

    #[test]
    fn test_works() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);

        let (tx, rx) = channel();
        for _ in 0..TEST_TASKS {
            let tx = tx.clone();
            pool.run(move || {
                tx.send(1usize).unwrap();
            });
        }

        assert_eq!(rx.iter().take(TEST_TASKS).fold(0, |a, b| a + b), TEST_TASKS);
    }

    #[test]
    #[should_panic(expected = "size must be positive")]
    fn test_zero_tasks_panic() {
        ScheduledThreadPool::new(0);
    }

    #[test]
    fn test_recovery_from_subtask_panic() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);

        // Panic all the existing threads.
        let waiter = Arc::new(Barrier::new(TEST_TASKS as usize));
        for _ in 0..TEST_TASKS {
            let waiter = waiter.clone();
            pool.run(move || -> () {
                waiter.wait();
                panic!();
            });
        }

        // Ensure new threads were spawned to compensate.
        let (tx, rx) = channel();
        let waiter = Arc::new(Barrier::new(TEST_TASKS as usize));
        for _ in 0..TEST_TASKS {
            let tx = tx.clone();
            let waiter = waiter.clone();
            pool.run(move || {
                waiter.wait();
                tx.send(1usize).unwrap();
            });
        }

        assert_eq!(rx.iter().take(TEST_TASKS).fold(0, |a, b| a + b), TEST_TASKS);
    }

    #[test]
    fn test_run_after() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);
        let (tx, rx) = channel();

        let tx1 = tx.clone();
        pool.run_after(Duration::from_secs(1), move || tx1.send(1usize).unwrap());
        pool.run_after(Duration::from_millis(500), move || tx.send(2usize).unwrap());

        assert_eq!(2, rx.recv().unwrap());
        assert_eq!(1, rx.recv().unwrap());
    }

    #[test]
    fn test_jobs_complete_after_drop() {
        let pool = ScheduledThreadPool::new(TEST_TASKS);
        let (tx, rx) = channel();

        let tx1 = tx.clone();
        pool.run_after(Duration::from_secs(1), move || tx1.send(1usize).unwrap());
        pool.run_after(Duration::from_millis(500), move || tx.send(2usize).unwrap());

        drop(pool);

        assert_eq!(2, rx.recv().unwrap());
        assert_eq!(1, rx.recv().unwrap());
    }

    #[test]
    fn test_fixed_delay_jobs_stop_after_drop() {
        let pool = Arc::new(ScheduledThreadPool::new(TEST_TASKS));
        let (tx, rx) = channel();
        let (tx2, rx2) = channel();

        let mut pool2 = Some(pool.clone());
        let mut i = 0i32;
        pool.run_at_fixed_rate(Duration::from_millis(500), move || {
            i += 1;
            tx.send(i).unwrap();
            rx2.recv().unwrap();
            if i == 2 {
                drop(pool2.take().unwrap());
            }
        });
        drop(pool);

        assert_eq!(Ok(1), rx.recv());
        tx2.send(()).unwrap();
        assert_eq!(Ok(2), rx.recv());
        tx2.send(()).unwrap();
        assert!(rx.recv().is_err());
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ffi::CString;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

use FsStats;

pub fn duplicate(file: &File) -> Result<File> {
    unsafe {
        let fd = libc::dup(file.as_raw_fd());

        if fd < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(File::from_raw_fd(fd))
        }
    }
}

pub fn lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH)
}

pub fn lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX)
}

pub fn try_lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH | libc::LOCK_NB)
}

pub fn try_lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX | libc::LOCK_NB)
}

pub fn unlock(file: &File) -> Result<()> {
    flock(file, libc::LOCK_UN)
}

pub fn lock_error() -> Error {
    Error::from_raw_os_error(libc::EWOULDBLOCK)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
    let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
    if ret < 0 { Err(Error::last_os_error()) } else { Ok(()) }
}

pub fn allocated_size(file: &File) -> Result<u64> {
    file.metadata().map(|m| m.blocks() as u64 * 512)
}

#[cfg(any(target_os = "linux",
          target_os = "freebsd",
          target_os = "android",
          target_os = "nacl"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let ret = unsafe { libc::posix_fallocate(file.as_raw_fd(), 0, len as libc::off_t) };
    if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let stat = try!(file.metadata());

    if len > stat.blocks() as u64 * 512 {
        let mut fstore = libc::fstore_t {
            fst_flags: libc::F_ALLOCATECONTIG,
            fst_posmode: libc::F_PEOFPOSMODE,
            fst_offset: 0,
            fst_length: len as libc::off_t,
            fst_bytesalloc: 0,
        };

        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
        if ret == -1 {
            // Unable to allocate contiguous disk space; attempt to allocate non-contiguously.
            fstore.fst_flags = libc::F_ALLOCATEALL;
            let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
            if ret == -1 {
                return Err(Error::last_os_error());
            }
        }
    }

    if len > stat.size() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

#[cfg(any(target_os = "openbsd",
          target_os = "netbsd",
          target_os = "dragonfly",
          target_os = "solaris"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    // No file allocation API available, just set the length if necessary.
    if len > try!(file.metadata()).len() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

pub fn statvfs(path: &Path) -> Result<FsStats> {
    let cstr = match CString::new(path.as_os_str().as_bytes()) {
        Ok(cstr) => cstr,
        Err(..) => return Err(Error::new(ErrorKind::InvalidInput, "path contained a null")),
    };

    unsafe {
        let mut stat: libc::statvfs = mem::zeroed();
        // danburkert/fs2-rs#1: cast is necessary for platforms where c_char != u8.
        if libc::statvfs(cstr.as_ptr() as *const _, &mut stat) != 0 {
            Err(Error::last_os_error())
        } else {
            Ok(FsStats {
                free_space: stat.f_frsize as u64 * stat.f_bfree as u64,
                available_space: stat.f_frsize as u64 * stat.f_bavail as u64,
                total_space: stat.f_frsize as u64 * stat.f_blocks as u64,
                allocation_granularity: stat.f_frsize as u64,
            })
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    extern crate libc;

    use std::fs::{self, File};
    use std::os::unix::io::AsRawFd;

    use {FileExt, lock_contended_error};

    /// The duplicate method returns a file with a new file descriptor.
    #[test]
    fn duplicate_new_fd() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        assert!(file1.as_raw_fd() != file2.as_raw_fd());
    }

    /// The duplicate method should preservesthe close on exec flag.
    #[test]
    fn duplicate_cloexec() {

        fn flags(file: &File) -> libc::c_int {
            unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL, 0) }
        }

        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();

        assert_eq!(flags(&file1), flags(&file2));
    }

    /// Tests that locking a file descriptor will replace any existing locks
    /// held on the file descriptor.
    #[test]
    fn lock_replace() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Creating a shared lock will drop an exclusive lock.
        file1.lock_exclusive().unwrap();
        file1.lock_shared().unwrap();
        file2.lock_shared().unwrap();

        // Attempting to replace a shared lock with an exclusive lock will fail
        // with multiple lock holders, and remove the original shared lock.
        assert_eq!(file2.try_lock_exclusive().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());
        file1.lock_shared().unwrap();
    }

    /// Tests that locks are shared among duplicated file descriptors.
    #[test]
    fn lock_duplicate() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        let file3 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Create a lock through fd1, then replace it through fd2.
        file1.lock_shared().unwrap();
        file2.lock_exclusive().unwrap();
        assert_eq!(file3.try_lock_shared().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());

        // Either of the file descriptors should be able to unlock.
        file1.unlock().unwrap();
        file3.lock_shared().unwrap();
    }
}
<commit_msg>Add Haiku target. (#17)<commit_after>extern crate libc;

use std::ffi::CString;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

use FsStats;

pub fn duplicate(file: &File) -> Result<File> {
    unsafe {
        let fd = libc::dup(file.as_raw_fd());

        if fd < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(File::from_raw_fd(fd))
        }
    }
}

pub fn lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH)
}

pub fn lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX)
}

pub fn try_lock_shared(file: &File) -> Result<()> {
    flock(file, libc::LOCK_SH | libc::LOCK_NB)
}

pub fn try_lock_exclusive(file: &File) -> Result<()> {
    flock(file, libc::LOCK_EX | libc::LOCK_NB)
}

pub fn unlock(file: &File) -> Result<()> {
    flock(file, libc::LOCK_UN)
}

pub fn lock_error() -> Error {
    Error::from_raw_os_error(libc::EWOULDBLOCK)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
    let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
    if ret < 0 { Err(Error::last_os_error()) } else { Ok(()) }
}

pub fn allocated_size(file: &File) -> Result<u64> {
    file.metadata().map(|m| m.blocks() as u64 * 512)
}

#[cfg(any(target_os = "linux",
          target_os = "freebsd",
          target_os = "android",
          target_os = "nacl"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let ret = unsafe { libc::posix_fallocate(file.as_raw_fd(), 0, len as libc::off_t) };
    if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    let stat = try!(file.metadata());

    if len > stat.blocks() as u64 * 512 {
        let mut fstore = libc::fstore_t {
            fst_flags: libc::F_ALLOCATECONTIG,
            fst_posmode: libc::F_PEOFPOSMODE,
            fst_offset: 0,
            fst_length: len as libc::off_t,
            fst_bytesalloc: 0,
        };

        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
        if ret == -1 {
            // Unable to allocate contiguous disk space; attempt to allocate non-contiguously.
            fstore.fst_flags = libc::F_ALLOCATEALL;
            let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_PREALLOCATE, &fstore) };
            if ret == -1 {
                return Err(Error::last_os_error());
            }
        }
    }

    if len > stat.size() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

#[cfg(any(target_os = "openbsd",
          target_os = "netbsd",
          target_os = "dragonfly",
          target_os = "solaris",
          target_os = "haiku"))]
pub fn allocate(file: &File, len: u64) -> Result<()> {
    // No file allocation API available, just set the length if necessary.
    if len > try!(file.metadata()).len() as u64 {
        file.set_len(len)
    } else {
        Ok(())
    }
}

pub fn statvfs(path: &Path) -> Result<FsStats> {
    let cstr = match CString::new(path.as_os_str().as_bytes()) {
        Ok(cstr) => cstr,
        Err(..) => return Err(Error::new(ErrorKind::InvalidInput, "path contained a null")),
    };

    unsafe {
        let mut stat: libc::statvfs = mem::zeroed();
        // danburkert/fs2-rs#1: cast is necessary for platforms where c_char != u8.
        if libc::statvfs(cstr.as_ptr() as *const _, &mut stat) != 0 {
            Err(Error::last_os_error())
        } else {
            Ok(FsStats {
                free_space: stat.f_frsize as u64 * stat.f_bfree as u64,
                available_space: stat.f_frsize as u64 * stat.f_bavail as u64,
                total_space: stat.f_frsize as u64 * stat.f_blocks as u64,
                allocation_granularity: stat.f_frsize as u64,
            })
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;
    extern crate libc;

    use std::fs::{self, File};
    use std::os::unix::io::AsRawFd;

    use {FileExt, lock_contended_error};

    /// The duplicate method returns a file with a new file descriptor.
    #[test]
    fn duplicate_new_fd() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        assert!(file1.as_raw_fd() != file2.as_raw_fd());
    }

    /// The duplicate method should preservesthe close on exec flag.
    #[test]
    fn duplicate_cloexec() {

        fn flags(file: &File) -> libc::c_int {
            unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL, 0) }
        }

        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();

        assert_eq!(flags(&file1), flags(&file2));
    }

    /// Tests that locking a file descriptor will replace any existing locks
    /// held on the file descriptor.
    #[test]
    fn lock_replace() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Creating a shared lock will drop an exclusive lock.
        file1.lock_exclusive().unwrap();
        file1.lock_shared().unwrap();
        file2.lock_shared().unwrap();

        // Attempting to replace a shared lock with an exclusive lock will fail
        // with multiple lock holders, and remove the original shared lock.
        assert_eq!(file2.try_lock_exclusive().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());
        file1.lock_shared().unwrap();
    }

    /// Tests that locks are shared among duplicated file descriptors.
    #[test]
    fn lock_duplicate() {
        let tempdir = tempdir::TempDir::new("fs2").unwrap();
        let path = tempdir.path().join("fs2");
        let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
        let file2 = file1.duplicate().unwrap();
        let file3 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();

        // Create a lock through fd1, then replace it through fd2.
        file1.lock_shared().unwrap();
        file2.lock_exclusive().unwrap();
        assert_eq!(file3.try_lock_shared().unwrap_err().raw_os_error(),
                   lock_contended_error().raw_os_error());

        // Either of the file descriptors should be able to unlock.
        file1.unlock().unwrap();
        file3.lock_shared().unwrap();
    }
}
<|endoftext|>


<commit_before>use std::{
    pin::Pin,
    task::{Context, Poll},
};

use futures_util::{ready, stream::Stream};
use pin_project_lite::pin_project;
use serde::{Deserialize, Serialize};
use shakmaty::ByColor;

#[derive(Serialize, Deserialize)]
#[serde(remote = "ByColor")]
pub struct ByColorDef<T> {
    white: T,
    black: T,
}

pub trait NevermindExt: Sized {
    fn nevermind(self, _msg: &str) {}
}

impl<T, E> NevermindExt for Result<T, E> {}

pub trait DeduplicateStreamExt: Stream {
    fn deduplicate_by<F, T>(self, f: F) -> DeduplicatedStream<Self, F, T>
    where
        Self: Sized,
    {
        DeduplicatedStream {
            stream: self,
            f,
            latest: None,
        }
    }
}

impl<S> DeduplicateStreamExt for S where S: Stream {}

pin_project! {
    pub struct DeduplicatedStream<S, F, T> where S: Stream {
        #[pin]
        stream: S,
        latest: Option<T>,
        f: F,
    }
}

impl<S, F, T> Stream for DeduplicatedStream<S, F, T>
where
    S: Stream,
    F: FnMut(&S::Item) -> T,
    T: PartialEq,
{
    type Item = S::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
        let mut this = self.project();

        Poll::Ready(loop {
            if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) {
                let latest = this.latest.replace((this.f)(&item));
                if latest != *this.latest {
                    break Some(item);
                }
            } else {
                break None;
            }
        })
    }
}
<commit_msg>remove unused NermindExt<commit_after>use std::{
    pin::Pin,
    task::{Context, Poll},
};

use futures_util::{ready, stream::Stream};
use pin_project_lite::pin_project;
use serde::{Deserialize, Serialize};
use shakmaty::ByColor;

#[derive(Serialize, Deserialize)]
#[serde(remote = "ByColor")]
pub struct ByColorDef<T> {
    white: T,
    black: T,
}

pub trait DeduplicateStreamExt: Stream {
    fn deduplicate_by<F, T>(self, f: F) -> DeduplicatedStream<Self, F, T>
    where
        Self: Sized,
    {
        DeduplicatedStream {
            stream: self,
            f,
            latest: None,
        }
    }
}

impl<S> DeduplicateStreamExt for S where S: Stream {}

pin_project! {
    pub struct DeduplicatedStream<S, F, T> where S: Stream {
        #[pin]
        stream: S,
        latest: Option<T>,
        f: F,
    }
}

impl<S, F, T> Stream for DeduplicatedStream<S, F, T>
where
    S: Stream,
    F: FnMut(&S::Item) -> T,
    T: PartialEq,
{
    type Item = S::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
        let mut this = self.project();

        Poll::Ready(loop {
            if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) {
                let latest = this.latest.replace((this.f)(&item));
                if latest != *this.latest {
                    break Some(item);
                }
            } else {
                break None;
            }
        })
    }
}
<|endoftext|>


<commit_before>use std::cmp::*;
use std::os;

pub fn is_word(word:&str) -> bool {
    // defines the following "container" sequences: (.*) and ".*"
    // words cannot end when either are unclosed
    regex!("^([^ \t\n\r\"()]*|\".*\"|\\(.*\\))*$").is_match(word)
}

// work around lack of DST
pub fn build_string(ch:char, count:usize) -> String {
    let mut s = String::new();
    let mut i = 0us;
    loop {
        if i == count {
            return s;
        }
        s.push(ch);
        i += 1;
    }
}

pub fn strip_words(line:Vec<String>) -> Vec<String> {
    let mut out = Vec::<String>::new();
    for word in line.iter() {
        out.push(strip_word(word));
    }
    return out;
}

pub fn strip_word(word:&String) -> String {
    let first_removed:String = regex!("\"").splitn(word.as_slice(), 2).collect::<Vec<&str>>().as_slice().concat();
    let second_removed:String = regex!("\"$").splitn(first_removed.as_slice(), 2).collect::<Vec<&str>>().as_slice().concat();
    return second_removed.to_string();
}

pub fn expand_path(path:Path) -> Path {
    if Path::new("~").is_ancestor_of(&path) {
        return match os::homedir() {
            None => Path::new("/"),
            Some(val) => Path::new(val)
        }.join(Path::new(path.as_vec().slice_from(min(path.as_vec().len(), 2))));
    } else {
        return path;
    }
}

pub fn condense_path(path:Path) -> Path {
    let homep = Path::new(match os::homedir() {
            None => return path,
            Some(val) => val
    });
    if homep.is_ancestor_of(&path) {
        match path.path_relative_from(&homep) {
            None => path,
            Some(path) => Path::new("~").join(path)
        }
    } else {
        return path;
    }
}

#[test]
fn build_string_test() {
    assert!(build_string('a', 5) == String::from_str("aaaaa"));
}

#[test]
fn is_word_test() {
    assert!(is_word(""));
    assert!(is_word("hello"));
    assert!(is_word("\"hello world\""));
    assert!(is_word("TEST=\"hello world\""));
    assert!(is_word("func(test function)"));
    assert!(is_word("this(is)a\"complex\"series"));

    assert!(!is_word("TEST=\"hello"));
    assert!(!is_word("func(test fun"));
    assert!(!is_word("this(is)a\"complex\"series(with no end"));
}

#[test]
fn strip_word_test() {
    assert!(strip_word(&String::from_str("\"hello world\"")) == String::from_str("hello world"));
    assert!(strip_word(&String::from_str("hello")) == String::from_str("hello"));
}

#[test]
fn strip_words_test() {
    assert!(strip_words(vec![String::from_str("\"hel\"lo world\""), String::from_str("hello")])
            == vec![String::from_str("hel\"lo world"), String::from_str("hello")]);
}

#[test]
fn expand_path_test() {
    // tests require the HOME env set
    let homep = Path::new(os::homedir().unwrap());
    assert!(expand_path(Path::new("~/Documents/scripts/")) == homep.join("Documents/scripts/"));
    assert!(expand_path(Path::new("/etc/wash/")) == Path::new("/etc/wash/"));
}

#[test]
fn condense_path_test() {
    // tests require the HOME env set
    let homep = Path::new(os::homedir().unwrap());
    assert!(condense_path(homep.join("Documents/scripts/")) ==
            Path::new("~/Documents/scripts/"));
    assert!(condense_path(Path::new("/home/")) == Path::new("/home/"));
    assert!(condense_path(Path::new("/etc/wash/")) == Path::new("/etc/wash/"));
}
<commit_msg>Improve is_word significantly<commit_after>use regex::NoExpand;

use std::cmp::*;
use std::os;

pub fn is_word(word:&str) -> bool {
    // try easy outs
    if regex!("^[^ \t\r\n\"()]*(\"[^\"]*\"|\\([^()]*\\))$").is_match(word) {
        // no nested delimiters means we can check with one regex call
        return true;
    } else if !regex!("^[^ \t\r\n\"()]*(\\(.*\\)|\".*\")*$").is_match(word) {
        // basic delimiter check, if this doesn't match then this is definitely not a word
        return false;
    }

    // ok, it's going to be more difficult
    let re = regex!("\"[^\"]*\"|\\([^()]*\\)");
    let check_re = regex!("^[^\"()]*$");
    let mut val = re.replace_all(word, NoExpand(""));
    let mut nval = re.replace_all(val.as_slice(), NoExpand(""));
    loop {
        if check_re.is_match(nval.as_slice()) {
            // all delimiters are balanced
            return true;
        } else if val == nval {
            // there are unbalanced delimiters
            return false;
        }
        // keep trying
        // Do two at a time to speed things up
        val = re.replace_all(nval.as_slice(), NoExpand(""));
        nval = re.replace_all(val.as_slice(), NoExpand(""));
    }
}

// work around lack of DST
pub fn build_string(ch:char, count:usize) -> String {
    let mut s = String::new();
    let mut i = 0us;
    loop {
        if i == count {
            return s;
        }
        s.push(ch);
        i += 1;
    }
}

pub fn strip_words(line:Vec<String>) -> Vec<String> {
    let mut out = Vec::<String>::new();
    for word in line.iter() {
        out.push(strip_word(word));
    }
    return out;
}

pub fn strip_word(word:&String) -> String {
    let first_removed:String = regex!("\"").splitn(word.as_slice(), 2).collect::<Vec<&str>>().as_slice().concat();
    let second_removed:String = regex!("\"$").splitn(first_removed.as_slice(), 2).collect::<Vec<&str>>().as_slice().concat();
    return second_removed.to_string();
}

pub fn expand_path(path:Path) -> Path {
    if Path::new("~").is_ancestor_of(&path) {
        return match os::homedir() {
            None => Path::new("/"),
            Some(val) => Path::new(val)
        }.join(Path::new(path.as_vec().slice_from(min(path.as_vec().len(), 2))));
    } else {
        return path;
    }
}

pub fn condense_path(path:Path) -> Path {
    let homep = Path::new(match os::homedir() {
            None => return path,
            Some(val) => val
    });
    if homep.is_ancestor_of(&path) {
        match path.path_relative_from(&homep) {
            None => path,
            Some(path) => Path::new("~").join(path)
        }
    } else {
        return path;
    }
}

#[test]
fn build_string_test() {
    assert!(build_string('a', 5) == String::from_str("aaaaa"));
}

#[test]
fn is_word_test() {
    assert!(is_word(""));
    assert!(is_word("hello"));
    assert!(is_word("\"hello world\""));
    assert!(is_word("TEST=\"hello world\""));
    assert!(is_word("func(test function)"));
    assert!(is_word("this(is a \"complex series\" with nested (delimiters))"));

    assert!(!is_word("TEST=\"hello"));
    assert!(!is_word("func(test fun"));
    assert!(!is_word("this(is a \"complex series\" with (unbalanced parens)"));
}

#[test]
fn strip_word_test() {
    assert!(strip_word(&String::from_str("\"hello world\"")) == String::from_str("hello world"));
    assert!(strip_word(&String::from_str("hello")) == String::from_str("hello"));
}

#[test]
fn strip_words_test() {
    assert!(strip_words(vec![String::from_str("\"hel\"lo world\""), String::from_str("hello")])
            == vec![String::from_str("hel\"lo world"), String::from_str("hello")]);
}

#[test]
fn expand_path_test() {
    // tests require the HOME env set
    let homep = Path::new(os::homedir().unwrap());
    assert!(expand_path(Path::new("~/Documents/scripts/")) == homep.join("Documents/scripts/"));
    assert!(expand_path(Path::new("/etc/wash/")) == Path::new("/etc/wash/"));
}

#[test]
fn condense_path_test() {
    // tests require the HOME env set
    let homep = Path::new(os::homedir().unwrap());
    assert!(condense_path(homep.join("Documents/scripts/")) ==
            Path::new("~/Documents/scripts/"));
    assert!(condense_path(Path::new("/home/")) == Path::new("/home/"));
    assert!(condense_path(Path::new("/etc/wash/")) == Path::new("/etc/wash/"));
}
<|endoftext|>