<commit_before>//! The Rust Linkage Model and Symbol Names
//! =======================================
//!
//! The semantic model of Rust linkage is, broadly, that "there's no global
//! namespace" between crates. Our aim is to preserve the illusion of this
//! model despite the fact that it's not *quite* possible to implement on
//! modern linkers. We initially didn't use system linkers at all, but have
//! been convinced of their utility.
//!
//! There are a few issues to handle:
//!
//!  - Linkers operate on a flat namespace, so we have to flatten names.
//!    We do this using the C++ namespace-mangling technique. Foo::bar
//!    symbols and such.
//!
//!  - Symbols for distinct items with the same *name* need to get different
//!    linkage-names. Examples of this are monomorphizations of functions or
//!    items within anonymous scopes that end up having the same path.
//!
//!  - Symbols in different crates but with same names "within" the crate need
//!    to get different linkage-names.
//!
//!  - Symbol names should be deterministic: Two consecutive runs of the
//!    compiler over the same code base should produce the same symbol names for
//!    the same items.
//!
//!  - Symbol names should not depend on any global properties of the code base,
//!    so that small modifications to the code base do not result in all symbols
//!    changing. In previous versions of the compiler, symbol names incorporated
//!    the SVH (Stable Version Hash) of the crate. This scheme turned out to be
//!    infeasible when used in conjunction with incremental compilation because
//!    small code changes would invalidate all symbols generated previously.
//!
//!  - Even symbols from different versions of the same crate should be able to
//!    live next to each other without conflict.
//!
//! In order to fulfill the above requirements the following scheme is used by
//! the compiler:
//!
//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit
//! hash value into every exported symbol name. Anything that makes a difference
//! to the symbol being named, but does not show up in the regular path needs to
//! be fed into this hash:
//!
//! - Different monomorphizations of the same item have the same path but differ
//!   in their concrete type parameters, so these parameters are part of the
//!   data being digested for the symbol hash.
//!
//! - Rust allows items to be defined in anonymous scopes, such as in
//!   `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have
//!   the path `foo::bar`, since the anonymous scopes do not contribute to the
//!   path of an item. The compiler already handles this case via so-called
//!   disambiguating `DefPaths` which use indices to distinguish items with the
//!   same name. The DefPaths of the functions above are thus `foo[0]::bar[0]`
//!   and `foo[0]::bar[1]`. In order to incorporate this disambiguation
//!   information into the symbol name too, these indices are fed into the
//!   symbol hash, so that the above two symbols would end up with different
//!   hash values.
//!
//! The two measures described above suffice to avoid intra-crate conflicts. In
//! order to also avoid inter-crate conflicts two more measures are taken:
//!
//! - The name of the crate containing the symbol is prepended to the symbol
//!   name, i.e., symbols are "crate qualified". For example, a function `foo` in
//!   module `bar` in crate `baz` would get a symbol name like
//!   `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids
//!   simple conflicts between functions from different crates.
//!
//! - In order to be able to also use symbols from two versions of the same
//!   crate (which naturally also have the same name), a stronger measure is
//!   required: The compiler accepts an arbitrary "disambiguator" value via the
//!   `-C metadata` command-line argument. This disambiguator is then fed into
//!   the symbol hash of every exported item. Consequently, the symbols in two
//!   identical crates but with different disambiguators are not in conflict
//!   with each other. This facility is mainly intended to be used by build
//!   tools like Cargo.
//!
//! A note on symbol name stability
//! -------------------------------
//! Previous versions of the compiler resorted to feeding NodeIds into the
//! symbol hash in order to disambiguate between items with the same path. The
//! current version of the name generation algorithm takes great care not to do
//! that, since NodeIds are notoriously unstable: A small change to the
//! code base will offset all NodeIds after the change and thus, much as using
//! the SVH in the hash, invalidate an unbounded number of symbol names. This
//! makes re-using previously compiled code for incremental compilation
//! virtually impossible. Thus, symbol hash generation exclusively relies on
//! DefPaths which are much more robust in the face of changes to the code base.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(never_type)]
#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(iter_zip)]
#![recursion_limit = "256"]

#[macro_use]
extern crate rustc_middle;

use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::Node;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_session::config::SymbolManglingVersion;

use tracing::debug;

mod legacy;
mod v0;

pub mod test;

/// This function computes the symbol name for the given `instance` and the
/// given instantiating crate. That is, if you know that instance X is
/// instantiated in crate Y, this is the symbol name this instance would have.
pub fn symbol_name_for_instance_in_crate(
    tcx: TyCtxt<'tcx>,
    instance: Instance<'tcx>,
    instantiating_crate: CrateNum,
) -> String {
    compute_symbol_name(tcx, instance, || instantiating_crate)
}

pub fn provide(providers: &mut Providers) {
    *providers = Providers { symbol_name: symbol_name_provider, ..*providers };
}

// The `symbol_name` query provides the symbol name for calling a given
// instance from the local crate. In particular, it will also look up the
// correct symbol name of instances from upstream crates.
fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName<'tcx> {
    let symbol_name = compute_symbol_name(tcx, instance, || {
        // This closure determines the instantiating crate for instances that
        // need an instantiating-crate-suffix for their symbol name, in order
        // to differentiate between local copies.
        if is_generic(instance.substs) {
            // For generics we might find re-usable upstream instances. If there
            // is one, we rely on the symbol being instantiated locally.
            instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE)
        } else {
            // For non-generic things that need to avoid naming conflicts, we
            // always instantiate a copy in the local crate.
            LOCAL_CRATE
        }
    });

    ty::SymbolName::new(tcx, &symbol_name)
}

/// Computes the symbol name for the given instance. This function will call
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
/// into the symbol name.
fn compute_symbol_name(
    tcx: TyCtxt<'tcx>,
    instance: Instance<'tcx>,
    compute_instantiating_crate: impl FnOnce() -> CrateNum,
) -> String {
    let def_id = instance.def_id();
    let substs = instance.substs;

    debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs);

    // FIXME(eddyb) Precompute a custom symbol name based on attributes.
    let is_foreign = if let Some(def_id) = def_id.as_local() {
        if tcx.proc_macro_decls_static(()) == Some(def_id) {
            let stable_crate_id = tcx.sess.local_stable_crate_id();
            return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
        }
        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
        matches!(tcx.hir().get(hir_id), Node::ForeignItem(_))
    } else {
        tcx.is_foreign_item(def_id)
    };

    let attrs = tcx.codegen_fn_attrs(def_id);

    // Foreign items by default use no mangling for their symbol name. There's a
    // few exceptions to this rule though:
    //
    // * This can be overridden with the `#[link_name]` attribute
    //
    // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the
    //   same-named symbol when imported from different wasm modules will get
    //   hooked up incorrectly. As a result foreign symbols, on the wasm target,
    //   with a wasm import module, get mangled. Additionally our codegen will
    //   deduplicate symbols based purely on the symbol name, but for wasm this
    //   isn't quite right because the same-named symbol on wasm can come from
    //   different modules. For these reasons if `#[link(wasm_import_module)]`
    //   is present we mangle everything on wasm because the demangled form will
    //   show up in the `wasm-import-name` custom attribute in LLVM IR.
    //
    // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316
    if is_foreign
        && (!tcx.sess.target.is_like_wasm
            || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id))
    {
        if let Some(name) = attrs.link_name {
            return name.to_string();
        }
        return tcx.item_name(def_id).to_string();
    }

    if let Some(name) = attrs.export_name {
        // Use provided name
        return name.to_string();
    }

    if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
        // Don't mangle
        return tcx.item_name(def_id).to_string();
    }

    let avoid_cross_crate_conflicts =
        // If this is an instance of a generic function, we also hash in
        // the ID of the instantiating crate. This avoids symbol conflicts
        // in case the same instances is emitted in two crates of the same
        // project.
        is_generic(substs) ||

        // If we're dealing with an instance of a function that's inlined from
        // another crate but we're marking it as globally shared to our
        // compliation (aka we're not making an internal copy in each of our
        // codegen units) then this symbol may become an exported (but hidden
        // visibility) symbol. This means that multiple crates may do the same
        // and we want to be sure to avoid any symbol conflicts here.
        matches!(MonoItem::Fn(instance).instantiation_mode(tcx), InstantiationMode::GloballyShared { may_conflict: true });

    let instantiating_crate =
        if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None };

    // Pick the crate responsible for the symbol mangling version, which has to:
    // 1. be stable for each instance, whether it's being defined or imported
    // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible
    // We solve these as follows:
    // 1. because symbol names depend on both `def_id` and `instantiating_crate`,
    // both their `CrateNum`s are stable for any given instance, so we can pick
    // either and have a stable choice of symbol mangling version
    // 2. we favor `instantiating_crate` where possible (i.e. when `Some`)
    let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate);
    let mangling_version = if mangling_version_crate == LOCAL_CRATE {
        tcx.sess.opts.debugging_opts.get_symbol_mangling_version()
    } else {
        tcx.symbol_mangling_version(mangling_version_crate)
    };

    match mangling_version {
        SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate),
        SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate),
    }
}

