<commit_before>use crate::keyboard::{Error, KeyboardRegKey};
use crate::language::LanguageRegKey;
use crate::platform::*;
use crate::types::*;
use indexmap::IndexMap;
use registry::{Data, Hive, RegKey, Security};
use std::convert::{TryFrom, TryInto};

fn enabled_input_methods() -> InputList {
    let langs = crate::enabled_languages().unwrap();
    let mut imes: Vec<String> = vec![];
    for lang in langs {
        imes.append(&mut bcp47langs::get_user_language_input_methods(&lang).unwrap());
    }
    InputList::try_from(imes).unwrap()
}

fn log_important_regkeys() {
    log::trace!("  == REGKEY WATCH ==");
    log::trace!("");

    let substitutes_key = Hive::CurrentUser
        .open(r"Keyboard Layout\Substitutes", Security::Read)
        .unwrap();

    let user_profile_key = Hive::CurrentUser
        .create(r"Control Panel\International\User Profile", Security::Read)
        .unwrap();

    let preload_key = Hive::CurrentUser
        .open(r"Keyboard Layout\Preload", Security::Read)
        .unwrap();

    let user_profile_subkeys = user_profile_key
        .keys()
        .filter_map(Result::ok)
        .map(|x| x.open(Security::Read))
        .filter_map(Result::ok);

    for key in user_profile_subkeys {
        log::trace!("{}", key);
        for value in key.values().filter_map(Result::ok) {
            let (inner_name, inner_data) = value.into_inner();

            let name = inner_name.to_string_lossy().to_string();
            let data = format!("{}", inner_data);
            log::trace!("  '{}' = '{}'", name, data);
        }
        log::trace!("");
    }

    log::trace!("{}", user_profile_key);
    for value in user_profile_key.values().filter_map(Result::ok) {
        let (inner_name, inner_data) = value.into_inner();

        let name = inner_name.to_string_lossy().to_string();
        let data = format!("{}", inner_data);
        log::trace!("  '{}' = '{}'", name, data);
    }
    log::trace!("");

    log::trace!("{}", preload_key);
    for value in preload_key.values().filter_map(Result::ok) {
        let (inner_name, inner_data) = value.into_inner();

        let name = inner_name.to_string_lossy().to_string();
        let data = format!("{}", inner_data);
        log::trace!("  '{}' = '{}'", name, data);
    }
    log::trace!("");

    log::trace!("{}", substitutes_key);
    for value in substitutes_key.values().filter_map(Result::ok) {
        let (inner_name, inner_data) = value.into_inner();

        let name = inner_name.to_string_lossy().to_string();
        let data = format!("{}", inner_data);
        log::trace!("  '{}' = '{}'", name, data);
    }
    log::trace!("");

    log::trace!("  == If you see a suspicious REGKEY in your neighbourhood, call 112 ==")
}

pub fn enable(tag: &str, product_code: &str, lang_name: Option<&str>) -> Result<(), Error> {
    log::info!("Enabling '{}' with product code '{}'", tag, product_code);
    log::info!("Lang name: {:?}", lang_name);

    log_important_regkeys();

    log::info!("Regenerating registry for keyboards, just in case.");
    regenerate_registry();

    let original_layout = winuser::current_keyboard();
    let record = match KeyboardRegKey::find_by_product_code(product_code) {
        Some(v) => v,
        None => return Err(Error::NotFound),
    };

    // Check language is enabled or LCID check will fail
    log::info!("Enabling language by tag");
    crate::enable_language(tag).unwrap();

    // Get all languages and keyboards
    let mut keyboards = crate::win8::enabled_keyboards()
        .unwrap()
        .into_iter()
        .collect::<IndexMap<_, _>>();
    log::trace!("Keyboards: {:?}", &keyboards);

    log::trace!("bcp47langs::lcid_from_bcp47(tag).unwrap()");
    let lcid = bcp47langs::lcid_from_bcp47(tag).unwrap();
    let tip = format!("{:04X}:{}", lcid, record.regkey_id());

    log::debug!("Injecting into keyboard list: {}", &tip);

    keyboards
        .entry(tag.to_string())
        .and_modify(|x| x.push(tip.clone()))
        .or_insert(vec![tip.clone()]);

    log::debug!("Keyboard list: {:?}", &keyboards);
    log_important_regkeys();

    // Remove all inputs internal
    // log::trace!("bcp47langs::remove_inputs_for_all_languages().unwrap();");
    // bcp47langs::remove_inputs_for_all_languages().unwrap();
    // log_important_regkeys();

    // Build input method list
    let mut first = true;
    for (lang_tag, tips) in keyboards {
        let _lcid = match bcp47langs::lcid_from_bcp47(&lang_tag) {
            Some(v) => v,
            None => {
                log::error!("No LCID for {}; continuing!", &lang_tag);
                continue;
            }
        };

        log::debug!("Tip for {}: {:?}", lang_tag, &tips);
        let inputs = InputList::try_from(tips).unwrap();
        log::debug!("Input list for {}: {:?}", lang_tag, &inputs);

        // Flag 256 seems to clear everything.
        // let flag = if first { 256 } else { 0 };
        let flag = 0;
        first = false;

        input::install_layout(inputs, flag).unwrap();
    }
    log_important_regkeys();

    log::info!("Regenerating registry for keyboards");
    regenerate_registry();
    log_important_regkeys();

    log::info!("Resetting current active keyboard");
    winuser::set_active_keyboard(original_layout);
    Ok(())
}

pub fn remove_invalid_kbids() {
    let installed_imes: Vec<String> = KeyboardRegKey::installed()
        .iter()
        .map(|x| x.regkey_id().to_owned())
        .collect();

    let enabled_imes = enabled_input_methods();
    let filtered_imes: Vec<InputListItem> = enabled_imes
        .into_inner()
        .into_iter()
        .filter(|i| {
            let kbid = i.kbid().to_string().to_lowercase();
            // Only handle custom keyboards
            if kbid.starts_with("a") {
                return true;
            }
            installed_imes.contains(&kbid)
        })
        .collect();

    bcp47langs::remove_inputs_for_all_languages().unwrap();
    input::install_layout(InputList::from(filtered_imes), 0).unwrap();
}

pub fn regenerate_registry() {
    let user_profile_key = Hive::CurrentUser
        .open(r"Control Panel\International\User Profile", Security::Read)
        .unwrap();
    let substitutes_key = Hive::CurrentUser
        .open(
            r"Keyboard Layout\Substitutes",
            Security::Read | Security::Write,
        )
        .unwrap();
    let preload_key = Hive::CurrentUser
        .open(r"Keyboard Layout\Preload", Security::Read | Security::Write)
        .unwrap();

    regenerate_given_registry(user_profile_key, substitutes_key, preload_key);
}

fn enable_default_user_lang(tag: &str) {
    let default_user_registry = registry::Hive::load_file(
        r"C:\Users\Default\NTUSER.DAT",
        Security::Read | Security::Write,
    )
    .unwrap();
    let language_key = LanguageRegKey::find_by_tag(tag).unwrap().regkey;
    let substitutes_key = Hive::CurrentUser
        .open(
            r"Keyboard Layout\Substitutes",
            Security::Read | Security::Write,
        )
        .unwrap();

    let default_user_profile_key = default_user_registry
        .create(
            format!(r"Control Panel\International\User Profile\{}", tag),
            Security::Read | Security::Write,
        )
        .unwrap();
    let default_substitutes_key = default_user_registry
        .open(
            r"Keyboard Layout\Substitutes",
            Security::Read | Security::Write,
        )
        .unwrap();

    for value in language_key.values() {
        let value = value.unwrap();
        default_user_profile_key
            .set_value(value.name(), value.data())
            .unwrap();
    }

    for value in substitutes_key.values() {
        let value = value.unwrap();
        default_substitutes_key
            .set_value(value.name(), value.data())
            .unwrap();
    }

    let preload_key = default_user_registry
        .open(r"Keyboard Layout\Preload", Security::Read | Security::Write)
        .unwrap();
    regenerate_given_registry(default_user_profile_key, substitutes_key, preload_key);
}

fn regenerate_given_registry(
    user_profile_key: RegKey,
    substitutes_key: RegKey,
    preload_key: RegKey,
) {
    log::debug!("regenerate_given_registry");
    let lang_keys: Vec<_> = user_profile_key
        .keys()
        .map(|k| k.unwrap().open(Security::Read).unwrap())
        .collect();

    log::trace!("Lang keys: {:?}", lang_keys);

    // Get known keyboard ids from Control Panel configured language list
    let keyboard_ids: Vec<_> = lang_keys
        .iter()
        .flat_map(|k| k.values())
        .map(|v| v.unwrap().name().to_string_lossy())
        .filter(|n| n.contains(":"))
        .map(|v| InputListItem::try_from(&*v))
        // .map(|n| n.split(":").last().unwrap().to_string())
        .filter_map(Result::ok)
        .collect();

    log::trace!("Keyboard IDs: {:?}", &keyboard_ids);

    // Get all substitutes into a list
    let subs = substitutes_key
        .values()
        .filter_map(Result::ok)
        .map(|x| {
            let x = x.into_inner();
            (x.0.to_string_lossy(), x.1.to_string())
        })
        .collect::<Vec<_>>();

    log::trace!("Substitutions: {:?}", &subs);

    // Clean up all invalid substitutions
    for (value_id, kbd_id) in subs.iter() {
        if keyboard_ids
            .iter()
            .find(|x| &format!("{:08x}", x.tip_id) == kbd_id)
            .is_none()
        {
            log::debug!("Deleting substitute: {:?}", value_id);
            substitutes_key.delete_value(value_id).unwrap();
        }
    }

    // Delete all preload values
    for value in preload_key.values() {
        let name = value.unwrap().name().to_owned();
        preload_key.delete_value(name).unwrap();
    }

    log::trace!("Cleared all preload keys");

    // Check if substitutes contains lang_id
    for (i, item) in keyboard_ids.iter().enumerate() {
        let lcid = format!("{:08x}", item.lang_id);
        let tip = format!("{:08x}", item.tip_id);

        let value = if subs.iter().find(|sub| sub.0 == lcid).is_some() {
            log::trace!("{}: Adding substitute lcid: {}", i + 1, &lcid);
            lcid.try_into().unwrap()
        } else {
            log::trace!("{}: Adding TIP: {}", i + 1, &tip);
            tip.try_into().unwrap()
        };

        preload_key
            .set_value((i + 1).to_string(), &Data::String(value))
            .unwrap();
    }
}
<commit_msg>Fix the order that preload keys are added<commit_after>use crate::keyboard::{Error, KeyboardRegKey};
use crate::language::LanguageRegKey;
use crate::platform::*;
use crate::types::*;
use indexmap::IndexMap;
use registry::{Data, Hive, RegKey, Security};
use std::convert::{TryFrom, TryInto};

