<commit_before>//! A macro that parses & compiles fractran code at compile-time.

#![crate_type="dylib"]

#![feature(quote, plugin_registrar, macro_rules)]

extern crate num;
extern crate syntax;
extern crate rustc;

extern crate slow_primes;

use std::cmp;

use syntax::{ast, codemap, ptr};
use syntax::ext::base::{mod, ExtCtxt, MacResult, MacExpr, DummyResult};
use syntax::ext::build::AstBuilder;
use rustc::plugin::Registry;

use fract::Fract;

mod fract;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(registrar: &mut Registry) {
    registrar.register_macro("fractran", fractran);
}
fn fractran(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
    let exprs = match base::get_exprs_from_tts(cx, sp, tts)
                          .and_then(|e| extract_exprs(cx, e.as_slice())) {
        None => return DummyResult::expr(sp),
        Some(e) => e,
    };

    let factored = fract::factorise(exprs.as_slice());

    let (length, states) = construct_states(cx, sp, factored.as_slice());

    MacExpr::new(quote_expr!(cx, {
        mod inner {
            extern crate fractran_support;

            pub struct Machine {
                _regs: [u32, .. $length]
            }
            impl Iterator<()> for Machine {
                fn next(&mut self) -> Option<()> {
                    $states;
                    Some(())
                }
            }
            impl fractran_support::Fractran for Machine {
                fn state<'a>(&'a self) -> &'a [u32] {
                    self._regs.as_slice()
                }
            }

            pub fn construct(init: &[u32]) -> Machine {
                // FIXME
                assert!(init.len() <= $length);

                let mut prog = Machine { _regs: [0, .. $length] };
                for (place, data) in prog._regs.mut_iter().zip(init.iter()) {
                    *place = *data
                }
                prog
            }
        }
        inner::construct
    }))
}

fn extract_exprs(cx: &mut ExtCtxt, exprs: &[ptr::P<ast::Expr>]) -> Option<Vec<Fract<Vec<uint>>>> {
    exprs.iter().map(|e| extract_expr(cx, &**e)).collect()
}

fn extract_expr(cx: &mut ExtCtxt, expr: &ast::Expr) -> Option<Fract<Vec<uint>>> {
    match expr.node {
        ast::ExprLit(ref lit) => {
            match lit.node {
                ast::LitInt(x, _) => Some(Fract { numer: vec![x as uint], denom: vec![1] }),
                _ => {
                    cx.span_err(lit.span, "unsupported value in `fractran!`");
                    None
                }
            }
        }
        ast::ExprBinary(ast::BiAdd, ref l, ref r) => {
            extract_expr(cx, &**r).and_then(|r| {
                extract_expr(cx, &**l).map(|mut l| {
                    let top = l.numer.iter().chain(r.denom.iter()).fold(1, |p, &a| p * a)
                        + r.numer.iter().chain(l.denom.iter()).fold(1, |p, &a| p * a);
                    l.denom.push_all(r.denom.as_slice());
                    Fract {
                        numer: vec![top],
                        denom: l.denom
                    }
                })
            })
        }
        ast::ExprBinary(ast::BiMul, ref l, ref r) => {
            extract_expr(cx, &**r).and_then(|Fract { numer: r_n, denom: r_d }| {
                extract_expr(cx, &**l).map(|Fract { numer: mut l_n, denom: mut l_d }| {
                    l_n.push_all(r_n.as_slice());
                    l_d.push_all(r_d.as_slice());
                    Fract {
                        numer: l_n,
                        denom: l_d,
                    }
                })
            })
        }
        ast::ExprBinary(ast::BiDiv, ref l, ref r) => {
            extract_expr(cx, &**r).and_then(|Fract { numer: r_n, denom: r_d }| {
                extract_expr(cx, &**l).map(|Fract { numer: mut l_n, denom: mut l_d }| {
                    l_n.push_all(r_d.as_slice());
                    l_d.push_all(r_n.as_slice());
                    Fract {
                        numer: l_n,
                        denom: l_d,
                    }
                })
            })
        }
        // abusing ^
        ast::ExprBinary(ast::BiBitXor, ref l, ref r) => {
            extract_expr(cx, &**l).and_then(|Fract { numer, denom }| {
                extract_expr(cx, &**r).and_then(|Fract { numer: exp_n, denom: exp_d }| {
                    if !exp_d.iter().all(|n| *n == 1) {
                        cx.span_err(r.span, "exponent must be an integer");
                        return None;
                    }

                    let repeat = exp_n.iter().fold(1, |a, &n| a * n);
                    let mut ret_n = Vec::with_capacity(numer.len() * repeat);
                    let mut ret_d = Vec::with_capacity(numer.len() * repeat);
                    for _ in range(0u, repeat) {
                        ret_n.push_all(numer.as_slice());
                        ret_d.push_all(denom.as_slice());
                    }

                    Some(Fract { numer: ret_n, denom: ret_d })
                })
            })
        }
        ast::ExprParen(ref e) => extract_expr(cx, &**e),
        _ => {
            cx.span_err(expr.span,
                        "unsupported expression type inside `fractran!`");
            None
        }
    }
}

fn construct_states(cx: &ExtCtxt, sp: codemap::Span,
                    fracts: &[(Fract<u64>,
                               Fract<Vec<uint>>)]) -> (uint, ptr::P<ast::Expr>) {
    let length = fracts.iter().map(|&(_, ref f)| {
        cmp::max(f.numer.len(), f.denom.len())
    }).max().unwrap_or(0);

    let regs = quote_expr!(cx, self._regs);

    let st = State {
        cx: cx, sp: sp, regs: regs
    };

    let mut states = quote_expr!(cx, { return None });

    for &(_, ref f) in fracts.iter().rev() {
        let cond = st.check_regs(f.denom.as_slice());
        let exec = st.step_regs(f.numer.as_slice(), f.denom.as_slice());
        states = cx.expr(sp, ast::ExprIf(cond, exec, Some(states)));
    }

    (length, states)
}

struct State<'a, 'b: 'a> {
    cx: &'a ExtCtxt<'b>,
    sp: codemap::Span,
    regs: ptr::P<ast::Expr>,
}

impl<'a, 'b> State<'a, 'b> {
    fn check_reg(&self, reg: uint, thresh: uint) -> ptr::P<ast::Expr> {
        let regs = &self.regs;
        let thresh = thresh as u32;
        quote_expr!(&*self.cx, $regs[$reg] >= $thresh )
    }
    fn step_reg(&self, reg: uint, amt: uint) -> ptr::P<ast::Expr> {
        let regs = &self.regs;
        let amt = amt as u32;
        quote_expr!(&*self.cx, $regs[$reg] += $amt)
    }

    fn check_regs(&self, values: &[uint]) -> ptr::P<ast::Expr> {
        let mut res = self.cx.expr_bool(self.sp, true);

        for (reg, &v) in values.iter().enumerate() {
            if v > 0 {
                res = self.cx.expr_binary(self.sp, ast::BiAnd,
                                          self.check_reg(reg, v), res)
            }
        }
        res
    }
    #[allow(unsigned_negate)]
    fn step_regs(&self, increase: &[uint], decrease: &[uint]) -> ptr::P<ast::Block> {
        let mut stmts = vec![];
        for (reg, &v) in decrease.iter().enumerate() {
            if v > 0 {
                stmts.push(self.cx.stmt_expr(self.step_reg(reg, -v)))
            }
        }
        for (reg, &v) in increase.iter().enumerate() {
            if v > 0 {
                stmts.push(self.cx.stmt_expr(self.step_reg(reg, v)))
            }
        }
        self.cx.block(self.sp, stmts, None)
    }
}
<commit_msg>Update to 40b244973 2014-10-14 23:22:20 +0000.<commit_after>//! A macro that parses & compiles fractran code at compile-time.

#![crate_type="dylib"]

#![feature(quote, plugin_registrar, macro_rules)]

extern crate num;
extern crate syntax;
extern crate rustc;

extern crate slow_primes;

use std::cmp;

use syntax::{ast, codemap, ptr};
use syntax::ext::base::{mod, ExtCtxt, MacResult, MacExpr, DummyResult};
use syntax::ext::build::AstBuilder;
use rustc::plugin::Registry;

use fract::Fract;

mod fract;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(registrar: &mut Registry) {
    registrar.register_macro("fractran", fractran);
}
fn fractran(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
    let exprs = match base::get_exprs_from_tts(cx, sp, tts)
                          .and_then(|e| extract_exprs(cx, e.as_slice())) {
        None => return DummyResult::expr(sp),
        Some(e) => e,
    };

    let factored = fract::factorise(exprs.as_slice());

    let (length, states) = construct_states(cx, sp, factored.as_slice());

    MacExpr::new(quote_expr!(cx, {
        mod inner {
            extern crate fractran_support;

            pub struct Machine {
                _regs: [u32, .. $length]
            }
            impl Iterator<()> for Machine {
                fn next(&mut self) -> Option<()> {
                    $states;
                    Some(())
                }
            }
            impl fractran_support::Fractran for Machine {
                fn state<'a>(&'a self) -> &'a [u32] {
                    self._regs.as_slice()
                }
            }

            pub fn construct(init: &[u32]) -> Machine {
                // FIXME
                assert!(init.len() <= $length);

                let mut prog = Machine { _regs: [0, .. $length] };
                for (place, data) in prog._regs.iter_mut().zip(init.iter()) {
                    *place = *data
                }
                prog
            }
        }
        inner::construct
    }))
}

