<commit_before>//! Provides a testing environment for the `BeaconChain`, `Attester` and `BlockProposer` objects.
//!
//! This environment bypasses networking and client run-times and connects the `Attester` and `Proposer`
//! directly to the `BeaconChain` via an `Arc`.
//!
//! The `BeaconChainHarness` contains a single `BeaconChain` instance and many `ValidatorHarness`
//! instances. All of the `ValidatorHarness` instances work to advance the `BeaconChain` by
//! producing blocks and attestations.
//!
//! Example:
//! ```
//! use test_harness::BeaconChainHarness;
//! use types::ChainSpec;
//!
//! let validator_count = 8;
//! let spec = ChainSpec::few_validators();
//!
//! let mut harness = BeaconChainHarness::new(spec, validator_count, None, true);
//!
//! harness.advance_chain_with_block();
//!
//! let chain = harness.chain_dump().unwrap();
//!
//! // One block should have been built on top of the genesis block.
//! assert_eq!(chain.len(), 2);
//! ```

mod beacon_chain_harness;
pub mod test_case;
mod validator_harness;

pub use self::beacon_chain_harness::BeaconChainHarness;
pub use self::validator_harness::ValidatorHarness;
<commit_msg>Fix failing doc test<commit_after>//! Provides a testing environment for the `BeaconChain`, `Attester` and `BlockProposer` objects.
//!
//! This environment bypasses networking and client run-times and connects the `Attester` and `Proposer`
//! directly to the `BeaconChain` via an `Arc`.
//!
//! The `BeaconChainHarness` contains a single `BeaconChain` instance and many `ValidatorHarness`
//! instances. All of the `ValidatorHarness` instances work to advance the `BeaconChain` by
//! producing blocks and attestations.
//!
//! Example:
//! ```
//! use test_harness::BeaconChainHarness;
//! use types::ChainSpec;
//!
//! let validator_count = 8;
//! let spec = ChainSpec::few_validators();
//!
//! let mut harness = BeaconChainHarness::new(spec, validator_count);
//!
//! harness.advance_chain_with_block();
//!
//! let chain = harness.chain_dump().unwrap();
//!
//! // One block should have been built on top of the genesis block.
//! assert_eq!(chain.len(), 2);
//! ```

mod beacon_chain_harness;
pub mod test_case;
mod validator_harness;

pub use self::beacon_chain_harness::BeaconChainHarness;
pub use self::validator_harness::ValidatorHarness;
<|endoftext|>


<commit_before>use database::models::{AutoReply, NewAutoReply};
use filters::Filter;

use discord::model::{permissions, UserId, ChannelId, LiveServer};

use diesel;
use diesel::prelude::*;

use lalafell::error::*;
use lalafell::commands::prelude::*;
use lalafell::commands::ChannelOrId;

#[derive(Debug, Deserialize)]
pub struct DeleteParams {
  id: i32
}

fn list_all() -> Result<String> {
  let ars: Vec<AutoReply> = ::bot::CONNECTION.with(|c| {
    use database::schema::auto_replies::dsl;
    dsl::auto_replies.load(c).chain_err(|| "could not load auto_replies")
  })?;
  Ok(ars.iter()
    .map(|r| format!("{id}. Replying to messages in {channel}{filters} with a delay of {delay} second{plural}.\n```{message}```",
                     id = r.id,
                     channel = ChannelId(*r.channel_id).mention(),
                     filters = r.filters.as_ref().map(|f| format!(" with filters `{}`", f)).unwrap_or_default(),
                     delay = r.delay,
                     plural = if r.delay == 1 { "" } else { "s" },
                     message = r.message
    ))
    .collect::<Vec<_>>()
    .join("\n"))
}

pub fn auto_reply<'a>(author: UserId, server: &LiveServer, content: &str) -> CommandResult<'a> {
  if !server.permissions_for(server.id.main(), author).contains(permissions::MANAGE_MESSAGES) {
    return Err(ExternalCommandFailure::default()
      .message(|e: EmbedBuilder| e
        .title("Not enough permissions.")
        .description("You don't have enough permissions to use this command."))
      .wrap());
  }
  let args: Vec<&str> = content.split(|c| c == ' ' || c == '\t').filter(|x| !x.is_empty()).skip(3).collect();
  if args.is_empty() {
    return Ok(list_all()?.into());
  }
  let subcommand = &args[0];
  let args = &args[1..];
  match subcommand.to_lowercase().as_str() {
    "add" | "create" => {
      if args.len() < 4 {
        return Err("Invalid parameters.".into());
      }
      let channel = ChannelOrId::parse(args[0]).map_err(|_| into!(CommandFailure, "Invalid channel reference."))?;
      let on_join = match args[1].to_lowercase().as_str() {
        "true" | "yes" | "y" => true,
        "false" | "no" | "n" => false,
        _ => return Err("Invalid on-join parameter.".into())
      };
      let (message, delay_str) = if args[2].contains('\n') {
        let joined = args[2..].join(" ");
        let parts: Vec<&str> = joined.splitn(2, '\n').collect();
        assert_eq!(2, parts.len()); // contains \n, so splitn should always return two parts
        (Some(parts[1].to_string()), parts[0].to_string())
      } else {
        (None, args[2].to_string())
      };
      let delay: u64 = match delay_str.parse() {
        Ok(d) => d,
        Err(_) => return Err("Invalid delay.".into())
      };
      let (filters, message) = match message {
        Some(m) => (None, m.to_string()),
        None => {
          let args = &args[3..];
          let joined_args = args.join(" ");
          let parts: Vec<&str> = joined_args.splitn(2, '\n').collect();
          if parts.len() == 1 {
            return Err("Missing message.".into());
          }
          let filters: Option<Vec<Filter>> = parts[0].split(' ').map(Filter::parse).collect();
          let filters = match filters {
            Some(f) => f.into_iter().map(|x| x.to_string()).collect::<Vec<_>>().join(" "),
            None => return Err("Invalid filters.".into())
          };
          (Some(filters), parts[1].to_string())
        }
      };
      let nar = NewAutoReply {
        server_id: server.id.0.into(),
        channel_id: channel.0.into(),
        message: message.to_string(),
        on_join: on_join,
        delay: delay as i32,
        filters: filters
      };
      ::bot::CONNECTION.with(|c| {
        use database::schema::auto_replies;
        diesel::insert(&nar)
          .into(auto_replies::table)
          .execute(c)
          .chain_err(|| "could not insert new dam")
      })?;
      Ok(CommandSuccess::default())
    },
    "remove" | "delete" => {
      let params: DeleteParams = ::lalafell::commands::params::from_str(&args.join(" "))
        .map_err(|_| into!(CommandFailure, "Invalid parameters."))?;
      let affected = ::bot::CONNECTION.with(|c| {
        use database::schema::auto_replies::dsl;
        diesel::delete(dsl::auto_replies.filter(dsl::id.eq(params.id)))
          .execute(c)
          .chain_err(|| "could not delete auto_replies")
      })?;
      if affected > 0 {
        Ok(CommandSuccess::default())
      } else {
        Err("No delete all messages were deleted.".into())
      }
    },
    _ => Err("No such subcommand.".into())
  }
}
<commit_msg>fix: add extra newline<commit_after>use database::models::{AutoReply, NewAutoReply};
use filters::Filter;

use discord::model::{permissions, UserId, ChannelId, LiveServer};

use diesel;
use diesel::prelude::*;

use lalafell::error::*;
use lalafell::commands::prelude::*;
use lalafell::commands::ChannelOrId;

#[derive(Debug, Deserialize)]
pub struct DeleteParams {
  id: i32
}

fn list_all() -> Result<String> {
  let ars: Vec<AutoReply> = ::bot::CONNECTION.with(|c| {
    use database::schema::auto_replies::dsl;
    dsl::auto_replies.load(c).chain_err(|| "could not load auto_replies")
  })?;
  Ok(ars.iter()
    .map(|r| format!("{id}. Replying to messages in {channel}{filters} with a delay of {delay} second{plural}.\n```{message}\n```",
                     id = r.id,
                     channel = ChannelId(*r.channel_id).mention(),
                     filters = r.filters.as_ref().map(|f| format!(" with filters `{}`", f)).unwrap_or_default(),
                     delay = r.delay,
                     plural = if r.delay == 1 { "" } else { "s" },
                     message = r.message
    ))
    .collect::<Vec<_>>()
    .join("\n"))
}

pub fn auto_reply<'a>(author: UserId, server: &LiveServer, content: &str) -> CommandResult<'a> {
  if !server.permissions_for(server.id.main(), author).contains(permissions::MANAGE_MESSAGES) {
    return Err(ExternalCommandFailure::default()
      .message(|e: EmbedBuilder| e
        .title("Not enough permissions.")
        .description("You don't have enough permissions to use this command."))
      .wrap());
  }
  let args: Vec<&str> = content.split(|c| c == ' ' || c == '\t').filter(|x| !x.is_empty()).skip(3).collect();
  if args.is_empty() {
    return Ok(list_all()?.into());
  }
  let subcommand = &args[0];
  let args = &args[1..];
  match subcommand.to_lowercase().as_str() {
    "add" | "create" => {
      if args.len() < 4 {
        return Err("Invalid parameters.".into());
      }
      let channel = ChannelOrId::parse(args[0]).map_err(|_| into!(CommandFailure, "Invalid channel reference."))?;
      let on_join = match args[1].to_lowercase().as_str() {
        "true" | "yes" | "y" => true,
        "false" | "no" | "n" => false,
        _ => return Err("Invalid on-join parameter.".into())
      };
      let (message, delay_str) = if args[2].contains('\n') {
        let joined = args[2..].join(" ");
        let parts: Vec<&str> = joined.splitn(2, '\n').collect();
        assert_eq!(2, parts.len()); // contains \n, so splitn should always return two parts
        (Some(parts[1].to_string()), parts[0].to_string())
      } else {
        (None, args[2].to_string())
      };
      let delay: u64 = match delay_str.parse() {
        Ok(d) => d,
        Err(_) => return Err("Invalid delay.".into())
      };
      let (filters, message) = match message {
        Some(m) => (None, m.to_string()),
        None => {
          let args = &args[3..];
          let joined_args = args.join(" ");
          let parts: Vec<&str> = joined_args.splitn(2, '\n').collect();
          if parts.len() == 1 {
            return Err("Missing message.".into());
          }
          let filters: Option<Vec<Filter>> = parts[0].split(' ').map(Filter::parse).collect();
          let filters = match filters {
            Some(f) => f.into_iter().map(|x| x.to_string()).collect::<Vec<_>>().join(" "),
            None => return Err("Invalid filters.".into())
          };
          (Some(filters), parts[1].to_string())
        }
      };
      let nar = NewAutoReply {
        server_id: server.id.0.into(),
        channel_id: channel.0.into(),
        message: message.to_string(),
        on_join: on_join,
        delay: delay as i32,
        filters: filters
      };
      ::bot::CONNECTION.with(|c| {
        use database::schema::auto_replies;
        diesel::insert(&nar)
          .into(auto_replies::table)
          .execute(c)
          .chain_err(|| "could not insert new dam")
      })?;
      Ok(CommandSuccess::default())
    },
    "remove" | "delete" => {
      let params: DeleteParams = ::lalafell::commands::params::from_str(&args.join(" "))
        .map_err(|_| into!(CommandFailure, "Invalid parameters."))?;
      let affected = ::bot::CONNECTION.with(|c| {
        use database::schema::auto_replies::dsl;
        diesel::delete(dsl::auto_replies.filter(dsl::id.eq(params.id)))
          .execute(c)
          .chain_err(|| "could not delete auto_replies")
      })?;
      if affected > 0 {
        Ok(CommandSuccess::default())
      } else {
        Err("No delete all messages were deleted.".into())
      }
    },
    _ => Err("No such subcommand.".into())
  }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// compile-flags: -Zmir-emit-validate=0

#![feature(unsized_tuple_coercion)]
use std::mem;

fn main() {
    let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
    let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
    let mut a = [y, x];
    a.sort();
    assert_eq!(a, [x, y]);

    assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");
    assert_eq!(mem::size_of_val(x), 16);
}
<commit_msg>enable a test that works just fine<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

#![feature(unsized_tuple_coercion)]
use std::mem;

fn main() {
    let x : &(i32, i32, [i32]) = &(0, 1, [2, 3]);
    let y : &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]);
    let mut a = [y, x];
    a.sort();
    assert_eq!(a, [x, y]);

    assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]");
    assert_eq!(mem::size_of_val(x), 16);
}
<|endoftext|>


