<commit_before>#[feature(macro_rules)];

use std::result::{Result,Ok,Err};

#[deriving(Eq,Show)]
enum SmtpCommand<'a> {
  MAIL(&'a str),
  RCPT(&'a str),
  DATA,
}

#[deriving(Eq,Show)]
enum ParseError {
  SyntaxError(&'static str),
  InvalidLineEnding,
  UnknownCommand,
}

fn ascii_eq_ignore_case(a: &[u8], b: &[u8]) -> bool {
  if a.len() == b.len() {
    a.to_ascii().eq_ignore_case(b.to_ascii())
  }
  else {
    false
  }
}

struct SliceScanner<'a, T> {
  data: &'a[T]
}

impl<'a, T:Sized+Send> SliceScanner<'a, T> {

  fn new(data: &'a[T]) -> SliceScanner<'a, T> {
      SliceScanner { data: data }
  }

  fn len(&self) -> uint { self.data.len() }
  fn is_empty(&self) -> bool { self.len() == 0 }
  fn data(&self) -> &'a[T] { self.data }

  /// Remove `n` (but no more than len()) items from the back and return them.
  fn pop_back(&mut self, n: uint) -> &'a[T] {
    if n > self.len() { debug!("pop_back(): n > len"); }
    let n = std::cmp::min(n, self.len());
    let (front, back) = self.split_at(self.len() - n);
    self.data = front;
    return back;
  }

  /// Remove `n` (but no more than len()) items from the front and return them.
  fn pop_front(&mut self, n: uint) -> &'a[T] {
    if n > self.len() { debug!("pop_front(): n > len"); }
    let n = std::cmp::min(n, self.len());

    let (front, back) = self.split_at(n);
    self.data = back;
    return front;
  }

  fn count_while(&self, cond: |&T| -> bool) -> uint {
    let mut cnt = 0;
    for b in self.data.iter() {
      if cond(b) {
        cnt += 1;
      } else {
        break;
      }
    }
    return cnt
  }

  fn pop_while(&mut self, cond: |&T| -> bool) -> &'a[T] {
    let cnt = self.count_while(cond);
    self.pop_front(cnt)
  }

  fn split_at(&self, pos: uint) -> (&'a[T], &'a[T]) {
    assert!(pos <= self.data.len());
    (self.data.slice(0, pos), self.data.slice(pos, self.data.len()))
  }
}

fn parse_command<'a>(line: &'a[u8]) -> Result<SmtpCommand<'a>, ParseError>  {
    let mut line = SliceScanner::new(line);

    let crlf = line.pop_back(2);
    if crlf != bytes!("\r\n") {
      return Err(InvalidLineEnding);
    }

    let cmd = line.pop_front(4);
    if ascii_eq_ignore_case(cmd, bytes!("MAIL")) {
        if line.pop_while(|&b| b == (' ' as u8) ).len() == 0 {
            return Err(SyntaxError("Invalid MAIL command: Missing SP"));
        }

        if ascii_eq_ignore_case(line.pop_front(5), bytes!("FROM:")) {
            let addr = line.pop_while(|&b| b != (' ' as u8));
            if line.is_empty() {
                // XXX: Verify mail addr
                Ok(MAIL(std::str::from_utf8(addr).unwrap()))
            }
            else {
                Err(SyntaxError("Invalid MAIL command"))
            }
        }
        else {
            Err(SyntaxError("Invalid MAIL command"))
        }
    }
    else if ascii_eq_ignore_case(cmd, bytes!("DATA")) {
      if line.is_empty() {
          Ok(DATA)
      }
      else {
          Err(SyntaxError("Invalid DATA command"))
      }
    }
    else {
      Err(UnknownCommand)
    }
}

macro_rules! assert_match(
    ($given:expr , $pattern:pat) => ({
        let given_val = $given;
        match given_val {
          $pattern => {}
          _ => {
             fail!("assertion failed: `value does not match pattern`");
          }
        }
    })
)

macro_rules! test_parse_command (
  ($str:expr, $pat:pat) => (
    match bytes!($str) {
      cmd => assert_match!(parse_command(cmd), $pat)
    }
  )
)

#[test]
fn test_commands() {
  //test_parse_command!("", Err(InvalidLineEnding));
  test_parse_command!("\r", Err(InvalidLineEnding));
  test_parse_command!("\n", Err(InvalidLineEnding));
  test_parse_command!("\n\r", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\r", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\n", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\n\r", Err(InvalidLineEnding));

  test_parse_command!("MAIL FROM:<mneumann@ntecs.de blah\r\n", Err(SyntaxError("Invalid MAIL command")));

  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\r\n", Ok(MAIL("<mneumann@ntecs.de>")));

  test_parse_command!("DATA\r\n", Ok(DATA));
  test_parse_command!("data\r\n", Ok(DATA));
  test_parse_command!("data test\r\n", Err(SyntaxError("Invalid DATA command")));
}

fn main() {
  let buf = bytes!("MAIL FROM:<mneumann@ntecs.de>\r\n");
  let cmd = parse_command(buf);
  println!("{}", cmd);
}
<commit_msg>Add alternate MAIL FROM: parsing without <><commit_after>#[feature(macro_rules)];

use std::result::{Result,Ok,Err};

#[deriving(Eq,Show)]
enum SmtpCommand<'a> {
  MAIL(&'a str),
  RCPT(&'a str),
  DATA,
}

#[deriving(Eq,Show)]
enum ParseError {
  SyntaxError(&'static str),
  InvalidLineEnding,
  UnknownCommand,
}

fn ascii_eq_ignore_case(a: &[u8], b: &[u8]) -> bool {
  if a.len() == b.len() {
    a.to_ascii().eq_ignore_case(b.to_ascii())
  }
  else {
    false
  }
}

struct SliceScanner<'a, T> {
  data: &'a[T]
}

impl<'a, T:Sized+Send> SliceScanner<'a, T> {

  fn new(data: &'a[T]) -> SliceScanner<'a, T> {
      SliceScanner { data: data }
  }

  fn len(&self) -> uint { self.data.len() }
  fn is_empty(&self) -> bool { self.len() == 0 }
  fn data(&self) -> &'a[T] { self.data }

  /// Remove `n` (but no more than len()) items from the back and return them.
  fn pop_back(&mut self, n: uint) -> &'a[T] {
    if n > self.len() { debug!("pop_back(): n > len"); }
    let n = std::cmp::min(n, self.len());
    let (front, back) = self.split_at(self.len() - n);
    self.data = front;
    return back;
  }

  /// Remove `n` (but no more than len()) items from the front and return them.
  fn pop_front(&mut self, n: uint) -> &'a[T] {
    if n > self.len() { debug!("pop_front(): n > len"); }
    let n = std::cmp::min(n, self.len());

    let (front, back) = self.split_at(n);
    self.data = back;
    return front;
  }

  /// Same as pop_front, but does not modify the underlying SliceScanner.
  fn ref_front(&self, n: uint) -> &'a[T] {
    if n > self.len() { debug!("ref_front(): n > len"); }
    let n = std::cmp::min(n, self.len());

    let (front, _) = self.split_at(n);
    return front;
  }

  fn count_while(&self, cond: |&T| -> bool) -> uint {
    let mut cnt = 0;
    for b in self.data.iter() {
      if cond(b) {
        cnt += 1;
      } else {
        break;
      }
    }
    return cnt
  }

  fn pop_while(&mut self, cond: |&T| -> bool) -> &'a[T] {
    let cnt = self.count_while(cond);
    self.pop_front(cnt)
  }

  fn split_at(&self, pos: uint) -> (&'a[T], &'a[T]) {
    assert!(pos <= self.data.len());
    (self.data.slice(0, pos), self.data.slice(pos, self.data.len()))
  }
}

fn parse_command<'a>(line: &'a[u8]) -> Result<SmtpCommand<'a>, ParseError>  {
    let mut line = SliceScanner::new(line);

    let crlf = line.pop_back(2);
    if crlf != bytes!("\r\n") {
      return Err(InvalidLineEnding);
    }

    let cmd = line.pop_front(4);
    if ascii_eq_ignore_case(cmd, bytes!("MAIL")) {
        if line.pop_while(|&b| b == (' ' as u8) ).len() == 0 {
            return Err(SyntaxError("Invalid MAIL command: Missing SP"));
        }

        if ascii_eq_ignore_case(line.pop_front(5), bytes!("FROM:")) {
            let addr =
            if line.ref_front(1) == bytes!("<") {
              let _ = line.pop_front(1);
              let addr = line.pop_while(|&b| b != ('>' as u8));
              if line.pop_front(1) != bytes!(">") {
                return Err(SyntaxError("Invalid MAIL command: Missing >"));
              }
              addr
            }
            else {
              line.pop_while(|&b| b != (' ' as u8))
            };

            if line.is_empty() {
                // XXX: Verify mail addr
                Ok(MAIL(std::str::from_utf8(addr).unwrap()))
            }
            else {
                Err(SyntaxError("Invalid MAIL command"))
            }
        }
        else {
            Err(SyntaxError("Invalid MAIL command"))
        }
    }
    else if ascii_eq_ignore_case(cmd, bytes!("DATA")) {
      if line.is_empty() {
          Ok(DATA)
      }
      else {
          Err(SyntaxError("Invalid DATA command"))
      }
    }
    else {
      Err(UnknownCommand)
    }
}

macro_rules! assert_match(
    ($given:expr , $pattern:pat) => ({
        let given_val = $given;
        match given_val {
          $pattern => {}
          _ => {
             fail!("assertion failed: `value does not match pattern`");
          }
        }
    })
)

macro_rules! test_parse_command (
  ($str:expr, $pat:pat) => (
    match bytes!($str) {
      cmd => assert_match!(parse_command(cmd), $pat)
    }
  )
)

#[test]
fn test_commands() {
  //test_parse_command!("", Err(InvalidLineEnding));
  test_parse_command!("\r", Err(InvalidLineEnding));
  test_parse_command!("\n", Err(InvalidLineEnding));
  test_parse_command!("\n\r", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\r", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\n", Err(InvalidLineEnding));
  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\n\r", Err(InvalidLineEnding));

  test_parse_command!("MAIL FROM:<mneumann@ntecs.de blah\r\n", Err(SyntaxError("Invalid MAIL command: Missing >")));

  test_parse_command!("MAIL FROM:<mneumann@ntecs.de>\r\n", Ok(MAIL("mneumann@ntecs.de")));
  test_parse_command!("MAIL FROM:mneumann@ntecs.de\r\n", Ok(MAIL("mneumann@ntecs.de")));

  test_parse_command!("DATA\r\n", Ok(DATA));
  test_parse_command!("data\r\n", Ok(DATA));
  test_parse_command!("data test\r\n", Err(SyntaxError("Invalid DATA command")));
}

