<commit_before>// !
// Resource caching smart pointer
//

extern crate serde;

use std::rc::Rc;
use std::ops::Deref;
use std::hash;
use std::collections::HashMap;
use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// A type that implements a singleton resource store.
pub trait ResourceStore<K = String> {
    fn get_resource(key: &K) -> Option<Rc<Self>> where Self: Sized;

    fn insert_resource(key: K, resource: Self);
}

/// Smart pointer for an immutable cached resource.
///
/// The semantics are similar to Rc pointers.
///
/// Resource values will serialize as their key values, so they can be attached to structures that
/// require compact serialization.
#[derive(Clone)]
pub struct Resource<T, K = String> {
    handle: Rc<T>,
    key: K,
}

impl<T: Sized, K> AsRef<T> for Resource<T, K> {
    fn as_ref(&self) -> &T {
        self.handle.as_ref()
    }
}

impl<T: Sized, K> Deref for Resource<T, K> {
    type Target = T;

    #[inline(always)]
    fn deref(&self) -> &T {
        self.handle.deref()
    }
}

impl<T> Serialize for Resource<T> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.key.serialize(serializer)
    }
}

impl<T: ResourceStore> Deserialize for Resource<T> {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        let key: String = try!(Deserialize::deserialize(deserializer));
        Ok(Self::new(key).unwrap())
    }
}

impl<T, K: hash::Hash> hash::Hash for Resource<T, K> {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.key.hash(state);
    }
}

impl<T, K: PartialEq> PartialEq for Resource<T, K> {
    fn eq(&self, other: &Self) -> bool {
        self.key.eq(&other.key)
    }
}

impl<T, K: Eq> Eq for Resource<T, K> {}

impl<T, K: fmt::Display> fmt::Display for Resource<T, K> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        self.key.fmt(formatter)
    }
}

impl<T, K: fmt::Debug> fmt::Debug for Resource<T, K> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        self.key.fmt(formatter)
    }
}

impl<K, T: ResourceStore<K>> Resource<T, K> {
    pub fn new(key: K) -> Option<Self> {
        if let Some(handle) = ResourceStore::get_resource(&key) {
            Some(Resource {
                handle: handle,
                key: key,
            })
        } else {
            None
        }
    }
}

/// A value that can be implicitly constructed given a key.
pub trait Loadable<K = String> {
    fn load(_key: &K) -> Option<Self>
        where Self: Sized
    {
        // Default implementation so that types with no load semantics can be used with
        // ResourceCache so that all inserts must be explicit.
        None
    }
}

/// A cache that associates resource values with paths.
///
/// Resources and paths are assumed to be immutable.
pub struct ResourceCache<T, K = String> {
    cache: HashMap<K, Rc<T>>,
}

impl<K: Eq + hash::Hash + Clone, T: Loadable<K>> ResourceCache<T, K> {
    pub fn new() -> ResourceCache<T, K> {
        ResourceCache { cache: HashMap::new() }
    }

    pub fn get(&mut self, key: &K) -> Option<Rc<T>> {
        if let Some(v) = self.cache.get(key) {
            return Some(v.clone());
        }

        if let Some(v) = T::load(key) {
            let v = Rc::new(v);
            self.cache.insert(key.clone(), v.clone());
            Some(v)
        } else {
            None
        }
    }

    pub fn insert(&mut self, key: K, value: T) {
        self.cache.insert(key, Rc::new(value));
    }
}

/// Implement a thread-local store for a given resource type.
///
/// Name is the identifier for the store, key and value are the types of the resource cache keys
/// and the resources.
///
///     # #[macro_use] extern crate calx_resource;
///     use calx_resource::{Resource, ResourceStore, Loadable};
///
///     // A custom resource type.
///     struct MyResource { pub text: String }
///
///     // Must be present even if there's no specific implementation.
///     impl Loadable for MyResource {}
///
///     // Generate a resource store.
///     impl_store!(MY_RESOURCE_STORE, String, MyResource);
///
///     fn main() {
///         // Save a resource in the store using a key.
///         MyResource::insert_resource(
///             "test_resource".to_string(),
///             MyResource { text: "Hello, world!".to_string() });
///
///         // Fetch a resource handle from the store using our key.
///         let handle: Resource<MyResource> =
///             Resource::new("test_resource".to_string()).unwrap();
///         assert!(&handle.text == "Hello, world!");
///     }
#[macro_export]
macro_rules! impl_store {
    ($name:ident, $key:ty, $value:ty) => {
    thread_local!(static $name: ::std::cell::RefCell<$crate::ResourceCache<$value, $key>> =
                  ::std::cell::RefCell::new($crate::ResourceCache::new()));

    impl $crate::ResourceStore<$key> for $value {
        fn get_resource(key: &$key) -> Option<::std::rc::Rc<Self>> {
            $name.with(|t| t.borrow_mut().get(key))
        }

        fn insert_resource(key: $key, value: $value) {
            $name.with(|t| t.borrow_mut().insert(key, value));
        }
    }}
}
<commit_msg>Resources are Arc instead of Rc for multithreaded use<commit_after>// !
// Resource caching smart pointer
//

extern crate serde;

use std::sync::Arc;
use std::ops::Deref;
use std::hash;
use std::collections::HashMap;
use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// A type that implements a singleton resource store.
pub trait ResourceStore<K = String> {
    fn get_resource(key: &K) -> Option<Arc<Self>> where Self: Sized;

    fn insert_resource(key: K, resource: Self);
}

/// Smart pointer for an immutable cached resource.
///
/// The semantics are similar to Arc pointers.
///
/// Resource values will serialize as their key values, so they can be attached to structures that
/// require compact serialization.
#[derive(Clone)]
pub struct Resource<T, K = String> {
    handle: Arc<T>,
    key: K,
}

impl<T: Sized, K> AsRef<T> for Resource<T, K> {
    fn as_ref(&self) -> &T {
        self.handle.as_ref()
    }
}

impl<T: Sized, K> Deref for Resource<T, K> {
    type Target = T;

    #[inline(always)]
    fn deref(&self) -> &T {
        self.handle.deref()
    }
}

impl<T> Serialize for Resource<T> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.key.serialize(serializer)
    }
}

impl<T: ResourceStore> Deserialize for Resource<T> {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        let key: String = try!(Deserialize::deserialize(deserializer));
        Ok(Self::new(key).unwrap())
    }
}

impl<T, K: hash::Hash> hash::Hash for Resource<T, K> {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.key.hash(state);
    }
}

impl<T, K: PartialEq> PartialEq for Resource<T, K> {
    fn eq(&self, other: &Self) -> bool {
        self.key.eq(&other.key)
    }
}

impl<T, K: Eq> Eq for Resource<T, K> {}

impl<T, K: fmt::Display> fmt::Display for Resource<T, K> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        self.key.fmt(formatter)
    }
}

impl<T, K: fmt::Debug> fmt::Debug for Resource<T, K> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        self.key.fmt(formatter)
    }
}

impl<K, T: ResourceStore<K>> Resource<T, K> {
    pub fn new(key: K) -> Option<Self> {
        if let Some(handle) = ResourceStore::get_resource(&key) {
            Some(Resource {
                handle: handle,
                key: key,
            })
        } else {
            None
        }
    }
}

/// A value that can be implicitly constructed given a key.
pub trait Loadable<K = String> {
    fn load(_key: &K) -> Option<Self>
        where Self: Sized
    {
        // Default implementation so that types with no load semantics can be used with
        // ResourceCache so that all inserts must be explicit.
        None
    }
}

/// A cache that associates resource values with paths.
///
/// Resources and paths are assumed to be immutable.
pub struct ResourceCache<T, K = String> {
    cache: HashMap<K, Arc<T>>,
}

impl<K: Eq + hash::Hash + Clone, T: Loadable<K>> ResourceCache<T, K> {
    pub fn new() -> ResourceCache<T, K> {
        ResourceCache { cache: HashMap::new() }
    }

    pub fn get(&mut self, key: &K) -> Option<Arc<T>> {
        if let Some(v) = self.cache.get(key) {
            return Some(v.clone());
        }

        if let Some(v) = T::load(key) {
            let v = Arc::new(v);
            self.cache.insert(key.clone(), v.clone());
            Some(v)
        } else {
            None
        }
    }

    pub fn insert(&mut self, key: K, value: T) {
        self.cache.insert(key, Arc::new(value));
    }
}

/// Implement a thread-local store for a given resource type.
///
/// Name is the identifier for the store, key and value are the types of the resource cache keys
/// and the resources.
///
///     # #[macro_use] extern crate calx_resource;
///     use calx_resource::{Resource, ResourceStore, Loadable};
///
///     // A custom resource type.
///     struct MyResource { pub text: String }
///
///     // Must be present even if there's no specific implementation.
///     impl Loadable for MyResource {}
///
///     // Generate a resource store.
///     impl_store!(MY_RESOURCE_STORE, String, MyResource);
///
///     fn main() {
///         // Save a resource in the store using a key.
///         MyResource::insert_resource(
///             "test_resource".to_string(),
///             MyResource { text: "Hello, world!".to_string() });
///
///         // Fetch a resource handle from the store using our key.
///         let handle: Resource<MyResource> =
///             Resource::new("test_resource".to_string()).unwrap();
///         assert!(&handle.text == "Hello, world!");
///     }
#[macro_export]
macro_rules! impl_store {
    ($name:ident, $key:ty, $value:ty) => {
    thread_local!(static $name: ::std::cell::RefCell<$crate::ResourceCache<$value, $key>> =
                  ::std::cell::RefCell::new($crate::ResourceCache::new()));

    impl $crate::ResourceStore<$key> for $value {
        fn get_resource(key: &$key) -> Option<::std::sync::Arc<Self>> {
            $name.with(|t| t.borrow_mut().get(key))
        }

        fn insert_resource(key: $key, value: $value) {
            $name.with(|t| t.borrow_mut().insert(key, value));
        }
    }}
}
<|endoftext|>


