<commit_before>#![feature(test)]
extern crate test;

#[macro_use]
extern crate nom;

use nom::{IResult,not_line_ending, space, alphanumeric, multispace};

use std::str;
use std::collections::HashMap;

named!(category<&str>, map_res!(
    terminated!(
        delimited!(tag!("["), take_until!("]"), tag!("]")),
        opt!(multispace)
    ),
    str::from_utf8
));

named!(key_value    <&[u8],(&str,&str)>,
  do_parse!(
    key: map_res!(alphanumeric, std::str::from_utf8) >>
         opt!(space)                                 >>
         tag!("=")                                   >>
         opt!(space)                                 >>
    val: map_res!(
           take_until_either!("\n;"),
           str::from_utf8
         )                                           >>
         opt!(space)                                 >>
         opt!(do_parse!(
           tag!(";")                                 >>
           not_line_ending                           >>
           ()
         ))                                          >>
         opt!(multispace)                            >>
    (key, val)
  )
);

named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      IResult::Done(i, tuple_vec.into_iter().collect())
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
  do_parse!(
    category: category    >>
    keys: keys_and_values >>
    (category, keys)
  )
);

named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      IResult::Done(i, tuple_vec.into_iter().collect())
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_category_test() {
  let ini_file = &b"[category]

parameter=value
key = value2"[..];

  let ini_without_category = &b"parameter=value
key = value2"[..];

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_category, "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = &b"parameter=value
key = value2"[..];

  let ini_without_key_value = &b"key = value2"[..];

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = &b"parameter = value
key = value2"[..];

  let ini_without_key_value = &b"key = value2"[..];

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = &b"parameter=value;abc
key = value2"[..];

  let ini_without_key_value = &b"key = value2"[..];

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = &b"parameter=value;abc

key = value2

[category]"[..];

  let ini_without_key_value = &b"[category]"[..];

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = &b"[abcd]
parameter=value;abc

key = value2

[category]"[..];

  let ini_after_parser = &b"[category]"[..];

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = &b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
"[..];

  let ini_after_parser = &b""[..];

  let res = categories(ini_file);
  //println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, IResult::Done(ini_after_parser, expected_h));
}

//#[cfg(feature = "test")]
#[bench]
fn bench_ini(b: &mut test::Bencher) {
  let str = "[owner]
name=John Doe
organization=Acme Widgets Inc.

[database]
server=192.0.2.62
port=143
file=payroll.dat
";

  b.iter(|| categories(str.as_bytes()).unwrap());
  b.bytes = str.len() as u64;
}
<commit_msg>add more individual benchmarks<commit_after>#![feature(test)]
extern crate test;

#[macro_use]
extern crate nom;

use nom::{IResult,not_line_ending, space, alphanumeric, multispace};

use std::str;
use std::collections::HashMap;

named!(category<&str>, map_res!(
    terminated!(
        delimited!(tag!("["), take_until!("]"), tag!("]")),
        opt!(multispace)
    ),
    str::from_utf8
));

named!(key_value    <&[u8],(&str,&str)>,
  do_parse!(
    key: map_res!(alphanumeric, std::str::from_utf8) >>
         opt!(space)                                 >>
         tag!("=")                                   >>
         opt!(space)                                 >>
    val: map_res!(
           take_until_either!("\n;"),
           str::from_utf8
         )                                           >>
         opt!(space)                                 >>
         opt!(do_parse!(
           tag!(";")                                 >>
           not_line_ending                           >>
           ()
         ))                                          >>
         opt!(multispace)                            >>
    (key, val)
  )
);

named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      IResult::Done(i, tuple_vec.into_iter().collect())
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
  do_parse!(
    category: category    >>
    keys: keys_and_values >>
    (category, keys)
  )
);

named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      IResult::Done(i, tuple_vec.into_iter().collect())
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_category_test() {
  let ini_file = &b"[category]

parameter=value
key = value2"[..];

  let ini_without_category = &b"parameter=value
key = value2"[..];

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_category, "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = &b"parameter=value
key = value2"[..];

  let ini_without_key_value = &b"key = value2"[..];

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = &b"parameter = value
key = value2"[..];

  let ini_without_key_value = &b"key = value2"[..];

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = &b"parameter=value;abc
key = value2"[..];

  let ini_without_key_value = &b"key = value2"[..];

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = &b"parameter=value;abc

key = value2

[category]"[..];

  let ini_without_key_value = &b"[category]"[..];

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = &b"[abcd]
parameter=value;abc

key = value2

[category]"[..];

  let ini_after_parser = &b"[category]"[..];

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = &b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
"[..];

  let ini_after_parser = &b""[..];

  let res = categories(ini_file);
  //println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, IResult::Done(ini_after_parser, expected_h));
}

#[bench]
fn bench_ini(b: &mut test::Bencher) {
  let str = "[owner]
name=John Doe
organization=Acme Widgets Inc.

[database]
server=192.0.2.62
port=143
file=payroll.dat
";

  b.iter(|| categories(str.as_bytes()).unwrap());
  b.bytes = str.len() as u64;
}

#[bench]
fn bench_ini_keys_and_values(b: &mut test::Bencher) {
  let str = "server=192.0.2.62
port=143
file=payroll.dat
";

  b.iter(|| keys_and_values_aggregator(str.as_bytes()).unwrap());
  b.bytes = str.len() as u64;
}

#[bench]
fn bench_ini_key_value(b: &mut test::Bencher) {
  let str = "server=192.0.2.62\n";

  b.iter(|| key_value(str.as_bytes()).unwrap());
  b.bytes = str.len() as u64;
}
<|endoftext|>


<commit_before>//! Shared settings module.
//!
//! This module defines data structures to access the settings defined in the meta language.
//!
//! Each settings group is translated to a `Flags` struct either in this module or in its
//! ISA-specific `settings` module. The struct provides individual getter methods for all of the
//! settings as well as computed predicate flags.
//!
//! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to
//! create it.
//!
//! # Example
//! ```
//! use cretonne_codegen::settings::{self, Configurable};
//!
//! let mut b = settings::builder();
//! b.set("opt_level", "fastest");
//!
//! let f = settings::Flags::new(&b);
//! assert_eq!(f.opt_level(), settings::OptLevel::Fastest);
//! ```

use constant_hash::{probe, simple_hash};
use isa::TargetIsa;
use std::fmt;
use std::result;
use std::vec::Vec;
use std::str;

/// A string-based configurator for settings groups.
///
/// The `Configurable` protocol allows settings to be modified by name before a finished `Flags`
/// struct is created.
pub trait Configurable {
    /// Set the string value of any setting by name.
    ///
    /// This can set any type of setting whether it is numeric, boolean, or enumerated.
    fn set(&mut self, name: &str, value: &str) -> Result<()>;

    /// Enable a boolean setting or apply a preset.
    ///
    /// If the identified setting isn't a boolean or a preset, a `BadType` error is returned.
    fn enable(&mut self, name: &str) -> Result<()>;
}

/// Collect settings values based on a template.
pub struct Builder {
    template: &'static detail::Template,
    bytes: Vec<u8>,
}

impl Builder {
    /// Create a new builder with defaults and names from the given template.
    pub fn new(tmpl: &'static detail::Template) -> Self {
        Self {
            template: tmpl,
            bytes: tmpl.defaults.into(),
        }
    }

    /// Extract contents of builder once everything is configured.
    pub fn state_for(&self, name: &str) -> &[u8] {
        assert_eq!(name, self.template.name);
        &self.bytes[..]
    }

    /// Set the value of a single bit.
    fn set_bit(&mut self, offset: usize, bit: u8, value: bool) {
        let byte = &mut self.bytes[offset];
        let mask = 1 << bit;
        if value {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    /// Apply a preset. The argument is a slice of (mask, value) bytes.
    fn apply_preset(&mut self, values: &[(u8, u8)]) {
        for (byte, &(mask, value)) in self.bytes.iter_mut().zip(values) {
            *byte = (*byte & !mask) | value;
        }
    }

    /// Look up a descriptor by name.
    fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
        match probe(self.template, name, simple_hash(name)) {
            Err(_) => Err(Error::BadName),
            Ok(entry) => {
                let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
                Ok((d.offset as usize, d.detail))
            }
        }
    }
}

fn parse_bool_value(value: &str) -> Result<bool> {
    match value {
        "true" | "on" | "yes" | "1" => Ok(true),
        "false" | "off" | "no" | "0" => Ok(false),
        _ => Err(Error::BadValue),
    }
}

fn parse_enum_value(value: &str, choices: &[&str]) -> Result<u8> {
    match choices.iter().position(|&tag| tag == value) {
        Some(idx) => Ok(idx as u8),
        None => Err(Error::BadValue),
    }
}

impl Configurable for Builder {
    fn enable(&mut self, name: &str) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        match detail {
            Detail::Bool { bit } => {
                self.set_bit(offset, bit, true);
                Ok(())
            }
            Detail::Preset => {
                self.apply_preset(&self.template.presets[offset..]);
                Ok(())
            }
            _ => Err(Error::BadType),
        }
    }

    fn set(&mut self, name: &str, value: &str) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        match detail {
            Detail::Bool { bit } => {
                // Cannot currently propagate Result<()> up on functions returning ()
                // with the `?` operator
                self.set_bit(offset, bit, parse_bool_value(value)?);
            }
            Detail::Num => {
                self.bytes[offset] = value.parse().map_err(|_| Error::BadValue)?;
            }
            Detail::Enum { last, enumerators } => {
                self.bytes[offset] =
                    parse_enum_value(value, self.template.enums(last, enumerators))?;
            }
            Detail::Preset => return Err(Error::BadName),
        }
        Ok(())
    }
}