fn main() {
  let buf = bytes!("MAIL FROM:<mneumann@ntecs.de>\r\n");
  let cmd = parse_command(buf);
  println!("{}", cmd);
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rusp Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::*;

use std::str;
use std::int;
use std::float;

///
/// A position in a source string
///
pub struct Position {
    line: uint,
    col: uint,
}

pub struct ParseFailure {
    description: ~str,
    position: Position,
}

impl ParseFailure {
    fn err<T>(descr: ~str, pos: Position) -> Result<T, ParseFailure> {
        Err(ParseFailure { description: descr, position: pos })
    }
    fn eof<T>(pos: Position) -> Result<T, ParseFailure> {
        ParseFailure::err(~"Unexpected EOF", pos)
    }
}

static EOF : char = 0 as char;

pub struct Parser<'self> {
    priv src_pos: uint,
    priv pos: Position,
    priv src: &'self str,
    priv src_len: uint,
}

pub type ParseResult = Result<@Value, ParseFailure>;

impl<'self> Parser<'self> {
    pub fn new<'r>(src: &'r str) -> Parser<'r> {
        Parser {
            src_pos: 0,
            pos: Position { line: 1, col: 1 },
            src: src,
            src_len: src.len()
        }
    }

    pub fn parse(&mut self) -> ParseResult {
        self.skip_whitespace();
        if self.eat('(') {
            self.parse_list()
        } else {
            match self.peek() {
                '.' if self.eat_str("..") => Ok(@Symbol(~"..")),
                '0'..'9' | '.' => self.parse_number(),
                't' if self.eat_str("true") => Ok(@Bool(true)),
                'f' if self.eat_str("false") => Ok(@Bool(false)),
                '"' => self.parse_string(),
                ')' => self.fail(~"Unexpected ')'"),
                _ => self.parse_ident()
            }
        }
    }

    pub fn eof(&self) -> bool {
        self.src_pos >= self.src_len
    }

    priv fn parse_list(&mut self) -> ParseResult {
        let mut vals = ~[];
        loop {
            if self.eof() {
                return self.fail(~"Unexpected EOF, expected ')'");
            }

            if self.eat(')') {
                return Ok(@List(vals));
            }

            match self.parse() {
                Ok(val) => vals.push(val),
                err => return err
            }

            self.skip_whitespace();
        }
    }

    priv fn parse_number(&mut self) -> ParseResult {
        let mut len = 0;
        let mut seen_point = self.peek() == '.';

        loop {
            match self.src.char_at(self.src_pos+len) {
                '.' if seen_point => return self.fail(~"Unexpected character '.' while parsing number"),
                '.' if !seen_point => {
                    len += 1; // I know that '.' is a single byte
                    seen_point = true;
                }
                '0'..'9' => len += 1, // Same here
                ')' => break,
                c if c.is_whitespace() => break,
                c => {
                    return self.fail(fmt!("Unexpected character '%c' while parsing number", c));
                }
            }
            if (self.src_pos+len) >= self.src_len {
                break
            }
        }

        let slice = self.slice_src(len);
        self.advance(slice.char_len());
        if seen_point {
            let n = float::from_str(slice).unwrap();
            Ok(@Float(n))
        } else {
            let n = int::from_str(slice).unwrap();
            Ok(@Int(n))
        }
    }

    priv fn parse_string(&mut self) -> ParseResult {
        if self.eat('"') {
            let mut s = ~"";

            loop {
                match self.pop() {
                    '"' => {
                        return Ok(@Str(s));
                    }
                    '\\' => {
                        match self.peek() {
                            // Minimal list of escape characters, most common ones
                            '0' => s.push_char(0x00 as char),
                            'a' => s.push_char(0x07 as char),
                            'b' => s.push_char(0x08 as char),
                            't' => s.push_char(0x09 as char),
                            'n' => s.push_char(0x0A as char),
                            'v' => s.push_char(0x0B as char),
                            'f' => s.push_char(0x0C as char),
                            'r' => s.push_char(0x0D as char),
                            '"' => s.push_char(0x22 as char),
                            '\''=> s.push_char(0x27 as char),
                            '\\'=> s.push_char(0x5C as char),

                            EOF => return self.fail(~"Unexpected EOF while parsing escape"),
                            _   => loop,
                        }
                        self.advance(1);
                    }
                    EOF => {
                        return self.fail(~"Unexpected EOF while parsing string");
                    }
                    a => {
                        s.push_char(a);
                    }
                }
            }

        } else {
            fail!("Parser Error, parse_string called at the wrong time");
        }
    }

    priv fn parse_ident(&mut self) -> ParseResult {
        let c = self.peek();

        if self.char_is_read_macro(c) {
            return self.eval_read_macro(c);
        }

        let mut ident = ~"";

        loop {
            match self.peek() {
                EOF => return self.fail(~"Unexpected EOF while parsing ident"),
                c if self.char_is_read_macro(c) => {
                    return self.fail(fmt!("Unexpected character '%c' while parsing ident", c));
                }
                ')' => return Ok(@Symbol(ident)),
                c if c.is_whitespace() => return Ok(@Symbol(ident)),
                c => ident.push_char(c),
            }
            self.advance(1);
        }
    }

    priv fn skip_whitespace(&mut self) {
        while self.peek().is_whitespace() {
            self.advance(1)
        }
    }

    priv fn fail(&self, msg: ~str) -> ParseResult {
        ParseFailure::err(msg, self.pos)
    }

    priv fn slice_src(&self, len: uint) -> &'self str {
        self.src.slice(self.src_pos, self.src_pos+len)
    }

    priv fn pop(&mut self) -> char {
        let c = self.peek();
        self.advance(1);
        c
    }

    priv fn peek(&self) -> char {
        if self.eof() {
            EOF
        } else {
            self.src.char_at(self.src_pos)
        }
    }

    priv fn eat(&mut self, c: char) -> bool {
        if self.peek() == c {
            self.advance(1);
            true
        } else {
            false
        }
    }

    priv fn eat_str(&mut self, s: &str) -> bool {
        let slen = s.len();
        if (self.src_pos + slen) >= self.src_len {
            false
        } else {
            let slice = self.slice_src(slen);
            if slice == s {
                self.advance(s.char_len());
                true
            } else {
                false
            }
        }
    }

    priv fn advance(&mut self, n: uint) {
        if self.eof() { return; }
        let mut n = n;
        while n > 0 {
            let str::CharRange {ch:c, next:next} = str::char_range_at(self.src, self.src_pos);
            self.src_pos = next;
            n -= 1;
            self.pos.col += 1;
            if c == '\n' {
                self.pos.col = 1;
                self.pos.line += 1;
            }
        }
    }

    priv fn char_is_read_macro(&mut self, c: char) -> bool {
        match c {
            '\'' | '`' | '#' => true,
            _ => false
        }
    }

    priv fn eval_read_macro(&mut self, _: char) -> ParseResult {
        self.advance(1);
        self.parse()
    }
}

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

    #[test]
    fn test_parser_ok() {
        macro_rules! test(
            ($s:expr) => ({
                let src = $s.clone();
                assert_eq!(Parser::new(src).parse().get().to_str(), src)
            })
        )

        test!(~"1");
        test!(~"(def a (+ 1 2))");
        test!(~"(do (def a 1) (def b 2) (+ a b))");
        test!(~"(list (1 2) 3 (+ 4 5))");

        test!(~"(if true (fn (a b) (+ 1 a b)) (quote (1 2 3)))");

        test!(~"(foo bar \"a b c d\")");

        test!(~"(ö ä å)");

        test!(~"\"new-line:\\n\"");
    }

    #[test]
    fn test_parser_err() {
        macro_rules! test(
            ($s:expr) => ({
                let src = $s.clone();
                assert!(Parser::new(src).parse().is_err())
            })
        )

        test!(~"(");
        test!(~")");
        test!(~"\"no closing quote")
    }
}
<commit_msg>Add parser support for [] and {} literals. Currently an alias for ().<commit_after>// Copyright 2013 The Rusp Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::*;

use std::str;
use std::int;
use std::float;

///
/// A position in a source string
///
pub struct Position {
    line: uint,
    col: uint,
}

pub struct ParseFailure {
    description: ~str,
    position: Position,
}

impl ParseFailure {
    fn err<T>(descr: ~str, pos: Position) -> Result<T, ParseFailure> {
        Err(ParseFailure { description: descr, position: pos })
    }
    fn eof<T>(pos: Position) -> Result<T, ParseFailure> {
        ParseFailure::err(~"Unexpected EOF", pos)
    }
}

static EOF : char = 0 as char;

pub struct Parser<'self> {
    priv src_pos: uint,
    priv pos: Position,
    priv src: &'self str,
    priv src_len: uint,
}

pub type ParseResult = Result<@Value, ParseFailure>;

