<commit_before>//! The `pact_verifier` crate provides a number of exported functions using C bindings for
//! controlling the pact verification process. These can be used in any language that supports C bindings.

#![warn(missing_docs)]

use std::ffi::{CStr, CString};
use std::panic::catch_unwind;

use anyhow::Context;
use libc::{c_char, c_int, c_ushort, EXIT_FAILURE};
use log::*;
use std::env;

use crate::{as_mut, ffi_fn, safe_str};
use crate::util::*;
use crate::util::string::if_null;
use serde::Serialize;

mod args;
pub mod verifier;
pub mod handle;

/// External interface to verifier a provider
///
/// * `args` - the same as the CLI interface, except newline delimited
///
/// # Errors
///
/// Errors are returned as non-zero numeric values.
///
/// | Error | Description |
/// |-------|-------------|
/// | 1 | The verification process failed, see output for errors |
/// | 2 | A null pointer was received |
/// | 3 | The method panicked |
/// | 4 | Invalid arguments were provided to the verification process |
///
/// # Safety
///
/// Exported functions are inherently unsafe. Deal.
#[no_mangle]
pub unsafe extern fn pactffi_verify(args: *const c_char) -> i32 {
  if args.is_null() {
    return 2;
  }

  let result = catch_unwind(|| {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(async {
      let args_raw = CStr::from_ptr(args).to_string_lossy().into_owned();
      let args: Vec<String> = args_raw.lines().map(|s| s.to_string()).collect();
      let result = verifier::handle_args(args).await;

      match result {
        Ok(_) => 0,
        Err(e) => e
      }
    })
  });

  match result {
    Ok(val) => val,
    Err(cause) => {
      log::error!("Caught a general panic: {:?}", cause);
      3
    }
  }
}

ffi_fn! {
    /// Get a Handle to a newly created verifier. You should call `pactffi_verifier_shutdown` when
    /// done with the verifier to free all allocated resources
    ///
    /// # Safety
    ///
    /// This function is safe.
    ///
    /// # Error Handling
    ///
    /// Returns NULL on error.
    fn pactffi_verifier_new() -> *mut handle::VerifierHandle {
        let handle = handle::VerifierHandle::new();
        ptr::raw_to(handle)
    } {
        ptr::null_mut_to::<handle::VerifierHandle>()
    }
}

ffi_fn! {
    /// Shutdown the verifier and release all resources
    fn pactffi_verifier_shutdown(handle: *mut handle::VerifierHandle) {
        ptr::drop_raw(handle);
    }
}

ffi_fn! {
    /// Set the provider details for the Pact verifier. Passing a NULL for any field will
    /// use the default value for that field.
    ///
    /// # Safety
    ///
    /// All string fields must contain valid UTF-8. Invalid UTF-8
    /// will be replaced with U+FFFD REPLACEMENT CHARACTER.
    ///
    fn pactffi_verifier_set_provider_info(
      handle: *mut handle::VerifierHandle,
      name: *const c_char,
      scheme: *const c_char,
      host: *const c_char,
      port: c_ushort,
      path: *const c_char
    ) {
      let handle = as_mut!(handle);
      let name = if_null(name, "provider");
      let scheme = if_null(scheme, "http");
      let host = if_null(host, "localhost");
      let path = if_null(path, "/");

      handle.update_provider_info(name, scheme, host, port as u16, path);
    }
}

ffi_fn! {
    /// Adds a Pact file as a source to verify.
    ///
    /// # Safety
    ///
    /// All string fields must contain valid UTF-8. Invalid UTF-8
    /// will be replaced with U+FFFD REPLACEMENT CHARACTER.
    ///
    fn pactffi_verifier_add_file_source(
      handle: *mut handle::VerifierHandle,
      file: *const c_char
    ) {
      let handle = as_mut!(handle);
      let file = safe_str!(file);

      handle.add_file_source(file);
    }
}

ffi_fn! {
    /// Runs the verification.
    ///
    /// # Error Handling
    ///
    /// Errors will be reported with a non-zero return value.
    fn pactffi_verifier_execute(handle: *mut handle::VerifierHandle) -> c_int {
      let handle = as_mut!(handle);

      handle.execute()
    } {
      EXIT_FAILURE
    }
}

#[derive(Serialize)]
struct Argument {
    name: Option<String>,
    short: Option<String>,
    long: Option<String>,
    help: Option<String>,
    possible_values: Option<Vec<String>>
}

/// External interface to retrieve the options and arguments available when calling the CLI interface,
/// returning them as a JSON string.
///
/// The purpose is to then be able to use in other languages which wrap the FFI library, to implement
/// the same CLI functionality automatically without manual maintenance of arguments, help descriptions
/// etc.
///
/// # Example structure
/// ```json
/// [
///     {
///         "name": "broker-url",
///         "short": "b",
///         "long": "broker-url",
///         "help": "URL of the pact broker to fetch pacts from to verify (requires the provider name parameter)"
///     },
///     {
///         "name": "port",
///         "short": "p",
///         "long": "port",
///         "help": "Provider port (defaults to protocol default 80/443)"
///     },
///     {
///         "name": "user",
///         "short": null,
///         "long": "user",
///         "help": "Username to use when fetching pacts from URLS"
///     }
/// ]
/// ```
///
/// # Safety
///
/// Exported functions are inherently unsafe.
#[no_mangle]
pub extern "C" fn pactffi_verifier_cli_args() -> *const c_char {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let app = args::setup_app(program, clap::crate_version!());

    // Iterate through the args, extracting info from each to then add to a Vector of args
    let mut args: Vec<Argument> = Vec::new();
    for opt in app.p.opts.iter() {
        let mut arg = Argument { name: None, short: None, long: None, help: None, possible_values: None };

        // Name
        // TODO: Maybe superfluous as this is always the same as the long
        let arg_name = opt.b.name;
        arg.name = Some(arg_name.to_string());

        // Short
        match opt.s.short {
            None => {}
            Some(_val) => {
                let c_str = CString::new(_val.to_string()).unwrap();
                let short = c_str.to_str().unwrap();
                arg.short = Some(short.to_string());
            }
        }

        // Long
        match opt.s.long {
            None => {}
            Some(_val) => {
                let c_str = CString::new(_val.to_string()).unwrap();
                let long = c_str.to_str().unwrap();
                arg.long = Some(long.to_string());
            }
        }

        // Help
        match opt.b.help {
            None => {}
            Some(_val) => {
                let c_str = CString::new(_val.to_string()).unwrap();
                let help = c_str.to_str().unwrap();
                arg.help = Some(help.to_string());
            }
        }

        // Possible Values
        match opt.v.possible_vals {
            None => {}
            Some(_) => {
                let mut possible_vals: Vec<String> = Vec::new();
                let possible_values = opt.v.possible_vals.clone().unwrap();
                for possible_val in possible_values.iter() {
                    possible_vals.push(possible_val.to_string())
                }
                arg.possible_values = Some(possible_vals);
            }
        }

        args.push(arg);
    }

    let json = serde_json::to_string(&args).unwrap();
    let c_str = CString::new(json).unwrap();
    c_str.into_raw() as *const c_char
}<commit_msg>feat(ffi verify): add in default values, start looking at flags<commit_after>//! The `pact_verifier` crate provides a number of exported functions using C bindings for
//! controlling the pact verification process. These can be used in any language that supports C bindings.

#![warn(missing_docs)]

use std::ffi::{CStr, CString};
use std::panic::catch_unwind;

use anyhow::Context;
use libc::{c_char, c_int, c_ushort, EXIT_FAILURE};
use log::*;
use std::env;

use crate::{as_mut, ffi_fn, safe_str};
use crate::util::*;
use crate::util::string::if_null;
use serde::Serialize;
use std::any::Any;
use clap::{Arg, ArgSettings};

mod args;
pub mod verifier;
pub mod handle;

/// External interface to verifier a provider
///
/// * `args` - the same as the CLI interface, except newline delimited
///
/// # Errors
///
/// Errors are returned as non-zero numeric values.
///
/// | Error | Description |
/// |-------|-------------|
/// | 1 | The verification process failed, see output for errors |
/// | 2 | A null pointer was received |
/// | 3 | The method panicked |
/// | 4 | Invalid arguments were provided to the verification process |
///
/// # Safety
///
/// Exported functions are inherently unsafe. Deal.
#[no_mangle]
pub unsafe extern fn pactffi_verify(args: *const c_char) -> i32 {
  if args.is_null() {
    return 2;
  }

  let result = catch_unwind(|| {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(async {
      let args_raw = CStr::from_ptr(args).to_string_lossy().into_owned();
      let args: Vec<String> = args_raw.lines().map(|s| s.to_string()).collect();
      let result = verifier::handle_args(args).await;

      match result {
        Ok(_) => 0,
        Err(e) => e
      }
    })
  });

  match result {
    Ok(val) => val,
    Err(cause) => {
      log::error!("Caught a general panic: {:?}", cause);
      3
    }
  }
}

ffi_fn! {
    /// Get a Handle to a newly created verifier. You should call `pactffi_verifier_shutdown` when
    /// done with the verifier to free all allocated resources
    ///
    /// # Safety
    ///
    /// This function is safe.
    ///
    /// # Error Handling
    ///
    /// Returns NULL on error.
    fn pactffi_verifier_new() -> *mut handle::VerifierHandle {
        let handle = handle::VerifierHandle::new();
        ptr::raw_to(handle)
    } {
        ptr::null_mut_to::<handle::VerifierHandle>()
    }
}

ffi_fn! {
    /// Shutdown the verifier and release all resources
    fn pactffi_verifier_shutdown(handle: *mut handle::VerifierHandle) {
        ptr::drop_raw(handle);
    }
}

ffi_fn! {
    /// Set the provider details for the Pact verifier. Passing a NULL for any field will
    /// use the default value for that field.
    ///
    /// # Safety
    ///
    /// All string fields must contain valid UTF-8. Invalid UTF-8
    /// will be replaced with U+FFFD REPLACEMENT CHARACTER.
    ///
    fn pactffi_verifier_set_provider_info(
      handle: *mut handle::VerifierHandle,
      name: *const c_char,
      scheme: *const c_char,
      host: *const c_char,
      port: c_ushort,
      path: *const c_char
    ) {
      let handle = as_mut!(handle);
      let name = if_null(name, "provider");
      let scheme = if_null(scheme, "http");
      let host = if_null(host, "localhost");
      let path = if_null(path, "/");

      handle.update_provider_info(name, scheme, host, port as u16, path);
    }
}