/// An error produced when changing a setting.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    /// No setting by this name exists.
    BadName,

    /// Type mismatch for setting (e.g., setting an enum setting as a bool).
    BadType,

    /// This is not a valid value for this setting.
    BadValue,
}

/// A result returned when changing a setting.
pub type Result<T> = result::Result<T, Error>;

/// A reference to just the boolean predicates of a settings object.
///
/// The settings objects themselves are generated and appear in the `isa/*/settings.rs` modules.
/// Each settings object provides a `predicate_view()` method that makes it possible to query
/// ISA predicates by number.
#[derive(Clone, Copy)]
pub struct PredicateView<'a>(&'a [u8]);

impl<'a> PredicateView<'a> {
    /// Create a new view of a precomputed predicate vector.
    ///
    /// See the `predicate_view()` method on the various `Flags` types defined for each ISA.
    pub fn new(bits: &'a [u8]) -> PredicateView {
        PredicateView(bits)
    }

    /// Check a numbered predicate.
    pub fn test(self, p: usize) -> bool {
        self.0[p / 8] & (1 << (p % 8)) != 0
    }
}

/// Implementation details for generated code.
///
/// This module holds definitions that need to be public so the can be instantiated by generated
/// code in other modules.
pub mod detail {
    use constant_hash;
    use std::fmt;

    /// An instruction group template.
    pub struct Template {
        /// Name of the instruction group.
        pub name: &'static str,
        /// List of setting descriptors.
        pub descriptors: &'static [Descriptor],
        /// Union of all enumerators.
        pub enumerators: &'static [&'static str],
        /// Hash table of settings.
        pub hash_table: &'static [u16],
        /// Default values.
        pub defaults: &'static [u8],
        /// Pairs of (mask, value) for presets.
        pub presets: &'static [(u8, u8)],
    }

    impl Template {
        /// Get enumerators corresponding to a `Details::Enum`.
        pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
            let from = enumerators as usize;
            let len = usize::from(last) + 1;
            &self.enumerators[from..from + len]
        }

        /// Format a setting value as a TOML string. This is mostly for use by the generated
        /// `Display` implementation.
        pub fn format_toml_value(
            &self,
            detail: Detail,
            byte: u8,
            f: &mut fmt::Formatter,
        ) -> fmt::Result {
            match detail {
                Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
                Detail::Num => write!(f, "{}", byte),
                Detail::Enum { last, enumerators } => {
                    if byte <= last {
                        let tags = self.enums(last, enumerators);
                        write!(f, "\"{}\"", tags[usize::from(byte)])
                    } else {
                        write!(f, "{}", byte)
                    }
                }
                // Presets aren't printed. They are reflected in the other settings.
                Detail::Preset { .. } => Ok(()),
            }
        }
    }

    /// The template contains a hash table for by-name lookup.
    impl<'a> constant_hash::Table<&'a str> for Template {
        fn len(&self) -> usize {
            self.hash_table.len()
        }

        fn key(&self, idx: usize) -> Option<&'a str> {
            let e = self.hash_table[idx] as usize;
            if e < self.descriptors.len() {
                Some(self.descriptors[e].name)
            } else {
                None
            }
        }
    }

    /// A setting descriptor holds the information needed to generically set and print a setting.
    ///
    /// Each settings group will be represented as a constant DESCRIPTORS array.
    pub struct Descriptor {
        /// Lower snake-case name of setting as defined in meta.
        pub name: &'static str,

        /// Offset of byte containing this setting.
        pub offset: u32,

        /// Additional details, depending on the kind of setting.
        pub detail: Detail,
    }

    /// The different kind of settings along with descriptor bits that depend on the kind.
    #[derive(Clone, Copy)]
    pub enum Detail {
        /// A boolean setting only uses one bit, numbered from LSB.
        Bool {
            /// 0-7.
            bit: u8,
        },

        /// A numerical setting uses the whole byte.
        Num,

        /// An Enum setting uses a range of enumerators.
        Enum {
            /// Numerical value of last enumerator, allowing for 1-256 enumerators.
            last: u8,

            /// First enumerator in the ENUMERATORS table.
            enumerators: u16,
        },

        /// A preset is not an individual setting, it is a collection of settings applied at once.
        ///
        /// The `Descriptor::offset` field refers to the `PRESETS` table.
        Preset,
    }

    impl Detail {
        /// Check if a detail is a Detail::Preset. Useful because the Descriptor
        /// offset field has a different meaning when the detail is a preset.
        pub fn is_preset(&self) -> bool {
            match *self {
                Detail::Preset => true,
                _ => false,
            }
        }
    }
}

// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct
// with an impl for all of the settings defined in `lib/codegen/meta/base/settings.py`.
include!(concat!(env!("OUT_DIR"), "/settings.rs"));

/// Wrapper containing flags and optionally a `TargetIsa` trait object.
///
/// A few passes need to access the flags but only optionally a target ISA. The `FlagsOrIsa`
/// wrapper can be used to pass either, and extract the flags so they are always accessible.
#[derive(Clone, Copy)]
pub struct FlagsOrIsa<'a> {
    /// Flags are always present.
    pub flags: &'a Flags,

    /// The ISA may not be present.
    pub isa: Option<&'a TargetIsa>,
}

impl<'a> From<&'a Flags> for FlagsOrIsa<'a> {
    fn from(flags: &'a Flags) -> FlagsOrIsa {
        FlagsOrIsa { flags, isa: None }
    }
}

