<commit_before>use std::ascii::AsciiExt;

pub fn camel_case(name: &str) -> String {
    let mut new_word = true;
    name.chars().fold("".to_string(), |mut result, ch| {
        if ch == '-' || ch == '_' || ch == ' ' {
            new_word = true;
            result
        } else {
            result.push(if new_word { ch.to_ascii_uppercase() } else { ch.to_ascii_lowercase() });
            new_word = false;
            result
        }
    })
}

pub fn snake_case(name: &str) -> String {
    match name {
        "type"   => "amqp_type".to_string(),
        "return" => "amqp_return".to_string(),
        name     => {
            let mut new_word       = false;
            let mut last_was_upper = false;
            name.chars().fold("".to_string(), |mut result, ch| {
                if ch == '-' || ch == '_' || ch == ' ' {
                    new_word = true;
                    result
                } else {
                    let uppercase = ch.is_uppercase();
                    if new_word || (!last_was_upper && result.len() > 0 && uppercase) {
                        result.push('_');
                        new_word = false;
                    }
                    last_was_upper = uppercase;
                    result.push(if uppercase { ch.to_ascii_lowercase() } else { ch });
                    result
                }
            })
        }
    }
}

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

    #[test]
    fn test_camel_case() {
        assert_eq!(camel_case(""),                  "");
        assert_eq!(camel_case("foobar"),            "Foobar");
        assert_eq!(camel_case("foo_bar"),           "FooBar");
        assert_eq!(camel_case("_foo__bar baz-zzz"), "FooBarBazZzz");
    }

    #[test]
    fn test_snake_case() {
        assert_eq!(snake_case(""),               "");
        assert_eq!(snake_case("Foobar"),         "foobar");
        assert_eq!(snake_case("FooBar"),         "foo_bar");
        assert_eq!(snake_case("Foo-BarBaz_zzz"), "foo_bar_baz_zzz");
    }

    #[test]
    fn test_snake_case_uint() {
        /* special case: we want UInt to be converted as uint */
        assert_eq!(snake_case("UInt"),     "uint");
        assert_eq!(snake_case("LongUInt"), "long_uint");
    }
}
<commit_msg>codegen: rework our reserved words aliases<commit_after>use std::ascii::AsciiExt;

pub fn camel_case(name: &str) -> String {
    let mut new_word = true;
    name.chars().fold("".to_string(), |mut result, ch| {
        if ch == '-' || ch == '_' || ch == ' ' {
            new_word = true;
            result
        } else {
            result.push(if new_word { ch.to_ascii_uppercase() } else { ch.to_ascii_lowercase() });
            new_word = false;
            result
        }
    })
}

pub fn snake_case(name: &str) -> String {
    match name {
        "type"   => "type_".to_string(),
        "return" => "return_".to_string(),
        name     => {
            let mut new_word       = false;
            let mut last_was_upper = false;
            name.chars().fold("".to_string(), |mut result, ch| {
                if ch == '-' || ch == '_' || ch == ' ' {
                    new_word = true;
                    result
                } else {
                    let uppercase = ch.is_uppercase();
                    if new_word || (!last_was_upper && result.len() > 0 && uppercase) {
                        result.push('_');
                        new_word = false;
                    }
                    last_was_upper = uppercase;
                    result.push(if uppercase { ch.to_ascii_lowercase() } else { ch });
                    result
                }
            })
        }
    }
}

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

    #[test]
    fn test_camel_case() {
        assert_eq!(camel_case(""),                  "");
        assert_eq!(camel_case("foobar"),            "Foobar");
        assert_eq!(camel_case("foo_bar"),           "FooBar");
        assert_eq!(camel_case("_foo__bar baz-zzz"), "FooBarBazZzz");
    }

    #[test]
    fn test_snake_case() {
        assert_eq!(snake_case(""),               "");
        assert_eq!(snake_case("Foobar"),         "foobar");
        assert_eq!(snake_case("FooBar"),         "foo_bar");
        assert_eq!(snake_case("Foo-BarBaz_zzz"), "foo_bar_baz_zzz");
    }

    #[test]
    fn test_snake_case_uint() {
        /* special case: we want UInt to be converted as uint */
        assert_eq!(snake_case("UInt"),     "uint");
        assert_eq!(snake_case("LongUInt"), "long_uint");
    }
}
<|endoftext|>


<commit_before>use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
    braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
    ItemUse, Token, Visibility,
};

pub struct Module {
    pub namespace: Namespace,
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub unsafety: Option<Token![unsafe]>,
    pub mod_token: Token![mod],
    pub ident: Ident,
    pub brace_token: token::Brace,
    pub content: Vec<Item>,
}

pub enum Item {
    Struct(ItemStruct),
    Enum(ItemEnum),
    ForeignMod(ItemForeignMod),
    Use(ItemUse),
    Other(RustItem),
}

pub struct ItemForeignMod {
    pub attrs: Vec<Attribute>,
    pub unsafety: Option<Token![unsafe]>,
    pub abi: Abi,
    pub brace_token: token::Brace,
    pub items: Vec<ForeignItem>,
}

impl Parse for Module {
    fn parse(input: ParseStream) -> Result<Self> {
        let namespace = Namespace::none();
        let mut attrs = input.call(Attribute::parse_outer)?;
        let vis: Visibility = input.parse()?;
        let unsafety: Option<Token![unsafe]> = input.parse()?;
        let mod_token: Token![mod] = input.parse()?;
        let ident: Ident = input.parse()?;

        let semi: Option<Token![;]> = input.parse()?;
        if let Some(semi) = semi {
            let span = quote!(#vis #mod_token #semi);
            return Err(Error::new_spanned(
                span,
                "#[cxx::bridge] module must have inline contents",
            ))?;
        }

        let content;
        let brace_token = braced!(content in input);
        attrs.extend(content.call(Attribute::parse_inner)?);

        let mut items = Vec::new();
        while !content.is_empty() {
            items.push(content.parse()?);
        }

        Ok(Module {
            namespace,
            attrs,
            vis,
            unsafety,
            mod_token,
            ident,
            brace_token,
            content: items,
        })
    }
}

impl Parse for Item {
    fn parse(input: ParseStream) -> Result<Self> {
        let item = input.parse()?;
        match item {
            RustItem::Struct(item) => Ok(Item::Struct(item)),
            RustItem::Enum(item) => Ok(Item::Enum(item)),
            RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod {
                attrs: item.attrs,
                unsafety: None,
                abi: item.abi,
                brace_token: item.brace_token,
                items: item.items,
            })),
            RustItem::Use(item) => Ok(Item::Use(item)),
            other => Ok(Item::Other(other)),
        }
    }
}
<commit_msg>Parse unsafety on extern blocks<commit_after>use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
    braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
    ItemUse, LitStr, Token, Visibility,
};

pub struct Module {
    pub namespace: Namespace,
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub unsafety: Option<Token![unsafe]>,
    pub mod_token: Token![mod],
    pub ident: Ident,
    pub brace_token: token::Brace,
    pub content: Vec<Item>,
}

pub enum Item {
    Struct(ItemStruct),
    Enum(ItemEnum),
    ForeignMod(ItemForeignMod),
    Use(ItemUse),
    Other(RustItem),
}

pub struct ItemForeignMod {
    pub attrs: Vec<Attribute>,
    pub unsafety: Option<Token![unsafe]>,
    pub abi: Abi,
    pub brace_token: token::Brace,
    pub items: Vec<ForeignItem>,
}

impl Parse for Module {
    fn parse(input: ParseStream) -> Result<Self> {
        let namespace = Namespace::none();
        let mut attrs = input.call(Attribute::parse_outer)?;
        let vis: Visibility = input.parse()?;
        let unsafety: Option<Token![unsafe]> = input.parse()?;
        let mod_token: Token![mod] = input.parse()?;
        let ident: Ident = input.parse()?;

        let semi: Option<Token![;]> = input.parse()?;
        if let Some(semi) = semi {
            let span = quote!(#vis #mod_token #semi);
            return Err(Error::new_spanned(
                span,
                "#[cxx::bridge] module must have inline contents",
            ))?;
        }

        let content;
        let brace_token = braced!(content in input);
        attrs.extend(content.call(Attribute::parse_inner)?);

        let mut items = Vec::new();
        while !content.is_empty() {
            items.push(content.parse()?);
        }

        Ok(Module {
            namespace,
            attrs,
            vis,
            unsafety,
            mod_token,
            ident,
            brace_token,
            content: items,
        })
    }
}

impl Parse for Item {
    fn parse(input: ParseStream) -> Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;

        let ahead = input.fork();
        let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
            && ahead.parse::<Option<Token![extern]>>()?.is_some()
            && ahead.parse::<Option<LitStr>>().is_ok()
            && ahead.peek(token::Brace)
        {
            Some(input.parse()?)
        } else {
            None
        };

        let item = input.parse()?;
        match item {
            RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })),
            RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })),
            RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod {
                attrs: item.attrs,
                unsafety,
                abi: item.abi,
                brace_token: item.brace_token,
                items: item.items,
            })),
            RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })),
            other => Ok(Item::Other(other)),
        }
    }
}
<|endoftext|>


<commit_before>//use std::os;
use std::io;
use std::num::from_str_radix;

fn main() {
    let mut ns: Vec<f64> = io::stdin()
        .lines()
        .map(|l| from_str_radix::<f64>(l.unwrap().as_slice().trim(), 10))
        .filter(|o| match *o {
            Some(_) => true,
            None => false,
        })
        .map(|n| match n {
            Some(x) => x,
            None => panic!("borkage")
        })
        .collect();

    ns.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Equal));

    let count = ns.len();

    let sum = ns.iter().fold(0.0, |a, &b| a + b);

    println!("count: {}", count);
    println!("sum:   {}", sum);

    if ns.is_empty() {
        println!("(empty set of numbers, remaining stats not available)");
    } else {
        println!("avg:   {}", sum/count as f64);
        println!("max:   {}", ns[count - 1]);
        println!("p999:  {}", ns[ns.len() * 999/1000]);
        println!("p99:   {}", ns[ns.len() * 99/100]);
        println!("p90:   {}", ns[ns.len() * 90/100]);
        println!("p50:   {}", ns[ns.len() * 50/100]);
        println!("min:   {}", ns[0]);
    }
}
<commit_msg>Remove commeneted out use.<commit_after>use std::io;
use std::num::from_str_radix;