ffi_fn! {
    /// Adds a Pact file as a source to verify.
    ///
    /// # Safety
    ///
    /// All string fields must contain valid UTF-8. Invalid UTF-8
    /// will be replaced with U+FFFD REPLACEMENT CHARACTER.
    ///
    fn pactffi_verifier_add_file_source(
      handle: *mut handle::VerifierHandle,
      file: *const c_char
    ) {
      let handle = as_mut!(handle);
      let file = safe_str!(file);

      handle.add_file_source(file);
    }
}

ffi_fn! {
    /// Runs the verification.
    ///
    /// # Error Handling
    ///
    /// Errors will be reported with a non-zero return value.
    fn pactffi_verifier_execute(handle: *mut handle::VerifierHandle) -> c_int {
      let handle = as_mut!(handle);

      handle.execute()
    } {
      EXIT_FAILURE
    }
}

#[derive(Serialize)]
struct Argument {
    name: Option<String>,
    short: Option<String>,
    long: Option<String>,
    help: Option<String>,
    possible_values: Option<Vec<String>>,
    default_value: Option<String>,
}

// #[derive(Serialize)]
// struct ArgumentsFlags {
//     arguments: Vec<Argument>,
//     flags: Vec<Argument>
// }

/// External interface to retrieve the options and arguments available when calling the CLI interface,
/// returning them as a JSON string.
///
/// The purpose is to then be able to use in other languages which wrap the FFI library, to implement
/// the same CLI functionality automatically without manual maintenance of arguments, help descriptions
/// etc.
///
/// # Example structure
/// ```json
/// [
///     {
///         "name": "broker-url",
///         "short": "b",
///         "long": "broker-url",
///         "help": "URL of the pact broker to fetch pacts from to verify (requires the provider name parameter)"
///     },
///     {
///         "name": "port",
///         "short": "p",
///         "long": "port",
///         "help": "Provider port (defaults to protocol default 80/443)"
///     },
///     {
///         "name": "user",
///         "short": null,
///         "long": "user",
///         "help": "Username to use when fetching pacts from URLS"
///     }
/// ]
/// ```
///
/// # Safety
///
/// Exported functions are inherently unsafe.
#[no_mangle]
pub extern "C" fn pactffi_verifier_cli_args() -> *const c_char {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let app = args::setup_app(program, clap::crate_version!());

    // Iterate through the args, extracting info from each to then add to a Vector of args
    let mut arguments: Vec<Argument> = Vec::new();
    // let mut flags: Vec<Argument> = Vec::new();
    // let mut args_flags = ArgumentsFlags { arguments:arguments, flags: flags};

    for opt in app.p.opts.iter() {
        let mut arg = Argument { name: None, short: None, long: None, help: None, possible_values: None, default_value: None };

        // Name
        // TODO: Maybe superfluous as this is always the same as the long
        let arg_name = opt.b.name;
        arg.name = Some(arg_name.to_string());

        // Short
        match opt.s.short {
            None => {}
            Some(_val) => {
                let c_str = CString::new(_val.to_string()).unwrap();
                let short = c_str.to_str().unwrap();
                arg.short = Some(short.to_string());
            }
        }

        // Long
        match opt.s.long {
            None => {}
            Some(_val) => {
                let c_str = CString::new(_val.to_string()).unwrap();
                let long = c_str.to_str().unwrap();
                arg.long = Some(long.to_string());
            }
        }

        // Help
        match opt.b.help {
            None => {}
            Some(_val) => {
                let c_str = CString::new(_val.to_string()).unwrap();
                let help = c_str.to_str().unwrap();
                arg.help = Some(help.to_string());
            }
        }

        // Possible values
        match opt.v.possible_vals {
            None => {}
            Some(_) => {
                let mut possible_vals: Vec<String> = Vec::new();
                let possible_values = opt.v.possible_vals.clone().unwrap();
                for possible_val in possible_values.iter() {
                    possible_vals.push(possible_val.to_string())
                }
                arg.possible_values = Some(possible_vals);
            }
        }

        arguments.push(arg);
    }

    // TODO: Also need to handle hopefully app.p.flags, where the takes_value(false) ones go

    let json = serde_json::to_string(&arguments).unwrap();
    let c_str = CString::new(json).unwrap();
    c_str.into_raw() as *const c_char
}<|endoftext|>


<commit_before>// ignore-tidy-linelength
// only-macos
// no-system-llvm
// ignore-debug: the debug assertions get in the way
// compile-flags: -O

#![crate_type = "rlib"]
#![feature(thread_local)]

// CHECK: @STATIC_VAR_1 = thread_local local_unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4
#[no_mangle]
#[thread_local]
static mut STATIC_VAR_1: [u32; 8] = [0; 8];

// CHECK: @STATIC_VAR_2 = thread_local local_unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4
#[no_mangle]
#[thread_local]
static mut STATIC_VAR_2: [u32; 8] = [4; 8];

#[no_mangle]
pub unsafe fn f(x: &mut [u32; 8]) {
    std::mem::swap(x, &mut STATIC_VAR_1)
}

#[no_mangle]
pub unsafe fn g(x: &mut [u32; 8]) {
    std::mem::swap(x, &mut STATIC_VAR_2)
}
<commit_msg>Ignore changes when debug assertions are enabled.<commit_after>// ignore-tidy-linelength
// only-macos
// no-system-llvm
// compile-flags: -O

#![crate_type = "rlib"]
#![feature(thread_local)]

// local_unnamed_addr does not appear when std is built with debug assertions.
// CHECK: @STATIC_VAR_1 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4
#[no_mangle]
#[thread_local]
static mut STATIC_VAR_1: [u32; 8] = [0; 8];

// CHECK: @STATIC_VAR_2 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4
#[no_mangle]
#[thread_local]
static mut STATIC_VAR_2: [u32; 8] = [4; 8];

#[no_mangle]
pub unsafe fn f(x: &mut [u32; 8]) {
    std::mem::swap(x, &mut STATIC_VAR_1)
}

#[no_mangle]
pub unsafe fn g(x: &mut [u32; 8]) {
    std::mem::swap(x, &mut STATIC_VAR_2)
}
<|endoftext|>


<commit_before>use gmp::mpq::Mpq;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::cmp::Ordering;
use std::vec::Vec;

fn is_equal(&o: &Ordering) -> bool {
    o == Ordering::Equal
}

fn count_equal(a: &[Ordering]) -> usize {
    a.iter().cloned().filter(is_equal).count()
}

pub fn proportional_completion<'a, Patterns>(patterns: Patterns) -> Box<[(Box<[usize]>, Mpq)]>
    where Patterns: Iterator<Item = (&'a [Ordering], &'a Mpq)>
{
    let mut patterns_by_count = vec![HashMap::<Box<[Ordering]>, Mpq>::new()];
    for (a, w) in patterns {
        let t = count_equal(a);
        if t >= patterns_by_count.len() {
            patterns_by_count.resize(t + 1, HashMap::new());
        }
        let e = patterns_by_count[t]
            .entry(a.to_vec().into_boxed_slice())
            .or_insert_with(Mpq::zero);
        *e = &*e + w;
    }
    while patterns_by_count.len() > 1 {
        let mut tied = patterns_by_count.pop().unwrap();
        let mut tied = tied.drain().collect::<Vec<_>>();
        while let Some((a, w)) = tied.pop() {
            let mut replacements = HashMap::new();
            let mut total = Mpq::zero();
            for (a1, w1) in patterns_by_count.iter()
                .flat_map(|it| it)
                .chain(tied.iter().map(|&(ref a1, ref w1)| (a1, w1))) {
                if a.iter()
                    .zip(a1.iter())
                    .any(|(&o, &o1)| o == Ordering::Equal && o1 != Ordering::Equal) {
                    let a2 = a.iter()
                        .zip(a1.iter())
                        .map(|(&o, &o1)| if o == Ordering::Equal { o1 } else { o })
                        .collect::<Vec<_>>()
                        .into_boxed_slice();
                    let e = replacements.entry(a2).or_insert_with(Mpq::zero);
                    *e = &*e + w1;
                    total = total + w1;
                }
            }
            if total.is_zero() {
                let scale = w / Mpq::from(2u64);
                for &o1 in &[Ordering::Greater, Ordering::Less] {
                    let a1 = a.iter()
                        .map(|&o| if o == Ordering::Equal { o1 } else { o })
                        .collect::<Vec<_>>()
                        .into_boxed_slice();
                    let e = patterns_by_count[count_equal(&a1)].entry(a1).or_insert_with(Mpq::zero);
                    *e = &*e + &scale;
                }
            } else {
                let scale = w / total;
                for (a1, w1) in &replacements {
                    let e = patterns_by_count[count_equal(a1)]
                        .entry(a1.clone())
                        .or_insert_with(Mpq::zero);
                    *e = &*e + w1 * &scale;
                }
            }
        }
    }
    patterns_by_count.pop()
        .unwrap()
        .iter()
        .map(|(a, w)| {
            (a.iter()
                 .enumerate()
                 .filter(|&(_, &o)| o == Ordering::Greater)
                 .map(|(c, _)| c)
                 .collect::<Vec<_>>()
                 .into_boxed_slice(),
             w.clone())
        })
        .collect::<Vec<_>>()
        .into_boxed_slice()
}

#[cfg(test)]
mod tests {
    use gmp::mpq::Mpq;
    use gmp::mpz::Mpz;
    use std::cmp::Ordering;
    use std::str::FromStr;

    use super::proportional_completion;

    const Q: fn(i64) -> Mpq = Mpq::from;

    fn sorted<T: Clone + Ord>(v: &[T]) -> Box<[T]> {
        let mut v1 = v.to_vec().into_boxed_slice();
        v1.sort();
        v1
    }