<commit_before>//! Utilities for interacting with tasks implemented on the Rosetta Code wiki.

use std::collections::HashMap;

use lazy_static::lazy_static;
use percent_encoding::{self, define_encode_set, QUERY_ENCODE_SET};
use regex::Regex;
use reqwest::Url;
use serde::Deserialize;

lazy_static! {
    /// Extracts code from the first Rust section from Rosetta Code wiki markup.
    static ref RUST_WIKI_SECTION_RE: Regex =
        Regex::new(r"==\{\{header\|[Rr]ust\}\}==(?s:.*?)<lang [Rr]ust>((?s:.*?))</lang>").unwrap();
}

define_encode_set! {
    /// Encoding set used for Rosetta Code URLs.
    ///
    /// The wiki generally encodes characters found in the query encode set, as well as some
    /// additional characters.
    pub ROSETTA_ENCODE_SET = [QUERY_ENCODE_SET] | { '+' }
}

/// Represents a task implemented on the Rosetta Code wiki.
#[derive(Debug, Clone)]
pub struct RemoteTask {
    title: String,
    url: Url,
    source: Option<String>,
}

impl RemoteTask {
    /// Returns the title of the task.
    pub fn title(&self) -> String {
        self.title.clone()
    }

    /// Returns the URL to the task on the wiki.
    pub fn url(&self) -> Url {
        self.url.clone()
    }

    /// If the task contains a Rust implementation on the wiki, this field will contain the first
    /// Rust code block of that section. Otherwise, returns `None`.
    pub fn source(&self) -> Option<String> {
        self.source.clone()
    }
}

#[derive(Debug, Deserialize)]
pub struct Response {
    pub query: Pages,
}

/// Intermediate container in the query API response.
#[derive(Debug, Deserialize)]
pub struct Pages {
    pub pages: HashMap<i32, Page>,
}

/// Individual page in the query API response.
#[derive(Debug, Deserialize)]
pub struct Page {
    #[serde(rename = "pageid")]
    pub id: i32,

    pub title: String,

    #[serde(default)]
    pub revisions: Vec<Revision>,
}

#[derive(Debug, Deserialize)]
pub struct Revision {
    #[serde(rename = "*")]
    pub content: String,
}

/// Transforms a URL-encoded task title from the wiki to a human-readable task title.
pub fn decode_title(title: &str) -> String {
    let title = title.replace('_', " ").into_bytes();
    let decoded = percent_encoding::percent_decode(&title);
    String::from_utf8(decoded.collect()).unwrap()
}

/// Transforms a human-readable task title to the form used in a wiki URL.
pub fn encode_title(title: &str) -> String {
    let snake_case_title = title.replace(' ', "_").into_bytes();
    let encoded = percent_encoding::percent_encode(&snake_case_title, ROSETTA_ENCODE_SET);
    encoded.collect()
}

