<commit_before>//! Interface for reading object files.

use alloc::vec::Vec;
use core::{cmp, fmt, result};

use crate::common::*;
use crate::Bytes;

mod util;
pub use util::StringTable;

mod any;
pub use any::*;

#[cfg(feature = "coff")]
pub mod coff;

#[cfg(feature = "elf")]
pub mod elf;

#[cfg(feature = "macho")]
pub mod macho;

#[cfg(feature = "pe")]
pub mod pe;

mod traits;
pub use traits::*;

#[cfg(feature = "wasm")]
pub mod wasm;

mod private {
    pub trait Sealed {}
}

/// The error type used within the read module.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Error(&'static str);

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.0)
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

/// The result type used within the read module.
pub type Result<T> = result::Result<T, Error>;

trait ReadError<T> {
    fn read_error(self, error: &'static str) -> Result<T>;
}

impl<T> ReadError<T> for result::Result<T, ()> {
    fn read_error(self, error: &'static str) -> Result<T> {
        self.map_err(|()| Error(error))
    }
}

impl<T> ReadError<T> for Option<T> {
    fn read_error(self, error: &'static str) -> Result<T> {
        self.ok_or(Error(error))
    }
}

/// The native executable file for the target platform.
#[cfg(all(target_os = "linux", target_pointer_width = "32", feature = "elf"))]
pub type NativeFile<'data> = elf::ElfFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "linux", target_pointer_width = "64", feature = "elf"))]
pub type NativeFile<'data> = elf::ElfFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
pub type NativeFile<'data> = macho::MachOFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
pub type NativeFile<'data> = macho::MachOFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
pub type NativeFile<'data> = pe::PeFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
pub type NativeFile<'data> = pe::PeFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
pub type NativeFile<'data> = wasm::WasmFile<'data>;

/// The index used to identify a section of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SectionIndex(pub usize);

/// The index used to identify a symbol of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolIndex(pub usize);

/// The section where a symbol is defined.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolSection {
    /// The section is unknown.
    Unknown,
    /// The section is not applicable for this symbol (such as file symbols).
    None,
    /// The symbol is undefined.
    Undefined,
    /// The symbol has an absolute value.
    Absolute,
    /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
    Common,
    /// The symbol is defined in the given section.
    Section(SectionIndex),
}

impl SymbolSection {
    /// Returns the section index for the section where the symbol is defined.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn index(self) -> Option<SectionIndex> {
        if let SymbolSection::Section(index) = self {
            Some(index)
        } else {
            None
        }
    }
}

/// A symbol table entry.
#[derive(Clone, Debug)]
pub struct Symbol<'data> {
    name: Option<&'data str>,
    address: u64,
    size: u64,
    kind: SymbolKind,
    section: SymbolSection,
    weak: bool,
    scope: SymbolScope,
    flags: SymbolFlags<SectionIndex>,
}

impl<'data> Symbol<'data> {
    /// Return the kind of this symbol.
    #[inline]
    pub fn kind(&self) -> SymbolKind {
        self.kind
    }

    /// Returns the section where the symbol is defined.
    #[inline]
    pub fn section(&self) -> SymbolSection {
        self.section
    }

