<commit_before>use crate::{convert, ops};

/// Used to tell an operation whether it should exit early or go on as usual.
///
/// This is used when exposing things (like graph traversals or visitors) where
/// you want the user to be able to choose whether to exit early.
/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
/// mean again?" -- and allows including a value.
///
/// # Examples
///
/// Early-exiting from [`Iterator::try_for_each`]:
/// ```
/// use std::ops::ControlFlow;
///
/// let r = (2..100).try_for_each(|x| {
///     if 403 % x == 0 {
///         return ControlFlow::Break(x)
///     }
///
///     ControlFlow::Continue(())
/// });
/// assert_eq!(r, ControlFlow::Break(13));
/// ```
///
/// A basic tree traversal:
/// ```
/// use std::ops::ControlFlow;
///
/// pub struct TreeNode<T> {
///     value: T,
///     left: Option<Box<TreeNode<T>>>,
///     right: Option<Box<TreeNode<T>>>,
/// }
///
/// impl<T> TreeNode<T> {
///     pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
///         if let Some(left) = &self.left {
///             left.traverse_inorder(f)?;
///         }
///         f(&self.value)?;
///         if let Some(right) = &self.right {
///             right.traverse_inorder(f)?;
///         }
///         ControlFlow::Continue(())
///     }
///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
///         Some(Box::new(Self { value, left: None, right: None }))
///     }
/// }
///
/// let node = TreeNode {
///     value: 0,
///     left: TreeNode::leaf(1),
///     right: Some(Box::new(TreeNode {
///         value: -1,
///         left: TreeNode::leaf(5),
///         right: TreeNode::leaf(2),
///     }))
/// };
/// let mut sum = 0;
///
/// let res = node.traverse_inorder(&mut |val| {
///     if *val < 0 {
///         ControlFlow::Break(*val)
///     } else {
///         sum += *val;
///         ControlFlow::Continue(())
///     }
/// });
/// assert_eq!(res, ControlFlow::Break(-1));
/// assert_eq!(sum, 6);
/// ```
#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> {
    /// Move on to the next phase of the operation as normal.
    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    #[lang = "Continue"]
    Continue(C),
    /// Exit the operation without running subsequent phases.
    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    #[lang = "Break"]
    Break(B),
    // Yes, the order of the variants doesn't match the type parameters.
    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
    // is a no-op conversion in the `Try` implementation.
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::Try for ControlFlow<B, C> {
    type Output = C;
    type Residual = ControlFlow<B, convert::Infallible>;

    #[inline]
    fn from_output(output: Self::Output) -> Self {
        ControlFlow::Continue(output)
    }

    #[inline]
    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        match self {
            ControlFlow::Continue(c) => ControlFlow::Continue(c),
            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
        }
    }
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::FromResidual for ControlFlow<B, C> {
    #[inline]
    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
        match residual {
            ControlFlow::Break(b) => ControlFlow::Break(b),
        }
    }
}

impl<B, C> ControlFlow<B, C> {
    /// Returns `true` if this is a `Break` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
    /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_break(&self) -> bool {
        matches!(*self, ControlFlow::Break(_))
    }

    /// Returns `true` if this is a `Continue` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
    /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_continue(&self) -> bool {
        matches!(*self, ControlFlow::Continue(_))
    }

    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
    /// `ControlFlow` was `Break` and `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
    /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn break_value(self) -> Option<B> {
        match self {
            ControlFlow::Continue(..) => None,
            ControlFlow::Break(x) => Some(x),
        }
    }

    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
    /// to the break value in case it exists.
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
    where
        F: FnOnce(B) -> T,
    {
        match self {
            ControlFlow::Continue(x) => ControlFlow::Continue(x),
            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
        }
    }
}

/// These are used only as part of implementing the iterator adapters.
/// They have mediocre names and non-obvious semantics, so aren't
/// currently on a path to potential stabilization.
impl<R: ops::Try> ControlFlow<R, R::Output> {
    /// Create a `ControlFlow` from any type implementing `Try`.
    #[inline]
    pub(crate) fn from_try(r: R) -> Self {
        match R::branch(r) {
            ControlFlow::Continue(v) => ControlFlow::Continue(v),
            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
        }
    }

    /// Convert a `ControlFlow` into any type implementing `Try`;
    #[inline]
    pub(crate) fn into_try(self) -> R {
        match self {
            ControlFlow::Continue(v) => R::from_output(v),
            ControlFlow::Break(v) => v,
        }
    }
}

impl<B> ControlFlow<B, ()> {
    /// It's frequently the case that there's no value needed with `Continue`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
    ///     partial_sum += x;
    ///     if partial_sum > 100 { ControlFlow::Break(x) }
    ///     else { ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(last_used.break_value(), Some(22));
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const CONTINUE: Self = ControlFlow::Continue(());
}

impl<C> ControlFlow<(), C> {
    /// APIs like `try_for_each` don't need values with `Break`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// (1..10).chain(20..25).try_for_each(|x| {
    ///     if partial_sum > 100 { ControlFlow::BREAK }
    ///     else { partial_sum += x; ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(partial_sum, 108);
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const BREAK: Self = ControlFlow::Break(());
}
<commit_msg>Rollup merge of #89446 - chrismit3s:issue-88715-fix, r=joshtriplett<commit_after>use crate::{convert, ops};

/// Used to tell an operation whether it should exit early or go on as usual.
///
/// This is used when exposing things (like graph traversals or visitors) where
/// you want the user to be able to choose whether to exit early.
/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
/// mean again?" -- and allows including a value.
///
/// Similar to [`Option`] and [`Result`], this enum can be used with the `?` operator
/// to return immediately if the [`Break`] variant is present or otherwise continue normally
/// with the value inside the [`Continue`] variant.
///
/// # Examples
///
/// Early-exiting from [`Iterator::try_for_each`]:
/// ```
/// use std::ops::ControlFlow;
///
/// let r = (2..100).try_for_each(|x| {
///     if 403 % x == 0 {
///         return ControlFlow::Break(x)
///     }
///
///     ControlFlow::Continue(())
/// });
/// assert_eq!(r, ControlFlow::Break(13));
/// ```
///
/// A basic tree traversal:
/// ```
/// use std::ops::ControlFlow;
///
/// pub struct TreeNode<T> {
///     value: T,
///     left: Option<Box<TreeNode<T>>>,
///     right: Option<Box<TreeNode<T>>>,
/// }
///
/// impl<T> TreeNode<T> {
///     pub fn traverse_inorder<B>(&self, f: &mut impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
///         if let Some(left) = &self.left {
///             left.traverse_inorder(f)?;
///         }
///         f(&self.value)?;
///         if let Some(right) = &self.right {
///             right.traverse_inorder(f)?;
///         }
///         ControlFlow::Continue(())
///     }
///     fn leaf(value: T) -> Option<Box<TreeNode<T>>> {
///         Some(Box::new(Self { value, left: None, right: None }))
///     }
/// }
///
/// let node = TreeNode {
///     value: 0,
///     left: TreeNode::leaf(1),
///     right: Some(Box::new(TreeNode {
///         value: -1,
///         left: TreeNode::leaf(5),
///         right: TreeNode::leaf(2),
///     }))
/// };
/// let mut sum = 0;
///
/// let res = node.traverse_inorder(&mut |val| {
///     if *val < 0 {
///         ControlFlow::Break(*val)
///     } else {
///         sum += *val;
///         ControlFlow::Continue(())
///     }
/// });
/// assert_eq!(res, ControlFlow::Break(-1));
/// assert_eq!(sum, 6);
/// ```
///
/// [`Break`]: ControlFlow::Break
/// [`Continue`]: ControlFlow::Continue
#[stable(feature = "control_flow_enum_type", since = "1.55.0")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> {
    /// Move on to the next phase of the operation as normal.
    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    #[lang = "Continue"]
    Continue(C),
    /// Exit the operation without running subsequent phases.
    #[stable(feature = "control_flow_enum_type", since = "1.55.0")]
    #[lang = "Break"]
    Break(B),
    // Yes, the order of the variants doesn't match the type parameters.
    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
    // is a no-op conversion in the `Try` implementation.
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::Try for ControlFlow<B, C> {
    type Output = C;
    type Residual = ControlFlow<B, convert::Infallible>;

    #[inline]
    fn from_output(output: Self::Output) -> Self {
        ControlFlow::Continue(output)
    }

    #[inline]
    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        match self {
            ControlFlow::Continue(c) => ControlFlow::Continue(c),
            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
        }
    }
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::FromResidual for ControlFlow<B, C> {
    #[inline]
    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
        match residual {
            ControlFlow::Break(b) => ControlFlow::Break(b),
        }
    }
}

impl<B, C> ControlFlow<B, C> {
    /// Returns `true` if this is a `Break` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
    /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_break(&self) -> bool {
        matches!(*self, ControlFlow::Break(_))
    }

    /// Returns `true` if this is a `Continue` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
    /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_continue(&self) -> bool {
        matches!(*self, ControlFlow::Continue(_))
    }

    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
    /// `ControlFlow` was `Break` and `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
    /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn break_value(self) -> Option<B> {
        match self {
            ControlFlow::Continue(..) => None,
            ControlFlow::Break(x) => Some(x),
        }
    }

    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
    /// to the break value in case it exists.
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
    where
        F: FnOnce(B) -> T,
    {
        match self {
            ControlFlow::Continue(x) => ControlFlow::Continue(x),
            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
        }
    }
}

