<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/. */

//! Restyle hints: an optimization to avoid unnecessarily matching selectors.

#[cfg(feature = "gecko")]
use gecko_bindings::structs::nsRestyleHint;
use traversal_flags::TraversalFlags;

bitflags! {
    /// The kind of restyle we need to do for a given element.
    pub flags RestyleHint: u8 {
        /// Do a selector match of the element.
        const RESTYLE_SELF = 1 << 0,

        /// Do a selector match of the element's descendants.
        const RESTYLE_DESCENDANTS = 1 << 1,

        /// Recascade the current element.
        const RECASCADE_SELF = 1 << 2,

        /// Recascade all descendant elements.
        const RECASCADE_DESCENDANTS = 1 << 3,

        /// Replace the style data coming from CSS transitions without updating
        /// any other style data. This hint is only processed in animation-only
        /// traversal which is prior to normal traversal.
        const RESTYLE_CSS_TRANSITIONS = 1 << 4,

        /// Replace the style data coming from CSS animations without updating
        /// any other style data. This hint is only processed in animation-only
        /// traversal which is prior to normal traversal.
        const RESTYLE_CSS_ANIMATIONS = 1 << 5,

        /// Don't re-run selector-matching on the element, only the style
        /// attribute has changed, and this change didn't have any other
        /// dependencies.
        const RESTYLE_STYLE_ATTRIBUTE = 1 << 6,

        /// Replace the style data coming from SMIL animations without updating
        /// any other style data. This hint is only processed in animation-only
        /// traversal which is prior to normal traversal.
        const RESTYLE_SMIL = 1 << 7,
    }
}

impl RestyleHint {
    /// Creates a new `RestyleHint` indicating that the current element and all
    /// its descendants must be fully restyled.
    pub fn restyle_subtree() -> Self {
        RESTYLE_SELF | RESTYLE_DESCENDANTS
    }

    /// Creates a new `RestyleHint` indicating that the current element and all
    /// its descendants must be recascaded.
    pub fn recascade_subtree() -> Self {
        RECASCADE_SELF | RECASCADE_DESCENDANTS
    }

    /// Returns whether this hint invalidates the element and all its
    /// descendants.
    pub fn contains_subtree(&self) -> bool {
        self.contains(RESTYLE_SELF | RESTYLE_DESCENDANTS)
    }

    /// Returns whether we need to restyle this element.
    pub fn has_non_animation_invalidations(&self) -> bool {
        self.intersects(
            RESTYLE_SELF |
            RECASCADE_SELF |
            (Self::replacements() & !Self::for_animations())
        )
    }

    /// Propagates this restyle hint to a child element.
    pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
        use std::mem;

        // In the middle of an animation only restyle, we don't need to
        // propagate any restyle hints, and we need to remove ourselves.
        if traversal_flags.for_animation_only() {
            self.remove_animation_hints();
            return Self::empty();
        }

        debug_assert!(!self.has_animation_hint(),
                      "There should not be any animation restyle hints \
                       during normal traversal");

        // Else we should clear ourselves, and return the propagated hint.
        mem::replace(self, Self::empty())
            .propagate_for_non_animation_restyle()
    }

    /// Returns a new `CascadeHint` appropriate for children of the current
    /// element.
    fn propagate_for_non_animation_restyle(&self) -> Self {
        if self.contains(RESTYLE_DESCENDANTS) {
            return Self::restyle_subtree()
        }
        if self.contains(RECASCADE_DESCENDANTS) {
            return Self::recascade_subtree()
        }
        Self::empty()
    }

    /// Creates a new `RestyleHint` that indicates the element must be
    /// recascaded.
    pub fn recascade_self() -> Self {
        RECASCADE_SELF
    }

    /// Returns a hint that contains all the replacement hints.
    pub fn replacements() -> Self {
        RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
    }

    /// The replacements for the animation cascade levels.
    #[inline]
    pub fn for_animations() -> Self {
        RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
    }

    /// Returns whether the hint specifies that the currently element must be
    /// recascaded.
    pub fn has_recascade_self(&self) -> bool {
        self.contains(RECASCADE_SELF)
    }

    /// Returns whether the hint specifies that an animation cascade level must
    /// be replaced.
    #[inline]
    pub fn has_animation_hint(&self) -> bool {
        self.intersects(Self::for_animations())
    }

    /// Returns whether the hint specifies that an animation cascade level must
    /// be replaced.
    #[inline]
    pub fn has_animation_hint_or_recascade(&self) -> bool {
        self.intersects(Self::for_animations() | RECASCADE_SELF)
    }

    /// Returns whether the hint specifies some restyle work other than an
    /// animation cascade level replacement.
    #[inline]
    pub fn has_non_animation_hint(&self) -> bool {
        !(*self & !Self::for_animations()).is_empty()
    }

    /// Returns whether the hint specifies that selector matching must be re-run
    /// for the element.
    #[inline]
    pub fn match_self(&self) -> bool {
        self.intersects(RESTYLE_SELF)
    }

    /// Returns whether the hint specifies that some cascade levels must be
    /// replaced.
    #[inline]
    pub fn has_replacements(&self) -> bool {
        self.intersects(Self::replacements())
    }

    /// Removes all of the animation-related hints.
    #[inline]
    pub fn remove_animation_hints(&mut self) {
        self.remove(Self::for_animations());

        // While RECASCADE_SELF is not animation-specific, we only ever add and
        // process it during traversal.  If we are here, removing animation
        // hints, then we are in an animation-only traversal, and we know that
        // any RECASCADE_SELF flag must have been set due to changes in
        // inherited values after restyling for animations, and thus we want to
        // remove it so that we don't later try to restyle the element during a
        // normal restyle.  (We could have separate RECASCADE_SELF_NORMAL and
        // RECASCADE_SELF_ANIMATIONS flags to make it clear, but this isn't
        // currently necessary.)
        self.remove(RECASCADE_SELF);
    }
}

impl Default for RestyleHint {
    fn default() -> Self {
        Self::empty()
    }
}

#[cfg(feature = "gecko")]
impl From<nsRestyleHint> for RestyleHint {
    fn from(raw: nsRestyleHint) -> Self {
        use gecko_bindings::structs::nsRestyleHint_eRestyle_ForceDescendants as eRestyle_ForceDescendants;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree;

        let mut hint = RestyleHint::empty();

        debug_assert!(raw.0 & eRestyle_LaterSiblings.0 == 0,
                      "Handle later siblings manually if necessary plz.");

        if (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0 {
            hint.insert(RESTYLE_SELF);
        }

        if (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0 {
            hint.insert(RESTYLE_DESCENDANTS);
        }

        if (raw.0 & eRestyle_ForceDescendants.0) != 0 {
            hint.insert(RECASCADE_DESCENDANTS);
        }

        hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8));

        hint
    }
}

#[cfg(feature = "servo")]
impl ::heapsize::HeapSizeOf for RestyleHint {
    fn heap_size_of_children(&self) -> usize { 0 }
}

/// Asserts that all replacement hints have a matching nsRestyleHint value.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_restyle_hints_match() {
    use gecko_bindings::structs;

    macro_rules! check_restyle_hints {
        ( $( $a:ident => $b:ident ),*, ) => {
            if cfg!(debug_assertions) {
                let mut replacements = RestyleHint::replacements();
                $(
                    assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
                    replacements.remove($b);
                )*
                assert_eq!(replacements, RestyleHint::empty(),
                           "all RestyleHint replacement bits should have an \
                            assertion");
            }
        }
    }

    check_restyle_hints! {
        nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
        nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
        nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
        nsRestyleHint_eRestyle_StyleAttribute_Animations => RESTYLE_SMIL,
    }
}
<commit_msg>Auto merge of #17933 - emilio:restyle-hint-conversion, r=Manishearth<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/. */

//! Restyle hints: an optimization to avoid unnecessarily matching selectors.

#[cfg(feature = "gecko")]
use gecko_bindings::structs::nsRestyleHint;
use traversal_flags::TraversalFlags;

bitflags! {
    /// The kind of restyle we need to do for a given element.
    pub flags RestyleHint: u8 {
        /// Do a selector match of the element.
        const RESTYLE_SELF = 1 << 0,

        /// Do a selector match of the element's descendants.
        const RESTYLE_DESCENDANTS = 1 << 1,

        /// Recascade the current element.
        const RECASCADE_SELF = 1 << 2,

        /// Recascade all descendant elements.
        const RECASCADE_DESCENDANTS = 1 << 3,

        /// Replace the style data coming from CSS transitions without updating
        /// any other style data. This hint is only processed in animation-only
        /// traversal which is prior to normal traversal.
        const RESTYLE_CSS_TRANSITIONS = 1 << 4,

        /// Replace the style data coming from CSS animations without updating
        /// any other style data. This hint is only processed in animation-only
        /// traversal which is prior to normal traversal.
        const RESTYLE_CSS_ANIMATIONS = 1 << 5,

        /// Don't re-run selector-matching on the element, only the style
        /// attribute has changed, and this change didn't have any other
        /// dependencies.
        const RESTYLE_STYLE_ATTRIBUTE = 1 << 6,

        /// Replace the style data coming from SMIL animations without updating
        /// any other style data. This hint is only processed in animation-only
        /// traversal which is prior to normal traversal.
        const RESTYLE_SMIL = 1 << 7,
    }
}

impl RestyleHint {
    /// Creates a new `RestyleHint` indicating that the current element and all
    /// its descendants must be fully restyled.
    pub fn restyle_subtree() -> Self {
        RESTYLE_SELF | RESTYLE_DESCENDANTS
    }

    /// Creates a new `RestyleHint` indicating that the current element and all
    /// its descendants must be recascaded.
    pub fn recascade_subtree() -> Self {
        RECASCADE_SELF | RECASCADE_DESCENDANTS
    }

    /// Returns whether this hint invalidates the element and all its
    /// descendants.
    pub fn contains_subtree(&self) -> bool {
        self.contains(RESTYLE_SELF | RESTYLE_DESCENDANTS)
    }