    #[test]
    fn test_proportional_completion_1() {
        // Schulze’s calcul02.pdf
        let (_1, _2, _3) = (Ordering::Greater, Ordering::Equal, Ordering::Less);
        let patterns: &[(&[Ordering], &Mpq)] = &[(&[_1, _1, _1, _1], &Q(46)),
                                                 (&[_1, _1, _1, _2], &Q(15)),
                                                 (&[_1, _1, _1, _3], &Q(24)),
                                                 (&[_1, _1, _2, _1], &Q(8)),
                                                 (&[_1, _1, _2, _2], &Q(10)),
                                                 (&[_1, _1, _3, _1], &Q(10)),
                                                 (&[_1, _1, _3, _3], &Q(19)),
                                                 (&[_1, _2, _1, _1], &Q(8)),
                                                 (&[_1, _2, _1, _2], &Q(29)),
                                                 (&[_1, _2, _2, _1], &Q(10)),
                                                 (&[_1, _2, _2, _2], &Q(26)),
                                                 (&[_1, _3, _1, _1], &Q(10)),
                                                 (&[_1, _3, _1, _3], &Q(15)),
                                                 (&[_1, _3, _2, _1], &Q(1)),
                                                 (&[_1, _3, _3, _1], &Q(9)),
                                                 (&[_1, _3, _3, _3], &Q(41)),
                                                 (&[_2, _1, _1, _1], &Q(3)),
                                                 (&[_2, _1, _1, _2], &Q(5)),
                                                 (&[_2, _1, _2, _1], &Q(5)),
                                                 (&[_2, _1, _2, _2], &Q(10)),
                                                 (&[_2, _2, _1, _1], &Q(7)),
                                                 (&[_2, _2, _1, _2], &Q(22)),
                                                 (&[_2, _2, _2, _1], &Q(14)),
                                                 (&[_2, _2, _2, _2], &Q(23)),
                                                 (&[_2, _3, _3, _3], &Q(1)),
                                                 (&[_3, _1, _1, _1], &Q(1)),
                                                 (&[_3, _1, _1, _3], &Q(3)),
                                                 (&[_3, _1, _3, _1], &Q(4)),
                                                 (&[_3, _1, _3, _3], &Q(5)),
                                                 (&[_3, _3, _1, _1], &Q(4)),
                                                 (&[_3, _3, _1, _3], &Q(11)),
                                                 (&[_3, _3, _3, _1], &Q(6)),
                                                 (&[_3, _3, _3, _3], &Q(55))];
        let expected: &[(&[Ordering], Mpq)] =
            &[(&[_1, _1, _1, _1],
               Mpq::ratio(&Mpz::from_str("45513455366183031714312799").unwrap(),
                          &Mpz::from_str("392675963004164103979050").unwrap())),
              (&[_1, _1, _1, _3], Q(313141398725389) / Q(5802708856700)),
              (&[_1, _1, _3, _1], Q(398610303226324835) / Q(17955446458922532)),
              (&[_1, _1, _3, _3], Q(56713304320) / Q(2097364647)),
              (&[_1, _3, _1, _1],
               Mpq::ratio(&Mpz::from_str("323263789854293839067").unwrap(),
                          &Mpz::from_str("10819009863732307590").unwrap())),
              (&[_1, _3, _1, _3], Q(2664088261) / Q(79938130)),
              (&[_1, _3, _3, _1], Q(10276259658824) / Q(618385674987)),
              (&[_1, _3, _3, _3], Q(14567002) / Q(288933)),
              (&[_3, _1, _1, _1], Q(9026619205849313) / Q(2353019619161400)),
              (&[_3, _1, _1, _3], Q(25937418993) / Q(4983004600)),
              (&[_3, _1, _3, _1], Q(16829300885) / Q(2878135428)),
              (&[_3, _1, _3, _3], Q(2025680) / Q(300181)),
              (&[_3, _3, _1, _1], Q(57854720993) / Q(8103800865)),
              (&[_3, _3, _1, _3], Q(4456496) / Q(291745)),
              (&[_3, _3, _3, _1], Q(837524) / Q(99123)),
              (&[_3, _3, _3, _3], Q(40878) / Q(703))];
        let expected = expected.iter()
            .map(|&(ref a, ref w)| {
                (a.iter()
                     .enumerate()
                     .filter(|&(_, &o)| o == _1)
                     .map(|(i, _)| i)
                     .collect::<Vec<_>>()
                     .into_boxed_slice(),
                 w.clone())
            })
            .collect::<Vec<_>>();
        assert_eq!(*sorted(&proportional_completion(patterns.iter().cloned())),
                   *sorted(&expected));
    }
}
<commit_msg>proportional_completion: Reimplement with faster bitwise arithmetic<commit_after>use gmp::mpq::Mpq;
use std::collections::btree_map::{BTreeMap, Entry as BEntry};
use std::collections::hash_map::{HashMap, Entry as HEntry};
use std::cmp::Ordering;
use std::vec::Vec;

fn encode_pattern(a: &[Ordering]) -> (usize, usize) {
    a.iter().rev().fold((0, 0), |(eq, gt), &o| {
        (eq << 1 | if o == Ordering::Equal { 1 } else { 0 },
         gt << 1 | if o == Ordering::Greater { 1 } else { 0 })
    })
}

fn decode_bits(mut gt: usize) -> Box<[usize]> {
    let mut cs = Vec::new();
    cs.reserve(gt.count_ones() as usize);
    while gt != 0 {
        let k = gt.trailing_zeros();
        cs.push(k as usize);
        gt &= !(1 << k);
    }
    cs.into_boxed_slice()
}

pub fn proportional_completion<'a, Patterns>(patterns: Patterns) -> Box<[(Box<[usize]>, Mpq)]>
    where Patterns: Iterator<Item = (&'a [Ordering], &'a Mpq)>
{
    let mut pattern_map = BTreeMap::new();
    let mut total = Mpq::zero();
    for (a, w) in patterns {
        if !w.is_zero() {
            match pattern_map.entry(encode_pattern(a)) {
                BEntry::Occupied(mut e) => {
                    let w1 = e.get() + w;
                    e.insert(w1);
                }
                BEntry::Vacant(e) => {
                    e.insert(w.clone());
                }
            }
            total = total + w;
        }
    }
    while let Some((&(eq, _), _)) = pattern_map.iter().next_back() {
        if eq == 0 {
            return pattern_map.iter()
                .map(|(&(_, gt), w)| (decode_bits(gt), w.clone()))
                .collect::<Vec<_>>()
                .into_boxed_slice();
        }

        let m = pattern_map.split_off(&(eq, 0));

        if pattern_map.is_empty() {
            return m.iter()
                .map(|(&(_, gt), w)| (decode_bits(gt), w / Mpq::from(2u64)))
                .chain(pattern_map.iter()
                    .map(|(&(_, gt), w)| (decode_bits(gt | eq), w / Mpq::from(2u64))))
                .collect::<Vec<_>>()
                .into_boxed_slice();
        }

        let scale = m.iter().fold(total.clone(), |acc, (_, w)| acc - w);

        let mut h = HashMap::new();
        for (&(eq1, gt1), w1) in &pattern_map {
            debug_assert!(eq1 < eq);
            match h.entry((eq & eq1, eq & gt1)) {
                HEntry::Occupied(mut e) => {
                    let w2 = e.get() + w1;
                    e.insert(w2);
                }
                HEntry::Vacant(e) => {
                    e.insert(w1.clone());
                }
            }
        }

        for ((eq_, gt), w) in m {
            let w_scaled = w / &scale;
            debug_assert_eq!(eq_, eq);
            for (&(eq1, gt1), w1) in &h {
                match pattern_map.entry((eq1, gt | gt1)) {
                    BEntry::Occupied(mut e) => {
                        let w2 = e.get() + w1 * &w_scaled;
                        e.insert(w2);
                    }
                    BEntry::Vacant(e) => {
                        e.insert(w1 * &w_scaled);
                    }
                }
            }
        }
        debug_assert_eq!(pattern_map.iter().fold(Mpq::zero(), |acc, (_, w)| acc + w),
                         total);
    }
    Box::new([])
}

#[cfg(test)]
mod tests {
    use gmp::mpq::Mpq;
    use gmp::mpz::Mpz;
    use std::cmp::Ordering;
    use std::str::FromStr;

    use super::proportional_completion;

    const Q: fn(i64) -> Mpq = Mpq::from;

    fn sorted<T: Clone + Ord>(v: &[T]) -> Box<[T]> {
        let mut v1 = v.to_vec().into_boxed_slice();
        v1.sort();
        v1
    }