fn extract_exprs(cx: &mut ExtCtxt, exprs: &[ptr::P<ast::Expr>]) -> Option<Vec<Fract<Vec<uint>>>> {
    exprs.iter().map(|e| extract_expr(cx, &**e)).collect()
}

fn extract_expr(cx: &mut ExtCtxt, expr: &ast::Expr) -> Option<Fract<Vec<uint>>> {
    match expr.node {
        ast::ExprLit(ref lit) => {
            match lit.node {
                ast::LitInt(x, _) => Some(Fract { numer: vec![x as uint], denom: vec![1] }),
                _ => {
                    cx.span_err(lit.span, "unsupported value in `fractran!`");
                    None
                }
            }
        }
        ast::ExprBinary(ast::BiAdd, ref l, ref r) => {
            extract_expr(cx, &**r).and_then(|r| {
                extract_expr(cx, &**l).map(|mut l| {
                    let top = l.numer.iter().chain(r.denom.iter()).fold(1, |p, &a| p * a)
                        + r.numer.iter().chain(l.denom.iter()).fold(1, |p, &a| p * a);
                    l.denom.push_all(r.denom.as_slice());
                    Fract {
                        numer: vec![top],
                        denom: l.denom
                    }
                })
            })
        }
        ast::ExprBinary(ast::BiMul, ref l, ref r) => {
            extract_expr(cx, &**r).and_then(|Fract { numer: r_n, denom: r_d }| {
                extract_expr(cx, &**l).map(|Fract { numer: mut l_n, denom: mut l_d }| {
                    l_n.push_all(r_n.as_slice());
                    l_d.push_all(r_d.as_slice());
                    Fract {
                        numer: l_n,
                        denom: l_d,
                    }
                })
            })
        }
        ast::ExprBinary(ast::BiDiv, ref l, ref r) => {
            extract_expr(cx, &**r).and_then(|Fract { numer: r_n, denom: r_d }| {
                extract_expr(cx, &**l).map(|Fract { numer: mut l_n, denom: mut l_d }| {
                    l_n.push_all(r_d.as_slice());
                    l_d.push_all(r_n.as_slice());
                    Fract {
                        numer: l_n,
                        denom: l_d,
                    }
                })
            })
        }
        // abusing ^
        ast::ExprBinary(ast::BiBitXor, ref l, ref r) => {
            extract_expr(cx, &**l).and_then(|Fract { numer, denom }| {
                extract_expr(cx, &**r).and_then(|Fract { numer: exp_n, denom: exp_d }| {
                    if !exp_d.iter().all(|n| *n == 1) {
                        cx.span_err(r.span, "exponent must be an integer");
                        return None;
                    }

                    let repeat = exp_n.iter().fold(1, |a, &n| a * n);
                    let mut ret_n = Vec::with_capacity(numer.len() * repeat);
                    let mut ret_d = Vec::with_capacity(numer.len() * repeat);
                    for _ in range(0u, repeat) {
                        ret_n.push_all(numer.as_slice());
                        ret_d.push_all(denom.as_slice());
                    }

                    Some(Fract { numer: ret_n, denom: ret_d })
                })
            })
        }
        ast::ExprParen(ref e) => extract_expr(cx, &**e),
        _ => {
            cx.span_err(expr.span,
                        "unsupported expression type inside `fractran!`");
            None
        }
    }
}

fn construct_states(cx: &ExtCtxt, sp: codemap::Span,
                    fracts: &[(Fract<u64>,
                               Fract<Vec<uint>>)]) -> (uint, ptr::P<ast::Expr>) {
    let length = fracts.iter().map(|&(_, ref f)| {
        cmp::max(f.numer.len(), f.denom.len())
    }).max().unwrap_or(0);

    let regs = quote_expr!(cx, self._regs);

    let st = State {
        cx: cx, sp: sp, regs: regs
    };

    let mut states = quote_expr!(cx, { return None });

    for &(_, ref f) in fracts.iter().rev() {
        let cond = st.check_regs(f.denom.as_slice());
        let exec = st.step_regs(f.numer.as_slice(), f.denom.as_slice());
        states = cx.expr(sp, ast::ExprIf(cond, exec, Some(states)));
    }

    (length, states)
}

struct State<'a, 'b: 'a> {
    cx: &'a ExtCtxt<'b>,
    sp: codemap::Span,
    regs: ptr::P<ast::Expr>,
}

impl<'a, 'b> State<'a, 'b> {
    fn check_reg(&self, reg: uint, thresh: uint) -> ptr::P<ast::Expr> {
        let regs = &self.regs;
        let thresh = thresh as u32;
        quote_expr!(&*self.cx, $regs[$reg] >= $thresh )
    }
    fn step_reg(&self, reg: uint, amt: uint) -> ptr::P<ast::Expr> {
        let regs = &self.regs;
        let amt = amt as u32;
        quote_expr!(&*self.cx, $regs[$reg] += $amt)
    }

    fn check_regs(&self, values: &[uint]) -> ptr::P<ast::Expr> {
        let mut res = self.cx.expr_bool(self.sp, true);

        for (reg, &v) in values.iter().enumerate() {
            if v > 0 {
                res = self.cx.expr_binary(self.sp, ast::BiAnd,
                                          self.check_reg(reg, v), res)
            }
        }
        res
    }
    #[allow(unsigned_negate)]
    fn step_regs(&self, increase: &[uint], decrease: &[uint]) -> ptr::P<ast::Block> {
        let mut stmts = vec![];
        for (reg, &v) in decrease.iter().enumerate() {
            if v > 0 {
                stmts.push(self.cx.stmt_expr(self.step_reg(reg, -v)))
            }
        }
        for (reg, &v) in increase.iter().enumerate() {
            if v > 0 {
                stmts.push(self.cx.stmt_expr(self.step_reg(reg, v)))
            }
        }
        self.cx.block(self.sp, stmts, None)
    }
}
<|endoftext|>


<commit_before>//! # Plist
//!
//! A rusty plist parser.
//!
//! ## Usage
//!
//! Put this in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! plist = "0.0.11"
//! ```
//!
//! And put this in your crate root:
//!
//! ```rust
//! extern crate plist;
//! ```
//!
//! ## Examples
//!
//! ```rust ignore
//! use plist::Plist;
//! use std::fs::File;
//!
//! let file = File::open("tests/data/xml.plist").unwrap();
//! let plist = Plist::read(file).unwrap();
//!
//! match plist {
//!     Plist::Array(_array) => (),
//!     _ => ()
//! }
//!
//! ```
//!
//!

extern crate byteorder;
extern crate chrono;
extern crate rustc_serialize;
extern crate xml as xml_rs;

pub mod binary;
pub mod xml;
mod builder;

use chrono::{DateTime, UTC};
use chrono::format::ParseError as ChronoParseError;
use std::collections::BTreeMap;
use std::io::{Read, Seek, SeekFrom};
use std::io::Error as IoError;

#[derive(Clone, Debug, PartialEq)]
pub enum Plist {
    Array(Vec<Plist>),
    Dictionary(BTreeMap<String, Plist>),
    Boolean(bool),
    Data(Vec<u8>),
    Date(DateTime<UTC>),
    Real(f64),
    Integer(i64),
    String(String),
}

use rustc_serialize::base64::{STANDARD, ToBase64};
use rustc_serialize::json::Json as RustcJson;

impl Plist {
    pub fn read<R: Read + Seek>(reader: R) -> Result<Plist, ()> {
        let reader = EventReader::new(reader);
        Plist::from_events(reader)
    }

    pub fn from_events<T>(events: T) -> Result<Plist, ()>
        where T: IntoIterator<Item = ReadResult<PlistEvent>>
    {
        let iter = events.into_iter();
        let builder = builder::Builder::new(iter);

        match builder.build() {
            Ok(plist) => Ok(plist),
            Err(_) => Err(()),
        }
    }

    pub fn into_events(self) -> Vec<PlistEvent> {
        let mut events = Vec::new();
        self.into_events_inner(&mut events);
        events
    }

    fn into_events_inner(self, events: &mut Vec<PlistEvent>) {
        match self {
            Plist::Array(array) => {
                events.push(PlistEvent::StartArray(Some(array.len() as u64)));
                for value in array.into_iter() {
                    value.into_events_inner(events);
                }
                events.push(PlistEvent::EndArray);
            }
            Plist::Dictionary(dict) => {
                events.push(PlistEvent::StartDictionary(Some(dict.len() as u64)));
                for (key, value) in dict.into_iter() {
                    events.push(PlistEvent::StringValue(key));
                    value.into_events_inner(events);
                }
                events.push(PlistEvent::EndDictionary);
            }
            Plist::Boolean(value) => events.push(PlistEvent::BooleanValue(value)),
            Plist::Data(value) => events.push(PlistEvent::DataValue(value)),
            Plist::Date(value) => events.push(PlistEvent::DateValue(value)),
            Plist::Real(value) => events.push(PlistEvent::RealValue(value)),
            Plist::Integer(value) => events.push(PlistEvent::IntegerValue(value)),
            Plist::String(value) => events.push(PlistEvent::StringValue(value)),
        }
    }