fn is_generic(substs: SubstsRef<'_>) -> bool {
    substs.non_erasable_generics().next().is_some()
}
<commit_msg>Rollup merge of #85534 - csmoe:demagnle-assert, r=michaelwoerister<commit_after>//! The Rust Linkage Model and Symbol Names
//! =======================================
//!
//! The semantic model of Rust linkage is, broadly, that "there's no global
//! namespace" between crates. Our aim is to preserve the illusion of this
//! model despite the fact that it's not *quite* possible to implement on
//! modern linkers. We initially didn't use system linkers at all, but have
//! been convinced of their utility.
//!
//! There are a few issues to handle:
//!
//!  - Linkers operate on a flat namespace, so we have to flatten names.
//!    We do this using the C++ namespace-mangling technique. Foo::bar
//!    symbols and such.
//!
//!  - Symbols for distinct items with the same *name* need to get different
//!    linkage-names. Examples of this are monomorphizations of functions or
//!    items within anonymous scopes that end up having the same path.
//!
//!  - Symbols in different crates but with same names "within" the crate need
//!    to get different linkage-names.
//!
//!  - Symbol names should be deterministic: Two consecutive runs of the
//!    compiler over the same code base should produce the same symbol names for
//!    the same items.
//!
//!  - Symbol names should not depend on any global properties of the code base,
//!    so that small modifications to the code base do not result in all symbols
//!    changing. In previous versions of the compiler, symbol names incorporated
//!    the SVH (Stable Version Hash) of the crate. This scheme turned out to be
//!    infeasible when used in conjunction with incremental compilation because
//!    small code changes would invalidate all symbols generated previously.
//!
//!  - Even symbols from different versions of the same crate should be able to
//!    live next to each other without conflict.
//!
//! In order to fulfill the above requirements the following scheme is used by
//! the compiler:
//!
//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit
//! hash value into every exported symbol name. Anything that makes a difference
//! to the symbol being named, but does not show up in the regular path needs to
//! be fed into this hash:
//!
//! - Different monomorphizations of the same item have the same path but differ
//!   in their concrete type parameters, so these parameters are part of the
//!   data being digested for the symbol hash.
//!
//! - Rust allows items to be defined in anonymous scopes, such as in
//!   `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have
//!   the path `foo::bar`, since the anonymous scopes do not contribute to the
//!   path of an item. The compiler already handles this case via so-called
//!   disambiguating `DefPaths` which use indices to distinguish items with the
//!   same name. The DefPaths of the functions above are thus `foo[0]::bar[0]`
//!   and `foo[0]::bar[1]`. In order to incorporate this disambiguation
//!   information into the symbol name too, these indices are fed into the
//!   symbol hash, so that the above two symbols would end up with different
//!   hash values.
//!
//! The two measures described above suffice to avoid intra-crate conflicts. In
//! order to also avoid inter-crate conflicts two more measures are taken:
//!
//! - The name of the crate containing the symbol is prepended to the symbol
//!   name, i.e., symbols are "crate qualified". For example, a function `foo` in
//!   module `bar` in crate `baz` would get a symbol name like
//!   `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids
//!   simple conflicts between functions from different crates.
//!
//! - In order to be able to also use symbols from two versions of the same
//!   crate (which naturally also have the same name), a stronger measure is
//!   required: The compiler accepts an arbitrary "disambiguator" value via the
//!   `-C metadata` command-line argument. This disambiguator is then fed into
//!   the symbol hash of every exported item. Consequently, the symbols in two
//!   identical crates but with different disambiguators are not in conflict
//!   with each other. This facility is mainly intended to be used by build
//!   tools like Cargo.
//!
//! A note on symbol name stability
//! -------------------------------
//! Previous versions of the compiler resorted to feeding NodeIds into the
//! symbol hash in order to disambiguate between items with the same path. The
//! current version of the name generation algorithm takes great care not to do
//! that, since NodeIds are notoriously unstable: A small change to the
//! code base will offset all NodeIds after the change and thus, much as using
//! the SVH in the hash, invalidate an unbounded number of symbol names. This
//! makes re-using previously compiled code for incremental compilation
//! virtually impossible. Thus, symbol hash generation exclusively relies on
//! DefPaths which are much more robust in the face of changes to the code base.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(never_type)]
#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(iter_zip)]
#![recursion_limit = "256"]

#[macro_use]
extern crate rustc_middle;

use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::Node;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_session::config::SymbolManglingVersion;

use tracing::debug;

mod legacy;
mod v0;

pub mod test;

/// This function computes the symbol name for the given `instance` and the
/// given instantiating crate. That is, if you know that instance X is
/// instantiated in crate Y, this is the symbol name this instance would have.
pub fn symbol_name_for_instance_in_crate(
    tcx: TyCtxt<'tcx>,
    instance: Instance<'tcx>,
    instantiating_crate: CrateNum,
) -> String {
    compute_symbol_name(tcx, instance, || instantiating_crate)
}

pub fn provide(providers: &mut Providers) {
    *providers = Providers { symbol_name: symbol_name_provider, ..*providers };
}

// The `symbol_name` query provides the symbol name for calling a given
// instance from the local crate. In particular, it will also look up the
// correct symbol name of instances from upstream crates.
fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName<'tcx> {
    let symbol_name = compute_symbol_name(tcx, instance, || {
        // This closure determines the instantiating crate for instances that
        // need an instantiating-crate-suffix for their symbol name, in order
        // to differentiate between local copies.
        if is_generic(instance.substs) {
            // For generics we might find re-usable upstream instances. If there
            // is one, we rely on the symbol being instantiated locally.
            instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE)
        } else {
            // For non-generic things that need to avoid naming conflicts, we
            // always instantiate a copy in the local crate.
            LOCAL_CRATE
        }
    });

    ty::SymbolName::new(tcx, &symbol_name)
}

/// Computes the symbol name for the given instance. This function will call
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
/// into the symbol name.
fn compute_symbol_name(
    tcx: TyCtxt<'tcx>,
    instance: Instance<'tcx>,
    compute_instantiating_crate: impl FnOnce() -> CrateNum,
) -> String {
    let def_id = instance.def_id();
    let substs = instance.substs;

    debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs);

    // FIXME(eddyb) Precompute a custom symbol name based on attributes.
    let is_foreign = if let Some(def_id) = def_id.as_local() {
        if tcx.proc_macro_decls_static(()) == Some(def_id) {
            let stable_crate_id = tcx.sess.local_stable_crate_id();
            return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
        }
        let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
        matches!(tcx.hir().get(hir_id), Node::ForeignItem(_))
    } else {
        tcx.is_foreign_item(def_id)
    };

    let attrs = tcx.codegen_fn_attrs(def_id);

    // Foreign items by default use no mangling for their symbol name. There's a
    // few exceptions to this rule though:
    //
    // * This can be overridden with the `#[link_name]` attribute
    //
    // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the
    //   same-named symbol when imported from different wasm modules will get
    //   hooked up incorrectly. As a result foreign symbols, on the wasm target,
    //   with a wasm import module, get mangled. Additionally our codegen will
    //   deduplicate symbols based purely on the symbol name, but for wasm this
    //   isn't quite right because the same-named symbol on wasm can come from
    //   different modules. For these reasons if `#[link(wasm_import_module)]`
    //   is present we mangle everything on wasm because the demangled form will
    //   show up in the `wasm-import-name` custom attribute in LLVM IR.
    //
    // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316
    if is_foreign
        && (!tcx.sess.target.is_like_wasm
            || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id))
    {
        if let Some(name) = attrs.link_name {
            return name.to_string();
        }
        return tcx.item_name(def_id).to_string();
    }

    if let Some(name) = attrs.export_name {
        // Use provided name
        return name.to_string();
    }

    if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
        // Don't mangle
        return tcx.item_name(def_id).to_string();
    }

    let avoid_cross_crate_conflicts =
        // If this is an instance of a generic function, we also hash in
        // the ID of the instantiating crate. This avoids symbol conflicts
        // in case the same instances is emitted in two crates of the same
        // project.
        is_generic(substs) ||

        // If we're dealing with an instance of a function that's inlined from
        // another crate but we're marking it as globally shared to our
        // compliation (aka we're not making an internal copy in each of our
        // codegen units) then this symbol may become an exported (but hidden
        // visibility) symbol. This means that multiple crates may do the same
        // and we want to be sure to avoid any symbol conflicts here.
        matches!(MonoItem::Fn(instance).instantiation_mode(tcx), InstantiationMode::GloballyShared { may_conflict: true });

    let instantiating_crate =
        if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None };

    // Pick the crate responsible for the symbol mangling version, which has to:
    // 1. be stable for each instance, whether it's being defined or imported
    // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible
    // We solve these as follows:
    // 1. because symbol names depend on both `def_id` and `instantiating_crate`,
    // both their `CrateNum`s are stable for any given instance, so we can pick
    // either and have a stable choice of symbol mangling version
    // 2. we favor `instantiating_crate` where possible (i.e. when `Some`)
    let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate);
    let mangling_version = if mangling_version_crate == LOCAL_CRATE {
        tcx.sess.opts.debugging_opts.get_symbol_mangling_version()
    } else {
        tcx.symbol_mangling_version(mangling_version_crate)
    };

    let symbol = match mangling_version {
        SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate),
        SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate),
    };

    debug_assert!(
        rustc_demangle::try_demangle(&symbol).is_ok(),
        "compute_symbol_name: `{}` cannot be demangled",
        symbol
    );

    symbol
}