<commit_before>use std::num::Wrapping;
use bitfield::Bitfield;
use gb_proc::cpu::{Handler, Interrupt};

// Every X clocks
#[derive(Clone, Copy, Debug)]
enum ClockSelect {
    C1024 = 0b00,
    C16   = 0b01,
    C64   = 0b10,
    C256  = 0b11,
}

impl Into<u8> for ClockSelect {
    fn into(self) -> u8 {
        self as u8
    }
}

impl From<u8> for ClockSelect {
    fn from(v: u8) -> ClockSelect {
        match v {
            0b00 => ClockSelect::C1024,
            0b01 => ClockSelect::C16,
            0b10 => ClockSelect::C64,
            0b11 => ClockSelect::C256,
            _ => panic!(),
        }
    }
}

pub struct TimerController {
    clock: usize,
    last_clock: usize,
    last_divider: usize,

    mapper: TimerMemoryMapper,
}

impl TimerController {
    pub fn new() -> TimerController {
        TimerController {
            clock: 0,
            last_clock: 0,
            last_divider: 0,

            mapper: TimerMemoryMapper::new(),
        }
    }

    pub fn add_cycles(&mut self, cycles: usize) -> Vec<Interrupt> {
        // println!("add_cycles cycles={} last_clock={}", cycles, self.last_clock);
        self.last_clock += cycles;
        self.last_divider += cycles;

        let mut interrupts = vec![];
        while self.last_clock >= 16 {
            self.last_clock -= 16;

            self.clock = (Wrapping(self.clock) + Wrapping(1)).0;

            let clock = self.inc_clock();
            let divider = self.inc_divider();

            if clock || divider {
                interrupts.push(Interrupt::Timer);
            }
        }

        if self.mapper.get_timer_enabled() == 0 {
            return vec![];
        }

        interrupts
    }

    fn inc_clock(&mut self) -> bool {
        let should_increment = match self.mapper.get_clock_select() {
            ClockSelect::C16   => true,
            ClockSelect::C64   => (self.clock %  4) == 0,
            ClockSelect::C256  => (self.clock % 16) == 0,
            ClockSelect::C1024 => (self.clock % 64) == 0,
        };

        if should_increment {
            if self.mapper.timer == 0xFF {
                self.mapper.timer = self.mapper.modulo;
                true
            } else {
                self.mapper.timer = self.mapper.timer + 1;
                false
            }
        } else {
            false
        }
    }

    fn inc_divider(&mut self) -> bool {
        if self.clock % 16 == 0 {
            self.mapper.divider = (Wrapping(self.mapper.divider) + Wrapping(1)).0;
            self.mapper.divider == 0x00
        } else {
            false
        }
    }

    pub fn write_callback(&mut self, address: u16) {
        match address {
            0xFF04 => { self.mapper.divider = 0 },
            _ => {},
        }
    }
}

memory_mapper!{
    name: TimerMemoryMapper,
    fields: [
        0xFF04, divider, 0;
        0xFF05, timer,   0;
        0xFF06, modulo,  0;
    ],
    bitfields: {
        getters: [
            0xFF07, control, 0, [
                get_01, get_clock_select,  ClockSelect;
                get_2,  get_timer_enabled, u8
            ]
        ],
        getter_setters: [],
    },
}

memory_handler!{
    parent: TimerController,
    mapper: mapper,
    callback: write_callback,
}
<commit_msg>Use u8_enum to generate ClockSelect.<commit_after>use std::num::Wrapping;
use bitfield::Bitfield;
use gb_proc::cpu::{Handler, Interrupt};

u8_enum!{
    ClockSelect {
        // Every X clocks
        C1024 = 0b00,
        C16   = 0b01,
        C64   = 0b10,
        C256  = 0b11,
    }
}

pub struct TimerController {
    clock: usize,
    last_clock: usize,
    last_divider: usize,

    mapper: TimerMemoryMapper,
}

impl TimerController {
    pub fn new() -> TimerController {
        TimerController {
            clock: 0,
            last_clock: 0,
            last_divider: 0,

            mapper: TimerMemoryMapper::new(),
        }
    }

    pub fn add_cycles(&mut self, cycles: usize) -> Vec<Interrupt> {
        // println!("add_cycles cycles={} last_clock={}", cycles, self.last_clock);
        self.last_clock += cycles;
        self.last_divider += cycles;

        let mut interrupts = vec![];
        while self.last_clock >= 16 {
            self.last_clock -= 16;

            self.clock = (Wrapping(self.clock) + Wrapping(1)).0;

            let clock = self.inc_clock();
            let divider = self.inc_divider();

            if clock || divider {
                interrupts.push(Interrupt::Timer);
            }
        }

        if self.mapper.get_timer_enabled() == 0 {
            return vec![];
        }

        interrupts
    }

    fn inc_clock(&mut self) -> bool {
        let should_increment = match self.mapper.get_clock_select() {
            ClockSelect::C16   => true,
            ClockSelect::C64   => (self.clock %  4) == 0,
            ClockSelect::C256  => (self.clock % 16) == 0,
            ClockSelect::C1024 => (self.clock % 64) == 0,
        };

        if should_increment {
            if self.mapper.timer == 0xFF {
                self.mapper.timer = self.mapper.modulo;
                true
            } else {
                self.mapper.timer = self.mapper.timer + 1;
                false
            }
        } else {
            false
        }
    }

    fn inc_divider(&mut self) -> bool {
        if self.clock % 16 == 0 {
            self.mapper.divider = (Wrapping(self.mapper.divider) + Wrapping(1)).0;
            self.mapper.divider == 0x00
        } else {
            false
        }
    }

    pub fn write_callback(&mut self, address: u16) {
        match address {
            0xFF04 => { self.mapper.divider = 0 },
            _ => {},
        }
    }
}

memory_mapper!{
    name: TimerMemoryMapper,
    fields: [
        0xFF04, divider, 0;
        0xFF05, timer,   0;
        0xFF06, modulo,  0;
    ],
    bitfields: {
        getters: [
            0xFF07, control, 0, [
                get_01, get_clock_select,  ClockSelect;
                get_2,  get_timer_enabled, u8
            ]
        ],
        getter_setters: [],
    },
}

memory_handler!{
    parent: TimerController,
    mapper: mapper,
    callback: write_callback,
}
<|endoftext|>


<commit_before>use grammars::pmcfg::{PMCFGRule, VarT};

use std::{ hash::Hash, collections::HashSet, fmt::Debug };
use integeriser::{HashIntegeriser, Integeriser};
use util::{IntMap, IntSet, search::Search};
use fnv::{FnvHashMap, FnvHashSet};

type RuleId = usize;
type CachedNt = usize;

/// Serializable filter for grammar rules.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedFilterPersistentStorage<T>
where
    T: Eq + Hash
{
    // key of initial nonterminal symbol
    initial_nt: usize,
    // rules without rhs nts and terminals
    free_rules: IntSet,
    // lhs nonterminals of those rules
    free_nts: IntSet,

    // rules with nts on rhs, no terminals
    only_nt: IntMap<Vec<(Vec<CachedNt>, RuleId, CachedNt)>>,
    // rules with no nts on rhs, but terminals
    only_t: FnvHashMap<T, Vec<(Vec<T>, RuleId, CachedNt)>>,
    // rules with nts and terminals
    nt_and_t: FnvHashMap<T, Vec<(Vec<T>, Vec<CachedNt>, RuleId, CachedNt)>>
}