    pub fn into_rustc_serialize_json(self) -> RustcJson {
        match self {
            Plist::Array(value) => {
                RustcJson::Array(value.into_iter().map(|p| p.into_rustc_serialize_json()).collect())
            }
            Plist::Dictionary(value) => {
                RustcJson::Object(value.into_iter()
                                       .map(|(k, v)| (k, v.into_rustc_serialize_json()))
                                       .collect())
            }
            Plist::Boolean(value) => RustcJson::Boolean(value),
            Plist::Data(value) => RustcJson::String(value.to_base64(STANDARD)),
            Plist::Date(value) => RustcJson::String(value.to_rfc3339()),
            Plist::Real(value) => RustcJson::F64(value),
            Plist::Integer(value) => RustcJson::I64(value),
            Plist::String(value) => RustcJson::String(value),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum PlistEvent {
    StartPlist,
    EndPlist,

    StartArray(Option<u64>),
    EndArray,

    StartDictionary(Option<u64>),
    EndDictionary,

    BooleanValue(bool),
    DataValue(Vec<u8>),
    DateValue(DateTime<UTC>),
    IntegerValue(i64),
    RealValue(f64),
    StringValue(String),
}

pub type ReadResult<T> = Result<T, ReadError>;

#[derive(Debug)]
pub enum ReadError {
    InvalidData,
    UnexpectedEof,
    UnsupportedType,
    Io(IoError),
}

impl From<IoError> for ReadError {
    fn from(io_error: IoError) -> ReadError {
        ReadError::Io(io_error)
    }
}

impl From<ChronoParseError> for ReadError {
    fn from(_: ChronoParseError) -> ReadError {
        ReadError::InvalidData
    }
}

pub struct EventReader<R: Read + Seek>(EventReaderInner<R>);

enum EventReaderInner<R: Read + Seek> {
    Uninitialized(Option<R>),
    Xml(xml::EventReader<R>),
    Binary(binary::EventReader<R>),
}

impl<R: Read + Seek> EventReader<R> {
    pub fn new(reader: R) -> EventReader<R> {
        EventReader(EventReaderInner::Uninitialized(Some(reader)))
    }

    fn is_binary(reader: &mut R) -> Result<bool, IoError> {
        try!(reader.seek(SeekFrom::Start(0)));
        let mut magic = [0; 8];
        try!(reader.read(&mut magic));
        try!(reader.seek(SeekFrom::Start(0)));

        Ok(if &magic == b"bplist00" {
            true
        } else {
            false
        })
    }
}

impl<R: Read + Seek> Iterator for EventReader<R> {
    type Item = ReadResult<PlistEvent>;

    fn next(&mut self) -> Option<ReadResult<PlistEvent>> {
        let mut reader = match self.0 {
            EventReaderInner::Xml(ref mut parser) => return parser.next(),
            EventReaderInner::Binary(ref mut parser) => return parser.next(),
            EventReaderInner::Uninitialized(ref mut reader) => reader.take().unwrap(),
        };

        let event_reader = match EventReader::is_binary(&mut reader) {
            Ok(true) => EventReaderInner::Binary(binary::EventReader::new(reader)),
            Ok(false) => EventReaderInner::Xml(xml::EventReader::new(reader)),
            Err(err) => {
                ::std::mem::replace(&mut self.0, EventReaderInner::Uninitialized(Some(reader)));
                return Some(Err(ReadError::Io(err)));
            }
        };

        ::std::mem::replace(&mut self.0, event_reader);

        self.next()
    }
}
<commit_msg>Add some information about PlistEvent<commit_after>//! # Plist
//!
//! A rusty plist parser.
//!
//! ## Usage
//!
//! Put this in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! plist = "0.0.11"
//! ```
//!
//! And put this in your crate root:
//!
//! ```rust
//! extern crate plist;
//! ```
//!
//! ## Examples
//!
//! ```rust ignore
//! use plist::Plist;
//! use std::fs::File;
//!
//! let file = File::open("tests/data/xml.plist").unwrap();
//! let plist = Plist::read(file).unwrap();
//!
//! match plist {
//!     Plist::Array(_array) => (),
//!     _ => ()
//! }
//!
//! ```
//!
//!

extern crate byteorder;
extern crate chrono;
extern crate rustc_serialize;
extern crate xml as xml_rs;

pub mod binary;
pub mod xml;
mod builder;

use chrono::{DateTime, UTC};
use chrono::format::ParseError as ChronoParseError;
use std::collections::BTreeMap;
use std::io::{Read, Seek, SeekFrom};
use std::io::Error as IoError;

#[derive(Clone, Debug, PartialEq)]
pub enum Plist {
    Array(Vec<Plist>),
    Dictionary(BTreeMap<String, Plist>),
    Boolean(bool),
    Data(Vec<u8>),
    Date(DateTime<UTC>),
    Real(f64),
    Integer(i64),
    String(String),
}

use rustc_serialize::base64::{STANDARD, ToBase64};
use rustc_serialize::json::Json as RustcJson;

impl Plist {
    pub fn read<R: Read + Seek>(reader: R) -> Result<Plist, ()> {
        let reader = EventReader::new(reader);
        Plist::from_events(reader)
    }

    pub fn from_events<T>(events: T) -> Result<Plist, ()>
        where T: IntoIterator<Item = ReadResult<PlistEvent>>
    {
        let iter = events.into_iter();
        let builder = builder::Builder::new(iter);

        match builder.build() {
            Ok(plist) => Ok(plist),
            Err(_) => Err(()),
        }
    }

    pub fn into_events(self) -> Vec<PlistEvent> {
        let mut events = Vec::new();
        self.into_events_inner(&mut events);
        events
    }

    fn into_events_inner(self, events: &mut Vec<PlistEvent>) {
        match self {
            Plist::Array(array) => {
                events.push(PlistEvent::StartArray(Some(array.len() as u64)));
                for value in array.into_iter() {
                    value.into_events_inner(events);
                }
                events.push(PlistEvent::EndArray);
            }
            Plist::Dictionary(dict) => {
                events.push(PlistEvent::StartDictionary(Some(dict.len() as u64)));
                for (key, value) in dict.into_iter() {
                    events.push(PlistEvent::StringValue(key));
                    value.into_events_inner(events);
                }
                events.push(PlistEvent::EndDictionary);
            }
            Plist::Boolean(value) => events.push(PlistEvent::BooleanValue(value)),
            Plist::Data(value) => events.push(PlistEvent::DataValue(value)),
            Plist::Date(value) => events.push(PlistEvent::DateValue(value)),
            Plist::Real(value) => events.push(PlistEvent::RealValue(value)),
            Plist::Integer(value) => events.push(PlistEvent::IntegerValue(value)),
            Plist::String(value) => events.push(PlistEvent::StringValue(value)),
        }
    }

    pub fn into_rustc_serialize_json(self) -> RustcJson {
        match self {
            Plist::Array(value) => {
                RustcJson::Array(value.into_iter().map(|p| p.into_rustc_serialize_json()).collect())
            }
            Plist::Dictionary(value) => {
                RustcJson::Object(value.into_iter()
                                       .map(|(k, v)| (k, v.into_rustc_serialize_json()))
                                       .collect())
            }
            Plist::Boolean(value) => RustcJson::Boolean(value),
            Plist::Data(value) => RustcJson::String(value.to_base64(STANDARD)),
            Plist::Date(value) => RustcJson::String(value.to_rfc3339()),
            Plist::Real(value) => RustcJson::F64(value),
            Plist::Integer(value) => RustcJson::I64(value),
            Plist::String(value) => RustcJson::String(value),
        }
    }
}

/// An encoding of a plist as a flat structure.
///
/// Output by the event readers.
///
/// Dictionary keys and values are represented as pairs of values e.g.:
///
/// ```ignore rust
/// StartDictionary
/// StringValue("Height") // Key
/// RealValue(181.2)      // Value
/// StringValue("Age")    // Key
/// IntegerValue(28)      // Value
/// EndDictionary
/// ```
#[derive(Clone, Debug, PartialEq)]
pub enum PlistEvent {
    StartPlist,
    EndPlist,

    StartArray(Option<u64>),
    EndArray,

    StartDictionary(Option<u64>),
    EndDictionary,

    BooleanValue(bool),
    DataValue(Vec<u8>),
    DateValue(DateTime<UTC>),
    IntegerValue(i64),
    RealValue(f64),
    StringValue(String),
}

pub type ReadResult<T> = Result<T, ReadError>;

#[derive(Debug)]
pub enum ReadError {
    InvalidData,
    UnexpectedEof,
    UnsupportedType,
    Io(IoError),
}

impl From<IoError> for ReadError {
    fn from(io_error: IoError) -> ReadError {
        ReadError::Io(io_error)
    }
}

impl From<ChronoParseError> for ReadError {
    fn from(_: ChronoParseError) -> ReadError {
        ReadError::InvalidData
    }
}

pub struct EventReader<R: Read + Seek>(EventReaderInner<R>);

enum EventReaderInner<R: Read + Seek> {
    Uninitialized(Option<R>),
    Xml(xml::EventReader<R>),
    Binary(binary::EventReader<R>),
}

impl<R: Read + Seek> EventReader<R> {
    pub fn new(reader: R) -> EventReader<R> {
        EventReader(EventReaderInner::Uninitialized(Some(reader)))
    }

    fn is_binary(reader: &mut R) -> Result<bool, IoError> {
        try!(reader.seek(SeekFrom::Start(0)));
        let mut magic = [0; 8];
        try!(reader.read(&mut magic));
        try!(reader.seek(SeekFrom::Start(0)));

        Ok(if &magic == b"bplist00" {
            true
        } else {
            false
        })
    }
}

impl<R: Read + Seek> Iterator for EventReader<R> {
    type Item = ReadResult<PlistEvent>;

    fn next(&mut self) -> Option<ReadResult<PlistEvent>> {
        let mut reader = match self.0 {
            EventReaderInner::Xml(ref mut parser) => return parser.next(),
            EventReaderInner::Binary(ref mut parser) => return parser.next(),
            EventReaderInner::Uninitialized(ref mut reader) => reader.take().unwrap(),
        };

        let event_reader = match EventReader::is_binary(&mut reader) {
            Ok(true) => EventReaderInner::Binary(binary::EventReader::new(reader)),
            Ok(false) => EventReaderInner::Xml(xml::EventReader::new(reader)),
            Err(err) => {
                ::std::mem::replace(&mut self.0, EventReaderInner::Uninitialized(Some(reader)));
                return Some(Err(ReadError::Io(err)));
            }
        };

        ::std::mem::replace(&mut self.0, event_reader);

        self.next()
    }
}
<|endoftext|>


<commit_before>//  Copyright (C) 2016 Sebastian Dröge <sebastian@centricular.com>
//                2016 Luis de Bethencourt <luisbg@osg.samsung.com>
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Library General Public License for more details.
//
//  You should have received a copy of the GNU Library General Public
//  License along with this library; if not, write to the
//  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//  Boston, MA 02110-1301, USA.

#![crate_type="cdylib"]

extern crate libc;
extern crate url;
extern crate hyper;

#[macro_use]
pub mod utils;
#[macro_use]
pub mod error;
pub mod rssource;
pub mod rssink;
pub mod rsfilesrc;
pub mod rshttpsrc;
pub mod rsfilesink;

use utils::*;
use rsfilesrc::FileSrc;
use rshttpsrc::HttpSrc;
use rsfilesink::FileSink;

use std::os::raw::c_void;
use libc::c_char;
use std::ffi::CString;

extern "C" {
    fn gst_rs_source_register(plugin: *const c_void,
                              name: *const c_char,
                              long_name: *const c_char,
                              description: *const c_char,
                              classification: *const c_char,
                              author: *const c_char,
                              rank: i32,
                              create_instance: *const c_void,
                              protocols: *const c_char,
                              push_only: GBoolean)
                              -> GBoolean;
}

extern "C" {
    fn gst_rs_sink_register(plugin: *const c_void,
                            name: *const c_char,
                            long_name: *const c_char,
                            description: *const c_char,
                            classification: *const c_char,
                            author: *const c_char,
                            rank: i32,
                            create_instance: *const c_void,
                            protocols: *const c_char)
                            -> GBoolean;
}

unsafe fn source_register(plugin: *const c_void,
                          name: &str,
                          long_name: &str,
                          description: &str,
                          classification: &str,
                          author: &str,
                          rank: i32,
                          create_instance: *const c_void,
                          protocols: &str,
                          push_only: bool) {
    let cname = CString::new(name).unwrap();
    let clong_name = CString::new(long_name).unwrap();
    let cdescription = CString::new(description).unwrap();
    let cclassification = CString::new(classification).unwrap();
    let cauthor = CString::new(author).unwrap();
    let cprotocols = CString::new(protocols).unwrap();

    gst_rs_source_register(plugin,
                           cname.as_ptr(),
                           clong_name.as_ptr(),
                           cdescription.as_ptr(),
                           cclassification.as_ptr(),
                           cauthor.as_ptr(),
                           rank,
                           create_instance,
                           cprotocols.as_ptr(),
                           GBoolean::from_bool(push_only));

}

#[no_mangle]
pub unsafe extern "C" fn sources_register(plugin: *const c_void) -> GBoolean {
    source_register(plugin,
                    "rsfilesrc",
                    "File Source",
                    "Reads local files",
                    "Source/File",
                    "Sebastian Dröge <sebastian@centricular.com>",
                    256 + 100,
                    FileSrc::new_boxed as *const c_void,
                    "file",
                    false);

    source_register(plugin,
                    "rshttpsrc",
                    "HTTP Source",
                    "Read HTTP/HTTPS files",
                    "Source/Network/HTTP",
                    "Sebastian Dröge <sebastian@centricular.com>",
                    256 + 100,
                    HttpSrc::new_boxed as *const c_void,
                    "http:https",
                    true);

    GBoolean::True
}

unsafe fn sink_register(plugin: *const c_void,
                        name: &str,
                        long_name: &str,
                        description: &str,
                        classification: &str,
                        author: &str,
                        rank: i32,
                        create_instance: *const c_void,
                        protocols: &str) {
    let cname = CString::new(name).unwrap();
    let clong_name = CString::new(long_name).unwrap();
    let cdescription = CString::new(description).unwrap();
    let cclassification = CString::new(classification).unwrap();
    let cauthor = CString::new(author).unwrap();
    let cprotocols = CString::new(protocols).unwrap();

    gst_rs_sink_register(plugin,
                         cname.as_ptr(),
                         clong_name.as_ptr(),
                         cdescription.as_ptr(),
                         cclassification.as_ptr(),
                         cauthor.as_ptr(),
                         rank,
                         create_instance,
                         cprotocols.as_ptr());
}

#[no_mangle]
pub unsafe extern "C" fn sinks_register(plugin: *const c_void) -> GBoolean {
    sink_register(plugin,
                  "rsfilesink",
                  "File Sink",
                  "Writes to local files",
                  "Sink/File",
                  "Luis de Bethencourt <luisbg@osg.samsung.com>",
                  256 + 100,
                  FileSink::new_boxed as *const c_void,
                  "file");

    GBoolean::True
}
<commit_msg>Move some extern C functions into the place where they're used<commit_after>//  Copyright (C) 2016 Sebastian Dröge <sebastian@centricular.com>
//                2016 Luis de Bethencourt <luisbg@osg.samsung.com>
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Library General Public License for more details.
//
//  You should have received a copy of the GNU Library General Public
//  License along with this library; if not, write to the
//  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//  Boston, MA 02110-1301, USA.

#![crate_type="cdylib"]

extern crate libc;
extern crate url;
extern crate hyper;

#[macro_use]
pub mod utils;
#[macro_use]
pub mod error;
pub mod rssource;
pub mod rssink;
pub mod rsfilesrc;
pub mod rshttpsrc;
pub mod rsfilesink;

use utils::*;
use rsfilesrc::FileSrc;
use rshttpsrc::HttpSrc;
use rsfilesink::FileSink;

use std::os::raw::c_void;
use libc::c_char;
use std::ffi::CString;

unsafe fn source_register(plugin: *const c_void,
                          name: &str,
                          long_name: &str,
                          description: &str,
                          classification: &str,
                          author: &str,
                          rank: i32,
                          create_instance: *const c_void,
                          protocols: &str,
                          push_only: bool) {

    extern "C" {
        fn gst_rs_source_register(plugin: *const c_void,
                                  name: *const c_char,
                                  long_name: *const c_char,
                                  description: *const c_char,
                                  classification: *const c_char,
                                  author: *const c_char,
                                  rank: i32,
                                  create_instance: *const c_void,
                                  protocols: *const c_char,
                                  push_only: GBoolean)
                                  -> GBoolean;
    }

    let cname = CString::new(name).unwrap();
    let clong_name = CString::new(long_name).unwrap();
    let cdescription = CString::new(description).unwrap();
    let cclassification = CString::new(classification).unwrap();
    let cauthor = CString::new(author).unwrap();
    let cprotocols = CString::new(protocols).unwrap();

    gst_rs_source_register(plugin,
                           cname.as_ptr(),
                           clong_name.as_ptr(),
                           cdescription.as_ptr(),
                           cclassification.as_ptr(),
                           cauthor.as_ptr(),
                           rank,
                           create_instance,
                           cprotocols.as_ptr(),
                           GBoolean::from_bool(push_only));

}

#[no_mangle]
pub unsafe extern "C" fn sources_register(plugin: *const c_void) -> GBoolean {
    source_register(plugin,
                    "rsfilesrc",
                    "File Source",
                    "Reads local files",
                    "Source/File",
                    "Sebastian Dröge <sebastian@centricular.com>",
                    256 + 100,
                    FileSrc::new_boxed as *const c_void,
                    "file",
                    false);

    source_register(plugin,
                    "rshttpsrc",
                    "HTTP Source",
                    "Read HTTP/HTTPS files",
                    "Source/Network/HTTP",
                    "Sebastian Dröge <sebastian@centricular.com>",
                    256 + 100,
                    HttpSrc::new_boxed as *const c_void,
                    "http:https",
                    true);

    GBoolean::True
}

unsafe fn sink_register(plugin: *const c_void,
                        name: &str,
                        long_name: &str,
                        description: &str,
                        classification: &str,
                        author: &str,
                        rank: i32,
                        create_instance: *const c_void,
                        protocols: &str) {
    extern "C" {
        fn gst_rs_sink_register(plugin: *const c_void,
                                name: *const c_char,
                                long_name: *const c_char,
                                description: *const c_char,
                                classification: *const c_char,
                                author: *const c_char,
                                rank: i32,
                                create_instance: *const c_void,
                                protocols: *const c_char)
                                -> GBoolean;
    }

    let cname = CString::new(name).unwrap();
    let clong_name = CString::new(long_name).unwrap();
    let cdescription = CString::new(description).unwrap();
    let cclassification = CString::new(classification).unwrap();
    let cauthor = CString::new(author).unwrap();
    let cprotocols = CString::new(protocols).unwrap();

    gst_rs_sink_register(plugin,
                         cname.as_ptr(),
                         clong_name.as_ptr(),
                         cdescription.as_ptr(),
                         cclassification.as_ptr(),
                         cauthor.as_ptr(),
                         rank,
                         create_instance,
                         cprotocols.as_ptr());
}

#[no_mangle]
pub unsafe extern "C" fn sinks_register(plugin: *const c_void) -> GBoolean {
    sink_register(plugin,
                  "rsfilesink",
                  "File Sink",
                  "Writes to local files",
                  "Sink/File",
                  "Luis de Bethencourt <luisbg@osg.samsung.com>",
                  256 + 100,
                  FileSink::new_boxed as *const c_void,
                  "file");

    GBoolean::True
}
<|endoftext|>


<commit_before>#![crate_name = "shader_version"]
#![deny(missing_doc)]

//! A helper library for detecting and picking compatible shaders.

/// Models knowledge about version range.
pub enum VersionRange<T> {
    /// The version range is working from a version,
    /// up to but not included another version.
    ///
    /// With other words, from first time it works until it breaks.
    VersionFromTo(T, T),
    /// The version range is working from a version,
    /// and possibly for all newer versions unless
    /// the context it is used implies otherwise.
    VersionFrom(T)
}

#[allow(non_camel_case_types)]
#[allow(missing_doc)]
pub enum OpenGL {
    OpenGL_2_0,
    OpenGL_2_1,
    OpenGL_3_0,
    OpenGL_3_1,
    OpenGL_3_2,
    OpenGL_3_3,
    OpenGL_4_0,
    OpenGL_4_1,
    OpenGL_4_2,
    OpenGL_4_3,
    OpenGL_4_4
}

/// For OpenGL version 3.3 and above,
/// the GLSL version is the same as the OpenGL version.
///
/// Source: http://www.opengl.org/wiki/Core_Language_%28GLSL%29
#[allow(missing_doc)]
#[allow(non_camel_case_types)]
pub enum GLSL {
    GLSL_1_10,
    GLSL_1_20,
    GLSL_1_30,
    GLSL_1_40,
    GLSL_1_50,
    GLSL_3_30,
    GLSL_4_00,
    GLSL_4_10,
    GLSL_4_20,
    GLSL_4_30,
    GLSL_4_40
}

impl OpenGL {
    /// Gets GLSL version associated with OpenGL.
    #[allow(non_snake_case_functions)]
    pub fn to_GLSL(&self) -> GLSL {
        match *self {
            OpenGL_2_0 => GLSL_1_10,
            OpenGL_2_1 => GLSL_1_20,
            OpenGL_3_0 => GLSL_1_30,
            OpenGL_3_1 => GLSL_1_40,
            OpenGL_3_2 => GLSL_1_50,
            OpenGL_3_3 => GLSL_3_30,
            OpenGL_4_0 => GLSL_4_00,
            OpenGL_4_1 => GLSL_4_10,
            OpenGL_4_2 => GLSL_4_20,
            OpenGL_4_3 => GLSL_4_30,
            OpenGL_4_4 => GLSL_4_40,
        }
    }
}

<commit_msg>Added `get_major_minor` method<commit_after>#![crate_name = "shader_version"]
#![deny(missing_doc)]

//! A helper library for detecting and picking compatible shaders.

/// Models knowledge about version range.
pub enum VersionRange<T> {
    /// The version range is working from a version,
    /// up to but not included another version.
    ///
    /// With other words, from first time it works until it breaks.
    VersionFromTo(T, T),
    /// The version range is working from a version,
    /// and possibly for all newer versions unless
    /// the context it is used implies otherwise.
    VersionFrom(T)
}

#[allow(non_camel_case_types)]
#[allow(missing_doc)]
pub enum OpenGL {
    OpenGL_2_0,
    OpenGL_2_1,
    OpenGL_3_0,
    OpenGL_3_1,
    OpenGL_3_2,
    OpenGL_3_3,
    OpenGL_4_0,
    OpenGL_4_1,
    OpenGL_4_2,
    OpenGL_4_3,
    OpenGL_4_4
}

/// For OpenGL version 3.3 and above,
/// the GLSL version is the same as the OpenGL version.
///
/// Source: http://www.opengl.org/wiki/Core_Language_%28GLSL%29
#[allow(missing_doc)]
#[allow(non_camel_case_types)]
pub enum GLSL {
    GLSL_1_10,
    GLSL_1_20,
    GLSL_1_30,
    GLSL_1_40,
    GLSL_1_50,
    GLSL_3_30,
    GLSL_4_00,
    GLSL_4_10,
    GLSL_4_20,
    GLSL_4_30,
    GLSL_4_40
}

impl OpenGL {
    /// Gets the minor version of OpenGL.
    pub fn get_major_minor(&self) -> (int, int) {
        match *self {
            OpenGL_2_0 => (2, 0),
            OpenGL_2_1 => (2, 1),
            OpenGL_3_0 => (3, 0),
            OpenGL_3_1 => (3, 1),
            OpenGL_3_2 => (3, 2),
            OpenGL_3_3 => (3, 3),
            OpenGL_4_0 => (4, 0),
            OpenGL_4_1 => (4, 1),
            OpenGL_4_2 => (4, 2),
            OpenGL_4_3 => (4, 3),
            OpenGL_4_4 => (4, 4)
        }
    }

    /// Gets GLSL version associated with OpenGL.
    #[allow(non_snake_case_functions)]
    pub fn to_GLSL(&self) -> GLSL {
        match *self {
            OpenGL_2_0 => GLSL_1_10,
            OpenGL_2_1 => GLSL_1_20,
            OpenGL_3_0 => GLSL_1_30,
            OpenGL_3_1 => GLSL_1_40,
            OpenGL_3_2 => GLSL_1_50,
            OpenGL_3_3 => GLSL_3_30,
            OpenGL_4_0 => GLSL_4_00,
            OpenGL_4_1 => GLSL_4_10,
            OpenGL_4_2 => GLSL_4_20,
            OpenGL_4_3 => GLSL_4_30,
            OpenGL_4_4 => GLSL_4_40,
        }
    }
}

<|endoftext|>


<commit_before>//! A generic connection pool.
//!
//! Opening a new database connection every time one is needed is both
//! inefficient and can lead to resource exhaustion under high traffic
//! conditions. A connection pool maintains a set of open connections to a
//! database, handing them out for repeated use.
//!
//! r2d2 is agnostic to the connection type it is managing. Implementors of the
//! `ManageConnection` trait provide the database-specific logic to create and
//! check the health of connections.
//!
//! # Example
//!
//! Using an imaginary "foodb" database.
//!
//! ```rust,ignore
//! use std::thread;
//!
//! extern crate r2d2;
//! extern crate r2d2_foodb;
//!
//! fn main() {
//!     let config = r2d2::Config::builder()
//!         .pool_size(15)
//!         .error_handler(Box::new(r2d2::LoggingErrorHandler))
//!         .build();
//!     let manager = r2d2_foodb::FooConnectionManager::new("localhost:1234");
//!
//!     let pool = r2d2::Pool::new(config, manager).unwrap();
//!
//!     for _ in 0..20 {
//!         let pool = pool.clone();
//!         thread::spawn(move || {
//!             let conn = pool.get().unwrap();
//!             // use the connection
//!             // it will be returned to the pool when it falls out of scope.
//!         })
//!     }
//! }
//! ```
#![warn(missing_docs)]
#![doc(html_root_url="https://sfackler.github.io/r2d2/doc/v0.6.2")]

#[macro_use]
extern crate log;
extern crate time;

use std::cmp;
use std::collections::VecDeque;
use std::error::Error;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::mem;
use std::sync::{Arc, Mutex, Condvar};
use time::{Duration, SteadyTime};

#[doc(inline)]
pub use config::Config;

use task::ScheduledThreadPool;

pub mod config;
mod task;
mod thunk;

#[cfg(test)]
mod test;

/// A trait which provides connection-specific functionality.
pub trait ManageConnection: Send + Sync + 'static {
    /// The connection type this manager deals with.
    type Connection: Send + 'static;

    /// The error type returned by `Connection`s.
    type Error: 'static;

    /// Attempts to create a new connection.
    fn connect(&self) -> Result<Self::Connection, Self::Error>;

    /// Determines if the connection is still connected to the database.
    ///
    /// A standard implementation would check if a simple query like `SELECT 1`
    /// succeeds.
    fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error>;

    /// *Quickly* determines if the connection is no longer usable.
    ///
    /// This will be called synchronously every time a connection is returned
    /// to the pool, so it should *not* block. If it returns `true`, the
    /// connection will be discarded.
    ///
    /// For example, an implementation might check if the underlying TCP socket
    /// has disconnected. Implementations that do not support this kind of
    /// fast health check may simply return `false`.
    fn has_broken(&self, conn: &mut Self::Connection) -> bool;
}

/// A trait which handles errors reported by the `ManageConnection`.
pub trait HandleError<E>: Send+Sync+'static {
    /// Handles an error.
    fn handle_error(&self, error: E);
}

/// A `HandleError` implementation which does nothing.
#[derive(Copy, Clone, Debug)]
pub struct NopErrorHandler;

impl<E> HandleError<E> for NopErrorHandler {
    fn handle_error(&self, _: E) {}
}

/// A `HandleError` implementation which logs at the error level.
#[derive(Copy, Clone, Debug)]
pub struct LoggingErrorHandler;

impl<E> HandleError<E> for LoggingErrorHandler where E: Error
{
    fn handle_error(&self, error: E) {
        error!("{}", error);
    }
}

/// A trait which allows for customization of connections.
pub trait CustomizeConnection<C, E>: Send+Sync+'static {
    /// Called with connections immediately after they are returned from
    /// `ManageConnection::connect`.
    ///
    /// The default implementation simply returns `Ok(())`.
    ///
    /// # Errors
    ///
    /// If this method returns an error, the connection will be discarded.
    #[allow(unused_variables)]
    fn on_acquire(&self, conn: &mut C) -> Result<(), E> {
        Ok(())
    }
}

