<commit_before>//HACK: This is how we're splitting the maze example into multiple files since
// cargo doesn't properly support examples with multiple files yet
#[path = "maze/wall.rs"]
mod wall;
#[path = "maze/cell.rs"]
mod cell;
#[path = "maze/maze.rs"]
mod maze;
#[path = "maze/grid.rs"]
mod grid;

extern crate turtle;

use turtle::Turtle;

pub use maze::Maze;
use grid::{GridIter};
use cell::Cell;

// Dimensions of the maze in turtle steps (pixels)
const WIDTH: f64 = 600.0; // px
const HEIGHT: f64 = 600.0; // px

fn main() {
    let maze = Maze::generate();

    let mut turtle = Turtle::new();
    turtle.set_speed(9);
    turtle.set_background_color("#A5D6A7");
    turtle.set_pen_color("#03A9F4");
    turtle.set_pen_size(5.0);

    // Get to the top left corner
    turtle.pen_up();
    turtle.forward(HEIGHT / 2.0);
    turtle.right(90.0);
    turtle.backward(WIDTH / 2.0);
    turtle.pen_down();

    let cell_width = WIDTH/(maze.row_size() as f64);
    let cell_height = WIDTH/(maze.row_size() as f64);

    // Draw rows
    draw_rows(
        &mut turtle,
        cell_width,
        cell_height,
        maze.first_row().map(|cell| cell.north.is_closed()),
        maze.rows(),
        |cell| cell.south.is_closed(),
        false,
    );

    // Draw columns
    turtle.left(90.0);
    draw_rows(
        &mut turtle,
        cell_height,
        cell_width,
        maze.last_col().map(|cell| cell.west.is_closed()),
        maze.rows(),
        |cell| cell.east.is_closed(),
        true,
    );
}

fn draw_rows<R: Iterator<Item=bool>, F: Fn(&Cell) -> bool>(
    turtle: &mut Turtle,
    // size of each cell in the row
    cell_size: f64,
    // gap between rows
    cell_gap: f64,
    first_row: R,
    rows: GridIter,
    row_walls: F,
    rotate_left: bool,
) {
    draw_row(turtle, cell_size, first_row);

    // Direction of rotation for all turns
    let rotation = if rotate_left { -1.0 } else { 1.0 };
    for (i, row) in rows.enumerate() {
        turtle.pen_up();

        // Direction of rotation for these turns
        let direction = rotation * if i % 2 == 0 {
            1.0
        } else {
            -1.0
        };
        turtle.right(direction * 90.0);
        turtle.forward(cell_gap);
        turtle.right(direction * 90.0);
        turtle.pen_down();

        let walls = row.map(&row_walls);

        // Every second row needs to be reversed so the turtle can zig-zag back and
        // forth instead of wasting too much time moving all the way to the left of
        // each row
        if i % 2 == 0 {
            draw_row(turtle, cell_size, walls);
        } else {
            draw_row(turtle, cell_size, walls.rev());
        }
    }
}

fn draw_row<I: Iterator<Item=bool>>(turtle: &mut Turtle, wall_size: f64, walls: I) {
    for should_draw in walls {
        if !should_draw {
            turtle.pen_up();
        }
        turtle.forward(wall_size);
        turtle.pen_down();
    }
}
<commit_msg>Fixing bugs in drawing code<commit_after>//HACK: This is how we're splitting the maze example into multiple files since
// cargo doesn't properly support examples with multiple files yet
#[path = "maze/wall.rs"]
mod wall;
#[path = "maze/cell.rs"]
mod cell;
#[path = "maze/maze.rs"]
mod maze;
#[path = "maze/grid.rs"]
mod grid;

extern crate turtle;

use turtle::Turtle;

pub use maze::Maze;
use grid::{GridCellIter};
use cell::Cell;

// Dimensions of the maze in turtle steps (pixels)
const WIDTH: f64 = 600.0; // px
const HEIGHT: f64 = 600.0; // px

fn main() {
    let maze = Maze::generate();

    let mut turtle = Turtle::new();
    turtle.set_speed(9);
    turtle.set_background_color("#A5D6A7");
    turtle.set_pen_color("#03A9F4");
    turtle.set_pen_size(5.0);

    // Get to the top left corner
    turtle.pen_up();
    turtle.forward(HEIGHT / 2.0);
    turtle.right(90.0);
    turtle.backward(WIDTH / 2.0);
    turtle.pen_down();

    let cell_width = WIDTH/(maze.row_size() as f64);
    let cell_height = HEIGHT/(maze.col_size() as f64);

    // Draw rows
    draw_rows(
        &mut turtle,
        cell_width,
        cell_height,
        maze.first_row().map(|cell| cell.north.is_closed()),
        maze.rows(),
        |cell| cell.south.is_closed(),
        false,
    );

    // Draw columns
    turtle.left(90.0);
    draw_rows(
        &mut turtle,
        cell_height,
        cell_width,
        maze.last_col().map(|cell| cell.west.is_closed()),
        maze.cols().rev(),
        |cell| cell.east.is_closed(),
        true,
    );
}

fn draw_rows<'a, R: Iterator<Item=bool>, G: Iterator<Item=GridCellIter<'a>>, F: Fn(&Cell) -> bool>(
    turtle: &mut Turtle,
    // size of each cell in the row
    cell_size: f64,
    // gap between rows
    cell_gap: f64,
    first_row: R,
    rows: G,
    row_walls: F,
    rotate_left: bool,
) {
    draw_row(turtle, cell_size, first_row);

    // Direction of rotation for all turns
    let rotation = if rotate_left { -1.0 } else { 1.0 };
    for (i, row) in rows.enumerate() {
        turtle.pen_up();

        // Direction of rotation for these turns
        let direction = rotation * if i % 2 == 0 {
            1.0
        } else {
            -1.0
        };
        turtle.right(direction * 90.0);
        turtle.forward(cell_gap);
        turtle.right(direction * 90.0);
        turtle.pen_down();

        let walls = row.map(&row_walls);

        // Every second row needs to be reversed so the turtle can zig-zag back and
        // forth instead of wasting too much time moving all the way to the left of
        // each row
        if i % 2 == 0 {
            draw_row(turtle, cell_size, walls.rev());
        }
        else {
            draw_row(turtle, cell_size, walls);
        }
    }
}

fn draw_row<I: Iterator<Item=bool>>(turtle: &mut Turtle, wall_size: f64, walls: I) {
    for should_draw in walls {
        if !should_draw {
            turtle.pen_up();
        }
        turtle.forward(wall_size);
        turtle.pen_down();
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains traits in script used generically in the rest of Servo.
//! The traits are here instead of in script so that these modules won't have
//! to depend on script.

#[deny(missing_docs)]

extern crate devtools_traits;
extern crate euclid;
extern crate libc;
extern crate msg;
extern crate net_traits;
extern crate util;
extern crate url;

use devtools_traits::DevtoolsControlChan;
use libc::c_void;
use msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData};
use msg::constellation_msg::{LoadData, SubpageId, Key, KeyState, KeyModifiers};
use msg::constellation_msg::{MozBrowserEvent, PipelineExitType};
use msg::compositor_msg::ScriptListener;
use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::ResourceTask;
use net_traits::image_cache_task::ImageCacheTask;
use net_traits::storage_task::StorageTask;
use std::any::Any;
use std::sync::mpsc::{Sender, Receiver};
use url::Url;

use euclid::point::Point2D;
use euclid::rect::Rect;

/// The address of a node. Layout sends these back. They must be validated via
/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
#[allow(raw_pointer_derive)]
#[derive(Copy, Clone)]
pub struct UntrustedNodeAddress(pub *const c_void);
unsafe impl Send for UntrustedNodeAddress {}

/// The initial data associated with a newly-created framed pipeline.
pub struct NewLayoutInfo {
    /// Id of the parent of this new pipeline.
    pub containing_pipeline_id: PipelineId,
    /// Id of the newly-created pipeline.
    pub new_pipeline_id: PipelineId,
    /// Id of the new frame associated with this pipeline.
    pub subpage_id: SubpageId,
    /// Channel for communicating with this new pipeline's layout task.
    /// (This is a LayoutChannel.)
    pub layout_chan: Box<Any+Send>,
    /// Network request data which will be initiated by the script task.
    pub load_data: LoadData,
}

pub trait StylesheetLoadResponder {
    fn respond(self: Box<Self>);
}

/// Used to determine if a script has any pending asynchronous activity.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ScriptState {
    DocumentLoaded,
    DocumentLoading,
}

/// Messages sent from the constellation to the script task
pub enum ConstellationControlMsg {
    /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent
    AttachLayout(NewLayoutInfo),
    /// Window resized.  Sends a DOM event eventually, but first we combine events.
    Resize(PipelineId, WindowSizeData),
    /// Notifies script that window has been resized but to not take immediate action.
    ResizeInactive(PipelineId, WindowSizeData),
    /// Notifies the script that a pipeline should be closed.
    ExitPipeline(PipelineId, PipelineExitType),
    /// Sends a DOM event.
    SendEvent(PipelineId, CompositorEvent),
    /// Notifies script that reflow is finished.
    ReflowComplete(PipelineId, u32),
    /// Notifies script of the viewport.
    Viewport(PipelineId, Rect<f32>),
    /// Requests that the script task immediately send the constellation the title of a pipeline.
    GetTitle(PipelineId),
    /// Notifies script task to suspend all its timers
    Freeze(PipelineId),
    /// Notifies script task to resume all its timers
    Thaw(PipelineId),
    /// Notifies script task that a url should be loaded in this iframe.
    Navigate(PipelineId, SubpageId, LoadData),
    /// Requests the script task forward a mozbrowser event to an iframe it owns
    MozBrowserEvent(PipelineId, SubpageId, MozBrowserEvent),
    /// Updates the current subpage id of a given iframe
    UpdateSubpageId(PipelineId, SubpageId, SubpageId),
    /// Set an iframe to be focused. Used when an element in an iframe gains focus.
    FocusIFrame(PipelineId, SubpageId),
    // Passes a webdriver command to the script task for execution
    WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
    /// Notifies script task that all animations are done
    TickAllAnimations(PipelineId),
    /// Notifies script that a stylesheet has finished loading.
    StylesheetLoadComplete(PipelineId, Url, Box<StylesheetLoadResponder+Send>),
    /// Get the current state of the script task for a given pipeline.
    GetCurrentState(Sender<ScriptState>, PipelineId),
}