impl<T> CachedFilterPersistentStorage<T>
where
    T: Clone + Eq + Hash,
{
    pub fn new<'a, N, W>(rules: impl Iterator<Item=(usize, &'a PMCFGRule<N, T, W>)> + 'a, initial: &N) -> Self
    where
        N: Debug + Eq + Hash + 'a,
        T: 'a,
        W: 'a
    {
        let mut integeriser: HashIntegeriser<&N> = HashIntegeriser::new();
        let mut free_rules: IntSet = IntSet::default();
        let mut free_nts: IntSet = IntSet::default();

        let mut only_nt = IntMap::default();
        let mut only_t = FnvHashMap::default();
        let mut nt_and_t = FnvHashMap::default();

        for (rid, rule) in rules {
            let nid = integeriser.integerise(&rule.head);
            let mut ts = (&rule.composition).into_iter()
                               .flat_map(|v| v)
                               .filter_map(|vart| if let &VarT::T(ref t) = vart { Some(t.clone()) } else { None })
                               .collect::<HashSet<_>>()
                               .into_iter()
                               .collect::<Vec<_>>();
            if rule.tail.is_empty() {
                if ts.is_empty()  {
                    free_rules.insert(rid);
                    free_nts.insert(nid);
                } else {
                    only_t.entry(ts.remove(0))
                          .or_insert_with(Vec::new)
                          .push((ts, rid, nid));
                }
            } else {
                let i_rhs: Vec<usize> = rule.tail.iter().map(|i| integeriser.integerise(i))
                                            .collect::<HashSet<usize>>().into_iter().collect();
                if ts.is_empty()  {
                    for rhs_index in 0..(i_rhs.len()) {
                        let mut rhs_remainder = i_rhs.clone();
                        only_nt.entry(rhs_remainder.remove(rhs_index))
                               .or_insert_with(Vec::new)
                               .push((rhs_remainder, rid, nid));
                    }
                } else {
                    nt_and_t.entry(ts.remove(0))
                            .or_insert_with(Vec::new)
                            .push((ts, i_rhs, rid, nid));
                }
            }
        }

        let initial_nt = integeriser.find_key(&initial).expect(&format!("no rules with initial nonterminal {:?}", initial));

        CachedFilterPersistentStorage {
            initial_nt,
            free_rules,
            free_nts,
            only_nt,
            only_t,
            nt_and_t
        }
    }

    pub fn instantiate(&self, word: &[T]) -> IntSet {
        let ts: FnvHashSet<&T> = word.iter().collect::<FnvHashSet<_>>();

        // ## step 1: search for productive nonterminals and rules

        let mut productive_nts = self.free_nts.clone();
        let mut only_nt = self.only_nt.clone();

        let mut productive_rules: IntMap<Vec<(Vec<usize>, usize)>> = IntMap::default();

        for t in &ts {
            for &(ref ts_to_check, rid, nid) in self.only_t.get(*t).into_iter().flat_map(|v| v) {
                if ts_to_check.iter().all(|symbol| ts.contains(&symbol)) {
                    productive_nts.insert(nid);
                    productive_rules.entry(nid)
                        .or_insert_with(Vec::new)
                        .push((Vec::new(), rid));
                }
            }
            for &(ref ts_to_check, ref nts, rid, nid) in self.nt_and_t.get(*t).into_iter().flat_map(|v| v) {
                if ts_to_check.iter().all(|symbol| ts.contains(&symbol)) {
                    for nts_index in 0..(nts.len()) {
                        let mut nts_without_index = nts.clone();
                        let nts_at_index = nts_without_index.remove(nts_index);
                        only_nt.entry(nts_at_index)
                            .or_insert_with(Vec::new)
                            .push((nts.clone(), rid, nid));
                    }
                }
            }
        }

        let mut runtime_stack = productive_nts.iter().cloned().collect::<Vec<_>>();

        while let Some(nt) = runtime_stack.pop() {
            for &(ref rhs, rule, lhs) in only_nt.get(&nt).into_iter().flatten() {
                if rhs.iter().all(|a| productive_nts.contains(a)) {
                    productive_rules.entry(lhs).or_insert_with(Vec::new).push((rhs.clone(), rule));
                    if productive_nts.insert(lhs) {
                        runtime_stack.push(lhs);
                    }
                }
            }
        }

        // ## step 2: search for reachable nonterminals and rules among the productive ones

        let mut productive_and_reachable_rules: IntSet = IntSet::default();

        {
            // search for reachable nonterminals in productive_nonterminals while adding
            // reachable rules to productive_and_reachable_rules
            let s = Search::unweighted(
                vec![self.initial_nt],
                |nt| {
                    let mut nts = Vec::new();
                    for (mut new_nts, r) in productive_rules.remove(nt).into_iter().flatten() {
                        nts.append(&mut new_nts);
                        productive_and_reachable_rules.insert(r);
                    }
                    nts
                }
            );

            // execute the search
            let _: Vec<_> = s.collect();
        }

        productive_and_reachable_rules
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use grammars::pmcfg::Composition;
    use integeriser::{HashIntegeriser, Integeriser};

    #[test]
    fn filter() {
        let rules = vec![
            PMCFGRule{ head: "A", tail: vec!["A"], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(1), VarT::Var(0,0)], vec![VarT::Var(0, 1), VarT::T(2)]])},
            PMCFGRule{ head: "A", tail: vec!["B"], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(1), VarT::Var(0,0)], vec![VarT::Var(0, 1), VarT::T(2)]])},
            PMCFGRule{ head: "A", tail: vec![], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(0)], vec![VarT::T(0)]])},
            PMCFGRule{ head: "B", tail: vec![], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(3)], vec![VarT::T(3)]])},
            PMCFGRule{ head: "C", tail: vec![], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(3)], vec![VarT::T(3)]])}
        ];

        let mut integeriser = HashIntegeriser::new();

        for rule in rules {
            integeriser.integerise(rule);
        }

        let filter = CachedFilterPersistentStorage::new(integeriser.values().iter().enumerate(), &"A");

        assert_eq!(filter.clone().instantiate(&[]), vec![].into_iter().collect::<IntSet>());
        assert_eq!(filter.clone().instantiate(&[1]), vec![].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[2]), vec![].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[1, 2]), vec![].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[0, 1]), vec![2].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[0, 1, 2]), vec![0, 2].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[1, 2, 3]), vec![0, 1, 3].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[0, 1, 2, 3]), vec![0, 1, 2, 3].into_iter().collect::<>());
    }
}
<commit_msg>…/cs_representation/automaton/rule_filter.rs: restored an optimisation<commit_after>use grammars::pmcfg::{PMCFGRule, VarT};

use std::{ hash::Hash, collections::HashSet, fmt::Debug };
use integeriser::{HashIntegeriser, Integeriser};
use util::{IntMap, IntSet, search::Search};
use fnv::{FnvHashMap, FnvHashSet};

type RuleId = usize;
type CachedNt = usize;

/// Serializable filter for grammar rules.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedFilterPersistentStorage<T>
where
    T: Eq + Hash
{
    // key of initial nonterminal symbol
    initial_nt: usize,
    // rules without rhs nts and terminals
    free_rules: IntSet,
    // lhs nonterminals of those rules
    free_nts: IntSet,

    // rules with nts on rhs, no terminals
    only_nt: IntMap<Vec<(Vec<CachedNt>, RuleId, CachedNt)>>,
    // rules with no nts on rhs, but terminals
    only_t: FnvHashMap<T, Vec<(Vec<T>, RuleId, CachedNt)>>,
    // rules with nts and terminals
    nt_and_t: FnvHashMap<T, Vec<(Vec<T>, Vec<CachedNt>, RuleId, CachedNt)>>
}

impl<T> CachedFilterPersistentStorage<T>
where
    T: Clone + Eq + Hash,
{
    pub fn new<'a, N, W>(rules: impl Iterator<Item=(usize, &'a PMCFGRule<N, T, W>)> + 'a, initial: &N) -> Self
    where
        N: Debug + Eq + Hash + 'a,
        T: 'a,
        W: 'a
    {
        let mut integeriser: HashIntegeriser<&N> = HashIntegeriser::new();
        let mut free_rules: IntSet = IntSet::default();
        let mut free_nts: IntSet = IntSet::default();

        let mut only_nt = IntMap::default();
        let mut only_t = FnvHashMap::default();
        let mut nt_and_t = FnvHashMap::default();

        for (rid, rule) in rules {
            let nid = integeriser.integerise(&rule.head);
            let mut ts = (&rule.composition).into_iter()
                               .flat_map(|v| v)
                               .filter_map(|vart| if let &VarT::T(ref t) = vart { Some(t.clone()) } else { None })
                               .collect::<HashSet<_>>()
                               .into_iter()
                               .collect::<Vec<_>>();
            if rule.tail.is_empty() {
                if ts.is_empty()  {
                    free_rules.insert(rid);
                    free_nts.insert(nid);
                } else {
                    only_t.entry(ts.remove(0))
                          .or_insert_with(Vec::new)
                          .push((ts, rid, nid));
                }
            } else {
                let i_rhs: Vec<usize> = rule.tail.iter().map(|i| integeriser.integerise(i))
                                            .collect::<HashSet<usize>>().into_iter().collect();
                if ts.is_empty()  {
                    for rhs_index in 0..(i_rhs.len()) {
                        let mut rhs_remainder = i_rhs.clone();
                        only_nt.entry(rhs_remainder.remove(rhs_index))
                               .or_insert_with(Vec::new)
                               .push((rhs_remainder, rid, nid));
                    }
                } else {
                    nt_and_t.entry(ts.remove(0))
                            .or_insert_with(Vec::new)
                            .push((ts, i_rhs, rid, nid));
                }
            }
        }

        let initial_nt = integeriser.find_key(&initial).expect(&format!("no rules with initial nonterminal {:?}", initial));

        CachedFilterPersistentStorage {
            initial_nt,
            free_rules,
            free_nts,
            only_nt,
            only_t,
            nt_and_t
        }
    }

    pub fn instantiate(&self, word: &[T]) -> IntSet {
        let ts: FnvHashSet<&T> = word.iter().collect::<FnvHashSet<_>>();

        // ## step 1: search for productive nonterminals and rules

        let mut productive_nts = self.free_nts.clone();
        let mut only_nt = self.only_nt.clone();

        let mut productive_rules: IntMap<Vec<(Vec<usize>, usize)>> = IntMap::default();

        for t in &ts {
            for &(ref ts_to_check, rid, nid) in self.only_t.get(*t).into_iter().flatten() {
                if ts_to_check.iter().all(|symbol| ts.contains(&symbol)) {
                    productive_nts.insert(nid);
                    productive_rules.entry(nid)
                        .or_insert_with(Vec::new)
                        .push((Vec::new(), rid));
                }
            }
            for &(ref ts_to_check, ref nts, rid, nid) in self.nt_and_t.get(*t).into_iter().flatten() {
                if ts_to_check.iter().all(|symbol| ts.contains(&symbol)) {
                    for nts_index in 0 .. nts.len() {
                        let mut nts_without_index = nts.clone();
                        let nts_at_index = nts_without_index.remove(nts_index);
                        only_nt.entry(nts_at_index)
                            .or_insert_with(Vec::new)
                            .push((nts_without_index, rid, nid));
                    }
                }
            }
        }

        let mut runtime_stack = productive_nts.iter().cloned().collect::<Vec<_>>();

        while let Some(nt) = runtime_stack.pop() {
            for &(ref rhs, rule, lhs) in only_nt.get(&nt).into_iter().flatten() {
                if rhs.iter().all(|a| productive_nts.contains(a)) {
                    let mut rhs_nts = rhs.clone();
                    rhs_nts.push(nt);
                    productive_rules.entry(lhs)
                        .or_insert_with(Vec::new)
                        .push((rhs_nts, rule));
                    if productive_nts.insert(lhs) {
                        runtime_stack.push(lhs);
                    }
                }
            }
        }

        // ## step 2: search for reachable nonterminals and rules among the productive ones

        let mut productive_and_reachable_rules: IntSet = IntSet::default();

        {
            // search for reachable nonterminals in productive_nonterminals while adding
            // reachable rules to productive_and_reachable_rules
            let s = Search::unweighted(
                vec![self.initial_nt],
                |nt| {
                    let mut nts = Vec::new();
                    for (mut new_nts, r) in productive_rules.remove(nt).into_iter().flatten() {
                        nts.append(&mut new_nts);
                        productive_and_reachable_rules.insert(r);
                    }
                    nts
                }
            );

            // execute the search
            let _: Vec<_> = s.collect();
        }

        productive_and_reachable_rules
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use grammars::pmcfg::Composition;
    use integeriser::{HashIntegeriser, Integeriser};

    #[test]
    fn filter() {
        let rules = vec![
            PMCFGRule{ head: "A", tail: vec!["A"], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(1), VarT::Var(0,0)], vec![VarT::Var(0, 1), VarT::T(2)]])},
            PMCFGRule{ head: "A", tail: vec!["B"], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(1), VarT::Var(0,0)], vec![VarT::Var(0, 1), VarT::T(2)]])},
            PMCFGRule{ head: "A", tail: vec![], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(0)], vec![VarT::T(0)]])},
            PMCFGRule{ head: "B", tail: vec![], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(3)], vec![VarT::T(3)]])},
            PMCFGRule{ head: "C", tail: vec![], weight: 1f64, composition: Composition::from(vec![vec![VarT::T(3)], vec![VarT::T(3)]])}
        ];

        let mut integeriser = HashIntegeriser::new();

        for rule in rules {
            integeriser.integerise(rule);
        }

        let filter = CachedFilterPersistentStorage::new(integeriser.values().iter().enumerate(), &"A");

        assert_eq!(filter.clone().instantiate(&[]), vec![].into_iter().collect::<IntSet>());
        assert_eq!(filter.clone().instantiate(&[1]), vec![].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[2]), vec![].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[1, 2]), vec![].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[0, 1]), vec![2].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[0, 1, 2]), vec![0, 2].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[1, 2, 3]), vec![0, 1, 3].into_iter().collect::<>());
        assert_eq!(filter.clone().instantiate(&[0, 1, 2, 3]), vec![0, 1, 2, 3].into_iter().collect::<>());
    }
}
<|endoftext|>


