<commit_before>use super::bitmask::BitMask;
use super::EMPTY;
use core::mem;

#[cfg(target_arch = "x86")]
use core::arch::x86;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64 as x86;

pub type BitMaskWord = u16;
pub const BITMASK_STRIDE: usize = 1;
pub const BITMASK_MASK: BitMaskWord = 0xffff;

/// Abstraction over a group of control bytes which can be scanned in
/// parallel.
///
/// This implementation uses a 128-bit SSE value.
#[derive(Copy, Clone)]
pub struct Group(x86::__m128i);

// FIXME: https://github.com/rust-lang/rust-clippy/issues/3859
#[allow(clippy::use_self)]
impl Group {
    /// Number of bytes in the group.
    pub const WIDTH: usize = mem::size_of::<Self>();

    /// Returns a full group of empty bytes, suitable for use as the initial
    /// value for an empty hash table.
    ///
    /// This is guaranteed to be aligned to the group size.
    pub const fn static_empty() -> &'static [u8; Group::WIDTH] {
        #[repr(C)]
        struct AlignedBytes {
            _align: [Group; 0],
            bytes: [u8; Group::WIDTH],
        };
        const ALIGNED_BYTES: AlignedBytes = AlignedBytes {
            _align: [],
            bytes: [EMPTY; Group::WIDTH],
        };
        &ALIGNED_BYTES.bytes
    }

    /// Loads a group of bytes starting at the given address.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)] // unaligned load
    pub unsafe fn load(ptr: *const u8) -> Self {
        Group(x86::_mm_loadu_si128(ptr as *const _))
    }

    /// Loads a group of bytes starting at the given address, which must be
    /// aligned to `mem::align_of::<Group>()`.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)]
    pub unsafe fn load_aligned(ptr: *const u8) -> Self {
        // FIXME: use align_offset once it stabilizes
        debug_assert_eq!(ptr as usize & (mem::align_of::<Self>() - 1), 0);
        Group(x86::_mm_load_si128(ptr as *const _))
    }

    /// Stores the group of bytes to the given address, which must be
    /// aligned to `mem::align_of::<Group>()`.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)]
    pub unsafe fn store_aligned(self, ptr: *mut u8) {
        // FIXME: use align_offset once it stabilizes
        debug_assert_eq!(ptr as usize & (mem::align_of::<Self>() - 1), 0);
        x86::_mm_store_si128(ptr as *mut _, self.0);
    }

    /// Returns a `BitMask` indicating all bytes in the group which have
    /// the given value.
    #[inline]
    pub fn match_byte(self, byte: u8) -> BitMask {
        #[allow(
            clippy::cast_possible_wrap, // byte: u8 as i8
            // byte: i32 as u16
            //   note: _mm_movemask_epi8 returns a 16-bit mask in a i32, the
            //   upper 16-bits of the i32 are zeroed:
            clippy::cast_sign_loss,
            clippy::cast_possible_truncation
        )]
        unsafe {
            let cmp = x86::_mm_cmpeq_epi8(self.0, x86::_mm_set1_epi8(byte as i8));
            BitMask(x86::_mm_movemask_epi8(cmp) as u16)
        }
    }

    /// Returns a `BitMask` indicating all bytes in the group which are
    /// `EMPTY`.
    #[inline]
    pub fn match_empty(self) -> BitMask {
        self.match_byte(EMPTY)
    }

    /// Returns a `BitMask` indicating all bytes in the group which are
    /// `EMPTY` or `DELETED`.
    #[inline]
    pub fn match_empty_or_deleted(self) -> BitMask {
        #[allow(
            // byte: i32 as u16
            //   note: _mm_movemask_epi8 returns a 16-bit mask in a i32, the
            //   upper 16-bits of the i32 are zeroed:
            clippy::cast_sign_loss,
            clippy::cast_possible_truncation
        )]
        unsafe {
            // A byte is EMPTY or DELETED iff the high bit is set
            BitMask(x86::_mm_movemask_epi8(self.0) as u16)
        }
    }

    /// Returns a `BitMask` indicating all bytes in the group which are full.
    #[inline]
    pub fn match_full(&self) -> BitMask {
        self.match_empty_or_deleted().invert()
    }

    /// Performs the following transformation on all bytes in the group:
    /// - `EMPTY => EMPTY`
    /// - `DELETED => EMPTY`
    /// - `FULL => DELETED`
    #[inline]
    pub fn convert_special_to_empty_and_full_to_deleted(self) -> Self {
        // Map high_bit = 1 (EMPTY or DELETED) to 1111_1111
        // and high_bit = 0 (FULL) to 1000_0000
        //
        // Here's this logic expanded to concrete values:
        //   let special = 0 > byte = 1111_1111 (true) or 0000_0000 (false)
        //   1111_1111 | 1000_0000 = 1111_1111
        //   0000_0000 | 1000_0000 = 1000_0000
        #[allow(
            clippy::cast_possible_wrap, // byte: 0x80_u8 as i8
        )]
        unsafe {
            let zero = x86::_mm_setzero_si128();
            let special = x86::_mm_cmpgt_epi8(zero, self.0);
            Group(x86::_mm_or_si128(
                special,
                x86::_mm_set1_epi8(0x80_u8 as i8),
            ))
        }
    }
}
<commit_msg>Suppress spurious clippy error.<commit_after>use super::bitmask::BitMask;
use super::EMPTY;
use core::mem;

#[cfg(target_arch = "x86")]
use core::arch::x86;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64 as x86;

pub type BitMaskWord = u16;
pub const BITMASK_STRIDE: usize = 1;
pub const BITMASK_MASK: BitMaskWord = 0xffff;

/// Abstraction over a group of control bytes which can be scanned in
/// parallel.
///
/// This implementation uses a 128-bit SSE value.
#[derive(Copy, Clone)]
pub struct Group(x86::__m128i);

// FIXME: https://github.com/rust-lang/rust-clippy/issues/3859
#[allow(clippy::use_self)]
impl Group {
    /// Number of bytes in the group.
    pub const WIDTH: usize = mem::size_of::<Self>();

    /// Returns a full group of empty bytes, suitable for use as the initial
    /// value for an empty hash table.
    ///
    /// This is guaranteed to be aligned to the group size.
    #[allow(clippy::items_after_statements)]
    pub const fn static_empty() -> &'static [u8; Group::WIDTH] {
        #[repr(C)]
        struct AlignedBytes {
            _align: [Group; 0],
            bytes: [u8; Group::WIDTH],
        };
        const ALIGNED_BYTES: AlignedBytes = AlignedBytes {
            _align: [],
            bytes: [EMPTY; Group::WIDTH],
        };
        &ALIGNED_BYTES.bytes
    }

    /// Loads a group of bytes starting at the given address.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)] // unaligned load
    pub unsafe fn load(ptr: *const u8) -> Self {
        Group(x86::_mm_loadu_si128(ptr as *const _))
    }

    /// Loads a group of bytes starting at the given address, which must be
    /// aligned to `mem::align_of::<Group>()`.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)]
    pub unsafe fn load_aligned(ptr: *const u8) -> Self {
        // FIXME: use align_offset once it stabilizes
        debug_assert_eq!(ptr as usize & (mem::align_of::<Self>() - 1), 0);
        Group(x86::_mm_load_si128(ptr as *const _))
    }

    /// Stores the group of bytes to the given address, which must be
    /// aligned to `mem::align_of::<Group>()`.
    #[inline]
    #[allow(clippy::cast_ptr_alignment)]
    pub unsafe fn store_aligned(self, ptr: *mut u8) {
        // FIXME: use align_offset once it stabilizes
        debug_assert_eq!(ptr as usize & (mem::align_of::<Self>() - 1), 0);
        x86::_mm_store_si128(ptr as *mut _, self.0);
    }

    /// Returns a `BitMask` indicating all bytes in the group which have
    /// the given value.
    #[inline]
    pub fn match_byte(self, byte: u8) -> BitMask {
        #[allow(
            clippy::cast_possible_wrap, // byte: u8 as i8
            // byte: i32 as u16
            //   note: _mm_movemask_epi8 returns a 16-bit mask in a i32, the
            //   upper 16-bits of the i32 are zeroed:
            clippy::cast_sign_loss,
            clippy::cast_possible_truncation
        )]
        unsafe {
            let cmp = x86::_mm_cmpeq_epi8(self.0, x86::_mm_set1_epi8(byte as i8));
            BitMask(x86::_mm_movemask_epi8(cmp) as u16)
        }
    }

    /// Returns a `BitMask` indicating all bytes in the group which are
    /// `EMPTY`.
    #[inline]
    pub fn match_empty(self) -> BitMask {
        self.match_byte(EMPTY)
    }

    /// Returns a `BitMask` indicating all bytes in the group which are
    /// `EMPTY` or `DELETED`.
    #[inline]
    pub fn match_empty_or_deleted(self) -> BitMask {
        #[allow(
            // byte: i32 as u16
            //   note: _mm_movemask_epi8 returns a 16-bit mask in a i32, the
            //   upper 16-bits of the i32 are zeroed:
            clippy::cast_sign_loss,
            clippy::cast_possible_truncation
        )]
        unsafe {
            // A byte is EMPTY or DELETED iff the high bit is set
            BitMask(x86::_mm_movemask_epi8(self.0) as u16)
        }
    }

    /// Returns a `BitMask` indicating all bytes in the group which are full.
    #[inline]
    pub fn match_full(&self) -> BitMask {
        self.match_empty_or_deleted().invert()
    }

    /// Performs the following transformation on all bytes in the group:
    /// - `EMPTY => EMPTY`
    /// - `DELETED => EMPTY`
    /// - `FULL => DELETED`
    #[inline]
    pub fn convert_special_to_empty_and_full_to_deleted(self) -> Self {
        // Map high_bit = 1 (EMPTY or DELETED) to 1111_1111
        // and high_bit = 0 (FULL) to 1000_0000
        //
        // Here's this logic expanded to concrete values:
        //   let special = 0 > byte = 1111_1111 (true) or 0000_0000 (false)
        //   1111_1111 | 1000_0000 = 1111_1111
        //   0000_0000 | 1000_0000 = 1000_0000
        #[allow(
            clippy::cast_possible_wrap, // byte: 0x80_u8 as i8
        )]
        unsafe {
            let zero = x86::_mm_setzero_si128();
            let special = x86::_mm_cmpgt_epi8(zero, self.0);
            Group(x86::_mm_or_si128(
                special,
                x86::_mm_set1_epi8(0x80_u8 as i8),
            ))
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{self, Read};

use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::status::StatusCode;
use hyper::Url;
use libflate::gzip;
use serde::de::DeserializeOwned;
use serde_json;

/// A Response to a submitted `Request`.
pub struct Response {
    inner: Decoder,
}

pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response {
    info!("Response: '{}' for {}", res.status, res.url);
    Response {
        inner: Decoder::from_hyper_response(res, gzip),
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => {
                f.debug_struct("Response")
                    .field("url", &hyper_response.url)
                    .field("status", &hyper_response.status)
                    .field("headers", &hyper_response.headers)
                    .finish()
            }
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => {
                f.debug_struct("Response")
                    .field("url", &head.url)
                    .field("status", &head.status)
                    .field("headers", &head.headers)
                    .finish()
            }
        }
    }
}

