| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! This crate contains definitions of newtypes corresponding to measurement units, including |
| //! several that are passed across node boundaries in Messages. |
| //! |
| //! Functionality has been added opportunistically so far and is not at all complete. A fully |
| //! developed measurement unit library would probably require const generics, so the compiler can |
| //! interpret exponents applied to different unit types. |
| |
| use fuchsia_zircon::sys; |
| use std::ops; |
| |
| /// Defines aspects of a measurement unit that are applicable regardless of the scalar type. |
| /// mul_scalar and div_scalar are defined to eliminate some common needs for shedding the unit type. |
| macro_rules! define_unit_base { |
| ( $unit_type:ident, $scalar_type:ident ) => { |
| #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] |
| pub struct $unit_type(pub $scalar_type); |
| |
| #[allow(dead_code)] |
| impl $unit_type { |
| pub fn mul_scalar(&self, other: $scalar_type) -> Self { |
| Self(self.0 * other) |
| } |
| pub fn div_scalar(&self, other: $scalar_type) -> Self { |
| Self(self.0 / other) |
| } |
| } |
| }; |
| } |
| |
| /// Defines a measurement unit with an underlying float scalar type. This allows definition of |
| /// associated functions that delegate to those of the underlying type. |
| macro_rules! define_float_unit { |
| ( $unit_type:ident, $scalar_type: ident ) => { |
| define_unit_base!($unit_type, $scalar_type); |
| |
| #[allow(dead_code)] |
| impl $unit_type { |
| pub fn min(a: Self, b: Self) -> Self { |
| Self($scalar_type::min(a.0, b.0)) |
| } |
| |
| pub fn max(a: Self, b: Self) -> Self { |
| Self($scalar_type::max(a.0, b.0)) |
| } |
| } |
| }; |
| } |
| |
| /// Defines a measurement unit, with an underlying scalar type. |
| macro_rules! define_unit { |
| ( $unit_type:ident, f32 ) => { |
| define_float_unit!($unit_type, f32); |
| }; |
| ( $unit_type:ident, f64 ) => { |
| define_float_unit!($unit_type, f64); |
| }; |
| ( $unit_type:ident, $scalar_type:ident ) => { |
| define_unit_base!($unit_type, $scalar_type); |
| }; |
| } |
| |
| // Standard unit types. |
| define_unit!(Celsius, f64); |
| define_unit!(Farads, f64); |
| define_unit!(Hertz, f64); |
| define_unit!(Seconds, f64); |
| define_unit!(Volts, f64); |
| define_unit!(Watts, f64); |
| define_unit!(Nanoseconds, i64); |
| define_unit!(Microseconds, i64); |
| define_unit!(Milliseconds, i64); |
| |
| // An unsigned integer in the range [0 - 100]. |
| define_unit!(ThermalLoad, u32); |
| |
| // An unsigned integer that a thermal client has defined in their thermal configuration. |
| define_unit!(ThermalState, u32); |
| |
| // Normalized performance units. The normalization is chosen such that 1 NormPerf is equivalent to a |
| // performance scale of 1.0 with respect to the Fuchsia kernel scheduler. In particular, this means |
| // that the wrapped f64 is appropriate for conversion to zx_cpu_performance_scale_t. |
| define_unit!(NormPerfs, f64); |
| |
| impl std::convert::TryFrom<NormPerfs> for sys::zx_cpu_performance_scale_t { |
| type Error = anyhow::Error; |
| |
| fn try_from(value: NormPerfs) -> Result<Self, Self::Error> { |
| let (fraction, integer) = libm::modf(value.0); |
| if integer > std::u32::MAX as f64 { |
| anyhow::bail!("Integer part {} exceeds std::u32::MAX", integer); |
| } |
| let integer_part = integer as u32; |
| let fractional_part = libm::ldexp(fraction, 32) as u32; |
| Ok(Self { integer_part, fractional_part }) |
| } |
| } |
| |
| // Addition and subtraction is implemented for types for which it is useful. Some, but not all, |
| // other unit types could reasonably support these operations. |
| macro_rules! define_arithmetic { |
| ( $unit_type:ident ) => { |
| impl ops::Add for $unit_type { |
| type Output = Self; |
| fn add(self, other: Self) -> Self::Output { |
| Self(self.0 + other.0) |
| } |
| } |
| |
| impl ops::AddAssign for $unit_type { |
| fn add_assign(&mut self, other: Self) { |
| self.0 += other.0; |
| } |
| } |
| |
| impl ops::Sub for $unit_type { |
| type Output = Self; |
| fn sub(self, other: Self) -> Self::Output { |
| Self(self.0 - other.0) |
| } |
| } |
| |
| impl std::iter::Sum for $unit_type { |
| fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { |
| iter.fold(Self::default(), |a, b| a + b) |
| } |
| } |
| }; |
| |
| ( $unit_type:ident, $($more:ident),+ ) => { |
| define_arithmetic!($unit_type); |
| define_arithmetic!($($more),+); |
| }; |
| } |
| define_arithmetic!(Seconds, Nanoseconds, Celsius, NormPerfs, Watts); |
| |
| impl From<Nanoseconds> for Seconds { |
| fn from(nanos: Nanoseconds) -> Self { |
| Seconds(nanos.0 as f64 / 1e9) |
| } |
| } |
| |
| impl From<Seconds> for Nanoseconds { |
| fn from(seconds: Seconds) -> Self { |
| Nanoseconds((seconds.0 * 1e9) as i64) |
| } |
| } |
| |
| impl From<Seconds> for Microseconds { |
| fn from(seconds: Seconds) -> Self { |
| Microseconds((seconds.0 * 1e6) as i64) |
| } |
| } |
| |
| impl From<Nanoseconds> for Microseconds { |
| fn from(nanos: Nanoseconds) -> Self { |
| Microseconds(nanos.0 / 1000) |
| } |
| } |
| |
| impl From<Seconds> for Milliseconds { |
| fn from(seconds: Seconds) -> Self { |
| Milliseconds((seconds.0 * 1e3) as i64) |
| } |
| } |
| |
| impl From<Nanoseconds> for Milliseconds { |
| fn from(nanos: Nanoseconds) -> Self { |
| Milliseconds(nanos.0 / 1_000_000) |
| } |
| } |
| |
| impl From<Seconds> for fuchsia_zircon::Duration { |
| fn from(seconds: Seconds) -> fuchsia_zircon::Duration { |
| fuchsia_zircon::Duration::from_nanos(Nanoseconds::from(seconds).0) |
| } |
| } |
| |
| impl From<Seconds> for fuchsia_async::Time { |
| fn from(seconds: Seconds) -> fuchsia_async::Time { |
| fuchsia_async::Time::from_nanos(Nanoseconds::from(seconds).0) |
| } |
| } |
| |
| impl From<fuchsia_async::Time> for Nanoseconds { |
| fn from(time: fuchsia_async::Time) -> Nanoseconds { |
| Nanoseconds(time.into_nanos()) |
| } |
| } |
| |
| // Define multiplication and division involving Seconds and Hertz. |
| impl ops::Mul<Hertz> for Seconds { |
| type Output = f64; |
| fn mul(self, rhs: Hertz) -> Self::Output { |
| self.0 * rhs.0 |
| } |
| } |
| |
| impl ops::Mul<Seconds> for Hertz { |
| type Output = f64; |
| fn mul(self, rhs: Seconds) -> Self::Output { |
| self.0 * rhs.0 |
| } |
| } |
| |
| impl ops::Div<Seconds> for f64 { |
| type Output = Hertz; |
| fn div(self, rhs: Seconds) -> Self::Output { |
| Hertz(self / rhs.0) |
| } |
| } |
| |
| impl ops::Div<Hertz> for f64 { |
| type Output = Seconds; |
| fn div(self, rhs: Hertz) -> Self::Output { |
| Seconds(self / rhs.0) |
| } |
| } |
| |
| /// Describes a processor performance state. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct PState { |
| pub frequency: Hertz, |
| pub voltage: Volts, |
| } |