<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

extern crate clap;

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, File, OpenOptions};
use std::io::Result;
use std::path::Path;

#[derive(Eq, PartialEq)]
enum TruncateMode {
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp,
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static OPT_IO_BLOCKS: &str = "io-blocks";
static OPT_NO_CREATE: &str = "no-create";
static OPT_REFERENCE: &str = "reference";
static OPT_SIZE: &str = "size";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    format!(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of"
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    /*
    let args = args.collect_str();

    let mut opts = getopts::Options::new();*/
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(OPT_IO_BLOCKS)
            .short("o")
            .long(OPT_IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(OPT_NO_CREATE)
            .short("c")
            .long(OPT_NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
            .short("r")
            .long(OPT_REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(OPT_SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(OPT_IO_BLOCKS);
        let no_create = matches.is_present(OPT_NO_CREATE);
        let reference = matches.value_of(OPT_REFERENCE).map(String::from);
        let size = matches.value_of(OPT_SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            match truncate(no_create, io_blocks, reference, size, files) {
                Ok(()) => ( /* pass */ ),
                Err(_) => return 1,
            }
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> Result<()> {
    let (refsize, mode) = match reference {
        Some(rfilename) => {
            let _ = match File::open(Path::new(&rfilename)) {
                Ok(m) => m,
                Err(f) => crash!(1, "{}", f.to_string()),
            };
            match metadata(rfilename) {
                Ok(meta) => (meta.len(), TruncateMode::Reference),
                Err(f) => crash!(1, "{}", f.to_string()),
            }
        }
        None => parse_size(size.unwrap().as_ref()),
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new()
            .read(true)
            .write(true)
            .create(!no_create)
            .open(path)
        {
            Ok(file) => {
                let fsize = match metadata(filename) {
                    Ok(meta) => meta.len(),
                    Err(f) => {
                        show_warning!("{}", f.to_string());
                        continue;
                    }
                };
                let tsize: u64 = match mode {
                    TruncateMode::Reference => refsize,
                    TruncateMode::Extend => fsize + refsize,
                    TruncateMode::Reduce => fsize - refsize,
                    TruncateMode::AtMost => {
                        if fsize > refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::AtLeast => {
                        if fsize < refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::RoundDown => fsize - fsize % refsize,
                    TruncateMode::RoundUp => fsize + fsize % refsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
    Ok(())
}

fn parse_size(size: &str) -> (u64, TruncateMode) {
    let mode = match size.chars().next().unwrap() {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '*' => TruncateMode::RoundUp,
        _ => TruncateMode::Reference, /* assume that the size is just a number */
    };
    let bytes = {
        let mut slice = if mode == TruncateMode::Reference {
            size
        } else {
            &size[1..]
        };
        if slice.chars().last().unwrap().is_alphabetic() {
            slice = &slice[..slice.len() - 1];
            if !slice.is_empty() && slice.chars().last().unwrap().is_alphabetic() {
                slice = &slice[..slice.len() - 1];
            }
        }
        slice
    }
    .to_owned();
    let mut number: u64 = match bytes.parse() {
        Ok(num) => num,
        Err(e) => crash!(1, "'{}' is not a valid number: {}", size, e),
    };
    if size.chars().last().unwrap().is_alphabetic() {
        number *= match size.chars().last().unwrap().to_ascii_uppercase() {
            'B' => match size
                .chars()
                .nth(size.len() - 2)
                .unwrap()
                .to_ascii_uppercase()
            {
                'K' => 1000u64,
                'M' => 1000u64.pow(2),
                'G' => 1000u64.pow(3),
                'T' => 1000u64.pow(4),
                'P' => 1000u64.pow(5),
                'E' => 1000u64.pow(6),
                'Z' => 1000u64.pow(7),
                'Y' => 1000u64.pow(8),
                letter => crash!(1, "'{}B' is not a valid suffix.", letter),
            },
            'K' => 1024u64,
            'M' => 1024u64.pow(2),
            'G' => 1024u64.pow(3),
            'T' => 1024u64.pow(4),
            'P' => 1024u64.pow(5),
            'E' => 1024u64.pow(6),
            'Z' => 1024u64.pow(7),
            'Y' => 1024u64.pow(8),
            letter => crash!(1, "'{}' is not a valid suffix.", letter),
        };
    }
    (number, mode)
}
<commit_msg>remove the useless format<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

extern crate clap;

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, File, OpenOptions};
use std::io::Result;
use std::path::Path;

#[derive(Eq, PartialEq)]
enum TruncateMode {
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp,
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static OPT_IO_BLOCKS: &str = "io-blocks";
static OPT_NO_CREATE: &str = "no-create";
static OPT_REFERENCE: &str = "reference";
static OPT_SIZE: &str = "size";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    /*
    let args = args.collect_str();

    let mut opts = getopts::Options::new();*/
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(OPT_IO_BLOCKS)
            .short("o")
            .long(OPT_IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(OPT_NO_CREATE)
            .short("c")
            .long(OPT_NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
            .short("r")
            .long(OPT_REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(OPT_SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(OPT_IO_BLOCKS);
        let no_create = matches.is_present(OPT_NO_CREATE);
        let reference = matches.value_of(OPT_REFERENCE).map(String::from);
        let size = matches.value_of(OPT_SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            match truncate(no_create, io_blocks, reference, size, files) {
                Ok(()) => ( /* pass */ ),
                Err(_) => return 1,
            }
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) -> Result<()> {
    let (refsize, mode) = match reference {
        Some(rfilename) => {
            let _ = match File::open(Path::new(&rfilename)) {
                Ok(m) => m,
                Err(f) => crash!(1, "{}", f.to_string()),
            };
            match metadata(rfilename) {
                Ok(meta) => (meta.len(), TruncateMode::Reference),
                Err(f) => crash!(1, "{}", f.to_string()),
            }
        }
        None => parse_size(size.unwrap().as_ref()),
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new()
            .read(true)
            .write(true)
            .create(!no_create)
            .open(path)
        {
            Ok(file) => {
                let fsize = match metadata(filename) {
                    Ok(meta) => meta.len(),
                    Err(f) => {
                        show_warning!("{}", f.to_string());
                        continue;
                    }
                };
                let tsize: u64 = match mode {
                    TruncateMode::Reference => refsize,
                    TruncateMode::Extend => fsize + refsize,
                    TruncateMode::Reduce => fsize - refsize,
                    TruncateMode::AtMost => {
                        if fsize > refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::AtLeast => {
                        if fsize < refsize {
                            refsize
                        } else {
                            fsize
                        }
                    }
                    TruncateMode::RoundDown => fsize - fsize % refsize,
                    TruncateMode::RoundUp => fsize + fsize % refsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
    Ok(())
}

fn parse_size(size: &str) -> (u64, TruncateMode) {
    let mode = match size.chars().next().unwrap() {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '*' => TruncateMode::RoundUp,
        _ => TruncateMode::Reference, /* assume that the size is just a number */
    };
    let bytes = {
        let mut slice = if mode == TruncateMode::Reference {
            size
        } else {
            &size[1..]
        };
        if slice.chars().last().unwrap().is_alphabetic() {
            slice = &slice[..slice.len() - 1];
            if !slice.is_empty() && slice.chars().last().unwrap().is_alphabetic() {
                slice = &slice[..slice.len() - 1];
            }
        }
        slice
    }
    .to_owned();
    let mut number: u64 = match bytes.parse() {
        Ok(num) => num,
        Err(e) => crash!(1, "'{}' is not a valid number: {}", size, e),
    };
    if size.chars().last().unwrap().is_alphabetic() {
        number *= match size.chars().last().unwrap().to_ascii_uppercase() {
            'B' => match size
                .chars()
                .nth(size.len() - 2)
                .unwrap()
                .to_ascii_uppercase()
            {
                'K' => 1000u64,
                'M' => 1000u64.pow(2),
                'G' => 1000u64.pow(3),
                'T' => 1000u64.pow(4),
                'P' => 1000u64.pow(5),
                'E' => 1000u64.pow(6),
                'Z' => 1000u64.pow(7),
                'Y' => 1000u64.pow(8),
                letter => crash!(1, "'{}B' is not a valid suffix.", letter),
            },
            'K' => 1024u64,
            'M' => 1024u64.pow(2),
            'G' => 1024u64.pow(3),
            'T' => 1024u64.pow(4),
            'P' => 1024u64.pow(5),
            'E' => 1024u64.pow(6),
            'Z' => 1024u64.pow(7),
            'Y' => 1024u64.pow(8),
            letter => crash!(1, "'{}' is not a valid suffix.", letter),
        };
    }
    (number, mode)
}
<|endoftext|>


<commit_before>use crate::utils::{match_def_path, span_lint_and_help};
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for usage of invalid atomic
    /// ordering in atomic loads/stores/exchanges/updates and
    /// memory fences.
    ///
    /// **Why is this bad?** Using an invalid atomic ordering
    /// will cause a panic at run-time.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,no_run
    /// # use std::sync::atomic::{self, AtomicU8, Ordering};
    ///
    /// let x = AtomicU8::new(0);
    ///
    /// let _ = x.load(Ordering::Release);
    /// let _ = x.load(Ordering::AcqRel);
    ///
    /// x.store(1, Ordering::Acquire);
    /// x.store(2, Ordering::AcqRel);
    ///
    /// atomic::fence(Ordering::Relaxed);
    /// atomic::compiler_fence(Ordering::Relaxed);
    ///
    /// let _ = x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::SeqCst);
    /// let _ = x.compare_exchange_weak(2, 3, Ordering::SeqCst, Ordering::Release);
    /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val + val));
    /// ```
    pub INVALID_ATOMIC_ORDERING,
    correctness,
    "usage of invalid atomic ordering in atomic operations and memory fences"
}

declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);

const ATOMIC_TYPES: [&str; 12] = [
    "AtomicBool",
    "AtomicI8",
    "AtomicI16",
    "AtomicI32",
    "AtomicI64",
    "AtomicIsize",
    "AtomicPtr",
    "AtomicU8",
    "AtomicU16",
    "AtomicU32",
    "AtomicU64",
    "AtomicUsize",
];

fn type_is_atomic(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind() {
        ATOMIC_TYPES
            .iter()
            .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty]))
    } else {
        false
    }
}

fn match_ordering_def_path(cx: &LateContext<'_>, did: DefId, orderings: &[&str]) -> bool {
    orderings
        .iter()
        .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering]))
}

fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
        let method = method_path.ident.name.as_str();
        if type_is_atomic(cx, &args[0]);
        if method == "load" || method == "store";
        let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
        if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
        if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
        then {
            if method == "load" &&
                match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
                span_lint_and_help(
                    cx,
                    INVALID_ATOMIC_ORDERING,
                    ordering_arg.span,
                    "atomic loads cannot have `Release` and `AcqRel` ordering",
                    None,
                    "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
                );
            } else if method == "store" &&
                match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
                span_lint_and_help(
                    cx,
                    INVALID_ATOMIC_ORDERING,
                    ordering_arg.span,
                    "atomic stores cannot have `Acquire` and `AcqRel` ordering",
                    None,
                    "consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
                );
            }
        }
    }
}

fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let ExprKind::Call(ref func, ref args) = expr.kind;
        if let ExprKind::Path(ref func_qpath) = func.kind;
        if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
        if ["fence", "compiler_fence"]
            .iter()
            .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
        if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
        if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
        if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
        then {
            span_lint_and_help(
                cx,
                INVALID_ATOMIC_ORDERING,
                args[0].span,
                "memory fences cannot have `Relaxed` ordering",
                None,
                "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`"
            );
        }
    }
}

fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
    if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
        cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
    } else {
        None
    }
}

fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
        let method = method_path.ident.name.as_str();
        if type_is_atomic(cx, &args[0]);
        if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update";
        let (success_order_arg, failure_order_arg) = if method == "fetch_update" {
            (&args[1], &args[2])
        } else {
            (&args[3], &args[4])
        };
        if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg);
        then {
            // Helper type holding on to some checking and error reporting data. Has
            // - (success ordering name,
            // - list of failure orderings forbidden by the success order,
            // - suggestion message)
            type OrdLintInfo = (&'static str, &'static [&'static str], &'static str);
            let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`");
            let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`");
            let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
            let release = ("Release", relaxed.1, relaxed.2);
            let acqrel = ("AcqRel", acquire.1, acquire.2);
            let search = [relaxed, acquire, seq_cst, release, acqrel];

            let success_lint_info = opt_ordering_defid(cx, success_order_arg)
                .and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
                    search
                        .iter()
                        .find(|(ordering, ..)| {
                            match_def_path(cx, success_ord_def_id,
                                &["core", "sync", "atomic", "Ordering", ordering])
                        })
                        .copied()
                });

            if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) {
                // If we don't know the success order is, use what we'd suggest
                // if it were maximally permissive.
                let suggested = success_lint_info.unwrap_or(seq_cst).2;
                span_lint_and_help(
                    cx,
                    INVALID_ATOMIC_ORDERING,
                    failure_order_arg.span,
                    &format!(
                        "{}'s failure ordering may not be `Release` or `AcqRel`",
                        method,
                    ),
                    None,
                    &format!("consider using {} instead", suggested),
                );
            } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info {
                if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) {
                    span_lint_and_help(
                        cx,
                        INVALID_ATOMIC_ORDERING,
                        failure_order_arg.span,
                        &format!(
                            "{}'s failure ordering may not be stronger than the success ordering of `{}`",
                            method,
                            success_ord_name,
                        ),
                        None,
                        &format!("consider using {} instead", suggested),
                    );
                }
            }
        }
    }
}