impl Response {
    /// Get the final `Url` of this response.
    #[inline]
    pub fn url(&self) -> &Url {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.url,
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.url,
        }
    }

    /// Get the `StatusCode`.
    #[inline]
    pub fn status(&self) -> StatusCode {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => hyper_response.status,
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => head.status,
        }
    }

    /// Get the `Headers`.
    #[inline]
    pub fn headers(&self) -> &Headers {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.headers,
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.headers,
        }
    }

    /// Try and deserialize the response body as JSON using `serde`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # #[macro_use] extern crate serde_derive;
    /// #
    /// # use reqwest::Error;
    /// #
    /// #[derive(Deserialize)]
    /// struct Response {
    ///     origin: String,
    /// }
    ///
    /// # fn run() -> Result<(), Error> {
    /// let resp: Response = reqwest::get("http://127.0.0.1/user.json")?.json()?;
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() {
    /// #     if let Err(error) = run() {
    /// #         println!("Error: {:?}", error);
    /// #     }
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails whenever the response body is not in JSON format
    /// or it cannot be properly deserialized to target type `T`. For more
    /// details please see [`serde_json::from_reader`].
    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        serde_json::from_reader(self).map_err(::error::from)
    }
}

enum Decoder {
    /// A `PlainText` decoder just returns the response content as is.
    PlainText(::hyper::client::Response),
    /// A `Gzip` decoder will uncompress the gziped response content before returning it.
    Gzip {
        decoder: gzip::Decoder<Peeked>,
        head: Head,
    },
    /// An error occured reading the Gzip header, so return that error
    /// when the user tries to read on the `Response`.
    Errored {
        err: Option<io::Error>,
        head: Head,
    }
}

impl Decoder {
    /// Constructs a Decoder from a hyper request.
    ///
    /// A decoder is just a wrapper around the hyper request that knows
    /// how to decode the content body of the request.
    ///
    /// Uses the correct variant by inspecting the Content-Encoding header.
    fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self {
        if !check_gzip {
            return Decoder::PlainText(res);
        }
        let content_encoding_gzip: bool;
        let mut is_gzip = {
            content_encoding_gzip = res.headers
                .get::<ContentEncoding>()
                .map_or(false, |encs| encs.contains(&Encoding::Gzip));
            content_encoding_gzip ||
            res.headers
                .get::<TransferEncoding>()
                .map_or(false, |encs| encs.contains(&Encoding::Gzip))
        };
        if is_gzip {
            if let Some(content_length) = res.headers.get::<ContentLength>() {
                if content_length.0 == 0 {
                    warn!("GZipped response with content-length of 0");
                    is_gzip = false;
                }
            }
        }
        if content_encoding_gzip {
            res.headers.remove::<ContentEncoding>();
            res.headers.remove::<ContentLength>();
        }
        if is_gzip {
            new_gzip(res)
        } else {
            Decoder::PlainText(res)
        }
    }
}

fn new_gzip(mut res: ::hyper::client::Response) -> Decoder {
    // libflate does a read_exact([0; 2]), so its impossible to tell
    // if the stream was empty, or truly had an UnexpectedEof.
    // Therefore, we need to peek a byte to make check for EOF first.
    let mut peek = [0];
    match res.read(&mut peek) {
        Ok(0) => return Decoder::PlainText(res),
        Ok(n) => {
            debug_assert_eq!(n, 1);
        }
        Err(e) => return Decoder::Errored {
            err: Some(e),
            head: Head {
                headers: res.headers.clone(),
                status: res.status,
                url: res.url.clone(),
            }
        }
    }

    let head = Head {
        headers: res.headers.clone(),
        status: res.status,
        url: res.url.clone(),
    };

    let reader = Peeked {
        peeked: Some(peek[0]),
        inner: res,
    };
    match gzip::Decoder::new(reader) {
        Ok(gzip) => Decoder::Gzip {
            decoder: gzip,
            head: head,
        },
        Err(e) => Decoder::Errored {
            err: Some(e),
            head: head,
        }
    }
}

struct Head {
    headers: ::hyper::header::Headers,
    url: ::hyper::Url,
    status: ::hyper::status::StatusCode,
}

struct Peeked {
    peeked: Option<u8>,
    inner: ::hyper::client::Response,
}

impl Read for Peeked {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }
        if let Some(byte) = self.peeked.take() {
            buf[0] = byte;
            Ok(1)
        } else {
            self.inner.read(buf)
        }
    }
}

impl Read for Decoder {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Decoder::PlainText(ref mut hyper_response) => hyper_response.read(buf),
            Decoder::Gzip { ref mut decoder, .. } => decoder.read(buf),
            Decoder::Errored { ref mut err, .. } => {
                Err(err.take().unwrap_or_else(previously_errored))
            }
        }
    }
}

#[inline]
fn previously_errored() -> io::Error {
    io::Error::new(io::ErrorKind::Other, "permanently errored")
}

/// Read the body of the Response.
impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}
<commit_msg>Response doc improvements<commit_after>use std::fmt;
use std::io::{self, Read};

use hyper::header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::status::StatusCode;
use hyper::Url;
use libflate::gzip;
use serde::de::DeserializeOwned;
use serde_json;

/// A Response to a submitted `Request`.
pub struct Response {
    inner: Decoder,
}

pub fn new(res: ::hyper::client::Response, gzip: bool) -> Response {
    info!("Response: '{}' for {}", res.status, res.url);
    Response {
        inner: Decoder::from_hyper_response(res, gzip),
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => {
                f.debug_struct("Response")
                    .field("url", &hyper_response.url)
                    .field("status", &hyper_response.status)
                    .field("headers", &hyper_response.headers)
                    .finish()
            }
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => {
                f.debug_struct("Response")
                    .field("url", &head.url)
                    .field("status", &head.status)
                    .field("headers", &head.headers)
                    .finish()
            }
        }
    }
}

impl Response {
    /// Get the final `Url` of this `Response`.
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn url(&self) -> &Url {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.url,
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.url,
        }
    }

    /// Get the `StatusCode` of this `Response`.
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/get")?;
    /// if resp.status().is_success() {
    ///     println!("success!");
    /// } else if resp.status().is_server_error() {
    ///     println!("server error!");
    /// } else {
    ///     println!("Something else happened. Status: {:?}", resp.status());
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// ```rust
    /// use reqwest::Client;
    /// use reqwest::StatusCode;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new()?;
    /// let resp = client.post("http://httpbin.org/post")?
    ///             .body("possibly too large")
    ///             .send()?;
    /// match resp.status() {
    ///     StatusCode::Ok => println!("success!"),
    ///     StatusCode::PayloadTooLarge => {
    ///         println!("Request payload is too large!");
    ///     }
    ///     s => println!("Received response status: {:?}", s),
    /// };
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn status(&self) -> StatusCode {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => hyper_response.status,
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => head.status,
        }
    }

    /// Get the `Headers` of this `Response`.
    ///
    /// ```rust
    /// # use std::io::{Read, Write};
    /// # use reqwest::Client;
    /// # use reqwest::header::ContentLength;
    /// #
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new()?;
    /// let mut resp = client.head("http://httpbin.org/bytes/3000")?.send()?;
    /// if resp.status().is_success() {
    ///     let len = resp.headers().get::<ContentLength>()
    ///                 .map(|ct_len| **ct_len)
    ///                 .unwrap_or(0);
    ///     // limit 1mb response
    ///     if len <= 1_000_000 {
    ///         let mut buf = Vec::with_capacity(len as usize);
    ///         let mut resp = reqwest::get("http://httpbin.org/bytes/3000")?;
    ///         if resp.status().is_success() {
    ///             ::std::io::copy(&mut resp, &mut buf)?;
    ///         }
    ///     }
    /// }
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn headers(&self) -> &Headers {
        match self.inner {
            Decoder::PlainText(ref hyper_response) => &hyper_response.headers,
            Decoder::Gzip { ref head, .. } |
            Decoder::Errored { ref head, .. } => &head.headers,
        }
    }

    /// Try and deserialize the response body as JSON using `serde`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # #[macro_use] extern crate serde_derive;
    /// #
    /// # use reqwest::Error;
    /// #
    /// #[derive(Deserialize)]
    /// struct Response {
    ///     origin: String,
    /// }
    ///
    /// # fn run() -> Result<(), Error> {
    /// let resp: Response = reqwest::get("http://httpbin.org/ip")?.json()?;
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() { }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails whenever the response body is not in JSON format
    /// or it cannot be properly deserialized to target type `T`. For more
    /// details please see [`serde_json::from_reader`].
    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        serde_json::from_reader(self).map_err(::error::from)
    }
}

