<commit_before>// Copyright 2014 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.

//! A simple DOM where every node is owned by its parent.
//!
//! Since ownership is more complicated during parsing, we actually
//! build a different type and then transmute to the public `Node`.
//! This is believed to be memory safe, but if you want to be extra
//! careful you can use `RcDom` instead.

use core::prelude::*;

use sink::common::{NodeEnum, Document, Doctype, Text, Comment, Element};

use tokenizer::Attribute;
use tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText};
use tree_builder;
use serialize::{Serializable, Serializer};
use driver::ParseResult;

use core::ty::Unsafe;
use core::default::Default;
use core::mem::transmute;
use core::kinds::marker;
use core::mem;
use alloc::boxed::Box;
use collections::{MutableSeq, Set, MutableSet};
use collections::vec::Vec;
use collections::string::String;
use collections::str::MaybeOwned;
use std::io::{Writer, IoResult};
use std::collections::HashSet;

use string_cache::Atom;

/// The internal type we use for nodes during parsing.
struct SquishyNode {
    node: NodeEnum,
    parent: Handle,
    children: Vec<Handle>,
}

impl SquishyNode {
    fn new(node: NodeEnum) -> SquishyNode {
        SquishyNode {
            node: node,
            parent: Handle::null(),
            children: vec!(),
        }
    }
}

struct Handle {
    ptr: *const Unsafe<SquishyNode>,
    no_send: marker::NoSend,
    no_sync: marker::NoSync,
}

impl Handle {
    fn new(ptr: *const Unsafe<SquishyNode>) -> Handle {
        Handle {
            ptr: ptr,
            no_send: marker::NoSend,
            no_sync: marker::NoSync,
        }
    }

    fn null() -> Handle {
        Handle::new(RawPtr::null())
    }

    fn is_null(&self) -> bool {
        self.ptr.is_null()
    }
}

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

impl Eq for Handle { }

impl Clone for Handle {
    fn clone(&self) -> Handle {
        Handle::new(self.ptr)
    }
}

// The safety of `Deref` and `DerefMut` depends on the invariant that `Handle`s
// can't escape the `Sink`, because nodes are deallocated by consuming the
// `Sink`.

impl DerefMut<SquishyNode> for Handle {
    fn deref_mut<'a>(&'a mut self) -> &'a mut SquishyNode {
        unsafe {
            transmute::<_, &'a mut SquishyNode>((*self.ptr).get())
        }
    }
}

impl Deref<SquishyNode> for Handle {
    fn deref<'a>(&'a self) -> &'a SquishyNode {
        unsafe {
            transmute::<_, &'a SquishyNode>((*self.ptr).get())
        }
    }
}

fn append(mut new_parent: Handle, mut child: Handle) {
    new_parent.children.push(child);
    let parent = &mut child.parent;
    assert!(parent.is_null());
    *parent = new_parent
}

fn get_parent_and_index(mut child: Handle) -> Option<(Handle, uint)> {
    if child.parent.is_null() {
        return None;
    }

    let to_find = child;
    match child.parent.children.iter().enumerate().find(|&(_, n)| *n == to_find) {
        Some((i, _)) => Some((child.parent, i)),
        None => fail!("have parent but couldn't find in parent's children!"),
    }
}

fn append_to_existing_text(mut prev: Handle, text: &str) -> bool {
    match prev.deref_mut().node {
        Text(ref mut existing) => {
            existing.push_str(text);
            true
        }
        _ => false,
    }
}

pub struct Sink {
    nodes: Vec<Box<Unsafe<SquishyNode>>>,
    document: Handle,
    errors: Vec<MaybeOwned<'static>>,
    quirks_mode: QuirksMode,
}

impl Default for Sink {
    fn default() -> Sink {
        let mut sink = Sink {
            nodes: vec!(),
            document: Handle::null(),
            errors: vec!(),
            quirks_mode: tree_builder::NoQuirks,
        };
        sink.document = sink.new_node(Document);
        sink
    }
}

impl Sink {
    fn new_node(&mut self, node: NodeEnum) -> Handle {
        self.nodes.push(box Unsafe::new(SquishyNode::new(node)));
        let ptr: *const Unsafe<SquishyNode> = &**self.nodes.last().unwrap();
        Handle::new(ptr)
    }
}

impl TreeSink<Handle> for Sink {
    fn parse_error(&mut self, msg: MaybeOwned<'static>) {
        self.errors.push(msg);
    }

    fn get_document(&mut self) -> Handle {
        self.document
    }

    fn set_quirks_mode(&mut self, mode: QuirksMode) {
        self.quirks_mode = mode;
    }

    fn same_node(&self, x: Handle, y: Handle) -> bool {
        x == y
    }

    fn elem_name(&self, target: Handle) -> (Atom, Atom) {
        match target.node {
            Element(ref name, _) => (ns!(HTML), name.clone()),
            _ => fail!("not an element!"),
        }
    }

    fn create_element(&mut self, ns: Atom, name: Atom, attrs: Vec<Attribute>) -> Handle {
        assert!(ns == ns!(HTML));
        self.new_node(Element(name, attrs))
    }

    fn create_comment(&mut self, text: String) -> Handle {
        self.new_node(Comment(text))
    }

    fn append(&mut self, mut parent: Handle, child: NodeOrText<Handle>) {
        // Append to an existing Text node if we have one.
        match child {
            AppendText(ref text) => match parent.children.last() {
                Some(h) => if append_to_existing_text(*h, text.as_slice()) { return; },
                _ => (),
            },
            _ => (),
        }

        append(parent, match child {
            AppendText(text) => self.new_node(Text(text)),
            AppendNode(node) => node
        });
    }

    fn append_before_sibling(&mut self,
            sibling: Handle,
            child: NodeOrText<Handle>) -> Result<(), NodeOrText<Handle>> {
        let (mut parent, i) = unwrap_or_return!(get_parent_and_index(sibling), Err(child));

        let mut child = match (child, i) {
            // No previous node.
            (AppendText(text), 0) => self.new_node(Text(text)),

            // Look for a text node before the insertion point.
            (AppendText(text), i) => {
                let prev = parent.children[i-1];
                if append_to_existing_text(prev, text.as_slice()) {
                    return Ok(());
                }
                self.new_node(Text(text))
            }

            // The tree builder promises we won't have a text node after
            // the insertion point.

            // Any other kind of node.
            (AppendNode(node), _) => node,
        };

        if !child.parent.is_null() {
            self.remove_from_parent(child);
        }

        child.parent = parent;
        parent.children.insert(i, child);
        Ok(())
    }

    fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {
        append(self.document, self.new_node(Doctype(name, public_id, system_id)));
    }

    fn add_attrs_if_missing(&mut self, mut target: Handle, mut attrs: Vec<Attribute>) {
        let existing = match target.deref_mut().node {
            Element(_, ref mut attrs) => attrs,
            _ => return,
        };

        // FIXME: quadratic time
        attrs.retain(|attr|
            !existing.iter().any(|e| e.name == attr.name));
        existing.extend(attrs.into_iter());
    }

    fn remove_from_parent(&mut self, mut target: Handle) {
        let (mut parent, i) = unwrap_or_return!(get_parent_and_index(target), ());
        parent.children.remove(i).expect("not found!");
        target.parent = Handle::null();
    }

    fn mark_script_already_started(&mut self, _node: Handle) { }
}

pub struct Node {
    pub node: NodeEnum,
    _parent_not_accessible: uint,
    pub children: Vec<Box<Node>>,
}

pub struct OwnedDom {
    pub document: Box<Node>,
    pub errors: Vec<MaybeOwned<'static>>,
    pub quirks_mode: QuirksMode,
}