/// These are used only as part of implementing the iterator adapters.
/// They have mediocre names and non-obvious semantics, so aren't
/// currently on a path to potential stabilization.
impl<R: ops::Try> ControlFlow<R, R::Output> {
    /// Create a `ControlFlow` from any type implementing `Try`.
    #[inline]
    pub(crate) fn from_try(r: R) -> Self {
        match R::branch(r) {
            ControlFlow::Continue(v) => ControlFlow::Continue(v),
            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
        }
    }

    /// Convert a `ControlFlow` into any type implementing `Try`;
    #[inline]
    pub(crate) fn into_try(self) -> R {
        match self {
            ControlFlow::Continue(v) => R::from_output(v),
            ControlFlow::Break(v) => v,
        }
    }
}

impl<B> ControlFlow<B, ()> {
    /// It's frequently the case that there's no value needed with `Continue`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
    ///     partial_sum += x;
    ///     if partial_sum > 100 { ControlFlow::Break(x) }
    ///     else { ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(last_used.break_value(), Some(22));
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const CONTINUE: Self = ControlFlow::Continue(());
}

impl<C> ControlFlow<(), C> {
    /// APIs like `try_for_each` don't need values with `Break`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// (1..10).chain(20..25).try_for_each(|x| {
    ///     if partial_sum > 100 { ControlFlow::BREAK }
    ///     else { partial_sum += x; ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(partial_sum, 108);
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const BREAK: Self = ControlFlow::Break(());
}
<|endoftext|>


<commit_before>// Copyright 2016 Alex Regueiro
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use ffi;
use {Error, DB};

use libc::{c_int, uint32_t};
use std::ffi::CString;
use std::path::Path;

pub struct BackupEngine {
    inner: *mut ffi::rocksdb_backup_engine_t,
}

pub struct BackupEngineOptions {
    inner: *mut ffi::rocksdb_options_t,
}

pub struct RestoreOptions {
    inner: *mut ffi::rocksdb_restore_options_t,
}

impl BackupEngine {
    /// Open a backup engine with the specified options.
    pub fn open<P: AsRef<Path>>(
        opts: &BackupEngineOptions,
        path: P,
    ) -> Result<BackupEngine, Error> {
        let path = path.as_ref();
        let cpath = match CString::new(path.to_string_lossy().as_bytes()) {
            Ok(c) => c,
            Err(_) => {
                return Err(Error::new(
                    "Failed to convert path to CString \
                     when opening backup engine"
                        .to_owned(),
                ));
            }
        };

        let be: *mut ffi::rocksdb_backup_engine_t;
        unsafe { be = ffi_try!(ffi::rocksdb_backup_engine_open(opts.inner, cpath.as_ptr(),)) }

        if be.is_null() {
            return Err(Error::new("Could not initialize backup engine.".to_owned()));
        }

        Ok(BackupEngine { inner: be })
    }

    pub fn create_new_backup(&mut self, db: &DB) -> Result<(), Error> {
        unsafe {
            ffi_try!(ffi::rocksdb_backup_engine_create_new_backup(
                self.inner, db.inner,
            ));
            Ok(())
        }
    }

    pub fn purge_old_backups(&mut self, num_backups_to_keep: usize) -> Result<(), Error> {
        unsafe {
            ffi_try!(ffi::rocksdb_backup_engine_purge_old_backups(
                self.inner,
                num_backups_to_keep as uint32_t,
            ));
            Ok(())
        }
    }
    /// Restore from the latest backup
    /// db_dir:  input db directory
    /// wal_dir:  input wal directory
    /// opts: restore options
    /// example:
    /// use rocksdb::backup::{BackupEngine, BackupEngineOptions};
    /// let backup_opts = BackupEngineOptions::default();
    /// let backup_engine = BackupEngine::open(&backup_opts, &backup_path).unwrap();
    /// let mut restore_option = rocksdb::backup::RestoreOptions::default();
    /// restore_option.set_keep_log_files(true); /// true to keep log files
    /// if let Err(e) = backup_engine.restore_from_latest_backup(&db_path, &wal_dir, &restore_option) {
    ///     error!("Failed to restore from the backup. Error:{:?}", e);
    ///     return Err(e.to_string());
    ///  }
    pub fn restore_from_latest_backup<P: AsRef<Path>>(
        &mut self,
        db_dir: P,
        wal_dir: P,
        opts: &RestoreOptions,
    ) -> Result<(), Error> {
        let db_dir = db_dir.as_ref();
        let c_db_dir = match CString::new(db_dir.to_string_lossy().as_bytes()) {
            Ok(c) => c,
            Err(_) => {
                return Err(Error::new(
                    "Failed to convert db_dir to CString \
                     when restoring from latest backup"
                        .to_owned(),
                ));
            }
        };

        let wal_dir = wal_dir.as_ref();
        let c_wal_dir = match CString::new(wal_dir.to_string_lossy().as_bytes()) {
            Ok(c) => c,
            Err(_) => {
                return Err(Error::new(
                    "Failed to convert wal_dir to CString \
                     when restoring from latest backup"
                        .to_owned(),
                ));
            }
        };

        unsafe {
            ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_latest_backup(
                self.inner,
                c_db_dir.as_ptr(),
                c_wal_dir.as_ptr(),
                opts.inner,
            ));
        }
        Ok(())
    }
}

impl BackupEngineOptions {
    //
}

impl RestoreOptions {
    pub fn set_keep_log_files(&mut self, keep_log_files: bool) {
        unsafe {
            ffi::rocksdb_restore_options_set_keep_log_files(self.inner, keep_log_files as c_int);
        }
    }
}

impl Default for BackupEngineOptions {
    fn default() -> BackupEngineOptions {
        unsafe {
            let opts = ffi::rocksdb_options_create();
            if opts.is_null() {
                panic!("Could not create RocksDB backup options".to_owned());
            }
            BackupEngineOptions { inner: opts }
        }
    }
}

impl Default for RestoreOptions {
    fn default() -> RestoreOptions {
        unsafe {
            let opts = ffi::rocksdb_restore_options_create();
            if opts.is_null() {
                panic!("Could not create RocksDB restore options".to_owned());
            }
            RestoreOptions { inner: opts }
        }
    }
}

impl Drop for BackupEngine {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_backup_engine_close(self.inner);
        }
    }
}

impl Drop for BackupEngineOptions {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_options_destroy(self.inner);
        }
    }
}

impl Drop for RestoreOptions {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_restore_options_destroy(self.inner);
        }
    }
}
<commit_msg>Specifying two separate Path<commit_after>// Copyright 2016 Alex Regueiro
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use ffi;
use {Error, DB};

use libc::{c_int, uint32_t};
use std::ffi::CString;
use std::path::Path;

pub struct BackupEngine {
    inner: *mut ffi::rocksdb_backup_engine_t,
}

pub struct BackupEngineOptions {
    inner: *mut ffi::rocksdb_options_t,
}

pub struct RestoreOptions {
    inner: *mut ffi::rocksdb_restore_options_t,
}

impl BackupEngine {
    /// Open a backup engine with the specified options.
    pub fn open<P: AsRef<Path>>(
        opts: &BackupEngineOptions,
        path: P,
    ) -> Result<BackupEngine, Error> {
        let path = path.as_ref();
        let cpath = match CString::new(path.to_string_lossy().as_bytes()) {
            Ok(c) => c,
            Err(_) => {
                return Err(Error::new(
                    "Failed to convert path to CString \
                     when opening backup engine"
                        .to_owned(),
                ));
            }
        };

        let be: *mut ffi::rocksdb_backup_engine_t;
        unsafe { be = ffi_try!(ffi::rocksdb_backup_engine_open(opts.inner, cpath.as_ptr(),)) }

        if be.is_null() {
            return Err(Error::new("Could not initialize backup engine.".to_owned()));
        }

        Ok(BackupEngine { inner: be })
    }

    pub fn create_new_backup(&mut self, db: &DB) -> Result<(), Error> {
        unsafe {
            ffi_try!(ffi::rocksdb_backup_engine_create_new_backup(
                self.inner, db.inner,
            ));
            Ok(())
        }
    }

    pub fn purge_old_backups(&mut self, num_backups_to_keep: usize) -> Result<(), Error> {
        unsafe {
            ffi_try!(ffi::rocksdb_backup_engine_purge_old_backups(
                self.inner,
                num_backups_to_keep as uint32_t,
            ));
            Ok(())
        }
    }
    /// Restore from the latest backup
    /// db_dir:  input db directory
    /// wal_dir:  input wal directory
    /// opts: restore options
    /// example:
    /// use rocksdb::backup::{BackupEngine, BackupEngineOptions};
    /// let backup_opts = BackupEngineOptions::default();
    /// let backup_engine = BackupEngine::open(&backup_opts, &backup_path).unwrap();
    /// let mut restore_option = rocksdb::backup::RestoreOptions::default();
    /// restore_option.set_keep_log_files(true); /// true to keep log files
    /// if let Err(e) = backup_engine.restore_from_latest_backup(&db_path, &wal_dir, &restore_option) {
    ///     error!("Failed to restore from the backup. Error:{:?}", e);
    ///     return Err(e.to_string());
    ///  }
    pub fn restore_from_latest_backup<D: AsRef<Path>, W: AsRef<Path>>(
        &mut self,
        db_dir: D,
        wal_dir: W,
        opts: &RestoreOptions,
    ) -> Result<(), Error> {
        let db_dir = db_dir.as_ref();
        let c_db_dir = match CString::new(db_dir.to_string_lossy().as_bytes()) {
            Ok(c) => c,
            Err(_) => {
                return Err(Error::new(
                    "Failed to convert db_dir to CString \
                     when restoring from latest backup"
                        .to_owned(),
                ));
            }
        };

        let wal_dir = wal_dir.as_ref();
        let c_wal_dir = match CString::new(wal_dir.to_string_lossy().as_bytes()) {
            Ok(c) => c,
            Err(_) => {
                return Err(Error::new(
                    "Failed to convert wal_dir to CString \
                     when restoring from latest backup"
                        .to_owned(),
                ));
            }
        };

        unsafe {
            ffi_try!(ffi::rocksdb_backup_engine_restore_db_from_latest_backup(
                self.inner,
                c_db_dir.as_ptr(),
                c_wal_dir.as_ptr(),
                opts.inner,
            ));
        }
        Ok(())
    }
}

