<commit_before>use docopt::Docopt;
use log::{warn, error};
use std::{
    fmt,
    process,
};
use serde_derive::Deserialize;

mod caps;
mod image;
mod tlv;
pub mod testlog;

use simflash::{SimFlash, SimFlashMap};
use mcuboot_sys::{c, AreaDesc, FlashId};

use crate::image::{
    Images,
    install_image,
    mark_upgrade,
    SlotInfo,
    show_sizes,
};

const USAGE: &'static str = "
Mcuboot simulator

Usage:
  bootsim sizes
  bootsim run --device TYPE [--align SIZE]
  bootsim runall
  bootsim (--help | --version)

Options:
  -h, --help         Show this message
  --version          Version
  --device TYPE      MCU to simulate
                     Valid values: stm32f4, k64f
  --align SIZE       Flash write alignment
";

#[derive(Debug, Deserialize)]
struct Args {
    flag_help: bool,
    flag_version: bool,
    flag_device: Option<DeviceName>,
    flag_align: Option<AlignArg>,
    cmd_sizes: bool,
    cmd_run: bool,
    cmd_runall: bool,
}

#[derive(Copy, Clone, Debug, Deserialize)]
pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840, Nrf52840SpiFlash, }

pub static ALL_DEVICES: &'static [DeviceName] = &[
    DeviceName::Stm32f4,
    DeviceName::K64f,
    DeviceName::K64fBig,
    DeviceName::Nrf52840,
    DeviceName::Nrf52840SpiFlash,
];

impl fmt::Display for DeviceName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = match *self {
            DeviceName::Stm32f4 => "stm32f4",
            DeviceName::K64f => "k64f",
            DeviceName::K64fBig => "k64fbig",
            DeviceName::Nrf52840 => "nrf52840",
            DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash",
        };
        f.write_str(name)
    }
}

#[derive(Debug)]
struct AlignArg(u8);

struct AlignArgVisitor;

impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
    type Value = AlignArg;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("1, 2, 4 or 8")
    }

    fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
        where E: serde::de::Error
    {
        Ok(match n {
            1 | 2 | 4 | 8 => AlignArg(n),
            n => {
                let err = format!("Could not deserialize '{}' as alignment", n);
                return Err(E::custom(err));
            }
        })
    }
}

impl<'de> serde::de::Deserialize<'de> for AlignArg {
    fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
        where D: serde::de::Deserializer<'de>
    {
        d.deserialize_u8(AlignArgVisitor)
    }
}

pub fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    // println!("args: {:#?}", args);

    if args.cmd_sizes {
        show_sizes();
        return;
    }

    let mut status = RunStatus::new();
    if args.cmd_run {

        let align = args.flag_align.map(|x| x.0).unwrap_or(1);

        let device = match args.flag_device {
            None => panic!("Missing mandatory device argument"),
            Some(dev) => dev,
        };

        status.run_single(device, align, 0xff);
    }

    if args.cmd_runall {
        for &dev in ALL_DEVICES {
            for &align in &[1, 2, 4, 8] {
                for &erased_val in &[0, 0xff] {
                    status.run_single(dev, align, erased_val);
                }
            }
        }
    }

    if status.failures > 0 {
        error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
        process::exit(1);
    } else {
        error!("{} Tests ran successfully", status.passes);
        process::exit(0);
    }
}

/// A test run, intended to be run from "cargo test", so panics on failure.
pub struct Run {
    flashmap: SimFlashMap,
    areadesc: AreaDesc,
    slots: [SlotInfo; 2],
}

impl Run {
    pub fn new(device: DeviceName, align: u8, erased_val: u8) -> Run {
        let (flashmap, areadesc) = make_device(device, align, erased_val);

        let (primary_slot_base, primary_slot_len, primary_slot_dev_id) =
             areadesc.find(FlashId::Image0);
        let (secondary_slot_base, secondary_slot_len, secondary_slot_dev_id) =
             areadesc.find(FlashId::Image1);

        // NOTE: not accounting "swap_size" because it is not used by sim...
        let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;

        // Construct a primary image.
        let primary_slot = SlotInfo {
            base_off: primary_slot_base as usize,
            trailer_off: primary_slot_base + primary_slot_len - offset_from_end,
            len: primary_slot_len as usize,
            dev_id: primary_slot_dev_id,
        };

        // And an upgrade image.
        let secondary_slot = SlotInfo {
            base_off: secondary_slot_base as usize,
            trailer_off: secondary_slot_base + secondary_slot_len - offset_from_end,
            len: secondary_slot_len as usize,
            dev_id: secondary_slot_dev_id,
        };

        Run {
            flashmap: flashmap,
            areadesc: areadesc,
            slots: [primary_slot, secondary_slot],
        }
    }

    pub fn each_device<F>(f: F)
        where F: Fn(&mut Run)
    {
        for &dev in ALL_DEVICES {
            for &align in &[1, 2, 4, 8] {
                for &erased_val in &[0, 0xff] {
                    let mut run = Run::new(dev, align, erased_val);
                    f(&mut run);
                }
            }
        }
    }

    /// Construct an `Images` that doesn't expect an upgrade to happen.
    pub fn make_no_upgrade_image(&self) -> Images {
        let mut flashmap = self.flashmap.clone();
        let primaries = install_image(&mut flashmap, &self.slots, 0, 32784, false);
        let upgrades = install_image(&mut flashmap, &self.slots, 1, 41928, false);
        Images {
            flashmap: flashmap,
            areadesc: self.areadesc.clone(),
            slots: [self.slots[0].clone(), self.slots[1].clone()],
            primaries: primaries,
            upgrades: upgrades,
            total_count: None,
        }
    }

    /// Construct an `Images` for normal testing.
    pub fn make_image(&self) -> Images {
        let mut images = self.make_no_upgrade_image();
        mark_upgrade(&mut images.flashmap, &images.slots[1]);

        // upgrades without fails, counts number of flash operations
        let total_count = match images.run_basic_upgrade() {
            Ok(v)  => v,
            Err(_) => {
                panic!("Unable to perform basic upgrade");
            },
        };

        images.total_count = Some(total_count);
        images
    }

    pub fn make_bad_secondary_slot_image(&self) -> Images {
        let mut bad_flashmap = self.flashmap.clone();
        let primaries = install_image(&mut bad_flashmap, &self.slots, 0, 32784, false);
        let upgrades = install_image(&mut bad_flashmap, &self.slots, 1, 41928, true);
        Images {
            flashmap: bad_flashmap,
            areadesc: self.areadesc.clone(),
            slots: [self.slots[0].clone(), self.slots[1].clone()],
            primaries: primaries,
            upgrades: upgrades,
            total_count: None,
        }
    }

}

pub struct RunStatus {
    failures: usize,
    passes: usize,
}

impl RunStatus {
    pub fn new() -> RunStatus {
        RunStatus {
            failures: 0,
            passes: 0,
        }
    }

    pub fn run_single(&mut self, device: DeviceName, align: u8, erased_val: u8) {
        warn!("Running on device {} with alignment {}", device, align);

        let run = Run::new(device, align, erased_val);

        let mut failed = false;

        // Creates a badly signed image in the secondary slot to check that
        // it is not upgraded to
        let bad_secondary_slot_image = run.make_bad_secondary_slot_image();

        failed |= bad_secondary_slot_image.run_signfail_upgrade();

        let images = run.make_no_upgrade_image();
        failed |= images.run_norevert_newimage();

        let images = run.make_image();

        failed |= images.run_basic_revert();
        failed |= images.run_revert_with_fails();
        failed |= images.run_perm_with_fails();
        failed |= images.run_perm_with_random_fails(5);
        failed |= images.run_norevert();

        failed |= images.run_with_status_fails_complete();
        failed |= images.run_with_status_fails_with_reset();

        //show_flash(&flash);

        if failed {
            self.failures += 1;
        } else {
            self.passes += 1;
        }
    }

    pub fn failures(&self) -> usize {
        self.failures
    }
}

/// Build the Flash and area descriptor for a given device.
pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlashMap, AreaDesc) {
    match device {
        DeviceName::Stm32f4 => {
            // STM style flash.  Large sectors, with a large scratch area.
            let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
                                      64 * 1024,
                                      128 * 1024, 128 * 1024, 128 * 1024],
                                      align as usize, erased_val);
            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::K64f => {
            // NXP style flash.  Small sectors, one small sector for scratch.
            let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);

            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
            areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::K64fBig => {
            // Simulating an STM style flash on top of an NXP style flash.  Underlying flash device
            // uses small sectors, but we tell the bootloader they are large.
            let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);

            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id);
            areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id);
            areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::Nrf52840 => {
            // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
            // does not divide into the image size.
            let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);

            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id);
            areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id);
            areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::Nrf52840SpiFlash => {
            // Simulate nrf52840 with external SPI flash. The external SPI flash
            // has a larger sector size so for now store scratch on that flash.
            let flash0 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
            let flash1 = SimFlash::new(vec![8192; 64], align as usize, erased_val);

            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(0, &flash0);
            areadesc.add_flash_sectors(1, &flash1);

            areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0);
            areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1);
            areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(0, flash0);
            flashmap.insert(1, flash1);
            (flashmap, areadesc)
        }
    }
}
<commit_msg>sim: Make `Run` into a builder for `Images`<commit_after>use docopt::Docopt;
use log::{warn, error};
use std::{
    fmt,
    process,
};
use serde_derive::Deserialize;

