<commit_before>include!(concat!(env!("OUT_DIR"), "/colors.rs"));

#[cfg(not(feature = "no_std"))]
use std::fmt;

/// A r g b a color.
#[derive(Copy, Clone, PartialOrd, Default)]
#[repr(packed)]
pub struct Color {
    pub data: u32,
}

impl Color {
    /// Create a new color from RGB
    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
        Color {
            data: 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
        }
    }

    /// Create a new color from HSV(0.0-360.0, 0.0-1.0, 0.0-1.0)
    pub fn hsv(h: f64, s: f64, v: f64) -> Self {
        Self::hsva(h, s, v, 1.0)
    }

    /// Create a new color from HSL(0.0-360.0, 0.0-1.0, 0.0-1.0)
    pub fn hsl(h: f64, s: f64, l: f64) -> Self {
        Self::hsla(h, s, l, 1.0)
    }

    /// Create a new color from RGB and alpha values
    pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
        Color {
            data: ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
        }
    }

    /// Create a new color from HSV(0.0-360.0, 0.0-1.0, 0.0-1.0) and alpha values(0.0-1.0)
    pub fn hsva(mut hue: f64, mut saturation: f64, mut value: f64, alpha: f64) -> Self {
        hue %= 360.0;
        saturation = saturation.max(0.0).min(1.0);
        value = value.max(0.0).min(1.0);
        let hh = hue / 60.0;
        let idx = hh.floor() as i32;
        let ff = hh.fract();
        let chroma = value * (1.0 - saturation);
        let second_component = value * (1.0 - (saturation * ff));
        let t = value * (1.0 - (saturation * (1.0 - ff)));
        let (r, g, b) = match idx {
            0 => (value, t, chroma),
            1 => (second_component, value, chroma),
            2 => (chroma, value, t),
            3 => (chroma, second_component, value),
            4 => (t, chroma, value),
            5 => (value, chroma, second_component),
            _ => unreachable!(),
        };
        Self::rgba(
            (r * 255.0) as u8,
            (g * 255.0) as u8,
            (b * 255.0) as u8,
            (alpha * 255.0) as u8,
        )
    }

    /// Create a new color from HSL(0.0-360.0, 0.0-1.0, 0.0-1.0) and alpha values(0.0-1.0)
    pub fn hsla(mut hue: f64, mut saturation: f64, mut lightness: f64, alpha: f64) -> Self {
        hue %= 360.0;
        saturation = saturation.max(0.0).min(1.0);
        lightness = lightness.max(0.0).min(1.0);
        let hh = hue / 60.0;
        let idx = hh.floor() as i32;
        let chroma = (1.0 - ((2.0 * lightness) - 1.0).abs()) * saturation;
        let second_component = chroma * (1.0 - (((idx % 2) as f64) - 1.0).abs());
        let (mut r, mut g, mut b) = match idx {
            0 => (chroma, second_component, 0.0),
            1 => (second_component, chroma, 0.0),
            2 => (0.0, chroma, second_component),
            3 => (0.0, second_component, chroma),
            4 => (second_component, 0.0, chroma),
            5 => (chroma, 0.0, second_component),
            _ => unreachable!(),
        };
        let adjustment = lightness - chroma / 2.0;
        r += adjustment;
        g += adjustment;
        b += adjustment;
        Self::rgba(
            (r.min(1.0) * 255.0) as u8,
            (g.min(1.0) * 255.0) as u8,
            (b.min(1.0) * 255.0) as u8,
            (alpha * 255.0) as u8,
        )
    }

    /// Get the r value
    pub fn r(self) -> u8 {
        ((self.data & 0x00FF_0000) >> 16) as u8
    }

    /// Get the g value
    pub fn g(self) -> u8 {
        ((self.data & 0x0000_FF00) >> 8) as u8
    }

    /// Get the b value
    pub fn b(self) -> u8 {
        (self.data & 0x0000_00FF) as u8
    }

    /// Get the alpha value
    pub fn a(self) -> u8 {
        ((self.data & 0xFF00_0000) >> 24) as u8
    }

    /// Attempts to get a color from its name, all the CSS colors are avaible and some other ones also.
    pub fn from_name(name: &str) -> Option<Color> {
        COLORS.get(name).cloned()
    }

    /// Interpolate between two colors
    pub fn interpolate(start_color: Color, end_color: Color, scale: f64) -> Color {
        let r = Color::interp(start_color.r(), end_color.r(), scale);
        let g = Color::interp(start_color.g(), end_color.g(), scale);
        let b = Color::interp(start_color.b(), end_color.b(), scale);
        let a = Color::interp(start_color.a(), end_color.a(), scale);
        Color::rgba(r, g, b, a)
    }

    fn interp(start_color: u8, end_color: u8, scale: f64) -> u8 {
        (end_color as f64 - start_color as f64).mul_add(scale, start_color as f64) as u8
    }
}

impl ToString for Color {
    fn to_string(&self) -> String {
        if self.a() == 0 {
            return String::from("transparent");
        }

        let data = self.data;

        let mut color = format!("#{:x}", data);
        color.remove(1);
        color.remove(1);
        color
    }
}

impl From<&str> for Color {
    fn from(s: &str) -> Color {
        if !s.starts_with('#') {
            if let Some(color) = Color::from_name(s) {
                return color;
            }
        }
        let clean_hex = s.trim_start_matches('#');
        match clean_hex.len() {
            3 | 4 => {
                let num = match u32::from_str_radix(&clean_hex, 16) {
                    Ok(x) => x,
                    Err(_) => 0,
                };

                let mut blue = (num & 0xF) << 4;
                let mut green = ((num >> 4) & 0xF) << 4;
                let mut red = ((num >> 8) & 0xF) << 4;
                let mut alpha = match clean_hex.len() == 4 {
                    true => ((num >> 12) & 0xF) << 4,
                    false => 0xF,
                };
                red |= red >> 4;
                green |= green >> 4;
                blue |= blue >> 4;
                alpha |= alpha >> 4;

                Color::rgba(red as u8, green as u8, blue as u8, alpha as u8)
            }
            6 | 8 => {
                let mut x = match u32::from_str_radix(&clean_hex, 16) {
                    Ok(x) => x,
                    Err(_) => 0,
                };

                if clean_hex.len() == 6 {
                    x |= 0xFF_000_000;
                }

                Color { data: x }
            }
            _ => Color { data: 0 },
        }
    }
}

impl From<String> for Color {
    fn from(s: String) -> Color {
        Color::from(s.as_str())
    }
}

/// Compares two colors (the alpha value is ignored)
impl PartialEq for Color {
    fn eq(&self, other: &Color) -> bool {
        self.r() == other.r() && self.g() == other.g() && self.b() == other.b()
    }
}

#[cfg(not(feature = "no_std"))]
impl fmt::Debug for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{:#010X}", { self.data })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn partial_eq() {
        assert_eq!(true, Color::rgb(1, 2, 3) == Color::rgba(1, 2, 3, 200));
        assert_eq!(false, Color::rgb(1, 2, 3) == Color::rgba(11, 2, 3, 200));
        assert_eq!(true, Color::rgba(1, 2, 3, 200) == Color::rgba(1, 2, 3, 200));
    }
}
<commit_msg>lint fix<commit_after>include!(concat!(env!("OUT_DIR"), "/colors.rs"));

#[cfg(not(feature = "no_std"))]
use std::fmt;

/// A r g b a color.
#[derive(Copy, Clone, PartialOrd, Default)]
#[repr(packed)]
pub struct Color {
    pub data: u32,
}

impl Color {
    /// Create a new color from RGB
    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
        Color {
            data: 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
        }
    }

    /// Create a new color from HSV(0.0-360.0, 0.0-1.0, 0.0-1.0)
    pub fn hsv(h: f64, s: f64, v: f64) -> Self {
        Self::hsva(h, s, v, 1.0)
    }

    /// Create a new color from HSL(0.0-360.0, 0.0-1.0, 0.0-1.0)
    pub fn hsl(h: f64, s: f64, l: f64) -> Self {
        Self::hsla(h, s, l, 1.0)
    }

    /// Create a new color from RGB and alpha values
    pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
        Color {
            data: ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
        }
    }

    /// Create a new color from HSV(0.0-360.0, 0.0-1.0, 0.0-1.0) and alpha values(0.0-1.0)
    pub fn hsva(mut hue: f64, mut saturation: f64, mut value: f64, alpha: f64) -> Self {
        hue %= 360.0;
        saturation = saturation.max(0.0).min(1.0);
        value = value.max(0.0).min(1.0);
        let hh = hue / 60.0;
        let idx = hh.floor() as i32;
        let ff = hh.fract();
        let chroma = value * (1.0 - saturation);
        let second_component = value * (1.0 - (saturation * ff));
        let t = value * (1.0 - (saturation * (1.0 - ff)));
        let (r, g, b) = match idx {
            0 => (value, t, chroma),
            1 => (second_component, value, chroma),
            2 => (chroma, value, t),
            3 => (chroma, second_component, value),
            4 => (t, chroma, value),
            5 => (value, chroma, second_component),
            _ => unreachable!(),
        };
        Self::rgba(
            (r * 255.0) as u8,
            (g * 255.0) as u8,
            (b * 255.0) as u8,
            (alpha * 255.0) as u8,
        )
    }

    /// Create a new color from HSL(0.0-360.0, 0.0-1.0, 0.0-1.0) and alpha values(0.0-1.0)
    pub fn hsla(mut hue: f64, mut saturation: f64, mut lightness: f64, alpha: f64) -> Self {
        hue %= 360.0;
        saturation = saturation.max(0.0).min(1.0);
        lightness = lightness.max(0.0).min(1.0);
        let hh = hue / 60.0;
        let idx = hh.floor() as i32;
        let chroma = (1.0 - ((2.0 * lightness) - 1.0).abs()) * saturation;
        let second_component = chroma * (1.0 - (((idx % 2) as f64) - 1.0).abs());
        let (mut r, mut g, mut b) = match idx {
            0 => (chroma, second_component, 0.0),
            1 => (second_component, chroma, 0.0),
            2 => (0.0, chroma, second_component),
            3 => (0.0, second_component, chroma),
            4 => (second_component, 0.0, chroma),
            5 => (chroma, 0.0, second_component),
            _ => unreachable!(),
        };
        let adjustment = lightness - chroma / 2.0;
        r += adjustment;
        g += adjustment;
        b += adjustment;
        Self::rgba(
            (r.min(1.0) * 255.0) as u8,
            (g.min(1.0) * 255.0) as u8,
            (b.min(1.0) * 255.0) as u8,
            (alpha * 255.0) as u8,
        )
    }

    /// Get the r value
    pub fn r(self) -> u8 {
        ((self.data & 0x00FF_0000) >> 16) as u8
    }

    /// Get the g value
    pub fn g(self) -> u8 {
        ((self.data & 0x0000_FF00) >> 8) as u8
    }

    /// Get the b value
    pub fn b(self) -> u8 {
        (self.data & 0x0000_00FF) as u8
    }

    /// Get the alpha value
    pub fn a(self) -> u8 {
        ((self.data & 0xFF00_0000) >> 24) as u8
    }

    /// Attempts to get a color from its name, all the CSS colors are avaible and some other ones also.
    pub fn from_name(name: &str) -> Option<Color> {
        COLORS.get(name).cloned()
    }

    /// Interpolate between two colors
    pub fn interpolate(start_color: Color, end_color: Color, scale: f64) -> Color {
        let r = Color::interp(start_color.r(), end_color.r(), scale);
        let g = Color::interp(start_color.g(), end_color.g(), scale);
        let b = Color::interp(start_color.b(), end_color.b(), scale);
        let a = Color::interp(start_color.a(), end_color.a(), scale);
        Color::rgba(r, g, b, a)
    }

    fn interp(start_color: u8, end_color: u8, scale: f64) -> u8 {
        (end_color as f64 - start_color as f64).mul_add(scale, start_color as f64) as u8
    }
}