impl BackupEngineOptions {
    //
}

impl RestoreOptions {
    pub fn set_keep_log_files(&mut self, keep_log_files: bool) {
        unsafe {
            ffi::rocksdb_restore_options_set_keep_log_files(self.inner, keep_log_files as c_int);
        }
    }
}

impl Default for BackupEngineOptions {
    fn default() -> BackupEngineOptions {
        unsafe {
            let opts = ffi::rocksdb_options_create();
            if opts.is_null() {
                panic!("Could not create RocksDB backup options".to_owned());
            }
            BackupEngineOptions { inner: opts }
        }
    }
}

impl Default for RestoreOptions {
    fn default() -> RestoreOptions {
        unsafe {
            let opts = ffi::rocksdb_restore_options_create();
            if opts.is_null() {
                panic!("Could not create RocksDB restore options".to_owned());
            }
            RestoreOptions { inner: opts }
        }
    }
}

impl Drop for BackupEngine {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_backup_engine_close(self.inner);
        }
    }
}

impl Drop for BackupEngineOptions {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_options_destroy(self.inner);
        }
    }
}

impl Drop for RestoreOptions {
    fn drop(&mut self) {
        unsafe {
            ffi::rocksdb_restore_options_destroy(self.inner);
        }
    }
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
use std::collections::HashMap;

use pest_meta::ast::*;

pub fn restore_on_err(rule: Rule, rules_to_exprs: &HashMap<String, Expr>) -> Rule {
    match rule {
        Rule { name, ty, expr } => {
            let expr = expr.map_bottom_up(|expr| {
                wrap_branching_exprs(expr, rules_to_exprs)
            });

            Rule {
                name,
                ty,
                expr
            }
        }
    }
}

fn wrap_branching_exprs(expr: Expr, rules_to_exprs: &HashMap<String, Expr>) -> Expr {
    match expr {
        Expr::Opt(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                Expr::Opt(Box::new(Expr::RestoreOnErr(expr)))
            } else {
                Expr::Opt(expr)
            }
        }
        Expr::Choice(lhs, rhs) => {
            let wrapped_lhs = if child_modifies_state(&lhs, rules_to_exprs) {
                Box::new(Expr::RestoreOnErr(lhs))
            } else {
                lhs
            };
            let wrapped_rhs = if child_modifies_state(&rhs, rules_to_exprs) {
                Box::new(Expr::RestoreOnErr(rhs))
            } else {
                rhs
            };
            Expr::Choice(wrapped_lhs, wrapped_rhs)
        }
        Expr::Rep(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                Expr::Rep(Box::new(Expr::RestoreOnErr(expr)))
            } else {
                Expr::Rep(expr)
            }
        }
        _ => expr
    }
}

fn child_modifies_state(expr: &Expr, rules_to_exprs: &HashMap<String, Expr>) -> bool {
    expr.iter_top_down().any(|expr| {
        match expr {
            Expr::Push(_) => true,
            Expr::Ident(ref s) if s == "pop" => true,
            Expr::Ident(ref name) => {
                let mut map = rules_to_exprs.clone();
                match map.remove(name) {
                    Some(rule_expr) => child_modifies_state(&rule_expr, &map),
                    None => false
                }
            },
            _ => false
        }
    })
}
<commit_msg>Definitely needed some tests<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
use std::collections::HashMap;

use pest_meta::ast::*;

pub fn restore_on_err(rule: Rule, rules_to_exprs: &HashMap<String, Expr>) -> Rule {
    match rule {
        Rule { name, ty, expr } => {
            let expr = expr.map_bottom_up(|expr| {
                wrap_branching_exprs(expr, rules_to_exprs)
            });

            Rule {
                name,
                ty,
                expr
            }
        }
    }
}

fn wrap_branching_exprs(expr: Expr, rules_to_exprs: &HashMap<String, Expr>) -> Expr {
    match expr {
        Expr::Opt(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                Expr::Opt(Box::new(Expr::RestoreOnErr(expr)))
            } else {
                Expr::Opt(expr)
            }
        }
        Expr::Choice(lhs, rhs) => {
            let wrapped_lhs = if child_modifies_state(&lhs, rules_to_exprs) {
                Box::new(Expr::RestoreOnErr(lhs))
            } else {
                lhs
            };
            let wrapped_rhs = if child_modifies_state(&rhs, rules_to_exprs) {
                Box::new(Expr::RestoreOnErr(rhs))
            } else {
                rhs
            };
            Expr::Choice(wrapped_lhs, wrapped_rhs)
        }
        Expr::Rep(expr) => {
            if child_modifies_state(&expr, rules_to_exprs) {
                Expr::Rep(Box::new(Expr::RestoreOnErr(expr)))
            } else {
                Expr::Rep(expr)
            }
        }
        _ => expr
    }
}

fn child_modifies_state(expr: &Expr, rules_to_exprs: &HashMap<String, Expr>) -> bool {
    expr.iter_top_down().any(|expr| {
        match expr {
            Expr::Push(_) => true,
            Expr::Ident(ref s) if s == "pop" => true,
            Expr::Ident(ref name) => {
                let mut map = rules_to_exprs.clone();
                match map.remove(name) {
                    Some(rule_expr) => child_modifies_state(&rule_expr, &map),
                    None => false
                }
            },
            _ => false
        }
    })
}

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

    #[test]
    fn restore_no_stack_children() {
        let rules = vec![
            Rule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Expr::Opt(Box::new(Expr::Str(String::from("a"))))
            },
        ];

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), rules[0].clone());
    }

    #[test]
    fn restore_with_child_stack_ops() {
        let rules = vec![
            Rule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Expr::Rep(
                    Box::new(Expr::Push(
                        Box::new(Expr::Str(String::from("a")))
                    ))
                )
            },
        ];

        let restored = Rule {
            name: "rule".to_owned(),
            ty: RuleType::Normal,
            expr: Expr::Rep(
                Box::new(Expr::RestoreOnErr(Box::new(Expr::Push(
                    Box::new(Expr::Str(String::from("a")))
                )))
            ))
        };

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), restored);
    }

    #[test]
    fn restore_choice_branch_with_and_branch_without() {
        let rules = vec![
            Rule {
                name: "rule".to_owned(),
                ty: RuleType::Normal,
                expr: Expr::Choice(
                    Box::new(Expr::Push(
                        Box::new(Expr::Str(String::from("a")))
                    )),
                    Box::new(Expr::Str(String::from("a")))
                )
            },
        ];

        let restored = Rule {
            name: "rule".to_owned(),
            ty: RuleType::Normal,
            expr: Expr::Choice(
                Box::new(Expr::RestoreOnErr(Box::new(Expr::Push(
                    Box::new(Expr::Str(String::from("a")))
                )))),
                Box::new(Expr::Str(String::from("a")))
            )
        };

        let rules_to_map = &populate_rules_to_exprs(&rules);

        assert_eq!(restore_on_err(rules[0].clone(), rules_to_map), restored);
    }
}
<|endoftext|>


<commit_before>use std::{mem, ptr, u32};

/// This is a pretty naive implementation of a BigUint abstracting all
/// math out to a vector of `u32` chunks.
///
/// It can only do 3 things:
/// - Be instantiated from an arbitrary byte slice
/// - Do a division by `u32`, mutating self and returning the remainder.
/// - Check if it's zero.
///
/// Turns out those are all the operations you need to encode base58,
/// or anything else, really.
pub struct BigUint {
    pub chunks: Vec<u32>
}

impl BigUint {
    /// Divide self by `divider`, return the remainder of the operation.
    #[inline(always)]
    pub fn rem_div(&mut self, divider: u32) -> u32 {
        let mut carry = 0u64;

        for chunk in self.chunks.iter_mut() {
            carry = (carry << 32) | *chunk as u64;
            *chunk = (carry / divider as u64) as u32;
            carry %= divider as u64;
        }

        carry as u32
    }

    /// Check if self is zero.
    #[inline(always)]
    pub fn is_zero(&self) -> bool {
        self.chunks.iter().all(|chunk| *chunk == 0)
    }
}

/// Helper function for transmuting 4 bytes into a chunk.
#[inline(always)]
fn bytes_to_u32(bytes: [u8; 4]) -> u32 {
    u32::from_be(unsafe { mem::transmute(bytes) })
}

/// Helper function for transmuting a slice into a chunk.
#[inline(always)]
fn slice_to_u32(slice: &[u8]) -> u32 {
    debug_assert!(slice.len() == 4);

    unsafe {
        let mut bytes: [u8; 4] = mem::uninitialized();

        ptr::copy_nonoverlapping(
            slice.as_ptr(),
            bytes.as_mut_ptr(),
            4
        );

        bytes_to_u32(bytes)
    }
}