impl<'tcx> LateLintPass<'tcx> for AtomicOrdering {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        check_atomic_load_store(cx, expr);
        check_memory_fence(cx, expr);
        check_atomic_compare_exchange(cx, expr);
    }
}
<commit_msg>Add comments to the invalid_atomic_ordering example<commit_after>use crate::utils::{match_def_path, span_lint_and_help};
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for usage of invalid atomic
    /// ordering in atomic loads/stores/exchanges/updates and
    /// memory fences.
    ///
    /// **Why is this bad?** Using an invalid atomic ordering
    /// will cause a panic at run-time.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,no_run
    /// # use std::sync::atomic::{self, AtomicU8, Ordering};
    ///
    /// let x = AtomicU8::new(0);
    ///
    /// // Bad: `Release` and `AcqRel` cannot be used for `load`.
    /// let _ = x.load(Ordering::Release);
    /// let _ = x.load(Ordering::AcqRel);
    ///
    /// // Bad: `Acquire` and `AcqRel` cannot be used for `store`.
    /// x.store(1, Ordering::Acquire);
    /// x.store(2, Ordering::AcqRel);
    ///
    /// // Bad: `Relaxed` cannot be used as a fence's ordering.
    /// atomic::fence(Ordering::Relaxed);
    /// atomic::compiler_fence(Ordering::Relaxed);
    ///
    /// // Bad: `Release` and `AcqRel` are both always invalid
    /// // for the failure ordering (the last arg).
    /// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);
    /// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);
    ///
    /// // Bad: The failure ordering is not allowed to be
    /// // stronger than the success order, and `SeqCst` is
    /// // stronger than `Relaxed`.
    /// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));
    /// ```
    pub INVALID_ATOMIC_ORDERING,
    correctness,
    "usage of invalid atomic ordering in atomic operations and memory fences"
}

declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);

const ATOMIC_TYPES: [&str; 12] = [
    "AtomicBool",
    "AtomicI8",
    "AtomicI16",
    "AtomicI32",
    "AtomicI64",
    "AtomicIsize",
    "AtomicPtr",
    "AtomicU8",
    "AtomicU16",
    "AtomicU32",
    "AtomicU64",
    "AtomicUsize",
];

fn type_is_atomic(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind() {
        ATOMIC_TYPES
            .iter()
            .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty]))
    } else {
        false
    }
}

fn match_ordering_def_path(cx: &LateContext<'_>, did: DefId, orderings: &[&str]) -> bool {
    orderings
        .iter()
        .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering]))
}

fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
        let method = method_path.ident.name.as_str();
        if type_is_atomic(cx, &args[0]);
        if method == "load" || method == "store";
        let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
        if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
        if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
        then {
            if method == "load" &&
                match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
                span_lint_and_help(
                    cx,
                    INVALID_ATOMIC_ORDERING,
                    ordering_arg.span,
                    "atomic loads cannot have `Release` and `AcqRel` ordering",
                    None,
                    "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
                );
            } else if method == "store" &&
                match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
                span_lint_and_help(
                    cx,
                    INVALID_ATOMIC_ORDERING,
                    ordering_arg.span,
                    "atomic stores cannot have `Acquire` and `AcqRel` ordering",
                    None,
                    "consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
                );
            }
        }
    }
}

fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let ExprKind::Call(ref func, ref args) = expr.kind;
        if let ExprKind::Path(ref func_qpath) = func.kind;
        if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
        if ["fence", "compiler_fence"]
            .iter()
            .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
        if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
        if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
        if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
        then {
            span_lint_and_help(
                cx,
                INVALID_ATOMIC_ORDERING,
                args[0].span,
                "memory fences cannot have `Relaxed` ordering",
                None,
                "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`"
            );
        }
    }
}

fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
    if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
        cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
    } else {
        None
    }
}

fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
    if_chain! {
        if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
        let method = method_path.ident.name.as_str();
        if type_is_atomic(cx, &args[0]);
        if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update";
        let (success_order_arg, failure_order_arg) = if method == "fetch_update" {
            (&args[1], &args[2])
        } else {
            (&args[3], &args[4])
        };
        if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg);
        then {
            // Helper type holding on to some checking and error reporting data. Has
            // - (success ordering name,
            // - list of failure orderings forbidden by the success order,
            // - suggestion message)
            type OrdLintInfo = (&'static str, &'static [&'static str], &'static str);
            let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`");
            let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`");
            let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
            let release = ("Release", relaxed.1, relaxed.2);
            let acqrel = ("AcqRel", acquire.1, acquire.2);
            let search = [relaxed, acquire, seq_cst, release, acqrel];

            let success_lint_info = opt_ordering_defid(cx, success_order_arg)
                .and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
                    search
                        .iter()
                        .find(|(ordering, ..)| {
                            match_def_path(cx, success_ord_def_id,
                                &["core", "sync", "atomic", "Ordering", ordering])
                        })
                        .copied()
                });

            if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) {
                // If we don't know the success order is, use what we'd suggest
                // if it were maximally permissive.
                let suggested = success_lint_info.unwrap_or(seq_cst).2;
                span_lint_and_help(
                    cx,
                    INVALID_ATOMIC_ORDERING,
                    failure_order_arg.span,
                    &format!(
                        "{}'s failure ordering may not be `Release` or `AcqRel`",
                        method,
                    ),
                    None,
                    &format!("consider using {} instead", suggested),
                );
            } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info {
                if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) {
                    span_lint_and_help(
                        cx,
                        INVALID_ATOMIC_ORDERING,
                        failure_order_arg.span,
                        &format!(
                            "{}'s failure ordering may not be stronger than the success ordering of `{}`",
                            method,
                            success_ord_name,
                        ),
                        None,
                        &format!("consider using {} instead", suggested),
                    );
                }
            }
        }
    }
}

impl<'tcx> LateLintPass<'tcx> for AtomicOrdering {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        check_atomic_load_store(cx, expr);
        check_memory_fence(cx, expr);
        check_atomic_compare_exchange(cx, expr);
    }
}
<|endoftext|>


<commit_before>use model::prelude::*;
use parking_lot::RwLock;
use serde_json::Value;
use std::{
    collections::HashMap,
    sync::Arc
};
use super::context::Context;
use ::client::bridge::gateway::event::*;

/// The core trait for handling events by serenity.
pub trait EventHandler {
    /// Dispatched when the cache gets full.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    fn cached(&self, _ctx: Context, _guilds: Vec<GuildId>) {}