impl ToString for Color {
    fn to_string(&self) -> String {
        if self.a() == 0 {
            return String::from("transparent");
        }

        let data = self.data;

        let mut color = format!("#{:x}", data);
        color.remove(1);
        color.remove(1);
        color
    }
}

impl From<&str> for Color {
    fn from(s: &str) -> Color {
        if !s.starts_with('#') {
            if let Some(color) = Color::from_name(s) {
                return color;
            }
        }
        let clean_hex = s.trim_start_matches('#');
        match clean_hex.len() {
            3 | 4 => {
                let num = match u32::from_str_radix(&clean_hex, 16) {
                    Ok(x) => x,
                    Err(_) => 0,
                };

                let mut blue = (num & 0xF) << 4;
                let mut green = ((num >> 4) & 0xF) << 4;
                let mut red = ((num >> 8) & 0xF) << 4;
                let mut alpha = match clean_hex.len() == 4 {
                    true => ((num >> 12) & 0xF) << 4,
                    false => 0xF,
                };
                red |= red >> 4;
                green |= green >> 4;
                blue |= blue >> 4;
                alpha |= alpha >> 4;

                Color::rgba(red as u8, green as u8, blue as u8, alpha as u8)
            }
            6 | 8 => {
                let mut x = match u32::from_str_radix(&clean_hex, 16) {
                    Ok(x) => x,
                    Err(_) => 0,
                };

                if clean_hex.len() == 6 {
                    x |= 0xFF00_0000;
                }

                Color { data: x }
            }
            _ => Color { data: 0 },
        }
    }
}

impl From<String> for Color {
    fn from(s: String) -> Color {
        Color::from(s.as_str())
    }
}

/// Compares two colors (the alpha value is ignored)
impl PartialEq for Color {
    fn eq(&self, other: &Color) -> bool {
        self.r() == other.r() && self.g() == other.g() && self.b() == other.b()
    }
}

#[cfg(not(feature = "no_std"))]
impl fmt::Debug for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{:#010X}", { self.data })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn partial_eq() {
        assert_eq!(true, Color::rgb(1, 2, 3) == Color::rgba(1, 2, 3, 200));
        assert_eq!(false, Color::rgb(1, 2, 3) == Color::rgba(11, 2, 3, 200));
        assert_eq!(true, Color::rgba(1, 2, 3, 200) == Color::rgba(1, 2, 3, 200));
    }
}
<|endoftext|>


<commit_before>use crate::sdk::InstrumentationLibrary;
use crate::{
    metrics::{
        sdk_api, AsyncRunner, BatchObserver, BatchObserverCallback, CounterBuilder, Descriptor,
        Measurement, NumberKind, ObserverResult, Result, SumObserverBuilder, UpDownCounterBuilder,
        UpDownSumObserverBuilder, ValueObserverBuilder, ValueRecorderBuilder,
    },
    Context, KeyValue,
};
use std::fmt;
use std::sync::Arc;

/// Returns named meter instances
pub trait MeterProvider: fmt::Debug {
    /// Creates an implementation of the [`Meter`] interface. The
    /// instrumentation name must be the name of the library providing
    /// instrumentation. This name may be the same as the instrumented code only if
    /// that code provides built-in instrumentation. If the instrumentation name is
    /// empty, then a implementation defined default name will be used instead.
    ///
    /// [`Meter`]: struct.Meter.html
    fn meter(
        &self,
        instrumentation_name: &'static str,
        instrumentation_version: Option<&'static str>,
    ) -> Meter;
}

/// Meter is the OpenTelemetry metric API, based on a sdk-defined `MeterCore`
/// implementation and the `Meter` library name.
#[derive(Debug)]
pub struct Meter {
    instrumentation_library: InstrumentationLibrary,
    core: Arc<dyn sdk_api::MeterCore + Send + Sync>,
}