mod caps;
mod image;
mod tlv;
pub mod testlog;

use simflash::{SimFlash, SimFlashMap};
use mcuboot_sys::{c, AreaDesc, FlashId};

use crate::image::{
    Images,
    install_image,
    mark_upgrade,
    SlotInfo,
    show_sizes,
};

const USAGE: &'static str = "
Mcuboot simulator

Usage:
  bootsim sizes
  bootsim run --device TYPE [--align SIZE]
  bootsim runall
  bootsim (--help | --version)

Options:
  -h, --help         Show this message
  --version          Version
  --device TYPE      MCU to simulate
                     Valid values: stm32f4, k64f
  --align SIZE       Flash write alignment
";

#[derive(Debug, Deserialize)]
struct Args {
    flag_help: bool,
    flag_version: bool,
    flag_device: Option<DeviceName>,
    flag_align: Option<AlignArg>,
    cmd_sizes: bool,
    cmd_run: bool,
    cmd_runall: bool,
}

#[derive(Copy, Clone, Debug, Deserialize)]
pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840, Nrf52840SpiFlash, }

pub static ALL_DEVICES: &'static [DeviceName] = &[
    DeviceName::Stm32f4,
    DeviceName::K64f,
    DeviceName::K64fBig,
    DeviceName::Nrf52840,
    DeviceName::Nrf52840SpiFlash,
];

impl fmt::Display for DeviceName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = match *self {
            DeviceName::Stm32f4 => "stm32f4",
            DeviceName::K64f => "k64f",
            DeviceName::K64fBig => "k64fbig",
            DeviceName::Nrf52840 => "nrf52840",
            DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash",
        };
        f.write_str(name)
    }
}

#[derive(Debug)]
struct AlignArg(u8);

struct AlignArgVisitor;

impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
    type Value = AlignArg;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("1, 2, 4 or 8")
    }

    fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
        where E: serde::de::Error
    {
        Ok(match n {
            1 | 2 | 4 | 8 => AlignArg(n),
            n => {
                let err = format!("Could not deserialize '{}' as alignment", n);
                return Err(E::custom(err));
            }
        })
    }
}

impl<'de> serde::de::Deserialize<'de> for AlignArg {
    fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
        where D: serde::de::Deserializer<'de>
    {
        d.deserialize_u8(AlignArgVisitor)
    }
}

pub fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    // println!("args: {:#?}", args);

    if args.cmd_sizes {
        show_sizes();
        return;
    }

    let mut status = RunStatus::new();
    if args.cmd_run {

        let align = args.flag_align.map(|x| x.0).unwrap_or(1);

        let device = match args.flag_device {
            None => panic!("Missing mandatory device argument"),
            Some(dev) => dev,
        };

        status.run_single(device, align, 0xff);
    }

    if args.cmd_runall {
        for &dev in ALL_DEVICES {
            for &align in &[1, 2, 4, 8] {
                for &erased_val in &[0, 0xff] {
                    status.run_single(dev, align, erased_val);
                }
            }
        }
    }

    if status.failures > 0 {
        error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
        process::exit(1);
    } else {
        error!("{} Tests ran successfully", status.passes);
        process::exit(0);
    }
}

/// A Run describes a single run of the simulator.  It captures the
/// configuration of a particular device configuration, including the flash
/// devices and the information about the slots.  This can be thought of as
/// a builder for `Images`.
#[derive(Clone)]
pub struct Run {
    flashmap: SimFlashMap,
    areadesc: AreaDesc,
    slots: [SlotInfo; 2],
}

impl Run {
    pub fn new(device: DeviceName, align: u8, erased_val: u8) -> Run {
        let (flashmap, areadesc) = make_device(device, align, erased_val);

        let (primary_slot_base, primary_slot_len, primary_slot_dev_id) =
             areadesc.find(FlashId::Image0);
        let (secondary_slot_base, secondary_slot_len, secondary_slot_dev_id) =
             areadesc.find(FlashId::Image1);

        // NOTE: not accounting "swap_size" because it is not used by sim...
        let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;

        // Construct a primary image.
        let primary_slot = SlotInfo {
            base_off: primary_slot_base as usize,
            trailer_off: primary_slot_base + primary_slot_len - offset_from_end,
            len: primary_slot_len as usize,
            dev_id: primary_slot_dev_id,
        };

        // And an upgrade image.
        let secondary_slot = SlotInfo {
            base_off: secondary_slot_base as usize,
            trailer_off: secondary_slot_base + secondary_slot_len - offset_from_end,
            len: secondary_slot_len as usize,
            dev_id: secondary_slot_dev_id,
        };

        Run {
            flashmap: flashmap,
            areadesc: areadesc,
            slots: [primary_slot, secondary_slot],
        }
    }

    pub fn each_device<F>(f: F)
        where F: Fn(Run)
    {
        for &dev in ALL_DEVICES {
            for &align in &[1, 2, 4, 8] {
                for &erased_val in &[0, 0xff] {
                    let run = Run::new(dev, align, erased_val);
                    f(run);
                }
            }
        }
    }

    /// Construct an `Images` that doesn't expect an upgrade to happen.
    pub fn make_no_upgrade_image(self) -> Images {
        let mut flashmap = self.flashmap;
        let primaries = install_image(&mut flashmap, &self.slots, 0, 32784, false);
        let upgrades = install_image(&mut flashmap, &self.slots, 1, 41928, false);
        Images {
            flashmap: flashmap,
            areadesc: self.areadesc,
            slots: self.slots,
            primaries: primaries,
            upgrades: upgrades,
            total_count: None,
        }
    }

    /// Construct an `Images` for normal testing.
    pub fn make_image(self) -> Images {
        let mut images = self.make_no_upgrade_image();
        mark_upgrade(&mut images.flashmap, &images.slots[1]);

        // upgrades without fails, counts number of flash operations
        let total_count = match images.run_basic_upgrade() {
            Ok(v)  => v,
            Err(_) => {
                panic!("Unable to perform basic upgrade");
            },
        };

        images.total_count = Some(total_count);
        images
    }

    pub fn make_bad_secondary_slot_image(self) -> Images {
        let mut bad_flashmap = self.flashmap.clone();
        let primaries = install_image(&mut bad_flashmap, &self.slots, 0, 32784, false);
        let upgrades = install_image(&mut bad_flashmap, &self.slots, 1, 41928, true);
        Images {
            flashmap: bad_flashmap,
            areadesc: self.areadesc,
            slots: self.slots,
            primaries: primaries,
            upgrades: upgrades,
            total_count: None,
        }
    }

}

pub struct RunStatus {
    failures: usize,
    passes: usize,
}

impl RunStatus {
    pub fn new() -> RunStatus {
        RunStatus {
            failures: 0,
            passes: 0,
        }
    }

    pub fn run_single(&mut self, device: DeviceName, align: u8, erased_val: u8) {
        warn!("Running on device {} with alignment {}", device, align);

        let run = Run::new(device, align, erased_val);

        let mut failed = false;

        // Creates a badly signed image in the secondary slot to check that
        // it is not upgraded to
        let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image();

        failed |= bad_secondary_slot_image.run_signfail_upgrade();

        let images = run.clone().make_no_upgrade_image();
        failed |= images.run_norevert_newimage();

        let images = run.make_image();

        failed |= images.run_basic_revert();
        failed |= images.run_revert_with_fails();
        failed |= images.run_perm_with_fails();
        failed |= images.run_perm_with_random_fails(5);
        failed |= images.run_norevert();

        failed |= images.run_with_status_fails_complete();
        failed |= images.run_with_status_fails_with_reset();

        //show_flash(&flash);

        if failed {
            self.failures += 1;
        } else {
            self.passes += 1;
        }
    }

    pub fn failures(&self) -> usize {
        self.failures
    }
}