impl<'a> From<&'a TargetIsa> for FlagsOrIsa<'a> {
    fn from(isa: &'a TargetIsa) -> FlagsOrIsa {
        FlagsOrIsa {
            flags: isa.flags(),
            isa: Some(isa),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Configurable;
    use super::Error::*;
    use super::{builder, Flags};
    use std::string::ToString;

    #[test]
    fn display_default() {
        let b = builder();
        let f = Flags::new(&b);
        assert_eq!(
            f.to_string(),
            "[shared]\n\
             opt_level = \"default\"\n\
             enable_verifier = true\n\
             is_64bit = false\n\
             call_conv = \"fast\"\n\
             is_pic = false\n\
             colocated_libcalls = false\n\
             return_at_end = false\n\
             avoid_div_traps = false\n\
             is_compressed = false\n\
             enable_float = true\n\
             enable_simd = true\n\
             enable_atomics = true\n\
             baldrdash_prologue_words = 0\n\
             allones_funcaddrs = false\n\
             probestack_enabled = true\n\
             probestack_func_adjusts_sp = false\n\
             probestack_size_log2 = 12\n"
        );
        assert_eq!(f.opt_level(), super::OptLevel::Default);
        assert_eq!(f.enable_simd(), true);
        assert_eq!(f.baldrdash_prologue_words(), 0);
    }

    #[test]
    fn modify_bool() {
        let mut b = builder();
        assert_eq!(b.enable("not_there"), Err(BadName));
        assert_eq!(b.enable("enable_simd"), Ok(()));
        assert_eq!(b.set("enable_simd", "false"), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
    }

    #[test]
    fn modify_string() {
        let mut b = builder();
        assert_eq!(b.set("not_there", "true"), Err(BadName));
        assert_eq!(b.set("enable_simd", ""), Err(BadValue));
        assert_eq!(b.set("enable_simd", "best"), Err(BadValue));
        assert_eq!(b.set("opt_level", "true"), Err(BadValue));
        assert_eq!(b.set("opt_level", "best"), Ok(()));
        assert_eq!(b.set("enable_simd", "0"), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
        assert_eq!(f.opt_level(), super::OptLevel::Best);
    }
}
<commit_msg>Replace Builder's Vec<u8> with a Box<[u8]>.<commit_after>//! Shared settings module.
//!
//! This module defines data structures to access the settings defined in the meta language.
//!
//! Each settings group is translated to a `Flags` struct either in this module or in its
//! ISA-specific `settings` module. The struct provides individual getter methods for all of the
//! settings as well as computed predicate flags.
//!
//! The `Flags` struct is immutable once it has been created. A `Builder` instance is used to
//! create it.
//!
//! # Example
//! ```
//! use cretonne_codegen::settings::{self, Configurable};
//!
//! let mut b = settings::builder();
//! b.set("opt_level", "fastest");
//!
//! let f = settings::Flags::new(&b);
//! assert_eq!(f.opt_level(), settings::OptLevel::Fastest);
//! ```

use constant_hash::{probe, simple_hash};
use isa::TargetIsa;
use std::fmt;
use std::result;
use std::boxed::Box;
use std::str;

/// A string-based configurator for settings groups.
///
/// The `Configurable` protocol allows settings to be modified by name before a finished `Flags`
/// struct is created.
pub trait Configurable {
    /// Set the string value of any setting by name.
    ///
    /// This can set any type of setting whether it is numeric, boolean, or enumerated.
    fn set(&mut self, name: &str, value: &str) -> Result<()>;

    /// Enable a boolean setting or apply a preset.
    ///
    /// If the identified setting isn't a boolean or a preset, a `BadType` error is returned.
    fn enable(&mut self, name: &str) -> Result<()>;
}

/// Collect settings values based on a template.
pub struct Builder {
    template: &'static detail::Template,
    bytes: Box<[u8]>,
}

impl Builder {
    /// Create a new builder with defaults and names from the given template.
    pub fn new(tmpl: &'static detail::Template) -> Self {
        Self {
            template: tmpl,
            bytes: tmpl.defaults.into(),
        }
    }

    /// Extract contents of builder once everything is configured.
    pub fn state_for(&self, name: &str) -> &[u8] {
        assert_eq!(name, self.template.name);
        &self.bytes[..]
    }

    /// Set the value of a single bit.
    fn set_bit(&mut self, offset: usize, bit: u8, value: bool) {
        let byte = &mut self.bytes[offset];
        let mask = 1 << bit;
        if value {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    /// Apply a preset. The argument is a slice of (mask, value) bytes.
    fn apply_preset(&mut self, values: &[(u8, u8)]) {
        for (byte, &(mask, value)) in self.bytes.iter_mut().zip(values) {
            *byte = (*byte & !mask) | value;
        }
    }

    /// Look up a descriptor by name.
    fn lookup(&self, name: &str) -> Result<(usize, detail::Detail)> {
        match probe(self.template, name, simple_hash(name)) {
            Err(_) => Err(Error::BadName),
            Ok(entry) => {
                let d = &self.template.descriptors[self.template.hash_table[entry] as usize];
                Ok((d.offset as usize, d.detail))
            }
        }
    }
}

fn parse_bool_value(value: &str) -> Result<bool> {
    match value {
        "true" | "on" | "yes" | "1" => Ok(true),
        "false" | "off" | "no" | "0" => Ok(false),
        _ => Err(Error::BadValue),
    }
}

fn parse_enum_value(value: &str, choices: &[&str]) -> Result<u8> {
    match choices.iter().position(|&tag| tag == value) {
        Some(idx) => Ok(idx as u8),
        None => Err(Error::BadValue),
    }
}

impl Configurable for Builder {
    fn enable(&mut self, name: &str) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        match detail {
            Detail::Bool { bit } => {
                self.set_bit(offset, bit, true);
                Ok(())
            }
            Detail::Preset => {
                self.apply_preset(&self.template.presets[offset..]);
                Ok(())
            }
            _ => Err(Error::BadType),
        }
    }

    fn set(&mut self, name: &str, value: &str) -> Result<()> {
        use self::detail::Detail;
        let (offset, detail) = self.lookup(name)?;
        match detail {
            Detail::Bool { bit } => {
                // Cannot currently propagate Result<()> up on functions returning ()
                // with the `?` operator
                self.set_bit(offset, bit, parse_bool_value(value)?);
            }
            Detail::Num => {
                self.bytes[offset] = value.parse().map_err(|_| Error::BadValue)?;
            }
            Detail::Enum { last, enumerators } => {
                self.bytes[offset] =
                    parse_enum_value(value, self.template.enums(last, enumerators))?;
            }
            Detail::Preset => return Err(Error::BadName),
        }
        Ok(())
    }
}

/// An error produced when changing a setting.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    /// No setting by this name exists.
    BadName,

    /// Type mismatch for setting (e.g., setting an enum setting as a bool).
    BadType,

    /// This is not a valid value for this setting.
    BadValue,
}

/// A result returned when changing a setting.
pub type Result<T> = result::Result<T, Error>;

/// A reference to just the boolean predicates of a settings object.
///
/// The settings objects themselves are generated and appear in the `isa/*/settings.rs` modules.
/// Each settings object provides a `predicate_view()` method that makes it possible to query
/// ISA predicates by number.
#[derive(Clone, Copy)]
pub struct PredicateView<'a>(&'a [u8]);

impl<'a> PredicateView<'a> {
    /// Create a new view of a precomputed predicate vector.
    ///
    /// See the `predicate_view()` method on the various `Flags` types defined for each ISA.
    pub fn new(bits: &'a [u8]) -> PredicateView {
        PredicateView(bits)
    }

    /// Check a numbered predicate.
    pub fn test(self, p: usize) -> bool {
        self.0[p / 8] & (1 << (p % 8)) != 0
    }
}

/// Implementation details for generated code.
///
/// This module holds definitions that need to be public so the can be instantiated by generated
/// code in other modules.
pub mod detail {
    use constant_hash;
    use std::fmt;

    /// An instruction group template.
    pub struct Template {
        /// Name of the instruction group.
        pub name: &'static str,
        /// List of setting descriptors.
        pub descriptors: &'static [Descriptor],
        /// Union of all enumerators.
        pub enumerators: &'static [&'static str],
        /// Hash table of settings.
        pub hash_table: &'static [u16],
        /// Default values.
        pub defaults: &'static [u8],
        /// Pairs of (mask, value) for presets.
        pub presets: &'static [(u8, u8)],
    }

    impl Template {
        /// Get enumerators corresponding to a `Details::Enum`.
        pub fn enums(&self, last: u8, enumerators: u16) -> &[&'static str] {
            let from = enumerators as usize;
            let len = usize::from(last) + 1;
            &self.enumerators[from..from + len]
        }

        /// Format a setting value as a TOML string. This is mostly for use by the generated
        /// `Display` implementation.
        pub fn format_toml_value(
            &self,
            detail: Detail,
            byte: u8,
            f: &mut fmt::Formatter,
        ) -> fmt::Result {
            match detail {
                Detail::Bool { bit } => write!(f, "{}", (byte & (1 << bit)) != 0),
                Detail::Num => write!(f, "{}", byte),
                Detail::Enum { last, enumerators } => {
                    if byte <= last {
                        let tags = self.enums(last, enumerators);
                        write!(f, "\"{}\"", tags[usize::from(byte)])
                    } else {
                        write!(f, "{}", byte)
                    }
                }
                // Presets aren't printed. They are reflected in the other settings.
                Detail::Preset { .. } => Ok(()),
            }
        }
    }

    /// The template contains a hash table for by-name lookup.
    impl<'a> constant_hash::Table<&'a str> for Template {
        fn len(&self) -> usize {
            self.hash_table.len()
        }

        fn key(&self, idx: usize) -> Option<&'a str> {
            let e = self.hash_table[idx] as usize;
            if e < self.descriptors.len() {
                Some(self.descriptors[e].name)
            } else {
                None
            }
        }
    }

    /// A setting descriptor holds the information needed to generically set and print a setting.
    ///
    /// Each settings group will be represented as a constant DESCRIPTORS array.
    pub struct Descriptor {
        /// Lower snake-case name of setting as defined in meta.
        pub name: &'static str,

        /// Offset of byte containing this setting.
        pub offset: u32,

        /// Additional details, depending on the kind of setting.
        pub detail: Detail,
    }

    /// The different kind of settings along with descriptor bits that depend on the kind.
    #[derive(Clone, Copy)]
    pub enum Detail {
        /// A boolean setting only uses one bit, numbered from LSB.
        Bool {
            /// 0-7.
            bit: u8,
        },

        /// A numerical setting uses the whole byte.
        Num,

        /// An Enum setting uses a range of enumerators.
        Enum {
            /// Numerical value of last enumerator, allowing for 1-256 enumerators.
            last: u8,

            /// First enumerator in the ENUMERATORS table.
            enumerators: u16,
        },

        /// A preset is not an individual setting, it is a collection of settings applied at once.
        ///
        /// The `Descriptor::offset` field refers to the `PRESETS` table.
        Preset,
    }

    impl Detail {
        /// Check if a detail is a Detail::Preset. Useful because the Descriptor
        /// offset field has a different meaning when the detail is a preset.
        pub fn is_preset(&self) -> bool {
            match *self {
                Detail::Preset => true,
                _ => false,
            }
        }
    }
}

// Include code generated by `meta/gen_settings.py`. This file contains a public `Flags` struct
// with an impl for all of the settings defined in `lib/codegen/meta/base/settings.py`.
include!(concat!(env!("OUT_DIR"), "/settings.rs"));

/// Wrapper containing flags and optionally a `TargetIsa` trait object.
///
/// A few passes need to access the flags but only optionally a target ISA. The `FlagsOrIsa`
/// wrapper can be used to pass either, and extract the flags so they are always accessible.
#[derive(Clone, Copy)]
pub struct FlagsOrIsa<'a> {
    /// Flags are always present.
    pub flags: &'a Flags,

    /// The ISA may not be present.
    pub isa: Option<&'a TargetIsa>,
}

impl<'a> From<&'a Flags> for FlagsOrIsa<'a> {
    fn from(flags: &'a Flags) -> FlagsOrIsa {
        FlagsOrIsa { flags, isa: None }
    }
}