fn is_generic(substs: SubstsRef<'_>) -> bool {
    substs.non_erasable_generics().next().is_some()
}
<|endoftext|>


<commit_before>//! Copy-pasted from the internet

/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
enum _CharacterSet {
    /// The standard character set (uses `+` and `/`)
    _Standard,
    /// The URL safe character set (uses `-` and `_`)
    _UrlSafe,
}

static STANDARD_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                        abcdefghijklmnopqrstuvwxyz\
                                        0123456789+/";

static _URLSAFE_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                       abcdefghijklmnopqrstuvwxyz\
                                       0123456789-_";

pub fn encode(input: &[u8]) -> String {
    let bytes = STANDARD_CHARS;

    let len = input.len();

    // Preallocate memory.
    let prealloc_len = (len + 2) / 3 * 4;
    let mut out_bytes = vec![b'='; prealloc_len];

    // Deal with padding bytes
    let mod_len = len % 3;

    // Use iterators to reduce branching
    {
        let mut s_in = input[..len - mod_len].iter().map(|&x| x as u32);
        let mut s_out = out_bytes.iter_mut();

        // Convenient shorthand
        let enc = |val| bytes[val as usize];
        let mut write = |val| *s_out.next().unwrap() = val;

        // Iterate though blocks of 4
        while let (Some(first), Some(second), Some(third)) = (s_in.next(), s_in.next(), s_in.next())
        {
            let n = first << 16 | second << 8 | third;

            // This 24-bit number gets separated into four 6-bit numbers.
            write(enc((n >> 18) & 63));
            write(enc((n >> 12) & 63));
            write(enc((n >> 6) & 63));
            write(enc((n >> 0) & 63));
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match mod_len {
            0 => (),
            1 => {
                let n = (input[len - 1] as u32) << 16;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
            }
            2 => {
                let n = (input[len - 2] as u32) << 16 | (input[len - 1] as u32) << 8;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
                write(enc((n >> 6) & 63));
            }
            _ => panic!("Algebra is broken, please alert the math police"),
        }
    }

    unsafe { String::from_utf8_unchecked(out_bytes) }
}

/// Errors that can occur when decoding a base64 encoded string
#[derive(Clone, Copy, Debug)]
pub enum FromBase64Error {
    /// The input contained a character not part of the base64 format
    InvalidBase64Byte(u8, usize),
    /// The input had an invalid length
    InvalidBase64Length,
}

pub fn decode(input: &str) -> Result<Vec<u8>, FromBase64Error> {
    let mut r = Vec::with_capacity(input.len());
    let mut buf: u32 = 0;
    let mut modulus = 0;

    let mut it = input.as_bytes().iter();
    for byte in it.by_ref() {
        let code = DECODE_TABLE[*byte as usize];
        if code >= SPECIAL_CODES_START {
            match code {
                NEWLINE_CODE => continue,
                EQUALS_CODE => break,
                INVALID_CODE => {
                    return Err(FromBase64Error::InvalidBase64Byte(
                        *byte,
                        (byte as *const _ as usize) - input.as_ptr() as usize,
                    ))
                }
                _ => unreachable!(),
            }
        }
        buf = (buf | code as u32) << 6;
        modulus += 1;
        if modulus == 4 {
            modulus = 0;
            r.push((buf >> 22) as u8);
            r.push((buf >> 14) as u8);
            r.push((buf >> 6) as u8);
        }
    }

    for byte in it {
        match *byte {
            b'=' | b'\r' | b'\n' => continue,
            _ => {
                return Err(FromBase64Error::InvalidBase64Byte(
                    *byte,
                    (byte as *const _ as usize) - input.as_ptr() as usize,
                ))
            }
        }
    }

    match modulus {
        2 => {
            r.push((buf >> 10) as u8);
        }
        3 => {
            r.push((buf >> 16) as u8);
            r.push((buf >> 8) as u8);
        }
        0 => (),
        _ => return Err(FromBase64Error::InvalidBase64Length),
    }

    Ok(r)
}

const DECODE_TABLE: [u8; 256] = [
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const INVALID_CODE: u8 = 0xFF;
const EQUALS_CODE: u8 = 0xFE;
const NEWLINE_CODE: u8 = 0xFD;
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;

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

    #[test]
    fn test_encode_basic() {
        assert_eq!(encode(b""), "");
        assert_eq!(encode(b"f"), "Zg==");
        assert_eq!(encode(b"fo"), "Zm8=");
        assert_eq!(encode(b"foo"), "Zm9v");
        assert_eq!(encode(b"foob"), "Zm9vYg==");
        assert_eq!(encode(b"fooba"), "Zm9vYmE=");
        assert_eq!(encode(b"foobar"), "Zm9vYmFy");
    }

    #[test]
    fn test_encode_standard_safe() {
        assert_eq!(encode(&[251, 255]), "+/8=");
    }

    #[test]
    fn test_decode_basic() {
        assert_eq!(decode("").unwrap(), b"");
        assert_eq!(decode("Zg==").unwrap(), b"f");
        assert_eq!(decode("Zm8=").unwrap(), b"fo");
        assert_eq!(decode("Zm9v").unwrap(), b"foo");
        assert_eq!(decode("Zm9vYg==").unwrap(), b"foob");
        assert_eq!(decode("Zm9vYmE=").unwrap(), b"fooba");
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode() {
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode_newlines() {
        assert_eq!(decode("Zm9v\r\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\r\n").unwrap(), b"foob");
        assert_eq!(decode("Zm9v\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\n").unwrap(), b"foob");
    }

    #[test]
    fn test_decode_urlsafe() {
        assert_eq!(decode("-_8").unwrap(), decode("+/8=").unwrap());
    }

    #[test]
    fn test_from_base64_invalid_char() {
        assert!(decode("Zm$=").is_err());
        assert!(decode("Zg==$").is_err());
    }

    #[test]
    fn test_decode_invalid_padding() {
        assert!(decode("Z===").is_err());
    }
}
<commit_msg>Explain why unsafe is safe in base64 module<commit_after>//! Copy-pasted from the internet

/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
enum _CharacterSet {
    /// The standard character set (uses `+` and `/`)
    _Standard,
    /// The URL safe character set (uses `-` and `_`)
    _UrlSafe,
}

static STANDARD_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                        abcdefghijklmnopqrstuvwxyz\
                                        0123456789+/";

static _URLSAFE_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                       abcdefghijklmnopqrstuvwxyz\
                                       0123456789-_";

pub fn encode(input: &[u8]) -> String {
    let bytes = STANDARD_CHARS;

    let len = input.len();

    // Preallocate memory.
    let prealloc_len = (len + 2) / 3 * 4;
    let mut out_bytes = vec![b'='; prealloc_len];

    // Deal with padding bytes
    let mod_len = len % 3;

    // Use iterators to reduce branching
    {
        let mut s_in = input[..len - mod_len].iter().map(|&x| x as u32);
        let mut s_out = out_bytes.iter_mut();

        // Convenient shorthand
        let enc = |val| bytes[val as usize];
        let mut write = |val| *s_out.next().unwrap() = val;

        // Iterate though blocks of 4
        while let (Some(first), Some(second), Some(third)) = (s_in.next(), s_in.next(), s_in.next())
        {
            let n = first << 16 | second << 8 | third;

            // This 24-bit number gets separated into four 6-bit numbers.
            write(enc((n >> 18) & 63));
            write(enc((n >> 12) & 63));
            write(enc((n >> 6) & 63));
            write(enc((n >> 0) & 63));
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match mod_len {
            0 => (),
            1 => {
                let n = (input[len - 1] as u32) << 16;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
            }
            2 => {
                let n = (input[len - 2] as u32) << 16 | (input[len - 1] as u32) << 8;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
                write(enc((n >> 6) & 63));
            }
            _ => panic!("Algebra is broken, please alert the math police"),
        }
    }

    // `out_bytes` vec is prepopulated with `=` symbols and then only updated
    // with base64 chars, so this unsafe is safe.
    unsafe { String::from_utf8_unchecked(out_bytes) }
}

/// Errors that can occur when decoding a base64 encoded string
#[derive(Clone, Copy, Debug)]
pub enum FromBase64Error {
    /// The input contained a character not part of the base64 format
    InvalidBase64Byte(u8, usize),
    /// The input had an invalid length
    InvalidBase64Length,
}

pub fn decode(input: &str) -> Result<Vec<u8>, FromBase64Error> {
    let mut r = Vec::with_capacity(input.len());
    let mut buf: u32 = 0;
    let mut modulus = 0;

    let mut it = input.as_bytes().iter();
    for byte in it.by_ref() {
        let code = DECODE_TABLE[*byte as usize];
        if code >= SPECIAL_CODES_START {
            match code {
                NEWLINE_CODE => continue,
                EQUALS_CODE => break,
                INVALID_CODE => {
                    return Err(FromBase64Error::InvalidBase64Byte(
                        *byte,
                        (byte as *const _ as usize) - input.as_ptr() as usize,
                    ))
                }
                _ => unreachable!(),
            }
        }
        buf = (buf | code as u32) << 6;
        modulus += 1;
        if modulus == 4 {
            modulus = 0;
            r.push((buf >> 22) as u8);
            r.push((buf >> 14) as u8);
            r.push((buf >> 6) as u8);
        }
    }

    for byte in it {
        match *byte {
            b'=' | b'\r' | b'\n' => continue,
            _ => {
                return Err(FromBase64Error::InvalidBase64Byte(
                    *byte,
                    (byte as *const _ as usize) - input.as_ptr() as usize,
                ))
            }
        }
    }

    match modulus {
        2 => {
            r.push((buf >> 10) as u8);
        }
        3 => {
            r.push((buf >> 16) as u8);
            r.push((buf >> 8) as u8);
        }
        0 => (),
        _ => return Err(FromBase64Error::InvalidBase64Length),
    }

    Ok(r)
}

const DECODE_TABLE: [u8; 256] = [
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const INVALID_CODE: u8 = 0xFF;
const EQUALS_CODE: u8 = 0xFE;
const NEWLINE_CODE: u8 = 0xFD;
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;

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

    #[test]
    fn test_encode_basic() {
        assert_eq!(encode(b""), "");
        assert_eq!(encode(b"f"), "Zg==");
        assert_eq!(encode(b"fo"), "Zm8=");
        assert_eq!(encode(b"foo"), "Zm9v");
        assert_eq!(encode(b"foob"), "Zm9vYg==");
        assert_eq!(encode(b"fooba"), "Zm9vYmE=");
        assert_eq!(encode(b"foobar"), "Zm9vYmFy");
    }

    #[test]
    fn test_encode_standard_safe() {
        assert_eq!(encode(&[251, 255]), "+/8=");
    }

    #[test]
    fn test_decode_basic() {
        assert_eq!(decode("").unwrap(), b"");
        assert_eq!(decode("Zg==").unwrap(), b"f");
        assert_eq!(decode("Zm8=").unwrap(), b"fo");
        assert_eq!(decode("Zm9v").unwrap(), b"foo");
        assert_eq!(decode("Zm9vYg==").unwrap(), b"foob");
        assert_eq!(decode("Zm9vYmE=").unwrap(), b"fooba");
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode() {
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode_newlines() {
        assert_eq!(decode("Zm9v\r\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\r\n").unwrap(), b"foob");
        assert_eq!(decode("Zm9v\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\n").unwrap(), b"foob");
    }

    #[test]
    fn test_decode_urlsafe() {
        assert_eq!(decode("-_8").unwrap(), decode("+/8=").unwrap());
    }

    #[test]
    fn test_from_base64_invalid_char() {
        assert!(decode("Zm$=").is_err());
        assert!(decode("Zg==$").is_err());
    }

    #[test]
    fn test_decode_invalid_padding() {
        assert!(decode("Z===").is_err());
    }
}
<|endoftext|>


<commit_before>#[path="../src/grammar.rs"]
mod grammar;
#[path="../src/parser.rs"]
mod parser;
#[path="../src/codegen.rs"]
mod codegen;
#[path="../src/lexicon.rs"]
mod lexicon;
#[path="../src/tokenizer.rs"]
mod tokenizer;

fn output_program(input_program: &str) -> Result<String, Vec<String>> {
    let program = parser::parse(input_program.into());
    codegen::generate_code(program)
}

#[test]
fn convert_const_str_to_var() {
    assert_eq!(output_program("const foo = \"bar\";"), Ok("var foo = \"bar\";".into()));
}

#[test]
fn convert_var_str_to_var() {
    assert_eq!(output_program("var foo = \"bar\";"), Ok("var foo = \"bar\";".into()));
}

#[test]
fn convert_const_number_to_var() {
    assert_eq!(output_program("const foo = 435;"), Ok("var foo = 435;".into()));
}

#[test]
fn convert_const_true_to_var() {
    assert_eq!(output_program("const foo = true;"), Ok("var foo = true;".into()));
}

#[test]
fn convert_const_false_to_var() {
    assert_eq!(output_program("const foo = false;"), Ok("var foo = false;".into()));
}

#[test]
fn convert_const_null_to_var() {
    assert_eq!(output_program("const foo = null;"), Ok("var foo = null;".into()));
}

#[test]
fn convert_multiple_const_declarations() {
    let program = "const binary = 42, octal = 42, hexal = 42;";
    let expected = "var binary = 42, octal = 42, hexal = 42;";
    assert_eq!(output_program(program), Ok(expected.into()));
}
<commit_msg>Remove tests for code generator<commit_after><|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
<commit_msg>Add error banner when running test suite without nightly compiler<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(/*=============================================]
#![===  Serde test suite requires a nightly compiler.  ===]
#![====================================================*/)]
<|endoftext|>


<commit_before>#![cfg(feature = "stable_graph")]

extern crate petgraph;
extern crate itertools;
#[macro_use] extern crate defmac;

use petgraph::prelude::*;
use petgraph::stable_graph::node_index as n;
use petgraph::EdgeType;
use petgraph::algo::{kosaraju_scc, tarjan_scc};
use petgraph::visit::{
    NodeIndexable,
    IntoNodeReferences,
    IntoEdgeReferences,
};
use petgraph::dot::Dot;

use itertools::assert_equal;

#[test]
fn node_indices() {
    let mut g = StableGraph::<_, ()>::new();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let c = g.add_node(2);
    g.remove_node(b);
    let mut iter = g.node_indices();
    assert_eq!(iter.next(), Some(a));
    assert_eq!(iter.next(), Some(c));
    assert_eq!(iter.next(), None);
}

#[test]
fn node_bound() {
    let mut g = StableGraph::<_, ()>::new();
    assert_eq!(g.node_bound(), g.node_count());
    for i in 0..10 {
        g.add_node(i);
        assert_eq!(g.node_bound(), g.node_count());
    }
    let full_count = g.node_count();
    g.remove_node(n(0));
    g.remove_node(n(2));
    assert_eq!(g.node_bound(), full_count);
    g.clear();
    assert_eq!(g.node_bound(), 0);
}

fn assert_sccs_eq(mut res: Vec<Vec<NodeIndex>>, normalized: Vec<Vec<NodeIndex>>) {
    // normalize the result and compare with the answer.
    for scc in &mut res {
        scc.sort();
    }
    // sort by minimum element
    res.sort_by(|v, w| v[0].cmp(&w[0]));
    assert_eq!(res, normalized);
}

fn scc_graph() -> StableGraph<(), ()> {
    let mut gr: StableGraph<(), ()> = StableGraph::from_edges(&[
        (6, 0),
        (0, 3),
        (3, 6),
        (8, 6), (8, 2),
        (2, 5), (5, 8), (7, 5),
        (1, 7),
        (7, 4),
        (4, 1)]);
    // make an identical replacement of n(4) and leave a hole
    let x = gr.add_node(());
    gr.add_edge(n(7), x, ());
    gr.add_edge(x, n(1), ());
    gr.remove_node(n(4));
    gr
}

#[test]
fn test_scc() {
    let gr = scc_graph();
    println!("{:?}", gr);

    let x = n(gr.node_bound() - 1);
    assert_sccs_eq(kosaraju_scc(&gr), vec![
        vec![n(0), n(3), n(6)],
        vec![n(1), n(7),   x ],
        vec![n(2), n(5), n(8)],
    ]);
}

#[test]
fn test_tarjan_scc() {
    let mut gr: StableGraph<(), ()> = StableGraph::from_edges(&[
        (6, 0),
        (0, 3),
        (3, 6),
        (8, 6), (8, 2),
        (2, 5), (5, 8), (7, 5),
        (1, 7),
        (7, 4),
        (4, 1)]);
    // make an identical replacement of n(4) and leave a hole
    let x = gr.add_node(());
    gr.add_edge(n(7), x, ());
    gr.add_edge(x, n(1), ());
    gr.remove_node(n(4));

    assert_sccs_eq(tarjan_scc(&gr), vec![
        vec![n(0), n(3), n(6)],
        vec![n(1), n(7),   x ],
        vec![n(2), n(5), n(8)],
    ]);
}

fn make_graph<Ty>() -> StableGraph<(), i32, Ty>
    where Ty: EdgeType,
{
    let mut gr = StableGraph::default();
    let mut c = 0..;
    let mut e = || -> i32 { c.next().unwrap() };
    gr.extend_with_edges(&[
        (6, 0, e()),
        (0, 3, e()),
        (3, 6, e()),
        (8, 6, e()),
        (8, 2, e()),
        (2, 5, e()),
        (5, 8, e()),
        (7, 5, e()),
        (1, 7, e()),
        (7, 4, e()),
        (8, 6, e()), // parallel edge
        (4, 1, e())]);
    // make an identical replacement of n(4) and leave a hole
    let x = gr.add_node(());
    gr.add_edge(n(7), x, e());
    gr.add_edge(x, n(1), e());
    gr.add_edge(x, x, e()); // make two self loops
    let rm_self_loop = gr.add_edge(x, x, e());
    gr.add_edge(x, x, e());
    gr.remove_node(n(4));
    gr.remove_node(n(6));
    gr.remove_edge(rm_self_loop);
    gr
}

defmac!(edges ref gr, x => gr.edges(x).map(|r| (r.target(), *r.weight())));

#[test]
fn test_edges_directed() {
    let gr = make_graph::<Directed>();
    let x = n(9);
    assert_equal(edges!(gr, x), vec![(x, 16), (x, 14), (n(1), 13)]);
    assert_equal(edges!(gr, n(0)), vec![(n(3), 1)]);
    assert_equal(edges!(gr, n(4)), vec![]);
}

#[test]
fn test_edge_references() {
    let gr = make_graph::<Directed>();
    assert_eq!(gr.edge_count(), gr.edge_references().count());
}

#[test]
fn test_edges_undirected() {
    let gr = make_graph::<Undirected>();
    let x = n(9);
    assert_equal(edges!(gr, x), vec![(x, 16), (x, 14), (n(1), 13), (n(7), 12)]);
    assert_equal(edges!(gr, n(0)), vec![(n(3), 1)]);
    assert_equal(edges!(gr, n(4)), vec![]);
}

#[test]
fn test_edge_iterators_directed() {
    let gr = make_graph::<Directed>();
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Outgoing),
            gr.edges(i));
    }
    let mut incoming = vec![Vec::new(); gr.node_bound()];

    for i in gr.node_indices() {
        for j in gr.neighbors(i) {
            incoming[j.index()].push(i);
        }
    }

    println!("{:#?}", gr);
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Incoming).map(|e| e.source()),
            incoming[i.index()].iter().rev().cloned());
    }
}

