<commit_before>use std::collections::{HashSet, HashMap};
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use rand::Rng;
use tar::Archive;
use spiral::Spiral;
use dfa::{Dfa, DfaState};
use player::Player;
use cell::Cell;
use field::{Pos, Field};

const PATTERNS_STR: &'static str = "patterns";

#[derive(Clone, Debug)]
struct Move {
  x: u32,
  y: u32,
  p: f64 // probability
}

struct Pattern {
  p: f64, // priority
  width: u32,
  height: u32,
  moves: Vec<Move>
}

pub struct Patterns {
  min_size: u32,
  dfa: Dfa,
  patterns: Vec<Pattern>
}

impl Patterns {
  pub fn empty() -> Patterns {
    Patterns {
      min_size: u32::max_value(),
      dfa: Dfa::empty(),
      patterns: Vec::with_capacity(0)
    }
  }

  fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
    s.clear();
    input.read_line(s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32.");
    let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
    let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
    let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
    (width, height, moves_count, priority)
  }

  fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
    s.clear();
    for _ in 0 .. height {
      let last_len = s.len();
      input.read_line(s).ok();
      s.pop();
      assert_eq!((s.len() - last_len) as u32, width)
    }
  }

  fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
    let mut moves = Vec::with_capacity(moves_count as usize);
    let mut priorities_sum = 0f64;
    for _ in 0 .. moves_count {
      s.clear();
      input.read_line(s).ok();
      s.pop();
      let mut split = s.split(' ').fuse();
      let x = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let y = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let p = f64::from_str(split.next().expect("Invalid pattern format: expected probability.")).expect("Invalid pattern format: probability must be f64.");
      priorities_sum += p;
      moves.push(Move {
        x: x,
        y: y,
        p: p
      });
    }
    for m in &mut moves {
      m.p /= priorities_sum;
    }
    moves
  }

  fn covering_spiral_length(side_of_square: u32) -> u32 {
    let x = side_of_square / 2 + 1;
    let y = (1 - side_of_square % 2) * side_of_square * 2;
    (8 * x - 13) * x + 6 - y
  }

  fn rotate(width: u32, height: u32, x: i32, y: i32, rotation: u32) -> (i32, i32) {
    match rotation {
      0 => (x, y),
      1 => (width as i32 - x - 1, y),
      2 => (x, height as i32 - y - 1),
      3 => (width as i32 - x - 1, height as i32 - y - 1),
      4 => (y, x),
      5 => (height as i32 - y - 1, x),
      6 => (y, width as i32 - x - 1),
      7 => (height as i32 - y - 1, width as i32 - x - 1),
      r => panic!("Invalid rotation number: {}", r)
    }
  }

  fn rotate_back(width: u32, height: u32, x: i32, y: i32, rotation: u32) -> (i32, i32) {
    let back_rotation = match rotation {
      5 => 6,
      6 => 5,
      r => r
    };
    Patterns::rotate(width, height, x, y, back_rotation)
  }

  fn rotate_sizes(width: u32, height: u32, rotation: u32) -> (u32, u32) {
    if rotation < 4 {
      (width, height)
    } else {
      (height, width)
    }
  }

  fn build_dfa(name: &str, width: u32, height: u32, pattern: usize, rotation: u32, s: &str) -> Dfa {
    let (rotated_width, rotated_height) = Patterns::rotate_sizes(width, height, rotation);
    let center_x = (rotated_width - 1) / 2;
    let center_y = (rotated_height - 1) / 2;
    let spiral_length = Patterns::covering_spiral_length(cmp::max(width, height)) as usize;
    let mut states = Vec::with_capacity(spiral_length + 2);
    let fs = spiral_length; // "Found" state.
    let nfs = spiral_length + 1; // "Not found" state.
    for (i, (shift_x, shift_y)) in Spiral::new().into_iter().take(spiral_length).enumerate() {
      let nxt = i + 1;
      let (x, y) = Patterns::rotate_back(rotated_width, rotated_height, center_x as i32 + shift_x, center_y as i32 + shift_y, rotation);
      let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
        let pos = y as u32 * width + x as u32;
        match s.char_at(pos as usize) {
          '.' => DfaState::new(nxt, nfs, nfs, nfs, false, HashSet::with_capacity(0)),
          '?' => DfaState::new(nxt, nxt, nxt, nfs, false, HashSet::with_capacity(0)),
          '*' => DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0)),
          'R' => DfaState::new(nfs, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'B' => DfaState::new(nfs, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          'r' => DfaState::new(nxt, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'b' => DfaState::new(nxt, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          '#' => DfaState::new(nfs, nfs, nfs, nxt, false, HashSet::with_capacity(0)),
          c   => panic!("Invalid character in the pattern '{}': {}", name, c)
        }
      } else {
        DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0))
      };
      states.push(state);
    }
    let mut c = 0;
    for state in states.iter().rev().take(spiral_length - 1) {
      if state.empty == nfs || state.red == nfs || state.black == nfs || state.bad == nfs {
        break;
      }
      c += 1;
    }
    let new_fs = fs - c;
    let new_nfs = nfs - c;
    if c > 0 {
      states.truncate(spiral_length - c);
      for state in &mut states {
        if state.empty == nfs {
          state.empty = new_nfs;
        }
        if state.red == nfs {
          state.red = new_nfs;
        }
        if state.black == nfs {
          state.black = new_nfs;
        }
        if state.bad == nfs {
          state.bad = new_nfs;
        }
      }
    }
    let mut patterns = HashSet::with_capacity(1);
    patterns.insert(pattern);
    states.push(DfaState::new(new_fs, new_fs, new_fs, new_fs, true, patterns));
    states.push(DfaState::new(new_nfs, new_nfs, new_nfs, new_nfs, true, HashSet::with_capacity(0)));
    Dfa::new(states)
  }

  pub fn load(file: File) -> Patterns {
    let archive = Archive::new(file);
    let mut s = String::new();
    let mut pattern_s = String::new();
    let mut patterns = Vec::new();
    let iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
    let mut dfa = Dfa::empty();
    let mut min_size = u32::max_value();
    for file in iter {
      let name = file.header().path().ok()
        .and_then(|path| path.to_str().map(|s| s.to_owned()))
        .unwrap_or_else(|| "<unknown>".to_owned());
      info!(target: PATTERNS_STR, "Loading pattern '{}'", name);
      let mut input = BufReader::new(file);
      let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
      if width < min_size {
        min_size = width;
      }
      if height < min_size {
        min_size = height;
      }
      Patterns::read_pattern(&mut input, &mut pattern_s, width, height);
      let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
      for rotation in 0 .. 8 {
        let cur_dfa = Patterns::build_dfa(name.as_str(), width, height, patterns.len(), rotation, &pattern_s);
        dfa = dfa.product(&cur_dfa);
        info!(target: PATTERNS_STR, "DFA total length {}.", dfa.states_count());
        let (rotated_width, rotated_height) = Patterns::rotate_sizes(width, height, rotation);
        patterns.push(Pattern {
          p: priority,
          width: rotated_width,
          height: rotated_height,
          moves: moves.iter().map(|m| {
            let (x, y) = Patterns::rotate(width, height, m.x as i32, m.y as i32, rotation);
            Move {
              x: x as u32,
              y: y as u32,
              p: m.p
            }
          }).collect()
        });
      }
    }
    Patterns {
      min_size: min_size,
      dfa: dfa,
      patterns: patterns
    }
  }

  pub fn find(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    if self.dfa.is_empty() {
      return Vec::with_capacity(0);
    }
    let mut priorities_sum = 0f64;
    let mut moves_count = 0usize;
    let mut matched = Vec::new();
    let left_border = (self.min_size - 1) / 2;
    let right_border = self.min_size / 2;
    let inv_color = player == Player::Black;
    for y in left_border .. field.height() - right_border {
      for x in left_border .. field.width() - right_border {
        let patterns = self.dfa.run(&mut Spiral::new().into_iter().map(|(shift_x, shift_y)| {
          let cur_x = x as i32 + shift_x;
          let cur_y = y as i32 + shift_y;
          if cur_x >= 0 && cur_x < field.width() as i32 && cur_y >= 0 && cur_y < field.height() as i32 {
            let pos = field.to_pos(cur_x as u32, cur_y as u32);
            field.cell(pos)
          } else {
            Cell::new(true)
          }
        }), inv_color, first_match);
        for &pattern_number in patterns {
          let pattern = &self.patterns[pattern_number];
          info!(target: PATTERNS_STR, "Found pattern {} ({} with rotation {}) at ({}, {}).", pattern_number, pattern_number / 8, pattern_number % 8, x - (pattern.width - 1) / 2, y -(pattern.height - 1) / 2);
          priorities_sum += pattern.p;
          moves_count += pattern.moves.len();
          matched.push((pattern_number, x, y));
        }
      }
    }
    let mut result = Vec::with_capacity(moves_count);
    for (pattern_number, center_x, center_y) in matched {
      let pattern = &self.patterns[pattern_number];
      for &Move { x, y, p: probability } in &pattern.moves {
        let move_x = center_x - (pattern.width - 1) / 2 + x;
        let move_y = center_y - (pattern.height - 1) / 2 + y;
        result.push((field.to_pos(move_x, move_y), probability * pattern.p / priorities_sum));
      }
    }
    info!(target: PATTERNS_STR, "Found moves: {:?}.", result.iter().map(|&(pos, p)| (field.to_x(pos), field.to_y(pos), p)).collect::<Vec<(u32, u32, f64)>>());
    result
  }

  pub fn find_sorted(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    let moves = self.find(field, player, first_match);
    let mut map = HashMap::with_capacity(moves.len());
    for (pos, p) in moves {
      let sum_p = map.get(&pos).cloned().unwrap_or(0f64) + p;
      map.insert(pos, sum_p);
    }
    let mut result = map.into_iter().collect::<Vec<(Pos, f64)>>();
    result.sort_by(|&(_, a), &(_, b)| b.partial_cmp(&a).expect("Cann't compare f64 types."));
    info!(target: PATTERNS_STR, "Found sorted moves: {:?}.", result.iter().map(|&(pos, p)| (field.to_x(pos), field.to_y(pos), p)).collect::<Vec<(u32, u32, f64)>>());
    result
  }

  pub fn find_foreground(&self, field: &Field, player: Player, first_match: bool) -> Option<Pos> {
    self.find_sorted(field, player, first_match).first().map(|&(pos, _)| pos)
  }

  pub fn find_rand<T: Rng>(&self, field: &Field, player: Player, first_match: bool, rng: &mut T) -> Option<Pos> {
    let moves = self.find_sorted(field, player, first_match);
    if moves.is_empty() {
      return None;
    }
    let rand = rng.gen();
    let mut sum = 0f64;
    let mut idx = 0;
    while sum < rand && idx < moves.len() {
      sum += moves[idx].1;
      idx += 1;
    }
    Some(moves[idx - 1].0)
  }
}
<commit_msg>Small fix.<commit_after>use std::collections::{HashSet, HashMap};
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use rand::Rng;
use tar::Archive;
use spiral::Spiral;
use dfa::{Dfa, DfaState};
use player::Player;
use cell::Cell;
use field::{Pos, Field};