/// Build the Flash and area descriptor for a given device.
pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlashMap, AreaDesc) {
    match device {
        DeviceName::Stm32f4 => {
            // STM style flash.  Large sectors, with a large scratch area.
            let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
                                      64 * 1024,
                                      128 * 1024, 128 * 1024, 128 * 1024],
                                      align as usize, erased_val);
            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::K64f => {
            // NXP style flash.  Small sectors, one small sector for scratch.
            let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);

            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
            areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::K64fBig => {
            // Simulating an STM style flash on top of an NXP style flash.  Underlying flash device
            // uses small sectors, but we tell the bootloader they are large.
            let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);

            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id);
            areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id);
            areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::Nrf52840 => {
            // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
            // does not divide into the image size.
            let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);

            let dev_id = 0;
            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(dev_id, &flash);
            areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id);
            areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id);
            areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(dev_id, flash);
            (flashmap, areadesc)
        }
        DeviceName::Nrf52840SpiFlash => {
            // Simulate nrf52840 with external SPI flash. The external SPI flash
            // has a larger sector size so for now store scratch on that flash.
            let flash0 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
            let flash1 = SimFlash::new(vec![8192; 64], align as usize, erased_val);

            let mut areadesc = AreaDesc::new();
            areadesc.add_flash_sectors(0, &flash0);
            areadesc.add_flash_sectors(1, &flash1);

            areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0);
            areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1);
            areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1);

            let mut flashmap = SimFlashMap::new();
            flashmap.insert(0, flash0);
            flashmap.insert(1, flash1);
            (flashmap, areadesc)
        }
    }
}
<|endoftext|>


<commit_before>#![feature(try_trait)]

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

use std::env;
use std::str;
use std::fs::File;
use std::option::NoneError;

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

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

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

#[derive(Debug)]
struct TimeControlError;

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

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

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

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

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

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

struct Indexer {
    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,
    current_game: Vec<u8>,
    plies: usize,
    batch: Vec<u8>,
    batch_size: usize,
}

impl Indexer {
    fn new() -> Indexer {
        Indexer {
            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,
            current_game: Vec::new(),
            plies: 0,
            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            // TODO: Send
            println!("{}", str::from_utf8(&self.batch).expect("utf8"));

            self.batch_size = 0;
            self.batch.clear();
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

use std::env;
use std::str;
use std::fs::File;
use std::option::NoneError;

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

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

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

#[derive(Debug)]
struct TimeControlError;

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

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

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

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

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

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

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

    current_game: Vec<u8>,
    plies: usize,

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

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

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

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

    fn send(&mut self) {
        if self.batch_size > 0 {
            // TODO: Send
            println!("{}", str::from_utf8(&self.batch).expect("utf8"));

            self.batch_size = 0;
            self.batch.clear();
        }
    }
}

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

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

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

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

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

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

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

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

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

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

        Skip(self.skip)
    }

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

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

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

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

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

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

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

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


<commit_before>use num::{Num, ToPrimitive};

use std::ops::{Add, Sub};

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Point<T: Num + ToPrimitive> {
    pub x: T,
    pub y: T,
}

impl<T: Num + ToPrimitive> Point<T> {
    pub fn new(x: T, y: T) -> Point<T> {
        Point {
            x: x,
            y: y
        }
    }

    pub fn sqr_radius(&self) -> f64 {
        let x = self.x.to_f64().expect("Failed to convert to float.");
        let y = self.y.to_f64().expect("Failed to convert to float.");
        x*x + y*y
    }
}

impl<T: Num + ToPrimitive> Add for Point<T> {
    type Output = Point<T>;
    fn add(self, Point{x, y}: Point<T>) -> Point<T> {
        Point{
            x: self.x + x,
            y: self.y + y,
        }
    }
}

impl<T: Num + ToPrimitive> Sub for Point<T> {
    type Output = Point<T>;
    fn sub(self, Point{x, y}: Point<T>) -> Point<T> {
        Point{
            x: self.x - x,
            y: self.y - y,
        }
    }
}<commit_msg>Added radius() function.<commit_after>use num::{Num, ToPrimitive};

use std::ops::{Add, Sub};

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Point<T: Num + ToPrimitive> {
    pub x: T,
    pub y: T,
}

impl<T: Num + ToPrimitive> Point<T> {
    pub fn new(x: T, y: T) -> Point<T> {
        Point {
            x: x,
            y: y
        }
    }

    pub fn radius(&self) -> f64 {
        self.sqr_radius().sqrt()
    }

    pub fn sqr_radius(&self) -> f64 {
        let x = self.x.to_f64().expect("Failed to convert to float.");
        let y = self.y.to_f64().expect("Failed to convert to float.");
        x*x + y*y
    }
}

impl<T: Num + ToPrimitive> Add for Point<T> {
    type Output = Point<T>;
    fn add(self, Point{x, y}: Point<T>) -> Point<T> {
        Point{
            x: self.x + x,
            y: self.y + y,
        }
    }
}

impl<T: Num + ToPrimitive> Sub for Point<T> {
    type Output = Point<T>;
    fn sub(self, Point{x, y}: Point<T>) -> Point<T> {
        Point{
            x: self.x - x,
            y: self.y - y,
        }
    }
}<|endoftext|>


<commit_before>use tcod::colors::{Color};

use traits::{Renderable, Movable, Position};
use Direction;
use point::Point;

use std::fs::File;
use std::path::Path;

use rand;
use rand::Rng;
use rand::distributions::{Weighted};

use serde_yaml;

const ERR_UNIT_LOAD: &str = "Unable to load unit type data.";

#[derive(Debug)]
pub struct UnitType {
    name: String,
    glyph: char,
    color: Color,
    is_blocking: bool,
    max_hp: u8,
    defence: u8,
    attack: u8,
}

impl UnitType {
    // Only used for the player for now.
    pub fn new(name: &str, glyph: char, color: Color) -> UnitType {
        UnitType {
            name: name.into(),
            glyph: glyph,
            color: color,
            is_blocking: true,
            // Following based on standard human in the data files.
            max_hp: 30,
            defence: 3,
            attack: 5,
        }
    }
}

impl<'a> From<&'a UnitTypeRaw> for UnitType {
    fn from(raw: &'a UnitTypeRaw) -> UnitType {
        UnitType {
            name: raw.name.clone(),
            glyph: raw.glyph,
            color: Color::new(raw.color[0], raw.color[1], raw.color[2]),
            is_blocking: raw.is_blocking,
            max_hp: raw.max_hp,
            defence: raw.defence,
            attack: raw.attack,
        }
    }
}

// The Color type in tcod isn't deserializable, so have this to make reading
// the data file easier.
#[derive(Debug, Deserialize)]
struct UnitTypeRaw {
    name: String,
    glyph: char,
    color: [u8; 3],
    chance: u32,
    is_blocking: bool,
    max_hp: u8,
    defence: u8,
    attack: u8,
}

pub struct UnitTypeLists {
    pub types: Vec<UnitType>,
    weights: Vec<Weighted<usize>>,
    max_weight: u32,
}

impl UnitTypeLists {
    pub fn get_random_type(&self, rng: &mut rand::ThreadRng) -> &UnitType {
        let mut val = rng.gen_range(0, self.max_weight);

        for weight in self.weights.iter() {
            if weight.weight > val {
                return &self.types[weight.item];
            }

            val -= weight.weight;
        }

        unreachable!()
    }
}

pub fn load_unit_types() -> UnitTypeLists{
    let path = Path::new("data").join("unit_types.yaml");
    let data_file = File::open(&path).expect(ERR_UNIT_LOAD);
    let raw_units: Vec<UnitTypeRaw> = serde_yaml::from_reader(&data_file).expect(ERR_UNIT_LOAD);

    let mut types = vec![];
    let mut weights = vec![];

    let mut running_total = 0;
    for (i, raw_unit) in raw_units.iter().enumerate() {
        types.push(raw_unit.into());
        running_total += raw_unit.chance;

        weights.push( Weighted{ weight: raw_unit.chance, item: i });
    }

    UnitTypeLists {
        types: types,
        weights: weights,
        max_weight: running_total,
    }
}

#[derive(Debug)]
pub struct Unit<'a> {
    position: Point<i16>,
    unit_type: &'a UnitType,
    cur_hp: u8,
}

impl<'a> Unit<'a> {
    pub fn new(pos: Point<i16>, unit_type: &'a UnitType) -> Unit<'a> {
        let cur_hp = unit_type.max_hp;
        Unit {
            position: pos,
            unit_type: unit_type,
            cur_hp: cur_hp,
        }
    }

    pub fn is_blocking(&self) -> bool {
        self.unit_type.is_blocking
    }

    pub fn get_name(&self) -> &str {
        &self.unit_type.name
    }

    pub fn take_turn(&mut self) {
        println!("The {} growls", self.get_name());
    }
}

impl<'a> Position for Unit<'a> {
    fn get_x(&self) -> i16 {
        self.position.x
    }

    fn get_y(&self) -> i16 {
        self.position.y
    }
}

impl<'a> Renderable for Unit<'a> {
    fn get_color(&self) -> Color {
        self.unit_type.color
    }