    #[test]
    fn test_proportional_completion_1() {
        // Schulze’s calcul02.pdf
        let (_1, _2, _3) = (Ordering::Greater, Ordering::Equal, Ordering::Less);
        let patterns: &[(&[Ordering], &Mpq)] = &[(&[_1, _1, _1, _1], &Q(46)),
                                                 (&[_1, _1, _1, _2], &Q(15)),
                                                 (&[_1, _1, _1, _3], &Q(24)),
                                                 (&[_1, _1, _2, _1], &Q(8)),
                                                 (&[_1, _1, _2, _2], &Q(10)),
                                                 (&[_1, _1, _3, _1], &Q(10)),
                                                 (&[_1, _1, _3, _3], &Q(19)),
                                                 (&[_1, _2, _1, _1], &Q(8)),
                                                 (&[_1, _2, _1, _2], &Q(29)),
                                                 (&[_1, _2, _2, _1], &Q(10)),
                                                 (&[_1, _2, _2, _2], &Q(26)),
                                                 (&[_1, _3, _1, _1], &Q(10)),
                                                 (&[_1, _3, _1, _3], &Q(15)),
                                                 (&[_1, _3, _2, _1], &Q(1)),
                                                 (&[_1, _3, _3, _1], &Q(9)),
                                                 (&[_1, _3, _3, _3], &Q(41)),
                                                 (&[_2, _1, _1, _1], &Q(3)),
                                                 (&[_2, _1, _1, _2], &Q(5)),
                                                 (&[_2, _1, _2, _1], &Q(5)),
                                                 (&[_2, _1, _2, _2], &Q(10)),
                                                 (&[_2, _2, _1, _1], &Q(7)),
                                                 (&[_2, _2, _1, _2], &Q(22)),
                                                 (&[_2, _2, _2, _1], &Q(14)),
                                                 (&[_2, _2, _2, _2], &Q(23)),
                                                 (&[_2, _3, _3, _3], &Q(1)),
                                                 (&[_3, _1, _1, _1], &Q(1)),
                                                 (&[_3, _1, _1, _3], &Q(3)),
                                                 (&[_3, _1, _3, _1], &Q(4)),
                                                 (&[_3, _1, _3, _3], &Q(5)),
                                                 (&[_3, _3, _1, _1], &Q(4)),
                                                 (&[_3, _3, _1, _3], &Q(11)),
                                                 (&[_3, _3, _3, _1], &Q(6)),
                                                 (&[_3, _3, _3, _3], &Q(55))];
        let expected: &[(&[Ordering], Mpq)] =
            &[(&[_1, _1, _1, _1],
               Mpq::ratio(&Mpz::from_str("45513455366183031714312799").unwrap(),
                          &Mpz::from_str("392675963004164103979050").unwrap())),
              (&[_1, _1, _1, _3], Q(313141398725389) / Q(5802708856700)),
              (&[_1, _1, _3, _1], Q(398610303226324835) / Q(17955446458922532)),
              (&[_1, _1, _3, _3], Q(56713304320) / Q(2097364647)),
              (&[_1, _3, _1, _1],
               Mpq::ratio(&Mpz::from_str("323263789854293839067").unwrap(),
                          &Mpz::from_str("10819009863732307590").unwrap())),
              (&[_1, _3, _1, _3], Q(2664088261) / Q(79938130)),
              (&[_1, _3, _3, _1], Q(10276259658824) / Q(618385674987)),
              (&[_1, _3, _3, _3], Q(14567002) / Q(288933)),
              (&[_3, _1, _1, _1], Q(9026619205849313) / Q(2353019619161400)),
              (&[_3, _1, _1, _3], Q(25937418993) / Q(4983004600)),
              (&[_3, _1, _3, _1], Q(16829300885) / Q(2878135428)),
              (&[_3, _1, _3, _3], Q(2025680) / Q(300181)),
              (&[_3, _3, _1, _1], Q(57854720993) / Q(8103800865)),
              (&[_3, _3, _1, _3], Q(4456496) / Q(291745)),
              (&[_3, _3, _3, _1], Q(837524) / Q(99123)),
              (&[_3, _3, _3, _3], Q(40878) / Q(703))];
        let expected = expected.iter()
            .map(|&(ref a, ref w)| {
                (a.iter()
                     .enumerate()
                     .filter(|&(_, &o)| o == _1)
                     .map(|(i, _)| i)
                     .collect::<Vec<_>>()
                     .into_boxed_slice(),
                 w.clone())
            })
            .collect::<Vec<_>>();
        assert_eq!(*sorted(&proportional_completion(patterns.iter().cloned())),
                   *sorted(&expected));
    }
}
<|endoftext|>


<commit_before>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;
extern crate reqwest;

use std::env;
use std::mem;
use std::fs::File;
use std::option::NoneError;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 21600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer {
    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,

    current_game: Vec<u8>,
    plies: usize,

    batch: Vec<u8>,
    batch_size: usize,
}

impl Indexer {
    fn new() -> Indexer {
        Indexer {
            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,

            current_game: Vec::new(),
            plies: 0,

            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            let mut res = reqwest::Client::new()
                .put("http://localhost:9000/import/lichess")
                .body(mem::replace(&mut self.batch, Vec::new()))
                .send().expect("send batch");

            let mut answer = String::new();
            res.read_to_string(&mut answer).expect("decode response");
            println!("{}", answer);
            assert!(res.status().is_success());
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn begin_game(&mut self) {
        self.current_game.clear();
        self.plies = 0;
    }

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        }

        let (key, value) = if key == b"Site" {
            (&b"LichessID"[..], value.rsplitn(2, |ch| *ch == b'/').next().expect("Site"))
        } else {
            (key, value)
        };

        self.current_game.push(b'[');
        self.current_game.extend(key);
        self.current_game.extend(b" \"");
        self.current_game.extend(value);
        self.current_game.extend(b"\"]\n");
    }

    fn end_headers(&mut self) -> Skip {
        self.current_game.push(b'\n');

        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = match self.time_control {
            TimeControl::Correspondence => 1.0,
            TimeControl::Classical if rating >= 2000 => 1.0,
            TimeControl::Classical if rating >= 1800 => 2.0 / 5.0,
            TimeControl::Classical => 1.0 / 8.0,
            TimeControl::Blitz if rating >= 2000 => 1.0,
            TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
            TimeControl::Blitz => 1.0 / 15.0,
            TimeControl::Bullet if rating >= 2300 => 1.0,
            TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
            TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
            TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
            _ => 1.0 / 20.0,
        };

        let Closed01(rnd) = random::<Closed01<f64>>();
        self.skip = probability < rnd;

        Skip(self.skip)
    }

    fn san(&mut self, san: San) {
        if self.plies < MAX_PLIES {
            if self.plies > 0 {
                self.current_game.push(b' ');
            }

            self.current_game.extend(san.to_string().as_bytes());
            self.plies += 1;
        }
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, _game: &'pgn [u8]) {
        if !self.skip && self.plies > 8 {
            if self.batch_size > BATCH_SIZE {
                self.send();
            }

            if self.batch_size > 0 {
                self.batch.extend(b"\n\n");
            }

            self.batch.extend(&self.current_game);
            self.batch_size += 1;
        }
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new();
        Reader::new(&mut indexer, &pgn[..]).read_all();
        indexer.send(); // send last
    }
}
<commit_msg>pgn indexer fixes<commit_after>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;
extern crate reqwest;

use std::env;
use std::mem;
use std::cmp::min;
use std::fs::File;
use std::option::NoneError;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

const BATCH_SIZE: usize = 1;

const MAX_PLIES: usize = 50;

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 21600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer {
    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,

    current_game: Vec<u8>,
    plies: usize,

    batch: Vec<u8>,
    batch_size: usize,
}

impl Indexer {
    fn new() -> Indexer {
        Indexer {
            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,

            current_game: Vec::new(),
            plies: 0,

            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            self.batch_size = 0;

            let mut res = reqwest::Client::new()
                .put("http://localhost:9000/import/lichess")
                .body(mem::replace(&mut self.batch, Vec::new()))
                .send().expect("send batch");

            let mut answer = String::new();
            res.read_to_string(&mut answer).expect("decode response");
            println!("{}", answer);
            assert!(res.status().is_success());
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn begin_game(&mut self) {
        self.current_game.clear();
        self.plies = 0;
    }

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        } else if key == b"Variant" {
            assert_eq!(value, b"Standard");
            return; // we add this unconditionally later
        }

        let (key, value) = if key == b"Site" {
            (&b"LichessID"[..], value.rsplitn(2, |ch| *ch == b'/').next().expect("Site"))
        } else {
            (key, value)
        };

        self.current_game.push(b'[');
        self.current_game.extend(key);
        self.current_game.extend(b" \"");
        self.current_game.extend(value);
        self.current_game.extend(b"\"]\n");
    }

    fn end_headers(&mut self) -> Skip {
        self.current_game.extend(b"[Variant \"Standard\"]\n");
        self.current_game.push(b'\n');

        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = match self.time_control {
            TimeControl::Correspondence => 1.0,
            TimeControl::Classical if rating >= 2000 => 1.0,
            TimeControl::Classical if rating >= 1800 => 2.0 / 5.0,
            TimeControl::Classical => 1.0 / 8.0,
            TimeControl::Blitz if rating >= 2000 => 1.0,
            TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
            TimeControl::Blitz => 1.0 / 15.0,
            TimeControl::Bullet if rating >= 2300 => 1.0,
            TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
            TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
            TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
            _ => 1.0 / 20.0,
        };

        let Closed01(rnd) = random::<Closed01<f64>>();
        let accept = min(self.white_elo, self.black_elo) >= 1500 && probability >= rnd;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: San) {
        if self.plies < MAX_PLIES {
            if self.plies > 0 {
                self.current_game.push(b' ');
            }

            self.current_game.extend(san.to_string().as_bytes());
            self.plies += 1;
        }
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, _game: &'pgn [u8]) {
        if !self.skip && self.plies > 8 {
            if self.batch_size >= BATCH_SIZE {
                self.send();
            }

            if self.batch_size > 0 {
                self.batch.extend(b"\n\n");
            }

            self.batch.extend(&self.current_game);
            self.batch_size += 1;
        }
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new();
        Reader::new(&mut indexer, &pgn[..]).read_all();
        indexer.send(); // send last
    }
}
<|endoftext|>


<commit_before>use std::num::div_rem;
use std::mem::transmute;
//#[deriving(Show)]
//pub type Int3=[int,..3];
pub struct Int3 {i:int,j:int,k:int}

//#[deriving(Show)]
pub struct Array3d<T>{
	size:Int3,
	data:Vec<T>,
}

struct Array3dIter<'a,T> {
	src:&'a Array3d<T>,
	i:int,
	j:int,
	k:int,
	begin:Int3,end:Int3

}

fn lin_index_sub(size:Int3, (i,j,k):(int,int,int))->int {
	i+size.i*(j+size.j*k)
}