const PATTERNS_STR: &'static str = "patterns";

#[derive(Clone, Debug)]
struct Move {
  x: u32,
  y: u32,
  p: f64 // probability
}

struct Pattern {
  p: f64, // priority
  width: u32,
  height: u32,
  moves: Vec<Move>
}

pub struct Patterns {
  min_size: u32,
  dfa: Dfa,
  patterns: Vec<Pattern>
}

impl Patterns {
  pub fn empty() -> Patterns {
    Patterns {
      min_size: u32::max_value(),
      dfa: Dfa::empty(),
      patterns: Vec::with_capacity(0)
    }
  }

  fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
    s.clear();
    input.read_line(s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32.");
    let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
    let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
    let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
    (width, height, moves_count, priority)
  }

  fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
    s.clear();
    for _ in 0 .. height {
      let last_len = s.len();
      input.read_line(s).ok();
      s.pop();
      assert_eq!((s.len() - last_len) as u32, width)
    }
  }

  fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
    let mut moves = Vec::with_capacity(moves_count as usize);
    let mut priorities_sum = 0f64;
    for _ in 0 .. moves_count {
      s.clear();
      input.read_line(s).ok();
      s.pop();
      let mut split = s.split(' ').fuse();
      let x = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let y = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let p = f64::from_str(split.next().expect("Invalid pattern format: expected probability.")).expect("Invalid pattern format: probability must be f64.");
      priorities_sum += p;
      moves.push(Move {
        x: x,
        y: y,
        p: p
      });
    }
    for m in &mut moves {
      m.p /= priorities_sum;
    }
    moves
  }

  fn covering_spiral_length(side_of_square: u32) -> u32 {
    let x = side_of_square / 2 + 1;
    let y = (1 - side_of_square % 2) * side_of_square * 2;
    (8 * x - 13) * x + 6 - y
  }

  fn rotate(width: u32, height: u32, x: i32, y: i32, rotation: u32) -> (i32, i32) {
    match rotation {
      0 => (x, y),
      1 => (width as i32 - x - 1, y),
      2 => (x, height as i32 - y - 1),
      3 => (width as i32 - x - 1, height as i32 - y - 1),
      4 => (y, x),
      5 => (height as i32 - y - 1, x),
      6 => (y, width as i32 - x - 1),
      7 => (height as i32 - y - 1, width as i32 - x - 1),
      r => panic!("Invalid rotation number: {}", r)
    }
  }

  fn rotate_back(width: u32, height: u32, x: i32, y: i32, rotation: u32) -> (i32, i32) {
    let back_rotation = match rotation {
      5 => 6,
      6 => 5,
      r => r
    };
    Patterns::rotate(width, height, x, y, back_rotation)
  }

  fn rotate_sizes(width: u32, height: u32, rotation: u32) -> (u32, u32) {
    if rotation < 4 {
      (width, height)
    } else {
      (height, width)
    }
  }

  fn build_dfa(name: &str, width: u32, height: u32, pattern: usize, rotation: u32, s: &str) -> Dfa {
    let (rotated_width, rotated_height) = Patterns::rotate_sizes(width, height, rotation);
    let center_x = (rotated_width - 1) / 2;
    let center_y = (rotated_height - 1) / 2;
    let spiral_length = Patterns::covering_spiral_length(cmp::max(width, height)) as usize;
    let mut states = Vec::with_capacity(spiral_length + 2);
    let fs = spiral_length; // "Found" state.
    let nfs = spiral_length + 1; // "Not found" state.
    for (i, (shift_x, shift_y)) in Spiral::new().into_iter().take(spiral_length).enumerate() {
      let nxt = i + 1;
      let (x, y) = Patterns::rotate_back(rotated_width, rotated_height, center_x as i32 + shift_x, center_y as i32 + shift_y, rotation);
      let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
        let pos = y as u32 * width + x as u32;
        match s.char_at(pos as usize) {
          '.' => DfaState::new(nxt, nfs, nfs, nfs, false, HashSet::with_capacity(0)),
          '?' => DfaState::new(nxt, nxt, nxt, nfs, false, HashSet::with_capacity(0)),
          '*' => DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0)),
          'R' => DfaState::new(nfs, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'B' => DfaState::new(nfs, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          'r' => DfaState::new(nxt, nxt, nfs, nfs, false, HashSet::with_capacity(0)),
          'b' => DfaState::new(nxt, nfs, nxt, nfs, false, HashSet::with_capacity(0)),
          '#' => DfaState::new(nfs, nfs, nfs, nxt, false, HashSet::with_capacity(0)),
          c   => panic!("Invalid character in the pattern '{}': {}", name, c)
        }
      } else {
        DfaState::new(nxt, nxt, nxt, nxt, false, HashSet::with_capacity(0))
      };
      states.push(state);
    }
    let mut c = 0;
    for state in states.iter().rev().take(spiral_length - 1) {
      if state.empty == nfs || state.red == nfs || state.black == nfs || state.bad == nfs {
        break;
      }
      c += 1;
    }
    let new_fs = fs - c;
    let new_nfs = nfs - c;
    if c > 0 {
      states.truncate(spiral_length - c);
      for state in &mut states {
        if state.empty == nfs {
          state.empty = new_nfs;
        }
        if state.red == nfs {
          state.red = new_nfs;
        }
        if state.black == nfs {
          state.black = new_nfs;
        }
        if state.bad == nfs {
          state.bad = new_nfs;
        }
      }
    }
    let mut patterns = HashSet::with_capacity(1);
    patterns.insert(pattern);
    states.push(DfaState::new(new_fs, new_fs, new_fs, new_fs, true, patterns));
    states.push(DfaState::new(new_nfs, new_nfs, new_nfs, new_nfs, true, HashSet::with_capacity(0)));
    Dfa::new(states)
  }

  pub fn load(file: File) -> Patterns {
    let archive = Archive::new(file);
    let mut s = String::new();
    let mut pattern_s = String::new();
    let mut patterns = Vec::new();
    let iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
    let mut dfa = Dfa::empty();
    let mut min_size = u32::max_value();
    for file in iter {
      let name = file.header().path().ok()
        .and_then(|path| path.to_str().map(|s| s.to_owned()))
        .unwrap_or_else(|| "<unknown>".to_owned());
      info!(target: PATTERNS_STR, "Loading pattern '{}'", name);
      let mut input = BufReader::new(file);
      let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
      if width < min_size {
        min_size = width;
      }
      if height < min_size {
        min_size = height;
      }
      Patterns::read_pattern(&mut input, &mut pattern_s, width, height);
      let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
      for rotation in 0 .. 8 {
        let cur_dfa = Patterns::build_dfa(name.as_str(), width, height, patterns.len(), rotation, &pattern_s);
        dfa = dfa.product(&cur_dfa);
        info!(target: PATTERNS_STR, "DFA total size {}.", dfa.states_count());
        let (rotated_width, rotated_height) = Patterns::rotate_sizes(width, height, rotation);
        patterns.push(Pattern {
          p: priority,
          width: rotated_width,
          height: rotated_height,
          moves: moves.iter().map(|m| {
            let (x, y) = Patterns::rotate(width, height, m.x as i32, m.y as i32, rotation);
            Move {
              x: x as u32,
              y: y as u32,
              p: m.p
            }
          }).collect()
        });
      }
    }
    Patterns {
      min_size: min_size,
      dfa: dfa,
      patterns: patterns
    }
  }

  pub fn find(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    if self.dfa.is_empty() {
      return Vec::with_capacity(0);
    }
    let mut priorities_sum = 0f64;
    let mut moves_count = 0usize;
    let mut matched = Vec::new();
    let left_border = (self.min_size - 1) / 2;
    let right_border = self.min_size / 2;
    let inv_color = player == Player::Black;
    for y in left_border .. field.height() - right_border {
      for x in left_border .. field.width() - right_border {
        let patterns = self.dfa.run(&mut Spiral::new().into_iter().map(|(shift_x, shift_y)| {
          let cur_x = x as i32 + shift_x;
          let cur_y = y as i32 + shift_y;
          if cur_x >= 0 && cur_x < field.width() as i32 && cur_y >= 0 && cur_y < field.height() as i32 {
            let pos = field.to_pos(cur_x as u32, cur_y as u32);
            field.cell(pos)
          } else {
            Cell::new(true)
          }
        }), inv_color, first_match);
        for &pattern_number in patterns {
          let pattern = &self.patterns[pattern_number];
          info!(target: PATTERNS_STR, "Found pattern {} ({} with rotation {}) at ({}, {}).", pattern_number, pattern_number / 8, pattern_number % 8, x - (pattern.width - 1) / 2, y -(pattern.height - 1) / 2);
          priorities_sum += pattern.p;
          moves_count += pattern.moves.len();
          matched.push((pattern_number, x, y));
        }
      }
    }
    let mut result = Vec::with_capacity(moves_count);
    for (pattern_number, center_x, center_y) in matched {
      let pattern = &self.patterns[pattern_number];
      for &Move { x, y, p: probability } in &pattern.moves {
        let move_x = center_x - (pattern.width - 1) / 2 + x;
        let move_y = center_y - (pattern.height - 1) / 2 + y;
        result.push((field.to_pos(move_x, move_y), probability * pattern.p / priorities_sum));
      }
    }
    info!(target: PATTERNS_STR, "Found moves: {:?}.", result.iter().map(|&(pos, p)| (field.to_x(pos), field.to_y(pos), p)).collect::<Vec<(u32, u32, f64)>>());
    result
  }

  pub fn find_sorted(&self, field: &Field, player: Player, first_match: bool) -> Vec<(Pos, f64)> {
    let moves = self.find(field, player, first_match);
    let mut map = HashMap::with_capacity(moves.len());
    for (pos, p) in moves {
      let sum_p = map.get(&pos).cloned().unwrap_or(0f64) + p;
      map.insert(pos, sum_p);
    }
    let mut result = map.into_iter().collect::<Vec<(Pos, f64)>>();
    result.sort_by(|&(_, a), &(_, b)| b.partial_cmp(&a).expect("Cann't compare f64 types."));
    info!(target: PATTERNS_STR, "Found sorted moves: {:?}.", result.iter().map(|&(pos, p)| (field.to_x(pos), field.to_y(pos), p)).collect::<Vec<(u32, u32, f64)>>());
    result
  }

  pub fn find_foreground(&self, field: &Field, player: Player, first_match: bool) -> Option<Pos> {
    self.find_sorted(field, player, first_match).first().map(|&(pos, _)| pos)
  }

  pub fn find_rand<T: Rng>(&self, field: &Field, player: Player, first_match: bool, rng: &mut T) -> Option<Pos> {
    let moves = self.find_sorted(field, player, first_match);
    if moves.is_empty() {
      return None;
    }
    let rand = rng.gen();
    let mut sum = 0f64;
    let mut idx = 0;
    while sum < rand && idx < moves.len() {
      sum += moves[idx].1;
      idx += 1;
    }
    Some(moves[idx - 1].0)
  }
}
<|endoftext|>