    /// Returns whether we need to restyle this element.
    pub fn has_non_animation_invalidations(&self) -> bool {
        self.intersects(
            RESTYLE_SELF |
            RECASCADE_SELF |
            (Self::replacements() & !Self::for_animations())
        )
    }

    /// Propagates this restyle hint to a child element.
    pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
        use std::mem;

        // In the middle of an animation only restyle, we don't need to
        // propagate any restyle hints, and we need to remove ourselves.
        if traversal_flags.for_animation_only() {
            self.remove_animation_hints();
            return Self::empty();
        }

        debug_assert!(!self.has_animation_hint(),
                      "There should not be any animation restyle hints \
                       during normal traversal");

        // Else we should clear ourselves, and return the propagated hint.
        mem::replace(self, Self::empty())
            .propagate_for_non_animation_restyle()
    }

    /// Returns a new `CascadeHint` appropriate for children of the current
    /// element.
    fn propagate_for_non_animation_restyle(&self) -> Self {
        if self.contains(RESTYLE_DESCENDANTS) {
            return Self::restyle_subtree()
        }
        if self.contains(RECASCADE_DESCENDANTS) {
            return Self::recascade_subtree()
        }
        Self::empty()
    }

    /// Creates a new `RestyleHint` that indicates the element must be
    /// recascaded.
    pub fn recascade_self() -> Self {
        RECASCADE_SELF
    }

    /// Returns a hint that contains all the replacement hints.
    pub fn replacements() -> Self {
        RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
    }

    /// The replacements for the animation cascade levels.
    #[inline]
    pub fn for_animations() -> Self {
        RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
    }

    /// Returns whether the hint specifies that the currently element must be
    /// recascaded.
    pub fn has_recascade_self(&self) -> bool {
        self.contains(RECASCADE_SELF)
    }

    /// Returns whether the hint specifies that an animation cascade level must
    /// be replaced.
    #[inline]
    pub fn has_animation_hint(&self) -> bool {
        self.intersects(Self::for_animations())
    }

    /// Returns whether the hint specifies that an animation cascade level must
    /// be replaced.
    #[inline]
    pub fn has_animation_hint_or_recascade(&self) -> bool {
        self.intersects(Self::for_animations() | RECASCADE_SELF)
    }

    /// Returns whether the hint specifies some restyle work other than an
    /// animation cascade level replacement.
    #[inline]
    pub fn has_non_animation_hint(&self) -> bool {
        !(*self & !Self::for_animations()).is_empty()
    }

    /// Returns whether the hint specifies that selector matching must be re-run
    /// for the element.
    #[inline]
    pub fn match_self(&self) -> bool {
        self.intersects(RESTYLE_SELF)
    }

    /// Returns whether the hint specifies that some cascade levels must be
    /// replaced.
    #[inline]
    pub fn has_replacements(&self) -> bool {
        self.intersects(Self::replacements())
    }

    /// Removes all of the animation-related hints.
    #[inline]
    pub fn remove_animation_hints(&mut self) {
        self.remove(Self::for_animations());

        // While RECASCADE_SELF is not animation-specific, we only ever add and
        // process it during traversal.  If we are here, removing animation
        // hints, then we are in an animation-only traversal, and we know that
        // any RECASCADE_SELF flag must have been set due to changes in
        // inherited values after restyling for animations, and thus we want to
        // remove it so that we don't later try to restyle the element during a
        // normal restyle.  (We could have separate RECASCADE_SELF_NORMAL and
        // RECASCADE_SELF_ANIMATIONS flags to make it clear, but this isn't
        // currently necessary.)
        self.remove(RECASCADE_SELF);
    }
}

impl Default for RestyleHint {
    fn default() -> Self {
        Self::empty()
    }
}

#[cfg(feature = "gecko")]
impl From<nsRestyleHint> for RestyleHint {
    fn from(mut raw: nsRestyleHint) -> Self {
        use gecko_bindings::structs::nsRestyleHint_eRestyle_Force as eRestyle_Force;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_ForceDescendants as eRestyle_ForceDescendants;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants;
        use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree;

        let mut hint = RestyleHint::empty();

        debug_assert!(raw.0 & eRestyle_LaterSiblings.0 == 0,
                      "Handle later siblings manually if necessary plz.");

        if (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0 {
            raw.0 &= !eRestyle_Self.0;
            hint.insert(RESTYLE_SELF);
        }

        if (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0 {
            raw.0 &= !eRestyle_Subtree.0;
            raw.0 &= !eRestyle_SomeDescendants.0;
            hint.insert(RESTYLE_DESCENDANTS);
        }

        if (raw.0 & (eRestyle_ForceDescendants.0 | eRestyle_Force.0)) != 0 {
            raw.0 &= !eRestyle_Force.0;
            hint.insert(RECASCADE_SELF);
        }

        if (raw.0 & eRestyle_ForceDescendants.0) != 0 {
            raw.0 &= !eRestyle_ForceDescendants.0;
            hint.insert(RECASCADE_DESCENDANTS);
        }

        hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8));

        hint
    }
}

#[cfg(feature = "servo")]
impl ::heapsize::HeapSizeOf for RestyleHint {
    fn heap_size_of_children(&self) -> usize { 0 }
}