impl ParseResult<Sink> for OwnedDom {
    fn get_result(sink: Sink) -> OwnedDom {
        fn walk(live: &mut HashSet<uint>, node: Handle) {
            live.insert(node.ptr as uint);
            for &child in node.deref().children.iter() {
                walk(live, child);
            }
        }

        // Collect addresses of all the nodes that made it into the final tree.
        let mut live = HashSet::new();
        walk(&mut live, sink.document);

        // Forget about the nodes in the final tree; they will be owned by
        // their parent.  In the process of iterating we drop all nodes that
        // aren't in the tree.
        for node in sink.nodes.into_iter() {
            let ptr: *const Unsafe<SquishyNode> = &*node;
            if live.contains(&(ptr as uint)) {
                unsafe {
                    mem::forget(node);
                }
            }
        }

        let old_addrs = addrs_of!(sink.document: node, parent, children);

        // Transmute the root to a Node, finalizing the transfer of ownership.
        let document = unsafe {
            mem::transmute::<*const Unsafe<SquishyNode>, Box<Node>>(sink.document.ptr)
        };

        // FIXME: do this assertion statically
        let new_addrs = addrs_of!(document: node, _parent_not_accessible, children);
        assert_eq!(old_addrs, new_addrs);

        OwnedDom {
            document: document,
            errors: sink.errors,
            quirks_mode: sink.quirks_mode,
        }
    }
}

impl Serializable for Node {
    fn serialize<'wr, Wr: Writer>(&self,
            serializer: &mut Serializer<'wr, Wr>,
            incl_self: bool) -> IoResult<()> {

        match (incl_self, &self.node) {
            (_, &Element(ref name, ref attrs)) => {
                if incl_self {
                    try!(serializer.start_elem(ns!(HTML), name.clone(),
                        attrs.iter().map(|at| (&at.name, at.value.as_slice()))));
                }

                for child in self.children.iter() {
                    try!(child.serialize(serializer, true));
                }

                if incl_self {
                    try!(serializer.end_elem(ns!(HTML), name.clone()));
                }
                Ok(())
            }

            (false, &Document) => {
                for child in self.children.iter() {
                    try!(child.serialize(serializer, true));
                }
                Ok(())
            }

            (false, _) => Ok(()),

            (true, &Doctype(ref name, _, _)) => serializer.write_doctype(name.as_slice()),
            (true, &Text(ref text)) => serializer.write_text(text.as_slice()),
            (true, &Comment(ref text)) => serializer.write_comment(text.as_slice()),

            (true, &Document) => fail!("Can't serialize Document node itself"),
        }
    }
}
<commit_msg>Add a warning on OwnedDom<commit_after>// Copyright 2014 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// 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.

#![experimental="This module uses unsafe code, has not been thoroughly \
                 audited, and the performance gains vs. RcDom have not \
                 been demonstrated"]

//! A simple DOM where every node is owned by its parent.
//!
//! Since ownership is more complicated during parsing, we actually
//! build a different type and then transmute to the public `Node`.
//! This is believed to be memory safe, but if you want to be extra
//! careful you can use `RcDom` instead.

use core::prelude::*;

use sink::common::{NodeEnum, Document, Doctype, Text, Comment, Element};

use tokenizer::Attribute;
use tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText};
use tree_builder;
use serialize::{Serializable, Serializer};
use driver::ParseResult;

use core::ty::Unsafe;
use core::default::Default;
use core::mem::transmute;
use core::kinds::marker;
use core::mem;
use alloc::boxed::Box;
use collections::{MutableSeq, Set, MutableSet};
use collections::vec::Vec;
use collections::string::String;
use collections::str::MaybeOwned;
use std::io::{Writer, IoResult};
use std::collections::HashSet;

use string_cache::Atom;

/// The internal type we use for nodes during parsing.
struct SquishyNode {
    node: NodeEnum,
    parent: Handle,
    children: Vec<Handle>,
}

impl SquishyNode {
    fn new(node: NodeEnum) -> SquishyNode {
        SquishyNode {
            node: node,
            parent: Handle::null(),
            children: vec!(),
        }
    }
}

struct Handle {
    ptr: *const Unsafe<SquishyNode>,
    no_send: marker::NoSend,
    no_sync: marker::NoSync,
}

impl Handle {
    fn new(ptr: *const Unsafe<SquishyNode>) -> Handle {
        Handle {
            ptr: ptr,
            no_send: marker::NoSend,
            no_sync: marker::NoSync,
        }
    }

    fn null() -> Handle {
        Handle::new(RawPtr::null())
    }

    fn is_null(&self) -> bool {
        self.ptr.is_null()
    }
}

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

impl Eq for Handle { }

impl Clone for Handle {
    fn clone(&self) -> Handle {
        Handle::new(self.ptr)
    }
}

// The safety of `Deref` and `DerefMut` depends on the invariant that `Handle`s
// can't escape the `Sink`, because nodes are deallocated by consuming the
// `Sink`.

impl DerefMut<SquishyNode> for Handle {
    fn deref_mut<'a>(&'a mut self) -> &'a mut SquishyNode {
        unsafe {
            transmute::<_, &'a mut SquishyNode>((*self.ptr).get())
        }
    }
}

impl Deref<SquishyNode> for Handle {
    fn deref<'a>(&'a self) -> &'a SquishyNode {
        unsafe {
            transmute::<_, &'a SquishyNode>((*self.ptr).get())
        }
    }
}

fn append(mut new_parent: Handle, mut child: Handle) {
    new_parent.children.push(child);
    let parent = &mut child.parent;
    assert!(parent.is_null());
    *parent = new_parent
}

fn get_parent_and_index(mut child: Handle) -> Option<(Handle, uint)> {
    if child.parent.is_null() {
        return None;
    }

    let to_find = child;
    match child.parent.children.iter().enumerate().find(|&(_, n)| *n == to_find) {
        Some((i, _)) => Some((child.parent, i)),
        None => fail!("have parent but couldn't find in parent's children!"),
    }
}

fn append_to_existing_text(mut prev: Handle, text: &str) -> bool {
    match prev.deref_mut().node {
        Text(ref mut existing) => {
            existing.push_str(text);
            true
        }
        _ => false,
    }
}

pub struct Sink {
    nodes: Vec<Box<Unsafe<SquishyNode>>>,
    document: Handle,
    errors: Vec<MaybeOwned<'static>>,
    quirks_mode: QuirksMode,
}

impl Default for Sink {
    fn default() -> Sink {
        let mut sink = Sink {
            nodes: vec!(),
            document: Handle::null(),
            errors: vec!(),
            quirks_mode: tree_builder::NoQuirks,
        };
        sink.document = sink.new_node(Document);
        sink
    }
}

impl Sink {
    fn new_node(&mut self, node: NodeEnum) -> Handle {
        self.nodes.push(box Unsafe::new(SquishyNode::new(node)));
        let ptr: *const Unsafe<SquishyNode> = &**self.nodes.last().unwrap();
        Handle::new(ptr)
    }
}

impl TreeSink<Handle> for Sink {
    fn parse_error(&mut self, msg: MaybeOwned<'static>) {
        self.errors.push(msg);
    }

    fn get_document(&mut self) -> Handle {
        self.document
    }

    fn set_quirks_mode(&mut self, mode: QuirksMode) {
        self.quirks_mode = mode;
    }

    fn same_node(&self, x: Handle, y: Handle) -> bool {
        x == y
    }

    fn elem_name(&self, target: Handle) -> (Atom, Atom) {
        match target.node {
            Element(ref name, _) => (ns!(HTML), name.clone()),
            _ => fail!("not an element!"),
        }
    }

    fn create_element(&mut self, ns: Atom, name: Atom, attrs: Vec<Attribute>) -> Handle {
        assert!(ns == ns!(HTML));
        self.new_node(Element(name, attrs))
    }

    fn create_comment(&mut self, text: String) -> Handle {
        self.new_node(Comment(text))
    }

    fn append(&mut self, mut parent: Handle, child: NodeOrText<Handle>) {
        // Append to an existing Text node if we have one.
        match child {
            AppendText(ref text) => match parent.children.last() {
                Some(h) => if append_to_existing_text(*h, text.as_slice()) { return; },
                _ => (),
            },
            _ => (),
        }

        append(parent, match child {
            AppendText(text) => self.new_node(Text(text)),
            AppendNode(node) => node
        });
    }

