<commit_before>use main::{WINDOW_WIDTH, WINDOW_HEIGHT, RGB};
use opencl;
use opencl::mem::CLBuffer;

pub struct Scene {
  pub center: [f32; 3],
  pub radius: f32,
  pub camera: [f32; 3],
}

impl Scene {
  pub fn render(&self) -> Vec<RGB> {
    let (device, ctx, queue) = opencl::util::create_compute_context().unwrap();

    let len = WINDOW_WIDTH as usize * WINDOW_HEIGHT as usize;

    let output_buffer: CLBuffer<RGB> = ctx.create_buffer(len, opencl::cl::CL_MEM_WRITE_ONLY);

    let program = {
      let ker = format!("
        __kernel void render(
          // sphere
          const float3 center,
          const float radius,

          const float3 eye,

          __global float * output)
        {{
          int W = {};
          int H = {};

          float fov_x = 3.14 / 2;
          float fov_y = 3.14 / 2;

          int i = get_global_id(0);

          float x_pix = i % W;
          float y_pix = i / W;

          float t_x = fov_x * (x_pix / W - 0.5);
          float t_y = fov_y * (y_pix / H - 0.5);

          float c = -cos(t_y);
          float3 ray = {{c*sin(t_x), sin(t_y), c*cos(t_x)}};
          float a = dot(ray, center - eye) / dot(ray, ray);
          float3 d = eye + a*ray - center;

          i = i * 3;
          if (dot(d, d) <= radius * radius) {{
            output[i+0] = 1;
            output[i+1] = 0;
            output[i+2] = 0;
          }} else {{
            output[i+0] = 0;
            output[i+1] = 0;
            output[i+2] = 0;
          }}
        }}",
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
      );
      ctx.create_program_from_source(ker.as_slice())
    };
    program.build(&device).unwrap();

    let kernel = program.create_kernel("render");
    kernel.set_arg(0, &self.center);
    kernel.set_arg(1, &self.radius);
    kernel.set_arg(2, &self.camera);

    // This is sketchy; we "implicitly cast" output_buffer from a CLBuffer<RGB> to a CLBuffer<f32>.
    kernel.set_arg(3, &output_buffer);

    let event = queue.enqueue_async_kernel(&kernel, len, None, ());

    queue.get(&output_buffer, &event)
  }
}
<commit_msg>Add TOI function.<commit_after>use main::{WINDOW_WIDTH, WINDOW_HEIGHT, RGB};
use opencl;
use opencl::mem::CLBuffer;

pub struct Scene {
  pub center: [f32; 3],
  pub radius: f32,
  pub camera: [f32; 3],
}

impl Scene {
  pub fn render(&self) -> Vec<RGB> {
    let (device, ctx, queue) = opencl::util::create_compute_context().unwrap();

    let len = WINDOW_WIDTH as usize * WINDOW_HEIGHT as usize;

    let output_buffer: CLBuffer<RGB> = ctx.create_buffer(len, opencl::cl::CL_MEM_WRITE_ONLY);

    let program = {
      let ker = format!("
        float toi(
          const float3 eye,
          const float3 look,

          const float3 sphere,
          const float radius)
        {{
          float a = dot(radius, radius);
          float b = 2 * dot(eye - sphere, look);
          float c = dot(sphere, sphere) + dot(eye, eye) - dot(eye, sphere) - radius * radius;

          float d = b*b - 4*a*c;

          if (d < 0) {{
            return -1;
          }}

          return (-sqrt(d) - b) / (2 * a);
        }}

        __kernel void render(
          const float3 obj_center,
          const float obj_radius,

          const float3 eye,

          __global float * output)
        {{
          int W = {};
          int H = {};

          float fov_x = 3.14 / 2;
          float fov_y = 3.14 / 2;

          int i = get_global_id(0);

          float x_pix = i % W;
          float y_pix = i / W;

          float t_x = fov_x * (x_pix / W - 0.5);
          float t_y = fov_y * (y_pix / H - 0.5);

          float c = -cos(t_y);
          float3 ray = {{c*sin(t_x), sin(t_y), c*cos(t_x)}};

          i = i * 3;
          if (toi(eye, ray, obj_center, obj_radius) >= 0) {{
            output[i+0] = 1;
            output[i+1] = 0;
            output[i+2] = 0;
          }} else {{
            output[i+0] = 0;
            output[i+1] = 0;
            output[i+2] = 0;
          }}
        }}",
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
      );
      ctx.create_program_from_source(ker.as_slice())
    };
    program.build(&device).unwrap();

    let kernel = program.create_kernel("render");
    kernel.set_arg(0, &self.center);
    kernel.set_arg(1, &self.radius);
    kernel.set_arg(2, &self.camera);

    // This is sketchy; we "implicitly cast" output_buffer from a CLBuffer<RGB> to a CLBuffer<f32>.
    kernel.set_arg(3, &output_buffer);

    let event = queue.enqueue_async_kernel(&kernel, len, None, ());

    queue.get(&output_buffer, &event)
  }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gfx-rs Developers.
//
// 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.

pub use gfx_device_gl::Version as GlslVersion;
#[cfg(target_os = "windows")]
pub use gfx_device_dx11::ShaderModel as DxShaderModel;

/// Shader backend with version numbers.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Backend {
    Glsl(GlslVersion),
    #[cfg(target_os = "windows")]
    Hlsl(DxShaderModel),
}

pub const EMPTY: &'static [u8] = &[];

/// A type storing shader source for different graphics APIs and versions.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Source<'a> {
    pub glsl_120: &'a [u8],
    pub glsl_130: &'a [u8],
    pub glsl_140: &'a [u8],
    pub glsl_150: &'a [u8],
    pub glsl_430: &'a [u8],
    pub hlsl_30 : &'a [u8],
    pub hlsl_40 : &'a [u8],
    pub hlsl_41 : &'a [u8],
    pub hlsl_50 : &'a [u8],
}

impl<'a> Source<'a> {
    /// Create an empty shader source. Useful for specifying the remaining
    /// structure members upon construction.
    pub fn empty() -> Source<'a> {
        Source {
            glsl_120: EMPTY,
            glsl_130: EMPTY,
            glsl_140: EMPTY,
            glsl_150: EMPTY,
            glsl_430: EMPTY,
            hlsl_30:  EMPTY,
            hlsl_40:  EMPTY,
            hlsl_41:  EMPTY,
            hlsl_50:  EMPTY,
        }
    }

    /// Pick one of the stored versions that is the highest supported by the backend.
    pub fn select(&self, backend: Backend) -> Result<&'a [u8], ()> {
        Ok(match backend {
            Backend::Glsl(version) => {
                let v = version.major * 100 + version.minor * 10;
                match *self {
                    Source { glsl_430: s, .. } if s != EMPTY && v >= 430 => s,
                    Source { glsl_150: s, .. } if s != EMPTY && v >= 150 => s,
                    Source { glsl_140: s, .. } if s != EMPTY && v >= 140 => s,
                    Source { glsl_130: s, .. } if s != EMPTY && v >= 130 => s,
                    Source { glsl_120: s, .. } if s != EMPTY && v >= 120 => s,
                    _ => return Err(())
                }
            },
            #[cfg(target_os = "windows")]
            Backend::Hlsl(model) => match *self {
                Source { hlsl_50: s, .. } if s != EMPTY && model >= 50 => s,
                Source { hlsl_41: s, .. } if s != EMPTY && model >= 41 => s,
                Source { hlsl_40: s, .. } if s != EMPTY && model >= 40 => s,
                Source { hlsl_30: s, .. } if s != EMPTY && model >= 30 => s,
                _ => return Err(())
            },
        })
    }
}
<commit_msg>fixed GLSL version check in gfx_app<commit_after>// Copyright 2016 The Gfx-rs Developers.
//
// 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.

pub use gfx_device_gl::Version as GlslVersion;
#[cfg(target_os = "windows")]
pub use gfx_device_dx11::ShaderModel as DxShaderModel;

/// Shader backend with version numbers.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Backend {
    Glsl(GlslVersion),
    #[cfg(target_os = "windows")]
    Hlsl(DxShaderModel),
}

pub const EMPTY: &'static [u8] = &[];

/// A type storing shader source for different graphics APIs and versions.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Source<'a> {
    pub glsl_120: &'a [u8],
    pub glsl_130: &'a [u8],
    pub glsl_140: &'a [u8],
    pub glsl_150: &'a [u8],
    pub glsl_430: &'a [u8],
    pub hlsl_30 : &'a [u8],
    pub hlsl_40 : &'a [u8],
    pub hlsl_41 : &'a [u8],
    pub hlsl_50 : &'a [u8],
}

impl<'a> Source<'a> {
    /// Create an empty shader source. Useful for specifying the remaining
    /// structure members upon construction.
    pub fn empty() -> Source<'a> {
        Source {
            glsl_120: EMPTY,
            glsl_130: EMPTY,
            glsl_140: EMPTY,
            glsl_150: EMPTY,
            glsl_430: EMPTY,
            hlsl_30:  EMPTY,
            hlsl_40:  EMPTY,
            hlsl_41:  EMPTY,
            hlsl_50:  EMPTY,
        }
    }

    /// Pick one of the stored versions that is the highest supported by the backend.
    pub fn select(&self, backend: Backend) -> Result<&'a [u8], ()> {
        Ok(match backend {
            Backend::Glsl(version) => {
                let v = version.major * 100 + version.minor;
                match *self {
                    Source { glsl_430: s, .. } if s != EMPTY && v >= 430 => s,
                    Source { glsl_150: s, .. } if s != EMPTY && v >= 150 => s,
                    Source { glsl_140: s, .. } if s != EMPTY && v >= 140 => s,
                    Source { glsl_130: s, .. } if s != EMPTY && v >= 130 => s,
                    Source { glsl_120: s, .. } if s != EMPTY && v >= 120 => s,
                    _ => return Err(())
                }
            },
            #[cfg(target_os = "windows")]
            Backend::Hlsl(model) => match *self {
                Source { hlsl_50: s, .. } if s != EMPTY && model >= 50 => s,
                Source { hlsl_41: s, .. } if s != EMPTY && model >= 41 => s,
                Source { hlsl_40: s, .. } if s != EMPTY && model >= 40 => s,
                Source { hlsl_30: s, .. } if s != EMPTY && model >= 30 => s,
                _ => return Err(())
            },
        })
    }
}
<|endoftext|>


