<commit_before>const LEFT_VARIABLE_DELIM: &'static str  = "{{";
const RIGHT_VARIABLE_DELIM: &'static str  = "}}";

// List of token types to emit to the parser.
// Different from the state enum despite some identical members
#[derive(PartialEq, Debug)]
pub enum TokenType {
  Text, // HTML text etc
  Space,
  VariableStart, // {{
  VariableEnd, // }}
  Variable, // variable name, tera keywords
  Int,
  Float,
  Operator, // + - * / .
  Error, // errors uncountered while lexing, such as 1.2.3 number
}

// List of different states the Tokenizer can be in
#[derive(Debug)]
enum State {
  Text,
  VariableStart,
  InsideBlock,
}

#[derive(PartialEq, Debug)]
pub struct Token {
  kind: TokenType,
  value: String,
}

impl Token {
  pub fn new(kind: TokenType, value: &str) -> Token {
    Token {
      kind: kind,
      value: value.to_string()
    }
  }
}

#[derive(Debug)]
pub struct Tokenizer {
  name: String,
  input: String,
  chars: Vec<(usize, char)>, // (bytes index, char)
  index: usize, // where we are in the chars vec
  state: State,
}

impl Tokenizer {
  pub fn new(input: &str) -> Tokenizer {
    // We want to figure out what's the initial state so we check the first
    // 2 chars
    let first_chars = input.chars().take(2).collect::<String>();
    // TODO: &* is pretty ugly, any way to fix that?
    let state = match &*first_chars {
      LEFT_VARIABLE_DELIM => State::VariableStart,
      _ => State::Text
    };

    Tokenizer {
      name: "test".to_string(),
      input: input.to_string(),
      chars: input.char_indices().collect(),
      index: 0,
      state: state,
    }
  }

  // Gets the substring a start index and self.index
  // Substring is non-inclusive
  fn get_substring(&self, start: usize) -> &str {
    let start_bytes = self.chars[start].0;
    let end_bytes = self.chars[self.index].0;

    // special case if the end index is the last char
    if self.is_over() {
      return &self.input[start_bytes..];
    }

    &self.input[start_bytes..end_bytes]
  }

  // We'll want to make sure we don't continue after the end in several
  // lexer methods
  fn is_over(&self) -> bool {
    self.index >= self.chars.len() - 1
  }

  // We know we have {{ with self.index being on the first
  fn lex_left_variable_delimiter(&mut self) -> Token {
    self.index += 2;
    self.state = State::InsideBlock;

    Token::new(TokenType::VariableStart, LEFT_VARIABLE_DELIM)
  }

  // TODO: merge with the one above?
  fn lex_right_variable_delimiter(&mut self) -> Token {
    self.index += 2;
    self.state = State::Text;

    Token::new(TokenType::VariableEnd, RIGHT_VARIABLE_DELIM)
  }

  fn lex_text(&mut self) -> Token {
    let start_index = self.index;

    loop {
      if self.input[self.chars[self.index].0..].starts_with(LEFT_VARIABLE_DELIM) {
        self.state = State::VariableStart;
        break;
      }

      if self.is_over() {
        break;
      }

      self.index += 1;
    }

    Token::new(TokenType::Text, self.get_substring(start_index))
  }

  // We know we have a space, we need to figure out how many
  fn lex_space(&mut self) -> Token {
    let start_index = self.index;

    loop {
      if !self.chars[self.index].1.is_whitespace() {
        break;
      }

      self.index += 1;
    }

    Token::new(TokenType::Space, self.get_substring(start_index))
  }

  fn lex_number(&mut self) -> Token {
    let start_index = self.index;
    let mut number_type = TokenType::Int;
    let mut error = "";

    loop {
      match self.chars[self.index].1 {
        x if x.is_whitespace() || x == '}' => break,
        '.' => {
          match number_type {
            TokenType::Int => number_type = TokenType::Float,
            TokenType::Float => error = "A number has 2 dots",
            _ => {}
          }
        },
        x if !x.is_numeric() => error = "A number has unallowed chars",
        _ => {}
      }
      self.index += 1;
    }

    if error.len() > 0 {
      return Token::new(TokenType::Error, error);
    }

    Token::new(number_type, self.get_substring(start_index))
  }

  fn lex_variable(&mut self) -> Token {
    let start_index = self.index;

    loop {
      match self.chars[self.index].1 {
        x if x.is_whitespace() || x == '.' => break,
        _ => {}
      }
      self.index += 1;
    }

    Token::new(TokenType::Variable, self.get_substring(start_index))
  }

  fn lex_operator(&mut self) -> Token {
    self.index += 1;
    self.state = State::InsideBlock;

    Token::new(TokenType::Operator, self.get_substring(self.index - 1))
  }

  fn lex_inside_variable_block(&mut self) -> Token {
    match self.chars[self.index].1 {
      x if x.is_whitespace() => self.lex_space(),
      x if x.is_alphabetic() || x == '_' => self.lex_variable(),
      '}' => self.lex_right_variable_delimiter(),
      x if x.is_numeric() => self.lex_number(),
      '*' | '+' | '-' | '/' | '.' => self.lex_operator(),
      _ => Token::new(TokenType::Error, "Unknown char in variable block"),
    }
  }
}

impl Iterator for Tokenizer {
  type Item = Token;

  fn next(&mut self) -> Option<Token> {
    // Empty template or we got to the end
    if self.input.len() == 0 || self.is_over() {
      return None;
    }

    match self.state {
      State::Text => Some(self.lex_text()),
      State::VariableStart => Some(self.lex_left_variable_delimiter()),
      State::InsideBlock => Some(self.lex_inside_variable_block()),
    }
  }
}

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

  #[derive(Debug)]
  struct LexerTest {
    name: String,
    input: String,
    expected: Vec<Token>
  }

  impl LexerTest {
    pub fn new(name: &str, input: &str, expected: Vec<Token>) -> LexerTest {
      LexerTest {
        name: name.to_string(),
        input: input.to_string(),
        expected: expected
      }
    }
  }

  fn text_token(value: &str) -> Token {
    Token::new(TokenType::Text, value)
  }

  fn variable_token(value: &str) -> Token {
    Token::new(TokenType::Variable, value)
  }

  fn variable_start_token() -> Token {
    Token::new(TokenType::VariableStart, "{{")
  }

  fn variable_end_token() -> Token {
    Token::new(TokenType::VariableEnd, "}}")
  }

  fn space_token() -> Token {
    Token::new(TokenType::Space, " ")
  }

  fn int_token(value: &str) -> Token {
    Token::new(TokenType::Int, value)
  }

  fn float_token(value: &str) -> Token {
    Token::new(TokenType::Float, value)
  }

  fn operator_token(value: &str) -> Token {
    Token::new(TokenType::Operator, value)
  }

  fn error_token() -> Token {
    Token::new(TokenType::Error, "")
  }

  fn check_if_correct(expected: &Vec<Token>, obtained: &Vec<Token>) -> bool {
    if expected.len() != obtained.len() {
      return false;
    }

    for i in 0..expected.len() {
      if expected[i].kind != obtained[i].kind {
        return false;
      }

      // Do not check error values as i'll probably change them often
      if expected[i].kind == TokenType::Error {
        continue;
      }

      if expected[i].value != obtained[i].value {
        return false;
      }
    }
    return true;
  }

  #[test]
  fn test_lexer() {
    let tests: Vec<LexerTest> = vec![
      LexerTest::new("empty", "", vec![]),
      LexerTest::new("only text", "Hello 世界", vec![text_token("Hello 世界")]),
      LexerTest::new("variable and text", "{{ greeting }} 世界", vec![
        variable_start_token(),
        space_token(),
        variable_token("greeting"),
        space_token(),
        variable_end_token(),
        text_token(" 世界"),
      ]),
      LexerTest::new("numbers", "{{1 3.14}}", vec![
        variable_start_token(),
        int_token("1"),
        space_token(),
        float_token("3.14"),
        variable_end_token(),
      ]),
      LexerTest::new("invalid numbers", "{{1up 3.14.15}}", vec![
        variable_start_token(),
        error_token(),
        space_token(),
        error_token(),
        variable_end_token(),
      ]),
      LexerTest::new("operators", "{{+ - * / .}}", vec![
        variable_start_token(),
        operator_token("+"),
        space_token(),
        operator_token("-"),
        space_token(),
        operator_token("*"),
        space_token(),
        operator_token("/"),
        space_token(),
        operator_token("."),
        variable_end_token(),
      ])
    ];

    for test in tests {
      let tokens: Vec<Token> = Tokenizer::new(&test.input).collect();
      if tokens.len() != test.expected.len() {
        println!("Test {} failed: different number of tokens.", test.name);
        println!("Expected: {:?}", test.expected);
        println!("Got: {:?}", tokens);
        assert!(false);
      }

      if check_if_correct(&test.expected, &tokens) {
        assert!(true);
      } else {
        println!("Test {} failed: different tokens", test.name);
        println!("Expected: {:?}", test.expected);
        println!("Got: {:?}", tokens);
        assert!(false);
      }
    }
  }
}
<commit_msg>Add basic lineno to token: to revisit later<commit_after>const LEFT_VARIABLE_DELIM: &'static str  = "{{";
const RIGHT_VARIABLE_DELIM: &'static str  = "}}";

// List of token types to emit to the parser.
// Different from the state enum despite some identical members
#[derive(PartialEq, Debug)]
pub enum TokenType {
  Text, // HTML text etc
  Space,
  VariableStart, // {{
  VariableEnd, // }}
  Variable, // variable name, tera keywords
  Int,
  Float,
  Operator, // + - * / .
  Error, // errors uncountered while lexing, such as 1.2.3 number
}

// List of different states the Tokenizer can be in
#[derive(Debug)]
enum State {
  Text,
  VariableStart,
  InsideBlock,
}

#[derive(PartialEq, Debug)]
pub struct Token {
  kind: TokenType,
  value: String,
  lineno: usize,
}

impl Token {
  pub fn new(kind: TokenType, value: &str, lineno: usize) -> Token {
    Token {
      kind: kind,
      value: value.to_string(),
      lineno: lineno,
    }
  }
}

#[derive(Debug)]
pub struct Tokenizer {
  name: String,
  input: String,
  chars: Vec<(usize, char)>, // (bytes index, char)
  index: usize, // where we are in the chars vec
  state: State,
}