<commit_before>use ram::schema::Schema;
use ram::chunk::Chunk;
use std::ptr;
use ram::io::{reader, writer};
use ram::types::{Map, Value, Id};
use std::collections::HashMap;

const MAX_CELL_SIZE :usize = 1 * 1024 * 1024;

pub type DataValue = Value;
pub type DataMap = Map<Value>;

#[repr(C, packed)]
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct Header {
    pub version: u64,
    pub size: u32,
    pub schema: u32,
    pub partition: u64,
    pub hash: u64,
}

#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub enum WriteError {
    SchemaDoesNotExisted(u32),
    CannotAllocateSpace,
    CellIsTooLarge(usize),
    CellAlreadyExisted,
    CellDoesNotExisted,
    ReadError(ReadError),
    UserCanceledUpdate,
    DeletionPredictionFailed,
    NetworkingError,
}

#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub enum ReadError {
    SchemaDoesNotExisted(u32),
    CellDoesNotExisted,
    NetworkingError,
}

impl Header {
    pub fn new(size: u32, schema: u32, id: &Id) -> Header {
        Header {
            version: 1,
            size: size,
            schema: schema,
            partition: id.higher,
            hash: id.lower,
        }
    }
    pub fn write(&self, location: usize) {
        unsafe {
            ptr::copy_nonoverlapping(self as *const Header, location as *mut Header, HEADER_SIZE);
        }
    }
    pub fn reserve(location: usize, size: usize) {
        Header {
            version: 0, // default a tombstone
            size: size as u32,
            schema: 0,
            hash: 0,
            partition: 0,
        }.write(location);
    }
    pub fn id(&self) -> Id {
        Id {
            higher: self.partition,
            lower: self.hash,
        }
    }
    pub fn set_id(&mut self, id: &Id) {
        self.partition = id.higher;
        self.hash = id.lower;
    }
}