impl<'a> From<&'a TargetIsa> for FlagsOrIsa<'a> {
    fn from(isa: &'a TargetIsa) -> FlagsOrIsa {
        FlagsOrIsa {
            flags: isa.flags(),
            isa: Some(isa),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Configurable;
    use super::Error::*;
    use super::{builder, Flags};
    use std::string::ToString;

    #[test]
    fn display_default() {
        let b = builder();
        let f = Flags::new(&b);
        assert_eq!(
            f.to_string(),
            "[shared]\n\
             opt_level = \"default\"\n\
             enable_verifier = true\n\
             is_64bit = false\n\
             call_conv = \"fast\"\n\
             is_pic = false\n\
             colocated_libcalls = false\n\
             return_at_end = false\n\
             avoid_div_traps = false\n\
             is_compressed = false\n\
             enable_float = true\n\
             enable_simd = true\n\
             enable_atomics = true\n\
             baldrdash_prologue_words = 0\n\
             allones_funcaddrs = false\n\
             probestack_enabled = true\n\
             probestack_func_adjusts_sp = false\n\
             probestack_size_log2 = 12\n"
        );
        assert_eq!(f.opt_level(), super::OptLevel::Default);
        assert_eq!(f.enable_simd(), true);
        assert_eq!(f.baldrdash_prologue_words(), 0);
    }

    #[test]
    fn modify_bool() {
        let mut b = builder();
        assert_eq!(b.enable("not_there"), Err(BadName));
        assert_eq!(b.enable("enable_simd"), Ok(()));
        assert_eq!(b.set("enable_simd", "false"), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
    }

    #[test]
    fn modify_string() {
        let mut b = builder();
        assert_eq!(b.set("not_there", "true"), Err(BadName));
        assert_eq!(b.set("enable_simd", ""), Err(BadValue));
        assert_eq!(b.set("enable_simd", "best"), Err(BadValue));
        assert_eq!(b.set("opt_level", "true"), Err(BadValue));
        assert_eq!(b.set("opt_level", "best"), Ok(()));
        assert_eq!(b.set("enable_simd", "0"), Ok(()));

        let f = Flags::new(&b);
        assert_eq!(f.enable_simd(), false);
        assert_eq!(f.opt_level(), super::OptLevel::Best);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Build a block to mine: gathers transactions from the pool, assembles
//! them into a block and returns it.

use crate::util::RwLock;
use chrono::prelude::{DateTime, NaiveDateTime, Utc};
use rand::{thread_rng, Rng};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use crate::chain;
use crate::common::types::Error;
use crate::core::core::verifier_cache::VerifierCache;
use crate::core::{consensus, core, global, ser};
use crate::keychain::{ExtKeychain, Identifier, Keychain};
use crate::pool;
use crate::util;
use crate::wallet::{self, BlockFees};

// Ensure a block suitable for mining is built and returned
// If a wallet listener URL is not provided the reward will be "burnt"
// Warning: This call does not return until/unless a new block can be built
pub fn get_block(
	chain: &Arc<chain::Chain>,
	tx_pool: &Arc<RwLock<pool::TransactionPool>>,
	verifier_cache: Arc<RwLock<dyn VerifierCache>>,
	key_id: Option<Identifier>,
	wallet_listener_url: Option<String>,
) -> (core::Block, BlockFees) {
	let wallet_retry_interval = 5;
	// get the latest chain state and build a block on top of it
	let mut result = build_block(
		chain,
		tx_pool,
		verifier_cache.clone(),
		key_id.clone(),
		wallet_listener_url.clone(),
	);
	while let Err(e) = result {
		match e {
			self::Error::Chain(c) => match c.kind() {
				chain::ErrorKind::DuplicateCommitment(_) => {
					debug!(
						"Duplicate commit for potential coinbase detected. Trying next derivation."
					);
				}
				_ => {
					error!("Chain Error: {}", c);
				}
			},
			self::Error::Wallet(_) => {
				error!(
					"Error building new block: Can't connect to wallet listener at {:?}; will retry",
					wallet_listener_url.as_ref().unwrap()
				);
				thread::sleep(Duration::from_secs(wallet_retry_interval));
			}
			ae => {
				warn!("Error building new block: {:?}. Retrying.", ae);
			}
		}
		thread::sleep(Duration::from_millis(100));
		result = build_block(
			chain,
			tx_pool,
			verifier_cache.clone(),
			key_id.clone(),
			wallet_listener_url.clone(),
		);
	}
	return result.unwrap();
}

/// Builds a new block with the chain head as previous and eligible
/// transactions from the pool.
fn build_block(
	chain: &Arc<chain::Chain>,
	tx_pool: &Arc<RwLock<pool::TransactionPool>>,
	verifier_cache: Arc<RwLock<dyn VerifierCache>>,
	key_id: Option<Identifier>,
	wallet_listener_url: Option<String>,
) -> Result<(core::Block, BlockFees), Error> {
	let head = chain.head_header()?;

	// prepare the block header timestamp
	let mut now_sec = Utc::now().timestamp();
	let head_sec = head.timestamp.timestamp();
	if now_sec <= head_sec {
		now_sec = head_sec + 1;
	}

	// Determine the difficulty our block should be at.
	// Note: do not keep the difficulty_iter in scope (it has an active batch).
	let difficulty = consensus::next_difficulty(head.height + 1, chain.difficulty_iter());

	// Extract current "mineable" transactions from the pool.
	// If this fails for *any* reason then fallback to an empty vec of txs.
	// This will allow us to mine an "empty" block if the txpool is in an
	// invalid (and unexpected) state.
	let txs = match tx_pool.read().prepare_mineable_transactions() {
		Ok(txs) => txs,
		Err(e) => {
			error!("build_block: Failed to prepare mineable txs from txpool: {:?}", e);
			warn!("build_block: Falling back to mining empty block.");
			vec![]
		}
	};

	// build the coinbase and the block itself
	let fees = txs.iter().map(|tx| tx.fee()).sum();
	let height = head.height + 1;
	let block_fees = BlockFees {
		fees,
		key_id,
		height,
	};

	let (output, kernel, block_fees) = get_coinbase(wallet_listener_url, block_fees)?;
	let mut b = core::Block::from_reward(&head, txs, output, kernel, difficulty.difficulty)?;

	// making sure we're not spending time mining a useless block
	b.validate(&head.total_kernel_offset, verifier_cache)?;

	b.header.pow.nonce = thread_rng().gen();
	b.header.pow.secondary_scaling = difficulty.secondary_scaling;
	b.header.timestamp = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(now_sec, 0), Utc);

	debug!(
		"Built new block with {} inputs and {} outputs, block difficulty: {}, cumulative difficulty {}",
		b.inputs().len(),
		b.outputs().len(),
		difficulty.difficulty,
		b.header.total_difficulty().to_num(),
	);

	// Now set txhashset roots and sizes on the header of the block being built.
	match chain.set_txhashset_roots(&mut b) {
		Ok(_) => Ok((b, block_fees)),
		Err(e) => {
			match e.kind() {
				// If this is a duplicate commitment then likely trying to use
				// a key that hass already been derived but not in the wallet
				// for some reason, allow caller to retry.
				chain::ErrorKind::DuplicateCommitment(e) => Err(Error::Chain(
					chain::ErrorKind::DuplicateCommitment(e).into(),
				)),

				// Some other issue, possibly duplicate kernel
				_ => {
					error!("Error setting txhashset root to build a block: {:?}", e);
					Err(Error::Chain(
						chain::ErrorKind::Other(format!("{:?}", e)).into(),
					))
				}
			}
		}
	}
}

///
/// Probably only want to do this when testing.
///
fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, BlockFees), Error> {
	warn!("Burning block fees: {:?}", block_fees);
	let keychain = ExtKeychain::from_random_seed(global::is_floonet()).unwrap();
	let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
	let (out, kernel) =
		crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees).unwrap();
	Ok((out, kernel, block_fees))
}

// Connect to the wallet listener and get coinbase.
// Warning: If a wallet listener URL is not provided the reward will be "burnt"
fn get_coinbase(
	wallet_listener_url: Option<String>,
	block_fees: BlockFees,
) -> Result<(core::Output, core::TxKernel, BlockFees), Error> {
	match wallet_listener_url {
		None => {
			// Burn it
			return burn_reward(block_fees);
		}
		Some(wallet_listener_url) => {
			let res = wallet::create_coinbase(&wallet_listener_url, &block_fees)?;
			let out_bin = util::from_hex(res.output).unwrap();
			let kern_bin = util::from_hex(res.kernel).unwrap();
			let key_id_bin = util::from_hex(res.key_id).unwrap();
			let output = ser::deserialize(&mut &out_bin[..]).unwrap();
			let kernel = ser::deserialize(&mut &kern_bin[..]).unwrap();
			let key_id = ser::deserialize(&mut &key_id_bin[..]).unwrap();
			let block_fees = BlockFees {
				key_id: Some(key_id),
				..block_fees
			};

			debug!("get_coinbase: {:?}", block_fees);
			return Ok((output, kernel, block_fees));
		}
	}
}
<commit_msg>rustfmt<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Build a block to mine: gathers transactions from the pool, assembles
//! them into a block and returns it.

use crate::util::RwLock;
use chrono::prelude::{DateTime, NaiveDateTime, Utc};
use rand::{thread_rng, Rng};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use crate::chain;
use crate::common::types::Error;
use crate::core::core::verifier_cache::VerifierCache;
use crate::core::{consensus, core, global, ser};
use crate::keychain::{ExtKeychain, Identifier, Keychain};
use crate::pool;
use crate::util;
use crate::wallet::{self, BlockFees};

// Ensure a block suitable for mining is built and returned
// If a wallet listener URL is not provided the reward will be "burnt"
// Warning: This call does not return until/unless a new block can be built
pub fn get_block(
	chain: &Arc<chain::Chain>,
	tx_pool: &Arc<RwLock<pool::TransactionPool>>,
	verifier_cache: Arc<RwLock<dyn VerifierCache>>,
	key_id: Option<Identifier>,
	wallet_listener_url: Option<String>,
) -> (core::Block, BlockFees) {
	let wallet_retry_interval = 5;
	// get the latest chain state and build a block on top of it
	let mut result = build_block(
		chain,
		tx_pool,
		verifier_cache.clone(),
		key_id.clone(),
		wallet_listener_url.clone(),
	);
	while let Err(e) = result {
		match e {
			self::Error::Chain(c) => match c.kind() {
				chain::ErrorKind::DuplicateCommitment(_) => {
					debug!(
						"Duplicate commit for potential coinbase detected. Trying next derivation."
					);
				}
				_ => {
					error!("Chain Error: {}", c);
				}
			},
			self::Error::Wallet(_) => {
				error!(
					"Error building new block: Can't connect to wallet listener at {:?}; will retry",
					wallet_listener_url.as_ref().unwrap()
				);
				thread::sleep(Duration::from_secs(wallet_retry_interval));
			}
			ae => {
				warn!("Error building new block: {:?}. Retrying.", ae);
			}
		}
		thread::sleep(Duration::from_millis(100));
		result = build_block(
			chain,
			tx_pool,
			verifier_cache.clone(),
			key_id.clone(),
			wallet_listener_url.clone(),
		);
	}
	return result.unwrap();
}

/// Builds a new block with the chain head as previous and eligible
/// transactions from the pool.
fn build_block(
	chain: &Arc<chain::Chain>,
	tx_pool: &Arc<RwLock<pool::TransactionPool>>,
	verifier_cache: Arc<RwLock<dyn VerifierCache>>,
	key_id: Option<Identifier>,
	wallet_listener_url: Option<String>,
) -> Result<(core::Block, BlockFees), Error> {
	let head = chain.head_header()?;

	// prepare the block header timestamp
	let mut now_sec = Utc::now().timestamp();
	let head_sec = head.timestamp.timestamp();
	if now_sec <= head_sec {
		now_sec = head_sec + 1;
	}

	// Determine the difficulty our block should be at.
	// Note: do not keep the difficulty_iter in scope (it has an active batch).
	let difficulty = consensus::next_difficulty(head.height + 1, chain.difficulty_iter());

	// Extract current "mineable" transactions from the pool.
	// If this fails for *any* reason then fallback to an empty vec of txs.
	// This will allow us to mine an "empty" block if the txpool is in an
	// invalid (and unexpected) state.
	let txs = match tx_pool.read().prepare_mineable_transactions() {
		Ok(txs) => txs,
		Err(e) => {
			error!(
				"build_block: Failed to prepare mineable txs from txpool: {:?}",
				e
			);
			warn!("build_block: Falling back to mining empty block.");
			vec![]
		}
	};

	// build the coinbase and the block itself
	let fees = txs.iter().map(|tx| tx.fee()).sum();
	let height = head.height + 1;
	let block_fees = BlockFees {
		fees,
		key_id,
		height,
	};

	let (output, kernel, block_fees) = get_coinbase(wallet_listener_url, block_fees)?;
	let mut b = core::Block::from_reward(&head, txs, output, kernel, difficulty.difficulty)?;

	// making sure we're not spending time mining a useless block
	b.validate(&head.total_kernel_offset, verifier_cache)?;

	b.header.pow.nonce = thread_rng().gen();
	b.header.pow.secondary_scaling = difficulty.secondary_scaling;
	b.header.timestamp = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(now_sec, 0), Utc);

	debug!(
		"Built new block with {} inputs and {} outputs, block difficulty: {}, cumulative difficulty {}",
		b.inputs().len(),
		b.outputs().len(),
		difficulty.difficulty,
		b.header.total_difficulty().to_num(),
	);

	// Now set txhashset roots and sizes on the header of the block being built.
	match chain.set_txhashset_roots(&mut b) {
		Ok(_) => Ok((b, block_fees)),
		Err(e) => {
			match e.kind() {
				// If this is a duplicate commitment then likely trying to use
				// a key that hass already been derived but not in the wallet
				// for some reason, allow caller to retry.
				chain::ErrorKind::DuplicateCommitment(e) => Err(Error::Chain(
					chain::ErrorKind::DuplicateCommitment(e).into(),
				)),

				// Some other issue, possibly duplicate kernel
				_ => {
					error!("Error setting txhashset root to build a block: {:?}", e);
					Err(Error::Chain(
						chain::ErrorKind::Other(format!("{:?}", e)).into(),
					))
				}
			}
		}
	}
}