impl Tokenizer {
  pub fn new(input: &str) -> Tokenizer {
    // We want to figure out what's the initial state so we check the first
    // 2 chars
    let first_chars = input.chars().take(2).collect::<String>();
    // TODO: &* is pretty ugly, any way to fix that?
    let state = match &*first_chars {
      LEFT_VARIABLE_DELIM => State::VariableStart,
      _ => State::Text
    };

    Tokenizer {
      name: "test".to_string(),
      input: input.to_string(),
      chars: input.char_indices().collect(),
      index: 0,
      state: state,
    }
  }

  // Gets the substring a start index and self.index
  // Substring is non-inclusive
  fn get_substring(&self, start: usize) -> &str {
    let start_bytes = self.chars[start].0;
    let end_bytes = self.chars[self.index].0;

    // special case if the end index is the last char
    if self.is_over() {
      return &self.input[start_bytes..];
    }

    &self.input[start_bytes..end_bytes]
  }

  // We'll want to make sure we don't continue after the end in several
  // lexer methods
  fn is_over(&self) -> bool {
    self.index >= self.chars.len() - 1
  }

  // TODO: check if this is actually correct once i have a parser/compiler
  // hint: probably not
  fn get_current_line_number(&self) -> usize {
    // String.matches is unstable for now
    1 + &self.input[..self.chars[self.index].0]
          .chars()
          .filter(|&c| c == '\n')
          .collect::<Vec<_>>()
          .len()
  }

  // It might be worth having a generic function to handle
  // {{, }}, operators and possibly more when the size is known
  fn lex_left_variable_delimiter(&mut self) -> Token {
    let lineno = self.get_current_line_number();

    self.index += 2;
    if self.index >= self.chars.len() {
      self.index = self.chars.len() - 1;
    }
    self.state = State::InsideBlock;

    Token::new(TokenType::VariableStart, LEFT_VARIABLE_DELIM, lineno)
  }

  fn lex_right_variable_delimiter(&mut self) -> Token {
    let lineno = self.get_current_line_number();
    self.index += 2;
    if self.index >= self.chars.len() {
      self.index = self.chars.len() - 1;
    }
    self.state = State::Text;

    Token::new(TokenType::VariableEnd, RIGHT_VARIABLE_DELIM, lineno)
  }

  fn lex_text(&mut self) -> Token {
    let lineno = self.get_current_line_number();
    let start_index = self.index;

    loop {
      if self.input[self.chars[self.index].0..].starts_with(LEFT_VARIABLE_DELIM) {
        self.state = State::VariableStart;
        break;
      }

      if self.is_over() {
        break;
      }

      self.index += 1;
    }

    Token::new(TokenType::Text, self.get_substring(start_index), lineno)
  }

  // We know we have a space, we need to figure out how many
  fn lex_space(&mut self) -> Token {
    let lineno = self.get_current_line_number();
    let start_index = self.index;

    loop {
      if !self.chars[self.index].1.is_whitespace() {
        break;
      }

      self.index += 1;
    }

    Token::new(TokenType::Space, self.get_substring(start_index), lineno)
  }

  fn lex_number(&mut self) -> Token {
    let lineno = self.get_current_line_number();
    let start_index = self.index;
    let mut number_type = TokenType::Int;
    let mut error = "";

    loop {
      match self.chars[self.index].1 {
        x if x.is_whitespace() || x == '}' => break,
        '.' => {
          match number_type {
            TokenType::Int => number_type = TokenType::Float,
            TokenType::Float => error = "A number has 2 dots",
            _ => {}
          }
        },
        x if !x.is_numeric() => error = "A number has unallowed chars",
        _ => {}
      }
      self.index += 1;
    }

    if error.len() > 0 {
      return Token::new(TokenType::Error, error, lineno);
    }

    Token::new(number_type, self.get_substring(start_index), lineno)
  }

  fn lex_variable(&mut self) -> Token {
    let lineno = self.get_current_line_number();
    let start_index = self.index;

    loop {
      match self.chars[self.index].1 {
        x if x.is_whitespace() || x == '.' => break,
        _ => {}
      }
      self.index += 1;
    }

    Token::new(TokenType::Variable, self.get_substring(start_index), lineno)
  }

  fn lex_operator(&mut self) -> Token {
    let lineno = self.get_current_line_number();
    self.index += 1;
    if self.index >= self.chars.len() {
      self.index = self.chars.len() - 1;
    }
    self.state = State::InsideBlock;

    Token::new(TokenType::Operator, self.get_substring(self.index - 1), lineno)
  }

  fn lex_inside_variable_block(&mut self) -> Token {
    let lineno = self.get_current_line_number();

    match self.chars[self.index].1 {
      x if x.is_whitespace() => self.lex_space(),
      x if x.is_alphabetic() || x == '_' => self.lex_variable(),
      '}' => self.lex_right_variable_delimiter(),
      x if x.is_numeric() => self.lex_number(),
      '*' | '+' | '-' | '/' | '.' => self.lex_operator(),
      _ => Token::new(TokenType::Error, "Unknown char in variable block", lineno),
    }
  }
}

impl Iterator for Tokenizer {
  type Item = Token;

  fn next(&mut self) -> Option<Token> {
    // Empty template or we got to the end
    if self.input.len() == 0 || self.is_over() {
      return None;
    }

    match self.state {
      State::Text => Some(self.lex_text()),
      State::VariableStart => Some(self.lex_left_variable_delimiter()),
      State::InsideBlock => Some(self.lex_inside_variable_block()),
    }
  }
}

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

  #[derive(Debug)]
  struct LexerTest {
    name: String,
    input: String,
    expected: Vec<Token>
  }

  impl LexerTest {
    pub fn new(name: &str, input: &str, expected: Vec<Token>) -> LexerTest {
      LexerTest {
        name: name.to_string(),
        input: input.to_string(),
        expected: expected
      }
    }
  }

  fn text_token(value: &str) -> Token {
    Token::new(TokenType::Text, value, 1)
  }

  fn variable_token(value: &str) -> Token {
    Token::new(TokenType::Variable, value, 1)
  }

  fn variable_start_token() -> Token {
    Token::new(TokenType::VariableStart, "{{", 1)
  }

  fn variable_end_token() -> Token {
    Token::new(TokenType::VariableEnd, "}}", 1)
  }

  fn space_token() -> Token {
    Token::new(TokenType::Space, " ", 1)
  }

  fn int_token(value: &str) -> Token {
    Token::new(TokenType::Int, value, 1)
  }

  fn float_token(value: &str) -> Token {
    Token::new(TokenType::Float, value, 1)
  }

  fn operator_token(value: &str) -> Token {
    Token::new(TokenType::Operator, value, 1)
  }

  fn error_token() -> Token {
    Token::new(TokenType::Error, "", 1)
  }

  fn check_if_correct(expected: &Vec<Token>, obtained: &Vec<Token>) -> bool {
    if expected.len() != obtained.len() {
      return false;
    }

    for i in 0..expected.len() {
      if expected[i].kind != obtained[i].kind {
        return false;
      }

      // Do not check error values as i'll probably change them often
      if expected[i].kind == TokenType::Error {
        continue;
      }

      if expected[i].value != obtained[i].value {
        return false;
      }
    }
    return true;
  }

  #[test]
  fn test_lexer() {
    let tests: Vec<LexerTest> = vec![
      LexerTest::new("empty", "", vec![]),
      LexerTest::new("only text", "Hello 世界", vec![text_token("Hello 世界")]),
      LexerTest::new("variable and text", "{{ greeting }} 世界", vec![
        variable_start_token(),
        space_token(),
        variable_token("greeting"),
        space_token(),
        variable_end_token(),
        text_token(" 世界"),
      ]),
      LexerTest::new("numbers", "{{1 3.14}}", vec![
        variable_start_token(),
        int_token("1"),
        space_token(),
        float_token("3.14"),
        variable_end_token(),
      ]),
      LexerTest::new("invalid numbers", "{{1up 3.14.15}}", vec![
        variable_start_token(),
        error_token(),
        space_token(),
        error_token(),
        variable_end_token(),
      ]),
      LexerTest::new("operators", "{{+ - * / .}}", vec![
        variable_start_token(),
        operator_token("+"),
        space_token(),
        operator_token("-"),
        space_token(),
        operator_token("*"),
        space_token(),
        operator_token("/"),
        space_token(),
        operator_token("."),
        variable_end_token(),
      ])
    ];

    for test in tests {
      println!("Testing: {}", test.name);
      let tokens: Vec<Token> = Tokenizer::new(&test.input).collect();
      if tokens.len() != test.expected.len() {
        println!("Test {} failed: different number of tokens.", test.name);
        println!("Expected: {:?}", test.expected);
        println!("Got: {:?}", tokens);
        assert!(false);
      }

      if check_if_correct(&test.expected, &tokens) {
        assert!(true);
      } else {
        println!("Test {} failed: different tokens", test.name);
        println!("Expected: {:?}", test.expected);
        println!("Got: {:?}", tokens);
        assert!(false);
      }
    }
  }
}
<|endoftext|>


<commit_before>use alloc::borrow::Cow;
use std::fmt;
use std::slice;
use alloc::vec::Vec;

use goblin::mach;
use goblin::mach::load_command::CommandVariant;
use uuid::Uuid;

use {Machine, Object, ObjectSection, ObjectSegment, SectionKind, Symbol, SymbolKind, SymbolMap};

/// A Mach-O object file.
#[derive(Debug)]
pub struct MachOFile<'data> {
    macho: mach::MachO<'data>,
}

/// An iterator over the segments of a `MachOFile`.
#[derive(Debug)]
pub struct MachOSegmentIterator<'data, 'file>
where
    'data: 'file,
{
    segments: slice::Iter<'file, mach::segment::Segment<'data>>,
}

/// A segment of a `MachOFile`.
#[derive(Debug)]
pub struct MachOSegment<'data, 'file>
where
    'data: 'file,
{
    segment: &'file mach::segment::Segment<'data>,
}

/// An iterator over the sections of a `MachOFile`.
pub struct MachOSectionIterator<'data, 'file>
where
    'data: 'file,
{
    segments: slice::Iter<'file, mach::segment::Segment<'data>>,
    sections: Option<mach::segment::SectionIterator<'data>>,
}

/// A section of a `MachOFile`.
#[derive(Debug)]
pub struct MachOSection<'data> {
    section: mach::segment::Section,
    data: mach::segment::SectionData<'data>,
}

/// An iterator over the symbols of a `MachOFile`.
pub struct MachOSymbolIterator<'data> {
    symbols: mach::symbols::SymbolIterator<'data>,
    section_kinds: Vec<SectionKind>,
}