pub const HEADER_SIZE :usize = 32;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Cell {
    pub header: Header,
    pub data: DataValue
}

impl Cell {

    pub fn new(schema_id: u32, id: &Id, map: Map<Value>) -> Cell {
        Cell {
            header: Header::new(0, schema_id, id),
            data: Value::Map(map)
        }
    }
    pub fn header_from_chunk_raw(ptr: usize) -> Result<Header, ReadError> {
        if ptr == 0 {return Err(ReadError::CellDoesNotExisted)}
        Ok(unsafe {(*(ptr as *const Header))})
    }
    pub fn from_chunk_raw(ptr: usize, chunk: &Chunk) -> Result<Cell, ReadError> {
        let header = Cell::header_from_chunk_raw(ptr)?;
        let data_ptr = ptr + HEADER_SIZE;
        let schema_id = &header.schema;
        if let Some(schema) = chunk.meta.schemas.get(schema_id) {
            Ok(Cell {
                header: header,
                data: reader::read_by_schema(data_ptr, &schema)
            })
        } else {
            error!("Schema {} does not existed to read", schema_id);
            return Err(ReadError::SchemaDoesNotExisted(*schema_id));
        }
    }
    pub fn write_to_chunk(&mut self, chunk: &Chunk) -> Result<usize, WriteError> {
        let schema_id = self.header.schema;
        if let Some(schema) = chunk.meta.schemas.get(&schema_id) {
            return self.write_to_chunk_with_schema(chunk, &schema)
        } else {
            error!("Schema {} does not existed to write", schema_id);
            return Err(WriteError::SchemaDoesNotExisted(schema_id));
        }
    }
    pub fn write_to_chunk_with_schema(&mut self, chunk: &Chunk, schema: &Schema) -> Result<usize, WriteError> {
        let mut offset: usize = 0;
        let mut instructions = Vec::<writer::Instruction>::new();
        let plan = writer::plan_write_field(&mut offset, &schema.fields, &self.data, &mut instructions);
        let total_size = offset + HEADER_SIZE;
        if total_size > MAX_CELL_SIZE {return Err(WriteError::CellIsTooLarge(total_size))}
        let addr_opt = chunk.try_acquire(total_size);
        self.header.size = total_size as u32;
        self.header.version += 1;
        match addr_opt {
            None => {
                error!("Cannot allocate new spaces in chunk");
                return Err(WriteError::CannotAllocateSpace);
            },
            Some((addr, _lock)) => {
                self.header.write(addr);
                writer::execute_plan(addr + HEADER_SIZE, instructions);
                return Ok(addr);
            }
        }
    }
    pub fn id(&self) -> Id {
        self.header.id()
    }
    pub fn set_id(&mut self, id: &Id) {
        self.header.set_id(id)
    }
}<commit_msg>make write private<commit_after>use ram::schema::Schema;
use ram::chunk::Chunk;
use std::ptr;
use ram::io::{reader, writer};
use ram::types::{Map, Value, Id};
use std::collections::HashMap;

const MAX_CELL_SIZE :usize = 1 * 1024 * 1024;

pub type DataValue = Value;
pub type DataMap = Map<Value>;

#[repr(C, packed)]
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct Header {
    pub version: u64,
    pub size: u32,
    pub schema: u32,
    pub partition: u64,
    pub hash: u64,
}

#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub enum WriteError {
    SchemaDoesNotExisted(u32),
    CannotAllocateSpace,
    CellIsTooLarge(usize),
    CellAlreadyExisted,
    CellDoesNotExisted,
    ReadError(ReadError),
    UserCanceledUpdate,
    DeletionPredictionFailed,
    NetworkingError,
}

#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
pub enum ReadError {
    SchemaDoesNotExisted(u32),
    CellDoesNotExisted,
    NetworkingError,
}

impl Header {
    pub fn new(size: u32, schema: u32, id: &Id) -> Header {
        Header {
            version: 1,
            size: size,
            schema: schema,
            partition: id.higher,
            hash: id.lower,
        }
    }
    fn write(&self, location: usize) {
        unsafe {
            ptr::copy_nonoverlapping(self as *const Header, location as *mut Header, HEADER_SIZE);
        }
    }
    pub fn reserve(location: usize, size: usize) {
        Header {
            version: 0, // default a tombstone
            size: size as u32,
            schema: 0,
            hash: 0,
            partition: 0,
        }.write(location);
    }
    pub fn id(&self) -> Id {
        Id {
            higher: self.partition,
            lower: self.hash,
        }
    }
    pub fn set_id(&mut self, id: &Id) {
        self.partition = id.higher;
        self.hash = id.lower;
    }
}

pub const HEADER_SIZE :usize = 32;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Cell {
    pub header: Header,
    pub data: DataValue
}

impl Cell {

    pub fn new(schema_id: u32, id: &Id, map: Map<Value>) -> Cell {
        Cell {
            header: Header::new(0, schema_id, id),
            data: Value::Map(map)
        }
    }
    pub fn header_from_chunk_raw(ptr: usize) -> Result<Header, ReadError> {
        if ptr == 0 {return Err(ReadError::CellDoesNotExisted)}
        Ok(unsafe {(*(ptr as *const Header))})
    }
    pub fn from_chunk_raw(ptr: usize, chunk: &Chunk) -> Result<Cell, ReadError> {
        let header = Cell::header_from_chunk_raw(ptr)?;
        let data_ptr = ptr + HEADER_SIZE;
        let schema_id = &header.schema;
        if let Some(schema) = chunk.meta.schemas.get(schema_id) {
            Ok(Cell {
                header: header,
                data: reader::read_by_schema(data_ptr, &schema)
            })
        } else {
            error!("Schema {} does not existed to read", schema_id);
            return Err(ReadError::SchemaDoesNotExisted(*schema_id));
        }
    }
    pub fn write_to_chunk(&mut self, chunk: &Chunk) -> Result<usize, WriteError> {
        let schema_id = self.header.schema;
        if let Some(schema) = chunk.meta.schemas.get(&schema_id) {
            return self.write_to_chunk_with_schema(chunk, &schema)
        } else {
            error!("Schema {} does not existed to write", schema_id);
            return Err(WriteError::SchemaDoesNotExisted(schema_id));
        }
    }
    pub fn write_to_chunk_with_schema(&mut self, chunk: &Chunk, schema: &Schema) -> Result<usize, WriteError> {
        let mut offset: usize = 0;
        let mut instructions = Vec::<writer::Instruction>::new();
        let plan = writer::plan_write_field(&mut offset, &schema.fields, &self.data, &mut instructions);
        let total_size = offset + HEADER_SIZE;
        if total_size > MAX_CELL_SIZE {return Err(WriteError::CellIsTooLarge(total_size))}
        let addr_opt = chunk.try_acquire(total_size);
        self.header.size = total_size as u32;
        self.header.version += 1;
        match addr_opt {
            None => {
                error!("Cannot allocate new spaces in chunk");
                return Err(WriteError::CannotAllocateSpace);
            },
            Some((addr, _lock)) => {
                self.header.write(addr);
                writer::execute_plan(addr + HEADER_SIZE, instructions);
                return Ok(addr);
            }
        }
    }
    pub fn id(&self) -> Id {
        self.header.id()
    }
    pub fn set_id(&mut self, id: &Id) {
        self.header.set_id(id)
    }
}<|endoftext|>