<commit_before>use std::io::stdin;
use std::io::stdout;
use std::io::Write;
use std::str;
use std::time::Duration;

use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZkError, ZooKeeper};
use zookeeper::acls;

struct MyWatcher;

impl Watcher for MyWatcher {
    fn handle(&self, _e: &WatchedEvent) {
        // println!("{:?}", e)
    }
}

pub struct Shell {
    hosts: String,
    zk: Option<ZooKeeper>,
    session_timeout: u64,
    default_acl: Vec<Acl>,
}

// are we connected?
macro_rules! fetch_zk {
    ($e:expr) => (
        match $e {
            Some(ref __zk) => __zk,
            _ => {
                println!("Not connected.");
                return;
            }
        })
}

macro_rules! check_args {
    ($args:ident, $count:expr, $params:expr) => (
        if $args.len() != $count {
            println!("Wrong number of arguments, expected parameters: {}", $params);
            return;
        })
}

fn report_error(error: ZkError, path: &str) {
    match error {
        ZkError::NoNode => println!("Path {} does not exist.", path),
        ZkError::NotEmpty => println!("Path {} is not empty.", path),
        unknown => println!("Unknown error: {:?}", unknown),
    }
}

impl Shell {
    pub fn new(hosts: &str) -> Shell {
        Shell {
            hosts: hosts.to_string(),
            zk: None,
            session_timeout: 5,
            default_acl: acls::OPEN_ACL_UNSAFE.clone(),
        }
    }

    pub fn run(&mut self) {
        if !self.hosts.is_empty() {
            println!("Connecting...");
            let zk = ZooKeeper::connect(
                &self.hosts,
                Duration::from_secs(self.session_timeout),
                MyWatcher).unwrap();
            self.zk = Some(zk);
        }

        loop {
            let mut line = String::new();

            print!("> ");
            let _ = stdout().flush();

            stdin()
                .read_line(&mut line)
                .ok()
                .expect("Failed to read line");

            let pieces: Vec<&str>  = line.trim().split_whitespace().collect();

            if pieces.len() == 0 {
                continue;
            }

            // dispatch the command
            let args = pieces[1..].to_vec();
            match pieces[0] {
                "get" => self.get(args),
                "set" => self.set(args),
                "ls" => self.ls(args),
                "create" => self.create(args),
                "rm" => self.rm(args),
                unknown => println!("Unknown command: {}", unknown)
            }
        }

    }