impl<'data> MachOFile<'data> {
    /// Get the Mach-O headers of the file.
    // TODO: this is temporary to allow access to features this crate doesn't provide yet
    #[inline]
    pub fn macho(&self) -> &mach::MachO<'data> {
        &self.macho
    }

    /// Parse the raw Mach-O file data.
    pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> {
        let macho = mach::MachO::parse(data, 0).map_err(|_| "Could not parse Mach-O header")?;
        Ok(MachOFile { macho })
    }
}

impl<'data, 'file> Object<'data, 'file> for MachOFile<'data>
where
    'data: 'file,
{
    type Segment = MachOSegment<'data, 'file>;
    type SegmentIterator = MachOSegmentIterator<'data, 'file>;
    type Section = MachOSection<'data>;
    type SectionIterator = MachOSectionIterator<'data, 'file>;
    type SymbolIterator = MachOSymbolIterator<'data>;

    fn machine(&self) -> Machine {
        match self.macho.header.cputype {
            mach::cputype::CPU_TYPE_ARM => Machine::Arm,
            mach::cputype::CPU_TYPE_ARM64 => Machine::Arm64,
            mach::cputype::CPU_TYPE_X86 => Machine::X86,
            mach::cputype::CPU_TYPE_X86_64 => Machine::X86_64,
            _ => Machine::Other,
        }
    }

    fn segments(&'file self) -> MachOSegmentIterator<'data, 'file> {
        MachOSegmentIterator {
            segments: self.macho.segments.iter(),
        }
    }

    fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg
        // ".debug_info" to "__debug_info".
        let (system_section, section_name) = if section_name.starts_with('.') {
            (true, &section_name[1..])
        } else {
            (false, section_name)
        };
        let cmp_section_name = |name: &str| if system_section {
            name.starts_with("__") && section_name == &name[2..]
        } else {
            section_name == name
        };

        for segment in &self.macho.segments {
            for section in segment {
                if let Ok((section, data)) = section {
                    if let Ok(name) = section.name() {
                        if cmp_section_name(name) {
                            return Some(Cow::from(data));
                        }
                    }
                }
            }
        }
        None
    }

    fn sections(&'file self) -> MachOSectionIterator<'data, 'file> {
        MachOSectionIterator {
            segments: self.macho.segments.iter(),
            sections: None,
        }
    }

    fn symbols(&'file self) -> MachOSymbolIterator<'data> {
        let symbols = match self.macho.symbols {
            Some(ref symbols) => symbols.into_iter(),
            None => mach::symbols::SymbolIterator::default(),
        };

        let mut section_kinds = Vec::new();
        // Don't use MachOSectionIterator because it skips sections it fails to parse,
        // and the section index is important.
        for segment in &self.macho.segments {
            for section in segment {
                if let Ok((section, data)) = section {
                    let section = MachOSection { section, data };
                    section_kinds.push(section.kind());
                } else {
                    // Add placeholder so that indexing works.
                    section_kinds.push(SectionKind::Unknown);
                }
            }
        }

        MachOSymbolIterator {
            symbols,
            section_kinds,
        }
    }

    fn dynamic_symbols(&'file self) -> MachOSymbolIterator<'data> {
        // The LC_DYSYMTAB command contains indices into the same symbol
        // table as the LC_SYMTAB command, so return all of them.
        self.symbols()
    }

    fn symbol_map(&self) -> SymbolMap<'data> {
        let mut symbols: Vec<_> = self.symbols().collect();

        // Add symbols for the end of each section.
        for section in self.sections() {
            symbols.push(Symbol {
                name: None,
                address: section.address() + section.size(),
                size: 0,
                kind: SymbolKind::Section,
                section_kind: None,
                global: false,
            });
        }

        // Calculate symbol sizes by sorting and finding the next symbol.
        symbols.sort_by(|a, b| {
            a.address.cmp(&b.address).then_with(|| {
                // Place the end of section symbols last.
                (a.kind == SymbolKind::Section).cmp(&(b.kind == SymbolKind::Section))
            })
        });

        for i in 0..symbols.len() {
            let (before, after) = symbols.split_at_mut(i + 1);
            let symbol = &mut before[i];
            if symbol.kind != SymbolKind::Section {
                if let Some(next) = after
                    .iter()
                    .skip_while(|x| x.kind != SymbolKind::Section && x.address == symbol.address)
                    .next()
                {
                    symbol.size = next.address - symbol.address;
                }
            }
        }

        symbols.retain(SymbolMap::filter);
        SymbolMap { symbols }
    }

    #[inline]
    fn is_little_endian(&self) -> bool {
        self.macho.little_endian
    }

    fn has_debug_symbols(&self) -> bool {
        self.section_data_by_name(".debug_info").is_some()
    }

    fn mach_uuid(&self) -> Option<Uuid> {
        // Return the UUID from the `LC_UUID` load command, if one is present.
        self.macho.load_commands.iter().filter_map(|lc| {
            match lc.command {
                CommandVariant::Uuid(ref cmd) => {
                    //TODO: Uuid should have a `from_array` method that can't fail.
                    Uuid::from_bytes(&cmd.uuid).ok()
                }
                _ => None,
            }
        }).nth(0)
    }

    fn entry(&self) -> u64 {
        self.macho.entry
    }
}

impl<'data, 'file> Iterator for MachOSegmentIterator<'data, 'file> {
    type Item = MachOSegment<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.segments.next().map(|segment| MachOSegment { segment })
    }
}

impl<'data, 'file> ObjectSegment<'data> for MachOSegment<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        self.segment.vmaddr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.segment.vmsize
    }

    #[inline]
    fn data(&self) -> &'data [u8] {
        self.segment.data
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        self.segment.name().ok()
    }
}

impl<'data, 'file> fmt::Debug for MachOSectionIterator<'data, 'file> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // It's painful to do much better than this
        f.debug_struct("MachOSectionIterator").finish()
    }
}

impl<'data, 'file> Iterator for MachOSectionIterator<'data, 'file> {
    type Item = MachOSection<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(ref mut sections) = self.sections {
                while let Some(section) = sections.next() {
                    if let Ok((section, data)) = section {
                        return Some(MachOSection { section, data });
                    }
                }
            }
            match self.segments.next() {
                None => return None,
                Some(segment) => {
                    self.sections = Some(segment.into_iter());
                }
            }
        }
    }
}

impl<'data> ObjectSection<'data> for MachOSection<'data> {
    #[inline]
    fn address(&self) -> u64 {
        self.section.addr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.section.size
    }

    #[inline]
    fn data(&self) -> Cow<'data, [u8]> {
        Cow::from(self.data)
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        self.section.name().ok()
    }

    #[inline]
    fn segment_name(&self) -> Option<&str> {
        self.section.segname().ok()
    }

    fn kind(&self) -> SectionKind {
        match (self.segment_name(), self.name()) {
            (Some("__TEXT"), Some("__text")) => SectionKind::Text,
            (Some("__DATA"), Some("__data")) => SectionKind::Data,
            (Some("__DATA"), Some("__bss")) => SectionKind::UninitializedData,
            _ => SectionKind::Other,
        }
    }
}

impl<'data> fmt::Debug for MachOSymbolIterator<'data> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("MachOSymbolIterator").finish()
    }
}

impl<'data> Iterator for MachOSymbolIterator<'data> {
    type Item = Symbol<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(symbol) = self.symbols.next() {
            if let Ok((name, nlist)) = symbol {
                if nlist.n_type & mach::symbols::N_STAB != 0 {
                    continue;
                }
                let n_type = nlist.n_type & mach::symbols::NLIST_TYPE_MASK;
                let section_kind = if n_type == mach::symbols::N_SECT {
                    if nlist.n_sect == 0 {
                        None
                    } else {
                        self.section_kinds.get(nlist.n_sect - 1).cloned()
                    }
                } else {
                    // TODO: better handling for other n_type values
                    None
                };
                let kind = match section_kind {
                    Some(SectionKind::Text) => SymbolKind::Text,
                    Some(SectionKind::Data)
                    | Some(SectionKind::ReadOnlyData)
                    | Some(SectionKind::UninitializedData) => SymbolKind::Data,
                    _ => SymbolKind::Unknown,
                };
                return Some(Symbol {
                    name: Some(name),
                    address: nlist.n_value,
                    // Only calculated for symbol maps
                    size: 0,
                    kind,
                    section_kind,
                    global: nlist.is_global(),
                });
            }
        }
        None
    }
}
<commit_msg>Update macho.rs<commit_after>use alloc::borrow::Cow;
use std::fmt;
use std::slice;
use alloc::vec::Vec;

use goblin::mach;
use goblin::mach::load_command::CommandVariant;
use uuid::Uuid;

use {Machine, Object, ObjectSection, ObjectSegment, SectionKind, Symbol, SymbolKind, SymbolMap};

/// A Mach-O object file.
#[derive(Debug)]
pub struct MachOFile<'data> {
    macho: mach::MachO<'data>,
}

/// An iterator over the segments of a `MachOFile`.
#[derive(Debug)]
pub struct MachOSegmentIterator<'data, 'file>
where
    'data: 'file,
{
    segments: slice::Iter<'file, mach::segment::Segment<'data>>,
}

/// A segment of a `MachOFile`.
#[derive(Debug)]
pub struct MachOSegment<'data, 'file>
where
    'data: 'file,
{
    segment: &'file mach::segment::Segment<'data>,
}

/// An iterator over the sections of a `MachOFile`.
pub struct MachOSectionIterator<'data, 'file>
where
    'data: 'file,
{
    segments: slice::Iter<'file, mach::segment::Segment<'data>>,
    sections: Option<mach::segment::SectionIterator<'data>>,
}

/// A section of a `MachOFile`.
#[derive(Debug)]
pub struct MachOSection<'data> {
    section: mach::segment::Section,
    data: mach::segment::SectionData<'data>,
}

/// An iterator over the symbols of a `MachOFile`.
pub struct MachOSymbolIterator<'data> {
    symbols: mach::symbols::SymbolIterator<'data>,
    section_kinds: Vec<SectionKind>,
}

impl<'data> MachOFile<'data> {
    /// Get the Mach-O headers of the file.
    // TODO: this is temporary to allow access to features this crate doesn't provide yet
    #[inline]
    pub fn macho(&self) -> &mach::MachO<'data> {
        &self.macho
    }

    /// Parse the raw Mach-O file data.
    pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> {
        let macho = mach::MachO::parse(data, 0).map_err(|_| "Could not parse Mach-O header")?;
        Ok(MachOFile { macho })
    }
}