impl<'a> From<&'a [u8]> for BigUint {
    #[inline]
    fn from(mut bytes: &[u8]) -> Self {
        let modulo = bytes.len() % 4;

        let mut chunks = Vec::new();

        if modulo > 0 {
            chunks.reserve(bytes.len() / 4 + 1);

            let mut first = [0u8; 4];

            for (r, w) in bytes[..modulo].iter().zip(first[4-modulo..].iter_mut()) {
                *w = *r;
            }

            bytes = &bytes[modulo..];

            chunks.push(bytes_to_u32(first));
        } else {
            chunks.reserve(bytes.len() / 4);
        }

        for slice in bytes.chunks(4) {
            chunks.push(slice_to_u32(slice))
        }

        BigUint {
            chunks: chunks
        }
    }
}

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

    #[test]
    fn big_uint_from_bytes() {
        let bytes: &[u8] = &[
                      0xDE,0xAD,0x00,0x00,0x00,0x13,
            0x37,0xAD,0x00,0x00,0x00,0x00,0xDE,0xAD,
        ];

        let big = BigUint::from(bytes);

        assert_eq!(big.chunks, vec![0x0000DEAD, 0x00000013, 0x37AD0000, 0x0000DEAD]);
    }

    #[test]
    fn big_uint_rem_div() {
        let mut big = BigUint {
            chunks: vec![0x136AD712,0x84322759]
        };

        let rem = big.rem_div(58);
        let merged = ((big.chunks[0] as u64) << 32) | big.chunks[1] as u64;

        assert_eq!(merged, 0x136AD71284322759 / 58);
        assert_eq!(rem as u64, 0x136AD71284322759 % 58);
    }
}
<commit_msg>Drop 0-ed chunks on rem_div<commit_after>use std::{mem, ptr, u32};

/// This is a pretty naive implementation of a BigUint abstracting all
/// math out to a vector of `u32` chunks.
///
/// It can only do 3 things:
/// - Be instantiated from an arbitrary byte slice
/// - Do a division by `u32`, mutating self and returning the remainder.
/// - Check if it's zero.
///
/// Turns out those are all the operations you need to encode base58,
/// or anything else, really.
pub struct BigUint {
    pub chunks: Vec<u32>
}

impl BigUint {
    /// Divide self by `divider`, return the remainder of the operation.
    #[inline(always)]
    pub fn rem_div(&mut self, divider: u32) -> u32 {
        let mut carry = 0u64;

        let mut pop = 0;

        for chunk in self.chunks.iter_mut() {
            carry = (carry << 32) | *chunk as u64;
            *chunk = (carry / divider as u64) as u32;
            carry %= divider as u64;

            if *chunk == 0 {
                pop += 1;
            }
        }

        // TODO: Do this without a loop (might need unsafe)
        for _ in 0..pop {
            self.chunks.remove(0);
        }

        carry as u32
    }

    /// Check if self is zero.
    #[inline(always)]
    pub fn is_zero(&self) -> bool {
        self.chunks.iter().all(|chunk| *chunk == 0)
    }
}

/// Helper function for transmuting 4 bytes into a chunk.
#[inline(always)]
fn bytes_to_u32(bytes: [u8; 4]) -> u32 {
    u32::from_be(unsafe { mem::transmute(bytes) })
}

/// Helper function for transmuting a slice into a chunk.
#[inline(always)]
fn slice_to_u32(slice: &[u8]) -> u32 {
    debug_assert!(slice.len() == 4);

    unsafe {
        let mut bytes: [u8; 4] = mem::uninitialized();

        ptr::copy_nonoverlapping(
            slice.as_ptr(),
            bytes.as_mut_ptr(),
            4
        );

        bytes_to_u32(bytes)
    }
}

impl<'a> From<&'a [u8]> for BigUint {
    #[inline]
    fn from(mut bytes: &[u8]) -> Self {
        let modulo = bytes.len() % 4;

        let mut chunks = Vec::new();

        if modulo > 0 {
            chunks.reserve(bytes.len() / 4 + 1);

            let mut first = [0u8; 4];

            for (r, w) in bytes[..modulo].iter().zip(first[4-modulo..].iter_mut()) {
                *w = *r;
            }

            bytes = &bytes[modulo..];

            chunks.push(bytes_to_u32(first));
        } else {
            chunks.reserve(bytes.len() / 4);
        }

        for slice in bytes.chunks(4) {
            chunks.push(slice_to_u32(slice))
        }

        BigUint {
            chunks: chunks
        }
    }
}

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

    #[test]
    fn big_uint_from_bytes() {
        let bytes: &[u8] = &[
                      0xDE,0xAD,0x00,0x00,0x00,0x13,
            0x37,0xAD,0x00,0x00,0x00,0x00,0xDE,0xAD,
        ];

        let big = BigUint::from(bytes);

        assert_eq!(big.chunks, vec![0x0000DEAD, 0x00000013, 0x37AD0000, 0x0000DEAD]);
    }

    #[test]
    fn big_uint_rem_div() {
        let mut big = BigUint {
            chunks: vec![0x136AD712,0x84322759]
        };

        let rem = big.rem_div(58);
        let merged = ((big.chunks[0] as u64) << 32) | big.chunks[1] as u64;

        assert_eq!(merged, 0x136AD71284322759 / 58);
        assert_eq!(rem as u64, 0x136AD71284322759 % 58);
    }
}
<|endoftext|>


<commit_before>use object_id::ObjectId;

pub struct Branch {
    pub name: String,
    pub canonical_name: String,
    pub tip_id: ObjectId,
    pub tracked_branch: Option<Box<Branch>>,
    pub is_remote: bool,
    pub is_detached: bool,
}

impl Branch {
    pub fn is_tracking(&self) -> bool {
        self.tracked_branch.is_some()
    }
}<commit_msg>Implement branch::tip() for finding the branch tip commit<commit_after>use object_id::ObjectId;
use object_database::find_object_by_id;
use error::CorruptRepository;
use error::GitError;
use repository::Repository;
use commit::Commit;
use commit;
use eobject::ECommit;
use commit_sort_strategy::MostRecent;
use commit_filter::CommitFilter;

pub struct Branch {
    pub name: String,
    pub canonical_name: String,
    pub tip_id: ObjectId,
    pub tracked_branch: Option<Box<Branch>>,
    pub is_remote: bool,
    pub is_detached: bool,
}

impl Branch {
    pub fn is_tracking(&self) -> bool {
        self.tracked_branch.is_some()
    }
}

pub fn tip(repository: &Repository, branch: &Branch) -> Result<Commit, GitError> {
    match find_object_by_id(repository, &branch.tip_id) {
        Ok(box ECommit(c)) => Ok(c),
        Err(e) => Err(e),
        _ => Err(CorruptRepository("Could not find the commit the branch points to")),
    }
}
}<|endoftext|>


<commit_before>use std::cmp;
use std::iter;
use std::io::{self, Read, BufRead};

pub struct BufReader<R> {
    inner: R,
    buf: Vec<u8>,
    pos: usize,
    cap: usize,
}

const INIT_BUFFER_SIZE: usize = 4096;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;

impl<R: Read> BufReader<R> {
    #[inline]
    pub fn new(rdr: R) -> BufReader<R> {
        BufReader::with_capacity(rdr, INIT_BUFFER_SIZE)
    }

    #[inline]
    pub fn with_capacity(rdr: R, cap: usize) -> BufReader<R> {
        let mut buf = Vec::with_capacity(cap);
        buf.extend(iter::repeat(0).take(cap));
        BufReader {
            inner: rdr,
            buf: buf,
            pos: 0,
            cap: 0,
        }
    }

    #[inline]
    pub fn get_ref(&self) -> &R { &self.inner }

    #[inline]
    pub fn get_mut(&mut self) -> &mut R { &mut self.inner }

    #[inline]
    pub fn get_buf(&self) -> &[u8] {
        if self.pos < self.cap {
            debug!("slicing {:?}", (self.pos, self.cap, self.buf.len()));
            &self.buf[self.pos..self.cap]
        } else {
            &[]
        }
    }

    #[inline]
    pub fn into_inner(self) -> R { self.inner }

    #[inline]
    pub fn read_into_buf(&mut self) -> io::Result<usize> {
        self.maybe_reserve();
        let v = &mut self.buf;
        if self.cap < v.capacity() {
            let nread = try!(self.inner.read(&mut v[self.cap..]));
            self.cap += nread;
            Ok(nread)
        } else {
            Ok(0)
        }
    }

    #[inline]
    fn maybe_reserve(&mut self) {
        let cap = self.buf.capacity();
        if self.cap == cap {
            self.buf.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
        }
    }
}

impl<R: Read> Read for BufReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if self.cap == self.pos && buf.len() >= self.buf.len() {
            return self.inner.read(buf);
        }
        let nread = {
           let mut rem = try!(self.fill_buf());
           try!(rem.read(buf))
        };
        self.consume(nread);
        Ok(nread)
    }
}

impl<R: Read> BufRead for BufReader<R> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        if self.pos == self.cap {
            self.cap = try!(self.inner.read(&mut self.buf));
            self.pos = 0;
        }
        Ok(&self.buf[self.pos..self.cap])
    }

    #[inline]
    fn consume(&mut self, amt: usize) {
        self.pos = cmp::min(self.pos + amt, self.cap);
        if self.pos == self.cap {
            self.pos = 0;
            self.cap = 0;
        }
    }
}

#[cfg(test)]
mod tests {

    use std::io::{self, Read, BufRead};
    use super::BufReader;

    struct SlowRead(u8);