enum Decoder {
    /// A `PlainText` decoder just returns the response content as is.
    PlainText(::hyper::client::Response),
    /// A `Gzip` decoder will uncompress the gziped response content before returning it.
    Gzip {
        decoder: gzip::Decoder<Peeked>,
        head: Head,
    },
    /// An error occured reading the Gzip header, so return that error
    /// when the user tries to read on the `Response`.
    Errored {
        err: Option<io::Error>,
        head: Head,
    }
}

impl Decoder {
    /// Constructs a Decoder from a hyper request.
    ///
    /// A decoder is just a wrapper around the hyper request that knows
    /// how to decode the content body of the request.
    ///
    /// Uses the correct variant by inspecting the Content-Encoding header.
    fn from_hyper_response(mut res: ::hyper::client::Response, check_gzip: bool) -> Self {
        if !check_gzip {
            return Decoder::PlainText(res);
        }
        let content_encoding_gzip: bool;
        let mut is_gzip = {
            content_encoding_gzip = res.headers
                .get::<ContentEncoding>()
                .map_or(false, |encs| encs.contains(&Encoding::Gzip));
            content_encoding_gzip ||
            res.headers
                .get::<TransferEncoding>()
                .map_or(false, |encs| encs.contains(&Encoding::Gzip))
        };
        if is_gzip {
            if let Some(content_length) = res.headers.get::<ContentLength>() {
                if content_length.0 == 0 {
                    warn!("GZipped response with content-length of 0");
                    is_gzip = false;
                }
            }
        }
        if content_encoding_gzip {
            res.headers.remove::<ContentEncoding>();
            res.headers.remove::<ContentLength>();
        }
        if is_gzip {
            new_gzip(res)
        } else {
            Decoder::PlainText(res)
        }
    }
}

fn new_gzip(mut res: ::hyper::client::Response) -> Decoder {
    // libflate does a read_exact([0; 2]), so its impossible to tell
    // if the stream was empty, or truly had an UnexpectedEof.
    // Therefore, we need to peek a byte to make check for EOF first.
    let mut peek = [0];
    match res.read(&mut peek) {
        Ok(0) => return Decoder::PlainText(res),
        Ok(n) => {
            debug_assert_eq!(n, 1);
        }
        Err(e) => return Decoder::Errored {
            err: Some(e),
            head: Head {
                headers: res.headers.clone(),
                status: res.status,
                url: res.url.clone(),
            }
        }
    }

    let head = Head {
        headers: res.headers.clone(),
        status: res.status,
        url: res.url.clone(),
    };

    let reader = Peeked {
        peeked: Some(peek[0]),
        inner: res,
    };
    match gzip::Decoder::new(reader) {
        Ok(gzip) => Decoder::Gzip {
            decoder: gzip,
            head: head,
        },
        Err(e) => Decoder::Errored {
            err: Some(e),
            head: head,
        }
    }
}

struct Head {
    headers: ::hyper::header::Headers,
    url: ::hyper::Url,
    status: ::hyper::status::StatusCode,
}

struct Peeked {
    peeked: Option<u8>,
    inner: ::hyper::client::Response,
}

impl Read for Peeked {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }
        if let Some(byte) = self.peeked.take() {
            buf[0] = byte;
            Ok(1)
        } else {
            self.inner.read(buf)
        }
    }
}

impl Read for Decoder {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Decoder::PlainText(ref mut hyper_response) => hyper_response.read(buf),
            Decoder::Gzip { ref mut decoder, .. } => decoder.read(buf),
            Decoder::Errored { ref mut err, .. } => {
                Err(err.take().unwrap_or_else(previously_errored))
            }
        }
    }
}

#[inline]
fn previously_errored() -> io::Error {
    io::Error::new(io::ErrorKind::Other, "permanently errored")
}

/// Read the body of the Response.
impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
}
<|endoftext|>


<commit_before>use common::{Header, HTTPVersion, StatusCode};
use std::io::{IoResult, MemReader};
use std::io::fs::File;
use std::io::util;
use std::io::util::NullReader;
use chunks::ChunksEncoder;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
///  of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///      will assume that the data from the `Reader` has the specified encoding
///      and will just pass-through.
///  - `Content-Length`: If you define this header to `N`, only the first `N` bytes
///      of the `Reader` will be read. If the `Reader` reaches `EOF` before `N` bytes have
///      been read, `0`s will be sent. Also, this header may not be passed to the final
///      output.
///
#[experimental]
pub struct Response<R> {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<uint>,
}

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

/// Builds a Date: header with the current date.
// TODO:
fn build_date_header() -> Header {
    from_str("Date: unknown").unwrap()
}

fn write_message_header<W: Writer>(mut writer: W, http_version: &HTTPVersion,
                                   status_code: &StatusCode, headers: &[Header])
    -> IoResult<()>
{
    // writing status line
    try!(write!(writer, "HTTP/{} {} {}\r\n",
        http_version,
        status_code.as_uint(),
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(write!(writer, "{}: {}\r\n", header.field, header.value));
    }

    // separator between header and data
    try!(write!(writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<uint>)
    -> TransferEncoding
{
    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if
        *http_version >= HTTPVersion(1, 1) &&
        entity_length.as_ref().filtered(|val| **val < chunks_threshold).is_none()
    {
        Chunked
    } else {
        Identity
    }
}

impl<R: Reader> Response<R> {
    #[experimental]
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<uint>) -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::new(),
            data_length: data_length,
        };

        for h in headers.move_iter() {
            response.add_header(h)
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    #[experimental]
    pub fn add_header(&mut self, header: Header) {
        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match from_str::<uint>(header.value.as_slice()) {
                Some(val) => self.data_length = Some(val),
                None => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header)
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[unstable]
    pub fn with_header(mut self, header: Header) -> Response<R> {
        self.add_header(header);
        self
    }

    /// Returns the same request, but with a different status code.
    #[unstable]
    pub fn with_status_code(mut self, code: StatusCode) -> Response<R> {
        self.status_code = code;
        self
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    // TODO: status codes 1xx, 204 and 304 must not include a response body
    #[unstable]
    pub fn raw_print<W: Writer>(mut self, mut writer: W, http_version: HTTPVersion,
                                request_headers: &[Header]) -> IoResult<()>
    {
        let transfer_encoding = choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length);

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.unshift(build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.unshift(
                from_str("Server: tiny-http (Rust)").unwrap()
            );
        }

        // writing data
        match transfer_encoding {
            Chunked => {
                self.headers.push(
                    from_str("Transfer-Encoding: chunked").unwrap()
                );

                try!(write_message_header(writer.by_ref(), &http_version, &self.status_code, self.headers.as_slice()));

                let mut writer = ChunksEncoder::new(writer);
                try!(util::copy(&mut self.reader, &mut writer));
            },

            Identity => {
                use util::EqualReader;

                assert!(self.data_length.is_some());

                self.headers.push(
                    from_str(format!("Content-Length: {}", self.data_length.unwrap()).as_slice()).unwrap()
                );

                try!(write_message_header(writer.by_ref(), &http_version, &self.status_code, self.headers.as_slice()));

                assert!(self.data_length.is_some());
                let (mut equ_reader, _) = EqualReader::new(self.reader.by_ref(), self.data_length.unwrap());
                try!(util::copy(&mut equ_reader, &mut writer));
            }
        };

        Ok(())
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    #[experimental]
    pub fn from_file(mut file: File) -> Response<File> {
        let file_size = file.stat().ok().map(|v| v.size as uint);

        Response::new(
            StatusCode(200),
            Vec::new(),
            file,
            file_size
        )
    }
}

impl Response<MemReader> {
    #[experimental]
    pub fn from_data(data: Vec<u8>) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::new(),
            MemReader::new(data),
            Some(data_len)
        )
    }

    #[experimental]
    pub fn from_string(data: String) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec!(
                from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
            ),
            MemReader::new(data.into_bytes()),
            Some(data_len)
        )
    }
}