/// A `CustomizeConnection` which does nothing.
#[derive(Copy, Clone, Debug)]
pub struct NopConnectionCustomizer;

impl<C, E> CustomizeConnection<C, E> for NopConnectionCustomizer {}

struct Conn<C> {
    conn: C,
    birth: SteadyTime,
    idle_start: SteadyTime,
}

impl<C> Conn<C> {
    fn new(conn: C) -> Conn<C> {
        let now = SteadyTime::now();
        Conn {
            conn: conn,
            birth: now,
            idle_start: now,
        }
    }
}

struct PoolInternals<C> {
    conns: VecDeque<Conn<C>>,
    num_conns: u32,
    pending_conns: u32,
}

struct SharedPool<M>
    where M: ManageConnection
{
    config: Config<M::Connection, M::Error>,
    manager: M,
    internals: Mutex<PoolInternals<M::Connection>>,
    cond: Condvar,
    thread_pool: ScheduledThreadPool,
}

impl<M> Drop for SharedPool<M> where M: ManageConnection
{
    fn drop(&mut self) {
        self.thread_pool.clear();
    }
}

fn drop_conn<M>(shared: &Arc<SharedPool<M>>, internals: &mut PoolInternals<M::Connection>)
    where M: ManageConnection
{
    internals.num_conns -= 1;

    let min = shared.config.min_idle().unwrap_or(shared.config.pool_size());
    if internals.num_conns + internals.pending_conns < min {
        add_connection(shared, internals);
    }
}