impl<'data, 'file> Object<'data, 'file> for MachOFile<'data>
where
    'data: 'file,
{
    type Segment = MachOSegment<'data, 'file>;
    type SegmentIterator = MachOSegmentIterator<'data, 'file>;
    type Section = MachOSection<'data>;
    type SectionIterator = MachOSectionIterator<'data, 'file>;
    type SymbolIterator = MachOSymbolIterator<'data>;

    fn machine(&self) -> Machine {
        match self.macho.header.cputype {
            mach::cputype::CPU_TYPE_ARM => Machine::Arm,
            mach::cputype::CPU_TYPE_ARM64 => Machine::Arm64,
            mach::cputype::CPU_TYPE_X86 => Machine::X86,
            mach::cputype::CPU_TYPE_X86_64 => Machine::X86_64,
            _ => Machine::Other,
        }
    }

    fn segments(&'file self) -> MachOSegmentIterator<'data, 'file> {
        MachOSegmentIterator {
            segments: self.macho.segments.iter(),
        }
    }

    fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg
        // ".debug_info" to "__debug_info".
        let (system_section, section_name) = if section_name.starts_with('.') {
            (true, &section_name[1..])
        } else {
            (false, section_name)
        };
        let cmp_section_name = |name: &str| if system_section {
            name.starts_with("__") && section_name == &name[2..]
        } else {
            section_name == name
        };

        for segment in &self.macho.segments {
            for section in segment {
                if let Ok((section, data)) = section {
                    if let Ok(name) = section.name() {
                        if cmp_section_name(name) {
                            return Some(Cow::from(data));
                        }
                    }
                }
            }
        }
        None
    }

    fn sections(&'file self) -> MachOSectionIterator<'data, 'file> {
        MachOSectionIterator {
            segments: self.macho.segments.iter(),
            sections: None,
        }
    }

    fn symbols(&'file self) -> MachOSymbolIterator<'data> {
        let symbols = match self.macho.symbols {
            Some(ref symbols) => symbols.into_iter(),
            None => mach::symbols::SymbolIterator::default(),
        };

        let mut section_kinds = Vec::new();
        // Don't use MachOSectionIterator because it skips sections it fails to parse,
        // and the section index is important.
        for segment in &self.macho.segments {
            for section in segment {
                if let Ok((section, data)) = section {
                    let section = MachOSection { section, data };
                    section_kinds.push(section.kind());
                } else {
                    // Add placeholder so that indexing works.
                    section_kinds.push(SectionKind::Unknown);
                }
            }
        }

        MachOSymbolIterator {
            symbols,
            section_kinds,
        }
    }

    fn dynamic_symbols(&'file self) -> MachOSymbolIterator<'data> {
        // The LC_DYSYMTAB command contains indices into the same symbol
        // table as the LC_SYMTAB command, so return all of them.
        self.symbols()
    }

    fn symbol_map(&self) -> SymbolMap<'data> {
        let mut symbols: Vec<_> = self.symbols().collect();

        // Add symbols for the end of each section.
        for section in self.sections() {
            symbols.push(Symbol {
                name: None,
                address: section.address() + section.size(),
                size: 0,
                kind: SymbolKind::Section,
                section_kind: None,
                global: false,
            });
        }

        // Calculate symbol sizes by sorting and finding the next symbol.
        symbols.sort_by(|a, b| {
            a.address.cmp(&b.address).then_with(|| {
                // Place the end of section symbols last.
                (a.kind == SymbolKind::Section).cmp(&(b.kind == SymbolKind::Section))
            })
        });

        for i in 0..symbols.len() {
            let (before, after) = symbols.split_at_mut(i + 1);
            let symbol = &mut before[i];
            if symbol.kind != SymbolKind::Section {
                if let Some(next) = after
                    .iter()
                    .skip_while(|x| x.kind != SymbolKind::Section && x.address == symbol.address)
                    .next()
                {
                    symbol.size = next.address - symbol.address;
                }
            }
        }

        symbols.retain(SymbolMap::filter);
        SymbolMap { symbols }
    }

    #[inline]
    fn is_little_endian(&self) -> bool {
        self.macho.little_endian
    }

    fn has_debug_symbols(&self) -> bool {
        self.section_data_by_name(".debug_info").is_some()
    }

    fn mach_uuid(&self) -> Option<Uuid> {
        // Return the UUID from the `LC_UUID` load command, if one is present.
        self.macho.load_commands.iter().filter_map(|lc| {
            match lc.command {
                CommandVariant::Uuid(ref cmd) => {
                    //TODO: Uuid should have a `from_array` method that can't fail.
                    Uuid::from_slice(&cmd.uuid).ok()
                }
                _ => None,
            }
        }).nth(0)
    }

    fn entry(&self) -> u64 {
        self.macho.entry
    }
}

impl<'data, 'file> Iterator for MachOSegmentIterator<'data, 'file> {
    type Item = MachOSegment<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.segments.next().map(|segment| MachOSegment { segment })
    }
}

impl<'data, 'file> ObjectSegment<'data> for MachOSegment<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        self.segment.vmaddr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.segment.vmsize
    }

    #[inline]
    fn data(&self) -> &'data [u8] {
        self.segment.data
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        self.segment.name().ok()
    }
}

impl<'data, 'file> fmt::Debug for MachOSectionIterator<'data, 'file> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // It's painful to do much better than this
        f.debug_struct("MachOSectionIterator").finish()
    }
}

impl<'data, 'file> Iterator for MachOSectionIterator<'data, 'file> {
    type Item = MachOSection<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(ref mut sections) = self.sections {
                while let Some(section) = sections.next() {
                    if let Ok((section, data)) = section {
                        return Some(MachOSection { section, data });
                    }
                }
            }
            match self.segments.next() {
                None => return None,
                Some(segment) => {
                    self.sections = Some(segment.into_iter());
                }
            }
        }
    }
}

impl<'data> ObjectSection<'data> for MachOSection<'data> {
    #[inline]
    fn address(&self) -> u64 {
        self.section.addr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.section.size
    }

    #[inline]
    fn data(&self) -> Cow<'data, [u8]> {
        Cow::from(self.data)
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        self.section.name().ok()
    }

    #[inline]
    fn segment_name(&self) -> Option<&str> {
        self.section.segname().ok()
    }

    fn kind(&self) -> SectionKind {
        match (self.segment_name(), self.name()) {
            (Some("__TEXT"), Some("__text")) => SectionKind::Text,
            (Some("__DATA"), Some("__data")) => SectionKind::Data,
            (Some("__DATA"), Some("__bss")) => SectionKind::UninitializedData,
            _ => SectionKind::Other,
        }
    }
}

impl<'data> fmt::Debug for MachOSymbolIterator<'data> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("MachOSymbolIterator").finish()
    }
}

impl<'data> Iterator for MachOSymbolIterator<'data> {
    type Item = Symbol<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(symbol) = self.symbols.next() {
            if let Ok((name, nlist)) = symbol {
                if nlist.n_type & mach::symbols::N_STAB != 0 {
                    continue;
                }
                let n_type = nlist.n_type & mach::symbols::NLIST_TYPE_MASK;
                let section_kind = if n_type == mach::symbols::N_SECT {
                    if nlist.n_sect == 0 {
                        None
                    } else {
                        self.section_kinds.get(nlist.n_sect - 1).cloned()
                    }
                } else {
                    // TODO: better handling for other n_type values
                    None
                };
                let kind = match section_kind {
                    Some(SectionKind::Text) => SymbolKind::Text,
                    Some(SectionKind::Data)
                    | Some(SectionKind::ReadOnlyData)
                    | Some(SectionKind::UninitializedData) => SymbolKind::Data,
                    _ => SymbolKind::Unknown,
                };
                return Some(Symbol {
                    name: Some(name),
                    address: nlist.n_value,
                    // Only calculated for symbol maps
                    size: 0,
                    kind,
                    section_kind,
                    global: nlist.is_global(),
                });
            }
        }
        None
    }
}
<|endoftext|>


<commit_before>
//
//             MBAST: Mini B-language Abstract Syntax Tree
//

#![allow(dead_code)]

enum Mbast {

    Component
    {   kind: ComponentKind
    ,   name: String
    ,   formal_param: Vec<String>
    ,   refines: Option<String>

    ,   imports: Vec< ( String, Option<Box<Mbast>> ) >
    ,   includes: Vec< ( String, Option<Box<Mbast>> ) >
    ,   extends: Vec< ( String, Option<Box<Mbast>> ) >

    ,   sees: Vec<String>
    ,   promotes: Vec<String>
    ,   uses: Vec<String>

    ,   abstract_constants: Vec<String>
    ,   abstract_variables: Vec<String>
    ,   concrete_constants: Vec<String>
    ,   concrete_variables: Vec<String>
    ,   constants: Vec<String>
    ,   variables: Vec<String>

    ,   constraints: Option<Box<Mbast>>
    ,   properties: Option<Box<Mbast>>
    ,   invariant: Option<Box<Mbast>>

    ,   sets: Vec<Box<Mbast>>
    ,   values: Vec<Box<Mbast>>
    ,   assertions: Vec<Box<Mbast>>

    ,   initialisation: Option<Box<Mbast>>
    ,   operations: Vec<Box<Mbast>>
    ,   local_operations: Vec<Box<Mbast>>
    },

    Operation(Vec<String>, String, Vec<String>, Box<Mbast>),

    SubBin(Box<Mbast>, SubBinOpe, Box<Mbast>),

    SubBegin( Box<Mbast> ),

    SubPreAssert( SubPreAssertOpe, Box<Mbast>, Box<Mbast> ),

    SubAnyLet( SubAnyLet, Vec<String>, Box<Mbast>, Box<Mbast> ),

    SubVar( Vec<String>, Box<Mbast> ),

    SubChoice( Vec<Box<Mbast>> ),

    SubIfSelect( SubIfSelect, Vec<( Box<Mbast>, Box<Mbast> )>, Option<Box<Mbast>> ),

    SubCase( Box<Mbast>, Vec<( Box<Mbast>, Box<Mbast> )>, Option<Box<Mbast>> ),

    SubWhile( Box<Mbast>, Box<Mbast>, Box<Mbast>, Box<Mbast> ),

    ExprUnary( ExprUnaryOpe, Box<Mbast> ),

    ExprStruct( Vec< ( String, Box<Mbast> ) > ),
    ExprRec( Vec< ( Option<String>, Box<Mbast> ) > ),

    ExprBin( Box<Mbast>, ExprBinOpe, Box<Mbast> ),

    ExprId(String),
    ExprNum(u32),

    ExprQPred( ExprQPredOpe, Vec<String>, Box<Mbast> ),
    ExprQuant( ExprQuantOpe, Vec<String>, Box<Mbast>, Box<Mbast> ),
}

enum ComponentKind{ Machine, Refinement, Implementation }

