<commit_before>use hyper::server::Request;
use hyper::status::StatusCode;
use hyper::method::Method;
use uuid::Uuid;

use super::super::Result;
use super::super::utils::Addr;
use super::super::memdb::Symbol as MemDbSymbol;
use super::server::{ServerContext, load_request_data};
use super::types::{ApiResponse, ApiError};

#[derive(Deserialize)]
struct SymbolLookupRequest {
    sdk_id: String,
    cpu_name: String,
    symbols: Vec<Symbol>,
}

#[derive(Serialize, Deserialize)]
struct Symbol {
    object_uuid: Option<Uuid>,
    object_name: Option<String>,
    symbol: Option<String>,
    addr: Addr,
}

impl<'a> From<MemDbSymbol<'a>> for Symbol {
    fn from(sym: MemDbSymbol<'a>) -> Symbol {
        Symbol {
            object_uuid: Some(sym.object_uuid()),
            object_name: Some(sym.object_name().to_string()),
            symbol: Some(sym.symbol().to_string()),
            addr: Addr(sym.addr()),
        }
    }
}

#[derive(Serialize)]
struct SymbolResponse {
    symbols: Vec<Option<Symbol>>,
}

pub fn healthcheck_handler(ctx: &ServerContext, req: Request) -> Result<ApiResponse>
{
    if req.method != Method::Get {
        return Err(ApiError::MethodNotAllowed.into());
    }
    let rv = ctx.check_health()?;
    let status = if rv.is_healthy {
        StatusCode::Ok
    } else {
        StatusCode::ServiceUnavailable
    };
    ApiResponse::new(rv, status)
}

pub fn lookup_symbol_handler(ctx: &ServerContext, mut req: Request) -> Result<ApiResponse>
{
    if req.method != Method::Post {
        return Err(ApiError::MethodNotAllowed.into());
    }

    let data: SymbolLookupRequest = load_request_data(&mut req)?;
    let sdk_infos = ctx.stash.fuzzy_match_sdk_id(&data.sdk_id)?;
    if sdk_infos.is_empty() {
        return Err(ApiError::SdkNotFound.into());
    }

    let mut rv = vec![];
    for symq in data.symbols {
        let mut rvsym = None;
        if let Some(ref uuid) = symq.object_uuid {
            for sdk_info in sdk_infos.iter() {
                let sdk = ctx.stash.get_memdb(&sdk_info)?;
                if let Some(sym) = sdk.lookup_by_uuid(uuid, symq.addr.into()) {
                    rvsym = Some(sym.into());
                    break;
                }
            }
        } else if let Some(ref name) = symq.object_name {
            for sdk_info in sdk_infos.iter() {
                let sdk = ctx.stash.get_memdb(&sdk_info)?;
                if let Some(sym) = sdk.lookup_by_object_name(
                   name, &data.cpu_name, symq.addr.into()) {
                    rvsym = Some(sym.into());
                    break;
                }
            }
        }
        rv.push(rvsym);
    }

    ApiResponse::new(SymbolResponse {
        symbols: rv,
    }, StatusCode::Ok)
}
<commit_msg>Added a local cache that bypasses some overhead in the stash<commit_after>use std::sync::Arc;
use std::collections::HashMap;

use hyper::server::Request;
use hyper::status::StatusCode;
use hyper::method::Method;
use uuid::Uuid;

use super::super::Result;
use super::super::utils::Addr;
use super::super::sdk::SdkInfo;
use super::super::memdb::{MemDb, Symbol as MemDbSymbol};
use super::super::memdbstash::MemDbStash;
use super::server::{ServerContext, load_request_data};
use super::types::{ApiResponse, ApiError};

#[derive(Deserialize)]
struct SymbolLookupRequest {
    sdk_id: String,
    cpu_name: String,
    symbols: Vec<Symbol>,
}

#[derive(Serialize, Deserialize)]
struct Symbol {
    object_uuid: Option<Uuid>,
    object_name: Option<String>,
    symbol: Option<String>,
    addr: Addr,
}

impl<'a> From<MemDbSymbol<'a>> for Symbol {
    fn from(sym: MemDbSymbol<'a>) -> Symbol {
        Symbol {
            object_uuid: Some(sym.object_uuid()),
            object_name: Some(sym.object_name().to_string()),
            symbol: Some(sym.symbol().to_string()),
            addr: Addr(sym.addr()),
        }
    }
}

#[derive(Serialize)]
struct SymbolResponse {
    symbols: Vec<Option<Symbol>>,
}

struct LocalMemDbCache<'a> {
    stash: &'a MemDbStash,
    cache: HashMap<SdkInfo, Arc<MemDb<'static>>>,
}

impl<'a> LocalMemDbCache<'a> {
    pub fn new(stash: &'a MemDbStash) -> LocalMemDbCache<'a> {
        LocalMemDbCache {
            stash: stash,
            cache: HashMap::new(),
        }
    }

    pub fn get_memdb(&mut self, info: &SdkInfo) -> Result<Arc<MemDb<'static>>> {
        if let Some(memdb) = self.cache.get(&info) {
            return Ok(memdb.clone());
        }
        let rv = self.stash.get_memdb(info)?;
        self.cache.insert(info.clone(), rv.clone());
        Ok(rv)
    }
}

pub fn healthcheck_handler(ctx: &ServerContext, req: Request) -> Result<ApiResponse>
{
    if req.method != Method::Get {
        return Err(ApiError::MethodNotAllowed.into());
    }
    let rv = ctx.check_health()?;
    let status = if rv.is_healthy {
        StatusCode::Ok
    } else {
        StatusCode::ServiceUnavailable
    };
    ApiResponse::new(rv, status)
}

pub fn lookup_symbol_handler(ctx: &ServerContext, mut req: Request) -> Result<ApiResponse>
{
    if req.method != Method::Post {
        return Err(ApiError::MethodNotAllowed.into());
    }

    let data: SymbolLookupRequest = load_request_data(&mut req)?;
    let sdk_infos = ctx.stash.fuzzy_match_sdk_id(&data.sdk_id)?;
    if sdk_infos.is_empty() {
        return Err(ApiError::SdkNotFound.into());
    }

    let mut lc = LocalMemDbCache::new(&ctx.stash);

    let mut rv = vec![];
    for symq in data.symbols {
        let mut rvsym = None;
        if let Some(ref uuid) = symq.object_uuid {
            for sdk_info in sdk_infos.iter() {
                if let Some(sym) = lc.get_memdb(sdk_info)?.lookup_by_uuid(
                   uuid, symq.addr.into()) {
                    rvsym = Some(sym.into());
                    break;
                }
            }
        } else if let Some(ref name) = symq.object_name {
            for sdk_info in sdk_infos.iter() {
                if let Some(sym) = lc.get_memdb(sdk_info)?.lookup_by_object_name(
                   name, &data.cpu_name, symq.addr.into()) {
                    rvsym = Some(sym.into());
                    break;
                }
            }
        }
        rv.push(rvsym);
    }

    ApiResponse::new(SymbolResponse {
        symbols: rv,
    }, StatusCode::Ok)
}
<|endoftext|>


<commit_before>//! Type represents a data type. Its implementation is pluggable.
//! So, Types are loaded from the packages.
//!

use std::fmt;
use std::mem;
use std::cmp::Ordering;
use std::rc::Rc;

use itertools::Itertools;

use err::Result;
use page::Chunk;

pub type TypeFactory = Rc<Fn(&str) -> Result<Ty>>;

pub mod name {
  pub static BOOL: &'static str = "bool";
  pub static U8: &'static str = "u8";
  pub static I8: &'static str = "i8";
  pub static I16: &'static str = "i16";
  pub static I32: &'static str = "i32";
  pub static I64: &'static str = "i64";
  pub static F32: &'static str = "f32";
  pub static F64: &'static str = "f64";
  pub static Array: &'static str = "Array";
  pub static Tuple: &'static str = "Tuple";
}

pub static BOOL: &'static Ty = &Ty::Bool;
pub static U8: &'static Ty = &Ty::U8;
pub static I8: &'static Ty = &Ty::I8;
pub static I16: &'static Ty = &Ty::I16;
pub static I32: &'static Ty = &Ty::I32;
pub static I64: &'static Ty = &Ty::I64;
pub static F32: &'static Ty = &Ty::F32;
pub static F64: &'static Ty = &Ty::F64;

/// Types
///
/// * Bool
/// * U8 : unsigned 8 bit integer
/// * I8 : signed 8 bit integer
/// * I16: signed 16 bit integer
/// * I32: signed 32 bit integer
/// * I64: signed 64 bit integer
/// * F32: single precision floating type
/// * F64: double precision floating type
/// * Array: multi-dimensional array of same elements
/// * Tuple: an ordered list of arbtrary type values
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub enum Ty {
  Bool,
  U8,
  I8,
  I16,
  I32,
  I64,
  F32,
  F64,
  Array(Box<Ty>, Vec<usize>),
  Tuple(Vec<Ty>),
}

impl Ty {
  /// Get a base name
  pub fn base(&self) -> &str {
    match *self {
      Ty::Bool => name::BOOL,
      Ty::U8 => name::U8,
      Ty::I8 => name::I8,
      Ty::I16 => name::I16,
      Ty::I32 => name::I32,
      Ty::I64 => name::I64,
      Ty::F32 => name::F32,
      Ty::F64 => name::F64,
      Ty::Array(..) => panic!("unsupported type: vector"),
      Ty::Tuple(_) => panic!("unsupported type: tuple"),
    }
  }

  #[inline(always)]
  pub fn is_int(&self) -> bool {
    self.is_sint() || self.is_uint()
  }

  pub fn is_sint(&self) -> bool {
    match *self {
      Ty::Bool |
      Ty::I8 |
      Ty::I16 |
      Ty::I32 |
      Ty::I64 => true,
      _ => false,
    }
  }

  pub fn is_uint(&self) -> bool {
    *self == Ty::U8
  }

  pub fn is_float(&self) -> bool {
    *self == Ty::F32 || *self == Ty::F64
  }

  pub fn size_of(&self) -> usize {
    match *self {
      Ty::Bool => 1,
      Ty::U8 | Ty::I8 => 1,
      Ty::I16 => 2,
      Ty::I32 | Ty::F32 => 4,
      Ty::I64 | Ty::F64 => 8,
      Ty::Array(ref ty, ref dims) => dims.iter().fold(0, |acc, dim| acc + (ty.size_of() * dim)),
      Ty::Tuple(ref tys) => tys.iter().fold(0, |acc, ty| acc + ty.size_of()),
    }
  }

  pub fn is_variable(&self) -> bool {
    false
  }
}

pub fn u(size: u32) -> &'static Ty {
  match size {
    8 => U8,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn i(size: u32) -> &'static Ty {
  match size {
    8 => I8,
    16 => I16,
    32 => I32,
    64 => I64,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn f(size: u32) -> &'static Ty {
  match size {
    32 => F32,
    64 => F64,
    _ => panic!("unsupported integer type: f{}", size),
  }
}

/// Typed implementation
pub trait HasType {
  fn ty(&self) -> &Ty;
}

impl fmt::Display for Ty {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      Ty::Bool => write!(f, "bool"),
      Ty::I8 => write!(f, "i8"),
      Ty::U8 => write!(f, "u8"),
      Ty::I16 => write!(f, "i16"),
      Ty::I32 => write!(f, "i32"),
      Ty::I64 => write!(f, "i64"),
      Ty::F32 => write!(f, "f32"),
      Ty::F64 => write!(f, "f64"),
      Ty::Array(ref ty, ref dims) => {
        write!(f, "Array({},{})", ty, dims.iter().map(|dim| format!("{}", dim)).join(","))
      }
      Ty::Tuple(ref types) => {
        write!(f, "({})", types.iter().map(|dim| format!("{}", dim)).join(","))
      }
    }
  }
}