#[test]
fn test_edge_iterators_undir() {
    let gr = make_graph::<Undirected>();
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Outgoing),
            gr.edges(i));
    }
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Incoming),
            gr.edges(i));
    }
}

#[test]
#[should_panic(expected="is not a node")]
fn add_edge_vacant() {
    let mut g = StableGraph::<_, _>::new();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let _ = g.add_node(2);
    let _ = g.remove_node(b);
    g.add_edge(a, b, 1);
}

#[test]
#[should_panic(expected="is not a node")]
fn add_edge_oob() {
    let mut g = StableGraph::<_, _>::new();
    let a = g.add_node(0);
    let _ = g.add_node(1);
    let _ = g.add_node(2);
    g.add_edge(a, n(4), 1);
}

#[test]
fn test_node_references() {
    let gr = scc_graph();

    itertools::assert_equal(
        gr.node_references().map(|(i, _)| i),
        gr.node_indices());
}

#[test]
fn iterators_undir() {
    let mut g = StableUnGraph::<_, _>::default();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let c = g.add_node(2);
    let d = g.add_node(3);
    g.extend_with_edges(&[
        (a, b, 1),
        (a, c, 2),
        (b, c, 3),
        (c, c, 4),
        (a, d, 5),
    ]);
    g.remove_node(b);

    itertools::assert_equal(
        g.neighbors(a),
        vec![d, c],
    );
    itertools::assert_equal(
        g.neighbors(c),
        vec![c, a],
    );
    itertools::assert_equal(
        g.neighbors(d),
        vec![a],
    );

    // the node that was removed
    itertools::assert_equal(
        g.neighbors(b),
        vec![],
    );

    // remove one more
    g.remove_node(c);
    itertools::assert_equal(
        g.neighbors(c),
        vec![],
    );
}