impl Response<NullReader> {
    /// Builds an empty `Response` with the given status code.
    #[experimental]
    pub fn new_empty(status_code: StatusCode) -> Response<NullReader> {
        Response::new(
            status_code,
            Vec::new(),
            NullReader,
            Some(0)
        )
    }
}
<commit_msg>Cleaner choose_transfer_encoding()<commit_after>use common::{Header, HTTPVersion, StatusCode};
use std::io::{IoResult, MemReader};
use std::io::fs::File;
use std::io::util;
use std::io::util::NullReader;
use chunks::ChunksEncoder;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
///  of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///      will assume that the data from the `Reader` has the specified encoding
///      and will just pass-through.
///  - `Content-Length`: If you define this header to `N`, only the first `N` bytes
///      of the `Reader` will be read. If the `Reader` reaches `EOF` before `N` bytes have
///      been read, `0`s will be sent. Also, this header may not be passed to the final
///      output.
///
#[experimental]
pub struct Response<R> {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<uint>,
}

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

/// Builds a Date: header with the current date.
// TODO:
fn build_date_header() -> Header {
    from_str("Date: unknown").unwrap()
}

fn write_message_header<W: Writer>(mut writer: W, http_version: &HTTPVersion,
                                   status_code: &StatusCode, headers: &[Header])
    -> IoResult<()>
{
    // writing status line
    try!(write!(writer, "HTTP/{} {} {}\r\n",
        http_version,
        status_code.as_uint(),
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(write!(writer, "{}: {}\r\n", header.field, header.value));
    }

    // separator between header and data
    try!(write!(writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<uint>)
    -> TransferEncoding
{
    // HTTP 1.0 doesn't support other encodings
    if http_version <= HTTPVersion(1, 0) {
        return Identity;
    }

    // TODO: parse the request's TE header

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().filtered(|val| **val < chunks_threshold).is_none() {
        return Chunked;
    }

    // Identity by default
    Identity
}

impl<R: Reader> Response<R> {
    #[experimental]
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<uint>) -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::new(),
            data_length: data_length,
        };

        for h in headers.move_iter() {
            response.add_header(h)
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    #[experimental]
    pub fn add_header(&mut self, header: Header) {
        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match from_str::<uint>(header.value.as_slice()) {
                Some(val) => self.data_length = Some(val),
                None => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header)
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[unstable]
    pub fn with_header(mut self, header: Header) -> Response<R> {
        self.add_header(header);
        self
    }

    /// Returns the same request, but with a different status code.
    #[unstable]
    pub fn with_status_code(mut self, code: StatusCode) -> Response<R> {
        self.status_code = code;
        self
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    // TODO: status codes 1xx, 204 and 304 must not include a response body
    #[unstable]
    pub fn raw_print<W: Writer>(mut self, mut writer: W, http_version: HTTPVersion,
                                request_headers: &[Header]) -> IoResult<()>
    {
        let transfer_encoding = choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length);

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.unshift(build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.unshift(
                from_str("Server: tiny-http (Rust)").unwrap()
            );
        }

        // writing data
        match transfer_encoding {
            Chunked => {
                self.headers.push(
                    from_str("Transfer-Encoding: chunked").unwrap()
                );

                try!(write_message_header(writer.by_ref(), &http_version, &self.status_code, self.headers.as_slice()));

                let mut writer = ChunksEncoder::new(writer);
                try!(util::copy(&mut self.reader, &mut writer));
            },

            Identity => {
                use util::EqualReader;

                assert!(self.data_length.is_some());

                self.headers.push(
                    from_str(format!("Content-Length: {}", self.data_length.unwrap()).as_slice()).unwrap()
                );

                try!(write_message_header(writer.by_ref(), &http_version, &self.status_code, self.headers.as_slice()));

                assert!(self.data_length.is_some());
                let (mut equ_reader, _) = EqualReader::new(self.reader.by_ref(), self.data_length.unwrap());
                try!(util::copy(&mut equ_reader, &mut writer));
            }
        };

        Ok(())
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    #[experimental]
    pub fn from_file(mut file: File) -> Response<File> {
        let file_size = file.stat().ok().map(|v| v.size as uint);

        Response::new(
            StatusCode(200),
            Vec::new(),
            file,
            file_size
        )
    }
}

impl Response<MemReader> {
    #[experimental]
    pub fn from_data(data: Vec<u8>) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::new(),
            MemReader::new(data),
            Some(data_len)
        )
    }

    #[experimental]
    pub fn from_string(data: String) -> Response<MemReader> {
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec!(
                from_str("Content-Type: text/plain; charset=UTF-8").unwrap()
            ),
            MemReader::new(data.into_bytes()),
            Some(data_len)
        )
    }
}

impl Response<NullReader> {
    /// Builds an empty `Response` with the given status code.
    #[experimental]
    pub fn new_empty(status_code: StatusCode) -> Response<NullReader> {
        Response::new(
            status_code,
            Vec::new(),
            NullReader,
            Some(0)
        )
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use sys::sdl as ll;
use event::EventPump;
use keyboard::KeyboardState;
use video::WindowBuilder;
use util::CStringExt;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
    NoMemError = ll::SDL_ENOMEM as isize,
    ReadError = ll::SDL_EFREAD as isize,
    WriteError = ll::SDL_EFWRITE as isize,
    SeekError = ll::SDL_EFSEEK as isize,
    UnsupportedError = ll::SDL_UNSUPPORTED as isize
}

pub type SdlResult<T> = Result<T, String>;

use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
/// Only one Sdl context can be alive at a time.
/// Set to false by default (not alive).
static IS_SDL_CONTEXT_ALIVE: AtomicBool = ATOMIC_BOOL_INIT;

/// The SDL context type. Initialize with `sdl2::init()`.
///
/// From a thread-safety perspective, `Sdl` represents the main thread.
/// Only one instance of `Sdl` is allowed per process, and cannot be moved or
/// used across non-main threads.
///
/// As such, `Sdl` is a useful type for ensuring that SDL types that can only
/// be used on the main thread are initialized that way.
///
/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be
/// called on the main thread.
/// All functionality that calls `SDL_PumpEvents()` is thus put into an
/// `EventPump` type, which can only be obtained through `Sdl`.
/// This guarantees that the only way to call event-pumping functions is on
/// the main thread.
pub struct Sdl {
    _nosyncsend: PhantomData<*mut ()>
}

impl Sdl {
    /// Returns the mask of the specified subsystems which have previously been initialized.
    pub fn was_init(&self, flags: u32) -> u32 {
        unsafe {
            ll::SDL_WasInit(flags)
        }
    }

    /// Obtains the SDL event pump.
    pub fn event_pump(&mut self) -> EventPump {
        EventPump::new(self)
    }

    pub fn keyboard_state(&self) -> KeyboardState {
        KeyboardState::new(self)
    }

    /// Initializes a new `WindowBuilder`; a convenience method that calls `WindowBuilder::new()`.
    pub fn window(&self, title: &str, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder::new(self, title, width, height)
    }
}

impl Drop for Sdl {
    fn drop(&mut self) {
        use std::sync::atomic::Ordering;

        let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
        assert!(was_alive);

        unsafe { ll::SDL_Quit(); }
    }
}

/// A RAII value representing initalized SDL subsystems. See `sdl2::Sdl::init_subsystem()`.
///
/// Subsystem initialization is ref-counted. Once `Subsystem::drop()` is called,
/// the specified subsystems' ref-counts are decremented via `SDL_QuitSubSystem`.
pub struct Subsystem<'sdl> {
    flags: u32,
    _marker: PhantomData<&'sdl Sdl>
}

impl<'sdl> Drop for Subsystem<'sdl> {
    fn drop(&mut self) {
        unsafe { ll::SDL_QuitSubSystem(self.flags); }
    }
}

/// The type that allows you to build the SDL2 context.
pub struct InitBuilder {
    flags: u32
}

impl InitBuilder {
    /// Initializes a new `InitBuilder`.
    pub fn new() -> InitBuilder {
        InitBuilder { flags: 0 }
    }

    /// Builds the SDL2 context.
    pub fn build(&self) -> SdlResult<Sdl> {
        unsafe {
            use std::sync::atomic::Ordering;

            // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true
            let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);

            if was_alive {
                Err(format!("Cannot have more than one `Sdl` in use at the same time"))
            } else {
                if ll::SDL_Init(self.flags) == 0 {
                    Ok(Sdl {
                        _nosyncsend: PhantomData
                    })
                } else {
                    IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
                    Err(get_error())
                }
            }
        }
    }

    /// Builds the SDL2 context. Convenience method for `.build().unwrap()`.
    ///
    /// Panics if there was an error initializing SDL2.
    pub fn unwrap(&self) -> Sdl { self.build().unwrap() }

    /// Builds an SDL2 subsystem. Requires SDL2 to have already been initialized.
    pub fn build_subsystem(&self, _sdl: &Sdl) -> SdlResult<Subsystem> {
        unsafe {
            if ll::SDL_InitSubSystem(self.flags) == 0 {
                Ok(Subsystem {
                    flags: self.flags,
                    _marker: PhantomData
                })
            } else {
                Err(get_error())
            }
        }
    }

    /// Initializes every subsystem.
    pub fn everything(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_EVERYTHING as u32;
        self
    }

    /// Initializes the timer subsystem.
    pub fn timer(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_TIMER as u32;
        self
    }

    /// Initializes the audio subsystem.
    pub fn audio(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_AUDIO as u32;
        self
    }

    /// Initializes the video subsystem.
    pub fn video(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_VIDEO as u32;
        self
    }

    /// Initializes the joystick subsystem.
    pub fn joystick(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_JOYSTICK as u32;
        self
    }

    /// Initializes the haptic (force feedback) subsystem.
    pub fn haptic(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_HAPTIC as u32;
        self
    }

    /// Initializes the controller subsystem.
    pub fn game_controller(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_GAMECONTROLLER as u32;
        self
    }

    /// Initializes the events subsystem.
    pub fn events(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_EVENTS as u32;
        self
    }
}