    /// Dispatched when a channel is created.
    ///
    /// Provides said channel's data.
    fn channel_create(&self, _ctx: Context, _channel: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a category is created.
    ///
    /// Provides said category's data.
    fn category_create(&self, _ctx: Context, _category: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a category is deleted.
    ///
    /// Provides said category's data.
    fn category_delete(&self, _ctx: Context, _category: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a private channel is created.
    ///
    /// Provides said channel's data.
    fn private_channel_create(&self, _ctx: Context, _channel: Arc<RwLock<PrivateChannel>>) {}

    /// Dispatched when a channel is deleted.
    ///
    /// Provides said channel's data.
    fn channel_delete(&self, _ctx: Context, _channel: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a pin is added, deleted.
    ///
    /// Provides said pin's data.
    fn channel_pins_update(&self, _ctx: Context, _pin: ChannelPinsUpdateEvent) {}

    /// Dispatched when a user is added to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_addition(&self, _ctx: Context, _group_id: ChannelId, _user: User) {}

    /// Dispatched when a user is removed to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_removal(&self, _ctx: Context, _group_id: ChannelId, _user: User) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the old channel data, and the new data.
    #[cfg(feature = "cache")]
    fn channel_update(&self, _ctx: Context, _old: Option<Channel>, _new: Channel) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn channel_update(&self, _ctx: Context, _new_data: Channel) {}

    /// Dispatched when a user is banned from a guild.
    ///
    /// Provides the guild's id and the banned user's data.
    fn guild_ban_addition(&self, _ctx: Context, _guild_id: GuildId, _banned_user: User) {}

    /// Dispatched when a user's ban is lifted from a guild.
    ///
    /// Provides the guild's id and the lifted user's data.
    fn guild_ban_removal(&self, _ctx: Context, _guild_id: GuildId, _unbanned_user: User) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data and whether the guild is new.
    #[cfg(feature = "cache")]
    fn guild_create(&self, _ctx: Context, _guild: Guild, _is_new: bool) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data.
    #[cfg(not(feature = "cache"))]
    fn guild_create(&self, _ctx: Context, _guild: Guild) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    #[cfg(feature = "cache")]
    fn guild_delete(&self, _ctx: Context, _incomplete: PartialGuild, _full: Option<Arc<RwLock<Guild>>>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    #[cfg(not(feature = "cache"))]
    fn guild_delete(&self, _ctx: Context, _incomplete: PartialGuild) {}

    /* the emojis were updated. */

    /// Dispatched when the emojis are updated.
    ///
    /// Provides the guild's id and the new state of the emojis in the guild.
    fn guild_emojis_update(&self, _ctx: Context, _guild_id: GuildId, _current_state: HashMap<EmojiId, Emoji>) {}

    /// Dispatched when a guild's integration is added, updated or removed.
    ///
    /// Provides the guild's id.
    fn guild_integrations_update(&self, _ctx: Context, _guild_id: GuildId) {}

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    fn guild_member_addition(&self, _ctx: Context, _guild_id: GuildId, _new_member: Member) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    #[cfg(feature = "cache")]
    fn guild_member_removal(&self, _ctx: Context, _guild: GuildId, _user: User, _member_data_if_available: Option<Member>) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _kicked: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the member's old data (if available) and the new data.
    #[cfg(feature = "cache")]
    fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_update(&self, _ctx: Context, _new: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    fn guild_members_chunk(&self, _ctx: Context, _guild_id: GuildId, _offline_members: HashMap<UserId, Member>) {}

    /// Dispatched when a role is created.
    ///
    /// Provides the guild's id and the new role's data.
    fn guild_role_create(&self, _ctx: Context, _guild_id: GuildId, _new: Role) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id and its data if available.
    #[cfg(feature = "cache")]
    fn guild_role_delete(&self, _ctx: Context, _guild_id: GuildId, _removed_role_id: RoleId, _removed_role_data_if_available: Option<Role>) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id.
    #[cfg(not(feature = "cache"))]
    fn guild_role_delete(&self, _ctx: Context, _guild_id: GuildId, _removed_role_id: RoleId) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id, the role's old (if available) and new data.
    #[cfg(feature = "cache")]
    fn guild_role_update(&self, _ctx: Context, _guild_id: GuildId, _old_data_if_available: Option<Role>, _new: Role) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id and the role's new data.
    #[cfg(not(feature = "cache"))]
    fn guild_role_update(&self, _ctx: Context, _guild_id: GuildId, _new_data: Role) {}

    /// Dispatched when a guild became unavailable.
    ///
    /// Provides the guild's id.
    fn guild_unavailable(&self, _ctx: Context, _guild_id: GuildId) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's old full data (if available) and the new, albeit partial data.
    #[cfg(feature = "cache")]
    fn guild_update(&self, _ctx: Context, _old_data_if_available: Option<Arc<RwLock<Guild>>>, _new_but_incomplete: PartialGuild) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's new, albeit partial data.
    #[cfg(not(feature = "cache"))]
    fn guild_update(&self, _ctx: Context, _new_but_incomplete_data: PartialGuild) {}

    /// Dispatched when a message is created.
    ///
    /// Provides the message's data.
    fn message(&self, _ctx: Context, _new_message: Message) {}

    /// Dispatched when a message is deleted.
    ///
    /// Provides the channel's id and the message's id.
    fn message_delete(&self, _ctx: Context, _channel_id: ChannelId, _deleted_message_id: MessageId) {}

    /// Dispatched when multiple messages were deleted at once.
    ///
    /// Provides the channel's id and the deleted messages' ids.
    fn message_delete_bulk(&self, _ctx: Context, _channel_id: ChannelId, _multiple_deleted_messages_ids: Vec<MessageId>) {}

    /// Dispatched when a new reaction is attached to a message.
    ///
    /// Provides the reaction's data.
    fn reaction_add(&self, _ctx: Context, _add_reaction: Reaction) {}

    /// Dispatched when a reaction is detached from a message.
    ///
    /// Provides the reaction's data.
    fn reaction_remove(&self, _ctx: Context, _removed_reaction: Reaction) {}

    /// Dispatched when all reactions of a message are detached from a message.
    ///
    /// Provides the channel's id and the message's id.
    fn reaction_remove_all(&self, _ctx: Context, _channel_id: ChannelId, _removed_from_message_id: MessageId) {}

    /// Dispatched when a message is updated.
    ///
    /// Provides the new data of the message.
    fn message_update(&self, _ctx: Context, _new_data: MessageUpdateEvent) {}

    fn presence_replace(&self, _ctx: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (e.g off -> on).
    ///
    /// Provides the presence's new data.
    fn presence_update(&self, _ctx: Context, _new_data: PresenceUpdateEvent) {}

    /// Dispatched upon startup.
    ///
    /// Provides data about the bot and the guilds it's in.
    fn ready(&self, _ctx: Context, _data_about_bot: Ready) {}

    /// Dispatched upon reconnection.
    fn resume(&self, _ctx: Context, _: ResumedEvent) {}

    /// Dispatched when a shard's connection stage is updated
    ///
    /// Provides the context of the shard and the event information about the update.
    fn shard_stage_update(&self, _ctx: Context, _: ShardStageUpdateEvent) {}

    /// Dispatched when a user starts typing.
    fn typing_start(&self, _ctx: Context, _: TypingStartEvent) {}

    /// Dispatched when an unknown event was sent from discord.
    ///
    /// Provides the event's name and its unparsed data.
    fn unknown(&self, _ctx: Context, _name: String, _raw: Value) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the old and new data.
    #[cfg(feature = "cache")]
    fn user_update(&self, _ctx: Context, _old_data: CurrentUser, _new: CurrentUser) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn user_update(&self, _ctx: Context, _new_data: CurrentUser) {}

    /// Dispatched when a guild's voice server was updated (or changed to another one).
    ///
    /// Provides the voice server's data.
    fn voice_server_update(&self, _ctx: Context, _: VoiceServerUpdateEvent) {}

    /// Dispatched when a user joins, leaves or moves a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _: VoiceState) {}

    /// Dispatched when a guild's webhook is updated.
    ///
    /// Provides the guild's id and the channel's id the webhook belongs in.
    fn webhook_update(&self, _ctx: Context, _guild_id: GuildId, _belongs_to_channel_id: ChannelId) {}
}
<commit_msg>Elaborate causes for `guild_member_removal` (#466)<commit_after>use model::prelude::*;
use parking_lot::RwLock;
use serde_json::Value;
use std::{
    collections::HashMap,
    sync::Arc
};
use super::context::Context;
use ::client::bridge::gateway::event::*;

/// The core trait for handling events by serenity.
pub trait EventHandler {
    /// Dispatched when the cache gets full.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    fn cached(&self, _ctx: Context, _guilds: Vec<GuildId>) {}

    /// Dispatched when a channel is created.
    ///
    /// Provides said channel's data.
    fn channel_create(&self, _ctx: Context, _channel: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a category is created.
    ///
    /// Provides said category's data.
    fn category_create(&self, _ctx: Context, _category: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a category is deleted.
    ///
    /// Provides said category's data.
    fn category_delete(&self, _ctx: Context, _category: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a private channel is created.
    ///
    /// Provides said channel's data.
    fn private_channel_create(&self, _ctx: Context, _channel: Arc<RwLock<PrivateChannel>>) {}

    /// Dispatched when a channel is deleted.
    ///
    /// Provides said channel's data.
    fn channel_delete(&self, _ctx: Context, _channel: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a pin is added, deleted.
    ///
    /// Provides said pin's data.
    fn channel_pins_update(&self, _ctx: Context, _pin: ChannelPinsUpdateEvent) {}

    /// Dispatched when a user is added to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_addition(&self, _ctx: Context, _group_id: ChannelId, _user: User) {}

    /// Dispatched when a user is removed to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_removal(&self, _ctx: Context, _group_id: ChannelId, _user: User) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the old channel data, and the new data.
    #[cfg(feature = "cache")]
    fn channel_update(&self, _ctx: Context, _old: Option<Channel>, _new: Channel) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn channel_update(&self, _ctx: Context, _new_data: Channel) {}

    /// Dispatched when a user is banned from a guild.
    ///
    /// Provides the guild's id and the banned user's data.
    fn guild_ban_addition(&self, _ctx: Context, _guild_id: GuildId, _banned_user: User) {}

    /// Dispatched when a user's ban is lifted from a guild.
    ///
    /// Provides the guild's id and the lifted user's data.
    fn guild_ban_removal(&self, _ctx: Context, _guild_id: GuildId, _unbanned_user: User) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data and whether the guild is new.
    #[cfg(feature = "cache")]
    fn guild_create(&self, _ctx: Context, _guild: Guild, _is_new: bool) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data.
    #[cfg(not(feature = "cache"))]
    fn guild_create(&self, _ctx: Context, _guild: Guild) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    #[cfg(feature = "cache")]
    fn guild_delete(&self, _ctx: Context, _incomplete: PartialGuild, _full: Option<Arc<RwLock<Guild>>>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    #[cfg(not(feature = "cache"))]
    fn guild_delete(&self, _ctx: Context, _incomplete: PartialGuild) {}

    /* the emojis were updated. */

    /// Dispatched when the emojis are updated.
    ///
    /// Provides the guild's id and the new state of the emojis in the guild.
    fn guild_emojis_update(&self, _ctx: Context, _guild_id: GuildId, _current_state: HashMap<EmojiId, Emoji>) {}

    /// Dispatched when a guild's integration is added, updated or removed.
    ///
    /// Provides the guild's id.
    fn guild_integrations_update(&self, _ctx: Context, _guild_id: GuildId) {}

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    fn guild_member_addition(&self, _ctx: Context, _guild_id: GuildId, _new_member: Member) {}

    /// Dispatched when a user's membership ends by leaving, getting kicked, or being banned.
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    #[cfg(feature = "cache")]
    fn guild_member_removal(&self, _ctx: Context, _guild: GuildId, _user: User, _member_data_if_available: Option<Member>) {}

    /// Dispatched when a user's membership ends by leaving, getting kicked, or being banned.
    ///
    /// Provides the guild's id, the user's data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_removal(&self, _ctx: Context, _guild_id: GuildId, _kicked: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated).
    ///
    /// Provides the member's old data (if available) and the new data.
    #[cfg(feature = "cache")]
    fn guild_member_update(&self, _ctx: Context, _old_if_available: Option<Member>, _new: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated).
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_update(&self, _ctx: Context, _new: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    fn guild_members_chunk(&self, _ctx: Context, _guild_id: GuildId, _offline_members: HashMap<UserId, Member>) {}

    /// Dispatched when a role is created.
    ///
    /// Provides the guild's id and the new role's data.
    fn guild_role_create(&self, _ctx: Context, _guild_id: GuildId, _new: Role) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id and its data if available.
    #[cfg(feature = "cache")]
    fn guild_role_delete(&self, _ctx: Context, _guild_id: GuildId, _removed_role_id: RoleId, _removed_role_data_if_available: Option<Role>) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id.
    #[cfg(not(feature = "cache"))]
    fn guild_role_delete(&self, _ctx: Context, _guild_id: GuildId, _removed_role_id: RoleId) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id, the role's old (if available) and new data.
    #[cfg(feature = "cache")]
    fn guild_role_update(&self, _ctx: Context, _guild_id: GuildId, _old_data_if_available: Option<Role>, _new: Role) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id and the role's new data.
    #[cfg(not(feature = "cache"))]
    fn guild_role_update(&self, _ctx: Context, _guild_id: GuildId, _new_data: Role) {}

    /// Dispatched when a guild became unavailable.
    ///
    /// Provides the guild's id.
    fn guild_unavailable(&self, _ctx: Context, _guild_id: GuildId) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's old full data (if available) and the new, albeit partial data.
    #[cfg(feature = "cache")]
    fn guild_update(&self, _ctx: Context, _old_data_if_available: Option<Arc<RwLock<Guild>>>, _new_but_incomplete: PartialGuild) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's new, albeit partial data.
    #[cfg(not(feature = "cache"))]
    fn guild_update(&self, _ctx: Context, _new_but_incomplete_data: PartialGuild) {}

    /// Dispatched when a message is created.
    ///
    /// Provides the message's data.
    fn message(&self, _ctx: Context, _new_message: Message) {}

    /// Dispatched when a message is deleted.
    ///
    /// Provides the channel's id and the message's id.
    fn message_delete(&self, _ctx: Context, _channel_id: ChannelId, _deleted_message_id: MessageId) {}

    /// Dispatched when multiple messages were deleted at once.
    ///
    /// Provides the channel's id and the deleted messages' ids.
    fn message_delete_bulk(&self, _ctx: Context, _channel_id: ChannelId, _multiple_deleted_messages_ids: Vec<MessageId>) {}

    /// Dispatched when a new reaction is attached to a message.
    ///
    /// Provides the reaction's data.
    fn reaction_add(&self, _ctx: Context, _add_reaction: Reaction) {}

    /// Dispatched when a reaction is detached from a message.
    ///
    /// Provides the reaction's data.
    fn reaction_remove(&self, _ctx: Context, _removed_reaction: Reaction) {}

    /// Dispatched when all reactions of a message are detached from a message.
    ///
    /// Provides the channel's id and the message's id.
    fn reaction_remove_all(&self, _ctx: Context, _channel_id: ChannelId, _removed_from_message_id: MessageId) {}

    /// Dispatched when a message is updated.
    ///
    /// Provides the new data of the message.
    fn message_update(&self, _ctx: Context, _new_data: MessageUpdateEvent) {}

    fn presence_replace(&self, _ctx: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (e.g off -> on).
    ///
    /// Provides the presence's new data.
    fn presence_update(&self, _ctx: Context, _new_data: PresenceUpdateEvent) {}

    /// Dispatched upon startup.
    ///
    /// Provides data about the bot and the guilds it's in.
    fn ready(&self, _ctx: Context, _data_about_bot: Ready) {}

    /// Dispatched upon reconnection.
    fn resume(&self, _ctx: Context, _: ResumedEvent) {}

    /// Dispatched when a shard's connection stage is updated
    ///
    /// Provides the context of the shard and the event information about the update.
    fn shard_stage_update(&self, _ctx: Context, _: ShardStageUpdateEvent) {}

    /// Dispatched when a user starts typing.
    fn typing_start(&self, _ctx: Context, _: TypingStartEvent) {}

    /// Dispatched when an unknown event was sent from discord.
    ///
    /// Provides the event's name and its unparsed data.
    fn unknown(&self, _ctx: Context, _name: String, _raw: Value) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the old and new data.
    #[cfg(feature = "cache")]
    fn user_update(&self, _ctx: Context, _old_data: CurrentUser, _new: CurrentUser) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn user_update(&self, _ctx: Context, _new_data: CurrentUser) {}

    /// Dispatched when a guild's voice server was updated (or changed to another one).
    ///
    /// Provides the voice server's data.
    fn voice_server_update(&self, _ctx: Context, _: VoiceServerUpdateEvent) {}

    /// Dispatched when a user joins, leaves or moves a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    fn voice_state_update(&self, _ctx: Context, _: Option<GuildId>, _: VoiceState) {}

    /// Dispatched when a guild's webhook is updated.
    ///
    /// Provides the guild's id and the channel's id the webhook belongs in.
    fn webhook_update(&self, _ctx: Context, _guild_id: GuildId, _belongs_to_channel_id: ChannelId) {}
}
<|endoftext|>


<commit_before>use std::clone::Clone;
use std::ops::Index;
use std::ops::IndexMut;

/// Elements of the finite field mod 2^448 - 2^224 - 1.  Used by the
/// Ed448-Goldilocks curve.  Uses 28-bit digits.

#[derive(Copy, Clone)]
pub struct Mod_e448_e224_1(pub [u32; 16]);

impl PrimeField for Mod_e448_e224_1 {
    fn zero() -> Mod_e448_e224_1 {
        Mod_e448_e224_1([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
    }

    fn one() -> Mod_e448_e224_1 {
        Mod_e448_e224_1([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
    }

    fn modulus() -> Mod_e448_e224_1 {
        Mod_e448_e224_1([ 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff,
                          0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff,
                          0x0ffffffe, 0x0fffffff, 0x0fffffff, 0x0fffffff,
                          0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff ])
    }
}

impl Mod_e448_e224_1 {
    fn pack(&self) -> [u8; 56] {
        let mut bytes = [0u8; 56];

        bytes[0] = (self[0] & 0b11111111) as u8;
        bytes[1] = ((self[0] >> 8) & 0b11111111) as u8;
        bytes[2] = ((self[0] >> 16) & 0b11111111) as u8;
        bytes[3] = (((self[0] >> 24) & 0b00001111) |
                    ((self[1] << 4) & 0b11110000)) as u8;
        bytes[4] = ((self[1] >> 4) & 0b11111111) as u8;
        bytes[5] = ((self[1] >> 12) & 0b11111111) as u8;
        bytes[6] = ((self[1] >> 20) & 0b11111111) as u8;
        bytes[7] = (self[2] & 0b11111111) as u8;
        bytes[8] = ((self[2] >> 8) & 0b11111111) as u8;
        bytes[9] = ((self[2] >> 16) & 0b11111111) as u8;
        bytes[10] = (((self[2] >> 24) & 0b00001111) |
                     ((self[3] << 4) & 0b11110000)) as u8;
        bytes[11] = ((self[3] >> 4) & 0b11111111) as u8;
        bytes[12] = ((self[3] >> 12) & 0b11111111) as u8;
        bytes[13] = ((self[3] >> 20) & 0b11111111) as u8;
        bytes[14] = (self[4] & 0b11111111) as u8;
        bytes[15] = ((self[4] >> 8) & 0b11111111) as u8;
        bytes[16] = ((self[4] >> 16) & 0b11111111) as u8;
        bytes[17] = (((self[4] >> 24) & 0b00001111) |
                     ((self[5] << 4) & 0b11110000)) as u8;
        bytes[18] = ((self[5] >> 4) & 0b11111111) as u8;
        bytes[19] = ((self[5] >> 12) & 0b11111111) as u8;
        bytes[20] = ((self[5] >> 20) & 0b11111111) as u8;
        bytes[21] = (self[6] & 0b11111111) as u8;
        bytes[22] = ((self[6] >> 8) & 0b11111111) as u8;
        bytes[23] = ((self[6] >> 16) & 0b11111111) as u8;
        bytes[24] = (((self[6] >> 24) & 0b00001111) |
                     ((self[7] << 4) & 0b11110000)) as u8;
        bytes[25] = ((self[7] >> 4) & 0b11111111) as u8;
        bytes[26] = ((self[7] >> 12) & 0b11111111) as u8;
        bytes[27] = ((self[7] >> 20) & 0b11111111) as u8;
        bytes[28] = (self[8] & 0b11111111) as u8;
        bytes[29] = ((self[8] >> 8) & 0b11111111) as u8;
        bytes[30] = ((self[8] >> 16) & 0b11111111) as u8;
        bytes[31] = (((self[8] >> 24) & 0b00001111) |
                     ((self[9] << 4) & 0b11110000)) as u8;
        bytes[32] = ((self[9] >> 4) & 0b11111111) as u8;
        bytes[33] = ((self[9] >> 12) & 0b11111111) as u8;
        bytes[34] = ((self[9] >> 20) & 0b11111111) as u8;
        bytes[35] = (self[10] & 0b11111111) as u8;
        bytes[36] = ((self[10] >> 8) & 0b11111111) as u8;
        bytes[37] = ((self[10] >> 16) & 0b11111111) as u8;
        bytes[38] = (((self[10] >> 24) & 0b00001111) |
                     ((self[11] << 4) & 0b11110000)) as u8;
        bytes[39] = ((self[11] >> 4) & 0b11111111) as u8;
        bytes[40] = ((self[11] >> 12) & 0b11111111) as u8;
        bytes[41] = ((self[11] >> 20) & 0b11111111) as u8;

        bytes[42] = (self[12] & 0b11111111) as u8;
        bytes[43] = ((self[12] >> 8) & 0b11111111) as u8;
        bytes[44] = ((self[12] >> 16) & 0b11111111) as u8;
        bytes[45] = (((self[12] >> 24) & 0b00001111) |
                     ((self[13] << 4) & 0b11110000)) as u8;
        bytes[46] = ((self[13] >> 4) & 0b11111111) as u8;
        bytes[47] = ((self[13] >> 12) & 0b11111111) as u8;
        bytes[48] = ((self[13] >> 20) & 0b11111111) as u8;
        bytes[49] = (self[14] & 0b11111111) as u8;
        bytes[50] = ((self[14] >> 8) & 0b11111111) as u8;
        bytes[51] = ((self[14] >> 16) & 0b11111111) as u8;
        bytes[52] = (((self[14] >> 24) & 0b00001111) |
                     ((self[15] << 4) & 0b11110000)) as u8;
        bytes[53] = ((self[15] >> 4) & 0b11111111) as u8;
        bytes[54] = ((self[15] >> 12) & 0b11111111) as u8;
        bytes[55] = ((self[15] >> 20) & 0b11111111) as u8;

        bytes;
    }

    fn unpack(bytes : &[u8; 56]) -> Mod_e448_e224_1 {
        let mut out = Mod_e448_e224_1([0u32; 16]);

        out[0] = ((bytes[0] as u32) & 0x000000ff) |
                 (((bytes[1] as u32) << 8) & 0x0000ff00) |
                 (((bytes[2] as u32) << 16) & 0x00ff0000) |
                 (((bytes[3] as u32) << 24) & 0x0f000000);
        out[1] = (((bytes[3] as u32) >> 4) & 0x0000000f) |
                 (((bytes[4] as u32) << 4) & 0x00000ff0) |
                 (((bytes[5] as u32) << 12) & 0x000ff000) |
                 (((bytes[6] as u32) << 20) & 0x0ff00000);
        out[2] = ((bytes[7] as u32) & 0x000000ff) |
                 (((bytes[8] as u32) << 8) & 0x0000ff00) |
                 (((bytes[9] as u32) << 16) & 0x00ff0000) |
                 (((bytes[10] as u32) << 24) & 0x0f000000);
        out[3] = (((bytes[10] as u32) >> 4) & 0x0000000f) |
                 (((bytes[11] as u32) << 4) & 0x00000ff0) |
                 (((bytes[12] as u32) << 12) & 0x000ff000) |
                 (((bytes[13] as u32) << 20) & 0x0ff00000);
        out[4] = ((bytes[14] as u32) & 0x000000ff) |
                 (((bytes[15] as u32) << 8) & 0x0000ff00) |
                 (((bytes[16] as u32) << 16) & 0x00ff0000) |
                 (((bytes[17] as u32) << 24) & 0x0f000000);
        out[5] = (((bytes[17] as u32) >> 4) & 0x0000000f) |
                 (((bytes[18] as u32) << 4) & 0x00000ff0) |
                 (((bytes[19] as u32) << 12) & 0x000ff000) |
                 (((bytes[20] as u32) << 20) & 0x0ff00000);
        out[6] = ((bytes[21] as u32) & 0x000000ff) |
                 (((bytes[22] as u32) << 8) & 0x0000ff00) |
                 (((bytes[23] as u32) << 16) & 0x00ff0000) |
                 (((bytes[24] as u32) << 24) & 0x0f000000);
        out[7] = (((bytes[24] as u32) >> 4) & 0x0000000f) |
                 (((bytes[25] as u32) << 4) & 0x00000ff0) |
                 (((bytes[26] as u32) << 12) & 0x000ff000) |
                 (((bytes[27] as u32) << 20) & 0x0ff00000);
        out[8] = ((bytes[28] as u32) & 0x000000ff) |
                 (((bytes[29] as u32) << 8) & 0x0000ff00) |
                 (((bytes[30] as u32) << 16) & 0x00ff0000) |
                 (((bytes[31] as u32) << 24) & 0x0f000000);
        out[9] = (((bytes[31] as u32) >> 4) & 0x0000000f) |
                 (((bytes[32] as u32) << 4) & 0x00000ff0) |
                 (((bytes[33] as u32) << 12) & 0x000ff000) |
                 (((bytes[34] as u32) << 20) & 0x0ff00000);
        out[10] = ((bytes[35] as u32) & 0x000000ff) |
                  (((bytes[36] as u32) << 8) & 0x0000ff00) |
                  (((bytes[37] as u32) << 16) & 0x00ff0000) |
                  (((bytes[38] as u32) << 24) & 0x0f000000);
        out[11] = (((bytes[38] as u32) >> 4) & 0x0000000f) |
                  (((bytes[39] as u32) << 4) & 0x00000ff0) |
                  (((bytes[40] as u32) << 12) & 0x000ff000) |
                  (((bytes[41] as u32) << 20) & 0x0ff00000);
        out[12] = ((bytes[42] as u32) & 0x000000ff) |
                  (((bytes[43] as u32) << 8) & 0x0000ff00) |
                  (((bytes[44] as u32) << 16) & 0x00ff0000) |
                  (((bytes[45] as u32) << 24) & 0x0f000000);
        out[13] = (((bytes[45] as u32) >> 4) & 0x0000000f) |
                  (((bytes[46] as u32) << 4) & 0x00000ff0) |
                  (((bytes[47] as u32) << 12) & 0x000ff000) |
                  (((bytes[48] as u32) << 20) & 0x0ff00000);
        out[14] = ((bytes[49] as u32) & 0x000000ff) |
                  (((bytes[50] as u32) << 8) & 0x0000ff00) |
                  (((bytes[51] as u32) << 16) & 0x00ff0000) |
                  (((bytes[52] as u32) << 24) & 0x0f000000);
        out[15] = (((bytes[52] as u32) >> 4) & 0x0000000f) |
                  (((bytes[53] as u32) << 4) & 0x00000ff0) |
                  (((bytes[54] as u32) << 12) & 0x000ff000) |
                  (((bytes[55] as u32) << 20) & 0x0ff00000);

        out;
    }
}

impl IndexMut<usize> for Mod_e448_e224_1 {
    fn index_mut<'a>(&'a mut self, idx : usize) -> &'a mut u32 {
        let ret : &'a mut u32 = &mut(self.0[idx]);
        ret
    }
}

impl Index<usize> for Mod_e448_e224_1 {
    type Output = u32;

    fn index<'a>(&'a self, idx : usize) -> &'a u32 {
        let ret : &'a u32 = &(self.0[idx]);
        ret
    }
}
<commit_msg>Drop goldilocks prime field for now<commit_after><|endoftext|>


<commit_before>/// Calculates the tetrahedron (four) coordinates
///
/// # Output
///
/// * `zeta` -- the tetrahedron coordinates (len = 4)
///
/// # Input
///
/// * `xa,xb,xc,xd` -- corners (each has len = 3)
/// * `xp` -- the point to calculate the coordinates (len = 3)
///
/// # Panics
///
/// This function will panic if the array sizes are incorrect
pub fn tetrahedron_coords(zeta: &mut [f64], xa: &[f64], xb: &[f64], xc: &[f64], xd: &[f64], xp: &[f64]) {
    let (x1, y1, z1) = (xa[0], xa[1], xa[2]);
    let (x2, y2, z2) = (xb[0], xb[1], xb[2]);
    let (x3, y3, z3) = (xc[0], xc[1], xc[2]);
    let (x4, y4, z4) = (xd[0], xd[1], xd[2]);
    let (x, y, z) = (xp[0], xp[1], xp[2]);
    let v6 = (x2 - x1) * ((y2 - y3) * (z3 - z4) - (y3 - y4) * (z2 - z3))
        + (x3 - x2) * ((y3 - y4) * (z1 - z2) - (y1 - y2) * (z3 - z4))
        + (x4 - x3) * ((y1 - y2) * (z2 - z3) - (y2 - y3) * (z1 - z2));
    zeta[0] = ((x2 * (y3 * z4 - y4 * z3) + x3 * (y4 * z2 - y2 * z4) + x4 * (y2 * z3 - y3 * z2))
        + x * ((y4 - y2) * (z3 - z2) - (y3 - y2) * (z4 - z2))
        + y * ((x3 - x2) * (z4 - z2) - (x4 - x2) * (z3 - z2))
        + z * ((x4 - x2) * (y3 - y2) - (x3 - x2) * (y4 - y2)))
        / v6;
    zeta[1] = ((x1 * (y4 * z3 - y3 * z4) + x3 * (y1 * z4 - y4 * z1) + x4 * (y3 * z1 - y1 * z3))
        + x * ((y3 - y1) * (z4 - z3) - (y3 - y4) * (z1 - z3))
        + y * ((x4 - x3) * (z3 - z1) - (x1 - x3) * (z3 - z4))
        + z * ((x3 - x1) * (y4 - y3) - (x3 - x4) * (y1 - y3)))
        / v6;
    zeta[2] = ((x1 * (y2 * z4 - y4 * z2) + x2 * (y4 * z1 - y1 * z4) + x4 * (y1 * z2 - y2 * z1))
        + x * ((y2 - y4) * (z1 - z4) - (y1 - y4) * (z2 - z4))
        + y * ((x1 - x4) * (z2 - z4) - (x2 - x4) * (z1 - z4))
        + z * ((x2 - x4) * (y1 - y4) - (x1 - x4) * (y2 - y4)))
        / v6;
    zeta[3] = ((x1 * (y3 * z2 - y2 * z3) + x2 * (y1 * z3 - y3 * z1) + x3 * (y2 * z1 - y1 * z2))
        + x * ((y1 - y3) * (z2 - z1) - (y1 - y2) * (z3 - z1))
        + y * ((x2 - x1) * (z1 - z3) - (x3 - x1) * (z1 - z2))
        + z * ((x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1)))
        / v6;
}

/// Indicates if a point is inside a tetrahedron by looking at its tetrahedron coordinates (zeta)
///
/// Note: the point is inside (or on a face) if all zeta are positive (or zero)
#[inline]
pub fn in_tetrahedron(zeta: &[f64]) -> bool {
    if zeta[0] < 0.0 || zeta[1] < 0.0 || zeta[2] < 0.0 || zeta[3] < 0.0 {
        false
    } else {
        true
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{in_tetrahedron, tetrahedron_coords};
    use crate::util::ONE_BY_3;
    use russell_chk::assert_vec_approx_eq;

    #[test]
    fn tetrahedron_coords_works() {
        let xa = &[0.0, 0.0, 0.0];
        let xb = &[1.0, 0.0, 0.0];
        let xc = &[0.0, 1.0, 0.0];
        let xd = &[0.0, 0.0, 1.0];
        let mut zeta = vec![0.0; 4];
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 0.0]);
        assert_eq!(zeta, &[1.0, 0.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 0.0, 0.0]);
        assert_eq!(zeta, &[0.0, 1.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 1.0, 0.0]);
        assert_eq!(zeta, &[0.0, 0.0, 1.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 1.0]);
        assert_eq!(zeta, &[0.0, 0.0, 0.0, 1.0]);

        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.0]);
        assert_eq!(zeta, &[0.0, 0.5, 0.5, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.5]);
        assert_eq!(zeta, &[-0.5, 0.5, 0.5, 0.5]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 1.0, 1.0]);
        assert_eq!(zeta, &[-2.0, 1.0, 1.0, 1.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[ONE_BY_3, ONE_BY_3, ONE_BY_3]);
        assert_vec_approx_eq!(zeta, &[0.0, ONE_BY_3, ONE_BY_3, ONE_BY_3], 1e-15);
    }

    #[test]
    fn in_tetrahedron_works() {
        assert_eq!(in_tetrahedron(&[-1.0, 0.0, 0.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, -1.0, 0.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, -1.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, 0.0, -1.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, 0.0, 0.0]), true);
    }
}
<commit_msg>Improve comments<commit_after>/// Calculates the tetrahedron (four) coordinates
///
/// # Output
///
/// * `zeta` -- the tetrahedron coordinates (len = 4)
///
/// # Input
///
/// * `xa,xb,xc,xd` -- corners (each has len = 3)
/// * `xp` -- the point to calculate the coordinates (len = 3)
///
/// # Panics
///
/// This function will panic if the array sizes are incorrect
pub fn tetrahedron_coords(zeta: &mut [f64], xa: &[f64], xb: &[f64], xc: &[f64], xd: &[f64], xp: &[f64]) {
    let (x1, y1, z1) = (xa[0], xa[1], xa[2]);
    let (x2, y2, z2) = (xb[0], xb[1], xb[2]);
    let (x3, y3, z3) = (xc[0], xc[1], xc[2]);
    let (x4, y4, z4) = (xd[0], xd[1], xd[2]);
    let (x, y, z) = (xp[0], xp[1], xp[2]);
    let v6 = (x2 - x1) * ((y2 - y3) * (z3 - z4) - (y3 - y4) * (z2 - z3))
        + (x3 - x2) * ((y3 - y4) * (z1 - z2) - (y1 - y2) * (z3 - z4))
        + (x4 - x3) * ((y1 - y2) * (z2 - z3) - (y2 - y3) * (z1 - z2));
    zeta[0] = ((x2 * (y3 * z4 - y4 * z3) + x3 * (y4 * z2 - y2 * z4) + x4 * (y2 * z3 - y3 * z2))
        + x * ((y4 - y2) * (z3 - z2) - (y3 - y2) * (z4 - z2))
        + y * ((x3 - x2) * (z4 - z2) - (x4 - x2) * (z3 - z2))
        + z * ((x4 - x2) * (y3 - y2) - (x3 - x2) * (y4 - y2)))
        / v6;
    zeta[1] = ((x1 * (y4 * z3 - y3 * z4) + x3 * (y1 * z4 - y4 * z1) + x4 * (y3 * z1 - y1 * z3))
        + x * ((y3 - y1) * (z4 - z3) - (y3 - y4) * (z1 - z3))
        + y * ((x4 - x3) * (z3 - z1) - (x1 - x3) * (z3 - z4))
        + z * ((x3 - x1) * (y4 - y3) - (x3 - x4) * (y1 - y3)))
        / v6;
    zeta[2] = ((x1 * (y2 * z4 - y4 * z2) + x2 * (y4 * z1 - y1 * z4) + x4 * (y1 * z2 - y2 * z1))
        + x * ((y2 - y4) * (z1 - z4) - (y1 - y4) * (z2 - z4))
        + y * ((x1 - x4) * (z2 - z4) - (x2 - x4) * (z1 - z4))
        + z * ((x2 - x4) * (y1 - y4) - (x1 - x4) * (y2 - y4)))
        / v6;
    zeta[3] = ((x1 * (y3 * z2 - y2 * z3) + x2 * (y1 * z3 - y3 * z1) + x3 * (y2 * z1 - y1 * z2))
        + x * ((y1 - y3) * (z2 - z1) - (y1 - y2) * (z3 - z1))
        + y * ((x2 - x1) * (z1 - z3) - (x3 - x1) * (z1 - z2))
        + z * ((x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1)))
        / v6;
}

/// Indicates if a point is inside a tetrahedron by looking at its tetrahedron coordinates (zeta)
///
/// Note: the point is inside (or on a face) if all zeta are positive (or zero)
///
/// # Input
///
/// * `zeta` -- the tetrahedron coordinates (len = 4)
///
/// # Panics
///
/// This function will panic if the array sizes are incorrect
#[inline]
pub fn in_tetrahedron(zeta: &[f64]) -> bool {
    if zeta[0] < 0.0 || zeta[1] < 0.0 || zeta[2] < 0.0 || zeta[3] < 0.0 {
        false
    } else {
        true
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{in_tetrahedron, tetrahedron_coords};
    use crate::util::ONE_BY_3;
    use russell_chk::assert_vec_approx_eq;

    #[test]
    fn tetrahedron_coords_works() {
        let xa = &[0.0, 0.0, 0.0];
        let xb = &[1.0, 0.0, 0.0];
        let xc = &[0.0, 1.0, 0.0];
        let xd = &[0.0, 0.0, 1.0];
        let mut zeta = vec![0.0; 4];
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 0.0]);
        assert_eq!(zeta, &[1.0, 0.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 0.0, 0.0]);
        assert_eq!(zeta, &[0.0, 1.0, 0.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 1.0, 0.0]);
        assert_eq!(zeta, &[0.0, 0.0, 1.0, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.0, 0.0, 1.0]);
        assert_eq!(zeta, &[0.0, 0.0, 0.0, 1.0]);

        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.0]);
        assert_eq!(zeta, &[0.0, 0.5, 0.5, 0.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[0.5, 0.5, 0.5]);
        assert_eq!(zeta, &[-0.5, 0.5, 0.5, 0.5]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[1.0, 1.0, 1.0]);
        assert_eq!(zeta, &[-2.0, 1.0, 1.0, 1.0]);
        tetrahedron_coords(&mut zeta, xa, xb, xc, xd, &[ONE_BY_3, ONE_BY_3, ONE_BY_3]);
        assert_vec_approx_eq!(zeta, &[0.0, ONE_BY_3, ONE_BY_3, ONE_BY_3], 1e-15);
    }

    #[test]
    fn in_tetrahedron_works() {
        assert_eq!(in_tetrahedron(&[-1.0, 0.0, 0.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, -1.0, 0.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, -1.0, 0.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, 0.0, -1.0]), false);
        assert_eq!(in_tetrahedron(&[0.0, 0.0, 0.0, 0.0]), true);
    }
}
<|endoftext|>


<commit_before>//! Fully serializable (ACID) multi-`Tree` transactions
//!
//! # Examples
//!
//! ```
//! use sled::{Db, Transactional};
//!
//! let db = Db::open("tx_db").unwrap();
//!
//! // Use write-only transactions as a writebatch:
//! db.transaction(|db| {
//!     db.insert(b"k1", b"cats")?;
//!     db.insert(b"k2", b"dogs")?;
//!     Ok(())
//! }).unwrap();
//!
//! // Atomically swap two items:
//! db.transaction(|db| {
//!     let v1_option = db.remove(b"k1")?;
//!     let v1 = v1_option.unwrap();
//!     let v2_option = db.remove(b"k2")?;
//!     let v2 = v2_option.unwrap();
//!
//!     db.insert(b"k1", v2);
//!     db.insert(b"k2", v1);
//!
//!     Ok(())
//! }).unwrap();
//!
//! assert_eq!(&db.get(b"k1").unwrap().unwrap(), b"dogs");
//! assert_eq!(&db.get(b"k2").unwrap().unwrap(), b"cats");
//!
//! // Transactions also work on tuples of `Tree`s,
//! // preserving serializable ACID semantics!
//! // In this example, we treat two trees like a
//! // work queue, atomically apply updates to
//! // data and move them from the unprocessed `Tree`
//! // to the processed `Tree`.
//! let unprocessed = db.open_tree(b"unprocessed items").unwrap();
//! let processed = db.open_tree(b"processed items").unwrap();
//!
//! // An update somehow gets into the tree, which we
//! // later trigger the atomic processing of.
//! unprocessed.insert(b"k3", b"ligers").unwrap();
//!
//! // Atomically process the new item and move it
//! // between `Tree`s.
//! (&unprocessed, &processed).transaction(|(unprocessed, processed)| {
//!     let unprocessed_item = unprocessed.remove(b"k3")?.unwrap();
//!     let mut processed_item = b"yappin' ".to_vec();
//!     processed_item.extend_from_slice(&unprocessed_item);
//!     processed.insert(b"k3", processed_item)?;
//!     Ok(())
//! }).unwrap();
//!
//! assert_eq!(unprocessed.get(b"k3").unwrap(), None);
//! assert_eq!(&processed.get(b"k3").unwrap().unwrap(), b"yappin' ligers");
//! ```

#![allow(unused)]
#![allow(missing_docs)]

use std::{cell::RefCell, collections::HashMap, sync::Arc};

use super::*;

/// A transaction that will
/// be applied atomically to the
/// Tree.
pub struct TransactionalTree<'a> {
    pub(super) tree: &'a Tree,
    pub(super) writes: RefCell<HashMap<IVec, Option<IVec>>>,
    pub(super) read_cache: RefCell<HashMap<IVec, Option<IVec>>>,
    pub(super) locks: RefCell<Vec<parking_lot::RwLockWriteGuard<'a, ()>>>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum TransactionError {
    Conflict,
    Abort,
    Storage(Error),
}

pub type TransactionResult<T> = std::result::Result<T, TransactionError>;

fn abort() -> TransactionError {
    TransactionError::Abort
}

impl From<Error> for TransactionError {
    fn from(error: Error) -> Self {
        TransactionError::Storage(error)
    }
}

impl<'a> TransactionalTree<'a> {
    /// Set a key to a new value
    pub fn insert<K, V>(
        &self,
        key: K,
        value: V,
    ) -> TransactionResult<Option<IVec>>
    where
        IVec: From<K>,
        IVec: From<V>,
        K: AsRef<[u8]>,
    {
        let old = self.get(key.as_ref())?;
        let mut writes = self.writes.borrow_mut();
        writes.insert(IVec::from(key), Some(IVec::from(value)));
        Ok(old)
    }

    /// Remove a key
    pub fn remove<K>(&self, key: K) -> TransactionResult<Option<IVec>>
    where
        IVec: From<K>,
        K: AsRef<[u8]>,
    {
        let old = self.get(key.as_ref());
        let mut writes = self.writes.borrow_mut();
        writes.insert(IVec::from(key), None);
        old
    }

    /// Get the value associated with a key
    pub fn get<K: AsRef<[u8]>>(
        &self,
        key: K,
    ) -> TransactionResult<Option<IVec>> {
        let writes = self.writes.borrow();
        if let Some(first_try) = writes.get(key.as_ref()) {
            return Ok(first_try.clone());
        }
        let mut reads = self.read_cache.borrow_mut();
        if let Some(second_try) = reads.get(key.as_ref()) {
            return Ok(second_try.clone());
        }

        // not found in a cache, need to hit the backing db
        let get = self.tree.get_inner(key.as_ref())?;
        reads.insert(key.as_ref().into(), get.clone());

        Ok(get)
    }

    /// Atomically apply multiple inserts and removals.
    pub fn apply_batch(&self, batch: Batch) -> TransactionResult<()> {
        for (k, v_opt) in batch.writes {
            if let Some(v) = v_opt {
                self.insert(k, v)?;
            } else {
                self.remove(k)?;
            }
        }
        Ok(())
    }

    fn stage(&self) -> bool {
        let mut locks = self.locks.borrow_mut();
        let guard = self.tree.concurrency_control.write();
        locks.push(guard);
        true
    }

    fn unstage(&self) {
        unimplemented!()
    }

    fn validate(&self) -> bool {
        true
    }

    fn commit(&self) -> Result<()> {
        let mut writes = self.writes.borrow_mut();
        for (k, v_opt) in &*writes {
            if let Some(v) = v_opt {
                self.tree.insert_inner(k, v)?;
            } else {
                self.tree.remove_inner(k)?;
            }
        }
        Ok(())
    }
}

pub struct TransactionalTrees<'a> {
    inner: Vec<TransactionalTree<'a>>,
}

impl<'a> TransactionalTrees<'a> {
    fn stage(&self) -> bool {
        // we want to stage our trees in
        // lexicographic order to guarantee
        // no deadlocks should they block
        // on mutexes in their own staging
        // implementations.
        let mut tree_idxs: Vec<(&[u8], usize)> = self
            .inner
            .iter()
            .enumerate()
            .map(|(idx, t)| (&*t.tree.tree_id, idx))
            .collect();
        tree_idxs.sort_unstable();

        let mut last_idx = usize::max_value();
        for (_, idx) in tree_idxs {
            if idx == last_idx {
                // prevents us from double-locking
                continue;
            }
            last_idx = idx;
            if !self.inner[idx].stage() {
                return false;
            }
        }
        true
    }

    fn unstage(&self) {
        for tree in &self.inner {
            tree.unstage();
        }
    }

    fn validate(&self) -> bool {
        for tree in &self.inner {
            if !tree.validate() {
                return false;
            }
        }
        true
    }

    fn commit(self) -> Result<()> {
        let peg = self.inner[0].tree.context.pin_log()?;
        for tree in &self.inner {
            tree.commit()?;
        }

        // when the peg drops, it ensures all updates
        // written to the log since its creation are
        // recovered atomically
        peg.seal_batch()
    }
}

pub trait Transactional {
    type View;

    fn make_overlay(&self) -> TransactionalTrees<'_>;

    fn view_overlay(overlay: &TransactionalTrees<'_>) -> Self::View;

    fn transaction<F, R>(&self, f: F) -> TransactionResult<R>
    where
        F: Fn(Self::View) -> TransactionResult<R>,
    {
        loop {
            let tt = self.make_overlay();
            let view = Self::view_overlay(&tt);
            if !tt.stage() {
                tt.unstage();
                continue;
            }
            let ret = f(view);
            if !tt.validate() {
                tt.unstage();
                continue;
            }
            match ret {
                Ok(r) => {
                    tt.commit()?;
                    return Ok(r);
                }
                Err(TransactionError::Abort) => {
                    return Err(TransactionError::Abort);
                }
                Err(TransactionError::Conflict) => continue,
                Err(TransactionError::Storage(e)) => {
                    return Err(TransactionError::Storage(e));
                }
            }
        }
    }
}

impl<'a> Transactional for &'a Tree {
    type View = &'static TransactionalTree<'static>;

    fn make_overlay(&self) -> TransactionalTrees<'_> {
        TransactionalTrees {
            inner: vec![TransactionalTree {
                tree: &self,
                writes: Default::default(),
                read_cache: Default::default(),
                locks: Default::default(),
            }],
        }
    }

    fn view_overlay(overlay: &TransactionalTrees<'_>) -> Self::View {
        unsafe {
            let unsafe_ptr: &'static TransactionalTree<'static> =
                std::mem::transmute(&overlay.inner[0]);
            &*unsafe_ptr
        }
    }
}

impl<A, B> Transactional for (A, B)
where
    A: AsRef<Tree>,
    B: AsRef<Tree>,
{
    type View = (
        &'static TransactionalTree<'static>,
        &'static TransactionalTree<'static>,
    );

    fn make_overlay(&self) -> TransactionalTrees<'_> {
        TransactionalTrees {
            inner: vec![
                TransactionalTree {
                    tree: self.0.as_ref(),
                    writes: Default::default(),
                    read_cache: Default::default(),
                    locks: Default::default(),
                },
                TransactionalTree {
                    tree: self.1.as_ref(),
                    writes: Default::default(),
                    read_cache: Default::default(),
                    locks: Default::default(),
                },
            ],
        }
    }

    fn view_overlay(overlay: &TransactionalTrees<'_>) -> Self::View {
        let t1 = unsafe {
            let unsafe_ptr: &'static TransactionalTree<'static> =
                std::mem::transmute(&overlay.inner[0]);
            &*unsafe_ptr
        };
        let t2 = unsafe {
            let unsafe_ptr: &'static TransactionalTree<'static> =
                std::mem::transmute(&overlay.inner[1]);
            &*unsafe_ptr
        };
        (t1, t2)
    }
}
<commit_msg>Implement std::error::Error for TransactionError<commit_after>//! Fully serializable (ACID) multi-`Tree` transactions
//!
//! # Examples
//!
//! ```
//! use sled::{Db, Transactional};
//!
//! let db = Db::open("tx_db").unwrap();
//!
//! // Use write-only transactions as a writebatch:
//! db.transaction(|db| {
//!     db.insert(b"k1", b"cats")?;
//!     db.insert(b"k2", b"dogs")?;
//!     Ok(())
//! }).unwrap();
//!
//! // Atomically swap two items:
//! db.transaction(|db| {
//!     let v1_option = db.remove(b"k1")?;
//!     let v1 = v1_option.unwrap();
//!     let v2_option = db.remove(b"k2")?;
//!     let v2 = v2_option.unwrap();
//!
//!     db.insert(b"k1", v2);
//!     db.insert(b"k2", v1);
//!
//!     Ok(())
//! }).unwrap();
//!
//! assert_eq!(&db.get(b"k1").unwrap().unwrap(), b"dogs");
//! assert_eq!(&db.get(b"k2").unwrap().unwrap(), b"cats");
//!
//! // Transactions also work on tuples of `Tree`s,
//! // preserving serializable ACID semantics!
//! // In this example, we treat two trees like a
//! // work queue, atomically apply updates to
//! // data and move them from the unprocessed `Tree`
//! // to the processed `Tree`.
//! let unprocessed = db.open_tree(b"unprocessed items").unwrap();
//! let processed = db.open_tree(b"processed items").unwrap();
//!
//! // An update somehow gets into the tree, which we
//! // later trigger the atomic processing of.
//! unprocessed.insert(b"k3", b"ligers").unwrap();
//!
//! // Atomically process the new item and move it
//! // between `Tree`s.
//! (&unprocessed, &processed).transaction(|(unprocessed, processed)| {
//!     let unprocessed_item = unprocessed.remove(b"k3")?.unwrap();
//!     let mut processed_item = b"yappin' ".to_vec();
//!     processed_item.extend_from_slice(&unprocessed_item);
//!     processed.insert(b"k3", processed_item)?;
//!     Ok(())
//! }).unwrap();
//!
//! assert_eq!(unprocessed.get(b"k3").unwrap(), None);
//! assert_eq!(&processed.get(b"k3").unwrap().unwrap(), b"yappin' ligers");
//! ```

#![allow(unused)]
#![allow(missing_docs)]

use std::{cell::RefCell, collections::HashMap, sync::Arc};

use super::*;

/// A transaction that will
/// be applied atomically to the
/// Tree.
pub struct TransactionalTree<'a> {
    pub(super) tree: &'a Tree,
    pub(super) writes: RefCell<HashMap<IVec, Option<IVec>>>,
    pub(super) read_cache: RefCell<HashMap<IVec, Option<IVec>>>,
    pub(super) locks: RefCell<Vec<parking_lot::RwLockWriteGuard<'a, ()>>>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum TransactionError {
    Conflict,
    Abort,
    Storage(Error),
}

impl std::fmt::Display for TransactionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use TransactionError::*;
        match self {
            Conflict => write!(f, "Conflict during transaction"),
            Abort => write!(f, "Transaction was aborted"),
            Storage(e) => e.fmt(f),
        }
    }
}

impl std::error::Error for TransactionError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            TransactionError::Storage(ref e) => Some(e),
            _ => None,
        }
    }
}

pub type TransactionResult<T> = std::result::Result<T, TransactionError>;

fn abort() -> TransactionError {
    TransactionError::Abort
}

impl From<Error> for TransactionError {
    fn from(error: Error) -> Self {
        TransactionError::Storage(error)
    }
}

impl<'a> TransactionalTree<'a> {
    /// Set a key to a new value
    pub fn insert<K, V>(
        &self,
        key: K,
        value: V,
    ) -> TransactionResult<Option<IVec>>
    where
        IVec: From<K>,
        IVec: From<V>,
        K: AsRef<[u8]>,
    {
        let old = self.get(key.as_ref())?;
        let mut writes = self.writes.borrow_mut();
        writes.insert(IVec::from(key), Some(IVec::from(value)));
        Ok(old)
    }