    fn append_before_sibling(&mut self,
            sibling: Handle,
            child: NodeOrText<Handle>) -> Result<(), NodeOrText<Handle>> {
        let (mut parent, i) = unwrap_or_return!(get_parent_and_index(sibling), Err(child));

        let mut child = match (child, i) {
            // No previous node.
            (AppendText(text), 0) => self.new_node(Text(text)),

            // Look for a text node before the insertion point.
            (AppendText(text), i) => {
                let prev = parent.children[i-1];
                if append_to_existing_text(prev, text.as_slice()) {
                    return Ok(());
                }
                self.new_node(Text(text))
            }

            // The tree builder promises we won't have a text node after
            // the insertion point.

            // Any other kind of node.
            (AppendNode(node), _) => node,
        };

        if !child.parent.is_null() {
            self.remove_from_parent(child);
        }

        child.parent = parent;
        parent.children.insert(i, child);
        Ok(())
    }

    fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {
        append(self.document, self.new_node(Doctype(name, public_id, system_id)));
    }

    fn add_attrs_if_missing(&mut self, mut target: Handle, mut attrs: Vec<Attribute>) {
        let existing = match target.deref_mut().node {
            Element(_, ref mut attrs) => attrs,
            _ => return,
        };

        // FIXME: quadratic time
        attrs.retain(|attr|
            !existing.iter().any(|e| e.name == attr.name));
        existing.extend(attrs.into_iter());
    }

    fn remove_from_parent(&mut self, mut target: Handle) {
        let (mut parent, i) = unwrap_or_return!(get_parent_and_index(target), ());
        parent.children.remove(i).expect("not found!");
        target.parent = Handle::null();
    }

    fn mark_script_already_started(&mut self, _node: Handle) { }
}

pub struct Node {
    pub node: NodeEnum,
    _parent_not_accessible: uint,
    pub children: Vec<Box<Node>>,
}

pub struct OwnedDom {
    pub document: Box<Node>,
    pub errors: Vec<MaybeOwned<'static>>,
    pub quirks_mode: QuirksMode,
}

impl ParseResult<Sink> for OwnedDom {
    fn get_result(sink: Sink) -> OwnedDom {
        fn walk(live: &mut HashSet<uint>, node: Handle) {
            live.insert(node.ptr as uint);
            for &child in node.deref().children.iter() {
                walk(live, child);
            }
        }

        // Collect addresses of all the nodes that made it into the final tree.
        let mut live = HashSet::new();
        walk(&mut live, sink.document);

        // Forget about the nodes in the final tree; they will be owned by
        // their parent.  In the process of iterating we drop all nodes that
        // aren't in the tree.
        for node in sink.nodes.into_iter() {
            let ptr: *const Unsafe<SquishyNode> = &*node;
            if live.contains(&(ptr as uint)) {
                unsafe {
                    mem::forget(node);
                }
            }
        }

        let old_addrs = addrs_of!(sink.document: node, parent, children);

        // Transmute the root to a Node, finalizing the transfer of ownership.
        let document = unsafe {
            mem::transmute::<*const Unsafe<SquishyNode>, Box<Node>>(sink.document.ptr)
        };

        // FIXME: do this assertion statically
        let new_addrs = addrs_of!(document: node, _parent_not_accessible, children);
        assert_eq!(old_addrs, new_addrs);

        OwnedDom {
            document: document,
            errors: sink.errors,
            quirks_mode: sink.quirks_mode,
        }
    }
}

impl Serializable for Node {
    fn serialize<'wr, Wr: Writer>(&self,
            serializer: &mut Serializer<'wr, Wr>,
            incl_self: bool) -> IoResult<()> {

        match (incl_self, &self.node) {
            (_, &Element(ref name, ref attrs)) => {
                if incl_self {
                    try!(serializer.start_elem(ns!(HTML), name.clone(),
                        attrs.iter().map(|at| (&at.name, at.value.as_slice()))));
                }

                for child in self.children.iter() {
                    try!(child.serialize(serializer, true));
                }

                if incl_self {
                    try!(serializer.end_elem(ns!(HTML), name.clone()));
                }
                Ok(())
            }

            (false, &Document) => {
                for child in self.children.iter() {
                    try!(child.serialize(serializer, true));
                }
                Ok(())
            }

            (false, _) => Ok(()),

            (true, &Doctype(ref name, _, _)) => serializer.write_doctype(name.as_slice()),
            (true, &Text(ref text)) => serializer.write_text(text.as_slice()),
            (true, &Comment(ref text)) => serializer.write_comment(text.as_slice()),

            (true, &Document) => fail!("Can't serialize Document node itself"),
        }
    }
}
<|endoftext|>