/// Initializes the SDL library.
/// This must be called before using any other SDL function.
///
/// # Example
/// ```no_run
/// let mut sdl_context = sdl2::init().everything().unwrap();
///
/// for event in sdl_context.event_pump().poll_iter() {
///     // ...
/// }
///
/// // SDL_Quit() is called here as `sdl_context` is dropped.
/// ```
pub fn init() -> InitBuilder { InitBuilder::new() }

pub fn get_error() -> String {
    unsafe {
        let err = ll::SDL_GetError();
        String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()).to_string()
    }
}

pub fn set_error(err: &str) {
    let err = CString::new(err).remove_nul();
    unsafe { ll::SDL_SetError(err.as_ptr()); }
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode); }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}

pub fn get_ticks() -> u32 {
    unsafe { ll::SDL_GetTicks() as u32 }
}
<commit_msg>Fix #285: Remove redundant get_ticks from sdl.rs.<commit_after>use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use sys::sdl as ll;
use event::EventPump;
use keyboard::KeyboardState;
use video::WindowBuilder;
use util::CStringExt;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
    NoMemError = ll::SDL_ENOMEM as isize,
    ReadError = ll::SDL_EFREAD as isize,
    WriteError = ll::SDL_EFWRITE as isize,
    SeekError = ll::SDL_EFSEEK as isize,
    UnsupportedError = ll::SDL_UNSUPPORTED as isize
}

pub type SdlResult<T> = Result<T, String>;

use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
/// Only one Sdl context can be alive at a time.
/// Set to false by default (not alive).
static IS_SDL_CONTEXT_ALIVE: AtomicBool = ATOMIC_BOOL_INIT;

/// The SDL context type. Initialize with `sdl2::init()`.
///
/// From a thread-safety perspective, `Sdl` represents the main thread.
/// Only one instance of `Sdl` is allowed per process, and cannot be moved or
/// used across non-main threads.
///
/// As such, `Sdl` is a useful type for ensuring that SDL types that can only
/// be used on the main thread are initialized that way.
///
/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be
/// called on the main thread.
/// All functionality that calls `SDL_PumpEvents()` is thus put into an
/// `EventPump` type, which can only be obtained through `Sdl`.
/// This guarantees that the only way to call event-pumping functions is on
/// the main thread.
pub struct Sdl {
    _nosyncsend: PhantomData<*mut ()>
}

impl Sdl {
    /// Returns the mask of the specified subsystems which have previously been initialized.
    pub fn was_init(&self, flags: u32) -> u32 {
        unsafe {
            ll::SDL_WasInit(flags)
        }
    }

    /// Obtains the SDL event pump.
    pub fn event_pump(&mut self) -> EventPump {
        EventPump::new(self)
    }

    pub fn keyboard_state(&self) -> KeyboardState {
        KeyboardState::new(self)
    }

    /// Initializes a new `WindowBuilder`; a convenience method that calls `WindowBuilder::new()`.
    pub fn window(&self, title: &str, width: u32, height: u32) -> WindowBuilder {
        WindowBuilder::new(self, title, width, height)
    }
}

impl Drop for Sdl {
    fn drop(&mut self) {
        use std::sync::atomic::Ordering;

        let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
        assert!(was_alive);

        unsafe { ll::SDL_Quit(); }
    }
}

/// A RAII value representing initalized SDL subsystems. See `sdl2::Sdl::init_subsystem()`.
///
/// Subsystem initialization is ref-counted. Once `Subsystem::drop()` is called,
/// the specified subsystems' ref-counts are decremented via `SDL_QuitSubSystem`.
pub struct Subsystem<'sdl> {
    flags: u32,
    _marker: PhantomData<&'sdl Sdl>
}

impl<'sdl> Drop for Subsystem<'sdl> {
    fn drop(&mut self) {
        unsafe { ll::SDL_QuitSubSystem(self.flags); }
    }
}

/// The type that allows you to build the SDL2 context.
pub struct InitBuilder {
    flags: u32
}

impl InitBuilder {
    /// Initializes a new `InitBuilder`.
    pub fn new() -> InitBuilder {
        InitBuilder { flags: 0 }
    }

    /// Builds the SDL2 context.
    pub fn build(&self) -> SdlResult<Sdl> {
        unsafe {
            use std::sync::atomic::Ordering;

            // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true
            let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);

            if was_alive {
                Err(format!("Cannot have more than one `Sdl` in use at the same time"))
            } else {
                if ll::SDL_Init(self.flags) == 0 {
                    Ok(Sdl {
                        _nosyncsend: PhantomData
                    })
                } else {
                    IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
                    Err(get_error())
                }
            }
        }
    }

    /// Builds the SDL2 context. Convenience method for `.build().unwrap()`.
    ///
    /// Panics if there was an error initializing SDL2.
    pub fn unwrap(&self) -> Sdl { self.build().unwrap() }

    /// Builds an SDL2 subsystem. Requires SDL2 to have already been initialized.
    pub fn build_subsystem(&self, _sdl: &Sdl) -> SdlResult<Subsystem> {
        unsafe {
            if ll::SDL_InitSubSystem(self.flags) == 0 {
                Ok(Subsystem {
                    flags: self.flags,
                    _marker: PhantomData
                })
            } else {
                Err(get_error())
            }
        }
    }

    /// Initializes every subsystem.
    pub fn everything(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_EVERYTHING as u32;
        self
    }

    /// Initializes the timer subsystem.
    pub fn timer(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_TIMER as u32;
        self
    }

    /// Initializes the audio subsystem.
    pub fn audio(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_AUDIO as u32;
        self
    }

    /// Initializes the video subsystem.
    pub fn video(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_VIDEO as u32;
        self
    }

    /// Initializes the joystick subsystem.
    pub fn joystick(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_JOYSTICK as u32;
        self
    }

    /// Initializes the haptic (force feedback) subsystem.
    pub fn haptic(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_HAPTIC as u32;
        self
    }

    /// Initializes the controller subsystem.
    pub fn game_controller(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_GAMECONTROLLER as u32;
        self
    }

    /// Initializes the events subsystem.
    pub fn events(&mut self) -> &mut InitBuilder {
        self.flags |= ll::SDL_INIT_EVENTS as u32;
        self
    }
}

/// Initializes the SDL library.
/// This must be called before using any other SDL function.
///
/// # Example
/// ```no_run
/// let mut sdl_context = sdl2::init().everything().unwrap();
///
/// for event in sdl_context.event_pump().poll_iter() {
///     // ...
/// }
///
/// // SDL_Quit() is called here as `sdl_context` is dropped.
/// ```
pub fn init() -> InitBuilder { InitBuilder::new() }

pub fn get_error() -> String {
    unsafe {
        let err = ll::SDL_GetError();
        String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()).to_string()
    }
}

pub fn set_error(err: &str) {
    let err = CString::new(err).remove_nul();
    unsafe { ll::SDL_SetError(err.as_ptr()); }
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode); }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}
<|endoftext|>


<commit_before>
use std::os::self_exe_path;
use std::io::{BufferedWriter, BufferedReader};
use std::io::fs::File;
use serialize::{
    json,
    Encodable,
    Decodable,
};