///
/// Probably only want to do this when testing.
///
fn burn_reward(block_fees: BlockFees) -> Result<(core::Output, core::TxKernel, BlockFees), Error> {
	warn!("Burning block fees: {:?}", block_fees);
	let keychain = ExtKeychain::from_random_seed(global::is_floonet()).unwrap();
	let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
	let (out, kernel) =
		crate::core::libtx::reward::output(&keychain, &key_id, block_fees.fees).unwrap();
	Ok((out, kernel, block_fees))
}

// Connect to the wallet listener and get coinbase.
// Warning: If a wallet listener URL is not provided the reward will be "burnt"
fn get_coinbase(
	wallet_listener_url: Option<String>,
	block_fees: BlockFees,
) -> Result<(core::Output, core::TxKernel, BlockFees), Error> {
	match wallet_listener_url {
		None => {
			// Burn it
			return burn_reward(block_fees);
		}
		Some(wallet_listener_url) => {
			let res = wallet::create_coinbase(&wallet_listener_url, &block_fees)?;
			let out_bin = util::from_hex(res.output).unwrap();
			let kern_bin = util::from_hex(res.kernel).unwrap();
			let key_id_bin = util::from_hex(res.key_id).unwrap();
			let output = ser::deserialize(&mut &out_bin[..]).unwrap();
			let kernel = ser::deserialize(&mut &kern_bin[..]).unwrap();
			let key_id = ser::deserialize(&mut &key_id_bin[..]).unwrap();
			let block_fees = BlockFees {
				key_id: Some(key_id),
				..block_fees
			};

			debug!("get_coinbase: {:?}", block_fees);
			return Ok((output, kernel, block_fees));
		}
	}
}
<|endoftext|>


<commit_before>/*!
`ndarray` compability

This adds support for reading data into [`ndarray`][ndarray] array objects. This primarily
enables support for automatically reshaping the resulting arrays to have the dimensionality of
the image that was asked for.

Data is read into the [`ndarray::ArrayD`][arrayd] type. The following methods from
[`FitsHdu`][fits-hdu] are supported:

* [`read_image`][read-image]
* [`read_region`][read-region]
* [`read_row`][read-row]
* [`read_rows`][read-rows]
* [`read_section`][read-section]

## `read_image`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 100);
assert_eq!(dim[1], 100);
assert_eq!(data[[20, 5]], 152);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_region`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 10);
assert_eq!(dim[1], 30);
assert_eq!(data[[5, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_row`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
assert_eq!(data.ndim(), 1);
assert_eq!(data[20], 156);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_rows`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[1, 52]], 184);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_section`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
use fitsio::errors::Error;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
    Err(Error::Message(msg)) => {
        assert_eq!(
            msg,
            "must request number of pixels exactly divisible by image width".to_string()
        );
    }
    _ => panic!("invalid result"),
}

let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[0, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

[ndarray]: https://crates.io/crates/ndarray
[arrayd]: https://docs.rs/ndarray/0.11.2/ndarray/type.ArrayD.html
[fits-hdu]: hdu/struct.FitsHdu.html
[read-image]: images/struct.FitsHdu.html#method.read_image
[read-region]: images/struct.FitsHdu.html#method.read_region
[read-row]: images/struct.FitsHdu.html#method.read_row
[read-rows]: images/struct.FitsHdu.html#method.read_rows
[read-section]: images/struct.FitsHdu.html#method.read_section
*/

use errors::Result;
use fitsfile::FitsFile;
use hdu::{FitsHdu, HduInfo};
use images::ReadImage;
use ndarray::{Array, ArrayD};
use std::ops::Range;