fn add_connection<M>(shared: &Arc<SharedPool<M>>, internals: &mut PoolInternals<M::Connection>)
    where M: ManageConnection
{
    internals.pending_conns += 1;
    inner(Duration::zero(), shared);

    fn inner<M>(delay: Duration, shared: &Arc<SharedPool<M>>)
        where M: ManageConnection
    {
        let new_shared = shared.clone();
        shared.thread_pool.run_after(delay, move || {
            let shared = new_shared;
            let conn = shared.manager.connect().and_then(|mut conn| {
                shared.config.connection_customizer().on_acquire(&mut conn).map(|_| conn)
            });
            match conn {
                Ok(conn) => {
                    let mut internals = shared.internals.lock().unwrap();
                    internals.conns.push_back(Conn::new(conn));
                    internals.pending_conns -= 1;
                    internals.num_conns += 1;
                    shared.cond.notify_one();
                }
                Err(err) => {
                    shared.config.error_handler().handle_error(err);
                    let delay = cmp::max(Duration::milliseconds(200), delay);
                    let delay = cmp::min(cvt(shared.config.connection_timeout()) / 2, delay * 2);
                    inner(delay, &shared);
                }
            }
        });
    }
}

fn reap_connections<M>(shared: &Arc<SharedPool<M>>)
    where M: ManageConnection
{
    let mut old = VecDeque::with_capacity(shared.config.pool_size() as usize);
    let mut to_drop = vec![];

    let mut internals = shared.internals.lock().unwrap();
    mem::swap(&mut old, &mut internals.conns);
    let now = SteadyTime::now();
    for conn in old {
        let mut reap = false;
        if let Some(timeout) = shared.config.idle_timeout() {
            reap |= now - conn.idle_start >= cvt(timeout);
        }
        if let Some(lifetime) = shared.config.max_lifetime() {
            reap |= now - conn.birth >= cvt(lifetime);
        }
        if reap {
            drop_conn(shared, &mut internals);
            to_drop.push(conn.conn);
        } else {
            internals.conns.push_back(conn);
        }
    }
    drop(internals); // make sure we run to_drop destructors without this locked
}