/*
pub static ASSET_FOLDER: &'static str = "assets";

pub static WINDOW_SIZE: [u32, ..2] = [
    (BOARD_PADDING * 2.0 + BOARD_SIZE[0]) as u32,
    (BOARD_PADDING * 2.0 + BOARD_SIZE[1] + BOARD_OFFSET_Y) as u32,
];
pub static WINDOW_BACKGROUND_COLOR: [f32, ..4] = [250.0 / 255.0, 248.0 / 255.0, 239.0 / 255.0, 1.0];

pub static BOARD_PADDING: f64 = 12.0;
pub static BOARD_SIZE: [f64, ..2] = [
    TILE_SIZE * TILE_WIDTH as f64 + TILE_PADDING * (TILE_WIDTH + 1) as f64,
    TILE_SIZE * TILE_HEIGHT as f64 + TILE_PADDING * (TILE_HEIGHT + 1) as f64,
];
pub static BOARD_OFFSET_Y: f64 = 128.0;

pub static TILE_WIDTH: int = 4;
pub static TILE_HEIGHT: int = 4;
pub static TILE_SIZE: f64 = 72.0;
pub static TILE_PADDING: f64 = 16.0;
pub static TILE_BACKGROUND_COLOR: [f32, ..4] = [187.0 / 255.0, 173.0 / 255.0, 160.0 / 255.0, 1.0];
pub static TILES_COLOR: [[f32, ..4], ..10] = [
    // empty color
    [204.0 / 255.0, 192.0 / 255.0, 179.0 / 255.0, 1.0],
    // 2 color
    [238.0 / 255.0, 228.0 / 255.0, 218.0 / 255.0, 1.0],
    // 4 color
    [237.0 / 255.0, 224.0 / 255.0, 200.0 / 255.0, 1.0],
    // 8 color
    [242.0 / 255.0, 177.0 / 255.0, 121.0 / 255.0, 1.0],
    // 16 color
    [245.0 / 255.0, 149.0 / 255.0, 99.0 / 255.0, 1.0],
    // 32 color
    [246.0 / 255.0, 124.0 / 255.0, 95.0 / 255.0, 1.0],
    // 64 color
    [246.0 / 255.0, 94.0 / 255.0, 59.0 / 255.0, 1.0],
    // 128 color
    [237.0 / 255.0, 207.0 / 255.0, 114.0 / 255.0, 1.0],
    // 256 color
    [237.0 / 255.0, 204.0 / 255.0, 97.0 / 255.0, 1.0],
    // 512 color
    [237.0 / 255.0, 200.0 / 255.0, 80.0 / 255.0, 1.0],
];
pub static TILE_UNKNOW_COLOR: [f32, ..4] = [0.8, 0.0, 0.0, 1.0];
pub static TILE_MOVE_TIME: f64 = 0.1;
pub static TILE_NEW_TIME: f64 = 0.1;
pub static TILE_COMBINE_TIME: f64 = 0.1;

pub static BEST_RECT: [f64, ..4] = [284.0, BOARD_PADDING, 96.0, 48.0];
pub static SCORE_RECT: [f64, ..4] = [284.0 - 96.0 - BOARD_PADDING, BOARD_PADDING, 96.0, 48.0];

pub static LABEL_COLOR: [f32, ..4] = [187.0 / 255.0, 173.0 / 255.0, 160.0 / 255.0, 1.0];
pub static BUTTON_COLOR: [f32, ..4] = [142.0 / 255.0, 122.0 / 255.0, 102.0 / 255.0, 1.0];

pub static TEXT_DARK_COLOR: [f32, ..4] = [119.0 / 255.0, 110.0 / 255.0, 101.0 / 255.0, 1.0];
pub static TEXT_LIGHT_COLOR: [f32, ..4] = [249.0 / 255.0, 246.0 / 255.0, 242.0 / 255.0, 1.0];
*/

static SETTING_FILENAME: &'static str = "settings.json";

pub struct Settings {
    pub asset_folder: StrBuf,
    pub window_size: [u32, ..2],
    pub window_background_color: [f32, ..3],
    pub board_padding: f64,
    pub board_size: [f64, ..2],
    pub board_offset_y: f64,
    pub tile_width: int,
    pub tile_height: int,
    pub tile_size: f64,
    pub tile_padding: f64,
    pub tile_background_color: [f32, ..3],
    pub tiles_colors: Vec<[f32, ..3]>,
    pub tile_unknow_color: [f32, ..3],
    pub tile_move_time: f64,
    pub tile_new_time: f64,
    pub tile_combine_time: f64,
    pub best_rect: [f64, ..4],
    pub score_rect: [f64, ..4],
    pub label_color: [f32, ..3],
    pub button_color: [f32, ..3],
    pub text_dark_color: [f32, ..3],
    pub text_light_color: [f32, ..3],
}

impl Settings {
    pub fn load() -> Settings {
        Settings::from_settings_in_json(&SettingsInJson::load())
    }

    fn from_settings_in_json<'a>(s: &'a SettingsInJson) -> Settings {
        let board_size = [
            s.tile_size * s.tile_width as f64 + s.tile_padding * (s.tile_width + 1) as f64,
            s.tile_size * s.tile_height as f64 + s.tile_padding * (s.tile_height + 1) as f64,

        ];

        let mut tiles_colors = Vec::<[f32, ..3]>::new();
        for color in s.tiles_colors.iter() {
            tiles_colors.push([
                *color.get(0) / 255.0,
                *color.get(1) / 255.0,
                *color.get(2) / 255.0,
            ]);
        }
        Settings {
            asset_folder: s.asset_folder.clone(),
            window_size: [
                (s.board_padding * 2.0 + board_size[0]) as u32,
                (s.board_padding * 2.0 + board_size[1] + s.board_offset_y) as u32,
            ],
            window_background_color: [
                *s.window_background_color.get(0) / 255.0,
                *s.window_background_color.get(1) / 255.0,
                *s.window_background_color.get(2) / 255.0,
            ],
            board_padding: s.board_padding,
            board_size: board_size,
            board_offset_y: s.board_offset_y,
            tile_width: s.tile_width,
            tile_height: s.tile_height,
            tile_size: s.tile_size,
            tile_padding: s.tile_padding,
            tile_background_color: [
                *s.tile_background_color.get(0) / 255.0,
                *s.tile_background_color.get(1) / 255.0,
                *s.tile_background_color.get(2) / 255.0,
            ],
            tiles_colors: tiles_colors,
            tile_unknow_color: [
                *s.tile_unknow_color.get(0) / 255.0,
                *s.tile_unknow_color.get(1) / 255.0,
                *s.tile_unknow_color.get(2) / 255.0,
            ],
            tile_move_time: s.tile_move_time,
            tile_new_time: s.tile_new_time,
            tile_combine_time: s.tile_combine_time,
            best_rect: [
                *s.best_rect.get(0),
                *s.best_rect.get(1),
                *s.best_rect.get(2),
                *s.best_rect.get(3),
            ],
            score_rect: [
                *s.score_rect.get(0),
                *s.score_rect.get(1),
                *s.score_rect.get(2),
                *s.score_rect.get(3),
            ],
            label_color: [
                *s.label_color.get(0) / 255.0,
                *s.label_color.get(1) / 255.0,
                *s.label_color.get(2) / 255.0,
            ],
            button_color: [
                *s.button_color.get(0) / 255.0,
                *s.button_color.get(1) / 255.0,
                *s.button_color.get(2) / 255.0,
            ],
            text_dark_color: [
                *s.text_dark_color.get(0) / 255.0,
                *s.text_dark_color.get(1) / 255.0,
                *s.text_dark_color.get(2) / 255.0,
            ],
            text_light_color: [
                *s.text_light_color.get(0) / 255.0,
                *s.text_light_color.get(1) / 255.0,
                *s.text_light_color.get(2) / 255.0,
            ],
        }
    }
}

#[deriving(Encodable, Decodable)]
struct SettingsInJson {
    asset_folder: StrBuf,

    // r g b (0 - 255)
    window_background_color: Vec<f32>,

    board_padding: f64,
    board_offset_y: f64,

    tile_width: int,
    tile_height: int,
    tile_size: f64,
    tile_padding: f64,
    tile_background_color: Vec<f32>,
    tiles_colors: Vec<Vec<f32>>,
    tile_unknow_color: Vec<f32>,

    tile_move_time: f64,
    tile_new_time: f64,
    tile_combine_time: f64,

    best_rect: Vec<f64>,
    score_rect: Vec<f64>,

    label_color: Vec<f32>,
    button_color: Vec<f32>,
    text_dark_color: Vec<f32>,
    text_light_color: Vec<f32>,
}

impl SettingsInJson {
    pub fn default_settings() -> SettingsInJson {
        let mut tiles_colors = Vec::<Vec<f32>>::new();
        // empty color
        tiles_colors.push(vec![204.0, 192.0, 179.0]);
        // 2 color
        tiles_colors.push(vec![238.0, 228.0, 218.0]);
        // 4 color
        tiles_colors.push(vec![237.0, 224.0, 200.0]);
        // 8 color
        tiles_colors.push(vec![242.0, 177.0, 121.0]);
        // 16 color
        tiles_colors.push(vec![245.0, 149.0, 99.0]);
        // 32 color
        tiles_colors.push(vec![246.0, 124.0, 95.0]);
        // 64 color
        tiles_colors.push(vec![246.0, 94.0, 59.0]);
        // 128 color
        tiles_colors.push(vec![237.0, 207.0, 114.0]);
        // 256 color
        tiles_colors.push(vec![237.0, 204.0, 97.0]);
        // 512 color
        tiles_colors.push(vec![237.0, 200.0, 80.0]);
        SettingsInJson {
            asset_folder: "assets".to_strbuf(),
            window_background_color: vec![255.0, 248.0, 239.0],
            board_padding: 12.0,
            board_offset_y: 128.0,
            tile_width: 4,
            tile_height: 4,
            tile_size: 72.0,
            tile_padding: 16.0,
            tile_background_color: vec![187.0, 173.0, 160.0],
            tiles_colors: tiles_colors,
            tile_unknow_color: vec![200.0, 0.0, 0.0],
            tile_move_time: 0.1,
            tile_new_time: 0.1,
            tile_combine_time: 0.1,
            best_rect: vec![284.0, 12.0, 96.0, 48.0,],
            score_rect: vec![176.0, 12.0, 96.0, 48.0],
            label_color: vec![187.0, 173.0, 160.0],
            button_color: vec![142.0, 122.0, 102.0],
            text_dark_color: vec![119.0, 110.0, 101.0],
            text_light_color: vec![249.0, 246.0, 242.0],
        }
    }