#[test]
fn dot() {
    let mut gr = StableGraph::new();
    let a = gr.add_node("x");
    let b = gr.add_node("y");
    gr.add_edge(a, a, "10");
    gr.add_edge(a, b, "20");
    let dot_output = format!("{}", Dot::new(&gr));
    assert_eq!(dot_output,
r#"digraph {
    0 [label="x"]
    1 [label="y"]
    0 -> 0 [label="10"]
    0 -> 1 [label="20"]
}
"#);
}

defmac!(iter_eq a, b => a.eq(b));
defmac!(nodes_eq ref a, ref b => a.node_references().eq(b.node_references()));
defmac!(edgew_eq ref a, ref b => a.edge_references().eq(b.edge_references()));
defmac!(edges_eq ref a, ref b =>
        iter_eq!(
            a.edge_references().map(|e| (e.source(), e.target())),
            b.edge_references().map(|e| (e.source(), e.target()))));

#[test]
fn from() {
    let mut gr1 = StableGraph::new();
    let a = gr1.add_node(1);
    let b = gr1.add_node(2);
    let c = gr1.add_node(3);
    gr1.add_edge(a, a, 10);
    gr1.add_edge(a, b, 20);
    gr1.add_edge(b, c, 30);
    gr1.add_edge(a, c, 40);

    let gr2 = Graph::from(gr1.clone());
    let gr3 = StableGraph::from(gr2.clone());
    assert!(nodes_eq!(gr1, gr3));
    assert!(edgew_eq!(gr1, gr3));
    assert!(edges_eq!(gr1, gr3));

    gr1.remove_node(b);

    let gr4 = Graph::from(gr1.clone());
    let gr5 = StableGraph::from(gr4.clone());

    let mut ans = StableGraph::new();
    let a = ans.add_node(1);
    let c = ans.add_node(3);
    ans.add_edge(a, a, 10);
    ans.add_edge(a, c, 40);

    assert!(nodes_eq!(gr4, ans));
    assert!(edges_eq!(gr4, ans));

    assert!(nodes_eq!(gr5, ans));
    assert!(edgew_eq!(gr5, ans));
    assert!(edges_eq!(gr5, ans));
}
<commit_msg>MAINT: Cleanup in test, reuse the same graph creation<commit_after>#![cfg(feature = "stable_graph")]

extern crate petgraph;
extern crate itertools;
#[macro_use] extern crate defmac;