/// A generic connection pool.
pub struct Pool<M>
    where M: ManageConnection
{
    shared: Arc<SharedPool<M>>,
}

/// Returns a new `Pool` referencing the same state as `self`.
impl<M> Clone for Pool<M> where M: ManageConnection
{
    fn clone(&self) -> Pool<M> {
        Pool { shared: self.shared.clone() }
    }
}

impl<M> fmt::Debug for Pool<M> where M: ManageConnection + fmt::Debug
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let inner = self.shared.internals.lock().unwrap();

        fmt.debug_struct("Pool")
           .field("connections", &inner.num_conns)
           .field("idle_connections", &inner.conns.len())
           .field("config", &self.shared.config)
           .field("manager", &self.shared.manager)
           .finish()
    }
}

/// An error returned by `Pool::new` if it fails to initialize connections.
#[derive(Debug)]
pub struct InitializationError(());

impl fmt::Display for InitializationError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

impl Error for InitializationError {
    fn description(&self) -> &str {
        "Unable to initialize connections"
    }
}

/// An error returned by `Pool::get` if it times out without retrieving a connection.
#[derive(Debug)]
pub struct GetTimeout(());

impl fmt::Display for GetTimeout {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

impl Error for GetTimeout {
    fn description(&self) -> &str {
        "Timed out while waiting for a connection"
    }
}

impl<M> Pool<M> where M: ManageConnection
{
    /// Creates a new connection pool.
    ///
    /// Returns an `Err` value if `initialization_fail_fast` is set to true in
    /// the configuration and the pool is unable to open all of its
    /// connections.
    pub fn new(config: Config<M::Connection, M::Error>,
               manager: M)
               -> Result<Pool<M>, InitializationError> {
        Pool::new_inner(config, manager, 30)
    }

    // for testing
    fn new_inner(config: Config<M::Connection, M::Error>,
                 manager: M,
                 reaper_rate: i64)
                 -> Result<Pool<M>, InitializationError> {
        let internals = PoolInternals {
            conns: VecDeque::with_capacity(config.pool_size() as usize),
            num_conns: 0,
            pending_conns: 0,
        };

        let shared = Arc::new(SharedPool {
            thread_pool: ScheduledThreadPool::new(config.helper_threads() as usize),
            config: config,
            manager: manager,
            internals: Mutex::new(internals),
            cond: Condvar::new(),
        });

        {
            let mut inner = shared.internals.lock().unwrap();
            for _ in 0..shared.config.min_idle().unwrap_or(shared.config.pool_size()) {
                add_connection(&shared, &mut inner);
            }
            drop(inner);
        }

        if shared.config.initialization_fail_fast() {
            let end = SteadyTime::now() + cvt(shared.config.connection_timeout());
            let mut internals = shared.internals.lock().unwrap();

            while internals.num_conns != shared.config.pool_size() {
                let wait = end - SteadyTime::now();
                if wait <= Duration::zero() {
                    return Err(InitializationError(()));
                }
                internals = shared.cond
                                  .wait_timeout_ms(internals, wait.num_milliseconds() as u32)
                                  .unwrap()
                                  .0;
            }
        }

        if shared.config.max_lifetime().is_some() || shared.config.idle_timeout().is_some() {
            let s = shared.clone();
            shared.thread_pool
                  .run_at_fixed_rate(Duration::seconds(reaper_rate), move || reap_connections(&s));
        }

        Ok(Pool { shared: shared })
    }