enum SubBinOpe{
    Sequence,       // sub ; sub
    Parallel,       // sub || sub
    Assign,         // expr := expr
    BecomeIn,       // expr :: expr
    BecomeSuchThat, // expr : ( expr )
    OperationCall,  // idList <-- id(idList)  (or variants)
}

enum SubPreAssertOpe{ Pre, Assert }
enum SubAnyLet{ Any, Let }
enum SubIfSelect{ If, Select }

enum ExprUnaryOpe
{   Minus               // -x
,   Inverse             // x~
,   Paren               // (x)
,   SetExtension        // {x}
,   ListExtension       // [x]
}

enum ExprQPredOpe{ Forall, Exist }
enum ExprQuantOpe{ Lambda, Sigma, Pi, Union, Inter }

enum ExprBinOpe
{   Apply         // f(x)
,   Image         // r[x]
,   SetCompreh    // { x | P }
,   Mod           // x mod y
,   Comma         // ,
,   Maplet        // |->
,   Plus          // +
,   Minus         // -
,   Star          // *
,   Slash         // /
,   Pow           // **
,   And           // &
,   Imply         // =>
,   Equiv         // <=>
,   Barbar        // ||
,   Semicolon     // ;
,   Equal         // =
,   Neq           // /=
,   Lt            // <
,   Leq           // <=
,   Gt            // >
,   Geq           // >=
,   Colon         // :
,   Nin           // /:
,   Incl          // <:
,   Nincl         // /<:
,   Inclstr       // <<:
,   Ninclstr      // /<<:
,   Pfun          // +->
,   Pinj          // >+>
,   Psur          // +->>
,   Pbij          // >+>>
,   Tfun          // -->
,   Tinj          // >->
,   Tsur          // -->>
,   Tbij          // >->>
,   Sinter        // \/
,   Sunion        // \/
,   Overw         // <+
,   Rel           // <->
,   Reldprod      // ><
,   Interval      // ..
,   Domrestr      // <|
,   Domsub        // <<|
,   Ranrestr      // |>
,   Ransub        // |>>
,   Larw          // <-
,   Rarw          // ->
,   Uparrow       // /|\
,   Lowarrow      // \|/
,   Caret         // ^
}<commit_msg>split mbast into: component, operation, substitution and expression<commit_after>
//
//             MBAST: Mini B-language Abstract Syntax Tree
//
// It's a "mini" B-AST because the types allow more than legal B in order to
// simplify and factorize.

#![allow(dead_code)]

struct Component {
    kind: ComponentKind,
    name: String,
    formal_param: Vec<String>,
    refines: Option<String>,

    imports: Vec< ( String, Option<Box<Expr>> ) >,
    includes: Vec< ( String, Option<Box<Expr>> ) >,
    extends: Vec< ( String, Option<Box<Expr>> ) >,

    sees: Vec<String>,
    promotes: Vec<String>,
    uses: Vec<String>,

    abstract_constants: Vec<String>,
    abstract_variables: Vec<String>,
    concrete_constants: Vec<String>,
    concrete_variables: Vec<String>,
    constants: Vec<String>,
    variables: Vec<String>,

    constraints: Option<Box<Expr>>,
    properties: Option<Box<Expr>>,
    invariant: Option<Box<Expr>>,

    sets: Vec<Box<Expr>>,
    values: Vec<Box<Expr>>,
    assertions: Vec<Box<Expr>>,

    initialisation: Option<Box<Sub>>,
    operations: Vec<Box<Ope>>,
    local_operations: Vec<Box<Ope>>,
}

struct Ope{
    param_out: Vec<String>,
    name: String,
    param_in: Vec<String>,
    sub: Box<Sub>,
}

enum Sub {
    SubBin(Box<Sub>, SubBinOpe, Box<Sub>),

    SubBegin( Box<Sub> ),

    SubPreAssert( SubPreAssertOpe, Box<Expr>, Box<Sub> ),

    SubAnyLet( SubAnyLet, Vec<String>, Box<Expr>, Box<Sub> ),

    SubVar( Vec<String>, Box<Sub> ),

    SubChoice( Vec<Box<Sub>> ),

    SubIfSelect( SubIfSelect, Vec<( Box<Expr>, Box<Sub> )>, Option<Box<Sub>> ),

    SubCase( Box<Expr>, Vec<( Box<Expr>, Box<Sub> )>, Option<Box<Sub>> ),

    SubWhile( Box<Expr>, Box<Sub>, Box<Expr>, Box<Expr> ),
}

enum Expr {
    ExprUnary( ExprUnaryOpe, Box<Expr> ),

    ExprStruct( Vec< ( String, Box<Expr> ) > ),
    ExprRec( Vec< ( Option<String>, Box<Expr> ) > ),

    ExprBin( Box<Expr>, ExprBinOpe, Box<Expr> ),

    ExprId(String),
    ExprNum(u32),

    ExprQPred( ExprQPredOpe, Vec<String>, Box<Expr> ),
    ExprQuant( ExprQuantOpe, Vec<String>, Box<Expr>, Box<Expr> ),
}

enum ComponentKind{ Machine, Refinement, Implementation }

enum SubBinOpe{
    Sequence,       // sub ; sub
    Parallel,       // sub || sub
    Assign,         // expr := expr
    BecomeIn,       // expr :: expr
    BecomeSuchThat, // expr : ( expr )
    OperationCall,  // idList <-- id(idList)  (or variants)
}

enum SubPreAssertOpe{ Pre, Assert }
enum SubAnyLet{ Any, Let }
enum SubIfSelect{ If, Select }

enum ExprUnaryOpe
{   Minus               // -x
,   Inverse             // x~
,   Paren               // (x)
,   SetExtension        // {x}
,   ListExtension       // [x]
}

enum ExprQPredOpe{ Forall, Exist }
enum ExprQuantOpe{ Lambda, Sigma, Pi, Union, Inter }

enum ExprBinOpe
{   Apply         // f(x)
,   Image         // r[x]
,   SetCompreh    // { x | P }
,   Mod           // x mod y
,   Comma         // ,
,   Maplet        // |->
,   Plus          // +
,   Minus         // -
,   Star          // *
,   Slash         // /
,   Pow           // **
,   And           // &
,   Imply         // =>
,   Equiv         // <=>
,   Barbar        // ||
,   Semicolon     // ;
,   Equal         // =
,   Neq           // /=
,   Lt            // <
,   Leq           // <=
,   Gt            // >
,   Geq           // >=
,   Colon         // :
,   Nin           // /:
,   Incl          // <:
,   Nincl         // /<:
,   Inclstr       // <<:
,   Ninclstr      // /<<:
,   Pfun          // +->
,   Pinj          // >+>
,   Psur          // +->>
,   Pbij          // >+>>
,   Tfun          // -->
,   Tinj          // >->
,   Tsur          // -->>
,   Tbij          // >->>
,   Sinter        // \/
,   Sunion        // \/
,   Overw         // <+
,   Rel           // <->
,   Reldprod      // ><
,   Interval      // ..
,   Domrestr      // <|
,   Domsub        // <<|
,   Ranrestr      // |>
,   Ransub        // |>>
,   Larw          // <-
,   Rarw          // ->
,   Uparrow       // /|\
,   Lowarrow      // \|/
,   Caret         // ^
}<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::iter::FromIterator;

use ::longest_common_subsequence;
use ::longest_common_subsequence::CommonSubsequence;

// TODO: Some of these parameters might be unused. Audit and remove if possible.
/// Represents the states of a 4-state machine representing the traversal through `old` to find
/// stable and unstable chunks: at any given moment, the part of `old` under consideration is either
/// unmatched in both `new` and `other`, matched in only one, or matched in both.
#[derive(Debug)]
enum MatchState {
    NeitherMatch,
    /// Parameters are the offsets into old and new where the match started.
    OnlyNewMatches(usize, usize),
    /// Parameters are the offsets into old and other where the match started.
    OnlyOtherMatches(usize, usize),
    /// Parameters are the offsets into old, new, and other where the match started.
    BothMatch(usize, usize, usize),
}

// TODO: I bet some of these parameters are unused. Audit and remove if possible.
/// Represents the transitions in the 4-state machine representing the traversal through `old` to
/// find stable and unstable chunks: at any index, either `new` or `other` may either start matching
/// `old` (if it were not already matching `old` at that index), or stop matching `old` (if if was
/// already matching `old` at that index).
///
/// For the set of valid (state, transition) pairs, see `merge()`.
#[derive(Debug, PartialEq, Eq)]
enum MatchStateTransition {
    /// Offset into old, offset into new
    NewStartsMatching(usize, usize),
    /// Offset into old, offset into new
    NewStopsMatching(usize, usize),
    /// Offset into old, offset into other
    OtherStartsMatching(usize, usize),
    /// Offset into old, offset into other
    OtherStopsMatching(usize, usize),
}

use merge::MatchState::*;
use merge::MatchStateTransition::*;

/// Orders MatchStateTransitions by their offset into old (which is the ordering that
/// `calculate_match_state_transitions()` cares about).
impl Ord for MatchStateTransition {
    fn cmp(&self, other: &MatchStateTransition) -> Ordering {
        let self_offset = match self {
            &NewStartsMatching(ref offset, _) => offset,
            &NewStopsMatching(ref offset, _) => offset,
            &OtherStartsMatching(ref offset, _) => offset,
            &OtherStopsMatching(ref offset, _) => offset,
        };
        let other_offset = match other {
            &NewStartsMatching(ref offset, _) => offset,
            &NewStopsMatching(ref offset, _) => offset,
            &OtherStartsMatching(ref offset, _) => offset,
            &OtherStopsMatching(ref offset, _) => offset,
        };
        match self_offset.cmp(other_offset) {
            Ordering::Less | Ordering::Greater => self_offset.cmp(other_offset),
            Ordering::Equal => {
                // For transitions at the same index, we put stops before starts to minimize the
                // number of empty chunks output, and put New* before Other* arbitrarily.
                let self_type = match self {
                    &NewStopsMatching(..) => 1,
                    &OtherStopsMatching(..) => 2,
                    &NewStartsMatching(..) => 3,
                    &OtherStartsMatching(..) => 4,
                };
                let other_type = match other {
                    &NewStopsMatching(..) => 1,
                    &OtherStopsMatching(..) => 2,
                    &NewStartsMatching(..) => 3,
                    &OtherStartsMatching(..) => 4,
                };
                self_type.cmp(&other_type)
            }
        }
    }
}

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

/// Reprpesents the  TODO: finish comment. Or, just use tuples (usize, usize, usize, bool) for this.
#[derive(Debug)]
enum ChunkEnd {
    Stable(usize, usize, usize),
    Unstable(usize, usize, usize),
}