/// The mouse button involved in the event.
#[derive(Clone, Debug)]
pub enum MouseButton {
    /// The left mouse button.
    Left,
    /// The middle mouse button.
    Middle,
    /// The right mouse button.
    Right,
}

/// Events from the compositor that the script task needs to know about
pub enum CompositorEvent {
    /// The window was resized.
    ResizeEvent(WindowSizeData),
    /// A point was clicked.
    ClickEvent(MouseButton, Point2D<f32>),
    /// A mouse button was pressed on a point.
    MouseDownEvent(MouseButton, Point2D<f32>),
    /// A mouse button was released on a point.
    MouseUpEvent(MouseButton, Point2D<f32>),
    /// The mouse was moved over a point.
    MouseMoveEvent(Point2D<f32>),
    /// A key was pressed.
    KeyEvent(Key, KeyState, KeyModifiers),
}

/// An opaque wrapper around script<->layout channels to avoid leaking message types into
/// crates that don't need to know about them.
pub struct OpaqueScriptLayoutChannel(pub (Box<Any+Send>, Box<Any+Send>));

/// Encapsulates external communication with the script task.
#[derive(Clone)]
pub struct ScriptControlChan(pub Sender<ConstellationControlMsg>);

pub trait ScriptTaskFactory {
    fn create<C>(_phantom: Option<&mut Self>,
                 id: PipelineId,
                 parent_info: Option<(PipelineId, SubpageId)>,
                 compositor: C,
                 layout_chan: &OpaqueScriptLayoutChannel,
                 control_chan: ScriptControlChan,
                 control_port: Receiver<ConstellationControlMsg>,
                 constellation_msg: ConstellationChan,
                 failure_msg: Failure,
                 resource_task: ResourceTask,
                 storage_task: StorageTask,
                 image_cache_task: ImageCacheTask,
                 devtools_chan: Option<DevtoolsControlChan>,
                 window_size: Option<WindowSizeData>,
                 load_data: LoadData)
                 where C: ScriptListener + Send;
    fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;
    fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel)
                            -> Box<Any+Send>;
}
<commit_msg>Auto merge of #6575 - Ms2ger:script-traits-docs, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains traits in script used generically in the rest of Servo.
//! The traits are here instead of in script so that these modules won't have
//! to depend on script.

#![deny(missing_docs)]

extern crate devtools_traits;
extern crate euclid;
extern crate libc;
extern crate msg;
extern crate net_traits;
extern crate util;
extern crate url;

use devtools_traits::DevtoolsControlChan;
use libc::c_void;
use msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData};
use msg::constellation_msg::{LoadData, SubpageId, Key, KeyState, KeyModifiers};
use msg::constellation_msg::{MozBrowserEvent, PipelineExitType};
use msg::compositor_msg::ScriptListener;
use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::ResourceTask;
use net_traits::image_cache_task::ImageCacheTask;
use net_traits::storage_task::StorageTask;
use std::any::Any;
use std::sync::mpsc::{Sender, Receiver};
use url::Url;

use euclid::point::Point2D;
use euclid::rect::Rect;

/// The address of a node. Layout sends these back. They must be validated via
/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
#[allow(raw_pointer_derive)]
#[derive(Copy, Clone)]
pub struct UntrustedNodeAddress(pub *const c_void);
unsafe impl Send for UntrustedNodeAddress {}

/// The initial data associated with a newly-created framed pipeline.
pub struct NewLayoutInfo {
    /// Id of the parent of this new pipeline.
    pub containing_pipeline_id: PipelineId,
    /// Id of the newly-created pipeline.
    pub new_pipeline_id: PipelineId,
    /// Id of the new frame associated with this pipeline.
    pub subpage_id: SubpageId,
    /// Channel for communicating with this new pipeline's layout task.
    /// (This is a LayoutChannel.)
    pub layout_chan: Box<Any+Send>,
    /// Network request data which will be initiated by the script task.
    pub load_data: LoadData,
}

/// `StylesheetLoadResponder` is used to notify a responder that a style sheet
/// has loaded.
pub trait StylesheetLoadResponder {
    /// Respond to a loaded style sheet.
    fn respond(self: Box<Self>);
}

/// Used to determine if a script has any pending asynchronous activity.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ScriptState {
    /// The document has been loaded.
    DocumentLoaded,
    /// The document is still loading.
    DocumentLoading,
}

/// Messages sent from the constellation to the script task
pub enum ConstellationControlMsg {
    /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent
    AttachLayout(NewLayoutInfo),
    /// Window resized.  Sends a DOM event eventually, but first we combine events.
    Resize(PipelineId, WindowSizeData),
    /// Notifies script that window has been resized but to not take immediate action.
    ResizeInactive(PipelineId, WindowSizeData),
    /// Notifies the script that a pipeline should be closed.
    ExitPipeline(PipelineId, PipelineExitType),
    /// Sends a DOM event.
    SendEvent(PipelineId, CompositorEvent),
    /// Notifies script that reflow is finished.
    ReflowComplete(PipelineId, u32),
    /// Notifies script of the viewport.
    Viewport(PipelineId, Rect<f32>),
    /// Requests that the script task immediately send the constellation the title of a pipeline.
    GetTitle(PipelineId),
    /// Notifies script task to suspend all its timers
    Freeze(PipelineId),
    /// Notifies script task to resume all its timers
    Thaw(PipelineId),
    /// Notifies script task that a url should be loaded in this iframe.
    Navigate(PipelineId, SubpageId, LoadData),
    /// Requests the script task forward a mozbrowser event to an iframe it owns
    MozBrowserEvent(PipelineId, SubpageId, MozBrowserEvent),
    /// Updates the current subpage id of a given iframe
    UpdateSubpageId(PipelineId, SubpageId, SubpageId),
    /// Set an iframe to be focused. Used when an element in an iframe gains focus.
    FocusIFrame(PipelineId, SubpageId),
    /// Passes a webdriver command to the script task for execution
    WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
    /// Notifies script task that all animations are done
    TickAllAnimations(PipelineId),
    /// Notifies script that a stylesheet has finished loading.
    StylesheetLoadComplete(PipelineId, Url, Box<StylesheetLoadResponder+Send>),
    /// Get the current state of the script task for a given pipeline.
    GetCurrentState(Sender<ScriptState>, PipelineId),
}

/// The mouse button involved in the event.
#[derive(Clone, Debug)]
pub enum MouseButton {
    /// The left mouse button.
    Left,
    /// The middle mouse button.
    Middle,
    /// The right mouse button.
    Right,
}

/// Events from the compositor that the script task needs to know about
pub enum CompositorEvent {
    /// The window was resized.
    ResizeEvent(WindowSizeData),
    /// A point was clicked.
    ClickEvent(MouseButton, Point2D<f32>),
    /// A mouse button was pressed on a point.
    MouseDownEvent(MouseButton, Point2D<f32>),
    /// A mouse button was released on a point.
    MouseUpEvent(MouseButton, Point2D<f32>),
    /// The mouse was moved over a point.
    MouseMoveEvent(Point2D<f32>),
    /// A key was pressed.
    KeyEvent(Key, KeyState, KeyModifiers),
}

/// An opaque wrapper around script<->layout channels to avoid leaking message types into
/// crates that don't need to know about them.
pub struct OpaqueScriptLayoutChannel(pub (Box<Any+Send>, Box<Any+Send>));

/// Encapsulates external communication with the script task.
#[derive(Clone)]
pub struct ScriptControlChan(pub Sender<ConstellationControlMsg>);

/// This trait allows creating a `ScriptTask` without depending on the `script`
/// crate.
pub trait ScriptTaskFactory {
    /// Create a `ScriptTask`.
    fn create<C>(_phantom: Option<&mut Self>,
                 id: PipelineId,
                 parent_info: Option<(PipelineId, SubpageId)>,
                 compositor: C,
                 layout_chan: &OpaqueScriptLayoutChannel,
                 control_chan: ScriptControlChan,
                 control_port: Receiver<ConstellationControlMsg>,
                 constellation_msg: ConstellationChan,
                 failure_msg: Failure,
                 resource_task: ResourceTask,
                 storage_task: StorageTask,
                 image_cache_task: ImageCacheTask,
                 devtools_chan: Option<DevtoolsControlChan>,
                 window_size: Option<WindowSizeData>,
                 load_data: LoadData)
                 where C: ScriptListener + Send;
    /// Create a script -> layout channel (`Sender`, `Receiver` pair).
    fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;
    /// Clone the `Sender` in `pair`.
    fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel)
                            -> Box<Any+Send>;
}
<|endoftext|>


<commit_before>
use std::cmp;
use std::mem;
use std::iter::*;
use image_data::*;
use rayon::prelude::*;

fn minf(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn maxf(a: f32, b: f32) -> f32 { if a > b { a } else { b } }
fn saturate(a: f32) -> f32 { maxf(minf(a, 1.0), 0.0) }
fn fnorm(a: u8) -> f32 { a as f32 / 255.0 }
fn bnorm(a: f32) -> u8 { (saturate(a) * 255.0) as u8 }

fn pixel_dist(a: &Rgba, b: &Rgba) -> u32 {
	fn dist_one(a: u8, b: u8) -> u32 {
		let x = a as i32 - b as i32;
		(x * x) as u32
	}
	dist_one(a[0], b[0]) +
	dist_one(a[1], b[1]) +
	dist_one(a[2], b[2])
}

fn block_dist(table: &[Rgba; 4], pixels: &Block) -> u32 {
	let mut sum = 0u32;
	let mut max = 0u32;
	for pix in pixels.into_iter().rev() {
		let e = (0..4).into_iter().map(|x| pixel_dist(&table[x], pix)).min().unwrap();
		sum += e;
		max = cmp::max(max, e);
	}
	sum + max * 16
}

fn build_entry(ends: &(Rgba, Rgba), index: usize) -> Rgba {
	let b = if index < 2 { index * 3 } else { index - 1 } as u16;
	let a = 3 - b;
	[((b * ends.0[0] as u16 + a * ends.1[0] as u16) / 3) as u8,
	 ((b * ends.0[1] as u16 + a * ends.1[1] as u16) / 3) as u8,
	 ((b * ends.0[2] as u16 + a * ends.1[2] as u16) / 3) as u8,
	 ((b * ends.0[3] as u16 + a * ends.1[3] as u16) / 3) as u8]
}

fn build_table(ends: &(Rgba, Rgba)) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = build_entry(ends, i);
	}
	interps
}