fn main() {
    let mut ns: Vec<f64> = io::stdin()
        .lines()
        .map(|l| from_str_radix::<f64>(l.unwrap().as_slice().trim(), 10))
        .filter(|o| match *o {
            Some(_) => true,
            None => false,
        })
        .map(|n| match n {
            Some(x) => x,
            None => panic!("borkage")
        })
        .collect();

    ns.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Equal));

    let count = ns.len();

    let sum = ns.iter().fold(0.0, |a, &b| a + b);

    println!("count: {}", count);
    println!("sum:   {}", sum);

    if ns.is_empty() {
        println!("(empty set of numbers, remaining stats not available)");
    } else {
        println!("avg:   {}", sum/count as f64);
        println!("max:   {}", ns[count - 1]);
        println!("p999:  {}", ns[ns.len() * 999/1000]);
        println!("p99:   {}", ns[ns.len() * 99/100]);
        println!("p90:   {}", ns[ns.len() * 90/100]);
        println!("p50:   {}", ns[ns.len() * 50/100]);
        println!("min:   {}", ns[0]);
    }
}
<|endoftext|>


<commit_before>#![feature(exit_status)]
#![feature(io)]
#![feature(os)]
extern crate octobuild;
extern crate tempdir;

use octobuild::common::BuildTask;
use octobuild::cache::Cache;
use octobuild::wincmd;
use octobuild::xg;
use octobuild::graph::{Graph, NodeIndex, Node, EdgeIndex, Edge};
use octobuild::vs::compiler::VsCompiler;
use octobuild::compiler::*;

use tempdir::TempDir;

use std::fs::File;
use std::env;
use std::io::{BufReader, Error, ErrorKind, Write};
use std::io;
use std::iter::FromIterator;
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;

#[derive(Debug)]
struct TaskMessage {
	index: NodeIndex,
	task: BuildTask
}

#[derive(Debug)]
struct ResultMessage {
	index: NodeIndex,
	task: BuildTask,
	worker: usize,
	result: Result<OutputInfo, Error>
}

fn main() {
	println!("XGConsole ({}):", octobuild::VERSION);
	let args = Vec::from_iter(env::args());
	for arg in args.iter() {
		println!("  {}", arg);
	}
	match execute(&args[1..]) {
		Ok(result) => {
			env::set_exit_status(match result {
				Some(r) => r,
				None => 500
			});
		}
		Err(e) => {
			println!("FATAL ERROR: {:?}", e);
			env::set_exit_status(500);
		}
	}
}

fn execute(args: &[String]) -> Result<Option<i32>, Error> {
	let cache = Cache::new();
	let temp_dir = try! (TempDir::new("octobuild"));
	for arg in args.iter() {
		if arg.starts_with("/") {continue}

		let (tx_result, rx_result): (Sender<ResultMessage>, Receiver<ResultMessage>) = channel();
		let (tx_task, rx_task): (Sender<TaskMessage>, Receiver<TaskMessage>) = channel();

		let mutex_rx_task = create_threads(rx_task, tx_result, std::os::num_cpus(), |worker_id:usize| {
			let temp_path = temp_dir.path().to_path_buf();
			let temp_cache = cache.clone();
			move |task:TaskMessage| -> ResultMessage {
				execute_task(&temp_cache, &temp_path, worker_id, task)
			}
		});

		let path = Path::new(arg);
		let file = try! (File::open(&path));
		let graph = try! (xg::parser::parse(BufReader::new(file)));
		let validated_graph = try! (validate_graph(graph));
		match try! (execute_graph(&validated_graph, tx_task, mutex_rx_task, rx_result)) {
			Some(v) if v == 0 => {}
			v => {return Ok(v)}
		}
	}
	cache.cleanup(16 * 1024 * 1024 * 1024);
	Ok(Some(0))
}

fn create_threads<R: 'static + Send, T: 'static + Send, Worker:'static + Fn(T) -> R + Send, Factory:Fn(usize) -> Worker>(rx_task: Receiver<T>, tx_result: Sender<R>, num_cpus: usize, factory: Factory) ->  Arc<Mutex<Receiver<T>>> {
	let mutex_rx_task = Arc::new(Mutex::new(rx_task));
	for cpu_id in 0..num_cpus {
		let local_rx_task = mutex_rx_task.clone();
		let local_tx_result = tx_result.clone();
		let worker = factory(cpu_id);
		thread::spawn(move || {
			loop {
				let task: T;
				match local_rx_task.lock().unwrap().recv() {
					Ok(v) => {task = v;}
					Err(_) => {break;}
				}
				match local_tx_result.send(worker(task)) {
					Ok(_) => {}
					Err(_) => {break;}
				}
			}
		});
	}
	mutex_rx_task
}

fn validate_graph(graph: Graph<BuildTask, ()>) -> Result<Graph<BuildTask, ()>, Error> {
	let mut completed:Vec<bool> = Vec::new();
	let mut queue:Vec<NodeIndex> = Vec::new();
	graph.each_node(|index: NodeIndex, _:&Node<BuildTask>|->bool {
		completed.push(false);
		queue.push(index);
		true
	});
	let mut count:usize = 0;
	let mut i:usize = 0;
	while i < queue.len() {
		let index = queue[i];
		if (!completed[index.node_id()]) && (is_ready(&graph, &completed, &index)) {
			completed[index.node_id()] = true;
			graph.each_incoming_edge(index, |_:EdgeIndex, edge:&Edge<()>| -> bool {
				queue.push(edge.source());
				true
			});
			count += 1;
			if count ==completed.len() {
				return Ok(graph);
			}
		}
		i = i + 1;
	}
	Err(Error::new(ErrorKind::InvalidInput, "Found cycles in build dependencies", None))
}

fn execute_task(cache: &Cache, temp_dir: &Path, worker: usize, message: TaskMessage) -> ResultMessage {
	let args = wincmd::expand_args(&message.task.args, &|name:&str|->Option<String>{env::var(name).ok()});
	let output = execute_compiler(cache, temp_dir, &message.task.exec, &Path::new(&message.task.working_dir), &args);
	ResultMessage {
		index: message.index,
		task: message.task,
		worker: worker,
		result: output,
	}
}

fn execute_compiler(cache: &Cache, temp_dir: &Path, program: &str, cwd: &Path, args: &[String]) -> Result<OutputInfo, Error> {
	let command = CommandInfo {
		program: Path::new(program).to_path_buf(),
		current_dir: Some(cwd.to_path_buf()),
	};
	if Path::new(program).ends_with("cl.exe") {
		let compiler = VsCompiler::new(cache, temp_dir);
		compiler.compile(command, args)
	} else {
		command.to_command()
			.args(&args)
			.output()
			.map(|o| OutputInfo::new(o))
	}
}

fn execute_graph(graph: &Graph<BuildTask, ()>, tx_task: Sender<TaskMessage>, mutex_rx_task: Arc<Mutex<Receiver<TaskMessage>>>, rx_result: Receiver<ResultMessage>) -> Result<Option<i32>, Error> {
	// Run all tasks.
	let result = execute_until_failed(graph, tx_task, &rx_result);
	// Cleanup task queue.
	for _ in mutex_rx_task.lock().unwrap().iter() {
	}
	// Wait for in progress task completion.
	for _ in rx_result.iter() {
	}
	result
}

fn execute_until_failed(graph: &Graph<BuildTask, ()>, tx_task: Sender<TaskMessage>, rx_result: &Receiver<ResultMessage>) -> Result<Option<i32>, Error> {
	let mut completed:Vec<bool> = Vec::new();
	if !graph.each_node(|index: NodeIndex, node:&Node<BuildTask>|->bool {
		let mut has_edges = false;
		graph.each_outgoing_edge(index, |_:EdgeIndex, _:&Edge<()>| -> bool {
			has_edges = true;
			false
		});
		completed.push(false);
		if !has_edges {
			match tx_task.send(TaskMessage{
				index: index,
				task: node.data.clone(),
			}) {
				Ok(_) => true,
				Err(_) => false,
			}
		} else {
			true
		}
	}) {
		return Err(Error::new(ErrorKind::BrokenPipe, "Can't schedule root tasks", None));
	}

	let mut count:usize = 0;
	for message in rx_result.iter() {
		assert!(!completed[message.index.node_id()]);
		count += 1;
		println!("#{} {}/{}: {}", message.worker, count, completed.len(), message.task.title);
		let result = try! (message.result);
		try! (io::stdout().write_all(&result.stdout));
		try! (io::stderr().write_all(&result.stderr));
		if !result.success() {
			return Ok(result.status);
		}
		completed[message.index.node_id()] = true;
		if !graph.each_incoming_edge(message.index, |_:EdgeIndex, edge:&Edge<()>| -> bool {
			let source = edge.source();
			if !completed[source.node_id()] {
				if is_ready(graph, &completed, &source) {
					match tx_task.send(TaskMessage{
						index: source,
						task: graph.node(source).data.clone(),
					}) {
						Ok(_) => {},
						Err(_) => {return false;}
					}
				}
			}
			true
		}) {
			return Err(Error::new(ErrorKind::BrokenPipe, "Can't schedule child task", None));
		};
		if count == completed.len() {
			return Ok(Some(0));
		}
	}
	panic! ("Unexpected end of result pipe");
}

fn is_ready(graph: &Graph<BuildTask, ()>, completed: &Vec<bool>, source: &NodeIndex) -> bool {
	let mut ready = true;
		graph.each_outgoing_edge(*source, |_:EdgeIndex, deps:&Edge<()>| -> bool {
		if !completed[deps.target().node_id()]{
			ready = false;
			false
		} else {
			true
		}
	});
	ready
}
<commit_msg>Add os and architecture to version info<commit_after>#![feature(exit_status)]
#![feature(io)]
#![feature(os)]
extern crate octobuild;
extern crate tempdir;

