<commit_before>//! Formatting for the time crate.

use core::fmt;

use crate::format_description::{modifier, Component};
use crate::{error, Date, Time, UtcOffset};

#[allow(clippy::clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&str; 12] = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
];

#[allow(clippy::missing_docs_in_private_items)]
const WEEKDAY_NAMES: [&str; 7] = [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
];

/// A trait that indicates the formatted width of the value can be determined.
///
/// Note that this should not be implemented for any signed integers. This forces the caller to
/// write the sign if desired.
trait DigitCount {
    /// The number of digits in the stringified value.
    fn num_digits(self) -> u8;
}
impl DigitCount for u8 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else {
            3
        }
    }
}
impl DigitCount for u16 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else {
            5
        }
    }
}
impl DigitCount for u32 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else if self < 100_000 {
            5
        } else if self < 1_000_000 {
            6
        } else if self < 10_000_000 {
            7
        } else if self < 100_000_000 {
            8
        } else if self < 1_000_000_000 {
            9
        } else {
            10
        }
    }
}

/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
fn format_number(
    output: &mut impl fmt::Write,
    value: impl itoa::Integer + DigitCount + Copy,
    padding: modifier::Padding,
    width: u8,
) -> Result<(), fmt::Error> {
    match padding {
        modifier::Padding::Space => {
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                output.write_char(' ')?;
            }
            itoa::fmt(output, value)
        }
        modifier::Padding::Zero => {
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                output.write_char('0')?;
            }
            itoa::fmt(output, value)
        }
        modifier::Padding::None => itoa::fmt(output, value),
    }
}

/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub(crate) fn format_component(
    output: &mut impl fmt::Write,
    component: Component,
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<UtcOffset>,
) -> Result<(), error::Format> {
    match (component, date, time, offset) {
        (Component::Day(modifier::Day { padding }), Some(date), ..) => {
            format_number(output, date.day(), padding, 2)?
        }
        (Component::Month(modifier::Month { padding, repr }), Some(date), ..) => match repr {
            modifier::MonthRepr::Numerical => format_number(output, date.month(), padding, 2)?,
            modifier::MonthRepr::Long => {
                output.write_str(MONTH_NAMES[date.month() as usize - 1])?
            }
            modifier::MonthRepr::Short => {
                output.write_str(&MONTH_NAMES[date.month() as usize - 1][..3])?
            }
        },
        (Component::Ordinal(modifier::Ordinal { padding }), Some(date), ..) => {
            format_number(output, date.ordinal(), padding, 3)?
        }
        (Component::Weekday(modifier::Weekday { repr, one_indexed }), Some(date), ..) => match repr
        {
            modifier::WeekdayRepr::Short => output.write_str(
                &WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
            )?,
            modifier::WeekdayRepr::Long => output
                .write_str(WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize])?,
            modifier::WeekdayRepr::Sunday => format_number(
                output,
                date.weekday().number_days_from_sunday() + one_indexed as u8,
                modifier::Padding::None,
                1,
            )?,
            modifier::WeekdayRepr::Monday => format_number(
                output,
                date.weekday().number_days_from_monday() + one_indexed as u8,
                modifier::Padding::None,
                1,
            )?,
        },
        (Component::WeekNumber(modifier::WeekNumber { padding, repr }), Some(date), ..) => {
            format_number(
                output,
                match repr {
                    modifier::WeekNumberRepr::Iso => date.iso_week(),
                    modifier::WeekNumberRepr::Sunday => date.sunday_based_week(),
                    modifier::WeekNumberRepr::Monday => date.monday_based_week(),
                },
                padding,
                2,
            )?
        }
        (
            Component::Year(modifier::Year {
                padding,
                repr,
                iso_week_based,
                sign_is_mandatory,
            }),
            Some(date),
            ..,
        ) => {
            let full_year = if iso_week_based {
                date.iso_year_week().0
            } else {
                date.year()
            };

            let value = match repr {
                modifier::YearRepr::Full => full_year,
                modifier::YearRepr::LastTwo => (full_year % 100).abs(),
            };

            let width = match repr {
                modifier::YearRepr::Full if value.abs() >= 100_000 => 6,
                modifier::YearRepr::Full if value.abs() >= 10_000 => 5,
                modifier::YearRepr::Full => 4,
                modifier::YearRepr::LastTwo => 2,
            };

            // Don't emit a sign when only displaying the last two digits.
            if repr != modifier::YearRepr::LastTwo {
                if full_year < 0 {
                    output.write_char('-')?;
                } else if sign_is_mandatory || full_year >= 10_000 {
                    output.write_char('+')?;
                }
            }

            format_number(output, value.abs() as u32, padding, width)?
        }
        (
            Component::Hour(modifier::Hour {
                padding,
                is_12_hour_clock,
            }),
            _,
            Some(time),
            _,
        ) => {
            let value = match (time.hour, is_12_hour_clock) {
                (hour, false) => hour,
                (0, true) | (12, true) => 12,
                (hour, true) if hour < 12 => hour,
                (hour, true) => hour - 12,
            };
            format_number(output, value, padding, 2)?
        }
        (Component::Minute(modifier::Minute { padding }), _, Some(time), _) => {
            format_number(output, time.minute, padding, 2)?
        }
        (Component::Period(modifier::Period { is_uppercase }), _, Some(time), _) => {
            match (time.hour >= 12, is_uppercase) {
                (false, false) => output.write_str("am"),
                (false, true) => output.write_str("AM"),
                (true, false) => output.write_str("pm"),
                (true, true) => output.write_str("PM"),
            }?
        }
        (Component::Second(modifier::Second { padding }), _, Some(time), _) => {
            format_number(output, time.second, padding, 2)?
        }
        (Component::Subsecond(modifier::Subsecond { digits }), _, Some(time), _) => {
            let (value, width) = match digits {
                modifier::SubsecondDigits::One => (time.nanosecond / 100_000_000, 1),
                modifier::SubsecondDigits::Two => (time.nanosecond / 10_000_000, 2),
                modifier::SubsecondDigits::Three => (time.nanosecond / 1_000_000, 3),
                modifier::SubsecondDigits::Four => (time.nanosecond / 100_000, 4),
                modifier::SubsecondDigits::Five => (time.nanosecond / 10_000, 5),
                modifier::SubsecondDigits::Six => (time.nanosecond / 1_000, 6),
                modifier::SubsecondDigits::Seven => (time.nanosecond / 100, 7),
                modifier::SubsecondDigits::Eight => (time.nanosecond / 10, 8),
                modifier::SubsecondDigits::Nine => (time.nanosecond, 9),
                modifier::SubsecondDigits::OneOrMore => match time.nanosecond {
                    nanos if nanos % 10 != 0 => (nanos, 9),
                    nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
                    nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
                    nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
                    nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
                    nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
                    nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
                    nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
                    nanos => (nanos / 100_000_000, 1),
                },
            };
            format_number(output, value, modifier::Padding::Zero, width)?
        }
        (
            Component::OffsetHour(modifier::OffsetHour {
                padding,
                sign_is_mandatory,
            }),
            _,
            _,
            Some(offset),
        ) => {
            if offset.hours < 0 || offset.minutes < 0 || offset.seconds < 0 {
                output.write_char('-')?;
            } else if sign_is_mandatory {
                output.write_char('+')?;
            }
            format_number(output, offset.hours.abs() as u8, padding, 2)?;
        }
        (Component::OffsetMinute(modifier::OffsetMinute { padding }), _, _, Some(offset)) => {
            format_number(output, offset.minutes.abs() as u8, padding, 2)?;
        }
        (Component::OffsetSecond(modifier::OffsetSecond { padding }), _, _, Some(offset)) => {
            format_number(output, offset.seconds.abs() as u8, padding, 2)?;
        }
        _ => return Err(error::Format::InsufficientTypeInformation),
    }

    Ok(())
}
<commit_msg>Gate unreachable behavior for large dates<commit_after>//! Formatting for the time crate.

use core::fmt;

use crate::format_description::{modifier, Component};
use crate::{error, Date, Time, UtcOffset};

#[allow(clippy::clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&str; 12] = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
];

#[allow(clippy::missing_docs_in_private_items)]
const WEEKDAY_NAMES: [&str; 7] = [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
];

/// A trait that indicates the formatted width of the value can be determined.
///
/// Note that this should not be implemented for any signed integers. This forces the caller to
/// write the sign if desired.
trait DigitCount {
    /// The number of digits in the stringified value.
    fn num_digits(self) -> u8;
}
impl DigitCount for u8 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else {
            3
        }
    }
}
impl DigitCount for u16 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else {
            5
        }
    }
}
impl DigitCount for u32 {
    fn num_digits(self) -> u8 {
        if self < 10 {
            1
        } else if self < 100 {
            2
        } else if self < 1_000 {
            3
        } else if self < 10_000 {
            4
        } else if self < 100_000 {
            5
        } else if self < 1_000_000 {
            6
        } else if self < 10_000_000 {
            7
        } else if self < 100_000_000 {
            8
        } else if self < 1_000_000_000 {
            9
        } else {
            10
        }
    }
}

/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
fn format_number(
    output: &mut impl fmt::Write,
    value: impl itoa::Integer + DigitCount + Copy,
    padding: modifier::Padding,
    width: u8,
) -> Result<(), fmt::Error> {
    match padding {
        modifier::Padding::Space => {
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                output.write_char(' ')?;
            }
            itoa::fmt(output, value)
        }
        modifier::Padding::Zero => {
            for _ in 0..(width.saturating_sub(value.num_digits())) {
                output.write_char('0')?;
            }
            itoa::fmt(output, value)
        }
        modifier::Padding::None => itoa::fmt(output, value),
    }
}