    fn get_glyph(&self) -> char {
        self.unit_type.glyph
    }
}

impl<'a> Movable for Unit<'a> {
    fn move_to(&mut self, pos: Point<i16>){
        self.position = pos;
    }
    fn nudge(&mut self, dir: Direction){
        self.position = self.position + dir.to_rel_point();
    }
}<commit_msg>Added (exceedingly) dumb AI movement.<commit_after>use tcod::colors::{Color};

use traits::{Renderable, Movable, Position};
use Direction;
use point::Point;
use map;
use map::Map;

use std::fs::File;
use std::path::Path;

use rand;
use rand::Rng;
use rand::distributions::{Weighted};

use serde_yaml;

const ERR_UNIT_LOAD: &str = "Unable to load unit type data.";

#[derive(Debug)]
pub struct UnitType {
    name: String,
    glyph: char,
    color: Color,
    is_blocking: bool,
    max_hp: u8,
    defence: u8,
    attack: u8,
}

impl UnitType {
    // Only used for the player for now.
    pub fn new(name: &str, glyph: char, color: Color) -> UnitType {
        UnitType {
            name: name.into(),
            glyph: glyph,
            color: color,
            is_blocking: true,
            // Following based on standard human in the data files.
            max_hp: 30,
            defence: 3,
            attack: 5,
        }
    }
}

impl<'a> From<&'a UnitTypeRaw> for UnitType {
    fn from(raw: &'a UnitTypeRaw) -> UnitType {
        UnitType {
            name: raw.name.clone(),
            glyph: raw.glyph,
            color: Color::new(raw.color[0], raw.color[1], raw.color[2]),
            is_blocking: raw.is_blocking,
            max_hp: raw.max_hp,
            defence: raw.defence,
            attack: raw.attack,
        }
    }
}

// The Color type in tcod isn't deserializable, so have this to make reading
// the data file easier.
#[derive(Debug, Deserialize)]
struct UnitTypeRaw {
    name: String,
    glyph: char,
    color: [u8; 3],
    chance: u32,
    is_blocking: bool,
    max_hp: u8,
    defence: u8,
    attack: u8,
}

pub struct UnitTypeLists {
    pub types: Vec<UnitType>,
    weights: Vec<Weighted<usize>>,
    max_weight: u32,
}

impl UnitTypeLists {
    pub fn get_random_type(&self, rng: &mut rand::ThreadRng) -> &UnitType {
        let mut val = rng.gen_range(0, self.max_weight);

        for weight in self.weights.iter() {
            if weight.weight > val {
                return &self.types[weight.item];
            }

            val -= weight.weight;
        }

        unreachable!()
    }
}

pub fn load_unit_types() -> UnitTypeLists{
    let path = Path::new("data").join("unit_types.yaml");
    let data_file = File::open(&path).expect(ERR_UNIT_LOAD);
    let raw_units: Vec<UnitTypeRaw> = serde_yaml::from_reader(&data_file).expect(ERR_UNIT_LOAD);

    let mut types = vec![];
    let mut weights = vec![];

    let mut running_total = 0;
    for (i, raw_unit) in raw_units.iter().enumerate() {
        types.push(raw_unit.into());
        running_total += raw_unit.chance;

        weights.push( Weighted{ weight: raw_unit.chance, item: i });
    }

    UnitTypeLists {
        types: types,
        weights: weights,
        max_weight: running_total,
    }
}

#[derive(Debug)]
pub struct Unit<'a> {
    position: Point<i16>,
    unit_type: &'a UnitType,
    cur_hp: u8,
}

impl<'a> Unit<'a> {
    pub fn new(pos: Point<i16>, unit_type: &'a UnitType) -> Unit<'a> {
        let cur_hp = unit_type.max_hp;
        Unit {
            position: pos,
            unit_type: unit_type,
            cur_hp: cur_hp,
        }
    }

    pub fn is_blocking(&self) -> bool {
        self.unit_type.is_blocking
    }

    pub fn get_name(&self) -> &str {
        &self.unit_type.name
    }

    pub fn get_hp(&self) -> u8 {
        self.cur_hp
    }

    fn get_step_towards(&mut self, target: Point<i16>) -> Point<i16> {
        let cur_pos = self.get_position();
        let delta = target - cur_pos;
        let dist = delta.radius();

        let new_pos = Point {
            x: (delta.x as f64 / dist).round() as i16,
            y: (delta.y as f64 / dist).round() as i16,
        };
        new_pos + cur_pos
    }

    pub fn take_turn(&mut self, map: &Map, player: &Unit) {
        if !map.point_in_fov(self.get_position()) {
            return;
        }

        if (self.get_position() - player.get_position()).radius() >= 2.0 {
            let new_pos = self.get_step_towards(player.get_position());
            if map.can_move_to(new_pos) == map::CanMoveResponse::Open {
                self.move_to(new_pos);
            }
        } else if player.get_hp() > 0 {
            println!("The attack of the {} bounces off your armour.", self.get_name());
        }
    }
}

impl<'a> Position for Unit<'a> {
    fn get_x(&self) -> i16 {
        self.position.x
    }

    fn get_y(&self) -> i16 {
        self.position.y
    }
}

impl<'a> Renderable for Unit<'a> {
    fn get_color(&self) -> Color {
        self.unit_type.color
    }

    fn get_glyph(&self) -> char {
        self.unit_type.glyph
    }
}

impl<'a> Movable for Unit<'a> {
    fn move_to(&mut self, pos: Point<i16>){
        self.position = pos;
    }
    fn nudge(&mut self, dir: Direction){
        self.position = self.position + dir.to_rel_point();
    }
}<|endoftext|>


<commit_before>//! Interface and implementations for the rollout phase of MCTS.

use crate::game::Game;
use crate::graph::{EdgeData, VertexData};
use crate::SearchSettings;

use std::convert::From;
use std::error::Error;
use std::fmt;
use std::result::Result;

use rand::Rng;

/// Error type for MCTS rollout.
pub enum RolloutError<G: Game, E: Error> {
  /// Rollout encountered a cycle.
  Cycle { root: G::State, elements: Vec<(G::Action, G::State)>, },
  /// The [RolloutSelector](struct.RolloutSelector.html) that
  /// [rollout](fn.rollout.html) delegates to reported some error.
  Selector(E),
}

impl<G: Game, E: Error> fmt::Debug for RolloutError<G, E> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      RolloutError::Cycle { ref root, ref elements, } => {
        write!(f, "Cycle in path: ")?;
        write!(f, "Start state: {:?}", root)?;
        for (action, state) in elements.iter() {
          write!(f, "; action: {:?}, state: {:?}", action, state)?;
        }
        Ok(())
      }
      RolloutError::Selector(ref e) => write!(f, "Selector error ({:?})", e),
    }
  }
}

impl<G: Game, E: Error> fmt::Display for RolloutError<G, E> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      RolloutError::Cycle { root: _, elements: _, } => write!(f, "Cycle in path"),
      RolloutError::Selector(ref e) => write!(f, "Selector error ({})", e),
    }
  }
}

impl<G: Game, E: Error> Error for RolloutError<G, E> {
  fn description(&self) -> &str {
    match *self {
      RolloutError::Cycle { root: _, elements: _, } => "Cycle",
      RolloutError::Selector(_) => "Selector error",
    }
  }

  fn cause(&self) -> Option<&Error> {
    match *self {
      RolloutError::Selector(ref e) => Some(e),
      _ => None,
    }
  }
}

impl<G: Game, E: Error> From<E> for RolloutError<G, E> {
  fn from(e: E) -> Self {
    RolloutError::Selector(e)
  }
}

/// Provides a method for selecting an outgoing child edge to follow during
/// the rollout phase of MCTS.
pub trait RolloutSelector: for<'a> From<&'a SearchSettings> {
  type Error: Error;

  /// Returns the element of `children` that should be followed, or an error.
  fn select<'a, 'id, G: Game, R: Rng>(
    &self,
    graph: &search_graph::view::View<'a, 'id, G::State, VertexData, EdgeData<G>>,
    parent: search_graph::view::NodeRef<'id>,
    rng: &mut R,
  ) -> Result<search_graph::view::EdgeRef<'id>, Self::Error>;
}