    /// Remove a key
    pub fn remove<K>(&self, key: K) -> TransactionResult<Option<IVec>>
    where
        IVec: From<K>,
        K: AsRef<[u8]>,
    {
        let old = self.get(key.as_ref());
        let mut writes = self.writes.borrow_mut();
        writes.insert(IVec::from(key), None);
        old
    }

    /// Get the value associated with a key
    pub fn get<K: AsRef<[u8]>>(
        &self,
        key: K,
    ) -> TransactionResult<Option<IVec>> {
        let writes = self.writes.borrow();
        if let Some(first_try) = writes.get(key.as_ref()) {
            return Ok(first_try.clone());
        }
        let mut reads = self.read_cache.borrow_mut();
        if let Some(second_try) = reads.get(key.as_ref()) {
            return Ok(second_try.clone());
        }

        // not found in a cache, need to hit the backing db
        let get = self.tree.get_inner(key.as_ref())?;
        reads.insert(key.as_ref().into(), get.clone());

        Ok(get)
    }

    /// Atomically apply multiple inserts and removals.
    pub fn apply_batch(&self, batch: Batch) -> TransactionResult<()> {
        for (k, v_opt) in batch.writes {
            if let Some(v) = v_opt {
                self.insert(k, v)?;
            } else {
                self.remove(k)?;
            }
        }
        Ok(())
    }