    /// Retrieves a connection from the pool.
    ///
    /// Waits for at most `Config::connection_timeout` before returning an
    /// error.
    pub fn get(&self) -> Result<PooledConnection<M>, GetTimeout> {
        let end = SteadyTime::now() + cvt(self.shared.config.connection_timeout());
        let mut internals = self.shared.internals.lock().unwrap();

        let connection;
        loop {
            match internals.conns.pop_front() {
                Some(mut conn) => {
                    drop(internals);

                    if self.shared.config.test_on_check_out() {
                        if let Err(e) = self.shared.manager.is_valid(&mut conn.conn) {
                            self.shared.config.error_handler().handle_error(e);
                            internals = self.shared.internals.lock().unwrap();
                            drop_conn(&self.shared, &mut internals);
                            continue;
                        }
                    }

                    connection = conn;
                    break;
                }
                None => {
                    if internals.num_conns + internals.pending_conns <
                       self.shared.config.pool_size() {
                        add_connection(&self.shared, &mut internals);
                    }

                    let now = SteadyTime::now();
                    let mut timeout = (end - now).num_milliseconds();
                    if timeout < 0 {
                        timeout = 0
                    };
                    let (new_internals, no_timeout) = self.shared
                                                          .cond
                                                          .wait_timeout_ms(internals,
                                                                           timeout as u32)
                                                          .unwrap();
                    internals = new_internals;

                    if !no_timeout {
                        return Err(GetTimeout(()));
                    }
                }
            }
        }

        Ok(PooledConnection {
            pool: self.clone(),
            conn: Some(connection),
        })
    }

    fn put_back(&self, mut conn: Conn<M::Connection>) {
        // This is specified to be fast, but call it before locking anyways
        let broken = self.shared.manager.has_broken(&mut conn.conn);

        let mut internals = self.shared.internals.lock().unwrap();
        if broken {
            drop_conn(&self.shared, &mut internals);
        } else {
            conn.idle_start = SteadyTime::now();
            internals.conns.push_back(conn);
            self.shared.cond.notify_one();
        }
    }
}

/// A smart pointer wrapping a connection.
pub struct PooledConnection<M>
    where M: ManageConnection
{
    pool: Pool<M>,
    conn: Option<Conn<M::Connection>>,
}

impl<M> fmt::Debug for PooledConnection<M>
    where M: ManageConnection + fmt::Debug,
          M::Connection: fmt::Debug
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("PooledConnection")
           .field("pool", &self.pool)
           .field("connection", &self.conn.as_ref().unwrap().conn)
           .finish()
    }
}

impl<M> Drop for PooledConnection<M> where M: ManageConnection
{
    fn drop(&mut self) {
        self.pool.put_back(self.conn.take().unwrap());
    }
}

impl<M> Deref for PooledConnection<M> where M: ManageConnection
{
    type Target = M::Connection;

    fn deref(&self) -> &M::Connection {
        &self.conn.as_ref().unwrap().conn
    }
}

impl<M> DerefMut for PooledConnection<M> where M: ManageConnection
{
    fn deref_mut(&mut self) -> &mut M::Connection {
        &mut self.conn.as_mut().unwrap().conn
    }
}

fn cvt(d: std::time::Duration) -> Duration {
    Duration::seconds(d.as_secs() as i64) + Duration::nanoseconds(d.subsec_nanos() as i64)
}
<commit_msg>Make PooledConnection's Debug impl delegate directly<commit_after>//! A generic connection pool.
//!
//! Opening a new database connection every time one is needed is both
//! inefficient and can lead to resource exhaustion under high traffic
//! conditions. A connection pool maintains a set of open connections to a
//! database, handing them out for repeated use.
//!
//! r2d2 is agnostic to the connection type it is managing. Implementors of the
//! `ManageConnection` trait provide the database-specific logic to create and
//! check the health of connections.
//!
//! # Example
//!
//! Using an imaginary "foodb" database.
//!
//! ```rust,ignore
//! use std::thread;
//!
//! extern crate r2d2;
//! extern crate r2d2_foodb;
//!
//! fn main() {
//!     let config = r2d2::Config::builder()
//!         .pool_size(15)
//!         .error_handler(Box::new(r2d2::LoggingErrorHandler))
//!         .build();
//!     let manager = r2d2_foodb::FooConnectionManager::new("localhost:1234");
//!
//!     let pool = r2d2::Pool::new(config, manager).unwrap();
//!
//!     for _ in 0..20 {
//!         let pool = pool.clone();
//!         thread::spawn(move || {
//!             let conn = pool.get().unwrap();
//!             // use the connection
//!             // it will be returned to the pool when it falls out of scope.
//!         })
//!     }
//! }
//! ```
#![warn(missing_docs)]
#![doc(html_root_url="https://sfackler.github.io/r2d2/doc/v0.6.2")]

#[macro_use]
extern crate log;
extern crate time;

use std::cmp;
use std::collections::VecDeque;
use std::error::Error;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::mem;
use std::sync::{Arc, Mutex, Condvar};
use time::{Duration, SteadyTime};

#[doc(inline)]
pub use config::Config;

use task::ScheduledThreadPool;

pub mod config;
mod task;
mod thunk;

#[cfg(test)]
mod test;

/// A trait which provides connection-specific functionality.
pub trait ManageConnection: Send + Sync + 'static {
    /// The connection type this manager deals with.
    type Connection: Send + 'static;

    /// The error type returned by `Connection`s.
    type Error: 'static;

    /// Attempts to create a new connection.
    fn connect(&self) -> Result<Self::Connection, Self::Error>;

    /// Determines if the connection is still connected to the database.
    ///
    /// A standard implementation would check if a simple query like `SELECT 1`
    /// succeeds.
    fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error>;

    /// *Quickly* determines if the connection is no longer usable.
    ///
    /// This will be called synchronously every time a connection is returned
    /// to the pool, so it should *not* block. If it returns `true`, the
    /// connection will be discarded.
    ///
    /// For example, an implementation might check if the underlying TCP socket
    /// has disconnected. Implementations that do not support this kind of
    /// fast health check may simply return `false`.
    fn has_broken(&self, conn: &mut Self::Connection) -> bool;
}

/// A trait which handles errors reported by the `ManageConnection`.
pub trait HandleError<E>: Send+Sync+'static {
    /// Handles an error.
    fn handle_error(&self, error: E);
}

/// A `HandleError` implementation which does nothing.
#[derive(Copy, Clone, Debug)]
pub struct NopErrorHandler;

impl<E> HandleError<E> for NopErrorHandler {
    fn handle_error(&self, _: E) {}
}

/// A `HandleError` implementation which logs at the error level.
#[derive(Copy, Clone, Debug)]
pub struct LoggingErrorHandler;

impl<E> HandleError<E> for LoggingErrorHandler where E: Error
{
    fn handle_error(&self, error: E) {
        error!("{}", error);
    }
}

/// A trait which allows for customization of connections.
pub trait CustomizeConnection<C, E>: Send+Sync+'static {
    /// Called with connections immediately after they are returned from
    /// `ManageConnection::connect`.
    ///
    /// The default implementation simply returns `Ok(())`.
    ///
    /// # Errors
    ///
    /// If this method returns an error, the connection will be discarded.
    #[allow(unused_variables)]
    fn on_acquire(&self, conn: &mut C) -> Result<(), E> {
        Ok(())
    }
}

/// A `CustomizeConnection` which does nothing.
#[derive(Copy, Clone, Debug)]
pub struct NopConnectionCustomizer;

impl<C, E> CustomizeConnection<C, E> for NopConnectionCustomizer {}

struct Conn<C> {
    conn: C,
    birth: SteadyTime,
    idle_start: SteadyTime,
}

impl<C> Conn<C> {
    fn new(conn: C) -> Conn<C> {
        let now = SteadyTime::now();
        Conn {
            conn: conn,
            birth: now,
            idle_start: now,
        }
    }
}

struct PoolInternals<C> {
    conns: VecDeque<Conn<C>>,
    num_conns: u32,
    pending_conns: u32,
}

struct SharedPool<M>
    where M: ManageConnection
{
    config: Config<M::Connection, M::Error>,
    manager: M,
    internals: Mutex<PoolInternals<M::Connection>>,
    cond: Condvar,
    thread_pool: ScheduledThreadPool,
}

impl<M> Drop for SharedPool<M> where M: ManageConnection
{
    fn drop(&mut self) {
        self.thread_pool.clear();
    }
}