impl<'self> Parser<'self> {
    pub fn new<'r>(src: &'r str) -> Parser<'r> {
        Parser {
            src_pos: 0,
            pos: Position { line: 1, col: 1 },
            src: src,
            src_len: src.len()
        }
    }

    pub fn parse(&mut self) -> ParseResult {
        self.skip_whitespace();

        match self.one_of(['(', '[', '{']) {
            Some('(') => self.parse_list(')', |v| @List(v)),
            Some('[') => self.parse_list(']', |v| @List(v)), // TODO
            Some('{') => self.parse_list('}', |v| @List(v)), // TODO
            _ => {
                let c = self.peek();
                match c {
                    '.' if self.eat_str("..") => Ok(@Symbol(~"..")),
                    '0'..'9' | '.' => self.parse_number(),
                    't' if self.eat_str("true") => Ok(@Bool(true)),
                    'f' if self.eat_str("false") => Ok(@Bool(false)),
                    '"' => self.parse_string(),
                    ')' | ']' | '}' => self.fail(fmt!("Unexpected '%c'", c)),
                    _ => self.parse_ident()
                }
            }
        }
    }

    pub fn eof(&self) -> bool {
        self.src_pos >= self.src_len
    }

    priv fn parse_list(&mut self, closing: char, ctor: &fn(~[@Value]) -> @Value) -> ParseResult {
        let mut vals = ~[];
        loop {
            if self.eof() {
                return self.fail(fmt!("Unexpected EOF, expected '%c'", closing));
            }

            if self.eat(closing) {
                return Ok(ctor(vals));
            }

            match self.parse() {
                Ok(val) => vals.push(val),
                err => return err
            }

            self.skip_whitespace();
        }
    }

    priv fn parse_number(&mut self) -> ParseResult {
        let mut len = 0;
        let mut seen_point = self.peek() == '.';

        loop {
            match self.src.char_at(self.src_pos+len) {
                '.' => {
                    if seen_point {
                        return self.fail(~"Unexpected character '.' while parsing number");
                    } else {
                        len += 1; // I know that '.' is a single byte
                        seen_point = true;
                    }
                }
                '0'..'9' => len += 1, // Same here
                ')' | ']' | '}' => break,
                c if c.is_whitespace() => break,
                c => {
                    return self.fail(fmt!("Unexpected character '%c' while parsing number", c));
                }
            }
            if (self.src_pos+len) >= self.src_len {
                break
            }
        }

        let slice = self.slice_src(len);
        self.advance(slice.char_len());
        if seen_point {
            let n = float::from_str(slice).unwrap();
            Ok(@Float(n))
        } else {
            let n = int::from_str(slice).unwrap();
            Ok(@Int(n))
        }
    }

    priv fn parse_string(&mut self) -> ParseResult {
        if self.eat('"') {
            let mut s = ~"";

            loop {
                match self.pop() {
                    '"' => {
                        return Ok(@Str(s));
                    }
                    '\\' => {
                        match self.peek() {
                            // Minimal list of escape characters, most common ones
                            '0' => s.push_char(0x00 as char),
                            'a' => s.push_char(0x07 as char),
                            'b' => s.push_char(0x08 as char),
                            't' => s.push_char(0x09 as char),
                            'n' => s.push_char(0x0A as char),
                            'v' => s.push_char(0x0B as char),
                            'f' => s.push_char(0x0C as char),
                            'r' => s.push_char(0x0D as char),
                            '"' => s.push_char(0x22 as char),
                            '\''=> s.push_char(0x27 as char),
                            '\\'=> s.push_char(0x5C as char),

                            EOF => return self.fail(~"Unexpected EOF while parsing escape"),
                            _   => loop,
                        }
                        self.advance(1);
                    }
                    EOF => {
                        return self.fail(~"Unexpected EOF while parsing string");
                    }
                    a => {
                        s.push_char(a);
                    }
                }
            }

        } else {
            fail!("Parser Error, parse_string called at the wrong time");
        }
    }

    priv fn parse_ident(&mut self) -> ParseResult {
        let c = self.peek();

        if self.char_is_read_macro(c) {
            return self.eval_read_macro(c);
        }

        let mut ident = ~"";

        loop {
            match self.peek() {
                EOF => return self.fail(~"Unexpected EOF while parsing ident"),
                c if self.char_is_read_macro(c) => {
                    return self.fail(fmt!("Unexpected character '%c' while parsing ident", c));
                }
                ')' | ']' | '}' => return Ok(@Symbol(ident)),
                c if c.is_whitespace() => return Ok(@Symbol(ident)),
                c => ident.push_char(c),
            }
            self.advance(1);
        }
    }

    priv fn skip_whitespace(&mut self) {
        while self.peek().is_whitespace() {
            self.advance(1)
        }
    }

    priv fn fail(&self, msg: ~str) -> ParseResult {
        ParseFailure::err(msg, self.pos)
    }

    priv fn slice_src(&self, len: uint) -> &'self str {
        self.src.slice(self.src_pos, self.src_pos+len)
    }

    priv fn pop(&mut self) -> char {
        let c = self.peek();
        self.advance(1);
        c
    }

    priv fn peek(&self) -> char {
        if self.eof() {
            EOF
        } else {
            self.src.char_at(self.src_pos)
        }
    }

    priv fn one_of(&mut self, chars: &[char]) -> Option<char> {
        for chars.each |&c| {
            if self.eat(c) {
                return Some(c);
            }
        }
        None
    }

    priv fn eat(&mut self, c: char) -> bool {
        if self.peek() == c {
            self.advance(1);
            true
        } else {
            false
        }
    }

    priv fn eat_str(&mut self, s: &str) -> bool {
        let slen = s.len();
        if (self.src_pos + slen) >= self.src_len {
            false
        } else {
            let slice = self.slice_src(slen);
            if slice == s {
                self.advance(s.char_len());
                true
            } else {
                false
            }
        }
    }

    priv fn advance(&mut self, n: uint) {
        if self.eof() { return; }
        let mut n = n;
        while n > 0 {
            let str::CharRange {ch:c, next:next} = str::char_range_at(self.src, self.src_pos);
            self.src_pos = next;
            n -= 1;
            self.pos.col += 1;
            if c == '\n' {
                self.pos.col = 1;
                self.pos.line += 1;
            }
        }
    }

    priv fn char_is_read_macro(&mut self, c: char) -> bool {
        match c {
            '\'' | '`' | '#' => true,
            _ => false
        }
    }

    priv fn eval_read_macro(&mut self, _: char) -> ParseResult {
        self.advance(1);
        self.parse()
    }
}

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

    #[test]
    fn test_parser_ok() {
        macro_rules! test(
            ($s:expr) => ({
                let src = $s.clone();
                assert_eq!(Parser::new(src).parse().get().to_str(), src)
            })
        )

        test!(~"1");
        test!(~"(def a (+ 1 2))");
        test!(~"(do (def a 1) (def b 2) (+ a b))");
        test!(~"(list (1 2) 3 (+ 4 5))");

        test!(~"(if true (fn (a b) (+ 1 a b)) (quote (1 2 3)))");

        test!(~"(foo bar \"a b c d\")");

        test!(~"(ö ä å)");

        test!(~"\"new-line:\\n\"");
    }

    #[test]
    fn test_parser_err() {
        macro_rules! test(
            ($s:expr) => ({
                let src = $s.clone();
                assert!(Parser::new(src).parse().is_err())
            })
        )

        test!(~"(");
        test!(~")");
        test!(~"\"no closing quote")
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use num_complex::Complex;
use num_traits::Zero;
use strength_reduce::StrengthReducedUsize;

use common::{FFTnum, verify_length, verify_length_divisible};

use math_utils;
use twiddles;
use ::{Length, IsInverse, FFT};

/// Implementation of Rader's Algorithm
///
/// This algorithm computes a prime-sized FFT in O(nlogn) time. It does this by converting this size n FFT into a
/// size (n - 1) FFT, which is guaranteed to be composite.
///
/// The worst case for this algorithm is when (n - 1) is 2 * prime, resulting in a
/// [Cunningham Chain](https://en.wikipedia.org/wiki/Cunningham_chain)
///
/// ~~~
/// // Computes a forward FFT of size 1201 (prime number), using Rader's Algorithm
/// use rustfft::algorithm::RadersAlgorithm;
/// use rustfft::{FFT, FFTplanner};
/// use rustfft::num_complex::Complex;
/// use rustfft::num_traits::Zero;
///
/// let mut input:  Vec<Complex<f32>> = vec![Zero::zero(); 1201];
/// let mut output: Vec<Complex<f32>> = vec![Zero::zero(); 1201];
///
/// // plan a FFT of size n - 1 = 1200
/// let mut planner = FFTplanner::new(false);
/// let inner_fft = planner.plan_fft(1200);
///
/// let fft = RadersAlgorithm::new(1201, inner_fft);
/// fft.process(&mut input, &mut output);
/// ~~~
///
/// Rader's Algorithm is relatively expensive compared to other FFT algorithms. Benchmarking shows that it is up to
/// an order of magnitude slower than similar composite sizes. In the example size above of 1201, benchmarking shows
/// that it takes 2.5x more time to compute than a FFT of size 1200.

pub struct RadersAlgorithm<T> {
    inner_fft: Arc<FFT<T>>,
    inner_fft_data: Box<[Complex<T>]>,

    input_output_map: Box<[usize]>,
}

impl<T: FFTnum> RadersAlgorithm<T> {
    /// Creates a FFT instance which will process inputs/outputs of size `len`. `inner_fft.len()` must be `len - 1`
    ///
    /// Note that this constructor is quite expensive to run; This algorithm must run a FFT of size n - 1 within the
    /// constructor. This further underlines the fact that Rader's Algorithm is more expensive to run than other
    /// FFT algorithms
    ///
    /// Note also that if `len` is not prime, this algorithm may silently produce garbage output
    pub fn new(len: usize, inner_fft: Arc<FFT<T>>) -> Self {
        assert_eq!(len - 1, inner_fft.len(), "For raders algorithm, inner_fft.len() must be self.len() - 1. Expected {}, got {}", len - 1, inner_fft.len());

        let inner_fft_len = len - 1;
        let reduced_len = StrengthReducedUsize::new(len);

        // compute the primitive root and its inverse for this size
        let primitive_root = math_utils::primitive_root(len as u64).unwrap() as usize;
        let root_inverse = math_utils::multiplicative_inverse(primitive_root as usize, len);

        // precompute the coefficients to use inside the process method
        let unity_scale = T::from_f64(1f64 / inner_fft_len as f64).unwrap();
        let mut inner_fft_input = vec![Complex::zero(); inner_fft_len];
        let mut twiddle_input = 1;
        for input_cell in &mut inner_fft_input {
            let twiddle = twiddles::single_twiddle(twiddle_input, len, inner_fft.is_inverse());
            *input_cell = twiddle * unity_scale;

            twiddle_input = (twiddle_input * root_inverse) % reduced_len;
        }

        //precompute a FFT of our reordered twiddle factors
        let mut inner_fft_output = vec![Zero::zero(); inner_fft_len];
        inner_fft.process(&mut inner_fft_input, &mut inner_fft_output);

        //precompute the indexes we'll used to reorder the input and output
        let mut input_output_map = vec![0; inner_fft_len - 1];
        let mut map_index = 1usize;
        for map_cell in &mut input_output_map {
            map_index = (primitive_root * map_index) % reduced_len;
            *map_cell = map_index - 1;
        }

        RadersAlgorithm {
            inner_fft: inner_fft,
            inner_fft_data: inner_fft_output.into_boxed_slice(),

            input_output_map: input_output_map.into_boxed_slice(),
        }
    }

    fn perform_fft(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {

        // The first output element is just the sum of all the input elements
        let (first_output, output) = output.split_first_mut().unwrap();
        *first_output = input.iter().sum();

        // Also split off the first input elements. After this, both input and output len will be n - 1
        let (first_input, input) = input.split_first_mut().unwrap();

        {
            // prepare the inner FFT by reordering the input buffer into the output buffer
            // Split off the last target element, because we're going to treat it separately
            let (output_last, output) = output.split_last_mut().unwrap();

            // we could compute the indexes here on the fly, but benchmarking shows it's faster to precompute and store them
            for (input_index, output_element) in self.input_output_map.iter().zip(output.iter_mut()) {
                *output_element = input[*input_index];
            }

            // the first element always gets copied to the last element
            *output_last = input[0];
        }

        // perform the first of two inner FFTs
        self.inner_fft.process(output, input);

        // multiply the inner result with our cached setup data
        // also conjugate every entry. this sets us up to do an inverse FFT
        // (because an inverse FFT is equivalent to a normal FFT where you conjugate both the inputs and outputs)
        for ((&input_cell, output_cell), &multiple) in input.iter().zip(output.iter_mut()).zip(self.inner_fft_data.iter()) {
            *output_cell = (input_cell * multiple).conj();
        }

        // execute the second FFT
        self.inner_fft.process(output, input);

        // copy the input buffer to the output buffer, reordering the elements as we go.
        // Split off the last input element, because we're going to treat it separately
        let (input_last, input) = input.split_last_mut().unwrap();

        // we could compute the indexes here on the fly, but benchmarking shows it's faster to precompute and store them
        let first_input_val = *first_input;
        for (&output_index, input_element) in self.input_output_map.iter().rev().zip(input.iter()) {
            output[output_index] = input_element.conj() + first_input_val;
        }

        // the last element always gets copied to the first element
        output[0] = input_last.conj() + first_input_val;
    }
}

impl<T: FFTnum> FFT<T> for RadersAlgorithm<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length(input, output, self.len());

        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length_divisible(input, output, self.len());

        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
}
impl<T> Length for RadersAlgorithm<T> {
    #[inline(always)]
    fn len(&self) -> usize {
        self.inner_fft_data.len() + 1
    }
}
impl<T> IsInverse for RadersAlgorithm<T> {
    #[inline(always)]
    fn is_inverse(&self) -> bool {
        self.inner_fft.is_inverse()
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::sync::Arc;
    use test_utils::check_fft_algorithm;
    use algorithm::DFT;

    #[test]
    fn test_raders() {
        for &len in &[3,5,7,11,13] {
            test_raders_with_length(len, false);
            test_raders_with_length(len, true);
        }
    }

    fn test_raders_with_length(len: usize, inverse: bool) {
        let inner_fft = Arc::new(DFT::new(len - 1, inverse));
        let fft = RadersAlgorithm::new(len, inner_fft);

        check_fft_algorithm(&fft, len, inverse);
    }
}
<commit_msg>Reduced setup time and memory overhead of raders algorithm<commit_after>use std::sync::Arc;

use num_complex::Complex;
use num_traits::Zero;
use strength_reduce::StrengthReducedUsize;

use common::{FFTnum, verify_length, verify_length_divisible};

use math_utils;
use twiddles;
use ::{Length, IsInverse, FFT};

/// Implementation of Rader's Algorithm
///
/// This algorithm computes a prime-sized FFT in O(nlogn) time. It does this by converting this size n FFT into a
/// size (n - 1) FFT, which is guaranteed to be composite.
///
/// The worst case for this algorithm is when (n - 1) is 2 * prime, resulting in a
/// [Cunningham Chain](https://en.wikipedia.org/wiki/Cunningham_chain)
///
/// ~~~
/// // Computes a forward FFT of size 1201 (prime number), using Rader's Algorithm
/// use rustfft::algorithm::RadersAlgorithm;
/// use rustfft::{FFT, FFTplanner};
/// use rustfft::num_complex::Complex;
/// use rustfft::num_traits::Zero;
///
/// let mut input:  Vec<Complex<f32>> = vec![Zero::zero(); 1201];
/// let mut output: Vec<Complex<f32>> = vec![Zero::zero(); 1201];
///
/// // plan a FFT of size n - 1 = 1200
/// let mut planner = FFTplanner::new(false);
/// let inner_fft = planner.plan_fft(1200);
///
/// let fft = RadersAlgorithm::new(1201, inner_fft);
/// fft.process(&mut input, &mut output);
/// ~~~
///
/// Rader's Algorithm is relatively expensive compared to other FFT algorithms. Benchmarking shows that it is up to
/// an order of magnitude slower than similar composite sizes. In the example size above of 1201, benchmarking shows
/// that it takes 2.5x more time to compute than a FFT of size 1200.

pub struct RadersAlgorithm<T> {
    inner_fft: Arc<FFT<T>>,
    inner_fft_data: Box<[Complex<T>]>,

    primitive_root: usize,
    primitive_root_inverse: usize,

    len: StrengthReducedUsize,
}

impl<T: FFTnum> RadersAlgorithm<T> {
    /// Creates a FFT instance which will process inputs/outputs of size `len`. `inner_fft.len()` must be `len - 1`
    ///
    /// Note that this constructor is quite expensive to run; This algorithm must run a FFT of size n - 1 within the
    /// constructor. This further underlines the fact that Rader's Algorithm is more expensive to run than other
    /// FFT algorithms
    ///
    /// Note also that if `len` is not prime, this algorithm may silently produce garbage output
    pub fn new(len: usize, inner_fft: Arc<FFT<T>>) -> Self {
        assert_eq!(len - 1, inner_fft.len(), "For raders algorithm, inner_fft.len() must be self.len() - 1. Expected {}, got {}", len - 1, inner_fft.len());

        let inner_fft_len = len - 1;
        let reduced_len = StrengthReducedUsize::new(len);

        // compute the primitive root and its inverse for this size
        let primitive_root = math_utils::primitive_root(len as u64).unwrap() as usize;
        let primitive_root_inverse = math_utils::multiplicative_inverse(primitive_root as usize, len);

        // precompute the coefficients to use inside the process method
        let unity_scale = T::from_f64(1f64 / inner_fft_len as f64).unwrap();
        let mut inner_fft_input = vec![Complex::zero(); inner_fft_len];
        let mut twiddle_input = 1;
        for input_cell in &mut inner_fft_input {
            let twiddle = twiddles::single_twiddle(twiddle_input, len, inner_fft.is_inverse());
            *input_cell = twiddle * unity_scale;

            twiddle_input = (twiddle_input * primitive_root_inverse) % reduced_len;
        }

        //precompute a FFT of our reordered twiddle factors
        let mut inner_fft_output = vec![Zero::zero(); inner_fft_len];
        inner_fft.process(&mut inner_fft_input, &mut inner_fft_output);

        Self {
            inner_fft: inner_fft,
            inner_fft_data: inner_fft_output.into_boxed_slice(),

            primitive_root,
            primitive_root_inverse,

            len: reduced_len,
        }
    }

    fn perform_fft(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {

        // The first output element is just the sum of all the input elements
        output[0] = input.iter().sum();
        let first_input_val = input[0];

        // we're now done with the first input and output
        let (_, output) = output.split_first_mut().unwrap();
        let (_, input) = input.split_first_mut().unwrap();

        // copy the input into the output, reordering as we go
        let mut input_index = 1;
        for output_element in output.iter_mut() {
            input_index = (input_index * self.primitive_root) % self.len;
            *output_element = input[input_index - 1];
        }

        // perform the first of two inner FFTs
        self.inner_fft.process(output, input);

        // multiply the inner result with our cached setup data
        // also conjugate every entry. this sets us up to do an inverse FFT
        // (because an inverse FFT is equivalent to a normal FFT where you conjugate both the inputs and outputs)
        for ((&input_cell, output_cell), &multiple) in input.iter().zip(output.iter_mut()).zip(self.inner_fft_data.iter()) {
            *output_cell = (input_cell * multiple).conj();
        }

        // execute the second FFT
        self.inner_fft.process(output, input);

        // copy the final values into the output, reordering as we go
        let mut output_index = 1;
        for input_element in input {
            output_index = (output_index * self.primitive_root_inverse) % self.len;
            output[output_index - 1] = input_element.conj() + first_input_val;
        }
    }
}

impl<T: FFTnum> FFT<T> for RadersAlgorithm<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length(input, output, self.len());

        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        verify_length_divisible(input, output, self.len());

        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
}
impl<T> Length for RadersAlgorithm<T> {
    #[inline(always)]
    fn len(&self) -> usize {
        self.len.get()
    }
}
impl<T> IsInverse for RadersAlgorithm<T> {
    #[inline(always)]
    fn is_inverse(&self) -> bool {
        self.inner_fft.is_inverse()
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::sync::Arc;
    use test_utils::check_fft_algorithm;
    use algorithm::DFT;

    #[test]
    fn test_raders() {
        for &len in &[3,5,7,11,13] {
            test_raders_with_length(len, false);
            test_raders_with_length(len, true);
        }
    }

    fn test_raders_with_length(len: usize, inverse: bool) {
        let inner_fft = Arc::new(DFT::new(len - 1, inverse));
        let fft = RadersAlgorithm::new(len, inner_fft);

        check_fft_algorithm(&fft, len, inverse);
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Logging pseudo-Node functionality.

use crate::RuntimeProxy;
use log::{error, info};
use oak_abi::OakStatus;
use oak_services::proto::oak::log::{Level, LogMessage};
use prost::Message;
use std::string::String;
use tokio::sync::oneshot;

/// Logging pseudo-Node.
pub struct LogNode {
    node_name: String,
}

impl LogNode {
    /// Creates a new [`LogNode`] instance, but does not start it.
    pub fn new(node_name: &str) -> Self {
        Self {
            node_name: node_name.to_string(),
        }
    }
}

impl super::Node for LogNode {
    /// Main execution loop for the logging pseudo-Node just waits for incoming
    /// `LogMessage`s and outputs them.
    fn run(
        self: Box<Self>,
        runtime: RuntimeProxy,
        handle: oak_abi::Handle,
        _notify_receiver: oneshot::Receiver<()>,
    ) {
        'wait: loop {
            let result = runtime.wait_on_channels(&[handle]);

            // Read all available log messages (even if the Runtime is terminating).
            'read: loop {
                match runtime.channel_read(handle) {
                    Ok(Some(message)) => match LogMessage::decode(&*message.bytes) {
                        Ok(msg) => {
                            // Log messages that arrive from Oak applications over a logging channel
                            // are controlled by IFC, and so need to be emitted independently of
                            // whether the Runtime has been built with the `oak_debug` feature
                            // enabled (and thus whether log! is connected up to anything or not).
                            // So send to stderr.
                            let now: chrono::DateTime<chrono::Utc> = chrono::Utc::now();
                            let timestamp = now.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
                            eprintln!(
                                "{{{} {} {}:{}}} {}",
                                timestamp,
                                level_to_string(msg.level),
                                msg.file,
                                msg.line,
                                msg.message
                            );
                        }
                        Err(error) => {
                            error!("{} Could not parse LogMessage: {}", self.node_name, error)
                        }
                    },
                    Ok(None) => {
                        // No more log messages to read, go back to waiting.
                        break 'read;
                    }
                    Err(OakStatus::ErrChannelClosed) => {
                        info!("{} channel closed by remote", self.node_name);
                        break 'wait;
                    }
                    Err(status) => {
                        error!("{} Failed channel read: {:?}", self.node_name, status);
                        break 'wait;
                    }
                }
            }

            if result == Err(OakStatus::ErrTerminated) {
                break 'wait;
            }
        }
        info!("{} logger execution complete", self.node_name);
        let _ = runtime.channel_close(handle);
    }
}

/// Translate a log level as encoded in a `LogMessage` into a string
fn level_to_string(level: i32) -> &'static str {
    // Pad all the strings to 5 characters to try to line things up.
    match Level::from_i32(level).unwrap_or(Level::UnknownLevel) {
        Level::UnknownLevel => " N/A ",
        Level::Error => "ERROR",
        Level::Warn => "WARN ",
        Level::Info => "INFO ",
        Level::Debugging => "DEBUG",
        Level::Trace => "TRACE",
    }
}
<commit_msg>runtime: move logger to use a Receiver<LogMessage><commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Logging pseudo-Node functionality.

use crate::{
    io::{Receiver, ReceiverExt},
    RuntimeProxy,
};
use log::{error, info, warn};
use oak_abi::OakStatus;
use oak_io::{handle::ReadHandle, OakError};
use oak_services::proto::oak::log::{Level, LogMessage};
use std::string::String;
use tokio::sync::oneshot;

/// Logging pseudo-Node.
pub struct LogNode {
    node_name: String,
}

impl LogNode {
    /// Creates a new [`LogNode`] instance, but does not start it.
    pub fn new(node_name: &str) -> Self {
        Self {
            node_name: node_name.to_string(),
        }
    }
}

impl super::Node for LogNode {
    /// Main execution loop for the logging pseudo-Node just waits for incoming
    /// `LogMessage`s and outputs them.
    fn run(
        self: Box<Self>,
        runtime: RuntimeProxy,
        handle: oak_abi::Handle,
        _notify_receiver: oneshot::Receiver<()>,
    ) {
        let receiver = Receiver::<LogMessage>::new(ReadHandle { handle });
        loop {
            match receiver.receive(&runtime) {
                Ok(msg) => {
                    // Log messages that arrive from Oak applications over a logging channel
                    // are controlled by IFC, and so need to be emitted independently of
                    // whether the Runtime has been built with the `oak_debug` feature
                    // enabled (and thus whether log! is connected up to anything or not).
                    // So send to stderr.
                    let now: chrono::DateTime<chrono::Utc> = chrono::Utc::now();
                    let timestamp = now.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
                    eprintln!(
                        "{{{} {} {}:{}}} {}",
                        timestamp,
                        level_to_string(msg.level),
                        msg.file,
                        msg.line,
                        msg.message
                    );
                }
                // Recoverable errors:
                Err(OakError::ProtobufDecodeError(err)) => {
                    warn!("{} failed to decode log message: {:?}", self.node_name, err);
                }
                // Errors that lead to Node termination:
                Err(OakError::OakStatus(OakStatus::ErrTerminated)) => break,
                Err(OakError::OakStatus(OakStatus::ErrChannelClosed)) => {
                    info!("{} channel closed", self.node_name);
                    break;
                }
                Err(err) => {
                    error!("{} failed channel receive: {:?}", self.node_name, err);
                    break;
                }
            }
        }
        info!("{} logger execution complete", self.node_name);
        let _ = runtime.channel_close(handle);
    }
}

/// Translate a log level as encoded in a `LogMessage` into a string
fn level_to_string(level: i32) -> &'static str {
    // Pad all the strings to 5 characters to try to line things up.
    match Level::from_i32(level).unwrap_or(Level::UnknownLevel) {
        Level::UnknownLevel => " N/A ",
        Level::Error => "ERROR",
        Level::Warn => "WARN ",
        Level::Info => "INFO ",
        Level::Debugging => "DEBUG",
        Level::Trace => "TRACE",
    }
}
<|endoftext|>


<commit_before>use crate::{
    backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture},
    utils::{Buffer, Logical, Physical, Point, Rectangle, Size},
    wayland::compositor::{
        is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState,
        SurfaceAttributes, TraversalAction,
    },
};
use std::{cell::RefCell, collections::HashSet};
use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};