//
// Determine a result data type from two expression data types.
// #[allow(unused_variables)]
// pub fn result_data_ty(lhs_ty: &Ty, rhs_ty: &Ty) -> Ty {
// match *lhs_ty {
// Ty::Bool => {
// match *rhs_ty {
// Ty::Bool => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
//
// Ty::Int1 => {
// match *rhs_ty {
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int2 => {
// match *rhs_ty {
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   => lhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int4 => {
// match *rhs_ty {
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int8 => {
// match *rhs_ty {
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float4 => {
// match *rhs_ty {
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 |
// Ty::Int8 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float8 => {
// match *rhs_ty {
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Numeric(ref p, ref s) => {
// panic!("Undefined operator")
// },
//
// Ty::Date => {
// panic!("Undefined Operator")
// },
//
// Ty::Time => {
// panic!("Undefined Operator")
// },
//
// Ty::Timez => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestamp => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestampz => {
// panic!("Undefined Operator")
// },
//
// Ty::Interval => {
// panic!("Undefined Operator")
// },
//
// Ty::Char(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Binary(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Clob => {
// match *rhs_ty {
// Ty::Clob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Blob => {
// match *rhs_ty {
// Ty::Blob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Array(ref ty) => {
// panic!("Undefined Operator")
// }
//
// Ty::Struct(ref tys) => {
// panic!("Undefined Operator")
// },
//
// Ty::Map(ref kty, ref vty) => {
// panic!("Undefined Operator")
// }
// }
// }
//

// pub fn bool_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<bool>()))
// })
// };
//
// Ty::new(BOOL_STR, true, true, handler)
// }
//
// pub fn i8_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i8>()))
// })
// };
//
// Ty::new(I8_STR, true, true, handler)
// }
//
// pub fn i16_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i16>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i32>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i64>()))
// })
// };
//
// Ty::new(I64_STR, true, true, handler)
// }
//
// pub fn f32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f32>()))
// })
// };
//
// Ty::new(F32_STR, true, true, handler)
// }
//
// pub fn f64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f64>()))
// })
// };
//
// Ty::new(F64_STR, true, true, handler)
// }
//

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

  fn assert_display(ty: Ty, expected: &str) {
    assert_eq!(&format!("{}", ty), expected)
  }

  #[test]
  fn test_display_agg_types() {
    assert_display(Ty::Array(Box::new(Ty::F64), vec!(4,4)), "Array(f64,4,4)");
    assert_display(Ty::Tuple(vec!(Ty::F64, Ty::F64, Ty::I64)), "(f64,f64,i64)");
  }
}<commit_msg>Fix some format in types.rs.<commit_after>//! Type represents a data type. Its implementation is pluggable.
//! So, Types are loaded from the packages.
//!

use std::fmt;
use std::mem;
use std::cmp::Ordering;
use std::rc::Rc;

use itertools::Itertools;

use err::Result;
use page::Chunk;

pub type TypeFactory = Rc<Fn(&str) -> Result<Ty>>;

pub mod name {
  pub static BOOL: &'static str = "bool";
  pub static U8: &'static str = "u8";
  pub static I8: &'static str = "i8";
  pub static I16: &'static str = "i16";
  pub static I32: &'static str = "i32";
  pub static I64: &'static str = "i64";
  pub static F32: &'static str = "f32";
  pub static F64: &'static str = "f64";
  pub static Array: &'static str = "Array";
  pub static Tuple: &'static str = "Tuple";
}

pub static BOOL: &'static Ty = &Ty::Bool;
pub static U8: &'static Ty = &Ty::U8;
pub static I8: &'static Ty = &Ty::I8;
pub static I16: &'static Ty = &Ty::I16;
pub static I32: &'static Ty = &Ty::I32;
pub static I64: &'static Ty = &Ty::I64;
pub static F32: &'static Ty = &Ty::F32;
pub static F64: &'static Ty = &Ty::F64;

/// Types
///
/// * Bool
/// * U8 : unsigned 8 bit integer
/// * I8 : signed 8 bit integer
/// * I16: signed 16 bit integer
/// * I32: signed 32 bit integer
/// * I64: signed 64 bit integer
/// * F32: single precision floating type
/// * F64: double precision floating type
/// * Array: multi-dimensional array of same elements
/// * Tuple: an ordered list of arbtrary type values
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub enum Ty {
  Bool,
  U8,
  I8,
  I16,
  I32,
  I64,
  F32,
  F64,
  Array(Box<Ty>, Vec<usize>),
  Tuple(Vec<Ty>),
}

impl Ty {
  /// Get a base name
  pub fn base(&self) -> &str {
    match *self {
      Ty::Bool => name::BOOL,
      Ty::U8 => name::U8,
      Ty::I8 => name::I8,
      Ty::I16 => name::I16,
      Ty::I32 => name::I32,
      Ty::I64 => name::I64,
      Ty::F32 => name::F32,
      Ty::F64 => name::F64,
      Ty::Array(..) => panic!("unsupported type: vector"),
      Ty::Tuple(_) => panic!("unsupported type: tuple"),
    }
  }

  #[inline(always)]
  pub fn is_int(&self) -> bool {
    self.is_sint() || self.is_uint()
  }

  pub fn is_sint(&self) -> bool {
    match *self {
      Ty::Bool |
      Ty::I8 |
      Ty::I16 |
      Ty::I32 |
      Ty::I64 => true,
      _ => false,
    }
  }

  pub fn is_uint(&self) -> bool {
    *self == Ty::U8
  }

  pub fn is_float(&self) -> bool {
    *self == Ty::F32 || *self == Ty::F64
  }

  pub fn size_of(&self) -> usize {
    match *self {
      Ty::Bool => 1,
      Ty::U8 | Ty::I8 => 1,
      Ty::I16 => 2,
      Ty::I32 | Ty::F32 => 4,
      Ty::I64 | Ty::F64 => 8,
      Ty::Array(ref ty, ref dims) => dims.iter().fold(0, |acc, dim| acc + (ty.size_of() * dim)),
      Ty::Tuple(ref tys) => tys.iter().fold(0, |acc, ty| acc + ty.size_of()),
    }
  }

  pub fn is_variable(&self) -> bool {
    false
  }
}

pub fn u(size: u32) -> &'static Ty {
  match size {
    8 => U8,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn i(size: u32) -> &'static Ty {
  match size {
    8 => I8,
    16 => I16,
    32 => I32,
    64 => I64,
    _ => panic!("unsupported integer type: i{}", size),
  }
}

pub fn f(size: u32) -> &'static Ty {
  match size {
    32 => F32,
    64 => F64,
    _ => panic!("unsupported integer type: f{}", size),
  }
}

/// Typed implementation
pub trait HasType {
  fn ty(&self) -> &Ty;
}

impl fmt::Display for Ty {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      Ty::Bool => write!(f, "bool"),
      Ty::I8 => write!(f, "i8"),
      Ty::U8 => write!(f, "u8"),
      Ty::I16 => write!(f, "i16"),
      Ty::I32 => write!(f, "i32"),
      Ty::I64 => write!(f, "i64"),
      Ty::F32 => write!(f, "f32"),
      Ty::F64 => write!(f, "f64"),
      Ty::Array(ref ty, ref dims) => {
        write!(f,
               "Array({},{})",
               ty,
               dims.iter().map(|dim| format!("{}", dim)).join(","))
      }
      Ty::Tuple(ref types) => {
        write!(f,
               "({})",
               types.iter().map(|dim| format!("{}", dim)).join(","))
      }
    }
  }
}

// Determine a result data type from two expression data types.
// #[allow(unused_variables)]
// pub fn result_data_ty(lhs_ty: &Ty, rhs_ty: &Ty) -> Ty {
// match *lhs_ty {
// Ty::Bool => {
// match *rhs_ty {
// Ty::Bool => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
//
// Ty::Int1 => {
// match *rhs_ty {
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int2 => {
// match *rhs_ty {
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   => lhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int4 => {
// match *rhs_ty {
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Int8 => {
// match *rhs_ty {
// Ty::Int8   |
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float4 => {
// match *rhs_ty {
// Ty::Float4 |
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1 |
// Ty::Int2 |
// Ty::Int4 |
// Ty::Int8 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Float8 => {
// match *rhs_ty {
// Ty::Float8 => rhs_ty.clone(),
//
// Ty::Int1   |
// Ty::Int2   |
// Ty::Int4   |
// Ty::Int8   |
// Ty::Float4 => lhs_ty.clone(),
//
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Numeric(ref p, ref s) => {
// panic!("Undefined operator")
// },
//
// Ty::Date => {
// panic!("Undefined Operator")
// },
//
// Ty::Time => {
// panic!("Undefined Operator")
// },
//
// Ty::Timez => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestamp => {
// panic!("Undefined Operator")
// },
//
// Ty::Timestampz => {
// panic!("Undefined Operator")
// },
//
// Ty::Interval => {
// panic!("Undefined Operator")
// },
//
// Ty::Char(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Binary(ref len) => {
// panic!("Undefined Operator")
// },
//
// Ty::Clob => {
// match *rhs_ty {
// Ty::Clob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Blob => {
// match *rhs_ty {
// Ty::Blob => rhs_ty.clone(),
// _ => panic!("Undefined Operator")
// }
// },
//
// Ty::Array(ref ty) => {
// panic!("Undefined Operator")
// }
//
// Ty::Struct(ref tys) => {
// panic!("Undefined Operator")
// },
//
// Ty::Map(ref kty, ref vty) => {
// panic!("Undefined Operator")
// }
// }
// }
//

// pub fn bool_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<bool>()))
// })
// };
//
// Ty::new(BOOL_STR, true, true, handler)
// }
//
// pub fn i8_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i8>()))
// })
// };
//
// Ty::new(I8_STR, true, true, handler)
// }
//
// pub fn i16_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i16>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i32>()))
// })
// };
//
// Ty::new(I32_STR, true, true, handler)
// }
//
// pub fn i64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<i64>()))
// })
// };
//
// Ty::new(I64_STR, true, true, handler)
// }
//
// pub fn f32_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f32>()))
// })
// };
//
// Ty::new(F32_STR, true, true, handler)
// }
//
// pub fn f64_ty() -> Ty {
// let handler = TypeHandler {
// create_minipage: Rc::new(|| -> Box<MiniPage> {
// Box::new(FMiniPage::new(mem::size_of::<f64>()))
// })
// };
//
// Ty::new(F64_STR, true, true, handler)
// }
//

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

  fn assert_display(ty: Ty, expected: &str) {
    assert_eq!(&format!("{}", ty), expected)
  }

  #[test]
  fn test_display_agg_types() {
    assert_display(Ty::Array(Box::new(Ty::F64), vec![4, 4]), "Array(f64,4,4)");
    assert_display(Ty::Tuple(vec![Ty::F64, Ty::F64, Ty::I64]), "(f64,f64,i64)");
  }
}
<|endoftext|>


<commit_before>use parking_lot::{RwLock, RwLockWriteGuard};
use serde;
use std;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;

use bifrost_hasher::{hash_bytes, hash_str};
use conshash::weights::client::SMClient as WeightSMClient;
use conshash::weights::DEFAULT_SERVICE_ID;
use membership::client::{Member, ObserverClient as MembershipClient};
use raft::client::{RaftClient, SubscriptionError, SubscriptionReceipt};
use raft::state_machine::master::ExecError;
use rand;
use utils::bincode::serialize;

use futures::prelude::*;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use tokio_core::reactor::Core;

pub mod weights;

#[derive(Debug)]
pub enum Action {
    Joined,
    Left,
}

#[derive(Debug)]
pub enum InitTableError {
    GroupNotExisted,
    NoWeightService(ExecError),
    NoWeightGroup,
    NoWeightInfo,
    Unknown,
}

#[derive(Debug)]
pub enum CHError {
    WatchError(Result<Result<SubscriptionReceipt, SubscriptionError>, ExecError>),
    InitTableError(InitTableError),
}

struct LookupTables {
    nodes: Vec<u64>,
    addrs: HashMap<u64, String>,
}

pub struct ConsistentHashing {
    tables: RwLock<LookupTables>,
    membership: Arc<MembershipClient>,
    weight_sm_client: WeightSMClient,
    group_name: String,
    watchers: RwLock<Vec<Box<Fn(&Member, &Action, &LookupTables, &Vec<u64>) + Send + Sync>>>,
    version: AtomicU64,
}

impl ConsistentHashing {
    pub fn new_with_id(
        id: u64,
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        let membership = Arc::new(MembershipClient::new(raft_client));
        let ch = Arc::new(ConsistentHashing {
            tables: RwLock::new(LookupTables {
                nodes: Vec::new(),
                addrs: HashMap::new(),
            }),
            membership: membership.clone(),
            weight_sm_client: WeightSMClient::new(id, &raft_client),
            group_name: group.to_string(),
            watchers: RwLock::new(Vec::new()),
            version: AtomicU64::new(0),
        });
        let mut core = Core::new().unwrap();
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_joined(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_joined(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_online(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_joined(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_left(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_left(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_offline(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_left(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        Ok(ch)
    }
    pub fn new(
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        Self::new_with_id(DEFAULT_SERVICE_ID, group, raft_client)
    }
    pub fn new_client(
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        Self::new_client_with_id(DEFAULT_SERVICE_ID, group, raft_client)
    }
    pub fn new_client_with_id(
        id: u64,
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        match ConsistentHashing::new_with_id(id, group, raft_client) {
            Err(e) => Err(e),
            Ok(ch) => match ch.init_table() {
                Err(e) => Err(CHError::InitTableError(e)),
                Ok(_) => Ok(ch.clone()),
            },
        }
    }
    pub fn init_table(&self) -> Result<(), InitTableError> {
        let mut table = &mut self.tables.write();
        self.init_table_(&mut table)
    }
    pub fn to_server_name(&self, server_id: u64) -> String {
        let lookup_table = self.tables.read();
        lookup_table.addrs.get(&server_id).unwrap().clone()
    }
    pub fn to_server_name_option(&self, server_id: Option<u64>) -> Option<String> {
        server_id.map(|id| {
            let lookup_table = self.tables.read();
            lookup_table.addrs.get(&id).unwrap().clone()
        })
    }
    pub fn get_server_id(&self, hash: u64) -> Option<u64> {
        let lookup_table = self.tables.read();
        let nodes = &lookup_table.nodes;
        let slot_count = nodes.len();
        if slot_count == 0 {
            return None;
        }
        let result = nodes.get(self.jump_hash(slot_count, hash));
        debug!("Hash {} have been point to {:?}", hash, result);
        result.cloned()
    }
    pub fn jump_hash(&self, slot_count: usize, hash: u64) -> usize {
        let mut b: i64 = -1;
        let mut j: i64 = 0;
        let mut h = hash;
        while j < (slot_count as i64) {
            b = j;
            h = h.wrapping_mul(2862933555777941757).wrapping_add(1);
            j = (((b.wrapping_add(1)) as f64) * ((1i64 << 31) as f64)
                / (((h >> 33).wrapping_add(1)) as f64)) as i64;
        }
        debug!(
            "Jump hash point to index {} for {}, with slots {}",
            b, hash, slot_count
        );
        b as usize
    }
    pub fn get_server(&self, hash: u64) -> Option<String> {
        self.to_server_name_option(self.get_server_id(hash))
    }
    pub fn get_server_by_string(&self, string: &String) -> Option<String> {
        self.get_server(hash_str(string))
    }
    pub fn get_server_by<T>(&self, obj: &T) -> Option<String>
    where
        T: serde::Serialize,
    {
        self.get_server(hash_bytes(serialize(obj).as_slice()))
    }
    pub fn get_server_id_by_string(&self, string: &String) -> Option<u64> {
        self.get_server_id(hash_str(string))
    }
    pub fn get_server_id_by<T>(&self, obj: &T) -> Option<u64>
    where
        T: serde::Serialize,
    {
        self.get_server_id(hash_bytes(serialize(obj).as_slice()))
    }
    pub fn rand_server(&self) -> Option<String> {
        let rand = rand::random::<u64>();
        self.get_server(rand)
    }
    pub fn nodes_count(&self) -> usize {
        let lookup_table = self.tables.read();
        return lookup_table.nodes.len();
    }
    pub fn set_weight(
        &self,
        server_name: &String,
        weight: u64,
    ) -> impl Future<Item = bool, Error = ExecError> {
        let group_id = hash_str(&self.group_name);
        let server_id = hash_str(server_name);
        self.weight_sm_client
            .set_weight(&group_id, &server_id, &weight)
            .map(|res| if let Ok(_) = res { true } else { false })
    }
    pub fn watch_all_actions<F>(&self, f: F)
    where
        F: Fn(&Member, &Action, &LookupTables, &Vec<u64>) + 'static + Send + Sync,
    {
        let mut watchers = self.watchers.write();
        watchers.push(Box::new(f));
    }
    pub fn watch_server_nodes_range_changed<F>(&self, server: &String, f: F)
    // return ranges [...,...)
    where
        F: Fn((usize, u32)) + 'static + Send + Sync,
    {
        let server_id = hash_str(server);
        let wrapper = move |_: &Member, _: &Action, lookup_table: &LookupTables, _: &Vec<u64>| {
            let nodes = &lookup_table.nodes;
            let node_len = nodes.len();
            let mut weight = 0;
            let mut start = None;
            for ni in 0..node_len {
                let node = nodes[ni];
                if node == server_id {
                    weight += 1;
                    if start.is_none() {
                        start = Some(ni)
                    }
                }
            }
            if start.is_some() {
                f((start.unwrap(), weight));
            } else {
                warn!("No node exists for watch");
            }
        };
        self.watch_all_actions(wrapper);
    }

    fn init_table_(
        &self,
        lookup_table: &mut RwLockWriteGuard<LookupTables>,
    ) -> Result<(), InitTableError> {
        if let Ok(Ok((mut members, version))) =
            self.membership.group_members(&self.group_name, true).wait()
        {
            let group_id = hash_str(&self.group_name);
            match self.weight_sm_client.get_weights(&group_id).wait() {
                Ok(Ok(Some(weights))) => {
                    if let Some(min_weight) = weights.values().min() {
                        let mut factors: BTreeMap<u64, u32> = BTreeMap::new();
                        let min_weight = *min_weight as f64;
                        for member in members.iter() {
                            let k = member.id;
                            let w = match weights.get(&k) {
                                Some(w) => *w as f64,
                                None => min_weight,
                            };
                            factors.insert(k, (w / min_weight) as u32);
                        }
                        let factor_sum: u32 = factors.values().sum();
                        lookup_table.nodes = Vec::with_capacity(factor_sum as usize);
                        for member in members.iter() {
                            lookup_table.addrs.insert(member.id, member.address.clone());
                        }
                        for (server_id, weight) in factors.into_iter() {
                            for i in 0..weight {
                                lookup_table.nodes.push(server_id);
                            }
                        }
                        self.version.store(version, Ordering::Relaxed);
                        Ok(())
                    } else {
                        Err(InitTableError::NoWeightInfo)
                    }
                }
                Err(e) => Err(InitTableError::NoWeightService(e)),
                Ok(Ok(None)) => Err(InitTableError::NoWeightGroup),
                _ => Err(InitTableError::Unknown),
            }
        } else {
            Err(InitTableError::GroupNotExisted)
        }
    }
}

fn server_joined(ch: &Arc<ConsistentHashing>, member: Member, version: u64) {
    server_changed(ch, member, Action::Joined, version);
}
fn server_left(ch: &Arc<ConsistentHashing>, member: Member, version: u64) {
    server_changed(ch, member, Action::Left, version);
}
fn server_changed(ch: &Arc<ConsistentHashing>, member: Member, action: Action, version: u64) {
    let ch_version = ch.version.load(Ordering::Relaxed);
    if ch_version < version {
        let ch = ch.clone();
        thread::Builder::new()
            .name(format!(
                "Conshash member changes update - {}",
                member.address
            ))
            .spawn(move || {
                let mut lookup_table = ch.tables.write();
                let watchers = ch.watchers.read();
                let ch_version = ch.version.load(Ordering::Relaxed);
                if ch_version >= version {
                    return;
                }
                let old_nodes = lookup_table.nodes.clone();
                ch.init_table_(&mut lookup_table);
                for watch in watchers.iter() {
                    watch(&member, &action, &*lookup_table, &old_nodes);
                }
            });
    }
}
<commit_msg>export membership client<commit_after>use parking_lot::{RwLock, RwLockWriteGuard};
use serde;
use std;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;

use bifrost_hasher::{hash_bytes, hash_str};
use conshash::weights::client::SMClient as WeightSMClient;
use conshash::weights::DEFAULT_SERVICE_ID;
use membership::client::{Member, ObserverClient as MembershipClient};
use raft::client::{RaftClient, SubscriptionError, SubscriptionReceipt};
use raft::state_machine::master::ExecError;
use rand;
use utils::bincode::serialize;

use futures::prelude::*;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use tokio_core::reactor::Core;

pub mod weights;

#[derive(Debug)]
pub enum Action {
    Joined,
    Left,
}

#[derive(Debug)]
pub enum InitTableError {
    GroupNotExisted,
    NoWeightService(ExecError),
    NoWeightGroup,
    NoWeightInfo,
    Unknown,
}

#[derive(Debug)]
pub enum CHError {
    WatchError(Result<Result<SubscriptionReceipt, SubscriptionError>, ExecError>),
    InitTableError(InitTableError),
}

struct LookupTables {
    nodes: Vec<u64>,
    addrs: HashMap<u64, String>,
}

pub struct ConsistentHashing {
    tables: RwLock<LookupTables>,
    membership: Arc<MembershipClient>,
    weight_sm_client: WeightSMClient,
    group_name: String,
    watchers: RwLock<Vec<Box<Fn(&Member, &Action, &LookupTables, &Vec<u64>) + Send + Sync>>>,
    version: AtomicU64,
}

impl ConsistentHashing {
    pub fn new_with_id(
        id: u64,
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        let membership = Arc::new(MembershipClient::new(raft_client));
        let ch = Arc::new(ConsistentHashing {
            tables: RwLock::new(LookupTables {
                nodes: Vec::new(),
                addrs: HashMap::new(),
            }),
            membership: membership.clone(),
            weight_sm_client: WeightSMClient::new(id, &raft_client),
            group_name: group.to_string(),
            watchers: RwLock::new(Vec::new()),
            version: AtomicU64::new(0),
        });
        let mut core = Core::new().unwrap();
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_joined(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_joined(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_online(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_joined(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_left(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_left(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        {
            let ch = ch.clone();
            let res_fut = membership
                .on_group_member_offline(
                    move |r| {
                        if let Ok((member, version)) = r {
                            server_left(&ch, member, version);
                        }
                    },
                    group,
                );
            let res = core.run(res_fut);
            if let Ok(Ok(_)) = res {
            } else {
                return Err(CHError::WatchError(res));
            }
        }
        Ok(ch)
    }
    pub fn new(
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        Self::new_with_id(DEFAULT_SERVICE_ID, group, raft_client)
    }
    pub fn new_client(
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        Self::new_client_with_id(DEFAULT_SERVICE_ID, group, raft_client)
    }
    pub fn new_client_with_id(
        id: u64,
        group: &str,
        raft_client: &Arc<RaftClient>,
    ) -> Result<Arc<ConsistentHashing>, CHError> {
        match ConsistentHashing::new_with_id(id, group, raft_client) {
            Err(e) => Err(e),
            Ok(ch) => match ch.init_table() {
                Err(e) => Err(CHError::InitTableError(e)),
                Ok(_) => Ok(ch.clone()),
            },
        }
    }
    pub fn init_table(&self) -> Result<(), InitTableError> {
        let mut table = &mut self.tables.write();
        self.init_table_(&mut table)
    }
    pub fn to_server_name(&self, server_id: u64) -> String {
        let lookup_table = self.tables.read();
        lookup_table.addrs.get(&server_id).unwrap().clone()
    }
    pub fn to_server_name_option(&self, server_id: Option<u64>) -> Option<String> {
        server_id.map(|id| {
            let lookup_table = self.tables.read();
            lookup_table.addrs.get(&id).unwrap().clone()
        })
    }
    pub fn get_server_id(&self, hash: u64) -> Option<u64> {
        let lookup_table = self.tables.read();
        let nodes = &lookup_table.nodes;
        let slot_count = nodes.len();
        if slot_count == 0 {
            return None;
        }
        let result = nodes.get(self.jump_hash(slot_count, hash));
        debug!("Hash {} have been point to {:?}", hash, result);
        result.cloned()
    }
    pub fn jump_hash(&self, slot_count: usize, hash: u64) -> usize {
        let mut b: i64 = -1;
        let mut j: i64 = 0;
        let mut h = hash;
        while j < (slot_count as i64) {
            b = j;
            h = h.wrapping_mul(2862933555777941757).wrapping_add(1);
            j = (((b.wrapping_add(1)) as f64) * ((1i64 << 31) as f64)
                / (((h >> 33).wrapping_add(1)) as f64)) as i64;
        }
        debug!(
            "Jump hash point to index {} for {}, with slots {}",
            b, hash, slot_count
        );
        b as usize
    }
    pub fn get_server(&self, hash: u64) -> Option<String> {
        self.to_server_name_option(self.get_server_id(hash))
    }
    pub fn get_server_by_string(&self, string: &String) -> Option<String> {
        self.get_server(hash_str(string))
    }
    pub fn get_server_by<T>(&self, obj: &T) -> Option<String>
    where
        T: serde::Serialize,
    {
        self.get_server(hash_bytes(serialize(obj).as_slice()))
    }
    pub fn get_server_id_by_string(&self, string: &String) -> Option<u64> {
        self.get_server_id(hash_str(string))
    }
    pub fn get_server_id_by<T>(&self, obj: &T) -> Option<u64>
    where
        T: serde::Serialize,
    {
        self.get_server_id(hash_bytes(serialize(obj).as_slice()))
    }
    pub fn rand_server(&self) -> Option<String> {
        let rand = rand::random::<u64>();
        self.get_server(rand)
    }
    pub fn nodes_count(&self) -> usize {
        let lookup_table = self.tables.read();
        return lookup_table.nodes.len();
    }
    pub fn set_weight(
        &self,
        server_name: &String,
        weight: u64,
    ) -> impl Future<Item = bool, Error = ExecError> {
        let group_id = hash_str(&self.group_name);
        let server_id = hash_str(server_name);
        self.weight_sm_client
            .set_weight(&group_id, &server_id, &weight)
            .map(|res| if let Ok(_) = res { true } else { false })
    }
    pub fn watch_all_actions<F>(&self, f: F)
    where
        F: Fn(&Member, &Action, &LookupTables, &Vec<u64>) + 'static + Send + Sync,
    {
        let mut watchers = self.watchers.write();
        watchers.push(Box::new(f));
    }
    pub fn watch_server_nodes_range_changed<F>(&self, server: &String, f: F)
    // return ranges [...,...)
    where
        F: Fn((usize, u32)) + 'static + Send + Sync,
    {
        let server_id = hash_str(server);
        let wrapper = move |_: &Member, _: &Action, lookup_table: &LookupTables, _: &Vec<u64>| {
            let nodes = &lookup_table.nodes;
            let node_len = nodes.len();
            let mut weight = 0;
            let mut start = None;
            for ni in 0..node_len {
                let node = nodes[ni];
                if node == server_id {
                    weight += 1;
                    if start.is_none() {
                        start = Some(ni)
                    }
                }
            }
            if start.is_some() {
                f((start.unwrap(), weight));
            } else {
                warn!("No node exists for watch");
            }
        };
        self.watch_all_actions(wrapper);
    }

    fn init_table_(
        &self,
        lookup_table: &mut RwLockWriteGuard<LookupTables>,
    ) -> Result<(), InitTableError> {
        if let Ok(Ok((mut members, version))) =
            self.membership.group_members(&self.group_name, true).wait()
        {
            let group_id = hash_str(&self.group_name);
            match self.weight_sm_client.get_weights(&group_id).wait() {
                Ok(Ok(Some(weights))) => {
                    if let Some(min_weight) = weights.values().min() {
                        let mut factors: BTreeMap<u64, u32> = BTreeMap::new();
                        let min_weight = *min_weight as f64;
                        for member in members.iter() {
                            let k = member.id;
                            let w = match weights.get(&k) {
                                Some(w) => *w as f64,
                                None => min_weight,
                            };
                            factors.insert(k, (w / min_weight) as u32);
                        }
                        let factor_sum: u32 = factors.values().sum();
                        lookup_table.nodes = Vec::with_capacity(factor_sum as usize);
                        for member in members.iter() {
                            lookup_table.addrs.insert(member.id, member.address.clone());
                        }
                        for (server_id, weight) in factors.into_iter() {
                            for i in 0..weight {
                                lookup_table.nodes.push(server_id);
                            }
                        }
                        self.version.store(version, Ordering::Relaxed);
                        Ok(())
                    } else {
                        Err(InitTableError::NoWeightInfo)
                    }
                }
                Err(e) => Err(InitTableError::NoWeightService(e)),
                Ok(Ok(None)) => Err(InitTableError::NoWeightGroup),
                _ => Err(InitTableError::Unknown),
            }
        } else {
            Err(InitTableError::GroupNotExisted)
        }
    }
    pub fn membership(&self) -> Arc<MembershipClient> {
        self.membership.clone()
    }
}

fn server_joined(ch: &Arc<ConsistentHashing>, member: Member, version: u64) {
    server_changed(ch, member, Action::Joined, version);
}
fn server_left(ch: &Arc<ConsistentHashing>, member: Member, version: u64) {
    server_changed(ch, member, Action::Left, version);
}
fn server_changed(ch: &Arc<ConsistentHashing>, member: Member, action: Action, version: u64) {
    let ch_version = ch.version.load(Ordering::Relaxed);
    if ch_version < version {
        let ch = ch.clone();
        thread::Builder::new()
            .name(format!(
                "Conshash member changes update - {}",
                member.address
            ))
            .spawn(move || {
                let mut lookup_table = ch.tables.write();
                let watchers = ch.watchers.read();
                let ch_version = ch.version.load(Ordering::Relaxed);
                if ch_version >= version {
                    return;
                }
                let old_nodes = lookup_table.nodes.clone();
                ch.init_table_(&mut lookup_table);
                for watch in watchers.iter() {
                    watch(&member, &action, &*lookup_table, &old_nodes);
                }
            });
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Jauhien Piatlicki.
//
// Copyright 2012-2015 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Core LLVM: Context
// LLVM-C header Core.h

use core::ContextRef;
use self::ffi::*;

pub struct Context {
    context: ContextRef,
    owned: bool
}

impl Context {
    pub fn new() -> Context {
        let context = unsafe {
            LLVMContextCreate()
        };

        Context{
            context: context,
            owned: false
        }
    }

    pub fn get_global() -> Context {
        let context = unsafe {
            LLVMGetGlobalContext()
        };

        Context{
            context: context,
            owned: true
        }
    }

    pub unsafe fn from_ref(context_ref : ContextRef) -> Context {
        Context{
            context: context_ref,
            owned: false
        }
    }

    pub fn get_ref(&self) -> ContextRef {
        self.context
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        if !self.owned {
            unsafe {
                LLVMContextDispose(self.context);
            }
        }
    }
}

pub mod ffi {
    use core::*;
    use libc::{c_char, c_void, c_uint};

    #[link(name = "LLVMCore")]
    extern {
        /**
         * Create a new context.
         *
         * Every call to this function should be paired with a call to
         * LLVMContextDispose() or the context will leak memory.
         */
        pub fn LLVMContextCreate() -> ContextRef;

        /**
         * Obtain the global context instance.
         */
        pub fn LLVMGetGlobalContext() -> ContextRef;

        /**
         * Set the diagnostic handler for this context.
         */
        pub fn LLVMContextSetDiagnosticHandler(C: ContextRef,
                                               Handler: DiagnosticHandler,
                                               DiagnosticContext: *mut c_void);

        /**
         * Set the yield callback function for this context.
         */
        pub fn LLVMContextSetYieldCallback(C: ContextRef,
                                           Handler: YieldCallback,
                                           OpaqueHandle: *mut c_void);

        /**
         * Destroy a context instance.
         *
         * This should be called for every call to LLVMContextCreate() or memory
         * will be leaked.
         */
        pub fn LLVMContextDispose(C: ContextRef);

        /**
         * Return a string representation of the DiagnosticInfo. Use
         * LLVMDisposeMessage to free the string.
         */
        pub fn LLVMGetDiagInfoDescription(DI: DiagnosticInfoRef) -> *const c_char;

        pub fn LLVMGetDiagInfoSeverity(DI: DiagnosticInfoRef) -> DiagnosticSeverity;

        pub fn LLVMGetMDKindIDInContext(C: ContextRef,
                                        Name: *const c_char,
                                        SLen: c_uint) -> c_uint;

        pub fn LLVMGetMDKindID(Name: *const c_char,
                               SLen: c_uint);
    }
}
<commit_msg>[core] fix Context destructor<commit_after>// Copyright 2015 Jauhien Piatlicki.
//
// Copyright 2012-2015 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Core LLVM: Context
// LLVM-C header Core.h

use core::ContextRef;
use self::ffi::*;

pub struct Context {
    context: ContextRef,
    owned: bool
}

impl Context {
    pub fn new() -> Context {
        let context = unsafe {
            LLVMContextCreate()
        };

        Context{
            context: context,
            owned: false
        }
    }

    pub fn get_global() -> Context {
        let context = unsafe {
            LLVMGetGlobalContext()
        };

        Context{
            context: context,
            owned: true
        }
    }

    pub unsafe fn from_ref(context_ref : ContextRef) -> Context {
        Context{
            context: context_ref,
            owned: false
        }
    }

    pub fn get_ref(&self) -> ContextRef {
        self.context
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                LLVMContextDispose(self.context);
            }
        }
    }
}

pub mod ffi {
    use core::*;
    use libc::{c_char, c_void, c_uint};

    #[link(name = "LLVMCore")]
    extern {
        /**
         * Create a new context.
         *
         * Every call to this function should be paired with a call to
         * LLVMContextDispose() or the context will leak memory.
         */
        pub fn LLVMContextCreate() -> ContextRef;

        /**
         * Obtain the global context instance.
         */
        pub fn LLVMGetGlobalContext() -> ContextRef;

        /**
         * Set the diagnostic handler for this context.
         */
        pub fn LLVMContextSetDiagnosticHandler(C: ContextRef,
                                               Handler: DiagnosticHandler,
                                               DiagnosticContext: *mut c_void);

        /**
         * Set the yield callback function for this context.
         */
        pub fn LLVMContextSetYieldCallback(C: ContextRef,
                                           Handler: YieldCallback,
                                           OpaqueHandle: *mut c_void);

        /**
         * Destroy a context instance.
         *
         * This should be called for every call to LLVMContextCreate() or memory
         * will be leaked.
         */
        pub fn LLVMContextDispose(C: ContextRef);

        /**
         * Return a string representation of the DiagnosticInfo. Use
         * LLVMDisposeMessage to free the string.
         */
        pub fn LLVMGetDiagInfoDescription(DI: DiagnosticInfoRef) -> *const c_char;

        pub fn LLVMGetDiagInfoSeverity(DI: DiagnosticInfoRef) -> DiagnosticSeverity;

        pub fn LLVMGetMDKindIDInContext(C: ContextRef,
                                        Name: *const c_char,
                                        SLen: c_uint) -> c_uint;

        pub fn LLVMGetMDKindID(Name: *const c_char,
                               SLen: c_uint);
    }
}
<|endoftext|>


<commit_before>use std::{cmp, uint};
use extra::bitv::BitvSet;

pub struct Range2D {
    cur: (int, int),
    dv: (int, int),
    cnt: uint
}

impl Range2D {
    #[inline]
    pub fn new((x0, y0): (int, int), (dx, dy): (int, int), (x_min, y_min): (int, int), (x_max, y_max): (int, int)) -> Range2D {
        if dx == 0 && dy == 0 { fail!("Range2D::new called with (dx, dy) == (0, 0)") }

        #[inline]
        fn get_cnt(p0: int, dp: int, min: int, max: int) -> uint {
            if p0 < min || max < p0 { return 0; }
            match dp.cmp(&0) {
                Equal   => uint::max_value,
                Greater => ((max + 1 - p0) / dp) as uint,
                Less    => ((p0 + 1 - min) / (-dp)) as uint
            }
        }

        Range2D {
            cur: (x0, y0),
            dv: (dx, dy),
            cnt: cmp::min(get_cnt(x0, dx, x_min, x_max), get_cnt(y0, dy, y_min, y_max))
        }
    }

    #[inline]
    pub fn new_from_matrix(start: (int, int), dv: (int, int), (w, h): (int, int)) -> Range2D {
        assert!(w > 0 && h > 0);
        Range2D::new(start, dv, (0, 0), (w - 1, h - 1))
    }
}

impl Iterator<(int, int)> for Range2D {
    #[inline(always)]
    fn next(&mut self) -> Option<(int, int)> {
        if self.cnt <= 0 { return None }
        self.cnt -= 1;
        let ((x, y), (dx, dy)) = (self.cur, self.dv);
        self.cur = (x + dx, y + dy);
        Some((x, y))
    }
}

pub struct Comb {
    priv consumed: bool,
    priv size: uint,
    priv set: BitvSet
}

impl Iterator<BitvSet> for Comb {
    fn next(&mut self) -> Option<BitvSet> {
        if !self.consumed {
            self.consumed = true;
            return Some(self.set.clone());
        }

        let mut n = self.size;
        loop {
            match range(0, n).invert().find(|i| self.set.contains(i)) {
                None => return None,
                Some(x) => {
                    if x < self.size - 1 && !self.set.contains(&(x + 1)) {
                        n = x;
                        break;
                    }
                    n = x;
                }
            }
        }
        self.set.remove(&n);
        self.set.insert(n + 1);
        let mut j = n + 2;
        for i in range(n + 2, self.size) {
            if self.set.contains(&i) {
                self.set.remove(&i);
                self.set.insert(j);
                j += 1;
            }
        }
        Some(self.set.clone())
    }
}

impl Comb {
    #[inline]
    pub fn new(cnt: uint, size: uint) -> Comb {
        assert!(cnt <= size);
        let mut set = BitvSet::new();
        for i in range(0, cnt) {
            set.insert(i);
        }
        Comb { consumed: false, size: size, set: set }
    }
}

#[cfg(test)]
mod test {
    use super::{Range2D, Comb};

    #[test]
    fn test_area2d() {
        let vs = Range2D::new((0, 0), (1, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (1, 1), (2, 2), (3, 3)]);

        let vs = Range2D::new((1, 1), (1, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(1, 1), (2, 2), (3, 3)]);

        let vs = Range2D::new((3, 3), (1, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(3, 3)]);

        let vs = Range2D::new((0, 0), (2, 2), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (2, 2)]);

        let vs = Range2D::new((0, 0), (0, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (0, 1), (0, 2), (0, 3)]);

        let vs = Range2D::new((0, 0), (0, 1), (0, 0), (3, 5)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]);

        let vs = Range2D::new((0, 0), (1, 2), (0, 0), (3, 5)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (1, 2), (2, 4)]);

        let vs = Range2D::new((3, 3), (-1, -1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(3, 3), (2, 2), (1, 1), (0, 0)]);

        let vs = Range2D::new((3, 3), (-2, -2), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(3, 3), (1, 1)]);
    }

    #[test]
    fn test_comb() {
        fn check(cnt: uint, size: uint, expected: ~[~[uint]]) {
            let actual = Comb::new(cnt, size)
                .map(|set| set.iter().to_owned_vec())
                .to_owned_vec();
            assert_eq!(actual, expected);
        }
        check(0, 4, ~[~[]]);
        check(1, 4, ~[~[0], ~[1], ~[2], ~[3]]);
        check(2, 4, ~[~[0, 1], ~[0, 2], ~[0, 3],
                      ~[1, 2], ~[1, 3],
                      ~[2, 3]]);
        check(3, 4, ~[~[0, 1, 2], ~[0, 1, 3], ~[0, 2, 3], ~[1, 2, 3]]);
        check(4, 4, ~[~[0, 1, 2, 3]]);

        check(0, 0, ~[~[]]);
        check(0, 1, ~[~[]]);
        check(1, 1, ~[~[0]]);
    }
}
<commit_msg>Refactor data:;extiter::Comb<commit_after>use std::{cmp, uint};
use extra::bitv::BitvSet;

pub struct Range2D {
    cur: (int, int),
    dv: (int, int),
    cnt: uint
}

impl Range2D {
    #[inline]
    pub fn new((x0, y0): (int, int), (dx, dy): (int, int), (x_min, y_min): (int, int), (x_max, y_max): (int, int)) -> Range2D {
        if dx == 0 && dy == 0 { fail!("Range2D::new called with (dx, dy) == (0, 0)") }

        #[inline]
        fn get_cnt(p0: int, dp: int, min: int, max: int) -> uint {
            if p0 < min || max < p0 { return 0; }
            match dp.cmp(&0) {
                Equal   => uint::max_value,
                Greater => ((max + 1 - p0) / dp) as uint,
                Less    => ((p0 + 1 - min) / (-dp)) as uint
            }
        }

        Range2D {
            cur: (x0, y0),
            dv: (dx, dy),
            cnt: cmp::min(get_cnt(x0, dx, x_min, x_max), get_cnt(y0, dy, y_min, y_max))
        }
    }

    #[inline]
    pub fn new_from_matrix(start: (int, int), dv: (int, int), (w, h): (int, int)) -> Range2D {
        assert!(w > 0 && h > 0);
        Range2D::new(start, dv, (0, 0), (w - 1, h - 1))
    }
}

impl Iterator<(int, int)> for Range2D {
    #[inline(always)]
    fn next(&mut self) -> Option<(int, int)> {
        if self.cnt <= 0 { return None }
        self.cnt -= 1;
        let ((x, y), (dx, dy)) = (self.cur, self.dv);
        self.cur = (x + dx, y + dy);
        Some((x, y))
    }
}

pub struct Comb {
    priv consumed: bool,
    priv size: uint,
    priv set: BitvSet
}

impl Iterator<BitvSet> for Comb {
    fn next(&mut self) -> Option<BitvSet> {
        if self.consumed { return None }

        let result = self.set.clone();
        match self.find_change_bit() {
            None => { self.consumed = true }
            Some(n) => {
                self.set.remove(&n);
                self.set.insert(n + 1);

                let mut j = n + 2;
                for i in range(n + 2, self.size) {
                    if self.set.contains(&i) {
                        if i != j {
                            self.set.remove(&i);
                            self.set.insert(j);
                        }
                        j += 1;
                    }
                }
            }
        }
        Some(result)
    }
}

impl Comb {
    #[inline]
    pub fn new(cnt: uint, size: uint) -> Comb {
        assert!(cnt <= size);
        let mut set = BitvSet::new();
        for i in range(0, cnt) {
            set.insert(i);
        }
        Comb { consumed: false, size: size, set: set }
    }

    fn find_change_bit(&self) -> Option<uint> {
        if self.size == 0 { return None }

        for n in range(0, self.size - 1).invert() {
            if self.set.contains(&n) && !self.set.contains(&(n + 1)) {
                return Some(n)
            }
        }
        None
    }
}

#[cfg(test)]
mod test {
    use super::{Range2D, Comb};

    #[test]
    fn area2d() {
        let vs = Range2D::new((0, 0), (1, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (1, 1), (2, 2), (3, 3)]);

        let vs = Range2D::new((1, 1), (1, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(1, 1), (2, 2), (3, 3)]);

        let vs = Range2D::new((3, 3), (1, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(3, 3)]);

        let vs = Range2D::new((0, 0), (2, 2), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (2, 2)]);

        let vs = Range2D::new((0, 0), (0, 1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (0, 1), (0, 2), (0, 3)]);

        let vs = Range2D::new((0, 0), (0, 1), (0, 0), (3, 5)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]);

        let vs = Range2D::new((0, 0), (1, 2), (0, 0), (3, 5)).to_owned_vec();
        assert_eq!(vs, ~[(0, 0), (1, 2), (2, 4)]);

        let vs = Range2D::new((3, 3), (-1, -1), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(3, 3), (2, 2), (1, 1), (0, 0)]);

        let vs = Range2D::new((3, 3), (-2, -2), (0, 0), (3, 3)).to_owned_vec();
        assert_eq!(vs, ~[(3, 3), (1, 1)]);
    }

    #[test]
    fn comb() {
        fn check(cnt: uint, size: uint, expected: ~[~[uint]]) {
            let actual = Comb::new(cnt, size)
                .map(|set| set.iter().to_owned_vec())
                .to_owned_vec();
            assert_eq!(actual, expected);
        }
        check(0, 4, ~[~[]]);
        check(1, 4, ~[~[0], ~[1], ~[2], ~[3]]);
        check(2, 4, ~[~[0, 1], ~[0, 2], ~[0, 3],
                      ~[1, 2], ~[1, 3],
                      ~[2, 3]]);
        check(3, 4, ~[~[0, 1, 2], ~[0, 1, 3], ~[0, 2, 3], ~[1, 2, 3]]);
        check(4, 4, ~[~[0, 1, 2, 3]]);

        check(0, 0, ~[~[]]);
        check(0, 1, ~[~[]]);
        check(1, 1, ~[~[0]]);
    }
}

#[cfg(test)]
mod bench {
    use super::Comb;
    use extra::test::BenchHarness;

    #[bench]
    fn comb(bh: &mut BenchHarness) {
        bh.iter(|| { Comb::new(5, 10).last(); });
    }
}
<|endoftext|>


<commit_before>use super::command;

fn get_all_sshkey_ids() -> String {
    let get_sshkeys_cmd = "doctl compute ssh-key list --no-header --format=ID";
    println!("Running command:\n\t\t{}", get_sshkeys_cmd);
    let result = command::run_host_cmd(&get_sshkeys_cmd);
    if !result.success {
        println!("Failed to get sshkeys with stderr:\n\n{}", result.stderr);
    }
    let ids : Vec<&str> = result.stdout.lines().collect();
    return ids.join(",");
}

pub fn create_droplet_by_name(name: &str) {
    let create_str = format!("doctl compute droplet create {} --image=ubuntu-16-04-x64 --region=sfo1 --size=512mb --ssh-keys=\"{}\" --wait", name, get_all_sshkey_ids());
    println!("Running command:\n\t\t{}", create_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_str);
    if !result.success {
        println!("Failed, with stderr:\n\n{}", result.stderr);
        return;
    }

    let list_command = "doctl compute droplet list --format Name,PublicIPv4,PublicIPv6,Status";
    println!("Running command:\n\t\t{}", list_command);
    let droplet_result = command::run_host_cmd(&list_command);
    if !droplet_result.success{
        println!("Failed, with stderr:\n\n{}", droplet_result.stderr);
        return;
    }
    let lines = droplet_result.stdout.lines();
    let mut ip_address = "";
    for line in lines {
        if line.starts_with(name) {
            println!("Found: {}", line);
            let fields : Vec<&str> = line.split_whitespace().collect();
            if fields.len() < 2 {
                println!("Couldn't find ip address in line: {}", line);
                return;
            }
            ip_address = fields[1];
            break;
        }
    }
    if ip_address.len() == 0 {
        println!("Couldn't locate droplet in output:\n\n{}", droplet_result.stdout);
        return;
    }
    // Create a DNS record to point to the droplet
    let record_str = format!("doctl compute domain records create one.haus --record-type=A --record-data={} --record-name={}", ip_address, name);
    println!("Creating DNS record:\n\t\t{}", record_str);
    let create_record_result = command::run_host_cmd(&record_str);
    if !create_record_result.success{
        println!("Failed, with stderr:\n\n{}", create_record_result.stderr);
    }
}

pub fn destroy_droplet_by_name(name: &str) {
    let create_str = format!("doctl compute droplet delete -f {}", name);
    println!("Running command:\n\t\t{}", create_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_str);
    if !result.success {
        println!("Failed with stderr:\n\n{}", result.stderr);
        return
    }
    let get_record_cmd = "doctl compute domain records list one.haus --format Name,ID --no-header";
    // Get A record and delete it!
    let record_result = command::run_host_cmd(&get_record_cmd);
    if !record_result.success {
        println!("Failed with stderr:\n\n{}", record_result.stderr);
        return
    }
    let lines = record_result.stdout.lines();
    let mut record_to_delete = "";
    for line in lines {
        let fields : Vec<&str> = line.split_whitespace().collect();
        if fields.len() < 2 {
            println!("Invalid output line: {}", line);
            continue;
        }
        if fields[0].starts_with(name) {
            println!("Found: {}", line);
            record_to_delete = fields[1];
            break;
        }
    }
    if record_to_delete.len() == 0 {
        println!("Couldn't locate record for [{}] in output:\n\n{}", name, record_result.stdout);
        return;
    }
    // Create a DNS record to point to the droplet
    let delete_record_cmd = format!("doctl compute domain records delete one.haus {}", record_to_delete);
    println!("Deleting DNS record:\n\t\t{}", delete_record_cmd);
    let delete_result = command::run_host_cmd(&delete_record_cmd);
    if !delete_result.success {
        println!("Failed with stderr:\n\n{}", delete_result.stderr);
        return
    }
}

pub fn create_sshkey(name: &str) {
    // By default, always attempt to add a new key with [name] mapping to ~/.ssh/id_rsa.pub
    let create_key_str = format!("doctl compute ssh-key create {} --public-key=\"$(cat ~/.ssh/id_rsa.pub)\"", name);
    println!("Running command:\n\t\t{}", create_key_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_key_str);
    if !result.success {
        println!("Failed with stderr:\n\n{}", result.stderr);
    }
}
<commit_msg>port create_droplet to command chain<commit_after>use super::command;
use super::chain;

pub fn create_droplet_by_name(name: &str) {

    let ssh_key_mapping_func = |res: &command::Result, cmd_str: String| -> String {
        let ids : Vec<&str> = res.stdout.lines().collect();
        let new_cmd = str::replace(&cmd_str, "%ssh_keys%", &ids.join(","));
        new_cmd.to_string()
    };

    let ip_address_mapping_func = |res: &command::Result, cmd_str: String| -> String {
        let new_cmd = str::replace(&cmd_str, "%ip_address%", &res.stdout);
        new_cmd.to_string()
    };

    // Get ip address from stdout
    let ip_address_extractor = |res: &command::Result| -> command::Result {
        let mut ip_address : Option<String> = None;
        let res_stdout = res.stdout.clone();
        let lines = res_stdout.lines();
        for line in lines {
            if line.starts_with(name) {
                debug!("Found: {}", line);
                let fields : Vec<&str> = line.split_whitespace().collect();
                if fields.len() < 2 {
                    warn!("Couldn't find ip address in line: {}", line);
                }
                ip_address = Some(fields[1].to_string());
                break;
            }
        }
        if ip_address == None {
            error!("Couldn't locate droplet in output: {}", res_stdout);
            return command::Result {
                exit_code: None,
                success: false,
                stdout: "".to_string(),
                stderr: res.stderr.clone()
            }
        }

        command::Result {
            exit_code: res.exit_code,
            success: res.success,
            // We check for None above
            stdout: ip_address.unwrap().trim().to_string(),
            stderr: res.stderr.clone()
        }
    };

    let create_str = format!("doctl compute droplet create {} --image=ubuntu-16-04-x64 --region=sfo1 --size=512mb --ssh-keys=\"%ssh_keys%\" --wait", name);
    let record_str = format!("doctl compute domain records create one.haus --record-type=A --record-data=%ip_address% --record-name={}", name);

    let chain = chain::CommandChain::new()
        .cmd("doctl compute ssh-key list --no-header --format=ID")
        .result_mapped_cmd(&ssh_key_mapping_func, &create_str)
        .cmd("doctl compute droplet list --format Name,PublicIPv4,PublicIPv6,Status")
        .result_proc(&ip_address_extractor)
        .result_mapped_cmd(&ip_address_mapping_func, &record_str)
        .execute();
}

pub fn destroy_droplet_by_name(name: &str) {
    let create_str = format!("doctl compute droplet delete -f {}", name);
    println!("Running command:\n\t\t{}", create_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_str);
    if !result.success {
        println!("Failed with stderr:\n\n{}", result.stderr);
        return
    }
    let get_record_cmd = "doctl compute domain records list one.haus --format Name,ID --no-header";
    // Get A record and delete it!
    let record_result = command::run_host_cmd(&get_record_cmd);
    if !record_result.success {
        println!("Failed with stderr:\n\n{}", record_result.stderr);
        return
    }
    let lines = record_result.stdout.lines();
    let mut record_to_delete = "";
    for line in lines {
        let fields : Vec<&str> = line.split_whitespace().collect();
        if fields.len() < 2 {
            println!("Invalid output line: {}", line);
            continue;
        }
        if fields[0].starts_with(name) {
            println!("Found: {}", line);
            record_to_delete = fields[1];
            break;
        }
    }
    if record_to_delete.len() == 0 {
        println!("Couldn't locate record for [{}] in output:\n\n{}", name, record_result.stdout);
        return;
    }
    // Create a DNS record to point to the droplet
    let delete_record_cmd = format!("doctl compute domain records delete one.haus {}", record_to_delete);
    println!("Deleting DNS record:\n\t\t{}", delete_record_cmd);
    let delete_result = command::run_host_cmd(&delete_record_cmd);
    if !delete_result.success {
        println!("Failed with stderr:\n\n{}", delete_result.stderr);
        return
    }
}

pub fn create_sshkey(name: &str) {
    // By default, always attempt to add a new key with [name] mapping to ~/.ssh/id_rsa.pub
    let create_key_str = format!("doctl compute ssh-key create {} --public-key=\"$(cat ~/.ssh/id_rsa.pub)\"", name);
    println!("Running command:\n\t\t{}", create_key_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_key_str);
    if !result.success {
        println!("Failed with stderr:\n\n{}", result.stderr);
    }
}
<|endoftext|>


<commit_before>extern crate fitsio;
extern crate tempdir;

/* This example docuents the following things:
 *
 * creating a new file
 * writing header keys
 * writing an image into the primary hdu
 * creating a table
 * adding data to a table
 * closing the file
 * re-opening it
 * reading an image region
 * reading some rows from a table column
 */

use std::error::Error;
use tempdir::TempDir;
use fitsio::FitsFile;
use fitsio::fitsfile::ImageDescription;
use fitsio::types::ImageType;
use fitsio::columndescription::{ColumnDataType, ColumnDescription};

fn run() -> Result<(), Box<Error>> {
    /* Create a temporary directory to work from */
    let tmp_dir = TempDir::new("fitsio")?;
    let file_path = tmp_dir.path().join("example.fits");

    // creating a new file with 512 rows and 1024 columns
    let primary_hdu_description = ImageDescription {
        data_type: ImageType::Double,
        dimensions: &[512, 1024],
    };

    {
        let mut fitsfile = FitsFile::create(&file_path)
            .with_custom_primary(&primary_hdu_description)
            .open()?;

        /* We will now add some dummy header keys. We add:
         * - the name of the project (String)
         * - the exposure time (f32)
         * - the image id (i64)
         */

        /* First we get the primary HDU */
        let hdu = fitsfile.primary_hdu()?;

        /* Now we add the header keys */
        hdu.write_key(&mut fitsfile, "PROJECT", "My First Astronomy Project")?;

        /* Now the exposure time */
        hdu.write_key(&mut fitsfile, "EXPTIME", 15.2f32)?;

        /* And finally the image id */
        hdu.write_key(&mut fitsfile, "IMAGE_ID", 20180101010005i64)?;

        /* Now we write some dummy data to the primary HDU. We write the full image in this case.
         * */

        let dummy_data: Vec<f32> = (0..(1024 * 512))
            .map(|val| (val as f32) * 12.5 + 102.5)
            .collect();

        hdu.write_image(&mut fitsfile, &dummy_data)?;

        /* We can create a new image with the following */
        let image_description = ImageDescription {
            data_type: ImageType::Long,
            dimensions: &[256, 256],
        };
        fitsfile.create_image("IMG", &image_description)?;

        /* Now we add a new table HDU called "DATA"
         *
         * We add three columns:
         *
         * 1. Object id (i32)
         * 2. Object name (String, up to 10 characters)
         * 3. Object magnitude (f32)
         * */
        let col1 = ColumnDescription::new("OBJ_ID")
            .with_type(ColumnDataType::Int)
            .create()?;
        let col2 = ColumnDescription::new("NAME")
            .with_type(ColumnDataType::String)
            .that_repeats(10)
            .create()?;
        let col3 = ColumnDescription::new("MAG")
            .with_type(ColumnDataType::Float)
            .create()?;
        let columns = &[col1, col2, col3];
        let table_hdu = fitsfile.create_table("DATA", columns)?;

        /* Add some data to the columns */
        let n_rows = 10;
        let obj_id_data: Vec<i32> = (0..n_rows).collect();
        let name_data: Vec<String> = (0..n_rows).map(|idx| format!("N{}", idx)).collect();
        let mag_data: Vec<f32> = (0..n_rows).map(|idx| -0.2 * idx as f32).collect();

        table_hdu.write_col(&mut fitsfile, "OBJ_ID", &obj_id_data)?;
        table_hdu.write_col(&mut fitsfile, "NAME", &name_data)?;
        table_hdu.write_col(&mut fitsfile, "MAG", &mag_data)?;

        /* `fitsfile` is dropped here at the end of the scope, which closes fhe file on disk */
    }

    /* Here we re-open the file. We want to adjust things, so we use the `edit` method */
    let mut fitsfile = FitsFile::edit(&file_path)?;

    /* Print the fits file contents to stdout */
    fitsfile.pretty_print().expect("printing fits file");

    /* Get the primary HDU and read a section of the image data */
    let phdu = fitsfile.primary_hdu()?;

    /* Let's say we have a region around a star that we want to extract. The star is at (25, 25,
     * 1-indexed) and we want to extract a 5x5 box around it. This means we want to read rows 19 to
     * 19, and columns 19 to 29 (0-indexed). The range arguments are exclusive of the upper bound,
     * so we must use 19..29 for each axis.
     *
     * Note: this is not performant, and if this is the desired use of `fitsio`, I would not
     * suggest this approach. */
    let image_data: Vec<f32> = phdu.read_region(&mut fitsfile, &[&(19..29), &(19..29)])?;
    assert_eq!(image_data.len(), 100);

    /* We can now get the column data. Let's assume we want all of the magnitudes for objects near
     * this one (by index). This object is at index 4 (0-indexed) and we want to read one value
     * before and after it. */
    let table_hdu = fitsfile.hdu("DATA")?;
    let magnitudes: Vec<f32> = table_hdu.read_col_range(&mut fitsfile, "MAG", &(3..6))?;
    assert_eq!(magnitudes.len(), 3);

    /* Read a single row from the file */
    // TODO

    /* The file is closed when it is dropped here */

    Ok(())
}

fn main() {
    run().unwrap();
}
<commit_msg>Update example to include reading fits row<commit_after>extern crate fitsio;
#[macro_use]
extern crate fitsio_derive;
extern crate tempdir;

/* This example docuents the following things:
 *
 * creating a new file
 * writing header keys
 * writing an image into the primary hdu
 * creating a table
 * adding data to a table
 * closing the file
 * re-opening it
 * reading an image region
 * reading some rows from a table column
 */

use std::error::Error;
use tempdir::TempDir;
use fitsio::FitsFile;
use fitsio::fitsfile::{FitsRow, ImageDescription};
use fitsio::types::ImageType;
use fitsio::columndescription::{ColumnDataType, ColumnDescription};

fn run() -> Result<(), Box<Error>> {
    /* Create a temporary directory to work from */
    let tmp_dir = TempDir::new("fitsio")?;
    let file_path = tmp_dir.path().join("example.fits");

    // creating a new file with 512 rows and 1024 columns
    let primary_hdu_description = ImageDescription {
        data_type: ImageType::Double,
        dimensions: &[512, 1024],
    };

    {
        let mut fitsfile = FitsFile::create(&file_path)
            .with_custom_primary(&primary_hdu_description)
            .open()?;

        /* We will now add some dummy header keys. We add:
         * - the name of the project (String)
         * - the exposure time (f32)
         * - the image id (i64)
         */

        /* First we get the primary HDU */
        let hdu = fitsfile.primary_hdu()?;

        /* Now we add the header keys */
        hdu.write_key(&mut fitsfile, "PROJECT", "My First Astronomy Project")?;

        /* Now the exposure time */
        hdu.write_key(&mut fitsfile, "EXPTIME", 15.2f32)?;

        /* And finally the image id */
        hdu.write_key(&mut fitsfile, "IMAGE_ID", 20180101010005i64)?;

        /* Now we write some dummy data to the primary HDU. We write the full image in this case.
         * */

        let dummy_data: Vec<f32> = (0..(1024 * 512))
            .map(|val| (val as f32) * 12.5 + 102.5)
            .collect();

        hdu.write_image(&mut fitsfile, &dummy_data)?;

        /* We can create a new image with the following */
        let image_description = ImageDescription {
            data_type: ImageType::Long,
            dimensions: &[256, 256],
        };
        fitsfile.create_image("IMG", &image_description)?;

        /* Now we add a new table HDU called "DATA"
         *
         * We add three columns:
         *
         * 1. Object id (i32)
         * 2. Object name (String, up to 10 characters)
         * 3. Object magnitude (f32)
         * */
        let col1 = ColumnDescription::new("OBJ_ID")
            .with_type(ColumnDataType::Int)
            .create()?;
        let col2 = ColumnDescription::new("NAME")
            .with_type(ColumnDataType::String)
            .that_repeats(10)
            .create()?;
        let col3 = ColumnDescription::new("MAG")
            .with_type(ColumnDataType::Float)
            .create()?;
        let columns = &[col1, col2, col3];
        let table_hdu = fitsfile.create_table("DATA", columns)?;

        /* Add some data to the columns */
        let n_rows = 10;
        let obj_id_data: Vec<i32> = (0..n_rows).collect();
        let name_data: Vec<String> = (0..n_rows).map(|idx| format!("N{}", idx)).collect();
        let mag_data: Vec<f32> = (0..n_rows).map(|idx| -0.2 * idx as f32).collect();

        table_hdu.write_col(&mut fitsfile, "OBJ_ID", &obj_id_data)?;
        table_hdu.write_col(&mut fitsfile, "NAME", &name_data)?;
        table_hdu.write_col(&mut fitsfile, "MAG", &mag_data)?;

        /* `fitsfile` is dropped here at the end of the scope, which closes fhe file on disk */
    }

    /* Here we re-open the file. We want to adjust things, so we use the `edit` method */
    let mut fitsfile = FitsFile::edit(&file_path)?;

    /* Print the fits file contents to stdout */
    fitsfile.pretty_print().expect("printing fits file");

    /* Get the primary HDU and read a section of the image data */
    let phdu = fitsfile.primary_hdu()?;

    /* Let's say we have a region around a star that we want to extract. The star is at (25, 25,
     * 1-indexed) and we want to extract a 5x5 box around it. This means we want to read rows 19 to
     * 19, and columns 19 to 29 (0-indexed). The range arguments are exclusive of the upper bound,
     * so we must use 19..29 for each axis.
     *
     * Note: this is not performant, and if this is the desired use of `fitsio`, I would not
     * suggest this approach. */
    let image_data: Vec<f32> = phdu.read_region(&mut fitsfile, &[&(19..29), &(19..29)])?;
    assert_eq!(image_data.len(), 100);

    /* We can now get the column data. Let's assume we want all of the magnitudes for objects near
     * this one (by index). This object is at index 4 (0-indexed) and we want to read one value
     * before and after it. */
    let table_hdu = fitsfile.hdu("DATA")?;
    let magnitudes: Vec<f32> = table_hdu.read_col_range(&mut fitsfile, "MAG", &(3..6))?;
    assert_eq!(magnitudes.len(), 3);

    /* Read a single row from the file. Columns can be renamed to allow for more rustic attribute
     * names */
    #[derive(Default, FitsRow)]
    struct Row {
        #[fitsio(colname = "OBJ_ID")]
        obj_id: i32,
        #[fitsio(colname = "NAME")]
        name: String,
        #[fitsio(colname = "MAG")]
        mag: f32,
    }
    let row: Row = table_hdu.row(&mut fitsfile, 4)?;
    assert_eq!(row.obj_id, 4);
    assert_eq!(row.name, "N4");
    assert!(row.mag > -1.0 && row.mag < -0.5);

    /* The file is closed when it is dropped here */

    Ok(())
}

fn main() {
    run().unwrap();
}
<|endoftext|>


<commit_before>
#[derive(Debug)]
#[repr(C)]
pub struct ElfSectionsTag {
    typ: u32,
    size: u32,
    pub number_of_sections: u32,
    entry_size: u32,
    shndx: u32, // string table
    first_section: ElfSection,
}

impl ElfSectionsTag {
    pub fn sections(&'static self) -> ElfSectionIter {
        ElfSectionIter {
            current_section: &self.first_section,
            remaining_sections: self.number_of_sections - 1,
            entry_size: self.entry_size,
        }
    }
}

#[derive(Clone)]
pub struct ElfSectionIter {
    current_section: &'static ElfSection,
    remaining_sections: u32,
    entry_size: u32,
}

impl Iterator for ElfSectionIter {
    type Item = &'static ElfSection;
    fn next(&mut self) -> Option<&'static ElfSection> {
        if self.remaining_sections == 0 {
            None
        } else {
            let section = self.current_section;
            let next_section_addr = (self.current_section as *const _ as u32) + self.entry_size;
            self.current_section = unsafe{ &*(next_section_addr as *const ElfSection) };
            self.remaining_sections -= 1;
			if section.typ == ElfSectionType::Unused as u32 {
				self.next()
			} else {
	            Some(section)
			}
        }
    }
}

#[derive(Debug)]
#[repr(C)]
pub struct ElfSection {
    name: u32,
    typ: u32,
    pub flags: u64,
    pub addr: u64,
    offset: u64,
    pub size: u64,
    link: u32,
    info: u32,
    addralign: u64,
    entry_size: u64,
}

#[repr(u32)]
pub enum ElfSectionType {
    Unused = 0,
    ProgramSection = 1,
    LinkerSymbolTable = 2,
    StringTable = 3,
    RelaRelocation = 4,
    SymbolHashTable = 5,
    DynamicLinkingTable = 6,
    Note = 7,
    Uninitialized = 8,
    RelRelocation = 9,
    Reserved = 10,
    DynamicLoaderSymbolTable = 11,
    // plus environment-specific use from 0x60000000 to 0x6FFFFFFF
    // plus processor-specific use from 0x70000000 to 0x7FFFFFFF
}

#[repr(u32)]
pub enum ElfSectionFlags {
    Writable = 0x1,
    Allocated = 0x2,
    Executable = 0x4,
    // plus environment-specific use at 0x0F000000
    // plus processor-specific use at 0xF0000000
}
<commit_msg>Fix bug caused by wrong alignment<commit_after>
#[derive(Debug)]
#[repr(packed)] // repr(C) would add unwanted padding before first_section
pub struct ElfSectionsTag {
    typ: u32,
    size: u32,
    pub number_of_sections: u32,
    entry_size: u32,
    shndx: u32, // string table
    first_section: ElfSection,
}

impl ElfSectionsTag {
    pub fn sections(&'static self) -> ElfSectionIter {
        ElfSectionIter {
            current_section: &self.first_section,
            remaining_sections: self.number_of_sections - 1,
            entry_size: self.entry_size,
        }
    }
}

#[derive(Clone)]
pub struct ElfSectionIter {
    current_section: &'static ElfSection,
    remaining_sections: u32,
    entry_size: u32,
}

impl Iterator for ElfSectionIter {
    type Item = &'static ElfSection;
    fn next(&mut self) -> Option<&'static ElfSection> {
        if self.remaining_sections == 0 {
            None
        } else {
            let section = self.current_section;
            let next_section_addr = (self.current_section as *const _ as u32) + self.entry_size;
            self.current_section = unsafe{ &*(next_section_addr as *const ElfSection) };
            self.remaining_sections -= 1;
			if section.typ == ElfSectionType::Unused as u32 {
				self.next()
			} else {
	            Some(section)
			}
        }
    }
}

#[derive(Debug)]
#[repr(C)]
pub struct ElfSection {
    name: u32,
    typ: u32,
    pub flags: u64,
    pub addr: u64,
    offset: u64,
    pub size: u64,
    link: u32,
    info: u32,
    addralign: u64,
    entry_size: u64,
}

#[repr(u32)]
pub enum ElfSectionType {
    Unused = 0,
    ProgramSection = 1,
    LinkerSymbolTable = 2,
    StringTable = 3,
    RelaRelocation = 4,
    SymbolHashTable = 5,
    DynamicLinkingTable = 6,
    Note = 7,
    Uninitialized = 8,
    RelRelocation = 9,
    Reserved = 10,
    DynamicLoaderSymbolTable = 11,
    // plus environment-specific use from 0x60000000 to 0x6FFFFFFF
    // plus processor-specific use from 0x70000000 to 0x7FFFFFFF
}

#[repr(u32)]
pub enum ElfSectionFlags {
    Writable = 0x1,
    Allocated = 0x2,
    Executable = 0x4,
    // plus environment-specific use at 0x0F000000
    // plus processor-specific use at 0xF0000000
}
<|endoftext|>


<commit_before>use super::memory::{self, address, Memory};
use super::memory::mappedmemory::MemoryToken;
use super::instruction::Instruction;

pub struct Emulator<'a, T, U>
    where T: Memory,
          U: Memory
{
    image_memory: MemoryToken<T>,
    bios_memory: MemoryToken<U>,
    controls_memory: memory::mappedmemory::MemoryToken<memory::SliceMemory>,
    pub memory: memory::MappedMemory<'a>,
    pub ip: usize,
    bios_mounted: bool,
}

impl<'a, T, U> Emulator<'a, T, U>
    where T: 'a + Memory,
          U: 'a + Memory
{
    pub fn new(img: T, bios: U) -> Emulator<'a, T, U> {
        let mut memory = memory::MappedMemory::new();
        let nm = MemoryToken::new(memory::NullMemory::new());
        memory.mount(0, &nm);

        let bios_memory = memory::mappedmemory::MemoryToken::new(bios);
        let image_memory = memory::mappedmemory::MemoryToken::new(img);
        memory.mount(0, &image_memory);
        memory.mount(address::BIOS_START_ADDRESS, &bios_memory);

        let controls_memory =
            MemoryToken::new(memory::SliceMemory::new(address::MAX_ADDRESS -
                                                      address::FLAGS_START_ADDRESS +
                                                      1));
        memory.mount(address::FLAGS_START_ADDRESS, &controls_memory);

        Emulator {
            memory: memory,
            image_memory: image_memory,
            bios_memory: bios_memory,
            ip: address::BIOS_START_ADDRESS,
            controls_memory: controls_memory,
            bios_mounted: true,
        }
    }

    pub fn flag_is_set(&self, flag_idx: usize) -> bool {
        let bit = flag_idx % 32;
        self.memory.get(address::FLAGS_START_ADDRESS + flag_idx / 32) & (1 << bit) != 0
    }

    pub fn get_register(&self, reg_idx: usize) -> u32 {
        self.memory.get(address::REGISTERS_START_ADDRESS + reg_idx * 4)
    }

    pub fn step(&mut self) {
        let instr = Instruction::from_memory(self.ip, &self.memory);
        instr.execute(&mut self.ip, &mut self.memory);

        self.check_bios_mount()
    }

    fn check_bios_mount(&mut self) {
        if self.flag_is_set(address::FLAG0_BIOS_DONE) && self.bios_mounted {
            self.memory.unmount(&self.bios_memory);
            self.bios_mounted = false;
        } else if !self.flag_is_set(address::FLAG0_BIOS_DONE) && !self.bios_mounted {
            self.memory.mount(address::BIOS_START_ADDRESS, &self.bios_memory);
            self.bios_mounted = true;
        }
    }

    pub fn is_halted(&self) -> bool {
        self.flag_is_set(address::FLAG0_HALTED)
    }
}

#[cfg(test)]
mod tests {
    use memory::{SliceMemory, NullMemory, address, Memory};

    #[test]
    fn unmounts_bios() {
        let bios = SliceMemory::from_slice(Box::new(
                                               [address::BIOS_START_ADDRESS as u32 + 16,
                                                 address::BIOS_START_ADDRESS as u32 + 20,
                                                 address::FLAGS_START_ADDRESS as u32,
                                                 0,

                                                 2,
                                                 0,
                                                 0,
                                                 0]));
        let mut emu = super::Emulator::new(NullMemory::new(), bios);

        assert!(!emu.flag_is_set(address::FLAG0_BIOS_DONE));
        assert_eq!(emu.memory.get(address::BIOS_START_ADDRESS + 16), 2);
        emu.step();
        assert!(emu.flag_is_set(address::FLAG0_BIOS_DONE));
        assert_eq!(emu.memory.get(address::BIOS_START_ADDRESS + 16), 0);
    }

    #[test]
    fn is_halted() {
        let bios = SliceMemory::from_slice(Box::new(
                                               [address::BIOS_START_ADDRESS as u32 + 16,
                                                 address::BIOS_START_ADDRESS as u32 + 20,
                                                 address::FLAGS_START_ADDRESS as u32,
                                                 0,

                                                 1,
                                                 0,
                                                 0,
                                                 0]));
        let mut emu = super::Emulator::new(NullMemory::new(), bios);

        assert!(!emu.is_halted());
        emu.step();
        assert!(emu.is_halted());
    }

    #[test]
    fn get_register() {
        let mut emu = super::Emulator::new(NullMemory::new(), NullMemory::new());
        emu.memory.set(address::REGISTERS_START_ADDRESS + 1, 101);
        emu.memory.set(address::REGISTERS_START_ADDRESS, 100);
        assert_eq!(emu.get_register(1), 101);
        assert_eq!(emu.get_register(0), 100);
    }
}
<commit_msg>refactor(emulator): Update for new memory design<commit_after>use super::memory::{self, address, Memory};
use super::memory::mappedmemory::MemoryToken;
use super::instruction::Instruction;

pub struct Emulator<'a, T, U>
    where T: Memory,
          U: Memory
{
    image_memory: MemoryToken<T>,
    bios_memory: MemoryToken<U>,
    controls_memory: memory::mappedmemory::MemoryToken<memory::SliceMemory>,
    pub memory: memory::MappedMemory<'a>,
    pub ip: usize,
    bios_mounted: bool,
}

impl<'a, T, U> Emulator<'a, T, U>
    where T: 'a + Memory,
          U: 'a + Memory
{
    pub fn new(img: T, bios: U) -> Emulator<'a, T, U> {
        let mut memory = memory::MappedMemory::new();
        let nm = MemoryToken::new(memory::NullMemory::new());
        memory.mount(0, &nm);

        let bios_memory = memory::mappedmemory::MemoryToken::new(bios);
        let image_memory = memory::mappedmemory::MemoryToken::new(img);
        memory.mount(0, &image_memory);
        memory.mount(address::BIOS_START_ADDRESS, &bios_memory);

        let controls_memory =
            MemoryToken::new(memory::SliceMemory::new(address::MAX_ADDRESS -
                                                      address::CONTROLS_ADDRESS +
                                                      1));
        memory.mount(address::CONTROLS_ADDRESS, &controls_memory);

        Emulator {
            memory: memory,
            image_memory: image_memory,
            bios_memory: bios_memory,
            ip: address::BIOS_START_ADDRESS,
            controls_memory: controls_memory,
            bios_mounted: true,
        }
    }

    pub fn flag_is_set(&self, flag_idx: usize) -> bool {
        let bit = flag_idx % 32;
        self.memory.get(address::FLAGS_START_ADDRESS + flag_idx / 32) & (1 << bit) != 0
    }

    pub fn get_register(&self, reg_idx: usize) -> u32 {
        self.memory.get(address::REGISTERS_START_ADDRESS + reg_idx)
    }

    pub fn step(&mut self) {
        let instr = Instruction::from_memory(self.ip, &self.memory);
        instr.execute(&mut self.ip, &mut self.memory);

        self.check_bios_mount()
    }

    fn check_bios_mount(&mut self) {
        if self.flag_is_set(address::FLAG0_BIOS_DONE) && self.bios_mounted {
            self.memory.unmount(&self.bios_memory);
            self.bios_mounted = false;
        } else if !self.flag_is_set(address::FLAG0_BIOS_DONE) && !self.bios_mounted {
            self.memory.mount(address::BIOS_START_ADDRESS, &self.bios_memory);
            self.bios_mounted = true;
        }
    }

    pub fn is_halted(&self) -> bool {
        self.flag_is_set(address::FLAG0_HALTED)
    }
}

#[cfg(test)]
mod tests {
    use memory::{SliceMemory, NullMemory, address, Memory};

    #[test]
    fn unmounts_bios() {
        let bios = SliceMemory::from_slice(Box::new(
                                               [address::BIOS_START_ADDRESS as u32 + 4,
                                                 address::BIOS_START_ADDRESS as u32 + 5,
                                                 address::FLAGS_START_ADDRESS as u32,
                                                 0,

                                                 2,
                                                 0,
                                                 0,
                                                 0]));
        let mut emu = super::Emulator::new(NullMemory::new(), bios);

        assert!(!emu.flag_is_set(address::FLAG0_BIOS_DONE));
        assert_eq!(emu.memory.get(address::BIOS_START_ADDRESS + 4), 2);
        emu.step();
        assert!(emu.flag_is_set(address::FLAG0_BIOS_DONE));
        assert_eq!(emu.memory.get(address::BIOS_START_ADDRESS + 4), 0);
    }

    #[test]
    fn is_halted() {
        let bios = SliceMemory::from_slice(Box::new(
                                               [address::BIOS_START_ADDRESS as u32 + 4,
                                                 address::BIOS_START_ADDRESS as u32 + 5,
                                                 address::FLAGS_START_ADDRESS as u32,
                                                 0,

                                                 1,
                                                 0,
                                                 0,
                                                 0]));
        let mut emu = super::Emulator::new(NullMemory::new(), bios);

        assert!(!emu.is_halted());
        emu.step();
        assert!(emu.is_halted());
    }

    #[test]
    fn get_register() {
        let mut emu = super::Emulator::new(NullMemory::new(), NullMemory::new());
        emu.memory.set(address::REGISTERS_START_ADDRESS + 1, 101);
        emu.memory.set(address::REGISTERS_START_ADDRESS, 100);
        assert_eq!(emu.get_register(1), 101);
        assert_eq!(emu.get_register(0), 100);
    }
}
<|endoftext|>