/// Traverses the game graph downwards from `node` down to some terminating
/// vertex in the search graph. The terminating vertex will either have a known
/// payoff, or yield `None` when `selector.select()` is called on it. Returns
/// the path to terminating vertex, whose last element is the terminating
/// vertex, or an error.
pub fn rollout<'a, 'id, G, S, R>(
  graph: &search_graph::view::View<'a, 'id, G::State, VertexData, EdgeData<G>>,
  mut node: search_graph::view::NodeRef<'id>,
  selector: S,
  rng: &mut R,
) -> Result<search_graph::view::NodeRef<'id>, RolloutError<G, S::Error>>
where
  G: Game,
  S: RolloutSelector,
  R: Rng,
{
  loop {
    if let Some(_) = G::payoff_of(graph.node_state(node)) {
      // Hit known payoff.
      break;
    } else if graph.child_count(node) == 0 {
      break;
    } else {
      let child = selector.select(graph, node, rng)?;
      graph.edge_data(child).mark_rollout_traversal();
      node = graph.edge_target(child);
    }
  }
  Ok(node)
}
<commit_msg>Tiny bump to docs.<commit_after>//! Interface and implementations for the rollout phase of MCTS.

use crate::game::Game;
use crate::graph::{EdgeData, VertexData};
use crate::SearchSettings;

use std::convert::From;
use std::error::Error;
use std::fmt;
use std::result::Result;

use rand::Rng;

/// Error type for MCTS rollout.
pub enum RolloutError<G: Game, E: Error> {
  /// Rollout encountered a cycle.
  Cycle { root: G::State, elements: Vec<(G::Action, G::State)>, },
  /// The [RolloutSelector](struct.RolloutSelector.html) that
  /// [rollout](fn.rollout.html) delegates to reported some error.
  Selector(E),
}

impl<G: Game, E: Error> fmt::Debug for RolloutError<G, E> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      RolloutError::Cycle { ref root, ref elements, } => {
        write!(f, "Cycle in path: ")?;
        write!(f, "Start state: {:?}", root)?;
        for (action, state) in elements.iter() {
          write!(f, "; action: {:?}, state: {:?}", action, state)?;
        }
        Ok(())
      }
      RolloutError::Selector(ref e) => write!(f, "Selector error ({:?})", e),
    }
  }
}

impl<G: Game, E: Error> fmt::Display for RolloutError<G, E> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      RolloutError::Cycle { root: _, elements: _, } => write!(f, "Cycle in path"),
      RolloutError::Selector(ref e) => write!(f, "Selector error ({})", e),
    }
  }
}

impl<G: Game, E: Error> Error for RolloutError<G, E> {
  fn description(&self) -> &str {
    match *self {
      RolloutError::Cycle { root: _, elements: _, } => "Cycle",
      RolloutError::Selector(_) => "Selector error",
    }
  }

  fn cause(&self) -> Option<&Error> {
    match *self {
      RolloutError::Selector(ref e) => Some(e),
      _ => None,
    }
  }
}

impl<G: Game, E: Error> From<E> for RolloutError<G, E> {
  fn from(e: E) -> Self {
    RolloutError::Selector(e)
  }
}

/// Provides a method for selecting an outgoing child edge to follow during
/// the rollout phase of MCTS.
pub trait RolloutSelector: for<'a> From<&'a SearchSettings> {
  type Error: Error;

  /// Returns the element of `children` that should be followed, or an error.
  fn select<'a, 'id, G: Game, R: Rng>(
    &self,
    graph: &search_graph::view::View<'a, 'id, G::State, VertexData, EdgeData<G>>,
    parent: search_graph::view::NodeRef<'id>,
    rng: &mut R,
  ) -> Result<search_graph::view::EdgeRef<'id>, Self::Error>;
}

/// Traverses the game graph downwards from `node` down to some terminating
/// vertex in the search graph.
///
/// The terminating vertex will either have a known payoff, or yield `None` when
/// `selector.select()` is called on it. Returns the path to terminating vertex,
/// whose last element is the terminating vertex, or an error.
///
/// Selection will be done minimax-style, i.e., always trying to maximize the
/// score for the currently active player.
pub fn rollout<'a, 'id, G, S, R>(
  graph: &search_graph::view::View<'a, 'id, G::State, VertexData, EdgeData<G>>,
  mut node: search_graph::view::NodeRef<'id>,
  selector: S,
  rng: &mut R,
) -> Result<search_graph::view::NodeRef<'id>, RolloutError<G, S::Error>>
where
  G: Game,
  S: RolloutSelector,
  R: Rng,
{
  loop {
    if let Some(_) = G::payoff_of(graph.node_state(node)) {
      // Hit known payoff.
      break;
    } else if graph.child_count(node) == 0 {
      // Hit leaf in search graph.
      break;
    } else {
      let child = selector.select(graph, node, rng)?;
      graph.edge_data(child).mark_rollout_traversal();
      node = graph.edge_target(child);
    }
  }
  Ok(node)
}
<|endoftext|>


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

//! Tests for sample currency service almost identical to one implemented in
//! the [`cryptocurrency`] tutorial.
//!
//! [`cryptocurrency`]: https://github.com/exonum/cryptocurrency/

#[macro_use]
extern crate exonum;
#[macro_use]
extern crate exonum_testkit;
#[macro_use]
extern crate serde_derive;

use std::collections::BTreeSet;
use std::iter::FromIterator;

use exonum::crypto::{self, PublicKey, SecretKey, CryptoHash};
use exonum_testkit::{ApiKind, ComparableSnapshot, TestKit, TestKitApi, TestKitBuilder};

mod cryptocurrency;
use cryptocurrency::{CurrencySchema, CurrencyService, TransactionResponse, TxCreateWallet,
                     TxTransfer, Wallet};

fn init_testkit() -> TestKit {
    TestKitBuilder::validator()
        .with_validators(4)
        .with_service(CurrencyService)
        .create()
}

fn create_wallet(api: &TestKitApi, name: &str) -> (TxCreateWallet, SecretKey) {
    let (pubkey, key) = crypto::gen_keypair();
    // Create a presigned transaction
    let tx = TxCreateWallet::new(&pubkey, name, &key);

    let tx_info: TransactionResponse = api.post(
        ApiKind::Service("cryptocurrency"),
        "v1/wallets/transaction",
        &tx,
    );
    assert_eq!(tx_info.tx_hash, tx.hash());

    (tx, key)
}

fn transfer(api: &TestKitApi, tx: &TxTransfer) {
    let tx_info: TransactionResponse = api.post(
        ApiKind::Service("cryptocurrency"),
        "v1/wallets/transaction",
        tx,
    );
    assert_eq!(tx_info.tx_hash, tx.hash());
}

fn get_wallet(api: &TestKitApi, pubkey: &PublicKey) -> Wallet {
    api.get(
        ApiKind::Service("cryptocurrency"),
        &format!("v1/wallet/{}", pubkey.to_string()),
    )
}

fn get_all_wallets(api: &TestKitApi) -> Vec<Wallet> {
    api.get(ApiKind::Service("cryptocurrency"), "v1/wallets")
}

#[test]
fn test_create_wallet() {
    let mut testkit = init_testkit();
    let api = testkit.api();
    let (tx, _) = create_wallet(&api, "Alice");

    testkit.create_block();

    // Check that the user indeed is persisted by the service
    let wallet = get_wallet(&api, tx.pub_key());
    assert_eq!(wallet.pub_key(), tx.pub_key());
    assert_eq!(wallet.name(), tx.name());
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_transfer() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Commit creation transactions
    testkit.create_block();

    // Check that the initial Alice's and Bob's balances are persisted by the service
    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 100);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 100);

    // Transfer funds
    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        10, // amount
        0, // seed
        &key_alice,
    );
    transfer(&api, &tx);
    testkit.create_block();

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 90);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 110);
}

#[test]
fn test_snapshot_completeness() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, _) = create_wallet(&api, "Alice");
    testkit.create_block();

    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Check that Alice's wallet is in the snapshot
    testkit
        .probe(tx_bob)
        .compare(testkit.snapshot())
        .map(CurrencySchema::new)
        .map(|schema| schema.wallet(tx_alice.pub_key()))
        .assert_inv("Alice's wallet is there", Option::is_some)
        .map(|w| w.as_ref().unwrap().balance())
        .assert_eq("Alice's balance hasn't changed");
}

#[test]
fn test_transfer_from_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Do not commit Alice's transaction
    testkit.create_block_with_tx_hashes(&[tx_bob.hash()]);

    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 100);

    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        10, // amount
        0, // seed
        &key_alice,
    );

    let comp = testkit.probe(tx).compare(testkit.snapshot());
    let comp = comp.map(CurrencySchema::new);
    comp.map(|s| s.wallet(tx_alice.pub_key())).assert_inv(
        "No Alice's wallet",
        Option::is_none,
    );
    comp.map(|s| {
        s.wallet(tx_bob.pub_key())
            .expect("No Bob's wallet!")
            .balance()
    }).assert_eq("Bob's balance hasn't changed");
}