#[derive(Default)]
pub(crate) struct SurfaceState {
    pub(crate) buffer_dimensions: Option<Size<i32, Physical>>,
    pub(crate) buffer_scale: i32,
    pub(crate) buffer: Option<WlBuffer>,
    pub(crate) texture: Option<Box<dyn std::any::Any + 'static>>,
    pub(crate) damage_seen: HashSet<(usize, *const ())>,
}

impl SurfaceState {
    pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
        match attrs.buffer.take() {
            Some(BufferAssignment::NewBuffer { buffer, .. }) => {
                // new contents
                self.buffer_dimensions = buffer_dimensions(&buffer);
                self.buffer_scale = attrs.buffer_scale;
                if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
                    if &old_buffer != self.buffer.as_ref().unwrap() {
                        old_buffer.release();
                    }
                }
                self.texture = None;
                self.damage_seen.clear();
            }
            Some(BufferAssignment::Removed) => {
                // remove the contents
                self.buffer_dimensions = None;
                if let Some(buffer) = self.buffer.take() {
                    buffer.release();
                };
                self.texture = None;
                self.damage_seen.clear();
            }
            None => {}
        }
    }
}

pub fn on_commit_buffer_handler(surface: &WlSurface) {
    if !is_sync_subsurface(surface) {
        with_surface_tree_upward(
            surface,
            (),
            |_, _, _| TraversalAction::DoChildren(()),
            |_surf, states, _| {
                states
                    .data_map
                    .insert_if_missing(|| RefCell::new(SurfaceState::default()));
                let mut data = states
                    .data_map
                    .get::<RefCell<SurfaceState>>()
                    .unwrap()
                    .borrow_mut();
                data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
            },
            |_, _, _| true,
        );
    }
}