<commit_before>use crate::types::GossipKind;
use crate::Enr;
use discv5::{Discv5Config, Discv5ConfigBuilder};
use libp2p::gossipsub::{
    GossipsubConfig, GossipsubConfigBuilder, GossipsubMessage, MessageId, ValidationMode,
};
use libp2p::Multiaddr;
use serde_derive::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::path::PathBuf;
use std::time::Duration;

pub const GOSSIP_MAX_SIZE: usize = 1_048_576;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
/// Network configuration for lighthouse.
pub struct Config {
    /// Data directory where node's keyfile is stored
    pub network_dir: PathBuf,

    /// IP address to listen on.
    pub listen_address: std::net::IpAddr,

    /// The TCP port that libp2p listens on.
    pub libp2p_port: u16,

    /// UDP port that discovery listens on.
    pub discovery_port: u16,

    /// The address to broadcast to peers about which address we are listening on. None indicates
    /// that no discovery address has been set in the CLI args.
    pub enr_address: Option<std::net::IpAddr>,

    /// The udp port to broadcast to peers in order to reach back for discovery.
    pub enr_udp_port: Option<u16>,

    /// The tcp port to broadcast to peers in order to reach back for libp2p services.
    pub enr_tcp_port: Option<u16>,

    /// Target number of connected peers.
    pub target_peers: usize,

    /// Gossipsub configuration parameters.
    #[serde(skip)]
    pub gs_config: GossipsubConfig,

    /// Discv5 configuration parameters.
    #[serde(skip)]
    pub discv5_config: Discv5Config,

    /// List of nodes to initially connect to.
    pub boot_nodes: Vec<Enr>,

    /// List of libp2p nodes to initially connect to.
    pub libp2p_nodes: Vec<Multiaddr>,

    /// Client version
    pub client_version: String,

    /// Disables the discovery protocol from starting.
    pub disable_discovery: bool,

    /// List of extra topics to initially subscribe to as strings.
    pub topics: Vec<GossipKind>,
}