use octobuild::common::BuildTask;
use octobuild::cache::Cache;
use octobuild::wincmd;
use octobuild::xg;
use octobuild::graph::{Graph, NodeIndex, Node, EdgeIndex, Edge};
use octobuild::vs::compiler::VsCompiler;
use octobuild::compiler::*;

use tempdir::TempDir;

use std::fs::File;
use std::env;
use std::io::{BufReader, Error, ErrorKind, Write};
use std::io;
use std::iter::FromIterator;
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;

#[derive(Debug)]
struct TaskMessage {
	index: NodeIndex,
	task: BuildTask
}

#[derive(Debug)]
struct ResultMessage {
	index: NodeIndex,
	task: BuildTask,
	worker: usize,
	result: Result<OutputInfo, Error>
}

fn main() {
	println!("XGConsole ({}, {}-{}):", octobuild::VERSION, std::env::consts::ARCH, std::env::consts::OS);
	let args = Vec::from_iter(env::args());
	for arg in args.iter() {
		println!("  {}", arg);
	}
	match execute(&args[1..]) {
		Ok(result) => {
			env::set_exit_status(match result {
				Some(r) => r,
				None => 500
			});
		}
		Err(e) => {
			println!("FATAL ERROR: {:?}", e);
			env::set_exit_status(500);
		}
	}
}

fn execute(args: &[String]) -> Result<Option<i32>, Error> {
	let cache = Cache::new();
	let temp_dir = try! (TempDir::new("octobuild"));
	for arg in args.iter() {
		if arg.starts_with("/") {continue}

		let (tx_result, rx_result): (Sender<ResultMessage>, Receiver<ResultMessage>) = channel();
		let (tx_task, rx_task): (Sender<TaskMessage>, Receiver<TaskMessage>) = channel();

		let mutex_rx_task = create_threads(rx_task, tx_result, std::os::num_cpus(), |worker_id:usize| {
			let temp_path = temp_dir.path().to_path_buf();
			let temp_cache = cache.clone();
			move |task:TaskMessage| -> ResultMessage {
				execute_task(&temp_cache, &temp_path, worker_id, task)
			}
		});

		let path = Path::new(arg);
		let file = try! (File::open(&path));
		let graph = try! (xg::parser::parse(BufReader::new(file)));
		let validated_graph = try! (validate_graph(graph));
		match try! (execute_graph(&validated_graph, tx_task, mutex_rx_task, rx_result)) {
			Some(v) if v == 0 => {}
			v => {return Ok(v)}
		}
	}
	cache.cleanup(16 * 1024 * 1024 * 1024);
	Ok(Some(0))
}

fn create_threads<R: 'static + Send, T: 'static + Send, Worker:'static + Fn(T) -> R + Send, Factory:Fn(usize) -> Worker>(rx_task: Receiver<T>, tx_result: Sender<R>, num_cpus: usize, factory: Factory) ->  Arc<Mutex<Receiver<T>>> {
	let mutex_rx_task = Arc::new(Mutex::new(rx_task));
	for cpu_id in 0..num_cpus {
		let local_rx_task = mutex_rx_task.clone();
		let local_tx_result = tx_result.clone();
		let worker = factory(cpu_id);
		thread::spawn(move || {
			loop {
				let task: T;
				match local_rx_task.lock().unwrap().recv() {
					Ok(v) => {task = v;}
					Err(_) => {break;}
				}
				match local_tx_result.send(worker(task)) {
					Ok(_) => {}
					Err(_) => {break;}
				}
			}
		});
	}
	mutex_rx_task
}

fn validate_graph(graph: Graph<BuildTask, ()>) -> Result<Graph<BuildTask, ()>, Error> {
	let mut completed:Vec<bool> = Vec::new();
	let mut queue:Vec<NodeIndex> = Vec::new();
	graph.each_node(|index: NodeIndex, _:&Node<BuildTask>|->bool {
		completed.push(false);
		queue.push(index);
		true
	});
	let mut count:usize = 0;
	let mut i:usize = 0;
	while i < queue.len() {
		let index = queue[i];
		if (!completed[index.node_id()]) && (is_ready(&graph, &completed, &index)) {
			completed[index.node_id()] = true;
			graph.each_incoming_edge(index, |_:EdgeIndex, edge:&Edge<()>| -> bool {
				queue.push(edge.source());
				true
			});
			count += 1;
			if count ==completed.len() {
				return Ok(graph);
			}
		}
		i = i + 1;
	}
	Err(Error::new(ErrorKind::InvalidInput, "Found cycles in build dependencies", None))
}

fn execute_task(cache: &Cache, temp_dir: &Path, worker: usize, message: TaskMessage) -> ResultMessage {
	let args = wincmd::expand_args(&message.task.args, &|name:&str|->Option<String>{env::var(name).ok()});
	let output = execute_compiler(cache, temp_dir, &message.task.exec, &Path::new(&message.task.working_dir), &args);
	ResultMessage {
		index: message.index,
		task: message.task,
		worker: worker,
		result: output,
	}
}

fn execute_compiler(cache: &Cache, temp_dir: &Path, program: &str, cwd: &Path, args: &[String]) -> Result<OutputInfo, Error> {
	let command = CommandInfo {
		program: Path::new(program).to_path_buf(),
		current_dir: Some(cwd.to_path_buf()),
	};
	if Path::new(program).ends_with("cl.exe") {
		let compiler = VsCompiler::new(cache, temp_dir);
		compiler.compile(command, args)
	} else {
		command.to_command()
			.args(&args)
			.output()
			.map(|o| OutputInfo::new(o))
	}
}

fn execute_graph(graph: &Graph<BuildTask, ()>, tx_task: Sender<TaskMessage>, mutex_rx_task: Arc<Mutex<Receiver<TaskMessage>>>, rx_result: Receiver<ResultMessage>) -> Result<Option<i32>, Error> {
	// Run all tasks.
	let result = execute_until_failed(graph, tx_task, &rx_result);
	// Cleanup task queue.
	for _ in mutex_rx_task.lock().unwrap().iter() {
	}
	// Wait for in progress task completion.
	for _ in rx_result.iter() {
	}
	result
}

fn execute_until_failed(graph: &Graph<BuildTask, ()>, tx_task: Sender<TaskMessage>, rx_result: &Receiver<ResultMessage>) -> Result<Option<i32>, Error> {
	let mut completed:Vec<bool> = Vec::new();
	if !graph.each_node(|index: NodeIndex, node:&Node<BuildTask>|->bool {
		let mut has_edges = false;
		graph.each_outgoing_edge(index, |_:EdgeIndex, _:&Edge<()>| -> bool {
			has_edges = true;
			false
		});
		completed.push(false);
		if !has_edges {
			match tx_task.send(TaskMessage{
				index: index,
				task: node.data.clone(),
			}) {
				Ok(_) => true,
				Err(_) => false,
			}
		} else {
			true
		}
	}) {
		return Err(Error::new(ErrorKind::BrokenPipe, "Can't schedule root tasks", None));
	}

	let mut count:usize = 0;
	for message in rx_result.iter() {
		assert!(!completed[message.index.node_id()]);
		count += 1;
		println!("#{} {}/{}: {}", message.worker, count, completed.len(), message.task.title);
		let result = try! (message.result);
		try! (io::stdout().write_all(&result.stdout));
		try! (io::stderr().write_all(&result.stderr));
		if !result.success() {
			return Ok(result.status);
		}
		completed[message.index.node_id()] = true;
		if !graph.each_incoming_edge(message.index, |_:EdgeIndex, edge:&Edge<()>| -> bool {
			let source = edge.source();
			if !completed[source.node_id()] {
				if is_ready(graph, &completed, &source) {
					match tx_task.send(TaskMessage{
						index: source,
						task: graph.node(source).data.clone(),
					}) {
						Ok(_) => {},
						Err(_) => {return false;}
					}
				}
			}
			true
		}) {
			return Err(Error::new(ErrorKind::BrokenPipe, "Can't schedule child task", None));
		};
		if count == completed.len() {
			return Ok(Some(0));
		}
	}
	panic! ("Unexpected end of result pipe");
}

fn is_ready(graph: &Graph<BuildTask, ()>, completed: &Vec<bool>, source: &NodeIndex) -> bool {
	let mut ready = true;
		graph.each_outgoing_edge(*source, |_:EdgeIndex, deps:&Edge<()>| -> bool {
		if !completed[deps.target().node_id()]{
			ready = false;
			false
		} else {
			true
		}
	});
	ready
}
<|endoftext|>


<commit_before>// Copyright (C) 2020 Jordan Petridis <jordan@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use byteorder::{BigEndian, WriteBytesExt};
use crc::crc32;
use nom::{
    self, bytes::complete::take, combinator::map_res, multi::many0, number::complete::be_u16,
    number::complete::be_u32, number::complete::be_u8, sequence::tuple, IResult,
};
use std::borrow::Cow;
use std::io::{self, Write};

#[derive(Debug)]
struct Prelude {
    total_bytes: u32,
    header_bytes: u32,
    prelude_crc: u32,
}

#[derive(Debug)]
pub struct Header {
    pub name: Cow<'static, str>,
    pub value_type: u8,
    pub value: Cow<'static, str>,
}

#[derive(Debug)]
pub struct Packet<'a> {
    prelude: Prelude,
    headers: Vec<Header>,
    pub payload: &'a [u8],
    msg_crc: u32,
}

fn write_header<W: Write>(w: &mut W, header: &Header) -> Result<(), io::Error> {
    w.write_u8(header.name.len() as u8)?;
    w.write_all(header.name.as_bytes())?;
    w.write_u8(header.value_type)?;
    w.write_u16::<BigEndian>(header.value.len() as u16)?;
    w.write_all(header.value.as_bytes())?;
    Ok(())
}