fn minmax(pixels: &Block) -> (Rgba, Rgba) {
	let mut min = [u8::max_value(); 4];
	let mut max = [0u8; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
		}
	}
	(min, max)
}

fn quantize_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	fn quantize(end: &Rgba) -> Rgba {
		[end[0] & 0xF8, end[1] & 0xFC, end[2] & 0xF8, end[3] & 0x80]
	}
	(quantize(&ends.0), quantize(&ends.1))
}

fn extrapolate_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	let mut a = ends.0;
	let mut b = ends.1;
	for i in 0..3 {
		let f = (fnorm(ends.0[i]) - fnorm(ends.1[i])) * 1.5;
		a[i] = bnorm(fnorm(ends.0[i]) - f);
		b[i] = bnorm(fnorm(ends.1[i]) + f);
	}
	(a, b)
}

fn endpoints_sorted(ends: &(Rgba, Rgba)) -> bool {
	encode_endpoint(&ends.0) < encode_endpoint(&ends.1)
}

fn sort_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	if endpoints_sorted(ends) {
		*ends
	} else {
		(ends.1, ends.0)
	}
}

fn sort_quantize_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	quantize_endpoints(&sort_endpoints(ends))
}

fn build_endpoints(pixels: &Block, quality: u8) -> Vec<(Rgba, Rgba)> {
	let (min, max) = quantize_endpoints(&minmax(pixels));

	let mut out = Vec::new();
	out.push((min, max));

	if quality == 255 {
		for i in 0..3 {
			for o in 0..(max[i] - min[i]) {
				let mut min = min;
				min[i] += o;

				for i in 0..3 {
					for o in 0..(max[i] - min[i]) {
						let mut max = max;
						max[i] -= o;
						out.push((min, max));
					}
				}
			}
		}
	} if quality > 0 && min != max {
		for i in 0..(BLOCK_PIXELS - 1) {
			for j in (i + 1)..BLOCK_PIXELS {

				let px = (pixels[i], pixels[j]);
				let qpx = sort_quantize_endpoints(&px);
				out.push(qpx);

				if quality > 1 {
					let extrapolated = sort_quantize_endpoints(&extrapolate_endpoints(&px));
					out.push(extrapolated);

					if quality > 2 {
						let e = (extrapolated.0, max);
						if endpoints_sorted(&e) {
							out.push(e);
						}

						let e = (min, extrapolated.1);
						if endpoints_sorted(&e) {
							out.push(e);
						}

						if quality > 3 {
							out.push(sort_endpoints(&(extrapolated.0, qpx.1)));
							out.push(sort_endpoints(&(qpx.0, extrapolated.1)));
						}
					}
				}

			}
		}
	}

	/*for endpoints in &out {
		let q = sort_quantize_endpoints(endpoints);
		if q != *endpoints {
		println!("{:?} != {:?}", q, endpoints);
			panic!("wtf!");
		}
	}*/

	out
}

fn encode_endpoint(end: &Rgba) -> u16 {
	((end[0] as u16 >> 3) << 11) |
	((end[1] as u16 >> 2) << 5) |
	(end[2] as u16 >> 3)
}

fn encode_pixels(table: &[Rgba; 4], pixels: &Block) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| pixel_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn encode_block(pixels: &Block, quality: u8) -> (u64, u32) {
	let ((min, max), table) = build_endpoints(pixels, quality).iter()
		.map(|pts| (*pts, build_table(&(pts.0, pts.1))))
		.min_by_key(|tbl| block_dist(&tbl.1, pixels)).unwrap();

	let dist = block_dist(&table, pixels);

	let min = encode_endpoint(&min) as u64;
	let max = encode_endpoint(&max) as u64;

	let encoded = encode_pixels(&table, &pixels) as u64;
	((encoded << 32) | (min << 16) | max, dist)
}

pub fn encode(image: &ImageData, quality: u8) -> Result<Vec<u8>, ()> {
	if image.size.0 % BLOCK_SIZE != 0 || image.size.1 % BLOCK_SIZE != 0 {
		return Err(());
	}

	let blocks = image.blocks().count();

    let mut out = Vec::with_capacity(blocks);
	(0..blocks).into_par_iter()
		.map(|i| {
			encode_block(&image.blocks().nth(i).unwrap(), quality).0
		}).collect_into(&mut out);

	unsafe {
		let mut out: Vec<u8> = mem::transmute(out);
		out.set_len(blocks * 8);
		Ok(out)
	}
}
<commit_msg>Optimized quality=255.<commit_after>
use std::cmp;
use std::mem;
use std::iter::*;
use image_data::*;
use rayon::prelude::*;

fn minf(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn maxf(a: f32, b: f32) -> f32 { if a > b { a } else { b } }
fn saturate(a: f32) -> f32 { maxf(minf(a, 1.0), 0.0) }
fn fnorm(a: u8) -> f32 { a as f32 / 255.0 }
fn bnorm(a: f32) -> u8 { (saturate(a) * 255.0) as u8 }

fn pixel_dist(a: &Rgba, b: &Rgba) -> u32 {
	fn dist_one(a: u8, b: u8) -> u32 {
		let x = a as i32 - b as i32;
		(x * x) as u32
	}
	dist_one(a[0], b[0]) +
	dist_one(a[1], b[1]) +
	dist_one(a[2], b[2])
}

fn block_dist(table: &[Rgba; 4], pixels: &Block) -> u32 {
	let mut sum = 0u32;
	let mut max = 0u32;
	for pix in pixels.into_iter().rev() {
		let e = (0..4).into_iter().map(|x| pixel_dist(&table[x], pix)).min().unwrap();
		sum += e;
		max = cmp::max(max, e);
	}
	sum + max * 16
}

fn build_entry(ends: &(Rgba, Rgba), index: usize) -> Rgba {
	let b = if index < 2 { index * 3 } else { index - 1 } as u16;
	let a = 3 - b;
	[((b * ends.0[0] as u16 + a * ends.1[0] as u16) / 3) as u8,
	 ((b * ends.0[1] as u16 + a * ends.1[1] as u16) / 3) as u8,
	 ((b * ends.0[2] as u16 + a * ends.1[2] as u16) / 3) as u8,
	 ((b * ends.0[3] as u16 + a * ends.1[3] as u16) / 3) as u8]
}

fn build_table(ends: &(Rgba, Rgba)) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = build_entry(ends, i);
	}
	interps
}

fn minmax(pixels: &Block) -> (Rgba, Rgba) {
	let mut min = [u8::max_value(); 4];
	let mut max = [0u8; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
		}
	}
	(min, max)
}

fn quantize_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	fn quantize(end: &Rgba) -> Rgba {
		[end[0] & 0xF8, end[1] & 0xFC, end[2] & 0xF8, end[3] & 0x80]
	}
	(quantize(&ends.0), quantize(&ends.1))
}

fn extrapolate_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	let mut a = ends.0;
	let mut b = ends.1;
	for i in 0..3 {
		let f = (fnorm(ends.0[i]) - fnorm(ends.1[i])) * 1.5;
		a[i] = bnorm(fnorm(ends.0[i]) - f);
		b[i] = bnorm(fnorm(ends.1[i]) + f);
	}
	(a, b)
}

fn endpoints_sorted(ends: &(Rgba, Rgba)) -> bool {
	encode_endpoint(&ends.0) < encode_endpoint(&ends.1)
}

fn sort_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	if endpoints_sorted(ends) {
		*ends
	} else {
		(ends.1, ends.0)
	}
}

fn sort_quantize_endpoints(ends: &(Rgba, Rgba)) -> (Rgba, Rgba) {
	quantize_endpoints(&sort_endpoints(ends))
}

fn build_endpoints(pixels: &Block, quality: u8) -> Vec<(Rgba, Rgba)> {
	let (min, max) = quantize_endpoints(&minmax(pixels));

	let mut out = Vec::new();
	out.push((min, max));

	if quality == 255 {
		for i in 0..3 {
			for o in (min[i] + 1)..max[i] {
				let mut min = min;
				min[i] = o;

				for i in 0..3 {
					for o in (min[i] + 1)..max[i] {
						let mut max = max;
						max[i] = o;

						out.push((min, max));
					}
				}
			}
		}
	} if quality > 0 && min != max {
		for i in 0..(BLOCK_PIXELS - 1) {
			for j in (i + 1)..BLOCK_PIXELS {

				let px = (pixels[i], pixels[j]);
				let qpx = sort_quantize_endpoints(&px);
				out.push(qpx);

				if quality > 1 {
					let extrapolated = sort_quantize_endpoints(&extrapolate_endpoints(&px));
					out.push(extrapolated);

					if quality > 2 {
						let e = (extrapolated.0, max);
						if endpoints_sorted(&e) {
							out.push(e);
						}

						let e = (min, extrapolated.1);
						if endpoints_sorted(&e) {
							out.push(e);
						}

						if quality > 3 {
							out.push(sort_endpoints(&(extrapolated.0, qpx.1)));
							out.push(sort_endpoints(&(qpx.0, extrapolated.1)));
						}
					}
				}

			}
		}
	}

	/*for endpoints in &out {
		let q = sort_quantize_endpoints(endpoints);
		if q != *endpoints {
		println!("{:?} != {:?}", q, endpoints);
			panic!("wtf!");
		}
	}*/

	out
}

fn encode_endpoint(end: &Rgba) -> u16 {
	((end[0] as u16 >> 3) << 11) |
	((end[1] as u16 >> 2) << 5) |
	(end[2] as u16 >> 3)
}