#[test]
fn test_transfer_to_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Do not commit Bob's transaction
    testkit.create_block_with_tx_hashes(&[tx_alice.hash()]);

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 100);

    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        10, // amount
        0, // seed
        &key_alice,
    );
    transfer(&api, &tx);

    let old_snapshot = testkit.snapshot();
    testkit.create_block_with_tx_hashes(&[tx.hash()]);

    let comp = testkit.snapshot().compare(old_snapshot);
    let comp = comp.map(CurrencySchema::new);
    comp.map(|s| s.wallet(tx_bob.pub_key())).assert_inv(
        "No Bob's wallet",
        Option::is_none,
    );
    comp.map(|s| {
        s.wallet(tx_alice.pub_key())
            .expect("No Alice's wallet!")
            .balance()
    }).assert_eq("Alice's balance hasn't changed");
}

#[test]
fn test_transfer_overcharge() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    testkit.create_block();

    // Transfer funds
    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        110, // amount
        0, // seed
        &key_alice,
    );
    transfer(&api, &tx);
    testkit.create_block();

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 100);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_transfers_in_single_block() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, key_bob) = create_wallet(&api, "Bob");
    testkit.create_block();

    // Transfer funds from Alice to Bob.
    let tx_a_to_b = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        90, // amount
        0, // seed
        &key_alice,
    );

    // Transfer funds back from Bob to Alice.
    let tx_b_to_a = TxTransfer::new(
        tx_bob.pub_key(),
        tx_alice.pub_key(),
        120, // amount
        0, // seed
        &key_bob,
    );

    {
        // See what happens if transactions are applied in an "incorrect" order.
        let comp = testkit
            .probe_all(txvec![tx_b_to_a.clone(), tx_a_to_b.clone()])
            .compare(testkit.snapshot());
        let comp = comp.map(CurrencySchema::new);
        comp.map(|s| s.wallet(tx_alice.pub_key()).unwrap().balance())
            .assert("Alice's balance decreases", |&old, &new| new == old - 90);
        comp.map(|s| s.wallet(tx_bob.pub_key()).unwrap().balance())
            .assert("Bob's balance increases", |&old, &new| new == old + 90);
    }

    transfer(&api, &tx_a_to_b);
    transfer(&api, &tx_b_to_a);
    testkit.create_block_with_tx_hashes(&[tx_a_to_b.hash(), tx_b_to_a.hash()]);

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 130);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 70);

    let wallets = get_all_wallets(&api);
    assert_eq!(wallets.len(), 2);
    assert_eq!(wallets.iter().fold(0, |acc, w| acc + w.balance()), 200);
    assert_eq!(
        BTreeSet::from_iter(wallets.iter().map(|w| *w.pub_key())),
        BTreeSet::from_iter(vec![*tx_alice.pub_key(), *tx_bob.pub_key()])
    );
}

#[test]
fn test_malformed_wallet_request() {
    let testkit = TestKitBuilder::validator()
        .with_validators(4)
        .with_service(CurrencyService)
        .create();
    let api = testkit.api();
    let info: String = api.get_err(ApiKind::Service("cryptocurrency"), "v1/wallet/c0ffee");
    assert!(info.starts_with("Invalid request param"));
}

#[test]
fn test_unknown_wallet_request() {
    let testkit = init_testkit();
    let api = testkit.api();

    // transaction is sent by API, but isn't committed
    let (tx_alice, _) = create_wallet(&api, "Alice");

    let info: String = api.get_err(
        ApiKind::Service("cryptocurrency"),
        &format!("v1/wallet/{}", tx_alice.pub_key().to_string()),
    );
    assert_eq!(info, "Wallet not found".to_string());
}

#[test]
fn test_nonverified_transaction_in_create_block() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (create_tx, key) = create_wallet(&api, "Alice");
    testkit.create_block();

    // Send transaction to self. As this transaction fails `verify()`, it should not
    // be executed and increase sender's balance.
    let transfer_tx = TxTransfer::new(create_tx.pub_key(), create_tx.pub_key(), 10, 0, &key);
    testkit.create_block_with_transactions(txvec![transfer_tx]);

    let wallet = get_wallet(&api, create_tx.pub_key());
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_nonsigned_transaction_in_create_block() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    testkit.create_block();

    let transfer_tx = TxTransfer::new(tx_alice.pub_key(), tx_bob.pub_key(), 10, 0, &key);
    // Transaction with an incorrect signature (all zeros)
    let bogus_transfer_tx = TxTransfer::new_with_signature(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        20,
        0,
        &crypto::Signature::new([0; 64]),
    );
    // Check execution of a mix of correct and incorrect transactions
    testkit.create_block_with_transactions(txvec![bogus_transfer_tx, transfer_tx]);

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 90);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 110);
}
<commit_msg>Fix Clippy<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Tests for sample currency service almost identical to one implemented in
//! the [`cryptocurrency`] tutorial.
//!
//! [`cryptocurrency`]: https://github.com/exonum/cryptocurrency/

#[macro_use]
extern crate exonum;
#[macro_use]
extern crate exonum_testkit;
#[macro_use]
extern crate serde_derive;

use std::collections::BTreeSet;
use std::iter::FromIterator;

use exonum::crypto::{self, PublicKey, SecretKey, CryptoHash};
use exonum_testkit::{ApiKind, ComparableSnapshot, TestKit, TestKitApi, TestKitBuilder};

mod cryptocurrency;
use cryptocurrency::{CurrencySchema, CurrencyService, TransactionResponse, TxCreateWallet,
                     TxTransfer, Wallet};

fn init_testkit() -> TestKit {
    TestKitBuilder::validator()
        .with_validators(4)
        .with_service(CurrencyService)
        .create()
}

fn create_wallet(api: &TestKitApi, name: &str) -> (TxCreateWallet, SecretKey) {
    let (pubkey, key) = crypto::gen_keypair();
    // Create a presigned transaction
    let tx = TxCreateWallet::new(&pubkey, name, &key);

    let tx_info: TransactionResponse = api.post(
        ApiKind::Service("cryptocurrency"),
        "v1/wallets/transaction",
        &tx,
    );
    assert_eq!(tx_info.tx_hash, tx.hash());

    (tx, key)
}

fn transfer(api: &TestKitApi, tx: &TxTransfer) {
    let tx_info: TransactionResponse = api.post(
        ApiKind::Service("cryptocurrency"),
        "v1/wallets/transaction",
        tx,
    );
    assert_eq!(tx_info.tx_hash, tx.hash());
}

fn get_wallet(api: &TestKitApi, pubkey: &PublicKey) -> Wallet {
    api.get(
        ApiKind::Service("cryptocurrency"),
        &format!("v1/wallet/{}", pubkey.to_string()),
    )
}

fn get_all_wallets(api: &TestKitApi) -> Vec<Wallet> {
    api.get(ApiKind::Service("cryptocurrency"), "v1/wallets")
}

#[test]
fn test_create_wallet() {
    let mut testkit = init_testkit();
    let api = testkit.api();
    let (tx, _) = create_wallet(&api, "Alice");

    testkit.create_block();

    // Check that the user indeed is persisted by the service
    let wallet = get_wallet(&api, tx.pub_key());
    assert_eq!(wallet.pub_key(), tx.pub_key());
    assert_eq!(wallet.name(), tx.name());
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_transfer() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Commit creation transactions
    testkit.create_block();

    // Check that the initial Alice's and Bob's balances are persisted by the service
    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 100);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 100);

    // Transfer funds
    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        10, // amount
        0, // seed
        &key_alice,
    );
    transfer(&api, &tx);
    testkit.create_block();

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 90);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 110);
}

#[test]
fn test_snapshot_completeness() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, _) = create_wallet(&api, "Alice");
    testkit.create_block();

    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Check that Alice's wallet is in the snapshot
    testkit
        .probe(tx_bob)
        .compare(testkit.snapshot())
        .map(CurrencySchema::new)
        .map(|schema| schema.wallet(tx_alice.pub_key()))
        .assert_inv("Alice's wallet is there", Option::is_some)
        .map(|w| w.as_ref().unwrap().balance())
        .assert_eq("Alice's balance hasn't changed");
}

#[test]
fn test_transfer_from_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Do not commit Alice's transaction
    testkit.create_block_with_tx_hashes(&[tx_bob.hash()]);

    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 100);

    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        10, // amount
        0, // seed
        &key_alice,
    );

    let comp = testkit.probe(tx).compare(testkit.snapshot());
    let comp = comp.map(CurrencySchema::new);
    comp.map(|s| s.wallet(tx_alice.pub_key())).assert_inv(
        "No Alice's wallet",
        Option::is_none,
    );
    comp.map(|s| {
        s.wallet(tx_bob.pub_key())
            .expect("No Bob's wallet!")
            .balance()
    }).assert_eq("Bob's balance hasn't changed");
}