    impl Read for SlowRead {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            let state = self.0;
            self.0 += 1;
            (&match state % 3 {
                0 => b"foo",
                1 => b"bar",
                _ => b"baz",
            }[..]).read(buf)
        }
    }

    #[test]
    fn test_consume_and_get_buf() {
        let mut rdr = BufReader::new(SlowRead(0));
        rdr.read_into_buf().unwrap();
        rdr.consume(1);
        assert_eq!(rdr.get_buf(), b"oo");
        rdr.read_into_buf().unwrap();
        rdr.read_into_buf().unwrap();
        assert_eq!(rdr.get_buf(), b"oobarbaz");
        rdr.consume(5);
        assert_eq!(rdr.get_buf(), b"baz");
        rdr.consume(3);
        assert_eq!(rdr.get_buf(), b"");
        assert_eq!(rdr.pos, 0);
        assert_eq!(rdr.cap, 0);
    }
}
<commit_msg>fix(buffer): zero out new capacity when buffer grows<commit_after>use std::cmp;
use std::iter;
use std::io::{self, Read, BufRead};

pub struct BufReader<R> {
    inner: R,
    buf: Vec<u8>,
    pos: usize,
    cap: usize,
}

const INIT_BUFFER_SIZE: usize = 4096;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;

impl<R: Read> BufReader<R> {
    #[inline]
    pub fn new(rdr: R) -> BufReader<R> {
        BufReader::with_capacity(rdr, INIT_BUFFER_SIZE)
    }

    #[inline]
    pub fn with_capacity(rdr: R, cap: usize) -> BufReader<R> {
        let mut buf = Vec::with_capacity(cap);
        buf.extend(iter::repeat(0).take(cap));
        BufReader {
            inner: rdr,
            buf: buf,
            pos: 0,
            cap: 0,
        }
    }

    #[inline]
    pub fn get_ref(&self) -> &R { &self.inner }

    #[inline]
    pub fn get_mut(&mut self) -> &mut R { &mut self.inner }

    #[inline]
    pub fn get_buf(&self) -> &[u8] {
        if self.pos < self.cap {
            debug!("slicing {:?}", (self.pos, self.cap, self.buf.len()));
            &self.buf[self.pos..self.cap]
        } else {
            &[]
        }
    }

    #[inline]
    pub fn into_inner(self) -> R { self.inner }

    #[inline]
    pub fn read_into_buf(&mut self) -> io::Result<usize> {
        self.maybe_reserve();
        let v = &mut self.buf;
        if self.cap < v.capacity() {
            let nread = try!(self.inner.read(&mut v[self.cap..]));
            self.cap += nread;
            Ok(nread)
        } else {
            Ok(0)
        }
    }

    #[inline]
    fn maybe_reserve(&mut self) {
        let cap = self.buf.capacity();
        if self.cap == cap {
            self.buf.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
            let new = self.buf.capacity() - self.buf.len();
            self.buf.extend(iter::repeat(0).take(new));
        }
    }
}

impl<R: Read> Read for BufReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if self.cap == self.pos && buf.len() >= self.buf.len() {
            return self.inner.read(buf);
        }
        let nread = {
           let mut rem = try!(self.fill_buf());
           try!(rem.read(buf))
        };
        self.consume(nread);
        Ok(nread)
    }
}

impl<R: Read> BufRead for BufReader<R> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        if self.pos == self.cap {
            self.cap = try!(self.inner.read(&mut self.buf));
            self.pos = 0;
        }
        Ok(&self.buf[self.pos..self.cap])
    }

    #[inline]
    fn consume(&mut self, amt: usize) {
        self.pos = cmp::min(self.pos + amt, self.cap);
        if self.pos == self.cap {
            self.pos = 0;
            self.cap = 0;
        }
    }
}

#[cfg(test)]
mod tests {

    use std::io::{self, Read, BufRead};
    use super::BufReader;

    struct SlowRead(u8);

    impl Read for SlowRead {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            let state = self.0;
            self.0 += 1;
            (&match state % 3 {
                0 => b"foo",
                1 => b"bar",
                _ => b"baz",
            }[..]).read(buf)
        }
    }

    #[test]
    fn test_consume_and_get_buf() {
        let mut rdr = BufReader::new(SlowRead(0));
        rdr.read_into_buf().unwrap();
        rdr.consume(1);
        assert_eq!(rdr.get_buf(), b"oo");
        rdr.read_into_buf().unwrap();
        rdr.read_into_buf().unwrap();
        assert_eq!(rdr.get_buf(), b"oobarbaz");
        rdr.consume(5);
        assert_eq!(rdr.get_buf(), b"baz");
        rdr.consume(3);
        assert_eq!(rdr.get_buf(), b"");
        assert_eq!(rdr.pos, 0);
        assert_eq!(rdr.cap, 0);
    }
}
<|endoftext|>


<commit_before>use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;

#[allow(clippy::module_name_repetitions)]
#[derive(Copy, Clone)]
pub struct UselessVec {
    pub too_large_for_stack: u64,
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for usage of `&vec![..]` when using `&[..]` would
    /// be possible.
    ///
    /// ### Why is this bad?
    /// This is less efficient.
    ///
    /// ### Example
    /// ```rust
    /// # fn foo(my_vec: &[u8]) {}
    ///
    /// // Bad
    /// foo(&vec![1, 2]);
    ///
    /// // Good
    /// foo(&[1, 2]);
    /// ```
    pub USELESS_VEC,
    perf,
    "useless `vec!`"
}

impl_lint_pass!(UselessVec => [USELESS_VEC]);

impl<'tcx> LateLintPass<'tcx> for UselessVec {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // search for `&vec![_]` expressions where the adjusted type is `&[_]`
        if_chain! {
            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
            if let ty::Slice(..) = ty.kind();
            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
            if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
            then {
                self.check_vec_macro(cx, &vec_args, mutability, expr.span);
            }
        }

        // search for `for _ in vec![…]`
        if_chain! {
            if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr);
            if let Some(vec_args) = higher::VecArgs::hir(cx, arg);
            if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
            then {
                // report the error around the `vec!` not inside `<std macros>:`
                let span = arg.span
                    .ctxt()
                    .outer_expn_data()
                    .call_site
                    .ctxt()
                    .outer_expn_data()
                    .call_site;
                self.check_vec_macro(cx, &vec_args, Mutability::Not, span);
            }
        }
    }
}

impl UselessVec {
    fn check_vec_macro<'tcx>(
        self,
        cx: &LateContext<'tcx>,
        vec_args: &higher::VecArgs<'tcx>,
        mutability: Mutability,
        span: Span,
    ) {
        let mut applicability = Applicability::MachineApplicable;
        let snippet = match *vec_args {
            higher::VecArgs::Repeat(elem, len) => {
                if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
                    #[allow(clippy::cast_possible_truncation)]
                    if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
                        return;
                    }

                    match mutability {
                        Mutability::Mut => {
                            format!(
                                "&mut [{}; {}]",
                                snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
                                snippet_with_applicability(cx, len.span, "len", &mut applicability)
                            )
                        },
                        Mutability::Not => {
                            format!(
                                "&[{}; {}]",
                                snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
                                snippet_with_applicability(cx, len.span, "len", &mut applicability)
                            )
                        },
                    }
                } else {
                    return;
                }
            },
            higher::VecArgs::Vec(args) => {
                if let Some(last) = args.iter().last() {
                    #[allow(clippy::cast_possible_truncation)]
                    if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
                        return;
                    }
                    let span = args[0].span.to(last.span);

                    match mutability {
                        Mutability::Mut => {
                            format!(
                                "&mut [{}]",
                                snippet_with_applicability(cx, span, "..", &mut applicability)
                            )
                        },
                        Mutability::Not => {
                            format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
                        },
                    }
                } else {
                    match mutability {
                        Mutability::Mut => "&mut []".into(),
                        Mutability::Not => "&[]".into(),
                    }
                }
            },
        };

        span_lint_and_sugg(
            cx,
            USELESS_VEC,
            span,
            "useless use of `vec!`",
            "you can use a slice directly",
            snippet,
            applicability,
        );
    }
}

fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
    let ty = cx.typeck_results().expr_ty_adjusted(expr);
    cx.layout_of(ty).map_or(0, |l| l.size.bytes())
}

/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
fn vec_type(ty: Ty<'_>) -> Ty<'_> {
    if let ty::Adt(_, substs) = ty.kind() {
        substs.type_at(0)
    } else {
        panic!("The type of `vec!` is a not a struct?");
    }
}
<commit_msg>Fix clippy with for loop span change<commit_after>use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;

#[allow(clippy::module_name_repetitions)]
#[derive(Copy, Clone)]
pub struct UselessVec {
    pub too_large_for_stack: u64,
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for usage of `&vec![..]` when using `&[..]` would
    /// be possible.
    ///
    /// ### Why is this bad?
    /// This is less efficient.
    ///
    /// ### Example
    /// ```rust
    /// # fn foo(my_vec: &[u8]) {}
    ///
    /// // Bad
    /// foo(&vec![1, 2]);
    ///
    /// // Good
    /// foo(&[1, 2]);
    /// ```
    pub USELESS_VEC,
    perf,
    "useless `vec!`"
}

impl_lint_pass!(UselessVec => [USELESS_VEC]);

impl<'tcx> LateLintPass<'tcx> for UselessVec {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // search for `&vec![_]` expressions where the adjusted type is `&[_]`
        if_chain! {
            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
            if let ty::Slice(..) = ty.kind();
            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
            if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
            then {
                self.check_vec_macro(cx, &vec_args, mutability, expr.span);
            }
        }

        // search for `for _ in vec![…]`
        if_chain! {
            if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr);
            if let Some(vec_args) = higher::VecArgs::hir(cx, arg);
            if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
            then {
                // report the error around the `vec!` not inside `<std macros>:`
                let span = arg.span.ctxt().outer_expn_data().call_site;
                self.check_vec_macro(cx, &vec_args, Mutability::Not, span);
            }
        }
    }
}