/// TODO
pub fn draw_surface_tree<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    surface: &WlSurface,
    scale: f64,
    location: Point<i32, Logical>,
    damage: &[Rectangle<i32, Logical>],
    log: &slog::Logger,
) -> Result<(), R::Error>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error,
    T: Texture + 'static,
{
    let mut result = Ok(());
    let damage = damage
        .iter()
        .map(|geo| geo.to_f64().to_physical(scale).to_i32_round())
        .collect::<Vec<_>>();
    with_surface_tree_upward(
        surface,
        location,
        |_surface, states, location| {
            let mut location = *location;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data = data.borrow_mut();
                let attributes = states.cached_state.current::<SurfaceAttributes>();
                // Import a new buffer if necessary
                if data.texture.is_none() {
                    if let Some(buffer) = data.buffer.as_ref() {
                        let damage = attributes
                            .damage
                            .iter()
                            .map(|dmg| match dmg {
                                Damage::Buffer(rect) => *rect,
                                // TODO also apply transformations
                                Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale),
                            })
                            .collect::<Vec<_>>();

                        match renderer.import_buffer(buffer, Some(states), &damage) {
                            Some(Ok(m)) => {
                                data.texture = Some(Box::new(m));
                            }
                            Some(Err(err)) => {
                                slog::warn!(log, "Error loading buffer: {}", err);
                            }
                            None => {
                                slog::error!(log, "Unknown buffer format for: {:?}", buffer);
                            }
                        }
                    }
                }
                // Now, should we be drawn ?
                if data.texture.is_some() {
                    // if yes, also process the children
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        location += current.location;
                    }
                    TraversalAction::DoChildren(location)
                } else {
                    // we are not displayed, so our children are neither
                    TraversalAction::SkipChildren
                }
            } else {
                // we are not displayed, so our children are neither
                TraversalAction::SkipChildren
            }
        },
        |surface, states, location| {
            let mut location = *location;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data = data.borrow_mut();
                let buffer_scale = data.buffer_scale;
                let buffer_dimensions = data.buffer_dimensions;
                let attributes = states.cached_state.current::<SurfaceAttributes>();
                if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::<T>()) {
                    // we need to re-extract the subsurface offset, as the previous closure
                    // only passes it to our children
                    let mut surface_offset = (0, 0).into();
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        surface_offset = current.location;
                        location += current.location;
                    }

                    let rect = Rectangle::<i32, Physical>::from_loc_and_size(
                        surface_offset.to_f64().to_physical(scale).to_i32_round(),
                        buffer_dimensions.unwrap(),
                    );
                    let new_damage = damage
                        .iter()
                        .cloned()
                        .flat_map(|geo| geo.intersection(rect))
                        .map(|mut geo| {
                            geo.loc -= rect.loc;
                            geo
                        })
                        .collect::<Vec<_>>();

                    // TODO: Take wp_viewporter into account
                    if let Err(err) = frame.render_texture_at(
                        texture,
                        location.to_f64().to_physical(scale).to_i32_round(),
                        buffer_scale,
                        scale,
                        attributes.buffer_transform.into(),
                        &new_damage,
                        1.0,
                    ) {
                        result = Err(err);
                    }
                }
            }
        },
        |_, _, _| true,
    );

    result
}
<commit_msg>renderer: fixup damage calculations for smaller buffer sizes<commit_after>use crate::{
    backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture},
    utils::{Buffer, Logical, Physical, Point, Rectangle, Size},
    wayland::compositor::{
        is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState,
        SurfaceAttributes, TraversalAction,
    },
};
use std::{cell::RefCell, collections::HashSet};
use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};

#[derive(Default)]
pub(crate) struct SurfaceState {
    pub(crate) buffer_dimensions: Option<Size<i32, Physical>>,
    pub(crate) buffer_scale: i32,
    pub(crate) buffer: Option<WlBuffer>,
    pub(crate) texture: Option<Box<dyn std::any::Any + 'static>>,
    pub(crate) damage_seen: HashSet<(usize, *const ())>,
}