<commit_before>use console;
use glium;
use mvp;
use shaders;
use units;
use utils;

pub struct Renderer {
    pub screen_size: units::Size2D,
    pub size: units::Size2D,
    pub display: glium::backend::glutin_backend::GlutinFacade,
    pub program: glium::Program,
    pub indices: glium::IndexBuffer<u16>,
    pub texture: glium::texture::Texture2d,
    pub vertex_buffer: console::VertexBuffer,
}

impl Renderer {
    pub fn new(width: i32, height: i32, tile_size: i32, texture_path: &str) -> Self {
        let screen_size = units::Size2D::new(width, height);
        let size = units::Size2D::new((screen_size.width / tile_size),
                                      (screen_size.height / tile_size));

        let display = display(screen_size);
        let program = program(&display);
        let indices = indices(&display, size);
        let texture = texture(&display, texture_path);
        let vertex_buffer = console::VertexBuffer::new(size);

        Renderer {
            size: size,
            screen_size: screen_size,
            display: display,
            program: program,
            indices: indices,
            texture: texture,
            vertex_buffer: vertex_buffer,
        }
    }

    pub fn render(&self) {
        use glium::Surface;

        let vb = glium::VertexBuffer::new(&self.display, &self.vertex_buffer.data()).unwrap();

        let cam_uniforms = {
            let mat4_id = utils::mat4_id();
            let ortho_projection = utils::ortho_projection(self.screen_size);

            let cam = [[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [-768.0, -512.0, 0.0, 1.0]];

            let tileset = self.texture
                .sampled()
                .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest);

            uniform! {
                mvp: mvp::model_view_projection(mat4_id, cam, ortho_projection),
                tileset: tileset,
            }
        };

        let mut target = self.display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        target.draw(&vb,
                  &self.indices,
                  &self.program,
                  &cam_uniforms,
                  &Default::default())
            .unwrap();
        target.finish().unwrap();
    }

    pub fn set(&mut self, screen_point: units::ScreenPoint2D, tile: console::Tile) {
        self.vertex_buffer.set(screen_point, tile);
    }
}

fn display(screen_size: units::Size2D) -> glium::backend::glutin_backend::GlutinFacade {
    use glium::DisplayBuild;
    glium::glutin::WindowBuilder::new()
        .with_dimensions(screen_size.width as u32, screen_size.height as u32)
        .build_glium()
        .unwrap()
}

fn program(display: &glium::backend::glutin_backend::GlutinFacade) -> glium::Program {
    glium::Program::from_source(display as &glium::backend::Facade,
                                &shaders::VERTEX,
                                &shaders::FRAGMENT,
                                None)
        .unwrap()
}

fn indices(
    display: &glium::backend::glutin_backend::GlutinFacade,
    size: units::Size2D
) -> glium::IndexBuffer<u16> {
    use glium::index::PrimitiveType;

    let indices = utils::indices((size.width * size.height) as usize);
    glium::IndexBuffer::new(display as &glium::backend::Facade,
                            PrimitiveType::TrianglesList,
                            &indices)
        .unwrap()
}

fn texture(
    display: &glium::backend::glutin_backend::GlutinFacade,
    path: &str
) -> glium::texture::Texture2d {
    let png = utils::read_bytes(path).unwrap();
    let texture = utils::read_png_to_texture(&png[..]);
    glium::texture::Texture2d::new(display as &glium::backend::Facade, texture).unwrap()
}
<commit_msg>chore(texture): better error<commit_after>use console;
use glium;
use mvp;
use shaders;
use units;
use utils;

pub struct Renderer {
    pub screen_size: units::Size2D,
    pub size: units::Size2D,
    pub display: glium::backend::glutin_backend::GlutinFacade,
    pub program: glium::Program,
    pub indices: glium::IndexBuffer<u16>,
    pub texture: glium::texture::Texture2d,
    pub vertex_buffer: console::VertexBuffer,
}

impl Renderer {
    pub fn new(width: i32, height: i32, tile_size: i32, texture_path: &str) -> Self {
        let screen_size = units::Size2D::new(width, height);
        let size = units::Size2D::new((screen_size.width / tile_size),
                                      (screen_size.height / tile_size));

        let display = display(screen_size);
        let program = program(&display);
        let indices = indices(&display, size);
        let texture = texture(&display, texture_path);
        let vertex_buffer = console::VertexBuffer::new(size);

        Renderer {
            size: size,
            screen_size: screen_size,
            display: display,
            program: program,
            indices: indices,
            texture: texture,
            vertex_buffer: vertex_buffer,
        }
    }

    pub fn render(&self) {
        use glium::Surface;

        let vb = glium::VertexBuffer::new(&self.display, &self.vertex_buffer.data()).unwrap();

        let cam_uniforms = {
            let mat4_id = utils::mat4_id();
            let ortho_projection = utils::ortho_projection(self.screen_size);

            let cam = [[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [-768.0, -512.0, 0.0, 1.0]];

            let tileset = self.texture
                .sampled()
                .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest);

            uniform! {
                mvp: mvp::model_view_projection(mat4_id, cam, ortho_projection),
                tileset: tileset,
            }
        };

        let mut target = self.display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        target.draw(&vb,
                  &self.indices,
                  &self.program,
                  &cam_uniforms,
                  &Default::default())
            .unwrap();
        target.finish().unwrap();
    }

    pub fn set(&mut self, screen_point: units::ScreenPoint2D, tile: console::Tile) {
        self.vertex_buffer.set(screen_point, tile);
    }
}

fn display(screen_size: units::Size2D) -> glium::backend::glutin_backend::GlutinFacade {
    use glium::DisplayBuild;
    glium::glutin::WindowBuilder::new()
        .with_dimensions(screen_size.width as u32, screen_size.height as u32)
        .build_glium()
        .unwrap()
}

fn program(display: &glium::backend::glutin_backend::GlutinFacade) -> glium::Program {
    glium::Program::from_source(display as &glium::backend::Facade,
                                &shaders::VERTEX,
                                &shaders::FRAGMENT,
                                None)
        .unwrap()
}

fn indices(
    display: &glium::backend::glutin_backend::GlutinFacade,
    size: units::Size2D
) -> glium::IndexBuffer<u16> {
    use glium::index::PrimitiveType;

    let indices = utils::indices((size.width * size.height) as usize);
    glium::IndexBuffer::new(display as &glium::backend::Facade,
                            PrimitiveType::TrianglesList,
                            &indices)
        .unwrap()
}

fn texture(
    display: &glium::backend::glutin_backend::GlutinFacade,
    path: &str
) -> glium::texture::Texture2d {
    let png = utils::read_bytes(path).expect("Texture not found");
    let texture = utils::read_png_to_texture(&png[..]);
    glium::texture::Texture2d::new(display as &glium::backend::Facade, texture).unwrap()
}
<|endoftext|>