#[derive(Debug, PartialEq, Eq)]
enum Chunk {
    // Parameters: The offset into old, and length of the chunk.
    Stable(usize, usize),
    // Parameters: The (offset into string, length) of the chunk for old, new, and other
    // respectively.
    Unstable((usize, usize), (usize, usize), (usize, usize)),
}

fn try_merge(old: &str, new: &str, other: &str) -> String {
    let new_substrings = longest_common_subsequence::get_longest_common_subsequence(old, new)
        .common_substrings.into_iter();
    let other_substrings = longest_common_subsequence::get_longest_common_subsequence(old, other)
        .common_substrings.into_iter();
    "asdf".to_string()
}

// TODO: doc comment
fn merge(new_lcs: CommonSubsequence, other_lcs: CommonSubsequence) -> Vec<Chunk> {
    let match_state_transitions = calculate_match_state_transitions(new_lcs, other_lcs);

    // TODO: I probably don't need to use a mutable vector like this. Try to rewrite with fold() if possible.
    let mut chunk_ends: Vec<ChunkEnd> = Vec::new();
    let mut match_state = NeitherMatch;
    for match_state_transition in match_state_transitions {
        //println!("{:?} {:?}", match_state, match_state_transition);
        // First, calculate the end offsets of each chunk.
        // TODO: If I just called these "previous_old_offset" &c., would that be any less understandable?
        match (&match_state, &match_state_transition) {
            (&OnlyNewMatches(previous_match_old_offset, previous_match_new_offset),
             &OtherStartsMatching(current_match_old_offset, current_match_other_offset)) => {
                let offset_in_old = current_match_old_offset;
                let offset_in_new = previous_match_new_offset +
                    (current_match_old_offset - previous_match_old_offset);
                let offset_in_other = current_match_other_offset;
                chunk_ends.push(ChunkEnd::Unstable(offset_in_old, offset_in_new, offset_in_other));
                println!("Output chunk end {:?}",
                         ChunkEnd::Unstable(offset_in_old, offset_in_new, offset_in_other));
            },
            (&OnlyOtherMatches(previous_match_old_offset, previous_match_other_offset),
             &NewStartsMatching(current_match_old_offset, current_match_new_offset)) => {
                let offset_in_old = current_match_old_offset;
                let offset_in_new = current_match_new_offset;
                let offset_in_other = previous_match_other_offset +
                    (current_match_old_offset - previous_match_old_offset);
                chunk_ends.push(ChunkEnd::Unstable(offset_in_old, offset_in_new, offset_in_other));
                println!("Output chunk end {:?}",
                         ChunkEnd::Unstable(offset_in_old, offset_in_new, offset_in_other));
            },
            (&BothMatch(previous_match_old_offset, _, previous_match_other_offset),
             &NewStopsMatching(current_match_old_offset, current_match_new_offset)) => {
                let length = current_match_old_offset - previous_match_old_offset;
                chunk_ends.push(ChunkEnd::Stable(current_match_old_offset, current_match_new_offset,
                                                 previous_match_other_offset + length));
                println!("Output chunk end {:?}",
                         ChunkEnd::Stable(current_match_old_offset, current_match_new_offset,
                                          previous_match_other_offset + length));
            }
            (&BothMatch(previous_match_old_offset, previous_match_new_offset, _),
             &OtherStopsMatching(current_match_old_offset, current_match_other_offset)) => {
                let length = current_match_old_offset - previous_match_old_offset;
                chunk_ends.push(
                    ChunkEnd::Stable(current_match_old_offset, previous_match_new_offset + length,
                                     current_match_other_offset));
                println!("Output chunk end {:?}",
                         ChunkEnd::Stable(current_match_old_offset, previous_match_new_offset + length,
                                     current_match_other_offset));
            }
            _ => (),
        }

        // Then, move to the next state in the state machine.
        match_state = match (match_state, match_state_transition) {
            (NeitherMatch,     NewStartsMatching(old, new))   => OnlyNewMatches(old, new),
            (NeitherMatch,     OtherStartsMatching(old, new)) => OnlyOtherMatches(old, new),

            (OnlyNewMatches(previous_match_old_offset, previous_match_new_offset),
             OtherStartsMatching(current_match_old_offset, current_match_other_offset)) => {
                let length = current_match_old_offset - previous_match_old_offset;
                BothMatch(current_match_old_offset, previous_match_new_offset + length,
                          current_match_other_offset)
            },
            (OnlyNewMatches(_, _), NewStopsMatching(_, _)) => NeitherMatch,

            (OnlyOtherMatches(previous_match_old_offset, previous_match_other_offset),
             NewStartsMatching(current_match_old_offset, current_match_new_offset))   => {
                let length = current_match_old_offset - previous_match_old_offset;
                BothMatch(current_match_old_offset, current_match_new_offset,
                          previous_match_other_offset + length)
            },
            (OnlyOtherMatches(_, _), OtherStopsMatching(_, _))  => NeitherMatch,

            (BothMatch(old, new, other), NewStopsMatching(_, _)) => OnlyOtherMatches(old, other),
            (BothMatch(old, new, other), OtherStopsMatching(_, _))  => OnlyNewMatches(old, new),

            (state, transition) => {
                unreachable!("Illegal transition {:?} from state {:?}", transition, state);
            },
        };
    }

    let mut chunks: Vec<Chunk> = Vec::with_capacity(chunk_ends.len());
    let mut old_offset = 0;
    let mut new_offset = 0;
    let mut other_offset = 0;
    for chunk_end in chunk_ends {
        match chunk_end {
            ChunkEnd::Stable(old, new, other) => {
                chunks.push(Chunk::Stable(old_offset, old - old_offset));
                old_offset = old;
                new_offset = new;
                other_offset = other;
            },
            ChunkEnd::Unstable(old, new, other) => {
                chunks.push(Chunk::Unstable((old_offset, old - old_offset),
                                            (new_offset, new - new_offset),
                                            (other_offset, other - other_offset)));
                old_offset = old;
                new_offset = new;
                other_offset = other;
            },
        }
    }
    chunks.into_iter().filter(|chunk| match chunk {
        &Chunk::Stable(_, length) => length != 0,
        &Chunk::Unstable((_, old_length), (_, new_length), (_, other_length)) =>
            old_length != 0 || new_length != 0 || other_length != 0,
    }).collect()
}

/// From the LCS's for `old`/`new` and `old`/`other`, constructs a vector representing the state transitions
/// over the course of the string.
fn calculate_match_state_transitions(new_lcs: CommonSubsequence, other_lcs: CommonSubsequence) ->
    Vec<MatchStateTransition> {
    let mut match_state_transitions = Vec::from_iter(
        new_lcs.common_substrings.into_iter().flat_map(|common_substring| vec![
            NewStartsMatching(common_substring.str1_offset, common_substring.str2_offset),
            NewStopsMatching(
                common_substring.str1_offset + common_substring.size_bytes,
                common_substring.str2_offset + common_substring.size_bytes)].into_iter()).chain(
            other_lcs.common_substrings.into_iter().flat_map(|common_substring| vec![
                OtherStartsMatching(common_substring.str1_offset, common_substring.str2_offset),
                OtherStopsMatching(
                    common_substring.str1_offset + common_substring.size_bytes,
                    common_substring.str2_offset + common_substring.size_bytes)].into_iter())));
    match_state_transitions.sort();
    match_state_transitions
}

#[cfg(test)]
mod tests {
    use super::{Chunk, calculate_match_state_transitions, merge};
    use super::MatchStateTransition::*;
    use longest_common_subsequence::{CommonSubsequence, CommonSubstring};

    #[test]
    fn test_calculate_match_state_transitions() {
        // This is from figure 1 of diff3-short.pdf.
        // TODO: comment better.
        let new_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 1,
                },
                CommonSubstring {
                    str1_offset: 1,
                    str2_offset: 3,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 4,
            size_chars: 4,
        };
        let other_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 3,
                    str2_offset: 2,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 5,
            size_chars: 5,
        };
        let expected = vec![
            NewStartsMatching(0, 0),
            OtherStartsMatching(0, 0),

            NewStopsMatching(1, 1),
            NewStartsMatching(1, 3),

            OtherStopsMatching(2, 2),

            NewStopsMatching(3, 5),
            OtherStartsMatching(3, 2),

            OtherStopsMatching(5, 4),
            NewStartsMatching(5, 5),
            OtherStartsMatching(5, 5),

            NewStopsMatching(6, 6),
            OtherStopsMatching(6, 6)];
        assert_eq!(expected, calculate_match_state_transitions(new_lcs, other_lcs));
    }

    // TODO: Make sure this test covers all branches, transitions, states, etc.
    #[test]
    fn test_whatever() {
        // This is from figure 1 of diff3-short.pdf.
        // TODO: comment better.
        let new_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 1,
                },
                CommonSubstring {
                    str1_offset: 1,
                    str2_offset: 3,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 4,
            size_chars: 4,
        };
        let other_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 3,
                    str2_offset: 2,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 5,
            size_chars: 5,
        };
        let expected = vec![Chunk::Stable(0, 1),
                            Chunk::Unstable((1, 0), (1, 2), (1, 0)),
                            Chunk::Stable(1, 1),
                            Chunk::Unstable((2, 3), (4, 1), (2, 3)),
                            Chunk::Stable(5, 1)];

        assert_eq!(expected, merge(new_lcs, other_lcs));
    }
}
<commit_msg>Some code cleanup in merge.rs.<commit_after>use std::cmp::Ordering;
use std::iter::FromIterator;

use ::longest_common_subsequence;
use ::longest_common_subsequence::CommonSubsequence;

/// Represents the states of a 4-state machine representing the traversal through `old` to find
/// stable and unstable chunks: at any given moment, the part of `old` under consideration is either
/// unmatched in both `new` and `other`, matched in only one, or matched in both.
#[derive(Debug)]
enum MatchState {
    NeitherMatch,
    /// Parameters are the offsets into old and new where the match started.
    OnlyNewMatches(usize, usize),
    /// Parameters are the offsets into old and other where the match started.
    OnlyOtherMatches(usize, usize),
    /// Parameters are the offsets into old, new, and other where the match started.
    BothMatch(usize, usize, usize),
}

/// Represents the transitions in the 4-state machine representing the traversal through `old` to
/// find stable and unstable chunks: at any index, either `new` or `other` may either start matching
/// `old` (if it were not already matching `old` at that index), or stop matching `old` (if if was
/// already matching `old` at that index).
///
/// For the set of valid (state, transition) pairs, see `merge()`.
#[derive(Debug, PartialEq, Eq)]
enum MatchStateTransition {
    /// Offset into old, offset into new
    NewStartsMatching(usize, usize),
    /// Offset into old, offset into new
    NewStopsMatching(usize, usize),
    /// Offset into old, offset into other
    OtherStartsMatching(usize, usize),
    /// Offset into old, offset into other
    OtherStopsMatching(usize, usize),
}