/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub(crate) fn format_component(
    output: &mut impl fmt::Write,
    component: Component,
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<UtcOffset>,
) -> Result<(), error::Format> {
    match (component, date, time, offset) {
        (Component::Day(modifier::Day { padding }), Some(date), ..) => {
            format_number(output, date.day(), padding, 2)?
        }
        (Component::Month(modifier::Month { padding, repr }), Some(date), ..) => match repr {
            modifier::MonthRepr::Numerical => format_number(output, date.month(), padding, 2)?,
            modifier::MonthRepr::Long => {
                output.write_str(MONTH_NAMES[date.month() as usize - 1])?
            }
            modifier::MonthRepr::Short => {
                output.write_str(&MONTH_NAMES[date.month() as usize - 1][..3])?
            }
        },
        (Component::Ordinal(modifier::Ordinal { padding }), Some(date), ..) => {
            format_number(output, date.ordinal(), padding, 3)?
        }
        (Component::Weekday(modifier::Weekday { repr, one_indexed }), Some(date), ..) => match repr
        {
            modifier::WeekdayRepr::Short => output.write_str(
                &WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
            )?,
            modifier::WeekdayRepr::Long => output
                .write_str(WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize])?,
            modifier::WeekdayRepr::Sunday => format_number(
                output,
                date.weekday().number_days_from_sunday() + one_indexed as u8,
                modifier::Padding::None,
                1,
            )?,
            modifier::WeekdayRepr::Monday => format_number(
                output,
                date.weekday().number_days_from_monday() + one_indexed as u8,
                modifier::Padding::None,
                1,
            )?,
        },
        (Component::WeekNumber(modifier::WeekNumber { padding, repr }), Some(date), ..) => {
            format_number(
                output,
                match repr {
                    modifier::WeekNumberRepr::Iso => date.iso_week(),
                    modifier::WeekNumberRepr::Sunday => date.sunday_based_week(),
                    modifier::WeekNumberRepr::Monday => date.monday_based_week(),
                },
                padding,
                2,
            )?
        }
        (
            Component::Year(modifier::Year {
                padding,
                repr,
                iso_week_based,
                sign_is_mandatory,
            }),
            Some(date),
            ..,
        ) => {
            let full_year = if iso_week_based {
                date.iso_year_week().0
            } else {
                date.year()
            };

            let value = match repr {
                modifier::YearRepr::Full => full_year,
                modifier::YearRepr::LastTwo => (full_year % 100).abs(),
            };

            let width = match repr {
                #[cfg(feature = "large-dates")]
                modifier::YearRepr::Full if value.abs() >= 100_000 => 6,
                #[cfg(feature = "large-dates")]
                modifier::YearRepr::Full if value.abs() >= 10_000 => 5,
                modifier::YearRepr::Full => 4,
                modifier::YearRepr::LastTwo => 2,
            };

            // Don't emit a sign when only displaying the last two digits.
            if repr != modifier::YearRepr::LastTwo {
                if full_year < 0 {
                    output.write_char('-')?;
                } else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000
                {
                    output.write_char('+')?;
                }
            }

            format_number(output, value.abs() as u32, padding, width)?
        }
        (
            Component::Hour(modifier::Hour {
                padding,
                is_12_hour_clock,
            }),
            _,
            Some(time),
            _,
        ) => {
            let value = match (time.hour, is_12_hour_clock) {
                (hour, false) => hour,
                (0, true) | (12, true) => 12,
                (hour, true) if hour < 12 => hour,
                (hour, true) => hour - 12,
            };
            format_number(output, value, padding, 2)?
        }
        (Component::Minute(modifier::Minute { padding }), _, Some(time), _) => {
            format_number(output, time.minute, padding, 2)?
        }
        (Component::Period(modifier::Period { is_uppercase }), _, Some(time), _) => {
            match (time.hour >= 12, is_uppercase) {
                (false, false) => output.write_str("am"),
                (false, true) => output.write_str("AM"),
                (true, false) => output.write_str("pm"),
                (true, true) => output.write_str("PM"),
            }?
        }
        (Component::Second(modifier::Second { padding }), _, Some(time), _) => {
            format_number(output, time.second, padding, 2)?
        }
        (Component::Subsecond(modifier::Subsecond { digits }), _, Some(time), _) => {
            let (value, width) = match digits {
                modifier::SubsecondDigits::One => (time.nanosecond / 100_000_000, 1),
                modifier::SubsecondDigits::Two => (time.nanosecond / 10_000_000, 2),
                modifier::SubsecondDigits::Three => (time.nanosecond / 1_000_000, 3),
                modifier::SubsecondDigits::Four => (time.nanosecond / 100_000, 4),
                modifier::SubsecondDigits::Five => (time.nanosecond / 10_000, 5),
                modifier::SubsecondDigits::Six => (time.nanosecond / 1_000, 6),
                modifier::SubsecondDigits::Seven => (time.nanosecond / 100, 7),
                modifier::SubsecondDigits::Eight => (time.nanosecond / 10, 8),
                modifier::SubsecondDigits::Nine => (time.nanosecond, 9),
                modifier::SubsecondDigits::OneOrMore => match time.nanosecond {
                    nanos if nanos % 10 != 0 => (nanos, 9),
                    nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
                    nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
                    nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
                    nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
                    nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
                    nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
                    nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
                    nanos => (nanos / 100_000_000, 1),
                },
            };
            format_number(output, value, modifier::Padding::Zero, width)?
        }
        (
            Component::OffsetHour(modifier::OffsetHour {
                padding,
                sign_is_mandatory,
            }),
            _,
            _,
            Some(offset),
        ) => {
            if offset.hours < 0 || offset.minutes < 0 || offset.seconds < 0 {
                output.write_char('-')?;
            } else if sign_is_mandatory {
                output.write_char('+')?;
            }
            format_number(output, offset.hours.abs() as u8, padding, 2)?;
        }
        (Component::OffsetMinute(modifier::OffsetMinute { padding }), _, _, Some(offset)) => {
            format_number(output, offset.minutes.abs() as u8, padding, 2)?;
        }
        (Component::OffsetSecond(modifier::OffsetSecond { padding }), _, _, Some(offset)) => {
            format_number(output, offset.seconds.abs() as u8, padding, 2)?;
        }
        _ => return Err(error::Format::InsufficientTypeInformation),
    }

    Ok(())
}
<|endoftext|>


<commit_before>use scene::{Scene, BoxedScene, SceneCommand};
use opengl_graphics::GlGraphics;
use event::{RenderArgs, UpdateArgs};
use input::{Button, Key};
use graphics;
use graphics::Transformed;
use entity::{Entity, RcEntity};
use keyhandler::KeyHandler;
use std::cell::RefCell;

#[derive(Clone)]
pub enum CellState {
    Empty,
    Dirt,
    Stone,
    Wall
}

impl CellState {
    pub fn is_passable(&self) -> bool {
        match *self {
            CellState::Empty | CellState::Dirt => true,
            _ => false
        }
    }
}

struct World {
    cells: Vec<CellState>,
    entities: Vec<RcEntity>,
    player: RcEntity,
    width: u32,
    height: u32
}

pub struct Adjacents {
    pub top: Option<(CellState, Option<RcEntity>)>,
    pub left: Option<(CellState, Option<RcEntity>)>,
    pub bottom: Option<(CellState, Option<RcEntity>)>,
    pub right: Option<(CellState, Option<RcEntity>)>
}

impl World {
    pub fn new(width: u32, height: u32) -> World {
        let mut entities = vec![];
        let mut cells = vec![CellState::Dirt; (width * height) as usize];
        let player = Entity::new(1,1);
        let borrow = player.clone();
        entities.push(player);
        cells[1] = CellState::Empty;
        cells[width as usize] = CellState::Empty;
        cells[width as usize + 1] = CellState::Empty;
        cells[width as usize + 2] = CellState::Empty;
        cells[(width as usize * 2) + 1] = CellState::Stone;

        World {
            cells: cells,
            entities: entities,
            width: width,
            height: height,
            player: borrow
        }
    }

    pub fn render(&self, context: &graphics::Context, gl: &mut GlGraphics, tick: u64) {
        for entity in self.entities.iter() {
            entity.borrow().render(context, gl, tick);
        }
    }

    pub fn at_pos(&self, x: u32, y: u32) -> (CellState, Option<RcEntity>) {
        let index = x + (y * self.width);
        return (self.cells[index as usize].clone(), None);
    }

    pub fn adjacents(&self, x: u32, y: u32) -> Adjacents {
        let top = if y > 0 {
            Some(self.at_pos(x, y - 1))
        } else {
            None
        };

        let left = if x > 0 {
            Some(self.at_pos(x - 1, y))
        } else {
            None
        };

        let bottom = if y < self.height - 1 {
            Some(self.at_pos(x, y + 1))
        } else {
            None
        };

        let right = if x < self.width - 1 {
            Some(self.at_pos(x + 1, y))
        } else {
            None
        };

        Adjacents{top: top, left: left, bottom: bottom, right: right}
    }

    pub fn set_pos(&mut self, x: u32, y: u32, state: CellState) {
        let index = x + (y * self.width);
        self.cells[index as usize] = state;
    }
}

pub struct GameScene {
    quit: bool,
    world: World,
    keyhandler: KeyHandler,
    tick: u64,
    camera_pos: (f64, f64)
}

impl GameScene {
    pub fn new() -> BoxedScene {
        Box::new(GameScene {
            quit: false,
            world: World::new(10, 10),
            keyhandler: KeyHandler::new(),
            tick: 0,
            camera_pos: (0.0, 0.0)
        })
    }

    fn adjust_camera_position(&mut self) {
        let (old_x * 32.0, old_y * 32.0) = self.camera_pos;
        let player = self.world.player.borrow();
        self.camera_pos = ((player.x * 32)  as f64, (player.y * 32) as f64);
    }
}