<commit_before>//! # num_cpus
//!
//! A crate with utilities to determine the number of CPUs available on the
//! current system.
//!
//! ## Example
//!
//! ```
//! let cpus = num_cpus::get();
//! ```
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of available CPUs of the current system.
///
/// # Note
///
/// This will check sched affinity on Linux.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current system.
///
/// If not possible on the particular architecture, returns same as `get()`
/// which is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(any(target_os = "linux", target_os = "windows", target_os="macos")))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus() -> usize {
    match get_num_physical_cpus_windows() {
        Some(num) => num,
        None => get_num_cpus()
    }
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus_windows() -> Option<usize> {
    // Inspired by https://msdn.microsoft.com/en-us/library/ms683194

    use std::ptr;
    use std::mem;

    #[allow(non_upper_case_globals)]
    const RelationProcessorCore: u32 = 0;

    #[repr(C)]
    #[allow(non_camel_case_types)]
    struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
        mask: usize,
        relationship: u32,
        _unused: [u64; 2]
    }

    extern "system" {
        fn GetLogicalProcessorInformation(
            info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
            length: &mut u32
        ) -> u32;
    }

    // First we need to determine how much space to reserve.

    // The required size of the buffer, in bytes.
    let mut needed_size = 0;

    unsafe {
        GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
    }

    let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;

    // Could be 0, or some other bogus size.
    if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
        return None;
    }

    let count = needed_size / struct_size;

    // Allocate some memory where we will store the processor info.
    let mut buf = Vec::with_capacity(count as usize);

    let result;

    unsafe {
        result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
    }

    // Failed for any reason.
    if result == 0 {
        return None;
    }

    let count = needed_size / struct_size;

    unsafe {
        buf.set_len(count as usize);
    }

    let phys_proc_count = buf.iter()
        // Only interested in processor packages (physical processors.)
        .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
        .count();

    if phys_proc_count == 0 {
        None
    } else {
        Some(phys_proc_count)
    }
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(target_os = "macos")]
fn get_num_physical_cpus() -> usize {
    use std::ffi::CStr;
    use std::ptr;

    let mut cpus: i32 = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0")
        .expect("byte literal is missing NUL");

    unsafe {
        if 0 != libc::sysctlbyname(sysctl_name.as_ptr(),
                                   &mut cpus as *mut _ as *mut _,
                                   &mut cpus_size as *mut _ as *mut _,
                                   ptr::null_mut(),
                                   0) {
            return get_num_cpus();
        }
    }
    cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let mut count: u32 = 0;
        for i in 0..libc::CPU_SETSIZE as usize {
            if unsafe { libc::CPU_ISSET(i, &set) } {
                count += 1
            }
        }
        count as usize
    } else {
        let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
        if cpus < 1 {
            1
        } else {
            cpus as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    // On ARM targets, processors could be turned off to save power.
    // Use `_SC_NPROCESSORS_CONF` to get the real number.
    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
    const CONF_NAME: libc::c_int = _SC_NPROCESSORS_CONF;
    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
    const CONF_NAME: libc::c_int = _SC_NPROCESSORS_ONLN;

    let cpus = unsafe { libc::sysconf(CONF_NAME) };
    if cpus < 1 {
        1
    } else {
        cpus as usize
    }
}

#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[cfg(test)]
mod tests {
    fn env_var(name: &'static str) -> Option<usize> {
        ::std::env::var(name).ok().map(|val| val.parse().unwrap())
    }

    #[test]
    fn test_get() {
        let num = super::get();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }

    #[test]
    fn test_get_physical() {
        let num = super::get_physical();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }

    #[test]
    fn test_physical_less_logical() {
        let logical = super::get();
        let physical = super::get_physical();
        println!("physical: {:?}, logical: {:?}", physical, logical);
        assert!(physical <= logical);
    }
}
<commit_msg>Fix build<commit_after>//! # num_cpus
//!
//! A crate with utilities to determine the number of CPUs available on the
//! current system.
//!
//! ## Example
//!
//! ```
//! let cpus = num_cpus::get();
//! ```
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![allow(non_snake_case)]

#[cfg(not(windows))]
extern crate libc;

/// Returns the number of available CPUs of the current system.
///
/// # Note
///
/// This will check sched affinity on Linux.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

/// Returns the number of physical cores of the current system.
///
/// If not possible on the particular architecture, returns same as `get()`
/// which is the logical CPUs.
#[inline]
pub fn get_physical() -> usize {
    get_num_physical_cpus()
}

#[cfg(not(any(target_os = "linux", target_os = "windows", target_os="macos")))]
#[inline]
fn get_num_physical_cpus() -> usize {
    // Not implemented, fallback
    get_num_cpus()
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus() -> usize {
    match get_num_physical_cpus_windows() {
        Some(num) => num,
        None => get_num_cpus()
    }
}

#[cfg(target_os = "windows")]
fn get_num_physical_cpus_windows() -> Option<usize> {
    // Inspired by https://msdn.microsoft.com/en-us/library/ms683194

    use std::ptr;
    use std::mem;

    #[allow(non_upper_case_globals)]
    const RelationProcessorCore: u32 = 0;

    #[repr(C)]
    #[allow(non_camel_case_types)]
    struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
        mask: usize,
        relationship: u32,
        _unused: [u64; 2]
    }

    extern "system" {
        fn GetLogicalProcessorInformation(
            info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
            length: &mut u32
        ) -> u32;
    }

    // First we need to determine how much space to reserve.

    // The required size of the buffer, in bytes.
    let mut needed_size = 0;

    unsafe {
        GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
    }

    let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;

    // Could be 0, or some other bogus size.
    if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
        return None;
    }

    let count = needed_size / struct_size;

    // Allocate some memory where we will store the processor info.
    let mut buf = Vec::with_capacity(count as usize);

    let result;

    unsafe {
        result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
    }

    // Failed for any reason.
    if result == 0 {
        return None;
    }

    let count = needed_size / struct_size;

    unsafe {
        buf.set_len(count as usize);
    }

    let phys_proc_count = buf.iter()
        // Only interested in processor packages (physical processors.)
        .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
        .count();

    if phys_proc_count == 0 {
        None
    } else {
        Some(phys_proc_count)
    }
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
    use std::io::BufReader;
    use std::io::BufRead;
    use std::fs::File;
    use std::collections::HashSet;

    let file = match File::open("/proc/cpuinfo") {
        Ok(val) => val,
        Err(_) => {return get_num_cpus()},
    };
    let reader = BufReader::new(file);
    let mut set = HashSet::new();
    let mut coreid: u32 = 0;
    let mut physid: u32 = 0;
    let mut chgcount = 0;
    for line in reader.lines().filter_map(|result| result.ok()) {
        let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
        if parts.len() != 2 {
            continue
        }
        if parts[0] == "core id" || parts[0] == "physical id" {
            let value = match parts[1].trim().parse() {
              Ok(val) => val,
              Err(_) => break,
            };
            match parts[0] {
                "core id"     => coreid = value,
                "physical id" => physid = value,
                _ => {},
            }
            chgcount += 1;
        }
        if chgcount == 2 {
            set.insert((physid, coreid));
            chgcount = 0;
        }
    }
    let count = set.len();
    if count == 0 { get_num_cpus() } else { count }
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    #[repr(C)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: u32,
        lpMinimumApplicationAddress: *mut u8,
        lpMaximumApplicationAddress: *mut u8,
        dwActiveProcessorMask: *mut u8,
        dwNumberOfProcessors: u32,
        dwProcessorType: u32,
        dwAllocationGranularity: u32,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    unsafe {
        let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "bitrig",
          target_os = "netbsd"))]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }
    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        unsafe {
            libc::sysctl(mib.as_mut_ptr(),
                         2,
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
                         0 as *mut _,
                         0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }
    cpus as usize
}

#[cfg(target_os = "openbsd")]
fn get_num_cpus() -> usize {
    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    unsafe {
        libc::sysctl(mib.as_mut_ptr(),
                     2,
                     &mut cpus as *mut _ as *mut _,
                     &mut cpus_size as *mut _ as *mut _,
                     0 as *mut _,
                     0);
    }
    if cpus < 1 {
        cpus = 1;
    }
    cpus as usize
}

#[cfg(target_os = "macos")]
fn get_num_physical_cpus() -> usize {
    use std::ffi::CStr;
    use std::ptr;

    let mut cpus: i32 = 0;
    let mut cpus_size = std::mem::size_of_val(&cpus);

    let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0")
        .expect("byte literal is missing NUL");

    unsafe {
        if 0 != libc::sysctlbyname(sysctl_name.as_ptr(),
                                   &mut cpus as *mut _ as *mut _,
                                   &mut cpus_size as *mut _ as *mut _,
                                   ptr::null_mut(),
                                   0) {
            return get_num_cpus();
        }
    }
    cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
    let mut set:  libc::cpu_set_t = unsafe { std::mem::zeroed() };
    if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
        let mut count: u32 = 0;
        for i in 0..libc::CPU_SETSIZE as usize {
            if unsafe { libc::CPU_ISSET(i, &set) } {
                count += 1
            }
        }
        count as usize
    } else {
        let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
        if cpus < 1 {
            1
        } else {
            cpus as usize
        }
    }
}

#[cfg(any(
    target_os = "nacl",
    target_os = "macos",
    target_os = "ios",
    target_os = "android",
    target_os = "solaris",
    target_os = "fuchsia")
)]
fn get_num_cpus() -> usize {
    // On ARM targets, processors could be turned off to save power.
    // Use `_SC_NPROCESSORS_CONF` to get the real number.
    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_CONF;
    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_ONLN;

    let cpus = unsafe { libc::sysconf(CONF_NAME) };
    if cpus < 1 {
        1
    } else {
        cpus as usize
    }
}

#[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
fn get_num_cpus() -> usize {
    1
}

#[cfg(test)]
mod tests {
    fn env_var(name: &'static str) -> Option<usize> {
        ::std::env::var(name).ok().map(|val| val.parse().unwrap())
    }

    #[test]
    fn test_get() {
        let num = super::get();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }

    #[test]
    fn test_get_physical() {
        let num = super::get_physical();
        if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
            assert_eq!(num, n);
        } else {
            assert!(num > 0);
            assert!(num < 236_451);
        }
    }

    #[test]
    fn test_physical_less_logical() {
        let logical = super::get();
        let physical = super::get_physical();
        println!("physical: {:?}, logical: {:?}", physical, logical);
        assert!(physical <= logical);
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://jordanbray.github.io/chess/")]

pub mod color;
pub mod piece;
pub mod square;
pub mod chess_move;
pub mod bitboard;
pub mod castle_rights;
pub mod board;
pub mod magic;
pub mod construct;
pub mod rank;
pub mod file;
pub mod zobrist;
pub mod cache_table;

extern crate rand;
#[macro_use]
extern crate lazy_static;

<commit_msg>Testing documentation.<commit_after>#![doc(html_root_url = "https://jordanbray.github.io/chess/")]

/// This is a rust chess move generator.
pub mod color;
pub mod piece;
pub mod square;
pub mod chess_move;
pub mod bitboard;
pub mod castle_rights;
pub mod board;
pub mod magic;
pub mod construct;
pub mod rank;
pub mod file;
pub mod zobrist;
pub mod cache_table;

extern crate rand;
#[macro_use]
extern crate lazy_static;

<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! A SDL2 window back-end for the Piston game engine.

extern crate sdl2;
extern crate piston;
extern crate shader_version;
extern crate gl;
extern crate num;

// External crates.
use std::mem::transmute;
use piston::window::{
    OpenGLWindow,
    ProcAddress,
    Window,
    AdvancedWindow,
    WindowSettings,
    Size,
};
use piston::input::{ keyboard, Button, MouseButton, Input, Motion };

pub use shader_version::OpenGL;

/// A widow implemented by SDL2 back-end.
pub struct Sdl2Window {
    /// SDL window handle.
    pub window: sdl2::video::Window,
    /// Allow dead code because this keeps track of the OpenGL context.
    /// Will be released on drop.
    #[allow(dead_code)]
    pub context: sdl2::video::GLContext,
    /// SDL context.
    pub sdl_context: sdl2::Sdl,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    exit_on_esc: bool,
    title: String,
    size: Size,
    draw_size: Size,
}

impl Sdl2Window {
    /// Creates a new game window for SDL2.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> Sdl2Window {
        let sdl_context = sdl2::init(sdl2::INIT_EVERYTHING).unwrap();

        // Not all drivers default to 32bit color, so explicitly set it to 32bit color.
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLRedSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLGreenSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLBlueSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLAlphaSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLStencilSize, 8);

        let (major, minor) = opengl.get_major_minor();
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMajorVersion,
            major as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMinorVersion,
            minor as i32
        );
        if opengl >= OpenGL::_3_2 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLContextProfileMask,
                sdl2::video::GLProfile::GLCoreProfile as i32
            );
        }
        if settings.get_samples() != 0 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleBuffers,
                1
            );
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleSamples,
                settings.get_samples() as i32
            );
        }

        let window_flags = if settings.get_fullscreen() {
            sdl2::video::OPENGL | sdl2::video::RESIZABLE | sdl2::video::FULLSCREEN
        } else {
            sdl2::video::OPENGL | sdl2::video::RESIZABLE
        };

        if settings.get_vsync() {
            sdl2::video::gl_set_swap_interval(1);
        } else {
            sdl2::video::gl_set_swap_interval(0);
        }

        let window = sdl2::video::Window::new(
            &sdl_context,
            &settings.get_title(),
            sdl2::video::WindowPos::PosCentered,
            sdl2::video::WindowPos::PosCentered,
            settings.get_size().width as i32,
            settings.get_size().height as i32,
            window_flags
        );
        let window = match window {
            Ok(w) => w,
            Err(_) =>
                if settings.get_samples() != 0 {
                    // Retry without requiring anti-aliasing.
                    sdl2::video::gl_set_attribute(
                        sdl2::video::GLAttr::GLMultiSampleBuffers,
                        0
                            );
                    sdl2::video::gl_set_attribute(
                        sdl2::video::GLAttr::GLMultiSampleSamples,
                        0
                            );
                    sdl2::video::Window::new(
                        &sdl_context,
                        &settings.get_title(),
                        sdl2::video::WindowPos::PosCentered,
                        sdl2::video::WindowPos::PosCentered,
                        settings.get_size().width as i32,
                        settings.get_size().height as i32,
                        window_flags
                            ).unwrap()
                } else {
                    window.unwrap() // Panic.
                }
        };

        // Send text input events.
        sdl2::keyboard::start_text_input();

        let context = window.gl_create_context().unwrap();

        // Load the OpenGL function pointers.
        gl::load_with(|s| unsafe {
            transmute(sdl2::video::gl_get_proc_address(s))
        });

        let mut window = Sdl2Window {
            exit_on_esc: settings.get_exit_on_esc(),
            should_close: false,
            window: window,
            context: context,
            sdl_context: sdl_context,
            mouse_relative: None,
            title: settings.get_title() ,
            size: settings.get_size(),
            draw_size: settings.get_size(),
        };
        window.update_draw_size();
        window
    }

    fn update_draw_size(&mut self) {
        let event_pump = self.sdl_context.event_pump();
        let properties = self.window.properties(&event_pump);
        let (w, h) = properties.get_drawable_size();
        self.draw_size = Size { width: w as u32, height: h as u32 };
    }

    fn poll_event(&mut self) -> Option<Input> {
        // First check for a pending relative mouse move event.
        match self.mouse_relative {
            Some((x, y)) => {
                self.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }

        // Even though we create a new EventPump each time we poll an event
        // this should not be a problem since it only contains phantom data
        // and therefore should actually not have any overhead.
        let event = match self.sdl_context.event_pump().poll_event() {
            Some( ev ) => ev,
            None => return None
        };
        match event {
            sdl2::event::Event::Quit{..} => {
                self.should_close = true;
            }
            sdl2::event::Event::TextInput { text, .. } => {
                return Some(Input::Text(text));
            }
            sdl2::event::Event::KeyDown { keycode: key, repeat, ..} => {
                // SDL2 repeats the key down event.
                // If the event is the same as last one, ignore it.
                if repeat {
                    return self.poll_event()
                }

                if self.exit_on_esc
                && key == sdl2::keycode::KeyCode::Escape {
                    self.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(sdl2_map_key(key))));
                }
            }
            sdl2::event::Event::KeyUp { keycode: key, repeat, .. } => {
                if repeat {
                    return self.poll_event()
                }
                return Some(Input::Release(Button::Keyboard(sdl2_map_key(key))));
            }
            sdl2::event::Event::MouseButtonDown { mouse_btn: button, .. } => {
                return Some(Input::Press(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseButtonUp { mouse_btn: button, .. } => {
                return Some(Input::Release(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseMotion { x, y, xrel: dx, yrel: dy, .. } => {
                // Send relative move movement next time.
                self.mouse_relative = Some((dx as f64, dy as f64));
                return Some(Input::Move(Motion::MouseCursor(x as f64, y as f64)));
            },
            sdl2::event::Event::MouseWheel { x, y, .. } => {
                return Some(Input::Move(Motion::MouseScroll(x as f64, y as f64)));
            }
            sdl2::event::Event::Window {
                win_event_id: sdl2::event::WindowEventId::Resized, data1: w, data2: h, .. } => {
                self.size.width = w as u32;
                self.size.height = h as u32;
                self.update_draw_size();
                return Some(Input::Resize(w as u32, h as u32));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusGained, .. } => {
                return Some(Input::Focus(true));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusLost, .. } => {
                return Some(Input::Focus(false));
            }
            _ => {}
        }
        None
    }
}

impl Drop for Sdl2Window {
    fn drop(&mut self) {
        self.set_capture_cursor(false);
    }
}

impl Window for Sdl2Window {
    type Event = Input;

    fn should_close(&self) -> bool { self.should_close }
    fn swap_buffers(&mut self) { self.window.gl_swap_window(); }
    fn size(&self) -> Size { self.size }
    fn poll_event(&mut self) -> Option<Input> { self.poll_event() }
    fn draw_size(&self) -> Size { self.draw_size }
}

impl AdvancedWindow for Sdl2Window {
    fn get_title(&self) -> String {
        self.title.clone()
    }
    fn set_title(&mut self, value: String) {
        let event_pump = self.sdl_context.event_pump();
        let _ = self.window.properties(&event_pump).set_title(&value);
        self.title = value
    }
    fn get_exit_on_esc(&self) -> bool { self.exit_on_esc }
    fn set_exit_on_esc(&mut self, value: bool) { self.exit_on_esc = value; }
    fn set_capture_cursor(&mut self, value: bool) {
        sdl2::mouse::set_relative_mouse_mode(value);
    }
}

impl OpenGLWindow for Sdl2Window {
    fn get_proc_address(&mut self, proc_name: &str) -> ProcAddress {
        unsafe {
            transmute(sdl2::video::gl_get_proc_address(proc_name))
        }
    }

    fn is_current(&self) -> bool {
        unsafe {
            let this_context = self.context.raw();
            let current_context = sdl2::video::gl_get_current_context().unwrap().raw();

            this_context == current_context
        }
    }

    fn make_current(&mut self) {
        let _ = self.window.gl_make_current(&self.context);
    }
}

/// Maps a SDL2 key to piston-input key.
pub fn sdl2_map_key(keycode: sdl2::keycode::KeyCode) -> keyboard::Key {
    use num::FromPrimitive;
    FromPrimitive::from_u64(keycode as u64).unwrap()
}

/// Maps a SDL2 mouse button to piston-input button.
pub fn sdl2_map_mouse(button: sdl2::mouse::Mouse) -> MouseButton {
    match button {
        sdl2::mouse::Mouse::Left => MouseButton::Left,
        sdl2::mouse::Mouse::Right => MouseButton::Right,
        sdl2::mouse::Mouse::Middle => MouseButton::Middle,
        sdl2::mouse::Mouse::X1 => MouseButton::X1,
        sdl2::mouse::Mouse::X2 => MouseButton::X2,
        sdl2::mouse::Mouse::Unknown(_) => MouseButton::Unknown,
    }
}
<commit_msg>Set vsync when there is a current context<commit_after>#![deny(missing_docs)]

//! A SDL2 window back-end for the Piston game engine.

extern crate sdl2;
extern crate piston;
extern crate shader_version;
extern crate gl;
extern crate num;

// External crates.
use std::mem::transmute;
use piston::window::{
    OpenGLWindow,
    ProcAddress,
    Window,
    AdvancedWindow,
    WindowSettings,
    Size,
};
use piston::input::{ keyboard, Button, MouseButton, Input, Motion };

pub use shader_version::OpenGL;

/// A widow implemented by SDL2 back-end.
pub struct Sdl2Window {
    /// SDL window handle.
    pub window: sdl2::video::Window,
    /// Allow dead code because this keeps track of the OpenGL context.
    /// Will be released on drop.
    #[allow(dead_code)]
    pub context: sdl2::video::GLContext,
    /// SDL context.
    pub sdl_context: sdl2::Sdl,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    exit_on_esc: bool,
    title: String,
    size: Size,
    draw_size: Size,
}

impl Sdl2Window {
    /// Creates a new game window for SDL2.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> Sdl2Window {
        let sdl_context = sdl2::init(sdl2::INIT_EVERYTHING).unwrap();

        // Not all drivers default to 32bit color, so explicitly set it to 32bit color.
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLRedSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLGreenSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLBlueSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLAlphaSize, 8);
        sdl2::video::gl_set_attribute(sdl2::video::GLAttr::GLStencilSize, 8);

        let (major, minor) = opengl.get_major_minor();
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMajorVersion,
            major as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMinorVersion,
            minor as i32
        );
        if opengl >= OpenGL::_3_2 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLContextProfileMask,
                sdl2::video::GLProfile::GLCoreProfile as i32
            );
        }
        if settings.get_samples() != 0 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleBuffers,
                1
            );
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleSamples,
                settings.get_samples() as i32
            );
        }

        let window_flags = if settings.get_fullscreen() {
            sdl2::video::OPENGL | sdl2::video::RESIZABLE | sdl2::video::FULLSCREEN
        } else {
            sdl2::video::OPENGL | sdl2::video::RESIZABLE
        };

        let window = sdl2::video::Window::new(
            &sdl_context,
            &settings.get_title(),
            sdl2::video::WindowPos::PosCentered,
            sdl2::video::WindowPos::PosCentered,
            settings.get_size().width as i32,
            settings.get_size().height as i32,
            window_flags
        );
        let window = match window {
            Ok(w) => w,
            Err(_) =>
                if settings.get_samples() != 0 {
                    // Retry without requiring anti-aliasing.
                    sdl2::video::gl_set_attribute(
                        sdl2::video::GLAttr::GLMultiSampleBuffers,
                        0
                            );
                    sdl2::video::gl_set_attribute(
                        sdl2::video::GLAttr::GLMultiSampleSamples,
                        0
                            );
                    sdl2::video::Window::new(
                        &sdl_context,
                        &settings.get_title(),
                        sdl2::video::WindowPos::PosCentered,
                        sdl2::video::WindowPos::PosCentered,
                        settings.get_size().width as i32,
                        settings.get_size().height as i32,
                        window_flags
                            ).unwrap()
                } else {
                    window.unwrap() // Panic.
                }
        };

        // Send text input events.
        sdl2::keyboard::start_text_input();

        let context = window.gl_create_context().unwrap();

        // Load the OpenGL function pointers.
        gl::load_with(|s| unsafe {
            transmute(sdl2::video::gl_get_proc_address(s))
        });

        if settings.get_vsync() {
            sdl2::video::gl_set_swap_interval(1);
        } else {
            sdl2::video::gl_set_swap_interval(0);
        }

        let mut window = Sdl2Window {
            exit_on_esc: settings.get_exit_on_esc(),
            should_close: false,
            window: window,
            context: context,
            sdl_context: sdl_context,
            mouse_relative: None,
            title: settings.get_title() ,
            size: settings.get_size(),
            draw_size: settings.get_size(),
        };
        window.update_draw_size();
        window
    }

    fn update_draw_size(&mut self) {
        let event_pump = self.sdl_context.event_pump();
        let properties = self.window.properties(&event_pump);
        let (w, h) = properties.get_drawable_size();
        self.draw_size = Size { width: w as u32, height: h as u32 };
    }

    fn poll_event(&mut self) -> Option<Input> {
        // First check for a pending relative mouse move event.
        match self.mouse_relative {
            Some((x, y)) => {
                self.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }

        // Even though we create a new EventPump each time we poll an event
        // this should not be a problem since it only contains phantom data
        // and therefore should actually not have any overhead.
        let event = match self.sdl_context.event_pump().poll_event() {
            Some( ev ) => ev,
            None => return None
        };
        match event {
            sdl2::event::Event::Quit{..} => {
                self.should_close = true;
            }
            sdl2::event::Event::TextInput { text, .. } => {
                return Some(Input::Text(text));
            }
            sdl2::event::Event::KeyDown { keycode: key, repeat, ..} => {
                // SDL2 repeats the key down event.
                // If the event is the same as last one, ignore it.
                if repeat {
                    return self.poll_event()
                }

                if self.exit_on_esc
                && key == sdl2::keycode::KeyCode::Escape {
                    self.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(sdl2_map_key(key))));
                }
            }
            sdl2::event::Event::KeyUp { keycode: key, repeat, .. } => {
                if repeat {
                    return self.poll_event()
                }
                return Some(Input::Release(Button::Keyboard(sdl2_map_key(key))));
            }
            sdl2::event::Event::MouseButtonDown { mouse_btn: button, .. } => {
                return Some(Input::Press(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseButtonUp { mouse_btn: button, .. } => {
                return Some(Input::Release(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseMotion { x, y, xrel: dx, yrel: dy, .. } => {
                // Send relative move movement next time.
                self.mouse_relative = Some((dx as f64, dy as f64));
                return Some(Input::Move(Motion::MouseCursor(x as f64, y as f64)));
            },
            sdl2::event::Event::MouseWheel { x, y, .. } => {
                return Some(Input::Move(Motion::MouseScroll(x as f64, y as f64)));
            }
            sdl2::event::Event::Window {
                win_event_id: sdl2::event::WindowEventId::Resized, data1: w, data2: h, .. } => {
                self.size.width = w as u32;
                self.size.height = h as u32;
                self.update_draw_size();
                return Some(Input::Resize(w as u32, h as u32));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusGained, .. } => {
                return Some(Input::Focus(true));
            }
            sdl2::event::Event::Window { win_event_id: sdl2::event::WindowEventId::FocusLost, .. } => {
                return Some(Input::Focus(false));
            }
            _ => {}
        }
        None
    }
}

impl Drop for Sdl2Window {
    fn drop(&mut self) {
        self.set_capture_cursor(false);
    }
}

impl Window for Sdl2Window {
    type Event = Input;

    fn should_close(&self) -> bool { self.should_close }
    fn swap_buffers(&mut self) { self.window.gl_swap_window(); }
    fn size(&self) -> Size { self.size }
    fn poll_event(&mut self) -> Option<Input> { self.poll_event() }
    fn draw_size(&self) -> Size { self.draw_size }
}

impl AdvancedWindow for Sdl2Window {
    fn get_title(&self) -> String {
        self.title.clone()
    }
    fn set_title(&mut self, value: String) {
        let event_pump = self.sdl_context.event_pump();
        let _ = self.window.properties(&event_pump).set_title(&value);
        self.title = value
    }
    fn get_exit_on_esc(&self) -> bool { self.exit_on_esc }
    fn set_exit_on_esc(&mut self, value: bool) { self.exit_on_esc = value; }
    fn set_capture_cursor(&mut self, value: bool) {
        sdl2::mouse::set_relative_mouse_mode(value);
    }
}

impl OpenGLWindow for Sdl2Window {
    fn get_proc_address(&mut self, proc_name: &str) -> ProcAddress {
        unsafe {
            transmute(sdl2::video::gl_get_proc_address(proc_name))
        }
    }

    fn is_current(&self) -> bool {
        unsafe {
            let this_context = self.context.raw();
            let current_context = sdl2::video::gl_get_current_context().unwrap().raw();

            this_context == current_context
        }
    }

    fn make_current(&mut self) {
        let _ = self.window.gl_make_current(&self.context);
    }
}

/// Maps a SDL2 key to piston-input key.
pub fn sdl2_map_key(keycode: sdl2::keycode::KeyCode) -> keyboard::Key {
    use num::FromPrimitive;
    FromPrimitive::from_u64(keycode as u64).unwrap()
}

/// Maps a SDL2 mouse button to piston-input button.
pub fn sdl2_map_mouse(button: sdl2::mouse::Mouse) -> MouseButton {
    match button {
        sdl2::mouse::Mouse::Left => MouseButton::Left,
        sdl2::mouse::Mouse::Right => MouseButton::Right,
        sdl2::mouse::Mouse::Middle => MouseButton::Middle,
        sdl2::mouse::Mouse::X1 => MouseButton::X1,
        sdl2::mouse::Mouse::X2 => MouseButton::X2,
        sdl2::mouse::Mouse::Unknown(_) => MouseButton::Unknown,
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

mod wtf8;

use std::path::PathBuf;

/// Interprets given bytes as an operating system path.
///
/// On Linux, the path is constructed from bytes as is since system paths can
/// be made of arbitrary sequence of bytes.
///
/// On Windows, the [WTF-8][wtf8] encoding is used.
///
/// [wtf8]: https://simonsapin.github.io/wtf-8
///
/// # Examples
///
/// ```
/// use std::ffi::OsStr;
///
/// let path = rrg_proto::path::from_bytes(b"foo/bar/baz".to_vec());
///
/// let mut components = path.components()
///     .map(|component| component.as_os_str());
///
/// assert_eq!(components.next(), Some(OsStr::new("foo")));
/// assert_eq!(components.next(), Some(OsStr::new("bar")));
/// assert_eq!(components.next(), Some(OsStr::new("baz")));
/// assert_eq!(components.next(), None);
/// ```
pub fn from_bytes(bytes: Vec<u8>) -> PathBuf {
    from_bytes_impl(bytes)
}

/// Serializes given path to a byte sequence.
///
/// On Linux, the path is emitted as is since system paths can consist of
/// arbitrary bytes.
///
/// On Windows, the [WTF-8][wtf8] encoding is used.
///
/// [wtf8]: https://simonsapin.github.io/wtf-8
///
/// # Examples
///
/// ```
/// use std::path::PathBuf;
///
/// let path = PathBuf::from("foo/bar/baz");
/// assert_eq!(rrg_proto::path::into_bytes(path), b"foo/bar/baz");
/// ```
pub fn into_bytes(path: PathBuf) -> Vec<u8> {
    into_bytes_impl(path)
}

#[cfg(target_family = "unix")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from_vec(bytes).into()
}

#[cfg(target_family = "windows")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    let bytes_u16 = wtf8::into_ill_formed_utf16(bytes.into_iter());

    use std::os::windows::ffi::OsStringExt as _;
    std::ffi::OsString::from_wide(&bytes_u16).into()
}

#[cfg(target_family = "unix")]
fn into_bytes_impl(path: PathBuf) -> Vec<u8> {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from(path).into_vec()
}

#[cfg(target_family = "windows")]
fn into_bytes_impl(path: PathBuf) -> Vec<u8> {
    let string = std::ffi::OsString::from(path);

    use std::os::windows::ffi::OsStrExt as _;
    wtf8::from_ill_formed_utf16(string.as_os_str().encode_wide())
}
<commit_msg>Compile WTF-8 utilities only for Windows or tests.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

#[cfg(any(target_os = "windows", test))]
mod wtf8;

use std::path::PathBuf;

/// Interprets given bytes as an operating system path.
///
/// On Linux, the path is constructed from bytes as is since system paths can
/// be made of arbitrary sequence of bytes.
///
/// On Windows, the [WTF-8][wtf8] encoding is used.
///
/// [wtf8]: https://simonsapin.github.io/wtf-8
///
/// # Examples
///
/// ```
/// use std::ffi::OsStr;
///
/// let path = rrg_proto::path::from_bytes(b"foo/bar/baz".to_vec());
///
/// let mut components = path.components()
///     .map(|component| component.as_os_str());
///
/// assert_eq!(components.next(), Some(OsStr::new("foo")));
/// assert_eq!(components.next(), Some(OsStr::new("bar")));
/// assert_eq!(components.next(), Some(OsStr::new("baz")));
/// assert_eq!(components.next(), None);
/// ```
pub fn from_bytes(bytes: Vec<u8>) -> PathBuf {
    from_bytes_impl(bytes)
}

/// Serializes given path to a byte sequence.
///
/// On Linux, the path is emitted as is since system paths can consist of
/// arbitrary bytes.
///
/// On Windows, the [WTF-8][wtf8] encoding is used.
///
/// [wtf8]: https://simonsapin.github.io/wtf-8
///
/// # Examples
///
/// ```
/// use std::path::PathBuf;
///
/// let path = PathBuf::from("foo/bar/baz");
/// assert_eq!(rrg_proto::path::into_bytes(path), b"foo/bar/baz");
/// ```
pub fn into_bytes(path: PathBuf) -> Vec<u8> {
    into_bytes_impl(path)
}

#[cfg(target_family = "unix")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from_vec(bytes).into()
}

#[cfg(target_family = "windows")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    let bytes_u16 = wtf8::into_ill_formed_utf16(bytes.into_iter());

    use std::os::windows::ffi::OsStringExt as _;
    std::ffi::OsString::from_wide(&bytes_u16).into()
}

#[cfg(target_family = "unix")]
fn into_bytes_impl(path: PathBuf) -> Vec<u8> {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from(path).into_vec()
}

#[cfg(target_family = "windows")]
fn into_bytes_impl(path: PathBuf) -> Vec<u8> {
    let string = std::ffi::OsString::from(path);

    use std::os::windows::ffi::OsStrExt as _;
    wtf8::from_ill_formed_utf16(string.as_os_str().encode_wide())
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;

use std::io;

use actix::prelude::Addr;
use actix_files::{Files, NamedFile};
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer, Result};
use dotenv::dotenv;
use futures::future;
use futures::future::Future;
use serde::Serialize;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct SecretBody {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: usize,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ApiError<T: Serialize> {
    error: T,
}

impl<T: Serialize> ApiError<T> {
    pub fn from(err: T) -> ApiError<T> {
        ApiError { error: err }
    }
}

#[derive(Debug, Clone, Copy)]
struct AppConfig {
    max_secret_length: usize,
    max_secret_expiration: usize,
}

struct AppState {
    redis: Addr<RedisActor>,
    config: AppConfig,
}

impl AppState {
    fn create_secret(
        self: &AppState,
        id: Uuid,
        body: String,
        expiration_seconds: usize,
    ) -> impl Future<Item = bool, Error = actix_web::Error> {
        self.redis
            .send(Command(resp_array![
                "SETEX",
                id.to_string(),
                expiration_seconds.to_string(),
                body
            ]))
            .map_err(actix_web::Error::from)
            .and_then(|res: Result<RespValue, actix_redis::Error>| match res {
                Ok(RespValue::SimpleString(_)) => Ok(true),

                err => {
                    println!("unexpected response to creating secret: {:?}", err);
                    Err(actix_web::error::ErrorInternalServerError(
                        "failed to create secret",
                    ))
                }
            })
    }

    fn fetch_secret(
        self: &AppState,
        id: String,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        self.redis
            .send(Command(resp_array!["GET", id]))
            .map_err(actix_web::Error::from)
            .map(|res| match res {
                Ok(RespValue::BulkString(bytes)) => {
                    let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                    Some(str)
                }

                _ => None,
            })
    }

    fn consume_secret(
        self: &AppState,
        id: String,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let fetch_secret = self.fetch_secret(id.clone());
        let delete_secret = self
            .redis
            .send(Command(resp_array!["DEL", id]))
            .map_err(actix_web::Error::from);

        fetch_secret.and_then(|res| delete_secret.map(|_| res))
    }
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validate = if body.content.len() > state.config.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > state.config.max_secret_expiration {
        Err("expiration too long")
    } else {
        Ok(())
    };

    future::result(validate)
        .map_err(|err| HttpResponse::BadRequest().json(ApiError::from(err)))
        .map_err(actix_web::Error::from)
        .and_then(move |_| {
            println!("Writing: {:?}: {:?}", id.to_string(), expiration);

            // TODO: Possibly re-encrypt secret here? libsodium or something.

            state
                .create_secret(id, body.content, body.expiration_seconds)
                .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }))
        })
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.to_string();
    println!("Fetching: {:?}", secret_id);

    state
        .consume_secret(secret_id)
        .map(|res: Option<String>| match res {
            Some(secret) => HttpResponse::Ok().json(SecretBody { content: secret }),
            None => HttpResponse::NotFound().json(ApiError::from("not found")),
        })
}