impl Meter {
    /// Create a new named meter from a sdk implemented core
    pub fn new<T: Into<&'static str>>(
        instrumentation_name: T,
        instrumentation_version: Option<T>,
        core: Arc<dyn sdk_api::MeterCore + Send + Sync>,
    ) -> Self {
        Meter {
            instrumentation_library: InstrumentationLibrary::new(
                instrumentation_name.into(),
                instrumentation_version.map(Into::into),
            ),
            core,
        }
    }

    #[deprecated(note = "use instrumentation_library() instead")]
    #[allow(dead_code)]
    pub(crate) fn instrumentation_name(&self) -> &str {
        self.instrumentation_library.name
    }

    pub(crate) fn instrumentation_library(&self) -> InstrumentationLibrary {
        self.instrumentation_library
    }

    /// Creates a new floating point `ValueObserverBuilder` instrument with the
    /// given name and callback
    pub fn f64_value_observer<T, F>(&self, name: T, callback: F) -> ValueObserverBuilder<'_, f64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<f64>) + Send + Sync + 'static,
    {
        ValueObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::F64(Box::new(callback)),
            NumberKind::F64,
        )
    }

    /// Creates a new integral `ValueObserverBuilder` instrument with the given name
    /// and callback
    pub fn u64_value_observer<T, F>(&self, name: T, callback: F) -> ValueObserverBuilder<'_, u64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<u64>) + Send + Sync + 'static,
    {
        ValueObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::U64(Box::new(callback)),
            NumberKind::U64,
        )
    }

    /// Creates a new integral `ValueObserverBuilder` instrument with the given name
    /// and callback
    pub fn i64_value_observer<T, F>(&self, name: T, callback: F) -> ValueObserverBuilder<'_, i64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<i64>) + Send + Sync + 'static,
    {
        ValueObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::I64(Box::new(callback)),
            NumberKind::I64,
        )
    }

    /// Creates a new floating point `SumObserverBuilder` instrument with the given
    /// name and callback
    pub fn f64_sum_observer<T, F>(&self, name: T, callback: F) -> SumObserverBuilder<'_, f64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<f64>) + Send + Sync + 'static,
    {
        SumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::F64(Box::new(callback)),
            NumberKind::F64,
        )
    }

    /// Creates a new integral `SumObserverBuilder` instrument with the given name
    /// and callback
    pub fn u64_sum_observer<T, F>(&self, name: T, callback: F) -> SumObserverBuilder<'_, u64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<u64>) + Send + Sync + 'static,
    {
        SumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::U64(Box::new(callback)),
            NumberKind::U64,
        )
    }

    /// Creates a new floating point `UpDownSumObserverBuilder` instrument with the
    /// given name and callback
    pub fn f64_up_down_sum_observer<T, F>(
        &self,
        name: T,
        callback: F,
    ) -> UpDownSumObserverBuilder<'_, f64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<f64>) + Send + Sync + 'static,
    {
        UpDownSumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::F64(Box::new(callback)),
            NumberKind::F64,
        )
    }

    /// Creates a new integral `SumUpDownObserverBuilder` instrument with the given
    /// name and callback
    pub fn i64_up_down_sum_observer<T, F>(
        &self,
        name: T,
        callback: F,
    ) -> UpDownSumObserverBuilder<'_, i64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<i64>) + Send + Sync + 'static,
    {
        UpDownSumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::I64(Box::new(callback)),
            NumberKind::I64,
        )
    }

    /// Creates a new `ValueRecorderBuilder` for `f64` values with the given name.
    pub fn f64_value_recorder<T>(&self, name: T) -> ValueRecorderBuilder<'_, f64>
    where
        T: Into<String>,
    {
        ValueRecorderBuilder::new(self, name.into(), NumberKind::F64)
    }

    /// Creates a new `ValueRecorderBuilder` for `i64` values with the given name.
    pub fn i64_value_recorder<T>(&self, name: T) -> ValueRecorderBuilder<'_, i64>
    where
        T: Into<String>,
    {
        ValueRecorderBuilder::new(self, name.into(), NumberKind::I64)
    }

    /// Creates a new `ValueRecorderBuilder` for `u64` values with the given name.
    pub fn u64_value_recorder<T>(&self, name: T) -> ValueRecorderBuilder<'_, u64>
    where
        T: Into<String>,
    {
        ValueRecorderBuilder::new(self, name.into(), NumberKind::U64)
    }

    /// Creates a new integer `CounterBuilder` for `u64` values with the given name.
    pub fn u64_counter<T>(&self, name: T) -> CounterBuilder<'_, u64>
    where
        T: Into<String>,
    {
        CounterBuilder::new(self, name.into(), NumberKind::U64)
    }

    /// Creates a new floating point `CounterBuilder` for `f64` values with the given name.
    pub fn f64_counter<T>(&self, name: T) -> CounterBuilder<'_, f64>
    where
        T: Into<String>,
    {
        CounterBuilder::new(self, name.into(), NumberKind::F64)
    }

    /// Creates a new integer `UpDownCounterBuilder` for an `i64` up down counter with the given name.
    pub fn i64_up_down_counter<T>(&self, name: T) -> UpDownCounterBuilder<'_, i64>
    where
        T: Into<String>,
    {
        UpDownCounterBuilder::new(self, name.into(), NumberKind::I64)
    }

    /// Creates a new floating point `UpDownCounterBuilder` for an `f64` up down counter with the given name.
    pub fn f64_up_down_counter<T>(&self, name: T) -> UpDownCounterBuilder<'_, f64>
    where
        T: Into<String>,
    {
        UpDownCounterBuilder::new(self, name.into(), NumberKind::F64)
    }

    /// Creates a new `BatchObserver` that supports making batches of observations for
    /// multiple instruments.
    pub fn batch_observer(&self, callback: BatchObserverCallback) -> BatchObserver<'_> {
        BatchObserver::new(self, AsyncRunner::Batch(callback))
    }

    /// Atomically record a batch of measurements.
    pub fn record_batch<T: IntoIterator<Item = Measurement>>(
        &self,
        labels: &[KeyValue],
        measurements: T,
    ) {
        self.record_batch_with_context(&Context::current(), labels, measurements)
    }

    /// Atomically record a batch of measurements with a given context
    pub fn record_batch_with_context<T: IntoIterator<Item = Measurement>>(
        &self,
        cx: &Context,
        labels: &[KeyValue],
        measurements: T,
    ) {
        self.core
            .record_batch_with_context(cx, labels, measurements.into_iter().collect())
    }

    pub(crate) fn new_sync_instrument(
        &self,
        descriptor: Descriptor,
    ) -> Result<Arc<dyn sdk_api::SyncInstrumentCore>> {
        self.core.new_sync_instrument(descriptor)
    }

    pub(crate) fn new_async_instrument(
        &self,
        descriptor: Descriptor,
        runner: AsyncRunner,
    ) -> Result<Arc<dyn sdk_api::AsyncInstrumentCore>> {
        self.core.new_async_instrument(descriptor, runner)
    }
}
<commit_msg>Missing metrics creator fns (#425)<commit_after>use crate::sdk::InstrumentationLibrary;
use crate::{
    metrics::{
        sdk_api, AsyncRunner, BatchObserver, BatchObserverCallback, CounterBuilder, Descriptor,
        Measurement, NumberKind, ObserverResult, Result, SumObserverBuilder, UpDownCounterBuilder,
        UpDownSumObserverBuilder, ValueObserverBuilder, ValueRecorderBuilder,
    },
    Context, KeyValue,
};
use std::fmt;
use std::sync::Arc;

/// Returns named meter instances
pub trait MeterProvider: fmt::Debug {
    /// Creates an implementation of the [`Meter`] interface. The
    /// instrumentation name must be the name of the library providing
    /// instrumentation. This name may be the same as the instrumented code only if
    /// that code provides built-in instrumentation. If the instrumentation name is
    /// empty, then a implementation defined default name will be used instead.
    ///
    /// [`Meter`]: struct.Meter.html
    fn meter(
        &self,
        instrumentation_name: &'static str,
        instrumentation_version: Option<&'static str>,
    ) -> Meter;
}

/// Meter is the OpenTelemetry metric API, based on a sdk-defined `MeterCore`
/// implementation and the `Meter` library name.
#[derive(Debug)]
pub struct Meter {
    instrumentation_library: InstrumentationLibrary,
    core: Arc<dyn sdk_api::MeterCore + Send + Sync>,
}

impl Meter {
    /// Create a new named meter from a sdk implemented core
    pub fn new<T: Into<&'static str>>(
        instrumentation_name: T,
        instrumentation_version: Option<T>,
        core: Arc<dyn sdk_api::MeterCore + Send + Sync>,
    ) -> Self {
        Meter {
            instrumentation_library: InstrumentationLibrary::new(
                instrumentation_name.into(),
                instrumentation_version.map(Into::into),
            ),
            core,
        }
    }

    #[deprecated(note = "use instrumentation_library() instead")]
    #[allow(dead_code)]
    pub(crate) fn instrumentation_name(&self) -> &str {
        self.instrumentation_library.name
    }

    pub(crate) fn instrumentation_library(&self) -> InstrumentationLibrary {
        self.instrumentation_library
    }

    /// Creates a new floating point `CounterBuilder` for `f64` values with the given name.
    pub fn f64_counter<T>(&self, name: T) -> CounterBuilder<'_, f64>
    where
        T: Into<String>,
    {
        CounterBuilder::new(self, name.into(), NumberKind::F64)
    }

    /// Creates a new floating point `UpDownCounterBuilder` for an `f64` up down counter with the given name.
    pub fn f64_up_down_counter<T>(&self, name: T) -> UpDownCounterBuilder<'_, f64>
    where
        T: Into<String>,
    {
        UpDownCounterBuilder::new(self, name.into(), NumberKind::F64)
    }

    /// Creates a new floating point `ValueRecorderBuilder` for `f64` values with the given name.
    pub fn f64_value_recorder<T>(&self, name: T) -> ValueRecorderBuilder<'_, f64>
    where
        T: Into<String>,
    {
        ValueRecorderBuilder::new(self, name.into(), NumberKind::F64)
    }

    /// Creates a new floating point `SumObserverBuilder` for `f64` values with the given name and callback.
    pub fn f64_sum_observer<T, F>(&self, name: T, callback: F) -> SumObserverBuilder<'_, f64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<f64>) + Send + Sync + 'static,
    {
        SumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::F64(Box::new(callback)),
            NumberKind::F64,
        )
    }

    /// Creates a new floating point `UpDownSumObserverBuilder` for `f64` values with the given name and callback.
    pub fn f64_up_down_sum_observer<T, F>(
        &self,
        name: T,
        callback: F,
    ) -> UpDownSumObserverBuilder<'_, f64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<f64>) + Send + Sync + 'static,
    {
        UpDownSumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::F64(Box::new(callback)),
            NumberKind::F64,
        )
    }

    /// Creates a new floating point `ValueObserverBuilder` for `f64` values with the given name and callback.
    pub fn f64_value_observer<T, F>(&self, name: T, callback: F) -> ValueObserverBuilder<'_, f64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<f64>) + Send + Sync + 'static,
    {
        ValueObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::F64(Box::new(callback)),
            NumberKind::F64,
        )
    }

    /// Creates a new integer `CounterBuilder` for `i64` values with the given name.
    pub fn i64_counter<T>(&self, name: T) -> CounterBuilder<'_, i64>
    where
        T: Into<String>,
    {
        CounterBuilder::new(self, name.into(), NumberKind::I64)
    }

    /// Creates a new integer `UpDownCounterBuilder` for an `i64` up down counter with the given name.
    pub fn i64_up_down_counter<T>(&self, name: T) -> UpDownCounterBuilder<'_, i64>
    where
        T: Into<String>,
    {
        UpDownCounterBuilder::new(self, name.into(), NumberKind::I64)
    }

    /// Creates a new integer `ValueRecorderBuilder` for `i64` values with the given name.
    pub fn i64_value_recorder<T>(&self, name: T) -> ValueRecorderBuilder<'_, i64>
    where
        T: Into<String>,
    {
        ValueRecorderBuilder::new(self, name.into(), NumberKind::I64)
    }

    /// Creates a new integer `SumObserverBuilder` for `i64` values with the given name and callback.
    pub fn i64_sum_observer<T, F>(&self, name: T, callback: F) -> SumObserverBuilder<'_, i64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<i64>) + Send + Sync + 'static,
    {
        SumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::I64(Box::new(callback)),
            NumberKind::I64,
        )
    }

    /// Creates a new integer `UpDownSumObserverBuilder` for `i64` values with the given name and callback.
    pub fn i64_up_down_sum_observer<T, F>(
        &self,
        name: T,
        callback: F,
    ) -> UpDownSumObserverBuilder<'_, i64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<i64>) + Send + Sync + 'static,
    {
        UpDownSumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::I64(Box::new(callback)),
            NumberKind::I64,
        )
    }

    /// Creates a new integer `ValueObserverBuilder` for `i64` values with the given name and callback.
    pub fn i64_value_observer<T, F>(&self, name: T, callback: F) -> ValueObserverBuilder<'_, i64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<i64>) + Send + Sync + 'static,
    {
        ValueObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::I64(Box::new(callback)),
            NumberKind::I64,
        )
    }

    /// Creates a new integer `CounterBuilder` for `u64` values with the given name.
    pub fn u64_counter<T>(&self, name: T) -> CounterBuilder<'_, u64>
    where
        T: Into<String>,
    {
        CounterBuilder::new(self, name.into(), NumberKind::U64)
    }

    /// Creates a new integer `UpDownCounterBuilder` for an `u64` up down counter with the given name.
    pub fn u64_up_down_counter<T>(&self, name: T) -> UpDownCounterBuilder<'_, u64>
    where
        T: Into<String>,
    {
        UpDownCounterBuilder::new(self, name.into(), NumberKind::U64)
    }

    /// Creates a new integer `ValueRecorderBuilder` for `u64` values with the given name.
    pub fn u64_value_recorder<T>(&self, name: T) -> ValueRecorderBuilder<'_, u64>
    where
        T: Into<String>,
    {
        ValueRecorderBuilder::new(self, name.into(), NumberKind::U64)
    }

    /// Creates a new integer `SumObserverBuilder` for `u64` values with the given name and callback.
    pub fn u64_sum_observer<T, F>(&self, name: T, callback: F) -> SumObserverBuilder<'_, u64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<u64>) + Send + Sync + 'static,
    {
        SumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::U64(Box::new(callback)),
            NumberKind::U64,
        )
    }

    /// Creates a new integer `UpDownSumObserverBuilder` for `u64` values with the given name and callback.
    pub fn u64_up_down_sum_observer<T, F>(
        &self,
        name: T,
        callback: F,
    ) -> UpDownSumObserverBuilder<'_, u64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<u64>) + Send + Sync + 'static,
    {
        UpDownSumObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::U64(Box::new(callback)),
            NumberKind::U64,
        )
    }

    /// Creates a new integer `ValueObserverBuilder` for `u64` values with the given name and callback.
    pub fn u64_value_observer<T, F>(&self, name: T, callback: F) -> ValueObserverBuilder<'_, u64>
    where
        T: Into<String>,
        F: Fn(ObserverResult<u64>) + Send + Sync + 'static,
    {
        ValueObserverBuilder::new(
            self,
            name.into(),
            AsyncRunner::U64(Box::new(callback)),
            NumberKind::U64,
        )
    }

    /// Creates a new `BatchObserver` that supports making batches of observations for
    /// multiple instruments.
    pub fn batch_observer(&self, callback: BatchObserverCallback) -> BatchObserver<'_> {
        BatchObserver::new(self, AsyncRunner::Batch(callback))
    }

    /// Atomically record a batch of measurements.
    pub fn record_batch<T: IntoIterator<Item = Measurement>>(
        &self,
        labels: &[KeyValue],
        measurements: T,
    ) {
        self.record_batch_with_context(&Context::current(), labels, measurements)
    }

    /// Atomically record a batch of measurements with a given context
    pub fn record_batch_with_context<T: IntoIterator<Item = Measurement>>(
        &self,
        cx: &Context,
        labels: &[KeyValue],
        measurements: T,
    ) {
        self.core
            .record_batch_with_context(cx, labels, measurements.into_iter().collect())
    }

    pub(crate) fn new_sync_instrument(
        &self,
        descriptor: Descriptor,
    ) -> Result<Arc<dyn sdk_api::SyncInstrumentCore>> {
        self.core.new_sync_instrument(descriptor)
    }

    pub(crate) fn new_async_instrument(
        &self,
        descriptor: Descriptor,
        runner: AsyncRunner,
    ) -> Result<Arc<dyn sdk_api::AsyncInstrumentCore>> {
        self.core.new_async_instrument(descriptor, runner)
    }
}
<|endoftext|>


<commit_before>//! Tests for configuration values that point to programs.

use cargo_test_support::{basic_lib_manifest, no_such_file_err_msg, project, rustc_host};

#[cargo_test]
fn pathless_tools() {
    let target = rustc_host();

    let foo = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{}]
                    linker = "nonexistent-linker"
                "#,
                target
            ),
        )
        .build();

    foo.cargo("build --verbose")
        .with_stderr(
            "\
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn absolute_tools() {
    let target = rustc_host();

    // Escaped as they appear within a TOML config file
    let linker = if cfg!(windows) {
        r#"C:\\bogus\\nonexistent-linker"#
    } else {
        r#"/bogus/nonexistent-linker"#
    };

    let foo = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{target}]
                    linker = "{linker}"
                "#,
                target = target,
                linker = linker
            ),
        )
        .build();

    foo.cargo("build --verbose")
        .with_stderr(
            "\
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `rustc [..] -C linker=[..]bogus/nonexistent-linker [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn relative_tools() {
    let target = rustc_host();

    // Escaped as they appear within a TOML config file
    let linker = if cfg!(windows) {
        r#".\\tools\\nonexistent-linker"#
    } else {
        r#"./tools/nonexistent-linker"#
    };

    // Funky directory structure to test that relative tool paths are made absolute
    // by reference to the `.cargo/..` directory and not to (for example) the CWD.
    let p = project()
        .no_manifest()
        .file("bar/Cargo.toml", &basic_lib_manifest("bar"))
        .file("bar/src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{target}]
                    linker = "{linker}"
                "#,
                target = target,
                linker = linker
            ),
        )
        .build();

    let prefix = p.root().into_os_string().into_string().unwrap();

    p.cargo("build --verbose")
        .cwd("bar")
        .with_stderr(&format!(
            "\
[COMPILING] bar v0.5.0 ([CWD])
[RUNNING] `rustc [..] -C linker={prefix}/./tools/nonexistent-linker [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
            prefix = prefix,
        ))
        .run();
}

#[cargo_test]
fn custom_runner() {
    let target = rustc_host();

    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file("tests/test.rs", "")
        .file("benches/bench.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{}]
                    runner = "nonexistent-runner -r"
                "#,
                target
            ),
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
",
        )
        .run();

    p.cargo("test --test test --verbose -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]`
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r [..]/target/debug/deps/test-[..][EXE] --param`
",
        )
        .run();

    p.cargo("bench --bench bench --verbose -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]`
[RUNNING] `rustc [..]`
[FINISHED] bench [optimized] target(s) in [..]
[RUNNING] `nonexistent-runner -r [..]/target/release/deps/bench-[..][EXE] --param --bench`
",
        )
        .run();
}