pub fn parse_tasks(response: &Response) -> Vec<RemoteTask> {
    response
        .query
        .pages
        .values()
        .map(|page| {
            let source = RUST_WIKI_SECTION_RE
                .captures(&page.revisions[0].content)
                .map(|captures| captures.get(1).unwrap())
                .map(|m| m.as_str().to_owned());

            let title = page.title.clone();
            let url = Url::parse(&format!(
                "http://rosettacode.org/wiki/{}",
                encode_title(&title)
            ))
            .unwrap();

            RemoteTask { title, url, source }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    #[test]
    fn encode() {
        use super::encode_title;
        assert_eq!(
            encode_title("K-means++ clustering"),
            "K-means%2B%2B_clustering"
        );
    }

    #[test]
    fn decode() {
        use super::decode_title;
        assert_eq!(
            decode_title("K-means%2B%2B_clustering"),
            "K-means++ clustering"
        );
    }

    #[test]
    fn encode_decode() {
        const TITLE: &str = "Penney's game";

        assert_eq!(super::decode_title(&super::encode_title(TITLE)), TITLE);
    }
}
<commit_msg>coverage: FIx error for tasks are with 0 revisions<commit_after>//! Utilities for interacting with tasks implemented on the Rosetta Code wiki.

use std::collections::HashMap;

use lazy_static::lazy_static;
use percent_encoding::{self, define_encode_set, QUERY_ENCODE_SET};
use regex::Regex;
use reqwest::Url;
use serde::Deserialize;

lazy_static! {
    /// Extracts code from the first Rust section from Rosetta Code wiki markup.
    static ref RUST_WIKI_SECTION_RE: Regex =
        Regex::new(r"==\{\{header\|[Rr]ust\}\}==(?s:.*?)<lang [Rr]ust>((?s:.*?))</lang>").unwrap();
}

define_encode_set! {
    /// Encoding set used for Rosetta Code URLs.
    ///
    /// The wiki generally encodes characters found in the query encode set, as well as some
    /// additional characters.
    pub ROSETTA_ENCODE_SET = [QUERY_ENCODE_SET] | { '+' }
}

/// Represents a task implemented on the Rosetta Code wiki.
#[derive(Debug, Clone)]
pub struct RemoteTask {
    title: String,
    url: Url,
    source: Option<String>,
}

impl RemoteTask {
    /// Returns the title of the task.
    pub fn title(&self) -> String {
        self.title.clone()
    }

    /// Returns the URL to the task on the wiki.
    pub fn url(&self) -> Url {
        self.url.clone()
    }

    /// If the task contains a Rust implementation on the wiki, this field will contain the first
    /// Rust code block of that section. Otherwise, returns `None`.
    pub fn source(&self) -> Option<String> {
        self.source.clone()
    }
}

#[derive(Debug, Deserialize)]
pub struct Response {
    pub query: Pages,
}

/// Intermediate container in the query API response.
#[derive(Debug, Deserialize)]
pub struct Pages {
    pub pages: HashMap<i32, Page>,
}

/// Individual page in the query API response.
#[derive(Debug, Deserialize)]
pub struct Page {
    #[serde(rename = "pageid")]
    pub id: i32,

    pub title: String,

    #[serde(default)]
    pub revisions: Vec<Revision>,
}

#[derive(Debug, Deserialize)]
pub struct Revision {
    #[serde(rename = "*")]
    pub content: String,
}

/// Transforms a URL-encoded task title from the wiki to a human-readable task title.
pub fn decode_title(title: &str) -> String {
    let title = title.replace('_', " ").into_bytes();
    let decoded = percent_encoding::percent_decode(&title);
    String::from_utf8(decoded.collect()).unwrap()
}

/// Transforms a human-readable task title to the form used in a wiki URL.
pub fn encode_title(title: &str) -> String {
    let snake_case_title = title.replace(' ', "_").into_bytes();
    let encoded = percent_encoding::percent_encode(&snake_case_title, ROSETTA_ENCODE_SET);
    encoded.collect()
}

pub fn parse_tasks(response: &Response) -> Vec<RemoteTask> {
    response
        .query
        .pages
        .values()
        .map(|page| {
            let mut source = None;
            if !page.revisions.is_empty() {
                source = RUST_WIKI_SECTION_RE
                    .captures(&page.revisions[0].content)
                    .map(|captures| captures.get(1).unwrap())
                    .map(|m| m.as_str().to_owned());
            }

            let title = page.title.clone();
            let url = Url::parse(&format!(
                "http://rosettacode.org/wiki/{}",
                encode_title(&title)
            ))
            .unwrap();

            RemoteTask { title, url, source }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    #[test]
    fn encode() {
        use super::encode_title;
        assert_eq!(
            encode_title("K-means++ clustering"),
            "K-means%2B%2B_clustering"
        );
    }

    #[test]
    fn decode() {
        use super::decode_title;
        assert_eq!(
            decode_title("K-means%2B%2B_clustering"),
            "K-means++ clustering"
        );
    }

    #[test]
    fn encode_decode() {
        const TITLE: &str = "Penney's game";

        assert_eq!(super::decode_title(&super::encode_title(TITLE)), TITLE);
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! ncurses-compatible database discovery
//!
//! Does not support hashed database, only filesystem!

use std::env;
use std::fs;
use std::path::PathBuf;

/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
    let mut dirs_to_search = Vec::new();
    let first_char = match term.chars().next() {
        Some(c) => c,
        None => return None,
    };

    // Find search directory
    if let Some(dir) = env::var_os("TERMINFO") {
        dirs_to_search.push(PathBuf::from(dir));
    }

    if let Ok(dirs) = match env::var("TERMINFO_DIRS") {
        for i in dirs.split(':') {
            if i == "" {
                dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
            } else {
                dirs_to_search.push(PathBuf::from(i));
            }
        }
    } else {
        // Found nothing in TERMINFO_DIRS, use the default paths:
        // According to  /etc/terminfo/README, after looking at
        // ~/.terminfo, ncurses will search /etc/terminfo, then
        // /lib/terminfo, and eventually /usr/share/terminfo.
        // On Haiku the database can be found at /boot/system/data/terminfo
        if let Some(mut homedir) = env::home_dir() {
            homedir.push(".terminfo");
            dirs_to_search.push(homedir)
        }

        dirs_to_search.push(PathBuf::from("/etc/terminfo"));
        dirs_to_search.push(PathBuf::from("/lib/terminfo"));
        dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
        dirs_to_search.push(PathBuf::from("/boot/system/data/terminfo"));
    }

    // Look for the terminal in all of the search directories
    for mut p in dirs_to_search {
        if fs::metadata(&p).is_ok() {
            p.push(&first_char.to_string());
            p.push(&term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
            p.pop();
            p.pop();

            // on some installations the dir is named after the hex of the char
            // (e.g. OS X)
            p.push(&format!("{:x}", first_char as usize));
            p.push(term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
        }
    }
    None
}
<commit_msg>Document why we don't stop at TERMINFO<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! ncurses-compatible database discovery
//!
//! Does not support hashed database, only filesystem!

use std::env;
use std::fs;
use std::path::PathBuf;

/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
    let mut dirs_to_search = Vec::new();
    let first_char = match term.chars().next() {
        Some(c) => c,
        None => return None,
    };

    // Find search directory
    // The terminfo manual says:
    //
    // > If  the  environment  variable  TERMINFO is set, it is interpreted
    // > as the pathname of a directory containing the compiled description
    // > you are working on.  Only that directory is searched.
    //
    // However, the ncurses manual says:
    //
    // > If the environment variable TERMINFO is defined, any program using
    // > curses checks for a local terminal definition  before  checking in
    // > the standard place.
    //
    // Given that ncurses is the defacto standard, we follow the ncurses manual.
    if let Some(dir) = env::var_os("TERMINFO") {
        dirs_to_search.push(PathBuf::from(dir));
    }

    if let Ok(dirs) = match env::var("TERMINFO_DIRS") {
        for i in dirs.split(':') {
            if i == "" {
                dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
            } else {
                dirs_to_search.push(PathBuf::from(i));
            }
        }
    } else {
        // Found nothing in TERMINFO_DIRS, use the default paths:
        // According to  /etc/terminfo/README, after looking at
        // ~/.terminfo, ncurses will search /etc/terminfo, then
        // /lib/terminfo, and eventually /usr/share/terminfo.
        // On Haiku the database can be found at /boot/system/data/terminfo
        if let Some(mut homedir) = env::home_dir() {
            homedir.push(".terminfo");
            dirs_to_search.push(homedir)
        }

        dirs_to_search.push(PathBuf::from("/etc/terminfo"));
        dirs_to_search.push(PathBuf::from("/lib/terminfo"));
        dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
        dirs_to_search.push(PathBuf::from("/boot/system/data/terminfo"));
    }

    // Look for the terminal in all of the search directories
    for mut p in dirs_to_search {
        if fs::metadata(&p).is_ok() {
            p.push(&first_char.to_string());
            p.push(&term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
            p.pop();
            p.pop();

            // on some installations the dir is named after the hex of the char
            // (e.g. OS X)
            p.push(&format!("{:x}", first_char as usize));
            p.push(term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
        }
    }
    None
}
<|endoftext|>


<commit_before>use crate::io::{self, ErrorKind, Read, Write};
use crate::mem::MaybeUninit;

/// Copies the entire contents of a reader into a writer.
///
/// This function will continuously read data from `reader` and then
/// write it into `writer` in a streaming fashion until `reader`
/// returns EOF.
///
/// On success, the total number of bytes that were copied from
/// `reader` to `writer` is returned.
///
/// If you’re wanting to copy the contents of one file to another and you’re
/// working with filesystem paths, see the [`fs::copy`] function.
///
/// [`fs::copy`]: crate::fs::copy
///
/// # Errors
///
/// This function will return an error immediately if any call to [`read`] or
/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
/// handled by this function and the underlying operation is retried.
///
/// [`read`]: Read::read
/// [`write`]: Write::write
///
/// # Examples
///
/// ```
/// use std::io;
///
/// fn main() -> io::Result<()> {
///     let mut reader: &[u8] = b"hello";
///     let mut writer: Vec<u8> = vec![];
///
///     io::copy(&mut reader, &mut writer)?;
///
///     assert_eq!(&b"hello"[..], &writer[..]);
///     Ok(())
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where
    R: Read,
    W: Write,
{
    #[cfg(any(target_os = "linux", target_os = "android"))]
    {
        kernel_copy::copy_spec(reader, writer)
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    generic_copy(reader, writer)
}

pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where
    R: Read,
    W: Write,
{
    let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit();
    // FIXME: #42788
    //
    //   - This creates a (mut) reference to a slice of
    //     _uninitialized_ integers, which is **undefined behavior**
    //
    //   - Only the standard library gets to soundly "ignore" this,
    //     based on its privileged knowledge of unstable rustc
    //     internals;
    unsafe {
        reader.initializer().initialize(buf.assume_init_mut());
    }

    let mut written = 0;
    loop {
        let len = match reader.read(unsafe { buf.assume_init_mut() }) {
            Ok(0) => return Ok(written),
            Ok(len) => len,
            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
            Err(e) => return Err(e),
        };
        writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
        written += len as u64;
    }
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod kernel_copy {

    use crate::cmp::min;
    use crate::convert::TryInto;
    use crate::fs::{File, Metadata};
    use crate::io::{
        BufRead, BufReader, BufWriter, Read, Result, StderrLock, StdinLock, StdoutLock, Take, Write,
    };
    use crate::mem::ManuallyDrop;
    use crate::net::TcpStream;
    use crate::os::unix::fs::FileTypeExt;
    use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
    use crate::process::{ChildStderr, ChildStdin, ChildStdout};

    pub(super) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
        read: &mut R,
        write: &mut W,
    ) -> Result<u64> {
        let copier = Copier { read, write };
        SpecCopy::copy(copier)
    }

    enum FdMeta {
        Metadata(Metadata),
        Socket,
        Pipe,
        None,
    }

    impl FdMeta {
        fn is_fifo(&self) -> bool {
            match self {
                FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
                FdMeta::Socket => false,
                FdMeta::Pipe => true,
                FdMeta::None => false,
            }
        }
    }

    struct CopyParams(FdMeta, Option<RawFd>);

    struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
        pub read: &'a mut R,
        pub write: &'b mut W,
    }

    trait SpecCopy {
        fn copy(self) -> Result<u64>;
    }

    impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
        default fn copy(self) -> Result<u64> {
            super::generic_copy(self.read, self.write)
        }
    }

    impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
        fn copy(self) -> Result<u64> {
            let (reader, writer) = (self.read, self.write);
            let r_cfg = reader.properties();
            let w_cfg = writer.properties();

            // before direct operations  on file descriptors ensure that all source and sink buffers are emtpy
            let mut flush = || -> crate::io::Result<u64> {
                let bytes = reader.drain_to(writer, u64::MAX)?;
                writer.flush()?;
                Ok(bytes)
            };

            match (r_cfg, w_cfg) {
                (
                    CopyParams(FdMeta::Metadata(reader_meta), Some(readfd)),
                    CopyParams(FdMeta::Metadata(writer_meta), Some(writefd)),
                ) if reader_meta.is_file() && writer_meta.is_file() => {
                    let bytes_flushed = flush()?;
                    let max_write = reader.min_limit();
                    let (mut reader, mut writer) =
                        unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
                    let len = reader_meta.len();
                    crate::sys::fs::copy_regular_files(
                        &mut reader,
                        &mut writer,
                        min(len, max_write),
                    )
                    .map(|bytes_copied| bytes_copied + bytes_flushed)
                }
                (CopyParams(reader_meta, Some(readfd)), CopyParams(writer_meta, Some(writefd)))
                    if reader_meta.is_fifo() || writer_meta.is_fifo() =>
                {
                    // splice
                    let bytes_flushed = flush()?;
                    let max_write = reader.min_limit();
                    let (mut reader, mut writer) =
                        unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
                    crate::sys::fs::sendfile_splice(
                        crate::sys::fs::SpliceMode::Splice,
                        &mut reader,
                        &mut writer,
                        max_write,
                    )
                    .map(|bytes_sent| bytes_sent + bytes_flushed)
                }
                (
                    CopyParams(FdMeta::Metadata(reader_meta), Some(readfd)),
                    CopyParams(_, Some(writefd)),
                ) if reader_meta.is_file() => {
                    // try sendfile, most modern systems it should work with any target as long as the source is a mmapable file.
                    // in the rare cases where it's no supported the wrapper function will fall back to a normal copy loop
                    let bytes_flushed = flush()?;
                    let (mut reader, mut writer) =
                        unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
                    let len = reader_meta.len();
                    let max_write = reader.min_limit();
                    crate::sys::fs::sendfile_splice(
                        crate::sys::fs::SpliceMode::Sendfile,
                        &mut reader,
                        &mut writer,
                        min(len, max_write),
                    )
                    .map(|bytes_sent| bytes_sent + bytes_flushed)
                }
                _ => super::generic_copy(reader, writer),
            }
        }
    }

    #[rustc_specialization_trait]
    trait CopyRead: Read {
        fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
            Ok(0)
        }

        /// The minimum of the limit of all `Take<_>` wrappers, `u64::MAX` otherwise.
        /// This method does not account for data `BufReader` buffers and would underreport
        /// the limit of a `Take<BufReader<Take<_>>>` type. Thus its result is only valid
        /// after draining the buffers.
        fn min_limit(&self) -> u64 {
            u64::MAX
        }

        fn properties(&self) -> CopyParams;
    }

    #[rustc_specialization_trait]
    trait CopyWrite: Write {
        fn properties(&self) -> CopyParams;
    }

    impl CopyRead for File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for &File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for &File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for &TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for &TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for ChildStdin {
        fn properties(&self) -> CopyParams {
            CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for ChildStdout {
        fn properties(&self) -> CopyParams {
            CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for ChildStderr {
        fn properties(&self) -> CopyParams {
            CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for StdinLock<'_> {
        fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
            let buf_reader = self.as_mut_buf();
            let buf = buf_reader.buffer();
            let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
            let bytes_drained = buf.len();
            writer.write_all(buf)?;
            buf_reader.consume(bytes_drained);

            Ok(bytes_drained as u64)
        }

        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for StdoutLock<'_> {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for StderrLock<'_> {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl<T: CopyRead> CopyRead for Take<T> {
        fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
            let local_limit = self.limit();
            let combined_limit = min(outer_limit, local_limit);
            let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
            // update limit since read() was bypassed
            self.set_limit(local_limit - bytes_drained);

            Ok(bytes_drained)
        }

        fn min_limit(&self) -> u64 {
            min(Take::limit(self), self.get_ref().min_limit())
        }

        fn properties(&self) -> CopyParams {
            self.get_ref().properties()
        }
    }

    impl<T: CopyRead> CopyRead for BufReader<T> {
        fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
            let buf = self.buffer();
            let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
            let bytes = buf.len();
            writer.write_all(buf)?;
            self.consume(bytes);

            let remaining = outer_limit - bytes as u64;

            // in case of nested bufreaders we also need to drain the ones closer to the source
            let inner_bytes = self.get_mut().drain_to(writer, remaining)?;

            Ok(bytes as u64 + inner_bytes)
        }

        fn min_limit(&self) -> u64 {
            self.get_ref().min_limit()
        }

        fn properties(&self) -> CopyParams {
            self.get_ref().properties()
        }
    }

    impl<T: CopyWrite> CopyWrite for BufWriter<T> {
        fn properties(&self) -> CopyParams {
            self.get_ref().properties()
        }
    }

    fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
        let fd = fd.as_raw_fd();
        let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
        match file.metadata() {
            Ok(meta) => FdMeta::Metadata(meta),
            Err(_) => FdMeta::None,
        }
    }

    unsafe fn fd_as_file(fd: RawFd) -> ManuallyDrop<File> {
        ManuallyDrop::new(File::from_raw_fd(fd))
    }
}
<commit_msg>prioritize sendfile over splice since it results in fewer context switches when sending to pipes<commit_after>use crate::io::{self, ErrorKind, Read, Write};
use crate::mem::MaybeUninit;

/// Copies the entire contents of a reader into a writer.
///
/// This function will continuously read data from `reader` and then
/// write it into `writer` in a streaming fashion until `reader`
/// returns EOF.
///
/// On success, the total number of bytes that were copied from
/// `reader` to `writer` is returned.
///
/// If you’re wanting to copy the contents of one file to another and you’re
/// working with filesystem paths, see the [`fs::copy`] function.
///
/// [`fs::copy`]: crate::fs::copy
///
/// # Errors
///
/// This function will return an error immediately if any call to [`read`] or
/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
/// handled by this function and the underlying operation is retried.
///
/// [`read`]: Read::read
/// [`write`]: Write::write
///
/// # Examples
///
/// ```
/// use std::io;
///
/// fn main() -> io::Result<()> {
///     let mut reader: &[u8] = b"hello";
///     let mut writer: Vec<u8> = vec![];
///
///     io::copy(&mut reader, &mut writer)?;
///
///     assert_eq!(&b"hello"[..], &writer[..]);
///     Ok(())
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where
    R: Read,
    W: Write,
{
    #[cfg(any(target_os = "linux", target_os = "android"))]
    {
        kernel_copy::copy_spec(reader, writer)
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    generic_copy(reader, writer)
}

pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where
    R: Read,
    W: Write,
{
    let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit();
    // FIXME: #42788
    //
    //   - This creates a (mut) reference to a slice of
    //     _uninitialized_ integers, which is **undefined behavior**
    //
    //   - Only the standard library gets to soundly "ignore" this,
    //     based on its privileged knowledge of unstable rustc
    //     internals;
    unsafe {
        reader.initializer().initialize(buf.assume_init_mut());
    }

    let mut written = 0;
    loop {
        let len = match reader.read(unsafe { buf.assume_init_mut() }) {
            Ok(0) => return Ok(written),
            Ok(len) => len,
            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
            Err(e) => return Err(e),
        };
        writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
        written += len as u64;
    }
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod kernel_copy {

    use crate::cmp::min;
    use crate::convert::TryInto;
    use crate::fs::{File, Metadata};
    use crate::io::{
        BufRead, BufReader, BufWriter, Read, Result, StderrLock, StdinLock, StdoutLock, Take, Write,
    };
    use crate::mem::ManuallyDrop;
    use crate::net::TcpStream;
    use crate::os::unix::fs::FileTypeExt;
    use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
    use crate::process::{ChildStderr, ChildStdin, ChildStdout};

    pub(super) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
        read: &mut R,
        write: &mut W,
    ) -> Result<u64> {
        let copier = Copier { read, write };
        SpecCopy::copy(copier)
    }

    enum FdMeta {
        Metadata(Metadata),
        Socket,
        Pipe,
        None,
    }

    impl FdMeta {
        fn is_fifo(&self) -> bool {
            match self {
                FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
                FdMeta::Socket => false,
                FdMeta::Pipe => true,
                FdMeta::None => false,
            }
        }
    }

    struct CopyParams(FdMeta, Option<RawFd>);

    struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
        pub read: &'a mut R,
        pub write: &'b mut W,
    }

    trait SpecCopy {
        fn copy(self) -> Result<u64>;
    }

    impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
        default fn copy(self) -> Result<u64> {
            super::generic_copy(self.read, self.write)
        }
    }

    impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
        fn copy(self) -> Result<u64> {
            let (reader, writer) = (self.read, self.write);
            let r_cfg = reader.properties();
            let w_cfg = writer.properties();

            // before direct operations  on file descriptors ensure that all source and sink buffers are emtpy
            let mut flush = || -> crate::io::Result<u64> {
                let bytes = reader.drain_to(writer, u64::MAX)?;
                writer.flush()?;
                Ok(bytes)
            };

            match (r_cfg, w_cfg) {
                (
                    CopyParams(FdMeta::Metadata(reader_meta), Some(readfd)),
                    CopyParams(FdMeta::Metadata(writer_meta), Some(writefd)),
                ) if reader_meta.is_file() && writer_meta.is_file() => {
                    let bytes_flushed = flush()?;
                    let max_write = reader.min_limit();
                    let (mut reader, mut writer) =
                        unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
                    let len = reader_meta.len();
                    crate::sys::fs::copy_regular_files(
                        &mut reader,
                        &mut writer,
                        min(len, max_write),
                    )
                    .map(|bytes_copied| bytes_copied + bytes_flushed)
                }
                (
                    CopyParams(FdMeta::Metadata(reader_meta), Some(readfd)),
                    CopyParams(_, Some(writefd)),
                ) if reader_meta.is_file() => {
                    // try sendfile, most modern systems it should work with any target as long as the source is a mmapable file.
                    // in the rare cases where it's no supported the wrapper function will fall back to a normal copy loop
                    let bytes_flushed = flush()?;
                    let (mut reader, mut writer) =
                        unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
                    let len = reader_meta.len();
                    let max_write = reader.min_limit();
                    crate::sys::fs::sendfile_splice(
                        crate::sys::fs::SpliceMode::Sendfile,
                        &mut reader,
                        &mut writer,
                        min(len, max_write),
                    )
                        .map(|bytes_sent| bytes_sent + bytes_flushed)
                }
                (CopyParams(reader_meta, Some(readfd)), CopyParams(writer_meta, Some(writefd)))
                    if reader_meta.is_fifo() || writer_meta.is_fifo() =>
                {
                    // splice
                    let bytes_flushed = flush()?;
                    let max_write = reader.min_limit();
                    let (mut reader, mut writer) =
                        unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
                    crate::sys::fs::sendfile_splice(
                        crate::sys::fs::SpliceMode::Splice,
                        &mut reader,
                        &mut writer,
                        max_write,
                    )
                    .map(|bytes_sent| bytes_sent + bytes_flushed)
                }
                _ => super::generic_copy(reader, writer),
            }
        }
    }

    #[rustc_specialization_trait]
    trait CopyRead: Read {
        fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
            Ok(0)
        }

        /// The minimum of the limit of all `Take<_>` wrappers, `u64::MAX` otherwise.
        /// This method does not account for data `BufReader` buffers and would underreport
        /// the limit of a `Take<BufReader<Take<_>>>` type. Thus its result is only valid
        /// after draining the buffers.
        fn min_limit(&self) -> u64 {
            u64::MAX
        }

        fn properties(&self) -> CopyParams;
    }

    #[rustc_specialization_trait]
    trait CopyWrite: Write {
        fn properties(&self) -> CopyParams;
    }

    impl CopyRead for File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for &File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for &File {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for &TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for &TcpStream {
        fn properties(&self) -> CopyParams {
            // avoid the stat syscall since we can be fairly sure it's a socket
            CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for ChildStdin {
        fn properties(&self) -> CopyParams {
            CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for ChildStdout {
        fn properties(&self) -> CopyParams {
            CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for ChildStderr {
        fn properties(&self) -> CopyParams {
            CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
        }
    }

    impl CopyRead for StdinLock<'_> {
        fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
            let buf_reader = self.as_mut_buf();
            let buf = buf_reader.buffer();
            let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
            let bytes_drained = buf.len();
            writer.write_all(buf)?;
            buf_reader.consume(bytes_drained);

            Ok(bytes_drained as u64)
        }

        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for StdoutLock<'_> {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl CopyWrite for StderrLock<'_> {
        fn properties(&self) -> CopyParams {
            CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
        }
    }

    impl<T: CopyRead> CopyRead for Take<T> {
        fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
            let local_limit = self.limit();
            let combined_limit = min(outer_limit, local_limit);
            let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
            // update limit since read() was bypassed
            self.set_limit(local_limit - bytes_drained);

            Ok(bytes_drained)
        }

        fn min_limit(&self) -> u64 {
            min(Take::limit(self), self.get_ref().min_limit())
        }

        fn properties(&self) -> CopyParams {
            self.get_ref().properties()
        }
    }

    impl<T: CopyRead> CopyRead for BufReader<T> {
        fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
            let buf = self.buffer();
            let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
            let bytes = buf.len();
            writer.write_all(buf)?;
            self.consume(bytes);

            let remaining = outer_limit - bytes as u64;

            // in case of nested bufreaders we also need to drain the ones closer to the source
            let inner_bytes = self.get_mut().drain_to(writer, remaining)?;

            Ok(bytes as u64 + inner_bytes)
        }

        fn min_limit(&self) -> u64 {
            self.get_ref().min_limit()
        }

        fn properties(&self) -> CopyParams {
            self.get_ref().properties()
        }
    }

    impl<T: CopyWrite> CopyWrite for BufWriter<T> {
        fn properties(&self) -> CopyParams {
            self.get_ref().properties()
        }
    }

    fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
        let fd = fd.as_raw_fd();
        let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
        match file.metadata() {
            Ok(meta) => FdMeta::Metadata(meta),
            Err(_) => FdMeta::None,
        }
    }

    unsafe fn fd_as_file(fd: RawFd) -> ManuallyDrop<File> {
        ManuallyDrop::new(File::from_raw_fd(fd))
    }
}
<|endoftext|>


<commit_before>
/// Implemented by contexts that can add round border.
pub trait AddRoundBorder<'a, T> {
    /// Adds a round radius and a width.
    fn round_border_radius(&'a self, radius: f64) -> T;
}

<commit_msg>Added `round_border_width` to `AddRoundBorder`<commit_after>
/// Implemented by contexts that can add round border.
pub trait AddRoundBorder<'a, T> {
    /// Adds a round radius and a width.
    fn round_border_radius(&'a self, radius: f64) -> T;

    /// Adds a round border width.
    #[inline(always)]
    fn round_border_width(&'a self, width: f64) -> T {
        self.round_border_radius(0.5 * width)
    }
}

<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;

use cassowary::strength::{REQUIRED, WEAK};
use cassowary::WeightedRelation::*;
use cassowary::{Constraint as CassowaryConstraint, Expression, Solver, Variable};

#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum Corner {
    TopLeft,
    TopRight,
    BottomRight,
    BottomLeft,
}

#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Constraint {
    // TODO: enforce range 0 - 100
    Percentage(u16),
    Ratio(u32, u32),
    Length(u16),
    Max(u16),
    Min(u16),
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

// TODO: enforce constraints size once const generics has landed
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Layout {
    direction: Direction,
    margin: u16,
    constraints: Vec<Constraint>,
}

thread_local! {
    static LAYOUT_CACHE: RefCell<HashMap<(Rect, Layout), Vec<Rect>>> = RefCell::new(HashMap::new());
}

impl Default for Layout {
    fn default() -> Layout {
        Layout {
            direction: Direction::Vertical,
            margin: 0,
            constraints: Vec::new(),
        }
    }
}

impl Layout {
    pub fn constraints<C>(mut self, constraints: C) -> Layout
    where
        C: Into<Vec<Constraint>>,
    {
        self.constraints = constraints.into();
        self
    }

    pub fn margin(mut self, margin: u16) -> Layout {
        self.margin = margin;
        self
    }

    pub fn direction(mut self, direction: Direction) -> Layout {
        self.direction = direction;
        self
    }

    /// Wrapper function around the cassowary-rs solver to be able to split a given
    /// area into smaller ones based on the preferred widths or heights and the direction.
    ///
    /// # Examples
    /// ```
    /// # use tui::layout::{Rect, Constraint, Direction, Layout};
    ///
    /// # fn main() {
    ///     let chunks = Layout::default()
    ///         .direction(Direction::Vertical)
    ///         .constraints([Constraint::Length(5), Constraint::Min(0)].as_ref())
    ///         .split(Rect{x: 2, y: 2, width: 10, height: 10});
    ///     assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
    ///                             Rect{x: 2, y: 7, width: 10, height: 5}])
    /// # }
    ///
    /// ```
    pub fn split(self, area: Rect) -> Vec<Rect> {
        // TODO: Maybe use a fixed size cache ?
        LAYOUT_CACHE.with(|c| {
            c.borrow_mut()
                .entry((area, self.clone()))
                .or_insert_with(|| split(area, &self))
                .clone()
        })
    }
}

fn split(area: Rect, layout: &Layout) -> Vec<Rect> {
    let mut solver = Solver::new();
    let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
    let elements = layout
        .constraints
        .iter()
        .map(|_| Element::new())
        .collect::<Vec<Element>>();
    let mut results = layout
        .constraints
        .iter()
        .map(|_| Rect::default())
        .collect::<Vec<Rect>>();

    let dest_area = area.inner(layout.margin);
    for (i, e) in elements.iter().enumerate() {
        vars.insert(e.x, (i, 0));
        vars.insert(e.y, (i, 1));
        vars.insert(e.width, (i, 2));
        vars.insert(e.height, (i, 3));
    }
    let mut ccs: Vec<CassowaryConstraint> =
        Vec::with_capacity(elements.len() * 4 + layout.constraints.len() * 6);
    for elt in &elements {
        ccs.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
        ccs.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
        ccs.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
        ccs.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
    }
    if let Some(first) = elements.first() {
        ccs.push(match layout.direction {
            Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
            Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
        });
    }
    if let Some(last) = elements.last() {
        ccs.push(match layout.direction {
            Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
            Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
        });
    }
    match layout.direction {
        Direction::Horizontal => {
            for pair in elements.windows(2) {
                ccs.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
            }
            for (i, size) in layout.constraints.iter().enumerate() {
                ccs.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
                ccs.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
                ccs.push(match *size {
                    Constraint::Length(v) => elements[i].width | EQ(WEAK) | f64::from(v),
                    Constraint::Percentage(v) => {
                        elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
                    }
                    Constraint::Ratio(n, d) => {
                        elements[i].width
                            | EQ(WEAK)
                            | (f64::from(dest_area.width) * f64::from(n) / f64::from(d))
                    }
                    Constraint::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
                    Constraint::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
                });
            }
        }
        Direction::Vertical => {
            for pair in elements.windows(2) {
                ccs.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
            }
            for (i, size) in layout.constraints.iter().enumerate() {
                ccs.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
                ccs.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
                ccs.push(match *size {
                    Constraint::Length(v) => elements[i].height | EQ(WEAK) | f64::from(v),
                    Constraint::Percentage(v) => {
                        elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
                    }
                    Constraint::Ratio(n, d) => {
                        elements[i].height
                            | EQ(WEAK)
                            | (f64::from(dest_area.height) * f64::from(n) / f64::from(d))
                    }
                    Constraint::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
                    Constraint::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
                });
            }
        }
    }
    solver.add_constraints(&ccs).unwrap();
    for &(var, value) in solver.fetch_changes() {
        let (index, attr) = vars[&var];
        let value = if value.is_sign_negative() {
            0
        } else {
            value as u16
        };
        match attr {
            0 => {
                results[index].x = value;
            }
            1 => {
                results[index].y = value;
            }
            2 => {
                results[index].width = value;
            }
            3 => {
                results[index].height = value;
            }
            _ => {}
        }
    }

    // Fix imprecision by extending the last item a bit if necessary
    if let Some(last) = results.last_mut() {
        match layout.direction {
            Direction::Vertical => {
                last.height = dest_area.bottom() - last.y;
            }
            Direction::Horizontal => {
                last.width = dest_area.right() - last.x;
            }
        }
    }
    results
}

/// A container used by the solver inside split
struct Element {
    x: Variable,
    y: Variable,
    width: Variable,
    height: Variable,
}

impl Element {
    fn new() -> Element {
        Element {
            x: Variable::new(),
            y: Variable::new(),
            width: Variable::new(),
            height: Variable::new(),
        }
    }

    fn left(&self) -> Variable {
        self.x
    }

    fn top(&self) -> Variable {
        self.y
    }

    fn right(&self) -> Expression {
        self.x + self.width
    }

    fn bottom(&self) -> Expression {
        self.y + self.height
    }
}

/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl Default for Rect {
    fn default() -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        }
    }
}

impl Rect {
    /// Creates a new rect, with width and height limited to keep the area under max u16.
    /// If clipped, aspect ratio will be preserved.
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
        let max_area = u16::max_value();
        let (clipped_width, clipped_height) =
            if u32::from(width) * u32::from(height) > u32::from(max_area) {
                let aspect_ratio = f64::from(width) / f64::from(height);
                let max_area_f = f64::from(max_area);
                let height_f = (max_area_f / aspect_ratio).sqrt();
                let width_f = height_f * aspect_ratio;
                (width_f as u16, height_f as u16)
            } else {
                (width, height)
            };
        Rect {
            x,
            y,
            width: clipped_width,
            height: clipped_height,
        }
    }

    pub fn area(self) -> u16 {
        self.width * self.height
    }

    pub fn left(self) -> u16 {
        self.x
    }

    pub fn right(self) -> u16 {
        self.x + self.width
    }

    pub fn top(self) -> u16 {
        self.y
    }

    pub fn bottom(self) -> u16 {
        self.y + self.height
    }

    pub fn inner(self, margin: u16) -> Rect {
        if self.width < 2 * margin || self.height < 2 * margin {
            Rect::default()
        } else {
            Rect {
                x: self.x + margin,
                y: self.y + margin,
                width: self.width - 2 * margin,
                height: self.height - 2 * margin,
            }
        }
    }

    pub fn union(self, other: Rect) -> Rect {
        let x1 = min(self.x, other.x);
        let y1 = min(self.y, other.y);
        let x2 = max(self.x + self.width, other.x + other.width);
        let y2 = max(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersection(self, other: Rect) -> Rect {
        let x1 = max(self.x, other.x);
        let y1 = max(self.y, other.y);
        let x2 = min(self.x + self.width, other.x + other.width);
        let y2 = min(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersects(self, other: Rect) -> bool {
        self.x < other.x + other.width
            && self.x + self.width > other.x
            && self.y < other.y + other.height
            && self.y + self.height > other.y
    }
}

#[test]
fn test_rect_size_truncation() {
    for width in 256u16..300u16 {
        for height in 256u16..300u16 {
            let rect = Rect::new(0, 0, width, height);
            rect.area(); // Should not panic.
            assert!(rect.width < width || rect.height < height);
            // The target dimensions are rounded down so the math will not be too precise
            // but let's make sure the ratios don't diverge crazily.
            assert!(
                (f64::from(rect.width) / f64::from(rect.height)
                    - f64::from(width) / f64::from(height))
                .abs()
                    < 1.0
            )
        }
    }

    // One dimension below 255, one above. Area above max u16.
    let width = 900;
    let height = 100;
    let rect = Rect::new(0, 0, width, height);
    assert_ne!(rect.width, 900);
    assert_ne!(rect.height, 100);
    assert!(rect.width < width || rect.height < height);
}

#[test]
fn test_rect_size_preservation() {
    for width in 0..256u16 {
        for height in 0..256u16 {
            let rect = Rect::new(0, 0, width, height);
            rect.area(); // Should not panic.
            assert_eq!(rect.width, width);
            assert_eq!(rect.height, height);
        }
    }

    // One dimension below 255, one above. Area below max u16.
    let rect = Rect::new(0, 0, 300, 100);
    assert_eq!(rect.width, 300);
    assert_eq!(rect.height, 100);
}
<commit_msg>Add code example for `Constraint::Ratio`<commit_after>use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;

use cassowary::strength::{REQUIRED, WEAK};
use cassowary::WeightedRelation::*;
use cassowary::{Constraint as CassowaryConstraint, Expression, Solver, Variable};

#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum Corner {
    TopLeft,
    TopRight,
    BottomRight,
    BottomLeft,
}

#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Constraint {
    // TODO: enforce range 0 - 100
    Percentage(u16),
    Ratio(u32, u32),
    Length(u16),
    Max(u16),
    Min(u16),
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

// TODO: enforce constraints size once const generics has landed
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Layout {
    direction: Direction,
    margin: u16,
    constraints: Vec<Constraint>,
}

thread_local! {
    static LAYOUT_CACHE: RefCell<HashMap<(Rect, Layout), Vec<Rect>>> = RefCell::new(HashMap::new());
}

impl Default for Layout {
    fn default() -> Layout {
        Layout {
            direction: Direction::Vertical,
            margin: 0,
            constraints: Vec::new(),
        }
    }
}

impl Layout {
    pub fn constraints<C>(mut self, constraints: C) -> Layout
    where
        C: Into<Vec<Constraint>>,
    {
        self.constraints = constraints.into();
        self
    }

    pub fn margin(mut self, margin: u16) -> Layout {
        self.margin = margin;
        self
    }

    pub fn direction(mut self, direction: Direction) -> Layout {
        self.direction = direction;
        self
    }

    /// Wrapper function around the cassowary-rs solver to be able to split a given
    /// area into smaller ones based on the preferred widths or heights and the direction.
    ///
    /// # Examples
    /// ```
    /// # use tui::layout::{Rect, Constraint, Direction, Layout};
    ///
    /// # fn main() {
    ///       let chunks = Layout::default()
    ///           .direction(Direction::Vertical)
    ///           .constraints([Constraint::Length(5), Constraint::Min(0)].as_ref())
    ///           .split(Rect {
    ///               x: 2,
    ///               y: 2,
    ///               width: 10,
    ///               height: 10,
    ///           });
    ///       assert_eq!(
    ///           chunks,
    ///           vec![
    ///               Rect {
    ///                   x: 2,
    ///                   y: 2,
    ///                   width: 10,
    ///                   height: 5
    ///               },
    ///               Rect {
    ///                   x: 2,
    ///                   y: 7,
    ///                   width: 10,
    ///                   height: 5
    ///               }
    ///           ]
    ///       );
    ///
    ///       let chunks = Layout::default()
    ///           .direction(Direction::Horizontal)
    ///           .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)].as_ref())
    ///           .split(Rect {
    ///               x: 0,
    ///               y: 0,
    ///               width: 9,
    ///               height: 2,
    ///           });
    ///       assert_eq!(
    ///           chunks,
    ///           vec![
    ///               Rect {
    ///                   x: 0,
    ///                   y: 0,
    ///                   width: 3,
    ///                   height: 2
    ///               },
    ///               Rect {
    ///                   x: 3,
    ///                   y: 0,
    ///                   width: 6,
    ///                   height: 2
    ///               }
    ///           ]
    ///       );
    /// # }
    ///
    /// ```
    pub fn split(self, area: Rect) -> Vec<Rect> {
        // TODO: Maybe use a fixed size cache ?
        LAYOUT_CACHE.with(|c| {
            c.borrow_mut()
                .entry((area, self.clone()))
                .or_insert_with(|| split(area, &self))
                .clone()
        })
    }
}

fn split(area: Rect, layout: &Layout) -> Vec<Rect> {
    let mut solver = Solver::new();
    let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
    let elements = layout
        .constraints
        .iter()
        .map(|_| Element::new())
        .collect::<Vec<Element>>();
    let mut results = layout
        .constraints
        .iter()
        .map(|_| Rect::default())
        .collect::<Vec<Rect>>();

    let dest_area = area.inner(layout.margin);
    for (i, e) in elements.iter().enumerate() {
        vars.insert(e.x, (i, 0));
        vars.insert(e.y, (i, 1));
        vars.insert(e.width, (i, 2));
        vars.insert(e.height, (i, 3));
    }
    let mut ccs: Vec<CassowaryConstraint> =
        Vec::with_capacity(elements.len() * 4 + layout.constraints.len() * 6);
    for elt in &elements {
        ccs.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
        ccs.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
        ccs.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
        ccs.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
    }
    if let Some(first) = elements.first() {
        ccs.push(match layout.direction {
            Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
            Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
        });
    }
    if let Some(last) = elements.last() {
        ccs.push(match layout.direction {
            Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
            Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
        });
    }
    match layout.direction {
        Direction::Horizontal => {
            for pair in elements.windows(2) {
                ccs.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
            }
            for (i, size) in layout.constraints.iter().enumerate() {
                ccs.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
                ccs.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
                ccs.push(match *size {
                    Constraint::Length(v) => elements[i].width | EQ(WEAK) | f64::from(v),
                    Constraint::Percentage(v) => {
                        elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
                    }
                    Constraint::Ratio(n, d) => {
                        elements[i].width
                            | EQ(WEAK)
                            | (f64::from(dest_area.width) * f64::from(n) / f64::from(d))
                    }
                    Constraint::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
                    Constraint::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
                });
            }
        }
        Direction::Vertical => {
            for pair in elements.windows(2) {
                ccs.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
            }
            for (i, size) in layout.constraints.iter().enumerate() {
                ccs.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
                ccs.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
                ccs.push(match *size {
                    Constraint::Length(v) => elements[i].height | EQ(WEAK) | f64::from(v),
                    Constraint::Percentage(v) => {
                        elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
                    }
                    Constraint::Ratio(n, d) => {
                        elements[i].height
                            | EQ(WEAK)
                            | (f64::from(dest_area.height) * f64::from(n) / f64::from(d))
                    }
                    Constraint::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
                    Constraint::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
                });
            }
        }
    }
    solver.add_constraints(&ccs).unwrap();
    for &(var, value) in solver.fetch_changes() {
        let (index, attr) = vars[&var];
        let value = if value.is_sign_negative() {
            0
        } else {
            value as u16
        };
        match attr {
            0 => {
                results[index].x = value;
            }
            1 => {
                results[index].y = value;
            }
            2 => {
                results[index].width = value;
            }
            3 => {
                results[index].height = value;
            }
            _ => {}
        }
    }

    // Fix imprecision by extending the last item a bit if necessary
    if let Some(last) = results.last_mut() {
        match layout.direction {
            Direction::Vertical => {
                last.height = dest_area.bottom() - last.y;
            }
            Direction::Horizontal => {
                last.width = dest_area.right() - last.x;
            }
        }
    }
    results
}

/// A container used by the solver inside split
struct Element {
    x: Variable,
    y: Variable,
    width: Variable,
    height: Variable,
}

impl Element {
    fn new() -> Element {
        Element {
            x: Variable::new(),
            y: Variable::new(),
            width: Variable::new(),
            height: Variable::new(),
        }
    }

    fn left(&self) -> Variable {
        self.x
    }

    fn top(&self) -> Variable {
        self.y
    }

    fn right(&self) -> Expression {
        self.x + self.width
    }

    fn bottom(&self) -> Expression {
        self.y + self.height
    }
}

/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl Default for Rect {
    fn default() -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        }
    }
}

impl Rect {
    /// Creates a new rect, with width and height limited to keep the area under max u16.
    /// If clipped, aspect ratio will be preserved.
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
        let max_area = u16::max_value();
        let (clipped_width, clipped_height) =
            if u32::from(width) * u32::from(height) > u32::from(max_area) {
                let aspect_ratio = f64::from(width) / f64::from(height);
                let max_area_f = f64::from(max_area);
                let height_f = (max_area_f / aspect_ratio).sqrt();
                let width_f = height_f * aspect_ratio;
                (width_f as u16, height_f as u16)
            } else {
                (width, height)
            };
        Rect {
            x,
            y,
            width: clipped_width,
            height: clipped_height,
        }
    }

    pub fn area(self) -> u16 {
        self.width * self.height
    }

    pub fn left(self) -> u16 {
        self.x
    }

    pub fn right(self) -> u16 {
        self.x + self.width
    }

    pub fn top(self) -> u16 {
        self.y
    }

    pub fn bottom(self) -> u16 {
        self.y + self.height
    }

    pub fn inner(self, margin: u16) -> Rect {
        if self.width < 2 * margin || self.height < 2 * margin {
            Rect::default()
        } else {
            Rect {
                x: self.x + margin,
                y: self.y + margin,
                width: self.width - 2 * margin,
                height: self.height - 2 * margin,
            }
        }
    }

    pub fn union(self, other: Rect) -> Rect {
        let x1 = min(self.x, other.x);
        let y1 = min(self.y, other.y);
        let x2 = max(self.x + self.width, other.x + other.width);
        let y2 = max(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersection(self, other: Rect) -> Rect {
        let x1 = max(self.x, other.x);
        let y1 = max(self.y, other.y);
        let x2 = min(self.x + self.width, other.x + other.width);
        let y2 = min(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersects(self, other: Rect) -> bool {
        self.x < other.x + other.width
            && self.x + self.width > other.x
            && self.y < other.y + other.height
            && self.y + self.height > other.y
    }
}

#[test]
fn test_rect_size_truncation() {
    for width in 256u16..300u16 {
        for height in 256u16..300u16 {
            let rect = Rect::new(0, 0, width, height);
            rect.area(); // Should not panic.
            assert!(rect.width < width || rect.height < height);
            // The target dimensions are rounded down so the math will not be too precise
            // but let's make sure the ratios don't diverge crazily.
            assert!(
                (f64::from(rect.width) / f64::from(rect.height)
                    - f64::from(width) / f64::from(height))
                .abs()
                    < 1.0
            )
        }
    }

    // One dimension below 255, one above. Area above max u16.
    let width = 900;
    let height = 100;
    let rect = Rect::new(0, 0, width, height);
    assert_ne!(rect.width, 900);
    assert_ne!(rect.height, 100);
    assert!(rect.width < width || rect.height < height);
}

#[test]
fn test_rect_size_preservation() {
    for width in 0..256u16 {
        for height in 0..256u16 {
            let rect = Rect::new(0, 0, width, height);
            rect.area(); // Should not panic.
            assert_eq!(rect.width, width);
            assert_eq!(rect.height, height);
        }
    }

    // One dimension below 255, one above. Area below max u16.
    let rect = Rect::new(0, 0, 300, 100);
    assert_eq!(rect.width, 300);
    assert_eq!(rect.height, 100);
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::LinkedList;

use common;
use implicit;
use explicit;
use typed;

use implicit::Expr;
use common::{Id, Result};
use refined::{Base, T};
pub use explicit::Metavar;

// Qbc (bounds checking)
// X: 0, *, len *
// ν < X
// ν <= X
// ν = X
// ν >= X
// ν > X

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum C {
    WellFormed(Box<Type>),
    SubType(Box<Type>, Box<Type>),
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Liquid {
    E(implicit::Expr),
    K(Metavar, Box<LinkedList<Expr>>), // list of pending substitutions
}

pub type Type = T<Liquid>;

pub type Constraint = ((HashSet<Id>, LinkedList<Expr>), C); // Boolean valued expressions & their environments

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Env {
    shape: HashMap<Id, explicit::Type>,
    refined_env: HashMap<Id, Type>,
    path_constraints: LinkedList<Expr>,
}

impl Env {
    fn new(shape: &HashMap<Id, explicit::Type>) -> Env {
        Env {
            shape: shape.clone(),
            refined_env: HashMap::new(),
            path_constraints: LinkedList::new(),
        }
    }

    fn get(&self, s: &Id) -> Type {
        self.refined_env.get(s).unwrap().clone()
    }

    fn insert(&mut self, s: &Id, ty: &Type) {
        self.refined_env.insert(s.clone(), ty.clone());
    }

    fn add_constraint(&mut self, e: &Expr) {
        self.path_constraints.push_back(e.clone())
    }

    fn in_scope(&self) -> HashSet<Id> {
        let keys: HashSet<_> = self.refined_env.keys().cloned().collect();
        return keys
    }

    fn snapshot(&self) -> (HashSet<Id>, LinkedList<Expr>) {
        (self.in_scope(), self.path_constraints.clone())
    }
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct KEnv {
    shape: HashMap<Id, explicit::Type>,
    env_id: String,
    next_id: i32,
}

fn hm_shape(env: &HashMap<Id, explicit::Type>, expr: &Expr) -> explicit::Type {
    use implicit::Expr::*;
    use explicit::Type::*;

    match *expr {
        Var(ref id) => env.get(id).unwrap().clone(),
        Const(common::Const::Int(_)) => TInt,
        Const(common::Const::Bool(_)) => TBool,
        Op2(op, _, _) => explicit::opty(op),
        Fun(ref id, ref e) => TFun(box env.get(id).unwrap().clone(), box hm_shape(env, e)),
        App(ref e1, _) => {
            if let TFun(_, e2) = hm_shape(env, e1) {
                *e2
            } else {
                panic!("expected TFun, not {:?}", expr);
            }
        }
        If(_, ref e2, _) => hm_shape(env, e2),
        Let(ref id, ref e1, ref e2) => hm_shape(env, e2),
        Fix(ref id, ref e) => hm_shape(env, e),
        MkArray(ref sz, ref n) => TIntArray,
        GetArray(ref id, ref idx) => TInt,
        SetArray(ref id, ref idx, ref v) => TIntArray,
        Star => panic!("star found when it shouldn't be"),
        V => panic!("v found when it shouldn't be"),
    }
}

impl KEnv {
    fn new(shape: &HashMap<Id, explicit::Type>) -> KEnv {
        KEnv {
            shape: shape.clone(),
            env_id: String::from("κ"), // ν
            next_id: 0,
        }
    }

    fn fresh_ty(&mut self, env: &Env, ty: &explicit::Type) -> Type {
        let id = self.next_id;
        self.next_id += 1;

        let base = match *ty {
            explicit::Type::TInt => Base::Int,
            explicit::Type::TBool => Base::Bool,
            _ => panic!("FIXME: handle {:?}", ty),
        };
        let k = Liquid::K((id, self.env_id.clone()), box LinkedList::new());
        T::Ref(env.in_scope(), base, box k)
    }

    fn fresh(&mut self, env: &Env, expr: &Expr) -> Type {
        if let &Expr::Fun(ref id, ref e) = expr {
            let t1 = &self.shape.get(id).unwrap().clone();
            let fx = self.fresh_ty(env, t1);
            let f = self.fresh(env, e);
            T::Fun(id.clone(), box fx, box f)
        } else {
            let ty = hm_shape(&env.shape, expr);
            self.fresh_ty(env, &ty)
        }
    }
}

fn ty<'a>(k_env: &mut KEnv, env: &Env, c: &common::Const) -> Type {
    use common::Op2;
    use common::Const::*;
    use self::Liquid::E;

    let base = match *c {
        Int(_) => Base::Int,
        Bool(_) => Base::Bool,
    };

    println!("ty({:?})", base);

    // {ν : int | ν = 3 }
    let eq = E(Expr::Op2(Op2::Eq, box Expr::V, box Expr::Const(*c)));
    T::Ref(HashSet::new(), base, box eq)
}

fn base(ty: &Type) -> Option<Base> {
    match *ty {
        T::Ref(_, b, _) => Some(b),
        _ => None,
    }
}

pub fn cons<'a>(k_env: &mut KEnv, env: &Env, expr: &Expr) -> (Type, LinkedList<Constraint>) {
    use implicit::Expr::*;
    use common::Op2::Eq;

    match expr {
        &Var(ref id) => {
            let ty: Type = if let Some(b) = base(&env.get(id)) {
                let eq = Op2(Eq, box V, box Var(id.clone()));
                T::Ref(env.in_scope(), b, box Liquid::E(eq))
            } else {
                env.get(id)
            };
            (ty, LinkedList::new())
        }
        &Const(ref c) => {
            (ty(k_env, &env, c), LinkedList::new())
        }
        &If(ref e1, ref e2, ref e3) => {
            let mut env_t = env.clone();
            let mut env_f = env.clone();
            env_t.add_constraint(&e1.clone());
            env_f.add_constraint(&App(box Var(String::from("not")), e1.clone()));

            // FIXME: need to pass up type of expr?
            let f = k_env.fresh(&env, expr);
            // type of e1 has already been verified to be a bool by HM
            let (_, mut c1) = cons(k_env, &env, e1);
            // add e1 to path constraints in env_t
            // add not e1 to path constraints in env_f
            let (f2, mut c2) = cons(k_env, &env_t, e2);
            let (f3, mut c3) = cons(k_env, &env_f, e3);
            c1.append(&mut c2);
            c1.append(&mut c3);
            // Γ ⊢ (f)
            c1.push_back((env.snapshot(), C::WellFormed(box f.clone())));

            // TODO: add constraints:
            // Γ,e1 ⊢ (f2 <: f)
            // Γ,¬e1 ⊢ (f3 <: f)

            (f2, c1)
        }
        &Fun(ref x, ref e) => {
            let mut env = env.clone();
            let fx = k_env.fresh(&env, &Var(x.clone()));
            env.insert(x, &fx);
            // FIXME: Trump-esque WRONG.
            let f = k_env.fresh(&env, e);
            let ffn = T::Fun(x.clone(), box fx.clone(), box f.clone());
            let (fe, mut c) = cons(k_env, &env, e);
            // Γ ⊢ (x:fx → f)
            c.push_back((env.snapshot(), C::WellFormed(box f.clone())));

            // TODO: add constraints:
            // Γ,x:fx ⊢ (fe <: f)

            (ffn, c)
        }
        _ => {
            (T::Ref(env.in_scope(), Base::Bool, box Liquid::E(Const(common::Const::Bool(true)))), LinkedList::new())
        }
    }
}

pub fn infer(expr: &Expr, env: &HashMap<Id, explicit::Type>) -> Result<Expr> {
    let mut k_env = KEnv::new(env);
    let (f, c) = cons(&mut k_env, &Env::new(env), expr);
    println!("CONS:\n");
    println!("f\t{:?}", f);
    println!("c\t{:?}", c);

    // TODO:
    // let a = Solve(Split(c), λκ.Inst(Γ, e, Q)) in
    // a(f)

    Ok(Expr::Const(common::Const::Bool(false)))
}
<commit_msg>lots of constraint generation work<commit_after>use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::LinkedList;

use common;
use implicit;
use explicit;
use typed;

use implicit::Expr;
use common::{Id, Result};
use refined::{Base, T};
pub use explicit::Metavar;

// Qbc (bounds checking)
// X: 0, *, len *
// ν < X
// ν <= X
// ν = X
// ν >= X
// ν > X

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum C {
    WellFormed(Box<Type>),
    Subtype(Box<Type>, Box<Type>),
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Liquid {
    E(implicit::Expr),
    K(Metavar, Box<LinkedList<Expr>>), // list of pending substitutions
}

pub type Type = T<Liquid>;

pub type Constraint = ((HashSet<Id>, LinkedList<Expr>), C); // Boolean valued expressions & their environments

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Env {
    shape: HashMap<Id, explicit::Type>,
    refined_env: HashMap<Id, Type>,
    path_constraints: LinkedList<Expr>,
}

impl Env {
    fn new(shape: &HashMap<Id, explicit::Type>) -> Env {
        Env {
            shape: shape.clone(),
            refined_env: HashMap::new(),
            path_constraints: LinkedList::new(),
        }
    }

    fn get(&self, s: &Id) -> Type {
        match self.refined_env.get(s) {
            Some(ty) => ty.clone(),
            None => panic!("env.get('{}' missing ({:?})", s, self.in_scope()),
        }
    }

    fn insert(&mut self, s: &Id, ty: &Type) {
        self.refined_env.insert(s.clone(), ty.clone());
    }

    fn add_constraint(&mut self, e: &Expr) {
        self.path_constraints.push_back(e.clone())
    }

    fn in_scope(&self) -> HashSet<Id> {
        let keys: HashSet<_> = self.refined_env.keys().cloned().collect();
        return keys
    }

    fn snapshot(&self) -> (HashSet<Id>, LinkedList<Expr>) {
        (self.in_scope(), self.path_constraints.clone())
    }
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct KEnv {
    shape: HashMap<Id, explicit::Type>,
    env_id: String,
    next_id: i32,
}

fn hm_shape(env: &HashMap<Id, explicit::Type>, expr: &Expr) -> explicit::Type {
    use implicit::Expr::*;
    use explicit::Type::*;

    match *expr {
        Var(ref id) => env.get(id).unwrap().clone(),
        Const(common::Const::Int(_)) => TInt,
        Const(common::Const::Bool(_)) => TBool,
        Op2(op, _, _) => explicit::opty(op),
        Fun(ref id, ref e) => TFun(box env.get(id).unwrap().clone(), box hm_shape(env, e)),
        App(ref e1, _) => {
            if let TFun(_, e2) = hm_shape(env, e1) {
                *e2
            } else {
                panic!("expected TFun, not {:?}", expr);
            }
        }
        If(_, ref e2, _) => hm_shape(env, e2),
        Let(ref id, ref e1, ref e2) => hm_shape(env, e2),
        Fix(ref id, ref e) => hm_shape(env, e),
        MkArray(ref sz, ref n) => TIntArray,
        GetArray(ref id, ref idx) => TInt,
        SetArray(ref id, ref idx, ref v) => TIntArray,
        Star => panic!("star found when it shouldn't be"),
        V => panic!("v found when it shouldn't be"),
    }
}

impl KEnv {
    fn new(shape: &HashMap<Id, explicit::Type>) -> KEnv {
        KEnv {
            shape: shape.clone(),
            env_id: String::from("κ"), // ν
            next_id: 0,
        }
    }

    fn fresh_ty(&mut self, env: &Env, ty: &explicit::Type) -> Type {
        let id = self.next_id;
        self.next_id += 1;

        let base = match *ty {
            explicit::Type::TInt => Base::Int,
            explicit::Type::TBool => Base::Bool,
            _ => panic!("FIXME: handle {:?}", ty),
        };
        let k = Liquid::K((id, self.env_id.clone()), box LinkedList::new());
        T::Ref(env.in_scope(), base, box k)
    }

    fn fresh(&mut self, env: &Env, expr: &Expr) -> Type {
        if let &Expr::Fun(ref id, ref e) = expr {
            let t1 = &self.shape.get(id).unwrap().clone();
            let fx = self.fresh_ty(env, t1);
            let f = self.fresh(env, e);
            T::Fun(id.clone(), box fx, box f)
        } else {
            let ty = hm_shape(&env.shape, expr);
            self.fresh_ty(env, &ty)
        }
    }
}

fn ty<'a>(k_env: &mut KEnv, env: &Env, c: &common::Const) -> Type {
    use common::Op2;
    use common::Const::*;
    use self::Liquid::E;

    let base = match *c {
        Int(_) => Base::Int,
        Bool(_) => Base::Bool,
    };

    println!("ty({:?})", base);

    // {ν : int | ν = 3 }
    let eq = E(Expr::Op2(Op2::Eq, box Expr::V, box Expr::Const(*c)));
    T::Ref(HashSet::new(), base, box eq)
}

fn base(ty: &Type) -> Option<Base> {
    match *ty {
        T::Ref(_, b, _) => Some(b),
        _ => None,
    }
}

pub fn cons<'a>(k_env: &mut KEnv, env: &Env, expr: &Expr) -> (Type, LinkedList<Constraint>) {
    use implicit::Expr::*;
    use common::Op2::Eq;

    match *expr {
        Var(ref id) => {
            let ty: Type = if let Some(b) = base(&env.get(id)) {
                let eq = Op2(Eq, box V, box Var(id.clone()));
                T::Ref(env.in_scope(), b, box Liquid::E(eq))
            } else {
                env.get(id)
            };
            (ty, LinkedList::new())
        }
        Const(ref c) => {
            (ty(k_env, &env, c), LinkedList::new())
        }
        Op2(op, ref e1, ref e2) => {
            let (f1, mut c1) = cons(k_env, env, e1);
            let (f2, mut c2) = cons(k_env, env, e2);
            c1.append(&mut c2);

            let ty = explicit::opty(op);
            let base = match ty {
                explicit::Type::TInt => Base::Int,
                explicit::Type::TBool => Base::Bool,
                _ => panic!("FIXME: handle {:?}", ty),
            };

            let eq = Op2(Eq, box V, box expr.clone());
            let f = T::Ref(env.in_scope(), base, box Liquid::E(eq));
            println!("!!f:\t{:?}", f);

            (f, c1)
        }
        If(ref e1, ref e2, ref e3) => {
            let mut env_t = env.clone();
            let mut env_f = env.clone();
            env_t.add_constraint(&e1.clone());
            env_f.add_constraint(&App(box Var(String::from("not")), e1.clone()));

            let f = k_env.fresh(&env, expr);
            // type of e1 has already been verified to be a bool by HM
            let (_, mut c1) = cons(k_env, &env, e1);
            let (f2, mut c2) = cons(k_env, &env_t, e2);
            let (f3, mut c3) = cons(k_env, &env_f, e3);
            c1.append(&mut c2);
            c1.append(&mut c3);
            // Γ ⊢ (f)
            c1.push_back((env.snapshot(), C::WellFormed(box f.clone())));
            // Γ,e1 ⊢ (f2 <: f)
            c1.push_back((env_t.snapshot(), C::Subtype(box f2.clone(), box f.clone())));
            // Γ,¬e1 ⊢ (f3 <: f)
            c1.push_back((env_f.snapshot(), C::Subtype(box f3.clone(), box f.clone())));

            (f, c1)
        }
        Fun(ref x, ref e) => {
            let mut env = env.clone();
            let fx = k_env.fresh(&env, &Var(x.clone()));
            env.insert(x, &fx);
            let f = k_env.fresh(&env, e);
            let (fe, mut c) = cons(k_env, &env, e);
            // Γ ⊢ (x:fx → f)
            c.push_back((env.snapshot(), C::WellFormed(box f.clone())));
            // Γ,x:fx ⊢ (fe <: f)
            c.push_back((env.snapshot(), C::Subtype(box fe.clone(), box f.clone())));

            (f, c)
        }
        Fix(ref x, ref e) => {
            // const w/ ∀α.(α→α)→α
            let mut env = env.clone();
            let fx = k_env.fresh(&env, e);
            env.insert(x, &fx);

            // FIXME
            cons(k_env, &env, e)
        }
        Let(ref id, ref e1, ref e2) => {
            let mut env = env.clone();

            let f = k_env.fresh(&env, expr);
            let (f1, mut c1) = cons(k_env, &env, e1);
            env.insert(id, &f1);
            let (f2, mut c2) = cons(k_env, &env, e2);
            c1.append(&mut c2);
            // Γ ⊢ (f)
            c1.push_back((env.snapshot(), C::WellFormed(box f.clone())));
            // Γ,x:f1 ⊢ (f2 <: f)
            c1.push_back((env.snapshot(), C::Subtype(box f2.clone(), box f.clone())));

            (f, c1)
        }
        _ => {
            println!("unhandled {:?}", expr);
            (T::Ref(env.in_scope(), Base::Bool, box Liquid::E(Const(common::Const::Bool(true)))), LinkedList::new())
        }
    }
}

pub fn infer(expr: &Expr, env: &HashMap<Id, explicit::Type>) -> Result<Expr> {
    let mut k_env = KEnv::new(env);
    let (f, c) = cons(&mut k_env, &Env::new(env), expr);
    println!("CONS:\n");
    println!("f\t{:?}", f);
    println!("c\t{:?}", c);

    // TODO:
    // let a = Solve(Split(c), λκ.Inst(Γ, e, Q)) in
    // a(f)

    Ok(Expr::Const(common::Const::Bool(false)))
}
<|endoftext|>