impl Default for Config {
    /// Generate a default network configuration.
    fn default() -> Self {
        let mut network_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
        network_dir.push(".lighthouse");
        network_dir.push("network");

        // The default topics that we will initially subscribe to
        let topics = vec![
            GossipKind::BeaconBlock,
            GossipKind::BeaconAggregateAndProof,
            GossipKind::VoluntaryExit,
            GossipKind::ProposerSlashing,
            GossipKind::AttesterSlashing,
        ];

        // The function used to generate a gossipsub message id
        // We use base64(SHA256(data)) for content addressing
        let gossip_message_id = |message: &GossipsubMessage| {
            MessageId::from(base64::encode_config(
                &Sha256::digest(&message.data),
                base64::URL_SAFE_NO_PAD,
            ))
        };

        // gossipsub configuration
        // Note: The topics by default are sent as plain strings. Hashes are an optional
        // parameter.
        let gs_config = GossipsubConfigBuilder::new()
            .max_transmit_size(GOSSIP_MAX_SIZE)
            .heartbeat_interval(Duration::from_secs(1))
            .validate_messages() // require validation before propagation
            .validation_mode(ValidationMode::Permissive)
            // Prevent duplicates by caching messages from an epoch + 1 slot amount of time (33*12)
            .duplicate_cache_time(Duration::from_secs(396))
            .message_id_fn(gossip_message_id)
            .build();

        // discv5 configuration
        let discv5_config = Discv5ConfigBuilder::new()
            .enable_packet_filter()
            .session_cache_capacity(1000)
            .request_timeout(Duration::from_secs(4))
            .request_retries(1)
            .enr_peer_update_min(10)
            .query_parallelism(5)
            .query_timeout(Duration::from_secs(30))
            .query_peer_timeout(Duration::from_secs(2))
            .ip_limit() // limits /24 IP's in buckets.
            .ping_interval(Duration::from_secs(300))
            .build();

        // NOTE: Some of these get overridden by the corresponding CLI default values.
        Config {
            network_dir,
            listen_address: "0.0.0.0".parse().expect("valid ip address"),
            libp2p_port: 9000,
            discovery_port: 9000,
            enr_address: None,
            enr_udp_port: None,
            enr_tcp_port: None,
            target_peers: 50,
            gs_config,
            discv5_config,
            boot_nodes: vec![],
            libp2p_nodes: vec![],
            client_version: lighthouse_version::version_with_platform(),
            disable_discovery: false,
            topics,
        }
    }
}
<commit_msg>Update v0.12.2 gossip params (#1449)<commit_after>use crate::types::GossipKind;
use crate::Enr;
use discv5::{Discv5Config, Discv5ConfigBuilder};
use libp2p::gossipsub::{
    GossipsubConfig, GossipsubConfigBuilder, GossipsubMessage, MessageId, ValidationMode,
};
use libp2p::Multiaddr;
use serde_derive::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::path::PathBuf;
use std::time::Duration;

pub const GOSSIP_MAX_SIZE: usize = 1_048_576;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
/// Network configuration for lighthouse.
pub struct Config {
    /// Data directory where node's keyfile is stored
    pub network_dir: PathBuf,

    /// IP address to listen on.
    pub listen_address: std::net::IpAddr,

    /// The TCP port that libp2p listens on.
    pub libp2p_port: u16,

    /// UDP port that discovery listens on.
    pub discovery_port: u16,

    /// The address to broadcast to peers about which address we are listening on. None indicates
    /// that no discovery address has been set in the CLI args.
    pub enr_address: Option<std::net::IpAddr>,

    /// The udp port to broadcast to peers in order to reach back for discovery.
    pub enr_udp_port: Option<u16>,

    /// The tcp port to broadcast to peers in order to reach back for libp2p services.
    pub enr_tcp_port: Option<u16>,

    /// Target number of connected peers.
    pub target_peers: usize,

    /// Gossipsub configuration parameters.
    #[serde(skip)]
    pub gs_config: GossipsubConfig,

    /// Discv5 configuration parameters.
    #[serde(skip)]
    pub discv5_config: Discv5Config,

    /// List of nodes to initially connect to.
    pub boot_nodes: Vec<Enr>,

    /// List of libp2p nodes to initially connect to.
    pub libp2p_nodes: Vec<Multiaddr>,

    /// Client version
    pub client_version: String,

    /// Disables the discovery protocol from starting.
    pub disable_discovery: bool,

    /// List of extra topics to initially subscribe to as strings.
    pub topics: Vec<GossipKind>,
}

impl Default for Config {
    /// Generate a default network configuration.
    fn default() -> Self {
        let mut network_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
        network_dir.push(".lighthouse");
        network_dir.push("network");

        // The default topics that we will initially subscribe to
        let topics = vec![
            GossipKind::BeaconBlock,
            GossipKind::BeaconAggregateAndProof,
            GossipKind::VoluntaryExit,
            GossipKind::ProposerSlashing,
            GossipKind::AttesterSlashing,
        ];

        // The function used to generate a gossipsub message id
        // We use base64(SHA256(data)) for content addressing
        let gossip_message_id = |message: &GossipsubMessage| {
            MessageId::from(base64::encode_config(
                &Sha256::digest(&message.data),
                base64::URL_SAFE_NO_PAD,
            ))
        };

        // gossipsub configuration
        // Note: The topics by default are sent as plain strings. Hashes are an optional
        // parameter.
        let gs_config = GossipsubConfigBuilder::new()
            .max_transmit_size(GOSSIP_MAX_SIZE)
            .heartbeat_interval(Duration::from_millis(700))
            .mesh_n(6)
            .mesh_n_low(5)
            .mesh_n_high(12)
            .gossip_lazy(6)
            .fanout_ttl(Duration::from_secs(60))
            .history_length(6)
            .history_gossip(3)
            .validate_messages() // require validation before propagation
            .validation_mode(ValidationMode::Permissive)
            // prevent duplicates for 550 heartbeats(700millis * 550) = 385 secs
            .duplicate_cache_time(Duration::from_secs(385))
            .message_id_fn(gossip_message_id)
            .build();

        // discv5 configuration
        let discv5_config = Discv5ConfigBuilder::new()
            .enable_packet_filter()
            .session_cache_capacity(1000)
            .request_timeout(Duration::from_secs(4))
            .request_retries(1)
            .enr_peer_update_min(10)
            .query_parallelism(5)
            .query_timeout(Duration::from_secs(30))
            .query_peer_timeout(Duration::from_secs(2))
            .ip_limit() // limits /24 IP's in buckets.
            .ping_interval(Duration::from_secs(300))
            .build();

        // NOTE: Some of these get overridden by the corresponding CLI default values.
        Config {
            network_dir,
            listen_address: "0.0.0.0".parse().expect("valid ip address"),
            libp2p_port: 9000,
            discovery_port: 9000,
            enr_address: None,
            enr_udp_port: None,
            enr_tcp_port: None,
            target_peers: 50,
            gs_config,
            discv5_config,
            boot_nodes: vec![],
            libp2p_nodes: vec![],
            client_version: lighthouse_version::version_with_platform(),
            disable_discovery: false,
            topics,
        }
    }
}
<|endoftext|>


<commit_before>use libc;
use glib::translate::*;

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum XmlSpace {
    Default,
    Preserve
}

/// Implements `xml:space` handling per the SVG spec
///
/// Normalizes a string as it comes out of the XML parser's handler
/// for character data according to the SVG rules in
/// https://www.w3.org/TR/SVG/text.html#WhiteSpace
pub fn xml_space_normalize(mode: XmlSpace, s: &str) -> String {
    match mode {
        XmlSpace::Default => normalize_default(s),
        XmlSpace::Preserve => normalize_preserve(s)
    }
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="default", the SVG user agent will do the following
// using a copy of the original character data content. First, it will
// remove all newline characters. Then it will convert all tab
// characters into space characters. Then, it will strip off all
// leading and trailing space characters. Then, all contiguous space
// characters will be consolidated.
fn normalize_default(s: &str) -> String {
    s.chars()
        .filter(|ch| *ch != '\n')
        .collect::<String>()
        .split(|ch| ch == ' ' || ch == '\t')
        .filter(|s| s.len() > 0)
        .collect::<Vec<_>>()
        .join(" ")
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="preserve", the SVG user agent will do the following
// using a copy of the original character data content. It will
// convert all newline and tab characters into space characters. Then,
// it will draw all space characters, including leading, trailing and
// multiple contiguous space characters. Thus, when drawn with
// xml:space="preserve", the string "a   b" (three spaces between "a"
// and "b") will produce a larger separation between "a" and "b" than
// "a b" (one space between "a" and "b").
fn normalize_preserve(s: &str) -> String {
    s.chars()
        .map(|ch| {
            match ch {
                '\n' | '\t' => ' ',

                c => c
            }
        })
        .collect()
}

#[no_mangle]
pub extern fn rsvg_xml_space_normalize(mode: XmlSpace, s: *const libc::c_char) -> *const libc::c_char {
    let rs = unsafe { String::from_glib_none (s) };

    xml_space_normalize(mode, &rs).to_glib_full()
}

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

    #[test]
    fn xml_space_default() {
        assert_eq!(xml_space_normalize(XmlSpace::Default, "\n    WS example\n    indented lines\n  "),
                   "WS example indented lines");
        assert_eq!(xml_space_normalize(XmlSpace::Default, "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "),
                   "WS example indented lines");

        assert_eq!(xml_space_normalize(XmlSpace::Default, "\nWS example\nnon-indented lines\n  "),
                   "WS examplenon-indented lines");
    }

    #[test]
    fn xml_space_preserve() {
        assert_eq!(xml_space_normalize(XmlSpace::Preserve, "\n    WS example\n    indented lines\n  "),
                   "     WS example     indented lines   ");
        assert_eq!(xml_space_normalize(XmlSpace::Preserve, "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "),
                   "       WS    example      indented lines       ");

    }
}
<commit_msg>Replace split/filter on ' ' || '/t' with .split_whitespace method.<commit_after>use libc;
use glib::translate::*;

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum XmlSpace {
    Default,
    Preserve
}

/// Implements `xml:space` handling per the SVG spec
///
/// Normalizes a string as it comes out of the XML parser's handler
/// for character data according to the SVG rules in
/// https://www.w3.org/TR/SVG/text.html#WhiteSpace
pub fn xml_space_normalize(mode: XmlSpace, s: &str) -> String {
    match mode {
        XmlSpace::Default => normalize_default(s),
        XmlSpace::Preserve => normalize_preserve(s)
    }
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="default", the SVG user agent will do the following
// using a copy of the original character data content. First, it will
// remove all newline characters. Then it will convert all tab
// characters into space characters. Then, it will strip off all
// leading and trailing space characters. Then, all contiguous space
// characters will be consolidated.
fn normalize_default(s: &str) -> String {
    s.chars()
        .filter(|ch| *ch != '\n')
        .collect::<String>()
        // split at whitespace, also trims whitespace.
        .split_whitespace()
        .collect::<Vec<_>>()
        .join(" ")
}

// From https://www.w3.org/TR/SVG/text.html#WhiteSpace
//
// When xml:space="preserve", the SVG user agent will do the following
// using a copy of the original character data content. It will
// convert all newline and tab characters into space characters. Then,
// it will draw all space characters, including leading, trailing and
// multiple contiguous space characters. Thus, when drawn with
// xml:space="preserve", the string "a   b" (three spaces between "a"
// and "b") will produce a larger separation between "a" and "b" than
// "a b" (one space between "a" and "b").
fn normalize_preserve(s: &str) -> String {
    s.chars()
        .map(|ch| {
            match ch {
                '\n' | '\t' => ' ',

                c => c
            }
        })
        .collect()
}

#[no_mangle]
pub extern fn rsvg_xml_space_normalize(mode: XmlSpace, s: *const libc::c_char) -> *const libc::c_char {
    let rs = unsafe { String::from_glib_none (s) };

    xml_space_normalize(mode, &rs).to_glib_full()
}

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

    #[test]
    fn xml_space_default() {
        assert_eq!(xml_space_normalize(XmlSpace::Default, "\n    WS example\n    indented lines\n  "),
                   "WS example indented lines");
        assert_eq!(xml_space_normalize(XmlSpace::Default, "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "),
                   "WS example indented lines");

        assert_eq!(xml_space_normalize(XmlSpace::Default, "\nWS example\nnon-indented lines\n  "),
                   "WS examplenon-indented lines");
    }

    #[test]
    fn xml_space_preserve() {
        assert_eq!(xml_space_normalize(XmlSpace::Preserve, "\n    WS example\n    indented lines\n  "),
                   "     WS example     indented lines   ");
        assert_eq!(xml_space_normalize(XmlSpace::Preserve, "\n  \t  \tWS \t\t\texample\n  \t  indented lines\t\t  \n  "),
                   "       WS    example      indented lines       ");

    }
}
<|endoftext|>


<commit_before>use lisp::lex::{tokenize, parse_form};
use lisp::cell::Cell;
use lisp::env::Environment;

pub fn eval(ast: Cell, env: &mut Environment) -> Cell {
    match ast {
        Cell::Symbol(name) => lookup_symbol(name, env),
        Cell::List(tokens) => eval_list(tokens, env),
        _ => ast,
    }
}

// Looks up the symbol in the environment, returning the associated value
fn lookup_symbol(name: String, env: &Environment) -> Cell {
    match env.lookup(&name) {
        Some(cell) => {
            match cell {
                &Cell::Number(n) => Cell::Number(n),
                &Cell::Symbol(ref n) => Cell::Symbol(n.to_string()),
                _ => Cell::Nil,
            }
        }
        _ => Cell::Nil,
    }
}

// Evaluates the list
// if the first element is a function or keyword, it executes that, otherwise returns
// the list itself
fn eval_list(mut tokens: Vec<Cell>, env: &mut Environment) -> Cell {
    // empty list -> no action
    if tokens.is_empty() {
        return Cell::List(tokens);
    }

    let first = tokens.remove(0);

    if let Cell::Symbol(name) = first {

        if let Some(&Cell::Proc(function)) = env.lookup(&name) {
            // eager eval: each of the arguments is evaluated before calling
            let mut args: Vec<Cell> = Vec::new();
            for arg in tokens {
                args.push(eval(arg, env));
            }

            function(Cell::List(args))
        } else {
            eval_core(name.as_ref(), &mut tokens, env)
        }
    } else {
        // first is not a symbol, it's a regular list
        tokens.insert(0, first);
        Cell::List(tokens)
    }
}

fn eval_core(keyword: &str, args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    match keyword {
        "quote" => eval_quote(args),
        "if" => eval_if(args, env),
        "def!" => eval_def(args, env),
        _ => Cell::Nil,
    }
}

// Implementation for def
// usage: (def! name value ...)
fn eval_def(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    if let Cell::Symbol(name) = args.remove(0) {
        let t = eval(args.remove(0), env);
        env.insert(name, t);
    }
    // assignment expressions return the assigned value
    Cell::Nil
}

// Implementation for quote
// usage: (quote value ...)
fn eval_quote(args: &mut Vec<Cell>) -> Cell {
    args.remove(0)
}

// Implementation for if
// usage: (if test eval_if_true [eval_if_false])
fn eval_if(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    let has_else = if args.len() == 3 {
        true
    } else {
        false
    };
    let condition = args.remove(0);

    match eval(condition, env) {
        Cell::True => eval(args.remove(0), env),
        Cell::False if has_else => eval(args.remove(1), env),
        _ => Cell::Nil,
    }
}

// Utility function
pub fn parse_and_eval(input: &str, env: &mut Environment) -> Cell {
    let mut tokens = tokenize(input);
    let tree = parse_form(&mut tokens);
    // TODO: have descriptive error messages
    match tree {
        Ok(cell) => eval(cell, env),
        Err(_) => Cell::Symbol("error".to_string()),
    }
}

// TODO: move tests in separate file
#[test]
fn eval_returns_pi() {
    let t = Cell::Symbol("pi".to_string());
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.14159265);
}

#[test]
fn eval_sum() {
    let args = vec![Cell::Symbol("+".to_string()), Cell::Number(2.0), Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.0);

    let args = vec![Cell::Symbol("+".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 6.0);
}

#[test]
fn eval_sub() {
    let args = vec![Cell::Symbol("-".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => -10.0,
    };

    assert_eq!(res, 0.0);
}
<commit_msg>Implemented "do" keyword<commit_after>use lisp::lex::{tokenize, parse_form};
use lisp::cell::Cell;
use lisp::env::Environment;

pub fn eval(ast: Cell, env: &mut Environment) -> Cell {
    match ast {
        Cell::Symbol(name) => lookup_symbol(name, env),
        Cell::List(tokens) => eval_list(tokens, env),
        _ => ast,
    }
}

// Looks up the symbol in the environment, returning the associated value
fn lookup_symbol(name: String, env: &Environment) -> Cell {
    match env.lookup(&name) {
        Some(cell) => {
            match cell {
                &Cell::Number(n) => Cell::Number(n),
                &Cell::Symbol(ref n) => Cell::Symbol(n.to_string()),
                _ => Cell::Nil,
            }
        }
        _ => Cell::Nil,
    }
}

// Evaluates the list
// if the first element is a function or keyword, it executes that, otherwise returns
// the list itself
fn eval_list(mut tokens: Vec<Cell>, env: &mut Environment) -> Cell {
    // empty list -> no action
    if tokens.is_empty() {
        return Cell::List(tokens);
    }

    let first = tokens.remove(0);

    if let Cell::Symbol(name) = first {

        if let Some(&Cell::Proc(function)) = env.lookup(&name) {
            // eager eval: each of the arguments is evaluated before calling
            let mut args: Vec<Cell> = Vec::new();
            for arg in tokens {
                args.push(eval(arg, env));
            }

            function(Cell::List(args))
        } else {
            eval_core(name.as_ref(), &mut tokens, env)
        }
    } else {
        // first is not a symbol, it's a regular list
        tokens.insert(0, first);
        Cell::List(tokens)
    }
}

fn eval_core(keyword: &str, args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    match keyword {
        "do" => eval_do(args, env),
        "quote" => eval_quote(args),
        "if" => eval_if(args, env),
        "def!" => eval_def(args, env),
        _ => Cell::Nil,
    }
}

// Implementation for def
// usage: (def! name value ...)
fn eval_def(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    // Check for a symbol as first argument
    if let Cell::Symbol(name) = args.remove(0) {
        let t = eval(args.remove(0), env);
        env.insert(name, t);
    }

    Cell::Nil
}

fn eval_do(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    while args.len() > 1 {
        let term = args.remove(0);
        eval(term, env);
    }

    // eval and return last element
    eval(args.remove(0), env)
}

// Implementation for quote
// usage: (quote value ...)
fn eval_quote(args: &mut Vec<Cell>) -> Cell {
    args.remove(0)
}

// Implementation for if
// usage: (if test eval_if_true [eval_if_false])
fn eval_if(args: &mut Vec<Cell>, env: &mut Environment) -> Cell {
    let has_else = if args.len() == 3 {
        true
    } else {
        false
    };
    let condition = args.remove(0);

    match eval(condition, env) {
        Cell::True => eval(args.remove(0), env),
        Cell::False if has_else => eval(args.remove(1), env),
        _ => Cell::Nil,
    }
}

// Utility function
pub fn parse_and_eval(input: &str, env: &mut Environment) -> Cell {
    let mut tokens = tokenize(input);
    let tree = parse_form(&mut tokens);
    // TODO: have descriptive error messages
    match tree {
        Ok(cell) => eval(cell, env),
        Err(_) => Cell::Symbol("error".to_string()),
    }
}

// TODO: move tests in separate file
#[test]
fn eval_returns_pi() {
    let t = Cell::Symbol("pi".to_string());
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.14159265);
}

#[test]
fn eval_sum() {
    let args = vec![Cell::Symbol("+".to_string()), Cell::Number(2.0), Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 3.0);

    let args = vec![Cell::Symbol("+".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => 0.0,
    };

    assert_eq!(res, 6.0);
}

#[test]
fn eval_sub() {
    let args = vec![Cell::Symbol("-".to_string()),
                    Cell::Number(3.0),
                    Cell::Number(2.0),
                    Cell::Number(1.0)];

    let t = Cell::List(args);
    let mut env = Environment::default();

    let res = match eval(t, &mut env) {
        Cell::Number(n) => n,
        _ => -10.0,
    };

    assert_eq!(res, 0.0);
}
<|endoftext|>


<commit_before>use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The Context::switch_to function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = ATOMIC_BOOL_INIT;

#[derive(Clone, Debug)]
pub struct Context {
    /// Page table pointer
    cr3: usize,
    /// RFLAGS register
    rflags: usize,
    /// RBX register
    rbx: usize,
    /// R12 register
    r12: usize,
    /// R13 register
    r13: usize,
    /// R14 register
    r14: usize,
    /// R15 register
    r15: usize,
    /// Base pointer
    rbp: usize,
    /// Stack pointer
    rsp: usize
}

impl Context {
    pub fn new() -> Context {
        Context {
            cr3: 0,
            rflags: 0,
            rbx: 0,
            r12: 0,
            r13: 0,
            r14: 0,
            r15: 0,
            rbp: 0,
            rsp: 0
        }
    }

    pub fn set_page_table(&mut self, address: usize) {
        self.cr3 = address;
    }

    pub fn set_stack(&mut self, address: usize) {
        self.rsp = address;
    }

    /// Switch to the next context by restoring its stack and registers
    #[inline(never)]
    #[naked]
    pub unsafe fn switch_to(&mut self, next: &mut Context) {
/*
        asm!("fxsave [$0]" : : "r"(self.fx) : "memory" : "intel", "volatile");
        self.loadable = true;
        if next.loadable {
            asm!("fxrstor [$0]" : : "r"(next.fx) : "memory" : "intel", "volatile");
        }else{
            asm!("fninit" : : : "memory" : "intel", "volatile");
        }
*/

        asm!("mov $0, cr3" : "=r"(self.cr3) : : "memory" : "intel", "volatile");
        if next.cr3 != self.cr3 {
            asm!("mov cr3, $0" : : "r"(next.cr3) : "memory" : "intel", "volatile");
        }

        asm!("pushfq ; pop $0" : "=r"(self.rflags) : : "memory" : "intel", "volatile");
        asm!("push $0 ; popfq" : : "r"(next.rflags) : "memory" : "intel", "volatile");

        asm!("mov $0, rbx" : "=r"(self.rbx) : : "memory" : "intel", "volatile");
        asm!("mov rbx, $0" : : "r"(next.rbx) : "memory" : "intel", "volatile");

        asm!("mov $0, r12" : "=r"(self.r12) : : "memory" : "intel", "volatile");
        asm!("mov r12, $0" : : "r"(next.r12) : "memory" : "intel", "volatile");

        asm!("mov $0, r13" : "=r"(self.r13) : : "memory" : "intel", "volatile");
        asm!("mov r13, $0" : : "r"(next.r13) : "memory" : "intel", "volatile");

        asm!("mov $0, r14" : "=r"(self.r14) : : "memory" : "intel", "volatile");
        asm!("mov r14, $0" : : "r"(next.r14) : "memory" : "intel", "volatile");

        asm!("mov $0, r15" : "=r"(self.r15) : : "memory" : "intel", "volatile");
        asm!("mov r15, $0" : : "r"(next.r15) : "memory" : "intel", "volatile");

        asm!("mov $0, rsp" : "=r"(self.rsp) : : "memory" : "intel", "volatile");
        asm!("mov rsp, $0" : : "r"(next.rsp) : "memory" : "intel", "volatile");

        asm!("mov $0, rbp" : "=r"(self.rbp) : : "memory" : "intel", "volatile");
        asm!("mov rbp, $0" : : "r"(next.rbp) : "memory" : "intel", "volatile");

        // Unset global lock, set inside of kernel
        CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
    }
}
<commit_msg>Unlock context switch lock without potentially screwing up switch_to function<commit_after>use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The Context::switch_to function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = ATOMIC_BOOL_INIT;

#[derive(Clone, Debug)]
pub struct Context {
    /// Page table pointer
    cr3: usize,
    /// RFLAGS register
    rflags: usize,
    /// RBX register
    rbx: usize,
    /// R12 register
    r12: usize,
    /// R13 register
    r13: usize,
    /// R14 register
    r14: usize,
    /// R15 register
    r15: usize,
    /// Base pointer
    rbp: usize,
    /// Stack pointer
    rsp: usize
}

impl Context {
    pub fn new() -> Context {
        Context {
            cr3: 0,
            rflags: 0,
            rbx: 0,
            r12: 0,
            r13: 0,
            r14: 0,
            r15: 0,
            rbp: 0,
            rsp: 0
        }
    }

    pub fn set_page_table(&mut self, address: usize) {
        self.cr3 = address;
    }

    pub fn set_stack(&mut self, address: usize) {
        self.rsp = address;
    }

    /// Switch to the next context by restoring its stack and registers
    #[cold]
    #[inline(never)]
    #[naked]
    pub unsafe fn switch_to(&mut self, next: &mut Context) {
/*
        asm!("fxsave [$0]" : : "r"(self.fx) : "memory" : "intel", "volatile");
        self.loadable = true;
        if next.loadable {
            asm!("fxrstor [$0]" : : "r"(next.fx) : "memory" : "intel", "volatile");
        }else{
            asm!("fninit" : : : "memory" : "intel", "volatile");
        }
*/

        asm!("mov $0, cr3" : "=r"(self.cr3) : : "memory" : "intel", "volatile");
        if next.cr3 != self.cr3 {
            asm!("mov cr3, $0" : : "r"(next.cr3) : "memory" : "intel", "volatile");
        }

        asm!("pushfq ; pop $0" : "=r"(self.rflags) : : "memory" : "intel", "volatile");
        asm!("push $0 ; popfq" : : "r"(next.rflags) : "memory" : "intel", "volatile");

        asm!("mov $0, rbx" : "=r"(self.rbx) : : "memory" : "intel", "volatile");
        asm!("mov rbx, $0" : : "r"(next.rbx) : "memory" : "intel", "volatile");

        asm!("mov $0, r12" : "=r"(self.r12) : : "memory" : "intel", "volatile");
        asm!("mov r12, $0" : : "r"(next.r12) : "memory" : "intel", "volatile");

        asm!("mov $0, r13" : "=r"(self.r13) : : "memory" : "intel", "volatile");
        asm!("mov r13, $0" : : "r"(next.r13) : "memory" : "intel", "volatile");

        asm!("mov $0, r14" : "=r"(self.r14) : : "memory" : "intel", "volatile");
        asm!("mov r14, $0" : : "r"(next.r14) : "memory" : "intel", "volatile");

        asm!("mov $0, r15" : "=r"(self.r15) : : "memory" : "intel", "volatile");
        asm!("mov r15, $0" : : "r"(next.r15) : "memory" : "intel", "volatile");

        asm!("mov $0, rsp" : "=r"(self.rsp) : : "memory" : "intel", "volatile");
        asm!("mov rsp, $0" : : "r"(next.rsp) : "memory" : "intel", "volatile");

        asm!("mov $0, rbp" : "=r"(self.rbp) : : "memory" : "intel", "volatile");
        asm!("mov rbp, $0" : : "r"(next.rbp) : "memory" : "intel", "volatile");

        asm!("call context_switch_unlock" : : : "memory" : "intel", "volatile");
    }
}

/// Unset global lock, set inside of kernel
#[no_mangle]
extern fn context_switch_unlock(){
    CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
}
<|endoftext|>


<commit_before>
// rustyVM - Java VM written in pure Rust
// Copyright (c) 2013 Alexander Gessler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

use std::hashmap::{HashMap};

use std::sync::atomics::{atomic_add, AcqRel};

use std::ptr;

use thread::{ThreadContext};
use object::{JavaObject, JavaObjectId};
use class::{JavaClassRef};
use objectbroker::*;

// LocalHeap is a thread-local utility for threads to create,
// destroy and access Java objects. Even though it is technically
// not a heap (actual heap management is forwarded to Rust's
// runtime heap manager), it is referred to as such because of it
// behaviour which is to provide the Java heap.
//
// LocalHeap is tightly 1:1 coupled to a ThreadContext.
// ThreadContext forwards all OB_RQ messages that it receives from
// ObjectBroker to LocalHeap.

pub struct LocalHeap {
	// backref to owning thread. Unfortunately a borrowed ref
	// cannot solve this so we need an unsafe pointer.
	// http://stackoverflow.com/questions/20698384
	priv thread : *mut ThreadContext,

	// shortcut to thread-id
	priv tid : uint,

	// heap objects currently owned by this thread context
	priv owned_objects : HashMap<JavaObjectId, ~JavaObject>,

	//
}

static LH_INITIAL_OBJ_HASHMAP_CAPACITY : uint = 1024;
static mut ObjectIdCounter : JavaObjectId = 0;

impl LocalHeap  {

	// ----------------------------------------------
	pub fn dummy() -> LocalHeap {
		LocalHeap {
			thread : ptr::mut_null(),
			tid : 0,
			owned_objects : HashMap::new(),
		}
	}

	// ----------------------------------------------
	pub unsafe fn new_with_owner(t : &mut ThreadContext) -> LocalHeap {
		LocalHeap {
			thread : ptr::to_mut_unsafe_ptr(t),
			tid : t.get_tid(),

			owned_objects : HashMap::with_capacity(LH_INITIAL_OBJ_HASHMAP_CAPACITY),
		}
	}

	// ----------------------------------------------
	#[inline]
	fn get_thread<'t>(&'t self) -> &'t ThreadContext {
		unsafe { &*self.thread }
	}

	#[inline]
	fn get_thread_mut<'t>(&'t self) -> &'t mut ThreadContext {
		unsafe { &mut *self.thread }
	}

	// ----------------------------------------------
	pub fn new_object(&mut self, jclass : JavaClassRef) -> JavaObjectId {
		// generate an unique object id
		let id = unsafe {
			atomic_add(&mut ObjectIdCounter, 1, AcqRel)
		};

		// this id must be unique - if not, we ran out of
		// 64bit indices ("impossible - our shields cannot be
		// broken") or there is a logic flaw somewhere.
		assert!(!self.owned_objects.contains_key(&id));

		// tell the object broker to ensure other threads
		// can request the object by its oid
		let op = OB_REMOTE_OBJECT_OP(self.tid, id,REMOTE_ADD_REF);

		self.get_thread_mut().send_message(op);
		self.owned_objects.insert(id, ~JavaObject::new(jclass, id));
		id
	}

	// ----------------------------------------------
	pub fn new_array_object() {
		// TODO
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn add_ref(&mut self, oid : JavaObjectId) {
		// if this is a local object, addref it
		match self.owned_objects.find_mut(&oid) {
			Some(obj) => {
				obj.intern_add_ref();
				return
			},
			// fallthru
			None => ()
		}
		// forward request to ObjectBroker for remote objects
		let op = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_ADD_REF);
		self.get_thread().send_message(op);
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn release(&mut self, oid : JavaObjectId) {
		// forward request to ObjectBroker for remote objects
		if !self.owned_objects.contains_key(&oid) {
			let op = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_RELEASE);
			self.get_thread().send_message(op);
			return;
		}

		{
			// if this is a local object, release it
			let m = self.owned_objects.find_mut(&oid).unwrap();
			if m.intern_release() {
				return;
			}
		}

		// the object's reference counter reached zero
		// and we can therefore safely drop it.
		self.owned_objects.pop(&oid);
	}

	// ----------------------------------------------
	// Access a specific object. If the object requested is owned (or
	// locked, depending on the access mode requested) by the current
	// thread, access is immediately granted, otherwise the current
	// task blocks until ownership can be obtained.
	//
	// The `access` parameter specifies the kind of access requested on
	// the object. Note that OBJECT_ACCESS_NORMAL is always granted unless
	// the thread who currently owns that object is deadlocked and any
	// of the MONITOR_ access modes can be a cause of deadlock.
	//
	// TODO: how do we deal with deadlocks in general?
	//
	// The closure passed in is called exactly once with a borrowed ref to
	// the object, to which it gets full access but cannot dispose of
	pub fn access_object(&mut self, access : RequestObjectAccessType,
		oid : JavaObjectId, wrap : |&JavaObject| -> ())
	{
		let mut done = false;
		let mut send_to_thread : Option<uint> = None;
		match self.owned_objects.find_mut(&oid) {
			Some(ref obj) => {

				match access {

					OBJECT_ACCESS_Normal => {
						wrap(**obj);
						done = true;
					},
					OBJECT_ACCESS_Monitor | OBJECT_ACCESS_MonitorPriority
						// even if we own the object, somebody else could
						// have the monitor lock.
						if obj.monitor().can_be_locked_by_thread(self.tid) => {
							wrap(**obj);
							done = true;
					},

					// fallthru
					_ => ()
				}

				if done {
					send_to_thread = obj.monitor_mut().pop_ready_thread();
				}
			},
			// fallthru
			None => ()
		}

		// check if we have any pending waiters for the object's monitor,
		// if so, satisfy them immediately. If we did this in
		// handle_pending_messages() [which might make more sense
		// in terms of code hygiene], we would need to maintain shortlist
		// of available objects.
		if done {
			match send_to_thread {
				None => (),
				Some(tid) => {
					self.send_to_thread(oid, tid);
				}
			}
			return;
		}

		// request to own the object
		let op = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_OWN(access));
		self.get_thread().send_message(op);

		// and block until we can get it
		if self.get_thread_mut().handle_messages_until(|msg : &ObjectBrokerMessage| {
			match *msg {
				OB_REMOTE_OBJECT_OP(ref rtid, ref roid, REMOTE_DISOWN(ref obj, ref rec)) => {
					// when waiting for objects, we always block on
					// obtaining them so it is not possible that
					// multiple requests are sent and responses
					// received in a different order.
					assert_eq!(*rec, self.tid);

					// also verify that the access mode requirement is fullfilled
					assert!(access != OBJECT_ACCESS_Monitor ||
						    access != OBJECT_ACCESS_MonitorPriority ||
						    obj.monitor().can_be_locked_by_thread(self.tid)
					);
					true
				},
				_ => false
			}
		}) {
			self.access_object(access, oid, wrap)
		} // else: VM shutdown - we simply ignore the closure
	}

	// ----------------------------------------------
	// Transfer ownership of an object to a particular thread
	pub fn send_to_thread(&mut self, oid : JavaObjectId, tid : uint) {
		let obj = self.owned_objects.pop(&oid).unwrap();
		let m = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_DISOWN(obj, tid));
		self.get_thread().send_message(m);
	}

	// ----------------------------------------------
	// Transfers ownership of the hashmap containing all owned
	// objects to the caller and destroys the LocalHeap
	pub fn unwrap_objects(self) -> HashMap<JavaObjectId,~JavaObject> {
		self.owned_objects
	}

	// ----------------------------------------------
	// Check if a particular object is currently owned by this thread
	pub fn owns(&self, b : JavaObjectId) -> bool {
		return self.owned_objects.contains_key(&b);
	}

	// ----------------------------------------------
	// Handle any of the remote object messages
	// a is the source thread id, and b is the object in question.
	pub fn handle_message(&mut self, a : uint, b : JavaObjectId, op : RemoteObjectOpMessage) {
		// TODO: owns() is not necessarily satisfied if we send back objects without being asked for
		assert!(self.owns(b));
		match op {
			REMOTE_WHO_OWNS => fail!("logic error, WHO_OWNS is not handled by threads"),
			REMOTE_ADD_REF => self.add_ref(b),
			REMOTE_RELEASE => self.release(b),
			REMOTE_OWN(mode) => {
				match mode {
					OBJECT_ACCESS_Monitor | OBJECT_ACCESS_MonitorPriority => {
						let obj = self.owned_objects.get_mut(&b);

						// we should assume that, in order to request Priority access,
						// the sender thread should already own the monitor as is
						// the requirement for calling wait() on an object.
						assert!(mode != OBJECT_ACCESS_MonitorPriority ||
							obj.monitor().is_locked_by_thread(a));

						if !obj.monitor().can_be_locked_by_thread(a) {
							// append the thread to the monitor's waiting queues
							obj.monitor_mut().push_thread(a,
								mode == OBJECT_ACCESS_MonitorPriority
							);

							return;
						}
					},
					// fallthru
					_ => (),
				}
				self.send_to_thread(b, a);
			},

			REMOTE_DISOWN(obj,rec) => {
				// currently we should not be receiving objects that we
				// did not request using OB_RQ_OWN
				assert_eq!(rec, self.tid);
				self.owned_objects.insert(b, obj);
			},
		}
	}
}
<commit_msg>Add JavaStrongObjectRef utility<commit_after>
// rustyVM - Java VM written in pure Rust
// Copyright (c) 2013 Alexander Gessler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