impl<'a,T> Array3d<T> {
	fn linear_index(&self,ijk:(int,int,int))->int {
		lin_index_sub(self.size, ijk)
	}
	pub fn get(&'a self,ijk:(int,int,int))->&'a T {
		self.data.get(self.linear_index(ijk) as uint)
	}
	pub fn get_mut(&'a mut self,ijk:(int,int,int))->&'a mut T {
		let idx=self.linear_index(ijk);
		self.data.get_mut(idx as uint)
	}
}

impl<'a,T> Array3dIter<'a,T> {
	fn next(&'a self)->Option<((int,int,int),&'a T)> {
		unsafe {
			let mself:&mut Array3dIter<'a,T>= transmute(self);
			if mself.k>=mself.end.k {return None;}
			let ret=((mself.i,mself.j,mself.k), mself.src.get((mself.i,mself.j,mself.k)));
			mself.i+=1;
			if mself.i>=mself.end.i{
				mself.i=mself.begin.i;
				mself.j+=1;
				if mself.j>=mself.end.j{
					mself.j=mself.begin.j;
					mself.k+=1
				}
			}
			return Some(ret);
		}
	}
}

impl<'a, T> Array3d<T> {
	fn iter(&'a self)->Array3dIter<'a,T>{
//		self.iter_range([0,0,0],self.size)
		Array3dIter::<T> {
			i:0,j:0,k:0,
			begin:Int3{i:0,j:0,k:0},
			end:self.size,
			src:self
		}
	}
	fn iter_range(&'a self, from:Int3, to:Int3)->Array3dIter<'a,T> {
		Array3dIter::<T> {
			i:from.i,j:from.j,k:from.k,
			begin:from,
			end:to,
			src:self
		}
	}
}

impl<T:Clone> Array3d<T> {
	fn set_val(&mut self,ijk:(int,int,int),v:&T) {
		*self.get_mut(ijk)=v.clone();
	}
	fn set(&mut self,ijk:(int,int,int),f:||->T) {
		*self.get_mut(ijk)=f();
	}
	fn update<Y>(&mut self, ijk:(int,int,int), f:|&mut T|->Y)->Y {
		f(self.get_mut(ijk))
	}
	fn linear_idx(&self,(i,j,k):(int,int,int))->int{
		(k*self.size.k+j)*self.size.j+i
	}
	fn size(&self)->Int3 {
		self.size
	}
	fn from_fn((si,sj,sk):(int,int,int),generate:|(int,int,int)|->T)->Array3d<T> {
		Array3d::<T>{
			size:Int3{i:si,j:sj,k:sk},
			// the functional syntax of rust is a WHOLE lot more pleasing.
			// todo: low-level, dodging the divides!
			data:Vec::<T>::from_fn(
				(si*sj*sk) as uint,
				|x|{
					let (k,ij)=div_rem(x as uint,(si*sj) as uint);
					let (j,i)=div_rem(ij,si as uint);
					generate((i as int,j as int,k as int))
				}
			)
		}
	}
	fn for_each<R>(&self,
						initial_val:R,
						f:|R,(int,int,int),&T|->R
						)->R {
		let mut acc=initial_val;
		for k in range(0,self.size.k) {
			for j in range(0,self.size.j) {
				for i in range(0,self.size.i) {
					acc=f(acc, (i,j,k), self.get((i,j,k)))
				}
			}
		}
		acc
	}

	fn for_each_mut<R>(&mut self,
						initial_val:R,
						f:|R,(int,int,int),&mut T|->R
						)->R {
		let mut acc=initial_val;
		for k in range(0,self.size.k) {
			for j in range(0,self.size.j) {
				for i in range(0,self.size.i) {
					acc=f(acc, (i,j,k), self.get_mut((i,j,k)))
				}
			}
		}
		acc
	}
}

#[cfg(run)]
fn main() {
	std::io::println("Array3d Test\n");
	let foo=Array3d::from_fn((4,3,2), |(i,j,k)|i+j*10+k*100);
	println!("3d array iterator")
	{
		let mut it=foo.iter();
		loop {
			match it.next() {
				Some(x)=>println!("{}",x),
				None=>break
			}
		}
	}
	println!("foreach ...")
	foo.for_each(0u32,
		|acc,(i,j,k),val|{
			println!("array[{},{},{}]={}",i,j,k,val);
			acc
		}
	);
}
<commit_msg>added mutable tiled array malarchy@<commit_after>use std::num::div_rem;
use std::mem::transmute;
//#[deriving(Show)]
//pub type Int3=[int,..3];
pub struct Int3 {i:int,j:int,k:int}

//#[deriving(Show)]
pub struct Vec3d<T>{
	size:Int3,
	data:Vec<T>,
}

struct Vec3dIter<'a,T> {
	src:&'a Vec3d<T>,
	i:int,
	j:int,
	k:int,
	begin:Int3,end:Int3

}

fn lin_index_sub(size:Int3, (i,j,k):(int,int,int))->int {
	i+size.i*(j+size.j*k)
}

impl<'a,T> Vec3d<T> {
	fn linear_index(&self,ijk:(int,int,int))->int {
		lin_index_sub(self.size, ijk)
	}
	pub fn get(&'a self,ijk:(int,int,int))->&'a T {
		self.data.get(self.linear_index(ijk) as uint)
	}
	pub fn get_mut(&'a mut self,ijk:(int,int,int))->&'a mut T {
		let idx=self.linear_index(ijk);
		self.data.get_mut(idx as uint)
	}
}

impl<'a,T> Vec3dIter<'a,T> {
	fn next(&'a self)->Option<((int,int,int),&'a T)> {
		unsafe {
			let mself:&mut Vec3dIter<'a,T>= transmute(self);
			if mself.k>=mself.end.k {return None;}
			let ret=((mself.i,mself.j,mself.k), mself.src.get((mself.i,mself.j,mself.k)));
			mself.i+=1;
			if mself.i>=mself.end.i{
				mself.i=mself.begin.i;
				mself.j+=1;
				if mself.j>=mself.end.j{
					mself.j=mself.begin.j;
					mself.k+=1
				}
			}
			return Some(ret);
		}
	}
}

type Array4x4x4<T>=[[[T,..4],..4],..4];

// ah, this is as far as we got last time before ..
enum Tile4<T> {
	Fill(T),				// single value across 4x4
	Detail(Box<Array4x4x4<T>>)	// mutated stuff
}

impl<T:Clone+Copy> Clone for Tile4<T> {
	fn clone(&self)->Tile4<T> {
		match *self {
			Fill(ref v)=>Fill(v.clone()),
			Detail(ref b)=>{
				let new_tile=**b;
				Detail(box new_tile )
			}
		}
	}
}

struct TiledArray4<T> {
	tiles:Vec3d<Tile4<T>>,
}
fn fill_array_4<T:Clone>(v:&T)->[T,..4]{ [v.clone(),v.clone(),v.clone(),v.clone()] }
// grr. clone for [T,..4]
// can't impl on intrinsic type, outside of ours.
impl<T:Clone+Copy> TiledArray4<T> {
	fn get<'a>(&'a self,(i,j,k):(int,int,int))->&'a T {
		let (ii,jj,kk)=(i&3,j&3,k&3);
		let tile:&Tile4<T>=self.tiles.get((i/4,j/4,k/4));
		match *tile{
			Fill(ref fill_value)	=> fill_value,
			Detail(ref tile)=> &tile[kk as uint][jj as uint][ii as uint],
		}
	}
	fn set(&mut self, (i,j,k):(int,int,int), new_val:&T) {
		let (ii,jj,kk)=((i&3) as uint,(j&3) as uint,(k&3) as uint);
		let tile:&mut Tile4<T>=self.tiles.get_mut((i/4,j/4,k/4));
	// if the old tile isn't allocated, update it..
		match *tile{
			Detail(ref mut tile)=> {
				tile[kk][jj][ii]=new_val.clone();
				return
			},
			_=>{},
		}

		let old_val=match *tile { Fill(ref old_val)=>old_val.clone(),_=>new_val.clone()};
		let mut new_row:[T,..4]=[old_val, ..4];//fill_array_4(new_val);
		let mut new_rowcol:[[T,..4],..4]=[new_row, ..4];
		let mut new_tile:[[[T,..4],..4],..4]=[new_rowcol, ..4];
		new_tile[kk][jj][ii]=new_val.clone();

		*tile=Detail(box new_tile);
	}
	/// Step through tiles, any that are homogenous are reverted to 'fill'
	fn compact(&mut self) {
	}
	fn from_val((numi,numj,numk):(int,int,int), init_val:&T)->TiledArray4<T> {
		TiledArray4::<T>{
			tiles:Vec3d::<Tile4<T>>::from_fn(((numi+3)/4,(numj+3)/4,(numk+3)/4), |(_,_,_)|Fill(init_val.clone()) )
		}
	}
}

impl<'a, T> Vec3d<T> {
	fn iter(&'a self)->Vec3dIter<'a,T>{
//		self.iter_range([0,0,0],self.size)
		Vec3dIter::<T> {
			i:0,j:0,k:0,
			begin:Int3{i:0,j:0,k:0},
			end:self.size,
			src:self
		}
	}
	fn iter_range(&'a self, from:Int3, to:Int3)->Vec3dIter<'a,T> {
		Vec3dIter::<T> {
			i:from.i,j:from.j,k:from.k,
			begin:from,
			end:to,
			src:self
		}
	}
}

impl<T:Clone> Vec3d<T> {
	fn set_val(&mut self,ijk:(int,int,int),v:&T) {
		*self.get_mut(ijk)=v.clone();
	}
	fn set(&mut self,ijk:(int,int,int),f:||->T) {
		*self.get_mut(ijk)=f();
	}
	fn update<Y>(&mut self, ijk:(int,int,int), f:|&mut T|->Y)->Y {
		f(self.get_mut(ijk))
	}
	fn linear_idx(&self,(i,j,k):(int,int,int))->int{
		(k*self.size.k+j)*self.size.j+i
	}
	fn size(&self)->Int3 {
		self.size
	}
	fn from_fn((si,sj,sk):(int,int,int),generate:|(int,int,int)|->T)->Vec3d<T> {
		Vec3d::<T>{
			size:Int3{i:si,j:sj,k:sk},
			// the functional syntax of rust is a WHOLE lot more pleasing.
			// todo: low-level, dodging the divides!
			data:Vec::<T>::from_fn(
				(si*sj*sk) as uint,
				|x|{
					let (k,ij)=div_rem(x as uint,(si*sj) as uint);
					let (j,i)=div_rem(ij,si as uint);
					generate((i as int,j as int,k as int))
				}
			)
		}
	}
	fn for_each<R>(&self,
						initial_val:R,
						f:|R,(int,int,int),&T|->R
						)->R {
		let mut acc=initial_val;
		for k in range(0,self.size.k) {
			for j in range(0,self.size.j) {
				for i in range(0,self.size.i) {
					acc=f(acc, (i,j,k), self.get((i,j,k)))
				}
			}
		}
		acc
	}

	fn for_each_mut<R>(&mut self,
						initial_val:R,
						f:|R,(int,int,int),&mut T|->R
						)->R {
		let mut acc=initial_val;
		for k in range(0,self.size.k) {
			for j in range(0,self.size.j) {
				for i in range(0,self.size.i) {
					acc=f(acc, (i,j,k), self.get_mut((i,j,k)))
				}
			}
		}
		acc
	}
}

#[cfg(run)]
fn main() {
	std::io::println("Array3d Test\n");
	let foo=Vec3d::from_fn((4,3,2), |(i,j,k)|i+j*10+k*100);
	println!("3d array iterator")
	{
		let mut it=foo.iter();
		loop {
			match it.next() {
				Some(x)=>println!("{}",x),
				None=>break
			}
		}
	}
	println!("foreach ...")
	foo.for_each(0u32,
		|acc,(i,j,k),val|{
			println!("array[{},{},{}]={}",i,j,k,val);
			acc
		}
	);
	let mut ta= TiledArray4::<int>::from_val((8,4,4),&0i);
	ta.set((1,3,2),&999);
	ta.set((6,2,1),&777);

	for k in range(0,4){
		for j in range(0,4){
			print!("(j,k)={}{} ",j,k);
			for i in range(0,8){
				print!("{} ", ta.get((i,j,k)));
			}
			println!("----");
		}
	}
}

<|endoftext|>


<commit_before>//! ra_ide_api crate provides "ide-centric" APIs for the rust-analyzer. That is,
//! it generally operates with files and text ranges, and returns results as
//! Strings, suitable for displaying to the human.
//!
//! What powers this API are the `RootDatabase` struct, which defines a `salsa`
//! database, and the `ra_hir` crate, where majority of the analysis happens.
//! However, IDE specific bits of the analysis (most notably completion) happen
//! in this crate.
//!
//! The sibling `ra_ide_api_light` handles those bits of IDE functionality
//! which are restricted to a single file and need only syntax.

// For proving that RootDatabase is RefUnwindSafe.
#![recursion_limit = "128"]

mod db;
pub mod mock_analysis;
mod symbol_index;
mod navigation_target;
mod change;

mod status;
mod completion;
mod runnables;
mod goto_definition;
mod extend_selection;
mod hover;
mod call_info;
mod syntax_highlighting;
mod parent_module;
mod references;
mod impls;
mod assists;
mod diagnostics;

#[cfg(test)]
mod marks;

use std::sync::Arc;

use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit};
use ra_text_edit::TextEdit;
use ra_db::{
    SourceDatabase, CheckCanceled,
    salsa::{self, ParallelDatabase},
};
use relative_path::RelativePathBuf;