fn encode_pixels(table: &[Rgba; 4], pixels: &Block) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| pixel_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn encode_block(pixels: &Block, quality: u8) -> (u64, u32) {
	let ((min, max), table) = build_endpoints(pixels, quality).iter()
		.map(|pts| (*pts, build_table(&(pts.0, pts.1))))
		.min_by_key(|tbl| block_dist(&tbl.1, pixels)).unwrap();

	let dist = block_dist(&table, pixels);

	let min = encode_endpoint(&min) as u64;
	let max = encode_endpoint(&max) as u64;

	let encoded = encode_pixels(&table, &pixels) as u64;
	((encoded << 32) | (min << 16) | max, dist)
}

pub fn encode(image: &ImageData, quality: u8) -> Result<Vec<u8>, ()> {
	if image.size.0 % BLOCK_SIZE != 0 || image.size.1 % BLOCK_SIZE != 0 {
		return Err(());
	}

	let blocks = image.blocks().count();

    let mut out = Vec::with_capacity(blocks);
	(0..blocks).into_par_iter()
		.map(|i| {
			encode_block(&image.blocks().nth(i).unwrap(), quality).0
		}).collect_into(&mut out);

	unsafe {
		let mut out: Vec<u8> = mem::transmute(out);
		out.set_len(blocks * 8);
		Ok(out)
	}
}
<|endoftext|>


<commit_before>#[link(name = "rusp",
       vers = "0.1",
       author = "Brendan Zabarauskas",
       url = "https://github.com/bjz/rusp")];

#[comment = "A minimal Lisp dialect for Rust."];
#[license = "ASL2"];
#[crate_type = "lib"];

// Inspired by Peter Norvig's [Lis.py](http://norvig.com/lispy.html)

use std::hashmap::*;

pub mod builtins;
pub mod parser;
pub mod pprint;

///
/// Performs a recursive decent parse of the source string.
///
pub fn parse(src: &str) -> Result<~[@Value], parser::ParseFailure> {
    let mut parsed = ~[];
    let mut p = parser::Parser::new(src);

    while !p.is_eof() {
        match p.parse() {
            Ok(v) => parsed.push(@v),
            Err(e) => return Err(e)
        }
    }

    Ok(parsed)
}

/// Symbol identifier
pub type Ident = ~str;

/// Holds a typed value
#[deriving(Eq, Clone)]
pub enum Value {
    Bool(bool),
    Int(int),
    Float(float),
    Str(~str),
    List(~[@Value]),
    Symbol(Ident),
    Fn(~[Ident], @Value),
    Macro(~[Ident], @Value),
    ExternFn(RustFn),
    ExternMacro(RustFn),
}

/// Workaround for `deriving` not working for rust closures
pub struct RustFn(@fn(params: &[@Value], env: @mut Rusp) -> EvalResult);

impl Eq for RustFn {
    fn eq(&self, _: &RustFn) -> bool {
        fail!("Cannot test equality of Rust functions (yet?).");
    }
    fn ne(&self, other: &RustFn) -> bool { !self.eq(other) }
}

impl Clone for RustFn {
    fn clone(&self) -> RustFn {
        // copy the pointer
        *self
    }
}

#[deriving(Eq)]
pub struct Rusp {
    values: HashMap<Ident, @Value>,
    outer: Option<@mut Rusp>,
}

pub type EvalResult = Result<@Value, ~str>;

impl Rusp {
    /// Initializes an empty environment
    pub fn empty() -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: None,
        }
    }

    /// Initializes an environment containing the default built-ins
    pub fn new() -> @mut Rusp {
        @mut Rusp {
            values: builtins::builtins(),
            outer: None
        }
    }

    /// Initializes a new environment with a parent environment
    pub fn new_with_outer(outer: @mut Rusp) -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: Some(outer),
        }
    }

    /// Defines a new identifier/value pair in the environment
    pub fn define(&mut self, ident: Ident, val: @Value) -> EvalResult {
        if self.values.insert(ident.clone(), val)  {
            Ok(@List(~[]))
        } else {
            Err(fmt!("`%s` was already defined in this environment.", ident.to_str()))
        }
    }

    /// Attempts to find a value corresponding to the supplied identifier
    pub fn find(&self, ident: &Ident) -> Option<@Value> {
        match self.values.find(ident) {
            Some(val) => Some(*val),
            None => self.outer.chain(|env| env.find(ident)),
        }
    }

    /// Evaluates a Rusp expression in the environment
    pub fn eval(@mut self, value: @Value) -> EvalResult {
        match *value {
            Symbol(ref id) => {
                match self.find(id) {
                    Some(val) => Ok(val),
                    None => Err(fmt!("The value of `%s` was not defined in this environment",
                                     id.to_str())),
                }
            }
            List([ref vals, .. args]) => {
                do self.eval(*vals).chain |res| {
                    self.eval_call(res, args)
                }
            }
            List([]) => Ok(@List(~[])),
            // everything else evaluates to itself
            _ => Ok(value)
        }
    }

    /// Eval a call using `func`, which should have been previously
    /// evaluated.
    fn eval_call(@mut self, func: &Value, args: &[@Value]) -> EvalResult {
        match *func {
            Fn(ref ids, ref body) => {
                if ids.len() != args.len() {
                    return Err(fmt!("lambda expects %u arguments", ids.len()));
                }
                // stores the argument bindings of the function
                let local = Rusp::new_with_outer(self);
                for vec::each2(*ids, args) |id, val| {
                    match self.eval(*val) {
                        Err(e) => return Err(e),
                        Ok(v) => match local.define(id.clone(), v) {
                            Err(e) => return Err(e),
                            _ => {}
                        }
                    }
                }
                local.eval(*body)
            }
            Macro(ref ids, ref body) => {
                if ids.len() != args.len() {
                    return Err(fmt!("macro expects %u arguments", ids.len()));
                }
                // stores the argument bindings of the macro
                let local = Rusp::new_with_outer(self);
                for vec::each2(*ids, args) |id, val| {
                    match local.define(id.clone(), *val) {
                        Err(e) => return Err(e),
                        _ => {}
                    }
                }
                local.eval(*body)
            }
            ExternFn(ref f) => {
                let mut evaled_args = ~[];
                for args.each |v| {
                    match self.eval(*v) {
                        Err(e) => return Err(e),
                        Ok(evaled) => { evaled_args.push(evaled) }
                    }
                }
                (**f)(evaled_args, self)
            }
            ExternMacro(ref f) => {
                (**f)(args, self)
            }
            _ => Err(~"not a function")
        }
    }
}

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

    #[test]
    fn test_env() {
        let outer = Rusp::new();
        outer.define(~"a", @Int(0));
        outer.define(~"b", @Float(1.0));
        outer.define(~"c", @Str(~"hi"));

        let inner = Rusp::new_with_outer(outer);
        inner.define(~"a", @Int(3));
        inner.define(~"b", @Int(4));

        assert_eq!(outer.find(&~"a"), Some(@Int(0)));
        assert_eq!(outer.find(&~"b"), Some(@Float(1.0)));
        assert_eq!(outer.find(&~"c"), Some(@Str(~"hi")));
        assert_eq!(inner.find(&~"a"), Some(@Int(3)));
        assert_eq!(inner.find(&~"b"), Some(@Int(4)));
        assert_eq!(inner.find(&~"c"), Some(@Str(~"hi")));
    }

    #[test]
    fn test_atoms() {
        assert_eq!(Rusp::new().eval(@Int(1)).get(), @Int(1));
        assert_eq!(Rusp::new().eval(@Float(1.0)).get(), @Float(1.0));
        assert_eq!(Rusp::new().eval(@Str(~"hi")).get(), @Str(~"hi"));
    }

    #[test]
    fn test_list() {
        assert_eq!(Rusp::new().eval(@List(~[])).get(), @List(~[]));
        assert_eq!(
            Rusp::new().eval(@List(~[
                @Symbol(~"list"),
                @Str(~"hi"),
                @List(~[@Symbol(~"quote"), @Symbol(~"x")]),
                @Int(1)
            ])).get(),
            @List(~[
                @Str(~"hi"),
                @Symbol(~"x"),
                @Int(1)
            ])
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(Rusp::new().eval(
            @List(~[@Symbol(~"quote"), @Symbol(~"x")])).get(),
            @Symbol(~"x")
        );
    }

    #[test]
    fn test_symbol() {
        let env = Rusp::new();
        env.define(~"a", @Int(0));
        env.define(~"b", @Float(1.0));
        env.define(~"c", @Str(~"hi"));

        assert_eq!(env.eval(@Symbol(~"a")).get(), @Int(0));
        assert_eq!(env.eval(@Symbol(~"b")).get(), @Float(1.0));
        assert_eq!(env.eval(@Symbol(~"c")).get(), @Str(~"hi"));
        assert!(env.eval(@Symbol(~"d")).is_err());
    }

    #[test]
    fn test_if() {
        fn not(test: Value) -> EvalResult {
            Rusp::new().eval(@List(~[@Symbol(~"if"), @test, @Bool(false), @Bool(true)]))
        }

        assert_eq!(not(Bool(true)).get(), @Bool(false));
        assert_eq!(not(Bool(false)).get(), @Bool(true));
        assert!(not(Str(~"hi")).is_err());
    }

    #[test]
    fn test_def() {
        let env = Rusp::new();
        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"a"), @Int(0)])).is_ok());
        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"b"), @Float(1.0)])).is_ok());
        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"c"), @Str(~"hi")])).is_ok());

        assert_eq!(env.find(&~"a"), Some(@Int(0)));
        assert_eq!(env.find(&~"b"), Some(@Float(1.0)));
        assert_eq!(env.find(&~"c"), Some(@Str(~"hi")));

        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"c"), @List(~[])])).is_err());
    }

    #[test]
    fn test_do() {
        assert_eq!(Rusp::new().eval(@List(~[@Symbol(~"do"), @Str(~"hi")])).get(), @Str(~"hi"));
        assert_eq!(
            Rusp::new().eval(@List(~[
                @Symbol(~"do"),
                @List(~[]),
                @List(~[@Symbol(~"do"), @List(~[])]),
                @Str(~"hi")
            ])).get(),
            @Str(~"hi")
        );

        assert!(Rusp::new().eval(@List(~[@Symbol(~"do"), @Str(~"hi"), @List(~[])])).is_err());
    }

    #[test]
    fn test_fn() {
        let env = Rusp::new();
        env.eval(parse("(def f (fn (a b) a))").unwrap()[0]);
        assert_eq!(env.eval(parse("(f 1 2)").unwrap()[0]).unwrap(), @Int(1));

        assert!(env.eval(parse("(f 1)").unwrap()[0]).is_err());
    }

    #[test]
    fn test_macro() {
        let env = Rusp::new();
        env.eval(parse("(def m (macro (a) a))").unwrap()[0]);
        env.eval(parse("(def f (fn (a) a))").unwrap()[0]);
        env.eval(parse("(def x 1)").unwrap()[0]);

        // check the semantics of macros vs fn, x is not evaluated for
        // `m`, but is for `f`.
        assert_eq!(env.eval(parse("(m x)").unwrap()[0]).unwrap(), @Symbol(~"x"));
        assert_eq!(env.eval(parse("(f x)").unwrap()[0]).unwrap(), @Int(1));
    }
}
<commit_msg>Remove pointless pattern<commit_after>#[link(name = "rusp",
       vers = "0.1",
       author = "Brendan Zabarauskas",
       url = "https://github.com/bjz/rusp")];