fn view_secret_page(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = NamedFile, Error = actix_web::Error> {
    let secret_id = path.to_string();

    state.fetch_secret(secret_id).and_then(|res| match res {
        Some(_) => Ok(NamedFile::open("./static/view.html")?),

        None => {
            let file = NamedFile::open("./static/404.html")?
                // TODO: (requires HTTP crate).set_status_code()
                ;

            Ok(file)
        }
    })
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    let config = AppConfig {
        max_secret_length: std::env::var("MAX_SECRET_LENGTH")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        max_secret_expiration: std::env::var("MAX_SECRET_EXPIRATION")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
    };

    let bind_addr = std::env::var("BIND_ADDR").unwrap_or("127.0.0.1:8080".to_string());

    println!("Booting with config: {:?}", config);

    HttpServer::new(move || {
        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            config: config,
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(
                web::resource("/api/secret").route(web::post().to_async(create_secret)),
            )
            .service(
                web::resource("/api/secret/{secret_id}")
                    .route(web::get().to_async(get_secret)),
            )
            .service(
                web::resource("/view/{secret_id}")
                    .route(web::get().to_async(view_secret_page)),
            )
            .service(Files::new("/", "./static/").index_file("index.html"))
    })
    .bind(bind_addr)?
    .run()
}
<commit_msg>Prefix redis keys<commit_after>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;

use std::io;

use actix::prelude::Addr;
use actix_files::{Files, NamedFile};
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer, Result};
use dotenv::dotenv;
use futures::future;
use futures::future::Future;
use serde::Serialize;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct SecretBody {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: usize,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ApiError<T: Serialize> {
    error: T,
}

impl<T: Serialize> ApiError<T> {
    pub fn from(err: T) -> ApiError<T> {
        ApiError { error: err }
    }
}

#[derive(Debug, Clone)]
struct AppConfig {
    redis_key_prefix: String,
    max_secret_length: usize,
    max_secret_expiration: usize,
}

struct AppState {
    redis: Addr<RedisActor>,
    config: AppConfig,
}

impl AppState {
    fn format_key(self: &AppState, id: &str) -> String {
        format!("{}.{}", self.config.redis_key_prefix, id)
    }

    fn create_secret(
        self: &AppState,
        id: Uuid,
        body: &str,
        expiration_seconds: usize,
    ) -> impl Future<Item = bool, Error = actix_web::Error> {
        let redis_key = self.format_key(&id.to_string());

        self.redis
            .send(Command(resp_array![
                "SETEX",
                redis_key,
                expiration_seconds.to_string(),
                body
            ]))
            .map_err(actix_web::Error::from)
            .and_then(|res: Result<RespValue, actix_redis::Error>| match res {
                Ok(RespValue::SimpleString(_)) => Ok(true),

                err => {
                    println!("unexpected response to creating secret: {:?}", err);
                    Err(actix_web::error::ErrorInternalServerError(
                        "failed to create secret",
                    ))
                }
            })
    }

    fn fetch_secret(
        self: &AppState,
        id: &str,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let redis_key = self.format_key(id);

        self.redis
            .send(Command(resp_array!["GET", redis_key]))
            .map_err(actix_web::Error::from)
            .map(|res| match res {
                Ok(RespValue::BulkString(bytes)) => {
                    let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                    Some(str)
                }

                _ => None,
            })
    }

    fn consume_secret(
        self: &AppState,
        id: &str,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let redis_key = self.format_key(id);

        let fetch_secret = self.fetch_secret(&redis_key);
        let delete_secret = self
            .redis
            .send(Command(resp_array!["DEL", redis_key]))
            .map_err(actix_web::Error::from);

        fetch_secret.and_then(|res| delete_secret.map(|_| res))
    }
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validate = if body.content.len() > state.config.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > state.config.max_secret_expiration {
        Err("expiration too long")
    } else {
        Ok(())
    };

    future::result(validate)
        .map_err(|err| HttpResponse::BadRequest().json(ApiError::from(err)))
        .map_err(actix_web::Error::from)
        .and_then(move |_| {
            println!("Writing: {:?}: {:?}", id.to_string(), expiration);

            // TODO: Possibly re-encrypt secret here? libsodium or something.

            state
                .create_secret(id, body.content.as_ref(), body.expiration_seconds)
                .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }))
        })
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.into_inner();
    println!("Fetching: {:?}", &secret_id);
    state
        .consume_secret(&secret_id)
        .map(|res: Option<String>| match res {
            Some(secret) => HttpResponse::Ok().json(SecretBody { content: secret }),
            None => HttpResponse::NotFound().json(ApiError::from("not found")),
        })
}