fn enabled_input_methods() -> InputList {
    let langs = crate::enabled_languages().unwrap();
    let mut imes: Vec<String> = vec![];
    for lang in langs {
        imes.append(&mut bcp47langs::get_user_language_input_methods(&lang).unwrap());
    }
    InputList::try_from(imes).unwrap()
}

fn log_important_regkeys() {
    log::trace!("  == REGKEY WATCH ==");
    log::trace!("");

    let substitutes_key = Hive::CurrentUser
        .open(r"Keyboard Layout\Substitutes", Security::Read)
        .unwrap();

    let user_profile_key = Hive::CurrentUser
        .create(r"Control Panel\International\User Profile", Security::Read)
        .unwrap();

    let preload_key = Hive::CurrentUser
        .open(r"Keyboard Layout\Preload", Security::Read)
        .unwrap();

    let user_profile_subkeys = user_profile_key
        .keys()
        .filter_map(Result::ok)
        .map(|x| x.open(Security::Read))
        .filter_map(Result::ok);

    for key in user_profile_subkeys {
        log::trace!("{}", key);
        for value in key.values().filter_map(Result::ok) {
            let (inner_name, inner_data) = value.into_inner();

            let name = inner_name.to_string_lossy().to_string();
            let data = format!("{}", inner_data);
            log::trace!("  '{}' = '{}'", name, data);
        }
        log::trace!("");
    }

    log::trace!("{}", user_profile_key);
    for value in user_profile_key.values().filter_map(Result::ok) {
        let (inner_name, inner_data) = value.into_inner();

        let name = inner_name.to_string_lossy().to_string();
        let data = format!("{}", inner_data);
        log::trace!("  '{}' = '{}'", name, data);
    }
    log::trace!("");

    log::trace!("{}", preload_key);
    for value in preload_key.values().filter_map(Result::ok) {
        let (inner_name, inner_data) = value.into_inner();

        let name = inner_name.to_string_lossy().to_string();
        let data = format!("{}", inner_data);
        log::trace!("  '{}' = '{}'", name, data);
    }
    log::trace!("");

    log::trace!("{}", substitutes_key);
    for value in substitutes_key.values().filter_map(Result::ok) {
        let (inner_name, inner_data) = value.into_inner();

        let name = inner_name.to_string_lossy().to_string();
        let data = format!("{}", inner_data);
        log::trace!("  '{}' = '{}'", name, data);
    }
    log::trace!("");

    log::trace!("  == If you see a suspicious REGKEY in your neighbourhood, call 112 ==")
}

pub fn enable(tag: &str, product_code: &str, lang_name: Option<&str>) -> Result<(), Error> {
    log::info!("Enabling '{}' with product code '{}'", tag, product_code);
    log::info!("Lang name: {:?}", lang_name);

    log_important_regkeys();

    log::info!("Regenerating registry for keyboards, just in case.");
    regenerate_registry();

    let original_layout = winuser::current_keyboard();
    let record = match KeyboardRegKey::find_by_product_code(product_code) {
        Some(v) => v,
        None => return Err(Error::NotFound),
    };

    // Check language is enabled or LCID check will fail
    log::info!("Enabling language by tag");
    crate::enable_language(tag).unwrap();

    // Get all languages and keyboards
    let mut keyboards = crate::win8::enabled_keyboards()
        .unwrap()
        .into_iter()
        .collect::<IndexMap<_, _>>();
    log::trace!("Keyboards: {:?}", &keyboards);

    log::trace!("bcp47langs::lcid_from_bcp47(tag).unwrap()");
    let lcid = bcp47langs::lcid_from_bcp47(tag).unwrap();
    let tip = format!("{:04X}:{}", lcid, record.regkey_id());

    log::debug!("Injecting into keyboard list: {}", &tip);

    keyboards
        .entry(tag.to_string())
        .and_modify(|x| x.push(tip.clone()))
        .or_insert(vec![tip.clone()]);

    log::debug!("Keyboard list: {:?}", &keyboards);
    log_important_regkeys();

    // Remove all inputs internal
    // log::trace!("bcp47langs::remove_inputs_for_all_languages().unwrap();");
    // bcp47langs::remove_inputs_for_all_languages().unwrap();
    // log_important_regkeys();

    // Build input method list
    let mut first = true;
    for (lang_tag, tips) in keyboards {
        let _lcid = match bcp47langs::lcid_from_bcp47(&lang_tag) {
            Some(v) => v,
            None => {
                log::error!("No LCID for {}; continuing!", &lang_tag);
                continue;
            }
        };

        log::debug!("Tip for {}: {:?}", lang_tag, &tips);
        let inputs = InputList::try_from(tips).unwrap();
        log::debug!("Input list for {}: {:?}", lang_tag, &inputs);

        // Flag 256 seems to clear everything.
        // let flag = if first { 256 } else { 0 };
        let flag = 0;
        first = false;

        input::install_layout(inputs, flag).unwrap();
    }
    log_important_regkeys();

    log::info!("Regenerating registry for keyboards");
    regenerate_registry();
    log_important_regkeys();

    log::info!("Resetting current active keyboard");
    winuser::set_active_keyboard(original_layout);
    Ok(())
}

pub fn remove_invalid_kbids() {
    let installed_imes: Vec<String> = KeyboardRegKey::installed()
        .iter()
        .map(|x| x.regkey_id().to_owned())
        .collect();

    let enabled_imes = enabled_input_methods();
    let filtered_imes: Vec<InputListItem> = enabled_imes
        .into_inner()
        .into_iter()
        .filter(|i| {
            let kbid = i.kbid().to_string().to_lowercase();
            // Only handle custom keyboards
            if kbid.starts_with("a") {
                return true;
            }
            installed_imes.contains(&kbid)
        })
        .collect();

    bcp47langs::remove_inputs_for_all_languages().unwrap();
    input::install_layout(InputList::from(filtered_imes), 0).unwrap();
}

pub fn regenerate_registry() {
    let user_profile_key = Hive::CurrentUser
        .open(r"Control Panel\International\User Profile", Security::Read)
        .unwrap();
    let substitutes_key = Hive::CurrentUser
        .open(
            r"Keyboard Layout\Substitutes",
            Security::Read | Security::Write,
        )
        .unwrap();
    let preload_key = Hive::CurrentUser
        .open(r"Keyboard Layout\Preload", Security::Read | Security::Write)
        .unwrap();

    regenerate_given_registry(user_profile_key, substitutes_key, preload_key);
}

fn enable_default_user_lang(tag: &str) {
    let default_user_registry = registry::Hive::load_file(
        r"C:\Users\Default\NTUSER.DAT",
        Security::Read | Security::Write,
    )
    .unwrap();
    let language_key = LanguageRegKey::find_by_tag(tag).unwrap().regkey;
    let substitutes_key = Hive::CurrentUser
        .open(
            r"Keyboard Layout\Substitutes",
            Security::Read | Security::Write,
        )
        .unwrap();

    let default_user_profile_key = default_user_registry
        .create(
            format!(r"Control Panel\International\User Profile\{}", tag),
            Security::Read | Security::Write,
        )
        .unwrap();
    let default_substitutes_key = default_user_registry
        .open(
            r"Keyboard Layout\Substitutes",
            Security::Read | Security::Write,
        )
        .unwrap();

    for value in language_key.values() {
        let value = value.unwrap();
        default_user_profile_key
            .set_value(value.name(), value.data())
            .unwrap();
    }

    for value in substitutes_key.values() {
        let value = value.unwrap();
        default_substitutes_key
            .set_value(value.name(), value.data())
            .unwrap();
    }

    let preload_key = default_user_registry
        .open(r"Keyboard Layout\Preload", Security::Read | Security::Write)
        .unwrap();
    regenerate_given_registry(default_user_profile_key, substitutes_key, preload_key);
}