#[comment = "A minimal Lisp dialect for Rust."];
#[license = "ASL2"];
#[crate_type = "lib"];

// Inspired by Peter Norvig's [Lis.py](http://norvig.com/lispy.html)

use std::hashmap::*;

pub mod builtins;
pub mod parser;
pub mod pprint;

///
/// Performs a recursive decent parse of the source string.
///
pub fn parse(src: &str) -> Result<~[@Value], parser::ParseFailure> {
    let mut parsed = ~[];
    let mut p = parser::Parser::new(src);

    while !p.is_eof() {
        match p.parse() {
            Ok(v) => parsed.push(@v),
            Err(e) => return Err(e)
        }
    }

    Ok(parsed)
}

/// Symbol identifier
pub type Ident = ~str;

/// Holds a typed value
#[deriving(Eq, Clone)]
pub enum Value {
    Bool(bool),
    Int(int),
    Float(float),
    Str(~str),
    List(~[@Value]),
    Symbol(Ident),
    Fn(~[Ident], @Value),
    Macro(~[Ident], @Value),
    ExternFn(RustFn),
    ExternMacro(RustFn),
}

/// Workaround for `deriving` not working for rust closures
pub struct RustFn(@fn(params: &[@Value], env: @mut Rusp) -> EvalResult);

impl Eq for RustFn {
    fn eq(&self, _: &RustFn) -> bool {
        fail!("Cannot test equality of Rust functions (yet?).");
    }
    fn ne(&self, other: &RustFn) -> bool { !self.eq(other) }
}

impl Clone for RustFn {
    fn clone(&self) -> RustFn {
        // copy the pointer
        *self
    }
}

#[deriving(Eq)]
pub struct Rusp {
    values: HashMap<Ident, @Value>,
    outer: Option<@mut Rusp>,
}

pub type EvalResult = Result<@Value, ~str>;

impl Rusp {
    /// Initializes an empty environment
    pub fn empty() -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: None,
        }
    }

    /// Initializes an environment containing the default built-ins
    pub fn new() -> @mut Rusp {
        @mut Rusp {
            values: builtins::builtins(),
            outer: None
        }
    }

    /// Initializes a new environment with a parent environment
    pub fn new_with_outer(outer: @mut Rusp) -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: Some(outer),
        }
    }

    /// Defines a new identifier/value pair in the environment
    pub fn define(&mut self, ident: Ident, val: @Value) -> EvalResult {
        if self.values.insert(ident.clone(), val)  {
            Ok(@List(~[]))
        } else {
            Err(fmt!("`%s` was already defined in this environment.", ident.to_str()))
        }
    }

    /// Attempts to find a value corresponding to the supplied identifier
    pub fn find(&self, ident: &Ident) -> Option<@Value> {
        match self.values.find(ident) {
            Some(val) => Some(*val),
            None => self.outer.chain(|env| env.find(ident)),
        }
    }

    /// Evaluates a Rusp expression in the environment
    pub fn eval(@mut self, value: @Value) -> EvalResult {
        match *value {
            Symbol(ref id) => {
                match self.find(id) {
                    Some(val) => Ok(val),
                    None => Err(fmt!("The value of `%s` was not defined in this environment",
                                     id.to_str())),
                }
            }
            List([ref vals, .. args]) => {
                do self.eval(*vals).chain |res| {
                    self.eval_call(res, args)
                }
            }
            // everything else evaluates to itself
            _ => Ok(value)
        }
    }

    /// Eval a call using `func`, which should have been previously
    /// evaluated.
    fn eval_call(@mut self, func: &Value, args: &[@Value]) -> EvalResult {
        match *func {
            Fn(ref ids, ref body) => {
                if ids.len() != args.len() {
                    return Err(fmt!("lambda expects %u arguments", ids.len()));
                }
                // stores the argument bindings of the function
                let local = Rusp::new_with_outer(self);
                for vec::each2(*ids, args) |id, val| {
                    match self.eval(*val) {
                        Err(e) => return Err(e),
                        Ok(v) => match local.define(id.clone(), v) {
                            Err(e) => return Err(e),
                            _ => {}
                        }
                    }
                }
                local.eval(*body)
            }
            Macro(ref ids, ref body) => {
                if ids.len() != args.len() {
                    return Err(fmt!("macro expects %u arguments", ids.len()));
                }
                // stores the argument bindings of the macro
                let local = Rusp::new_with_outer(self);
                for vec::each2(*ids, args) |id, val| {
                    match local.define(id.clone(), *val) {
                        Err(e) => return Err(e),
                        _ => {}
                    }
                }
                local.eval(*body)
            }
            ExternFn(ref f) => {
                let mut evaled_args = ~[];
                for args.each |v| {
                    match self.eval(*v) {
                        Err(e) => return Err(e),
                        Ok(evaled) => { evaled_args.push(evaled) }
                    }
                }
                (**f)(evaled_args, self)
            }
            ExternMacro(ref f) => {
                (**f)(args, self)
            }
            _ => Err(~"not a function")
        }
    }
}

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

    #[test]
    fn test_env() {
        let outer = Rusp::new();
        outer.define(~"a", @Int(0));
        outer.define(~"b", @Float(1.0));
        outer.define(~"c", @Str(~"hi"));

        let inner = Rusp::new_with_outer(outer);
        inner.define(~"a", @Int(3));
        inner.define(~"b", @Int(4));

        assert_eq!(outer.find(&~"a"), Some(@Int(0)));
        assert_eq!(outer.find(&~"b"), Some(@Float(1.0)));
        assert_eq!(outer.find(&~"c"), Some(@Str(~"hi")));
        assert_eq!(inner.find(&~"a"), Some(@Int(3)));
        assert_eq!(inner.find(&~"b"), Some(@Int(4)));
        assert_eq!(inner.find(&~"c"), Some(@Str(~"hi")));
    }

    #[test]
    fn test_atoms() {
        assert_eq!(Rusp::new().eval(@Int(1)).get(), @Int(1));
        assert_eq!(Rusp::new().eval(@Float(1.0)).get(), @Float(1.0));
        assert_eq!(Rusp::new().eval(@Str(~"hi")).get(), @Str(~"hi"));
    }

    #[test]
    fn test_list() {
        assert_eq!(Rusp::new().eval(@List(~[])).get(), @List(~[]));
        assert_eq!(
            Rusp::new().eval(@List(~[
                @Symbol(~"list"),
                @Str(~"hi"),
                @List(~[@Symbol(~"quote"), @Symbol(~"x")]),
                @Int(1)
            ])).get(),
            @List(~[
                @Str(~"hi"),
                @Symbol(~"x"),
                @Int(1)
            ])
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(Rusp::new().eval(
            @List(~[@Symbol(~"quote"), @Symbol(~"x")])).get(),
            @Symbol(~"x")
        );
    }

    #[test]
    fn test_symbol() {
        let env = Rusp::new();
        env.define(~"a", @Int(0));
        env.define(~"b", @Float(1.0));
        env.define(~"c", @Str(~"hi"));

        assert_eq!(env.eval(@Symbol(~"a")).get(), @Int(0));
        assert_eq!(env.eval(@Symbol(~"b")).get(), @Float(1.0));
        assert_eq!(env.eval(@Symbol(~"c")).get(), @Str(~"hi"));
        assert!(env.eval(@Symbol(~"d")).is_err());
    }

    #[test]
    fn test_if() {
        fn not(test: Value) -> EvalResult {
            Rusp::new().eval(@List(~[@Symbol(~"if"), @test, @Bool(false), @Bool(true)]))
        }

        assert_eq!(not(Bool(true)).get(), @Bool(false));
        assert_eq!(not(Bool(false)).get(), @Bool(true));
        assert!(not(Str(~"hi")).is_err());
    }

    #[test]
    fn test_def() {
        let env = Rusp::new();
        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"a"), @Int(0)])).is_ok());
        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"b"), @Float(1.0)])).is_ok());
        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"c"), @Str(~"hi")])).is_ok());

        assert_eq!(env.find(&~"a"), Some(@Int(0)));
        assert_eq!(env.find(&~"b"), Some(@Float(1.0)));
        assert_eq!(env.find(&~"c"), Some(@Str(~"hi")));

        assert!(env.eval(@List(~[@Symbol(~"def"), @Symbol(~"c"), @List(~[])])).is_err());
    }

    #[test]
    fn test_do() {
        assert_eq!(Rusp::new().eval(@List(~[@Symbol(~"do"), @Str(~"hi")])).get(), @Str(~"hi"));
        assert_eq!(
            Rusp::new().eval(@List(~[
                @Symbol(~"do"),
                @List(~[]),
                @List(~[@Symbol(~"do"), @List(~[])]),
                @Str(~"hi")
            ])).get(),
            @Str(~"hi")
        );

        assert!(Rusp::new().eval(@List(~[@Symbol(~"do"), @Str(~"hi"), @List(~[])])).is_err());
    }

    #[test]
    fn test_fn() {
        let env = Rusp::new();
        env.eval(parse("(def f (fn (a b) a))").unwrap()[0]);
        assert_eq!(env.eval(parse("(f 1 2)").unwrap()[0]).unwrap(), @Int(1));

        assert!(env.eval(parse("(f 1)").unwrap()[0]).is_err());
    }

    #[test]
    fn test_macro() {
        let env = Rusp::new();
        env.eval(parse("(def m (macro (a) a))").unwrap()[0]);
        env.eval(parse("(def f (fn (a) a))").unwrap()[0]);
        env.eval(parse("(def x 1)").unwrap()[0]);

        // check the semantics of macros vs fn, x is not evaluated for
        // `m`, but is for `f`.
        assert_eq!(env.eval(parse("(m x)").unwrap()[0]).unwrap(), @Symbol(~"x"));
        assert_eq!(env.eval(parse("(f x)").unwrap()[0]).unwrap(), @Int(1));
    }
}
<|endoftext|>