impl<T> ReadImage for ArrayD<T>
where
    T: Clone,
    Vec<T>: ReadImage,
{
    fn read_section(fits_file: &mut FitsFile, hdu: &FitsHdu, range: Range<usize>) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                if shape.len() != 2 {
                    return Err("Only 2D images supported for now".into());
                }

                let width = shape[1];

                if range.start % width != 0 {
                    return Err("range must start on row boundary".into());
                }
                let start_pixel = range.start / width;

                let n_pixels_requested = range.end - range.start;
                if n_pixels_requested % width != 0 {
                    return Err(
                        "must request number of pixels exactly divisible by image width".into(),
                    );
                }

                let n_rows = n_pixels_requested / width;
                ReadImage::read_rows(fits_file, hdu, start_pixel, n_rows)
            }
            HduInfo::TableInfo { .. } => {
                return Err("Cannot read image data from a FITS table".into())
            }
            _ => unreachable!(),
        }
    }

    fn read_rows(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        start_row: usize,
        num_rows: usize,
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_rows(fits_file, hdu, start_row, num_rows)?;
        let arr = Array::from_vec(data);
        let row_length = arr.len() / num_rows;
        Ok(arr.into_shape(vec![num_rows, row_length]).unwrap())
    }

    fn read_row(fits_file: &mut FitsFile, hdu: &FitsHdu, row: usize) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_row(fits_file, hdu, row)?;
        let shape = vec![data.len()];
        Ok(Array::from_shape_vec(shape, data).unwrap())
    }

    fn read_region(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        ranges: &[&Range<usize>],
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_region(fits_file, hdu, ranges)?;
        let shape: Vec<usize> = (0..ranges.len())
            .map(|i| (ranges[i].end + 1) - ranges[i].start)
            .collect();
        let arr = Array::from_shape_vec(shape, data).unwrap();
        Ok(arr)
    }

    fn read_image(fits_file: &mut FitsFile, hdu: &FitsHdu) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                let data: Vec<T> = ReadImage::read_image(fits_file, hdu)?;
                let shape: Vec<usize> = (0..2).map(|i| shape[i]).collect();
                let arr = Array::from_shape_vec(shape, data).unwrap();
                Ok(arr)
            }
            _ => unreachable!(),
        }
    }
}

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

    #[test]
    fn test_read_image() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 100);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[20, 5]], 152);
    }

    #[test]
    fn test_read_rows() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[1, 52]], 184);
    }

    #[test]
    fn test_read_row() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
        assert_eq!(data.ndim(), 1);
        assert_eq!(data[20], 156);
    }

    #[test]
    fn test_read_region() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 11);
        assert_eq!(dim[1], 31);
        assert_eq!(data[[5, 10]], 179);
    }

    #[test]
    fn test_read_section() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
            Err(Error::Message(msg)) => {
                assert_eq!(
                    msg,
                    "must request number of pixels exactly divisible by image width".to_string()
                );
            }
            _ => panic!("invalid result"),
        }

        let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[0, 10]], 160);
    }
}
<commit_msg>Update ndarray doctests<commit_after>/*!
`ndarray` compability

This adds support for reading data into [`ndarray`][ndarray] array objects. This primarily
enables support for automatically reshaping the resulting arrays to have the dimensionality of
the image that was asked for.

Data is read into the [`ndarray::ArrayD`][arrayd] type. The following methods from
[`FitsHdu`][fits-hdu] are supported:

* [`read_image`][read-image]
* [`read_region`][read-region]
* [`read_row`][read-row]
* [`read_rows`][read-rows]
* [`read_section`][read-section]

## `read_image`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 100);
assert_eq!(dim[1], 100);
assert_eq!(data[[20, 5]], 152);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_region`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 11);
assert_eq!(dim[1], 31);
assert_eq!(data[[5, 10]], 179);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_row`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
assert_eq!(data.ndim(), 1);
assert_eq!(data[20], 156);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_rows`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[1, 52]], 184);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_section`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
use fitsio::FitsFile;
use fitsio::errors::Error;
# #[cfg(feature = "array")]
use ndarray::ArrayD;

# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
    Err(Error::Message(msg)) => {
        assert_eq!(
            msg,
            "must request number of pixels exactly divisible by image width".to_string()
        );
    }
    _ => panic!("invalid result"),
}

let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[0, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

[ndarray]: https://crates.io/crates/ndarray
[arrayd]: https://docs.rs/ndarray/0.11.2/ndarray/type.ArrayD.html
[fits-hdu]: hdu/struct.FitsHdu.html
[read-image]: images/struct.FitsHdu.html#method.read_image
[read-region]: images/struct.FitsHdu.html#method.read_region
[read-row]: images/struct.FitsHdu.html#method.read_row
[read-rows]: images/struct.FitsHdu.html#method.read_rows
[read-section]: images/struct.FitsHdu.html#method.read_section
*/

use errors::Result;
use fitsfile::FitsFile;
use hdu::{FitsHdu, HduInfo};
use images::ReadImage;
use ndarray::{Array, ArrayD};
use std::ops::Range;

impl<T> ReadImage for ArrayD<T>
where
    T: Clone,
    Vec<T>: ReadImage,
{
    fn read_section(fits_file: &mut FitsFile, hdu: &FitsHdu, range: Range<usize>) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                if shape.len() != 2 {
                    return Err("Only 2D images supported for now".into());
                }

                let width = shape[1];

                if range.start % width != 0 {
                    return Err("range must start on row boundary".into());
                }
                let start_pixel = range.start / width;

                let n_pixels_requested = range.end - range.start;
                if n_pixels_requested % width != 0 {
                    return Err(
                        "must request number of pixels exactly divisible by image width".into(),
                    );
                }

                let n_rows = n_pixels_requested / width;
                ReadImage::read_rows(fits_file, hdu, start_pixel, n_rows)
            }
            HduInfo::TableInfo { .. } => {
                return Err("Cannot read image data from a FITS table".into())
            }
            _ => unreachable!(),
        }
    }

    fn read_rows(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        start_row: usize,
        num_rows: usize,
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_rows(fits_file, hdu, start_row, num_rows)?;
        let arr = Array::from_vec(data);
        let row_length = arr.len() / num_rows;
        Ok(arr.into_shape(vec![num_rows, row_length]).unwrap())
    }

    fn read_row(fits_file: &mut FitsFile, hdu: &FitsHdu, row: usize) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_row(fits_file, hdu, row)?;
        let shape = vec![data.len()];
        Ok(Array::from_shape_vec(shape, data).unwrap())
    }

    fn read_region(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        ranges: &[&Range<usize>],
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_region(fits_file, hdu, ranges)?;
        let shape: Vec<usize> = (0..ranges.len())
            .map(|i| (ranges[i].end + 1) - ranges[i].start)
            .collect();
        let arr = Array::from_shape_vec(shape, data).unwrap();
        Ok(arr)
    }

    fn read_image(fits_file: &mut FitsFile, hdu: &FitsHdu) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                let data: Vec<T> = ReadImage::read_image(fits_file, hdu)?;
                let shape: Vec<usize> = (0..2).map(|i| shape[i]).collect();
                let arr = Array::from_shape_vec(shape, data).unwrap();
                Ok(arr)
            }
            _ => unreachable!(),
        }
    }
}

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

    #[test]
    fn test_read_image() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 100);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[20, 5]], 152);
    }

    #[test]
    fn test_read_rows() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[1, 52]], 184);
    }

    #[test]
    fn test_read_row() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
        assert_eq!(data.ndim(), 1);
        assert_eq!(data[20], 156);
    }

    #[test]
    fn test_read_region() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 11);
        assert_eq!(dim[1], 31);
        assert_eq!(data[[5, 10]], 179);
    }

    #[test]
    fn test_read_section() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
            Err(Error::Message(msg)) => {
                assert_eq!(
                    msg,
                    "must request number of pixels exactly divisible by image width".to_string()
                );
            }
            _ => panic!("invalid result"),
        }

        let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[0, 10]], 160);
    }
}
<|endoftext|>


<commit_before>extern crate quick_xml;

use quick_xml::events::{BytesStart, Event};
use quick_xml::{Reader, Result};
use std::str::from_utf8;

#[test]
fn sample_1_short() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_short.txt"),
        true,
    );
}

#[test]
fn sample_1_full() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_full.txt"),
        false,
    );
}

#[test]
fn sample_2_short() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_short.txt"),
        true,
    );
}

#[test]
fn sample_2_full() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_full.txt"),
        false,
    );
}

// #[test]
// fn sample_3_short() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_3_full() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_4_short() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_4_full() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_full.txt"),
//         false
//     );
//
// }

#[test]
fn sample_ns_short() {
    test(
        include_bytes!("documents/sample_ns.xml"),
        include_bytes!("documents/sample_ns_short.txt"),
        true,
    );
}

#[test]
fn eof_1() {
    test(
        br#"<?xml"#,
        br#"Error: Unexpected EOF during reading XmlDecl."#,
        true,
    );
}

#[test]
fn bad_1() {
    test(
        br#"<?xml&.,"#,
        br#"1:6 Error: Unexpected EOF during reading XmlDecl."#,
        true,
    );
}

#[test]
fn dashes_in_comments() {
    test(
        br#"<hello/>"#,
        br#"
        |Error: Unexpected token '--'
        "#,
        true,
    );

    test(
        br#"<hello/>"#,
        br#"
        |Error: Unexpected token '--'
        "#,
        true,
    );
}

#[test]
fn tabs_1() {
    test(
        b"\t\t",
        br#"
            StartElement(a)
            EmptyElement(b)
            EndElement(a)
            EndDocument
        "#,
        true,
    );
}

#[test]
fn issue_83_duplicate_attributes() {
    // Error when parsing attributes won't stop main event reader
    // as it is a lazy operation => add ending events
    test(
        br#"<hello><some-tag a='10' a="20"/></hello>"#,
        b"
            |StartElement(hello)
            |1:30 EmptyElement(some-tag, attr-error: error while parsing \
                  attribute at position 16: Duplicate attribute at position 9 and 16)
            |EndElement(hello)
            |EndDocument
        ",
        true,
    );
}

#[test]
fn issue_93_large_characters_in_entity_references() {
    test(r#"<hello>&𤶼;</hello>"#.as_bytes(),
         r#"
            |StartElement(hello)
            |1:10 Error while escaping character at range 1..5: Unrecognized escape symbol: Ok("𤶼")
            |EndElement(hello)
            |EndDocument
        "#.as_bytes(),
         true)
}

#[test]
fn issue_98_cdata_ending_with_right_bracket() {
    test(
        br#"<hello><![CDATA[Foo [Bar]]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData(Foo [Bar])
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    )
}

#[test]
fn issue_105_unexpected_double_dash() {
    test(
        br#"<hello>-- </hello>"#,
        br#"
            |StartElement(hello)
            |Characters(-- )
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );

    test(
        br#"<hello>--</hello>"#,
        br#"
            |StartElement(hello)
            |Characters(--)
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );

    test(
        br#"<hello>--></hello>"#,
        br#"
            |StartElement(hello)
            |Characters(-->)
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );

    test(
        br#"<hello><![CDATA[--]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData(--)
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );
}