    fn stage(&self) -> bool {
        let mut locks = self.locks.borrow_mut();
        let guard = self.tree.concurrency_control.write();
        locks.push(guard);
        true
    }

    fn unstage(&self) {
        unimplemented!()
    }

    fn validate(&self) -> bool {
        true
    }

    fn commit(&self) -> Result<()> {
        let mut writes = self.writes.borrow_mut();
        for (k, v_opt) in &*writes {
            if let Some(v) = v_opt {
                self.tree.insert_inner(k, v)?;
            } else {
                self.tree.remove_inner(k)?;
            }
        }
        Ok(())
    }
}

pub struct TransactionalTrees<'a> {
    inner: Vec<TransactionalTree<'a>>,
}

impl<'a> TransactionalTrees<'a> {
    fn stage(&self) -> bool {
        // we want to stage our trees in
        // lexicographic order to guarantee
        // no deadlocks should they block
        // on mutexes in their own staging
        // implementations.
        let mut tree_idxs: Vec<(&[u8], usize)> = self
            .inner
            .iter()
            .enumerate()
            .map(|(idx, t)| (&*t.tree.tree_id, idx))
            .collect();
        tree_idxs.sort_unstable();

        let mut last_idx = usize::max_value();
        for (_, idx) in tree_idxs {
            if idx == last_idx {
                // prevents us from double-locking
                continue;
            }
            last_idx = idx;
            if !self.inner[idx].stage() {
                return false;
            }
        }
        true
    }