fn regenerate_given_registry(
    user_profile_key: RegKey,
    substitutes_key: RegKey,
    preload_key: RegKey,
) {
    log::debug!("regenerate_given_registry");
    let lang_keys: Vec<_> = user_profile_key
        .keys()
        .map(|k| k.unwrap().open(Security::Read).unwrap())
        .collect();

    log::trace!("Lang keys: {:?}", lang_keys);

    // Get known keyboard ids from Control Panel configured language list
    let mut keyboard_ids: Vec<_> = lang_keys
        .iter()
        .flat_map(|k| k.values())
        .map(|v| v.unwrap().name().to_string_lossy())
        .filter(|n| n.contains(":"))
        .map(|v| InputListItem::try_from(&*v))
        // .map(|n| n.split(":").last().unwrap().to_string())
        .filter_map(Result::ok)
        .collect();

    keyboard_ids.sort_by(|a, b| a.tip_id.cmp(&b.tip_id));
    keyboard_ids.sort_by(|a, b| a.lang_id.cmp(&b.lang_id));

    log::trace!("Keyboard IDs: {:?}", &keyboard_ids);

    // Get all substitutes into a list
    let subs = substitutes_key
        .values()
        .filter_map(Result::ok)
        .map(|x| {
            let x = x.into_inner();
            (x.0.to_string_lossy(), x.1.to_string())
        })
        .collect::<Vec<_>>();

    log::trace!("Substitutions: {:?}", &subs);

    // Clean up all invalid substitutions
    for (value_id, kbd_id) in subs.iter() {
        if keyboard_ids
            .iter()
            .find(|x| &format!("{:08x}", x.tip_id) == kbd_id)
            .is_none()
        {
            log::debug!("Deleting substitute: {:?}", value_id);
            substitutes_key.delete_value(value_id).unwrap();
        }
    }

    // Delete all preload values
    for value in preload_key.values() {
        let name = value.unwrap().name().to_owned();
        preload_key.delete_value(name).unwrap();
    }

    log::trace!("Cleared all preload keys");

    // Check if substitutes contains lang_id
    for (i, item) in keyboard_ids.iter().enumerate() {
        let lcid = format!("{:08x}", item.lang_id);
        let tip = format!("{:08x}", item.tip_id);

        let value = if let Some(sub) = subs.iter().filter(|sub| sub.1 == tip && sub.0[4..] == lcid[4..]).nth(0) {
            log::trace!("{}: Adding substitute lcid: {}", i + 1, &sub.0);
            sub.0.clone().try_into().unwrap()
        } else {
            log::trace!("{}: Adding TIP: {}", i + 1, &tip);
            tip.try_into().unwrap()
        };

        preload_key
            .set_value((i + 1).to_string(), &Data::String(value))
            .unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::stacked_borrows::{AccessKind, Item, Permission, SbTag, SbTagExtra};
use rustc_data_structures::fx::FxHashSet;
#[cfg(feature = "stack-cache")]
use std::ops::Range;

/// Exactly what cache size we should use is a difficult tradeoff. There will always be some
/// workload which has a `SbTag` working set which exceeds the size of the cache, and ends up
/// falling back to linear searches of the borrow stack very often.
/// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
/// entries in the cache stay correct after an insert becomes expensive.
#[cfg(feature = "stack-cache")]
const CACHE_LEN: usize = 32;

/// Extra per-location state.
#[derive(Clone, Debug)]
pub struct Stack {
    /// Used *mostly* as a stack; never empty.
    /// Invariants:
    /// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
    /// * Except for `Untagged`, no tag occurs in the stack more than once.
    borrows: Vec<Item>,
    /// If this is `Some(id)`, then the actual current stack is unknown. This can happen when
    /// wildcard pointers are used to access this location. What we do know is that `borrows` are at
    /// the top of the stack, and below it are arbitrarily many items whose `tag` is strictly less
    /// than `id`.
    /// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom;
    /// we never have the unknown-to-known boundary in an SRW group.
    unknown_bottom: Option<SbTag>,

    /// A small LRU cache of searches of the borrow stack. This only caches accesses of `Tagged`,
    /// because those are unique in the stack.
    #[cfg(feature = "stack-cache")]
    cache: StackCache,
    /// On a read, we need to disable all `Unique` above the granting item. We can avoid most of
    /// this scan by keeping track of the region of the borrow stack that may contain `Unique`s.
    #[cfg(feature = "stack-cache")]
    unique_range: Range<usize>,
}

/// A very small cache of searches of the borrow stack
/// This maps tags to locations in the borrow stack. Any use of this still needs to do a
/// probably-cold random access into the borrow stack to figure out what `Permission` an
/// `SbTag` grants. We could avoid this by also storing the `Permission` in the cache, but
/// most lookups into the cache are immediately followed by access of the full borrow stack anyway.
#[cfg(feature = "stack-cache")]
#[derive(Clone, Debug)]
struct StackCache {
    tags: [SbTag; CACHE_LEN], // Hot in find_granting
    idx: [usize; CACHE_LEN],  // Hot in grant
}

#[cfg(feature = "stack-cache")]
impl StackCache {
    fn add(&mut self, idx: usize, tag: SbTag) {
        self.tags.copy_within(0..CACHE_LEN - 1, 1);
        self.tags[0] = tag;
        self.idx.copy_within(0..CACHE_LEN - 1, 1);
        self.idx[0] = idx;
    }
}

impl PartialEq for Stack {
    fn eq(&self, other: &Self) -> bool {
        // All the semantics of Stack are in self.borrows, everything else is caching
        self.borrows == other.borrows
    }
}

impl Eq for Stack {}

impl<'tcx> Stack {
    /// Panics if any of the caching mechanisms have broken,
    /// - The StackCache indices don't refer to the parallel tags,
    /// - There are no Unique tags outside of first_unique..last_unique
    #[cfg(feature = "expensive-debug-assertions")]
    fn verify_cache_consistency(&self) {
        if self.borrows.len() > CACHE_LEN {
            for (tag, stack_idx) in self.cache.tags.iter().zip(self.cache.idx.iter()) {
                assert_eq!(self.borrows[*stack_idx].tag, *tag);
            }
        }

        for (idx, item) in self.borrows.iter().enumerate() {
            if item.perm == Permission::Unique {
                assert!(
                    self.unique_range.contains(&idx),
                    "{:?} {:?}",
                    self.unique_range,
                    self.borrows
                );
            }
        }
    }

    /// Find the item granting the given kind of access to the given tag, and return where
    /// it is on the stack. For wildcard tags, the given index is approximate, but if *no*
    /// index is given it means the match was *not* in the known part of the stack.
    /// `Ok(None)` indicates it matched the "unknown" part of the stack.
    /// `Err` indicates it was not found.
    pub fn find_granting(
        &mut self,
        access: AccessKind,
        tag: SbTagExtra,
        exposed_tags: &FxHashSet<SbTag>,
    ) -> Result<Option<usize>, ()> {
        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();

        let SbTagExtra::Concrete(tag) = tag else {
            // Handle the wildcard case.
            // Go search the stack for an exposed tag.
            if let Some(idx) =
                self.borrows
                    .iter()
                    .enumerate() // we also need to know *where* in the stack
                    .rev() // search top-to-bottom
                    .find_map(|(idx, item)| {
                        // If the item fits and *might* be this wildcard, use it.
                        if item.perm.grants(access) && exposed_tags.contains(&item.tag) {
                            Some(idx)
                        } else {
                            None
                        }
                    })
            {
                return Ok(Some(idx));
            }
            // If we couldn't find it in the stack, check the unknown bottom.
            return if self.unknown_bottom.is_some() { Ok(None) } else { Err(()) };
        };

        if let Some(idx) = self.find_granting_tagged(access, tag) {
            return Ok(Some(idx));
        }

        // Couldn't find it in the stack; but if there is an unknown bottom it might be there.
        let found = self.unknown_bottom.is_some_and(|&unknown_limit| {
            tag.0 < unknown_limit.0 // unknown_limit is an upper bound for what can be in the unknown bottom.
        });
        if found { Ok(None) } else { Err(()) }
    }

    fn find_granting_tagged(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> {
        #[cfg(feature = "stack-cache")]
        if let Some(idx) = self.find_granting_cache(access, tag) {
            return Some(idx);
        }

        // If we didn't find the tag in the cache, fall back to a linear search of the
        // whole stack, and add the tag to the stack.
        for (stack_idx, item) in self.borrows.iter().enumerate().rev() {
            if tag == item.tag && item.perm.grants(access) {
                #[cfg(feature = "stack-cache")]
                self.cache.add(stack_idx, tag);
                return Some(stack_idx);
            }
        }
        None
    }

    #[cfg(feature = "stack-cache")]
    fn find_granting_cache(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> {
        // When the borrow stack is empty, there are no tags we could put into the cache that would
        // be valid. Additionally, since lookups into the cache are a linear search it doesn't make
        // sense to use the cache when it is no smaller than a search of the borrow stack itself.
        if self.borrows.len() <= CACHE_LEN {
            return None;
        }
        // Search the cache for the tag we're looking up
        let cache_idx = self.cache.tags.iter().position(|t| *t == tag)?;
        let stack_idx = self.cache.idx[cache_idx];
        // If we found the tag, look up its position in the stack to see if it grants
        // the required permission
        if self.borrows[stack_idx].perm.grants(access) {
            // If it does, and it's already in the most-recently-used position, move it
            // there.
            if cache_idx != 0 {
                self.cache.add(stack_idx, tag);
            }
            Some(stack_idx)
        } else {
            // Tag is in the cache, but it doesn't grant the required permission
            None
        }
    }

    pub fn insert(&mut self, new_idx: usize, new: Item) {
        self.borrows.insert(new_idx, new);

        #[cfg(feature = "stack-cache")]
        self.insert_cache(new_idx, new);
    }

    #[cfg(feature = "stack-cache")]
    fn insert_cache(&mut self, new_idx: usize, new: Item) {
        // Adjust the possibly-unique range if an insert occurs before or within it
        if self.unique_range.start >= new_idx {
            self.unique_range.start += 1;
        }
        if self.unique_range.end >= new_idx {
            self.unique_range.end += 1;
        }
        if new.perm == Permission::Unique {
            // Make sure the possibly-unique range contains the new borrow
            self.unique_range.start = self.unique_range.start.min(new_idx);
            self.unique_range.end = self.unique_range.end.max(new_idx + 1);
        }

        // The above insert changes the meaning of every index in the cache >= new_idx, so now
        // we need to find every one of those indexes and increment it.
        for idx in &mut self.cache.idx {
            if *idx >= new_idx {
                *idx += 1;
            }
        }

        // This primes the cache for the next access, which is almost always the just-added tag.
        self.cache.add(new_idx, new.tag);

        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();
    }

    /// Construct a new `Stack` using the passed `Item` as the base tag.
    pub fn new(item: Item) -> Self {
        Stack {
            borrows: vec![item],
            unknown_bottom: None,
            #[cfg(feature = "stack-cache")]
            cache: StackCache { idx: [0; CACHE_LEN], tags: [item.tag; CACHE_LEN] },
            #[cfg(feature = "stack-cache")]
            unique_range: if item.perm == Permission::Unique { 0..1 } else { 0..0 },
        }
    }

    pub fn get(&self, idx: usize) -> Option<Item> {
        self.borrows.get(idx).cloned()
    }

    #[allow(clippy::len_without_is_empty)] // Stacks are never empty
    pub fn len(&self) -> usize {
        self.borrows.len()
    }

    pub fn unknown_bottom(&self) -> Option<SbTag> {
        self.unknown_bottom
    }

    pub fn set_unknown_bottom(&mut self, tag: SbTag) {
        self.borrows.clear();
        self.unknown_bottom = Some(tag);
    }

    /// Find all `Unique` elements in this borrow stack above `granting_idx`, pass a copy of them
    /// to the `visitor`, then set their `Permission` to `Disabled`.
    pub fn disable_uniques_starting_at<V: FnMut(Item) -> crate::InterpResult<'tcx>>(
        &mut self,
        disable_start: usize,
        mut visitor: V,
    ) -> crate::InterpResult<'tcx> {
        #[cfg(feature = "stack-cache")]
        let unique_range = self.unique_range.clone();
        #[cfg(not(feature = "stack-cache"))]
        let unique_range = 0..self.len();

        if disable_start <= unique_range.end {
            // add 1 so we don't disable the granting item
            let lower = unique_range.start.max(disable_start);
            let upper = (unique_range.end + 1).min(self.borrows.len());
            for item in &mut self.borrows[lower..upper] {
                if item.perm == Permission::Unique {
                    log::trace!("access: disabling item {:?}", item);
                    visitor(*item)?;
                    item.perm = Permission::Disabled;
                }
            }
        }

        #[cfg(feature = "stack-cache")]
        if disable_start < self.unique_range.start {
            // We disabled all Unique items
            self.unique_range.start = 0;
            self.unique_range.end = 0;
        } else {
            // Truncate the range to disable_start. This is + 2 because we are only removing
            // elements after disable_start, and this range does not include the end.
            self.unique_range.end = self.unique_range.end.min(disable_start + 1);
        }

        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();

        Ok(())
    }

    /// Produces an iterator which iterates over `range` in reverse, and when dropped removes that
    /// range of `Item`s from this `Stack`.
    pub fn pop_items_after<V: FnMut(Item) -> crate::InterpResult<'tcx>>(
        &mut self,
        start: usize,
        mut visitor: V,
    ) -> crate::InterpResult<'tcx> {
        while self.borrows.len() > start {
            let item = self.borrows.pop().unwrap();
            visitor(item)?;
        }

        #[cfg(feature = "stack-cache")]
        if !self.borrows.is_empty() {
            // After we remove from the borrow stack, every aspect of our caching may be invalid, but it is
            // also possible that the whole cache is still valid. So we call this method to repair what
            // aspects of the cache are now invalid, instead of resetting the whole thing to a trivially
            // valid default state.
            let base_tag = self.borrows[0].tag;
            let mut removed = 0;
            let mut cursor = 0;
            // Remove invalid entries from the cache by rotating them to the end of the cache, then
            // keep track of how many invalid elements there are and overwrite them with the base tag.
            // The base tag here serves as a harmless default value.
            for _ in 0..CACHE_LEN - 1 {
                if self.cache.idx[cursor] >= start {
                    self.cache.idx[cursor..CACHE_LEN - removed].rotate_left(1);
                    self.cache.tags[cursor..CACHE_LEN - removed].rotate_left(1);
                    removed += 1;
                } else {
                    cursor += 1;
                }
            }
            for i in CACHE_LEN - removed - 1..CACHE_LEN {
                self.cache.idx[i] = 0;
                self.cache.tags[i] = base_tag;
            }

            if start < self.unique_range.start.saturating_sub(1) {
                // We removed all the Unique items
                self.unique_range = 0..0;
            } else {
                // Ensure the range doesn't extend past the new top of the stack
                self.unique_range.end = self.unique_range.end.min(start + 1);
            }
        } else {
            self.unique_range = 0..0;
        }

        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();
        Ok(())
    }
}
<commit_msg>Document implementation a bit, add some fast paths<commit_after>use crate::stacked_borrows::{AccessKind, Item, Permission, SbTag, SbTagExtra};
use rustc_data_structures::fx::FxHashSet;
#[cfg(feature = "stack-cache")]
use std::ops::Range;