<commit_before>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            if tokens.has_next() {
                                resolve(sub_document, tokens.next().unwrap().deref())
                            } else {
                                Ok(sub_document)
                            }
                        },
                    }
                },

                &Token::Index { .. } => unimplemented!(),
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => Ok(ary.index_mut(i)),
                &Token::Identifier { .. } => unimplemented!(),
            }
        },

        _ => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    Err(Error::from(ErrorKind::IdentifierNotFoundInDocument(ident.clone())))
                },

                &Token::Index { .. } => unimplemented!(),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

}
<commit_msg>Impl resolve to fix test_resolve_array_table_query_1<commit_after>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            if tokens.has_next() {
                                resolve(sub_document, tokens.next().unwrap().deref())
                            } else {
                                Ok(sub_document)
                            }
                        },
                    }
                },

                &Token::Index { .. } => unimplemented!(),
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    if tokens.has_next() {
                        resolve(ary.get_mut(i).unwrap(), tokens.next().unwrap().deref())
                    } else {
                        Ok(ary.index_mut(i))
                    }
                },
                &Token::Identifier { .. } => unimplemented!(),
            }
        },

        _ => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    Err(Error::from(ErrorKind::IdentifierNotFoundInDocument(ident.clone())))
                },

                &Token::Index { .. } => unimplemented!(),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

}
<|endoftext|>


<commit_before>
//! A NIF resource allows you to safely store rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };

/// Re-export a type used by the `resource_struct_init!` macro.
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
#[doc(hidden)]
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when
/// resource_struct_init! is called on it. It provides the NifResourceType.
///
/// In most cases the user should not have to worry about this.
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync + 'static {
    fn get_type() -> &'static NifResourceType<Self>;
}

impl<T> NifEncoder for ResourceCell<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceCell<T> where T: NifResourceTypeProvider + 'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceCell::from_term(term)
    }
}

/// Drop a T that lives in an Erlang resource. (erlang_nif-sys requires us to declare this
/// function safe, but it is of course thoroughly unsafe!)
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
    unsafe {
        let aligned = align_alloced_mem_for_struct::<T>(handle);
        let res = aligned as *mut T;
        ptr::read(res);
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
///
/// # Panics
///
/// Panics if `name` isn't null-terminated.
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = unsafe {
        ::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
    };
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Given a pointer `ptr` to an allocation of `get_alloc_size_struct::<T>()` bytes, return the
/// first aligned pointer within the allocation where a `T` may be stored.
/// Unsafe: `ptr` must point to a large enough allocation and not be null.
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

/// This is the struct that holds a reference to a resource. It increments the refcounter for the
/// resource instance on creation, and decrements when dropped.
pub struct ResourceCell<T> where T: NifResourceTypeProvider {
    raw: *const c_void,
    inner: *mut T,
}

// Safe because T is `Sync` and `Send`.
unsafe impl<T> Send for ResourceCell<T> where T: NifResourceTypeProvider {}
unsafe impl<T> Sync for ResourceCell<T> where T: NifResourceTypeProvider {}

impl<T> ResourceCell<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceCell from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<T>();
        let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };

        unsafe { ptr::write(aligned_mem, data) };

        ResourceCell {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }

    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        unsafe { ::wrapper::resource::keep_resource(res_resource); }
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
        Ok(ResourceCell {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
    }

    fn as_c_arg(&mut self) -> *const c_void {
        self.raw
    }

    fn inner(&self) -> &T {
        unsafe { &*self.inner }
    }
}

impl<T> Deref for ResourceCell<T> where T: NifResourceTypeProvider {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner()
    }
}

impl<T> Clone for ResourceCell<T> where T: NifResourceTypeProvider {
    /// For a ResourceCell this will simply increment the refcounter for the resource instance and
    /// perform a fairly standard clone.
    fn clone(&self) -> Self {
        unsafe { ::wrapper::resource::keep_resource(self.raw); }
        ResourceCell {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceCell<T> where T: NifResourceTypeProvider {
    /// When drop is called for a ResourceCell, the reference held to the resource by the Cell is
    /// released.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}

#[macro_export]
macro_rules! resource_struct_init {
    ($struct_name:ident, $env: ident) => {
        {
            static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;

            let temp_struct_type =
                match $crate::resource::open_struct_resource_type::<$struct_name>(
                    $env,
                    concat!(stringify!($struct_name), "\x00"),
                    $crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
                    ) {
                    Some(inner) => inner,
                    None => {
                        println!("Failure in creating resource type");
                        return false;
                    }
                };
            unsafe { STRUCT_TYPE = Some(temp_struct_type) };

            impl $crate::resource::NifResourceTypeProvider for $struct_name {
                fn get_type() -> &'static $crate::resource::NifResourceType<Self> {
                    unsafe { &STRUCT_TYPE }.as_ref().unwrap()
                }
            }
        }
    }
}
<commit_msg>Minor documentation changes for resources.<commit_after>//! Support for storing Rust data in Erlang terms.
//!
//! A NIF resource allows you to safely store Rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };

/// Re-export a type used by the `resource_struct_init!` macro.
#[doc(hidden)]
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
#[doc(hidden)]
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when
/// resource_struct_init! is called on it. It provides the NifResourceType.
///
/// In most cases the user should not have to worry about this.
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync + 'static {
    fn get_type() -> &'static NifResourceType<Self>;
}

impl<T> NifEncoder for ResourceCell<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceCell<T> where T: NifResourceTypeProvider + 'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceCell::from_term(term)
    }
}

/// Drop a T that lives in an Erlang resource. (erlang_nif-sys requires us to declare this
/// function safe, but it is of course thoroughly unsafe!)
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
    unsafe {
        let aligned = align_alloced_mem_for_struct::<T>(handle);
        let res = aligned as *mut T;
        ptr::read(res);
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
///
/// # Panics
///
/// Panics if `name` isn't null-terminated.
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = unsafe {
        ::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
    };
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Given a pointer `ptr` to an allocation of `get_alloc_size_struct::<T>()` bytes, return the
/// first aligned pointer within the allocation where a `T` may be stored.
/// Unsafe: `ptr` must point to a large enough allocation and not be null.
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

/// This is the struct that holds a reference to a resource. It increments the refcounter for the
/// resource instance on creation, and decrements when dropped.
pub struct ResourceCell<T> where T: NifResourceTypeProvider {
    raw: *const c_void,
    inner: *mut T,
}

// Safe because T is `Sync` and `Send`.
unsafe impl<T> Send for ResourceCell<T> where T: NifResourceTypeProvider {}
unsafe impl<T> Sync for ResourceCell<T> where T: NifResourceTypeProvider {}

impl<T> ResourceCell<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceCell from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<T>();
        let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };

        unsafe { ptr::write(aligned_mem, data) };

        ResourceCell {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }

    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        unsafe { ::wrapper::resource::keep_resource(res_resource); }
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
        Ok(ResourceCell {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
    }

    fn as_c_arg(&mut self) -> *const c_void {
        self.raw
    }

    fn inner(&self) -> &T {
        unsafe { &*self.inner }
    }
}

impl<T> Deref for ResourceCell<T> where T: NifResourceTypeProvider {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner()
    }
}