impl Scene for GameScene {
    fn render(&self, gl: &mut GlGraphics, args: &RenderArgs) {
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
        const BROWN: [f32; 4] = [0.2, 0.2, 0.0, 1.0];
        const GREY: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
        const WHITE: [f32; 4] = [1.0; 4];
        const CELL_SIZE: u32 = 32;

        let (camera_x, camera_y) = self.camera_pos;

        let context = &graphics::Context::abs(args.width as f64, args.height as f64)
            .trans(((args.width / 2) - (CELL_SIZE / 2)) as f64, ((args.height / 2) - (CELL_SIZE / 2)) as f64) //Camera is in the centre of the screen
            .trans(-camera_x, -camera_y);

        gl.draw(args.viewport(), |_, gl| {
            graphics::clear(BLACK, gl);
            for (i, cell) in self.world.cells.iter().enumerate() {
                let x = (i as u32 % self.world.width) * CELL_SIZE;
                let y = (i as u32 / self.world.height) * CELL_SIZE;

                match *cell {
                    CellState::Dirt => {
                        graphics::rectangle(BROWN, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    CellState::Stone => {
                        graphics::rectangle(GREY, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    CellState::Wall => {
                        graphics::rectangle(WHITE, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    }
                    _ => ()
                }
            };
            self.world.render(context, gl, self.tick);
        });

    }

    fn think(&mut self, args: &UpdateArgs) -> Option<SceneCommand> {
        self.tick += (args.dt * 100000.0) as u64;
        self.keyhandler.think(self.tick);

        if self.tick / 1000 % 10 == 0 {
            {
                //FIXME: Make this use weak references once we have them
                let entity = self.world.player.clone();
                let mut entity = entity.borrow_mut();
                //Move existing
                let x = entity.x;
                let y = entity.y;
                entity.think(self.tick, &self.world.adjacents(x, y));

                let x = entity.x;
                let y = entity.y;
                self.world.set_pos(x, y, CellState::Empty);

                //Handle player input
                match self.keyhandler.last_key() {
                    Some((key, tick)) => {
                        let difference = self.tick - tick;
                        if difference < 8000 || difference > 20000 {
                            let x = entity.x;
                            let y = entity.y;
                            entity.input(key, &self.world.adjacents(x, y));
                        }
                    },
                    None => ()
                }
            }

            self.adjust_camera_position();
        }

        if self.quit {
            Some(SceneCommand::PopScene)
        } else {
            None
        }
    }

    fn press(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(key) => {
                self.keyhandler.press(key)
            },
            _ => ()
        };
        //self.quit = true;
    }

    fn release(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(key) => {
                self.keyhandler.release(key)
            },
            _ => ()
        };
    }
}
<commit_msg>Much more stable game loop<commit_after>use scene::{Scene, BoxedScene, SceneCommand};
use opengl_graphics::GlGraphics;
use event::{RenderArgs, UpdateArgs};
use input::{Button, Key};
use graphics;
use graphics::Transformed;
use entity::{Entity, RcEntity};
use keyhandler::KeyHandler;
use std::cell::RefCell;

#[derive(Clone)]
pub enum CellState {
    Empty,
    Dirt,
    Stone,
    Wall
}

impl CellState {
    pub fn is_passable(&self) -> bool {
        match *self {
            CellState::Empty | CellState::Dirt => true,
            _ => false
        }
    }
}

struct World {
    cells: Vec<CellState>,
    entities: Vec<RcEntity>,
    player: RcEntity,
    width: u32,
    height: u32
}

pub struct Adjacents {
    pub top: Option<(CellState, Option<RcEntity>)>,
    pub left: Option<(CellState, Option<RcEntity>)>,
    pub bottom: Option<(CellState, Option<RcEntity>)>,
    pub right: Option<(CellState, Option<RcEntity>)>
}

impl World {
    pub fn new(width: u32, height: u32) -> World {
        let mut entities = vec![];
        let mut cells = vec![CellState::Dirt; (width * height) as usize];
        let player = Entity::new(1,1);
        let borrow = player.clone();
        entities.push(player);
        cells[1] = CellState::Empty;
        cells[width as usize] = CellState::Empty;
        cells[width as usize + 1] = CellState::Empty;
        cells[width as usize + 2] = CellState::Empty;
        cells[(width as usize * 2) + 1] = CellState::Stone;

        World {
            cells: cells,
            entities: entities,
            width: width,
            height: height,
            player: borrow
        }
    }

    pub fn render(&self, context: &graphics::Context, gl: &mut GlGraphics, tick: u64) {
        for entity in self.entities.iter() {
            entity.borrow().render(context, gl, tick);
        }
    }

    pub fn at_pos(&self, x: u32, y: u32) -> (CellState, Option<RcEntity>) {
        let index = x + (y * self.width);
        return (self.cells[index as usize].clone(), None);
    }

    pub fn adjacents(&self, x: u32, y: u32) -> Adjacents {
        let top = if y > 0 {
            Some(self.at_pos(x, y - 1))
        } else {
            None
        };

        let left = if x > 0 {
            Some(self.at_pos(x - 1, y))
        } else {
            None
        };

        let bottom = if y < self.height - 1 {
            Some(self.at_pos(x, y + 1))
        } else {
            None
        };

        let right = if x < self.width - 1 {
            Some(self.at_pos(x + 1, y))
        } else {
            None
        };

        Adjacents{top: top, left: left, bottom: bottom, right: right}
    }

    pub fn set_pos(&mut self, x: u32, y: u32, state: CellState) {
        let index = x + (y * self.width);
        self.cells[index as usize] = state;
    }
}

pub struct GameScene {
    quit: bool,
    world: World,
    keyhandler: KeyHandler,
    tick: u64,
    next_think: u64,
    camera_pos: (f64, f64)
}

impl GameScene {
    pub fn new() -> BoxedScene {
        Box::new(GameScene {
            quit: false,
            world: World::new(10, 10),
            keyhandler: KeyHandler::new(),
            tick: 0,
            next_think: 0,
            camera_pos: (0.0, 0.0)
        })
    }

    fn adjust_camera_position(&mut self) {
        let (old_x, old_y) = self.camera_pos;
        let player = self.world.player.borrow();
        self.camera_pos = ((player.x * 32)  as f64, (player.y * 32) as f64);
    }
}

impl Scene for GameScene {
    fn render(&self, gl: &mut GlGraphics, args: &RenderArgs) {
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
        const BROWN: [f32; 4] = [0.2, 0.2, 0.0, 1.0];
        const GREY: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
        const WHITE: [f32; 4] = [1.0; 4];
        const CELL_SIZE: u32 = 32;

        let (camera_x, camera_y) = self.camera_pos;

        let context = &graphics::Context::abs(args.width as f64, args.height as f64)
            .trans(((args.width / 2) - (CELL_SIZE / 2)) as f64, ((args.height / 2) - (CELL_SIZE / 2)) as f64) //Camera is in the centre of the screen
            .trans(-camera_x, -camera_y);

        gl.draw(args.viewport(), |_, gl| {
            graphics::clear(BLACK, gl);
            for (i, cell) in self.world.cells.iter().enumerate() {
                let x = (i as u32 % self.world.width) * CELL_SIZE;
                let y = (i as u32 / self.world.height) * CELL_SIZE;

                match *cell {
                    CellState::Dirt => {
                        graphics::rectangle(BROWN, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    CellState::Stone => {
                        graphics::rectangle(GREY, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    },
                    CellState::Wall => {
                        graphics::rectangle(WHITE, graphics::rectangle::square(x as f64, y as f64, CELL_SIZE as f64), context.transform, gl);
                    }
                    _ => ()
                }
            };
            self.world.render(context, gl, self.tick);
        });

    }

    fn think(&mut self, args: &UpdateArgs) -> Option<SceneCommand> {
        self.tick += (args.dt * 100000.0) as u64;
        self.keyhandler.think(self.tick);

        if self.tick >= self.next_think {
            self.next_think += 10000;
            {
                //FIXME: Make this use weak references once we have them
                let entity = self.world.player.clone();
                let mut entity = entity.borrow_mut();
                //Move existing
                let x = entity.x;
                let y = entity.y;
                entity.think(self.tick, &self.world.adjacents(x, y));

                let x = entity.x;
                let y = entity.y;
                self.world.set_pos(x, y, CellState::Empty);

                //Handle player input
                match self.keyhandler.last_key() {
                    Some((key, tick)) => {
                        let difference = self.tick - tick;
                        if difference < 8000 || difference > 20000 {
                            let x = entity.x;
                            let y = entity.y;
                            entity.input(key, &self.world.adjacents(x, y));
                        }
                    },
                    None => ()
                }
            }

            self.adjust_camera_position();
        }

        if self.quit {
            Some(SceneCommand::PopScene)
        } else {
            None
        }
    }

    fn press(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(key) => {
                self.keyhandler.press(key)
            },
            _ => ()
        };
        //self.quit = true;
    }

    fn release(&mut self, button: &Button) {
        match button {
            &Button::Keyboard(key) => {
                self.keyhandler.release(key)
            },
            _ => ()
        };
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#![allow(dead_code)]

use libc::{self, c_int, c_ulong, c_void, ioctl, read};
use std::fs::{File, OpenOptions};
use std::io;
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
use std::time::Duration;

use gpio::{Error, Level, Result, Trigger};

#[cfg(target_env = "gnu")]
type IoctlLong = libc::c_ulong;
#[cfg(target_env = "musl")]
type IoctlLong = libc::c_long;

fn parse_retval(retval: c_int) -> Result<i32> {
    if retval == -1 {
        Err(Error::Io(io::Error::last_os_error()))
    } else {
        Ok(retval)
    }
}

const NRBITS: u8 = 8;
const TYPEBITS: u8 = 8;
const SIZEBITS: u8 = 14;
const DIRBITS: u8 = 2;

const NRSHIFT: u8 = 0;
const TYPESHIFT: u8 = (NRSHIFT + NRBITS);
const SIZESHIFT: u8 = (TYPESHIFT + TYPEBITS);
const DIRSHIFT: u8 = (SIZESHIFT + SIZEBITS);

const NR_GET_CHIP_INFO: IoctlLong = 0x01 << NRSHIFT;
const NR_GET_LINE_INFO: IoctlLong = 0x02 << NRSHIFT;
const NR_GET_LINE_HANDLE: IoctlLong = 0x03 << NRSHIFT;
const NR_GET_LINE_EVENT: IoctlLong = 0x04 << NRSHIFT;
const NR_GET_LINE_VALUES: IoctlLong = 0x08 << NRSHIFT;
const NR_SET_LINE_VALUES: IoctlLong = 0x09 << NRSHIFT;

const TYPE_GPIO: IoctlLong = (0xB4 as IoctlLong) << TYPESHIFT;

const SIZE_CHIP_INFO: IoctlLong = (size_of::<ChipInfo>() as IoctlLong) << SIZESHIFT;
const SIZE_LINE_INFO: IoctlLong = (size_of::<LineInfo>() as IoctlLong) << SIZESHIFT;
const SIZE_HANDLE_REQUEST: IoctlLong = (size_of::<HandleRequest>() as IoctlLong) << SIZESHIFT;
const SIZE_EVENT_REQUEST: IoctlLong = (size_of::<EventRequest>() as IoctlLong) << SIZESHIFT;
const SIZE_HANDLE_DATA: IoctlLong = (size_of::<HandleData>() as IoctlLong) << SIZESHIFT;

const DIR_NONE: c_ulong = 0;
const DIR_WRITE: IoctlLong = 1 << DIRSHIFT;
const DIR_READ: IoctlLong = 2 << DIRSHIFT;
const DIR_READ_WRITE: IoctlLong = DIR_READ | DIR_WRITE;

const REQ_GET_CHIP_INFO: IoctlLong = DIR_READ | TYPE_GPIO | NR_GET_CHIP_INFO | SIZE_CHIP_INFO;
const REQ_GET_LINE_INFO: IoctlLong = DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_INFO | SIZE_LINE_INFO;
const REQ_GET_LINE_HANDLE: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_HANDLE | SIZE_HANDLE_REQUEST;
const REQ_GET_LINE_EVENT: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_EVENT | SIZE_EVENT_REQUEST;
const REQ_GET_LINE_VALUES: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_VALUES | SIZE_HANDLE_DATA;
const REQ_SET_LINE_VALUES: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_SET_LINE_VALUES | SIZE_HANDLE_DATA;

const NAME_BUFSIZE: usize = 32;
const LABEL_BUFSIZE: usize = 32;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct ChipInfo {
    pub name: [u8; NAME_BUFSIZE],
    pub label: [u8; LABEL_BUFSIZE],
    pub lines: u32,
}

impl ChipInfo {
    pub fn new(cdev_fd: c_int) -> Result<ChipInfo> {
        let mut chip_info = ChipInfo {
            name: [0u8; NAME_BUFSIZE],
            label: [0u8; LABEL_BUFSIZE],
            lines: 0,
        };

        parse_retval(unsafe { ioctl(cdev_fd, REQ_GET_CHIP_INFO, &mut chip_info) })?;

        Ok(chip_info)
    }
}

const LINE_FLAG_KERNEL: u32 = 0x01;
const LINE_FLAG_IS_OUT: u32 = 0x02;
const LINE_FLAG_ACTIVE_LOW: u32 = 0x04;
const LINE_FLAG_OPEN_DRAIN: u32 = 0x08;
const LINE_FLAG_OPEN_SOURCE: u32 = 0x10;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct LineInfo {
    pub line_offset: u32,
    pub flags: u32,
    pub name: [u8; NAME_BUFSIZE],
    pub consumer: [u8; LABEL_BUFSIZE],
}

impl LineInfo {
    pub fn new() -> LineInfo {
        LineInfo {
            line_offset: 0,
            flags: 0,
            name: [0u8; NAME_BUFSIZE],
            consumer: [0u8; LABEL_BUFSIZE],
        }
    }
}

const HANDLES_MAX: usize = 64;
const HANDLE_FLAG_INPUT: u32 = 0x01;
const HANDLE_FLAG_OUTPUT: u32 = 0x02;
const HANDLE_FLAG_ACTIVE_LOW: u32 = 0x04;
const HANDLE_FLAG_OPEN_DRAIN: u32 = 0x08;
const HANDLE_FLAG_OPEN_SOURCE: u32 = 0x10;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct HandleRequest {
    pub line_offsets: [u32; HANDLES_MAX],
    pub flags: u32,
    pub default_values: [u8; HANDLES_MAX],
    pub consumer_label: [u8; LABEL_BUFSIZE],
    pub lines: u32,
    pub fd: c_int,
}

impl HandleRequest {
    pub fn new(cdev_fd: c_int, pins: &[u8]) -> Result<HandleRequest> {
        let mut handle_request = HandleRequest {
            line_offsets: [0u32; HANDLES_MAX],
            flags: 0,
            default_values: [0u8; HANDLES_MAX],
            consumer_label: [0u8; LABEL_BUFSIZE],
            lines: 0,
            fd: 0,
        };

        let pins: &[u8] = if pins.len() > HANDLES_MAX {
            handle_request.lines = HANDLES_MAX as u32;
            &pins[0..HANDLES_MAX]
        } else {
            handle_request.lines = pins.len() as u32;
            pins
        };

        for (idx, pin) in pins.iter().enumerate() {
            handle_request.line_offsets[idx] = u32::from(*pin);
        }

        parse_retval(unsafe { ioctl(cdev_fd, REQ_GET_LINE_HANDLE, &mut handle_request) })?;

        Ok(handle_request)
    }

    pub fn levels(&self) -> Result<HandleData> {
        let mut handle_data = HandleData::new();

        parse_retval(unsafe { ioctl(self.fd, REQ_GET_LINE_VALUES, &mut handle_data) })?;

        Ok(handle_data)
    }

    pub fn set_levels(&mut self, levels: &[Level]) -> Result<()> {
        let mut handle_data = HandleData::new();
        let levels: &[Level] = if levels.len() > HANDLES_MAX {
            &levels[0..HANDLES_MAX]
        } else {
            levels
        };

        for (idx, level) in levels.iter().enumerate() {
            handle_data.values[idx] = *level as u8;
        }

        parse_retval(unsafe { ioctl(self.fd, REQ_SET_LINE_VALUES, &mut handle_data) })?;

        Ok(())
    }
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct HandleData {
    pub values: [u8; HANDLES_MAX],
}

impl HandleData {
    pub fn new() -> HandleData {
        HandleData {
            values: [0u8; HANDLES_MAX],
        }
    }
}

const EVENT_FLAG_RISING_EDGE: u32 = 0x01;
const EVENT_FLAG_FALLING_EDGE: u32 = 0x02;
const EVENT_FLAG_BOTH_EDGES: u32 = EVENT_FLAG_RISING_EDGE | EVENT_FLAG_FALLING_EDGE;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct EventRequest {
    pub line_offset: u32,
    pub handle_flags: u32,
    pub event_flags: u32,
    pub consumer_label: [u8; LABEL_BUFSIZE],
    pub fd: c_int,
}

impl EventRequest {
    pub fn new(cdev_fd: c_int, pin: u8, trigger: Trigger) -> Result<EventRequest> {
        let mut event_request = EventRequest {
            line_offset: u32::from(pin),
            handle_flags: HANDLE_FLAG_INPUT,
            event_flags: trigger as u32,
            consumer_label: [0u8; LABEL_BUFSIZE],
            fd: 0,
        };

        parse_retval(unsafe { ioctl(cdev_fd, REQ_GET_LINE_EVENT, &mut event_request) })?;

        Ok(event_request)
    }
}

const EVENT_TYPE_RISING_EDGE: u32 = 0x01;
const EVENT_TYPE_FALLING_EDGE: u32 = 0x02;

#[derive(Copy, Clone)]
#[repr(C)]
struct EventData {
    timestamp: u64,
    id: u32,
}

impl EventData {
    fn new(event_fd: c_int) -> Result<Option<EventData>> {
        let mut event_data = EventData {
            timestamp: 0,
            id: 0,
        };

        let bytes_read = parse_retval(unsafe {
            read(
                event_fd,
                &mut event_data as *mut EventData as *mut c_void,
                size_of::<EventData>(),
            ) as i32
        })?;

        if bytes_read != size_of::<EventData>() as i32 {
            Ok(None)
        } else {
            Ok(Some(event_data))
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct Event {
    pub trigger: Trigger,
    pub timestamp: Duration,
}

impl Event {
    fn from_event_data(event_data: EventData) -> Event {
        Event {
            trigger: if event_data.id == EVENT_TYPE_RISING_EDGE {
                Trigger::RisingEdge
            } else {
                Trigger::FallingEdge
            },
            timestamp: Duration::from_nanos(event_data.timestamp),
        }
    }
}

// Read interrupt event
pub fn get_event(event_fd: c_int) -> Result<Option<Event>> {
    if let Some(event_data) = EventData::new(event_fd)? {
        Ok(Some(Event::from_event_data(event_data)))
    } else {
        Ok(None)
    }
}

// Find the correct gpiochip device based on its label
pub fn find_driver() -> Result<File> {
    let driver_name = b"pinctrl-bcm2835\0";

    for idx in 0..=255 {
        let gpiochip = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/gpiochip{}", idx))?;

        let chip_info = ChipInfo::new(gpiochip.as_raw_fd())?;
        if chip_info.label[0..driver_name.len()] == driver_name[..] {
            return Ok(gpiochip);
        }
    }

    Err(Error::Io(io::Error::from_raw_os_error(2)))
}

pub fn get_level(cdev_fd: c_int, pin: u8) -> Result<Level> {
    let chip_info = ChipInfo::new(cdev_fd)?;

    if u32::from(pin) > chip_info.lines {
        return Err(Error::InvalidPin(pin));
    }

    match HandleRequest::new(cdev_fd, &[pin])?.levels()?.values[0] {
        1 => Ok(Level::High),
        _ => Ok(Level::Low),
    }
}

pub fn close(fd: c_int) {
    unsafe {
        libc::close(fd);
    }
}
<commit_msg>Use `unreachable` if level is not 0/1.<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#![allow(dead_code)]

use libc::{self, c_int, c_ulong, c_void, ioctl, read};
use std::fs::{File, OpenOptions};
use std::io;
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
use std::time::Duration;

use gpio::{Error, Level, Result, Trigger};

#[cfg(target_env = "gnu")]
type IoctlLong = libc::c_ulong;
#[cfg(target_env = "musl")]
type IoctlLong = libc::c_long;

fn parse_retval(retval: c_int) -> Result<i32> {
    if retval == -1 {
        Err(Error::Io(io::Error::last_os_error()))
    } else {
        Ok(retval)
    }
}

const NRBITS: u8 = 8;
const TYPEBITS: u8 = 8;
const SIZEBITS: u8 = 14;
const DIRBITS: u8 = 2;

const NRSHIFT: u8 = 0;
const TYPESHIFT: u8 = (NRSHIFT + NRBITS);
const SIZESHIFT: u8 = (TYPESHIFT + TYPEBITS);
const DIRSHIFT: u8 = (SIZESHIFT + SIZEBITS);

const NR_GET_CHIP_INFO: IoctlLong = 0x01 << NRSHIFT;
const NR_GET_LINE_INFO: IoctlLong = 0x02 << NRSHIFT;
const NR_GET_LINE_HANDLE: IoctlLong = 0x03 << NRSHIFT;
const NR_GET_LINE_EVENT: IoctlLong = 0x04 << NRSHIFT;
const NR_GET_LINE_VALUES: IoctlLong = 0x08 << NRSHIFT;
const NR_SET_LINE_VALUES: IoctlLong = 0x09 << NRSHIFT;

const TYPE_GPIO: IoctlLong = (0xB4 as IoctlLong) << TYPESHIFT;

const SIZE_CHIP_INFO: IoctlLong = (size_of::<ChipInfo>() as IoctlLong) << SIZESHIFT;
const SIZE_LINE_INFO: IoctlLong = (size_of::<LineInfo>() as IoctlLong) << SIZESHIFT;
const SIZE_HANDLE_REQUEST: IoctlLong = (size_of::<HandleRequest>() as IoctlLong) << SIZESHIFT;
const SIZE_EVENT_REQUEST: IoctlLong = (size_of::<EventRequest>() as IoctlLong) << SIZESHIFT;
const SIZE_HANDLE_DATA: IoctlLong = (size_of::<HandleData>() as IoctlLong) << SIZESHIFT;

const DIR_NONE: c_ulong = 0;
const DIR_WRITE: IoctlLong = 1 << DIRSHIFT;
const DIR_READ: IoctlLong = 2 << DIRSHIFT;
const DIR_READ_WRITE: IoctlLong = DIR_READ | DIR_WRITE;

const REQ_GET_CHIP_INFO: IoctlLong = DIR_READ | TYPE_GPIO | NR_GET_CHIP_INFO | SIZE_CHIP_INFO;
const REQ_GET_LINE_INFO: IoctlLong = DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_INFO | SIZE_LINE_INFO;
const REQ_GET_LINE_HANDLE: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_HANDLE | SIZE_HANDLE_REQUEST;
const REQ_GET_LINE_EVENT: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_EVENT | SIZE_EVENT_REQUEST;
const REQ_GET_LINE_VALUES: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_GET_LINE_VALUES | SIZE_HANDLE_DATA;
const REQ_SET_LINE_VALUES: IoctlLong =
    DIR_READ_WRITE | TYPE_GPIO | NR_SET_LINE_VALUES | SIZE_HANDLE_DATA;

const NAME_BUFSIZE: usize = 32;
const LABEL_BUFSIZE: usize = 32;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct ChipInfo {
    pub name: [u8; NAME_BUFSIZE],
    pub label: [u8; LABEL_BUFSIZE],
    pub lines: u32,
}

impl ChipInfo {
    pub fn new(cdev_fd: c_int) -> Result<ChipInfo> {
        let mut chip_info = ChipInfo {
            name: [0u8; NAME_BUFSIZE],
            label: [0u8; LABEL_BUFSIZE],
            lines: 0,
        };

        parse_retval(unsafe { ioctl(cdev_fd, REQ_GET_CHIP_INFO, &mut chip_info) })?;

        Ok(chip_info)
    }
}

const LINE_FLAG_KERNEL: u32 = 0x01;
const LINE_FLAG_IS_OUT: u32 = 0x02;
const LINE_FLAG_ACTIVE_LOW: u32 = 0x04;
const LINE_FLAG_OPEN_DRAIN: u32 = 0x08;
const LINE_FLAG_OPEN_SOURCE: u32 = 0x10;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct LineInfo {
    pub line_offset: u32,
    pub flags: u32,
    pub name: [u8; NAME_BUFSIZE],
    pub consumer: [u8; LABEL_BUFSIZE],
}

impl LineInfo {
    pub fn new() -> LineInfo {
        LineInfo {
            line_offset: 0,
            flags: 0,
            name: [0u8; NAME_BUFSIZE],
            consumer: [0u8; LABEL_BUFSIZE],
        }
    }
}

const HANDLES_MAX: usize = 64;
const HANDLE_FLAG_INPUT: u32 = 0x01;
const HANDLE_FLAG_OUTPUT: u32 = 0x02;
const HANDLE_FLAG_ACTIVE_LOW: u32 = 0x04;
const HANDLE_FLAG_OPEN_DRAIN: u32 = 0x08;
const HANDLE_FLAG_OPEN_SOURCE: u32 = 0x10;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct HandleRequest {
    pub line_offsets: [u32; HANDLES_MAX],
    pub flags: u32,
    pub default_values: [u8; HANDLES_MAX],
    pub consumer_label: [u8; LABEL_BUFSIZE],
    pub lines: u32,
    pub fd: c_int,
}

impl HandleRequest {
    pub fn new(cdev_fd: c_int, pins: &[u8]) -> Result<HandleRequest> {
        let mut handle_request = HandleRequest {
            line_offsets: [0u32; HANDLES_MAX],
            flags: 0,
            default_values: [0u8; HANDLES_MAX],
            consumer_label: [0u8; LABEL_BUFSIZE],
            lines: 0,
            fd: 0,
        };

        let pins: &[u8] = if pins.len() > HANDLES_MAX {
            handle_request.lines = HANDLES_MAX as u32;
            &pins[0..HANDLES_MAX]
        } else {
            handle_request.lines = pins.len() as u32;
            pins
        };

        for (idx, pin) in pins.iter().enumerate() {
            handle_request.line_offsets[idx] = u32::from(*pin);
        }

        parse_retval(unsafe { ioctl(cdev_fd, REQ_GET_LINE_HANDLE, &mut handle_request) })?;

        Ok(handle_request)
    }

    pub fn levels(&self) -> Result<HandleData> {
        let mut handle_data = HandleData::new();

        parse_retval(unsafe { ioctl(self.fd, REQ_GET_LINE_VALUES, &mut handle_data) })?;

        Ok(handle_data)
    }

    pub fn set_levels(&mut self, levels: &[Level]) -> Result<()> {
        let mut handle_data = HandleData::new();
        let levels: &[Level] = if levels.len() > HANDLES_MAX {
            &levels[0..HANDLES_MAX]
        } else {
            levels
        };

        for (idx, level) in levels.iter().enumerate() {
            handle_data.values[idx] = *level as u8;
        }

        parse_retval(unsafe { ioctl(self.fd, REQ_SET_LINE_VALUES, &mut handle_data) })?;

        Ok(())
    }
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct HandleData {
    pub values: [u8; HANDLES_MAX],
}

impl HandleData {
    pub fn new() -> HandleData {
        HandleData {
            values: [0u8; HANDLES_MAX],
        }
    }
}

const EVENT_FLAG_RISING_EDGE: u32 = 0x01;
const EVENT_FLAG_FALLING_EDGE: u32 = 0x02;
const EVENT_FLAG_BOTH_EDGES: u32 = EVENT_FLAG_RISING_EDGE | EVENT_FLAG_FALLING_EDGE;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct EventRequest {
    pub line_offset: u32,
    pub handle_flags: u32,
    pub event_flags: u32,
    pub consumer_label: [u8; LABEL_BUFSIZE],
    pub fd: c_int,
}

impl EventRequest {
    pub fn new(cdev_fd: c_int, pin: u8, trigger: Trigger) -> Result<EventRequest> {
        let mut event_request = EventRequest {
            line_offset: u32::from(pin),
            handle_flags: HANDLE_FLAG_INPUT,
            event_flags: trigger as u32,
            consumer_label: [0u8; LABEL_BUFSIZE],
            fd: 0,
        };

        parse_retval(unsafe { ioctl(cdev_fd, REQ_GET_LINE_EVENT, &mut event_request) })?;

        Ok(event_request)
    }
}

const EVENT_TYPE_RISING_EDGE: u32 = 0x01;
const EVENT_TYPE_FALLING_EDGE: u32 = 0x02;

#[derive(Copy, Clone)]
#[repr(C)]
struct EventData {
    timestamp: u64,
    id: u32,
}

impl EventData {
    fn new(event_fd: c_int) -> Result<Option<EventData>> {
        let mut event_data = EventData {
            timestamp: 0,
            id: 0,
        };

        let bytes_read = parse_retval(unsafe {
            read(
                event_fd,
                &mut event_data as *mut EventData as *mut c_void,
                size_of::<EventData>(),
            ) as i32
        })?;

        if bytes_read != size_of::<EventData>() as i32 {
            Ok(None)
        } else {
            Ok(Some(event_data))
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct Event {
    pub trigger: Trigger,
    pub timestamp: Duration,
}

impl Event {
    fn from_event_data(event_data: EventData) -> Event {
        Event {
            trigger: if event_data.id == EVENT_TYPE_RISING_EDGE {
                Trigger::RisingEdge
            } else {
                Trigger::FallingEdge
            },
            timestamp: Duration::from_nanos(event_data.timestamp),
        }
    }
}

// Read interrupt event
pub fn get_event(event_fd: c_int) -> Result<Option<Event>> {
    if let Some(event_data) = EventData::new(event_fd)? {
        Ok(Some(Event::from_event_data(event_data)))
    } else {
        Ok(None)
    }
}

// Find the correct gpiochip device based on its label
pub fn find_driver() -> Result<File> {
    let driver_name = b"pinctrl-bcm2835\0";

    for idx in 0..=255 {
        let gpiochip = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/gpiochip{}", idx))?;

        let chip_info = ChipInfo::new(gpiochip.as_raw_fd())?;
        if chip_info.label[0..driver_name.len()] == driver_name[..] {
            return Ok(gpiochip);
        }
    }

    Err(Error::Io(io::Error::from_raw_os_error(2)))
}

pub fn get_level(cdev_fd: c_int, pin: u8) -> Result<Level> {
    let chip_info = ChipInfo::new(cdev_fd)?;

    if u32::from(pin) > chip_info.lines {
        return Err(Error::InvalidPin(pin));
    }

    match HandleRequest::new(cdev_fd, &[pin])?.levels()?.values[0] {
        0 => Ok(Level::Low),
        1 => Ok(Level::High),
        _ => unreachable!(),
    }
}

pub fn close(fd: c_int) {
    unsafe {
        libc::close(fd);
    }
}
<|endoftext|>


<commit_before>use hyper::client::Response;
use std::{
    error::Error as StdError,
    fmt::{
        Display,
        Formatter,
        Result as FmtResult
    }
};

#[derive(Debug)]
pub enum Error {
    /// When a non-successful status code was received for a request.
    UnsuccessfulRequest(Response),
    /// When the decoding of a ratelimit header could not be properly decoded
    /// into an `i64`.
    RateLimitI64,
    /// When the decoding of a ratelimit header could not be properly decoded
    /// from UTF-8.
    RateLimitUtf8,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult { f.write_str(self.description()) }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnsuccessfulRequest(_) => {
                "A non-successful response status code was received"
            },
            Error::RateLimitI64 => "Error decoding a header into an i64",
            Error::RateLimitUtf8 => "Error decoding a header from UTF-8",
        }
    }
}
<commit_msg>Remove inconsistent braces (#397)<commit_after>use hyper::client::Response;
use std::{
    error::Error as StdError,
    fmt::{
        Display,
        Formatter,
        Result as FmtResult
    }
};

#[derive(Debug)]
pub enum Error {
    /// When a non-successful status code was received for a request.
    UnsuccessfulRequest(Response),
    /// When the decoding of a ratelimit header could not be properly decoded
    /// into an `i64`.
    RateLimitI64,
    /// When the decoding of a ratelimit header could not be properly decoded
    /// from UTF-8.
    RateLimitUtf8,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult { f.write_str(self.description()) }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnsuccessfulRequest(_) => "A non-successful response status code was received",
            Error::RateLimitI64 => "Error decoding a header into an i64",
            Error::RateLimitUtf8 => "Error decoding a header from UTF-8",
        }
    }
}
<|endoftext|>


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

use syntax::codemap::{Spanned, Span};
use syntax::ast;
use std::rc::Rc;
use serialize::Encodable;

/// A variant of an enum field type
#[deriving(Clone, Decodable, Encodable)]
pub struct Variant {
  pub name: Spanned<String>,
  pub value: Spanned<uint>,
  pub docstring: Option<Spanned<ast::Ident>>,
}

/// A bit field type
#[deriving(Clone, Decodable, Encodable)]
pub enum FieldType {
  /// A unsigned integer
  UIntField,
  /// A boolean flag
  BoolField,
  /// A enum
  EnumField {
    opt_name: Option<String>,
    variants: Vec<Variant>,
  },
}

#[deriving(PartialEq, Eq, Clone, Decodable, Encodable)]
pub enum Access {
  ReadWrite,
  ReadOnly,
  WriteOnly,
  /// A flag which can be set to clear
  SetToClear,
}

#[deriving(Clone, Decodable, Encodable)]
pub struct Field {
  pub name: Spanned<String>,
  /// The index of the first (lowest order) bit of the field
  pub low_bit: uint,
  /// The width in bits of a single array element
  pub width: uint,
  /// The number of array elements
  pub count: Spanned<uint>,
  pub bit_range_span: Span,
  pub access: Access,
  pub ty: Spanned<FieldType>,
  pub docstring: Option<Spanned<ast::Ident>>,
}

impl Field {
  /// The index of the highest order bit owned by this field
  pub fn high_bit(&self) -> uint {
    self.low_bit + self.width * self.count.node - 1
  }
}

#[deriving(Clone, Decodable, Encodable)]
pub enum RegWidth {
  /// A 32-bit wide register
  Reg32,
  /// A 16-bit wide register
  Reg16,
  /// An 8-bit wide register
  Reg8,
}

impl RegWidth {
  /// Size of register type in bytes
  pub fn size(&self) -> uint {
    match *self {
      RegWidth::Reg32 => 4,
      RegWidth::Reg16 => 2,
      RegWidth::Reg8  => 1,
    }
  }
}

#[deriving(Clone, Decodable, Encodable)]
pub enum RegType {
  /// A primitive bitfield
  RegPrim(RegWidth, Vec<Field>),
  /// A group
  RegUnion(Rc<Vec<Reg>>),
}

impl RegType {
  /// Size of register type in bytes
  pub fn size(&self) -> uint {
    match self {
      &RegType::RegPrim(ref width, _)  => width.size(),
      &RegType::RegUnion(ref regs) => regs_size(regs.deref()),
    }
  }
}

/// A single register, either a union or primitive
#[deriving(Clone, Decodable, Encodable)]
pub struct Reg {
  pub offset: uint,
  pub name: Spanned<String>,
  pub ty: RegType,
  pub count: Spanned<uint>,
  pub docstring: Option<Spanned<ast::Ident>>,
}

impl Reg {
  /// Size of a register in bytes
  pub fn size(&self) -> uint {
    self.count.node * self.ty.size()
  }
  /// The offset of the last byte owned by this register
  pub fn last_byte(&self) -> uint {
    self.offset + self.size() - 1
  }
}

/// Size of registers of register group in bytes
pub fn regs_size(regs: &Vec<Reg>) -> uint {
  match regs.iter().max_by(|r| r.offset) {
    Some(last) => last.offset + last.ty.size(),
    None => 0,
  }
}

pub trait RegVisitor {
  /// Path includes name of `Reg` being visited
  fn visit_prim_reg<'a>(&'a mut self, _path: &Vec<String>, _reg: &'a Reg,
                        _width: &RegWidth, _fields: &Vec<Field>) {}
  fn visit_union_reg<'a>(&'a mut self, _path: &Vec<String>, _reg: &'a Reg,
                         _subregs: Rc<Vec<Reg>>) {}
}

pub fn visit_reg<T: RegVisitor>(reg: &Reg, visitor: &mut T) {
  visit_reg_(reg, visitor, vec!(reg.name.node.clone()))
}

fn visit_reg_<T: RegVisitor>(reg: &Reg, visitor: &mut T, path: Vec<String>) {
  match reg.ty {
    RegType::RegUnion(ref regs) => {
      visitor.visit_union_reg(&path, reg, regs.clone());
      for r in regs.iter() {
        let mut new_path = path.clone();
        new_path.push(r.name.node.clone()); // TODO(bgamari) fix clone
        visit_reg_(r, visitor, new_path);
      }
    },
    RegType::RegPrim(ref width, ref fields) =>
      visitor.visit_prim_reg(&path, reg, width, fields)
  }
}
<commit_msg>ioreg::node: Add Copy impls for enums<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::codemap::{Spanned, Span};
use syntax::ast;
use std::rc::Rc;
use serialize::Encodable;

/// A variant of an enum field type
#[deriving(Clone, Decodable, Encodable)]
pub struct Variant {
  pub name: Spanned<String>,
  pub value: Spanned<uint>,
  pub docstring: Option<Spanned<ast::Ident>>,
}

/// A bit field type
#[deriving(Clone, Decodable, Encodable)]
pub enum FieldType {
  /// A unsigned integer
  UIntField,
  /// A boolean flag
  BoolField,
  /// A enum
  EnumField {
    opt_name: Option<String>,
    variants: Vec<Variant>,
  },
}

#[deriving(Copy, PartialEq, Eq, Clone, Decodable, Encodable)]
pub enum Access {
  ReadWrite,
  ReadOnly,
  WriteOnly,
  /// A flag which can be set to clear
  SetToClear,
}

#[deriving(Clone, Decodable, Encodable)]
pub struct Field {
  pub name: Spanned<String>,
  /// The index of the first (lowest order) bit of the field
  pub low_bit: uint,
  /// The width in bits of a single array element
  pub width: uint,
  /// The number of array elements
  pub count: Spanned<uint>,
  pub bit_range_span: Span,
  pub access: Access,
  pub ty: Spanned<FieldType>,
  pub docstring: Option<Spanned<ast::Ident>>,
}

impl Field {
  /// The index of the highest order bit owned by this field
  pub fn high_bit(&self) -> uint {
    self.low_bit + self.width * self.count.node - 1
  }
}

#[deriving(Copy, Clone, Decodable, Encodable)]
pub enum RegWidth {
  /// A 32-bit wide register
  Reg32,
  /// A 16-bit wide register
  Reg16,
  /// An 8-bit wide register
  Reg8,
}

impl RegWidth {
  /// Size of register type in bytes
  pub fn size(&self) -> uint {
    match *self {
      RegWidth::Reg32 => 4,
      RegWidth::Reg16 => 2,
      RegWidth::Reg8  => 1,
    }
  }
}

#[deriving(Clone, Decodable, Encodable)]
pub enum RegType {
  /// A primitive bitfield
  RegPrim(RegWidth, Vec<Field>),
  /// A group
  RegUnion(Rc<Vec<Reg>>),
}

impl RegType {
  /// Size of register type in bytes
  pub fn size(&self) -> uint {
    match self {
      &RegType::RegPrim(ref width, _)  => width.size(),
      &RegType::RegUnion(ref regs) => regs_size(regs.deref()),
    }
  }
}

/// A single register, either a union or primitive
#[deriving(Clone, Decodable, Encodable)]
pub struct Reg {
  pub offset: uint,
  pub name: Spanned<String>,
  pub ty: RegType,
  pub count: Spanned<uint>,
  pub docstring: Option<Spanned<ast::Ident>>,
}

impl Reg {
  /// Size of a register in bytes
  pub fn size(&self) -> uint {
    self.count.node * self.ty.size()
  }
  /// The offset of the last byte owned by this register
  pub fn last_byte(&self) -> uint {
    self.offset + self.size() - 1
  }
}

/// Size of registers of register group in bytes
pub fn regs_size(regs: &Vec<Reg>) -> uint {
  match regs.iter().max_by(|r| r.offset) {
    Some(last) => last.offset + last.ty.size(),
    None => 0,
  }
}

pub trait RegVisitor {
  /// Path includes name of `Reg` being visited
  fn visit_prim_reg<'a>(&'a mut self, _path: &Vec<String>, _reg: &'a Reg,
                        _width: &RegWidth, _fields: &Vec<Field>) {}
  fn visit_union_reg<'a>(&'a mut self, _path: &Vec<String>, _reg: &'a Reg,
                         _subregs: Rc<Vec<Reg>>) {}
}

pub fn visit_reg<T: RegVisitor>(reg: &Reg, visitor: &mut T) {
  visit_reg_(reg, visitor, vec!(reg.name.node.clone()))
}

fn visit_reg_<T: RegVisitor>(reg: &Reg, visitor: &mut T, path: Vec<String>) {
  match reg.ty {
    RegType::RegUnion(ref regs) => {
      visitor.visit_union_reg(&path, reg, regs.clone());
      for r in regs.iter() {
        let mut new_path = path.clone();
        new_path.push(r.name.node.clone()); // TODO(bgamari) fix clone
        visit_reg_(r, visitor, new_path);
      }
    },
    RegType::RegPrim(ref width, ref fields) =>
      visitor.visit_prim_reg(&path, reg, width, fields)
  }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::{
    collections::{HashMap, HashSet},
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use serde_json;

use devicemapper::{devnode_to_devno, Device, Sectors};

use crate::{
    engine::{
        strat_engine::{
            backstore::{
                blkdev_size,
                metadata::{StaticHeader, BDA},
                util::get_stratis_block_devices,
                StratBlockDev,
            },
            serde_structs::{BackstoreSave, BaseBlockDevSave, PoolSave},
        },
        BlockDevTier, DevUuid, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                if let Some((pool_uuid, _)) = StaticHeader::device_identifiers(
                    &mut OpenOptions::new().read(true).open(&devnode)?,
                )? {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode);
                }
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(clippy::implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<PoolSave>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas
            .iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in bdas
        .iter()
        .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time))
    {
        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data successfully read";
    Err(StratisError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices. Sort the blockdevs in the order in which they were
/// recorded in the metadata.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
#[allow(clippy::implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> = backstore_save
        .data_tier
        .blockdev
        .devs
        .iter()
        .enumerate()
        .map(|(i, bds)| (bds.uuid, (i, bds)))
        .collect();

    let recorded_cache_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> =
        match backstore_save.cache_tier {
            Some(ref cache_tier) => cache_tier
                .blockdev
                .devs
                .iter()
                .enumerate()
                .map(|(i, bds)| (bds.uuid, (i, bds)))
                .collect(),
            None => HashMap::new(),
        };

    let mut segment_table: HashMap<DevUuid, Vec<(Sectors, Sectors)>> = HashMap::new();
    for seg in &backstore_save.data_tier.blockdev.allocs[0] {
        segment_table
            .entry(seg.parent)
            .or_insert_with(Vec::default)
            .push((seg.start, seg.length))
    }

    if let Some(ref cache_tier) = backstore_save.cache_tier {
        for seg in cache_tier.blockdev.allocs.iter().flat_map(|i| i.iter()) {
            segment_table
                .entry(seg.parent)
                .or_insert_with(Vec::default)
                .push((seg.start, seg.length))
        }
    }

    // Construct a single StratBlockDev. Return the tier to which the
    // blockdev has been found to belong. Returns an error if the block
    // device has shrunk, no metadata can be found for the block device,
    // or it is impossible to set up the device because the recorded
    // allocation information is impossible.
    fn get_blockdev(
        device: Device,
        devnode: &Path,
        bda: BDA,
        data_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        cache_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        segment_table: &HashMap<DevUuid, Vec<(Sectors, Sectors)>>,
    ) -> StratisResult<(BlockDevTier, StratBlockDev)> {
        // Return an error if apparent size of Stratis block device appears to
        // have decreased since metadata was recorded or if size of block
        // device could not be obtained.
        blkdev_size(&OpenOptions::new().read(true).open(devnode)?).and_then(|actual_size| {
            let actual_size_sectors = actual_size.sectors();
            let recorded_size = bda.dev_size();
            if actual_size_sectors < recorded_size {
                let err_msg = format!(
                    "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had recorded size ({}), but actual size is less at ({})",
                    device,
                    devnode.display(),
                    bda.pool_uuid(),
                    bda.dev_uuid(),
                    recorded_size,
                    actual_size
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            } else {
                Ok(())
            }
        })?;

        let dev_uuid = bda.dev_uuid();

        // Locate the device in the metadata using its uuid. Return the device
        // metadata and whether it was a cache or a datadev.
        let (tier, &(_, bd_save)) = data_map
            .get(&dev_uuid)
            .map(|bd_save| (BlockDevTier::Data, bd_save))
            .or_else(|| {
                cache_map
                    .get(&dev_uuid)
                    .map(|bd_save| (BlockDevTier::Cache, bd_save))
            })
            .ok_or_else(|| {
                let err_msg = format!(
                        "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had no record in pool metadata",
                        device,
                        devnode.display(),
                        bda.pool_uuid(),
                        bda.dev_uuid()
                    );
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        Ok((
            tier,
            StratBlockDev::new(
                device,
                devnode.to_owned(),
                bda,
                segments.unwrap_or(&vec![]),
                bd_save.user_info.clone(),
                bd_save.hardware_info.clone(),
            )?,
        ))
    }

    let (mut datadevs, mut cachedevs): (Vec<StratBlockDev>, Vec<StratBlockDev>) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        get_blockdev(
            *device,
            devnode,
            bda,
            &recorded_data_map,
            &recorded_cache_map,
            &segment_table,
        )
        .map(|(tier, blockdev)| {
            match tier {
                BlockDevTier::Data => &mut datadevs,
                BlockDevTier::Cache => &mut cachedevs,
            }
            .push(blockdev)
        })?;
    }

    // Verify that devices located are congruent with the metadata recorded
    // and generally consistent with expectations. If all seems correct,
    // sort the devices according to their order in the metadata.
    fn check_and_sort_devs(
        mut devs: Vec<StratBlockDev>,
        dev_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
    ) -> StratisResult<Vec<StratBlockDev>> {
        let mut uuids = HashSet::new();
        let mut duplicate_uuids = Vec::new();
        for dev in &devs {
            let dev_uuid = dev.uuid();
            if !uuids.insert(dev_uuid) {
                duplicate_uuids.push(dev_uuid);
            }
        }

        if !duplicate_uuids.is_empty() {
            let err_msg = format!(
                "The following list of Stratis UUIDs were each claimed by more than one Stratis device: {}",
                duplicate_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", ")
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        let recorded_uuids: HashSet<_> = dev_map.keys().cloned().collect();
        if uuids != recorded_uuids {
            let err_msg = format!(
                "UUIDs of devices found ({}) did not correspond with UUIDs specified in the metadata for this group of devices ({})",
                uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
                recorded_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        // Sort the devices according to their original location in the
        // metadata. Use a faster unstable sort, because the order of
        // devs before the sort is arbitrary and does not need to be
        // preserved.
        devs.sort_unstable_by_key(|dev| dev_map[&dev.uuid()].0);
        Ok(devs)
    }

    let datadevs = check_and_sort_devs(datadevs, &recorded_data_map)?;
    let cachedevs = check_and_sort_devs(cachedevs, &recorded_cache_map)?;

    Ok((datadevs, cachedevs))
}
<commit_msg>Alow a clippy false positive<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::{
    collections::{HashMap, HashSet},
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use serde_json;

use devicemapper::{devnode_to_devno, Device, Sectors};

use crate::{
    engine::{
        strat_engine::{
            backstore::{
                blkdev_size,
                metadata::{StaticHeader, BDA},
                util::get_stratis_block_devices,
                StratBlockDev,
            },
            serde_structs::{BackstoreSave, BaseBlockDevSave, PoolSave},
        },
        BlockDevTier, DevUuid, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    // NOTE: The allow is required due to a false positive in clippy 1.35.
    // See issue: https://github.com/rust-lang/rust-clippy/issues/4133.
    #[allow(clippy::identity_conversion)]
    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                if let Some((pool_uuid, _)) = StaticHeader::device_identifiers(
                    &mut OpenOptions::new().read(true).open(&devnode)?,
                )? {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode);
                }
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(clippy::implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<PoolSave>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas
            .iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in bdas
        .iter()
        .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time))
    {
        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data successfully read";
    Err(StratisError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices. Sort the blockdevs in the order in which they were
/// recorded in the metadata.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
#[allow(clippy::implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> = backstore_save
        .data_tier
        .blockdev
        .devs
        .iter()
        .enumerate()
        .map(|(i, bds)| (bds.uuid, (i, bds)))
        .collect();

    let recorded_cache_map: HashMap<DevUuid, (usize, &BaseBlockDevSave)> =
        match backstore_save.cache_tier {
            Some(ref cache_tier) => cache_tier
                .blockdev
                .devs
                .iter()
                .enumerate()
                .map(|(i, bds)| (bds.uuid, (i, bds)))
                .collect(),
            None => HashMap::new(),
        };

    let mut segment_table: HashMap<DevUuid, Vec<(Sectors, Sectors)>> = HashMap::new();
    for seg in &backstore_save.data_tier.blockdev.allocs[0] {
        segment_table
            .entry(seg.parent)
            .or_insert_with(Vec::default)
            .push((seg.start, seg.length))
    }

    if let Some(ref cache_tier) = backstore_save.cache_tier {
        for seg in cache_tier.blockdev.allocs.iter().flat_map(|i| i.iter()) {
            segment_table
                .entry(seg.parent)
                .or_insert_with(Vec::default)
                .push((seg.start, seg.length))
        }
    }

    // Construct a single StratBlockDev. Return the tier to which the
    // blockdev has been found to belong. Returns an error if the block
    // device has shrunk, no metadata can be found for the block device,
    // or it is impossible to set up the device because the recorded
    // allocation information is impossible.
    fn get_blockdev(
        device: Device,
        devnode: &Path,
        bda: BDA,
        data_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        cache_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
        segment_table: &HashMap<DevUuid, Vec<(Sectors, Sectors)>>,
    ) -> StratisResult<(BlockDevTier, StratBlockDev)> {
        // Return an error if apparent size of Stratis block device appears to
        // have decreased since metadata was recorded or if size of block
        // device could not be obtained.
        blkdev_size(&OpenOptions::new().read(true).open(devnode)?).and_then(|actual_size| {
            let actual_size_sectors = actual_size.sectors();
            let recorded_size = bda.dev_size();
            if actual_size_sectors < recorded_size {
                let err_msg = format!(
                    "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had recorded size ({}), but actual size is less at ({})",
                    device,
                    devnode.display(),
                    bda.pool_uuid(),
                    bda.dev_uuid(),
                    recorded_size,
                    actual_size
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            } else {
                Ok(())
            }
        })?;

        let dev_uuid = bda.dev_uuid();

        // Locate the device in the metadata using its uuid. Return the device
        // metadata and whether it was a cache or a datadev.
        let (tier, &(_, bd_save)) = data_map
            .get(&dev_uuid)
            .map(|bd_save| (BlockDevTier::Data, bd_save))
            .or_else(|| {
                cache_map
                    .get(&dev_uuid)
                    .map(|bd_save| (BlockDevTier::Cache, bd_save))
            })
            .ok_or_else(|| {
                let err_msg = format!(
                        "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had no record in pool metadata",
                        device,
                        devnode.display(),
                        bda.pool_uuid(),
                        bda.dev_uuid()
                    );
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        Ok((
            tier,
            StratBlockDev::new(
                device,
                devnode.to_owned(),
                bda,
                segments.unwrap_or(&vec![]),
                bd_save.user_info.clone(),
                bd_save.hardware_info.clone(),
            )?,
        ))
    }

    let (mut datadevs, mut cachedevs): (Vec<StratBlockDev>, Vec<StratBlockDev>) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        get_blockdev(
            *device,
            devnode,
            bda,
            &recorded_data_map,
            &recorded_cache_map,
            &segment_table,
        )
        .map(|(tier, blockdev)| {
            match tier {
                BlockDevTier::Data => &mut datadevs,
                BlockDevTier::Cache => &mut cachedevs,
            }
            .push(blockdev)
        })?;
    }

    // Verify that devices located are congruent with the metadata recorded
    // and generally consistent with expectations. If all seems correct,
    // sort the devices according to their order in the metadata.
    fn check_and_sort_devs(
        mut devs: Vec<StratBlockDev>,
        dev_map: &HashMap<DevUuid, (usize, &BaseBlockDevSave)>,
    ) -> StratisResult<Vec<StratBlockDev>> {
        let mut uuids = HashSet::new();
        let mut duplicate_uuids = Vec::new();
        for dev in &devs {
            let dev_uuid = dev.uuid();
            if !uuids.insert(dev_uuid) {
                duplicate_uuids.push(dev_uuid);
            }
        }

        if !duplicate_uuids.is_empty() {
            let err_msg = format!(
                "The following list of Stratis UUIDs were each claimed by more than one Stratis device: {}",
                duplicate_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", ")
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        let recorded_uuids: HashSet<_> = dev_map.keys().cloned().collect();
        if uuids != recorded_uuids {
            let err_msg = format!(
                "UUIDs of devices found ({}) did not correspond with UUIDs specified in the metadata for this group of devices ({})",
                uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
                recorded_uuids.iter().map(|u| u.to_simple_ref().to_string()).collect::<Vec<_>>().join(", "),
            );
            return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
        }

        // Sort the devices according to their original location in the
        // metadata. Use a faster unstable sort, because the order of
        // devs before the sort is arbitrary and does not need to be
        // preserved.
        devs.sort_unstable_by_key(|dev| dev_map[&dev.uuid()].0);
        Ok(devs)
    }

    let datadevs = check_and_sort_devs(datadevs, &recorded_data_map)?;
    let cachedevs = check_and_sort_devs(cachedevs, &recorded_cache_map)?;

    Ok((datadevs, cachedevs))
}
<|endoftext|>


<commit_before>use super::FastFieldDataAccess;
use super::FastFieldSerializer;
use super::FastFieldSerializerEstimate;
use super::FastFieldStats;
use crate::common::BinarySerializable;
use std::io::{self, Write};
use tantivy_bitpacker::compute_num_bits;
use tantivy_bitpacker::BitPacker;

/// Fastfield serializer, which tries to guess values by linear interpolation
/// and stores the difference.
pub struct LinearInterpolFastFieldSerializer<'a, W: 'a + Write> {
    bit_packer: BitPacker,
    write: &'a mut W,
    min_value: u64,
    num_bits: u8,
}

impl<'a, W: Write> LinearInterpolFastFieldSerializer<'a, W> {
    /// Creates a new fast field serializer.
    ///
    /// The serializer in fact encode the values by bitpacking
    /// `(val - min_value)`.
    ///
    /// It requires a `min_value` and a `max_value` to compute
    /// compute the minimum number of bits required to encode
    /// values.
    pub(crate) fn create(
        write: &'a mut W,
        fastfield_accessor: &impl FastFieldDataAccess,
        stats: FastFieldStats,
        data_iter: impl Iterator<Item = u64>,
    ) -> io::Result<()> {
        assert!(stats.min_value <= stats.max_value);
        stats.min_value.serialize(write)?;
        let amplitude = stats.max_value - stats.min_value;
        amplitude.serialize(write)?;
        let num_bits = compute_num_bits(amplitude);
        let mut serializer = LinearInterpolFastFieldSerializer {
            bit_packer: BitPacker::new(),
            write,
            min_value: stats.min_value,
            num_bits,
        };

        for val in data_iter {
            serializer.add_val(val)?;
        }
        serializer.close_field()?;

        Ok(())
    }
}

impl<'a, W: 'a + Write> FastFieldSerializer for LinearInterpolFastFieldSerializer<'a, W> {
    /// Pushes a new value to the currently open u64 fast field.
    fn add_val(&mut self, val: u64) -> io::Result<()> {
        let val_to_write: u64 = val - self.min_value;
        self.bit_packer
            .write(val_to_write, self.num_bits, &mut self.write)?;
        Ok(())
    }
    fn close_field(mut self) -> io::Result<()> {
        self.bit_packer.close(&mut self.write)
    }
}

impl<'a, W: 'a + Write> FastFieldSerializerEstimate for LinearInterpolFastFieldSerializer<'a, W> {
    fn estimate(
        _fastfield_accessor: &impl FastFieldDataAccess,
        stats: FastFieldStats,
    ) -> (f32, &'static str) {
        let amplitude = stats.max_value - stats.min_value;
        let num_bits = compute_num_bits(amplitude);
        let num_bits_uncompressed = 64;
        let ratio = num_bits as f32 / num_bits_uncompressed as f32;
        let name = Self::codec_id().0;
        (ratio, name)
    }
    fn codec_id() -> (&'static str, u8) {
        ("Bitpacked", 2)
    }
}
<commit_msg>implement linear interpol serializer<commit_after>use super::FastFieldDataAccess;
use super::FastFieldSerializerEstimate;
use super::FastFieldStats;
use crate::common::BinarySerializable;
use std::io::{self, Write};
use tantivy_bitpacker::compute_num_bits;
use tantivy_bitpacker::BitPacker;

/// Fastfield serializer, which tries to guess values by linear interpolation
/// and stores the difference.
pub struct LinearInterpolFastFieldSerializer {}

impl LinearInterpolFastFieldSerializer {
    /// Creates a new fast field serializer.
    pub(crate) fn create(
        write: &mut impl Write,
        _fastfield_accessor: &impl FastFieldDataAccess,
        stats: FastFieldStats,
        data_iter: impl Iterator<Item = u64>,
        data_iter1: impl Iterator<Item = u64>,
        data_iter2: impl Iterator<Item = u64>,
    ) -> io::Result<()> {
        assert!(stats.min_value <= stats.max_value);

        let step = (stats.max_value - stats.min_value) as f64 / (stats.num_vals as u64 - 1) as f64;
        // offset to ensure all values are positive
        let offset = data_iter1
            .enumerate()
            .map(|(pos, val)| {
                let calculated_value = stats.min_value + (pos as f64 * step) as u64;
                val as i64 - calculated_value as i64
            })
            .min()
            .unwrap()
            .abs() as u64;

        //calc new max
        let rel_max = data_iter2
            .enumerate()
            .map(|(pos, val)| {
                let calculated_value = stats.min_value + (pos as f64 * step) as u64;
                (val + offset) - calculated_value
            })
            .max()
            .unwrap();

        stats.min_value.serialize(write)?;
        let amplitude = rel_max;
        amplitude.serialize(write)?;
        offset.serialize(write)?;
        stats.min_value.serialize(write)?;
        let num_bits = compute_num_bits(amplitude);
        let mut bit_packer = BitPacker::new();
        for val in data_iter {
            bit_packer.write(val, num_bits, write)?;
        }
        bit_packer.close(write)?;

        Ok(())
    }
}

impl FastFieldSerializerEstimate for LinearInterpolFastFieldSerializer {
    fn estimate(
        _fastfield_accessor: &impl FastFieldDataAccess,
        stats: FastFieldStats,
    ) -> (f32, &'static str) {
        let amplitude = stats.max_value - stats.min_value;
        let num_bits = compute_num_bits(amplitude);
        let num_bits_uncompressed = 64;
        let ratio = num_bits as f32 / num_bits_uncompressed as f32;
        let name = Self::codec_id().0;
        (ratio, name)
    }
    fn codec_id() -> (&'static str, u8) {
        ("LinearInterpol", 2)
    }
}
<|endoftext|>