use merge::MatchState::*;
use merge::MatchStateTransition::*;

/// Orders MatchStateTransitions by their offset into old (which is the ordering that
/// `calculate_match_state_transitions()` cares about).
impl Ord for MatchStateTransition {
    fn cmp(&self, other: &MatchStateTransition) -> Ordering {
        let self_offset = match self {
            &NewStartsMatching(ref offset, _) => offset,
            &NewStopsMatching(ref offset, _) => offset,
            &OtherStartsMatching(ref offset, _) => offset,
            &OtherStopsMatching(ref offset, _) => offset,
        };
        let other_offset = match other {
            &NewStartsMatching(ref offset, _) => offset,
            &NewStopsMatching(ref offset, _) => offset,
            &OtherStartsMatching(ref offset, _) => offset,
            &OtherStopsMatching(ref offset, _) => offset,
        };
        match self_offset.cmp(other_offset) {
            Ordering::Less | Ordering::Greater => self_offset.cmp(other_offset),
            Ordering::Equal => {
                // For transitions at the same offset in old, we put stops before starts to minimize
                // the number of empty chunks output, and put New* before Other* arbitrarily.
                let self_type = match self {
                    &NewStopsMatching(..) => 1,
                    &OtherStopsMatching(..) => 2,
                    &NewStartsMatching(..) => 3,
                    &OtherStartsMatching(..) => 4,
                };
                let other_type = match other {
                    &NewStopsMatching(..) => 1,
                    &OtherStopsMatching(..) => 2,
                    &NewStartsMatching(..) => 3,
                    &OtherStartsMatching(..) => 4,
                };
                self_type.cmp(&other_type)
            }
        }
    }
}

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

/// Reprpesents the  TODO: finish comment. Or, just use tuples (usize, usize, usize, bool) for this.
#[derive(Debug)]
enum ChunkEnd {
    Stable(usize, usize, usize),
    Unstable(usize, usize, usize),
}

#[derive(Debug, PartialEq, Eq)]
enum Chunk {
    // Parameters: The offset into old, and length of the chunk.
    Stable(usize, usize),
    // Parameters: The (offset into string, length) of the chunk for old, new, and other
    // respectively.
    Unstable((usize, usize), (usize, usize), (usize, usize)),
}

fn try_merge(old: &str, new: &str, other: &str) -> String {
    let new_substrings = longest_common_subsequence::get_longest_common_subsequence(old, new)
        .common_substrings.into_iter();
    let other_substrings = longest_common_subsequence::get_longest_common_subsequence(old, other)
        .common_substrings.into_iter();
    "asdf".to_string()
}

// TODO: doc comment
fn parse(new_lcs: CommonSubsequence, other_lcs: CommonSubsequence) -> Vec<Chunk> {
    let match_state_transitions = calculate_match_state_transitions(new_lcs, other_lcs);

    let mut chunk_ends: Vec<ChunkEnd> = Vec::new();
    let mut match_state = NeitherMatch;
    for transition in match_state_transitions {
        match calculate_chunk_end(&match_state, &transition) {
            Some(chunk_end) => chunk_ends.push(chunk_end),
            None => (),
        }
        match_state = calculate_next_state(&match_state, &transition);
    }

    let mut chunks: Vec<Chunk> = Vec::with_capacity(chunk_ends.len());
    let mut old_offset = 0;
    let mut new_offset = 0;
    let mut other_offset = 0;
    for chunk_end in chunk_ends {
        match chunk_end {
            ChunkEnd::Stable(old, new, other) => {
                if old != old_offset {
                    chunks.push(Chunk::Stable(old_offset, old - old_offset));
                    old_offset = old;
                    new_offset = new;
                    other_offset = other;
                }
            },
            ChunkEnd::Unstable(old, new, other) => {
                if old != old_offset || new != new_offset || other != other_offset {
                    chunks.push(Chunk::Unstable((old_offset, old - old_offset),
                                                (new_offset, new - new_offset),
                                                (other_offset, other - other_offset)));
                    old_offset = old;
                    new_offset = new;
                    other_offset = other;
                }
            },
        }
    }
    chunks
}

/// From the LCS's for `old`/`new` and `old`/`other`, constructs a vector representing the state transitions
/// over the course of the string.
fn calculate_match_state_transitions(new_lcs: CommonSubsequence, other_lcs: CommonSubsequence) ->
    Vec<MatchStateTransition> {
    let mut match_state_transitions = Vec::from_iter(
        new_lcs.common_substrings.into_iter().flat_map(|common_substring| vec![
            NewStartsMatching(common_substring.str1_offset, common_substring.str2_offset),
            NewStopsMatching(
                common_substring.str1_offset + common_substring.size_bytes,
                common_substring.str2_offset + common_substring.size_bytes)].into_iter()).chain(
            other_lcs.common_substrings.into_iter().flat_map(|common_substring| vec![
                OtherStartsMatching(common_substring.str1_offset, common_substring.str2_offset),
                OtherStopsMatching(
                    common_substring.str1_offset + common_substring.size_bytes,
                    common_substring.str2_offset + common_substring.size_bytes)].into_iter())));
    match_state_transitions.sort();
    match_state_transitions
}

// TODO: doc comment
fn calculate_chunk_end(match_state: &MatchState, transition: &MatchStateTransition) -> Option<ChunkEnd> {
    match (match_state, transition) {
        (&OnlyNewMatches(previous_old_offset, previous_new_offset),
         &OtherStartsMatching(current_old_offset, current_other_offset)) => {
            Some(ChunkEnd::Unstable(
                current_old_offset,
                previous_new_offset + (current_old_offset - previous_old_offset),
                current_other_offset))
        },
        (&OnlyOtherMatches(previous_old_offset, previous_other_offset),
         &NewStartsMatching(current_old_offset, current_new_offset)) => {
            Some(ChunkEnd::Unstable(
                current_old_offset, current_new_offset,
                previous_other_offset + (current_old_offset - previous_old_offset)))
        },
        (&BothMatch(previous_old_offset, _, previous_other_offset),
         &NewStopsMatching(current_old_offset, current_new_offset)) => {
            let length = current_old_offset - previous_old_offset;
            Some(ChunkEnd::Stable(
                current_old_offset, current_new_offset, previous_other_offset + length))
        }
        (&BothMatch(previous_old_offset, previous_new_offset, _),
         &OtherStopsMatching(current_old_offset, current_other_offset)) => {
            let length = current_old_offset - previous_old_offset;
            Some(ChunkEnd::Stable(
                current_old_offset, previous_new_offset + length, current_other_offset))
        }
        _ => None,
    }
}

// TODO: doc comment
fn calculate_next_state(match_state: &MatchState, transition: &MatchStateTransition) -> MatchState {
    match (match_state, transition) {
        (&NeitherMatch, &NewStartsMatching(old, new))   => OnlyNewMatches(old, new),
        (&NeitherMatch, &OtherStartsMatching(old, new)) => OnlyOtherMatches(old, new),

        (&OnlyNewMatches(previous_old_offset, previous_new_offset),
         &OtherStartsMatching(current_old_offset, current_other_offset)) => {
            let length = current_old_offset - previous_old_offset;
            BothMatch(current_old_offset, previous_new_offset + length,
                      current_other_offset)
        },
        (&OnlyNewMatches(_, _), &NewStopsMatching(_, _)) => NeitherMatch,

        (&OnlyOtherMatches(previous_old_offset, previous_other_offset),
         &NewStartsMatching(current_old_offset, current_new_offset))   => {
            let length = current_old_offset - previous_old_offset;
            BothMatch(current_old_offset, current_new_offset,
                      previous_other_offset + length)
        },
        (&OnlyOtherMatches(_, _), &OtherStopsMatching(_, _))  => NeitherMatch,

        (&BothMatch(old, new, other), &NewStopsMatching(_, _)) => OnlyOtherMatches(old, other),
        (&BothMatch(old, new, other), &OtherStopsMatching(_, _))  => OnlyNewMatches(old, new),

        (state, transition) => {
            unreachable!("Illegal transition {:?} from state {:?}", transition, state);
        },
    }
}

#[cfg(test)]
mod tests {
    use super::{Chunk, calculate_match_state_transitions, parse};
    use super::MatchStateTransition::*;
    use longest_common_subsequence::{CommonSubsequence, CommonSubstring};

    #[test]
    fn test_calculate_match_state_transitions() {
        // This is from figure 1 of diff3-short.pdf.
        // TODO: comment better.
        let new_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 1,
                },
                CommonSubstring {
                    str1_offset: 1,
                    str2_offset: 3,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 4,
            size_chars: 4,
        };
        let other_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 3,
                    str2_offset: 2,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 5,
            size_chars: 5,
        };
        let expected = vec![
            NewStartsMatching(0, 0),
            OtherStartsMatching(0, 0),

            NewStopsMatching(1, 1),
            NewStartsMatching(1, 3),

            OtherStopsMatching(2, 2),

            NewStopsMatching(3, 5),
            OtherStartsMatching(3, 2),

            OtherStopsMatching(5, 4),
            NewStartsMatching(5, 5),
            OtherStartsMatching(5, 5),

            NewStopsMatching(6, 6),
            OtherStopsMatching(6, 6)];
        assert_eq!(expected, calculate_match_state_transitions(new_lcs, other_lcs));
    }

    // TODO: Make sure this test covers all branches, transitions, states, etc.
    #[test]
    fn test_whatever() {
        // This is from figure 1 of diff3-short.pdf.
        // TODO: comment better.
        let new_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 1,
                },
                CommonSubstring {
                    str1_offset: 1,
                    str2_offset: 3,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 4,
            size_chars: 4,
        };
        let other_lcs = CommonSubsequence {
            common_substrings: vec![
                CommonSubstring {
                    str1_offset: 0,
                    str2_offset: 0,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 3,
                    str2_offset: 2,
                    size_bytes: 2,
                },
                CommonSubstring {
                    str1_offset: 5,
                    str2_offset: 5,
                    size_bytes: 1,
                }],
            size_bytes: 5,
            size_chars: 5,
        };
        let expected = vec![Chunk::Stable(0, 1),
                            Chunk::Unstable((1, 0), (1, 2), (1, 0)),
                            Chunk::Stable(1, 1),
                            Chunk::Unstable((2, 3), (4, 1), (2, 3)),
                            Chunk::Stable(5, 1)];
        assert_eq!(expected, parse(new_lcs, other_lcs));
    }
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;
use std::fmt::{self, Write};
use std::str::FromStr;