impl UselessVec {
    fn check_vec_macro<'tcx>(
        self,
        cx: &LateContext<'tcx>,
        vec_args: &higher::VecArgs<'tcx>,
        mutability: Mutability,
        span: Span,
    ) {
        let mut applicability = Applicability::MachineApplicable;
        let snippet = match *vec_args {
            higher::VecArgs::Repeat(elem, len) => {
                if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
                    #[allow(clippy::cast_possible_truncation)]
                    if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
                        return;
                    }

                    match mutability {
                        Mutability::Mut => {
                            format!(
                                "&mut [{}; {}]",
                                snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
                                snippet_with_applicability(cx, len.span, "len", &mut applicability)
                            )
                        },
                        Mutability::Not => {
                            format!(
                                "&[{}; {}]",
                                snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
                                snippet_with_applicability(cx, len.span, "len", &mut applicability)
                            )
                        },
                    }
                } else {
                    return;
                }
            },
            higher::VecArgs::Vec(args) => {
                if let Some(last) = args.iter().last() {
                    #[allow(clippy::cast_possible_truncation)]
                    if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
                        return;
                    }
                    let span = args[0].span.to(last.span);

                    match mutability {
                        Mutability::Mut => {
                            format!(
                                "&mut [{}]",
                                snippet_with_applicability(cx, span, "..", &mut applicability)
                            )
                        },
                        Mutability::Not => {
                            format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
                        },
                    }
                } else {
                    match mutability {
                        Mutability::Mut => "&mut []".into(),
                        Mutability::Not => "&[]".into(),
                    }
                }
            },
        };

        span_lint_and_sugg(
            cx,
            USELESS_VEC,
            span,
            "useless use of `vec!`",
            "you can use a slice directly",
            snippet,
            applicability,
        );
    }
}

fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
    let ty = cx.typeck_results().expr_ty_adjusted(expr);
    cx.layout_of(ty).map_or(0, |l| l.size.bytes())
}

/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
fn vec_type(ty: Ty<'_>) -> Ty<'_> {
    if let ty::Adt(_, substs) = ty.kind() {
        substs.type_at(0)
    } else {
        panic!("The type of `vec!` is a not a struct?");
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! An invalidation processor for style changes due to state and attribute
//! changes.

use Atom;
use context::{QuirksMode, SharedStyleContext};
use data::ElementData;
use dom::TElement;
use element_state::{ElementState, IN_VISITED_OR_UNVISITED_STATE};
use invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
use invalidation::element::invalidation_map::*;
use invalidation::element::invalidator::{InvalidationVector, Invalidation, InvalidationProcessor};
use invalidation::element::restyle_hints::*;
use selector_map::SelectorMap;
use selector_parser::Snapshot;
use selectors::NthIndexCache;
use selectors::attr::CaseSensitivity;
use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode};
use selectors::matching::matches_selector;
use smallvec::SmallVec;

#[derive(Debug, PartialEq)]
enum VisitedDependent {
    Yes,
    No,
}

/// The collector implementation.
struct Collector<'a, 'b: 'a, E>
where
    E: TElement,
{
    element: E,
    wrapper: ElementWrapper<'b, E>,
    nth_index_cache: Option<&'a mut NthIndexCache>,
    snapshot: &'a Snapshot,
    quirks_mode: QuirksMode,
    lookup_element: E,
    removed_id: Option<&'a Atom>,
    added_id: Option<&'a Atom>,
    classes_removed: &'a SmallVec<[Atom; 8]>,
    classes_added: &'a SmallVec<[Atom; 8]>,
    state_changes: ElementState,
    descendant_invalidations: &'a mut InvalidationVector,
    sibling_invalidations: &'a mut InvalidationVector,
    invalidates_self: bool,
}

/// An invalidation processor for style changes due to state and attribute
/// changes.
pub struct StateAndAttrInvalidationProcessor;

impl<E> InvalidationProcessor<E> for StateAndAttrInvalidationProcessor
where
    E: TElement,
{
    fn collect_invalidations(
        &self,
        element: E,
        mut data: Option<&mut ElementData>,
        nth_index_cache: Option<&mut NthIndexCache>,
        shared_context: &SharedStyleContext,
        descendant_invalidations: &mut InvalidationVector,
        sibling_invalidations: &mut InvalidationVector,
    ) -> bool {
        debug_assert!(element.has_snapshot(), "Why bothering?");
        debug_assert!(data.is_some(), "How exactly?");

        let wrapper =
            ElementWrapper::new(element, &*shared_context.snapshot_map);

        let state_changes = wrapper.state_changes();
        let snapshot = wrapper.snapshot().expect("has_snapshot lied");

        if !snapshot.has_attrs() && state_changes.is_empty() {
            return false;
        }

        // If we are sensitive to visitedness and the visited state changed, we
        // force a restyle here. Matching doesn't depend on the actual visited
        // state at all, so we can't look at matching results to decide what to
        // do for this case.
        if state_changes.intersects(IN_VISITED_OR_UNVISITED_STATE) {
            trace!(" > visitedness change, force subtree restyle");
            // We can't just return here because there may also be attribute
            // changes as well that imply additional hints.
            let data = data.as_mut().unwrap();
            data.hint.insert(RestyleHint::restyle_subtree());
        }

        let mut classes_removed = SmallVec::<[Atom; 8]>::new();
        let mut classes_added = SmallVec::<[Atom; 8]>::new();
        if snapshot.class_changed() {
            // TODO(emilio): Do this more efficiently!
            snapshot.each_class(|c| {
                if !element.has_class(c, CaseSensitivity::CaseSensitive) {
                    classes_removed.push(c.clone())
                }
            });

            element.each_class(|c| {
                if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
                    classes_added.push(c.clone())
                }
            })
        }

        let mut id_removed = None;
        let mut id_added = None;
        if snapshot.id_changed() {
            let old_id = snapshot.id_attr();
            let current_id = element.get_id();

            if old_id != current_id {
                id_removed = old_id;
                id_added = current_id;
            }
        }

        let lookup_element =
            if element.implemented_pseudo_element().is_some() {
                element.pseudo_element_originating_element().unwrap()
            } else {
                element
            };

        let invalidated_self = {
            let mut collector = Collector {
                wrapper,
                lookup_element,
                nth_index_cache,
                state_changes,
                element,
                snapshot: &snapshot,
                quirks_mode: shared_context.quirks_mode(),
                removed_id: id_removed.as_ref(),
                added_id: id_added.as_ref(),
                classes_removed: &classes_removed,
                classes_added: &classes_added,
                descendant_invalidations,
                sibling_invalidations,
                invalidates_self: false,
            };

            shared_context.stylist.each_invalidation_map(|invalidation_map| {
                collector.collect_dependencies_in_invalidation_map(invalidation_map);
            });

            // TODO(emilio): Consider storing dependencies from the UA sheet in
            // a different map. If we do that, we can skip the stuff on the
            // shared stylist iff cut_off_inheritance is true, and we can look
            // just at that map.
            let _cut_off_inheritance =
                element.each_xbl_stylist(|stylist| {
                    // FIXME(emilio): Replace with assert / remove when we
                    // figure out what to do with the quirks mode mismatches
                    // (that is, when bug 1406875 is properly fixed).
                    collector.quirks_mode = stylist.quirks_mode();
                    stylist.each_invalidation_map(|invalidation_map| {
                        collector.collect_dependencies_in_invalidation_map(invalidation_map);
                    });
                });

            collector.invalidates_self
        };

        if invalidated_self {
            if let Some(ref mut data) = data {
                data.hint.insert(RESTYLE_SELF);
            }
        }

        invalidated_self
    }

    fn should_process_descendants(
        &self,
        _element: E,
        data: Option<&mut ElementData>,
    ) -> bool {
        let data = match data {
            None => return false,
            Some(ref data) => data,
        };

        // FIXME(emilio): should check only RESTYLE_DESCENDANTS.
        //
        // Also, could probably return false if data.styles.is_display_none()
        // returns true.
        !data.hint.contains_subtree()
    }

    fn recursion_limit_exceeded(
        &self,
        _element: E,
        data: Option<&mut ElementData>,
    ) {
        if let Some(data) = data {
            data.hint.insert(RESTYLE_DESCENDANTS);
        }
    }

    fn invalidated_descendants(
        &self,
        element: E,
        data: Option<&mut ElementData>,
        child: E,
    ) {
        if child.get_data().is_none() {
            return;
        }

        if data.as_ref().map_or(true, |d| d.styles.is_display_none()) {
            return;
        }

        // The child may not be a flattened tree child of the current element,
        // but may be arbitrarily deep.
        //
        // Since we keep the traversal flags in terms of the flattened tree,
        // we need to propagate it as appropriate.
        let mut current = child.traversal_parent();
        while let Some(parent) = current.take() {
            unsafe { parent.set_dirty_descendants() };
            current = parent.traversal_parent();

            if parent == element {
                break;
            }
        }
    }

    fn invalidated_self(
        &self,
        _element: E,
        data: Option<&mut ElementData>,
    ) {
        if let Some(data) = data {
            data.hint.insert(RESTYLE_SELF);
        }
    }
}

impl<'a, 'b, E> Collector<'a, 'b, E>
where
    E: TElement,
{
    fn collect_dependencies_in_invalidation_map(
        &mut self,
        map: &InvalidationMap,
    ) {
        let quirks_mode = self.quirks_mode;
        let removed_id = self.removed_id;
        if let Some(ref id) = removed_id {
            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
                for dep in deps {
                    self.scan_dependency(dep, VisitedDependent::No);
                }
            }
        }

        let added_id = self.added_id;
        if let Some(ref id) = added_id {
            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
                for dep in deps {
                    self.scan_dependency(dep, VisitedDependent::No);
                }
            }
        }

        for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
            if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
                for dep in deps {
                    self.scan_dependency(dep, VisitedDependent::No);
                }
            }
        }

        let should_examine_attribute_selector_map =
            self.snapshot.other_attr_changed() ||
            (self.snapshot.class_changed() && map.has_class_attribute_selectors) ||
            (self.snapshot.id_changed() && map.has_id_attribute_selectors);

        if should_examine_attribute_selector_map {
            self.collect_dependencies_in_map(
                &map.other_attribute_affecting_selectors
            )
        }

        let state_changes = self.state_changes;
        if !state_changes.is_empty() {
            self.collect_state_dependencies(
                &map.state_affecting_selectors,
                state_changes,
            )
        }
    }

    fn collect_dependencies_in_map(
        &mut self,
        map: &SelectorMap<Dependency>,
    ) {
        map.lookup_with_additional(
            self.lookup_element,
            self.quirks_mode,
            self.removed_id,
            self.classes_removed,
            &mut |dependency| {
                self.scan_dependency(dependency, VisitedDependent::No);
                true
            },
        );
    }

    fn collect_state_dependencies(
        &mut self,
        map: &SelectorMap<StateDependency>,
        state_changes: ElementState,
    ) {
        map.lookup_with_additional(
            self.lookup_element,
            self.quirks_mode,
            self.removed_id,
            self.classes_removed,
            &mut |dependency| {
                if !dependency.state.intersects(state_changes) {
                    return true;
                }
                let visited_dependent =
                    if dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE) {
                        VisitedDependent::Yes
                    } else {
                        VisitedDependent::No
                    };
                self.scan_dependency(&dependency.dep, visited_dependent);
                true
            },
        );
    }

    /// Check whether a dependency should be taken into account, using a given
    /// visited handling mode.
    fn check_dependency(
        &mut self,
        visited_handling_mode: VisitedHandlingMode,
        dependency: &Dependency,
        relevant_link_found: &mut bool,
    ) -> bool {
        let (matches_now, relevant_link_found_now) = {
            let mut context = MatchingContext::new_for_visited(
                MatchingMode::Normal,
                None,
                self.nth_index_cache.as_mut().map(|c| &mut **c),
                visited_handling_mode,
                self.quirks_mode,
            );

            let matches_now = matches_selector(
                &dependency.selector,
                dependency.selector_offset,
                None,
                &self.element,
                &mut context,
                &mut |_, _| {},
            );

            (matches_now, context.relevant_link_found)
        };

        let (matched_then, relevant_link_found_then) = {
            let mut context = MatchingContext::new_for_visited(
                MatchingMode::Normal,
                None,
                self.nth_index_cache.as_mut().map(|c| &mut **c),
                visited_handling_mode,
                self.quirks_mode,
            );

            let matched_then = matches_selector(
                &dependency.selector,
                dependency.selector_offset,
                None,
                &self.wrapper,
                &mut context,
                &mut |_, _| {},
            );

            (matched_then, context.relevant_link_found)
        };

        *relevant_link_found = relevant_link_found_now;

        // Check for mismatches in both the match result and also the status
        // of whether a relevant link was found.
        matched_then != matches_now ||
            relevant_link_found_now != relevant_link_found_then
    }

    fn scan_dependency(
        &mut self,
        dependency: &Dependency,
        is_visited_dependent: VisitedDependent,
    ) {
        debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {:?})",
               self.element,
               dependency,
               is_visited_dependent);

        if !self.dependency_may_be_relevant(dependency) {
            return;
        }

        let mut relevant_link_found = false;

        let should_account_for_dependency = self.check_dependency(
            VisitedHandlingMode::AllLinksUnvisited,
            dependency,
            &mut relevant_link_found,
        );

        if should_account_for_dependency {
            return self.note_dependency(dependency);
        }

        // If there is a relevant link, then we also matched in visited
        // mode.
        //
        // Match again in this mode to ensure this also matches.
        //
        // Note that we never actually match directly against the element's true
        // visited state at all, since that would expose us to timing attacks.
        //
        // The matching process only considers the relevant link state and
        // visited handling mode when deciding if visited matches.  Instead, we
        // are rematching here in case there is some :visited selector whose
        // matching result changed for some other state or attribute change of
        // this element (for example, for things like [foo]:visited).
        //
        // NOTE: This thing is actually untested because testing it is flaky,
        // see the tests that were added and then backed out in bug 1328509.
        if is_visited_dependent == VisitedDependent::Yes && relevant_link_found {
            let should_account_for_dependency = self.check_dependency(
                VisitedHandlingMode::RelevantLinkVisited,
                dependency,
                &mut false,
            );

            if should_account_for_dependency {
                return self.note_dependency(dependency);
            }
        }
    }

    fn note_dependency(&mut self, dependency: &Dependency) {
        if dependency.affects_self() {
            self.invalidates_self = true;
        }

        if dependency.affects_descendants() {
            debug_assert_ne!(dependency.selector_offset, 0);
            debug_assert!(!dependency.affects_later_siblings());
            self.descendant_invalidations.push(Invalidation::new(
                dependency.selector.clone(),
                dependency.selector_offset,
            ));
        } else if dependency.affects_later_siblings() {
            debug_assert_ne!(dependency.selector_offset, 0);
            self.sibling_invalidations.push(Invalidation::new(
                dependency.selector.clone(),
                dependency.selector_offset,
            ));
        }
    }

    /// Returns whether `dependency` may cause us to invalidate the style of
    /// more elements than what we've already invalidated.
    fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
        if dependency.affects_descendants() || dependency.affects_later_siblings() {
            return true;
        }

        debug_assert!(dependency.affects_self());
        !self.invalidates_self
    }
}
<commit_msg>Be more precise in should_process_descendants.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! An invalidation processor for style changes due to state and attribute
//! changes.