#[test]
fn issue_attributes_have_no_default_namespace() {
    // At the moment, the 'test' method doesn't render namespaces for attribute names.
    // This test only checks whether the default namespace got applied to the EmptyElement.
    test(
        br#"<hello xmlns="urn:foo" x="y"/>"#,
        br#"
             |EmptyElement({urn:foo}hello [x="y"])
             |EndDocument
         "#,
        true,
    );
}

#[test]
fn issue_default_namespace_on_outermost_element() {
    // Regression test
    test(
        br#"<hello xmlns="urn:foo"/>"#,
        br#"
                |EmptyElement({urn:foo}hello)
                |EndDocument
            "#,
        true,
    );
}

#[test]
fn default_namespace_applies_to_end_elem() {
    test(
        br#"<hello xmlns="urn:foo" x="y">
              <inner/>
            </hello>"#,
        br#"
            |StartElement({urn:foo}hello [x="y"])
            |EmptyElement({urn:foo}inner)
            |EndElement({urn:foo}hello)
            |EndDocument
        "#,
        true,
    );
}

fn test(input: &[u8], output: &[u8], is_short: bool) {
    let mut reader = Reader::from_reader(input);
    reader
        .trim_text(is_short)
        .check_comments(true)
        .expand_empty_elements(false);

    let mut spec_lines = SpecIter(output).enumerate();
    let mut buf = Vec::new();
    let mut ns_buffer = Vec::new();

    if !is_short {
        // discard first whitespace
        reader.read_event(&mut buf).unwrap();
    }

    loop {
        buf.clear();
        let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer);
        let line = xmlrs_display(&event);
        if let Some((n, spec)) = spec_lines.next() {
            if spec.trim() == "EndDocument" {
                break;
            }
            if line.trim() != spec.trim() {
                panic!(
                    "\n-------------------\n\
                     Unexpected event at line {}:\n\
                     Expected: {}\nFound: {}\n\
                     -------------------\n",
                    n + 1,
                    spec,
                    line
                );
            }
        } else {
            if line == "EndDocument" {
                break;
            }
            panic!("Unexpected event: {}", line);
        }

        if !is_short && line.starts_with("StartDocument") {
            // advance next Characters(empty space) ...
            if let Ok(Event::Text(ref e)) = reader.read_event(&mut Vec::new()) {
                if e.iter().any(|b| match *b {
                    b' ' | b'\r' | b'\n' | b'\t' => false,
                    _ => true,
                }) {
                    panic!("Reader expects empty Text event after a StartDocument");
                }
            } else {
                panic!("Reader expects empty Text event after a StartDocument");
            }
        }
    }
}

fn namespace_name(n: &Option<&[u8]>, name: &[u8]) -> String {
    match *n {
        Some(n) => format!("{{{}}}{}", from_utf8(n).unwrap(), from_utf8(name).unwrap()),
        None => from_utf8(name).unwrap().to_owned(),
    }
}

fn make_attrs(e: &BytesStart) -> ::std::result::Result<String, String> {
    let mut atts = Vec::new();
    for a in e.attributes() {
        match a {
            Ok(a) => {
                if a.key.len() < 5 || !a.key.starts_with(b"xmlns") {
                    atts.push(format!(
                        "{}=\"{}\"",
                        from_utf8(a.key).unwrap(),
                        from_utf8(&*a.unescaped_value().unwrap()).unwrap()
                    ));
                }
            }
            Err(e) => return Err(e.to_string()),
        }
    }
    Ok(atts.join(", "))
}

fn xmlrs_display(opt_event: &Result<(Option<&[u8]>, Event)>) -> String {
    match opt_event {
        Ok((ref n, Event::Start(ref e))) => {
            let name = namespace_name(n, e.name());
            match make_attrs(e) {
                Ok(ref attrs) if attrs.is_empty() => format!("StartElement({})", &name),
                Ok(ref attrs) => format!("StartElement({} [{}])", &name, &attrs),
                Err(e) => format!("StartElement({}, attr-error: {})", &name, &e),
            }
        }
        Ok((ref n, Event::Empty(ref e))) => {
            let name = namespace_name(n, e.name());
            match make_attrs(e) {
                Ok(ref attrs) if attrs.is_empty() => format!("EmptyElement({})", &name),
                Ok(ref attrs) => format!("EmptyElement({} [{}])", &name, &attrs),
                Err(e) => format!("EmptyElement({}, attr-error: {})", &name, &e),
            }
        }
        Ok((ref n, Event::End(ref e))) => format!("EndElement({})", namespace_name(n, e.name())),
        Ok((_, Event::Comment(ref e))) => format!("Comment({})", from_utf8(e).unwrap()),
        Ok((_, Event::CData(ref e))) => format!("CData({})", from_utf8(e).unwrap()),
        Ok((_, Event::Text(ref e))) => match e.unescaped() {
            Ok(c) => format!("Characters({})", from_utf8(&*c).unwrap()),
            Err(ref e) => format!("{}", e),
        },
        Ok((_, Event::Decl(ref e))) => {
            let version_cow = e.version().unwrap();
            let version = from_utf8(version_cow.as_ref()).unwrap();
            let encoding_cow = e.encoding().unwrap().unwrap();
            let encoding = from_utf8(encoding_cow.as_ref()).unwrap();
            format!("StartDocument({}, {})", version, encoding)
        }
        Ok((_, Event::Eof)) => format!("EndDocument"),
        Ok((_, Event::PI(ref e))) => format!("ProcessingInstruction(PI={})", from_utf8(e).unwrap()),
        Err(ref e) => format!("Error: {}", e),
        Ok((_, Event::DocType(ref e))) => format!("DocType({})", from_utf8(e).unwrap()),
    }
}

struct SpecIter<'a>(&'a [u8]);

impl<'a> Iterator for SpecIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let start = self
            .0
            .iter()
            .position(|b| match *b {
                b' ' | b'\r' | b'\n' | b'\t' | b'|' | b':' => false,
                b'0'...b'9' => false,
                _ => true,
            })
            .unwrap_or(0);
        if let Some(p) = self.0.windows(2).position(|w| w == b")\n") {
            let (prev, next) = self.0.split_at(p + 1);
            self.0 = next;
            Some(from_utf8(&prev[start..]).expect("Error decoding to uft8"))
        } else {
            if self.0.is_empty() {
                None
            } else {
                let p = self.0;
                self.0 = &[];
                Some(from_utf8(&p[start..]).unwrap())
            }
        }
    }
}
<commit_msg>Rerun rustfmt<commit_after>extern crate quick_xml;

use quick_xml::events::{BytesStart, Event};
use quick_xml::{Reader, Result};
use std::str::from_utf8;

#[test]
fn sample_1_short() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_short.txt"),
        true,
    );
}

#[test]
fn sample_1_full() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_full.txt"),
        false,
    );
}

#[test]
fn sample_2_short() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_short.txt"),
        true,
    );
}

#[test]
fn sample_2_full() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_full.txt"),
        false,
    );
}

// #[test]
// fn sample_3_short() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_3_full() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_4_short() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_4_full() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_full.txt"),
//         false
//     );
//
// }

#[test]
fn sample_ns_short() {
    test(
        include_bytes!("documents/sample_ns.xml"),
        include_bytes!("documents/sample_ns_short.txt"),
        true,
    );
}

#[test]
fn eof_1() {
    test(
        br#"<?xml"#,
        br#"Error: Unexpected EOF during reading XmlDecl."#,
        true,
    );
}

#[test]
fn bad_1() {
    test(
        br#"<?xml&.,"#,
        br#"1:6 Error: Unexpected EOF during reading XmlDecl."#,
        true,
    );
}

#[test]
fn dashes_in_comments() {
    test(
        br#"<hello/>"#,
        br#"
        |Error: Unexpected token '--'
        "#,
        true,
    );

    test(
        br#"<hello/>"#,
        br#"
        |Error: Unexpected token '--'
        "#,
        true,
    );
}

#[test]
fn tabs_1() {
    test(
        b"\t\t",
        br#"
            StartElement(a)
            EmptyElement(b)
            EndElement(a)
            EndDocument
        "#,
        true,
    );
}

#[test]
fn issue_83_duplicate_attributes() {
    // Error when parsing attributes won't stop main event reader
    // as it is a lazy operation => add ending events
    test(
        br#"<hello><some-tag a='10' a="20"/></hello>"#,
        b"
            |StartElement(hello)
            |1:30 EmptyElement(some-tag, attr-error: error while parsing \
                  attribute at position 16: Duplicate attribute at position 9 and 16)
            |EndElement(hello)
            |EndDocument
        ",
        true,
    );
}

#[test]
fn issue_93_large_characters_in_entity_references() {
    test(
        r#"<hello>&𤶼;</hello>"#.as_bytes(),
        r#"
            |StartElement(hello)
            |1:10 Error while escaping character at range 1..5: Unrecognized escape symbol: Ok("𤶼")
            |EndElement(hello)
            |EndDocument
        "#
        .as_bytes(),
        true,
    )
}

#[test]
fn issue_98_cdata_ending_with_right_bracket() {
    test(
        br#"<hello><![CDATA[Foo [Bar]]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData(Foo [Bar])
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    )
}

#[test]
fn issue_105_unexpected_double_dash() {
    test(
        br#"<hello>-- </hello>"#,
        br#"
            |StartElement(hello)
            |Characters(-- )
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );

    test(
        br#"<hello>--</hello>"#,
        br#"
            |StartElement(hello)
            |Characters(--)
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );

    test(
        br#"<hello>--></hello>"#,
        br#"
            |StartElement(hello)
            |Characters(-->)
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );

    test(
        br#"<hello><![CDATA[--]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData(--)
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false,
    );
}