// can set a custom runner via `target.'cfg(..)'.runner`
#[cargo_test]
fn custom_runner_cfg() {
    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config",
            r#"
            [target.'cfg(not(target_os = "none"))']
            runner = "nonexistent-runner -r"
            "#,
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
",
        )
        .run();
}

// custom runner set via `target.$triple.runner` have precedence over `target.'cfg(..)'.runner`
#[cargo_test]
fn custom_runner_cfg_precedence() {
    let target = rustc_host();

    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.'cfg(not(target_os = "none"))']
                    runner = "ignored-runner"

                    [target.{}]
                    runner = "nonexistent-runner -r"
                "#,
                target
            ),
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
",
        )
        .run();
}

#[cargo_test]
fn custom_runner_cfg_collision() {
    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config",
            r#"
            [target.'cfg(not(target_arch = "avr"))']
            runner = "true"

            [target.'cfg(not(target_os = "none"))']
            runner = "false"
            "#,
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr(
            "\
[ERROR] several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`
first match `cfg(not(target_arch = \"avr\"))` located in [..]/foo/.cargo/config
second match `cfg(not(target_os = \"none\"))` located in [..]/foo/.cargo/config
",
        )
        .run();
}

#[cargo_test]
fn custom_runner_env() {
    let target = rustc_host();
    let p = project().file("src/main.rs", "fn main() {}").build();

    let key = format!(
        "CARGO_TARGET_{}_RUNNER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("run")
        .env(&key, "nonexistent-runner --foo")
        .with_status(101)
        .with_stderr(&format!(
            "\
[COMPILING] foo [..]
[FINISHED] dev [..]
[RUNNING] `nonexistent-runner --foo target/debug/foo[EXE]`
[ERROR] could not execute process `nonexistent-runner --foo target/debug/foo[EXE]` (never executed)

Caused by:
  {}
",
            no_such_file_err_msg()
        ))
        .run();
}

#[cargo_test]
fn custom_runner_env_overrides_config() {
    let target = rustc_host();
    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config.toml",
            &format!(
                r#"
                    [target.{}]
                    runner = "should-not-run -r"
                "#,
                target
            ),
        )
        .build();

    let key = format!(
        "CARGO_TARGET_{}_RUNNER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("run")
        .env(&key, "should-run --foo")
        .with_status(101)
        .with_stderr_contains("[RUNNING] `should-run --foo target/debug/foo[EXE]`")
        .run();
}

#[cargo_test]
#[cfg(unix)] // Assumes `true` is in PATH.
fn custom_runner_env_true() {
    // Check for a bug where "true" was interpreted as a boolean instead of
    // the executable.
    let target = rustc_host();
    let p = project().file("src/main.rs", "fn main() {}").build();

    let key = format!(
        "CARGO_TARGET_{}_RUNNER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("run")
        .env(&key, "true")
        .with_stderr_contains("[RUNNING] `true target/debug/foo[EXE]`")
        .run();
}

#[cargo_test]
fn custom_linker_env() {
    let target = rustc_host();
    let p = project().file("src/main.rs", "fn main() {}").build();

    let key = format!(
        "CARGO_TARGET_{}_LINKER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("build -v")
        .env(&key, "nonexistent-linker")
        .with_status(101)
        .with_stderr_contains("[RUNNING] `rustc [..]-C linker=nonexistent-linker [..]")
        .run();
}

#[cargo_test]
#[cfg(not(windows))]
fn target_in_environment_contains_lower_case() {
    let p = project().file("src/main.rs", "fn main() {}").build();

    let target_keys = [
        "CARGO_TARGET_X86_64_UNKNOWN_LINUX_musl_LINKER",
        "CARGO_TARGET_x86_64_unknown_linux_musl_LINKER",
    ];

    for target_key in &target_keys {
        p.cargo("build -v --target X86_64_unknown_linux_musl")
            .env(target_key, "nonexistent-linker")
            .with_status(101)
            .with_stderr_contains(format!(
                "warning: Variables in environment require uppercase,
                        but given variable: {}, contains lowercase or dash.",
                target_key
            ))
            .run();
    }
}

#[cargo_test]
fn cfg_ignored_fields() {
    // Test for some ignored fields in [target.'cfg()'] tables.
    let p = project()
        .file(
            ".cargo/config",
            r#"
            # Try some empty tables.
            [target.'cfg(not(foo))']
            [target.'cfg(not(bar))'.somelib]

            # A bunch of unused fields.
            [target.'cfg(not(target_os = "none"))']
            linker = 'false'
            ar = 'false'
            foo = {rustc-flags = "-l foo"}
            invalid = 1
            runner = 'false'
            rustflags = ''
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("check")
        .with_stderr(
            "\
[WARNING] unused key `somelib` in [target] config table `cfg(not(bar))`
[WARNING] unused key `ar` in [target] config table `cfg(not(target_os = \"none\"))`
[WARNING] unused key `foo` in [target] config table `cfg(not(target_os = \"none\"))`
[WARNING] unused key `invalid` in [target] config table `cfg(not(target_os = \"none\"))`
[WARNING] unused key `linker` in [target] config table `cfg(not(target_os = \"none\"))`
[CHECKING] foo v0.0.1 ([..])
[FINISHED] [..]
",
        )
        .run();
}
<commit_msg>Correct target<commit_after>//! Tests for configuration values that point to programs.

use cargo_test_support::{basic_lib_manifest, no_such_file_err_msg, project, rustc_host};

#[cargo_test]
fn pathless_tools() {
    let target = rustc_host();

    let foo = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{}]
                    linker = "nonexistent-linker"
                "#,
                target
            ),
        )
        .build();

    foo.cargo("build --verbose")
        .with_stderr(
            "\
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn absolute_tools() {
    let target = rustc_host();

    // Escaped as they appear within a TOML config file
    let linker = if cfg!(windows) {
        r#"C:\\bogus\\nonexistent-linker"#
    } else {
        r#"/bogus/nonexistent-linker"#
    };

    let foo = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{target}]
                    linker = "{linker}"
                "#,
                target = target,
                linker = linker
            ),
        )
        .build();

    foo.cargo("build --verbose")
        .with_stderr(
            "\
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `rustc [..] -C linker=[..]bogus/nonexistent-linker [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn relative_tools() {
    let target = rustc_host();

    // Escaped as they appear within a TOML config file
    let linker = if cfg!(windows) {
        r#".\\tools\\nonexistent-linker"#
    } else {
        r#"./tools/nonexistent-linker"#
    };

    // Funky directory structure to test that relative tool paths are made absolute
    // by reference to the `.cargo/..` directory and not to (for example) the CWD.
    let p = project()
        .no_manifest()
        .file("bar/Cargo.toml", &basic_lib_manifest("bar"))
        .file("bar/src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{target}]
                    linker = "{linker}"
                "#,
                target = target,
                linker = linker
            ),
        )
        .build();

    let prefix = p.root().into_os_string().into_string().unwrap();

    p.cargo("build --verbose")
        .cwd("bar")
        .with_stderr(&format!(
            "\
[COMPILING] bar v0.5.0 ([CWD])
[RUNNING] `rustc [..] -C linker={prefix}/./tools/nonexistent-linker [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
            prefix = prefix,
        ))
        .run();
}

#[cargo_test]
fn custom_runner() {
    let target = rustc_host();

    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file("tests/test.rs", "")
        .file("benches/bench.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.{}]
                    runner = "nonexistent-runner -r"
                "#,
                target
            ),
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
",
        )
        .run();

    p.cargo("test --test test --verbose -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]`
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r [..]/target/debug/deps/test-[..][EXE] --param`
",
        )
        .run();

    p.cargo("bench --bench bench --verbose -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]`
[RUNNING] `rustc [..]`
[FINISHED] bench [optimized] target(s) in [..]
[RUNNING] `nonexistent-runner -r [..]/target/release/deps/bench-[..][EXE] --param --bench`
",
        )
        .run();
}

// can set a custom runner via `target.'cfg(..)'.runner`
#[cargo_test]
fn custom_runner_cfg() {
    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config",
            r#"
            [target.'cfg(not(target_os = "none"))']
            runner = "nonexistent-runner -r"
            "#,
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
",
        )
        .run();
}

// custom runner set via `target.$triple.runner` have precedence over `target.'cfg(..)'.runner`
#[cargo_test]
fn custom_runner_cfg_precedence() {
    let target = rustc_host();

    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config",
            &format!(
                r#"
                    [target.'cfg(not(target_os = "none"))']
                    runner = "ignored-runner"

                    [target.{}]
                    runner = "nonexistent-runner -r"
                "#,
                target
            ),
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `nonexistent-runner -r target/debug/foo[EXE] --param`
",
        )
        .run();
}

#[cargo_test]
fn custom_runner_cfg_collision() {
    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config",
            r#"
            [target.'cfg(not(target_arch = "avr"))']
            runner = "true"

            [target.'cfg(not(target_os = "none"))']
            runner = "false"
            "#,
        )
        .build();

    p.cargo("run -- --param")
        .with_status(101)
        .with_stderr(
            "\
[ERROR] several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`
first match `cfg(not(target_arch = \"avr\"))` located in [..]/foo/.cargo/config
second match `cfg(not(target_os = \"none\"))` located in [..]/foo/.cargo/config
",
        )
        .run();
}