impl<T> Clone for ResourceCell<T> where T: NifResourceTypeProvider {
    /// Cloning a `ResourceCell` simply increments the reference count for the
    /// resource. The `T` value is not cloned.
    fn clone(&self) -> Self {
        unsafe { ::wrapper::resource::keep_resource(self.raw); }
        ResourceCell {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceCell<T> where T: NifResourceTypeProvider {
    /// When a `ResourceCell` is dropped, the reference count is decremented. If
    /// there are no other references to the resource, the `T` value is dropped.
    ///
    /// However, note that in general, the Rust value in a resource is dropped
    /// at an unpredictable time: whenever the VM decides to do garbage
    /// collection.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}

#[macro_export]
macro_rules! resource_struct_init {
    ($struct_name:ident, $env: ident) => {
        {
            static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;

            let temp_struct_type =
                match $crate::resource::open_struct_resource_type::<$struct_name>(
                    $env,
                    concat!(stringify!($struct_name), "\x00"),
                    $crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
                    ) {
                    Some(inner) => inner,
                    None => {
                        println!("Failure in creating resource type");
                        return false;
                    }
                };
            unsafe { STRUCT_TYPE = Some(temp_struct_type) };

            impl $crate::resource::NifResourceTypeProvider for $struct_name {
                fn get_type() -> &'static $crate::resource::NifResourceType<Self> {
                    unsafe { &STRUCT_TYPE }.as_ref().unwrap()
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//!
//! Helper to compose arbitrary data structures into packets of binary data.
//!

use std::{cast, ptr, mem, vec};
use std::io::{FileType, TypeFile, TypeDirectory, TypeNamedPipe, TypeBlockSpecial, TypeSymlink, TypeUnknown};
use std::libc::{S_IFREG, S_IFDIR, S_IFCHR, S_IFBLK, S_IFLNK};
use fuse::{fuse_file_lock, fuse_entry_out, fuse_attr_out, fuse_open_out};
use fuse::{fuse_write_out, fuse_statfs_out, fuse_getxattr_out, fuse_lk_out};
use fuse::{fuse_init_out, fuse_bmap_out, fuse_out_header, fuse_dirent};
#[cfg(target_os = "macos")]
use fuse::{fuse_getxtimes_out};

/// Trait for types that can be sent as a reply to the FUSE kernel driver
pub trait Sendable {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// Generally send a memory copy of a type (this works for all
		// structs, i.e. fuse_*_out)
		unsafe {
			let ptr = ptr::to_unsafe_ptr(self);
			let len = mem::size_of::<Self>();
			vec::raw::buf_as_slice(ptr as *u8, len, |bytes| {
				f([bytes])
			})
		}
	}
}

// Implemente sendable trait for fuse_*_out data types
impl Sendable for fuse_file_lock { }
impl Sendable for fuse_entry_out { }
impl Sendable for fuse_attr_out { }
impl Sendable for fuse_open_out { }
impl Sendable for fuse_write_out { }
impl Sendable for fuse_statfs_out { }
impl Sendable for fuse_getxattr_out { }
impl Sendable for fuse_lk_out { }
impl Sendable for fuse_init_out { }
impl Sendable for fuse_bmap_out { }
impl Sendable for fuse_out_header { }
#[cfg(target_os = "macos")]
impl Sendable for fuse_getxtimes_out { }

impl Sendable for () {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// A unit value has nothing to send
		f([])
	}
}

impl<S1: Sendable, S2: Sendable> Sendable for (S1, S2) {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		match self {
			&(ref s1, ref s2) => {
				s1.as_bytegroups(|d1| {
					s2.as_bytegroups(|d2| {
						f(d1 + d2)
					})
				})
			},
		}
	}
}

impl<'a> Sendable for &'a [u8] {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		f([*self])
	}
}

impl Sendable for ~[u8] {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		f([self.as_slice()])
	}
}

impl<'a> Sendable for &'a str {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// Sending a string uses its byte-representation (without trailing NUL)
		f([self.as_bytes()])
	}
}

/// Buffer for replying with a list of directory entries
pub struct DirBuffer {
	priv data: ~[u8],
}

impl DirBuffer {
	/// Create a new dir buffer of the given size
	pub fn new (size: uint) -> DirBuffer {
		DirBuffer { data: vec::with_capacity(size) }
	}

	/// Add an entry to the dir buffer. Returns true if the buffer is full.
	/// A transparent offset value can be provided for each entry. The
	/// kernel uses these value to request the next entries in further
	/// readdir calls
	pub fn fill (&mut self, ino: u64, offset: u64, typ: FileType, name: &PosixPath) -> bool {
		let name = name.as_vec();
		let entlen = mem::size_of::<fuse_dirent>() + name.len();
		let entsize = (entlen + mem::size_of::<u64>() - 1) & !(mem::size_of::<u64>() - 1);	// 64bit align
		let padlen = entsize - entlen;
		if self.data.len() + entsize > self.data.capacity() { return true; }
		unsafe {
			let p = self.data.as_mut_ptr().offset(self.data.len() as int);
			let pdirent: *mut fuse_dirent = cast::transmute(p);
			(*pdirent).ino = ino;
			(*pdirent).off = offset;
			(*pdirent).namelen = name.len() as u32;
			(*pdirent).typ = match typ {
				TypeFile => S_IFREG, TypeDirectory => S_IFDIR, TypeNamedPipe => S_IFCHR,
				TypeBlockSpecial => S_IFBLK, TypeSymlink => S_IFLNK, TypeUnknown => 0,
			} as u32 >> 12;
			let p = p.offset(mem::size_of_val(&*pdirent) as int);
			ptr::copy_memory(p, name.as_ptr(), name.len());
			let p = p.offset(name.len() as int);
			ptr::zero_memory(p, padlen);
			let newlen = self.data.len() + entsize;
			self.data.set_len(newlen);
		}
		false
	}

	/// Returns the size of the data that has been filled into the buffer
	pub fn len (&self) -> uint {
		self.data.len()
	}
}

impl Sendable for DirBuffer {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// Send a dirbuffer by sending its data vector
		self.data.as_bytegroups(f)
	}
}

#[cfg(test)]
mod test {
	use std::io::{TypeFile, TypeDirectory};
	use super::{Sendable, DirBuffer};

	struct test_data_t { p1: u8, p2: u8, p3: u16 }
	impl Sendable for test_data_t {}

	#[test]
	fn sendable_struct () {
		let data = test_data_t { p1: 111, p2: 222, p3: 333 };
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendable struct should be represented as a single bytes slice");
			assert!(bytes[0] == [0x6f, 0xde, 0x4d, 0x01], "sendable struct should be represented by a bytes slice with the byte representation of the struct");
		});
	}

	#[test]
	fn sendable_null () {
		let data = ();
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 0, "sendable empty element should be represented by no bytes slice at all");
		});
	}

	#[test]
	fn sendable_tuple () {
		let data = (test_data_t { p1: 111, p2: 222, p3: 333 }, test_data_t { p1: 112, p2: 223, p3: 334 });
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 2, "sendable tuple should be represented as multiple bytes slices");
			assert!(bytes[0] == [0x6f, 0xde, 0x4d, 0x01], "sendable tuple should first be represented by a bytes slice with the byte representation of the first element");
			assert!(bytes[1] == [0x70, 0xdf, 0x4e, 0x01], "sendable tuple should second be represented by a bytes slice with the byte representation of the second element");
		});
	}

	#[test]
	fn sendable_bytevector () {
		let data = [11, 22, 33, 44, 55];
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendabled buffer should be represented as a single bytes slice");
			assert!(bytes[0] == data, "sendable buffer should be represented by a bytes slice with the contents of the buffer");
		});
	}

	#[test]
	fn sendable_owned_bytevector () {
		let data = ~[11, 22, 33, 44, 55];
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendabled buffer should be represented as a single bytes slice");
			assert!(bytes[0] == data, "sendable buffer should be represented by a bytes slice with the contents of the buffer");
		});
	}

	#[test]
	fn sendable_string () {
		let data = "hello";
		let expected = [104, 101, 108, 108, 111];	// no trailing NUL
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendable string should be represented as a single bytes slice");
			assert!(bytes[0] == expected, "sendable string should be represented by a bytes slice with the contents of the string");
		});
	}

	#[test]
	fn sendable_dirbuffer () {
		let mut buf = DirBuffer::new(128);
		buf.fill(111, 222, TypeDirectory, &PosixPath::new("hello"));
		buf.fill(444, 555, TypeFile, &PosixPath::new("world.rs"));
		let expected = [
			111, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 104, 101, 108, 108, 111,  0,   0,   0,
			188, 1, 0, 0, 0, 0, 0, 0,  43, 2, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 119, 111, 114, 108, 100, 46, 114, 115,
		];
		buf.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendable dirbuffer should be represented as a single bytes slice");
			assert!(bytes[0] == expected, "sendable dirbuffer should be represented by a bytes slice with the contents of the dirbuffer");
		});
	}
}
<commit_msg>ptr::to_unsafe_ptr no longer exists; replace a cast<commit_after>//!
//! Helper to compose arbitrary data structures into packets of binary data.
//!