fn write_headers<W: Write>(w: &mut W, headers: &[Header]) -> Result<(), io::Error> {
    for header in headers {
        write_header(w, header)?;
    }
    Ok(())
}

pub fn encode_packet(payload: &[u8], headers: &[Header]) -> Result<Vec<u8>, io::Error> {
    let mut res = Vec::with_capacity(1024);

    // Total length
    res.write_u32::<BigEndian>(0)?;
    // Header length
    res.write_u32::<BigEndian>(0)?;
    // Prelude CRC32 placeholder
    res.write_u32::<BigEndian>(0)?;

    // Write all headers
    write_headers(&mut res, headers)?;

    // Rewrite header length
    let header_length = res.len() - 12;
    (&mut res[4..8]).write_u32::<BigEndian>(header_length as u32)?;

    // Write payload
    res.write_all(payload)?;

    // Rewrite total length
    let total_length = res.len() + 4;
    (&mut res[0..4]).write_u32::<BigEndian>(total_length as u32)?;

    // Rewrite the prelude crc since we replaced the lengths
    let prelude_crc = crc32::checksum_ieee(&res[0..8]);
    (&mut res[8..12]).write_u32::<BigEndian>(prelude_crc)?;

    // Message CRC
    let message_crc = crc32::checksum_ieee(&res);
    res.write_u32::<BigEndian>(message_crc)?;

    Ok(res)
}

fn parse_prelude(input: &[u8]) -> IResult<&[u8], Prelude> {
    map_res(
        tuple((be_u32, be_u32, be_u32)),
        |(total_bytes, header_bytes, prelude_crc)| {
            let sum = crc32::checksum_ieee(&input[0..8]);
            if prelude_crc != sum {
                return Err(nom::Err::Error((
                    "Prelude CRC doesn't match",
                    nom::error::ErrorKind::MapRes,
                )));
            }

            Ok(Prelude {
                total_bytes,
                header_bytes,
                prelude_crc,
            })
        },
    )(input)
}

fn parse_header(input: &[u8]) -> IResult<&[u8], Header> {
    let (input, header_length) = be_u8(input)?;
    let (input, name) = map_res(take(header_length), std::str::from_utf8)(input)?;
    let (input, value_type) = be_u8(input)?;
    let (input, value_length) = be_u16(input)?;
    let (input, value) = map_res(take(value_length), std::str::from_utf8)(input)?;

    let header = Header {
        name: name.to_string().into(),
        value_type,
        value: value.to_string().into(),
    };

    Ok((input, header))
}

pub fn packet_is_exception(packet: &Packet) -> bool {
    for header in &packet.headers {
        if header.name == ":message-type" && header.value_type == 7 {
            return true;
        }
    }

    false
}

pub fn parse_packet(input: &[u8]) -> IResult<&[u8], Packet> {
    let (remainder, prelude) = parse_prelude(input)?;

    // Check the crc of the whole input
    let sum = crc32::checksum_ieee(&input[..input.len() - 4]);
    let (_, msg_crc) = be_u32(&input[input.len() - 4..])?;

    if msg_crc != sum {
        return Err(nom::Err::Error((
            b"Prelude CRC doesn't match",
            nom::error::ErrorKind::MapRes,
        )));
    }

    let (remainder, header_input) = take(prelude.header_bytes)(remainder)?;
    let (_, headers) = many0(parse_header)(header_input)?;

    let payload_length = prelude.total_bytes - prelude.header_bytes - 4 - 12;
    let (remainder, payload) = take(payload_length)(remainder)?;

    // only the message_crc we check before should be remaining now
    assert_eq!(remainder.len(), 4);

    Ok((
        input,
        Packet {
            prelude,
            headers,
            payload,
            msg_crc,
        },
    ))
}
<commit_msg>transcribe: fix exception packet detection<commit_after>// Copyright (C) 2020 Jordan Petridis <jordan@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use byteorder::{BigEndian, WriteBytesExt};
use crc::crc32;
use nom::{
    self, bytes::complete::take, combinator::map_res, multi::many0, number::complete::be_u16,
    number::complete::be_u32, number::complete::be_u8, sequence::tuple, IResult,
};
use std::borrow::Cow;
use std::io::{self, Write};

#[derive(Debug)]
struct Prelude {
    total_bytes: u32,
    header_bytes: u32,
    prelude_crc: u32,
}

#[derive(Debug)]
pub struct Header {
    pub name: Cow<'static, str>,
    pub value_type: u8,
    pub value: Cow<'static, str>,
}

#[derive(Debug)]
pub struct Packet<'a> {
    prelude: Prelude,
    headers: Vec<Header>,
    pub payload: &'a [u8],
    msg_crc: u32,
}

fn write_header<W: Write>(w: &mut W, header: &Header) -> Result<(), io::Error> {
    w.write_u8(header.name.len() as u8)?;
    w.write_all(header.name.as_bytes())?;
    w.write_u8(header.value_type)?;
    w.write_u16::<BigEndian>(header.value.len() as u16)?;
    w.write_all(header.value.as_bytes())?;
    Ok(())
}

fn write_headers<W: Write>(w: &mut W, headers: &[Header]) -> Result<(), io::Error> {
    for header in headers {
        write_header(w, header)?;
    }
    Ok(())
}

pub fn encode_packet(payload: &[u8], headers: &[Header]) -> Result<Vec<u8>, io::Error> {
    let mut res = Vec::with_capacity(1024);

    // Total length
    res.write_u32::<BigEndian>(0)?;
    // Header length
    res.write_u32::<BigEndian>(0)?;
    // Prelude CRC32 placeholder
    res.write_u32::<BigEndian>(0)?;

    // Write all headers
    write_headers(&mut res, headers)?;

    // Rewrite header length
    let header_length = res.len() - 12;
    (&mut res[4..8]).write_u32::<BigEndian>(header_length as u32)?;

    // Write payload
    res.write_all(payload)?;

    // Rewrite total length
    let total_length = res.len() + 4;
    (&mut res[0..4]).write_u32::<BigEndian>(total_length as u32)?;

    // Rewrite the prelude crc since we replaced the lengths
    let prelude_crc = crc32::checksum_ieee(&res[0..8]);
    (&mut res[8..12]).write_u32::<BigEndian>(prelude_crc)?;

    // Message CRC
    let message_crc = crc32::checksum_ieee(&res);
    res.write_u32::<BigEndian>(message_crc)?;

    Ok(res)
}

fn parse_prelude(input: &[u8]) -> IResult<&[u8], Prelude> {
    map_res(
        tuple((be_u32, be_u32, be_u32)),
        |(total_bytes, header_bytes, prelude_crc)| {
            let sum = crc32::checksum_ieee(&input[0..8]);
            if prelude_crc != sum {
                return Err(nom::Err::Error((
                    "Prelude CRC doesn't match",
                    nom::error::ErrorKind::MapRes,
                )));
            }

            Ok(Prelude {
                total_bytes,
                header_bytes,
                prelude_crc,
            })
        },
    )(input)
}

fn parse_header(input: &[u8]) -> IResult<&[u8], Header> {
    let (input, header_length) = be_u8(input)?;
    let (input, name) = map_res(take(header_length), std::str::from_utf8)(input)?;
    let (input, value_type) = be_u8(input)?;
    let (input, value_length) = be_u16(input)?;
    let (input, value) = map_res(take(value_length), std::str::from_utf8)(input)?;

    let header = Header {
        name: name.to_string().into(),
        value_type,
        value: value.to_string().into(),
    };

    Ok((input, header))
}

pub fn packet_is_exception(packet: &Packet) -> bool {
    for header in &packet.headers {
        if header.name == ":message-type" && header.value == "exception" {
            return true;
        }
    }

    false
}

pub fn parse_packet(input: &[u8]) -> IResult<&[u8], Packet> {
    let (remainder, prelude) = parse_prelude(input)?;

    // Check the crc of the whole input
    let sum = crc32::checksum_ieee(&input[..input.len() - 4]);
    let (_, msg_crc) = be_u32(&input[input.len() - 4..])?;

    if msg_crc != sum {
        return Err(nom::Err::Error((
            b"Prelude CRC doesn't match",
            nom::error::ErrorKind::MapRes,
        )));
    }

    let (remainder, header_input) = take(prelude.header_bytes)(remainder)?;
    let (_, headers) = many0(parse_header)(header_input)?;

    let payload_length = prelude.total_bytes - prelude.header_bytes - 4 - 12;
    let (remainder, payload) = take(payload_length)(remainder)?;

    // only the message_crc we check before should be remaining now
    assert_eq!(remainder.len(), 4);

    Ok((
        input,
        Packet {
            prelude,
            headers,
            payload,
            msg_crc,
        },
    ))
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
 *
 * 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.
 */
// TODO, I've implemented this as a seperate entity from the cache, but I wonder if the cache
//  should be the only "front-end" for lookups, where if that misses, then we go to the catalog
//  then, if requested, do a recursive lookup... i.e. the catalog would only point to files.
use std::collections::HashMap;
use std::io;
use std::sync::RwLock;

use server::{Request, RequestHandler, ResponseHandler};
use trust_dns::op::{Edns, Header, LowerQuery, MessageType, OpCode, ResponseCode};
use trust_dns::rr::dnssec::{Algorithm, SupportedAlgorithms};
use trust_dns::rr::rdata::opt::{EdnsCode, EdnsOption};
use trust_dns::rr::{LowerName, RecordType};

use authority::{
    AuthLookup, Authority, LookupRecords, MessageRequest, MessageResponse, MessageResponseBuilder,
    ZoneType,
};

/// Set of authorities, zones, available to this server.
pub struct Catalog {
    authorities: HashMap<LowerName, RwLock<Authority>>,
}

fn send_response<R: ResponseHandler + 'static>(
    response_edns: Option<Edns>,
    mut response: MessageResponse,
    response_handle: R,
) -> io::Result<()> {
    if let Some(mut resp_edns) = response_edns {
        // set edns DAU and DHU
        // send along the algorithms which are supported by this authority
        let mut algorithms = SupportedAlgorithms::new();
        algorithms.set(Algorithm::RSASHA256);
        algorithms.set(Algorithm::ECDSAP256SHA256);
        algorithms.set(Algorithm::ECDSAP384SHA384);
        algorithms.set(Algorithm::ED25519);

        let dau = EdnsOption::DAU(algorithms);
        let dhu = EdnsOption::DHU(algorithms);

        resp_edns.set_option(dau);
        resp_edns.set_option(dhu);

        response.set_edns(resp_edns);
    }

    response_handle.send(response)
}