/// Exactly what cache size we should use is a difficult tradeoff. There will always be some
/// workload which has a `SbTag` working set which exceeds the size of the cache, and ends up
/// falling back to linear searches of the borrow stack very often.
/// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
/// entries in the cache stay correct after an insert becomes expensive.
#[cfg(feature = "stack-cache")]
const CACHE_LEN: usize = 32;

/// Extra per-location state.
#[derive(Clone, Debug)]
pub struct Stack {
    /// Used *mostly* as a stack; never empty.
    /// Invariants:
    /// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
    /// * Except for `Untagged`, no tag occurs in the stack more than once.
    borrows: Vec<Item>,
    /// If this is `Some(id)`, then the actual current stack is unknown. This can happen when
    /// wildcard pointers are used to access this location. What we do know is that `borrows` are at
    /// the top of the stack, and below it are arbitrarily many items whose `tag` is strictly less
    /// than `id`.
    /// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom;
    /// we never have the unknown-to-known boundary in an SRW group.
    unknown_bottom: Option<SbTag>,

    /// A small LRU cache of searches of the borrow stack. This only caches accesses of `Tagged`,
    /// because those are unique in the stack.
    #[cfg(feature = "stack-cache")]
    cache: StackCache,
    /// On a read, we need to disable all `Unique` above the granting item. We can avoid most of
    /// this scan by keeping track of the region of the borrow stack that may contain `Unique`s.
    #[cfg(feature = "stack-cache")]
    unique_range: Range<usize>,
}

/// A very small cache of searches of the borrow stack
/// This maps tags to locations in the borrow stack. Any use of this still needs to do a
/// probably-cold random access into the borrow stack to figure out what `Permission` an
/// `SbTag` grants. We could avoid this by also storing the `Permission` in the cache, but
/// most lookups into the cache are immediately followed by access of the full borrow stack anyway.
#[cfg(feature = "stack-cache")]
#[derive(Clone, Debug)]
struct StackCache {
    tags: [SbTag; CACHE_LEN], // Hot in find_granting
    idx: [usize; CACHE_LEN],  // Hot in grant
}

#[cfg(feature = "stack-cache")]
impl StackCache {
    /// When a tag is used, we call this function to add or refresh it in the cache.
    ///
    /// We use position in the cache to represent how recently a tag was used; the first position
    /// is the most recently used tag. So an add shifts every element towards the end, and inserts
    /// the new element at the start. We lose the last element.
    /// This strategy is effective at keeping the most-accessed tags in the cache, but it costs a
    /// linear shift across the entire cache when we add a new tag.
    fn add(&mut self, idx: usize, tag: SbTag) {
        self.tags.copy_within(0..CACHE_LEN - 1, 1);
        self.tags[0] = tag;
        self.idx.copy_within(0..CACHE_LEN - 1, 1);
        self.idx[0] = idx;
    }
}

impl PartialEq for Stack {
    fn eq(&self, other: &Self) -> bool {
        // All the semantics of Stack are in self.borrows, everything else is caching
        self.borrows == other.borrows
    }
}

impl Eq for Stack {}