    fn get(&mut self, args: Vec<&str>) {
        check_args!(args, 1, "<path>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let ret = zk.get_data(path, false);

        match ret {
            Ok(data_stat) =>  {
                let (bytes, _) = data_stat;
                let datastr = str::from_utf8(&bytes[..]).unwrap().to_string();
                println!("{}", datastr);
            },
            Err(err) => report_error(err, path),
        }
    }

    fn set(&mut self, args: Vec<&str>) {
        check_args!(args, 2, "<path> <data>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let data = args[1].as_bytes().to_vec();
        let ret = zk.set_data(path, data, -1);

        match ret {
            Ok(_) => (),
            Err(err) => report_error(err, path),
        }
    }

    fn ls(&mut self, args: Vec<&str>) {
        check_args!(args, 1, "<path>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let ret = zk.get_children(path, false);

        match ret {
            Ok(children) => println!("{}", children.join(" ")),
            Err(err) => report_error(err, path),
        }
    }

    fn create(&mut self, args: Vec<&str>) {
        check_args!(args, 2, "<path> <data>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let data = args[1].as_bytes().to_vec();

        let ret = zk.create(
            path, data, self.default_acl.clone(), CreateMode::Persistent);

        match ret {
            Ok(_) => (),
            Err(err) => report_error(err, path),
        }
    }

    fn rm(&mut self, args: Vec<&str>) {
        check_args!(args, 1, "<path>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let ret = zk.delete(path, -1);

        match ret {
            Ok(()) =>  (),
            Err(err) => report_error(err, path),
        }

    }
}
<commit_msg>feature(shell::create): support ephemeral/sequential znodes<commit_after>use std::io::stdin;
use std::io::stdout;
use std::io::Write;
use std::str;
use std::time::Duration;

use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZkError, ZooKeeper};
use zookeeper::acls;

struct MyWatcher;

impl Watcher for MyWatcher {
    fn handle(&self, _e: &WatchedEvent) {
        // println!("{:?}", e)
    }
}

pub struct Shell {
    hosts: String,
    zk: Option<ZooKeeper>,
    session_timeout: u64,
    default_acl: Vec<Acl>,
}

// are we connected?
macro_rules! fetch_zk {
    ($e:expr) => (
        match $e {
            Some(ref __zk) => __zk,
            _ => {
                println!("Not connected.");
                return;
            }
        })
}

macro_rules! check_args {
    ($args:ident, $min:expr, $max:expr, $params:expr) => (
        if $args.len() < $min || $args.len() > $max {
            println!("Wrong number of arguments, expected parameters: {}", $params);
            return;
        } else {
            $args.len()
        })
}

fn report_error(error: ZkError, path: &str) {
    match error {
        ZkError::NoNode => println!("Path {} does not exist.", path),
        ZkError::NotEmpty => println!("Path {} is not empty.", path),
        unknown => println!("Unknown error: {:?}", unknown),
    }
}

impl Shell {
    pub fn new(hosts: &str) -> Shell {
        Shell {
            hosts: hosts.to_string(),
            zk: None,
            session_timeout: 5,
            default_acl: acls::OPEN_ACL_UNSAFE.clone(),
        }
    }

    pub fn run(&mut self) {
        if !self.hosts.is_empty() {
            println!("Connecting...");
            let zk = ZooKeeper::connect(
                &self.hosts,
                Duration::from_secs(self.session_timeout),
                MyWatcher).unwrap();
            self.zk = Some(zk);
        }

        loop {
            let mut line = String::new();

            print!("> ");
            let _ = stdout().flush();

            stdin()
                .read_line(&mut line)
                .ok()
                .expect("Failed to read line");

            let pieces: Vec<&str>  = line.trim().split_whitespace().collect();

            if pieces.len() == 0 {
                continue;
            }

            // dispatch the command
            let args = pieces[1..].to_vec();
            match pieces[0] {
                "get" => self.get(args),
                "set" => self.set(args),
                "ls" => self.ls(args),
                "create" => self.create(args),
                "rm" => self.rm(args),
                unknown => println!("Unknown command: {}", unknown)
            }
        }

    }

    fn get(&mut self, args: Vec<&str>) {
        let _ = check_args!(args, 1, 1, "<path>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let ret = zk.get_data(path, false);

        match ret {
            Ok(data_stat) =>  {
                let (bytes, _) = data_stat;
                let datastr = str::from_utf8(&bytes[..]).unwrap().to_string();
                println!("{}", datastr);
            },
            Err(err) => report_error(err, path),
        }
    }

    fn set(&mut self, args: Vec<&str>) {
        let _ = check_args!(args, 2, 2, "<path> <data>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let data = args[1].as_bytes().to_vec();
        let ret = zk.set_data(path, data, -1);

        match ret {
            Ok(_) => (),
            Err(err) => report_error(err, path),
        }
    }

    fn ls(&mut self, args: Vec<&str>) {
        let _ = check_args!(args, 1, 1, "<path>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let ret = zk.get_children(path, false);

        match ret {
            Ok(children) => println!("{}", children.join(" ")),
            Err(err) => report_error(err, path),
        }
    }

    fn create(&mut self, args: Vec<&str>) {
        let mut mode: CreateMode = CreateMode::Persistent;

        let argc = check_args!(args, 2, 4, "<path> <data> [ephemeral] [sequential]");
        if argc >= 3 {
            if args[2].to_lowercase() == "true" {
                mode = CreateMode::Ephemeral;
            }
        }

        if argc == 4 {
            if args[3].to_lowercase() == "true" {
                mode = match mode {
                    CreateMode::Ephemeral => CreateMode::EphemeralSequential,
                    _ => CreateMode::PersistentSequential
                }
            }
        }

        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let data = args[1].as_bytes().to_vec();

        let ret = zk.create(
            path, data, self.default_acl.clone(), mode);

        match ret {
            Ok(_) => (),
            Err(err) => report_error(err, path),
        }
    }

    fn rm(&mut self, args: Vec<&str>) {
        let _ = check_args!(args, 1, 1, "<path>");
        let zk = fetch_zk!(self.zk);
        let path = args[0];
        let ret = zk.delete(path, -1);

        match ret {
            Ok(()) =>  (),
            Err(err) => report_error(err, path),
        }
    }
}
<|endoftext|>


<commit_before>/* Copyright (c) 2016
 * Jeff Nettleton
 *
 * Licensed under the MIT license (http://opensource.org/licenses/MIT). This
 * file may not be copied, modified, or distributed except according to those
 * terms
 */

use route::*;
use request::*;
use response::*;

#[test]
fn test_response_http_message() {
    assert_eq!("OK", Response::get_http_message(200));
}

#[test]
fn test_tooutput_trait_str() {
    let ar: [u8; 3] = [97, 98, 99];

    assert_eq!(ar, ToOutput::to_output("abc"));
}

#[test]
fn test_tooutput_trait_string() {
    let ar: [u8; 3] = [97, 98, 99];
    let st = String::from("abc");

    assert_eq!(ar, ToOutput::to_output(&st));
}

#[test]
fn test_tooutput_trait_vec() {
    let ar: [u8; 5] = [1, 2, 3, 4, 5];
    let vc: Vec<u8> = vec![1, 2, 3, 4, 5];

    assert_eq!(ar, ToOutput::to_output(&vc));
}

#[test]
fn test_fromuri_trait_i32() {
    let mut orig: String = String::from("1234");
    assert_eq!(1234, FromUri::from_uri(&orig));

    orig = String::from("-4321");
    assert_eq!(-4321, FromUri::from_uri(&orig));
}

#[test]
fn test_fromuri_trait_string() {
    let orig: String = String::from("foobar");
    let conv: String = FromUri::from_uri(&orig);

    assert_eq!("foobar", conv);
}

#[test]
fn test_fromuri_trait_float() {
    let mut orig: String = String::from("123.45");
    assert_eq!(123.45f32, FromUri::from_uri(&orig));

    orig = String::from("-54.321");
    assert_eq!(-54.321f32, FromUri::from_uri(&orig));
}

#[test]
fn test_route_match_fail() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>", Method::Get, Route::err_404);
    let mut req = Request::new();

    req.path = String::from("/api/v1/bar");
    req.method = Method::Get;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo");
    req.method = Method::Post;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo/asdf");
    req.method = Method::Get;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo/123");
    req.method = Method::Get;

    assert_eq!(true, rt.is_match(&req));
}

#[test]
fn test_route_match_simple() {
    let route = Route::new("/api/v1/foo/<foo_stuff>", Method::Get, Route::err_404);
    let parsed = route.parse("/api/v1/foo/blahblahblah");

    assert_eq!("blahblahblah", parsed.get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_single_int() {
    let route = Route::new("/api/v1/foo/<int:foo_id>", Method::Get, Route::err_404);
    let parsed = route.parse("/api/v1/foo/123");

    assert_eq!("123", parsed.get("foo_id").unwrap());
}

#[test]
fn test_route_match_single_str() {
    let rt = Route::new("/api/v1/foo/<str:foo_stuff>", Method::Get, Route::err_404);
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_many() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>/bar/<str:bar>/baz/<int:baz_id>", Method::Get, Route::err_404);
    let rm = rt.parse("/api/v1/foo/123/bar/bar/baz/456");

    assert_eq!("123", rm.get("foo_id").unwrap());
    assert_eq!("bar", rm.get("bar").unwrap());
    assert_eq!("456", rm.get("baz_id").unwrap());
}

#[test]
fn test_find_route_native_types() {
    let mut request = Request::new();
    let routes: Vec<Route> = vec![Route::new("/api/v1/foo/<int:foo_id>", Method::Get, Route::err_404),
                                  Route::new("/api/v1/foo/<int:foo_id>/bar/<int:bar_id>", Method::Get, Route::err_404)];

    request.method = Method::Get;
    request.path = String::from("/api/v1/foo/42/bar/1234");

    for route in routes {
        match route.is_match(&request) {
            false => continue,
            true  => {
                request.params = route.parse(&request.path);
                break;
            },
        }
    }

    let foo_id: i32 = request.get("foo_id");
    let bar_id: i32 = request.get("bar_id");

    assert_eq!(42, foo_id);
    assert_eq!(1234, bar_id);
}
<commit_msg>update our tests<commit_after>/* Copyright (c) 2016
 * Jeff Nettleton
 *
 * Licensed under the MIT license (http://opensource.org/licenses/MIT). This
 * file may not be copied, modified, or distributed except according to those
 * terms
 */

use route::*;
use request::*;
use response::*;
use utils::*;

#[test]
fn test_response_http_message() {
    assert_eq!("OK", Response::get_http_message(200));
}

#[test]
fn test_tooutput_trait_str() {
    let ar: [u8; 3] = [97, 98, 99];

    assert_eq!(ar, ToOutput::to_output("abc"));
}

#[test]
fn test_tooutput_trait_string() {
    let ar: [u8; 3] = [97, 98, 99];
    let st = String::from("abc");

    assert_eq!(ar, ToOutput::to_output(&st));
}

#[test]
fn test_tooutput_trait_vec() {
    let ar: [u8; 5] = [1, 2, 3, 4, 5];
    let vc: Vec<u8> = vec![1, 2, 3, 4, 5];

    assert_eq!(ar, ToOutput::to_output(&vc));
}

#[test]
fn test_fromuri_trait_i32() {
    let mut orig: String = String::from("1234");
    assert_eq!(1234, FromUri::from_uri(&orig));

    orig = String::from("-4321");
    assert_eq!(-4321, FromUri::from_uri(&orig));
}

#[test]
fn test_fromuri_trait_string() {
    let orig: String = String::from("foobar");
    let conv: String = FromUri::from_uri(&orig);

    assert_eq!("foobar", conv);
}

#[test]
fn test_fromuri_trait_float() {
    let mut orig: String = String::from("123.45");
    assert_eq!(123.45f32, FromUri::from_uri(&orig));

    orig = String::from("-54.321");
    assert_eq!(-54.321f32, FromUri::from_uri(&orig));
}

#[test]
fn test_route_match_fail() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>", Method::Get, err_404);
    let mut req = Request::new();

    req.path = String::from("/api/v1/bar");
    req.method = Method::Get;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo");
    req.method = Method::Post;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo/asdf");
    req.method = Method::Get;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo/123");
    req.method = Method::Get;

    assert_eq!(true, rt.is_match(&req));
}

#[test]
fn test_route_match_simple() {
    let route = Route::new("/api/v1/foo/<foo_stuff>", Method::Get, err_404);
    let parsed = route.parse("/api/v1/foo/blahblahblah");

    assert_eq!("blahblahblah", parsed.get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_single_int() {
    let route = Route::new("/api/v1/foo/<int:foo_id>", Method::Get, err_404);
    let parsed = route.parse("/api/v1/foo/123");

    assert_eq!("123", parsed.get("foo_id").unwrap());
}

#[test]
fn test_route_match_single_str() {
    let rt = Route::new("/api/v1/foo/<str:foo_stuff>", Method::Get, err_404);
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_many() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>/bar/<str:bar>/baz/<int:baz_id>", Method::Get, err_404);
    let rm = rt.parse("/api/v1/foo/123/bar/bar/baz/456");

    assert_eq!("123", rm.get("foo_id").unwrap());
    assert_eq!("bar", rm.get("bar").unwrap());
    assert_eq!("456", rm.get("baz_id").unwrap());
}

#[test]
fn test_find_route_native_types() {
    let mut request = Request::new();
    let routes: Vec<Route> = vec![Route::new("/api/v1/foo/<int:foo_id>", Method::Get, err_404),
                                  Route::new("/api/v1/foo/<int:foo_id>/bar/<int:bar_id>", Method::Get, err_404)];

    request.method = Method::Get;
    request.path = String::from("/api/v1/foo/42/bar/1234");

    for route in routes {
        match route.is_match(&request) {
            false => continue,
            true  => {
                request.params = route.parse(&request.path);
                break;
            },
        }
    }

    let foo_id: i32 = request.get("foo_id");
    let bar_id: i32 = request.get("bar_id");

    assert_eq!(42, foo_id);
    assert_eq!(1234, bar_id);
}
<|endoftext|>


<commit_before>/// Represents a document and its associated score
#[derive(Debug)]
pub struct Document {
    /// Document
    pub doc: String,
    /// Score of document
    pub score: f32
}

/// Represents TF struct, vector of Documents
#[derive(Debug)]
pub struct Tf(pub Vec<Document>);

impl PartialEq for Document {
    fn eq(&self, other: &Document) -> bool {
        self.doc == other.doc && self.score == other.score
    }
}

impl PartialEq for Tf {
    fn eq(&self, other: &Tf) -> bool {
        self.0 == other.0
    }
}

impl Tf {
    pub fn add(doc: String, raw_docs: Vec<String>) -> f32 {
        let occurance: f32 = raw_docs.into_iter().filter(
            |dx| dx.to_string() == doc.to_string()
        ).count() as f32;

        return match occurance {
            0.0f32 => 0.0f32,
            _ => {
                occurance.log10() + 1.0f32
            }
        };
    }
}
<commit_msg>fixing typo<commit_after>/// Represents a document and its associated score
#[derive(Debug)]
pub struct Document {
    /// Document
    pub doc: String,
    /// Score of document
    pub score: f32
}

/// Represents TF struct, vector of Documents
#[derive(Debug)]
pub struct Tf(pub Vec<Document>);

impl PartialEq for Document {
    fn eq(&self, other: &Document) -> bool {
        self.doc == other.doc && self.score == other.score
    }
}

impl PartialEq for Tf {
    fn eq(&self, other: &Tf) -> bool {
        self.0 == other.0
    }
}

impl Tf {
    pub fn add(doc: String, raw_docs: Vec<String>) -> f32 {
        let counts: f32 = raw_docs.into_iter().filter(
            |dx| dx.to_string() == doc.to_string()
        ).count() as f32;

        return match counts {
            0.0f32 => 0.0f32,
            _ => {
                counts.log10() + 1.0f32
            }
        };
    }
}
<|endoftext|>


<commit_before>// Atomic unit in the grammar is called "token".
// For example, `123`, `"abc"` and `while` are tokens.
// The tokenizer splits an input string into tokens.
// Spaces and comments are removed by the tokenizer.

#![allow(non_camel_case_types)]

use std::cmp;
use std::sync::Mutex;
use std::collections::HashMap;

lazy_static! {
    static ref SYMBOLS: Mutex<Vec<Symbol>> = Mutex::new(Vec::new());
    static ref ESCAPED: Mutex<HashMap<char, char>> = Mutex::new(HashMap::new());
}

fn init_symbols() {
    let mut symbols = SYMBOLS.lock().unwrap();

    symbols.push(Symbol { name: "_Alignof", ty: TokenType::ALIGNOF });
    symbols.push(Symbol { name: "break", ty: TokenType::BREAK });
    symbols.push(Symbol { name: "char", ty: TokenType::CHAR });
    symbols.push(Symbol { name: "do", ty: TokenType::DO });
    symbols.push(Symbol { name: "else", ty: TokenType::ELSE });
    symbols.push(Symbol { name: "extern", ty: TokenType::EXTERN });
    symbols.push(Symbol { name: "for", ty: TokenType::FOR });
    symbols.push(Symbol { name: "if", ty: TokenType::IF });
    symbols.push(Symbol { name: "int", ty: TokenType::INT });
    symbols.push(Symbol { name: "return", ty: TokenType::RETURN });
    symbols.push(Symbol { name: "sizeof", ty: TokenType::SIZEOF });
    symbols.push(Symbol { name: "struct", ty: TokenType::STRUCT });
    symbols.push(Symbol { name: "typedef", ty: TokenType::TYPEDEF });
    symbols.push(Symbol { name: "void", ty: TokenType::VOID });
    symbols.push(Symbol { name: "while", ty: TokenType::WHILE });
    symbols.push(Symbol { name: "!=", ty: TokenType::NE });
    symbols.push(Symbol { name: "&&", ty: TokenType::LOGAND });
    symbols.push(Symbol { name: "++", ty: TokenType::INC });
    symbols.push(Symbol { name: "--", ty: TokenType::DEC });
    symbols.push(Symbol { name: "->", ty: TokenType::ARROW });
    symbols.push(Symbol { name: "<<", ty: TokenType::SHL });
    symbols.push(Symbol { name: "<=", ty: TokenType::LE });
    symbols.push(Symbol { name: "==", ty: TokenType::EQ });
    symbols.push(Symbol { name: ">=", ty: TokenType::GE });
    symbols.push(Symbol { name: ">>", ty: TokenType::SHR });
    symbols.push(Symbol { name: "||", ty: TokenType::LOGOR });
}

fn init_escaped() {
    let mut escaped = ESCAPED.lock().unwrap();

    escaped.insert('a', char::from(7));  // \a
    escaped.insert('b', char::from(8));  // \b
    escaped.insert('f', char::from(12)); // \f
    escaped.insert('n', char::from(10)); // \n
    escaped.insert('r', char::from(13)); // \r
    escaped.insert('t', char::from(9));  // \t
    escaped.insert('v', char::from(11)); // \v
    escaped.insert('e', char::from(27)); // \033
    escaped.insert('E', char::from(27)); // \033
}

fn escaped(c: char) -> Option<char> {
    let mut ret = None;
    match ESCAPED.lock() {
        Ok(escaped) => {
            match escaped.get(&c) {
                Some(c) => {
                    ret = Some(*c);
                }
                None => { }
            }
        }
        Err(_) => { }
    }
    return ret;
}

struct Symbol {
    name: &'static str,
    ty: TokenType,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TokenType {
    ADD,        // +
    SUB,        // -
    MUL,        // *
    DIV,        // /
    EQL,        // =
    LT,         // <
    GT,         // >
    OR,         // |
    HAT,        // ^
    BRA,        // (
    KET,        // )
    C_BRA,      // {
    C_KET,      // }
    S_BRA,      // [
    S_KET,      // ]
    AMP,        // &
    MOD,        // %
    EXCLAM,     // !
    QUEST,      // ?
    DOT,        // .
    COMMA,      // ,
    COLON,      // :
    SEMI_COLON, // ;
    NUM,        // Number literal
    STR,        // String literal
    IDENT,      // Identifier
    ARROW,      // "->"
    EXTERN,     // "extern"
    TYPEDEF,    // "typedef"
    INT,        // "int"
    CHAR,       // "char"
    VOID,       // "void"
    STRUCT,     // "struct"
    IF,         // "if"
    ELSE,       // "else"
    FOR,        // "for"
    DO,         // "do"
    WHILE,      // "while"
    BREAK,      // "break"
    EQ,         // ==
    NE,         // !=
    LE,         // <=
    GE,         // >=
    LOGOR,      // ||
    LOGAND,     // &&
    SHL,        // <<
    SHR,        // >>
    INC,        // ++
    DEC,        // --
    RETURN,     // "return"
    SIZEOF,     // "sizeof"
    ALIGNOF,    // "_Alignof"
    EOF,        // End marker
}

#[derive(Debug)]
pub struct Token {
    pub ty: TokenType, // Token type
    pub val: i32,      // Nuber literal
    pub name: String,  // Identifier
    pub input: String, // Token string (for error reporting)

    // String literal
    pub str_cnt: String,
    pub len: usize,
}

fn new_token(ty: TokenType) -> Token {
    Token{
        ty: ty,
        val: 0,
        name: String::new(),
        input: String::new(),
        str_cnt: String::new(),
        len: 0,
    }
}

struct CharInfo {
    chr: char,
    len: usize,
}

fn read_char(p: &String, idx: usize) -> CharInfo {
    if p.len() <= idx {
        panic!("premature end of input");
    }

    let char_bytes = p.as_bytes();
    let mut len = 0;
    let mut c = char::from(char_bytes[idx]);
    if c != '\\' {
        len += 1;
    } else {
        len += 1;
        if p.len() <= idx + len {
            panic!("premature end of input");
        }
        c = char::from(char_bytes[idx+len]);
        match escaped(c) {
            Some(esc) => {
                c = esc;
            }
            None => { }
        }
        len += 1;
    }

    if char::from(char_bytes[idx+len]) != '\'' {
        panic!("unclosed character literal");
    }
    len += 1;
    return CharInfo {
        chr: c,
        len: len,
    };
}

struct StrInfo {
    cnt: String,
    len: usize,
}

fn read_string(p: &String, idx: usize) -> StrInfo {
    let mut len = 0;
    let mut ret = String::new();

    let char_bytes = p.as_bytes();
    while (idx+len) < p.len() && char::from(char_bytes[idx+len]) != '"' {
        let mut c = char::from(char_bytes[idx+len]);

        if c != '\\' {
            ret.push(c);
            len += 1;
            continue;
        }

        len += 1;
        c = char::from(char_bytes[idx+len]);
        if c == '\0' {
            panic!("premature end of input");
        }
        match escaped(c) {
            Some(esc) => ret.push(esc),
            None => ret.push(c),
        }
        len += 1;
    }
    if (idx+len) >= p.len() {
        panic!("premature end of input: {}", ret);
    }
    //ret.push(&format!("\\{:03o}", u32::from(c)));
    ret.push(char::from(0));
    return StrInfo{
        cnt: ret,
        len: len,
    };
}

pub fn tokenize(p: &String) -> Vec<Token> {
    init_symbols();
    init_escaped();

    let mut tokens = Vec::new();
    let char_bytes = p.as_bytes();
    let mut idx = 0;
    'outer: while idx < char_bytes.len() {
        let c: char = char::from(char_bytes[idx]);
        if c.is_whitespace() {
            idx += 1;
            continue;
        }

        // Line comment
        if c == '/' && char::from(char_bytes[idx+1]) == '/' {
            while char::from(char_bytes[idx]) != '\n' {
                idx += 1;
            }
            continue;
        }

        // Block comment
        if &p[idx..idx+2] == "/*" {
            idx += 2;
            while idx < p.len() {
                if &p[idx..idx+2] != "*/" {
                    idx += 1;
                    continue;
                }
                idx += 2;
                continue 'outer;
            }
            panic!("unclosed comment");
        }

        // Character literal
        if c == '\'' {
            let mut t = new_token(TokenType::NUM);
            idx += 1;
            let info = read_char(p, idx);
            t.val = u32::from(info.chr) as i32;

            tokens.push(t);
            idx += info.len;
            continue;
        }

        // String literal
        if c == '"' {
            let mut t = new_token(TokenType::STR);
            idx += 1;

            let info = read_string(p, idx);
            let s = info.cnt;
            let len = info.len;
            t.str_cnt = s;
            t.len = len;
            tokens.push(t);
            idx += len + 1;
            continue;
        }

        // Multi-letter token
        match SYMBOLS.lock() {
            Ok(symbols) => {
                for s in symbols.iter() {
                    let bs = &char_bytes[idx..cmp::min(idx+s.name.len(), char_bytes.len())];
                    if s.name.as_bytes() != bs {
                        continue;
                    }

                    tokens.push(Token{
                        ty: s.ty,
                        val: 0,
                        name: String::from(s.name),
                        input: String::from(s.name),
                        str_cnt: String::new(),
                        len: 0,
                    });

                    idx += s.name.len();
                    continue 'outer;
                }
            }
            Err(_) => {
                panic!();
            }
        }

        // Single-letter token
        if "+-*/;=(),{}<>[]&.!?:|^%".contains(c) {
            let ty = match c {
                '+' => TokenType::ADD,
                '-' => TokenType::SUB,
                '*' => TokenType::MUL,
                '/' => TokenType::DIV,
                ':' => TokenType::COLON,
                ';' => TokenType::SEMI_COLON,
                '=' => TokenType::EQL,
                '<' => TokenType::LT,
                '>' => TokenType::GT,
                '|' => TokenType::OR,
                '^' => TokenType::HAT,
                ',' => TokenType::COMMA,
                '(' => TokenType::BRA,
                ')' => TokenType::KET,
                '{' => TokenType::C_BRA,
                '}' => TokenType::C_KET,
                '[' => TokenType::S_BRA,
                ']' => TokenType::S_KET,
                '&' => TokenType::AMP,
                '!' => TokenType::EXCLAM,
                '?' => TokenType::QUEST,
                '.' => TokenType::DOT,
                '%' => TokenType::MOD,
                _ => panic!("unknown {}", c),
            };
            let tok = Token {
                ty: ty,
                val: 0,
                name: String::new(),
                input: format!("{}", c),
                str_cnt: String::new(),
                len: 0,
            };

            tokens.push(tok);

            idx += 1;
            continue;
        }

        // Identifier
        if c.is_alphabetic() || c == '_' {
            let mut s = String::new();
            s.push(c);
            idx += 1;
            while idx < char_bytes.len() {
                let d = char::from(char_bytes[idx]);
                if d.is_alphabetic() || d.is_digit(10) || d == '_' {
                    s.push(d);
                    idx += 1;
                } else {
                    break;
                }
            }

            let tok = Token {
                ty: TokenType::IDENT,
                val: 0,
                name: s.clone(),
                input: s.clone(),
                str_cnt: String::new(),
                len: 0,
            };

            tokens.push(tok);
            continue;
        }

        // Number
        if c.is_digit(10) {
            let mut s = String::new();
            s.push(c);
            idx += 1;
            while idx < char_bytes.len() {
                let d = char::from(char_bytes[idx]);
                if d.is_digit(10) {
                    s.push(d);
                    idx += 1;
                } else {
                    break;
                }
            }
            let tok = Token {
                ty: TokenType::NUM,
                val: i32::from_str_radix(&s, 10).unwrap(),
                name: String::new(),
                input: s,
                str_cnt: String::new(),
                len: 0,
            };

            tokens.push(tok);
            continue;
        }

        panic!("cannot tokenize: {}", c);
    }

    let tok = Token {
        ty: TokenType::EOF,
        val: 0,
        name: String::new(),
        input: String::new(),
        str_cnt: String::new(),
        len: 0,
    };
    tokens.push(tok);

    return tokens;
}

<commit_msg>Fix a bug that a keyword cannot be a prefix of an identifier.<commit_after>// Atomic unit in the grammar is called "token".
// For example, `123`, `"abc"` and `while` are tokens.
// The tokenizer splits an input string into tokens.
// Spaces and comments are removed by the tokenizer.

#![allow(non_camel_case_types)]

use std::cmp;
use std::sync::Mutex;
use std::collections::HashMap;

lazy_static! {
    static ref SYMBOLS: Mutex<Vec<Symbol>> = Mutex::new(Vec::new());
    static ref ESCAPED: Mutex<HashMap<char, char>> = Mutex::new(HashMap::new());
}

fn init_symbols() {
    let mut symbols = SYMBOLS.lock().unwrap();

//    symbols.push(Symbol { name: "_Alignof", ty: TokenType::ALIGNOF });
//    symbols.push(Symbol { name: "break", ty: TokenType::BREAK });
//    symbols.push(Symbol { name: "char", ty: TokenType::CHAR });
//    symbols.push(Symbol { name: "do", ty: TokenType::DO });
//    symbols.push(Symbol { name: "else", ty: TokenType::ELSE });
//    symbols.push(Symbol { name: "extern", ty: TokenType::EXTERN });
//    symbols.push(Symbol { name: "for", ty: TokenType::FOR });
//    symbols.push(Symbol { name: "if", ty: TokenType::IF });
//    symbols.push(Symbol { name: "int", ty: TokenType::INT });
//    symbols.push(Symbol { name: "return", ty: TokenType::RETURN });
//    symbols.push(Symbol { name: "sizeof", ty: TokenType::SIZEOF });
//    symbols.push(Symbol { name: "struct", ty: TokenType::STRUCT });
//    symbols.push(Symbol { name: "typedef", ty: TokenType::TYPEDEF });
//    symbols.push(Symbol { name: "void", ty: TokenType::VOID });
//    symbols.push(Symbol { name: "while", ty: TokenType::WHILE });

    symbols.push(Symbol { name: "!=", ty: TokenType::NE });
    symbols.push(Symbol { name: "&&", ty: TokenType::LOGAND });
    symbols.push(Symbol { name: "++", ty: TokenType::INC });
    symbols.push(Symbol { name: "--", ty: TokenType::DEC });
    symbols.push(Symbol { name: "->", ty: TokenType::ARROW });
    symbols.push(Symbol { name: "<<", ty: TokenType::SHL });
    symbols.push(Symbol { name: "<=", ty: TokenType::LE });
    symbols.push(Symbol { name: "==", ty: TokenType::EQ });
    symbols.push(Symbol { name: ">=", ty: TokenType::GE });
    symbols.push(Symbol { name: ">>", ty: TokenType::SHR });
    symbols.push(Symbol { name: "||", ty: TokenType::LOGOR });
    symbols.push(Symbol { name: "*=", ty: TokenType::MUL_EQ });
    symbols.push(Symbol { name: "/=", ty: TokenType::DIV_EQ });
    symbols.push(Symbol { name: "%=", ty: TokenType::MOD_EQ });
    symbols.push(Symbol { name: "+=", ty: TokenType::ADD_EQ });
    symbols.push(Symbol { name: "-=", ty: TokenType::SUB_EQ });
    symbols.push(Symbol { name: "<<=", ty: TokenType::SHL_EQ });
    symbols.push(Symbol { name: ">>=", ty: TokenType::SHR_EQ });
    symbols.push(Symbol { name: "&=", ty: TokenType::BITAND_EQ });
    symbols.push(Symbol { name: "^=", ty: TokenType::XOR_EQ });
    symbols.push(Symbol { name: "|=", ty: TokenType::BITOR_EQ });
}

fn init_escaped() {
    let mut escaped = ESCAPED.lock().unwrap();

    escaped.insert('a', char::from(7));  // \a
    escaped.insert('b', char::from(8));  // \b
    escaped.insert('f', char::from(12)); // \f
    escaped.insert('n', char::from(10)); // \n
    escaped.insert('r', char::from(13)); // \r
    escaped.insert('t', char::from(9));  // \t
    escaped.insert('v', char::from(11)); // \v
    escaped.insert('e', char::from(27)); // \033
    escaped.insert('E', char::from(27)); // \033
}

fn escaped(c: char) -> Option<char> {
    let mut ret = None;
    match ESCAPED.lock() {
        Ok(escaped) => {
            match escaped.get(&c) {
                Some(c) => {
                    ret = Some(*c);
                }
                None => { }
            }
        }
        Err(_) => { }
    }
    return ret;
}

struct Symbol {
    name: &'static str,
    ty: TokenType,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TokenType {
    ADD,        // +
    SUB,        // -
    MUL,        // *
    DIV,        // /
    EQL,        // =
    LT,         // <
    GT,         // >
    OR,         // |
    HAT,        // ^
    BRA,        // (
    KET,        // )
    C_BRA,      // {
    C_KET,      // }
    S_BRA,      // [
    S_KET,      // ]
    AMP,        // &
    MOD,        // %
    EXCLAM,     // !
    QUEST,      // ?
    DOT,        // .
    COMMA,      // ,
    COLON,      // :
    SEMI_COLON, // ;
    NUM,        // Number literal
    STR,        // String literal
    IDENT,      // Identifier
    ARROW,      // "->"
    EXTERN,     // "extern"
    TYPEDEF,    // "typedef"
    INT,        // "int"
    CHAR,       // "char"
    VOID,       // "void"
    STRUCT,     // "struct"
    IF,         // "if"
    ELSE,       // "else"
    FOR,        // "for"
    DO,         // "do"
    WHILE,      // "while"
    BREAK,      // "break"
    EQ,         // ==
    NE,         // !=
    LE,         // <=
    GE,         // >=
    LOGOR,      // ||
    LOGAND,     // &&
    SHL,        // <<
    SHR,        // >>
    INC,        // ++
    DEC,        // --
    MUL_EQ,     // *=
    DIV_EQ,     // /=
    MOD_EQ,     // %=
    ADD_EQ,     // +=
    SUB_EQ,     // -=
    SHL_EQ,     // <<=
    SHR_EQ,     // >>=
    BITAND_EQ,  // &=
    XOR_EQ,     // ^=
    BITOR_EQ,   // |=
    RETURN,     // "return"
    SIZEOF,     // "sizeof"
    ALIGNOF,    // "_Alignof"
    EOF,        // End marker
}

#[derive(Debug)]
pub struct Token {
    pub ty: TokenType, // Token type
    pub val: i32,      // Nuber literal
    pub name: String,  // Identifier
    pub input: String, // Token string (for error reporting)

    // String literal
    pub str_cnt: String,
    pub len: usize,
}

fn new_token(ty: TokenType) -> Token {
    Token{
        ty: ty,
        val: 0,
        name: String::new(),
        input: String::new(),
        str_cnt: String::new(),
        len: 0,
    }
}

struct CharInfo {
    chr: char,
    len: usize,
}

fn read_char(p: &String, idx: usize) -> CharInfo {
    if p.len() <= idx {
        panic!("premature end of input");
    }

    let char_bytes = p.as_bytes();
    let mut len = 0;
    let mut c = char::from(char_bytes[idx]);
    if c != '\\' {
        len += 1;
    } else {
        len += 1;
        if p.len() <= idx + len {
            panic!("premature end of input");
        }
        c = char::from(char_bytes[idx+len]);
        match escaped(c) {
            Some(esc) => {
                c = esc;
            }
            None => { }
        }
        len += 1;
    }

    if char::from(char_bytes[idx+len]) != '\'' {
        panic!("unclosed character literal");
    }
    len += 1;
    return CharInfo {
        chr: c,
        len: len,
    };
}

struct StrInfo {
    cnt: String,
    len: usize,
}

fn read_string(p: &String, idx: usize) -> StrInfo {
    let mut len = 0;
    let mut ret = String::new();

    let char_bytes = p.as_bytes();
    while (idx+len) < p.len() && char::from(char_bytes[idx+len]) != '"' {
        let mut c = char::from(char_bytes[idx+len]);

        if c != '\\' {
            ret.push(c);
            len += 1;
            continue;
        }

        len += 1;
        c = char::from(char_bytes[idx+len]);
        if c == '\0' {
            panic!("premature end of input");
        }
        match escaped(c) {
            Some(esc) => ret.push(esc),
            None => ret.push(c),
        }
        len += 1;
    }
    if (idx+len) >= p.len() {
        panic!("premature end of input: {}", ret);
    }
    //ret.push(&format!("\\{:03o}", u32::from(c)));
    ret.push(char::from(0));
    return StrInfo{
        cnt: ret,
        len: len,
    };
}

fn keyword_map() -> HashMap<String, TokenType> {
    let mut keywords = HashMap::new();
    keywords.insert("_Alignof".to_string(), TokenType::ALIGNOF);
    keywords.insert("break".to_string(), TokenType::BREAK);
    keywords.insert("char".to_string(), TokenType::CHAR);
    keywords.insert("do".to_string(), TokenType::DO);
    keywords.insert("else".to_string(), TokenType::ELSE);
    keywords.insert("extern".to_string(), TokenType::EXTERN);
    keywords.insert("for".to_string(), TokenType::FOR);
    keywords.insert("if".to_string(), TokenType::IF);
    keywords.insert("int".to_string(), TokenType::INT);
    keywords.insert("return".to_string(), TokenType::RETURN);
    keywords.insert("sizeof".to_string(), TokenType::SIZEOF);
    keywords.insert("struct".to_string(), TokenType::STRUCT);
    keywords.insert("typedef".to_string(), TokenType::TYPEDEF);
    keywords.insert("void".to_string(), TokenType::VOID);
    keywords.insert("while".to_string(), TokenType::WHILE);
    return keywords;
}

pub fn tokenize(p: &String) -> Vec<Token> {
    init_symbols();
    init_escaped();

    let keywords = keyword_map();
    let mut tokens = Vec::new();
    let char_bytes = p.as_bytes();
    let mut idx = 0;
    'outer: while idx < char_bytes.len() {
        let c: char = char::from(char_bytes[idx]);
        if c.is_whitespace() {
            idx += 1;
            continue;
        }

        // Line comment
        if c == '/' && char::from(char_bytes[idx+1]) == '/' {
            while char::from(char_bytes[idx]) != '\n' {
                idx += 1;
            }
            continue;
        }

        // Block comment
        if &p[idx..idx+2] == "/*" {
            idx += 2;
            while idx < p.len() {
                if &p[idx..idx+2] != "*/" {
                    idx += 1;
                    continue;
                }
                idx += 2;
                continue 'outer;
            }
            panic!("unclosed comment");
        }

        // Character literal
        if c == '\'' {
            let mut t = new_token(TokenType::NUM);
            idx += 1;
            let info = read_char(p, idx);
            t.val = u32::from(info.chr) as i32;

            tokens.push(t);
            idx += info.len;
            continue;
        }

        // String literal
        if c == '"' {
            let mut t = new_token(TokenType::STR);
            idx += 1;

            let info = read_string(p, idx);
            let s = info.cnt;
            let len = info.len;
            t.str_cnt = s;
            t.len = len;
            tokens.push(t);
            idx += len + 1;
            continue;
        }

        // Multi-letter symbol
        match SYMBOLS.lock() {
            Ok(symbols) => {
                for s in symbols.iter() {
                    let bs = &char_bytes[idx..cmp::min(idx+s.name.len(), char_bytes.len())];
                    if s.name.as_bytes() != bs {
                        continue;
                    }

                    tokens.push(Token{
                        ty: s.ty,
                        val: 0,
                        name: String::from(s.name),
                        input: String::from(s.name),
                        str_cnt: String::new(),
                        len: 0,
                    });

                    idx += s.name.len();
                    continue 'outer;
                }
            }
            Err(_) => {
                panic!();
            }
        }

        // Single-letter symbol
        if "+-*/;=(),{}<>[]&.!?:|^%".contains(c) {
            let ty = match c {
                '+' => TokenType::ADD,
                '-' => TokenType::SUB,
                '*' => TokenType::MUL,
                '/' => TokenType::DIV,
                ':' => TokenType::COLON,
                ';' => TokenType::SEMI_COLON,
                '=' => TokenType::EQL,
                '<' => TokenType::LT,
                '>' => TokenType::GT,
                '|' => TokenType::OR,
                '^' => TokenType::HAT,
                ',' => TokenType::COMMA,
                '(' => TokenType::BRA,
                ')' => TokenType::KET,
                '{' => TokenType::C_BRA,
                '}' => TokenType::C_KET,
                '[' => TokenType::S_BRA,
                ']' => TokenType::S_KET,
                '&' => TokenType::AMP,
                '!' => TokenType::EXCLAM,
                '?' => TokenType::QUEST,
                '.' => TokenType::DOT,
                '%' => TokenType::MOD,
                _ => panic!("unknown {}", c),
            };
            let tok = Token {
                ty: ty,
                val: 0,
                name: String::new(),
                input: format!("{}", c),
                str_cnt: String::new(),
                len: 0,
            };

            tokens.push(tok);

            idx += 1;
            continue;
        }

        // Keyword or identifier
        if c.is_alphabetic() || c == '_' {
            let mut name = String::new();
            name.push(c);
            idx += 1;
            while idx < char_bytes.len() {
                let d = char::from(char_bytes[idx]);
                if d.is_alphabetic() || d.is_digit(10) || d == '_' {
                    name.push(d);
                    idx += 1;
                } else {
                    break;
                }
            }

            let ty = match keywords.get(&name) {
                Some(k) => *k,
                None => TokenType::IDENT,
            };

            let tok = Token {
                ty: ty,
                val: 0,
                name: name.clone(),
                input: name.clone(),
                str_cnt: String::new(),
                len: 0,
            };

            tokens.push(tok);
            continue;
        }

        // Number
        if c.is_digit(10) {
            let mut s = String::new();
            s.push(c);
            idx += 1;
            while idx < char_bytes.len() {
                let d = char::from(char_bytes[idx]);
                if d.is_digit(10) {
                    s.push(d);
                    idx += 1;
                } else {
                    break;
                }
            }
            let tok = Token {
                ty: TokenType::NUM,
                val: i32::from_str_radix(&s, 10).unwrap(),
                name: String::new(),
                input: s,
                str_cnt: String::new(),
                len: 0,
            };

            tokens.push(tok);
            continue;
        }

        panic!("cannot tokenize: {}", c);
    }

    let tok = Token {
        ty: TokenType::EOF,
        val: 0,
        name: String::new(),
        input: String::new(),
        str_cnt: String::new(),
        len: 0,
    };
    tokens.push(tok);

    return tokens;
}

<|endoftext|>


<commit_before>use dsp::{Sample, FromSample, Node, Settings};
use waveforms::{Waveform, Sine};

pub type Frequency = f64;
pub type Phase = f64;
pub type Output = f32; // f64 not supported yet
pub type Amp = f64;
pub type Volume = f64;

pub struct Oscillator {
  phase: Phase,
  waveform: Sine // TODO: any waveform
}

pub struct Synth {
  oscillator: Oscillator,
  frequency: Frequency,
  volume: Amp,
}

impl Oscillator {
    pub fn new(waveform: Sine) -> Oscillator {
        Oscillator {
            phase: 0.0,
            waveform: waveform
        }
    }

    pub fn amp_at_next_phase(&mut self, frequency: Frequency, settings: Settings) -> Amp {
        let val = self.waveform.amp_at_phase(self.phase);
        self.phase += frequency / settings.sample_hz as f64;
        val
    }
}

impl Synth {
    pub fn new(oscillator: Oscillator, frequency: Frequency, volume: Amp) -> Synth {
        Synth {
            oscillator: oscillator, // TODO: multiple voices
            frequency: frequency,  // TODO: dynamic frequency
            volume: volume  // TODO: dynamic volume
        }
    }

    fn volume<S: Sample>(&self, amp: Amp) -> S
        where S: Sample + FromSample<f64>,
    {
        (amp * self.volume).to_sample::<S>()
    }
}

impl Node<Output> for Synth {
    /// Here we'll override the audio_requested method and generate a sound.
    fn audio_requested(&mut self, buffer: &mut [Output], settings: Settings) {
        for frame in buffer.chunks_mut(settings.channels as usize) {
            let val = self.oscillator.amp_at_next_phase(self.frequency, settings);
            for channel in frame.iter_mut() {
                *channel = self.volume(val);
            }
        }
    }
}

pub fn sine_synth (frequency: Frequency) -> Synth {
    Synth::new(Oscillator::new(Sine), frequency, 0.15)
}
<commit_msg>Simplify<commit_after>use dsp::{Node, Settings};
use waveforms::{Waveform, Sine};

pub type Frequency = f64;
pub type Phase = f64;
pub type Output = f32; // f64 not supported yet
pub type Amp = f64;
pub type Volume = f64;

pub struct Oscillator {
  phase: Phase,
  waveform: Sine // TODO: any waveform
}

pub struct Synth {
  oscillator: Oscillator,
  frequency: Frequency,
  volume: Amp,
}

impl Oscillator {
    pub fn new(waveform: Sine) -> Oscillator {
        Oscillator {
            phase: 0.0,
            waveform: waveform
        }
    }

    pub fn amp_at_next_phase(&mut self, frequency: Frequency, settings: Settings) -> Amp {
        let val = self.waveform.amp_at_phase(self.phase);
        self.phase += frequency / settings.sample_hz as f64;
        val
    }
}

impl Synth {
    pub fn new(oscillator: Oscillator, frequency: Frequency, volume: Amp) -> Synth {
        Synth {
            oscillator: oscillator, // TODO: multiple voices
            frequency: frequency,  // TODO: dynamic frequency
            volume: volume  // TODO: dynamic volume
        }
    }

    fn volume(&self, amp: Amp) -> Volume {
        amp * self.volume
    }
}

impl Node<Output> for Synth {
    /// Here we'll override the audio_requested method and generate a sound.
    fn audio_requested(&mut self, buffer: &mut [Output], settings: Settings) {
        for frame in buffer.chunks_mut(settings.channels as usize) {
            let val = self.oscillator.amp_at_next_phase(self.frequency, settings);
            for channel in frame.iter_mut() {
                *channel = self.volume(val) as f32;
            }
        }
    }
}

pub fn sine_synth (frequency: Frequency) -> Synth {
    Synth::new(Oscillator::new(Sine), frequency, 0.15)
}
<|endoftext|>


<commit_before>use config::{parse_config, Benchmark, Config};
use Commit;
use git2;
use Push;
use repo::Workspace;

use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus, Output};
use std::str;

/// `(val, percentage_change)`
#[derive(Debug, Clone)]
pub enum BenchmarkResult<T> {
    Improvement(T, f64),
    Regression(T, f64),
    Neutral(T, f64),
}

#[derive(Debug, Clone)]
pub struct TastingResult {
    pub branch: Option<String>,
    pub commit: Commit,
    pub build: bool,
    pub test: bool,
    pub bench: bool,
    pub results: Option<Vec<(Benchmark, ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>,
}

fn run_benchmark(workdir: &str, cfg: &Config, bench: &Benchmark, timeout: Option<u64>) -> Output {
    let mut cmd = if cfg.version.is_none() || cfg.version.unwrap() < 2 {
        // older taster configs assume an implied "cargo" prefix on each benchmark command
        match timeout {
            None => {
                let mut cmd = Command::new("cargo");
                cmd.arg(&bench.cmd);
                cmd
            }
            Some(timeout) => {
                let mut cmd = Command::new("timeout");
                cmd.arg("-k")
                    .arg(&format!("{}s", timeout + 30))
                    .arg(&format!("{}s", timeout))
                    .arg("cargo")
                    .arg(&bench.cmd);
                cmd
            }
        }
    } else {
        // from taster config version 2, we no longer assume an implicit "cargo" prefix on
        // benchmark commands
        match timeout {
            None => Command::new(&bench.cmd),
            Some(timeout) => {
                let mut cmd = Command::new("timeout");
                cmd.arg("-k")
                    .arg(&format!("{}s", timeout + 30))
                    .arg(&format!("{}s", timeout))
                    .arg(&bench.cmd);
                cmd
            }
        }
    };

    cmd.current_dir(workdir)
        .env("RUST_BACKTRACE", "1")
        .args(bench.args.as_slice());

    cmd.output()
        .expect(&format!("Failed to execute benchmark '{}'!", bench.name))
}

fn write_output(output: &Output, commit_id: git2::Oid, name: &str) {
    use std::fs::File;
    use std::io::Write;

    let mut stdout_file =
        File::create(&format!("{}-{}-stdout.log", commit_id, name)).expect(&format!(
            "Failed to create stdout log file for '{}' at commit '{}'.",
            name, commit_id
        ));
    stdout_file
        .write_all(output.stdout.as_slice())
        .expect("Failed to write output to stdout log file!");
    let mut stderr_file =
        File::create(&format!("{}-{}-stderr.log", commit_id, name)).expect(&format!(
            "Failed to create stderr log file for '{}' at commit '{}'.",
            name, commit_id
        ));
    stderr_file
        .write_all(output.stderr.as_slice())
        .expect("Failed to write output to stderr log file!");
}

fn benchmark(
    workdir: &str,
    cfg: &Config,
    bench: &Benchmark,
    commit_id: git2::Oid,
    previous_result: Option<&HashMap<String, BenchmarkResult<f64>>>,
    timeout: Option<u64>,
) -> (ExitStatus, HashMap<String, BenchmarkResult<f64>>) {
    // Run the benchmark and collect its output
    let output = run_benchmark(workdir, cfg, bench, timeout);
    write_output(&output, commit_id, &bench.name);

    let lines = str::from_utf8(output.stdout.as_slice())
        .unwrap()
        .lines()
        .chain(str::from_utf8(output.stderr.as_slice()).unwrap().lines());
    let mut res = HashMap::new();

    // Don't try parsing the output if we didn't succeed
    if !output.status.success() {
        return (output.status, res);
    }

    // Success, so let's look for the results
    for l in lines {
        for (i, regex) in bench.result_expr.iter().enumerate() {
            for cap in regex.captures_iter(l) {
                let (metric, value) = if cap.len() > 2 {
                    (String::from(cap.at(1).unwrap()), cap.at(2))
                } else {
                    (format!("{}", i), cap.at(1))
                };
                let bm_name = format!("{}/{}", bench.name, &metric);
                if let Some(c) = value {
                    use std::str::FromStr;
                    let val = match f64::from_str(&c) {
                        Ok(f) => f,
                        Err(_) => {
                            println!(
                                "failed to parse value '{}' for {} into f64 number, ignoring",
                                c, bm_name
                            );
                            continue;
                        }
                    };
                    let new_result = match previous_result {
                        None => BenchmarkResult::Improvement(val, 0.0),
                        Some(prev_res) => {
                            let old_val = match prev_res.get(&bm_name) {
                                None => val,
                                Some(pv) => match *pv {
                                    BenchmarkResult::Improvement(v, _) => v,
                                    BenchmarkResult::Regression(v, _) => v,
                                    BenchmarkResult::Neutral(v, _) => v,
                                },
                            };
                            let new_result = if bench.lower_is_better {
                                if val >= old_val * (1.0 + bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            } else {
                                if val >= old_val * (1.0 + bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            };
                            new_result
                        }
                    };
                    res.insert(bm_name, new_result);
                }
            }
        }
    }
    (output.status, res)
}

fn build(workdir: &str) -> Output {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("check")
        .arg("--all")
        .arg("--all-targets")
        .env("RUST_BACKTRACE", "1")
        .output()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(
    ws: &Workspace,
    history: &mut HashMap<String, HashMap<String, HashMap<String, BenchmarkResult<f64>>>>,
    push: &Push,
    commit: &Commit,
    def_improvement_threshold: f64,
    def_regression_threshold: f64,
    timeout: Option<u64>,
) -> Result<(Option<Config>, TastingResult), String> {
    println!("Tasting commit {}", commit.id);
    ws.checkout_commit(&commit.id)?;

    let branch = match push.push_ref {
        None => None,
        Some(ref pr) => match pr.rfind("/") {
            None => None,
            Some(i) => Some(String::from(&pr[i + 1..])),
        },
    };

    let do_update = !Path::new(&format!("{}/Cargo.lock", ws.path)).exists();

    let build_success = {
        let update_success = if do_update {
            println!("running 'cargo update'");
            let update_output = update(&ws.path);
            if !update_output.status.success() {
                println!("update failed: output status is {:?}", update_output.status);
            }
            update_output.status.success()
        } else {
            // nothing to do, always succeeds
            true
        };

        let build_output = build(&ws.path);
        write_output(&build_output, commit.id, "build");
        if !build_output.status.success() {
            println!("build failed: output status is {:?}", build_output.status);
        }

        update_success && build_output.status.success()
    };

    let test_output = test(&ws.path, timeout);
    write_output(&test_output, commit.id, "test");

    if !test_output.status.success() {
        println!("tests failed: output status is {:?}", test_output.status);
    }

    let cfg = match parse_config(
        Path::new(&format!("{}/taster.toml", ws.path)),
        def_improvement_threshold,
        def_regression_threshold,
    ) {
        Ok(c) => c,
        Err(e) => match e.kind() {
            io::ErrorKind::NotFound => {
                println!(
                    "Skipping commit {} which doesn't have a Taster config.",
                    commit.id
                );
                return Ok((
                    None,
                    TastingResult {
                        branch: branch,
                        commit: commit.clone(),
                        build: build_success,
                        test: test_output.status.success(),
                        bench: false,
                        results: None,
                    },
                ));
            }
            io::ErrorKind::InvalidInput => {
                println!(
                    "Skipping commit {} which has an invalid Taster config.",
                    commit.id
                );
                return Ok((
                    None,
                    TastingResult {
                        branch: branch,
                        commit: commit.clone(),
                        build: build_success,
                        test: test_output.status.success(),
                        bench: false,
                        results: None,
                    },
                ));
            }
            _ => unimplemented!(),
        },
    };

    let bench_results =
        match branch {
            Some(ref branch) => {
                let branch_history = history.entry(branch.clone()).or_insert(HashMap::new());
                cfg.benchmarks
                .iter()
                .map(|b| {
                    let (status, res) =
                        benchmark(&ws.path, &cfg, b, commit.id, branch_history.get(&b.name),
                                  timeout);
                    branch_history.insert(b.name.clone(), res.clone());
                    (b.clone(), status, res)
                })
                .collect::<Vec<(Benchmark, ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
            }
            None => cfg.benchmarks
                .iter()
                .map(|b| {
                    let (status, res) = benchmark(&ws.path, &cfg, b, commit.id, None, timeout);
                    (b.clone(), status, res)
                })
                .collect::<Vec<(Benchmark, ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>(),
        };
    let bench_success = bench_results.iter().all(|x| x.1.success());

    Ok((
        Some(cfg),
        TastingResult {
            branch: branch,
            commit: commit.clone(),
            build: build_success,
            test: test_output.status.success(),
            bench: bench_success,
            results: Some(bench_results),
        },
    ))
}

fn test(workdir: &str, timeout: Option<u64>) -> Output {
    let mut cmd = match timeout {
        None => {
            let mut cmd = Command::new("cargo");
            cmd.arg("test");
            cmd
        }
        Some(timeout) => {
            let mut cmd = Command::new("timeout");
            cmd.arg("-k")
                .arg(&format!("{}s", timeout + 30))
                .arg(&format!("{}s", timeout))
                .arg("cargo")
                .arg("test");
            cmd
        }
    };
    cmd.current_dir(workdir)
        .env("RUST_BACKTRACE", "1")
        .env("RUST_TEST_THREADS", "1")
        .output()
        .expect("Failed to execute 'cargo test'!")
}

fn update(workdir: &str) -> Output {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .output()
        .expect("Failed to execute 'cargo update'!")
}
<commit_msg>Use "cargo test --all"<commit_after>use config::{parse_config, Benchmark, Config};
use Commit;
use git2;
use Push;
use repo::Workspace;

use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus, Output};
use std::str;

/// `(val, percentage_change)`
#[derive(Debug, Clone)]
pub enum BenchmarkResult<T> {
    Improvement(T, f64),
    Regression(T, f64),
    Neutral(T, f64),
}

#[derive(Debug, Clone)]
pub struct TastingResult {
    pub branch: Option<String>,
    pub commit: Commit,
    pub build: bool,
    pub test: bool,
    pub bench: bool,
    pub results: Option<Vec<(Benchmark, ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>,
}

fn run_benchmark(workdir: &str, cfg: &Config, bench: &Benchmark, timeout: Option<u64>) -> Output {
    let mut cmd = if cfg.version.is_none() || cfg.version.unwrap() < 2 {
        // older taster configs assume an implied "cargo" prefix on each benchmark command
        match timeout {
            None => {
                let mut cmd = Command::new("cargo");
                cmd.arg(&bench.cmd);
                cmd
            }
            Some(timeout) => {
                let mut cmd = Command::new("timeout");
                cmd.arg("-k")
                    .arg(&format!("{}s", timeout + 30))
                    .arg(&format!("{}s", timeout))
                    .arg("cargo")
                    .arg(&bench.cmd);
                cmd
            }
        }
    } else {
        // from taster config version 2, we no longer assume an implicit "cargo" prefix on
        // benchmark commands
        match timeout {
            None => Command::new(&bench.cmd),
            Some(timeout) => {
                let mut cmd = Command::new("timeout");
                cmd.arg("-k")
                    .arg(&format!("{}s", timeout + 30))
                    .arg(&format!("{}s", timeout))
                    .arg(&bench.cmd);
                cmd
            }
        }
    };

    cmd.current_dir(workdir)
        .env("RUST_BACKTRACE", "1")
        .args(bench.args.as_slice());

    cmd.output()
        .expect(&format!("Failed to execute benchmark '{}'!", bench.name))
}

fn write_output(output: &Output, commit_id: git2::Oid, name: &str) {
    use std::fs::File;
    use std::io::Write;

    let mut stdout_file =
        File::create(&format!("{}-{}-stdout.log", commit_id, name)).expect(&format!(
            "Failed to create stdout log file for '{}' at commit '{}'.",
            name, commit_id
        ));
    stdout_file
        .write_all(output.stdout.as_slice())
        .expect("Failed to write output to stdout log file!");
    let mut stderr_file =
        File::create(&format!("{}-{}-stderr.log", commit_id, name)).expect(&format!(
            "Failed to create stderr log file for '{}' at commit '{}'.",
            name, commit_id
        ));
    stderr_file
        .write_all(output.stderr.as_slice())
        .expect("Failed to write output to stderr log file!");
}

fn benchmark(
    workdir: &str,
    cfg: &Config,
    bench: &Benchmark,
    commit_id: git2::Oid,
    previous_result: Option<&HashMap<String, BenchmarkResult<f64>>>,
    timeout: Option<u64>,
) -> (ExitStatus, HashMap<String, BenchmarkResult<f64>>) {
    // Run the benchmark and collect its output
    let output = run_benchmark(workdir, cfg, bench, timeout);
    write_output(&output, commit_id, &bench.name);

    let lines = str::from_utf8(output.stdout.as_slice())
        .unwrap()
        .lines()
        .chain(str::from_utf8(output.stderr.as_slice()).unwrap().lines());
    let mut res = HashMap::new();

    // Don't try parsing the output if we didn't succeed
    if !output.status.success() {
        return (output.status, res);
    }

    // Success, so let's look for the results
    for l in lines {
        for (i, regex) in bench.result_expr.iter().enumerate() {
            for cap in regex.captures_iter(l) {
                let (metric, value) = if cap.len() > 2 {
                    (String::from(cap.at(1).unwrap()), cap.at(2))
                } else {
                    (format!("{}", i), cap.at(1))
                };
                let bm_name = format!("{}/{}", bench.name, &metric);
                if let Some(c) = value {
                    use std::str::FromStr;
                    let val = match f64::from_str(&c) {
                        Ok(f) => f,
                        Err(_) => {
                            println!(
                                "failed to parse value '{}' for {} into f64 number, ignoring",
                                c, bm_name
                            );
                            continue;
                        }
                    };
                    let new_result = match previous_result {
                        None => BenchmarkResult::Improvement(val, 0.0),
                        Some(prev_res) => {
                            let old_val = match prev_res.get(&bm_name) {
                                None => val,
                                Some(pv) => match *pv {
                                    BenchmarkResult::Improvement(v, _) => v,
                                    BenchmarkResult::Regression(v, _) => v,
                                    BenchmarkResult::Neutral(v, _) => v,
                                },
                            };
                            let new_result = if bench.lower_is_better {
                                if val >= old_val * (1.0 + bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            } else {
                                if val >= old_val * (1.0 + bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            };
                            new_result
                        }
                    };
                    res.insert(bm_name, new_result);
                }
            }
        }
    }
    (output.status, res)
}

fn build(workdir: &str) -> Output {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("check")
        .arg("--all")
        .arg("--all-targets")
        .env("RUST_BACKTRACE", "1")
        .output()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(
    ws: &Workspace,
    history: &mut HashMap<String, HashMap<String, HashMap<String, BenchmarkResult<f64>>>>,
    push: &Push,
    commit: &Commit,
    def_improvement_threshold: f64,
    def_regression_threshold: f64,
    timeout: Option<u64>,
) -> Result<(Option<Config>, TastingResult), String> {
    println!("Tasting commit {}", commit.id);
    ws.checkout_commit(&commit.id)?;

    let branch = match push.push_ref {
        None => None,
        Some(ref pr) => match pr.rfind("/") {
            None => None,
            Some(i) => Some(String::from(&pr[i + 1..])),
        },
    };

    let do_update = !Path::new(&format!("{}/Cargo.lock", ws.path)).exists();

    let build_success = {
        let update_success = if do_update {
            println!("running 'cargo update'");
            let update_output = update(&ws.path);
            if !update_output.status.success() {
                println!("update failed: output status is {:?}", update_output.status);
            }
            update_output.status.success()
        } else {
            // nothing to do, always succeeds
            true
        };

        let build_output = build(&ws.path);
        write_output(&build_output, commit.id, "build");
        if !build_output.status.success() {
            println!("build failed: output status is {:?}", build_output.status);
        }

        update_success && build_output.status.success()
    };

    let test_output = test(&ws.path, timeout);
    write_output(&test_output, commit.id, "test");

    if !test_output.status.success() {
        println!("tests failed: output status is {:?}", test_output.status);
    }

    let cfg = match parse_config(
        Path::new(&format!("{}/taster.toml", ws.path)),
        def_improvement_threshold,
        def_regression_threshold,
    ) {
        Ok(c) => c,
        Err(e) => match e.kind() {
            io::ErrorKind::NotFound => {
                println!(
                    "Skipping commit {} which doesn't have a Taster config.",
                    commit.id
                );
                return Ok((
                    None,
                    TastingResult {
                        branch: branch,
                        commit: commit.clone(),
                        build: build_success,
                        test: test_output.status.success(),
                        bench: false,
                        results: None,
                    },
                ));
            }
            io::ErrorKind::InvalidInput => {
                println!(
                    "Skipping commit {} which has an invalid Taster config.",
                    commit.id
                );
                return Ok((
                    None,
                    TastingResult {
                        branch: branch,
                        commit: commit.clone(),
                        build: build_success,
                        test: test_output.status.success(),
                        bench: false,
                        results: None,
                    },
                ));
            }
            _ => unimplemented!(),
        },
    };

    let bench_results =
        match branch {
            Some(ref branch) => {
                let branch_history = history.entry(branch.clone()).or_insert(HashMap::new());
                cfg.benchmarks
                .iter()
                .map(|b| {
                    let (status, res) =
                        benchmark(&ws.path, &cfg, b, commit.id, branch_history.get(&b.name),
                                  timeout);
                    branch_history.insert(b.name.clone(), res.clone());
                    (b.clone(), status, res)
                })
                .collect::<Vec<(Benchmark, ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
            }
            None => cfg.benchmarks
                .iter()
                .map(|b| {
                    let (status, res) = benchmark(&ws.path, &cfg, b, commit.id, None, timeout);
                    (b.clone(), status, res)
                })
                .collect::<Vec<(Benchmark, ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>(),
        };
    let bench_success = bench_results.iter().all(|x| x.1.success());

    Ok((
        Some(cfg),
        TastingResult {
            branch: branch,
            commit: commit.clone(),
            build: build_success,
            test: test_output.status.success(),
            bench: bench_success,
            results: Some(bench_results),
        },
    ))
}

fn test(workdir: &str, timeout: Option<u64>) -> Output {
    let mut cmd = match timeout {
        None => {
            let mut cmd = Command::new("cargo");
            cmd.arg("test").arg("--all");
            cmd
        }
        Some(timeout) => {
            let mut cmd = Command::new("timeout");
            cmd.arg("-k")
                .arg(&format!("{}s", timeout + 30))
                .arg(&format!("{}s", timeout))
                .arg("cargo")
                .arg("test")
                .arg("--all");
            cmd
        }
    };
    cmd.current_dir(workdir)
        .env("RUST_BACKTRACE", "1")
        .env("RUST_TEST_THREADS", "1")
        .output()
        .expect("Failed to execute 'cargo test'!")
}

fn update(workdir: &str) -> Output {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .output()
        .expect("Failed to execute 'cargo update'!")
}
<|endoftext|>