impl SurfaceState {
    pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
        match attrs.buffer.take() {
            Some(BufferAssignment::NewBuffer { buffer, .. }) => {
                // new contents
                self.buffer_dimensions = buffer_dimensions(&buffer);
                self.buffer_scale = attrs.buffer_scale;
                if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
                    if &old_buffer != self.buffer.as_ref().unwrap() {
                        old_buffer.release();
                    }
                }
                self.texture = None;
                self.damage_seen.clear();
            }
            Some(BufferAssignment::Removed) => {
                // remove the contents
                self.buffer_dimensions = None;
                if let Some(buffer) = self.buffer.take() {
                    buffer.release();
                };
                self.texture = None;
                self.damage_seen.clear();
            }
            None => {}
        }
    }
}

pub fn on_commit_buffer_handler(surface: &WlSurface) {
    if !is_sync_subsurface(surface) {
        with_surface_tree_upward(
            surface,
            (),
            |_, _, _| TraversalAction::DoChildren(()),
            |_surf, states, _| {
                states
                    .data_map
                    .insert_if_missing(|| RefCell::new(SurfaceState::default()));
                let mut data = states
                    .data_map
                    .get::<RefCell<SurfaceState>>()
                    .unwrap()
                    .borrow_mut();
                data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
            },
            |_, _, _| true,
        );
    }
}

/// TODO
pub fn draw_surface_tree<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    surface: &WlSurface,
    scale: f64,
    location: Point<i32, Logical>,
    damage: &[Rectangle<i32, Logical>],
    log: &slog::Logger,
) -> Result<(), R::Error>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error,
    T: Texture + 'static,
{
    let mut result = Ok(());
    let damage = damage
        .iter()
        .map(|geo| geo.to_f64().to_physical(scale).to_i32_up())
        .collect::<Vec<_>>();
    with_surface_tree_upward(
        surface,
        location,
        |_surface, states, location| {
            let mut location = *location;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data = data.borrow_mut();
                let attributes = states.cached_state.current::<SurfaceAttributes>();
                // Import a new buffer if necessary
                if data.texture.is_none() {
                    if let Some(buffer) = data.buffer.as_ref() {
                        let damage = attributes
                            .damage
                            .iter()
                            .map(|dmg| match dmg {
                                Damage::Buffer(rect) => *rect,
                                // TODO also apply transformations
                                Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale),
                            })
                            .collect::<Vec<_>>();

                        match renderer.import_buffer(buffer, Some(states), &damage) {
                            Some(Ok(m)) => {
                                data.texture = Some(Box::new(m));
                            }
                            Some(Err(err)) => {
                                slog::warn!(log, "Error loading buffer: {}", err);
                            }
                            None => {
                                slog::error!(log, "Unknown buffer format for: {:?}", buffer);
                            }
                        }
                    }
                }
                // Now, should we be drawn ?
                if data.texture.is_some() {
                    // if yes, also process the children
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        location += current.location;
                    }
                    TraversalAction::DoChildren(location)
                } else {
                    // we are not displayed, so our children are neither
                    TraversalAction::SkipChildren
                }
            } else {
                // we are not displayed, so our children are neither
                TraversalAction::SkipChildren
            }
        },
        |surface, states, location| {
            let mut location = *location;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data = data.borrow_mut();
                let buffer_scale = data.buffer_scale;
                let buffer_dimensions = data.buffer_dimensions;
                let attributes = states.cached_state.current::<SurfaceAttributes>();
                if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::<T>()) {
                    // we need to re-extract the subsurface offset, as the previous closure
                    // only passes it to our children
                    let mut surface_offset = (0, 0).into();
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        surface_offset = current.location;
                        location += current.location;
                    }

                    let rect = Rectangle::<i32, Physical>::from_loc_and_size(
                        surface_offset.to_f64().to_physical(scale).to_i32_round(),
                        buffer_dimensions
                            .unwrap_or_default()
                            .to_logical(buffer_scale)
                            .to_f64()
                            .to_physical(scale)
                            .to_i32_round(),
                    );
                    let new_damage = damage
                        .iter()
                        .cloned()
                        .flat_map(|geo| geo.intersection(rect))
                        .map(|mut geo| {
                            geo.loc -= rect.loc;
                            geo
                        })
                        .collect::<Vec<_>>();

                    // TODO: Take wp_viewporter into account
                    if let Err(err) = frame.render_texture_at(
                        texture,
                        location.to_f64().to_physical(scale).to_i32_round(),
                        buffer_scale,
                        scale,
                        attributes.buffer_transform.into(),
                        &new_damage,
                        1.0,
                    ) {
                        result = Err(err);
                    }
                }
            }
        },
        |_, _, _| true,
    );

    result
}
<|endoftext|>


<commit_before>extern crate byteorder;

use std::io::Read;
use byteorder::{LittleEndian as E, ReadBytesExt};

pub struct Splay {
    tree1: [i32; 512],
    tree1u: [u8; 512],
    tree2: [i32; 512],
    tree2u: [u8; 512],
}

impl Splay {
    pub fn new<I: ReadBytesExt>(input: &mut I) -> Splay {
        let mut splay = Splay {
            tree1: [0; 512],
            tree1u: [0; 512],
            tree2: [0; 512],
            tree2u: [0; 512],
        };
        for i in 0..512 {
            let v = input.read_i32::<E>().unwrap();
            splay.tree1[i] = v;
            splay.tree1u[i] = (!v as u8).wrapping_add(1);
        }
        for i in 0..512 {
            let v = input.read_i32::<E>().unwrap();
            splay.tree2[i] = v;
            splay.tree2u[i] = (!v as u8).wrapping_add(1);
        }
        splay
    }

    fn decompress<I: Read, F: Fn(u8, u8)->u8>(tree: &[i32], tree_char: &[u8],
                  input: &mut I, output: &mut [u8], fun: F) {
        let mut cur_size = 0;
        let mut last_char = 0u8;
        let mut c_index = 1usize;
        loop {
            let cur = input.read_u8().unwrap();
            for bit in (0..8).rev() {
                let i = (c_index << 1) + ((cur >> bit) as usize & 1);
                let code = tree[i];
                c_index = if code <= 0 {
                    last_char = fun(last_char, tree_char[i]);
                    output[cur_size] = last_char;
                    cur_size += 1;
                    if cur_size == output.len() {
                        return
                    }
                    1
                }else {
                    code as usize
                };
            }
        }
    }

    pub fn expand1<I: Read>(&self, input: &mut I, output: &mut [u8]) {
        Splay::decompress(&self.tree1, &self.tree1u, input, output, |b, c| b.wrapping_add(c));
    }
    pub fn expand2<I: Read>(&self, input: &mut I, output: &mut [u8]) {
        Splay::decompress(&self.tree2, &self.tree2u, input, output, |b, c| b^c);
    }
}
<commit_msg>New code path for Splay that is faster in Debug<commit_after>extern crate byteorder;

use std::io::Read;
use byteorder::{LittleEndian as E, ReadBytesExt};

pub struct Splay {
    tree1: [i32; 512],
    tree2: [i32; 512],
}

impl Splay {
    pub fn new<I: ReadBytesExt>(input: &mut I) -> Splay {
        let mut splay = Splay {
            tree1: [0; 512],
            tree2: [0; 512],
        };
        for i in 0..512 {
            let v = input.read_i32::<E>().unwrap();
            splay.tree1[i] = v;
        }
        for i in 0..512 {
            let v = input.read_i32::<E>().unwrap();
            splay.tree2[i] = v;
        }
        splay
    }

    fn decompress<I: Read, F: Fn(u8, u8)->u8>(tree: &[i32],
                  input: &mut I, output: &mut [u8], fun: F) {
        let mut last_char = 0u8;
        let mut bit = 0;
        let mut cur = 0u8;
        for out in output.iter_mut() {
            let mut code = 1i32;
            while code > 0 {
                bit = if bit == 0 {
                    cur = input.read_u8().unwrap();
                    7
                } else {
                    bit - 1
                };
                let i = ((code as usize) << 1) + ((cur >> bit) as usize & 1);
                code = tree[i];
            }
            last_char = fun(last_char, -code as u8);
            *out = last_char;
        }
    }

    #[allow(dead_code)]
    fn decompress_orig<I: Read, F: Fn(u8, u8)->u8>(tree: &[i32],
                       input: &mut I, output: &mut [u8], fun: F) {
        let mut last_char = 0u8;
        let mut c_index = 1usize;
        let mut cur_size = 0;
        loop {
            let cur = input.read_u8().unwrap();
            for bit in (0..8).rev() {
                let i = (c_index << 1) + ((cur >> bit) as usize & 1);
                let code = tree[i];
                c_index = if code <= 0 {
                    last_char = fun(last_char, -code as u8);
                    output[cur_size] = last_char;
                    cur_size += 1;
                    if cur_size == output.len() {
                        return
                    }
                    1
                } else {
                    code as usize
                };
            }
        }
    }

    pub fn expand1<I: Read>(&self, input: &mut I, output: &mut [u8]) {
        Splay::decompress(&self.tree1, input, output, |b, c| b.wrapping_add(c));
    }
    pub fn expand2<I: Read>(&self, input: &mut I, output: &mut [u8]) {
        Splay::decompress(&self.tree2, input, output, |b, c| b^c);
    }
}
<|endoftext|>