impl<'tcx> Stack {
    /// Panics if any of the caching mechanisms have broken,
    /// - The StackCache indices don't refer to the parallel tags,
    /// - There are no Unique tags outside of first_unique..last_unique
    #[cfg(feature = "expensive-debug-assertions")]
    fn verify_cache_consistency(&self) {
        if self.borrows.len() > CACHE_LEN {
            for (tag, stack_idx) in self.cache.tags.iter().zip(self.cache.idx.iter()) {
                assert_eq!(self.borrows[*stack_idx].tag, *tag);
            }
        }

        for (idx, item) in self.borrows.iter().enumerate() {
            if item.perm == Permission::Unique {
                assert!(
                    self.unique_range.contains(&idx),
                    "{:?} {:?}",
                    self.unique_range,
                    self.borrows
                );
            }
        }
    }

    /// Find the item granting the given kind of access to the given tag, and return where
    /// it is on the stack. For wildcard tags, the given index is approximate, but if *no*
    /// index is given it means the match was *not* in the known part of the stack.
    /// `Ok(None)` indicates it matched the "unknown" part of the stack.
    /// `Err` indicates it was not found.
    pub fn find_granting(
        &mut self,
        access: AccessKind,
        tag: SbTagExtra,
        exposed_tags: &FxHashSet<SbTag>,
    ) -> Result<Option<usize>, ()> {
        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();

        let SbTagExtra::Concrete(tag) = tag else {
            // Handle the wildcard case.
            // Go search the stack for an exposed tag.
            if let Some(idx) =
                self.borrows
                    .iter()
                    .enumerate() // we also need to know *where* in the stack
                    .rev() // search top-to-bottom
                    .find_map(|(idx, item)| {
                        // If the item fits and *might* be this wildcard, use it.
                        if item.perm.grants(access) && exposed_tags.contains(&item.tag) {
                            Some(idx)
                        } else {
                            None
                        }
                    })
            {
                return Ok(Some(idx));
            }
            // If we couldn't find it in the stack, check the unknown bottom.
            return if self.unknown_bottom.is_some() { Ok(None) } else { Err(()) };
        };

        if let Some(idx) = self.find_granting_tagged(access, tag) {
            return Ok(Some(idx));
        }

        // Couldn't find it in the stack; but if there is an unknown bottom it might be there.
        let found = self.unknown_bottom.is_some_and(|&unknown_limit| {
            tag.0 < unknown_limit.0 // unknown_limit is an upper bound for what can be in the unknown bottom.
        });
        if found { Ok(None) } else { Err(()) }
    }

    fn find_granting_tagged(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> {
        #[cfg(feature = "stack-cache")]
        if let Some(idx) = self.find_granting_cache(access, tag) {
            return Some(idx);
        }

        // If we didn't find the tag in the cache, fall back to a linear search of the
        // whole stack, and add the tag to the stack.
        for (stack_idx, item) in self.borrows.iter().enumerate().rev() {
            if tag == item.tag && item.perm.grants(access) {
                #[cfg(feature = "stack-cache")]
                self.cache.add(stack_idx, tag);
                return Some(stack_idx);
            }
        }
        None
    }

    #[cfg(feature = "stack-cache")]
    fn find_granting_cache(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> {
        // When the borrow stack is empty, there are no tags we could put into the cache that would
        // be valid. Additionally, since lookups into the cache are a linear search it doesn't make
        // sense to use the cache when it is no smaller than a search of the borrow stack itself.
        if self.borrows.len() <= CACHE_LEN {
            return None;
        }
        // Search the cache for the tag we're looking up
        let cache_idx = self.cache.tags.iter().position(|t| *t == tag)?;
        let stack_idx = self.cache.idx[cache_idx];
        // If we found the tag, look up its position in the stack to see if it grants
        // the required permission
        if self.borrows[stack_idx].perm.grants(access) {
            // If it does, and it's not already in the most-recently-used position, move it there.
            // Except if the tag is in position 1, this is equivalent to just a swap, so do that.
            if cache_idx == 1 {
                self.cache.tags.swap(0, 1);
                self.cache.idx.swap(0, 1);
            } else if cache_idx > 1 {
                self.cache.add(stack_idx, tag);
            }
            Some(stack_idx)
        } else {
            // Tag is in the cache, but it doesn't grant the required permission
            None
        }
    }

    pub fn insert(&mut self, new_idx: usize, new: Item) {
        self.borrows.insert(new_idx, new);

        #[cfg(feature = "stack-cache")]
        self.insert_cache(new_idx, new);
    }

    #[cfg(feature = "stack-cache")]
    fn insert_cache(&mut self, new_idx: usize, new: Item) {
        // Adjust the possibly-unique range if an insert occurs before or within it
        if self.unique_range.start >= new_idx {
            self.unique_range.start += 1;
        }
        if self.unique_range.end >= new_idx {
            self.unique_range.end += 1;
        }
        if new.perm == Permission::Unique {
            // Make sure the possibly-unique range contains the new borrow
            self.unique_range.start = self.unique_range.start.min(new_idx);
            self.unique_range.end = self.unique_range.end.max(new_idx + 1);
        }

        // The above insert changes the meaning of every index in the cache >= new_idx, so now
        // we need to find every one of those indexes and increment it.
        // But if the insert is at the end (equivalent to a push), we can skip this step because
        // it didn't change the position of any other tags.
        if new_idx != self.borrows.len() - 1 {
            for idx in &mut self.cache.idx {
                if *idx >= new_idx {
                    *idx += 1;
                }
            }
        }

        // This primes the cache for the next access, which is almost always the just-added tag.
        self.cache.add(new_idx, new.tag);

        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();
    }

    /// Construct a new `Stack` using the passed `Item` as the base tag.
    pub fn new(item: Item) -> Self {
        Stack {
            borrows: vec![item],
            unknown_bottom: None,
            #[cfg(feature = "stack-cache")]
            cache: StackCache { idx: [0; CACHE_LEN], tags: [item.tag; CACHE_LEN] },
            #[cfg(feature = "stack-cache")]
            unique_range: if item.perm == Permission::Unique { 0..1 } else { 0..0 },
        }
    }

    pub fn get(&self, idx: usize) -> Option<Item> {
        self.borrows.get(idx).cloned()
    }

    #[allow(clippy::len_without_is_empty)] // Stacks are never empty
    pub fn len(&self) -> usize {
        self.borrows.len()
    }

    pub fn unknown_bottom(&self) -> Option<SbTag> {
        self.unknown_bottom
    }

    pub fn set_unknown_bottom(&mut self, tag: SbTag) {
        self.borrows.clear();
        self.unknown_bottom = Some(tag);
    }

    /// Find all `Unique` elements in this borrow stack above `granting_idx`, pass a copy of them
    /// to the `visitor`, then set their `Permission` to `Disabled`.
    pub fn disable_uniques_starting_at<V: FnMut(Item) -> crate::InterpResult<'tcx>>(
        &mut self,
        disable_start: usize,
        mut visitor: V,
    ) -> crate::InterpResult<'tcx> {
        #[cfg(feature = "stack-cache")]
        let unique_range = self.unique_range.clone();
        #[cfg(not(feature = "stack-cache"))]
        let unique_range = 0..self.len();

        if disable_start <= unique_range.end {
            // add 1 so we don't disable the granting item
            let lower = unique_range.start.max(disable_start);
            let upper = (unique_range.end + 1).min(self.borrows.len());
            for item in &mut self.borrows[lower..upper] {
                if item.perm == Permission::Unique {
                    log::trace!("access: disabling item {:?}", item);
                    visitor(*item)?;
                    item.perm = Permission::Disabled;
                }
            }
        }

        #[cfg(feature = "stack-cache")]
        if disable_start < self.unique_range.start {
            // We disabled all Unique items
            self.unique_range.start = 0;
            self.unique_range.end = 0;
        } else {
            // Truncate the range to disable_start. This is + 2 because we are only removing
            // elements after disable_start, and this range does not include the end.
            self.unique_range.end = self.unique_range.end.min(disable_start + 1);
        }

        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();

        Ok(())
    }

    /// Produces an iterator which iterates over `range` in reverse, and when dropped removes that
    /// range of `Item`s from this `Stack`.
    pub fn pop_items_after<V: FnMut(Item) -> crate::InterpResult<'tcx>>(
        &mut self,
        start: usize,
        mut visitor: V,
    ) -> crate::InterpResult<'tcx> {
        while self.borrows.len() > start {
            let item = self.borrows.pop().unwrap();
            visitor(item)?;
        }

        #[cfg(feature = "stack-cache")]
        if !self.borrows.is_empty() {
            // After we remove from the borrow stack, every aspect of our caching may be invalid, but it is
            // also possible that the whole cache is still valid. So we call this method to repair what
            // aspects of the cache are now invalid, instead of resetting the whole thing to a trivially
            // valid default state.
            let base_tag = self.borrows[0].tag;
            let mut removed = 0;
            let mut cursor = 0;
            // Remove invalid entries from the cache by rotating them to the end of the cache, then
            // keep track of how many invalid elements there are and overwrite them with the base tag.
            // The base tag here serves as a harmless default value.
            for _ in 0..CACHE_LEN - 1 {
                if self.cache.idx[cursor] >= start {
                    self.cache.idx[cursor..CACHE_LEN - removed].rotate_left(1);
                    self.cache.tags[cursor..CACHE_LEN - removed].rotate_left(1);
                    removed += 1;
                } else {
                    cursor += 1;
                }
            }
            for i in CACHE_LEN - removed - 1..CACHE_LEN {
                self.cache.idx[i] = 0;
                self.cache.tags[i] = base_tag;
            }

            if start < self.unique_range.start.saturating_sub(1) {
                // We removed all the Unique items
                self.unique_range = 0..0;
            } else {
                // Ensure the range doesn't extend past the new top of the stack
                self.unique_range.end = self.unique_range.end.min(start + 1);
            }
        } else {
            self.unique_range = 0..0;
        }

        #[cfg(feature = "expensive-debug-assertions")]
        self.verify_cache_consistency();
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Renderer for `enum` variants.

use std::iter;

use hir::{db::HirDatabase, HasAttrs, HirDisplay, StructKind};
use ide_db::SymbolKind;
use itertools::Itertools;
use syntax::SmolStr;

use crate::{
    item::{CompletionItem, ImportEdit},
    render::{builder_ext::Params, compute_ref_match, compute_type_match, RenderContext},
    CompletionRelevance,
};

pub(crate) fn render_variant(
    ctx: RenderContext<'_>,
    import_to_add: Option<ImportEdit>,
    local_name: Option<hir::Name>,
    variant: hir::Variant,
    path: Option<hir::ModPath>,
) -> CompletionItem {
    let _p = profile::span("render_enum_variant");
    render(ctx, local_name, variant, path, import_to_add)
}

fn render(
    ctx @ RenderContext { completion }: RenderContext<'_>,
    local_name: Option<hir::Name>,
    variant: hir::Variant,
    path: Option<hir::ModPath>,
    import_to_add: Option<ImportEdit>,
) -> CompletionItem {
    let db = completion.db;
    let name = local_name.unwrap_or_else(|| variant.name(db));
    let variant_kind = variant.kind(db);

    let (qualified_name, short_qualified_name, qualified) = match path {
        Some(path) => {
            let short = hir::ModPath::from_segments(
                hir::PathKind::Plain,
                path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
            );
            (path, short, true)
        }
        None => (
            hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())),
            hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name)),
            false,
        ),
    };
    let qualified_name = qualified_name.to_string();
    let short_qualified_name: SmolStr = short_qualified_name.to_string().into();

    let mut item = CompletionItem::new(SymbolKind::Variant, ctx.source_range(), qualified_name);
    item.set_documentation(variant.docs(db))
        .set_deprecated(ctx.is_deprecated(variant))
        .detail(detail(db, variant, variant_kind));

    if let Some(import_to_add) = import_to_add {
        item.add_import(import_to_add);
    }

    if variant_kind == hir::StructKind::Tuple {
        cov_mark::hit!(inserts_parens_for_tuple_enums);
        let params = Params::Anonymous(variant.fields(db).len());
        item.add_call_parens(ctx.completion, short_qualified_name, params);
    } else if qualified {
        item.lookup_by(short_qualified_name);
    }

    let ty = variant.parent_enum(ctx.completion.db).ty(ctx.completion.db);
    item.set_relevance(CompletionRelevance {
        type_match: compute_type_match(ctx.completion, &ty),
        ..CompletionRelevance::default()
    });

    if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
        item.ref_match(ref_match);
    }

    item.build()
}