    pub fn load() -> SettingsInJson {
        let exe_path = self_exe_path();
        if exe_path.is_none() {
            return SettingsInJson::default_settings();
        }
        let exe_path = exe_path.unwrap();
        let path = exe_path.join(Path::new(SETTING_FILENAME));
        if !path.exists() || !path.is_file() {
            let default = SettingsInJson::default_settings();
            default.save();
            return default;
        }
        let file = File::open(&path).unwrap();
        let mut reader = BufferedReader::new(file);
        let mut decoder = json::Decoder::new(json::from_reader(&mut reader).unwrap());
        Decodable::decode(&mut decoder).unwrap()
    }

    pub fn save(&self) {
        let exe_path = self_exe_path().unwrap();
        let path = exe_path.join(Path::new(SETTING_FILENAME));
        let file = File::create(&path).unwrap();
        let mut writer = BufferedWriter::new(file);
        let mut encoder = json::Encoder::new(&mut writer);
        match self.encode(&mut encoder) {
            Ok(()) => (),
            Err(e) => { println!("WARNING: Failed to save settings: {}", e); },
        }
    }
}

<commit_msg>Cleanup code<commit_after>
use std::os::self_exe_path;
use std::io::{BufferedWriter, BufferedReader};
use std::io::fs::File;
use serialize::{
    json,
    Encodable,
    Decodable,
};

static SETTING_FILENAME: &'static str = "settings.json";

pub struct Settings {
    pub asset_folder: StrBuf,
    pub window_size: [u32, ..2],
    pub window_background_color: [f32, ..3],
    pub board_padding: f64,
    pub board_size: [f64, ..2],
    pub board_offset_y: f64,
    pub tile_width: int,
    pub tile_height: int,
    pub tile_size: f64,
    pub tile_padding: f64,
    pub tile_background_color: [f32, ..3],
    pub tiles_colors: Vec<[f32, ..3]>,
    pub tile_unknow_color: [f32, ..3],
    pub tile_move_time: f64,
    pub tile_new_time: f64,
    pub tile_combine_time: f64,
    pub best_rect: [f64, ..4],
    pub score_rect: [f64, ..4],
    pub label_color: [f32, ..3],
    pub button_color: [f32, ..3],
    pub text_dark_color: [f32, ..3],
    pub text_light_color: [f32, ..3],
}

impl Settings {
    pub fn load() -> Settings {
        Settings::from_settings_in_json(&SettingsInJson::load())
    }

    fn from_settings_in_json<'a>(s: &'a SettingsInJson) -> Settings {
        let board_size = [
            s.tile_size * s.tile_width as f64 + s.tile_padding * (s.tile_width + 1) as f64,
            s.tile_size * s.tile_height as f64 + s.tile_padding * (s.tile_height + 1) as f64,

        ];

        let mut tiles_colors = Vec::<[f32, ..3]>::new();
        for color in s.tiles_colors.iter() {
            tiles_colors.push([
                *color.get(0) / 255.0,
                *color.get(1) / 255.0,
                *color.get(2) / 255.0,
            ]);
        }
        Settings {
            asset_folder: s.asset_folder.clone(),
            window_size: [
                (s.board_padding * 2.0 + board_size[0]) as u32,
                (s.board_padding * 2.0 + board_size[1] + s.board_offset_y) as u32,
            ],
            window_background_color: [
                *s.window_background_color.get(0) / 255.0,
                *s.window_background_color.get(1) / 255.0,
                *s.window_background_color.get(2) / 255.0,
            ],
            board_padding: s.board_padding,
            board_size: board_size,
            board_offset_y: s.board_offset_y,
            tile_width: s.tile_width,
            tile_height: s.tile_height,
            tile_size: s.tile_size,
            tile_padding: s.tile_padding,
            tile_background_color: [
                *s.tile_background_color.get(0) / 255.0,
                *s.tile_background_color.get(1) / 255.0,
                *s.tile_background_color.get(2) / 255.0,
            ],
            tiles_colors: tiles_colors,
            tile_unknow_color: [
                *s.tile_unknow_color.get(0) / 255.0,
                *s.tile_unknow_color.get(1) / 255.0,
                *s.tile_unknow_color.get(2) / 255.0,
            ],
            tile_move_time: s.tile_move_time,
            tile_new_time: s.tile_new_time,
            tile_combine_time: s.tile_combine_time,
            best_rect: [
                *s.best_rect.get(0),
                *s.best_rect.get(1),
                *s.best_rect.get(2),
                *s.best_rect.get(3),
            ],
            score_rect: [
                *s.score_rect.get(0),
                *s.score_rect.get(1),
                *s.score_rect.get(2),
                *s.score_rect.get(3),
            ],
            label_color: [
                *s.label_color.get(0) / 255.0,
                *s.label_color.get(1) / 255.0,
                *s.label_color.get(2) / 255.0,
            ],
            button_color: [
                *s.button_color.get(0) / 255.0,
                *s.button_color.get(1) / 255.0,
                *s.button_color.get(2) / 255.0,
            ],
            text_dark_color: [
                *s.text_dark_color.get(0) / 255.0,
                *s.text_dark_color.get(1) / 255.0,
                *s.text_dark_color.get(2) / 255.0,
            ],
            text_light_color: [
                *s.text_light_color.get(0) / 255.0,
                *s.text_light_color.get(1) / 255.0,
                *s.text_light_color.get(2) / 255.0,
            ],
        }
    }
}

#[deriving(Encodable, Decodable)]
struct SettingsInJson {
    asset_folder: StrBuf,

    // r g b (0 - 255)
    window_background_color: Vec<f32>,

    board_padding: f64,
    board_offset_y: f64,

    tile_width: int,
    tile_height: int,
    tile_size: f64,
    tile_padding: f64,
    tile_background_color: Vec<f32>,
    tiles_colors: Vec<Vec<f32>>,
    tile_unknow_color: Vec<f32>,

    tile_move_time: f64,
    tile_new_time: f64,
    tile_combine_time: f64,

    best_rect: Vec<f64>,
    score_rect: Vec<f64>,

    label_color: Vec<f32>,
    button_color: Vec<f32>,
    text_dark_color: Vec<f32>,
    text_light_color: Vec<f32>,
}

impl SettingsInJson {
    pub fn default_settings() -> SettingsInJson {
        let mut tiles_colors = Vec::<Vec<f32>>::new();
        // empty color
        tiles_colors.push(vec![204.0, 192.0, 179.0]);
        // 2 color
        tiles_colors.push(vec![238.0, 228.0, 218.0]);
        // 4 color
        tiles_colors.push(vec![237.0, 224.0, 200.0]);
        // 8 color
        tiles_colors.push(vec![242.0, 177.0, 121.0]);
        // 16 color
        tiles_colors.push(vec![245.0, 149.0, 99.0]);
        // 32 color
        tiles_colors.push(vec![246.0, 124.0, 95.0]);
        // 64 color
        tiles_colors.push(vec![246.0, 94.0, 59.0]);
        // 128 color
        tiles_colors.push(vec![237.0, 207.0, 114.0]);
        // 256 color
        tiles_colors.push(vec![237.0, 204.0, 97.0]);
        // 512 color
        tiles_colors.push(vec![237.0, 200.0, 80.0]);
        SettingsInJson {
            asset_folder: "assets".to_strbuf(),
            window_background_color: vec![255.0, 248.0, 239.0],
            board_padding: 12.0,
            board_offset_y: 128.0,
            tile_width: 4,
            tile_height: 4,
            tile_size: 72.0,
            tile_padding: 16.0,
            tile_background_color: vec![187.0, 173.0, 160.0],
            tiles_colors: tiles_colors,
            tile_unknow_color: vec![200.0, 0.0, 0.0],
            tile_move_time: 0.1,
            tile_new_time: 0.1,
            tile_combine_time: 0.1,
            best_rect: vec![284.0, 12.0, 96.0, 48.0,],
            score_rect: vec![176.0, 12.0, 96.0, 48.0],
            label_color: vec![187.0, 173.0, 160.0],
            button_color: vec![142.0, 122.0, 102.0],
            text_dark_color: vec![119.0, 110.0, 101.0],
            text_light_color: vec![249.0, 246.0, 242.0],
        }
    }

    pub fn load() -> SettingsInJson {
        let exe_path = self_exe_path();
        if exe_path.is_none() {
            return SettingsInJson::default_settings();
        }
        let exe_path = exe_path.unwrap();
        let path = exe_path.join(Path::new(SETTING_FILENAME));
        if !path.exists() || !path.is_file() {
            let default = SettingsInJson::default_settings();
            default.save();
            return default;
        }
        let file = File::open(&path).unwrap();
        let mut reader = BufferedReader::new(file);
        let mut decoder = json::Decoder::new(json::from_reader(&mut reader).unwrap());
        Decodable::decode(&mut decoder).unwrap()
    }

    pub fn save(&self) {
        let exe_path = self_exe_path().unwrap();
        if exe_path.is_none() {
            println!("WARNING: Failed to save settings: can't find exe path.");
            return;
        }
        let path = exe_path.join(Path::new(SETTING_FILENAME));
        let file = File::create(&path).unwrap();
        let mut writer = BufferedWriter::new(file);
        let mut encoder = json::Encoder::new(&mut writer);
        match self.encode(&mut encoder) {
            Ok(()) => (),
            Err(e) => { println!("WARNING: Failed to save settings: {}", e); },
        }
    }
}