#[derive(Debug)]
struct InvalidGameId;

#[derive(Debug, Clone, Eq, PartialEq)]
struct GameId(u64);

impl TryFrom<u64> for GameId {
    type Error = InvalidGameId;

    fn try_from(n: u64) -> Result<Self, Self::Error> {
        if n < 62u64.pow(8) {
            Ok(GameId(n))
        } else {
            Err(InvalidGameId)
        }
    }
}

impl FromStr for GameId {
    type Err = InvalidGameId;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if dbg!(s).len() != 8 {
            return Err(InvalidGameId);
        }

        let mut n = 0;
        let mut digit = 1;
        for c in s.bytes() {
            n += match c {
                b'0'..=b'9' => c - b'0',
                b'A'..=b'Z' => c - b'A' + 10,
                b'a'..=b'z' => c - b'a' + 10 + 26,
                _ => return Err(InvalidGameId),
            } as u64 * digit;
            digit *= 62;
        }

        Ok(GameId(n))
    }
}

impl fmt::Display for GameId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut n = self.0;
        for _ in 0..8 {
            let rem = n % 62;
            f.write_char(char::from(if rem >= 10 + 26 {
                (rem - (10 + 26)) as u8 + b'a'
            } else if rem >= 10 {
                (rem - 10) as u8 + b'A'
            } else {
                rem as u8 + b'0'
            }))?;
            n /= 62;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::{Arbitrary, Gen, quickcheck};
    use super::*;

    impl Arbitrary for GameId {
        fn arbitrary(g: &mut Gen) -> Self {
            GameId(u64::arbitrary(g) % 62u64.pow(8))
        }
    }

    quickcheck! {
        fn game_id_roundtrip(game_id: GameId) -> bool {
            GameId::from_str(&game_id.to_string()).unwrap() == game_id
        }
    }
}
<commit_msg>simplify game id parsing<commit_after>use std::convert::TryFrom;
use std::fmt::{self, Write};
use std::str::FromStr;

#[derive(Debug)]
struct InvalidGameId;

#[derive(Debug, Clone, Eq, PartialEq)]
struct GameId(u64);

impl TryFrom<u64> for GameId {
    type Error = InvalidGameId;

    fn try_from(n: u64) -> Result<Self, Self::Error> {
        if n < 62u64.pow(8) {
            Ok(GameId(n))
        } else {
            Err(InvalidGameId)
        }
    }
}

impl FromStr for GameId {
    type Err = InvalidGameId;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if dbg!(s).len() != 8 {
            return Err(InvalidGameId);
        }

        let mut n = 0;
        for c in s.bytes().rev() {
            n = match c {
                b'0'..=b'9' => c - b'0',
                b'A'..=b'Z' => c - b'A' + 10,
                b'a'..=b'z' => c - b'a' + 10 + 26,
                _ => return Err(InvalidGameId),
            } as u64 + n * 62;
        }

        Ok(GameId(n))
    }
}

impl fmt::Display for GameId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut n = self.0;
        for _ in 0..8 {
            let rem = n % 62;
            f.write_char(char::from(if rem >= 10 + 26 {
                (rem - (10 + 26)) as u8 + b'a'
            } else if rem >= 10 {
                (rem - 10) as u8 + b'A'
            } else {
                rem as u8 + b'0'
            }))?;
            n /= 62;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::{Arbitrary, Gen, quickcheck};
    use super::*;

    impl Arbitrary for GameId {
        fn arbitrary(g: &mut Gen) -> Self {
            GameId(u64::arbitrary(g) % 62u64.pow(8))
        }
    }

    quickcheck! {
        fn game_id_roundtrip(game_id: GameId) -> bool {
            GameId::from_str(&game_id.to_string()).unwrap() == game_id
        }
    }
}
<|endoftext|>


<commit_before>//! Functions for adding synthetic noise to images.

use image::{GenericImage, ImageBuffer, Pixel};
use rand::{SeedableRng, StdRng};
use rand::distributions::{IndependentSample, Normal, Range};
use crate::definitions::{Clamp, HasBlack, HasWhite, Image};
use conv::ValueInto;
use crate::math::cast;

/// Adds independent additive Gaussian noise to all channels
/// of an image, with the given mean and standard deviation.
pub fn gaussian_noise
(image: &Image
, mean: f64, stddev: f64, seed: usize) -> Image

where
    P: Pixel + 'static,
    P::Subpixel: ValueInto<f64> + Clamp<f64>,
{
    let mut out = ImageBuffer::new(image.width(), image.height());
    out.copy_from(image, 0, 0);
    gaussian_noise_mut(&mut out, mean, stddev, seed);
    out
}

/// Adds independent additive Gaussian noise to all channels
/// of an image in place, with the given mean and standard deviation.
pub fn gaussian_noise_mut
(image: &mut Image
, mean: f64, stddev: f64, seed: usize)
where
    P: Pixel + 'static,
    P::Subpixel: ValueInto<f64> + Clamp<f64>,
{
    let seed_array: &[_] = &[seed];
    let mut rng: StdRng = SeedableRng::from_seed(seed_array);
    let normal = Normal::new(mean, stddev);
    let num_channels = P::channel_count() as usize;

    for p in image.pixels_mut() {
        for c in 0..num_channels {
            let noise = normal.ind_sample(&mut rng);
            let channel: f64 = cast(p.channels()[c]);
            p.channels_mut()[c] = P::Subpixel::clamp(channel + noise);
        }
    }
}

/// Converts pixels to black or white at the given `rate` (between 0.0 and 1.0).
/// Black and white occur with equal probability.
pub fn salt_and_pepper_noise
(image: &Image
, rate: f64, seed: usize) -> Image

where
    P: Pixel + HasBlack + HasWhite + 'static,
{
    let mut out = ImageBuffer::new(image.width(), image.height());
    out.copy_from(image, 0, 0);
    salt_and_pepper_noise_mut(&mut out, rate, seed);
    out
}

/// Converts pixels to black or white in place at the given `rate` (between 0.0 and 1.0).
/// Black and white occur with equal probability.
pub fn salt_and_pepper_noise_mut
(image: &mut Image
, rate: f64, seed: usize)
where
    P: Pixel + HasBlack + HasWhite + 'static,
{
    let seed_array: &[_] = &[seed];
    let mut rng: StdRng = SeedableRng::from_seed(seed_array);
    let uniform = Range::new(0.0, 1.0);

    for p in image.pixels_mut() {
        if uniform.ind_sample(&mut rng) > rate {
            continue;
        }
        let r = uniform.ind_sample(&mut rng);
        *p = if r >= 0.5 { P::white() } else { P::black() };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use image::GrayImage;
    use test::{Bencher, black_box};

    #[bench]
    fn bench_gaussian_noise_mut(b: &mut Bencher) {
        let mut image = GrayImage::new(100, 100);
        b.iter(|| { gaussian_noise_mut(&mut image, 30.0, 40.0, 1usize); });
        black_box(image);
    }

    #[bench]
    fn bench_salt_and_pepper_noise_mut(b: &mut Bencher) {
        let mut image = GrayImage::new(100, 100);
        b.iter(|| { salt_and_pepper_noise_mut(&mut image, 0.3, 1usize); });
        black_box(image);
    }
}
<commit_msg>Small tidy of noise functions (#306)<commit_after>//! Functions for adding synthetic noise to images.

use image::Pixel;
use rand::{SeedableRng, StdRng};
use rand::distributions::{IndependentSample, Normal, Range};
use crate::definitions::{Clamp, HasBlack, HasWhite, Image};
use conv::ValueInto;
use crate::math::cast;

/// Adds independent additive Gaussian noise to all channels
/// of an image, with the given mean and standard deviation.
pub fn gaussian_noise
(image: &Image
, mean: f64, stddev: f64, seed: usize) -> Image

where
    P: Pixel + 'static,
    P::Subpixel: ValueInto<f64> + Clamp<f64>,
{
    let mut out = image.clone();
    gaussian_noise_mut(&mut out, mean, stddev, seed);
    out
}

/// Adds independent additive Gaussian noise to all channels
/// of an image in place, with the given mean and standard deviation.
pub fn gaussian_noise_mut
(image: &mut Image
, mean: f64, stddev: f64, seed: usize)
where
    P: Pixel + 'static,
    P::Subpixel: ValueInto<f64> + Clamp<f64>,
{
    let seed_array: &[_] = &[seed];
    let mut rng: StdRng = SeedableRng::from_seed(seed_array);
    let normal = Normal::new(mean, stddev);

    for p in image.pixels_mut() {
        for c in p.channels_mut() {
            let noise = normal.ind_sample(&mut rng);
            *c = P::Subpixel::clamp(cast(*c) + noise);
        }
    }
}

/// Converts pixels to black or white at the given `rate` (between 0.0 and 1.0).
/// Black and white occur with equal probability.
pub fn salt_and_pepper_noise
(image: &Image
, rate: f64, seed: usize) -> Image

where
    P: Pixel + HasBlack + HasWhite + 'static,
{
    let mut out = image.clone();
    salt_and_pepper_noise_mut(&mut out, rate, seed);
    out
}

/// Converts pixels to black or white in place at the given `rate` (between 0.0 and 1.0).
/// Black and white occur with equal probability.
pub fn salt_and_pepper_noise_mut
(image: &mut Image
, rate: f64, seed: usize)
where
    P: Pixel + HasBlack + HasWhite + 'static,
{
    let seed_array: &[_] = &[seed];
    let mut rng: StdRng = SeedableRng::from_seed(seed_array);
    let uniform = Range::new(0.0, 1.0);

    for p in image.pixels_mut() {
        if uniform.ind_sample(&mut rng) > rate {
            continue;
        }
        let r = uniform.ind_sample(&mut rng);
        *p = if r >= 0.5 { P::white() } else { P::black() };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use image::GrayImage;
    use test::{Bencher, black_box};

    #[bench]
    fn bench_gaussian_noise_mut(b: &mut Bencher) {
        let mut image = GrayImage::new(100, 100);
        b.iter(|| { gaussian_noise_mut(&mut image, 30.0, 40.0, 1usize); });
        black_box(image);
    }

    #[bench]
    fn bench_salt_and_pepper_noise_mut(b: &mut Bencher) {
        let mut image = GrayImage::new(100, 100);
        b.iter(|| { salt_and_pepper_noise_mut(&mut image, 0.3, 1usize); });
        black_box(image);
    }
}
<|endoftext|>