fn detail(db: &dyn HirDatabase, variant: hir::Variant, variant_kind: StructKind) -> String {
    let detail_types = variant.fields(db).into_iter().map(|field| (field.name(db), field.ty(db)));

    match variant_kind {
        hir::StructKind::Tuple | hir::StructKind::Unit => {
            format!("({})", detail_types.format_with(", ", |(_, t), f| f(&t.display(db))))
        }
        hir::StructKind::Record => {
            format!(
                "{{{}}}",
                detail_types.format_with(", ", |(n, t), f| {
                    f(&n)?;
                    f(&": ")?;
                    f(&t.display(db))
                }),
            )
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::check_edit;

    #[test]
    fn inserts_parens_for_tuple_enums() {
        cov_mark::check!(inserts_parens_for_tuple_enums);
        check_edit(
            "Some",
            r#"
enum Option<T> { Some(T), None }
use Option::*;
fn main() -> Option<i32> {
    Som$0
}
"#,
            r#"
enum Option<T> { Some(T), None }
use Option::*;
fn main() -> Option<i32> {
    Some($0)
}
"#,
        );
    }
}
<commit_msg>minor<commit_after>//! Renderer for `enum` variants.

use hir::{db::HirDatabase, HasAttrs, HirDisplay, StructKind};
use ide_db::SymbolKind;
use itertools::Itertools;
use syntax::SmolStr;

use crate::{
    item::{CompletionItem, ImportEdit},
    render::{builder_ext::Params, compute_ref_match, compute_type_match, RenderContext},
    CompletionRelevance,
};

pub(crate) fn render_variant(
    ctx: RenderContext<'_>,
    import_to_add: Option<ImportEdit>,
    local_name: Option<hir::Name>,
    variant: hir::Variant,
    path: Option<hir::ModPath>,
) -> CompletionItem {
    let _p = profile::span("render_enum_variant");
    render(ctx, local_name, variant, path, import_to_add)
}

fn render(
    ctx @ RenderContext { completion }: RenderContext<'_>,
    local_name: Option<hir::Name>,
    variant: hir::Variant,
    path: Option<hir::ModPath>,
    import_to_add: Option<ImportEdit>,
) -> CompletionItem {
    let db = completion.db;
    let name = local_name.unwrap_or_else(|| variant.name(db));
    let variant_kind = variant.kind(db);

    let (qualified_name, short_qualified_name, qualified) = match path {
        Some(path) => {
            let short = hir::ModPath::from_segments(
                hir::PathKind::Plain,
                path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
            );
            (path, short, true)
        }
        None => (name.clone().into(), name.into(), false),
    };
    let qualified_name = qualified_name.to_string();
    let short_qualified_name: SmolStr = short_qualified_name.to_string().into();

    let mut item = CompletionItem::new(SymbolKind::Variant, ctx.source_range(), qualified_name);
    item.set_documentation(variant.docs(db))
        .set_deprecated(ctx.is_deprecated(variant))
        .detail(detail(db, variant, variant_kind));

    if let Some(import_to_add) = import_to_add {
        item.add_import(import_to_add);
    }

    if variant_kind == hir::StructKind::Tuple {
        cov_mark::hit!(inserts_parens_for_tuple_enums);
        let params = Params::Anonymous(variant.fields(db).len());
        item.add_call_parens(ctx.completion, short_qualified_name, params);
    } else if qualified {
        item.lookup_by(short_qualified_name);
    }

    let ty = variant.parent_enum(ctx.completion.db).ty(ctx.completion.db);
    item.set_relevance(CompletionRelevance {
        type_match: compute_type_match(ctx.completion, &ty),
        ..CompletionRelevance::default()
    });

    if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
        item.ref_match(ref_match);
    }

    item.build()
}

fn detail(db: &dyn HirDatabase, variant: hir::Variant, variant_kind: StructKind) -> String {
    let detail_types = variant.fields(db).into_iter().map(|field| (field.name(db), field.ty(db)));

    match variant_kind {
        hir::StructKind::Tuple | hir::StructKind::Unit => {
            format!("({})", detail_types.format_with(", ", |(_, t), f| f(&t.display(db))))
        }
        hir::StructKind::Record => {
            format!(
                "{{{}}}",
                detail_types.format_with(", ", |(n, t), f| {
                    f(&n)?;
                    f(&": ")?;
                    f(&t.display(db))
                }),
            )
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::check_edit;

    #[test]
    fn inserts_parens_for_tuple_enums() {
        cov_mark::check!(inserts_parens_for_tuple_enums);
        check_edit(
            "Some",
            r#"
enum Option<T> { Some(T), None }
use Option::*;
fn main() -> Option<i32> {
    Som$0
}
"#,
            r#"
enum Option<T> { Some(T), None }
use Option::*;
fn main() -> Option<i32> {
    Some($0)
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Grin Developers
//
// 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.
//

//! Tests for async mode.. should be run with RUST_TEST_THREADS=1

extern crate cuckoo_miner as cuckoo;
extern crate time;

mod common;

use std::path::PathBuf;

use cuckoo::CuckooMinerError;
use cuckoo::{CuckooMinerConfig, CuckooMinerSolution, CuckooMiner};
use cuckoo::{CuckooPluginManager, CuckooPluginCapabilities};

// Helper function, tests a particular miner implementation against a known set
fn mine_async_for_duration(full_paths: Vec<&str>, duration_in_seconds: i64) {
	let mut config_vec=Vec::new();
	for p in full_paths.into_iter() {
		let mut config = CuckooMinerConfig::new();
		config.plugin_full_path = String::from(p);
		config_vec.push(config);
	}

	let stat_check_interval = 3;
	let deadline = time::get_time().sec + duration_in_seconds;
	let mut next_stat_check = time::get_time().sec + stat_check_interval;
	let mut stats_updated=false;

	while time::get_time().sec < deadline {

		println!("Test mining for {} seconds, looking for difficulty > 0", duration_in_seconds);
		let mut i=0;
		for c in config_vec.clone().into_iter(){
			println!("Plugin {}: {}", i, c.plugin_full_path);
			i+=1;
		}

		// these always get consumed after a notify
		let miner = CuckooMiner::new(config_vec.clone()).expect("");
		let job_handle = miner.notify(1, common::SAMPLE_GRIN_PRE_HEADER_1, common::SAMPLE_GRIN_POST_HEADER_1, 0).unwrap();

		loop {
			if let Some(s) = job_handle.get_solution() {
				println!("Sol found: {}, {:?}", s.get_nonce_as_u64(), s);
				// up to you to read it and check difficulty
				continue;
			}
			if time::get_time().sec >= next_stat_check {
				let mut sps_total=0.0;
				for index in 0..config_vec.len() {
					let stats_vec=job_handle.get_stats(index).unwrap();
					for s in stats_vec.into_iter() {
						let last_solution_time_secs = s.last_solution_time as f64 / 1000.0;
						let last_hashes_per_sec = 1.0 / last_solution_time_secs;
						println!("Plugin {} - Device {} ({}) - Last Solution time: {}; Solutions per second: {:.*}",
						index,s.device_id, s.device_name, last_solution_time_secs, 3, last_hashes_per_sec);
						if last_hashes_per_sec.is_finite() {
							sps_total+=last_hashes_per_sec;
						}
						if last_solution_time_secs > 0.0 {
							stats_updated = true;
						}
						i+=1;
					}
				}
				println!("Total solutions per second: {}", sps_total);
				next_stat_check = time::get_time().sec + stat_check_interval;
			}
			if time::get_time().sec > deadline {
				println!("Stopping jobs and waiting for cleanup");
				job_handle.stop_jobs();
				break;
			}
		}
	}
	assert!(stats_updated==true);
}

//mines for a bit on each available plugin, one after the other
#[test]
fn on_commit_mine_single_plugin_async() {
	let caps = common::get_plugin_vec("");
	for c in &caps {
	 let mut plugin_path_vec:Vec<&str> = Vec::new();
		plugin_path_vec.push(&c.full_path);
		mine_async_for_duration(plugin_path_vec, 75);
	}
}

//Same as above, but only for cuda
#[test]
fn on_cuda_commit_mine_single_plugin_async() {
	let caps = common::get_plugin_vec("cuda");
	for c in &caps {
	 let mut plugin_path_vec:Vec<&str> = Vec::new();
		plugin_path_vec.push(&c.full_path);
		mine_async_for_duration(plugin_path_vec, 75);
	}
}

//Mines for a bit on all available plugins at once
//(won't be efficient, but should stress-tes plugins nicely)
#[test]
fn on_commit_mine_all_plugins_async() {
	// Get a list of installed plugins and capabilities
	// only do cuckoo 30s
	let caps = common::get_plugin_vec("30");
	let mut plugin_path_vec:Vec<&str> = Vec::new();
	for c in &caps {
		plugin_path_vec.push(&c.full_path);
	}
	mine_async_for_duration(plugin_path_vec, 75);
}
<commit_msg>travis integration<commit_after>// Copyright 2017 The Grin Developers
//
// 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.
//

//! Tests for async mode.. should be run with RUST_TEST_THREADS=1

extern crate cuckoo_miner as cuckoo;
extern crate time;

mod common;

use std::path::PathBuf;

use cuckoo::CuckooMinerError;
use cuckoo::{CuckooMinerConfig, CuckooMinerSolution, CuckooMiner};
use cuckoo::{CuckooPluginManager, CuckooPluginCapabilities};

// Helper function, tests a particular miner implementation against a known set
fn mine_async_for_duration(full_paths: Vec<&str>, duration_in_seconds: i64) {
	let mut config_vec=Vec::new();
	for p in full_paths.into_iter() {
		let mut config = CuckooMinerConfig::new();
		config.plugin_full_path = String::from(p);
		config_vec.push(config);
	}

	let stat_check_interval = 3;
	let deadline = time::get_time().sec + duration_in_seconds;
	let mut next_stat_check = time::get_time().sec + stat_check_interval;
	let mut stats_updated=false;

	while time::get_time().sec < deadline {

		println!("Test mining for {} seconds, looking for difficulty > 0", duration_in_seconds);
		let mut i=0;
		for c in config_vec.clone().into_iter(){
			println!("Plugin {}: {}", i, c.plugin_full_path);
			i+=1;
		}

		// these always get consumed after a notify
		let miner = CuckooMiner::new(config_vec.clone()).expect("");
		let job_handle = miner.notify(1, common::SAMPLE_GRIN_PRE_HEADER_1, common::SAMPLE_GRIN_POST_HEADER_1, 0).unwrap();

		loop {
			if let Some(s) = job_handle.get_solution() {
				println!("Sol found: {}, {:?}", s.get_nonce_as_u64(), s);
				// up to you to read it and check difficulty
				continue;
			}
			if time::get_time().sec >= next_stat_check {
				let mut sps_total=0.0;
				for index in 0..config_vec.len() {
					let stats_vec=job_handle.get_stats(index);
					if let Err(_) = stats_vec {
						break;
					}
					for s in stats_vec.unwrap().into_iter() {
						let last_solution_time_secs = s.last_solution_time as f64 / 1000.0;
						let last_hashes_per_sec = 1.0 / last_solution_time_secs;
						println!("Plugin {} - Device {} ({}) - Last Solution time: {}; Solutions per second: {:.*}",
						index,s.device_id, s.device_name, last_solution_time_secs, 3, last_hashes_per_sec);
						if last_hashes_per_sec.is_finite() {
							sps_total+=last_hashes_per_sec;
						}
						if last_solution_time_secs > 0.0 {
							stats_updated = true;
						}
						i+=1;
					}
				}
				println!("Total solutions per second: {}", sps_total);
				next_stat_check = time::get_time().sec + stat_check_interval;
			}
			if time::get_time().sec > deadline {
				println!("Stopping jobs and waiting for cleanup");
				job_handle.stop_jobs();
				break;
			}
		}
	}
	assert!(stats_updated==true);
}

//mines for a bit on each available plugin, one after the other
#[test]
fn on_commit_mine_single_plugin_async() {
	let caps = common::get_plugin_vec("");
	for c in &caps {
	 let mut plugin_path_vec:Vec<&str> = Vec::new();
		plugin_path_vec.push(&c.full_path);
		mine_async_for_duration(plugin_path_vec, 75);
	}
}

//Same as above, but only for cuda
#[test]
fn on_cuda_commit_mine_single_plugin_async() {
	let caps = common::get_plugin_vec("cuda");
	for c in &caps {
	 let mut plugin_path_vec:Vec<&str> = Vec::new();
		plugin_path_vec.push(&c.full_path);
		mine_async_for_duration(plugin_path_vec, 75);
	}
}

//Mines for a bit on all available plugins at once
//(won't be efficient, but should stress-tes plugins nicely)
#[test]
fn on_commit_mine_all_plugins_async() {
	// Get a list of installed plugins and capabilities
	// only do cuckoo 30s
	let caps = common::get_plugin_vec("30");
	let mut plugin_path_vec:Vec<&str> = Vec::new();
	for c in &caps {
		plugin_path_vec.push(&c.full_path);
	}
	mine_async_for_duration(plugin_path_vec, 75);
}
<|endoftext|>


<commit_before>//! Operations on ASCII strings and characters.
//!
//! Most string operations in Rust act on UTF-8 strings. However, at times it
//! makes more sense to only consider the ASCII character set for a specific
//! operation.
//!
//! The [`escape_default`] function provides an iterator over the bytes of an
//! escaped version of the character given.
//!
//! [`escape_default`]: fn.escape_default.html

#![stable(feature = "core_ascii", since = "1.26.0")]

use crate::fmt;
use crate::ops::Range;
use crate::iter::FusedIterator;

/// An iterator over the escaped version of a byte.
///
/// This `struct` is created by the [`escape_default`] function. See its
/// documentation for more.
///
/// [`escape_default`]: fn.escape_default.html
#[stable(feature = "rust1", since = "1.0.0")]
pub struct EscapeDefault {
    range: Range<usize>,
    data: [u8; 4],
}

/// Returns an iterator that produces an escaped version of a `u8`.
///
/// The default is chosen with a bias toward producing literals that are
/// legal in a variety of languages, including C++11 and similar C-family
/// languages. The exact rules are:
///
/// * Tab is escaped as `\t`.
/// * Carriage return is escaped as `\r`.
/// * Line feed is escaped as `\n`.
/// * Single quote is escaped as `\'`.
/// * Double quote is escaped as `\"`.
/// * Backslash is escaped as `\\`.
/// * Any character in the 'printable ASCII' range `0x20` .. `0x7e`
///   inclusive is not escaped.
/// * Any other chars are given hex escapes of the form '\xNN'.
/// * Unicode escapes are never generated by this function.
///
/// # Examples
///
/// ```
/// use std::ascii;
///
/// let escaped = ascii::escape_default(b'0').next().unwrap();
/// assert_eq!(b'0', escaped);
///
/// let mut escaped = ascii::escape_default(b'\t');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b't', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\r');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'r', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\n');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'n', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\'');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'\'', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'"');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'"', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\\');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'\\', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\x9d');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'x', escaped.next().unwrap());
/// assert_eq!(b'9', escaped.next().unwrap());
/// assert_eq!(b'd', escaped.next().unwrap());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn escape_default(c: u8) -> EscapeDefault {
    let (data, len) = match c {
        b'\t' => ([b'\\', b't', 0, 0], 2),
        b'\r' => ([b'\\', b'r', 0, 0], 2),
        b'\n' => ([b'\\', b'n', 0, 0], 2),
        b'\\' => ([b'\\', b'\\', 0, 0], 2),
        b'\'' => ([b'\\', b'\'', 0, 0], 2),
        b'"' => ([b'\\', b'"', 0, 0], 2),
        b'\x20' ..= b'\x7e' => ([c, 0, 0, 0], 1),
        _ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4),
    };

    return EscapeDefault { range: 0..len, data };

    fn hexify(b: u8) -> u8 {
        match b {
            0 ..= 9 => b'0' + b,
            _ => b'a' + b - 10,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl Iterator for EscapeDefault {
    type Item = u8;
    fn next(&mut self) -> Option<u8> { self.range.next().map(|i| self.data[i]) }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
    fn last(mut self) -> Option<u8> { self.next_back() }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl DoubleEndedIterator for EscapeDefault {
    fn next_back(&mut self) -> Option<u8> {
        self.range.next_back().map(|i| self.data[i])
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl ExactSizeIterator for EscapeDefault {}
#[stable(feature = "fused", since = "1.26.0")]
impl FusedIterator for EscapeDefault {}

#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for EscapeDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("EscapeDefault { .. }")
    }
}
<commit_msg>Implement Clone, Display for ascii::EscapeDefault<commit_after>//! Operations on ASCII strings and characters.
//!
//! Most string operations in Rust act on UTF-8 strings. However, at times it
//! makes more sense to only consider the ASCII character set for a specific
//! operation.
//!
//! The [`escape_default`] function provides an iterator over the bytes of an
//! escaped version of the character given.
//!
//! [`escape_default`]: fn.escape_default.html

#![stable(feature = "core_ascii", since = "1.26.0")]

use crate::fmt;
use crate::ops::Range;
use crate::iter::FusedIterator;
use crate::str::from_utf8_unchecked;

/// An iterator over the escaped version of a byte.
///
/// This `struct` is created by the [`escape_default`] function. See its
/// documentation for more.
///
/// [`escape_default`]: fn.escape_default.html
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Clone)]
pub struct EscapeDefault {
    range: Range<usize>,
    data: [u8; 4],
}

/// Returns an iterator that produces an escaped version of a `u8`.
///
/// The default is chosen with a bias toward producing literals that are
/// legal in a variety of languages, including C++11 and similar C-family
/// languages. The exact rules are:
///
/// * Tab is escaped as `\t`.
/// * Carriage return is escaped as `\r`.
/// * Line feed is escaped as `\n`.
/// * Single quote is escaped as `\'`.
/// * Double quote is escaped as `\"`.
/// * Backslash is escaped as `\\`.
/// * Any character in the 'printable ASCII' range `0x20` .. `0x7e`
///   inclusive is not escaped.
/// * Any other chars are given hex escapes of the form '\xNN'.
/// * Unicode escapes are never generated by this function.
///
/// # Examples
///
/// ```
/// use std::ascii;
///
/// let escaped = ascii::escape_default(b'0').next().unwrap();
/// assert_eq!(b'0', escaped);
///
/// let mut escaped = ascii::escape_default(b'\t');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b't', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\r');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'r', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\n');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'n', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\'');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'\'', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'"');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'"', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\\');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'\\', escaped.next().unwrap());
///
/// let mut escaped = ascii::escape_default(b'\x9d');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b'x', escaped.next().unwrap());
/// assert_eq!(b'9', escaped.next().unwrap());
/// assert_eq!(b'd', escaped.next().unwrap());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn escape_default(c: u8) -> EscapeDefault {
    let (data, len) = match c {
        b'\t' => ([b'\\', b't', 0, 0], 2),
        b'\r' => ([b'\\', b'r', 0, 0], 2),
        b'\n' => ([b'\\', b'n', 0, 0], 2),
        b'\\' => ([b'\\', b'\\', 0, 0], 2),
        b'\'' => ([b'\\', b'\'', 0, 0], 2),
        b'"' => ([b'\\', b'"', 0, 0], 2),
        b'\x20' ..= b'\x7e' => ([c, 0, 0, 0], 1),
        _ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4),
    };

    return EscapeDefault { range: 0..len, data };

    fn hexify(b: u8) -> u8 {
        match b {
            0 ..= 9 => b'0' + b,
            _ => b'a' + b - 10,
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl Iterator for EscapeDefault {
    type Item = u8;
    fn next(&mut self) -> Option<u8> { self.range.next().map(|i| self.data[i]) }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
    fn last(mut self) -> Option<u8> { self.next_back() }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl DoubleEndedIterator for EscapeDefault {
    fn next_back(&mut self) -> Option<u8> {
        self.range.next_back().map(|i| self.data[i])
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl ExactSizeIterator for EscapeDefault {}
#[stable(feature = "fused", since = "1.26.0")]
impl FusedIterator for EscapeDefault {}

#[stable(feature = "ascii_escape_display", since = "1.39.0")]
impl fmt::Display for EscapeDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(unsafe { from_utf8_unchecked(&self.data[self.range.clone()]) })
    }
}

#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for EscapeDefault {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("EscapeDefault { .. }")
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// gpio_status.rs
//
// Retrieves the mode and logic level for each of the pins on the standard
// 40-pin GPIO header, and displays the results in an ASCII table.

use std::fmt;
use std::process::exit;

use rppal::gpio::Gpio;
use rppal::system::{DeviceInfo, Model};

enum PinType {
    Gpio(u8),
    Ground,
    Power3v3,
    Power5v,
}

impl fmt::Display for PinType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PinType::Gpio(pin) => write!(f, "GPIO{}", pin),
            PinType::Ground => write!(f, "{:<5}", "GND"),
            PinType::Power3v3 => write!(f, "{:<5}", "3.3 V"),
            PinType::Power5v => write!(f, "{:<5}", "5 V"),
        }
    }
}

const HEADER_40: [PinType; 40] = [
    PinType::Power3v3, // Physical pin 1
    PinType::Power5v,  // Physical pin 2
    PinType::Gpio(2),  // Physical pin 3
    PinType::Power5v,  // Physical pin 4
    PinType::Gpio(3),  // Physical pin 5
    PinType::Ground,   // Physical pin 6
    PinType::Gpio(4),  // Physical pin 7
    PinType::Gpio(14), // Physical pin 8
    PinType::Ground,   // Physical pin 9
    PinType::Gpio(15), // Physical pin 10
    PinType::Gpio(17), // Physical pin 11
    PinType::Gpio(18), // Physical pin 12
    PinType::Gpio(27), // Physical pin 13
    PinType::Ground,   // Physical pin 14
    PinType::Gpio(22), // Physical pin 15
    PinType::Gpio(23), // Physical pin 16
    PinType::Power3v3, // Physical pin 17
    PinType::Gpio(24), // Physical pin 18
    PinType::Gpio(10), // Physical pin 19
    PinType::Ground,   // Physical pin 20
    PinType::Gpio(9),  // Physical pin 21
    PinType::Gpio(25), // Physical pin 22
    PinType::Gpio(11), // Physical pin 23
    PinType::Gpio(8),  // Physical pin 24
    PinType::Ground,   // Physical pin 25
    PinType::Gpio(7),  // Physical pin 26
    PinType::Gpio(0),  // Physical pin 27
    PinType::Gpio(1),  // Physical pin 28
    PinType::Gpio(5),  // Physical pin 29
    PinType::Ground,   // Physical pin 30
    PinType::Gpio(6),  // Physical pin 31
    PinType::Gpio(12), // Physical pin 32
    PinType::Gpio(13), // Physical pin 33
    PinType::Ground,   // Physical pin 34
    PinType::Gpio(19), // Physical pin 35
    PinType::Gpio(16), // Physical pin 36
    PinType::Gpio(26), // Physical pin 37
    PinType::Gpio(20), // Physical pin 38
    PinType::Ground,   // Physical pin 39
    PinType::Gpio(21), // Physical pin 40
];

fn print_pin(
    pin: usize,
    gpio: impl fmt::Display,
    mode: impl fmt::Display,
    level: impl fmt::Display,
) {
    if pin % 2 != 0 {
        print!("| {:>4} | {:<5} | {:>1} | {:>2} |", gpio, mode, level, pin);
    } else {
        println!(" {:>2} | {:>1} | {:<5} | {:>4} |", pin, level, mode, gpio);
    }
}

fn print_gpio_status() {
    let gpio = Gpio::new().unwrap_or_else(|e| {
        println!("Error: Can't access GPIO peripheral ({})", e);
        exit(1);
    });

    println!("+------+-------+---+---------+---+-------+------+");
    println!("| GPIO | Mode  | L |   Pin   | L | Mode  | GPIO |");
    println!("+------+-------+---+----+----+---+-------+------+");

    for (idx, pin_type) in HEADER_40.iter().enumerate() {
        match pin_type {
            PinType::Gpio(bcm_gpio) => {
                let pin = gpio.get(*bcm_gpio).unwrap_or_else(|| {
                    println!("\nError: Can't access GPIO pin {}", *bcm_gpio);
                    exit(1);
                });

                print_pin(
                    idx + 1,
                    bcm_gpio,
                    format!("{}", pin.mode()).to_uppercase(),
                    pin.read() as u8,
                );
            }
            _ => print_pin(idx + 1, "", pin_type, ""),
        };
    }

    println!("+------+-------+---+----+----+---+-------+------+");
}

fn main() {
    match DeviceInfo::new()
        .unwrap_or_else(|e| {
            println!("Error: Can't identify Raspberry Pi model ({})", e);
            exit(1);
        })
        .model()
    {
        Model::RaspberryPiAPlus
        | Model::RaspberryPiBPlus
        | Model::RaspberryPi2B
        | Model::RaspberryPi3APlus
        | Model::RaspberryPi3B
        | Model::RaspberryPi3BPlus
        | Model::RaspberryPiZero
        | Model::RaspberryPiZeroW => print_gpio_status(),
        model => {
            println!("No GPIO header information available for the {}.", model);
        }
    }
}
<commit_msg>Output errors using the eprintln! macro<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// gpio_status.rs
//
// Retrieves the mode and logic level for each of the pins on the standard
// 40-pin GPIO header, and displays the results in an ASCII table.

use std::fmt;
use std::process::exit;

use rppal::gpio::Gpio;
use rppal::system::{DeviceInfo, Model};

enum PinType {
    Gpio(u8),
    Ground,
    Power3v3,
    Power5v,
}

impl fmt::Display for PinType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            PinType::Gpio(pin) => write!(f, "GPIO{}", pin),
            PinType::Ground => write!(f, "{:<5}", "GND"),
            PinType::Power3v3 => write!(f, "{:<5}", "3.3 V"),
            PinType::Power5v => write!(f, "{:<5}", "5 V"),
        }
    }
}

const HEADER_40: [PinType; 40] = [
    PinType::Power3v3, // Physical pin 1
    PinType::Power5v,  // Physical pin 2
    PinType::Gpio(2),  // Physical pin 3
    PinType::Power5v,  // Physical pin 4
    PinType::Gpio(3),  // Physical pin 5
    PinType::Ground,   // Physical pin 6
    PinType::Gpio(4),  // Physical pin 7
    PinType::Gpio(14), // Physical pin 8
    PinType::Ground,   // Physical pin 9
    PinType::Gpio(15), // Physical pin 10
    PinType::Gpio(17), // Physical pin 11
    PinType::Gpio(18), // Physical pin 12
    PinType::Gpio(27), // Physical pin 13
    PinType::Ground,   // Physical pin 14
    PinType::Gpio(22), // Physical pin 15
    PinType::Gpio(23), // Physical pin 16
    PinType::Power3v3, // Physical pin 17
    PinType::Gpio(24), // Physical pin 18
    PinType::Gpio(10), // Physical pin 19
    PinType::Ground,   // Physical pin 20
    PinType::Gpio(9),  // Physical pin 21
    PinType::Gpio(25), // Physical pin 22
    PinType::Gpio(11), // Physical pin 23
    PinType::Gpio(8),  // Physical pin 24
    PinType::Ground,   // Physical pin 25
    PinType::Gpio(7),  // Physical pin 26
    PinType::Gpio(0),  // Physical pin 27
    PinType::Gpio(1),  // Physical pin 28
    PinType::Gpio(5),  // Physical pin 29
    PinType::Ground,   // Physical pin 30
    PinType::Gpio(6),  // Physical pin 31
    PinType::Gpio(12), // Physical pin 32
    PinType::Gpio(13), // Physical pin 33
    PinType::Ground,   // Physical pin 34
    PinType::Gpio(19), // Physical pin 35
    PinType::Gpio(16), // Physical pin 36
    PinType::Gpio(26), // Physical pin 37
    PinType::Gpio(20), // Physical pin 38
    PinType::Ground,   // Physical pin 39
    PinType::Gpio(21), // Physical pin 40
];

fn print_pin(
    pin: usize,
    gpio: impl fmt::Display,
    mode: impl fmt::Display,
    level: impl fmt::Display,
) {
    if pin % 2 != 0 {
        print!("| {:>4} | {:<5} | {:>1} | {:>2} |", gpio, mode, level, pin);
    } else {
        println!(" {:>2} | {:>1} | {:<5} | {:>4} |", pin, level, mode, gpio);
    }
}

fn print_gpio_status() {
    let gpio = Gpio::new().unwrap_or_else(|e| {
        eprintln!("Error: Can't access GPIO peripheral ({})", e);
        exit(1);
    });

    println!("+------+-------+---+---------+---+-------+------+");
    println!("| GPIO | Mode  | L |   Pin   | L | Mode  | GPIO |");
    println!("+------+-------+---+----+----+---+-------+------+");

    for (idx, pin_type) in HEADER_40.iter().enumerate() {
        match pin_type {
            PinType::Gpio(bcm_gpio) => {
                let pin = gpio.get(*bcm_gpio).unwrap_or_else(|| {
                    eprintln!("Error: Can't access GPIO pin {}", *bcm_gpio);
                    exit(1);
                });

                print_pin(
                    idx + 1,
                    bcm_gpio,
                    format!("{}", pin.mode()).to_uppercase(),
                    pin.read() as u8,
                );
            }
            _ => print_pin(idx + 1, "", pin_type, ""),
        };
    }

    println!("+------+-------+---+----+----+---+-------+------+");
}

fn main() {
    match DeviceInfo::new()
        .unwrap_or_else(|e| {
            eprintln!("Error: Can't identify Raspberry Pi model ({})", e);
            exit(1);
        })
        .model()
    {
        Model::RaspberryPiAPlus
        | Model::RaspberryPiBPlus
        | Model::RaspberryPi2B
        | Model::RaspberryPi3APlus
        | Model::RaspberryPi3B
        | Model::RaspberryPi3BPlus
        | Model::RaspberryPiZero
        | Model::RaspberryPiZeroW => print_gpio_status(),
        model => {
            eprintln!("Error: No GPIO header information available for {}", model);
        }
    }
}
<|endoftext|>