/// Asserts that all replacement hints have a matching nsRestyleHint value.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_restyle_hints_match() {
    use gecko_bindings::structs;

    macro_rules! check_restyle_hints {
        ( $( $a:ident => $b:ident ),*, ) => {
            if cfg!(debug_assertions) {
                let mut replacements = RestyleHint::replacements();
                $(
                    assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
                    replacements.remove($b);
                )*
                assert_eq!(replacements, RestyleHint::empty(),
                           "all RestyleHint replacement bits should have an \
                            assertion");
            }
        }
    }

    check_restyle_hints! {
        nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
        nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
        nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
        nsRestyleHint_eRestyle_StyleAttribute_Animations => RESTYLE_SMIL,
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::u64;

use crate::rocks;
use crate::rocks::{Range, TablePropertiesCollection, Writable, WriteBatch, DB};
use crate::{CF_LOCK, CF_RAFT, CF_WRITE};

use super::{Error, Result};
use super::{IterOption, Iterable};
use tikv_util::keybuilder::KeyBuilder;

/// Check if key in range [`start_key`, `end_key`).
pub fn check_key_in_range(
    key: &[u8],
    region_id: u64,
    start_key: &[u8],
    end_key: &[u8],
) -> Result<()> {
    if key >= start_key && (end_key.is_empty() || key < end_key) {
        Ok(())
    } else {
        Err(Error::NotInRange(
            key.to_vec(),
            region_id,
            start_key.to_vec(),
            end_key.to_vec(),
        ))
    }
}

// In our tests, we found that if the batch size is too large, running delete_all_in_range will
// reduce OLTP QPS by 30% ~ 60%. We found that 32K is a proper choice.
pub const MAX_DELETE_BATCH_SIZE: usize = 32 * 1024;

pub fn delete_all_in_range(
    db: &DB,
    start_key: &[u8],
    end_key: &[u8],
    use_delete_range: bool,
) -> Result<()> {
    if start_key >= end_key {
        return Ok(());
    }

    for cf in db.cf_names() {
        delete_all_in_range_cf(db, cf, start_key, end_key, use_delete_range)?;
    }

    Ok(())
}

pub fn delete_all_in_range_cf(
    db: &DB,
    cf: &str,
    start_key: &[u8],
    end_key: &[u8],
    use_delete_range: bool,
) -> Result<()> {
    let handle = rocks::util::get_cf_handle(db, cf)?;
    let wb = WriteBatch::new();
    // Since CF_RAFT and CF_LOCK is usually small, so using
    // traditional way to cleanup.
    if use_delete_range && cf != CF_RAFT && cf != CF_LOCK {
        // HACK: TiKV always enable prefix bloom filter for Write CF,
        // which requires key.len() > 8
        //
        // FIXME: It should delete the `start_key` too.
        if cf == CF_WRITE {
            let mut start = start_key.to_vec();
            start.extend_from_slice(&[0; 8]);
            wb.delete_range_cf(handle, &start, end_key)?;
        } else {
            wb.delete_range_cf(handle, start_key, end_key)?;
        }
    } else {
        let start = KeyBuilder::from_slice(start_key, 0, 0);
        let end = KeyBuilder::from_slice(end_key, 0, 0);
        let iter_opt = IterOption::new(Some(start), Some(end), false);
        let mut it = db.new_iterator_cf(cf, iter_opt)?;
        it.seek(start_key.into());
        while it.valid() {
            wb.delete_cf(handle, it.key())?;
            if wb.data_size() >= MAX_DELETE_BATCH_SIZE {
                // Can't use write_without_wal here.
                // Otherwise it may cause dirty data when applying snapshot.
                db.write(&wb)?;
                wb.clear();
            }

            if !it.next() {
                break;
            }
        }
    }

    if wb.count() > 0 {
        db.write(&wb)?;
    }

    Ok(())
}

pub fn delete_all_files_in_range(db: &DB, start_key: &[u8], end_key: &[u8]) -> Result<()> {
    if start_key >= end_key {
        return Ok(());
    }

    for cf in db.cf_names() {
        let handle = rocks::util::get_cf_handle(db, cf)?;
        db.delete_files_in_range_cf(handle, start_key, end_key, false)?;
    }

    Ok(())
}

pub fn get_range_properties_cf(
    db: &DB,
    cfname: &str,
    start_key: &[u8],
    end_key: &[u8],
) -> Result<TablePropertiesCollection> {
    let cf = rocks::util::get_cf_handle(db, cfname)?;
    let range = Range::new(start_key, end_key);
    db.get_properties_of_tables_in_range(cf, &[range])
        .map_err(|e| e.into())
}

#[cfg(test)]
mod tests {
    use tempdir::TempDir;

    use crate::rocks;
    use crate::rocks::util::{get_cf_handle, new_engine_opt, CFOptions};
    use crate::rocks::{ColumnFamilyOptions, DBOptions, SeekKey, Writable};
    use crate::ALL_CFS;
    use crate::DB;

    use super::*;

    fn check_data(db: &DB, cfs: &[&str], expected: &[(&[u8], &[u8])]) {
        for cf in cfs {
            let handle = get_cf_handle(db, cf).unwrap();
            let mut iter = db.iter_cf(handle);
            iter.seek(SeekKey::Start);
            for &(k, v) in expected {
                assert_eq!(k, iter.key());
                assert_eq!(v, iter.value());
                iter.next();
            }
            assert!(!iter.valid());
        }
    }

    fn test_delete_all_in_range(use_delete_range: bool) {
        let path = TempDir::new("engine_delete_all_in_range").unwrap();
        let path_str = path.path().to_str().unwrap();

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| CFOptions::new(cf, ColumnFamilyOptions::new()))
            .collect();
        let db = new_engine_opt(path_str, DBOptions::new(), cfs_opts).unwrap();

        let wb = WriteBatch::new();
        let ts: u8 = 12;
        let keys: Vec<_> = vec![
            b"k1".to_vec(),
            b"k2".to_vec(),
            b"k3".to_vec(),
            b"k4".to_vec(),
        ]
        .into_iter()
        .map(|mut k| {
            k.append(&mut vec![ts; 8]);
            k
        })
        .collect();

        let mut kvs: Vec<(&[u8], &[u8])> = vec![];
        for (_, key) in keys.iter().enumerate() {
            kvs.push((key.as_slice(), b"value"));
        }
        let kvs_left: Vec<(&[u8], &[u8])> = vec![(kvs[0].0, kvs[0].1), (kvs[3].0, kvs[3].1)];
        for &(k, v) in kvs.as_slice() {
            for cf in ALL_CFS {
                let handle = get_cf_handle(&db, cf).unwrap();
                wb.put_cf(handle, k, v).unwrap();
            }
        }
        db.write(&wb).unwrap();
        check_data(&db, ALL_CFS, kvs.as_slice());

        // Delete all in ["k2", "k4").
        let start = b"k2";
        let end = b"k4";
        delete_all_in_range(&db, start, end, use_delete_range).unwrap();
        check_data(&db, ALL_CFS, kvs_left.as_slice());
    }

    #[test]
    fn test_delete_all_in_range_use_delete_range() {
        test_delete_all_in_range(true);
    }

    #[test]
    fn test_delete_all_in_range_not_use_delete_range() {
        test_delete_all_in_range(false);
    }

    #[test]
    fn test_delete_all_files_in_range() {
        let path = TempDir::new("engine_delete_all_files_in_range").unwrap();
        let path_str = path.path().to_str().unwrap();

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| {
                let mut cf_opts = ColumnFamilyOptions::new();
                cf_opts.set_level_zero_file_num_compaction_trigger(1);
                CFOptions::new(cf, cf_opts)
            })
            .collect();
        let db = new_engine_opt(path_str, DBOptions::new(), cfs_opts).unwrap();

        let keys = vec![b"k1", b"k2", b"k3", b"k4"];

        let mut kvs: Vec<(&[u8], &[u8])> = vec![];
        for key in keys {
            kvs.push((key, b"value"));
        }
        let kvs_left: Vec<(&[u8], &[u8])> = vec![(kvs[0].0, kvs[0].1), (kvs[3].0, kvs[3].1)];
        for cf in ALL_CFS {
            let handle = get_cf_handle(&db, cf).unwrap();
            for &(k, v) in kvs.as_slice() {
                db.put_cf(handle, k, v).unwrap();
                db.flush_cf(handle, true).unwrap();
            }
        }
        check_data(&db, ALL_CFS, kvs.as_slice());

        delete_all_files_in_range(&db, b"k2", b"k4").unwrap();
        check_data(&db, ALL_CFS, kvs_left.as_slice());
    }

    #[test]
    fn test_delete_range_prefix_bloom_case() {
        let path = TempDir::new("engine_delete_range_prefix_bloom").unwrap();
        let path_str = path.path().to_str().unwrap();

        let mut opts = DBOptions::new();
        opts.create_if_missing(true);

        let mut cf_opts = ColumnFamilyOptions::new();
        // Prefix extractor(trim the timestamp at tail) for write cf.
        cf_opts
            .set_prefix_extractor(
                "FixedSuffixSliceTransform",
                Box::new(rocks::util::FixedSuffixSliceTransform::new(8)),
            )
            .unwrap_or_else(|err| panic!("{:?}", err));
        // Create prefix bloom filter for memtable.
        cf_opts.set_memtable_prefix_bloom_size_ratio(0.1 as f64);
        let cf = "default";
        let db = DB::open_cf(opts, path_str, vec![(cf, cf_opts)]).unwrap();
        let wb = WriteBatch::new();
        let kvs: Vec<(&[u8], &[u8])> = vec![
            (b"kabcdefg1", b"v1"),
            (b"kabcdefg2", b"v2"),
            (b"kabcdefg3", b"v3"),
            (b"kabcdefg4", b"v4"),
        ];
        let kvs_left: Vec<(&[u8], &[u8])> = vec![(b"kabcdefg1", b"v1"), (b"kabcdefg4", b"v4")];

        for &(k, v) in kvs.as_slice() {
            let handle = get_cf_handle(&db, cf).unwrap();
            wb.put_cf(handle, k, v).unwrap();
        }
        db.write(&wb).unwrap();
        check_data(&db, &[cf], kvs.as_slice());

        // Delete all in ["k2", "k4").
        delete_all_in_range(&db, b"kabcdefg2", b"kabcdefg4", true).unwrap();
        check_data(&db, &[cf], kvs_left.as_slice());
    }
}
<commit_msg>engine: remove delete range extend key hack (#4526)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::u64;

use crate::rocks;
use crate::rocks::{Range, TablePropertiesCollection, Writable, WriteBatch, DB};
use crate::{CF_LOCK, CF_RAFT};

use super::{Error, Result};
use super::{IterOption, Iterable};
use tikv_util::keybuilder::KeyBuilder;

/// Check if key in range [`start_key`, `end_key`).
pub fn check_key_in_range(
    key: &[u8],
    region_id: u64,
    start_key: &[u8],
    end_key: &[u8],
) -> Result<()> {
    if key >= start_key && (end_key.is_empty() || key < end_key) {
        Ok(())
    } else {
        Err(Error::NotInRange(
            key.to_vec(),
            region_id,
            start_key.to_vec(),
            end_key.to_vec(),
        ))
    }
}

// In our tests, we found that if the batch size is too large, running delete_all_in_range will
// reduce OLTP QPS by 30% ~ 60%. We found that 32K is a proper choice.
pub const MAX_DELETE_BATCH_SIZE: usize = 32 * 1024;

pub fn delete_all_in_range(
    db: &DB,
    start_key: &[u8],
    end_key: &[u8],
    use_delete_range: bool,
) -> Result<()> {
    if start_key >= end_key {
        return Ok(());
    }

    for cf in db.cf_names() {
        delete_all_in_range_cf(db, cf, start_key, end_key, use_delete_range)?;
    }

    Ok(())
}

pub fn delete_all_in_range_cf(
    db: &DB,
    cf: &str,
    start_key: &[u8],
    end_key: &[u8],
    use_delete_range: bool,
) -> Result<()> {
    let handle = rocks::util::get_cf_handle(db, cf)?;
    let wb = WriteBatch::new();
    // Since CF_RAFT and CF_LOCK is usually small, so using
    // traditional way to cleanup.
    if use_delete_range && cf != CF_RAFT && cf != CF_LOCK {
        wb.delete_range_cf(handle, start_key, end_key)?;
    } else {
        let start = KeyBuilder::from_slice(start_key, 0, 0);
        let end = KeyBuilder::from_slice(end_key, 0, 0);
        let iter_opt = IterOption::new(Some(start), Some(end), false);
        let mut it = db.new_iterator_cf(cf, iter_opt)?;
        it.seek(start_key.into());
        while it.valid() {
            wb.delete_cf(handle, it.key())?;
            if wb.data_size() >= MAX_DELETE_BATCH_SIZE {
                // Can't use write_without_wal here.
                // Otherwise it may cause dirty data when applying snapshot.
                db.write(&wb)?;
                wb.clear();
            }

            if !it.next() {
                break;
            }
        }
    }

    if wb.count() > 0 {
        db.write(&wb)?;
    }

    Ok(())
}

pub fn delete_all_files_in_range(db: &DB, start_key: &[u8], end_key: &[u8]) -> Result<()> {
    if start_key >= end_key {
        return Ok(());
    }

    for cf in db.cf_names() {
        let handle = rocks::util::get_cf_handle(db, cf)?;
        db.delete_files_in_range_cf(handle, start_key, end_key, false)?;
    }

    Ok(())
}

pub fn get_range_properties_cf(
    db: &DB,
    cfname: &str,
    start_key: &[u8],
    end_key: &[u8],
) -> Result<TablePropertiesCollection> {
    let cf = rocks::util::get_cf_handle(db, cfname)?;
    let range = Range::new(start_key, end_key);
    db.get_properties_of_tables_in_range(cf, &[range])
        .map_err(|e| e.into())
}

#[cfg(test)]
mod tests {
    use tempdir::TempDir;

    use crate::rocks;
    use crate::rocks::util::{get_cf_handle, new_engine_opt, CFOptions};
    use crate::rocks::{ColumnFamilyOptions, DBOptions, SeekKey, Writable};
    use crate::ALL_CFS;
    use crate::DB;

    use super::*;

    fn check_data(db: &DB, cfs: &[&str], expected: &[(&[u8], &[u8])]) {
        for cf in cfs {
            let handle = get_cf_handle(db, cf).unwrap();
            let mut iter = db.iter_cf(handle);
            iter.seek(SeekKey::Start);
            for &(k, v) in expected {
                assert_eq!(k, iter.key());
                assert_eq!(v, iter.value());
                iter.next();
            }
            assert!(!iter.valid());
        }
    }

    fn test_delete_all_in_range(use_delete_range: bool) {
        let path = TempDir::new("engine_delete_all_in_range").unwrap();
        let path_str = path.path().to_str().unwrap();

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| CFOptions::new(cf, ColumnFamilyOptions::new()))
            .collect();
        let db = new_engine_opt(path_str, DBOptions::new(), cfs_opts).unwrap();

        let wb = WriteBatch::new();
        let ts: u8 = 12;
        let keys: Vec<_> = vec![
            b"k1".to_vec(),
            b"k2".to_vec(),
            b"k3".to_vec(),
            b"k4".to_vec(),
        ]
        .into_iter()
        .map(|mut k| {
            k.append(&mut vec![ts; 8]);
            k
        })
        .collect();

        let mut kvs: Vec<(&[u8], &[u8])> = vec![];
        for (_, key) in keys.iter().enumerate() {
            kvs.push((key.as_slice(), b"value"));
        }
        let kvs_left: Vec<(&[u8], &[u8])> = vec![(kvs[0].0, kvs[0].1), (kvs[3].0, kvs[3].1)];
        for &(k, v) in kvs.as_slice() {
            for cf in ALL_CFS {
                let handle = get_cf_handle(&db, cf).unwrap();
                wb.put_cf(handle, k, v).unwrap();
            }
        }
        db.write(&wb).unwrap();
        check_data(&db, ALL_CFS, kvs.as_slice());

        // Delete all in ["k2", "k4").
        let start = b"k2";
        let end = b"k4";
        delete_all_in_range(&db, start, end, use_delete_range).unwrap();
        check_data(&db, ALL_CFS, kvs_left.as_slice());
    }

    #[test]
    fn test_delete_all_in_range_use_delete_range() {
        test_delete_all_in_range(true);
    }

    #[test]
    fn test_delete_all_in_range_not_use_delete_range() {
        test_delete_all_in_range(false);
    }

    #[test]
    fn test_delete_all_files_in_range() {
        let path = TempDir::new("engine_delete_all_files_in_range").unwrap();
        let path_str = path.path().to_str().unwrap();

        let cfs_opts = ALL_CFS
            .iter()
            .map(|cf| {
                let mut cf_opts = ColumnFamilyOptions::new();
                cf_opts.set_level_zero_file_num_compaction_trigger(1);
                CFOptions::new(cf, cf_opts)
            })
            .collect();
        let db = new_engine_opt(path_str, DBOptions::new(), cfs_opts).unwrap();

        let keys = vec![b"k1", b"k2", b"k3", b"k4"];

        let mut kvs: Vec<(&[u8], &[u8])> = vec![];
        for key in keys {
            kvs.push((key, b"value"));
        }
        let kvs_left: Vec<(&[u8], &[u8])> = vec![(kvs[0].0, kvs[0].1), (kvs[3].0, kvs[3].1)];
        for cf in ALL_CFS {
            let handle = get_cf_handle(&db, cf).unwrap();
            for &(k, v) in kvs.as_slice() {
                db.put_cf(handle, k, v).unwrap();
                db.flush_cf(handle, true).unwrap();
            }
        }
        check_data(&db, ALL_CFS, kvs.as_slice());

        delete_all_files_in_range(&db, b"k2", b"k4").unwrap();
        check_data(&db, ALL_CFS, kvs_left.as_slice());
    }

    #[test]
    fn test_delete_range_prefix_bloom_case() {
        let path = TempDir::new("engine_delete_range_prefix_bloom").unwrap();
        let path_str = path.path().to_str().unwrap();

        let mut opts = DBOptions::new();
        opts.create_if_missing(true);

        let mut cf_opts = ColumnFamilyOptions::new();
        // Prefix extractor(trim the timestamp at tail) for write cf.
        cf_opts
            .set_prefix_extractor(
                "FixedSuffixSliceTransform",
                Box::new(rocks::util::FixedSuffixSliceTransform::new(8)),
            )
            .unwrap_or_else(|err| panic!("{:?}", err));
        // Create prefix bloom filter for memtable.
        cf_opts.set_memtable_prefix_bloom_size_ratio(0.1 as f64);
        let cf = "default";
        let db = DB::open_cf(opts, path_str, vec![(cf, cf_opts)]).unwrap();
        let wb = WriteBatch::new();
        let kvs: Vec<(&[u8], &[u8])> = vec![
            (b"kabcdefg1", b"v1"),
            (b"kabcdefg2", b"v2"),
            (b"kabcdefg3", b"v3"),
            (b"kabcdefg4", b"v4"),
        ];
        let kvs_left: Vec<(&[u8], &[u8])> = vec![(b"kabcdefg1", b"v1"), (b"kabcdefg4", b"v4")];

        for &(k, v) in kvs.as_slice() {
            let handle = get_cf_handle(&db, cf).unwrap();
            wb.put_cf(handle, k, v).unwrap();
        }
        db.write(&wb).unwrap();
        check_data(&db, &[cf], kvs.as_slice());

        // Delete all in ["k2", "k4").
        delete_all_in_range(&db, b"kabcdefg2", b"kabcdefg4", true).unwrap();
        check_data(&db, &[cf], kvs_left.as_slice());
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::io::Write;
use std::io::stderr;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::ops::Deref;

use error::RuntimeErrorKind as EK;
use error::RuntimeError as RE;
use error::ResultExt;
use runtime::Runtime;

use clap::ArgMatches;
use log::{Log, LogLevel, LogRecord, LogMetadata};
use toml::Value;
use toml_query::read::TomlValueReadExt;
use handlebars::Handlebars;

type ModuleName = String;
type Result<T> = ::std::result::Result<T, RE>;

enum LogDestination {
    Stderr,
    File(Arc<Mutex<::std::fs::File>>),
}

impl Default for LogDestination {
    fn default() -> LogDestination {
        LogDestination::Stderr
    }
}

struct ModuleSettings {
    enabled:        bool,
    level:          Option<LogLevel>,

    #[allow(unused)]
    destinations:   Option<Vec<LogDestination>>,
}

/// Logger implementation for `log` crate.
pub struct ImagLogger {
    global_loglevel     : LogLevel,

    #[allow(unused)]
    global_destinations : Vec<LogDestination>,
    // global_format_trace : ,
    // global_format_debug : ,
    // global_format_info  : ,
    // global_format_warn  : ,
    // global_format_error : ,
    module_settings     : BTreeMap<ModuleName, ModuleSettings>,

    handlebars: Handlebars,
}

impl ImagLogger {

    /// Create a new ImagLogger object with a certain level
    pub fn new(matches: &ArgMatches, config: Option<&Value>) -> Result<ImagLogger> {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(::handlebars::no_escape);

        ::libimaginteraction::format::register_all_color_helpers(&mut handlebars);
        ::libimaginteraction::format::register_all_format_helpers(&mut handlebars);

        {
            let fmt = aggregate_global_format_trace(config)?;
            handlebars.register_template_string("TRACE", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_debug(config)?;
            handlebars.register_template_string("DEBUG", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_info(config)?;
            handlebars.register_template_string("INFO", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_warn(config)?;
            handlebars.register_template_string("WARN", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_error(config)?;
            handlebars.register_template_string("ERROR", fmt)?; // name must be uppercase
        }

        Ok(ImagLogger {
            global_loglevel     : aggregate_global_loglevel(matches, config)?,
            global_destinations : aggregate_global_destinations(matches, config)?,
            module_settings     : aggregate_module_settings(matches, config)?,
            handlebars          : handlebars,
        })
    }

    pub fn global_loglevel(&self) -> LogLevel {
        self.global_loglevel
    }

}

impl Log for ImagLogger {

    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.global_loglevel
    }

    fn log(&self, record: &LogRecord) {
        if record.location().module_path().starts_with("handlebars") {
            // This is a ugly, yet necessary hack. When logging, we use handlebars for templating.
            // But as the handlebars library itselfs logs via a normal logging macro ("debug!()"),
            // we have a recursion in our chain.
            //
            // To prevent this recursion, we return here.
            //
            // (As of handlebars 0.29.0 - please check whether you can update handlebars if you see
            // this. Hopefully the next version has a compiletime flag to disable logging)
            return;
        }

        let mut data = BTreeMap::new();

        {
            data.insert("level",        format!("{}", record.level()));
            data.insert("module_path",  String::from(record.location().module_path()));
            data.insert("file",         String::from(record.location().file()));
            data.insert("line",         format!("{}", record.location().line()));
            data.insert("target",       String::from(record.target()));
            data.insert("message",      format!("{}", record.args()));
        }

        let logtext = self
            .handlebars
            .render(&format!("{}", record.level()), &data)
            .unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e));

        let log_to_destination = |d: &LogDestination| match d {
            &LogDestination::Stderr => {
                let _ = write!(stderr(), "{}\n", logtext);
            },
            &LogDestination::File(ref arc_mutex_logdest) => {
                // if there is an error in the lock, we cannot do anything. So we ignore it here.
                let _ = arc_mutex_logdest
                    .deref()
                    .lock()
                    .map(|mut logdest| {
                        write!(logdest, "{}\n", logtext)
                    });
            }
        };

        // hack to get the right target configuration.
        // If there is no element here, we use the empty string which automatically drops through
        // to the unwrap_or_else() case
        let record_target = record
            .target()
            .split("::")
            .next()
            .unwrap_or("");

        self.module_settings
            .get(record_target)
            .map(|module_setting| {
                let set = module_setting.enabled &&
                    module_setting.level.unwrap_or(self.global_loglevel) >= record.level();

                if set {
                    module_setting.destinations.as_ref().map(|destinations| for d in destinations {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    });

                    for d in self.global_destinations.iter() {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    }
                }
            })
        .unwrap_or_else(|| {
            if self.global_loglevel >= record.level() {
                // Yes, we log
                for d in self.global_destinations.iter() {
                    // If there's an error, we cannot do anything, can we?
                    let _ = log_to_destination(&d);
                }
            }
        });
    }
}

fn match_log_level_str(s: &str) -> Result<LogLevel> {
    match s {
        "trace" => Ok(LogLevel::Trace),
        "debug" => Ok(LogLevel::Debug),
        "info"  => Ok(LogLevel::Info),
        "warn"  => Ok(LogLevel::Warn),
        "error" => Ok(LogLevel::Error),
        _       => return Err(RE::from_kind(EK::InvalidLogLevelSpec)),
    }
}

fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>)
    -> Result<LogLevel>
{
    fn get_arg_loglevel(matches: &ArgMatches) -> Result<Option<LogLevel>> {
        if matches.is_present(Runtime::arg_debugging_name()) {
            return Ok(Some(LogLevel::Debug))
        }

        if matches.is_present(Runtime::arg_verbosity_name()) {
            return Ok(Some(LogLevel::Info))
        }

        match matches.value_of(Runtime::arg_verbosity_name()) {
            Some(v) => match_log_level_str(v).map(Some),
            None    => Ok(None),
        }
    }

    if let Some(cfg) = config {
        let cfg_loglevel = match cfg.read("imag.logging.level") {
            Ok(Some(&Value::String(ref s))) => match_log_level_str(s),
            Ok(Some(_)) => {
                let path = "imag.logging.level".to_owned();
                let ty   = "String";
                return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => return Err(RE::from_kind(EK::GlobalLogLevelConfigMissing)),
            Err(e)      => return Err(e).map_err(From::from),
        }?;

        if let Some(cli_loglevel) = get_arg_loglevel(matches)? {
            if cli_loglevel > cfg_loglevel {
                return Ok(cli_loglevel)
            }
        }

        Ok(cfg_loglevel)

    } else {
        get_arg_loglevel(matches).map(|o| o.unwrap_or(LogLevel::Info))
    }
}

fn translate_destination(raw: &str) -> Result<LogDestination> {
    use std::fs::OpenOptions;

    match raw {
        "-" => Ok(LogDestination::Stderr),
        other => {
            OpenOptions::new()
                .append(true)
                .create(true)
                .open(other)
                .map(Mutex::new)
                .map(Arc::new)
                .map(LogDestination::File)
                .chain_err(|| EK::IOLogFileOpenError)
        }
    }
}

fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
    raw.iter()
        .fold(Ok(vec![]), |acc, val| {
            acc.and_then(|mut v| {
                let dest = match *val {
                    Value::String(ref s) => translate_destination(s)?,
                    _ => {
                        let path = "imag.logging.modules.<mod>.destinations".to_owned();
                        let ty   = "Array<String>";
                        return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                    },
                };
                v.push(dest);
                Ok(v)
            })
        })
}

fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
    -> Result<Vec<LogDestination>>
{

    match config {
        Some(cfg) => match cfg.read("imag.logging.destinations") {
            Ok(Some(&Value::Array(ref a))) => translate_destinations(a),
            Ok(Some(_)) => {
                let path = "imag.logging.destinations".to_owned();
                let ty   = "Array";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => Err(RE::from_kind(EK::GlobalDestinationConfigMissing)),
            Err(e)      => Err(e).map_err(From::from),
        },
        None => {
            if let Some(values) = matches.value_of(Runtime::arg_logdest_name()) {
                // parse logdest specification from commandline

                values.split(",")
                    .fold(Ok(vec![]), move |acc, dest| {
                        acc.and_then(|mut v| {
                            v.push(translate_destination(dest)?);
                            Ok(v)
                        })
                    })
            } else {
                Ok(vec![ LogDestination::default() ])
            }
        }
    }
}

macro_rules! aggregate_global_format {
    ($read_str:expr, $error_kind_if_missing:expr, $config:expr) => {
        match try!($config.ok_or(RE::from_kind($error_kind_if_missing))).read($read_str) {
            Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
            Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError($read_str.to_owned(), "String"))),
            Ok(None)    => Err(RE::from_kind($error_kind_if_missing)),
            Err(e)      => Err(e).map_err(From::from),
        }
    };
}

fn aggregate_global_format_trace(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.trace",
                            EK::ConfigMissingLoggingFormatTrace,
                            config)
}