<commit_before>use storage::Cache;
use storage::Key;
use storage::Value;
use storage::utils::time_now_utc;

use super::cmd::Cmd;
use super::cmd::Get;
use super::cmd::Resp;
use super::cmd::Set;
use super::cmd::Stat;
use super::cmd::Value as CmdValue;

pub struct Driver {
    cache: Cache,
}

impl Driver {
    pub fn new(cache: Cache) -> Driver {
        Driver { cache: cache }
    }

    fn do_get(&mut self, get: Get) -> Resp {
        // XXX get rid of all the cloning
        let get_clone = get.clone();
        let key = Key::new(get.key.into_bytes());

        let rv = self.cache.get(&key);

        match rv {
            Ok(value) => {
                Resp::Value(CmdValue {
                    key: get_clone.key,
                    data: value.item.clone(),
                })
            }
            Err(_) => Resp::Error,
        }
    }

    fn do_set(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());
        let mut value = Value::new(set.data);

        // If exptime is greater than zero we need to set it on the value
        if set.exptime > 0 {
            let mut tm = 0.0;

            // Is it an interval greater than 30 days? Then it's a timestamp
            if set.exptime > 60 * 60 * 24 * 30 {
                tm = set.exptime as f64;

                // Otherwise it's relative from now
            } else {
                tm = time_now_utc() + set.exptime as f64;
            }

            value.set_exptime(tm);
        }

        let rv = self.cache.set(key, value);

        match rv {
            Ok(_) => Resp::Stored,
            Err(_) => Resp::Error,
        }
    }

    fn do_stats(&self) -> Resp {
        let curr_items = self.cache.len();

        let stat = Stat::new("curr_items", curr_items.to_string());
        Resp::Stats(vec![stat])
    }

    pub fn run(&mut self, cmd: Cmd) -> Resp {
        match cmd {
            Cmd::Get(get) => self.do_get(get),
            Cmd::Set(set) => self.do_set(set),
            Cmd::Stats => self.do_stats(),
        }
    }
}
<commit_msg>extract method set_exptime<commit_after>use storage::Cache;
use storage::Key;
use storage::Value;
use storage::utils::time_now_utc;

use super::cmd::Cmd;
use super::cmd::Get;
use super::cmd::Resp;
use super::cmd::Set;
use super::cmd::Stat;
use super::cmd::Value as CmdValue;

pub struct Driver {
    cache: Cache,
}

impl Driver {
    pub fn new(cache: Cache) -> Driver {
        Driver { cache: cache }
    }

    fn set_exptime(&self, value: &mut Value, exptime: u32) {
        // If exptime is greater than zero we need to set it on the value
        if exptime > 0 {
            let mut tm = -1.0;

            // Is it an interval greater than 30 days? Then it's a timestamp
            if exptime > 60 * 60 * 24 * 30 {
                tm = exptime as f64;

                // Otherwise it's relative from now
            } else {
                tm = time_now_utc() + exptime as f64;
            }

            value.set_exptime(tm);
        }
    }

    fn do_get(&mut self, get: Get) -> Resp {
        // XXX get rid of all the cloning
        let get_clone = get.clone();
        let key = Key::new(get.key.into_bytes());

        let rv = self.cache.get(&key);

        match rv {
            Ok(value) => {
                Resp::Value(CmdValue {
                    key: get_clone.key,
                    data: value.item.clone(),
                })
            }
            Err(_) => Resp::Error,
        }
    }

    fn do_set(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());
        let mut value = Value::new(set.data);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        match rv {
            Ok(_) => Resp::Stored,
            Err(_) => Resp::Error,
        }
    }

    fn do_stats(&self) -> Resp {
        let curr_items = self.cache.len();

        let stat = Stat::new("curr_items", curr_items.to_string());
        Resp::Stats(vec![stat])
    }

    pub fn run(&mut self, cmd: Cmd) -> Resp {
        match cmd {
            Cmd::Get(get) => self.do_get(get),
            Cmd::Set(set) => self.do_set(set),
            Cmd::Stats => self.do_stats(),
        }
    }
}
<|endoftext|>


<commit_before>//! This module greps parser's code for specially formatted comments and turnes
//! them into tests.

use std::{
    collections::HashMap,
    fs,
    path::{Path, PathBuf},
};

use crate::{
    codegen::{self, update, Mode},
    project_root, Result,
};

pub fn generate_parser_tests(mode: Mode) -> Result<()> {
    let tests = tests_from_dir(&project_root().join(Path::new(codegen::GRAMMAR_DIR)))?;
    fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> {
        let tests_dir = project_root().join(into);
        if !tests_dir.is_dir() {
            fs::create_dir_all(&tests_dir)?;
        }
        // ok is never actually read, but it needs to be specified to create a Test in existing_tests
        let existing = existing_tests(&tests_dir, true)?;
        for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
            panic!("Test is deleted: {}", t);
        }

        let mut new_idx = existing.len() + 1;
        for (name, test) in tests {
            let path = match existing.get(name) {
                Some((path, _test)) => path.clone(),
                None => {
                    let file_name = format!("{:04}_{}.rs", new_idx, name);
                    new_idx += 1;
                    tests_dir.join(file_name)
                }
            };
            update(&path, &test.text, mode)?;
        }
        Ok(())
    }
    install_tests(&tests.ok, codegen::OK_INLINE_TESTS_DIR, mode)?;
    install_tests(&tests.err, codegen::ERR_INLINE_TESTS_DIR, mode)
}

#[derive(Debug)]
struct Test {
    pub name: String,
    pub text: String,
    pub ok: bool,
}

#[derive(Default, Debug)]
struct Tests {
    pub ok: HashMap<String, Test>,
    pub err: HashMap<String, Test>,
}

fn collect_tests(s: &str) -> Vec<(usize, Test)> {
    let mut res = vec![];
    let prefix = "// ";
    let lines = s.lines().map(str::trim_start).enumerate();

    let mut block = vec![];
    for (line_idx, line) in lines {
        let is_comment = line.starts_with(prefix);
        if is_comment {
            block.push((line_idx, &line[prefix.len()..]));
        } else {
            process_block(&mut res, &block);
            block.clear();
        }
    }
    process_block(&mut res, &block);
    return res;

    fn process_block(acc: &mut Vec<(usize, Test)>, block: &[(usize, &str)]) {
        if block.is_empty() {
            return;
        }
        let mut ok = true;
        let mut block = block.iter();
        let (start_line, name) = loop {
            match block.next() {
                Some(&(idx, line)) if line.starts_with("test ") => {
                    break (idx, line["test ".len()..].to_string());
                }
                Some(&(idx, line)) if line.starts_with("test_err ") => {
                    ok = false;
                    break (idx, line["test_err ".len()..].to_string());
                }
                Some(_) => (),
                None => return,
            }
        };
        let text: String =
            block.map(|(_, line)| *line).chain(std::iter::once("")).collect::<Vec<_>>().join("\n");
        assert!(!text.trim().is_empty() && text.ends_with('\n'));
        acc.push((start_line, Test { name, text, ok }))
    }
}

fn tests_from_dir(dir: &Path) -> Result<Tests> {
    let mut res = Tests::default();
    for entry in ::walkdir::WalkDir::new(dir) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue;
        }
        if entry.path().extension().unwrap_or_default() != "rs" {
            continue;
        }
        process_file(&mut res, entry.path())?;
    }
    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
    process_file(&mut res, &grammar_rs)?;
    return Ok(res);
    fn process_file(res: &mut Tests, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;

        for (_, test) in collect_tests(&text) {
            if test.ok {
                if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
                    Err(format!("Duplicate test: {}", old_test.name))?
                }
            } else {
                if let Some(old_test) = res.err.insert(test.name.clone(), test) {
                    Err(format!("Duplicate test: {}", old_test.name))?
                }
            }
        }
        Ok(())
    }
}

fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test)>> {
    let mut res = HashMap::new();
    for file in fs::read_dir(dir)? {
        let file = file?;
        let path = file.path();
        if path.extension().unwrap_or_default() != "rs" {
            continue;
        }
        let name = {
            let file_name = path.file_name().unwrap().to_str().unwrap();
            file_name[5..file_name.len() - 3].to_string()
        };
        let text = fs::read_to_string(&path)?;
        let test = Test { name: name.clone(), text, ok };
        if let Some(old) = res.insert(name, (path, test)) {
            println!("Duplicate test: {:?}", old);
        }
    }
    Ok(res)
}
<commit_msg>simplify<commit_after>//! This module greps parser's code for specially formatted comments and turnes
//! them into tests.

use std::{
    collections::HashMap,
    fs,
    path::{Path, PathBuf},
};

use crate::{
    codegen::{self, update, Mode},
    project_root, Result,
};

pub fn generate_parser_tests(mode: Mode) -> Result<()> {
    let tests = tests_from_dir(&project_root().join(Path::new(codegen::GRAMMAR_DIR)))?;
    fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> {
        let tests_dir = project_root().join(into);
        if !tests_dir.is_dir() {
            fs::create_dir_all(&tests_dir)?;
        }
        // ok is never actually read, but it needs to be specified to create a Test in existing_tests
        let existing = existing_tests(&tests_dir, true)?;
        for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
            panic!("Test is deleted: {}", t);
        }

        let mut new_idx = existing.len() + 1;
        for (name, test) in tests {
            let path = match existing.get(name) {
                Some((path, _test)) => path.clone(),
                None => {
                    let file_name = format!("{:04}_{}.rs", new_idx, name);
                    new_idx += 1;
                    tests_dir.join(file_name)
                }
            };
            update(&path, &test.text, mode)?;
        }
        Ok(())
    }
    install_tests(&tests.ok, codegen::OK_INLINE_TESTS_DIR, mode)?;
    install_tests(&tests.err, codegen::ERR_INLINE_TESTS_DIR, mode)
}