#[cargo_test]
fn custom_runner_env() {
    let target = rustc_host();
    let p = project().file("src/main.rs", "fn main() {}").build();

    let key = format!(
        "CARGO_TARGET_{}_RUNNER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("run")
        .env(&key, "nonexistent-runner --foo")
        .with_status(101)
        .with_stderr(&format!(
            "\
[COMPILING] foo [..]
[FINISHED] dev [..]
[RUNNING] `nonexistent-runner --foo target/debug/foo[EXE]`
[ERROR] could not execute process `nonexistent-runner --foo target/debug/foo[EXE]` (never executed)

Caused by:
  {}
",
            no_such_file_err_msg()
        ))
        .run();
}

#[cargo_test]
fn custom_runner_env_overrides_config() {
    let target = rustc_host();
    let p = project()
        .file("src/main.rs", "fn main() {}")
        .file(
            ".cargo/config.toml",
            &format!(
                r#"
                    [target.{}]
                    runner = "should-not-run -r"
                "#,
                target
            ),
        )
        .build();

    let key = format!(
        "CARGO_TARGET_{}_RUNNER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("run")
        .env(&key, "should-run --foo")
        .with_status(101)
        .with_stderr_contains("[RUNNING] `should-run --foo target/debug/foo[EXE]`")
        .run();
}

#[cargo_test]
#[cfg(unix)] // Assumes `true` is in PATH.
fn custom_runner_env_true() {
    // Check for a bug where "true" was interpreted as a boolean instead of
    // the executable.
    let target = rustc_host();
    let p = project().file("src/main.rs", "fn main() {}").build();

    let key = format!(
        "CARGO_TARGET_{}_RUNNER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("run")
        .env(&key, "true")
        .with_stderr_contains("[RUNNING] `true target/debug/foo[EXE]`")
        .run();
}

#[cargo_test]
fn custom_linker_env() {
    let target = rustc_host();
    let p = project().file("src/main.rs", "fn main() {}").build();

    let key = format!(
        "CARGO_TARGET_{}_LINKER",
        target.to_uppercase().replace('-', "_")
    );

    p.cargo("build -v")
        .env(&key, "nonexistent-linker")
        .with_status(101)
        .with_stderr_contains("[RUNNING] `rustc [..]-C linker=nonexistent-linker [..]")
        .run();
}

#[cargo_test]
#[cfg(not(windows))]
fn target_in_environment_contains_lower_case() {
    let p = project().file("src/main.rs", "fn main() {}").build();

    let target_keys = [
        "CARGO_TARGET_X86_64_UNKNOWN_LINUX_musl_LINKER",
        "CARGO_TARGET_x86_64_unknown_linux_musl_LINKER",
    ];

    for target_key in &target_keys {
        p.cargo("build -v --target x86_64-unknown-linux-musl")
            .env(target_key, "nonexistent-linker")
            .with_status(101)
            .with_stderr_contains(format!(
                "warning: Variables in environment require uppercase,
                        but given variable: {}, contains lowercase or dash.",
                target_key
            ))
            .run();
    }
}

#[cargo_test]
fn cfg_ignored_fields() {
    // Test for some ignored fields in [target.'cfg()'] tables.
    let p = project()
        .file(
            ".cargo/config",
            r#"
            # Try some empty tables.
            [target.'cfg(not(foo))']
            [target.'cfg(not(bar))'.somelib]

            # A bunch of unused fields.
            [target.'cfg(not(target_os = "none"))']
            linker = 'false'
            ar = 'false'
            foo = {rustc-flags = "-l foo"}
            invalid = 1
            runner = 'false'
            rustflags = ''
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("check")
        .with_stderr(
            "\
[WARNING] unused key `somelib` in [target] config table `cfg(not(bar))`
[WARNING] unused key `ar` in [target] config table `cfg(not(target_os = \"none\"))`
[WARNING] unused key `foo` in [target] config table `cfg(not(target_os = \"none\"))`
[WARNING] unused key `invalid` in [target] config table `cfg(not(target_os = \"none\"))`
[WARNING] unused key `linker` in [target] config table `cfg(not(target_os = \"none\"))`
[CHECKING] foo v0.0.1 ([..])
[FINISHED] [..]
",
        )
        .run();
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

use std::collections::HashSet;

use clap::{Arg, App};

use advent_of_code::bbox::*;

static DIRECTIONS_3D : [(i32,i32,i32); 26] = [
    (-1,-1,-1), ( 0,-1,-1), ( 1,-1,-1),
    (-1, 0,-1), ( 0, 0,-1), ( 1, 0,-1),
    (-1, 1,-1), ( 0, 1,-1), ( 1, 1,-1),

    (-1,-1, 0), ( 0,-1, 0), ( 1,-1, 0),
    (-1, 0, 0),             ( 1, 0, 0),
    (-1, 1, 0), ( 0, 1, 0), ( 1, 1, 0),

    (-1,-1, 1), ( 0,-1, 1), ( 1,-1, 1),
    (-1, 0, 1), ( 0, 0, 1), ( 1, 0, 1),
    (-1, 1, 1), ( 0, 1, 1), ( 1, 1, 1),
];

static DIRECTIONS_4D : [(i32,i32,i32,i32); 80] = [
    (-1,-1,-1,-1), ( 0,-1,-1,-1), ( 1,-1,-1,-1),
    (-1, 0,-1,-1), ( 0, 0,-1,-1), ( 1, 0,-1,-1),
    (-1, 1,-1,-1), ( 0, 1,-1,-1), ( 1, 1,-1,-1),

    (-1,-1, 0,-1), ( 0,-1, 0,-1), ( 1,-1, 0,-1),
    (-1, 0, 0,-1), ( 0, 0, 0,-1), ( 1, 0, 0,-1),
    (-1, 1, 0,-1), ( 0, 1, 0,-1), ( 1, 1, 0,-1),

    (-1,-1, 1,-1), ( 0,-1, 1,-1), ( 1,-1, 1,-1),
    (-1, 0, 1,-1), ( 0, 0, 1,-1), ( 1, 0, 1,-1),
    (-1, 1, 1,-1), ( 0, 1, 1,-1), ( 1, 1, 1,-1),

    (-1,-1,-1, 0), ( 0,-1,-1, 0), ( 1,-1,-1, 0),
    (-1, 0,-1, 0), ( 0, 0,-1, 0), ( 1, 0,-1, 0),
    (-1, 1,-1, 0), ( 0, 1,-1, 0), ( 1, 1,-1, 0),

    (-1,-1, 0, 0), ( 0,-1, 0, 0), ( 1,-1, 0, 0),
    (-1, 0, 0, 0),                ( 1, 0, 0, 0),
    (-1, 1, 0, 0), ( 0, 1, 0, 0), ( 1, 1, 0, 0),

    (-1,-1, 1, 0), ( 0,-1, 1, 0), ( 1,-1, 1, 0),
    (-1, 0, 1, 0), ( 0, 0, 1, 0), ( 1, 0, 1, 0),
    (-1, 1, 1, 0), ( 0, 1, 1, 0), ( 1, 1, 1, 0),

    (-1,-1,-1, 1), ( 0,-1,-1, 1), ( 1,-1,-1, 1),
    (-1, 0,-1, 1), ( 0, 0,-1, 1), ( 1, 0,-1, 1),
    (-1, 1,-1, 1), ( 0, 1,-1, 1), ( 1, 1,-1, 1),

    (-1,-1, 0, 1), ( 0,-1, 0, 1), ( 1,-1, 0, 1),
    (-1, 0, 0, 1), ( 0, 0, 0, 1), ( 1, 0, 0, 1),
    (-1, 1, 0, 1), ( 0, 1, 0, 1), ( 1, 1, 0, 1),

    (-1,-1, 1, 1), ( 0,-1, 1, 1), ( 1,-1, 1, 1),
    (-1, 0, 1, 1), ( 0, 0, 1, 1), ( 1, 0, 1, 1),
    (-1, 1, 1, 1), ( 0, 1, 1, 1), ( 1, 1, 1, 1),
];

use Cube::*;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Cube { Active, Inactive, }
impl std::fmt::Display for Cube {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Active => write!(f, "#"),
            Inactive => write!(f, "."),
        }
    }
}

#[derive(Clone)]
pub struct ConwayLife3D {
    pub map: HashSet<(i32,i32,i32)>,
    pub bbox: BBox3D<i32>,
}

impl ConwayLife3D {
    pub fn new() -> ConwayLife3D {
        ConwayLife3D { map: HashSet::new(), bbox: BBox3D::new() }
    }

    pub fn load(&mut self, plane: &str) {
        for (j, line) in plane.lines().enumerate() {
            for (i, ch) in line.chars().enumerate() {
                if ch == '#' {
                    self.map.insert((i as i32, j as i32, 0));
                    self.bbox.update(&(i as i32, j as i32, 0));
                }
            }
        }
    }

    pub fn get(&self, pt: &(i32, i32, i32)) -> Cube {
        if self.map.contains(pt) { Active } else { Inactive }
    }

    pub fn step(&mut self) -> bool {
        let mut add = HashSet::new();
        let mut rm  = HashSet::new();

        for x in self.bbox.xmin()-1..=self.bbox.xmax()+1 {
        for y in self.bbox.ymin()-1..=self.bbox.ymax()+1 {
        for z in self.bbox.zmin()-1..=self.bbox.zmax()+1 {
            let mut count = 0;
            for (dx,dy,dz) in DIRECTIONS_3D.iter() {
                if self.get(&(x+dx,y+dy,z+dz)) == Active {
                    count += 1;
                    if count > 3 { break; } // shortcut
                }
            }
            if self.map.contains(&(x,y,z)) {
                if count != 2 && count != 3 { rm.insert((x,y,z)); }
            } else {
                if count == 3 { add.insert((x,y,z)); }
            }
        }}}

        if add.is_empty() && rm.is_empty() { return false; }

        for pt in rm.iter()   { self.map.remove(pt); }
        for pt in add.drain() {
            self.bbox.update(&pt);
            self.map.insert(pt);
        }
        return true;
    }

    #[inline]
    pub fn count(&self) -> usize { self.map.len() }
}
impl std::fmt::Display for ConwayLife3D {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for z in self.bbox.zmin()..=self.bbox.zmax() {
            if z > self.bbox.zmin() { write!(f, "\nz={}\n", z)?; }
            else                    { write!(f, "z={}\n", z)?; }
            for y in self.bbox.ymin()..=self.bbox.ymax() {
                if y > self.bbox.ymin() { write!(f, "\n")?; }
                for x in self.bbox.xmin()..=self.bbox.xmax() {
                    write!(f, "{}", self.get(&(x,y,z)))?;
                }
            }
        }
        Ok(())
    }
}

#[derive(Clone)]
pub struct ConwayLife4D {
    pub map: HashSet<(i32,i32,i32,i32)>,
    pub bbox: BBox4D<i32>,
}

impl ConwayLife4D {
    pub fn new() -> ConwayLife4D {
        ConwayLife4D { map: HashSet::new(), bbox: BBox4D::new() }
    }

    pub fn load(&mut self, plane: &str) {
        for (j, line) in plane.lines().enumerate() {
            for (i, ch) in line.chars().enumerate() {
                if ch == '#' {
                    self.map.insert((i as i32, j as i32, 0, 0));
                    self.bbox.update(&(i as i32, j as i32, 0, 0));
                }
            }
        }
    }

    #[inline]
    pub fn get(&self, pt: &(i32, i32, i32, i32)) -> Cube {
        if self.map.contains(pt) { Active } else { Inactive }
    }

    pub fn step(&mut self) -> bool {
        let mut add = HashSet::new();
        let mut rm  = HashSet::new();

        for x in self.bbox.xmin()-1..=self.bbox.xmax()+1 {
        for y in self.bbox.ymin()-1..=self.bbox.ymax()+1 {
        for z in self.bbox.zmin()-1..=self.bbox.zmax()+1 {
        for w in self.bbox.wmin()-1..=self.bbox.wmax()+1 {
            let mut count = 0;
            for (dx,dy,dz,dw) in DIRECTIONS_4D.iter() {
                if self.get(&(x+dx,y+dy,z+dz,w+dw)) == Active {
                    count += 1;
                    if count > 3 { break; } // shortcut
                }
            }
            if self.map.contains(&(x,y,z,w)) {
                if count != 2 && count != 3 { rm.insert((x,y,z,w)); }
            } else {
                if count == 3 { add.insert((x,y,z,w)); }
            }
        }}}}

        if add.is_empty() && rm.is_empty() { return false; }

        for pt in rm.iter()   { self.map.remove(pt); }
        for pt in add.drain() {
            self.bbox.update(&pt);
            self.map.insert(pt);
        }
        return true;
    }

    #[inline]
    pub fn count(&self) -> usize { self.map.len() }
}

fn main() {
    let matches = App::new("Advent of code 2020, Day 17 Solution")
        .arg(Arg::with_name("FILE").help("Input file to process").index(1))
        .get_matches();
    let fname = matches.value_of("FILE").unwrap_or("17.in");
    let contents = std::fs::read_to_string(fname)
        .unwrap_or_else(|err| panic!("Error reading {}: {}", fname, err));

    let mut life = ConwayLife3D::new();
    life.load(&contents);
    for _ in 0..6 { life.step(); }
    println!("Part 1: after 6 cycles, {} cubes are active", life.count());

    let mut life = ConwayLife4D::new();
    life.load(&contents);
    for _ in 0..6 { life.step(); }
    println!("Part 2: after 6 cycles, {} cubes are active", life.count());
}
<commit_msg>Day 17 optimizations<commit_after>// SPDX-License-Identifier: MIT

use std::collections::HashSet;
use std::collections::HashMap;

use clap::{Arg, App};

use advent_of_code::bbox::*;

static DIRECTIONS_3D : [(i32,i32,i32); 26] = [
    (-1,-1,-1), ( 0,-1,-1), ( 1,-1,-1),
    (-1, 0,-1), ( 0, 0,-1), ( 1, 0,-1),
    (-1, 1,-1), ( 0, 1,-1), ( 1, 1,-1),

    (-1,-1, 0), ( 0,-1, 0), ( 1,-1, 0),
    (-1, 0, 0),             ( 1, 0, 0),
    (-1, 1, 0), ( 0, 1, 0), ( 1, 1, 0),

    (-1,-1, 1), ( 0,-1, 1), ( 1,-1, 1),
    (-1, 0, 1), ( 0, 0, 1), ( 1, 0, 1),
    (-1, 1, 1), ( 0, 1, 1), ( 1, 1, 1),
];

static DIRECTIONS_4D : [(i32,i32,i32,i32); 80] = [
    (-1,-1,-1,-1), ( 0,-1,-1,-1), ( 1,-1,-1,-1),
    (-1, 0,-1,-1), ( 0, 0,-1,-1), ( 1, 0,-1,-1),
    (-1, 1,-1,-1), ( 0, 1,-1,-1), ( 1, 1,-1,-1),

    (-1,-1, 0,-1), ( 0,-1, 0,-1), ( 1,-1, 0,-1),
    (-1, 0, 0,-1), ( 0, 0, 0,-1), ( 1, 0, 0,-1),
    (-1, 1, 0,-1), ( 0, 1, 0,-1), ( 1, 1, 0,-1),

    (-1,-1, 1,-1), ( 0,-1, 1,-1), ( 1,-1, 1,-1),
    (-1, 0, 1,-1), ( 0, 0, 1,-1), ( 1, 0, 1,-1),
    (-1, 1, 1,-1), ( 0, 1, 1,-1), ( 1, 1, 1,-1),

    (-1,-1,-1, 0), ( 0,-1,-1, 0), ( 1,-1,-1, 0),
    (-1, 0,-1, 0), ( 0, 0,-1, 0), ( 1, 0,-1, 0),
    (-1, 1,-1, 0), ( 0, 1,-1, 0), ( 1, 1,-1, 0),

    (-1,-1, 0, 0), ( 0,-1, 0, 0), ( 1,-1, 0, 0),
    (-1, 0, 0, 0),                ( 1, 0, 0, 0),
    (-1, 1, 0, 0), ( 0, 1, 0, 0), ( 1, 1, 0, 0),

    (-1,-1, 1, 0), ( 0,-1, 1, 0), ( 1,-1, 1, 0),
    (-1, 0, 1, 0), ( 0, 0, 1, 0), ( 1, 0, 1, 0),
    (-1, 1, 1, 0), ( 0, 1, 1, 0), ( 1, 1, 1, 0),

    (-1,-1,-1, 1), ( 0,-1,-1, 1), ( 1,-1,-1, 1),
    (-1, 0,-1, 1), ( 0, 0,-1, 1), ( 1, 0,-1, 1),
    (-1, 1,-1, 1), ( 0, 1,-1, 1), ( 1, 1,-1, 1),

    (-1,-1, 0, 1), ( 0,-1, 0, 1), ( 1,-1, 0, 1),
    (-1, 0, 0, 1), ( 0, 0, 0, 1), ( 1, 0, 0, 1),
    (-1, 1, 0, 1), ( 0, 1, 0, 1), ( 1, 1, 0, 1),

    (-1,-1, 1, 1), ( 0,-1, 1, 1), ( 1,-1, 1, 1),
    (-1, 0, 1, 1), ( 0, 0, 1, 1), ( 1, 0, 1, 1),
    (-1, 1, 1, 1), ( 0, 1, 1, 1), ( 1, 1, 1, 1),
];

use Cube::*;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Cube { Active, Inactive, }
impl std::fmt::Display for Cube {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Active => write!(f, "#"),
            Inactive => write!(f, "."),
        }
    }
}