use crate::{
    symbol_index::FileSymbol,
    db::LineIndexDatabase,
};

pub use crate::{
    change::{AnalysisChange, LibraryData},
    completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
    runnables::{Runnable, RunnableKind},
    navigation_target::NavigationTarget,
};
pub use ra_ide_api_light::{
    Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit,
    LineIndex, LineCol, translate_offset_with_edit,
};
pub use ra_db::{
    Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
    Edition
};
pub use hir::Documentation;

// We use jemalloc mainly to get heap usage statistics, actual performance
// difference is not measures.
#[cfg(feature = "jemalloc")]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

pub type Cancelable<T> = Result<T, Canceled>;

#[derive(Debug)]
pub struct SourceChange {
    pub label: String,
    pub source_file_edits: Vec<SourceFileEdit>,
    pub file_system_edits: Vec<FileSystemEdit>,
    pub cursor_position: Option<FilePosition>,
}

#[derive(Debug)]
pub struct SourceFileEdit {
    pub file_id: FileId,
    pub edit: TextEdit,
}

#[derive(Debug)]
pub enum FileSystemEdit {
    CreateFile { source_root: SourceRootId, path: RelativePathBuf },
    MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
}

#[derive(Debug)]
pub struct Diagnostic {
    pub message: String,
    pub range: TextRange,
    pub fix: Option<SourceChange>,
    pub severity: Severity,
}

#[derive(Debug)]
pub struct Query {
    query: String,
    lowercased: String,
    only_types: bool,
    libs: bool,
    exact: bool,
    limit: usize,
}

impl Query {
    pub fn new(query: String) -> Query {
        let lowercased = query.to_lowercase();
        Query {
            query,
            lowercased,
            only_types: false,
            libs: false,
            exact: false,
            limit: usize::max_value(),
        }
    }

    pub fn only_types(&mut self) {
        self.only_types = true;
    }

    pub fn libs(&mut self) {
        self.libs = true;
    }

    pub fn exact(&mut self) {
        self.exact = true;
    }

    pub fn limit(&mut self, limit: usize) {
        self.limit = limit
    }
}

#[derive(Debug)]
pub struct RangeInfo<T> {
    pub range: TextRange,
    pub info: T,
}

impl<T> RangeInfo<T> {
    pub fn new(range: TextRange, info: T) -> RangeInfo<T> {
        RangeInfo { range, info }
    }
}

#[derive(Debug)]
pub struct CallInfo {
    pub label: String,
    pub doc: Option<Documentation>,
    pub parameters: Vec<String>,
    pub active_parameter: Option<usize>,
}

/// `AnalysisHost` stores the current state of the world.
#[derive(Debug, Default)]
pub struct AnalysisHost {
    db: db::RootDatabase,
}

impl AnalysisHost {
    /// Returns a snapshot of the current state, which you can query for
    /// semantic information.
    pub fn analysis(&self) -> Analysis {
        Analysis { db: self.db.snapshot() }
    }

    /// Applies changes to the current state of the world. If there are
    /// outstanding snapshots, they will be canceled.
    pub fn apply_change(&mut self, change: AnalysisChange) {
        self.db.apply_change(change)
    }

    pub fn maybe_collect_garbage(&mut self) {
        self.db.maybe_collect_garbage();
    }

    pub fn collect_garbage(&mut self) {
        self.db.collect_garbage();
    }
}

/// Analysis is a snapshot of a world state at a moment in time. It is the main
/// entry point for asking semantic information about the world. When the world
/// state is advanced using `AnalysisHost::apply_change` method, all existing
/// `Analysis` are canceled (most method return `Err(Canceled)`).
#[derive(Debug)]
pub struct Analysis {
    db: salsa::Snapshot<db::RootDatabase>,
}

impl Analysis {
    /// Debug info about the current state of the analysis
    pub fn status(&self) -> String {
        status::status(&*self.db)
    }

    /// Gets the text of the source file.
    pub fn file_text(&self, file_id: FileId) -> Arc<String> {
        self.db.file_text(file_id)
    }

    /// Gets the syntax tree of the file.
    pub fn parse(&self, file_id: FileId) -> TreeArc<SourceFile> {
        self.db.parse(file_id).clone()
    }

    /// Gets the file's `LineIndex`: data structure to convert between absolute
    /// offsets and line/column representation.
    pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
        self.db.line_index(file_id)
    }

    /// Selects the next syntactic nodes encompassing the range.
    pub fn extend_selection(&self, frange: FileRange) -> Cancelable<TextRange> {
        self.with_db(|db| extend_selection::extend_selection(db, frange))
    }

    /// Returns position of the matching brace (all types of braces are
    /// supported).
    pub fn matching_brace(&self, position: FilePosition) -> Option<TextUnit> {
        let file = self.db.parse(position.file_id);
        ra_ide_api_light::matching_brace(&file, position.offset)
    }

    /// Returns a syntax tree represented as `String`, for debug purposes.
    // FIXME: use a better name here.
    pub fn syntax_tree(&self, file_id: FileId) -> String {
        let file = self.db.parse(file_id);
        ra_ide_api_light::syntax_tree(&file)
    }

    /// Returns an edit to remove all newlines in the range, cleaning up minor
    /// stuff like trailing commas.
    pub fn join_lines(&self, frange: FileRange) -> SourceChange {
        let file = self.db.parse(frange.file_id);
        SourceChange::from_local_edit(
            frange.file_id,
            ra_ide_api_light::join_lines(&file, frange.range),
        )
    }

    /// Returns an edit which should be applied when opening a new line, fixing
    /// up minor stuff like continuing the comment.
    pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
        let file = self.db.parse(position.file_id);
        let edit = ra_ide_api_light::on_enter(&file, position.offset)?;
        Some(SourceChange::from_local_edit(position.file_id, edit))
    }

    /// Returns an edit which should be applied after `=` was typed. Primarily,
    /// this works when adding `let =`.
    // FIXME: use a snippet completion instead of this hack here.
    pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
        let file = self.db.parse(position.file_id);
        let edit = ra_ide_api_light::on_eq_typed(&file, position.offset)?;
        Some(SourceChange::from_local_edit(position.file_id, edit))
    }

    /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
    pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> {
        let file = self.db.parse(position.file_id);
        let edit = ra_ide_api_light::on_dot_typed(&file, position.offset)?;
        Some(SourceChange::from_local_edit(position.file_id, edit))
    }

    /// Returns a tree representation of symbols in the file. Useful to draw a
    /// file outline.
    pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
        let file = self.db.parse(file_id);
        ra_ide_api_light::file_structure(&file)
    }

    /// Returns the set of folding ranges.
    pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
        let file = self.db.parse(file_id);
        ra_ide_api_light::folding_ranges(&file)
    }

    /// Fuzzy searches for a symbol.
    pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
        self.with_db(|db| {
            symbol_index::world_symbols(db, query)
                .into_iter()
                .map(NavigationTarget::from_symbol)
                .collect::<Vec<_>>()
        })
    }

    pub fn goto_definition(
        &self,
        position: FilePosition,
    ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
        self.with_db(|db| goto_definition::goto_definition(db, position))
    }

    pub fn goto_implementation(
        &self,
        position: FilePosition,
    ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
        self.with_db(|db| impls::goto_implementation(db, position))
    }

    /// Finds all usages of the reference at point.
    pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
        self.with_db(|db| references::find_all_refs(db, position))
    }

    /// Returns a short text describing element at position.
    pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> {
        self.with_db(|db| hover::hover(db, position))
    }

    /// Computes parameter information for the given call expression.
    pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> {
        self.with_db(|db| call_info::call_info(db, position))
    }

    /// Returns a `mod name;` declaration which created the current module.
    pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
        self.with_db(|db| parent_module::parent_module(db, position))
    }

    /// Returns crates this file belongs too.
    pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
        self.with_db(|db| parent_module::crate_for(db, file_id))
    }

    /// Returns the root file of the given crate.
    pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
        self.with_db(|db| db.crate_graph().crate_root(crate_id))
    }

    /// Returns the set of possible targets to run for the current file.
    pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
        self.with_db(|db| runnables::runnables(db, file_id))
    }

    /// Computes syntax highlighting for the given file.
    pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
        self.with_db(|db| syntax_highlighting::highlight(db, file_id))
    }

    /// Computes completions at the given position.
    pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
        self.with_db(|db| completion::completions(db, position).map(Into::into))
    }

    /// Computes assists (aks code actons aka intentions) for the given
    /// position.
    pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
        self.with_db(|db| assists::assists(db, frange))
    }

    /// Computes the set of diagnostics for the given file.
    pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
        self.with_db(|db| diagnostics::diagnostics(db, file_id))
    }

    /// Computes the type of the expression at the given position.
    pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
        self.with_db(|db| hover::type_of(db, frange))
    }

    /// Returns the edit required to rename reference at the position to the new
    /// name.
    pub fn rename(
        &self,
        position: FilePosition,
        new_name: &str,
    ) -> Cancelable<Option<SourceChange>> {
        self.with_db(|db| references::rename(db, position, new_name))
    }

    fn with_db<F: FnOnce(&db::RootDatabase) -> T + std::panic::UnwindSafe, T>(
        &self,
        f: F,
    ) -> Cancelable<T> {
        self.db.catch_canceled(f)
    }
}