<commit_before>#[cfg(not(feature = "fitsio-src"))]
fn bind_cfitsio() {
    use pkg_config::Error;
    use std::io::Write;

    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {}
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write_all(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}

#[cfg(feature = "fitsio-src")]
fn bind_cfitsio() {
    use std::env::var;
    use std::path::PathBuf;

    let cfitsio_project_dir = PathBuf::from("ext/cfitsio");
    if !cfitsio_project_dir.exists() {
        panic!(
            "Expected to find cfitsio source directory {}",
            cfitsio_project_dir.display()
        );
    }
    // Make sure the source directory isn't empty.
    match std::fs::read_dir(&cfitsio_project_dir) {
        Ok(mut d) => {
            if let None = d.next() {
                panic!("cfitsio source directory ext/cfitsio is empty!");
            }
        }
        _ => panic!("Could not read from cfitsio source directory ext/cfitsio !"),
    }

    // Translate rustc optimisation levels to things a C compiler can
    // understand. I don't know if all C compilers agree here, but it should
    // at least work for gcc.
    let opt_level: String = match var("OPT_LEVEL").as_ref().map(|o| o.as_str()) {
        Err(_) => panic!("Something wrong with OPT_LEVEL"),
        // gcc doesn't handle 'z'. Just set it to 's', which also optimises
        // for size.
        Ok("z") => "s",
        Ok(o) => o,
    }
    .to_string();

    // Run the contigure script. I'd use the autotools crate here, but it
    // always outputs two of --{enable,disable}-{shared,static}, none of
    // which is supported by the cfitsio configure script! So, just run the
    // script manually.
    let dst = PathBuf::from(var("OUT_DIR").unwrap());

    std::process::Command::new("make")
        .arg("clean")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio make clean")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("sh")
        .args(&[
            "./configure",
            &format!("--prefix={}", dst.display()),
            // cfitsio should always be built with reentrant support.
            "--enable-reentrant",
            // curl functionality is not used through rust-fitsio.
            "--disable-curl",
        ])
        .env(
            "CFLAGS",
            &format!("-Wall -O{} -march=native -fPIE", opt_level),
        )
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio configure script")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("make")
        .arg("-j")
        .arg("install")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio makefile")
        .wait()
        .expect("Failed to wait on child");

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=cfitsio");
}

fn main() {
    bind_cfitsio();
}
<commit_msg>Improve fitsio-src feature on M1 macs<commit_after>#[cfg(not(feature = "fitsio-src"))]
fn bind_cfitsio() {
    use pkg_config::Error;
    use std::io::Write;

    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {}
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write_all(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}

#[cfg(feature = "fitsio-src")]
fn bind_cfitsio() {
    use std::env::var;
    use std::path::PathBuf;

    let cfitsio_project_dir = PathBuf::from("ext/cfitsio");
    if !cfitsio_project_dir.exists() {
        panic!(
            "Expected to find cfitsio source directory {}",
            cfitsio_project_dir.display()
        );
    }
    // Make sure the source directory isn't empty.
    match std::fs::read_dir(&cfitsio_project_dir) {
        Ok(mut d) => {
            if let None = d.next() {
                panic!("cfitsio source directory ext/cfitsio is empty!");
            }
        }
        _ => panic!("Could not read from cfitsio source directory ext/cfitsio !"),
    }

    // Translate rustc optimisation levels to things a C compiler can
    // understand. I don't know if all C compilers agree here, but it should
    // at least work for gcc.
    let opt_level: String = match var("OPT_LEVEL").as_ref().map(|o| o.as_str()) {
        Err(_) => panic!("Something wrong with OPT_LEVEL"),
        // gcc doesn't handle 'z'. Just set it to 's', which also optimises
        // for size.
        Ok("z") => "s",
        Ok(o) => o,
    }
    .to_string();

    // Run the contigure script. I'd use the autotools crate here, but it
    // always outputs two of --{enable,disable}-{shared,static}, none of
    // which is supported by the cfitsio configure script! So, just run the
    // script manually.
    let dst = PathBuf::from(var("OUT_DIR").unwrap());

    if cfitsio_project_dir.join("Makefile").is_file() {
        std::process::Command::new("make")
            .arg("clean")
            .current_dir(&cfitsio_project_dir)
            .spawn()
            .expect("Couldn't run cfitsio make clean")
            .wait()
            .expect("Failed to wait on child");
    }

    std::process::Command::new("sh")
        .args(&[
            "./configure",
            &format!("--prefix={}", dst.display()),
            // cfitsio should always be built with reentrant support.
            "--enable-reentrant",
            // curl functionality is not used through rust-fitsio.
            "--disable-curl",
        ])
        .env("CFLAGS", &format!("-Wall -O{} -fPIE", opt_level))
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio configure script")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("make")
        .arg("-j")
        .arg("install")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio makefile")
        .wait()
        .expect("Failed to wait on child");

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=cfitsio");
}

fn main() {
    bind_cfitsio();
}
<|endoftext|>


<commit_before>use std::io;
use std::error::Error;
use std::fmt;
use byteorder::{WriteBytesExt, BigEndian};

pub enum Command {
    Ping,
    AddEntry(AddEntry),
    RemoveEntry(RemoveEntry)
}

/// ts: i64 | len: u16 | data: (len)
pub struct AddEntry {
    timestamp: i64,
    data: Vec<u8>,
}

/// ts: i64 | id: u16
pub struct RemoveEntry {
    timestamp: i64,
    id: u16,
}

pub enum CommandResult {
    Pong,
    EntryAdded { id: u16 },
    EntryRemoved
}

#[derive(Debug)]
enum WriteError {
    DataLengthOverflow
}

impl Error for WriteError {
    fn description(&self) -> &str {
        match self {
            &WriteError::DataLengthOverflow => "Data overflows maximum length"
        }
    }
}

impl fmt::Display for WriteError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl Command {
    pub fn command_type(&self) -> u8 {
        match self {
            &Command::Ping => 0,
            &Command::AddEntry(_) => 1,
            &Command::RemoveEntry(_) => 2
        }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8(self.command_type())?;

        match self {
            &Command::Ping => Ok(()),
            &Command::RemoveEntry(ref cmd) => cmd.write_to(target),
            &Command::AddEntry(ref cmd) => cmd.write_to(target)
        }
    }
}

impl AddEntry {
    pub fn new(timestamp: i64, data: Vec<u8>) -> Self {
        AddEntry { timestamp, data }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        let len = self.data.len();

        if len > u16::max_value() as usize {
            // TODO: we need a better error here
            return Err(io::Error::new(io::ErrorKind::Other, WriteError::DataLengthOverflow))
        }

        target.write_i64::<BigEndian>(self.timestamp)?;
        target.write_u16::<BigEndian>(len as u16)?;

        for byte in &self.data {
            target.write_u8(*byte)?;
        }

        Ok(())
    }
}

impl RemoveEntry {
    pub fn new(timestamp: i64, id: u16) -> Self {
        RemoveEntry { timestamp, id }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_i64::<BigEndian>(self.timestamp)?;
        target.write_u16::<BigEndian>(self.id)?;

        Ok(())
    }
}

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

    #[test]
    fn test_ping() {
        let cmd = Command::Ping;
        let mut vec = Vec::new();

        cmd.write_to(&mut vec);

        /// cmd: u8
        assert_eq!(vec![0], vec);
    }

    #[test]
    fn test_add_entry() {
        let cmd = Command::AddEntry(AddEntry::new(10, vec![1, 2, 3]));
        let mut vec = Vec::<u8>::new();

        assert!(cmd.write_to(&mut vec).is_ok());

        assert_eq!(
            vec![
                /* cmd  */ 1,
                /* ts   */ 0, 0, 0, 0, 0, 0, 0, 10,
                /* len  */ 0, 3,
                /* data */ 1, 2, 3
            ],
            vec
        );
    }

    #[test]
    fn test_add_entry_checks_size() {
        let mut data = Vec::<u8>::new();

        for _ in 0..((u16::max_value() as u32) + 20) {
            data.push(0);
        }

        let cmd = Command::AddEntry(AddEntry::new(0, data));
        let mut target = Vec::<u8>::new();

        let result = cmd.write_to(&mut target);

        assert!(result.is_err());
        assert!(result.err().unwrap().description() == WriteError::DataLengthOverflow.description())
    }

    #[test]
    fn test_remove_entry() {
        let cmd = Command::RemoveEntry(RemoveEntry::new(12345, 23));
        let mut vec = Vec::<u8>::new();

        assert!(cmd.write_to(&mut vec).is_ok());

        assert_eq!(
            vec![
                /* cmd */ 2,
                /* ts  */ 0, 0, 0, 0, 0, 0, 48, 57,
                /* id  */ 0, 23,
            ],
            vec
        );
    }
}<commit_msg>Write whole vector<commit_after>use std::io;
use std::error::Error;
use std::fmt;
use byteorder::{WriteBytesExt, BigEndian};

pub enum Command {
    Ping,
    AddEntry(AddEntry),
    RemoveEntry(RemoveEntry)
}

/// ts: i64 | len: u16 | data: (len)
pub struct AddEntry {
    timestamp: i64,
    data: Vec<u8>,
}

/// ts: i64 | id: u16
pub struct RemoveEntry {
    timestamp: i64,
    id: u16,
}

pub enum CommandResult {
    Pong,
    EntryAdded { id: u16 },
    EntryRemoved
}

#[derive(Debug)]
enum WriteError {
    DataLengthOverflow
}

impl Error for WriteError {
    fn description(&self) -> &str {
        match self {
            &WriteError::DataLengthOverflow => "Data overflows maximum length"
        }
    }
}

impl fmt::Display for WriteError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl Command {
    pub fn command_type(&self) -> u8 {
        match self {
            &Command::Ping => 0,
            &Command::AddEntry(_) => 1,
            &Command::RemoveEntry(_) => 2
        }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8(self.command_type())?;

        match self {
            &Command::Ping => Ok(()),
            &Command::RemoveEntry(ref cmd) => cmd.write_to(target),
            &Command::AddEntry(ref cmd) => cmd.write_to(target)
        }
    }
}

impl AddEntry {
    pub fn new(timestamp: i64, data: Vec<u8>) -> Self {
        AddEntry { timestamp, data }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        let len = self.data.len();

        if len > u16::max_value() as usize {
            // TODO: we need a better error here
            return Err(io::Error::new(io::ErrorKind::Other, WriteError::DataLengthOverflow))
        }

        target.write_i64::<BigEndian>(self.timestamp)?;
        target.write_u16::<BigEndian>(len as u16)?;

        target.write(&self.data)?;

        Ok(())
    }
}

impl RemoveEntry {
    pub fn new(timestamp: i64, id: u16) -> Self {
        RemoveEntry { timestamp, id }
    }

    pub fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_i64::<BigEndian>(self.timestamp)?;
        target.write_u16::<BigEndian>(self.id)?;

        Ok(())
    }
}

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

    #[test]
    fn test_ping() {
        let cmd = Command::Ping;
        let mut vec = Vec::new();

        cmd.write_to(&mut vec);

        /// cmd: u8
        assert_eq!(vec![0], vec);
    }

    #[test]
    fn test_add_entry() {
        let cmd = Command::AddEntry(AddEntry::new(10, vec![1, 2, 3]));
        let mut vec = Vec::<u8>::new();

        assert!(cmd.write_to(&mut vec).is_ok());

        assert_eq!(
            vec![
                /* cmd  */ 1,
                /* ts   */ 0, 0, 0, 0, 0, 0, 0, 10,
                /* len  */ 0, 3,
                /* data */ 1, 2, 3
            ],
            vec
        );
    }

    #[test]
    fn test_add_entry_checks_size() {
        let mut data = Vec::<u8>::new();

        for _ in 0..((u16::max_value() as u32) + 20) {
            data.push(0);
        }

        let cmd = Command::AddEntry(AddEntry::new(0, data));
        let mut target = Vec::<u8>::new();

        let result = cmd.write_to(&mut target);

        assert!(result.is_err());
        assert!(result.err().unwrap().description() == WriteError::DataLengthOverflow.description())
    }

    #[test]
    fn test_remove_entry() {
        let cmd = Command::RemoveEntry(RemoveEntry::new(12345, 23));
        let mut vec = Vec::<u8>::new();

        assert!(cmd.write_to(&mut vec).is_ok());

        assert_eq!(
            vec![
                /* cmd */ 2,
                /* ts  */ 0, 0, 0, 0, 0, 0, 48, 57,
                /* id  */ 0, 23,
            ],
            vec
        );
    }
}<|endoftext|>