impl RequestHandler for Catalog {
    /// Determine's what needs to happen given the type of request, i.e. Query or Update.
    ///
    /// # Arguments
    ///
    /// * `request` - the requested action to perform.
    /// * `response_handle` - sink for the response message to be sent
    fn handle_request<'q, 'a, R: ResponseHandler + 'static>(
        &'a self,
        request: &'q Request,
        response_handle: R,
    ) -> io::Result<()> {
        let request_message = &request.message;
        trace!("request: {:?}", request_message);

        let response_edns: Option<Edns>;

        // check if it's edns
        if let Some(req_edns) = request_message.edns() {
            let mut response = MessageResponseBuilder::new(Some(request_message.raw_queries()));
            let mut response_header = Header::default();
            response_header.set_id(request_message.id());

            let mut resp_edns: Edns = Edns::new();

            // check our version against the request
            // TODO: what version are we?
            let our_version = 0;
            resp_edns.set_dnssec_ok(true);
            resp_edns.set_max_payload(req_edns.max_payload().max(512));
            resp_edns.set_version(our_version);

            if req_edns.version() > our_version {
                warn!(
                    "request edns version greater than {}: {}",
                    our_version,
                    req_edns.version()
                );
                response_header.set_response_code(ResponseCode::BADVERS);
                response.edns(resp_edns);

                // TODO: should ResponseHandle consume self?
                return response_handle.send(response.build(response_header));
            }

            response_edns = Some(resp_edns);
        } else {
            response_edns = None;
        }

        match request_message.message_type() {
            // TODO think about threading query lookups for multiple lookups, this could be a huge improvement
            //  especially for recursive lookups
            MessageType::Query => match request_message.op_code() {
                OpCode::Query => {
                    return self.lookup(request_message, response_edns, response_handle)
                }
                OpCode::Update => {
                    return self.update(request_message, response_edns, response_handle)
                }
                c @ _ => {
                    error!("unimplemented op_code: {:?}", c);
                    let response = MessageResponseBuilder::new(Some(request_message.raw_queries()));
                    return response_handle.send(response.error_msg(
                        request_message.id(),
                        request_message.op_code(),
                        ResponseCode::NotImp,
                    ));
                }
            },
            MessageType::Response => {
                warn!(
                    "got a response as a request from id: {}",
                    request_message.id()
                );
                let response = MessageResponseBuilder::new(Some(request_message.raw_queries()));

                return response_handle.send(response.error_msg(
                    request_message.id(),
                    request_message.op_code(),
                    ResponseCode::FormErr,
                ));
            }
        };
    }
}

impl Catalog {
    /// Constructs a new Catalog
    pub fn new() -> Self {
        Catalog {
            authorities: HashMap::new(),
        }
    }

    /// Insert or update a zone authority
    ///
    /// # Arguments
    ///
    /// * `name` - zone name, e.g. example.com.
    /// * `authority` - the zone data
    pub fn upsert(&mut self, name: LowerName, authority: Authority) {
        self.authorities.insert(name, RwLock::new(authority));
    }

    /// Update the zone given the Update request.
    ///
    /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
    ///
    /// ```text
    /// 3.1 - Process Zone Section
    ///
    ///   3.1.1. The Zone Section is checked to see that there is exactly one
    ///   RR therein and that the RR's ZTYPE is SOA, else signal FORMERR to the
    ///   requestor.  Next, the ZNAME and ZCLASS are checked to see if the zone
    ///   so named is one of this server's authority zones, else signal NOTAUTH
    ///   to the requestor.  If the server is a zone slave, the request will be
    ///   forwarded toward the primary master.
    ///
    ///   3.1.2 - Pseudocode For Zone Section Processing
    ///
    ///      if (zcount != 1 || ztype != SOA)
    ///           return (FORMERR)
    ///      if (zone_type(zname, zclass) == SLAVE)
    ///           return forward()
    ///      if (zone_type(zname, zclass) == MASTER)
    ///           return update()
    ///      return (NOTAUTH)
    ///
    ///   Sections 3.2 through 3.8 describe the primary master's behaviour,
    ///   whereas Section 6 describes a forwarder's behaviour.
    ///
    /// 3.8 - Response
    ///
    ///   At the end of UPDATE processing, a response code will be known.  A
    ///   response message is generated by copying the ID and Opcode fields
    ///   from the request, and either copying the ZOCOUNT, PRCOUNT, UPCOUNT,
    ///   and ADCOUNT fields and associated sections, or placing zeros (0) in
    ///   the these "count" fields and not including any part of the original
    ///   update.  The QR bit is set to one (1), and the response is sent back
    ///   to the requestor.  If the requestor used UDP, then the response will
    ///   be sent to the requestor's source UDP port.  If the requestor used
    ///   TCP, then the response will be sent back on the requestor's open TCP
    ///   connection.
    /// ```
    ///
    /// The "request" should be an update formatted message.
    ///  The response will be in the alternate, all 0's format described in RFC 2136 section 3.8
    ///  as this is more efficient.
    ///
    /// # Arguments
    ///
    /// * `request` - an update message
    /// * `response_handle` - sink for the response message to be sent
    pub fn update<'q, R: ResponseHandler + 'static>(
        &self,
        update: &'q MessageRequest,
        response_edns: Option<Edns>,
        response_handle: R,
    ) -> io::Result<()> {
        let response = MessageResponseBuilder::new(None);
        let mut response_header = Header::default();
        response_header.set_id(update.id());
        response_header.set_op_code(OpCode::Update);
        response_header.set_message_type(MessageType::Response);

        let zones: &[LowerQuery] = update.queries();

        // 2.3 - Zone Section
        //
        //  All records to be updated must be in the same zone, and
        //  therefore the Zone Section is allowed to contain exactly one record.
        //  The ZNAME is the zone name, the ZTYPE must be SOA, and the ZCLASS is
        //  the zone's class.
        if zones.len() != 1 || zones[0].query_type() != RecordType::SOA {
            response_header.set_response_code(ResponseCode::FormErr);

            return send_response(
                response_edns,
                response.build(response_header),
                response_handle,
            );
        }

        if let Some(authority) = self.find(zones[0].name()) {
            let mut authority = authority.write().unwrap(); // poison errors should panic...
            match authority.zone_type() {
                ZoneType::Slave => {
                    error!("slave forwarding for update not yet implemented");
                    response_header.set_response_code(ResponseCode::NotImp);

                    return send_response(
                        response_edns,
                        response.build(response_header),
                        response_handle,
                    );
                }
                ZoneType::Master => {
                    let update_result = authority.update(update);
                    match update_result {
                        // successful update
                        Ok(..) => {
                            response_header.set_response_code(ResponseCode::NoError);
                        }
                        Err(response_code) => {
                            response_header.set_response_code(response_code);
                        }
                    }

                    return send_response(
                        response_edns,
                        response.build(response_header),
                        response_handle,
                    );
                }
                _ => {
                    response_header.set_response_code(ResponseCode::NotAuth);

                    return send_response(
                        response_edns,
                        response.build(response_header),
                        response_handle,
                    );
                }
            }
        } else {
            response_header.set_response_code(ResponseCode::NXDomain);

            return send_response(
                response_edns,
                response.build(response_header),
                response_handle,
            );
        }
    }

    /// Checks whether the `Catalog` contains DNS records for `name`
    ///
    /// Use this when you know the exact `LowerName` that was used when
    /// adding an authority and you don't care about the authority it
    /// contains. For public domain names, `LowerName` is usually the
    /// top level domain name like `example.com.`.
    ///
    /// If you do not know the exact domain name to use or you actually
    /// want to use the authority it contains, use `find` instead.
    pub fn contains(&self, name: &LowerName) -> bool {
        self.authorities.contains_key(name)
    }