use petgraph::prelude::*;
use petgraph::stable_graph::node_index as n;
use petgraph::EdgeType;
use petgraph::algo::{kosaraju_scc, tarjan_scc};
use petgraph::visit::{
    NodeIndexable,
    IntoNodeReferences,
    IntoEdgeReferences,
};
use petgraph::dot::Dot;

use itertools::assert_equal;

#[test]
fn node_indices() {
    let mut g = StableGraph::<_, ()>::new();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let c = g.add_node(2);
    g.remove_node(b);
    let mut iter = g.node_indices();
    assert_eq!(iter.next(), Some(a));
    assert_eq!(iter.next(), Some(c));
    assert_eq!(iter.next(), None);
}

#[test]
fn node_bound() {
    let mut g = StableGraph::<_, ()>::new();
    assert_eq!(g.node_bound(), g.node_count());
    for i in 0..10 {
        g.add_node(i);
        assert_eq!(g.node_bound(), g.node_count());
    }
    let full_count = g.node_count();
    g.remove_node(n(0));
    g.remove_node(n(2));
    assert_eq!(g.node_bound(), full_count);
    g.clear();
    assert_eq!(g.node_bound(), 0);
}

fn assert_sccs_eq(mut res: Vec<Vec<NodeIndex>>, normalized: Vec<Vec<NodeIndex>>) {
    // normalize the result and compare with the answer.
    for scc in &mut res {
        scc.sort();
    }
    // sort by minimum element
    res.sort_by(|v, w| v[0].cmp(&w[0]));
    assert_eq!(res, normalized);
}

fn scc_graph() -> StableGraph<(), ()> {
    let mut gr: StableGraph<(), ()> = StableGraph::from_edges(&[
        (6, 0),
        (0, 3),
        (3, 6),
        (8, 6), (8, 2),
        (2, 5), (5, 8), (7, 5),
        (1, 7),
        (7, 4),
        (4, 1)]);
    // make an identical replacement of n(4) and leave a hole
    let x = gr.add_node(());
    gr.add_edge(n(7), x, ());
    gr.add_edge(x, n(1), ());
    gr.remove_node(n(4));
    gr
}

#[test]
fn test_scc() {
    let gr = scc_graph();
    println!("{:?}", gr);

    let x = n(gr.node_bound() - 1);
    assert_sccs_eq(kosaraju_scc(&gr), vec![
        vec![n(0), n(3), n(6)],
        vec![n(1), n(7),   x ],
        vec![n(2), n(5), n(8)],
    ]);
}

#[test]
fn test_tarjan_scc() {
    let gr = scc_graph();

    let x = n(gr.node_bound() - 1);
    assert_sccs_eq(tarjan_scc(&gr), vec![
        vec![n(0), n(3), n(6)],
        vec![n(1), n(7),   x ],
        vec![n(2), n(5), n(8)],
    ]);
}

fn make_graph<Ty>() -> StableGraph<(), i32, Ty>
    where Ty: EdgeType,
{
    let mut gr = StableGraph::default();
    let mut c = 0..;
    let mut e = || -> i32 { c.next().unwrap() };
    gr.extend_with_edges(&[
        (6, 0, e()),
        (0, 3, e()),
        (3, 6, e()),
        (8, 6, e()),
        (8, 2, e()),
        (2, 5, e()),
        (5, 8, e()),
        (7, 5, e()),
        (1, 7, e()),
        (7, 4, e()),
        (8, 6, e()), // parallel edge
        (4, 1, e())]);
    // make an identical replacement of n(4) and leave a hole
    let x = gr.add_node(());
    gr.add_edge(n(7), x, e());
    gr.add_edge(x, n(1), e());
    gr.add_edge(x, x, e()); // make two self loops
    let rm_self_loop = gr.add_edge(x, x, e());
    gr.add_edge(x, x, e());
    gr.remove_node(n(4));
    gr.remove_node(n(6));
    gr.remove_edge(rm_self_loop);
    gr
}

defmac!(edges ref gr, x => gr.edges(x).map(|r| (r.target(), *r.weight())));

#[test]
fn test_edges_directed() {
    let gr = make_graph::<Directed>();
    let x = n(9);
    assert_equal(edges!(gr, x), vec![(x, 16), (x, 14), (n(1), 13)]);
    assert_equal(edges!(gr, n(0)), vec![(n(3), 1)]);
    assert_equal(edges!(gr, n(4)), vec![]);
}

#[test]
fn test_edge_references() {
    let gr = make_graph::<Directed>();
    assert_eq!(gr.edge_count(), gr.edge_references().count());
}

#[test]
fn test_edges_undirected() {
    let gr = make_graph::<Undirected>();
    let x = n(9);
    assert_equal(edges!(gr, x), vec![(x, 16), (x, 14), (n(1), 13), (n(7), 12)]);
    assert_equal(edges!(gr, n(0)), vec![(n(3), 1)]);
    assert_equal(edges!(gr, n(4)), vec![]);
}

#[test]
fn test_edge_iterators_directed() {
    let gr = make_graph::<Directed>();
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Outgoing),
            gr.edges(i));
    }
    let mut incoming = vec![Vec::new(); gr.node_bound()];

    for i in gr.node_indices() {
        for j in gr.neighbors(i) {
            incoming[j.index()].push(i);
        }
    }

    println!("{:#?}", gr);
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Incoming).map(|e| e.source()),
            incoming[i.index()].iter().rev().cloned());
    }
}

#[test]
fn test_edge_iterators_undir() {
    let gr = make_graph::<Undirected>();
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Outgoing),
            gr.edges(i));
    }
    for i in gr.node_indices() {
        itertools::assert_equal(
            gr.edges_directed(i, Incoming),
            gr.edges(i));
    }
}

#[test]
#[should_panic(expected="is not a node")]
fn add_edge_vacant() {
    let mut g = StableGraph::<_, _>::new();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let _ = g.add_node(2);
    let _ = g.remove_node(b);
    g.add_edge(a, b, 1);
}

#[test]
#[should_panic(expected="is not a node")]
fn add_edge_oob() {
    let mut g = StableGraph::<_, _>::new();
    let a = g.add_node(0);
    let _ = g.add_node(1);
    let _ = g.add_node(2);
    g.add_edge(a, n(4), 1);
}

#[test]
fn test_node_references() {
    let gr = scc_graph();

    itertools::assert_equal(
        gr.node_references().map(|(i, _)| i),
        gr.node_indices());
}

#[test]
fn iterators_undir() {
    let mut g = StableUnGraph::<_, _>::default();
    let a = g.add_node(0);
    let b = g.add_node(1);
    let c = g.add_node(2);
    let d = g.add_node(3);
    g.extend_with_edges(&[
        (a, b, 1),
        (a, c, 2),
        (b, c, 3),
        (c, c, 4),
        (a, d, 5),
    ]);
    g.remove_node(b);

    itertools::assert_equal(
        g.neighbors(a),
        vec![d, c],
    );
    itertools::assert_equal(
        g.neighbors(c),
        vec![c, a],
    );
    itertools::assert_equal(
        g.neighbors(d),
        vec![a],
    );

    // the node that was removed
    itertools::assert_equal(
        g.neighbors(b),
        vec![],
    );

    // remove one more
    g.remove_node(c);
    itertools::assert_equal(
        g.neighbors(c),
        vec![],
    );
}

#[test]
fn dot() {
    let mut gr = StableGraph::new();
    let a = gr.add_node("x");
    let b = gr.add_node("y");
    gr.add_edge(a, a, "10");
    gr.add_edge(a, b, "20");
    let dot_output = format!("{}", Dot::new(&gr));
    assert_eq!(dot_output,
r#"digraph {
    0 [label="x"]
    1 [label="y"]
    0 -> 0 [label="10"]
    0 -> 1 [label="20"]
}
"#);
}

defmac!(iter_eq a, b => a.eq(b));
defmac!(nodes_eq ref a, ref b => a.node_references().eq(b.node_references()));
defmac!(edgew_eq ref a, ref b => a.edge_references().eq(b.edge_references()));
defmac!(edges_eq ref a, ref b =>
        iter_eq!(
            a.edge_references().map(|e| (e.source(), e.target())),
            b.edge_references().map(|e| (e.source(), e.target()))));

