mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-24 02:59:03 +00:00
feat(core): Add Instant and Duration types
This commit is contained in:
parent
ee34b5ba08
commit
998210d569
@ -111,6 +111,8 @@ fn generate_micropython_bindings() {
|
||||
.allowlist_var("mp_type_OverflowError")
|
||||
.allowlist_var("mp_type_ValueError")
|
||||
.allowlist_var("mp_type_TypeError")
|
||||
// time
|
||||
.allowlist_function("mp_hal_ticks_ms")
|
||||
// typ
|
||||
.allowlist_var("mp_type_type");
|
||||
|
||||
@ -161,6 +163,7 @@ fn generate_micropython_bindings() {
|
||||
"-I../unix",
|
||||
"-I../../build/unix",
|
||||
"-I../../vendor/micropython",
|
||||
"-I../../vendor/micropython/ports/unix",
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "py/gc.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
@ -8,6 +8,7 @@ mod error;
|
||||
#[macro_use]
|
||||
mod micropython;
|
||||
mod protobuf;
|
||||
mod time;
|
||||
#[cfg(feature = "ui_debug")]
|
||||
mod trace;
|
||||
mod trezorhal;
|
||||
|
@ -13,4 +13,5 @@ pub mod map;
|
||||
pub mod obj;
|
||||
pub mod qstr;
|
||||
pub mod runtime;
|
||||
pub mod time;
|
||||
pub mod typ;
|
||||
|
5
core/embed/rust/src/micropython/time.rs
Normal file
5
core/embed/rust/src/micropython/time.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use super::ffi;
|
||||
|
||||
pub fn ticks_ms() -> u32 {
|
||||
unsafe { ffi::mp_hal_ticks_ms() as _ }
|
||||
}
|
147
core/embed/rust/src/time.rs
Normal file
147
core/embed/rust/src/time.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use core::{cmp::Ordering, ops::Div};
|
||||
|
||||
use crate::micropython::time;
|
||||
|
||||
const MILLIS_PER_SEC: u32 = 1000;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Duration {
|
||||
millis: u32,
|
||||
}
|
||||
|
||||
impl Duration {
|
||||
pub const ZERO: Self = Self::from_millis(0);
|
||||
|
||||
pub const fn from_millis(millis: u32) -> Self {
|
||||
Self { millis }
|
||||
}
|
||||
|
||||
pub const fn from_secs(secs: u32) -> Self {
|
||||
Self {
|
||||
millis: secs * MILLIS_PER_SEC,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_millis(self) -> u32 {
|
||||
self.millis
|
||||
}
|
||||
|
||||
pub fn checked_add(self, rhs: Self) -> Option<Self> {
|
||||
self.millis.checked_add(rhs.millis).map(Self::from_millis)
|
||||
}
|
||||
|
||||
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||
self.millis.checked_sub(rhs.millis).map(Self::from_millis)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u32> for Duration {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: u32) -> Self::Output {
|
||||
Self::from_millis(self.millis / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Duration> for Duration {
|
||||
type Output = f32;
|
||||
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
self.to_millis() as f32 / rhs.to_millis() as f32
|
||||
}
|
||||
}
|
||||
|
||||
/* Instants can wrap around and we want them to be comparable even after
|
||||
* wrapping around. This works by setting a maximum allowable difference
|
||||
* between two Instants to half the range. In checked_add and checked_sub, we
|
||||
* make sure that the step from one Instant to another is at most
|
||||
* MAX_DIFFERENCE_IN_MILLIS. In the Ord implementation, if the difference is
|
||||
* more than MAX_DIFFERENCE_IN_MILLIS, we can assume that the smaller Instant
|
||||
* is actually wrapped around and so is in the future. */
|
||||
const MAX_DIFFERENCE_IN_MILLIS: u32 = u32::MAX / 2;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Instant {
|
||||
millis: u32,
|
||||
}
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Self {
|
||||
Self {
|
||||
millis: time::ticks_ms(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn saturating_duration_since(self, earlier: Self) -> Duration {
|
||||
self.checked_duration_since(earlier)
|
||||
.unwrap_or(Duration::ZERO)
|
||||
}
|
||||
|
||||
pub fn checked_duration_since(self, earlier: Self) -> Option<Duration> {
|
||||
if self >= earlier {
|
||||
Some(Duration::from_millis(
|
||||
self.millis.wrapping_sub(earlier.millis),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_add(self, duration: Duration) -> Option<Self> {
|
||||
let add_millis = duration.to_millis();
|
||||
if add_millis <= MAX_DIFFERENCE_IN_MILLIS {
|
||||
Some(Self {
|
||||
millis: self.millis.wrapping_add(add_millis),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_sub(self, duration: Duration) -> Option<Self> {
|
||||
let add_millis = duration.to_millis();
|
||||
if add_millis <= MAX_DIFFERENCE_IN_MILLIS {
|
||||
Some(Self {
|
||||
millis: self.millis.wrapping_sub(add_millis),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Instant {
|
||||
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Instant {
|
||||
fn cmp(&self, rhs: &Self) -> Ordering {
|
||||
if self.millis == rhs.millis {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
// If the difference is greater than MAX_DIFFERENCE_IN_MILLIS, we assume
|
||||
// that the larger Instant is in the past.
|
||||
// See explanation on MAX_DIFFERENCE_IN_MILLIS
|
||||
self.millis
|
||||
.wrapping_sub(rhs.millis)
|
||||
.cmp(&MAX_DIFFERENCE_IN_MILLIS)
|
||||
.reverse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instant_wraps_and_compares_correctly() {
|
||||
let milli = Duration { millis: 1 };
|
||||
let earlier = Instant { millis: u32::MAX };
|
||||
let later = earlier.checked_add(milli).unwrap();
|
||||
assert_eq!(later, Instant { millis: 0 });
|
||||
assert!(earlier < later);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user