#[derive(Clone)]
pub struct ConwayLife3D {
    pub map: HashSet<(i32,i32,i32)>,
    pub bbox: BBox3D<i32>,
}

impl ConwayLife3D {
    pub fn new() -> ConwayLife3D {
        ConwayLife3D { map: HashSet::new(), bbox: BBox3D::new() }
    }

    pub fn load(&mut self, plane: &str) {
        for (j, line) in plane.lines().enumerate() {
            for (i, ch) in line.chars().enumerate() {
                if ch == '#' {
                    self.map.insert((i as i32, j as i32, 0));
                    self.bbox.update(&(i as i32, j as i32, 0));
                }
            }
        }
    }

    pub fn get(&self, pt: &(i32, i32, i32)) -> Cube {
        if self.map.contains(pt) { Active } else { Inactive }
    }

    pub fn step(&mut self) -> bool {
        let mut add = HashSet::new();
        let mut rm  = HashSet::new();

        for x in self.bbox.xmin()-1..=self.bbox.xmax()+1 {
        for y in self.bbox.ymin()-1..=self.bbox.ymax()+1 {
        for z in self.bbox.zmin()-1..=self.bbox.zmax()+1 {
            let mut count = 0;
            for (dx,dy,dz) in DIRECTIONS_3D.iter() {
                if self.get(&(x+dx,y+dy,z+dz)) == Active {
                    count += 1;
                    if count > 3 { break; } // shortcut
                }
            }
            if self.map.contains(&(x,y,z)) {
                if count != 2 && count != 3 { rm.insert((x,y,z)); }
            } else {
                if count == 3 { add.insert((x,y,z)); }
            }
        }}}

        if add.is_empty() && rm.is_empty() { return false; }

        for pt in rm.iter()   { self.map.remove(pt); }
        for pt in add.drain() {
            self.bbox.update(&pt);
            self.map.insert(pt);
        }
        return true;
    }

    #[inline]
    pub fn count(&self) -> usize { self.map.len() }
}
impl std::fmt::Display for ConwayLife3D {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for z in self.bbox.zmin()..=self.bbox.zmax() {
            if z > self.bbox.zmin() { write!(f, "\nz={}\n", z)?; }
            else                    { write!(f, "z={}\n", z)?; }
            for y in self.bbox.ymin()..=self.bbox.ymax() {
                if y > self.bbox.ymin() { write!(f, "\n")?; }
                for x in self.bbox.xmin()..=self.bbox.xmax() {
                    write!(f, "{}", self.get(&(x,y,z)))?;
                }
            }
        }
        Ok(())
    }
}

#[derive(Clone)]
pub struct ConwayLife4D {
    pub map: HashSet<(i32,i32,i32,i32)>,
    pub bbox: BBox4D<i32>,
}

impl ConwayLife4D {
    pub fn new() -> ConwayLife4D {
        ConwayLife4D { map: HashSet::new(), bbox: BBox4D::new() }
    }

    pub fn load(&mut self, plane: &str) {
        for (j, line) in plane.lines().enumerate() {
            for (i, ch) in line.chars().enumerate() {
                if ch == '#' {
                    self.map.insert((i as i32, j as i32, 0, 0));
                    self.bbox.update(&(i as i32, j as i32, 0, 0));
                }
            }
        }
    }

    #[inline]
    pub fn get(&self, pt: &(i32, i32, i32, i32)) -> Cube {
        if self.map.contains(pt) { Active } else { Inactive }
    }

    // 230 ms
    pub fn step1(&mut self) -> bool {
        let mut add = HashSet::new();
        let mut rm  = HashSet::new();

        for x in self.bbox.xmin()-1..=self.bbox.xmax()+1 {
        for y in self.bbox.ymin()-1..=self.bbox.ymax()+1 {
        for z in self.bbox.zmin()-1..=self.bbox.zmax()+1 {
        for w in self.bbox.wmin()-1..=self.bbox.wmax()+1 {
            let mut count = 0;
            for (dx,dy,dz,dw) in DIRECTIONS_4D.iter() {
                if self.get(&(x+dx,y+dy,z+dz,w+dw)) == Active {
                    count += 1;
                    if count > 3 { break; } // shortcut (~35ms)
                }
            }
            if self.map.contains(&(x,y,z,w)) {
                if count != 2 && count != 3 { rm.insert((x,y,z,w)); }
            } else {
                if count == 3 { add.insert((x,y,z,w)); }
            }
        }}}}

        if add.is_empty() && rm.is_empty() { return false; }

        for pt in rm.iter()   { self.map.remove(pt); }
        for pt in add.drain() {
            self.bbox.update(&pt);
            self.map.insert(pt);
        }
        return true;
    }

    // 150 ms
    pub fn step2(&mut self) -> bool {
        let mut add = HashSet::new();
        let mut rm  = HashSet::new();

        for &(x,y,z,w) in self.map.iter() {
            let mut count = 0;
            for (dx,dy,dz,dw) in DIRECTIONS_4D.iter() {
                if self.get(&(x+dx,y+dy,z+dz,w+dw)) == Active {
                    count += 1;
                }
                else if !add.contains(&(x+dx,y+dy,z+dz,w+dw)) && !rm.contains(&(x+dx,y+dy,z+dz,w+dw)) {
                    let mut c = 0;
                    for (ddx,ddy,ddz,ddw) in DIRECTIONS_4D.iter() {
                        if self.map.contains(&(x+dx+ddx,y+dy+ddy,z+dz+ddz,w+dw+ddw)) {
                            c += 1;
                            if c > 3 { break; } // shortcut
                        }
                    }
                    if 3 == c { add.insert((x+dx,y+dy,z+dz,w+dw)); }
                    else { rm.insert((x+dx,y+dy,z+dz,w+dw)); }
                }
            }
            if count != 2 && count != 3 { rm.insert((x,y,z,w)); }
        }

        if add.is_empty() && rm.is_empty() { return false; }

        for pt in rm.drain()  { self.map.remove(&pt); }
        for pt in add.drain() {
            self.bbox.update(&pt);
            self.map.insert(pt);
        }
        return true;
    }

    // 143 ms  -- NOTE: using "new" set with step1() algorithm is 350ms
    pub fn step3(&mut self) {
        let mut new = HashSet::new();
        let mut seen = HashSet::new();

        for &(x,y,z,w) in self.map.iter() {
            let mut count = 0;
            for (dx,dy,dz,dw) in DIRECTIONS_4D.iter() {
                if self.get(&(x+dx,y+dy,z+dz,w+dw)) == Active {
                    count += 1;
                }
                else if !new.contains(&(x+dx,y+dy,z+dz,w+dw)) && !seen.contains(&(x+dx,y+dy,z+dz,w+dw)) {
                    let mut c = 0;
                    for (ddx,ddy,ddz,ddw) in DIRECTIONS_4D.iter() {
                        if self.map.contains(&(x+dx+ddx,y+dy+ddy,z+dz+ddz,w+dw+ddw)) {
                            c += 1;
                            if c > 3 { break; } // shortcut
                        }
                    }
                    if 3 == c { new.insert((x+dx,y+dy,z+dz,w+dw)); }
                    else { seen.insert((x+dx,y+dy,z+dz,w+dw)); }
                }
            }
            if count == 2 || count == 3 { new.insert((x,y,z,w)); }
        }

        self.map = new;
    }