use std::hashmap::{HashMap};

use std::sync::atomics::{atomic_add, AcqRel};

use std::ptr;

use thread::{ThreadContext};
use object::{JavaObject, JavaObjectId};
use class::{JavaClassRef};
use objectbroker::*;

// LocalHeap is a thread-local utility for threads to create,
// destroy and access Java objects. Even though it is technically
// not a heap (actual heap management is forwarded to Rust's
// runtime heap manager), it is referred to as such because of it
// behaviour which is to provide the Java heap.
//
// LocalHeap is tightly 1:1 coupled to a ThreadContext.
// ThreadContext forwards all OB_RQ messages that it receives from
// ObjectBroker to LocalHeap.

pub struct LocalHeap {
	// backref to owning thread. Unfortunately a borrowed ref
	// cannot solve this so we need an unsafe pointer.
	// http://stackoverflow.com/questions/20698384
	priv thread : *mut ThreadContext,

	// shortcut to thread-id
	priv tid : uint,

	// heap objects currently owned by this thread context
	priv owned_objects : HashMap<JavaObjectId, ~JavaObject>,

	//
}

static LH_INITIAL_OBJ_HASHMAP_CAPACITY : uint = 1024;
static mut ObjectIdCounter : JavaObjectId = 0;

impl LocalHeap  {