#[test]
fn from() {
    let mut gr1 = StableGraph::new();
    let a = gr1.add_node(1);
    let b = gr1.add_node(2);
    let c = gr1.add_node(3);
    gr1.add_edge(a, a, 10);
    gr1.add_edge(a, b, 20);
    gr1.add_edge(b, c, 30);
    gr1.add_edge(a, c, 40);

    let gr2 = Graph::from(gr1.clone());
    let gr3 = StableGraph::from(gr2.clone());
    assert!(nodes_eq!(gr1, gr3));
    assert!(edgew_eq!(gr1, gr3));
    assert!(edges_eq!(gr1, gr3));

    gr1.remove_node(b);

    let gr4 = Graph::from(gr1.clone());
    let gr5 = StableGraph::from(gr4.clone());

    let mut ans = StableGraph::new();
    let a = ans.add_node(1);
    let c = ans.add_node(3);
    ans.add_edge(a, a, 10);
    ans.add_edge(a, c, 40);

    assert!(nodes_eq!(gr4, ans));
    assert!(edges_eq!(gr4, ans));

    assert!(nodes_eq!(gr5, ans));
    assert!(edgew_eq!(gr5, ans));
    assert!(edges_eq!(gr5, ans));
}
<|endoftext|>


<commit_before>#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .exec()
        .expect("could not obtain cargo metadata");
    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
        .exec()
        .expect("could not obtain cargo metadata");
    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
    for package in &clippy_meta.packages[0].dependencies {
        if package.name == "clippy_lints" {
            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
            return;
        }
    }
}
<commit_msg>test(versioncheck): Use .no_deps()<commit_after>#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");
    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
    for package in &clippy_meta.packages[0].dependencies {
        if package.name == "clippy_lints" {
            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
            return;
        }
    }
}
<|endoftext|>


<commit_before>use ruma_identifiers::RoomVersionId;

use crate::{Error, Result};

#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum RoomDisposition {
    /// A room version that has a stable specification.
    Stable,
    /// A room version that is not yet fully specified.
    #[allow(dead_code)]
    Unstable,
}

#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum EventFormatVersion {
    /// $id:server event id format
    V1,
    /// MSC1659-style $hash event id format: introduced for room v3
    V2,
    /// MSC1884-style $hash format: introduced for room v4
    V3,
}

#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum StateResolutionVersion {
    /// State resolution for rooms at version 1.
    V1,
    /// State resolution for room at version 2 or later.
    V2,
}

#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RoomVersion {
    /// The version this room is set to.
    pub version: RoomVersionId,
    /// The stability of this room.
    pub disposition: RoomDisposition,
    /// The format of the EventId.
    pub event_format: EventFormatVersion,
    /// Which state resolution algorithm is used.
    pub state_res: StateResolutionVersion,
    // FIXME: not sure what this one means?
    pub enforce_key_validity: bool,

    /// `m.room.aliases` had special auth rules and redaction rules
    /// before room version 6.
    ///
    /// before MSC2261/MSC2432,
    pub special_case_aliases_auth: bool,
    /// Strictly enforce canonical json, do not allow:
    /// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
    /// * Floats
    /// * NaN, Infinity, -Infinity
    pub strict_canonicaljson: bool,
    /// Verify notifications key while checking m.room.power_levels.
    ///
    /// bool: MSC2209: Check 'notifications'
    pub limit_notifications_power_levels: bool,
    /// Extra rules when verifying redaction events.
    pub extra_redaction_checks: bool,
}

impl RoomVersion {
    pub fn new(version: &RoomVersionId) -> Result<Self> {
        Ok(match version {
            RoomVersionId::Version1 => Self::version_1(),
            RoomVersionId::Version2 => Self::version_2(),
            RoomVersionId::Version3 => Self::version_3(),
            RoomVersionId::Version4 => Self::version_4(),
            RoomVersionId::Version5 => Self::version_5(),
            RoomVersionId::Version6 => Self::version_6(),
            ver => return Err(Error::Unsupported(format!("found version `{}`", ver.as_str()))),
        })
    }

    pub fn version_1() -> Self {
        Self {
            version: RoomVersionId::Version1,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V1,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
        }
    }

    pub fn version_2() -> Self {
        Self {
            version: RoomVersionId::Version2,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
        }
    }