use Atom;
use context::{QuirksMode, SharedStyleContext};
use data::ElementData;
use dom::TElement;
use element_state::{ElementState, IN_VISITED_OR_UNVISITED_STATE};
use invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
use invalidation::element::invalidation_map::*;
use invalidation::element::invalidator::{InvalidationVector, Invalidation, InvalidationProcessor};
use invalidation::element::restyle_hints::*;
use selector_map::SelectorMap;
use selector_parser::Snapshot;
use selectors::NthIndexCache;
use selectors::attr::CaseSensitivity;
use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode};
use selectors::matching::matches_selector;
use smallvec::SmallVec;

#[derive(Debug, PartialEq)]
enum VisitedDependent {
    Yes,
    No,
}

/// The collector implementation.
struct Collector<'a, 'b: 'a, E>
where
    E: TElement,
{
    element: E,
    wrapper: ElementWrapper<'b, E>,
    nth_index_cache: Option<&'a mut NthIndexCache>,
    snapshot: &'a Snapshot,
    quirks_mode: QuirksMode,
    lookup_element: E,
    removed_id: Option<&'a Atom>,
    added_id: Option<&'a Atom>,
    classes_removed: &'a SmallVec<[Atom; 8]>,
    classes_added: &'a SmallVec<[Atom; 8]>,
    state_changes: ElementState,
    descendant_invalidations: &'a mut InvalidationVector,
    sibling_invalidations: &'a mut InvalidationVector,
    invalidates_self: bool,
}

/// An invalidation processor for style changes due to state and attribute
/// changes.
pub struct StateAndAttrInvalidationProcessor;