	// ----------------------------------------------
	pub fn dummy() -> LocalHeap {
		LocalHeap {
			thread : ptr::mut_null(),
			tid : 0,
			owned_objects : HashMap::new(),
		}
	}

	// ----------------------------------------------
	pub unsafe fn new_with_owner(t : &mut ThreadContext) -> LocalHeap {
		LocalHeap {
			thread : ptr::to_mut_unsafe_ptr(t),
			tid : t.get_tid(),

			owned_objects : HashMap::with_capacity(LH_INITIAL_OBJ_HASHMAP_CAPACITY),
		}
	}

	// ----------------------------------------------
	#[inline]
	fn get_thread<'t>(&'t self) -> &'t ThreadContext {
		unsafe { &*self.thread }
	}

	#[inline]
	fn get_thread_mut<'t>(&'t self) -> &'t mut ThreadContext {
		unsafe { &mut *self.thread }
	}

	// ----------------------------------------------
	pub fn new_object(&mut self, jclass : JavaClassRef) -> JavaObjectId {
		// generate an unique object id
		let id = unsafe {
			atomic_add(&mut ObjectIdCounter, 1, AcqRel)
		};

		// this id must be unique - if not, we ran out of
		// 64bit indices ("impossible - our shields cannot be
		// broken") or there is a logic flaw somewhere.
		assert!(!self.owned_objects.contains_key(&id));

		// tell the object broker to ensure other threads
		// can request the object by its oid
		let op = OB_REMOTE_OBJECT_OP(self.tid, id,REMOTE_ADD_REF);

		self.get_thread_mut().send_message(op);
		self.owned_objects.insert(id, ~JavaObject::new(jclass, id));
		id
	}

	// ----------------------------------------------
	pub fn new_array_object() {
		// TODO
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn add_ref(&mut self, oid : JavaObjectId) {
		// if this is a local object, addref it
		match self.owned_objects.find_mut(&oid) {
			Some(obj) => {
				obj.intern_add_ref();
				return
			},
			// fallthru
			None => ()
		}
		// forward request to ObjectBroker for remote objects
		let op = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_ADD_REF);
		self.get_thread().send_message(op);
	}

	// ----------------------------------------------
	// AddRef a specific java object. This works both for local
	// objects (i.e. owned by current thread) and for remote
	// objects.
	pub fn release(&mut self, oid : JavaObjectId) {
		// forward request to ObjectBroker for remote objects
		if !self.owned_objects.contains_key(&oid) {
			let op = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_RELEASE);
			self.get_thread().send_message(op);
			return;
		}

		{
			// if this is a local object, release it
			let m = self.owned_objects.find_mut(&oid).unwrap();
			if m.intern_release() {
				return;
			}
		}

		// the object's reference counter reached zero
		// and we can therefore safely drop it.
		self.owned_objects.pop(&oid);
	}

	// ----------------------------------------------
	// Access a specific object. If the object requested is owned (or
	// locked, depending on the access mode requested) by the current
	// thread, access is immediately granted, otherwise the current
	// task blocks until ownership can be obtained.
	//
	// The caller is required to ensure that the object requested remains
	// alive until control returns. This can be achieved e.g. by holding
	// a strong reference to it.
	//
	// The `access` parameter specifies the kind of access requested on
	// the object. Note that OBJECT_ACCESS_NORMAL is always granted unless
	// the thread who currently owns that object is deadlocked and any
	// of the MONITOR_ access modes can be a cause of deadlock.
	//
	// TODO: how do we deal with deadlocks in general?
	//
	// The closure passed in is called exactly once with a borrowed ref to
	// the object, to which it gets full access but cannot dispose of
	pub fn access_object(&mut self, access : RequestObjectAccessType,
		oid : JavaObjectId, wrap : |&JavaObject| -> ())
	{
		let mut done = false;
		let mut send_to_thread : Option<uint> = None;
		match self.owned_objects.find_mut(&oid) {
			Some(ref obj) => {

				match access {

					OBJECT_ACCESS_Normal => {
						wrap(**obj);
						done = true;
					},
					OBJECT_ACCESS_Monitor | OBJECT_ACCESS_MonitorPriority
						// even if we own the object, somebody else could
						// have the monitor lock.
						if obj.monitor().can_be_locked_by_thread(self.tid) => {
							wrap(**obj);
							done = true;
					},

					// fallthru
					_ => ()
				}

				if done {
					send_to_thread = obj.monitor_mut().pop_ready_thread();
				}
			},
			// fallthru
			None => ()
		}

		// check if we have any pending waiters for the object's monitor,
		// if so, satisfy them immediately. If we did this in
		// handle_pending_messages() [which might make more sense
		// in terms of code hygiene], we would need to maintain shortlist
		// of available objects.
		if done {
			match send_to_thread {
				None => (),
				Some(tid) => {
					self.send_to_thread(oid, tid);
				}
			}
			return;
		}

		// request to own the object
		let op = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_OWN(access));
		self.get_thread().send_message(op);

		// and block until we can get it
		if self.get_thread_mut().handle_messages_until(|msg : &ObjectBrokerMessage| {
			match *msg {
				OB_REMOTE_OBJECT_OP(ref rtid, ref roid, REMOTE_DISOWN(ref obj, ref rec)) => {
					// when waiting for objects, we always block on
					// obtaining them so it is not possible that
					// multiple requests are sent and responses
					// received in a different order.
					assert_eq!(*rec, self.tid);

					// also verify that the access mode requirement is fullfilled
					assert!(access != OBJECT_ACCESS_Monitor ||
						    access != OBJECT_ACCESS_MonitorPriority ||
						    obj.monitor().can_be_locked_by_thread(self.tid)
					);
					true
				},
				_ => false
			}
		}) {
			self.access_object(access, oid, wrap)
		} // else: VM shutdown - we simply ignore the closure
	}

	// ----------------------------------------------
	// Transfer ownership of an object to a particular thread
	pub fn send_to_thread(&mut self, oid : JavaObjectId, tid : uint) {
		let obj = self.owned_objects.pop(&oid).unwrap();
		let m = OB_REMOTE_OBJECT_OP(self.tid, oid, REMOTE_DISOWN(obj, tid));
		self.get_thread().send_message(m);
	}

	// ----------------------------------------------
	// Transfers ownership of the hashmap containing all owned
	// objects to the caller and destroys the LocalHeap
	pub fn unwrap_objects(self) -> HashMap<JavaObjectId,~JavaObject> {
		self.owned_objects
	}

	// ----------------------------------------------
	// Check if a particular object is currently owned by this thread
	pub fn owns(&self, b : JavaObjectId) -> bool {
		return self.owned_objects.contains_key(&b);
	}

	// ----------------------------------------------
	// Handle any of the remote object messages
	// a is the source thread id, and b is the object in question.
	pub fn handle_message(&mut self, a : uint, b : JavaObjectId, op : RemoteObjectOpMessage) {
		// TODO: owns() is not necessarily satisfied if we send back objects without being asked for
		assert!(self.owns(b));
		match op {
			REMOTE_WHO_OWNS => fail!("logic error, WHO_OWNS is not handled by threads"),
			REMOTE_ADD_REF => self.add_ref(b),
			REMOTE_RELEASE => self.release(b),
			REMOTE_OWN(mode) => {
				match mode {
					OBJECT_ACCESS_Monitor | OBJECT_ACCESS_MonitorPriority => {
						let obj = self.owned_objects.get_mut(&b);

						// we should assume that, in order to request Priority access,
						// the sender thread should already own the monitor as is
						// the requirement for calling wait() on an object.
						assert!(mode != OBJECT_ACCESS_MonitorPriority ||
							obj.monitor().is_locked_by_thread(a));

						if !obj.monitor().can_be_locked_by_thread(a) {
							// append the thread to the monitor's waiting queues
							obj.monitor_mut().push_thread(a,
								mode == OBJECT_ACCESS_MonitorPriority
							);

							return;
						}
					},
					// fallthru
					_ => (),
				}
				self.send_to_thread(b, a);
			},

			REMOTE_DISOWN(obj,rec) => {
				// currently we should not be receiving objects that we
				// did not request using OB_RQ_OWN
				assert_eq!(rec, self.tid);
				self.owned_objects.insert(b, obj);
			},
		}
	}
}

// A JavaStrongObjectRef is a reference to a Java object that guarantees
// that the referenced objects stays alive for at least the lifetime of
// the reference.

pub struct JavaStrongObjectRef
{
	priv jid : JavaObjectId,
	priv heapref : *mut LocalHeap,
}

impl JavaStrongObjectRef {

	// ----------------------------------------------
	// Construct a strong ref for the given object id
	// note: this triggers a remote add-ref on that object. If the
	// caller's intent is to access the object immediately anyway,
	// it is better not to construct a strong ref but to use
	// heap::access_object().
	//
	// It is assumed that the given localheap remains alive for the entire
	// duration of the object reference.
	pub fn new(jid : JavaObjectId, heap : &mut LocalHeap) -> JavaStrongObjectRef {
		heap.add_ref(jid);
		JavaStrongObjectRef {
			jid : jid,
			heapref : unsafe { heap }
		}
	}
}

impl Drop for JavaStrongObjectRef {
	fn drop(&mut self) {
		let ref mut heap = unsafe { &mut (*self.heapref) };
		heap.release(self.jid);
	}
}
<|endoftext|>