    /// Given the requested query, lookup and return any matching results.
    ///
    /// # Arguments
    ///
    /// * `request` - the query message.
    /// * `response_handle` - sink for the response message to be sent
    pub fn lookup<'q, R: ResponseHandler + 'static>(
        &self,
        request: &'q MessageRequest,
        response_edns: Option<Edns>,
        response_handle: R,
    ) -> io::Result<()> {
        // TODO: the spec is very unclear on what to do with multiple queries
        //  we will search for each, in the future, maybe make this threaded to respond even faster.
        for query in request.queries() {
            if let Some(ref_authority) = self.find(query.name()) {
                let authority = &ref_authority.read().unwrap(); // poison errors should panic
                info!(
                    "request: {} found authority: {}",
                    request.id(),
                    authority.origin()
                );

                let mut response = MessageResponseBuilder::new(Some(request.raw_queries()));
                let mut response_header = Header::new();
                response_header.set_id(request.id());
                response_header.set_op_code(OpCode::Query);
                response_header.set_message_type(MessageType::Response);

                let (is_dnssec, supported_algorithms) = request.edns().map_or(
                    (false, SupportedAlgorithms::new()),
                    |edns| {
                        let supported_algorithms =
                            if let Some(&EdnsOption::DAU(algs)) = edns.option(&EdnsCode::DAU) {
                                algs
                            } else {
                                debug!("no DAU in request, used default SupportAlgorithms");
                                Default::default()
                            };

                        (edns.dnssec_ok(), supported_algorithms)
                    },
                );

                // log algorithms being requested
                if is_dnssec {
                    info!(
                        "request: {} supported_algs: {}",
                        request.id(),
                        supported_algorithms
                    );
                }

                let records = authority.search(query, is_dnssec, supported_algorithms);

                // setup headers
                //  and add records
                if !records.is_empty() {
                    response_header.set_response_code(ResponseCode::NoError);
                    response_header.set_authoritative(true);
                    response.answers(records);

                    // get the NS records
                    let ns = authority.ns(is_dnssec, supported_algorithms);
                    // chain here to match type below...
                    response.name_servers(ns.chain(LookupRecords::NxDomain));
                } else {
                    // in the not found case it's standard to return the SOA in the authority section
                    //   if the name is in this zone, etc.
                    // see https://tools.ietf.org/html/rfc2308 for proper response construct
                    match records {
                        AuthLookup::NxDomain => {
                            response_header.set_response_code(ResponseCode::NXDomain)
                        }
                        AuthLookup::NameExists => {
                            response_header.set_response_code(ResponseCode::NoError)
                        }
                        _ => panic!(
                            "programming error, should have return NoError with records above"
                        ),
                    };

                    // in the dnssec case, nsec records should exist, we return NoError + NoData + NSec...
                    let ns = if is_dnssec {
                        // get NSEC records
                        let mut nsecs = authority.get_nsec_records(
                            query.name(),
                            is_dnssec,
                            supported_algorithms,
                        );
                        debug!("request: {} non-existent adding nsecs", request.id(),);

                        response_header.set_response_code(ResponseCode::NoError);
                        nsecs
                    } else {
                        // place holder...
                        debug!("request: {} non-existent", request.id());
                        LookupRecords::NxDomain
                    };

                    let soa = authority.soa_secure(is_dnssec, supported_algorithms);
                    let ns = ns.chain(soa);

                    response.name_servers(ns);
                }

                return send_response(
                    response_edns,
                    response.build(response_header),
                    response_handle,
                );
            }
        }

        let response = MessageResponseBuilder::new(Some(request.raw_queries()));
        return send_response(
            response_edns,
            response.error_msg(request.id(), request.op_code(), ResponseCode::NXDomain),
            response_handle,
        );
    }

    /// Recursively searches the catalog for a matching authority
    pub fn find(&self, name: &LowerName) -> Option<&RwLock<Authority>> {
        self.authorities.get(name)
            .or_else(|| {
                let name = name.base_name();
                if !name.is_root() {
                    self.find(&name)
                } else {
                    None
                }
            })
    }
}
<commit_msg>add `Catalog::remove`<commit_after>/*
 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
 *
 * 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.
 */
// TODO, I've implemented this as a seperate entity from the cache, but I wonder if the cache
//  should be the only "front-end" for lookups, where if that misses, then we go to the catalog
//  then, if requested, do a recursive lookup... i.e. the catalog would only point to files.
use std::collections::HashMap;
use std::io;
use std::sync::RwLock;

use server::{Request, RequestHandler, ResponseHandler};
use trust_dns::op::{Edns, Header, LowerQuery, MessageType, OpCode, ResponseCode};
use trust_dns::rr::dnssec::{Algorithm, SupportedAlgorithms};
use trust_dns::rr::rdata::opt::{EdnsCode, EdnsOption};
use trust_dns::rr::{LowerName, RecordType};

use authority::{
    AuthLookup, Authority, LookupRecords, MessageRequest, MessageResponse, MessageResponseBuilder,
    ZoneType,
};

/// Set of authorities, zones, available to this server.
pub struct Catalog {
    authorities: HashMap<LowerName, RwLock<Authority>>,
}

fn send_response<R: ResponseHandler + 'static>(
    response_edns: Option<Edns>,
    mut response: MessageResponse,
    response_handle: R,
) -> io::Result<()> {
    if let Some(mut resp_edns) = response_edns {
        // set edns DAU and DHU
        // send along the algorithms which are supported by this authority
        let mut algorithms = SupportedAlgorithms::new();
        algorithms.set(Algorithm::RSASHA256);
        algorithms.set(Algorithm::ECDSAP256SHA256);
        algorithms.set(Algorithm::ECDSAP384SHA384);
        algorithms.set(Algorithm::ED25519);

        let dau = EdnsOption::DAU(algorithms);
        let dhu = EdnsOption::DHU(algorithms);

        resp_edns.set_option(dau);
        resp_edns.set_option(dhu);

        response.set_edns(resp_edns);
    }

    response_handle.send(response)
}

impl RequestHandler for Catalog {
    /// Determine's what needs to happen given the type of request, i.e. Query or Update.
    ///
    /// # Arguments
    ///
    /// * `request` - the requested action to perform.
    /// * `response_handle` - sink for the response message to be sent
    fn handle_request<'q, 'a, R: ResponseHandler + 'static>(
        &'a self,
        request: &'q Request,
        response_handle: R,
    ) -> io::Result<()> {
        let request_message = &request.message;
        trace!("request: {:?}", request_message);

        let response_edns: Option<Edns>;

        // check if it's edns
        if let Some(req_edns) = request_message.edns() {
            let mut response = MessageResponseBuilder::new(Some(request_message.raw_queries()));
            let mut response_header = Header::default();
            response_header.set_id(request_message.id());

            let mut resp_edns: Edns = Edns::new();

            // check our version against the request
            // TODO: what version are we?
            let our_version = 0;
            resp_edns.set_dnssec_ok(true);
            resp_edns.set_max_payload(req_edns.max_payload().max(512));
            resp_edns.set_version(our_version);

            if req_edns.version() > our_version {
                warn!(
                    "request edns version greater than {}: {}",
                    our_version,
                    req_edns.version()
                );
                response_header.set_response_code(ResponseCode::BADVERS);
                response.edns(resp_edns);

                // TODO: should ResponseHandle consume self?
                return response_handle.send(response.build(response_header));
            }

            response_edns = Some(resp_edns);
        } else {
            response_edns = None;
        }

        match request_message.message_type() {
            // TODO think about threading query lookups for multiple lookups, this could be a huge improvement
            //  especially for recursive lookups
            MessageType::Query => match request_message.op_code() {
                OpCode::Query => {
                    return self.lookup(request_message, response_edns, response_handle)
                }
                OpCode::Update => {
                    return self.update(request_message, response_edns, response_handle)
                }
                c @ _ => {
                    error!("unimplemented op_code: {:?}", c);
                    let response = MessageResponseBuilder::new(Some(request_message.raw_queries()));
                    return response_handle.send(response.error_msg(
                        request_message.id(),
                        request_message.op_code(),
                        ResponseCode::NotImp,
                    ));
                }
            },
            MessageType::Response => {
                warn!(
                    "got a response as a request from id: {}",
                    request_message.id()
                );
                let response = MessageResponseBuilder::new(Some(request_message.raw_queries()));

                return response_handle.send(response.error_msg(
                    request_message.id(),
                    request_message.op_code(),
                    ResponseCode::FormErr,
                ));
            }
        };
    }
}

impl Catalog {
    /// Constructs a new Catalog
    pub fn new() -> Self {
        Catalog {
            authorities: HashMap::new(),
        }
    }

    /// Insert or update a zone authority
    ///
    /// # Arguments
    ///
    /// * `name` - zone name, e.g. example.com.
    /// * `authority` - the zone data
    pub fn upsert(&mut self, name: LowerName, authority: Authority) {
        self.authorities.insert(name, RwLock::new(authority));
    }

    /// Remove a zone from the catalog
    pub fn remove(&mut self, name: &LowerName) -> Option<RwLock<Authority>> {
        self.authorities.remove(name)
    }

    /// Update the zone given the Update request.
    ///
    /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
    ///
    /// ```text
    /// 3.1 - Process Zone Section
    ///
    ///   3.1.1. The Zone Section is checked to see that there is exactly one
    ///   RR therein and that the RR's ZTYPE is SOA, else signal FORMERR to the
    ///   requestor.  Next, the ZNAME and ZCLASS are checked to see if the zone
    ///   so named is one of this server's authority zones, else signal NOTAUTH
    ///   to the requestor.  If the server is a zone slave, the request will be
    ///   forwarded toward the primary master.
    ///
    ///   3.1.2 - Pseudocode For Zone Section Processing
    ///
    ///      if (zcount != 1 || ztype != SOA)
    ///           return (FORMERR)
    ///      if (zone_type(zname, zclass) == SLAVE)
    ///           return forward()
    ///      if (zone_type(zname, zclass) == MASTER)
    ///           return update()
    ///      return (NOTAUTH)
    ///
    ///   Sections 3.2 through 3.8 describe the primary master's behaviour,
    ///   whereas Section 6 describes a forwarder's behaviour.
    ///
    /// 3.8 - Response
    ///
    ///   At the end of UPDATE processing, a response code will be known.  A
    ///   response message is generated by copying the ID and Opcode fields
    ///   from the request, and either copying the ZOCOUNT, PRCOUNT, UPCOUNT,
    ///   and ADCOUNT fields and associated sections, or placing zeros (0) in
    ///   the these "count" fields and not including any part of the original
    ///   update.  The QR bit is set to one (1), and the response is sent back
    ///   to the requestor.  If the requestor used UDP, then the response will
    ///   be sent to the requestor's source UDP port.  If the requestor used
    ///   TCP, then the response will be sent back on the requestor's open TCP
    ///   connection.
    /// ```
    ///
    /// The "request" should be an update formatted message.
    ///  The response will be in the alternate, all 0's format described in RFC 2136 section 3.8
    ///  as this is more efficient.
    ///
    /// # Arguments
    ///
    /// * `request` - an update message
    /// * `response_handle` - sink for the response message to be sent
    pub fn update<'q, R: ResponseHandler + 'static>(
        &self,
        update: &'q MessageRequest,
        response_edns: Option<Edns>,
        response_handle: R,
    ) -> io::Result<()> {
        let response = MessageResponseBuilder::new(None);
        let mut response_header = Header::default();
        response_header.set_id(update.id());
        response_header.set_op_code(OpCode::Update);
        response_header.set_message_type(MessageType::Response);

        let zones: &[LowerQuery] = update.queries();

        // 2.3 - Zone Section
        //
        //  All records to be updated must be in the same zone, and
        //  therefore the Zone Section is allowed to contain exactly one record.
        //  The ZNAME is the zone name, the ZTYPE must be SOA, and the ZCLASS is
        //  the zone's class.
        if zones.len() != 1 || zones[0].query_type() != RecordType::SOA {
            response_header.set_response_code(ResponseCode::FormErr);

            return send_response(
                response_edns,
                response.build(response_header),
                response_handle,
            );
        }

        if let Some(authority) = self.find(zones[0].name()) {
            let mut authority = authority.write().unwrap(); // poison errors should panic...
            match authority.zone_type() {
                ZoneType::Slave => {
                    error!("slave forwarding for update not yet implemented");
                    response_header.set_response_code(ResponseCode::NotImp);

                    return send_response(
                        response_edns,
                        response.build(response_header),
                        response_handle,
                    );
                }
                ZoneType::Master => {
                    let update_result = authority.update(update);
                    match update_result {
                        // successful update
                        Ok(..) => {
                            response_header.set_response_code(ResponseCode::NoError);
                        }
                        Err(response_code) => {
                            response_header.set_response_code(response_code);
                        }
                    }

                    return send_response(
                        response_edns,
                        response.build(response_header),
                        response_handle,
                    );
                }
                _ => {
                    response_header.set_response_code(ResponseCode::NotAuth);

                    return send_response(
                        response_edns,
                        response.build(response_header),
                        response_handle,
                    );
                }
            }
        } else {
            response_header.set_response_code(ResponseCode::NXDomain);

            return send_response(
                response_edns,
                response.build(response_header),
                response_handle,
            );
        }
    }