#[derive(Debug)]
struct Test {
    pub name: String,
    pub text: String,
    pub ok: bool,
}

#[derive(Default, Debug)]
struct Tests {
    pub ok: HashMap<String, Test>,
    pub err: HashMap<String, Test>,
}

fn collect_tests(s: &str) -> Vec<Test> {
    let mut res = vec![];
    let prefix = "// ";
    let lines = s.lines().map(str::trim_start);

    let mut block = vec![];
    for line in lines {
        let is_comment = line.starts_with(prefix);
        if is_comment {
            block.push(&line[prefix.len()..]);
        } else {
            process_block(&mut res, &block);
            block.clear();
        }
    }
    process_block(&mut res, &block);
    return res;

    fn process_block(acc: &mut Vec<Test>, block: &[&str]) {
        if block.is_empty() {
            return;
        }
        let mut ok = true;
        let mut block = block.iter();
        let name = loop {
            match block.next() {
                Some(line) if line.starts_with("test ") => {
                    break line["test ".len()..].to_string();
                }
                Some(line) if line.starts_with("test_err ") => {
                    ok = false;
                    break line["test_err ".len()..].to_string();
                }
                Some(_) => (),
                None => return,
            }
        };
        let text: String = block.copied().chain(std::iter::once("")).collect::<Vec<_>>().join("\n");
        assert!(!text.trim().is_empty() && text.ends_with('\n'));
        acc.push(Test { name, text, ok })
    }
}

fn tests_from_dir(dir: &Path) -> Result<Tests> {
    let mut res = Tests::default();
    for entry in ::walkdir::WalkDir::new(dir) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue;
        }
        if entry.path().extension().unwrap_or_default() != "rs" {
            continue;
        }
        process_file(&mut res, entry.path())?;
    }
    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
    process_file(&mut res, &grammar_rs)?;
    return Ok(res);
    fn process_file(res: &mut Tests, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;

        for test in collect_tests(&text) {
            if test.ok {
                if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
                    Err(format!("Duplicate test: {}", old_test.name))?
                }
            } else {
                if let Some(old_test) = res.err.insert(test.name.clone(), test) {
                    Err(format!("Duplicate test: {}", old_test.name))?
                }
            }
        }
        Ok(())
    }
}

fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test)>> {
    let mut res = HashMap::new();
    for file in fs::read_dir(dir)? {
        let file = file?;
        let path = file.path();
        if path.extension().unwrap_or_default() != "rs" {
            continue;
        }
        let name = {
            let file_name = path.file_name().unwrap().to_str().unwrap();
            file_name[5..file_name.len() - 3].to_string()
        };
        let text = fs::read_to_string(&path)?;
        let test = Test { name: name.clone(), text, ok };
        if let Some(old) = res.insert(name, (path, test)) {
            println!("Duplicate test: {:?}", old);
        }
    }
    Ok(res)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// This examples simply print all interfaces to stdout

extern crate pnet_datalink;

fn main() {
    for interface in pnet_datalink::interfaces() {
        let mac = interface.mac.map(|mac| mac.to_string()).unwrap_or("N/A".to_owned());
        println!("{}:", interface.name);
        println!("  index: {}", interface.index);
        println!("  flags: {}", interface.flags);
        println!("  MAC: {}", mac);
        println!("  IPs:");
        for ip in interface.ips {
            println!("    {:?}", ip);
        }
    }
}
<commit_msg>Update main.rs<commit_after>
extern crate pnet_datalink;

fn main() {
    for interface in pnet_datalink::interfaces() {
        let mac = interface.mac.map(|mac| mac.to_string()).unwrap_or("N/A".to_owned());
        println!("{}:", interface.name);
        println!("  index: {}", interface.index);
        println!("  flags: {}", interface.flags);
        println!("  MAC: {}", mac);
        println!("  IPs:");
        for ip in interface.ips {
            println!("    {:?}", ip);
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "stable_graph")]

extern crate petgraph;

use petgraph::graph::stable::StableGraph;
use petgraph::graph::node_index;

#[test]
fn node_indices() {
    let mut g = StableGraph::<_, ()>::new();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let c = g.add_node(2);
    g.remove_node(b);
    let mut iter = g.node_indices();
    assert_eq!(iter.next(), Some(a));
    assert_eq!(iter.next(), Some(c));
    assert_eq!(iter.next(), None);
}
<commit_msg>Fix warning in tests/stablegraph.rs<commit_after>#![cfg(feature = "stable_graph")]

extern crate petgraph;

use petgraph::graph::stable::StableGraph;

#[test]
fn node_indices() {
    let mut g = StableGraph::<_, ()>::new();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let c = g.add_node(2);
    g.remove_node(b);
    let mut iter = g.node_indices();
    assert_eq!(iter.next(), Some(a));
    assert_eq!(iter.next(), Some(c));
    assert_eq!(iter.next(), None);
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 core::cell::Cell;
use h1b::crypto::aes::AesEngine;
use kernel::{AppId, Callback, Driver, Grant, ReturnCode, Shared, AppSlice};
use kernel::common::cells::TakeCell;
use kernel::hil::symmetric_encryption;
use kernel::hil::symmetric_encryption::{AES128_BLOCK_SIZE, AES128_KEY_SIZE};
use kernel::hil::symmetric_encryption::{AES128, AES128CBC, AES128Ctr, AES128Ecb};

pub const DRIVER_NUM: usize = 0x40000;

pub static mut AES_BUF: [u8; AES128_BLOCK_SIZE] = [0; AES128_BLOCK_SIZE];

#[derive(Default)]
pub struct AppData {
    key: Option<AppSlice<Shared, u8>>,
    input_buffer: Option<AppSlice<Shared, u8>>,
    output_buffer: Option<AppSlice<Shared, u8>>,
    iv_buffer: Option<AppSlice<Shared, u8>>,
    callback: Option<Callback>,
}

pub struct AesDriver<'a> {
    device: &'a AesEngine<'a>,
    apps: Grant<AppData>,
    current_user: Cell<Option<AppId>>,
    buffer: TakeCell<'a, [u8]>,
}

impl<'a> AesDriver<'a> {
    pub fn new(device: &'a mut AesEngine<'a>,
               container: Grant<AppData>) -> AesDriver<'a> {
        AesDriver {
            device: device,
            apps: container,
            current_user: Cell::new(None),
            buffer: TakeCell::empty(),
        }
    }

    // Register a buffer, which must be of size AES128_BLOCK_SIZE; if
    // it is not the proper size, return the buffer in the
    // Option. Return None if the buffer was correct.
    pub fn initialize(&self,
                      input_buffer: &'a mut [u8]) -> Option<&'a mut [u8]>  {
        self.device.setup();

        if input_buffer.len() != AES128_BLOCK_SIZE {
            Some(input_buffer)
        } else {
            self.buffer.replace(input_buffer);
            None
        }
    }

    fn run_aes(&self, caller_id: AppId) -> ReturnCode {
        self.apps.enter(caller_id, |app_data, _| {
            if app_data.input_buffer.is_none() {
                debug!("Missing input buffer.\n");
                return ReturnCode::ENOMEM;
            } else if app_data.key.is_none() {
                debug!("Missing application encryption key.\n");
                return ReturnCode::ENOMEM;
            } else if self.buffer.is_none() {
                debug!("Missing kernel buffer.\n");
                return ReturnCode::ENOMEM;
            }
            let key = app_data.key.take();
            key.map(|key| {
                if key.len() == AES128_KEY_SIZE {
                    self.device.set_key(key.as_ref());
                }
                app_data.key = Some(key);
            });

            // Copy application data into the kernel buffer
            self.buffer.map(|buf| {
                app_data.input_buffer.as_ref().map(|src| {
                    for (i, c) in src.as_ref()[0..AES128_BLOCK_SIZE].iter().enumerate() {
                        buf[i] = *c;
                    }
                });
            });
            let buf = self.buffer.take().unwrap();
            let opt =  AES128::crypt(self.device, None, buf, 0, AES128_BLOCK_SIZE);
            if let Some((rcode, _ibufopt, obuf)) = opt {
                debug!("Failed to invoke AES encryption: {:?}", rcode);
                self.buffer.put(Some(obuf));
                rcode
            } else {
                ReturnCode::SUCCESS
            }
        }).unwrap_or(ReturnCode::ENOMEM)
    }
}

impl<'a> symmetric_encryption::Client<'a> for AesDriver<'a> {
    fn crypt_done(&self, _source: Option<&'a mut [u8]>, output: &'a mut [u8]) {
        self.current_user.get().map(|current_user| {
            let _ = self.apps.enter(current_user, move |app_data, _| {
                if let Some(ref mut slice) = app_data.output_buffer {
                    self.device.read_data(slice.as_mut());
                }
                let val = {
                    if let Some(ref mut slice) = app_data.input_buffer {
                        self.device.read_data(slice.as_mut())
                    } else {
                        0
                    }
                };
                self.current_user.set(None);
                app_data.callback.map(|mut cb| cb.schedule(val, 0, 0));
            });
        });
        self.buffer.replace(output);
    }
}

impl<'a> Driver for AesDriver<'a> {
    fn subscribe(&self,
                 subscribe_num: usize,
                 callback: Option<Callback>,
                 app_id: AppId,
    ) -> ReturnCode {
        match subscribe_num {
            0 => {
                self.apps.enter(app_id, |app_data, _| {
                    app_data.callback = callback;
                    ReturnCode::SUCCESS
                }).unwrap_or(ReturnCode::ENOMEM)
            }
            _ => ReturnCode::ENOSUPPORT
        }
    }

    fn command(&self, command_num: usize, _arg1: usize, _: usize, caller_id: AppId) -> ReturnCode {
        if self.current_user.get() == None {
            self.current_user.set(Some(caller_id));
        }
        match command_num {
            0 /* Check if present */ => ReturnCode::SUCCESS,
            1 /* encrypt ECB */ => {
                self.device.set_mode_aes128ecb(true);
                self.run_aes(caller_id)
            },
            2 /* decrypt ECB */ => {
                self.device.set_mode_aes128ecb(false);
                self.run_aes(caller_id)
            }
            3 | 4 /* encrypt/decrypt CTR */ => {
                self.apps.enter(caller_id, |app_data, _| {
                    self.device.set_mode_aes128ctr(true);
                    let buffer = app_data.iv_buffer.take();
                    buffer.map_or(ReturnCode::ENOMEM, |iv| {
                        self.device.set_iv(iv.as_ref());
                        app_data.iv_buffer = Some(iv);
                        self.run_aes(caller_id)
                    })
                }).unwrap_or(ReturnCode::ENOMEM)
            }
            5 /* encrypt CBC */ => {
                self.device.set_mode_aes128cbc(true);
                self.run_aes(caller_id)
            },
            6 /* decrypt CBC */ => {
                self.device.set_mode_aes128cbc(false);
                self.run_aes(caller_id)
            },
            _ => {
                self.current_user.set(None);
                ReturnCode::ENOSUPPORT
            }
        }
    }

    fn allow(&self,
             app_id: AppId,
             minor_num: usize,
             slice: Option<AppSlice<Shared, u8>>
    ) -> ReturnCode {
        match minor_num {
                0 => {
                    // Key
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_KEY_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.key = Some(s);
                            } else {
                                app_data.key = slice;
                            }

                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
                1 => {
                    // Input Buffer
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_BLOCK_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.input_buffer = Some(s);
                            } else {
                                app_data.input_buffer = slice;
                            }
                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
                2 => {
                    // Output Buffer
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_BLOCK_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.output_buffer = Some(s);
                            } else {
                                app_data.output_buffer = slice;
                            }
                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
                3 => {
                    // Initialization vector/Counter
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_BLOCK_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.iv_buffer = Some(s);
                            } else {
                                app_data.iv_buffer = slice;
                            }
                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
            _ => ReturnCode::ENOSUPPORT,

        }
    }
}

impl<'a> symmetric_encryption::Client<'a> for AesDriver<'a> {
    fn crypt_done(&self, _source: Option<&'a mut [u8]>, _dest: &'a mut [u8]) {
        self.current_user.get().map(|current_user| {
            let _ = self.apps.enter(current_user, |app_data, _| {
                let val = match app_data.output_buffer {
                    Some(ref mut slice) => {
                        self.device.read_data(slice.as_mut())
                    },
                    None => {0}
                };
                app_data.callbacks.done_cipher.map(|mut cb| cb.schedule(val, 0, 0));
            });
        });
    }
}
<commit_msg>Rely on H1B for AES128Ecb rather than hil::symmetric_encryption.<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 core::cell::Cell;
use h1b::crypto::aes::{AesEngine, AES128Ecb};
use kernel::{AppId, Callback, Driver, Grant, ReturnCode, Shared, AppSlice};
use kernel::common::cells::TakeCell;
use kernel::hil::symmetric_encryption;
use kernel::hil::symmetric_encryption::{AES128_BLOCK_SIZE, AES128_KEY_SIZE};
use kernel::hil::symmetric_encryption::{AES128, AES128CBC, AES128Ctr};