impl SourceChange {
    pub(crate) fn from_local_edit(file_id: FileId, edit: LocalEdit) -> SourceChange {
        let file_edit = SourceFileEdit { file_id, edit: edit.edit };
        SourceChange {
            label: edit.label,
            source_file_edits: vec![file_edit],
            file_system_edits: vec![],
            cursor_position: edit.cursor_position.map(|offset| FilePosition { offset, file_id }),
        }
    }
}

#[test]
fn analysis_is_send() {
    fn is_send<T: Send>() {}
    is_send::<Analysis>();
}
<commit_msg>document design guideline<commit_after>//! ra_ide_api crate provides "ide-centric" APIs for the rust-analyzer. That is,
//! it generally operates with files and text ranges, and returns results as
//! Strings, suitable for displaying to the human.
//!
//! What powers this API are the `RootDatabase` struct, which defines a `salsa`
//! database, and the `ra_hir` crate, where majority of the analysis happens.
//! However, IDE specific bits of the analysis (most notably completion) happen
//! in this crate.
//!
//! The sibling `ra_ide_api_light` handles those bits of IDE functionality
//! which are restricted to a single file and need only syntax.

// For proving that RootDatabase is RefUnwindSafe.
#![recursion_limit = "128"]

mod db;
pub mod mock_analysis;
mod symbol_index;
mod navigation_target;
mod change;

mod status;
mod completion;
mod runnables;
mod goto_definition;
mod extend_selection;
mod hover;
mod call_info;
mod syntax_highlighting;
mod parent_module;
mod references;
mod impls;
mod assists;
mod diagnostics;

#[cfg(test)]
mod marks;

use std::sync::Arc;

use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit};
use ra_text_edit::TextEdit;
use ra_db::{
    SourceDatabase, CheckCanceled,
    salsa::{self, ParallelDatabase},
};
use relative_path::RelativePathBuf;

use crate::{
    symbol_index::FileSymbol,
    db::LineIndexDatabase,
};

pub use crate::{
    change::{AnalysisChange, LibraryData},
    completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
    runnables::{Runnable, RunnableKind},
    navigation_target::NavigationTarget,
};
pub use ra_ide_api_light::{
    Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit,
    LineIndex, LineCol, translate_offset_with_edit,
};
pub use ra_db::{
    Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId,
    Edition
};
pub use hir::Documentation;

// We use jemalloc mainly to get heap usage statistics, actual performance
// difference is not measures.
#[cfg(feature = "jemalloc")]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

pub type Cancelable<T> = Result<T, Canceled>;

#[derive(Debug)]
pub struct SourceChange {
    pub label: String,
    pub source_file_edits: Vec<SourceFileEdit>,
    pub file_system_edits: Vec<FileSystemEdit>,
    pub cursor_position: Option<FilePosition>,
}

#[derive(Debug)]
pub struct SourceFileEdit {
    pub file_id: FileId,
    pub edit: TextEdit,
}

#[derive(Debug)]
pub enum FileSystemEdit {
    CreateFile { source_root: SourceRootId, path: RelativePathBuf },
    MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf },
}

#[derive(Debug)]
pub struct Diagnostic {
    pub message: String,
    pub range: TextRange,
    pub fix: Option<SourceChange>,
    pub severity: Severity,
}

#[derive(Debug)]
pub struct Query {
    query: String,
    lowercased: String,
    only_types: bool,
    libs: bool,
    exact: bool,
    limit: usize,
}

impl Query {
    pub fn new(query: String) -> Query {
        let lowercased = query.to_lowercase();
        Query {
            query,
            lowercased,
            only_types: false,
            libs: false,
            exact: false,
            limit: usize::max_value(),
        }
    }

    pub fn only_types(&mut self) {
        self.only_types = true;
    }

    pub fn libs(&mut self) {
        self.libs = true;
    }

    pub fn exact(&mut self) {
        self.exact = true;
    }

    pub fn limit(&mut self, limit: usize) {
        self.limit = limit
    }
}

#[derive(Debug)]
pub struct RangeInfo<T> {
    pub range: TextRange,
    pub info: T,
}

impl<T> RangeInfo<T> {
    pub fn new(range: TextRange, info: T) -> RangeInfo<T> {
        RangeInfo { range, info }
    }
}

#[derive(Debug)]
pub struct CallInfo {
    pub label: String,
    pub doc: Option<Documentation>,
    pub parameters: Vec<String>,
    pub active_parameter: Option<usize>,
}

/// `AnalysisHost` stores the current state of the world.
#[derive(Debug, Default)]
pub struct AnalysisHost {
    db: db::RootDatabase,
}

impl AnalysisHost {
    /// Returns a snapshot of the current state, which you can query for
    /// semantic information.
    pub fn analysis(&self) -> Analysis {
        Analysis { db: self.db.snapshot() }
    }

    /// Applies changes to the current state of the world. If there are
    /// outstanding snapshots, they will be canceled.
    pub fn apply_change(&mut self, change: AnalysisChange) {
        self.db.apply_change(change)
    }

    pub fn maybe_collect_garbage(&mut self) {
        self.db.maybe_collect_garbage();
    }

    pub fn collect_garbage(&mut self) {
        self.db.collect_garbage();
    }
}

/// Analysis is a snapshot of a world state at a moment in time. It is the main
/// entry point for asking semantic information about the world. When the world
/// state is advanced using `AnalysisHost::apply_change` method, all existing
/// `Analysis` are canceled (most method return `Err(Canceled)`).
#[derive(Debug)]
pub struct Analysis {
    db: salsa::Snapshot<db::RootDatabase>,
}

// As a general design guideline, `Analysis` API are intended to be independent
// from the language server protocol. That is, when exposing some functionality
// we should think in terms of "what API makes most sense" and not in terms of
// "what types LSP uses". Although currently LSP is the only consumer of the
// API, the API should in theory be usable as a library, or via a different
// protocol.
impl Analysis {
    /// Debug info about the current state of the analysis
    pub fn status(&self) -> String {
        status::status(&*self.db)
    }

    /// Gets the text of the source file.
    pub fn file_text(&self, file_id: FileId) -> Arc<String> {
        self.db.file_text(file_id)
    }

    /// Gets the syntax tree of the file.
    pub fn parse(&self, file_id: FileId) -> TreeArc<SourceFile> {
        self.db.parse(file_id).clone()
    }

    /// Gets the file's `LineIndex`: data structure to convert between absolute
    /// offsets and line/column representation.
    pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
        self.db.line_index(file_id)
    }

    /// Selects the next syntactic nodes encompassing the range.
    pub fn extend_selection(&self, frange: FileRange) -> Cancelable<TextRange> {
        self.with_db(|db| extend_selection::extend_selection(db, frange))
    }

    /// Returns position of the matching brace (all types of braces are
    /// supported).
    pub fn matching_brace(&self, position: FilePosition) -> Option<TextUnit> {
        let file = self.db.parse(position.file_id);
        ra_ide_api_light::matching_brace(&file, position.offset)
    }

    /// Returns a syntax tree represented as `String`, for debug purposes.
    // FIXME: use a better name here.
    pub fn syntax_tree(&self, file_id: FileId) -> String {
        let file = self.db.parse(file_id);
        ra_ide_api_light::syntax_tree(&file)
    }

    /// Returns an edit to remove all newlines in the range, cleaning up minor
    /// stuff like trailing commas.
    pub fn join_lines(&self, frange: FileRange) -> SourceChange {
        let file = self.db.parse(frange.file_id);
        SourceChange::from_local_edit(
            frange.file_id,
            ra_ide_api_light::join_lines(&file, frange.range),
        )
    }

    /// Returns an edit which should be applied when opening a new line, fixing
    /// up minor stuff like continuing the comment.
    pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
        let file = self.db.parse(position.file_id);
        let edit = ra_ide_api_light::on_enter(&file, position.offset)?;
        Some(SourceChange::from_local_edit(position.file_id, edit))
    }

    /// Returns an edit which should be applied after `=` was typed. Primarily,
    /// this works when adding `let =`.
    // FIXME: use a snippet completion instead of this hack here.
    pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
        let file = self.db.parse(position.file_id);
        let edit = ra_ide_api_light::on_eq_typed(&file, position.offset)?;
        Some(SourceChange::from_local_edit(position.file_id, edit))
    }

    /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
    pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> {
        let file = self.db.parse(position.file_id);
        let edit = ra_ide_api_light::on_dot_typed(&file, position.offset)?;
        Some(SourceChange::from_local_edit(position.file_id, edit))
    }

    /// Returns a tree representation of symbols in the file. Useful to draw a
    /// file outline.
    pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
        let file = self.db.parse(file_id);
        ra_ide_api_light::file_structure(&file)
    }

    /// Returns the set of folding ranges.
    pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
        let file = self.db.parse(file_id);
        ra_ide_api_light::folding_ranges(&file)
    }

    /// Fuzzy searches for a symbol.
    pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> {
        self.with_db(|db| {
            symbol_index::world_symbols(db, query)
                .into_iter()
                .map(NavigationTarget::from_symbol)
                .collect::<Vec<_>>()
        })
    }

    pub fn goto_definition(
        &self,
        position: FilePosition,
    ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
        self.with_db(|db| goto_definition::goto_definition(db, position))
    }

    pub fn goto_implementation(
        &self,
        position: FilePosition,
    ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
        self.with_db(|db| impls::goto_implementation(db, position))
    }

    /// Finds all usages of the reference at point.
    pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
        self.with_db(|db| references::find_all_refs(db, position))
    }

    /// Returns a short text describing element at position.
    pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> {
        self.with_db(|db| hover::hover(db, position))
    }

    /// Computes parameter information for the given call expression.
    pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> {
        self.with_db(|db| call_info::call_info(db, position))
    }

    /// Returns a `mod name;` declaration which created the current module.
    pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> {
        self.with_db(|db| parent_module::parent_module(db, position))
    }

    /// Returns crates this file belongs too.
    pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
        self.with_db(|db| parent_module::crate_for(db, file_id))
    }

    /// Returns the root file of the given crate.
    pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
        self.with_db(|db| db.crate_graph().crate_root(crate_id))
    }

    /// Returns the set of possible targets to run for the current file.
    pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
        self.with_db(|db| runnables::runnables(db, file_id))
    }

    /// Computes syntax highlighting for the given file.
    pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
        self.with_db(|db| syntax_highlighting::highlight(db, file_id))
    }

    /// Computes completions at the given position.
    pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
        self.with_db(|db| completion::completions(db, position).map(Into::into))
    }

    /// Computes assists (aks code actons aka intentions) for the given
    /// position.
    pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
        self.with_db(|db| assists::assists(db, frange))
    }

    /// Computes the set of diagnostics for the given file.
    pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
        self.with_db(|db| diagnostics::diagnostics(db, file_id))
    }

    /// Computes the type of the expression at the given position.
    pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
        self.with_db(|db| hover::type_of(db, frange))
    }

    /// Returns the edit required to rename reference at the position to the new
    /// name.
    pub fn rename(
        &self,
        position: FilePosition,
        new_name: &str,
    ) -> Cancelable<Option<SourceChange>> {
        self.with_db(|db| references::rename(db, position, new_name))
    }

    fn with_db<F: FnOnce(&db::RootDatabase) -> T + std::panic::UnwindSafe, T>(
        &self,
        f: F,
    ) -> Cancelable<T> {
        self.db.catch_canceled(f)
    }
}