fn aggregate_global_format_debug(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.debug",
                            EK::ConfigMissingLoggingFormatDebug,
                            config)
}

fn aggregate_global_format_info(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.info",
                            EK::ConfigMissingLoggingFormatInfo,
                            config)
}

fn aggregate_global_format_warn(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.warn",
                            EK::ConfigMissingLoggingFormatWarn,
                            config)
}

fn aggregate_global_format_error(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.error",
                            EK::ConfigMissingLoggingFormatError,
                            config)
}

fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
    -> Result<BTreeMap<ModuleName, ModuleSettings>>
{
    match config {
        Some(cfg) => match cfg.read("imag.logging.modules") {
            Ok(Some(&Value::Table(ref t))) => {
                // translate the module settings from the table `t`
                let mut settings = BTreeMap::new();

                for (module_name, v) in t {
                    let destinations = match v.read("destinations") {
                        Ok(Some(&Value::Array(ref a))) => translate_destinations(a).map(Some),
                        Ok(None)    => Ok(None),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.destinations".to_owned();
                            let ty = "Array";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    }?;

                    let level = match v.read("level") {
                        Ok(Some(&Value::String(ref s))) => match_log_level_str(s).map(Some),
                        Ok(None)    => Ok(None),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.level".to_owned();
                            let ty = "String";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    }?;

                    let enabled = match v.read("enabled") {
                        Ok(Some(&Value::Boolean(b))) => Ok(b),
                        Ok(None)    => Ok(false),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.enabled".to_owned();
                            let ty = "Boolean";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    }?;

                    let module_settings = ModuleSettings {
                        enabled: enabled,
                        level: level,
                        destinations: destinations,
                    };

                    // We don't care whether there was a value, we override it.
                    let _ = settings.insert(module_name.to_owned(), module_settings);
                }

                Ok(settings)
            },
            Ok(Some(_)) => {
                let path = "imag.logging.modules".to_owned();
                let ty = "Table";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => {
                // No modules configured. This is okay!
                Ok(BTreeMap::new())
            },
            Err(e) => Err(e).map_err(From::from),
        },
        None => {
            write!(stderr(), "No Configuration.").ok();
            write!(stderr(), "cannot find module-settings for logging.").ok();
            write!(stderr(), "Will use global defaults").ok();

            Ok(BTreeMap::new())
        }
    }
}

<commit_msg>Simplify matching<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::io::Write;
use std::io::stderr;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::ops::Deref;

use error::RuntimeErrorKind as EK;
use error::RuntimeError as RE;
use error::ResultExt;
use runtime::Runtime;

use clap::ArgMatches;
use log::{Log, LogLevel, LogRecord, LogMetadata};
use toml::Value;
use toml_query::read::TomlValueReadExt;
use handlebars::Handlebars;

type ModuleName = String;
type Result<T> = ::std::result::Result<T, RE>;

enum LogDestination {
    Stderr,
    File(Arc<Mutex<::std::fs::File>>),
}

impl Default for LogDestination {
    fn default() -> LogDestination {
        LogDestination::Stderr
    }
}

struct ModuleSettings {
    enabled:        bool,
    level:          Option<LogLevel>,

    #[allow(unused)]
    destinations:   Option<Vec<LogDestination>>,
}

/// Logger implementation for `log` crate.
pub struct ImagLogger {
    global_loglevel     : LogLevel,

    #[allow(unused)]
    global_destinations : Vec<LogDestination>,
    // global_format_trace : ,
    // global_format_debug : ,
    // global_format_info  : ,
    // global_format_warn  : ,
    // global_format_error : ,
    module_settings     : BTreeMap<ModuleName, ModuleSettings>,

    handlebars: Handlebars,
}

impl ImagLogger {

    /// Create a new ImagLogger object with a certain level
    pub fn new(matches: &ArgMatches, config: Option<&Value>) -> Result<ImagLogger> {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(::handlebars::no_escape);

        ::libimaginteraction::format::register_all_color_helpers(&mut handlebars);
        ::libimaginteraction::format::register_all_format_helpers(&mut handlebars);

        {
            let fmt = aggregate_global_format_trace(config)?;
            handlebars.register_template_string("TRACE", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_debug(config)?;
            handlebars.register_template_string("DEBUG", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_info(config)?;
            handlebars.register_template_string("INFO", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_warn(config)?;
            handlebars.register_template_string("WARN", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_error(config)?;
            handlebars.register_template_string("ERROR", fmt)?; // name must be uppercase
        }

        Ok(ImagLogger {
            global_loglevel     : aggregate_global_loglevel(matches, config)?,
            global_destinations : aggregate_global_destinations(matches, config)?,
            module_settings     : aggregate_module_settings(matches, config)?,
            handlebars          : handlebars,
        })
    }

    pub fn global_loglevel(&self) -> LogLevel {
        self.global_loglevel
    }

}

impl Log for ImagLogger {

    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.global_loglevel
    }

    fn log(&self, record: &LogRecord) {
        if record.location().module_path().starts_with("handlebars") {
            // This is a ugly, yet necessary hack. When logging, we use handlebars for templating.
            // But as the handlebars library itselfs logs via a normal logging macro ("debug!()"),
            // we have a recursion in our chain.
            //
            // To prevent this recursion, we return here.
            //
            // (As of handlebars 0.29.0 - please check whether you can update handlebars if you see
            // this. Hopefully the next version has a compiletime flag to disable logging)
            return;
        }

        let mut data = BTreeMap::new();

        {
            data.insert("level",        format!("{}", record.level()));
            data.insert("module_path",  String::from(record.location().module_path()));
            data.insert("file",         String::from(record.location().file()));
            data.insert("line",         format!("{}", record.location().line()));
            data.insert("target",       String::from(record.target()));
            data.insert("message",      format!("{}", record.args()));
        }

        let logtext = self
            .handlebars
            .render(&format!("{}", record.level()), &data)
            .unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e));

        let log_to_destination = |d: &LogDestination| match d {
            &LogDestination::Stderr => {
                let _ = write!(stderr(), "{}\n", logtext);
            },
            &LogDestination::File(ref arc_mutex_logdest) => {
                // if there is an error in the lock, we cannot do anything. So we ignore it here.
                let _ = arc_mutex_logdest
                    .deref()
                    .lock()
                    .map(|mut logdest| {
                        write!(logdest, "{}\n", logtext)
                    });
            }
        };

        // hack to get the right target configuration.
        // If there is no element here, we use the empty string which automatically drops through
        // to the unwrap_or_else() case
        let record_target = record
            .target()
            .split("::")
            .next()
            .unwrap_or("");

        self.module_settings
            .get(record_target)
            .map(|module_setting| {
                let set = module_setting.enabled &&
                    module_setting.level.unwrap_or(self.global_loglevel) >= record.level();

                if set {
                    module_setting.destinations.as_ref().map(|destinations| for d in destinations {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    });

                    for d in self.global_destinations.iter() {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    }
                }
            })
        .unwrap_or_else(|| {
            if self.global_loglevel >= record.level() {
                // Yes, we log
                for d in self.global_destinations.iter() {
                    // If there's an error, we cannot do anything, can we?
                    let _ = log_to_destination(&d);
                }
            }
        });
    }
}

fn match_log_level_str(s: &str) -> Result<LogLevel> {
    match s {
        "trace" => Ok(LogLevel::Trace),
        "debug" => Ok(LogLevel::Debug),
        "info"  => Ok(LogLevel::Info),
        "warn"  => Ok(LogLevel::Warn),
        "error" => Ok(LogLevel::Error),
        _       => return Err(RE::from_kind(EK::InvalidLogLevelSpec)),
    }
}

fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>)
    -> Result<LogLevel>
{
    fn get_arg_loglevel(matches: &ArgMatches) -> Result<Option<LogLevel>> {
        if matches.is_present(Runtime::arg_debugging_name()) {
            return Ok(Some(LogLevel::Debug))
        }

        if matches.is_present(Runtime::arg_verbosity_name()) {
            return Ok(Some(LogLevel::Info))
        }

        match matches.value_of(Runtime::arg_verbosity_name()) {
            Some(v) => match_log_level_str(v).map(Some),
            None    => Ok(None),
        }
    }

    if let Some(cfg) = config {
        let cfg_loglevel = match cfg.read("imag.logging.level") {
            Ok(Some(&Value::String(ref s))) => match_log_level_str(s),
            Ok(Some(_)) => {
                let path = "imag.logging.level".to_owned();
                let ty   = "String";
                return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => return Err(RE::from_kind(EK::GlobalLogLevelConfigMissing)),
            Err(e)      => return Err(e).map_err(From::from),
        }?;

        if let Some(cli_loglevel) = get_arg_loglevel(matches)? {
            if cli_loglevel > cfg_loglevel {
                return Ok(cli_loglevel)
            }
        }

        Ok(cfg_loglevel)

    } else {
        get_arg_loglevel(matches).map(|o| o.unwrap_or(LogLevel::Info))
    }
}

fn translate_destination(raw: &str) -> Result<LogDestination> {
    use std::fs::OpenOptions;

    match raw {
        "-" => Ok(LogDestination::Stderr),
        other => {
            OpenOptions::new()
                .append(true)
                .create(true)
                .open(other)
                .map(Mutex::new)
                .map(Arc::new)
                .map(LogDestination::File)
                .chain_err(|| EK::IOLogFileOpenError)
        }
    }
}

fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
    raw.iter()
        .fold(Ok(vec![]), |acc, val| {
            acc.and_then(|mut v| {
                let dest = match *val {
                    Value::String(ref s) => translate_destination(s)?,
                    _ => {
                        let path = "imag.logging.modules.<mod>.destinations".to_owned();
                        let ty   = "Array<String>";
                        return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                    },
                };
                v.push(dest);
                Ok(v)
            })
        })
}

fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
    -> Result<Vec<LogDestination>>
{

    match config {
        Some(cfg) => match cfg.read("imag.logging.destinations") {
            Ok(Some(&Value::Array(ref a))) => translate_destinations(a),
            Ok(Some(_)) => {
                let path = "imag.logging.destinations".to_owned();
                let ty   = "Array";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => Err(RE::from_kind(EK::GlobalDestinationConfigMissing)),
            Err(e)      => Err(e).map_err(From::from),
        },
        None => {
            if let Some(values) = matches.value_of(Runtime::arg_logdest_name()) {
                // parse logdest specification from commandline

                values.split(",")
                    .fold(Ok(vec![]), move |acc, dest| {
                        acc.and_then(|mut v| {
                            v.push(translate_destination(dest)?);
                            Ok(v)
                        })
                    })
            } else {
                Ok(vec![ LogDestination::default() ])
            }
        }
    }
}

macro_rules! aggregate_global_format {
    ($read_str:expr, $error_kind_if_missing:expr, $config:expr) => {
        match try!($config.ok_or(RE::from_kind($error_kind_if_missing))).read($read_str) {
            Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
            Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError($read_str.to_owned(), "String"))),
            Ok(None)    => Err(RE::from_kind($error_kind_if_missing)),
            Err(e)      => Err(e).map_err(From::from),
        }
    };
}