<|endoftext|>


<commit_before>use syntax::{
    ast::{Expr, GenericArg},
    ast::{LetStmt, Type::InferType},
    AstNode, TextRange,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: replace_turbofish_with_explicit_type
//
// Converts `::<_>` to an explicit type assignment.
//
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a = make$0::<i32>();
// }
// ```
// ->
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a: i32 = make();
// }
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
    acc: &mut Assists,
    ctx: &AssistContext,
) -> Option<()> {
    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;

    let initializer = let_stmt.initializer()?;

    let generic_args = match &initializer {
        Expr::MethodCallExpr(ce) => {
            ce.generic_arg_list()?
        }
        Expr::CallExpr(ce) => {
            if let Expr::PathExpr(pe) = ce.expr()? {
                pe.path()?.segment()?.generic_arg_list()?
            } else {
                cov_mark::hit!(not_applicable_if_non_path_function_call);
                return None;
            }
        }
        _ => {
            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
            return None;
        }
    };

    // Find range of ::<_>
    let colon2 = generic_args.coloncolon_token()?;
    let r_angle = generic_args.r_angle_token()?;
    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());

    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();

    // Find type of ::<_>
    if turbofish_args.len() != 1 {
        cov_mark::hit!(not_applicable_if_not_single_arg);
        return None;
    }

    // An improvement would be to check that this is correctly part of the return value of the
    // function call, or sub in the actual return type.
    let turbofish_type = &turbofish_args[0];

    let initializer_start = initializer.syntax().text_range().start();
    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
        cov_mark::hit!(not_applicable_outside_turbofish);
        return None;
    }

    if let None = let_stmt.colon_token() {
        // If there's no colon in a let statement, then there is no explicit type.
        // let x = fn::<...>();
        let ident_range = let_stmt.pat()?.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace turbofish with explicit type",
            TextRange::new(initializer_start, turbofish_range.end()),
            |builder| {
                builder.insert(ident_range.end(), format!(": {}", turbofish_type));
                builder.delete(turbofish_range);
            },
        );
    } else if let Some(InferType(t)) = let_stmt.ty() {
        // If there's a type inferrence underscore, we can offer to replace it with the type in
        // the turbofish.
        // let x: _ = fn::<...>();
        let underscore_range = t.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace `_` with turbofish type",
            turbofish_range,
            |builder| {
                builder.replace(underscore_range, turbofish_type.to_string());
                builder.delete(turbofish_range);
            },
        );
    }

    None
}

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

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn replaces_turbofish_for_vec_string() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_method_calls() {
        // foo.make() is a method call which uses a different expr in the let initializer
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = foo.make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = foo.make();
}
"#,
        );
    }

    #[test]
    fn replace_turbofish_target() {
        check_assist_target(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = $0make::<Vec<String>>();
}
"#,
            r#"make::<Vec<String>>"#,
        );
    }

    #[test]
    fn not_applicable_outside_turbofish() {
        cov_mark::check!(not_applicable_outside_turbofish);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let $0a = make::<Vec<String>>();
}
"#,
        );
    }

    #[test]
    fn replace_inferred_type_placeholder() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: _ = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn not_applicable_constant_initializer() {
        cov_mark::check!(not_applicable_if_non_function_call_initializer);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = "foo"$0;
}
"#,
        );
    }

    #[test]
    fn not_applicable_non_path_function_call() {
        cov_mark::check!(not_applicable_if_non_path_function_call);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    $0let a = (|| {})();
}
"#,
        );
    }

    #[test]
    fn non_applicable_multiple_generic_args() {
        cov_mark::check!(not_applicable_if_not_single_arg);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>, i32>();
}
"#,
        );
    }
}
<commit_msg>fixup! rustfmt<commit_after>use syntax::{
    ast::{Expr, GenericArg},
    ast::{LetStmt, Type::InferType},
    AstNode, TextRange,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: replace_turbofish_with_explicit_type
//
// Converts `::<_>` to an explicit type assignment.
//
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a = make$0::<i32>();
// }
// ```
// ->
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a: i32 = make();
// }
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
    acc: &mut Assists,
    ctx: &AssistContext,
) -> Option<()> {
    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;

    let initializer = let_stmt.initializer()?;

    let generic_args = match &initializer {
        Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
        Expr::CallExpr(ce) => {
            if let Expr::PathExpr(pe) = ce.expr()? {
                pe.path()?.segment()?.generic_arg_list()?
            } else {
                cov_mark::hit!(not_applicable_if_non_path_function_call);
                return None;
            }
        }
        _ => {
            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
            return None;
        }
    };

    // Find range of ::<_>
    let colon2 = generic_args.coloncolon_token()?;
    let r_angle = generic_args.r_angle_token()?;
    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());

    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();

    // Find type of ::<_>
    if turbofish_args.len() != 1 {
        cov_mark::hit!(not_applicable_if_not_single_arg);
        return None;
    }

    // An improvement would be to check that this is correctly part of the return value of the
    // function call, or sub in the actual return type.
    let turbofish_type = &turbofish_args[0];

    let initializer_start = initializer.syntax().text_range().start();
    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
        cov_mark::hit!(not_applicable_outside_turbofish);
        return None;
    }

    if let None = let_stmt.colon_token() {
        // If there's no colon in a let statement, then there is no explicit type.
        // let x = fn::<...>();
        let ident_range = let_stmt.pat()?.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace turbofish with explicit type",
            TextRange::new(initializer_start, turbofish_range.end()),
            |builder| {
                builder.insert(ident_range.end(), format!(": {}", turbofish_type));
                builder.delete(turbofish_range);
            },
        );
    } else if let Some(InferType(t)) = let_stmt.ty() {
        // If there's a type inferrence underscore, we can offer to replace it with the type in
        // the turbofish.
        // let x: _ = fn::<...>();
        let underscore_range = t.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace `_` with turbofish type",
            turbofish_range,
            |builder| {
                builder.replace(underscore_range, turbofish_type.to_string());
                builder.delete(turbofish_range);
            },
        );
    }

    None
}

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

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn replaces_turbofish_for_vec_string() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_method_calls() {
        // foo.make() is a method call which uses a different expr in the let initializer
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = foo.make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = foo.make();
}
"#,
        );
    }

    #[test]
    fn replace_turbofish_target() {
        check_assist_target(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = $0make::<Vec<String>>();
}
"#,
            r#"make::<Vec<String>>"#,
        );
    }

    #[test]
    fn not_applicable_outside_turbofish() {
        cov_mark::check!(not_applicable_outside_turbofish);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let $0a = make::<Vec<String>>();
}
"#,
        );
    }

    #[test]
    fn replace_inferred_type_placeholder() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: _ = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn not_applicable_constant_initializer() {
        cov_mark::check!(not_applicable_if_non_function_call_initializer);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = "foo"$0;
}
"#,
        );
    }

    #[test]
    fn not_applicable_non_path_function_call() {
        cov_mark::check!(not_applicable_if_non_path_function_call);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    $0let a = (|| {})();
}
"#,
        );
    }

    #[test]
    fn non_applicable_multiple_generic_args() {
        cov_mark::check!(not_applicable_if_not_single_arg);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>, i32>();
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>use crate::io::AsyncRead;
use futures_core::future::Future;
use futures_core::task::{self, Poll};
use std::io;
use std::marker::Unpin;
use std::mem::PinMut;

/// A future which can be used to easily read available number of bytes to fill
/// a buffer.
///
/// Created by the [`read`] function.
#[derive(Debug)]
pub struct Read<'a, R: ?Sized + 'a> {
    reader: &'a mut R,
    buf: &'a mut [u8],
}

// Pinning is never projected to fields
impl<'a, R: ?Sized> Unpin for Read<'a, R> {}

impl<'a, R: AsyncRead + ?Sized> Read<'a, R> {
    pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Read<'a, R> {
        Read { reader, buf }
    }
}

impl<'a, R: AsyncRead + ?Sized> Future for Read<'a, R> {
    type Output = io::Result<usize>;

    fn poll(mut self: PinMut<Self>, cx: &mut task::Context) -> Poll<Self::Output> {
        let this = &mut *self;
        this.reader.poll_read(cx, this.buf)
    }
}
<commit_msg>Remove obsoleted comment<commit_after>use crate::io::AsyncRead;
use futures_core::future::Future;
use futures_core::task::{self, Poll};
use std::io;
use std::marker::Unpin;
use std::mem::PinMut;

/// A future which can be used to easily read available number of bytes to fill
/// a buffer.
#[derive(Debug)]
pub struct Read<'a, R: ?Sized + 'a> {
    reader: &'a mut R,
    buf: &'a mut [u8],
}

// Pinning is never projected to fields
impl<'a, R: ?Sized> Unpin for Read<'a, R> {}

impl<'a, R: AsyncRead + ?Sized> Read<'a, R> {
    pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Read<'a, R> {
        Read { reader, buf }
    }
}

impl<'a, R: AsyncRead + ?Sized> Future for Read<'a, R> {
    type Output = io::Result<usize>;

    fn poll(mut self: PinMut<Self>, cx: &mut task::Context) -> Poll<Self::Output> {
        let this = &mut *self;
        this.reader.poll_read(cx, this.buf)
    }
}
<|endoftext|>