    fn unstage(&self) {
        for tree in &self.inner {
            tree.unstage();
        }
    }

    fn validate(&self) -> bool {
        for tree in &self.inner {
            if !tree.validate() {
                return false;
            }
        }
        true
    }

    fn commit(self) -> Result<()> {
        let peg = self.inner[0].tree.context.pin_log()?;
        for tree in &self.inner {
            tree.commit()?;
        }

        // when the peg drops, it ensures all updates
        // written to the log since its creation are
        // recovered atomically
        peg.seal_batch()
    }
}

pub trait Transactional {
    type View;

    fn make_overlay(&self) -> TransactionalTrees<'_>;

    fn view_overlay(overlay: &TransactionalTrees<'_>) -> Self::View;

    fn transaction<F, R>(&self, f: F) -> TransactionResult<R>
    where
        F: Fn(Self::View) -> TransactionResult<R>,
    {
        loop {
            let tt = self.make_overlay();
            let view = Self::view_overlay(&tt);
            if !tt.stage() {
                tt.unstage();
                continue;
            }
            let ret = f(view);
            if !tt.validate() {
                tt.unstage();
                continue;
            }
            match ret {
                Ok(r) => {
                    tt.commit()?;
                    return Ok(r);
                }
                Err(TransactionError::Abort) => {
                    return Err(TransactionError::Abort);
                }
                Err(TransactionError::Conflict) => continue,
                Err(TransactionError::Storage(e)) => {
                    return Err(TransactionError::Storage(e));
                }
            }
        }
    }
}

impl<'a> Transactional for &'a Tree {
    type View = &'static TransactionalTree<'static>;