<commit_before>extern crate chrono;
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate regex;
#[macro_use]
extern crate safe_unwrap;
use std::io;

use chrono::NaiveDate;
use clap::{App, Arg, SubCommand};
use regex::Regex;

#[derive(Clone, Debug, Eq, PartialEq)]
struct CsvxMetadata {
    pub table_name: String,
    pub date: NaiveDate,
    pub schema: String,
    pub schema_version: usize,
    pub csvx_version: usize,
    pub is_schema: bool,
}

fn parse_filename<S: AsRef<str>>(filename: S) -> Option<CsvxMetadata> {
    lazy_static! {
        // `tablename_date_schema-schemaversion_csvxversion.csvx`
        static ref FN_RE: Regex = Regex::new(r"^([a-z][a-z0-9_].*)-\d.*$")
        .expect("built-in Regex is broken. Please file a bug");
    }
    println!("Input: {:?}", filename.as_ref());
    println!("Result: {:?}", FN_RE.find(filename.as_ref()));

    unimplemented!()
}

fn main() {
    let app = App::new("csvx")
        .version("2.0.0")
        .about("csvx version 2 utility")
        .subcommand(SubCommand::with_name("check")
                        .about("Check csvx files for conformance")
                        .arg(Arg::with_name("schema_file")
                                 .help("Schema file to check against")
                                 .required(true)
                                 .takes_value(true))
                        .arg(Arg::with_name("input_files")
                                 .help("Input files to check")
                                 .multiple(true)
                                 .takes_value(true)));
    let m = app.clone().get_matches();

    match m.subcommand {
        Some(ref cmd) if cmd.name == "check" => {
            let schema_file = safe_unwrap!("required argument",
                                           cmd.matches.value_of("schema_file"));
            // first, check filename of schema file
            parse_filename(schema_file);
            // println!("{:?}", m);
        }
        _ => app.write_help(&mut io::stdout()).unwrap(),
    }
}

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

    use chrono::NaiveDate;

    #[test]
    fn filename_parsing_rejects_invalid() {
        assert_eq!(parse_filename("asdf"), None);
        assert_eq!(parse_filename(""), None);
        assert_eq!(parse_filename("test.csv"), None);
        assert_eq!(parse_filename("test.csv"), None);
    }

    #[test]
    fn filename_parsing_parses_valid() {
        assert_eq!(parse_filename("zoo-nyc_20170401_animals-2_3.csv").unwrap(),
                   CsvxMetadata {
                       table_name: "zoo-nyc".to_owned(),
                       date: NaiveDate::from_ymd(2017, 04, 01),
                       schema: "animals".to_owned(),
                       schema_version: 2,
                       csvx_version: 3,
                       is_schema: false,
                   });
    }

}
<commit_msg>First working schema parse.<commit_after>extern crate chrono;
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate regex;
#[macro_use]
extern crate safe_unwrap;
use std::io;

use chrono::NaiveDate;
use clap::{App, Arg, SubCommand};
use regex::Regex;

#[derive(Clone, Debug, Eq, PartialEq)]
struct CsvxMetadata {
    pub table_name: String,
    pub date: NaiveDate,
    pub schema: String,
    pub schema_version: usize,
    pub csvx_version: usize,
    pub is_schema: bool,
}

#[inline]
fn cap<T>(c: &regex::Captures, idx: usize) -> T
    where T: std::str::FromStr,
          T::Err: std::fmt::Debug
{
    println!("TRYING: {:?}", c.get(idx));

    safe_unwrap!("input validated via regular expression",
                 safe_unwrap!("valid regex group index", c.get(idx))
                     .as_str()
                     .parse())

}

fn parse_filename<S: AsRef<str>>(filename: S) -> Option<CsvxMetadata> {
    lazy_static! {
        // `tablename_date_schema-schemaversion_csvxversion.csvx`
        static ref FN_RE: Regex = Regex::new(
            r"^([a-z][a-zA-Z0-9-]*)_(\d{4})(\d{2})(\d{2})_([a-z][a-zA-Z0-9-]*)-(\d+)_(\d+).csv(x?)$"
        ).expect("built-in Regex is broken. Please file a bug");
    }

    println!("Input: {:?}", filename.as_ref());
    println!("Result: {:?}", FN_RE.find(filename.as_ref()));

    match FN_RE.captures(filename.as_ref()) {
        Some(caps) => {
            let table_name = safe_unwrap!("known group", caps.get(1))
                .as_str()
                .to_string();
            let year = cap(&caps, 2);
            let month = cap(&caps, 3);
            let day = cap(&caps, 4);
            let schema = safe_unwrap!("known group", caps.get(5))
                .as_str()
                .to_string();
            let schema_version = cap(&caps, 6);
            let csvx_version = cap(&caps, 7);
            let trailing_x = safe_unwrap!("known group", caps.get(8));

            Some(CsvxMetadata {
                     table_name: table_name,
                     date: NaiveDate::from_ymd(year, month, day),
                     schema: schema,
                     schema_version: schema_version,
                     csvx_version: csvx_version,
                     is_schema: trailing_x.start() != trailing_x.end(),
                 })
        }
        None => None,
    }
}

fn main() {
    let app = App::new("csvx")
        .version("2.0.0")
        .about("csvx version 2 utility")
        .subcommand(SubCommand::with_name("check")
                        .about("Check csvx files for conformance")
                        .arg(Arg::with_name("schema_file")
                                 .help("Schema file to check against")
                                 .required(true)
                                 .takes_value(true))
                        .arg(Arg::with_name("input_files")
                                 .help("Input files to check")
                                 .multiple(true)
                                 .takes_value(true)));
    let m = app.clone().get_matches();

    match m.subcommand {
        Some(ref cmd) if cmd.name == "check" => {
            let schema_file = safe_unwrap!("required argument",
                                           cmd.matches.value_of("schema_file"));
            // first, check filename of schema file
            parse_filename(schema_file);
            // println!("{:?}", m);
        }
        _ => app.write_help(&mut io::stdout()).unwrap(),
    }
}

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

    use chrono::NaiveDate;

    #[test]
    fn filename_parsing_rejects_invalid() {
        assert_eq!(parse_filename("asdf"), None);
        assert_eq!(parse_filename(""), None);
        assert_eq!(parse_filename("test.csv"), None);
        assert_eq!(parse_filename("test.csv"), None);
    }

    #[test]
    fn filename_parsing_parses_valid() {
        assert_eq!(parse_filename("zoo-nyc_20170401_animals-2_3.csv").unwrap(),
                   CsvxMetadata {
                       table_name: "zoo-nyc".to_owned(),
                       date: NaiveDate::from_ymd(2017, 04, 01),
                       schema: "animals".to_owned(),
                       schema_version: 2,
                       csvx_version: 3,
                       is_schema: false,
                   });
    }

}
<|endoftext|>


<commit_before>//! `Ports` are the IO channels for a `Node`
//!
//! They can be used as a `Source` and `Destination` for certain `Instruction`s.

/// A `Port` can be read from and write to
pub struct Port {
    input: Vec<i32>,
    output: Vec<i32>,
}

/// The result of reading from a `Port`
pub enum PortReadResult {
    /// Success
    Success(Port, i32),
    /// Failure
    Failure
}

impl Port {
    /// Create a port with a number of readable values
    pub fn new(input: Vec<i32>) -> Port {
        Port { input: input, output: vec![] }
    }

    fn with(input: Vec<i32>, output: Vec<i32>) -> Port {
        Port { input: input, output: output }
    }

    /// Read from this `Port`. Will return a `PortReadResult::Success` when a
    /// value is available, otherwise a `PortReadResult::Failure`
    pub fn read(&self) -> PortReadResult {
        if self.input.len() > 0 {
            let mut result_input = vec![];
            for index in 1..self.input.len() {
                result_input.push(self.input[index]);
            }
            PortReadResult::Success(Port::with(result_input, vec![]), self.input[0])
        } else {
            PortReadResult::Failure
        }
    }
}

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

    #[test]
    fn read_from_a_port_should_work_correctly() {
        let port: Port = Port::new(vec![0, 1, 2]);

        match port.read() {
            PortReadResult::Success(_, value) => assert_eq!(0, value),
            PortReadResult::Failure => panic!(),

        }
    }
}
<commit_msg>Check next_port when reading<commit_after>//! `Ports` are the IO channels for a `Node`
//!
//! They can be used as a `Source` and `Destination` for certain `Instruction`s.

/// A `Port` can be read from and write to
#[derive(Debug,PartialEq,Eq)]
pub struct Port {
    input: Vec<i32>,
    output: Vec<i32>,
}

/// The result of reading from a `Port`
#[derive(Debug)]
pub enum PortReadResult {
    /// Success
    Success(Port, i32),
    /// Failure
    Failure
}

impl Port {
    /// Create a port with a number of readable values
    pub fn new(input: Vec<i32>) -> Port {
        Port { input: input, output: vec![] }
    }

    fn with(input: Vec<i32>, output: Vec<i32>) -> Port {
        Port { input: input, output: output }
    }

    /// Read from this `Port`. Will return a `PortReadResult::Success` when a
    /// value is available, otherwise a `PortReadResult::Failure`
    pub fn read(&self) -> PortReadResult {
        if self.input.len() > 0 {
            let mut result_input = vec![];
            for index in 1..self.input.len() {
                result_input.push(self.input[index]);
            }
            PortReadResult::Success(Port::with(result_input, vec![]), self.input[0])
        } else {
            PortReadResult::Failure
        }
    }
}

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

    #[test]
    fn read_from_a_port_should_work_correctly() {
        let port: Port = Port::new(vec![0, 1, 2]);

        match port.read() {
            PortReadResult::Success(next_port, value) => {
                assert_eq!(0, value);
                assert_eq!(Port::with(vec![1,2], vec![]), next_port);
            },
            PortReadResult::Failure => panic!(),

        }
    }
}
<|endoftext|>