#[test]
fn issue_attributes_have_no_default_namespace() {
    // At the moment, the 'test' method doesn't render namespaces for attribute names.
    // This test only checks whether the default namespace got applied to the EmptyElement.
    test(
        br#"<hello xmlns="urn:foo" x="y"/>"#,
        br#"
             |EmptyElement({urn:foo}hello [x="y"])
             |EndDocument
         "#,
        true,
    );
}

#[test]
fn issue_default_namespace_on_outermost_element() {
    // Regression test
    test(
        br#"<hello xmlns="urn:foo"/>"#,
        br#"
                |EmptyElement({urn:foo}hello)
                |EndDocument
            "#,
        true,
    );
}

#[test]
fn default_namespace_applies_to_end_elem() {
    test(
        br#"<hello xmlns="urn:foo" x="y">
              <inner/>
            </hello>"#,
        br#"
            |StartElement({urn:foo}hello [x="y"])
            |EmptyElement({urn:foo}inner)
            |EndElement({urn:foo}hello)
            |EndDocument
        "#,
        true,
    );
}

fn test(input: &[u8], output: &[u8], is_short: bool) {
    let mut reader = Reader::from_reader(input);
    reader
        .trim_text(is_short)
        .check_comments(true)
        .expand_empty_elements(false);

    let mut spec_lines = SpecIter(output).enumerate();
    let mut buf = Vec::new();
    let mut ns_buffer = Vec::new();

    if !is_short {
        // discard first whitespace
        reader.read_event(&mut buf).unwrap();
    }

    loop {
        buf.clear();
        let event = reader.read_namespaced_event(&mut buf, &mut ns_buffer);
        let line = xmlrs_display(&event);
        if let Some((n, spec)) = spec_lines.next() {
            if spec.trim() == "EndDocument" {
                break;
            }
            if line.trim() != spec.trim() {
                panic!(
                    "\n-------------------\n\
                     Unexpected event at line {}:\n\
                     Expected: {}\nFound: {}\n\
                     -------------------\n",
                    n + 1,
                    spec,
                    line
                );
            }
        } else {
            if line == "EndDocument" {
                break;
            }
            panic!("Unexpected event: {}", line);
        }

        if !is_short && line.starts_with("StartDocument") {
            // advance next Characters(empty space) ...
            if let Ok(Event::Text(ref e)) = reader.read_event(&mut Vec::new()) {
                if e.iter().any(|b| match *b {
                    b' ' | b'\r' | b'\n' | b'\t' => false,
                    _ => true,
                }) {
                    panic!("Reader expects empty Text event after a StartDocument");
                }
            } else {
                panic!("Reader expects empty Text event after a StartDocument");
            }
        }
    }
}

fn namespace_name(n: &Option<&[u8]>, name: &[u8]) -> String {
    match *n {
        Some(n) => format!("{{{}}}{}", from_utf8(n).unwrap(), from_utf8(name).unwrap()),
        None => from_utf8(name).unwrap().to_owned(),
    }
}

fn make_attrs(e: &BytesStart) -> ::std::result::Result<String, String> {
    let mut atts = Vec::new();
    for a in e.attributes() {
        match a {
            Ok(a) => {
                if a.key.len() < 5 || !a.key.starts_with(b"xmlns") {
                    atts.push(format!(
                        "{}=\"{}\"",
                        from_utf8(a.key).unwrap(),
                        from_utf8(&*a.unescaped_value().unwrap()).unwrap()
                    ));
                }
            }
            Err(e) => return Err(e.to_string()),
        }
    }
    Ok(atts.join(", "))
}

fn xmlrs_display(opt_event: &Result<(Option<&[u8]>, Event)>) -> String {
    match opt_event {
        Ok((ref n, Event::Start(ref e))) => {
            let name = namespace_name(n, e.name());
            match make_attrs(e) {
                Ok(ref attrs) if attrs.is_empty() => format!("StartElement({})", &name),
                Ok(ref attrs) => format!("StartElement({} [{}])", &name, &attrs),
                Err(e) => format!("StartElement({}, attr-error: {})", &name, &e),
            }
        }
        Ok((ref n, Event::Empty(ref e))) => {
            let name = namespace_name(n, e.name());
            match make_attrs(e) {
                Ok(ref attrs) if attrs.is_empty() => format!("EmptyElement({})", &name),
                Ok(ref attrs) => format!("EmptyElement({} [{}])", &name, &attrs),
                Err(e) => format!("EmptyElement({}, attr-error: {})", &name, &e),
            }
        }
        Ok((ref n, Event::End(ref e))) => format!("EndElement({})", namespace_name(n, e.name())),
        Ok((_, Event::Comment(ref e))) => format!("Comment({})", from_utf8(e).unwrap()),
        Ok((_, Event::CData(ref e))) => format!("CData({})", from_utf8(e).unwrap()),
        Ok((_, Event::Text(ref e))) => match e.unescaped() {
            Ok(c) => format!("Characters({})", from_utf8(&*c).unwrap()),
            Err(ref e) => format!("{}", e),
        },
        Ok((_, Event::Decl(ref e))) => {
            let version_cow = e.version().unwrap();
            let version = from_utf8(version_cow.as_ref()).unwrap();
            let encoding_cow = e.encoding().unwrap().unwrap();
            let encoding = from_utf8(encoding_cow.as_ref()).unwrap();
            format!("StartDocument({}, {})", version, encoding)
        }
        Ok((_, Event::Eof)) => format!("EndDocument"),
        Ok((_, Event::PI(ref e))) => format!("ProcessingInstruction(PI={})", from_utf8(e).unwrap()),
        Err(ref e) => format!("Error: {}", e),
        Ok((_, Event::DocType(ref e))) => format!("DocType({})", from_utf8(e).unwrap()),
    }
}

struct SpecIter<'a>(&'a [u8]);

impl<'a> Iterator for SpecIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let start = self
            .0
            .iter()
            .position(|b| match *b {
                b' ' | b'\r' | b'\n' | b'\t' | b'|' | b':' => false,
                b'0'...b'9' => false,
                _ => true,
            })
            .unwrap_or(0);
        if let Some(p) = self.0.windows(2).position(|w| w == b")\n") {
            let (prev, next) = self.0.split_at(p + 1);
            self.0 = next;
            Some(from_utf8(&prev[start..]).expect("Error decoding to uft8"))
        } else {
            if self.0.is_empty() {
                None
            } else {
                let p = self.0;
                self.0 = &[];
                Some(from_utf8(&p[start..]).unwrap())
            }
        }
    }
}
<|endoftext|>


<commit_before>//! [GET /_matrix/client/r0/rooms/{roomId}/members](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-rooms-roomid-members)

use ruma_api::ruma_api;
use ruma_events::{room::member::MemberEvent, EventResult};
use ruma_identifiers::RoomId;

ruma_api! {
    metadata {
        description: "Get membership events for a room.",
        method: GET,
        name: "get_member_events",
        path: "/_matrix/client/r0/rooms/:room_id/members",
        rate_limited: false,
        requires_authentication: true,
    }

    request {
        /// The room to get the member events for.
        #[ruma_api(path)]
        pub room_id: RoomId,
    }

    response {
        /// A list of member events.
        #[wrap_incoming(MemberEvent with EventResult)]
        pub chunk: Vec<MemberEvent>
    }

    error: crate::Error
}
<commit_msg> Add missing query parameters to members list endpoint<commit_after>//! [GET /_matrix/client/r0/rooms/{roomId}/members](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-members)

use ruma_api::ruma_api;
use ruma_events::{room::member::MemberEvent, EventResult};
use ruma_identifiers::RoomId;
use serde::{Deserialize, Serialize};

ruma_api! {
    metadata {
        description: "Get membership events for a room.",
        method: GET,
        name: "get_member_events",
        path: "/_matrix/client/r0/rooms/:room_id/members",
        rate_limited: false,
        requires_authentication: true,
    }

    request {
        /// The room to get the member events for.
        #[ruma_api(path)]
        pub room_id: RoomId,

        /// The point in time (pagination token) to return members for in the room. This token can
        /// be obtained from a prev_batch token returned for each room by the sync API.
        #[serde(skip_serializing_if = "Option::is_none")]
        #[ruma_api(query)]
        pub at: Option<String>,

        /// The kind of memberships to filter for. Defaults to no filtering if unspecified. When
        /// specified alongside not_membership, the two parameters create an 'or' condition: either
        /// the membership is the same as membership or is not the same as not_membership.
        #[serde(skip_serializing_if = "Option::is_none")]
        #[ruma_api(query)]
        pub membership: Option<MembershipEventFilter>,

        /// The kind of memberships to *exclude* from the results. Defaults to no filtering if
        /// unspecified.
        #[serde(skip_serializing_if = "Option::is_none")]
        #[ruma_api(query)]
        pub not_membership: Option<MembershipEventFilter>,
    }

    response {
        /// A list of member events.
        #[wrap_incoming(MemberEvent with EventResult)]
        pub chunk: Vec<MemberEvent>
    }

    error: crate::Error
}

/// The kind of membership events to filter for.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum MembershipEventFilter {
    /// The user has joined.
    Join,
    /// The user has been invited.
    Invite,
    /// The user has left.
    Leave,
    /// The user has been banned.
    Ban,
}
<|endoftext|>