    pub fn version_3() -> Self {
        Self {
            version: RoomVersionId::Version3,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V2,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_4() -> Self {
        Self {
            version: RoomVersionId::Version4,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_5() -> Self {
        Self {
            version: RoomVersionId::Version5,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_6() -> Self {
        Self {
            version: RoomVersionId::Version6,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
        }
    }
}
<commit_msg>state-res: Add info for room version 7, 8, and 9<commit_after>use ruma_identifiers::RoomVersionId;

use crate::{Error, Result};

#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum RoomDisposition {
    /// A room version that has a stable specification.
    Stable,
    /// A room version that is not yet fully specified.
    #[allow(dead_code)]
    Unstable,
}

#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum EventFormatVersion {
    /// $id:server event id format
    V1,
    /// MSC1659-style $hash event id format: introduced for room v3
    V2,
    /// MSC1884-style $hash format: introduced for room v4
    V3,
}

#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum StateResolutionVersion {
    /// State resolution for rooms at version 1.
    V1,
    /// State resolution for room at version 2 or later.
    V2,
}

#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RoomVersion {
    /// The version this room is set to.
    pub version: RoomVersionId,
    /// The stability of this room.
    pub disposition: RoomDisposition,
    /// The format of the EventId.
    pub event_format: EventFormatVersion,
    /// Which state resolution algorithm is used.
    pub state_res: StateResolutionVersion,
    // FIXME: not sure what this one means?
    pub enforce_key_validity: bool,

    /// `m.room.aliases` had special auth rules and redaction rules
    /// before room version 6.
    ///
    /// before MSC2261/MSC2432,
    pub special_case_aliases_auth: bool,
    /// Strictly enforce canonical json, do not allow:
    /// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
    /// * Floats
    /// * NaN, Infinity, -Infinity
    pub strict_canonicaljson: bool,
    /// Verify notifications key while checking m.room.power_levels.
    ///
    /// bool: MSC2209: Check 'notifications'
    pub limit_notifications_power_levels: bool,
    /// Extra rules when verifying redaction events.
    pub extra_redaction_checks: bool,
    /// Allow knocking in event authentication.
    pub allow_knocking: bool,
    /// Allow knocking in event authentication.
    pub restricted_join_rules: bool,
}

impl RoomVersion {
    pub fn new(version: &RoomVersionId) -> Result<Self> {
        Ok(match version {
            RoomVersionId::Version1 => Self::version_1(),
            RoomVersionId::Version2 => Self::version_2(),
            RoomVersionId::Version3 => Self::version_3(),
            RoomVersionId::Version4 => Self::version_4(),
            RoomVersionId::Version5 => Self::version_5(),
            RoomVersionId::Version6 => Self::version_6(),
            #[cfg(feature = "unstable-pre-spec")]
            RoomVersionId::Version7 => Self::version_7(),
            #[cfg(feature = "unstable-pre-spec")]
            RoomVersionId::Version8 => Self::version_8(),
            #[cfg(feature = "unstable-pre-spec")]
            RoomVersionId::Version9 => Self::version_9(),
            ver => return Err(Error::Unsupported(format!("found version `{}`", ver.as_str()))),
        })
    }

    pub fn version_1() -> Self {
        Self {
            version: RoomVersionId::Version1,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V1,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
            allow_knocking: false,
            restricted_join_rules: false,
        }
    }

    pub fn version_2() -> Self {
        Self {
            version: RoomVersionId::Version2,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
            allow_knocking: false,
            restricted_join_rules: false,
        }
    }

    pub fn version_3() -> Self {
        Self {
            version: RoomVersionId::Version3,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V2,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
            allow_knocking: false,
            restricted_join_rules: false,
        }
    }

    pub fn version_4() -> Self {
        Self {
            version: RoomVersionId::Version4,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
            allow_knocking: false,
            restricted_join_rules: false,
        }
    }

    pub fn version_5() -> Self {
        Self {
            version: RoomVersionId::Version5,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
            allow_knocking: false,
            restricted_join_rules: false,
        }
    }

    pub fn version_6() -> Self {
        Self {
            version: RoomVersionId::Version6,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
            allow_knocking: false,
            restricted_join_rules: false,
        }
    }

    #[cfg(feature = "unstable-pre-spec")]
    pub fn version_7() -> Self {
        Self {
            version: RoomVersionId::Version7,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
            allow_knocking: true,
            restricted_join_rules: false,
        }
    }

    #[cfg(feature = "unstable-pre-spec")]
    pub fn version_8() -> Self {
        Self {
            version: RoomVersionId::Version8,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
            // Wait so should this be false?
            // because: https://github.com/matrix-org/matrix-doc/pull/3289#issuecomment-884165177
            allow_knocking: false,
            restricted_join_rules: true,
        }
    }

    #[cfg(feature = "unstable-pre-spec")]
    pub fn version_9() -> Self {
        Self {
            version: RoomVersionId::Version9,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
            allow_knocking: true,
            restricted_join_rules: true,
        }
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! A simple allocator using the [`InitParams`] memory map.
//!
//! This is basically just a bump pointer allocator for frames; since
//! it doesn't support deallocating frames.
use super::{Frame, FrameRange, Allocator};
use ::{AllocResult, AllocErr, Layout};
use params::{InitParams, mem};
use memory::{Page, PAGE_SIZE, PAddr};

use core::iter::Step;
use core::convert::From;
/// A simple area allocator.
///
/// This is based on the memory area allocation scheme described
/// by Phil Oppermann at [http://os.phil-opp.com/allocating-frames.html].
///
/// This is Not A Good Allocation Scheme, as it does not currently support
/// reallocation of freed frames. The plan is that it will only be used
/// initially, and after we've allocated everything once, we'll switch over
/// to a better allocator.
pub struct MemMapAllocator<'a> { next_free: Frame
                               , current_area: Option<&'a mem::Area>
                               , areas: mem::Map<'a>
                               , kernel_frames: FrameRange
                               , mb_frames: FrameRange
                               }
impl<'a> MemMapAllocator<'a> {
    fn next_area(&mut self) {
        // println!("In next_area");
        self.current_area
            = self.areas
                  .clone()
                  .filter(|a|
                      Frame::containing(a.end_addr) >= self.next_free)
                  .min_by_key(|a| a.start_addr)
                  .map(|area| {
                      let start = Frame::containing(area.start_addr);
                      if self.next_free > start { self.next_free = start };
                      area
                  })
    }

}

impl<'a> From<&'a InitParams> for MemMapAllocator<'a> {
    fn from(params: &'a InitParams) -> Self {
        let mut new_allocator = MemMapAllocator {
              next_free: Frame::containing(PAddr::new(0x0))
            , current_area: None
            , areas: params.mem_map()
            , kernel_frames: params.kernel_frames()
            // TODO: handle non-multiboot case
            , mb_frames: Frame::containing(params.multiboot_start()) ..
                         Frame::containing(params.multiboot_end())
            };
        new_allocator.next_area();
        new_allocator
    }
}

impl<'a> Allocator for MemMapAllocator<'a> {
    // type Frame = Frame;

    unsafe fn allocate(&mut self) -> AllocResult<Frame> {
        // // println!("In alloc method");
        if let Some(area) = self.current_area {
            match self.next_free {
                // all frames in the current memory area are in use
                f if f > Frame::containing(area.end_addr) => {
                    // so we advance to the next free area

                    // println!("All frames in current area in use.");
                    self.next_area();
                    // println!("...and returning None");
                }
              , // this frame is in use by the kernel.
                f if self.kernel_frames.contains(f) => {
                    // skip ahead to the end of the kernel
                    // println!("In kernel frame, skipping.");
                    self.next_free = self.kernel_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is part of the multiboot info.
                f if self.mb_frames.contains(f) => {
                    // skip ahead to the end of the multiboot info.
                    // println!("In multiboot frame, skipping...");
                    self.next_free = self.mb_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is free.
                frame => {
                    // advance the next free frame and return this frame.
                    // println!("In free frame, advancing...");
                    self.next_free = self.next_free.add_one();
                    // println!("...and returning {:?}", frame);
                    return Ok(frame)
                }
            };
            self.allocate()
        } else {
            // println!("No free frames remain!");
            Err(AllocErr::Exhausted {
                    request: Layout::from_size_align( PAGE_SIZE as usize, PAGE_SIZE as usize)
            })
        }
    }

    /// Deallocate a frame
    unsafe fn deallocate(&mut self, _frame: Frame) {
        // just leak it
    }

    /// Allocate a range of frames
    unsafe fn allocate_range(&mut self, _num: usize) -> AllocResult<FrameRange> {
        unimplemented!()
    }
    /// Deallocate a range of frames
    unsafe fn deallocate_range(&mut self, _range: FrameRange) {
        //just leak it
    }
}
<commit_msg>fix(alloc::frame::mem_map): MemMapFrameAllocator no longer clones<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! A simple allocator using the [`InitParams`] memory map.
//!
//! This is basically just a bump pointer allocator for frames; since
//! it doesn't support deallocating frames.
use super::{Frame, FrameRange, Allocator};
use ::{AllocResult, AllocErr, Layout};
use params::{InitParams, mem};
use memory::{Page, PAGE_SIZE, PAddr};

use core::iter::Step;
use core::convert::From;
/// A simple area allocator.
///
/// This is based on the memory area allocation scheme described
/// by Phil Oppermann at [http://os.phil-opp.com/allocating-frames.html].
///
/// This is Not A Good Allocation Scheme, as it does not currently support
/// reallocation of freed frames. The plan is that it will only be used
/// initially, and after we've allocated everything once, we'll switch over
/// to a better allocator.
pub struct MemMapAllocator<'a> { next_free: Frame
                               , current_area: Option<&'a mem::Area>
                               , areas: mem::Map<'a>
                               , kernel_frames: FrameRange
                               , mb_frames: FrameRange
                               }
impl<'a> MemMapAllocator<'a> {
    fn next_area(&mut self) {
        // println!("In next_area");
        self.current_area
            = self.areas.clone()
                  .filter(|a|
                      Frame::containing(a.end_addr) >= self.next_free)
                  .min_by_key(|a| a.start_addr)
                  .map(|area| {
                      let start = Frame::containing(area.start_addr);
                      if self.next_free > start { self.next_free = start };
                      area
                  })
    }

}

impl<'a> From<&'a InitParams> for MemMapAllocator<'a> {
    fn from(params: &'a InitParams) -> Self {
        let mut new_allocator = MemMapAllocator {
              next_free: Frame::containing(PAddr::new(0x12000))
            , current_area: None
            , areas: params.mem_map()
            , kernel_frames: params.kernel_frames()
            // TODO: handle non-multiboot case
            , mb_frames: Frame::containing(params.multiboot_start()) ..
                         Frame::containing(params.multiboot_end()).add_one()
            };
        trace!("creating mem map allocator");
        trace!("kernel frames: {:?}", new_allocator.kernel_frames);
        trace!("multiboot frames: {:?}", new_allocator.mb_frames);
        new_allocator.next_area();
        new_allocator
    }
}

impl<'a> Allocator for MemMapAllocator<'a> {
    // type Frame = Frame;

    unsafe fn allocate(&mut self) -> AllocResult<Frame> {
        // // println!("In alloc method");
        if let Some(area) = self.current_area {
            let frame = Frame { number: self.next_free.number };
            match frame {
                // all frames in the current memory area are in use
                f if f > Frame::containing(area.end_addr) => {
                    // so we advance to the next free area

                    // println!("All frames in current area in use.");
                    self.next_area();
                    // println!("...and returning None");
                }
              , // this frame is in use by the kernel.
                f if f >= self.kernel_frames.start && f <= self.kernel_frames.end => {
                    // skip ahead to the end of the kernel
                    // println!("In kernel frame, skipping.");
                    self.next_free = self.kernel_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is part of the multiboot info.
                f if f >= self.mb_frames.start && f <= self.mb_frames.end=> {
                    // skip ahead to the end of the multiboot info.
                    // println!("In multiboot frame, skipping...");
                    self.next_free = self.mb_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is free.
                frame => {
                    // advance the next free frame and return this frame.
                    // println!("In free frame, advancing...");
                    self.next_free = self.next_free.add_one();
                    // println!("...and returning {:?}", frame);
                    trace!("allocated {:?}", frame);
                    return Ok(frame)
                }
            };
            self.allocate()
        } else {
            // println!("No free frames remain!");
            Err(AllocErr::Exhausted {
                    request: Layout::from_size_align( PAGE_SIZE as usize, PAGE_SIZE as usize)
            })
        }
    }

    /// Deallocate a frame
    unsafe fn deallocate(&mut self, _frame: Frame) {
        // just leak it
    }

    /// Allocate a range of frames
    unsafe fn allocate_range(&mut self, _num: usize) -> AllocResult<FrameRange> {
        unimplemented!()
    }
    /// Deallocate a range of frames
    unsafe fn deallocate_range(&mut self, _range: FrameRange) {
        //just leak it
    }
}
<|endoftext|>