#[test]
fn test_transfer_to_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    // Do not commit Bob's transaction
    testkit.create_block_with_tx_hashes(&[tx_alice.hash()]);

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 100);

    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        10, // amount
        0, // seed
        &key_alice,
    );
    transfer(&api, &tx);

    let old_snapshot = testkit.snapshot();
    testkit.create_block_with_tx_hashes(&[tx.hash()]);

    let comp = testkit.snapshot().compare(old_snapshot);
    let comp = comp.map(CurrencySchema::new);
    comp.map(|s| s.wallet(tx_bob.pub_key())).assert_inv(
        "No Bob's wallet",
        Option::is_none,
    );
    comp.map(|s| {
        s.wallet(tx_alice.pub_key())
            .expect("No Alice's wallet!")
            .balance()
    }).assert_eq("Alice's balance hasn't changed");
}

#[test]
fn test_transfer_overcharge() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    testkit.create_block();

    // Transfer funds
    let tx = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        110, // amount
        0, // seed
        &key_alice,
    );
    transfer(&api, &tx);
    testkit.create_block();

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 100);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_transfers_in_single_block() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key_alice) = create_wallet(&api, "Alice");
    let (tx_bob, key_bob) = create_wallet(&api, "Bob");
    testkit.create_block();

    // Transfer funds from Alice to Bob.
    let tx_a_to_b = TxTransfer::new(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        90, // amount
        0, // seed
        &key_alice,
    );

    // Transfer funds back from Bob to Alice.
    let tx_b_to_a = TxTransfer::new(
        tx_bob.pub_key(),
        tx_alice.pub_key(),
        120, // amount
        0, // seed
        &key_bob,
    );

    {
        // See what happens if transactions are applied in an "incorrect" order.
        let comp = testkit
            .probe_all(txvec![tx_b_to_a.clone(), tx_a_to_b.clone()])
            .compare(testkit.snapshot());
        let comp = comp.map(CurrencySchema::new);
        comp.map(|s| s.wallet(tx_alice.pub_key()).unwrap().balance())
            .assert("Alice's balance decreases", |&old, &new| new == old - 90);
        comp.map(|s| s.wallet(tx_bob.pub_key()).unwrap().balance())
            .assert("Bob's balance increases", |&old, &new| new == old + 90);
    }

    transfer(&api, &tx_a_to_b);
    transfer(&api, &tx_b_to_a);
    testkit.create_block_with_tx_hashes(&[tx_a_to_b.hash(), tx_b_to_a.hash()]);

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 130);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 70);

    let wallets = get_all_wallets(&api);
    assert_eq!(wallets.len(), 2);
    assert_eq!(wallets.iter().map(|w| w.balance()).sum::<u64>(), 200);
    assert_eq!(
        BTreeSet::from_iter(wallets.iter().map(|w| *w.pub_key())),
        BTreeSet::from_iter(vec![*tx_alice.pub_key(), *tx_bob.pub_key()])
    );
}

#[test]
fn test_malformed_wallet_request() {
    let testkit = TestKitBuilder::validator()
        .with_validators(4)
        .with_service(CurrencyService)
        .create();
    let api = testkit.api();
    let info: String = api.get_err(ApiKind::Service("cryptocurrency"), "v1/wallet/c0ffee");
    assert!(info.starts_with("Invalid request param"));
}

#[test]
fn test_unknown_wallet_request() {
    let testkit = init_testkit();
    let api = testkit.api();

    // transaction is sent by API, but isn't committed
    let (tx_alice, _) = create_wallet(&api, "Alice");

    let info: String = api.get_err(
        ApiKind::Service("cryptocurrency"),
        &format!("v1/wallet/{}", tx_alice.pub_key().to_string()),
    );
    assert_eq!(info, "Wallet not found".to_string());
}

#[test]
fn test_nonverified_transaction_in_create_block() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (create_tx, key) = create_wallet(&api, "Alice");
    testkit.create_block();

    // Send transaction to self. As this transaction fails `verify()`, it should not
    // be executed and increase sender's balance.
    let transfer_tx = TxTransfer::new(create_tx.pub_key(), create_tx.pub_key(), 10, 0, &key);
    testkit.create_block_with_transactions(txvec![transfer_tx]);

    let wallet = get_wallet(&api, create_tx.pub_key());
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_nonsigned_transaction_in_create_block() {
    let mut testkit = init_testkit();
    let api = testkit.api();

    let (tx_alice, key) = create_wallet(&api, "Alice");
    let (tx_bob, _) = create_wallet(&api, "Bob");
    testkit.create_block();

    let transfer_tx = TxTransfer::new(tx_alice.pub_key(), tx_bob.pub_key(), 10, 0, &key);
    // Transaction with an incorrect signature (all zeros)
    let bogus_transfer_tx = TxTransfer::new_with_signature(
        tx_alice.pub_key(),
        tx_bob.pub_key(),
        20,
        0,
        &crypto::Signature::new([0; 64]),
    );
    // Check execution of a mix of correct and incorrect transactions
    testkit.create_block_with_transactions(txvec![bogus_transfer_tx, transfer_tx]);

    let wallet = get_wallet(&api, tx_alice.pub_key());
    assert_eq!(wallet.balance(), 90);
    let wallet = get_wallet(&api, tx_bob.pub_key());
    assert_eq!(wallet.balance(), 110);
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// The location of something in an address space.
///
/// This is used to provide a location of a [`Function`], [`Instruction`],
/// or other item.
///
/// XXX: Should this have any indication for what type of address it is?
///      An address might be an address within a file, a resolved address
///      after being loaded, etc.
///
/// XXX: Should this include any information about the address space
///      that it is from?
///
/// [`Function`]: struct.Function.html
/// [`Instruction`]: trait.Instruction.html
#[derive(Clone,Copy,Debug)]
pub struct Address {
    address: usize,
}

impl Address {
    /// Construct an `Address`.
    pub fn new(address: usize) -> Self {
        Address { address: address }
    }
}
<commit_msg>Define equality, hashing, ordering for Address.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// The location of something in an address space.
///
/// This is used to provide a location of a [`Function`], [`Instruction`],
/// or other item.
///
/// XXX: Should this have any indication for what type of address it is?
///      An address might be an address within a file, a resolved address
///      after being loaded, etc.
///
/// XXX: Should this include any information about the address space
///      that it is from?
///
/// [`Function`]: struct.Function.html
/// [`Instruction`]: trait.Instruction.html
#[derive(Clone,Copy,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)]
pub struct Address {
    address: usize,
}

impl Address {
    /// Construct an `Address`.
    pub fn new(address: usize) -> Self {
        Address { address: address }
    }
}

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

    #[test]
    fn address_comparison() {
        let addr1 = Address::new(5);
        let addr2 = Address::new(3);
        let addr3 = Address::new(5);
        assert!(addr2 < addr1);
        assert_eq!(addr1, addr3);
    }
}
<|endoftext|>


<commit_before>#![allow(unused_imports, unused_must_use, unused_variables, dead_code)]

extern crate combine;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::str;
use std::collections::HashMap;
use std::marker::PhantomData;
use combine::{spaces, parser, between, many, many1, digit, char, any, string, token, Parser, ParserExt, ParseError};
use combine::primitives::{State, Stream, ParseResult, Consumed};
use combine::combinator::{Between, Token, FnParser, satisfy};

//my own bencode stuff!
//
fn open_file <P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut fd = File::open(path).unwrap();
    let mut buffer:Vec<u8> = Vec::new();
    fd.read_to_end(&mut buffer);
    buffer
}

#[derive(Debug, Eq, PartialEq)]
enum Bencode {
    Int(i64),
    String(String),
    List(Vec<Bencode>),
    Dict(HashMap<String, Bencode>)
}

fn bencode_integer<I>(input: State<I>) -> ParseResult<i64, I> where I:Stream<Item=char> {
    let (open, close) = (char('i'), char('e'));
    let mut int = between(open, close, many1::<String, _>(digit())).map(|x| {
        x.parse::<i64>().unwrap()
    });
    int.parse_state(input)
}

fn bencode_string<I>(input: State<I>) -> ParseResult<String, I> where I:Stream<Item=char> {
    let (len, input_) = try!(bencode_string_length_prefix(input));
    input_.combine(|input__| take(len).parse_state(input__))
}

fn bencode_string_length_prefix<I>(input: State<I>) -> ParseResult<i32, I> where I:Stream<Item=char> {
    let many_digit = many1::<String, _>(digit());
    let mut get_len = (many_digit, char(':')).map(|(length, _)| length.parse::<i32>().unwrap());
    get_len.parse_state(input)
}

fn bencode_list<I>(input: State<I>) -> ParseResult<Vec<Bencode>, I> where I:Stream<Item=char> {
    let (open, close) = (char('l'), char('e'));
    let bencode_types = parser(bencode_integer).map(Bencode::Int)
                            .or(parser(bencode_string).map(Bencode::String))
                            .or(parser(bencode_list).map(Bencode::List))
                            .or(parser(bencode_dict).map(Bencode::Dict));
    let list_contents = many::<Vec<Bencode>, _>(bencode_types);
    let mut list = between(open, close, list_contents);
    list.parse_state(input)
}