    fn make_overlay(&self) -> TransactionalTrees<'_> {
        TransactionalTrees {
            inner: vec![TransactionalTree {
                tree: &self,
                writes: Default::default(),
                read_cache: Default::default(),
                locks: Default::default(),
            }],
        }
    }

    fn view_overlay(overlay: &TransactionalTrees<'_>) -> Self::View {
        unsafe {
            let unsafe_ptr: &'static TransactionalTree<'static> =
                std::mem::transmute(&overlay.inner[0]);
            &*unsafe_ptr
        }
    }
}

impl<A, B> Transactional for (A, B)
where
    A: AsRef<Tree>,
    B: AsRef<Tree>,
{
    type View = (
        &'static TransactionalTree<'static>,
        &'static TransactionalTree<'static>,
    );

    fn make_overlay(&self) -> TransactionalTrees<'_> {
        TransactionalTrees {
            inner: vec![
                TransactionalTree {
                    tree: self.0.as_ref(),
                    writes: Default::default(),
                    read_cache: Default::default(),
                    locks: Default::default(),
                },
                TransactionalTree {
                    tree: self.1.as_ref(),
                    writes: Default::default(),
                    read_cache: Default::default(),
                    locks: Default::default(),
                },
            ],
        }
    }

    fn view_overlay(overlay: &TransactionalTrees<'_>) -> Self::View {
        let t1 = unsafe {
            let unsafe_ptr: &'static TransactionalTree<'static> =
                std::mem::transmute(&overlay.inner[0]);
            &*unsafe_ptr
        };
        let t2 = unsafe {
            let unsafe_ptr: &'static TransactionalTree<'static> =
                std::mem::transmute(&overlay.inner[1]);
            &*unsafe_ptr
        };
        (t1, t2)
    }
}
<|endoftext|>