use std::{cast, ptr, mem, vec};
use std::io::{FileType, TypeFile, TypeDirectory, TypeNamedPipe, TypeBlockSpecial, TypeSymlink, TypeUnknown};
use std::libc::{S_IFREG, S_IFDIR, S_IFCHR, S_IFBLK, S_IFLNK};
use fuse::{fuse_file_lock, fuse_entry_out, fuse_attr_out, fuse_open_out};
use fuse::{fuse_write_out, fuse_statfs_out, fuse_getxattr_out, fuse_lk_out};
use fuse::{fuse_init_out, fuse_bmap_out, fuse_out_header, fuse_dirent};
#[cfg(target_os = "macos")]
use fuse::{fuse_getxtimes_out};

/// Trait for types that can be sent as a reply to the FUSE kernel driver
pub trait Sendable {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// Generally send a memory copy of a type (this works for all
		// structs, i.e. fuse_*_out)
		unsafe {
			let len = mem::size_of::<Self>();
			vec::raw::buf_as_slice(self as *Self as *u8, len, |bytes| {
				f([bytes])
			})
		}
	}
}

// Implemente sendable trait for fuse_*_out data types
impl Sendable for fuse_file_lock { }
impl Sendable for fuse_entry_out { }
impl Sendable for fuse_attr_out { }
impl Sendable for fuse_open_out { }
impl Sendable for fuse_write_out { }
impl Sendable for fuse_statfs_out { }
impl Sendable for fuse_getxattr_out { }
impl Sendable for fuse_lk_out { }
impl Sendable for fuse_init_out { }
impl Sendable for fuse_bmap_out { }
impl Sendable for fuse_out_header { }
#[cfg(target_os = "macos")]
impl Sendable for fuse_getxtimes_out { }

impl Sendable for () {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// A unit value has nothing to send
		f([])
	}
}

impl<S1: Sendable, S2: Sendable> Sendable for (S1, S2) {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		match self {
			&(ref s1, ref s2) => {
				s1.as_bytegroups(|d1| {
					s2.as_bytegroups(|d2| {
						f(d1 + d2)
					})
				})
			},
		}
	}
}

impl<'a> Sendable for &'a [u8] {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		f([*self])
	}
}

impl Sendable for ~[u8] {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		f([self.as_slice()])
	}
}

impl<'a> Sendable for &'a str {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// Sending a string uses its byte-representation (without trailing NUL)
		f([self.as_bytes()])
	}
}

/// Buffer for replying with a list of directory entries
pub struct DirBuffer {
	priv data: ~[u8],
}

impl DirBuffer {
	/// Create a new dir buffer of the given size
	pub fn new (size: uint) -> DirBuffer {
		DirBuffer { data: vec::with_capacity(size) }
	}

	/// Add an entry to the dir buffer. Returns true if the buffer is full.
	/// A transparent offset value can be provided for each entry. The
	/// kernel uses these value to request the next entries in further
	/// readdir calls
	pub fn fill (&mut self, ino: u64, offset: u64, typ: FileType, name: &PosixPath) -> bool {
		let name = name.as_vec();
		let entlen = mem::size_of::<fuse_dirent>() + name.len();
		let entsize = (entlen + mem::size_of::<u64>() - 1) & !(mem::size_of::<u64>() - 1);	// 64bit align
		let padlen = entsize - entlen;
		if self.data.len() + entsize > self.data.capacity() { return true; }
		unsafe {
			let p = self.data.as_mut_ptr().offset(self.data.len() as int);
			let pdirent: *mut fuse_dirent = cast::transmute(p);
			(*pdirent).ino = ino;
			(*pdirent).off = offset;
			(*pdirent).namelen = name.len() as u32;
			(*pdirent).typ = match typ {
				TypeFile => S_IFREG, TypeDirectory => S_IFDIR, TypeNamedPipe => S_IFCHR,
				TypeBlockSpecial => S_IFBLK, TypeSymlink => S_IFLNK, TypeUnknown => 0,
			} as u32 >> 12;
			let p = p.offset(mem::size_of_val(&*pdirent) as int);
			ptr::copy_memory(p, name.as_ptr(), name.len());
			let p = p.offset(name.len() as int);
			ptr::zero_memory(p, padlen);
			let newlen = self.data.len() + entsize;
			self.data.set_len(newlen);
		}
		false
	}

	/// Returns the size of the data that has been filled into the buffer
	pub fn len (&self) -> uint {
		self.data.len()
	}
}

impl Sendable for DirBuffer {
	fn as_bytegroups<T> (&self, f: |&[&[u8]]| -> T) -> T {
		// Send a dirbuffer by sending its data vector
		self.data.as_bytegroups(f)
	}
}

#[cfg(test)]
mod test {
	use std::io::{TypeFile, TypeDirectory};
	use super::{Sendable, DirBuffer};

	struct test_data_t { p1: u8, p2: u8, p3: u16 }
	impl Sendable for test_data_t {}

	#[test]
	fn sendable_struct () {
		let data = test_data_t { p1: 111, p2: 222, p3: 333 };
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendable struct should be represented as a single bytes slice");
			assert!(bytes[0] == [0x6f, 0xde, 0x4d, 0x01], "sendable struct should be represented by a bytes slice with the byte representation of the struct");
		});
	}

	#[test]
	fn sendable_null () {
		let data = ();
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 0, "sendable empty element should be represented by no bytes slice at all");
		});
	}

	#[test]
	fn sendable_tuple () {
		let data = (test_data_t { p1: 111, p2: 222, p3: 333 }, test_data_t { p1: 112, p2: 223, p3: 334 });
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 2, "sendable tuple should be represented as multiple bytes slices");
			assert!(bytes[0] == [0x6f, 0xde, 0x4d, 0x01], "sendable tuple should first be represented by a bytes slice with the byte representation of the first element");
			assert!(bytes[1] == [0x70, 0xdf, 0x4e, 0x01], "sendable tuple should second be represented by a bytes slice with the byte representation of the second element");
		});
	}

	#[test]
	fn sendable_bytevector () {
		let data = [11, 22, 33, 44, 55];
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendabled buffer should be represented as a single bytes slice");
			assert!(bytes[0] == data, "sendable buffer should be represented by a bytes slice with the contents of the buffer");
		});
	}

	#[test]
	fn sendable_owned_bytevector () {
		let data = ~[11, 22, 33, 44, 55];
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendabled buffer should be represented as a single bytes slice");
			assert!(bytes[0] == data, "sendable buffer should be represented by a bytes slice with the contents of the buffer");
		});
	}

	#[test]
	fn sendable_string () {
		let data = "hello";
		let expected = [104, 101, 108, 108, 111];	// no trailing NUL
		data.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendable string should be represented as a single bytes slice");
			assert!(bytes[0] == expected, "sendable string should be represented by a bytes slice with the contents of the string");
		});
	}

	#[test]
	fn sendable_dirbuffer () {
		let mut buf = DirBuffer::new(128);
		buf.fill(111, 222, TypeDirectory, &PosixPath::new("hello"));
		buf.fill(444, 555, TypeFile, &PosixPath::new("world.rs"));
		let expected = [
			111, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 104, 101, 108, 108, 111,  0,   0,   0,
			188, 1, 0, 0, 0, 0, 0, 0,  43, 2, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 119, 111, 114, 108, 100, 46, 114, 115,
		];
		buf.as_bytegroups(|bytes| {
			assert!(bytes.len() == 1, "sendable dirbuffer should be represented as a single bytes slice");
			assert!(bytes[0] == expected, "sendable dirbuffer should be represented by a bytes slice with the contents of the dirbuffer");
		});
	}
}
<|endoftext|>