fn bencode_dict<I>(input: State<I>) -> ParseResult<HashMap<String, Bencode>, I> where I:Stream<Item=char> {
    let (open, close) = (char('d'), char('e'));
    let dict_contents = parser(bencode_integer).map(Bencode::Int)
                            .or(parser(bencode_string).map(Bencode::String))
                            .or(parser(bencode_list).map(Bencode::List))
                            .or(parser(bencode_dict).map(Bencode::Dict));
    let pairs = (parser(bencode_string), dict_contents);
    let mut dict = between(open, close, many(pairs)).map(|entries:Vec<(String, Bencode)>|{
        let mut hash_map = HashMap::new();
        for (k, v) in entries {
            hash_map.insert(k, v);
        }
        hash_map
    });
    dict.parse_state(input)
}

fn take <I> (num: i32) -> SizedBuffer<I> where I: Stream<Item=char> {
    SizedBuffer(num, PhantomData)
}

#[derive(Clone)]
pub struct SizedBuffer <I>(i32, PhantomData<I>);
impl <I> Parser for SizedBuffer<I> where I: Stream<Item=char> {
    type Input = I;
    type Output = String;
    fn parse_lazy(&mut self, mut input: State<I>) -> ParseResult<String, I> {
        let start = input.position;
        let mut vec_buf:Vec<char> = Vec::new();
        for _ in 0..self.0 {
            match input.uncons() {
                Ok((other, rest)) => {
                    vec_buf.push(other);
                    input = rest.into_inner();
                }
                Err(error) => {
                    return error.combine(|mut error| {
                        error.position = start;
                        Err(Consumed::Consumed(error))
                    })
                }
            };
        }
        Ok((vec_buf.into_iter().collect(), Consumed::Consumed(input)))
    }
}

fn main () {
    let file_contents = open_file("t-test.torrent");

    match str::from_utf8(&file_contents) {
        Ok(v) => println!("file: {}", v),
        Err(e) => panic!("Not a UTF-8 String: {}", e)
    };

    let result = parser(bencode_dict).parse("d3:abci4ee");
}

#[test]
fn test_integer() {
    let result = parser(bencode_integer).parse("i57e");
    assert_eq!(result, Ok((57,"")));
}

#[test]
fn test_string() {
    let result = parser(bencode_string).parse("5:abcde");
    assert_eq!(result, Ok(("abcde".to_string(), "")));;
}

#[test]
fn test_list() {
    let homogenous_int_list = parser(bencode_list).parse("li57ei32ee");
    assert_eq!(homogenous_int_list, Ok((vec![Bencode::Int(57), Bencode::Int(32)], "")));

    let homogenous_str_list = parser(bencode_list).parse("l3:abc5:defghe");
    assert_eq!(homogenous_str_list, Ok((vec![Bencode::String("abc".to_string()), Bencode::String("defgh".to_string())], "")));

    let hetero_list = parser(bencode_list).parse("li32e3:abce");
    assert_eq!(hetero_list, Ok((vec![Bencode::Int(32), Bencode::String("abc".to_string())], "")));
}

#[test]
fn test_dict() {

}
<commit_msg>made bencode_any, improves compilation time somewhat<commit_after>#![allow(unused_imports, unused_must_use, unused_variables, dead_code)]

extern crate combine;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::str;
use std::collections::HashMap;
use std::marker::PhantomData;
use combine::{spaces, parser, between, many, many1, digit, char, any, string, token, Parser, ParserExt, ParseError};
use combine::primitives::{State, Stream, ParseResult, Consumed};
use combine::combinator::{Between, Token, FnParser, satisfy};

//my own bencode stuff!
//
fn open_file <P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut fd = File::open(path).unwrap();
    let mut buffer:Vec<u8> = Vec::new();
    fd.read_to_end(&mut buffer);
    buffer
}

#[derive(Debug, Eq, PartialEq)]
enum Bencode {
    Int(i64),
    String(String),
    List(Vec<Bencode>),
    Dict(HashMap<String, Bencode>)
}

fn bencode_integer<I>(input: State<I>) -> ParseResult<i64, I> where I:Stream<Item=char> {
    let (open, close) = (char('i'), char('e'));
    let mut int = between(open, close, many1::<String, _>(digit())).map(|x| {
        x.parse::<i64>().unwrap()
    });
    int.parse_state(input)
}

fn bencode_string<I>(input: State<I>) -> ParseResult<String, I> where I:Stream<Item=char> {
    let (len, input_) = try!(bencode_string_length_prefix(input));
    input_.combine(|input__| take(len).parse_state(input__))
}

fn bencode_string_length_prefix<I>(input: State<I>) -> ParseResult<i32, I> where I:Stream<Item=char> {
    let many_digit = many1::<String, _>(digit());
    let mut get_len = (many_digit, char(':')).map(|(length, _)| length.parse::<i32>().unwrap());
    get_len.parse_state(input)
}

fn bencode_list<I>(input: State<I>) -> ParseResult<Vec<Bencode>, I> where I:Stream<Item=char> {
    let (open, close) = (char('l'), char('e'));
    let list_contents = many::<Vec<Bencode>, _>(bencode_any());
    let mut list = between(open, close, list_contents);
    list.parse_state(input)
}

fn bencode_dict<I>(input: State<I>) -> ParseResult<HashMap<String, Bencode>, I> where I:Stream<Item=char> {
    let (open, close) = (char('d'), char('e'));
    let pairs = (parser(bencode_string), bencode_any());
    let mut dict = between(open, close, many(pairs)).map(|entries:Vec<(String, Bencode)>|{
        let mut hash_map = HashMap::new();
        for (k, v) in entries {
            hash_map.insert(k, v);
        }
        hash_map
    });
    dict.parse_state(input)
}

fn bencode_any<I>() -> FnParser<I, fn (State<I>) -> ParseResult<Bencode, I>> where I:Stream<Item=char> {
    fn bencode_any_<I>(input: State<I>) -> ParseResult<Bencode, I> where I: Stream<Item=char> {
        parser(bencode_integer).map(Bencode::Int)
            .or(parser(bencode_string).map(Bencode::String))
            .or(parser(bencode_list).map(Bencode::List))
            .or(parser(bencode_dict).map(Bencode::Dict)).parse_state(input)
    }
    parser(bencode_any_)
}

fn take <I> (num: i32) -> SizedBuffer<I> where I: Stream<Item=char> {
    SizedBuffer(num, PhantomData)
}

#[derive(Clone)]
pub struct SizedBuffer <I>(i32, PhantomData<I>);
impl <I> Parser for SizedBuffer<I> where I: Stream<Item=char> {
    type Input = I;
    type Output = String;
    fn parse_lazy(&mut self, mut input: State<I>) -> ParseResult<String, I> {
        let start = input.position;
        let mut vec_buf:Vec<char> = Vec::new();
        for _ in 0..self.0 {
            match input.uncons() {
                Ok((other, rest)) => {
                    vec_buf.push(other);
                    input = rest.into_inner();
                }
                Err(error) => {
                    return error.combine(|mut error| {
                        error.position = start;
                        Err(Consumed::Consumed(error))
                    })
                }
            };
        }
        Ok((vec_buf.into_iter().collect(), Consumed::Consumed(input)))
    }
}

fn main () {
    let file_contents = open_file("t-test.torrent");

    match str::from_utf8(&file_contents) {
        Ok(v) => println!("file: {}", v),
        Err(e) => panic!("Not a UTF-8 String: {}", e)
    };

    let result = parser(bencode_dict).parse("d3:abci4ee");
}

#[test]
fn test_integer() {
    let result = parser(bencode_integer).parse("i57e");
    assert_eq!(result, Ok((57,"")));
}

#[test]
fn test_string() {
    let result = parser(bencode_string).parse("5:abcde");
    assert_eq!(result, Ok(("abcde".to_string(), "")));;
}

#[test]
fn test_list() {
    let homogenous_int_list = parser(bencode_list).parse("li57ei32ee");
    assert_eq!(homogenous_int_list, Ok((vec![Bencode::Int(57), Bencode::Int(32)], "")));

    let homogenous_str_list = parser(bencode_list).parse("l3:abc5:defghe");
    assert_eq!(homogenous_str_list, Ok((vec![Bencode::String("abc".to_string()), Bencode::String("defgh".to_string())], "")));

    let hetero_list = parser(bencode_list).parse("li32e3:abce");
    assert_eq!(hetero_list, Ok((vec![Bencode::Int(32), Bencode::String("abc".to_string())], "")));
}

#[test]
fn test_dict() {

}
<|endoftext|>