fn view_secret_page(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = NamedFile, Error = actix_web::Error> {
    let secret_id = path.into_inner();

    state.fetch_secret(&secret_id).and_then(|res| match res {
        Some(_) => Ok(NamedFile::open("./static/view.html")?),

        None => {
            let file = NamedFile::open("./static/404.html")?
                // TODO: (requires HTTP crate).set_status_code()
                ;

            Ok(file)
        }
    })
}

fn config_from_env() -> AppConfig {
    AppConfig {
        max_secret_length: std::env::var("MAX_SECRET_LENGTH")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        max_secret_expiration: std::env::var("MAX_SECRET_EXPIRATION")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        redis_key_prefix: std::env::var("REDIS_KEY_PREFIX")
            .unwrap_or("tofu:".to_string()),
    }
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    let bind_addr = std::env::var("BIND_ADDR").unwrap_or("127.0.0.1:8080".to_string());

    HttpServer::new(move || {
        let config = config_from_env();
        println!("Booting with config: {:?}", config);

        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            config: config,
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(
                web::resource("/api/secret").route(web::post().to_async(create_secret)),
            )
            .service(
                web::resource("/api/secret/{secret_id}")
                    .route(web::get().to_async(get_secret)),
            )
            .service(
                web::resource("/view/{secret_id}")
                    .route(web::get().to_async(view_secret_page)),
            )
            .service(Files::new("/", "./static/").index_file("index.html"))
    })
    .bind(bind_addr)?
    .run()
}
<|endoftext|>