pub const DRIVER_NUM: usize = 0x40000;

pub static mut AES_BUF: [u8; AES128_BLOCK_SIZE] = [0; AES128_BLOCK_SIZE];

#[derive(Default)]
pub struct AppData {
    key: Option<AppSlice<Shared, u8>>,
    input_buffer: Option<AppSlice<Shared, u8>>,
    output_buffer: Option<AppSlice<Shared, u8>>,
    iv_buffer: Option<AppSlice<Shared, u8>>,
    callback: Option<Callback>,
}

pub struct AesDriver<'a> {
    device: &'a AesEngine<'a>,
    apps: Grant<AppData>,
    current_user: Cell<Option<AppId>>,
    buffer: TakeCell<'a, [u8]>,
}

impl<'a> AesDriver<'a> {
    pub fn new(device: &'a mut AesEngine<'a>,
               container: Grant<AppData>) -> AesDriver<'a> {
        AesDriver {
            device: device,
            apps: container,
            current_user: Cell::new(None),
            buffer: TakeCell::empty(),
        }
    }

    // Register a buffer, which must be of size AES128_BLOCK_SIZE; if
    // it is not the proper size, return the buffer in the
    // Option. Return None if the buffer was correct.
    pub fn initialize(&self,
                      input_buffer: &'a mut [u8]) -> Option<&'a mut [u8]>  {
        self.device.setup();

        if input_buffer.len() != AES128_BLOCK_SIZE {
            Some(input_buffer)
        } else {
            self.buffer.replace(input_buffer);
            None
        }
    }

    fn run_aes(&self, caller_id: AppId) -> ReturnCode {
        self.apps.enter(caller_id, |app_data, _| {
            if app_data.input_buffer.is_none() {
                debug!("Missing input buffer.\n");
                return ReturnCode::ENOMEM;
            } else if app_data.key.is_none() {
                debug!("Missing application encryption key.\n");
                return ReturnCode::ENOMEM;
            } else if self.buffer.is_none() {
                debug!("Missing kernel buffer.\n");
                return ReturnCode::ENOMEM;
            }
            let key = app_data.key.take();
            key.map(|key| {
                if key.len() == AES128_KEY_SIZE {
                    self.device.set_key(key.as_ref());
                }
                app_data.key = Some(key);
            });

            // Copy application data into the kernel buffer
            self.buffer.map(|buf| {
                app_data.input_buffer.as_ref().map(|src| {
                    for (i, c) in src.as_ref()[0..AES128_BLOCK_SIZE].iter().enumerate() {
                        buf[i] = *c;
                    }
                });
            });
            let buf = self.buffer.take().unwrap();
            let opt =  AES128::crypt(self.device, None, buf, 0, AES128_BLOCK_SIZE);
            if let Some((rcode, _ibufopt, obuf)) = opt {
                debug!("Failed to invoke AES encryption: {:?}", rcode);
                self.buffer.put(Some(obuf));
                rcode
            } else {
                ReturnCode::SUCCESS
            }
        }).unwrap_or(ReturnCode::ENOMEM)
    }
}

impl<'a> symmetric_encryption::Client<'a> for AesDriver<'a> {
    fn crypt_done(&self, _source: Option<&'a mut [u8]>, output: &'a mut [u8]) {
        self.current_user.get().map(|current_user| {
            let _ = self.apps.enter(current_user, move |app_data, _| {
                if let Some(ref mut slice) = app_data.output_buffer {
                    self.device.read_data(slice.as_mut());
                }
                let val = {
                    if let Some(ref mut slice) = app_data.input_buffer {
                        self.device.read_data(slice.as_mut())
                    } else {
                        0
                    }
                };
                self.current_user.set(None);
                app_data.callback.map(|mut cb| cb.schedule(val, 0, 0));
            });
        });
        self.buffer.replace(output);
    }
}

impl<'a> Driver for AesDriver<'a> {
    fn subscribe(&self,
                 subscribe_num: usize,
                 callback: Option<Callback>,
                 app_id: AppId,
    ) -> ReturnCode {
        match subscribe_num {
            0 => {
                self.apps.enter(app_id, |app_data, _| {
                    app_data.callback = callback;
                    ReturnCode::SUCCESS
                }).unwrap_or(ReturnCode::ENOMEM)
            }
            _ => ReturnCode::ENOSUPPORT
        }
    }

    fn command(&self, command_num: usize, _arg1: usize, _: usize, caller_id: AppId) -> ReturnCode {
        if self.current_user.get() == None {
            self.current_user.set(Some(caller_id));
        }
        match command_num {
            0 /* Check if present */ => ReturnCode::SUCCESS,
            1 /* encrypt ECB */ => {
                self.device.set_mode_aes128ecb(true);
                self.run_aes(caller_id)
            },
            2 /* decrypt ECB */ => {
                self.device.set_mode_aes128ecb(false);
                self.run_aes(caller_id)
            }
            3 | 4 /* encrypt/decrypt CTR */ => {
                self.apps.enter(caller_id, |app_data, _| {
                    self.device.set_mode_aes128ctr(true);
                    let buffer = app_data.iv_buffer.take();
                    buffer.map_or(ReturnCode::ENOMEM, |iv| {
                        self.device.set_iv(iv.as_ref());
                        app_data.iv_buffer = Some(iv);
                        self.run_aes(caller_id)
                    })
                }).unwrap_or(ReturnCode::ENOMEM)
            }
            5 /* encrypt CBC */ => {
                self.device.set_mode_aes128cbc(true);
                self.run_aes(caller_id)
            },
            6 /* decrypt CBC */ => {
                self.device.set_mode_aes128cbc(false);
                self.run_aes(caller_id)
            },
            _ => {
                self.current_user.set(None);
                ReturnCode::ENOSUPPORT
            }
        }
    }

    fn allow(&self,
             app_id: AppId,
             minor_num: usize,
             slice: Option<AppSlice<Shared, u8>>
    ) -> ReturnCode {
        match minor_num {
                0 => {
                    // Key
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_KEY_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.key = Some(s);
                            } else {
                                app_data.key = slice;
                            }

                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
                1 => {
                    // Input Buffer
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_BLOCK_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.input_buffer = Some(s);
                            } else {
                                app_data.input_buffer = slice;
                            }
                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
                2 => {
                    // Output Buffer
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_BLOCK_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.output_buffer = Some(s);
                            } else {
                                app_data.output_buffer = slice;
                            }
                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
                3 => {
                    // Initialization vector/Counter
                    self.apps
                        .enter(app_id, |app_data, _| {
                            if let Some(s) = slice {
                                if s.len() != AES128_BLOCK_SIZE {
                                    return ReturnCode::ESIZE;
                                }
                                app_data.iv_buffer = Some(s);
                            } else {
                                app_data.iv_buffer = slice;
                            }
                            ReturnCode::SUCCESS
                        })
                        .unwrap_or(ReturnCode::FAIL)
                }
            _ => ReturnCode::ENOSUPPORT,

        }
    }
}
<|endoftext|>