    // 27 ms
    pub fn step(&mut self) {
        let mut seen = HashMap::new();
        for &(x,y,z,w) in self.map.iter() {
            for (dx,dy,dz,dw) in DIRECTIONS_4D.iter() {
                seen.entry((x+dx,y+dy,z+dz,w+dw)).and_modify(|e| { *e += 1 }).or_insert(1);
            }
        }

        let mut new = HashSet::new();
        for (pt, n) in seen.drain() {
            if n == 3 || (n == 2 && self.map.contains(&pt)) { new.insert(pt); }
        }
        self.map = new;
    }

    #[inline]
    pub fn count(&self) -> usize { self.map.len() }
}

fn main() {
    let matches = App::new("Advent of code 2020, Day 17 Solution")
        .arg(Arg::with_name("FILE").help("Input file to process").index(1))
        .get_matches();
    let fname = matches.value_of("FILE").unwrap_or("17.in");
    let contents = std::fs::read_to_string(fname)
        .unwrap_or_else(|err| panic!("Error reading {}: {}", fname, err));

    let mut life = ConwayLife3D::new();
    life.load(&contents);
    for _ in 0..6 { life.step(); }
    println!("Part 1: after 6 cycles, {} cubes are active", life.count());

    let mut life = ConwayLife4D::new();
    life.load(&contents);
    for _ in 0..6 { life.step(); }
    println!("Part 2: after 6 cycles, {} cubes are active", life.count());
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate env_logger;
extern crate docopt;
extern crate libc;
extern crate rand;
extern crate rustc_serialize;

#[macro_use]
extern crate error_chain;

use docopt::Docopt;
use rand::{Rng, SeedableRng, XorShiftRng};
use rustc_serialize::{Decodable, Decoder};
use std::mem;
use std::slice;

mod area;
mod c;
mod flash;
pub mod api;
mod pdump;

use flash::Flash;
use area::{AreaDesc, FlashId};

const USAGE: &'static str = "
Mcuboot simulator

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

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

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

#[derive(Debug, RustcDecodable)]
enum DeviceName { Stm32f4, K64f }

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

impl Decodable for AlignArg {
    // Decode the alignment ourselves, to restrict it to the valid possible alignments.
    fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
        let m = d.read_u8()?;
        match m {
            1 | 2 | 4 | 8 => Ok(AlignArg(m)),
            _ => Err(d.error("Invalid alignment")),
        }
    }
}

fn main() {
    env_logger::init().unwrap();

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

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

    let (mut flash, areadesc) = match args.flag_device {
        None => panic!("Missing mandatory argument"),
        Some(DeviceName::Stm32f4) => {
            // STM style flash.  Large sectors, with a large scratch area.
            let flash = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
                                   64 * 1024,
                                   128 * 1024, 128 * 1024, 128 * 1024]);
            let mut areadesc = AreaDesc::new(&flash);
            areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
            areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
            (flash, areadesc)
        }
        Some(DeviceName::K64f) => {
            // NXP style flash.  Small sectors, one small sector for scratch.
            let flash = Flash::new(vec![4096; 128]);

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

    // println!("Areas: {:#?}", areadesc.get_c());

    // Install the boot trailer signature, so that the code will start an upgrade.
    let primary = install_image(&mut flash, 0x020000, 32779);

    // Install an upgrade image.
    let upgrade = install_image(&mut flash, 0x040000, 41922);

    // Set an alignment, and position the magic value.
    c::set_sim_flash_align(args.flag_align.map(|x| x.0).unwrap_or(1));
    let trailer_size = c::boot_trailer_sz();

    // Mark the upgrade as ready to install.  (This looks like it might be a bug in the code,
    // however.)
    mark_upgrade(&mut flash, 0x060000 - trailer_size as usize);

    let (fl2, total_count) = try_upgrade(&flash, &areadesc, None);
    info!("First boot, count={}", total_count);
    assert!(verify_image(&fl2, 0x020000, &upgrade));

    let mut bad = 0;
    // Let's try an image halfway through.
    for i in 1 .. total_count {
        info!("Try interruption at {}", i);
        let (fl3, total_count) = try_upgrade(&flash, &areadesc, Some(i));
        info!("Second boot, count={}", total_count);
        if !verify_image(&fl3, 0x020000, &upgrade) {
            warn!("FAIL at step {} of {}", i, total_count);
            bad += 1;
        }
        if !verify_image(&fl3, 0x040000, &primary) {
            warn!("Slot 1 FAIL at step {} of {}", i, total_count);
            bad += 1;
        }
    }
    error!("{} out of {} failed {:.2}%",
           bad, total_count,
           bad as f32 * 100.0 / total_count as f32);

    info!("Try revert");
    let fl2 = try_revert(&flash, &areadesc);
    assert!(verify_image(&fl2, 0x020000, &primary));

    info!("Try norevert");
    let fl2 = try_norevert(&flash, &areadesc);
    assert!(verify_image(&fl2, 0x020000, &upgrade));

    /*
    // show_flash(&flash);

    println!("First boot for upgrade");
    // c::set_flash_counter(570);
    c::boot_go(&mut flash, &areadesc);
    // println!("{} flash ops", c::get_flash_counter());

    verify_image(&flash, 0x020000, &upgrade);

    println!("\n------------------\nSecond boot");
    c::boot_go(&mut flash, &areadesc);
    */
}

/// Test a boot, optionally stopping after 'n' flash options.  Returns a count of the number of
/// flash operations done total.
fn try_upgrade(flash: &Flash, areadesc: &AreaDesc, stop: Option<i32>) -> (Flash, i32) {
    // Clone the flash to have a new copy.
    let mut fl = flash.clone();

    c::set_flash_counter(stop.unwrap_or(0));
    let (first_interrupted, cnt1) = match c::boot_go(&mut fl, &areadesc) {
        -0x13579 => (true, stop.unwrap()),
        0 => (false, -c::get_flash_counter()),
        x => panic!("Unknown return: {}", x),
    };
    c::set_flash_counter(0);

    if first_interrupted {
        // fl.dump();
        match c::boot_go(&mut fl, &areadesc) {
            -0x13579 => panic!("Shouldn't stop again"),
            0 => (),
            x => panic!("Unknown return: {}", x),
        }
    }

    let cnt2 = cnt1 - c::get_flash_counter();

    (fl, cnt2)
}

fn try_revert(flash: &Flash, areadesc: &AreaDesc) -> Flash {
    let mut fl = flash.clone();
    c::set_flash_counter(0);

    assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    fl
}

fn try_norevert(flash: &Flash, areadesc: &AreaDesc) -> Flash {
    let mut fl = flash.clone();
    c::set_flash_counter(0);
    let align = c::get_sim_flash_align() as usize;

    assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    // Write boot_ok
    fl.write(0x040000 - align, &[1]).unwrap();
    assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    fl
}

/// Show the flash layout.
#[allow(dead_code)]
fn show_flash(flash: &Flash) {
    println!("---- Flash configuration ----");
    for sector in flash.sector_iter() {
        println!("    {:2}: 0x{:08x}, 0x{:08x}",
                 sector.num, sector.base, sector.size);
    }
    println!("");
}

/// Install a "program" into the given image.  This fakes the image header, or at least all of the
/// fields used by the given code.  Returns a copy of the image that was written.
fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
    let offset0 = offset;

    // Generate a boot header.  Note that the size doesn't include the header.
    let header = ImageHeader {
        magic: 0x96f3b83c,
        tlv_size: 0,
        _pad1: 0,
        hdr_size: 32,
        key_id: 0,
        _pad2: 0,
        img_size: len as u32,
        flags: 0,
        ver: ImageVersion {
            major: 1,
            minor: 0,
            revision: 1,
            build_num: 1,
        },
        _pad3: 0,
    };

    let b_header = header.as_raw();
    /*
    let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
                                                  mem::size_of::<ImageHeader>()) };
                                                  */
    assert_eq!(b_header.len(), 32);
    flash.write(offset, &b_header).unwrap();
    let offset = offset + b_header.len();

    // The core of the image itself is just pseudorandom data.
    let mut buf = vec![0; len];
    splat(&mut buf, offset);
    flash.write(offset, &buf).unwrap();
    let offset = offset + buf.len();

    // Copy out the image so that we can verify that the image was installed correctly later.
    let mut copy = vec![0u8; offset - offset0];
    flash.read(offset0, &mut copy).unwrap();

    copy
}

/// Verify that given image is present in the flash at the given offset.
fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
    let mut copy = vec![0u8; buf.len()];
    flash.read(offset, &mut copy).unwrap();

    if buf != &copy[..] {
        for i in 0 .. buf.len() {
            if buf[i] != copy[i] {
                info!("First failure at {:#x}", offset + i);
                break;
            }
        }
        false
    } else {
        true
    }
}

/// The image header
#[repr(C)]
pub struct ImageHeader {
    magic: u32,
    tlv_size: u16,
    key_id: u8,
    _pad1: u8,
    hdr_size: u16,
    _pad2: u16,
    img_size: u32,
    flags: u32,
    ver: ImageVersion,
    _pad3: u32,
}

impl AsRaw for ImageHeader {}

#[repr(C)]
pub struct ImageVersion {
    major: u8,
    minor: u8,
    revision: u16,
    build_num: u32,
}

/// Write out the magic so that the loader tries doing an upgrade.
fn mark_upgrade(flash: &mut Flash, offset: usize) {
    let magic = vec![0x77, 0xc2, 0x95, 0xf3,
                     0x60, 0xd2, 0xef, 0x7f,
                     0x35, 0x52, 0x50, 0x0f,
                     0x2c, 0xb6, 0x79, 0x80];
    flash.write(offset, &magic).unwrap();
}

// Drop some pseudo-random gibberish onto the data.
fn splat(data: &mut [u8], seed: usize) {
    let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
    let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
    rng.fill_bytes(data);
}

/// Return a read-only view into the raw bytes of this object
trait AsRaw : Sized {
    fn as_raw<'a>(&'a self) -> &'a [u8] {
        unsafe { slice::from_raw_parts(self as *const _ as *const u8,
                                       mem::size_of::<Self>()) }
    }
}

fn show_sizes() {
    // This isn't panic safe.
    let old_align = c::get_sim_flash_align();
    for min in &[1, 2, 4, 8] {
        c::set_sim_flash_align(*min);
        let msize = c::boot_trailer_sz();
        println!("{:2}: {} (0x{:x})", min, msize, msize);
    }
    c::set_sim_flash_align(old_align);
}
<commit_msg>sim: Test multiple reverts<commit_after>#[macro_use] extern crate log;
extern crate env_logger;
extern crate docopt;
extern crate libc;
extern crate rand;
extern crate rustc_serialize;

#[macro_use]
extern crate error_chain;

use docopt::Docopt;
use rand::{Rng, SeedableRng, XorShiftRng};
use rustc_serialize::{Decodable, Decoder};
use std::mem;
use std::slice;

mod area;
mod c;
mod flash;
pub mod api;
mod pdump;