    /// Checks whether the `Catalog` contains DNS records for `name`
    ///
    /// Use this when you know the exact `LowerName` that was used when
    /// adding an authority and you don't care about the authority it
    /// contains. For public domain names, `LowerName` is usually the
    /// top level domain name like `example.com.`.
    ///
    /// If you do not know the exact domain name to use or you actually
    /// want to use the authority it contains, use `find` instead.
    pub fn contains(&self, name: &LowerName) -> bool {
        self.authorities.contains_key(name)
    }

    /// Given the requested query, lookup and return any matching results.
    ///
    /// # Arguments
    ///
    /// * `request` - the query message.
    /// * `response_handle` - sink for the response message to be sent
    pub fn lookup<'q, R: ResponseHandler + 'static>(
        &self,
        request: &'q MessageRequest,
        response_edns: Option<Edns>,
        response_handle: R,
    ) -> io::Result<()> {
        // TODO: the spec is very unclear on what to do with multiple queries
        //  we will search for each, in the future, maybe make this threaded to respond even faster.
        for query in request.queries() {
            if let Some(ref_authority) = self.find(query.name()) {
                let authority = &ref_authority.read().unwrap(); // poison errors should panic
                info!(
                    "request: {} found authority: {}",
                    request.id(),
                    authority.origin()
                );

                let mut response = MessageResponseBuilder::new(Some(request.raw_queries()));
                let mut response_header = Header::new();
                response_header.set_id(request.id());
                response_header.set_op_code(OpCode::Query);
                response_header.set_message_type(MessageType::Response);

                let (is_dnssec, supported_algorithms) = request.edns().map_or(
                    (false, SupportedAlgorithms::new()),
                    |edns| {
                        let supported_algorithms =
                            if let Some(&EdnsOption::DAU(algs)) = edns.option(&EdnsCode::DAU) {
                                algs
                            } else {
                                debug!("no DAU in request, used default SupportAlgorithms");
                                Default::default()
                            };

                        (edns.dnssec_ok(), supported_algorithms)
                    },
                );

                // log algorithms being requested
                if is_dnssec {
                    info!(
                        "request: {} supported_algs: {}",
                        request.id(),
                        supported_algorithms
                    );
                }

                let records = authority.search(query, is_dnssec, supported_algorithms);

                // setup headers
                //  and add records
                if !records.is_empty() {
                    response_header.set_response_code(ResponseCode::NoError);
                    response_header.set_authoritative(true);
                    response.answers(records);

                    // get the NS records
                    let ns = authority.ns(is_dnssec, supported_algorithms);
                    // chain here to match type below...
                    response.name_servers(ns.chain(LookupRecords::NxDomain));
                } else {
                    // in the not found case it's standard to return the SOA in the authority section
                    //   if the name is in this zone, etc.
                    // see https://tools.ietf.org/html/rfc2308 for proper response construct
                    match records {
                        AuthLookup::NxDomain => {
                            response_header.set_response_code(ResponseCode::NXDomain)
                        }
                        AuthLookup::NameExists => {
                            response_header.set_response_code(ResponseCode::NoError)
                        }
                        _ => panic!(
                            "programming error, should have return NoError with records above"
                        ),
                    };

                    // in the dnssec case, nsec records should exist, we return NoError + NoData + NSec...
                    let ns = if is_dnssec {
                        // get NSEC records
                        let mut nsecs = authority.get_nsec_records(
                            query.name(),
                            is_dnssec,
                            supported_algorithms,
                        );
                        debug!("request: {} non-existent adding nsecs", request.id(),);

                        response_header.set_response_code(ResponseCode::NoError);
                        nsecs
                    } else {
                        // place holder...
                        debug!("request: {} non-existent", request.id());
                        LookupRecords::NxDomain
                    };

                    let soa = authority.soa_secure(is_dnssec, supported_algorithms);
                    let ns = ns.chain(soa);

                    response.name_servers(ns);
                }

                return send_response(
                    response_edns,
                    response.build(response_header),
                    response_handle,
                );
            }
        }

        let response = MessageResponseBuilder::new(Some(request.raw_queries()));
        return send_response(
            response_edns,
            response.error_msg(request.id(), request.op_code(), ResponseCode::NXDomain),
            response_handle,
        );
    }

    /// Recursively searches the catalog for a matching authority
    pub fn find(&self, name: &LowerName) -> Option<&RwLock<Authority>> {
        self.authorities.get(name)
            .or_else(|| {
                let name = name.base_name();
                if !name.is_root() {
                    self.find(&name)
                } else {
                    None
                }
            })
    }
}
<|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/. */

use font::FontHandleMethods;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use servo_atoms::Atom;
use std::fmt::{Debug, Error, Formatter};
use std::io::Error as IoError;
use std::sync::{Arc, Weak};
use std::u32;
use style::computed_values::{font_stretch, font_weight};

/// Describes how to select a font from a given family. This is very basic at the moment and needs
/// to be expanded or refactored when we support more of the font styling parameters.
///
/// NB: If you change this, you will need to update `style::properties::compute_font_hash()`.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Serialize)]
pub struct FontTemplateDescriptor {
    pub weight: font_weight::T,
    pub stretch: font_stretch::T,
    pub italic: bool,
}

impl FontTemplateDescriptor {
    #[inline]
    pub fn new(weight: font_weight::T, stretch: font_stretch::T, italic: bool)
               -> FontTemplateDescriptor {
        FontTemplateDescriptor {
            weight: weight,
            stretch: stretch,
            italic: italic,
        }
    }

    /// Returns a score indicating how far apart visually the two font descriptors are. This is
    /// used for fuzzy font selection.
    ///
    /// The smaller the score, the better the fonts match. 0 indicates an exact match. This must
    /// be commutative (distance(A, B) == distance(B, A)).
    #[inline]
    fn distance_from(&self, other: &FontTemplateDescriptor) -> u32 {
        if self.stretch != other.stretch || self.italic != other.italic {
            // A value higher than all weights.
            return 1000
        }
        ((self.weight.0 as i16) - (other.weight.0 as i16)).abs() as u32
    }
}

impl PartialEq for FontTemplateDescriptor {
    fn eq(&self, other: &FontTemplateDescriptor) -> bool {
        self.weight == other.weight && self.stretch == other.stretch && self.italic == other.italic
    }
}

/// This describes all the information needed to create
/// font instance handles. It contains a unique
/// FontTemplateData structure that is platform specific.
pub struct FontTemplate {
    identifier: Atom,
    descriptor: Option<FontTemplateDescriptor>,
    weak_ref: Option<Weak<FontTemplateData>>,
    // GWTODO: Add code path to unset the strong_ref for web fonts!
    strong_ref: Option<Arc<FontTemplateData>>,
    is_valid: bool,
}

impl Debug for FontTemplate {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        self.identifier.fmt(f)
    }
}

/// Holds all of the template information for a font that
/// is common, regardless of the number of instances of
/// this font handle per thread.
impl FontTemplate {
    pub fn new(identifier: Atom, maybe_bytes: Option<Vec<u8>>) -> Result<FontTemplate, IoError> {
        let maybe_data = match maybe_bytes {
            Some(_) => Some(FontTemplateData::new(identifier.clone(), maybe_bytes)?),
            None => None,
        };

        let maybe_strong_ref = match maybe_data {
            Some(data) => Some(Arc::new(data)),
            None => None,
        };

        let maybe_weak_ref = match maybe_strong_ref {
            Some(ref strong_ref) => Some(Arc::downgrade(strong_ref)),
            None => None,
        };

        Ok(FontTemplate {
            identifier: identifier,
            descriptor: None,
            weak_ref: maybe_weak_ref,
            strong_ref: maybe_strong_ref,
            is_valid: true,
        })
    }

    pub fn identifier(&self) -> &Atom {
        &self.identifier
    }

    /// Get the data for creating a font if it matches a given descriptor.
    pub fn data_for_descriptor(&mut self,
                               fctx: &FontContextHandle,
                               requested_desc: &FontTemplateDescriptor)
                               -> Option<Arc<FontTemplateData>> {
        // The font template data can be unloaded when nothing is referencing
        // it (via the Weak reference to the Arc above). However, if we have
        // already loaded a font, store the style information about it separately,
        // so that we can do font matching against it again in the future
        // without having to reload the font (unless it is an actual match).
        match self.descriptor {
            Some(actual_desc) if *requested_desc == actual_desc => self.data().ok(),
            Some(_) => None,
            None => {
                if self.instantiate(fctx).is_err() {
                    return None
                }

                if self.descriptor
                       .as_ref()
                       .expect("Instantiation succeeded but no descriptor?") == requested_desc {
                    self.data().ok()
                } else {
                    None
                }
            }
        }
    }