impl<E> InvalidationProcessor<E> for StateAndAttrInvalidationProcessor
where
    E: TElement,
{
    fn collect_invalidations(
        &self,
        element: E,
        mut data: Option<&mut ElementData>,
        nth_index_cache: Option<&mut NthIndexCache>,
        shared_context: &SharedStyleContext,
        descendant_invalidations: &mut InvalidationVector,
        sibling_invalidations: &mut InvalidationVector,
    ) -> bool {
        debug_assert!(element.has_snapshot(), "Why bothering?");
        debug_assert!(data.is_some(), "How exactly?");

        let wrapper =
            ElementWrapper::new(element, &*shared_context.snapshot_map);

        let state_changes = wrapper.state_changes();
        let snapshot = wrapper.snapshot().expect("has_snapshot lied");

        if !snapshot.has_attrs() && state_changes.is_empty() {
            return false;
        }

        // If we are sensitive to visitedness and the visited state changed, we
        // force a restyle here. Matching doesn't depend on the actual visited
        // state at all, so we can't look at matching results to decide what to
        // do for this case.
        if state_changes.intersects(IN_VISITED_OR_UNVISITED_STATE) {
            trace!(" > visitedness change, force subtree restyle");
            // We can't just return here because there may also be attribute
            // changes as well that imply additional hints.
            let data = data.as_mut().unwrap();
            data.hint.insert(RestyleHint::restyle_subtree());
        }

        let mut classes_removed = SmallVec::<[Atom; 8]>::new();
        let mut classes_added = SmallVec::<[Atom; 8]>::new();
        if snapshot.class_changed() {
            // TODO(emilio): Do this more efficiently!
            snapshot.each_class(|c| {
                if !element.has_class(c, CaseSensitivity::CaseSensitive) {
                    classes_removed.push(c.clone())
                }
            });

            element.each_class(|c| {
                if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
                    classes_added.push(c.clone())
                }
            })
        }

        let mut id_removed = None;
        let mut id_added = None;
        if snapshot.id_changed() {
            let old_id = snapshot.id_attr();
            let current_id = element.get_id();

            if old_id != current_id {
                id_removed = old_id;
                id_added = current_id;
            }
        }

        let lookup_element =
            if element.implemented_pseudo_element().is_some() {
                element.pseudo_element_originating_element().unwrap()
            } else {
                element
            };

        let invalidated_self = {
            let mut collector = Collector {
                wrapper,
                lookup_element,
                nth_index_cache,
                state_changes,
                element,
                snapshot: &snapshot,
                quirks_mode: shared_context.quirks_mode(),
                removed_id: id_removed.as_ref(),
                added_id: id_added.as_ref(),
                classes_removed: &classes_removed,
                classes_added: &classes_added,
                descendant_invalidations,
                sibling_invalidations,
                invalidates_self: false,
            };

            shared_context.stylist.each_invalidation_map(|invalidation_map| {
                collector.collect_dependencies_in_invalidation_map(invalidation_map);
            });

            // TODO(emilio): Consider storing dependencies from the UA sheet in
            // a different map. If we do that, we can skip the stuff on the
            // shared stylist iff cut_off_inheritance is true, and we can look
            // just at that map.
            let _cut_off_inheritance =
                element.each_xbl_stylist(|stylist| {
                    // FIXME(emilio): Replace with assert / remove when we
                    // figure out what to do with the quirks mode mismatches
                    // (that is, when bug 1406875 is properly fixed).
                    collector.quirks_mode = stylist.quirks_mode();
                    stylist.each_invalidation_map(|invalidation_map| {
                        collector.collect_dependencies_in_invalidation_map(invalidation_map);
                    });
                });

            collector.invalidates_self
        };

        if invalidated_self {
            if let Some(ref mut data) = data {
                data.hint.insert(RESTYLE_SELF);
            }
        }

        invalidated_self
    }

    fn should_process_descendants(
        &self,
        _element: E,
        data: Option<&mut ElementData>,
    ) -> bool {
        let data = match data {
            None => return false,
            Some(ref data) => data,
        };

        !data.styles.is_display_none() &&
            !data.hint.contains(RESTYLE_DESCENDANTS)
    }

    fn recursion_limit_exceeded(
        &self,
        _element: E,
        data: Option<&mut ElementData>,
    ) {
        if let Some(data) = data {
            data.hint.insert(RESTYLE_DESCENDANTS);
        }
    }

    fn invalidated_descendants(
        &self,
        element: E,
        data: Option<&mut ElementData>,
        child: E,
    ) {
        if child.get_data().is_none() {
            return;
        }

        if data.as_ref().map_or(true, |d| d.styles.is_display_none()) {
            return;
        }

        // The child may not be a flattened tree child of the current element,
        // but may be arbitrarily deep.
        //
        // Since we keep the traversal flags in terms of the flattened tree,
        // we need to propagate it as appropriate.
        let mut current = child.traversal_parent();
        while let Some(parent) = current.take() {
            unsafe { parent.set_dirty_descendants() };
            current = parent.traversal_parent();

            if parent == element {
                break;
            }
        }
    }

    fn invalidated_self(
        &self,
        _element: E,
        data: Option<&mut ElementData>,
    ) {
        if let Some(data) = data {
            data.hint.insert(RESTYLE_SELF);
        }
    }
}

impl<'a, 'b, E> Collector<'a, 'b, E>
where
    E: TElement,
{
    fn collect_dependencies_in_invalidation_map(
        &mut self,
        map: &InvalidationMap,
    ) {
        let quirks_mode = self.quirks_mode;
        let removed_id = self.removed_id;
        if let Some(ref id) = removed_id {
            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
                for dep in deps {
                    self.scan_dependency(dep, VisitedDependent::No);
                }
            }
        }

        let added_id = self.added_id;
        if let Some(ref id) = added_id {
            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
                for dep in deps {
                    self.scan_dependency(dep, VisitedDependent::No);
                }
            }
        }

        for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
            if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
                for dep in deps {
                    self.scan_dependency(dep, VisitedDependent::No);
                }
            }
        }

        let should_examine_attribute_selector_map =
            self.snapshot.other_attr_changed() ||
            (self.snapshot.class_changed() && map.has_class_attribute_selectors) ||
            (self.snapshot.id_changed() && map.has_id_attribute_selectors);

        if should_examine_attribute_selector_map {
            self.collect_dependencies_in_map(
                &map.other_attribute_affecting_selectors
            )
        }

        let state_changes = self.state_changes;
        if !state_changes.is_empty() {
            self.collect_state_dependencies(
                &map.state_affecting_selectors,
                state_changes,
            )
        }
    }

    fn collect_dependencies_in_map(
        &mut self,
        map: &SelectorMap<Dependency>,
    ) {
        map.lookup_with_additional(
            self.lookup_element,
            self.quirks_mode,
            self.removed_id,
            self.classes_removed,
            &mut |dependency| {
                self.scan_dependency(dependency, VisitedDependent::No);
                true
            },
        );
    }

    fn collect_state_dependencies(
        &mut self,
        map: &SelectorMap<StateDependency>,
        state_changes: ElementState,
    ) {
        map.lookup_with_additional(
            self.lookup_element,
            self.quirks_mode,
            self.removed_id,
            self.classes_removed,
            &mut |dependency| {
                if !dependency.state.intersects(state_changes) {
                    return true;
                }
                let visited_dependent =
                    if dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE) {
                        VisitedDependent::Yes
                    } else {
                        VisitedDependent::No
                    };
                self.scan_dependency(&dependency.dep, visited_dependent);
                true
            },
        );
    }

    /// Check whether a dependency should be taken into account, using a given
    /// visited handling mode.
    fn check_dependency(
        &mut self,
        visited_handling_mode: VisitedHandlingMode,
        dependency: &Dependency,
        relevant_link_found: &mut bool,
    ) -> bool {
        let (matches_now, relevant_link_found_now) = {
            let mut context = MatchingContext::new_for_visited(
                MatchingMode::Normal,
                None,
                self.nth_index_cache.as_mut().map(|c| &mut **c),
                visited_handling_mode,
                self.quirks_mode,
            );

            let matches_now = matches_selector(
                &dependency.selector,
                dependency.selector_offset,
                None,
                &self.element,
                &mut context,
                &mut |_, _| {},
            );

            (matches_now, context.relevant_link_found)
        };

        let (matched_then, relevant_link_found_then) = {
            let mut context = MatchingContext::new_for_visited(
                MatchingMode::Normal,
                None,
                self.nth_index_cache.as_mut().map(|c| &mut **c),
                visited_handling_mode,
                self.quirks_mode,
            );

            let matched_then = matches_selector(
                &dependency.selector,
                dependency.selector_offset,
                None,
                &self.wrapper,
                &mut context,
                &mut |_, _| {},
            );

            (matched_then, context.relevant_link_found)
        };

        *relevant_link_found = relevant_link_found_now;

        // Check for mismatches in both the match result and also the status
        // of whether a relevant link was found.
        matched_then != matches_now ||
            relevant_link_found_now != relevant_link_found_then
    }

    fn scan_dependency(
        &mut self,
        dependency: &Dependency,
        is_visited_dependent: VisitedDependent,
    ) {
        debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {:?})",
               self.element,
               dependency,
               is_visited_dependent);

        if !self.dependency_may_be_relevant(dependency) {
            return;
        }

        let mut relevant_link_found = false;

        let should_account_for_dependency = self.check_dependency(
            VisitedHandlingMode::AllLinksUnvisited,
            dependency,
            &mut relevant_link_found,
        );

        if should_account_for_dependency {
            return self.note_dependency(dependency);
        }

        // If there is a relevant link, then we also matched in visited
        // mode.
        //
        // Match again in this mode to ensure this also matches.
        //
        // Note that we never actually match directly against the element's true
        // visited state at all, since that would expose us to timing attacks.
        //
        // The matching process only considers the relevant link state and
        // visited handling mode when deciding if visited matches.  Instead, we
        // are rematching here in case there is some :visited selector whose
        // matching result changed for some other state or attribute change of
        // this element (for example, for things like [foo]:visited).
        //
        // NOTE: This thing is actually untested because testing it is flaky,
        // see the tests that were added and then backed out in bug 1328509.
        if is_visited_dependent == VisitedDependent::Yes && relevant_link_found {
            let should_account_for_dependency = self.check_dependency(
                VisitedHandlingMode::RelevantLinkVisited,
                dependency,
                &mut false,
            );

            if should_account_for_dependency {
                return self.note_dependency(dependency);
            }
        }
    }

    fn note_dependency(&mut self, dependency: &Dependency) {
        if dependency.affects_self() {
            self.invalidates_self = true;
        }

        if dependency.affects_descendants() {
            debug_assert_ne!(dependency.selector_offset, 0);
            debug_assert!(!dependency.affects_later_siblings());
            self.descendant_invalidations.push(Invalidation::new(
                dependency.selector.clone(),
                dependency.selector_offset,
            ));
        } else if dependency.affects_later_siblings() {
            debug_assert_ne!(dependency.selector_offset, 0);
            self.sibling_invalidations.push(Invalidation::new(
                dependency.selector.clone(),
                dependency.selector_offset,
            ));
        }
    }

    /// Returns whether `dependency` may cause us to invalidate the style of
    /// more elements than what we've already invalidated.
    fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
        if dependency.affects_descendants() || dependency.affects_later_siblings() {
            return true;
        }

        debug_assert!(dependency.affects_self());
        !self.invalidates_self
    }
}
<|endoftext|>