use flash::Flash;
use area::{AreaDesc, FlashId};

const USAGE: &'static str = "
Mcuboot simulator

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

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

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

#[derive(Debug, RustcDecodable)]
enum DeviceName { Stm32f4, K64f }

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

impl Decodable for AlignArg {
    // Decode the alignment ourselves, to restrict it to the valid possible alignments.
    fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
        let m = d.read_u8()?;
        match m {
            1 | 2 | 4 | 8 => Ok(AlignArg(m)),
            _ => Err(d.error("Invalid alignment")),
        }
    }
}

fn main() {
    env_logger::init().unwrap();

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

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

    let (mut flash, areadesc) = match args.flag_device {
        None => panic!("Missing mandatory argument"),
        Some(DeviceName::Stm32f4) => {
            // STM style flash.  Large sectors, with a large scratch area.
            let flash = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
                                   64 * 1024,
                                   128 * 1024, 128 * 1024, 128 * 1024]);
            let mut areadesc = AreaDesc::new(&flash);
            areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
            areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
            (flash, areadesc)
        }
        Some(DeviceName::K64f) => {
            // NXP style flash.  Small sectors, one small sector for scratch.
            let flash = Flash::new(vec![4096; 128]);

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

    // println!("Areas: {:#?}", areadesc.get_c());

    // Install the boot trailer signature, so that the code will start an upgrade.
    let primary = install_image(&mut flash, 0x020000, 32779);

    // Install an upgrade image.
    let upgrade = install_image(&mut flash, 0x040000, 41922);

    // Set an alignment, and position the magic value.
    c::set_sim_flash_align(args.flag_align.map(|x| x.0).unwrap_or(1));
    let trailer_size = c::boot_trailer_sz();

    // Mark the upgrade as ready to install.  (This looks like it might be a bug in the code,
    // however.)
    mark_upgrade(&mut flash, 0x060000 - trailer_size as usize);

    let (fl2, total_count) = try_upgrade(&flash, &areadesc, None);
    info!("First boot, count={}", total_count);
    assert!(verify_image(&fl2, 0x020000, &upgrade));

    let mut bad = 0;
    // Let's try an image halfway through.
    for i in 1 .. total_count {
        info!("Try interruption at {}", i);
        let (fl3, total_count) = try_upgrade(&flash, &areadesc, Some(i));
        info!("Second boot, count={}", total_count);
        if !verify_image(&fl3, 0x020000, &upgrade) {
            warn!("FAIL at step {} of {}", i, total_count);
            bad += 1;
        }
        if !verify_image(&fl3, 0x040000, &primary) {
            warn!("Slot 1 FAIL at step {} of {}", i, total_count);
            bad += 1;
        }
    }
    error!("{} out of {} failed {:.2}%",
           bad, total_count,
           bad as f32 * 100.0 / total_count as f32);

    for count in 2 .. 5 {
        info!("Try revert: {}", count);
        let fl2 = try_revert(&flash, &areadesc, count);
        assert!(verify_image(&fl2, 0x020000, &primary));
    }

    info!("Try norevert");
    let fl2 = try_norevert(&flash, &areadesc);
    assert!(verify_image(&fl2, 0x020000, &upgrade));

    /*
    // show_flash(&flash);

    println!("First boot for upgrade");
    // c::set_flash_counter(570);
    c::boot_go(&mut flash, &areadesc);
    // println!("{} flash ops", c::get_flash_counter());

    verify_image(&flash, 0x020000, &upgrade);

    println!("\n------------------\nSecond boot");
    c::boot_go(&mut flash, &areadesc);
    */
}

/// Test a boot, optionally stopping after 'n' flash options.  Returns a count of the number of
/// flash operations done total.
fn try_upgrade(flash: &Flash, areadesc: &AreaDesc, stop: Option<i32>) -> (Flash, i32) {
    // Clone the flash to have a new copy.
    let mut fl = flash.clone();

    c::set_flash_counter(stop.unwrap_or(0));
    let (first_interrupted, cnt1) = match c::boot_go(&mut fl, &areadesc) {
        -0x13579 => (true, stop.unwrap()),
        0 => (false, -c::get_flash_counter()),
        x => panic!("Unknown return: {}", x),
    };
    c::set_flash_counter(0);

    if first_interrupted {
        // fl.dump();
        match c::boot_go(&mut fl, &areadesc) {
            -0x13579 => panic!("Shouldn't stop again"),
            0 => (),
            x => panic!("Unknown return: {}", x),
        }
    }

    let cnt2 = cnt1 - c::get_flash_counter();

    (fl, cnt2)
}

fn try_revert(flash: &Flash, areadesc: &AreaDesc, count: usize) -> Flash {
    let mut fl = flash.clone();
    c::set_flash_counter(0);

    for _ in 0 .. count {
        assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    }
    fl
}

fn try_norevert(flash: &Flash, areadesc: &AreaDesc) -> Flash {
    let mut fl = flash.clone();
    c::set_flash_counter(0);
    let align = c::get_sim_flash_align() as usize;

    assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    // Write boot_ok
    fl.write(0x040000 - align, &[1]).unwrap();
    assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
    fl
}

/// Show the flash layout.
#[allow(dead_code)]
fn show_flash(flash: &Flash) {
    println!("---- Flash configuration ----");
    for sector in flash.sector_iter() {
        println!("    {:2}: 0x{:08x}, 0x{:08x}",
                 sector.num, sector.base, sector.size);
    }
    println!("");
}

/// Install a "program" into the given image.  This fakes the image header, or at least all of the
/// fields used by the given code.  Returns a copy of the image that was written.
fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
    let offset0 = offset;

    // Generate a boot header.  Note that the size doesn't include the header.
    let header = ImageHeader {
        magic: 0x96f3b83c,
        tlv_size: 0,
        _pad1: 0,
        hdr_size: 32,
        key_id: 0,
        _pad2: 0,
        img_size: len as u32,
        flags: 0,
        ver: ImageVersion {
            major: 1,
            minor: 0,
            revision: 1,
            build_num: 1,
        },
        _pad3: 0,
    };

    let b_header = header.as_raw();
    /*
    let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
                                                  mem::size_of::<ImageHeader>()) };
                                                  */
    assert_eq!(b_header.len(), 32);
    flash.write(offset, &b_header).unwrap();
    let offset = offset + b_header.len();

    // The core of the image itself is just pseudorandom data.
    let mut buf = vec![0; len];
    splat(&mut buf, offset);
    flash.write(offset, &buf).unwrap();
    let offset = offset + buf.len();

    // Copy out the image so that we can verify that the image was installed correctly later.
    let mut copy = vec![0u8; offset - offset0];
    flash.read(offset0, &mut copy).unwrap();

    copy
}

/// Verify that given image is present in the flash at the given offset.
fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
    let mut copy = vec![0u8; buf.len()];
    flash.read(offset, &mut copy).unwrap();

    if buf != &copy[..] {
        for i in 0 .. buf.len() {
            if buf[i] != copy[i] {
                info!("First failure at {:#x}", offset + i);
                break;
            }
        }
        false
    } else {
        true
    }
}

/// The image header
#[repr(C)]
pub struct ImageHeader {
    magic: u32,
    tlv_size: u16,
    key_id: u8,
    _pad1: u8,
    hdr_size: u16,
    _pad2: u16,
    img_size: u32,
    flags: u32,
    ver: ImageVersion,
    _pad3: u32,
}

impl AsRaw for ImageHeader {}

#[repr(C)]
pub struct ImageVersion {
    major: u8,
    minor: u8,
    revision: u16,
    build_num: u32,
}

/// Write out the magic so that the loader tries doing an upgrade.
fn mark_upgrade(flash: &mut Flash, offset: usize) {
    let magic = vec![0x77, 0xc2, 0x95, 0xf3,
                     0x60, 0xd2, 0xef, 0x7f,
                     0x35, 0x52, 0x50, 0x0f,
                     0x2c, 0xb6, 0x79, 0x80];
    flash.write(offset, &magic).unwrap();
}

// Drop some pseudo-random gibberish onto the data.
fn splat(data: &mut [u8], seed: usize) {
    let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
    let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
    rng.fill_bytes(data);
}

/// Return a read-only view into the raw bytes of this object
trait AsRaw : Sized {
    fn as_raw<'a>(&'a self) -> &'a [u8] {
        unsafe { slice::from_raw_parts(self as *const _ as *const u8,
                                       mem::size_of::<Self>()) }
    }
}

fn show_sizes() {
    // This isn't panic safe.
    let old_align = c::get_sim_flash_align();
    for min in &[1, 2, 4, 8] {
        c::set_sim_flash_align(*min);
        let msize = c::boot_trailer_sz();
        println!("{:2}: {} (0x{:x})", min, msize, msize);
    }
    c::set_sim_flash_align(old_align);
}
<|endoftext|>


<commit_before>//! Types and traits for working with the Matrix protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//! Depending on which parts of Matrix are relevant to you, activate the
//! following features:
//!
//! * `client-api` for the client-server API
//! * `federation-api` for the server-server (federation) API
//! * `appservice-api` for the application service API

#![deny(missing_docs)]

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};

pub use ruma_common::*;
#[doc(inline)]
pub use ruma_identifiers as identifiers;
#[doc(inline)]
pub use ruma_serde as serde;

pub use ruma_serde::Outgoing;

#[allow(deprecated)] // Allow re-export of deprecated items
pub use ruma_identifiers::{
    device_id, device_key_id, event_id, room_alias_id, room_id, room_version_id, server_key_id,
    server_name, server_signing_key_id, user_id, DeviceId, DeviceKeyAlgorithm, DeviceKeyId,
    EventId, RoomAliasId, RoomId, RoomIdOrAliasId, RoomVersionId, ServerName, ServerSigningKeyId,
    SigningKeyAlgorithm, UserId,
};

#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_events as events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;

/// Rust types for various Matrix APIs requests and responses and abstractions for them.
#[cfg(feature = "api")]
#[doc(inline)]
pub mod api {
    pub use ruma_api::*;

    #[cfg(feature = "appservice-api")]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "client-api")]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "federation-api")]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "identity-service-api")]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "push-gateway-api")]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}
<commit_msg>Remove useless #[doc(inline)]<commit_after>//! Types and traits for working with the Matrix protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//! Depending on which parts of Matrix are relevant to you, activate the
//! following features:
//!
//! * `client-api` for the client-server API
//! * `federation-api` for the server-server (federation) API
//! * `appservice-api` for the application service API

#![deny(missing_docs)]

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};

pub use ruma_common::*;
#[doc(inline)]
pub use ruma_identifiers as identifiers;
#[doc(inline)]
pub use ruma_serde as serde;

pub use ruma_serde::Outgoing;

#[allow(deprecated)] // Allow re-export of deprecated items
pub use ruma_identifiers::{
    device_id, device_key_id, event_id, room_alias_id, room_id, room_version_id, server_key_id,
    server_name, server_signing_key_id, user_id, DeviceId, DeviceKeyAlgorithm, DeviceKeyId,
    EventId, RoomAliasId, RoomId, RoomIdOrAliasId, RoomVersionId, ServerName, ServerSigningKeyId,
    SigningKeyAlgorithm, UserId,
};

#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_events as events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;

/// Rust types for various Matrix APIs requests and responses and abstractions for them.
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_api::*;

    #[cfg(feature = "appservice-api")]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "client-api")]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "federation-api")]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "identity-service-api")]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "push-gateway-api")]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}
<|endoftext|>