impl SourceChange {
    pub(crate) fn from_local_edit(file_id: FileId, edit: LocalEdit) -> SourceChange {
        let file_edit = SourceFileEdit { file_id, edit: edit.edit };
        SourceChange {
            label: edit.label,
            source_file_edits: vec![file_edit],
            file_system_edits: vec![],
            cursor_position: edit.cursor_position.map(|offset| FilePosition { offset, file_id }),
        }
    }
}

#[test]
fn analysis_is_send() {
    fn is_send<T: Send>() {}
    is_send::<Analysis>();
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests;

use crate::fmt;
use crate::sync::{Condvar, Mutex};

/// A barrier enables multiple threads to synchronize the beginning
/// of some computation.
///
/// # Examples
///
/// ```
/// use std::sync::{Arc, Barrier};
/// use std::thread;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
///     let c = Arc::clone(&barrier);
///     // The same messages will be printed together.
///     // You will NOT see any interleaving.
///     handles.push(thread::spawn(move|| {
///         println!("before wait");
///         c.wait();
///         println!("after wait");
///     }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
///     handle.join().unwrap();
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Barrier {
    lock: Mutex<BarrierState>,
    cvar: Condvar,
    num_threads: usize,
}

// The inner state of a double barrier
struct BarrierState {
    count: usize,
    generation_id: usize,
}

/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
/// in the [`Barrier`] have rendezvoused.
///
/// # Examples
///
/// ```
/// use std::sync::Barrier;
///
/// let barrier = Barrier::new(1);
/// let barrier_wait_result = barrier.wait();
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct BarrierWaitResult(bool);

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

impl Barrier {
    /// Creates a new barrier that can block a given number of threads.
    ///
    /// A barrier will block `n`-1 threads which call [`wait()`] and then wake
    /// up all threads at once when the `n`th thread calls [`wait()`].
    ///
    /// [`wait()`]: Barrier::wait
    ///
    /// # Examples
    ///
    /// ```
    /// use std::sync::Barrier;
    ///
    /// let barrier = Barrier::new(10);
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn new(n: usize) -> Barrier {
        Barrier {
            lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }),
            cvar: Condvar::new(),
            num_threads: n,
        }
    }

    /// Blocks the current thread until all threads have rendezvoused here.
    ///
    /// Barriers are re-usable after all threads have rendezvoused once, and can
    /// be used continuously.
    ///
    /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
    /// returns `true` from [`is_leader`] when returning from this function, and
    /// all other threads will receive a result that will return `false` from
    /// [`BarrierWaitResult::is_leader`].
    ///
    /// # Examples
    ///
    /// ```
    /// use std::sync::{Arc, Barrier};
    /// use std::thread;
    ///
    /// let mut handles = Vec::with_capacity(10);
    /// let barrier = Arc::new(Barrier::new(10));
    /// for _ in 0..10 {
    ///     let c = Arc::clone(&barrier);
    ///     // The same messages will be printed together.
    ///     // You will NOT see any interleaving.
    ///     handles.push(thread::spawn(move|| {
    ///         println!("before wait");
    ///         c.wait();
    ///         println!("after wait");
    ///     }));
    /// }
    /// // Wait for other threads to finish.
    /// for handle in handles {
    ///     handle.join().unwrap();
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn wait(&self) -> BarrierWaitResult {
        let mut lock = self.lock.lock().unwrap();
        let local_gen = lock.generation_id;
        lock.count += 1;
        if lock.count < self.num_threads {
            // We need a while loop to guard against spurious wakeups.
            // https://en.wikipedia.org/wiki/Spurious_wakeup
            while local_gen == lock.generation_id && lock.count < self.num_threads {
                lock = self.cvar.wait(lock).unwrap();
            }
            BarrierWaitResult(false)
        } else {
            lock.count = 0;
            lock.generation_id = lock.generation_id.wrapping_add(1);
            self.cvar.notify_all();
            BarrierWaitResult(true)
        }
    }
}

#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for BarrierWaitResult {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish()
    }
}

impl BarrierWaitResult {
    /// Returns `true` if this thread from [`Barrier::wait()`] is the
    /// "leader thread".
    ///
    /// Only one thread will have `true` returned from their result, all other
    /// threads will have `false` returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::sync::Barrier;
    ///
    /// let barrier = Barrier::new(1);
    /// let barrier_wait_result = barrier.wait();
    /// println!("{:?}", barrier_wait_result.is_leader());
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn is_leader(&self) -> bool {
        self.0
    }
}
<commit_msg>Fix broken link<commit_after>#[cfg(test)]
mod tests;

use crate::fmt;
use crate::sync::{Condvar, Mutex};

/// A barrier enables multiple threads to synchronize the beginning
/// of some computation.
///
/// # Examples
///
/// ```
/// use std::sync::{Arc, Barrier};
/// use std::thread;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
///     let c = Arc::clone(&barrier);
///     // The same messages will be printed together.
///     // You will NOT see any interleaving.
///     handles.push(thread::spawn(move|| {
///         println!("before wait");
///         c.wait();
///         println!("after wait");
///     }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
///     handle.join().unwrap();
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Barrier {
    lock: Mutex<BarrierState>,
    cvar: Condvar,
    num_threads: usize,
}

// The inner state of a double barrier
struct BarrierState {
    count: usize,
    generation_id: usize,
}

/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
/// in the [`Barrier`] have rendezvoused.
///
/// # Examples
///
/// ```
/// use std::sync::Barrier;
///
/// let barrier = Barrier::new(1);
/// let barrier_wait_result = barrier.wait();
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct BarrierWaitResult(bool);

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

impl Barrier {
    /// Creates a new barrier that can block a given number of threads.
    ///
    /// A barrier will block `n`-1 threads which call [`wait()`] and then wake
    /// up all threads at once when the `n`th thread calls [`wait()`].
    ///
    /// [`wait()`]: Barrier::wait
    ///
    /// # Examples
    ///
    /// ```
    /// use std::sync::Barrier;
    ///
    /// let barrier = Barrier::new(10);
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn new(n: usize) -> Barrier {
        Barrier {
            lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }),
            cvar: Condvar::new(),
            num_threads: n,
        }
    }

    /// Blocks the current thread until all threads have rendezvoused here.
    ///
    /// Barriers are re-usable after all threads have rendezvoused once, and can
    /// be used continuously.
    ///
    /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
    /// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
    /// from this function, and all other threads will receive a result that
    /// will return `false` from [`BarrierWaitResult::is_leader()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use std::sync::{Arc, Barrier};
    /// use std::thread;
    ///
    /// let mut handles = Vec::with_capacity(10);
    /// let barrier = Arc::new(Barrier::new(10));
    /// for _ in 0..10 {
    ///     let c = Arc::clone(&barrier);
    ///     // The same messages will be printed together.
    ///     // You will NOT see any interleaving.
    ///     handles.push(thread::spawn(move|| {
    ///         println!("before wait");
    ///         c.wait();
    ///         println!("after wait");
    ///     }));
    /// }
    /// // Wait for other threads to finish.
    /// for handle in handles {
    ///     handle.join().unwrap();
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn wait(&self) -> BarrierWaitResult {
        let mut lock = self.lock.lock().unwrap();
        let local_gen = lock.generation_id;
        lock.count += 1;
        if lock.count < self.num_threads {
            // We need a while loop to guard against spurious wakeups.
            // https://en.wikipedia.org/wiki/Spurious_wakeup
            while local_gen == lock.generation_id && lock.count < self.num_threads {
                lock = self.cvar.wait(lock).unwrap();
            }
            BarrierWaitResult(false)
        } else {
            lock.count = 0;
            lock.generation_id = lock.generation_id.wrapping_add(1);
            self.cvar.notify_all();
            BarrierWaitResult(true)
        }
    }
}

#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for BarrierWaitResult {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish()
    }
}

impl BarrierWaitResult {
    /// Returns `true` if this thread from [`Barrier::wait()`] is the
    /// "leader thread".
    ///
    /// Only one thread will have `true` returned from their result, all other
    /// threads will have `false` returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::sync::Barrier;
    ///
    /// let barrier = Barrier::new(1);
    /// let barrier_wait_result = barrier.wait();
    /// println!("{:?}", barrier_wait_result.is_leader());
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn is_leader(&self) -> bool {
        self.0
    }
}
<|endoftext|>


<commit_before><commit_msg>Reject invalid repeats, rather than accepting them silently<commit_after><|endoftext|>