fn aggregate_global_format_trace(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.trace",
                            EK::ConfigMissingLoggingFormatTrace,
                            config)
}

fn aggregate_global_format_debug(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.debug",
                            EK::ConfigMissingLoggingFormatDebug,
                            config)
}

fn aggregate_global_format_info(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.info",
                            EK::ConfigMissingLoggingFormatInfo,
                            config)
}

fn aggregate_global_format_warn(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.warn",
                            EK::ConfigMissingLoggingFormatWarn,
                            config)
}

fn aggregate_global_format_error(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.error",
                            EK::ConfigMissingLoggingFormatError,
                            config)
}

fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
    -> Result<BTreeMap<ModuleName, ModuleSettings>>
{
    match config {
        Some(cfg) => match cfg.read("imag.logging.modules") {
            Ok(Some(&Value::Table(ref t))) => {
                // translate the module settings from the table `t`
                let mut settings = BTreeMap::new();

                for (module_name, v) in t {
                    let destinations = match v.read("destinations")? {
                        Some(&Value::Array(ref a)) => Some(translate_destinations(a)?),
                        None                       => None,
                        Some(_) => {
                            let path = "imag.logging.modules.<mod>.destinations".to_owned();
                            let ty = "Array";
                            return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                    };

                    let level = match v.read("level")? {
                        Some(&Value::String(ref s)) => Some(match_log_level_str(s)?),
                        None                        => None,
                        Some(_) => {
                            let path = "imag.logging.modules.<mod>.level".to_owned();
                            let ty = "String";
                            return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                    };

                    let enabled = match v.read("enabled")? {
                        Some(&Value::Boolean(b)) => b,
                        None                     => false,
                        Some(_) => {
                            let path = "imag.logging.modules.<mod>.enabled".to_owned();
                            let ty = "Boolean";
                            return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                    };

                    let module_settings = ModuleSettings {
                        enabled: enabled,
                        level: level,
                        destinations: destinations,
                    };

                    // We don't care whether there was a value, we override it.
                    let _ = settings.insert(module_name.to_owned(), module_settings);
                }

                Ok(settings)
            },
            Ok(Some(_)) => {
                let path = "imag.logging.modules".to_owned();
                let ty = "Table";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => {
                // No modules configured. This is okay!
                Ok(BTreeMap::new())
            },
            Err(e) => Err(e).map_err(From::from),
        },
        None => {
            write!(stderr(), "No Configuration.").ok();
            write!(stderr(), "cannot find module-settings for logging.").ok();
            write!(stderr(), "Will use global defaults").ok();

            Ok(BTreeMap::new())
        }
    }
}

<|endoftext|>


<commit_before>// run-pass

#![feature(const_int_checked)]
#![feature(const_int_euclidean)]
#![feature(const_int_overflowing)]
#![feature(const_int_saturating)]
#![feature(const_int_wrapping)]

macro_rules! assert_same_const {
    ($(const $ident:ident: $ty:ty = $exp:expr;)+) => {
        $(const $ident: $ty = $exp;)+

        pub fn main() {
            $(assert_eq!($exp, $ident);)+
        }
    }
}

assert_same_const! {
    // `const_int_checked`
    const CHECKED_ADD_I32_A: Option<i32> = 5i32.checked_add(2);
    const CHECKED_ADD_I8_A: Option<i8> = 127i8.checked_add(2);
    const CHECKED_ADD_U8_A: Option<u8> = 255u8.checked_add(2);

    const CHECKED_SUB_I32_A: Option<i32> = 5i32.checked_sub(2);
    const CHECKED_SUB_I8_A: Option<i8> = (-127 as i8).checked_sub(2);
    const CHECKED_SUB_U8_A: Option<u8> = 1u8.checked_sub(2);

    const CHECKED_MUL_I32_A: Option<i32> = 5i32.checked_mul(7777);
    const CHECKED_MUL_I8_A: Option<i8> = (-127 as i8).checked_mul(-99);
    const CHECKED_MUL_U8_A: Option<u8> = 1u8.checked_mul(3);

    const CHECKED_DIV_I32_A: Option<i32> = 5i32.checked_div(7777);
    const CHECKED_DIV_I8_A: Option<i8> = (-127 as i8).checked_div(-99);
    const CHECKED_DIV_U8_A: Option<u8> = 1u8.checked_div(3);

    const CHECKED_REM_I32_A: Option<i32> = 5i32.checked_rem(7777);
    const CHECKED_REM_I8_A: Option<i8> = (-127 as i8).checked_rem(-99);
    const CHECKED_REM_U8_A: Option<u8> = 1u8.checked_rem(3);
    const CHECKED_REM_U8_B: Option<u8> = 1u8.checked_rem(0);

    const CHECKED_NEG_I32_A: Option<i32> = 5i32.checked_neg();
    const CHECKED_NEG_I8_A: Option<i8> = (-127 as i8).checked_neg();
    const CHECKED_NEG_U8_A: Option<u8> = 1u8.checked_neg();
    const CHECKED_NEG_U8_B: Option<u8> = u8::min_value().checked_neg();

    const CHECKED_SHL_I32_A: Option<i32> = 5i32.checked_shl(77777);
    const CHECKED_SHL_I8_A: Option<i8> = (-127 as i8).checked_shl(2);
    const CHECKED_SHL_U8_A: Option<u8> = 1u8.checked_shl(8);
    const CHECKED_SHL_U8_B: Option<u8> = 1u8.checked_shl(0);

    const CHECKED_SHR_I32_A: Option<i32> = 5i32.checked_shr(77777);
    const CHECKED_SHR_I8_A: Option<i8> = (-127 as i8).checked_shr(2);
    const CHECKED_SHR_U8_A: Option<u8> = 1u8.checked_shr(8);
    const CHECKED_SHR_U8_B: Option<u8> = 1u8.checked_shr(0);

    const CHECKED_ABS_I32_A: Option<i32> = 5i32.checked_abs();
    const CHECKED_ABS_I8_A: Option<i8> = (-127 as i8).checked_abs();
    const CHECKED_ABS_I8_B: Option<i8> = 1i8.checked_abs();
    const CHECKED_ABS_I8_C: Option<i8> = i8::min_value().checked_abs();

    // `const_int_overflowing`
    const DIV_A: (i8, bool) = 8i8.overflowing_div(2);
    const DIV_B: (i8, bool) = 8i8.overflowing_div(3);
    const DIV_C: (i8, bool) = i8::min_value().overflowing_div(-1i8);
    const DIV_D: (u8, bool) = 8u8.overflowing_div(2);
    const DIV_E: (u8, bool) = 8u8.overflowing_div(3);

    const REM_A: (i8, bool) = 8i8.overflowing_rem(2);
    const REM_B: (i8, bool) = 8i8.overflowing_rem(3);
    const REM_C: (i8, bool) = i8::min_value().overflowing_rem(-1i8);
    const REM_D: (u8, bool) = 8u8.overflowing_rem(2);
    const REM_E: (u8, bool) = 8u8.overflowing_rem(3);

    // `const_int_saturating`
    const ADD_INT_U32_NO: u32 = (42 as u32).saturating_add(2);
    const ADD_INT_U32: u32 = u32::max_value().saturating_add(1);
    const ADD_INT_U128: u128 = u128::max_value().saturating_add(1);
    const ADD_INT_I128: i128 = i128::max_value().saturating_add(1);
    const ADD_INT_I128_NEG: i128 = i128::min_value().saturating_add(-1);

    const SUB_INT_U32_NO: u32 = (42 as u32).saturating_sub(2);
    const SUB_INT_U32: u32 = (1 as u32).saturating_sub(2);
    const SUB_INT_I32_NO: i32 = (-42 as i32).saturating_sub(2);
    const SUB_INT_I32_NEG: i32 = i32::min_value().saturating_sub(1);
    const SUB_INT_I32_POS: i32 = i32::max_value().saturating_sub(-1);
    const SUB_INT_U128: u128 = (0 as u128).saturating_sub(1);
    const SUB_INT_I128_NEG: i128 = i128::min_value().saturating_sub(1);
    const SUB_INT_I128_POS: i128 = i128::max_value().saturating_sub(-1);

    const MUL_INT_U32_NO: u32 = (42 as u32).saturating_mul(2);
    const MUL_INT_U32: u32 = (1 as u32).saturating_mul(2);
    const MUL_INT_I32_NO: i32 = (-42 as i32).saturating_mul(2);
    const MUL_INT_I32_NEG: i32 = i32::min_value().saturating_mul(1);
    const MUL_INT_I32_POS: i32 = i32::max_value().saturating_mul(2);
    const MUL_INT_U128: u128 = (0 as u128).saturating_mul(1);
    const MUL_INT_I128_NEG: i128 = i128::min_value().saturating_mul(2);
    const MUL_INT_I128_POS: i128 = i128::max_value().saturating_mul(2);

    const NEG_INT_I8: i8 = (-42i8).saturating_neg();
    const NEG_INT_I8_B: i8 = i8::min_value().saturating_neg();
    const NEG_INT_I32: i32 = i32::min_value().saturating_neg();
    const NEG_INT_I32_B: i32 = i32::max_value().saturating_neg();
    const NEG_INT_I128: i128 = i128::min_value().saturating_neg();
    const NEG_INT_I128_B: i128 = i128::max_value().saturating_neg();

    const ABS_INT_I8_A: i8 = 4i8.saturating_abs();
    const ABS_INT_I8_B: i8 = -4i8.saturating_abs();
    const ABS_INT_I8_C: i8 = i8::min_value().saturating_abs();
    const ABS_INT_I32_A: i32 = 4i32.saturating_abs();
    const ABS_INT_I32_B: i32 = -4i32.saturating_abs();
    const ABS_INT_I32_C: i32 = i32::min_value().saturating_abs();
    const ABS_INT_I128_A: i128 = 4i128.saturating_abs();
    const ABS_INT_I128_B: i128 = -4i128.saturating_abs();
    const ABS_INT_I128_C: i128 = i128::min_value().saturating_abs();

    // `const_int_euclidean`
    const CHECKED_DIV_I32_A: Option<i32> = 5i32.checked_div_euclid(7777);
    const CHECKED_DIV_I8_A: Option<i8> = (-127 as i8).checked_div_euclid(-99);
    const CHECKED_DIV_I8_B: Option<i8> = (-127 as i8).checked_div_euclid(1);
    const CHECKED_DIV_I8_C: Option<i8> = i8::min_value().checked_div_euclid(-1);
    const CHECKED_DIV_U8_A: Option<u8> = 1u8.checked_div_euclid(3);

    const CHECKED_REM_I32_A: Option<i32> = 5i32.checked_rem_euclid(7777);
    const CHECKED_REM_I8_A: Option<i8> = (-127 as i8).checked_rem_euclid(-99);
    const CHECKED_REM_I8_B: Option<i8> = (-127 as i8).checked_rem_euclid(0);
    const CHECKED_REM_I8_C: Option<i8> = i8::min_value().checked_rem_euclid(-1);
    const CHECKED_REM_U8_A: Option<u8> = 1u8.checked_rem_euclid(3);

    const WRAPPING_DIV_I32_A: i32 = 5i32.wrapping_div_euclid(7777);
    const WRAPPING_DIV_I8_A: i8 = (-127 as i8).wrapping_div_euclid(-99);
    const WRAPPING_DIV_I8_B: i8 = (-127 as i8).wrapping_div_euclid(1);
    const WRAPPING_DIV_I8_C: i8 = i8::min_value().wrapping_div_euclid(-1);
    const WRAPPING_DIV_U8_A: u8 = 1u8.wrapping_div_euclid(3);

    const WRAPPING_REM_I32_A: i32 = 5i32.wrapping_rem_euclid(7777);
    const WRAPPING_REM_I8_A: i8 = (-127 as i8).wrapping_rem_euclid(-99);
    const WRAPPING_REM_I8_B: i8 = (-127 as i8).wrapping_rem_euclid(1);
    const WRAPPING_REM_I8_C: i8 = i8::min_value().wrapping_rem_euclid(-1);
    const WRAPPING_REM_U8_A: u8 = 1u8.wrapping_rem_euclid(3);

    const OVERFLOWING_DIV_I32_A: (i32, bool) = 5i32.overflowing_div_euclid(7777);
    const OVERFLOWING_DIV_I8_A: (i8, bool) = (-127 as i8).overflowing_div_euclid(-99);
    const OVERFLOWING_DIV_I8_B: (i8, bool) = (-127 as i8).overflowing_div_euclid(1);
    const OVERFLOWING_DIV_I8_C: (i8, bool) = i8::min_value().overflowing_div_euclid(-1);
    const OVERFLOWING_DIV_U8_A: (u8, bool) = 1u8.overflowing_div_euclid(3);

    const OVERFLOWING_REM_I32_A: (i32, bool) = 5i32.overflowing_rem_euclid(7777);
    const OVERFLOWING_REM_I8_A: (i8, bool) = (-127 as i8).overflowing_rem_euclid(-99);
    const OVERFLOWING_REM_I8_B: (i8, bool) = (-127 as i8).overflowing_rem_euclid(1);
    const OVERFLOWING_REM_I8_C: (i8, bool) = i8::min_value().overflowing_rem_euclid(-1);
    const OVERFLOWING_REM_U8_A: (u8, bool) = 1u8.overflowing_rem_euclid(3);

    // `const_int_wrapping`
    const DIV_A: i8 = 8i8.wrapping_div(2);
    const DIV_B: i8 = 8i8.wrapping_div(3);
    const DIV_C: i8 = i8::min_value().wrapping_div(-1i8);
    const DIV_D: u8 = 8u8.wrapping_div(2);
    const DIV_E: u8 = 8u8.wrapping_div(3);

    const REM_A: i8 = 8i8.wrapping_rem(2);
    const REM_B: i8 = 8i8.wrapping_rem(3);
    const REM_C: i8 = i8::min_value().wrapping_rem(-1i8);
    const REM_D: u8 = 8u8.wrapping_rem(2);
    const REM_E: u8 = 8u8.wrapping_rem(3);
}
<commit_msg>Fix test<commit_after>// run-pass

#![feature(saturating_neg)]
#![feature(const_checked_int_methods)]
#![feature(const_euclidean_int_methods)]
#![feature(const_overflowing_int_methods)]
#![feature(const_saturating_int_methods)]
#![feature(const_wrapping_int_methods)]

use std::i8;

macro_rules! suite {
    ($(
        $fn:ident -> $ty:ty { $( $label:ident : $expr:expr, $result:expr; )* }
    )*) => { $(
        fn $fn() {
            $(
                const $label: $ty = $expr;
                assert_eq!($label, $result);
            )*
        }
    )* }
}

suite!(
    checked -> Option<i8> {
        // `const_checked_int_methods`
        C1: 5i8.checked_add(2), Some(7);
        C2: 127i8.checked_add(2), None;

        C3: 5i8.checked_sub(2), Some(3);
        C4: (-127i8).checked_sub(2), None;

        C5: 1i8.checked_mul(3), Some(3);
        C6: 5i8.checked_mul(122), None;
        C7: (-127i8).checked_mul(-99), None;

        C8: (i8::min_value() + 1).checked_div(-1), Some(127);
        C9: i8::min_value().checked_div(-1), None;
        C10: 1i8.checked_div(0), None;

        C11: 5i8.checked_rem(2), Some(1);
        C12: 5i8.checked_rem(0), None;
        C13: i8::MIN.checked_rem(-1), None;

        C14: 5i8.checked_neg(), Some(-5);
        C15: i8::MIN.checked_neg(), None;

        C16: 0x1i8.checked_shl(4), Some(0x10);
        C17: 0x1i8.checked_shl(129), None;

        C18: 0x10i8.checked_shr(4), Some(0x1);
        C19: 0x10i8.checked_shr(128), None;

        C20: (-5i8).checked_abs(), Some(5);
        C21: i8::MIN.checked_abs(), None;

        // `const_euclidean_int_methods`
        C22: (i8::min_value() + 1).checked_div_euclid(-1), Some(127);
        C23: i8::min_value().checked_div_euclid(-1), None;
        C24: (1i8).checked_div_euclid(0), None;

        C25: 5i8.checked_rem_euclid(2), Some(1);
        C26: 5i8.checked_rem_euclid(0), None;
        C27: i8::MIN.checked_rem_euclid(-1), None;
    }

    saturating_and_wrapping -> i8 {
        // `const_saturating_int_methods`
        C28: 100i8.saturating_add(1), 101;
        C29: i8::max_value().saturating_add(100), i8::max_value();
        C30: i8::min_value().saturating_add(-1), i8::min_value();

        C31: 100i8.saturating_sub(127), -27;
        C32: i8::min_value().saturating_sub(100), i8::min_value();
        C33: i8::max_value().saturating_sub(-1), i8::max_value();

        C34: 10i8.saturating_mul(12), 120;
        C35: i8::MAX.saturating_mul(10), i8::MAX;
        C36: i8::MIN.saturating_mul(10), i8::MIN;

        C37: 100i8.saturating_neg(), -100;
        C38: (-100i8).saturating_neg(), 100;
        C39: i8::min_value().saturating_neg(), i8::max_value();
        C40: i8::max_value().saturating_neg(), i8::min_value() + 1;

        C57: 100i8.saturating_abs(), 100;
        C58: (-100i8).saturating_abs(), 100;
        C59: i8::min_value().saturating_abs(), i8::max_value();
        C60: (i8::min_value() + 1).saturating_abs(), i8::max_value();

        // `const_wrapping_int_methods`
        C41: 100i8.wrapping_div(10), 10;
        C42: (-128i8).wrapping_div(-1), -128;

        C43: 100i8.wrapping_rem(10), 0;
        C44: (-128i8).wrapping_rem(-1), 0;

        // `const_euclidean_int_methods`
        C45: 100i8.wrapping_div_euclid(10), 10;
        C46: (-128i8).wrapping_div_euclid(-1), -128;

        C47: 100i8.wrapping_rem_euclid(10), 0;
        C48: (-128i8).wrapping_rem_euclid(-1), 0;
    }

    overflowing -> (i8, bool) {
        // `const_overflowing_int_methods`
        C49: 5i8.overflowing_div(2), (2, false);
        C50: i8::MIN.overflowing_div(-1), (i8::MIN, true);

        C51: 5i8.overflowing_rem(2), (1, false);
        C52: i8::MIN.overflowing_rem(-1), (0, true);

        // `const_euclidean_int_methods`
        C53: 5i8.overflowing_div_euclid(2), (2, false);
        C54: i8::MIN.overflowing_div_euclid(-1), (i8::MIN, true);

        C55: 5i8.overflowing_rem_euclid(2), (1, false);
        C56: i8::MIN.overflowing_rem_euclid(-1), (0, true);

    }
);

fn main() {
   checked();
   saturating_and_wrapping();
   overflowing();
}
<|endoftext|>


<commit_before>use base_db::fixture::WithFixture;

use crate::test_db::TestDB;

fn check_diagnostics(ra_fixture: &str) {
    let db: TestDB = TestDB::with_files(ra_fixture);
    db.check_diagnostics();
}

#[test]
fn unresolved_import() {
    check_diagnostics(
        r"
        use does_exist;
        use does_not_exist;
          //^^^^^^^^^^^^^^ unresolved import

        mod does_exist {}
        ",
    );
}

#[test]
fn unresolved_import_in_use_tree() {
    // Only the relevant part of a nested `use` item should be highlighted.
    check_diagnostics(
        r"
        use does_exist::{Exists, DoesntExist};
                               //^^^^^^^^^^^ unresolved import

        use {does_not_exist::*, does_exist};
           //^^^^^^^^^^^^^^^^^ unresolved import

        use does_not_exist::{
            a,
          //^ unresolved import
            b,
          //^ unresolved import
            c,
          //^ unresolved import
        };

        mod does_exist {
            pub struct Exists;
        }
        ",
    );
}

#[test]
fn unresolved_extern_crate() {
    check_diagnostics(
        r"
        //- /main.rs crate:main deps:core
        extern crate core;
          extern crate doesnotexist;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
        //- /lib.rs crate:core
        ",
    );
}

#[test]
fn extern_crate_self_as() {
    cov_mark::check!(extern_crate_self_as);
    check_diagnostics(
        r"
        //- /lib.rs
          extern crate doesnotexist;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
        // Should not error.
        extern crate self as foo;
        struct Foo;
        use foo::Foo as Bar;
        ",
    );
}

#[test]
fn dedup_unresolved_import_from_unresolved_crate() {
    check_diagnostics(
        r"
        //- /main.rs crate:main
        mod a {
            extern crate doesnotexist;
          //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate

            // Should not error, since we already errored for the missing crate.
            use doesnotexist::{self, bla, *};

            use crate::doesnotexist;
              //^^^^^^^^^^^^^^^^^^^ unresolved import
        }

        mod m {
            use super::doesnotexist;
              //^^^^^^^^^^^^^^^^^^^ unresolved import
        }
        ",
    );
}

#[test]
fn unresolved_module() {
    check_diagnostics(
        r"
        //- /lib.rs
        mod foo;
          mod bar;
        //^^^^^^^^ unresolved module
        mod baz {}
        //- /foo.rs
        ",
    );
}

#[test]
fn inactive_item() {
    // Additional tests in `cfg` crate. This only tests disabled cfgs.

    check_diagnostics(
        r#"
        //- /lib.rs
          #[cfg(no)] pub fn f() {}
        //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled

          #[cfg(no)] #[cfg(no2)] mod m;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled

          #[cfg(all(not(a), b))] enum E {}
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled

          #[cfg(feature = "std")] use std;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
        "#,
    );
}

/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
#[test]
fn inactive_via_cfg_attr() {
    cov_mark::check!(cfg_attr_active);
    check_diagnostics(
        r#"
        //- /lib.rs
          #[cfg_attr(not(never), cfg(no))] fn f() {}
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled

          #[cfg_attr(not(never), cfg(not(no)))] fn f() {}

          #[cfg_attr(never, cfg(no))] fn g() {}
        "#,
    );
}
<commit_msg>Extend cfg_attr test<commit_after>use base_db::fixture::WithFixture;

use crate::test_db::TestDB;

fn check_diagnostics(ra_fixture: &str) {
    let db: TestDB = TestDB::with_files(ra_fixture);
    db.check_diagnostics();
}

#[test]
fn unresolved_import() {
    check_diagnostics(
        r"
        use does_exist;
        use does_not_exist;
          //^^^^^^^^^^^^^^ unresolved import

        mod does_exist {}
        ",
    );
}

#[test]
fn unresolved_import_in_use_tree() {
    // Only the relevant part of a nested `use` item should be highlighted.
    check_diagnostics(
        r"
        use does_exist::{Exists, DoesntExist};
                               //^^^^^^^^^^^ unresolved import

        use {does_not_exist::*, does_exist};
           //^^^^^^^^^^^^^^^^^ unresolved import

        use does_not_exist::{
            a,
          //^ unresolved import
            b,
          //^ unresolved import
            c,
          //^ unresolved import
        };

        mod does_exist {
            pub struct Exists;
        }
        ",
    );
}

#[test]
fn unresolved_extern_crate() {
    check_diagnostics(
        r"
        //- /main.rs crate:main deps:core
        extern crate core;
          extern crate doesnotexist;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
        //- /lib.rs crate:core
        ",
    );
}

#[test]
fn extern_crate_self_as() {
    cov_mark::check!(extern_crate_self_as);
    check_diagnostics(
        r"
        //- /lib.rs
          extern crate doesnotexist;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
        // Should not error.
        extern crate self as foo;
        struct Foo;
        use foo::Foo as Bar;
        ",
    );
}

#[test]
fn dedup_unresolved_import_from_unresolved_crate() {
    check_diagnostics(
        r"
        //- /main.rs crate:main
        mod a {
            extern crate doesnotexist;
          //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate

            // Should not error, since we already errored for the missing crate.
            use doesnotexist::{self, bla, *};

            use crate::doesnotexist;
              //^^^^^^^^^^^^^^^^^^^ unresolved import
        }

        mod m {
            use super::doesnotexist;
              //^^^^^^^^^^^^^^^^^^^ unresolved import
        }
        ",
    );
}

#[test]
fn unresolved_module() {
    check_diagnostics(
        r"
        //- /lib.rs
        mod foo;
          mod bar;
        //^^^^^^^^ unresolved module
        mod baz {}
        //- /foo.rs
        ",
    );
}

#[test]
fn inactive_item() {
    // Additional tests in `cfg` crate. This only tests disabled cfgs.

    check_diagnostics(
        r#"
        //- /lib.rs
          #[cfg(no)] pub fn f() {}
        //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled

          #[cfg(no)] #[cfg(no2)] mod m;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled

          #[cfg(all(not(a), b))] enum E {}
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled

          #[cfg(feature = "std")] use std;
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
        "#,
    );
}

/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
#[test]
fn inactive_via_cfg_attr() {
    cov_mark::check!(cfg_attr_active);
    check_diagnostics(
        r#"
        //- /lib.rs
          #[cfg_attr(not(never), cfg(no))] fn f() {}
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled

          #[cfg_attr(not(never), cfg(not(no)))] fn f() {}

          #[cfg_attr(never, cfg(no))] fn g() {}

          #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
        "#,
    );
}
<|endoftext|>