    /// Returns the section index for the section containing this symbol.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn section_index(&self) -> Option<SectionIndex> {
        self.section.index()
    }

    /// Return true if the symbol is undefined.
    #[inline]
    pub fn is_undefined(&self) -> bool {
        self.section == SymbolSection::Undefined
    }

    /// Return true if the symbol is common data.
    ///
    /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`.
    #[inline]
    fn is_common(&self) -> bool {
        self.section == SymbolSection::Common
    }

    /// Return true if the symbol is weak.
    #[inline]
    pub fn is_weak(&self) -> bool {
        self.weak
    }

    /// Return true if the symbol visible outside of the compilation unit.
    ///
    /// This treats `SymbolScope::Unknown` as global.
    #[inline]
    pub fn is_global(&self) -> bool {
        !self.is_local()
    }

    /// Return true if the symbol is only visible within the compilation unit.
    #[inline]
    pub fn is_local(&self) -> bool {
        self.scope == SymbolScope::Compilation
    }

    /// Returns the symbol scope.
    #[inline]
    pub fn scope(&self) -> SymbolScope {
        self.scope
    }

    /// Symbol flags that are specific to each file format.
    #[inline]
    pub fn flags(&self) -> SymbolFlags<SectionIndex> {
        self.flags
    }

    /// The name of the symbol.
    #[inline]
    pub fn name(&self) -> Option<&'data str> {
        self.name
    }

    /// The address of the symbol. May be zero if the address is unknown.
    #[inline]
    pub fn address(&self) -> u64 {
        self.address
    }

    /// The size of the symbol. May be zero if the size is unknown.
    #[inline]
    pub fn size(&self) -> u64 {
        self.size
    }
}

/// A map from addresses to symbols.
#[derive(Debug)]
pub struct SymbolMap<'data> {
    symbols: Vec<Symbol<'data>>,
}

impl<'data> SymbolMap<'data> {
    /// Get the symbol containing the given address.
    pub fn get(&self, address: u64) -> Option<&Symbol<'data>> {
        self.symbols
            .binary_search_by(|symbol| {
                if address < symbol.address {
                    cmp::Ordering::Greater
                } else if address < symbol.address + symbol.size {
                    cmp::Ordering::Equal
                } else {
                    cmp::Ordering::Less
                }
            })
            .ok()
            .and_then(|index| self.symbols.get(index))
    }

    /// Get all symbols in the map.
    #[inline]
    pub fn symbols(&self) -> &[Symbol<'data>] {
        &self.symbols
    }

    /// Return true for symbols that should be included in the map.
    fn filter(symbol: &Symbol<'_>) -> bool {
        match symbol.kind() {
            SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {}
            SymbolKind::Null
            | SymbolKind::Section
            | SymbolKind::File
            | SymbolKind::Label
            | SymbolKind::Tls => {
                return false;
            }
        }
        !symbol.is_undefined() && !symbol.is_common() && symbol.size() > 0
    }
}

/// The target referenced by a relocation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationTarget {
    /// The target is a symbol.
    Symbol(SymbolIndex),
    /// The target is a section.
    Section(SectionIndex),
}

/// A relocation entry.
#[derive(Debug)]
pub struct Relocation {
    kind: RelocationKind,
    encoding: RelocationEncoding,
    size: u8,
    target: RelocationTarget,
    addend: i64,
    implicit_addend: bool,
}

impl Relocation {
    /// The operation used to calculate the result of the relocation.
    #[inline]
    pub fn kind(&self) -> RelocationKind {
        self.kind
    }

    /// Information about how the result of the relocation operation is encoded in the place.
    #[inline]
    pub fn encoding(&self) -> RelocationEncoding {
        self.encoding
    }

    /// The size in bits of the place of the relocation.
    ///
    /// If 0, then the size is determined by the relocation kind.
    #[inline]
    pub fn size(&self) -> u8 {
        self.size
    }

    /// The target of the relocation.
    #[inline]
    pub fn target(&self) -> RelocationTarget {
        self.target
    }

    /// The addend to use in the relocation calculation.
    #[inline]
    pub fn addend(&self) -> i64 {
        self.addend
    }

    /// Set the addend to use in the relocation calculation.
    #[inline]
    pub fn set_addend(&mut self, addend: i64) {
        self.addend = addend
    }

    /// Returns true if there is an implicit addend stored in the data at the offset
    /// to be relocated.
    #[inline]
    pub fn has_implicit_addend(&self) -> bool {
        self.implicit_addend
    }
}

fn data_range(data: Bytes, data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> {
    let offset = range_address.checked_sub(data_address)?;
    let data = data.read_bytes_at(offset as usize, size as usize).ok()?;
    Some(data.0)
}
<commit_msg>read: all non-mac unix platforms use ELF as NativeFile<commit_after>//! Interface for reading object files.

use alloc::vec::Vec;
use core::{cmp, fmt, result};

use crate::common::*;
use crate::Bytes;

mod util;
pub use util::StringTable;

mod any;
pub use any::*;

#[cfg(feature = "coff")]
pub mod coff;

#[cfg(feature = "elf")]
pub mod elf;

#[cfg(feature = "macho")]
pub mod macho;

#[cfg(feature = "pe")]
pub mod pe;

mod traits;
pub use traits::*;

#[cfg(feature = "wasm")]
pub mod wasm;

mod private {
    pub trait Sealed {}
}

/// The error type used within the read module.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Error(&'static str);

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.0)
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

/// The result type used within the read module.
pub type Result<T> = result::Result<T, Error>;

trait ReadError<T> {
    fn read_error(self, error: &'static str) -> Result<T>;
}

impl<T> ReadError<T> for result::Result<T, ()> {
    fn read_error(self, error: &'static str) -> Result<T> {
        self.map_err(|()| Error(error))
    }
}

impl<T> ReadError<T> for Option<T> {
    fn read_error(self, error: &'static str) -> Result<T> {
        self.ok_or(Error(error))
    }
}

/// The native executable file for the target platform.
#[cfg(all(
    unix,
    not(target_os = "macos"),
    target_pointer_width = "32",
    feature = "elf"
))]
pub type NativeFile<'data> = elf::ElfFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(
    unix,
    not(target_os = "macos"),
    target_pointer_width = "64",
    feature = "elf"
))]
pub type NativeFile<'data> = elf::ElfFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
pub type NativeFile<'data> = macho::MachOFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
pub type NativeFile<'data> = macho::MachOFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
pub type NativeFile<'data> = pe::PeFile32<'data>;

/// The native executable file for the target platform.
#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
pub type NativeFile<'data> = pe::PeFile64<'data>;

/// The native executable file for the target platform.
#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
pub type NativeFile<'data> = wasm::WasmFile<'data>;

/// The index used to identify a section of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SectionIndex(pub usize);

/// The index used to identify a symbol of a file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SymbolIndex(pub usize);

/// The section where a symbol is defined.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SymbolSection {
    /// The section is unknown.
    Unknown,
    /// The section is not applicable for this symbol (such as file symbols).
    None,
    /// The symbol is undefined.
    Undefined,
    /// The symbol has an absolute value.
    Absolute,
    /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
    Common,
    /// The symbol is defined in the given section.
    Section(SectionIndex),
}

impl SymbolSection {
    /// Returns the section index for the section where the symbol is defined.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn index(self) -> Option<SectionIndex> {
        if let SymbolSection::Section(index) = self {
            Some(index)
        } else {
            None
        }
    }
}

/// A symbol table entry.
#[derive(Clone, Debug)]
pub struct Symbol<'data> {
    name: Option<&'data str>,
    address: u64,
    size: u64,
    kind: SymbolKind,
    section: SymbolSection,
    weak: bool,
    scope: SymbolScope,
    flags: SymbolFlags<SectionIndex>,
}

impl<'data> Symbol<'data> {
    /// Return the kind of this symbol.
    #[inline]
    pub fn kind(&self) -> SymbolKind {
        self.kind
    }

    /// Returns the section where the symbol is defined.
    #[inline]
    pub fn section(&self) -> SymbolSection {
        self.section
    }

    /// Returns the section index for the section containing this symbol.
    ///
    /// May return `None` if the symbol is not defined in a section.
    #[inline]
    pub fn section_index(&self) -> Option<SectionIndex> {
        self.section.index()
    }

    /// Return true if the symbol is undefined.
    #[inline]
    pub fn is_undefined(&self) -> bool {
        self.section == SymbolSection::Undefined
    }

    /// Return true if the symbol is common data.
    ///
    /// Note: does not check for `SymbolSection::Section` with `SectionKind::Common`.
    #[inline]
    fn is_common(&self) -> bool {
        self.section == SymbolSection::Common
    }

    /// Return true if the symbol is weak.
    #[inline]
    pub fn is_weak(&self) -> bool {
        self.weak
    }

    /// Return true if the symbol visible outside of the compilation unit.
    ///
    /// This treats `SymbolScope::Unknown` as global.
    #[inline]
    pub fn is_global(&self) -> bool {
        !self.is_local()
    }

    /// Return true if the symbol is only visible within the compilation unit.
    #[inline]
    pub fn is_local(&self) -> bool {
        self.scope == SymbolScope::Compilation
    }

    /// Returns the symbol scope.
    #[inline]
    pub fn scope(&self) -> SymbolScope {
        self.scope
    }

    /// Symbol flags that are specific to each file format.
    #[inline]
    pub fn flags(&self) -> SymbolFlags<SectionIndex> {
        self.flags
    }

    /// The name of the symbol.
    #[inline]
    pub fn name(&self) -> Option<&'data str> {
        self.name
    }

    /// The address of the symbol. May be zero if the address is unknown.
    #[inline]
    pub fn address(&self) -> u64 {
        self.address
    }

    /// The size of the symbol. May be zero if the size is unknown.
    #[inline]
    pub fn size(&self) -> u64 {
        self.size
    }
}

/// A map from addresses to symbols.
#[derive(Debug)]
pub struct SymbolMap<'data> {
    symbols: Vec<Symbol<'data>>,
}

impl<'data> SymbolMap<'data> {
    /// Get the symbol containing the given address.
    pub fn get(&self, address: u64) -> Option<&Symbol<'data>> {
        self.symbols
            .binary_search_by(|symbol| {
                if address < symbol.address {
                    cmp::Ordering::Greater
                } else if address < symbol.address + symbol.size {
                    cmp::Ordering::Equal
                } else {
                    cmp::Ordering::Less
                }
            })
            .ok()
            .and_then(|index| self.symbols.get(index))
    }

    /// Get all symbols in the map.
    #[inline]
    pub fn symbols(&self) -> &[Symbol<'data>] {
        &self.symbols
    }

    /// Return true for symbols that should be included in the map.
    fn filter(symbol: &Symbol<'_>) -> bool {
        match symbol.kind() {
            SymbolKind::Unknown | SymbolKind::Text | SymbolKind::Data => {}
            SymbolKind::Null
            | SymbolKind::Section
            | SymbolKind::File
            | SymbolKind::Label
            | SymbolKind::Tls => {
                return false;
            }
        }
        !symbol.is_undefined() && !symbol.is_common() && symbol.size() > 0
    }
}

/// The target referenced by a relocation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RelocationTarget {
    /// The target is a symbol.
    Symbol(SymbolIndex),
    /// The target is a section.
    Section(SectionIndex),
}

/// A relocation entry.
#[derive(Debug)]
pub struct Relocation {
    kind: RelocationKind,
    encoding: RelocationEncoding,
    size: u8,
    target: RelocationTarget,
    addend: i64,
    implicit_addend: bool,
}

impl Relocation {
    /// The operation used to calculate the result of the relocation.
    #[inline]
    pub fn kind(&self) -> RelocationKind {
        self.kind
    }

    /// Information about how the result of the relocation operation is encoded in the place.
    #[inline]
    pub fn encoding(&self) -> RelocationEncoding {
        self.encoding
    }

    /// The size in bits of the place of the relocation.
    ///
    /// If 0, then the size is determined by the relocation kind.
    #[inline]
    pub fn size(&self) -> u8 {
        self.size
    }

    /// The target of the relocation.
    #[inline]
    pub fn target(&self) -> RelocationTarget {
        self.target
    }

    /// The addend to use in the relocation calculation.
    #[inline]
    pub fn addend(&self) -> i64 {
        self.addend
    }

    /// Set the addend to use in the relocation calculation.
    #[inline]
    pub fn set_addend(&mut self, addend: i64) {
        self.addend = addend
    }

    /// Returns true if there is an implicit addend stored in the data at the offset
    /// to be relocated.
    #[inline]
    pub fn has_implicit_addend(&self) -> bool {
        self.implicit_addend
    }
}

fn data_range(data: Bytes, data_address: u64, range_address: u64, size: u64) -> Option<&[u8]> {
    let offset = range_address.checked_sub(data_address)?;
    let data = data.read_bytes_at(offset as usize, size as usize).ok()?;
    Some(data.0)
}
<|endoftext|>


<commit_before>use baseband::{Decoder, Decider};
use error::{P25Error, Result};
use nid;
use status::{StreamSymbol, StatusDeinterleaver};
use sync::{SyncCorrelator, SyncDetector};

use self::State::*;
use self::StateChange::*;

const PRIME_SAMPLES: u32 = 6000;

#[derive(Debug)]
pub enum ReceiverEvent {
    Symbol(StreamSymbol),
    NetworkID(nid::NetworkID),
}

#[derive(Copy, Clone)]
struct Receiver {
    recv: Decoder,
    status: StatusDeinterleaver,
}

impl Receiver {
    pub fn new(recv: Decoder) -> Receiver {
        Receiver {
            recv: recv,
            status: StatusDeinterleaver::new(),
        }
    }

    pub fn feed(&mut self, s: f32) -> Option<StreamSymbol> {
        let dibit = match self.recv.feed(s) {
            Some(dibit) => dibit,
            None => return None,
        };

        Some(self.status.feed(dibit))
    }
}

enum State {
    Prime(u32),
    Sync(SyncDetector),
    DecodeNID(Receiver, nid::NIDReceiver),
    DecodePacket(Receiver),
    FlushPads(Receiver),
}

enum StateChange {
    Change(State),
    Event(ReceiverEvent),
    EventChange(ReceiverEvent, State),
    Error(P25Error),
    NoChange,
}

impl State {
    pub fn prime() -> State { Prime(1) }
    pub fn sync() -> State { Sync(SyncDetector::new()) }
    pub fn decode_nid(decoder: Decoder) -> State {
        DecodeNID(Receiver::new(decoder), nid::NIDReceiver::new())
    }
    pub fn decode_packet(recv: Receiver) -> State { DecodePacket(recv) }
    pub fn flush_pads(recv: Receiver) -> State { FlushPads(recv) }
}

pub struct DataUnitReceiver {
    state: State,
    corr: SyncCorrelator,
}

impl DataUnitReceiver {
    pub fn new() -> DataUnitReceiver {
        DataUnitReceiver {
            state: State::prime(),
            corr: SyncCorrelator::new(),
        }
    }

    pub fn flush_pads(&mut self) {
        match self.state {
            DecodePacket(recv) => self.state = State::flush_pads(recv),
            Sync(_) => {},
            _ => panic!("not decoding a packet"),
        }
    }

    pub fn resync(&mut self) { self.state = State::sync(); }

    fn handle(&mut self, s: f32) -> StateChange {
        let (power, thresh) = self.corr.feed(s);

        match self.state {
            Prime(t) => if t == PRIME_SAMPLES {
                Change(State::sync())
            } else {
                Change(Prime(t + 1))
            },
            Sync(ref mut sync) => if sync.feed(power, thresh) {
                let (p, m, n) = self.corr.thresholds();
                Change(State::decode_nid(Decoder::new(Decider::new(p, m, n))))
            } else {
                NoChange
            },
            DecodeNID(ref mut recv, ref mut nid) => {
                let dibit = match recv.feed(s) {
                    Some(StreamSymbol::Data(d)) => d,
                    Some(s) => return Event(ReceiverEvent::Symbol(s)),
                    None => return NoChange,
                };

                match nid.feed(dibit) {
                    Some(Ok(nid)) => EventChange(ReceiverEvent::NetworkID(nid),
                                                 State::decode_packet(*recv)),
                    Some(Err(e)) => Error(e),
                    None => NoChange,
                }
            },
            DecodePacket(ref mut recv) => match recv.feed(s) {
                Some(x) => Event(ReceiverEvent::Symbol(x)),
                None => NoChange,
            },
            FlushPads(ref mut recv) => match recv.feed(s) {
                Some(StreamSymbol::Status(_)) => Change(State::sync()),
                _ => NoChange,
            },
        }
    }

    pub fn feed(&mut self, s: f32) -> Option<Result<ReceiverEvent>> {
        match self.handle(s) {
            Change(state) => {
                self.state = state;
                None
            },
            Event(event) => Some(Ok(event)),
            EventChange(event, state) => {
                self.state = state;
                Some(Ok(event))
            },
            Error(err) => Some(Err(err)),
            NoChange => None,
        }
    }
}
<commit_msg>Simplify Receiver::feed<commit_after>use baseband::{Decoder, Decider};
use error::{P25Error, Result};
use nid;
use status::{StreamSymbol, StatusDeinterleaver};
use sync::{SyncCorrelator, SyncDetector};

use self::State::*;
use self::StateChange::*;

const PRIME_SAMPLES: u32 = 6000;

#[derive(Debug)]
pub enum ReceiverEvent {
    Symbol(StreamSymbol),
    NetworkID(nid::NetworkID),
}

#[derive(Copy, Clone)]
struct Receiver {
    recv: Decoder,
    status: StatusDeinterleaver,
}

impl Receiver {
    pub fn new(recv: Decoder) -> Receiver {
        Receiver {
            recv: recv,
            status: StatusDeinterleaver::new(),
        }
    }

    pub fn feed(&mut self, s: f32) -> Option<StreamSymbol> {
        match self.recv.feed(s) {
            Some(dibit) => Some(self.status.feed(dibit)),
            None => None,
        }
    }
}

enum State {
    Prime(u32),
    Sync(SyncDetector),
    DecodeNID(Receiver, nid::NIDReceiver),
    DecodePacket(Receiver),
    FlushPads(Receiver),
}

enum StateChange {
    Change(State),
    Event(ReceiverEvent),
    EventChange(ReceiverEvent, State),
    Error(P25Error),
    NoChange,
}

impl State {
    pub fn prime() -> State { Prime(1) }
    pub fn sync() -> State { Sync(SyncDetector::new()) }
    pub fn decode_nid(decoder: Decoder) -> State {
        DecodeNID(Receiver::new(decoder), nid::NIDReceiver::new())
    }
    pub fn decode_packet(recv: Receiver) -> State { DecodePacket(recv) }
    pub fn flush_pads(recv: Receiver) -> State { FlushPads(recv) }
}

pub struct DataUnitReceiver {
    state: State,
    corr: SyncCorrelator,
}

impl DataUnitReceiver {
    pub fn new() -> DataUnitReceiver {
        DataUnitReceiver {
            state: State::prime(),
            corr: SyncCorrelator::new(),
        }
    }

    pub fn flush_pads(&mut self) {
        match self.state {
            DecodePacket(recv) => self.state = State::flush_pads(recv),
            Sync(_) => {},
            _ => panic!("not decoding a packet"),
        }
    }

    pub fn resync(&mut self) { self.state = State::sync(); }

    fn handle(&mut self, s: f32) -> StateChange {
        let (power, thresh) = self.corr.feed(s);

        match self.state {
            Prime(t) => if t == PRIME_SAMPLES {
                Change(State::sync())
            } else {
                Change(Prime(t + 1))
            },
            Sync(ref mut sync) => if sync.feed(power, thresh) {
                let (p, m, n) = self.corr.thresholds();
                Change(State::decode_nid(Decoder::new(Decider::new(p, m, n))))
            } else {
                NoChange
            },
            DecodeNID(ref mut recv, ref mut nid) => {
                let dibit = match recv.feed(s) {
                    Some(StreamSymbol::Data(d)) => d,
                    Some(s) => return Event(ReceiverEvent::Symbol(s)),
                    None => return NoChange,
                };

                match nid.feed(dibit) {
                    Some(Ok(nid)) => EventChange(ReceiverEvent::NetworkID(nid),
                                                 State::decode_packet(*recv)),
                    Some(Err(e)) => Error(e),
                    None => NoChange,
                }
            },
            DecodePacket(ref mut recv) => match recv.feed(s) {
                Some(x) => Event(ReceiverEvent::Symbol(x)),
                None => NoChange,
            },
            FlushPads(ref mut recv) => match recv.feed(s) {
                Some(StreamSymbol::Status(_)) => Change(State::sync()),
                _ => NoChange,
            },
        }
    }

    pub fn feed(&mut self, s: f32) -> Option<Result<ReceiverEvent>> {
        match self.handle(s) {
            Change(state) => {
                self.state = state;
                None
            },
            Event(event) => Some(Ok(event)),
            EventChange(event, state) => {
                self.state = state;
                Some(Ok(event))
            },
            Error(err) => Some(Err(err)),
            NoChange => None,
        }
    }
}
<|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.

//! Transaction pool implementation.
//! Used for both the txpool and stempool layers in the pool.

use std::collections::HashSet;
use std::sync::Arc;

use core::core::hash::Hashed;
use core::core::id::ShortIdentifiable;
use core::core::transaction;
use core::core::{Block, CompactBlock, Transaction, TxKernel};
use types::{BlockChain, PoolEntry, PoolEntryState, PoolError};
use util::LOGGER;

pub struct Pool<T> {
	/// Entries in the pool (tx + info + timer) in simple insertion order.
	pub entries: Vec<PoolEntry>,
	/// The blockchain
	pub blockchain: Arc<T>,
	pub name: String,
}

impl<T> Pool<T>
where
	T: BlockChain,
{
	pub fn new(chain: Arc<T>, name: String) -> Pool<T> {
		Pool {
			entries: vec![],
			blockchain: chain.clone(),
			name,
		}
	}

	/// Query the tx pool for all known txs based on kernel short_ids
	/// from the provided compact_block.
	/// Note: does not validate that we return the full set of required txs.
	/// The caller will need to validate that themselves.
	pub fn retrieve_transactions(&self, cb: &CompactBlock) -> Vec<Transaction> {
		let mut txs = vec![];

		for x in &self.entries {
			for kernel in &x.tx.kernels {
				// rehash each kernel to calculate the block specific short_id
				let short_id = kernel.short_id(&cb.hash(), cb.nonce);

				// if any kernel matches then keep the tx for later
				if cb.kern_ids.contains(&short_id) {
					txs.push(x.tx.clone());
					break;
				}
			}
		}

		txs
	}

	/// Take the first num_to_fetch txs based on insertion order.
	pub fn prepare_mineable_transactions(&self, num_to_fetch: u32) -> Vec<Transaction> {
		self.entries
			.iter()
			.take(num_to_fetch as usize)
			.map(|x| x.tx.clone())
			.collect()
	}

	pub fn all_transactions(&self) -> Vec<Transaction> {
		self.entries.iter().map(|x| x.tx.clone()).collect()
	}

	pub fn aggregate_transaction(&self) -> Result<Option<Transaction>, PoolError> {
		let txs = self.all_transactions();
		if txs.is_empty() {
			return Ok(None);
		}

		let tx = transaction::aggregate(txs)?;
		Ok(Some(tx))
	}

	pub fn select_valid_transactions(
		&mut self,
		from_state: PoolEntryState,
		to_state: PoolEntryState,
		extra_tx: Option<Transaction>,
	) -> Result<Vec<Transaction>, PoolError> {
		let entries = &mut self.entries
			.iter_mut()
			.filter(|x| x.state == from_state)
			.collect::<Vec<_>>();

		let candidate_txs = entries.iter().map(|x| x.tx.clone()).collect();
		let valid_txs = self.blockchain.validate_raw_txs(candidate_txs, extra_tx)?;

		// Update state on all entries included in final vec of valid txs.
		for x in &mut entries.iter_mut() {
			if valid_txs.contains(&x.tx) {
				x.state = to_state.clone();
			}
		}

		Ok(valid_txs)
	}

	// Aggregate this new tx with all existing txs in the pool.
	// If we can validate the aggregated tx against the current chain state
	// then we can safely add the tx to the pool.
	pub fn add_to_pool(
		&mut self,
		entry: PoolEntry,
		extra_txs: Vec<Transaction>,
	) -> Result<(), PoolError> {
		debug!(
			LOGGER,
			"pool [{}]: add_to_pool: {}, {:?}",
			self.name,
			entry.tx.hash(),
			entry.src,
		);

		// Combine all the txs from the pool with any extra txs provided.
		let mut txs = self.all_transactions();
		txs.extend(extra_txs);

		let agg_tx = if txs.is_empty() {
			// If we have nothing to aggregate then simply return the tx itself.
			entry.tx.clone()
		} else {
			// Create a single aggregated tx from the existing pool txs (to check pool is
			// valid).
			let agg_tx = transaction::aggregate(txs)?;

			// Then check new tx would not introduce a duplicate output in the pool.
			for x in &entry.tx.outputs {
				if agg_tx.outputs.contains(&x) {
					return Err(PoolError::DuplicateCommitment);
				}
			}

			// Finally aggregate the new tx with everything in the pool (with any extra
			// txs).
			transaction::aggregate(vec![agg_tx, entry.tx.clone()])?
		};

		// Validate aggregated tx against the current chain state (via txhashset
		// extension).
		self.blockchain.validate_raw_txs(vec![], Some(agg_tx))?;

		// If we get here successfully then we can safely add the entry to the pool.
		self.entries.push(entry);

		Ok(())
	}

	pub fn reconcile(&mut self, extra_tx: Option<Transaction>) -> Result<(), PoolError> {
		let candidate_txs = self.all_transactions();
		let existing_len = candidate_txs.len();

		if candidate_txs.is_empty() {
			return Ok(());
		}

		// Go through the candidate txs and keep everything that validates incrementally
		// against the current chain state, accounting for the "extra tx" as necessary.
		let valid_txs = self.blockchain.validate_raw_txs(candidate_txs, extra_tx)?;
		self.entries.retain(|x| valid_txs.contains(&x.tx));

		debug!(
			LOGGER,
			"pool [{}]: reconcile: existing txs {}, retained txs {}",
			self.name,
			existing_len,
			self.entries.len(),
		);

		Ok(())
	}

	// Filter txs in the pool based on the latest block.
	// Reject any txs where we see a matching tx kernel in the block.
	// Also reject any txs where we see a conflicting tx,
	// where an input is spent in a different tx.
	fn remaining_transactions(&self, block: &Block) -> Vec<Transaction> {
		self.entries
			.iter()
			.filter(|x| !x.tx.kernels.iter().any(|y| block.kernels.contains(y)))
			.filter(|x| !x.tx.inputs.iter().any(|y| block.inputs.contains(y)))
			.map(|x| x.tx.clone())
			.collect()
	}

	pub fn find_matching_transactions(&self, kernels: Vec<TxKernel>) -> Vec<Transaction> {
		// While the inputs outputs can be cut-through the kernel will stay intact
		// In order to deaggregate tx we look for tx with the same kernel
		let mut found_txs = vec![];

		// Gather all the kernels of the multi-kernel transaction in one set
		let kernel_set = kernels.into_iter().collect::<HashSet<_>>();

		// Check each transaction in the pool
		for entry in &self.entries {
			let entry_kernel_set = entry.tx.kernels.iter().cloned().collect::<HashSet<_>>();
			if entry_kernel_set.is_subset(&kernel_set) {
				found_txs.push(entry.tx.clone());
			}
		}
		found_txs
	}

	/// Quick reconciliation step - we can evict any txs in the pool where
	/// inputs or kernels intersect with the block.
	pub fn reconcile_block(&mut self, block: &Block) -> Result<(), PoolError> {
		let candidate_txs = self.remaining_transactions(block);
		self.entries.retain(|x| candidate_txs.contains(&x.tx));
		Ok(())
	}

	pub fn size(&self) -> usize {
		self.entries.len()
	}
}
<commit_msg>Fix empty candidate txs (#1168)<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.

//! Transaction pool implementation.
//! Used for both the txpool and stempool layers in the pool.

use std::collections::HashSet;
use std::sync::Arc;

use core::core::hash::Hashed;
use core::core::id::ShortIdentifiable;
use core::core::transaction;
use core::core::{Block, CompactBlock, Transaction, TxKernel};
use types::{BlockChain, PoolEntry, PoolEntryState, PoolError};
use util::LOGGER;

pub struct Pool<T> {
	/// Entries in the pool (tx + info + timer) in simple insertion order.
	pub entries: Vec<PoolEntry>,
	/// The blockchain
	pub blockchain: Arc<T>,
	pub name: String,
}

impl<T> Pool<T>
where
	T: BlockChain,
{
	pub fn new(chain: Arc<T>, name: String) -> Pool<T> {
		Pool {
			entries: vec![],
			blockchain: chain.clone(),
			name,
		}
	}

	/// Query the tx pool for all known txs based on kernel short_ids
	/// from the provided compact_block.
	/// Note: does not validate that we return the full set of required txs.
	/// The caller will need to validate that themselves.
	pub fn retrieve_transactions(&self, cb: &CompactBlock) -> Vec<Transaction> {
		let mut txs = vec![];

		for x in &self.entries {
			for kernel in &x.tx.kernels {
				// rehash each kernel to calculate the block specific short_id
				let short_id = kernel.short_id(&cb.hash(), cb.nonce);

				// if any kernel matches then keep the tx for later
				if cb.kern_ids.contains(&short_id) {
					txs.push(x.tx.clone());
					break;
				}
			}
		}

		txs
	}

	/// Take the first num_to_fetch txs based on insertion order.
	pub fn prepare_mineable_transactions(&self, num_to_fetch: u32) -> Vec<Transaction> {
		self.entries
			.iter()
			.take(num_to_fetch as usize)
			.map(|x| x.tx.clone())
			.collect()
	}

	pub fn all_transactions(&self) -> Vec<Transaction> {
		self.entries.iter().map(|x| x.tx.clone()).collect()
	}

	pub fn aggregate_transaction(&self) -> Result<Option<Transaction>, PoolError> {
		let txs = self.all_transactions();
		if txs.is_empty() {
			return Ok(None);
		}

		let tx = transaction::aggregate(txs)?;
		Ok(Some(tx))
	}

	pub fn select_valid_transactions(
		&mut self,
		from_state: PoolEntryState,
		to_state: PoolEntryState,
		extra_tx: Option<Transaction>,
	) -> Result<Vec<Transaction>, PoolError> {
		let entries = &mut self.entries
			.iter_mut()
			.filter(|x| x.state == from_state)
			.collect::<Vec<_>>();

		let candidate_txs: Vec<Transaction> = entries.iter().map(|x| x.tx.clone()).collect();
		if candidate_txs.is_empty() {
			return Ok(vec![]);
		}
		let valid_txs = self.blockchain.validate_raw_txs(candidate_txs, extra_tx)?;

		// Update state on all entries included in final vec of valid txs.
		for x in &mut entries.iter_mut() {
			if valid_txs.contains(&x.tx) {
				x.state = to_state.clone();
			}
		}

		Ok(valid_txs)
	}

	// Aggregate this new tx with all existing txs in the pool.
	// If we can validate the aggregated tx against the current chain state
	// then we can safely add the tx to the pool.
	pub fn add_to_pool(
		&mut self,
		entry: PoolEntry,
		extra_txs: Vec<Transaction>,
	) -> Result<(), PoolError> {
		debug!(
			LOGGER,
			"pool [{}]: add_to_pool: {}, {:?}",
			self.name,
			entry.tx.hash(),
			entry.src,
		);

		// Combine all the txs from the pool with any extra txs provided.
		let mut txs = self.all_transactions();
		txs.extend(extra_txs);

		let agg_tx = if txs.is_empty() {
			// If we have nothing to aggregate then simply return the tx itself.
			entry.tx.clone()
		} else {
			// Create a single aggregated tx from the existing pool txs (to check pool is
			// valid).
			let agg_tx = transaction::aggregate(txs)?;

			// Then check new tx would not introduce a duplicate output in the pool.
			for x in &entry.tx.outputs {
				if agg_tx.outputs.contains(&x) {
					return Err(PoolError::DuplicateCommitment);
				}
			}

			// Finally aggregate the new tx with everything in the pool (with any extra
			// txs).
			transaction::aggregate(vec![agg_tx, entry.tx.clone()])?
		};

		// Validate aggregated tx against the current chain state (via txhashset
		// extension).
		self.blockchain.validate_raw_txs(vec![], Some(agg_tx))?;

		// If we get here successfully then we can safely add the entry to the pool.
		self.entries.push(entry);

		Ok(())
	}

	pub fn reconcile(&mut self, extra_tx: Option<Transaction>) -> Result<(), PoolError> {
		let candidate_txs = self.all_transactions();
		let existing_len = candidate_txs.len();

		if candidate_txs.is_empty() {
			return Ok(());
		}

		// Go through the candidate txs and keep everything that validates incrementally
		// against the current chain state, accounting for the "extra tx" as necessary.
		let valid_txs = self.blockchain.validate_raw_txs(candidate_txs, extra_tx)?;
		self.entries.retain(|x| valid_txs.contains(&x.tx));

		debug!(
			LOGGER,
			"pool [{}]: reconcile: existing txs {}, retained txs {}",
			self.name,
			existing_len,
			self.entries.len(),
		);

		Ok(())
	}

	// Filter txs in the pool based on the latest block.
	// Reject any txs where we see a matching tx kernel in the block.
	// Also reject any txs where we see a conflicting tx,
	// where an input is spent in a different tx.
	fn remaining_transactions(&self, block: &Block) -> Vec<Transaction> {
		self.entries
			.iter()
			.filter(|x| !x.tx.kernels.iter().any(|y| block.kernels.contains(y)))
			.filter(|x| !x.tx.inputs.iter().any(|y| block.inputs.contains(y)))
			.map(|x| x.tx.clone())
			.collect()
	}

	pub fn find_matching_transactions(&self, kernels: Vec<TxKernel>) -> Vec<Transaction> {
		// While the inputs outputs can be cut-through the kernel will stay intact
		// In order to deaggregate tx we look for tx with the same kernel
		let mut found_txs = vec![];

		// Gather all the kernels of the multi-kernel transaction in one set
		let kernel_set = kernels.into_iter().collect::<HashSet<_>>();

		// Check each transaction in the pool
		for entry in &self.entries {
			let entry_kernel_set = entry.tx.kernels.iter().cloned().collect::<HashSet<_>>();
			if entry_kernel_set.is_subset(&kernel_set) {
				found_txs.push(entry.tx.clone());
			}
		}
		found_txs
	}

	/// Quick reconciliation step - we can evict any txs in the pool where
	/// inputs or kernels intersect with the block.
	pub fn reconcile_block(&mut self, block: &Block) -> Result<(), PoolError> {
		let candidate_txs = self.remaining_transactions(block);
		self.entries.retain(|x| candidate_txs.contains(&x.tx));
		Ok(())
	}

	pub fn size(&self) -> usize {
		self.entries.len()
	}
}
<|endoftext|>


<commit_before>use kay::{Recipient, Actor, Fate};
use super::CurrentPlan;
use core::geometry::AnyShape;

#[derive(Default)]
pub struct Interaction {
    cmd_pressed: bool,
    shift_pressed: bool,
}

use super::InitInteractable;
use monet::{Renderer, AddEyeListener};
use core::ui::{UserInterface, Add, Focus};

impl Recipient<InitInteractable> for CurrentPlan {
    fn receive(&mut self, _msg: &InitInteractable) -> Fate {
        UserInterface::id() << Add::Interactable3d(CurrentPlan::id(), AnyShape::Everywhere, 0);
        UserInterface::id() << Focus(CurrentPlan::id());
        Renderer::id() <<
        AddEyeListener {
            scene_id: 0,
            listener: CurrentPlan::id(),
        };
        Fate::Live
    }
}

use monet::EyeMoved;

impl Recipient<EyeMoved> for CurrentPlan {
    fn receive(&mut self, msg: &EyeMoved) -> Fate {
        match *msg {
            EyeMoved { eye, .. } => {
                if eye.position.z < 100.0 {
                    self.settings.select_parallel = false;
                    self.settings.select_opposite = false;
                } else if eye.position.z < 130.0 {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = false;
                } else {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = true;
                }
                Fate::Live
            }
        }
    }
}

use core::ui::{Event3d, VirtualKeyCode};
use super::{Intent, ChangeIntent, IntentProgress, Materialize, Undo, Redo, SetNLanes,
            ToggleBothSides};

impl Recipient<Event3d> for CurrentPlan {
    fn receive(&mut self, msg: &Event3d) -> Fate {
        match *msg {
            Event3d::KeyDown(VirtualKeyCode::Return) => {
                CurrentPlan::id() << Materialize;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LControl) |
            Event3d::KeyDown(VirtualKeyCode::RControl) |
            Event3d::KeyDown(VirtualKeyCode::LWin) |
            Event3d::KeyDown(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LControl) |
            Event3d::KeyUp(VirtualKeyCode::RControl) |
            Event3d::KeyUp(VirtualKeyCode::LWin) |
            Event3d::KeyUp(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LShift) |
            Event3d::KeyDown(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LShift) |
            Event3d::KeyUp(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Z) => {
                if self.interaction.cmd_pressed {
                    if self.interaction.shift_pressed {
                        CurrentPlan::id() << Redo;
                    } else {
                        CurrentPlan::id() << Undo;
                    }
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::C) => {
                // Swarm::<::game::lanes_and_cars::lane::Lane>::all() <<
                // ToRandom {
                //     n_recipients: 5000,
                //     message: Event3d::DragFinished {
                //         from: P3::new(0.0, 0.0, 0.0),
                //         to: P3::new(0.0, 0.0, 0.0),
                //     },
                // };
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::G) => {
                // CurrentPlan::id() << CreateGrid(if self.interaction.shift_pressed { 15 } else { 10 });
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Back) => {
                Self::id() << ChangeIntent(Intent::DeleteSelection, IntentProgress::Finished);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key1) => {
                Self::id() << SetNLanes(1);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key2) => {
                Self::id() << SetNLanes(2);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key3) => {
                Self::id() << SetNLanes(3);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key4) => {
                Self::id() << SetNLanes(4);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key5) => {
                Self::id() << SetNLanes(5);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key6) => {
                Self::id() << SetNLanes(6);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key7) => {
                Self::id() << SetNLanes(7);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key8) => {
                Self::id() << SetNLanes(8);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key9) => {
                Self::id() << SetNLanes(9);
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::Key0) => {
                Self::id() << ToggleBothSides;
                Fate::Live
            }
            _ => Fate::Live,
        }
    }
}

pub fn setup() {
    CurrentPlan::handle::<InitInteractable>();
    CurrentPlan::handle::<EyeMoved>();
    CurrentPlan::handle::<Event3d>();
    CurrentPlan::id() << InitInteractable;
}<commit_msg>Build grids<commit_after>use kay::{Recipient, Actor, Fate};
use super::CurrentPlan;
use core::geometry::AnyShape;
use descartes::{N, P2};

#[derive(Default)]
pub struct Interaction {
    cmd_pressed: bool,
    shift_pressed: bool,
}

use super::InitInteractable;
use monet::{Renderer, AddEyeListener};
use core::ui::{UserInterface, Add, Focus};

impl Recipient<InitInteractable> for CurrentPlan {
    fn receive(&mut self, _msg: &InitInteractable) -> Fate {
        UserInterface::id() << Add::Interactable3d(CurrentPlan::id(), AnyShape::Everywhere, 0);
        UserInterface::id() << Focus(CurrentPlan::id());
        Renderer::id() <<
        AddEyeListener {
            scene_id: 0,
            listener: CurrentPlan::id(),
        };
        Fate::Live
    }
}

use monet::EyeMoved;

impl Recipient<EyeMoved> for CurrentPlan {
    fn receive(&mut self, msg: &EyeMoved) -> Fate {
        match *msg {
            EyeMoved { eye, .. } => {
                if eye.position.z < 100.0 {
                    self.settings.select_parallel = false;
                    self.settings.select_opposite = false;
                } else if eye.position.z < 130.0 {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = false;
                } else {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = true;
                }
                Fate::Live
            }
        }
    }
}

use core::ui::{Event3d, VirtualKeyCode};
use super::{Intent, ChangeIntent, IntentProgress, Materialize, Undo, Redo, SetNLanes,
            ToggleBothSides};
use super::stroke_canvas::{Stroke, StrokeState};

impl Recipient<Event3d> for CurrentPlan {
    fn receive(&mut self, msg: &Event3d) -> Fate {
        match *msg {
            Event3d::KeyDown(VirtualKeyCode::Return) => {
                CurrentPlan::id() << Materialize;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LControl) |
            Event3d::KeyDown(VirtualKeyCode::RControl) |
            Event3d::KeyDown(VirtualKeyCode::LWin) |
            Event3d::KeyDown(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LControl) |
            Event3d::KeyUp(VirtualKeyCode::RControl) |
            Event3d::KeyUp(VirtualKeyCode::LWin) |
            Event3d::KeyUp(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LShift) |
            Event3d::KeyDown(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LShift) |
            Event3d::KeyUp(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Z) => {
                if self.interaction.cmd_pressed {
                    if self.interaction.shift_pressed {
                        CurrentPlan::id() << Redo;
                    } else {
                        CurrentPlan::id() << Undo;
                    }
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::C) => {
                // Swarm::<::game::lanes_and_cars::lane::Lane>::all() <<
                // ToRandom {
                //     n_recipients: 5000,
                //     message: Event3d::DragFinished {
                //         from: P3::new(0.0, 0.0, 0.0),
                //         to: P3::new(0.0, 0.0, 0.0),
                //     },
                // };
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::G) => {
                const GRID_SPACING: N = 1000.0;
                let grid_size = if self.interaction.shift_pressed {
                    15usize
                } else {
                    10usize
                };
                for x in 0..grid_size {
                    Self::id() <<
                    Stroke(vec![P2::new((x as f32 + 0.5) * GRID_SPACING, 0.0),
                                P2::new((x as f32 + 0.5) * GRID_SPACING,
                                        grid_size as f32 * GRID_SPACING)]
                               .into(),
                           StrokeState::Finished);
                }
                for y in 0..grid_size {
                    Self::id() <<
                    Stroke(vec![P2::new(0.0, (y as f32 + 0.5) * GRID_SPACING),
                                P2::new(grid_size as f32 * GRID_SPACING,
                                        (y as f32 + 0.5) * GRID_SPACING)]
                               .into(),
                           StrokeState::Finished);
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Back) => {
                Self::id() << ChangeIntent(Intent::DeleteSelection, IntentProgress::Finished);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key1) => {
                Self::id() << SetNLanes(1);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key2) => {
                Self::id() << SetNLanes(2);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key3) => {
                Self::id() << SetNLanes(3);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key4) => {
                Self::id() << SetNLanes(4);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key5) => {
                Self::id() << SetNLanes(5);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key6) => {
                Self::id() << SetNLanes(6);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key7) => {
                Self::id() << SetNLanes(7);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key8) => {
                Self::id() << SetNLanes(8);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key9) => {
                Self::id() << SetNLanes(9);
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::Key0) => {
                Self::id() << ToggleBothSides;
                Fate::Live
            }
            _ => Fate::Live,
        }
    }
}

pub fn setup() {
    CurrentPlan::handle::<InitInteractable>();
    CurrentPlan::handle::<EyeMoved>();
    CurrentPlan::handle::<Event3d>();
    CurrentPlan::id() << InitInteractable;
}<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use vec_map::VecMap;
use calx_ecs::Entity;
use location::Location;
use item::Slot;
use self::Place::*;

/// Entities can be placed either on open locations or inside other entities.
/// A sum type will represent this nicely.
#[derive(Copy, Eq, PartialEq, Clone, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum Place {
    At(Location),
    In(Entity, Option<Slot>),
}

/// Spatial index for game entities
pub struct Spatial {
    place_to_entities: BTreeMap<Place, Vec<Entity>>,
    entity_to_place: VecMap<Place>,
}

impl Spatial {
    pub fn new() -> Spatial {
        Spatial {
            place_to_entities: BTreeMap::new(),
            entity_to_place: VecMap::new(),
        }
    }

    fn insert(&mut self, Entity(idx): Entity, p: Place) {
        // Remove the entity from its old position.
        self.single_remove(Entity(idx));

        if let In(_, Some(_)) = p {
            // Slotted in-places are a special case that can hold at most one entity.
            if self.place_to_entities.contains_key(&p) {
                panic!("Equipping to an occupied inventory slot");
            }
        }

        self.entity_to_place.insert(idx, p);
        match self.place_to_entities.get_mut(&p) {
            Some(v) => {
                v.push(Entity(idx));
                return;
            }
            _ => (),
        };
        // Didn't return above, that means this location isn't indexed
        // yet and needs a brand new container. (Can't do this in match
        // block because borrows.)
        self.place_to_entities.insert(p, vec![Entity(idx)]);
    }

    /// Insert an entity into space.
    pub fn insert_at(&mut self, e: Entity, loc: Location) {
        self.insert(e, At(loc));
    }

    /// Return whether the parent entity or an entity contained in the parent
    /// entity contains entity e.
    pub fn contains(&self, parent: Entity, Entity(idx): Entity) -> bool {
        match self.entity_to_place.get(&idx) {
            Some(&In(p, _)) if p == parent => true,
            Some(&In(p, _)) => self.contains(parent, p),
            _ => false,
        }
    }

    /// Insert an entity into container.
    pub fn insert_in(&mut self, e: Entity, parent: Entity) {
        assert!(!self.contains(e, parent),
                "Trying to create circular containment");
        self.insert(e, In(parent, None));
    }

    /// Insert an entity into an equipment slot. Will panic if there already
    /// is an item present in the slot.
    pub fn equip(&mut self, e: Entity, parent: Entity, slot: Slot) {
        self.insert(e, In(parent, Some(slot)));
    }

    /// Remove an entity from the local structures but do not pop out its
    /// items. Unless the entity is added back in or the contents are handled
    /// somehow, this will leave the spatial index in an inconsistent state.
    fn single_remove(&mut self, Entity(idx): Entity) {
        if !self.entity_to_place.contains_key(&idx) {
            return;
        }

        let &p = self.entity_to_place.get(&idx).unwrap();
        self.entity_to_place.remove(&idx);

        {
            let v = self.place_to_entities.get_mut(&p).unwrap();
            assert!(v.len() > 0);
            if v.len() > 1 {
                // More than one entity present, remove this one, keep the
                // rest.
                for i in 0..v.len() {
                    if v[i] == Entity(idx) {
                        v.swap_remove(i);
                        return;
                    }
                }
                panic!("Entity being removed from place it's not in");
            } else {
                // This was the only entity in the location.
                // Drop the entry for this location from the index.
                // (Need to drop out of scope for borrows reasons)
                assert!((*v)[0] == Entity(idx));
            }
        }
        // We only end up here if we need to clear the container for the
        // location.
        self.place_to_entities.remove(&p);
    }

    /// Remove an entity from the space. Entities contained in the entity will
    /// also be removed from the space.
    pub fn remove(&mut self, Entity(idx): Entity) {
        // Remove the contents
        for &content in self.entities_in(Entity(idx)).iter() {
            self.remove(content);
        }
        self.single_remove(Entity(idx));
    }

    fn entities(&self, p: Place) -> Vec<Entity> {
        match self.place_to_entities.get(&p) {
            None => vec![],
            Some(v) => v.clone(),
        }
    }

    /// List entities at a location.
    pub fn entities_at(&self, loc: Location) -> Vec<Entity> {
        self.entities(At(loc))
    }

    /// List entities in a container.
    pub fn entities_in(&self, parent: Entity) -> Vec<Entity> {
        // XXX: Can't make the API return an iterator (more efficient than
        // running collect) since the chain depends on a closure that captures
        // the 'parent' parameter from the outside scope, and closures can't
        // be typed in the return signature.

        // TODO: Get range iteration for BTreeMaps working in stable Rust.
        /*
        self.place_to_entities.range(Included(&In(parent, None)), Unbounded)
            // Consume the contingent elements for the parent container.
            .take_while(|&(ref k, _)| if let &&In(ref p, _) = k { *p == parent } else { false })
         */

        // XXX: This replacement thing is quite nasty, it iterates over the
        // whole collection for every query.
        self.place_to_entities
            .iter()
            .filter(|&(ref k, _)| {
                if let &&In(ref p, _) = k {
                    *p == parent
                } else {
                    false
                }
            })
            .flat_map(|(_, ref v)| v.iter())
            .map(|&x| x)
            .collect()
    }

    pub fn entity_equipped(&self, parent: Entity, slot: Slot) -> Option<Entity> {
        match self.place_to_entities.get(&In(parent, Some(slot))) {
            None => None,
            Some(v) => {
                assert!(v.len() == 1, "Slot entity container corrupt");
                Some(v[0])
            }
        }
    }

    /// Return the place of an entity if the entity is present in the space.
    pub fn get(&self, Entity(idx): Entity) -> Option<Place> {
        self.entity_to_place.get(&idx).map(|&loc| loc)
    }

    /// Flatten to an easily serializable vector.
    fn dump(&self) -> Vec<Elt> {
        let mut ret = vec![];
        for (idx, &loc) in self.entity_to_place.iter() {
            ret.push(Elt(Entity(idx), loc));
        }
        ret
    }

    /// Construct from the serialized vector.
    fn slurp(dump: Vec<Elt>) -> Spatial {
        let mut ret = Spatial::new();

        for &Elt(e, loc) in dump.iter() {
            ret.insert(e, loc);
        }
        ret
    }
}

#[derive(Clone, Serialize, Deserialize)]
struct Elt(Entity, Place);

impl Serialize for Spatial {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.dump().serialize(serializer)
    }
}

impl Deserialize for Spatial {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        Ok(Spatial::slurp(try!(Deserialize::deserialize(deserializer))))
    }
}

#[cfg(test)]
mod test {
    use super::{Spatial, Place};
    use item::Slot;
    use calx_ecs::Entity;
    use location::Location;

    #[test]
    fn test_place_adjacency() {
        // Test that the Place type gets a lexical ordering where elements in
        // the same parent entity get sorted next to each other, and that None
        // is the minimum value for the slot option.
        //
        // This needs to be right for the containment logic to function, but
        // it's not obvious which way the derived lexical order sorts, so put
        // an unit test here to check it out.
        let mut places = vec![
            Place::In(Entity(0), Some(Slot::Melee)),
            Place::In(Entity(1), None),
            Place::In(Entity(0), Some(Slot::Ranged)),
            Place::In(Entity(0), None),
        ];

        places.sort();
        assert_eq!(places,
                   vec![
                Place::In(Entity(0), None),
                Place::In(Entity(0), Some(Slot::Melee)),
                Place::In(Entity(0), Some(Slot::Ranged)),
                Place::In(Entity(1), None),
            ]);
    }

    #[test]
    fn test_serialization() {
        use bincode::{serde, SizeLimit};

        let mut spatial = Spatial::new();
        let p1 = Place::At(Location::new(10, 10));
        let p2 = Place::In(Entity(1), None);
        spatial.insert(Entity(1), p1);
        spatial.insert(Entity(2), p2);

        let saved = serde::serialize(&spatial, SizeLimit::Infinite)
                        .expect("Spatial serialization failed");
        let spatial2 = serde::deserialize::<Spatial>(&saved)
                           .expect("Spatial deserialization failed");

        assert_eq!(spatial2.get(Entity(1)), Some(p1));
        assert_eq!(spatial2.get(Entity(2)), Some(p2));
    }
}
<commit_msg>VecMap API update<commit_after>use std::collections::BTreeMap;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use vec_map::VecMap;
use calx_ecs::Entity;
use location::Location;
use item::Slot;
use self::Place::*;

/// Entities can be placed either on open locations or inside other entities.
/// A sum type will represent this nicely.
#[derive(Copy, Eq, PartialEq, Clone, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum Place {
    At(Location),
    In(Entity, Option<Slot>),
}

/// Spatial index for game entities
pub struct Spatial {
    place_to_entities: BTreeMap<Place, Vec<Entity>>,
    entity_to_place: VecMap<Place>,
}

impl Spatial {
    pub fn new() -> Spatial {
        Spatial {
            place_to_entities: BTreeMap::new(),
            entity_to_place: VecMap::new(),
        }
    }

    fn insert(&mut self, Entity(idx): Entity, p: Place) {
        // Remove the entity from its old position.
        self.single_remove(Entity(idx));

        if let In(_, Some(_)) = p {
            // Slotted in-places are a special case that can hold at most one entity.
            if self.place_to_entities.contains_key(&p) {
                panic!("Equipping to an occupied inventory slot");
            }
        }

        self.entity_to_place.insert(idx, p);
        match self.place_to_entities.get_mut(&p) {
            Some(v) => {
                v.push(Entity(idx));
                return;
            }
            _ => (),
        };
        // Didn't return above, that means this location isn't indexed
        // yet and needs a brand new container. (Can't do this in match
        // block because borrows.)
        self.place_to_entities.insert(p, vec![Entity(idx)]);
    }

    /// Insert an entity into space.
    pub fn insert_at(&mut self, e: Entity, loc: Location) {
        self.insert(e, At(loc));
    }

    /// Return whether the parent entity or an entity contained in the parent
    /// entity contains entity e.
    pub fn contains(&self, parent: Entity, Entity(idx): Entity) -> bool {
        match self.entity_to_place.get(idx) {
            Some(&In(p, _)) if p == parent => true,
            Some(&In(p, _)) => self.contains(parent, p),
            _ => false,
        }
    }

    /// Insert an entity into container.
    pub fn insert_in(&mut self, e: Entity, parent: Entity) {
        assert!(!self.contains(e, parent),
                "Trying to create circular containment");
        self.insert(e, In(parent, None));
    }

    /// Insert an entity into an equipment slot. Will panic if there already
    /// is an item present in the slot.
    pub fn equip(&mut self, e: Entity, parent: Entity, slot: Slot) {
        self.insert(e, In(parent, Some(slot)));
    }

    /// Remove an entity from the local structures but do not pop out its
    /// items. Unless the entity is added back in or the contents are handled
    /// somehow, this will leave the spatial index in an inconsistent state.
    fn single_remove(&mut self, Entity(idx): Entity) {
        if !self.entity_to_place.contains_key(idx) {
            return;
        }

        let &p = self.entity_to_place.get(idx).unwrap();
        self.entity_to_place.remove(idx);

        {
            let v = self.place_to_entities.get_mut(&p).unwrap();
            assert!(v.len() > 0);
            if v.len() > 1 {
                // More than one entity present, remove this one, keep the
                // rest.
                for i in 0..v.len() {
                    if v[i] == Entity(idx) {
                        v.swap_remove(i);
                        return;
                    }
                }
                panic!("Entity being removed from place it's not in");
            } else {
                // This was the only entity in the location.
                // Drop the entry for this location from the index.
                // (Need to drop out of scope for borrows reasons)
                assert!((*v)[0] == Entity(idx));
            }
        }
        // We only end up here if we need to clear the container for the
        // location.
        self.place_to_entities.remove(&p);
    }

    /// Remove an entity from the space. Entities contained in the entity will
    /// also be removed from the space.
    pub fn remove(&mut self, Entity(idx): Entity) {
        // Remove the contents
        for &content in self.entities_in(Entity(idx)).iter() {
            self.remove(content);
        }
        self.single_remove(Entity(idx));
    }

    fn entities(&self, p: Place) -> Vec<Entity> {
        match self.place_to_entities.get(&p) {
            None => vec![],
            Some(v) => v.clone(),
        }
    }

    /// List entities at a location.
    pub fn entities_at(&self, loc: Location) -> Vec<Entity> {
        self.entities(At(loc))
    }

    /// List entities in a container.
    pub fn entities_in(&self, parent: Entity) -> Vec<Entity> {
        // XXX: Can't make the API return an iterator (more efficient than
        // running collect) since the chain depends on a closure that captures
        // the 'parent' parameter from the outside scope, and closures can't
        // be typed in the return signature.

        // TODO: Get range iteration for BTreeMaps working in stable Rust.
        /*
        self.place_to_entities.range(Included(&In(parent, None)), Unbounded)
            // Consume the contingent elements for the parent container.
            .take_while(|&(ref k, _)| if let &&In(ref p, _) = k { *p == parent } else { false })
         */

        // XXX: This replacement thing is quite nasty, it iterates over the
        // whole collection for every query.
        self.place_to_entities
            .iter()
            .filter(|&(ref k, _)| {
                if let &&In(ref p, _) = k {
                    *p == parent
                } else {
                    false
                }
            })
            .flat_map(|(_, ref v)| v.iter())
            .map(|&x| x)
            .collect()
    }

    pub fn entity_equipped(&self, parent: Entity, slot: Slot) -> Option<Entity> {
        match self.place_to_entities.get(&In(parent, Some(slot))) {
            None => None,
            Some(v) => {
                assert!(v.len() == 1, "Slot entity container corrupt");
                Some(v[0])
            }
        }
    }

    /// Return the place of an entity if the entity is present in the space.
    pub fn get(&self, Entity(idx): Entity) -> Option<Place> {
        self.entity_to_place.get(idx).map(|&loc| loc)
    }

    /// Flatten to an easily serializable vector.
    fn dump(&self) -> Vec<Elt> {
        let mut ret = vec![];
        for (idx, &loc) in self.entity_to_place.iter() {
            ret.push(Elt(Entity(idx), loc));
        }
        ret
    }

    /// Construct from the serialized vector.
    fn slurp(dump: Vec<Elt>) -> Spatial {
        let mut ret = Spatial::new();

        for &Elt(e, loc) in dump.iter() {
            ret.insert(e, loc);
        }
        ret
    }
}

#[derive(Clone, Serialize, Deserialize)]
struct Elt(Entity, Place);

impl Serialize for Spatial {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.dump().serialize(serializer)
    }
}

impl Deserialize for Spatial {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        Ok(Spatial::slurp(try!(Deserialize::deserialize(deserializer))))
    }
}

#[cfg(test)]
mod test {
    use super::{Spatial, Place};
    use item::Slot;
    use calx_ecs::Entity;
    use location::Location;

    #[test]
    fn test_place_adjacency() {
        // Test that the Place type gets a lexical ordering where elements in
        // the same parent entity get sorted next to each other, and that None
        // is the minimum value for the slot option.
        //
        // This needs to be right for the containment logic to function, but
        // it's not obvious which way the derived lexical order sorts, so put
        // an unit test here to check it out.
        let mut places = vec![
            Place::In(Entity(0), Some(Slot::Melee)),
            Place::In(Entity(1), None),
            Place::In(Entity(0), Some(Slot::Ranged)),
            Place::In(Entity(0), None),
        ];

        places.sort();
        assert_eq!(places,
                   vec![
                Place::In(Entity(0), None),
                Place::In(Entity(0), Some(Slot::Melee)),
                Place::In(Entity(0), Some(Slot::Ranged)),
                Place::In(Entity(1), None),
            ]);
    }

    #[test]
    fn test_serialization() {
        use bincode::{serde, SizeLimit};

        let mut spatial = Spatial::new();
        let p1 = Place::At(Location::new(10, 10));
        let p2 = Place::In(Entity(1), None);
        spatial.insert(Entity(1), p1);
        spatial.insert(Entity(2), p2);

        let saved = serde::serialize(&spatial, SizeLimit::Infinite)
                        .expect("Spatial serialization failed");
        let spatial2 = serde::deserialize::<Spatial>(&saved)
                           .expect("Spatial deserialization failed");

        assert_eq!(spatial2.get(Entity(1)), Some(p1));
        assert_eq!(spatial2.get(Entity(2)), Some(p2));
    }
}
<|endoftext|>


<commit_before><commit_msg>Initial implementation for Custom Set<commit_after>pub struct CustomSet {
    set: Vec<i32>,
}

impl CustomSet {
    pub fn new(vec: Vec<i32>) -> CustomSet {}
}
<|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.

use tempdir::TempDir;

use super::{Database, LevelDB, LevelDBOptions, MemoryDB, Snapshot, Fork};

fn leveldb_database() -> LevelDB {
    let mut options = LevelDBOptions::new();
    options.create_if_missing = true;
    LevelDB::open(TempDir::new("exonum").unwrap().path(), options).unwrap()
}

fn memorydb_database() -> MemoryDB {
    MemoryDB::new()
}

fn fork_iter<T: Database>(mut db: T) {
    let mut fork = db.fork();

    fork.put(vec![10], vec![10]);
    fork.put(vec![20], vec![20]);
    fork.put(vec![30], vec![30]);

    db.merge(fork.into_patch()).unwrap();

    let mut fork = db.fork();

    fn assert_iter(fork: &Fork, from: u8, assumed: &[(u8, u8)]) {
        let mut values = Vec::new();
        let mut iter = fork.iter(&[from]);
        while let Some((k, v)) = iter.next() {
            values.push((k[0], v[0]));
        }
        assert_eq!(values, assumed);
    }

    // Stored
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
    assert_iter(&fork, 5, &[(10, 10), (20, 20), (30, 30)]);
    assert_iter(&fork, 10, &[(10, 10), (20, 20), (30, 30)]);
    assert_iter(&fork, 11, &[(20, 20), (30, 30)]);
    assert_iter(&fork, 31, &[]);

    // Inserted
    fork.put(vec![5], vec![5]);
    assert_iter(&fork, 0, &[(5, 5), (10, 10), (20, 20), (30, 30)]);
    fork.put(vec![25], vec![25]);
    assert_iter(&fork, 0, &[(5, 5), (10, 10), (20, 20), (25, 25), (30, 30)]);
    fork.put(vec![35], vec![35]);
    assert_iter(
        &fork,
        0,
        &[(5, 5), (10, 10), (20, 20), (25, 25), (30, 30), (35, 35)],
    );

    // Double inserted
    fork.put(vec![25], vec![23]);
    assert_iter(
        &fork,
        0,
        &[(5, 5), (10, 10), (20, 20), (25, 23), (30, 30), (35, 35)],
    );
    fork.put(vec![26], vec![26]);
    assert_iter(
        &fork,
        0,
        &[(5, 5), (10, 10), (20, 20), (25, 23), (26, 26), (30, 30), (35, 35)],
    );

    // Replaced
    let mut fork = db.fork();
    fork.put(vec![10], vec![11]);
    assert_iter(&fork, 0, &[(10, 11), (20, 20), (30, 30)]);
    fork.put(vec![30], vec![31]);
    assert_iter(&fork, 0, &[(10, 11), (20, 20), (30, 31)]);

    // Deleted
    let mut fork = db.fork();
    fork.remove(vec![20]);
    assert_iter(&fork, 0, &[(10, 10), (30, 30)]);
    fork.remove(vec![10]);
    assert_iter(&fork, 0, &[(30, 30)]);
    fork.put(vec![10], vec![11]);
    assert_iter(&fork, 0, &[(10, 11), (30, 30)]);
    fork.remove(vec![10]);
    assert_iter(&fork, 0, &[(30, 30)]);

    // MissDeleted
    let mut fork = db.fork();
    fork.remove(vec![5]);
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
    fork.remove(vec![15]);
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
    fork.remove(vec![35]);
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
}

fn changelog<T: Database>(db: T) {
    let mut fork = db.fork();

    fork.put(vec![1], vec![1]);
    fork.put(vec![2], vec![2]);
    fork.put(vec![3], vec![3]);

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));

    fork.checkpoint();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));

    fork.put(vec![1], vec![10]);
    fork.put(vec![4], vec![40]);
    fork.remove(vec![2]);

    assert_eq!(fork.get(&[1]), Some(vec![10]));
    assert_eq!(fork.get(&[2]), None);
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), Some(vec![40]));

    fork.rollback();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), None);

    fork.checkpoint();

    fork.put(vec![4], vec![40]);
    fork.put(vec![4], vec![41]);
    fork.remove(vec![2]);
    fork.put(vec![2], vec![20]);

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![20]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), Some(vec![41]));

    fork.rollback();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), None);

    fork.put(vec![2], vec![20]);

    fork.checkpoint();

    fork.put(vec![3], vec![30]);

    fork.rollback();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![20]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), None);
}

#[test]
fn test_leveldb_fork_iter() {
    fork_iter(leveldb_database())
}

#[test]
fn test_memory_fork_iter() {
    fork_iter(memorydb_database())
}

#[test]
fn test_leveldb_changelog() {
    changelog(leveldb_database())
}

#[test]
fn test_memory_changelog() {
    changelog(memorydb_database())
}

<commit_msg>Removed old test for memorydb. (Rustfmt)<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.

use tempdir::TempDir;

use super::{Database, LevelDB, LevelDBOptions, MemoryDB, Snapshot, Fork};

fn leveldb_database() -> LevelDB {
    let mut options = LevelDBOptions::new();
    options.create_if_missing = true;
    LevelDB::open(TempDir::new("exonum").unwrap().path(), options).unwrap()
}

fn memorydb_database() -> MemoryDB {
    MemoryDB::new()
}

fn fork_iter<T: Database>(mut db: T) {
    let mut fork = db.fork();

    fork.put(vec![10], vec![10]);
    fork.put(vec![20], vec![20]);
    fork.put(vec![30], vec![30]);

    db.merge(fork.into_patch()).unwrap();

    let mut fork = db.fork();

    fn assert_iter(fork: &Fork, from: u8, assumed: &[(u8, u8)]) {
        let mut values = Vec::new();
        let mut iter = fork.iter(&[from]);
        while let Some((k, v)) = iter.next() {
            values.push((k[0], v[0]));
        }
        assert_eq!(values, assumed);
    }

    // Stored
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
    assert_iter(&fork, 5, &[(10, 10), (20, 20), (30, 30)]);
    assert_iter(&fork, 10, &[(10, 10), (20, 20), (30, 30)]);
    assert_iter(&fork, 11, &[(20, 20), (30, 30)]);
    assert_iter(&fork, 31, &[]);

    // Inserted
    fork.put(vec![5], vec![5]);
    assert_iter(&fork, 0, &[(5, 5), (10, 10), (20, 20), (30, 30)]);
    fork.put(vec![25], vec![25]);
    assert_iter(&fork, 0, &[(5, 5), (10, 10), (20, 20), (25, 25), (30, 30)]);
    fork.put(vec![35], vec![35]);
    assert_iter(
        &fork,
        0,
        &[(5, 5), (10, 10), (20, 20), (25, 25), (30, 30), (35, 35)],
    );

    // Double inserted
    fork.put(vec![25], vec![23]);
    assert_iter(
        &fork,
        0,
        &[(5, 5), (10, 10), (20, 20), (25, 23), (30, 30), (35, 35)],
    );
    fork.put(vec![26], vec![26]);
    assert_iter(
        &fork,
        0,
        &[(5, 5), (10, 10), (20, 20), (25, 23), (26, 26), (30, 30), (35, 35)],
    );

    // Replaced
    let mut fork = db.fork();
    fork.put(vec![10], vec![11]);
    assert_iter(&fork, 0, &[(10, 11), (20, 20), (30, 30)]);
    fork.put(vec![30], vec![31]);
    assert_iter(&fork, 0, &[(10, 11), (20, 20), (30, 31)]);

    // Deleted
    let mut fork = db.fork();
    fork.remove(vec![20]);
    assert_iter(&fork, 0, &[(10, 10), (30, 30)]);
    fork.remove(vec![10]);
    assert_iter(&fork, 0, &[(30, 30)]);
    fork.put(vec![10], vec![11]);
    assert_iter(&fork, 0, &[(10, 11), (30, 30)]);
    fork.remove(vec![10]);
    assert_iter(&fork, 0, &[(30, 30)]);

    // MissDeleted
    let mut fork = db.fork();
    fork.remove(vec![5]);
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
    fork.remove(vec![15]);
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
    fork.remove(vec![35]);
    assert_iter(&fork, 0, &[(10, 10), (20, 20), (30, 30)]);
}

fn changelog<T: Database>(db: T) {
    let mut fork = db.fork();

    fork.put(vec![1], vec![1]);
    fork.put(vec![2], vec![2]);
    fork.put(vec![3], vec![3]);

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));

    fork.checkpoint();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));

    fork.put(vec![1], vec![10]);
    fork.put(vec![4], vec![40]);
    fork.remove(vec![2]);

    assert_eq!(fork.get(&[1]), Some(vec![10]));
    assert_eq!(fork.get(&[2]), None);
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), Some(vec![40]));

    fork.rollback();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), None);

    fork.checkpoint();

    fork.put(vec![4], vec![40]);
    fork.put(vec![4], vec![41]);
    fork.remove(vec![2]);
    fork.put(vec![2], vec![20]);

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![20]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), Some(vec![41]));

    fork.rollback();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![2]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), None);

    fork.put(vec![2], vec![20]);

    fork.checkpoint();

    fork.put(vec![3], vec![30]);

    fork.rollback();

    assert_eq!(fork.get(&[1]), Some(vec![1]));
    assert_eq!(fork.get(&[2]), Some(vec![20]));
    assert_eq!(fork.get(&[3]), Some(vec![3]));
    assert_eq!(fork.get(&[4]), None);
}

#[test]
fn test_leveldb_fork_iter() {
    fork_iter(leveldb_database())
}

#[test]
fn test_memory_fork_iter() {
    fork_iter(memorydb_database())
}

#[test]
fn test_leveldb_changelog() {
    changelog(leveldb_database())
}

#[test]
fn test_memory_changelog() {
    changelog(memorydb_database())
}
<|endoftext|>


<commit_before>// Magic addresses used to lay out x86_64 VMs.

// The 'zero page', a.k.a linux kernel bootparams.
pub const ZERO_PAGE_START: usize = 0x7000;
// Initial stack for the boot CPU.
pub const BOOT_STACK_POINTER: usize = 0x8000;
// Initial pagetables.
pub const PML4_START: usize = 0x9000;
pub const PDPTE_START: usize = 0xa000;
// Kernel command line.
pub const CMDLINE_START: usize = 0x20000;
pub const CMDLINE_MAX_SIZE: usize = 0x10000;
// MPTABLE, describing VCPUS.
pub const MPTABLE_START: usize = 0x9fc00;
// Where BIOS/VGA magic would live on a real PC.
pub const EBDA_START: u64 = 0x9fc00;
// 1MB.  We don't put anything above here except the kernel itself.
pub const HIMEM_START: usize = 0x100000;
<commit_msg>Move boot stack pointer to the top of the page.<commit_after>// Magic addresses used to lay out x86_64 VMs.

// The 'zero page', a.k.a linux kernel bootparams.
pub const ZERO_PAGE_START: usize = 0x7000;
// Initial stack for the boot CPU.
pub const BOOT_STACK_START: usize = 0x8000;
pub const BOOT_STACK_POINTER: usize = 0x8ff0;
// Initial pagetables.
pub const PML4_START: usize = 0x9000;
pub const PDPTE_START: usize = 0xa000;
// Kernel command line.
pub const CMDLINE_START: usize = 0x20000;
pub const CMDLINE_MAX_SIZE: usize = 0x10000;
// MPTABLE, describing VCPUS.
pub const MPTABLE_START: usize = 0x9fc00;
// Where BIOS/VGA magic would live on a real PC.
pub const EBDA_START: u64 = 0x9fc00;
// 1MB.  We don't put anything above here except the kernel itself.
pub const HIMEM_START: usize = 0x100000;
<|endoftext|>


<commit_before>use std::{
    collections::BTreeMap,
    env, fs,
    io::Write as _,
    path::Path,
    time::{Instant, SystemTime, UNIX_EPOCH},
};

use anyhow::{bail, format_err};
use xshell::{cmd, Shell};

use crate::flags;

type Unit = String;

impl flags::Metrics {
    pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
        let mut metrics = Metrics::new(sh)?;
        if !self.dry_run {
            let _ = sh.remove_path("./target/release");
        }
        if !Path::new("./target/rustc-perf").exists() {
            sh.create_dir("./target/rustc-perf")?;
            cmd!(sh, "git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")
                .run()?;
        }
        {
            let _d = sh.push_dir("./target/rustc-perf");
            let revision = &metrics.perf_revision;
            cmd!(sh, "git reset --hard {revision}").run()?;
        }

        let _env = sh.push_env("RA_METRICS", "1");

        {
            // https://github.com/rust-analyzer/rust-analyzer/issues/9997
            let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender");
            cmd!(sh, "cargo update -p url --precise 1.6.1").run()?;
        }
        metrics.measure_build(sh)?;
        metrics.measure_analysis_stats_self(sh)?;
        metrics.measure_analysis_stats(sh, "ripgrep")?;
        metrics.measure_analysis_stats(sh, "webrender")?;
        metrics.measure_analysis_stats(sh, "diesel/diesel")?;

        if !self.dry_run {
            let _d = sh.push_dir("target");
            let metrics_token = env::var("METRICS_TOKEN").unwrap();
            cmd!(
                sh,
                "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
            )
            .run()?;
            let _d = sh.push_dir("metrics");

            let mut file = fs::File::options().append(true).open("metrics.json")?;
            writeln!(file, "{}", metrics.json())?;
            cmd!(sh, "git add .").run()?;
            cmd!(sh, "git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈")
                .run()?;
            cmd!(sh, "git push origin master").run()?;
        }
        eprintln!("{metrics:#?}");
        Ok(())
    }
}

impl Metrics {
    fn measure_build(&mut self, sh: &Shell) -> anyhow::Result<()> {
        eprintln!("\nMeasuring build");
        cmd!(sh, "cargo fetch").run()?;

        let time = Instant::now();
        cmd!(sh, "cargo build --release --package rust-analyzer --bin rust-analyzer").run()?;
        let time = time.elapsed();
        self.report("build", time.as_millis() as u64, "ms".into());
        Ok(())
    }
    fn measure_analysis_stats_self(&mut self, sh: &Shell) -> anyhow::Result<()> {
        self.measure_analysis_stats_path(sh, "self", ".")
    }
    fn measure_analysis_stats(&mut self, sh: &Shell, bench: &str) -> anyhow::Result<()> {
        self.measure_analysis_stats_path(
            sh,
            bench,
            &format!("./target/rustc-perf/collector/benchmarks/{}", bench),
        )
    }
    fn measure_analysis_stats_path(
        &mut self,
        sh: &Shell,
        name: &str,
        path: &str,
    ) -> anyhow::Result<()> {
        eprintln!("\nMeasuring analysis-stats/{name}");
        let output =
            cmd!(sh, "./target/release/rust-analyzer -q analysis-stats --memory-usage {path}")
                .read()?;
        for (metric, value, unit) in parse_metrics(&output) {
            self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into());
        }
        Ok(())
    }
}

fn parse_metrics(output: &str) -> Vec<(&str, u64, &str)> {
    output
        .lines()
        .filter_map(|it| {
            let entry = it.split(':').collect::<Vec<_>>();
            match entry.as_slice() {
                ["METRIC", name, value, unit] => Some((*name, value.parse().unwrap(), *unit)),
                _ => None,
            }
        })
        .collect()
}

#[derive(Debug)]
struct Metrics {
    host: Host,
    timestamp: SystemTime,
    revision: String,
    perf_revision: String,
    metrics: BTreeMap<String, (u64, Unit)>,
}

#[derive(Debug)]
struct Host {
    os: String,
    cpu: String,
    mem: String,
}

impl Metrics {
    fn new(sh: &Shell) -> anyhow::Result<Metrics> {
        let host = Host::new(sh)?;
        let timestamp = SystemTime::now();
        let revision = cmd!(sh, "git rev-parse HEAD").read()?;
        let perf_revision = "c52ee623e231e7690a93be88d943016968c1036b".into();
        Ok(Metrics { host, timestamp, revision, perf_revision, metrics: BTreeMap::new() })
    }

    fn report(&mut self, name: &str, value: u64, unit: Unit) {
        self.metrics.insert(name.into(), (value, unit));
    }

    fn json(&self) -> String {
        let mut buf = String::new();
        self.to_json(write_json::object(&mut buf));
        buf
    }

    fn to_json(&self, mut obj: write_json::Object<'_>) {
        self.host.to_json(obj.object("host"));
        let timestamp = self.timestamp.duration_since(UNIX_EPOCH).unwrap();
        obj.number("timestamp", timestamp.as_secs() as f64);
        obj.string("revision", &self.revision);
        obj.string("perf_revision", &self.perf_revision);
        let mut metrics = obj.object("metrics");
        for (k, (value, unit)) in &self.metrics {
            metrics.array(k).number(*value as f64).string(unit);
        }
    }
}

impl Host {
    fn new(sh: &Shell) -> anyhow::Result<Host> {
        if cfg!(not(target_os = "linux")) {
            bail!("can only collect metrics on Linux ");
        }

        let os = read_field(sh, "/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_string();

        let cpu = read_field(sh, "/proc/cpuinfo", "model name")?
            .trim_start_matches(':')
            .trim()
            .to_string();

        let mem = read_field(sh, "/proc/meminfo", "MemTotal:")?;

        return Ok(Host { os, cpu, mem });

        fn read_field(sh: &Shell, path: &str, field: &str) -> anyhow::Result<String> {
            let text = sh.read_file(path)?;

            text.lines()
                .find_map(|it| it.strip_prefix(field))
                .map(|it| it.trim().to_string())
                .ok_or_else(|| format_err!("can't parse {}", path))
        }
    }
    fn to_json(&self, mut obj: write_json::Object<'_>) {
        obj.string("os", &self.os).string("cpu", &self.cpu).string("mem", &self.mem);
    }
}
<commit_msg>Fix metrics.json path<commit_after>use std::{
    collections::BTreeMap,
    env, fs,
    io::Write as _,
    path::Path,
    time::{Instant, SystemTime, UNIX_EPOCH},
};

use anyhow::{bail, format_err};
use xshell::{cmd, Shell};

use crate::flags;

type Unit = String;

impl flags::Metrics {
    pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
        let mut metrics = Metrics::new(sh)?;
        if !self.dry_run {
            let _ = sh.remove_path("./target/release");
        }
        if !Path::new("./target/rustc-perf").exists() {
            sh.create_dir("./target/rustc-perf")?;
            cmd!(sh, "git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")
                .run()?;
        }
        {
            let _d = sh.push_dir("./target/rustc-perf");
            let revision = &metrics.perf_revision;
            cmd!(sh, "git reset --hard {revision}").run()?;
        }

        let _env = sh.push_env("RA_METRICS", "1");

        {
            // https://github.com/rust-analyzer/rust-analyzer/issues/9997
            let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender");
            cmd!(sh, "cargo update -p url --precise 1.6.1").run()?;
        }
        metrics.measure_build(sh)?;
        metrics.measure_analysis_stats_self(sh)?;
        metrics.measure_analysis_stats(sh, "ripgrep")?;
        metrics.measure_analysis_stats(sh, "webrender")?;
        metrics.measure_analysis_stats(sh, "diesel/diesel")?;

        if !self.dry_run {
            let _d = sh.push_dir("target");
            let metrics_token = env::var("METRICS_TOKEN").unwrap();
            cmd!(
                sh,
                "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
            )
            .run()?;

            {
                let mut file =
                    fs::File::options().append(true).open("target/metrics/metrics.json")?;
                writeln!(file, "{}", metrics.json())?;
            }

            let _d = sh.push_dir("metrics");
            cmd!(sh, "git add .").run()?;
            cmd!(sh, "git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈")
                .run()?;
            cmd!(sh, "git push origin master").run()?;
        }
        eprintln!("{metrics:#?}");
        Ok(())
    }
}

impl Metrics {
    fn measure_build(&mut self, sh: &Shell) -> anyhow::Result<()> {
        eprintln!("\nMeasuring build");
        cmd!(sh, "cargo fetch").run()?;

        let time = Instant::now();
        cmd!(sh, "cargo build --release --package rust-analyzer --bin rust-analyzer").run()?;
        let time = time.elapsed();
        self.report("build", time.as_millis() as u64, "ms".into());
        Ok(())
    }
    fn measure_analysis_stats_self(&mut self, sh: &Shell) -> anyhow::Result<()> {
        self.measure_analysis_stats_path(sh, "self", ".")
    }
    fn measure_analysis_stats(&mut self, sh: &Shell, bench: &str) -> anyhow::Result<()> {
        self.measure_analysis_stats_path(
            sh,
            bench,
            &format!("./target/rustc-perf/collector/benchmarks/{}", bench),
        )
    }
    fn measure_analysis_stats_path(
        &mut self,
        sh: &Shell,
        name: &str,
        path: &str,
    ) -> anyhow::Result<()> {
        eprintln!("\nMeasuring analysis-stats/{name}");
        let output =
            cmd!(sh, "./target/release/rust-analyzer -q analysis-stats --memory-usage {path}")
                .read()?;
        for (metric, value, unit) in parse_metrics(&output) {
            self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into());
        }
        Ok(())
    }
}

fn parse_metrics(output: &str) -> Vec<(&str, u64, &str)> {
    output
        .lines()
        .filter_map(|it| {
            let entry = it.split(':').collect::<Vec<_>>();
            match entry.as_slice() {
                ["METRIC", name, value, unit] => Some((*name, value.parse().unwrap(), *unit)),
                _ => None,
            }
        })
        .collect()
}

#[derive(Debug)]
struct Metrics {
    host: Host,
    timestamp: SystemTime,
    revision: String,
    perf_revision: String,
    metrics: BTreeMap<String, (u64, Unit)>,
}

#[derive(Debug)]
struct Host {
    os: String,
    cpu: String,
    mem: String,
}

impl Metrics {
    fn new(sh: &Shell) -> anyhow::Result<Metrics> {
        let host = Host::new(sh)?;
        let timestamp = SystemTime::now();
        let revision = cmd!(sh, "git rev-parse HEAD").read()?;
        let perf_revision = "c52ee623e231e7690a93be88d943016968c1036b".into();
        Ok(Metrics { host, timestamp, revision, perf_revision, metrics: BTreeMap::new() })
    }

    fn report(&mut self, name: &str, value: u64, unit: Unit) {
        self.metrics.insert(name.into(), (value, unit));
    }

    fn json(&self) -> String {
        let mut buf = String::new();
        self.to_json(write_json::object(&mut buf));
        buf
    }

    fn to_json(&self, mut obj: write_json::Object<'_>) {
        self.host.to_json(obj.object("host"));
        let timestamp = self.timestamp.duration_since(UNIX_EPOCH).unwrap();
        obj.number("timestamp", timestamp.as_secs() as f64);
        obj.string("revision", &self.revision);
        obj.string("perf_revision", &self.perf_revision);
        let mut metrics = obj.object("metrics");
        for (k, (value, unit)) in &self.metrics {
            metrics.array(k).number(*value as f64).string(unit);
        }
    }
}

impl Host {
    fn new(sh: &Shell) -> anyhow::Result<Host> {
        if cfg!(not(target_os = "linux")) {
            bail!("can only collect metrics on Linux ");
        }

        let os = read_field(sh, "/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_string();

        let cpu = read_field(sh, "/proc/cpuinfo", "model name")?
            .trim_start_matches(':')
            .trim()
            .to_string();

        let mem = read_field(sh, "/proc/meminfo", "MemTotal:")?;

        return Ok(Host { os, cpu, mem });

        fn read_field(sh: &Shell, path: &str, field: &str) -> anyhow::Result<String> {
            let text = sh.read_file(path)?;

            text.lines()
                .find_map(|it| it.strip_prefix(field))
                .map(|it| it.trim().to_string())
                .ok_or_else(|| format_err!("can't parse {}", path))
        }
    }
    fn to_json(&self, mut obj: write_json::Object<'_>) {
        obj.string("os", &self.os).string("cpu", &self.cpu).string("mem", &self.mem);
    }
}
<|endoftext|>