    /// Returns the font data along with the distance between this font's descriptor and the given
    /// descriptor, if the font can be loaded.
    pub fn data_for_approximate_descriptor(&mut self,
                                           font_context: &FontContextHandle,
                                           requested_descriptor: &FontTemplateDescriptor)
                                           -> Option<(Arc<FontTemplateData>, u32)> {
        match self.descriptor {
            Some(actual_descriptor) => {
                self.data().ok().map(|data| {
                    (data, actual_descriptor.distance_from(requested_descriptor))
                })
            }
            None => {
                if self.instantiate(font_context).is_ok() {
                    let distance = self.descriptor
                                       .as_ref()
                                       .expect("Instantiation successful but no descriptor?")
                                       .distance_from(requested_descriptor);
                    self.data().ok().map(|data| (data, distance))
                } else {
                    None
                }
            }
        }
    }

    fn instantiate(&mut self, font_context: &FontContextHandle) -> Result<(), ()> {
        if !self.is_valid {
            return Err(())
        }

        let data = self.data().map_err(|_| ())?;
        let handle: Result<FontHandle, ()> = FontHandleMethods::new_from_template(font_context,
                                                                                data,
                                                                                None);
        self.is_valid = handle.is_ok();
        let handle = handle?;
        self.descriptor = Some(FontTemplateDescriptor::new(handle.boldness(),
                                                           handle.stretchiness(),
                                                           handle.is_italic()));
        Ok(())
    }

    /// Get the data for creating a font.
    pub fn get(&mut self) -> Option<Arc<FontTemplateData>> {
        if self.is_valid {
            self.data().ok()
        } else {
            None
        }
    }

    /// Get the font template data. If any strong references still
    /// exist, it will return a clone, otherwise it will load the
    /// font data and store a weak reference to it internally.
    pub fn data(&mut self) -> Result<Arc<FontTemplateData>, IoError> {
        let maybe_data = match self.weak_ref {
            Some(ref data) => data.upgrade(),
            None => None,
        };

        if let Some(data) = maybe_data {
            return Ok(data)
        }

        assert!(self.strong_ref.is_none());
        let template_data = Arc::new(FontTemplateData::new(self.identifier.clone(), None)?);
        self.weak_ref = Some(Arc::downgrade(&template_data));
        Ok(template_data)
    }
}
<commit_msg>Change selection of the best font for a style<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/. */

use font::FontHandleMethods;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use servo_atoms::Atom;
use std::fmt::{Debug, Error, Formatter};
use std::io::Error as IoError;
use std::sync::{Arc, Weak};
use std::u32;
use style::computed_values::{font_stretch, font_weight};

/// Describes how to select a font from a given family. This is very basic at the moment and needs
/// to be expanded or refactored when we support more of the font styling parameters.
///
/// NB: If you change this, you will need to update `style::properties::compute_font_hash()`.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Serialize)]
pub struct FontTemplateDescriptor {
    pub weight: font_weight::T,
    pub stretch: font_stretch::T,
    pub italic: bool,
}

impl FontTemplateDescriptor {
    #[inline]
    pub fn new(weight: font_weight::T, stretch: font_stretch::T, italic: bool)
               -> FontTemplateDescriptor {
        FontTemplateDescriptor {
            weight: weight,
            stretch: stretch,
            italic: italic,
        }
    }

    /// Returns a score indicating how far apart visually the two font descriptors are. This is
    /// used for fuzzy font selection.
    ///
    /// The smaller the score, the better the fonts match. 0 indicates an exact match. This must
    /// be commutative (distance(A, B) == distance(B, A)).
    ///
    /// The policy is to care most about differences in italicness, then weight, then stretch
    #[inline]
    fn distance_from(&self, other: &FontTemplateDescriptor) -> u32 {
        let italic_part = if self.italic == other.italic { 0 } else { 1000 };
        // 0 <= weightPart <= 800
        let weight_part = ((self.weight.0 as i16) - (other.weight.0 as i16)).abs() as u32;
        // 0 <= stretchPart <= 8
        let stretch_part = (self.stretch_number() - other.stretch_number()).abs() as u32;
        italic_part + weight_part + stretch_part
    }

    /// Returns a number between 1 and 9 for the stretch property.
    /// 1 is ultra_condensed, 5 is normal, and 9 is ultra_expanded
    #[inline]
    fn stretch_number(&self) -> i32 {
        match self.stretch {
            font_stretch::T::UltraCondensed => 1,
            font_stretch::T::ExtraCondensed => 2,
            font_stretch::T::Condensed      => 3,
            font_stretch::T::SemiCondensed  => 4,
            font_stretch::T::Normal         => 5,
            font_stretch::T::SemiExpanded   => 6,
            font_stretch::T::Expanded       => 7,
            font_stretch::T::ExtraExpanded  => 8,
            font_stretch::T::UltraExpanded  => 9,
        }
    }
}

impl PartialEq for FontTemplateDescriptor {
    fn eq(&self, other: &FontTemplateDescriptor) -> bool {
        self.weight == other.weight && self.stretch == other.stretch && self.italic == other.italic
    }
}

/// This describes all the information needed to create
/// font instance handles. It contains a unique
/// FontTemplateData structure that is platform specific.
pub struct FontTemplate {
    identifier: Atom,
    descriptor: Option<FontTemplateDescriptor>,
    weak_ref: Option<Weak<FontTemplateData>>,
    // GWTODO: Add code path to unset the strong_ref for web fonts!
    strong_ref: Option<Arc<FontTemplateData>>,
    is_valid: bool,
}

impl Debug for FontTemplate {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        self.identifier.fmt(f)
    }
}

/// Holds all of the template information for a font that
/// is common, regardless of the number of instances of
/// this font handle per thread.
impl FontTemplate {
    pub fn new(identifier: Atom, maybe_bytes: Option<Vec<u8>>) -> Result<FontTemplate, IoError> {
        let maybe_data = match maybe_bytes {
            Some(_) => Some(FontTemplateData::new(identifier.clone(), maybe_bytes)?),
            None => None,
        };

        let maybe_strong_ref = match maybe_data {
            Some(data) => Some(Arc::new(data)),
            None => None,
        };

        let maybe_weak_ref = match maybe_strong_ref {
            Some(ref strong_ref) => Some(Arc::downgrade(strong_ref)),
            None => None,
        };

        Ok(FontTemplate {
            identifier: identifier,
            descriptor: None,
            weak_ref: maybe_weak_ref,
            strong_ref: maybe_strong_ref,
            is_valid: true,
        })
    }

    pub fn identifier(&self) -> &Atom {
        &self.identifier
    }

    /// Get the data for creating a font if it matches a given descriptor.
    pub fn data_for_descriptor(&mut self,
                               fctx: &FontContextHandle,
                               requested_desc: &FontTemplateDescriptor)
                               -> Option<Arc<FontTemplateData>> {
        // The font template data can be unloaded when nothing is referencing
        // it (via the Weak reference to the Arc above). However, if we have
        // already loaded a font, store the style information about it separately,
        // so that we can do font matching against it again in the future
        // without having to reload the font (unless it is an actual match).
        match self.descriptor {
            Some(actual_desc) if *requested_desc == actual_desc => self.data().ok(),
            Some(_) => None,
            None => {
                if self.instantiate(fctx).is_err() {
                    return None
                }

                if self.descriptor
                       .as_ref()
                       .expect("Instantiation succeeded but no descriptor?") == requested_desc {
                    self.data().ok()
                } else {
                    None
                }
            }
        }
    }

    /// Returns the font data along with the distance between this font's descriptor and the given
    /// descriptor, if the font can be loaded.
    pub fn data_for_approximate_descriptor(&mut self,
                                           font_context: &FontContextHandle,
                                           requested_descriptor: &FontTemplateDescriptor)
                                           -> Option<(Arc<FontTemplateData>, u32)> {
        match self.descriptor {
            Some(actual_descriptor) => {
                self.data().ok().map(|data| {
                    (data, actual_descriptor.distance_from(requested_descriptor))
                })
            }
            None => {
                if self.instantiate(font_context).is_ok() {
                    let distance = self.descriptor
                                       .as_ref()
                                       .expect("Instantiation successful but no descriptor?")
                                       .distance_from(requested_descriptor);
                    self.data().ok().map(|data| (data, distance))
                } else {
                    None
                }
            }
        }
    }

    fn instantiate(&mut self, font_context: &FontContextHandle) -> Result<(), ()> {
        if !self.is_valid {
            return Err(())
        }

        let data = self.data().map_err(|_| ())?;
        let handle: Result<FontHandle, ()> = FontHandleMethods::new_from_template(font_context,
                                                                                data,
                                                                                None);
        self.is_valid = handle.is_ok();
        let handle = handle?;
        self.descriptor = Some(FontTemplateDescriptor::new(handle.boldness(),
                                                           handle.stretchiness(),
                                                           handle.is_italic()));
        Ok(())
    }

    /// Get the data for creating a font.
    pub fn get(&mut self) -> Option<Arc<FontTemplateData>> {
        if self.is_valid {
            self.data().ok()
        } else {
            None
        }
    }

    /// Get the font template data. If any strong references still
    /// exist, it will return a clone, otherwise it will load the
    /// font data and store a weak reference to it internally.
    pub fn data(&mut self) -> Result<Arc<FontTemplateData>, IoError> {
        let maybe_data = match self.weak_ref {
            Some(ref data) => data.upgrade(),
            None => None,
        };

        if let Some(data) = maybe_data {
            return Ok(data)
        }

        assert!(self.strong_ref.is_none());
        let template_data = Arc::new(FontTemplateData::new(self.identifier.clone(), None)?);
        self.weak_ref = Some(Arc::downgrade(&template_data));
        Ok(template_data)
    }
}
<|endoftext|>