fn drop_conn<M>(shared: &Arc<SharedPool<M>>, internals: &mut PoolInternals<M::Connection>)
    where M: ManageConnection
{
    internals.num_conns -= 1;

    let min = shared.config.min_idle().unwrap_or(shared.config.pool_size());
    if internals.num_conns + internals.pending_conns < min {
        add_connection(shared, internals);
    }
}

fn add_connection<M>(shared: &Arc<SharedPool<M>>, internals: &mut PoolInternals<M::Connection>)
    where M: ManageConnection
{
    internals.pending_conns += 1;
    inner(Duration::zero(), shared);

    fn inner<M>(delay: Duration, shared: &Arc<SharedPool<M>>)
        where M: ManageConnection
    {
        let new_shared = shared.clone();
        shared.thread_pool.run_after(delay, move || {
            let shared = new_shared;
            let conn = shared.manager.connect().and_then(|mut conn| {
                shared.config.connection_customizer().on_acquire(&mut conn).map(|_| conn)
            });
            match conn {
                Ok(conn) => {
                    let mut internals = shared.internals.lock().unwrap();
                    internals.conns.push_back(Conn::new(conn));
                    internals.pending_conns -= 1;
                    internals.num_conns += 1;
                    shared.cond.notify_one();
                }
                Err(err) => {
                    shared.config.error_handler().handle_error(err);
                    let delay = cmp::max(Duration::milliseconds(200), delay);
                    let delay = cmp::min(cvt(shared.config.connection_timeout()) / 2, delay * 2);
                    inner(delay, &shared);
                }
            }
        });
    }
}

fn reap_connections<M>(shared: &Arc<SharedPool<M>>)
    where M: ManageConnection
{
    let mut old = VecDeque::with_capacity(shared.config.pool_size() as usize);
    let mut to_drop = vec![];

    let mut internals = shared.internals.lock().unwrap();
    mem::swap(&mut old, &mut internals.conns);
    let now = SteadyTime::now();
    for conn in old {
        let mut reap = false;
        if let Some(timeout) = shared.config.idle_timeout() {
            reap |= now - conn.idle_start >= cvt(timeout);
        }
        if let Some(lifetime) = shared.config.max_lifetime() {
            reap |= now - conn.birth >= cvt(lifetime);
        }
        if reap {
            drop_conn(shared, &mut internals);
            to_drop.push(conn.conn);
        } else {
            internals.conns.push_back(conn);
        }
    }
    drop(internals); // make sure we run to_drop destructors without this locked
}

/// A generic connection pool.
pub struct Pool<M>
    where M: ManageConnection
{
    shared: Arc<SharedPool<M>>,
}

/// Returns a new `Pool` referencing the same state as `self`.
impl<M> Clone for Pool<M> where M: ManageConnection
{
    fn clone(&self) -> Pool<M> {
        Pool { shared: self.shared.clone() }
    }
}

impl<M> fmt::Debug for Pool<M> where M: ManageConnection + fmt::Debug
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let inner = self.shared.internals.lock().unwrap();

        fmt.debug_struct("Pool")
           .field("connections", &inner.num_conns)
           .field("idle_connections", &inner.conns.len())
           .field("config", &self.shared.config)
           .field("manager", &self.shared.manager)
           .finish()
    }
}

/// An error returned by `Pool::new` if it fails to initialize connections.
#[derive(Debug)]
pub struct InitializationError(());

impl fmt::Display for InitializationError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

impl Error for InitializationError {
    fn description(&self) -> &str {
        "Unable to initialize connections"
    }
}

/// An error returned by `Pool::get` if it times out without retrieving a connection.
#[derive(Debug)]
pub struct GetTimeout(());

impl fmt::Display for GetTimeout {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.description())
    }
}

impl Error for GetTimeout {
    fn description(&self) -> &str {
        "Timed out while waiting for a connection"
    }
}

impl<M> Pool<M> where M: ManageConnection
{
    /// Creates a new connection pool.
    ///
    /// Returns an `Err` value if `initialization_fail_fast` is set to true in
    /// the configuration and the pool is unable to open all of its
    /// connections.
    pub fn new(config: Config<M::Connection, M::Error>,
               manager: M)
               -> Result<Pool<M>, InitializationError> {
        Pool::new_inner(config, manager, 30)
    }

    // for testing
    fn new_inner(config: Config<M::Connection, M::Error>,
                 manager: M,
                 reaper_rate: i64)
                 -> Result<Pool<M>, InitializationError> {
        let internals = PoolInternals {
            conns: VecDeque::with_capacity(config.pool_size() as usize),
            num_conns: 0,
            pending_conns: 0,
        };

        let shared = Arc::new(SharedPool {
            thread_pool: ScheduledThreadPool::new(config.helper_threads() as usize),
            config: config,
            manager: manager,
            internals: Mutex::new(internals),
            cond: Condvar::new(),
        });

        {
            let mut inner = shared.internals.lock().unwrap();
            for _ in 0..shared.config.min_idle().unwrap_or(shared.config.pool_size()) {
                add_connection(&shared, &mut inner);
            }
            drop(inner);
        }

        if shared.config.initialization_fail_fast() {
            let end = SteadyTime::now() + cvt(shared.config.connection_timeout());
            let mut internals = shared.internals.lock().unwrap();

            while internals.num_conns != shared.config.pool_size() {
                let wait = end - SteadyTime::now();
                if wait <= Duration::zero() {
                    return Err(InitializationError(()));
                }
                internals = shared.cond
                                  .wait_timeout_ms(internals, wait.num_milliseconds() as u32)
                                  .unwrap()
                                  .0;
            }
        }

        if shared.config.max_lifetime().is_some() || shared.config.idle_timeout().is_some() {
            let s = shared.clone();
            shared.thread_pool
                  .run_at_fixed_rate(Duration::seconds(reaper_rate), move || reap_connections(&s));
        }

        Ok(Pool { shared: shared })
    }

    /// Retrieves a connection from the pool.
    ///
    /// Waits for at most `Config::connection_timeout` before returning an
    /// error.
    pub fn get(&self) -> Result<PooledConnection<M>, GetTimeout> {
        let end = SteadyTime::now() + cvt(self.shared.config.connection_timeout());
        let mut internals = self.shared.internals.lock().unwrap();

        let connection;
        loop {
            match internals.conns.pop_front() {
                Some(mut conn) => {
                    drop(internals);

                    if self.shared.config.test_on_check_out() {
                        if let Err(e) = self.shared.manager.is_valid(&mut conn.conn) {
                            self.shared.config.error_handler().handle_error(e);
                            internals = self.shared.internals.lock().unwrap();
                            drop_conn(&self.shared, &mut internals);
                            continue;
                        }
                    }

                    connection = conn;
                    break;
                }
                None => {
                    if internals.num_conns + internals.pending_conns <
                       self.shared.config.pool_size() {
                        add_connection(&self.shared, &mut internals);
                    }

                    let now = SteadyTime::now();
                    let mut timeout = (end - now).num_milliseconds();
                    if timeout < 0 {
                        timeout = 0
                    };
                    let (new_internals, no_timeout) = self.shared
                                                          .cond
                                                          .wait_timeout_ms(internals,
                                                                           timeout as u32)
                                                          .unwrap();
                    internals = new_internals;

                    if !no_timeout {
                        return Err(GetTimeout(()));
                    }
                }
            }
        }

        Ok(PooledConnection {
            pool: self.clone(),
            conn: Some(connection),
        })
    }

    fn put_back(&self, mut conn: Conn<M::Connection>) {
        // This is specified to be fast, but call it before locking anyways
        let broken = self.shared.manager.has_broken(&mut conn.conn);

        let mut internals = self.shared.internals.lock().unwrap();
        if broken {
            drop_conn(&self.shared, &mut internals);
        } else {
            conn.idle_start = SteadyTime::now();
            internals.conns.push_back(conn);
            self.shared.cond.notify_one();
        }
    }
}

/// A smart pointer wrapping a connection.
pub struct PooledConnection<M>
    where M: ManageConnection
{
    pool: Pool<M>,
    conn: Option<Conn<M::Connection>>,
}

impl<M> fmt::Debug for PooledConnection<M>
    where M: ManageConnection + fmt::Debug,
          M::Connection: fmt::Debug
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.conn.as_ref().unwrap().conn, fmt)
    }
}

impl<M> Drop for PooledConnection<M> where M: ManageConnection
{
    fn drop(&mut self) {
        self.pool.put_back(self.conn.take().unwrap());
    }
}

impl<M> Deref for PooledConnection<M> where M: ManageConnection
{
    type Target = M::Connection;

    fn deref(&self) -> &M::Connection {
        &self.conn.as_ref().unwrap().conn
    }
}

impl<M> DerefMut for PooledConnection<M> where M: ManageConnection
{
    fn deref_mut(&mut self) -> &mut M::Connection {
        &mut self.conn.as_mut().unwrap().conn
    }
}

fn cvt(d: std::time::Duration) -> Duration {
    Duration::seconds(d.as_secs() as i64) + Duration::nanoseconds(d.subsec_nanos() as i64)
}
<|endoftext|>