Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/nss-rs/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 8 kB image not shown  

Quelle  time.rs   Sprache: Rust

 

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![expect(
    clippy::unwrap_used,
    reason = "Let's assume the use of `unwrap` was checked when the use of `unsafe` was reviewed."
)]

use std::{
    convert::{TryFrom as _, TryInto as _},
    ops::Deref,
    os::raw::c_void,
    pin::Pin,
    time::{Duration, Instant},
};

use once_cell::sync::OnceCell;

use crate::{
    agent::as_c_void,
    err::{Error, Res},
    prio::PRFileDesc,
    ssl::SSLTimeFunc,
};

include!(concat!(env!("OUT_DIR"), "/nspr_time.rs"));

experimental_api!(SSL_SetTimeFunc(
    fd: *mut PRFileDesc,
    cb: SSLTimeFunc,
    arg: *mut c_void,
));

/// This struct holds the zero time used for converting between `Instant` and `PRTime`.
#[derive(Debug)]
struct TimeZero {
    instant: Instant,
    prtime: PRTime,
}

impl TimeZero {
    /// This function sets a baseline from an instance of `Instant`.
    /// This allows for the possibility that code that uses these APIs will create
    /// instances of `Instant` before any of this code is run.  If `Instant`s older than
    /// `BASE_TIME` are used with these conversion functions, they will fail.
    /// To avoid that, we make sure that this sets the base time using the first value
    /// it sees if it is in the past.  If it is not, then use `Instant::now()` instead.
    pub fn baseline(t: Instant) -> Self {
        #[expect(
            clippy::disallowed_methods,
            reason = "Special handling for NSPR time conversion"
        )]
        let now = Instant::now();
        let prnow = unsafe { PR_Now() };

        if now <= t {
            // `t` is in the future, just use `now`.
            Self {
                instant: now,
                prtime: prnow,
            }
        } else {
            let elapsed = Interval::from(now.duration_since(now));
            // An error from these unwrap functions would require
            // ridiculously long application running time.
            let prelapsed: PRTime = elapsed.try_into().unwrap();
            Self {
                instant: t,
                prtime: prnow.checked_sub(prelapsed).unwrap(),
            }
        }
    }
}

static BASE_TIME: OnceCell<TimeZero> = OnceCell::new();

fn get_base() -> &'static TimeZero {
    BASE_TIME.get_or_init(|| TimeZero {
        #[expect(
            clippy::disallowed_methods,
            reason = "Special handling for NSPR time conversion"
        )]
        instant: Instant::now(),
        prtime: unsafe { PR_Now() },
    })
}

pub fn init() {
    _ = get_base();
}

/// Time wraps Instant and provides conversion functions into `PRTime`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Time {
    t: Instant,
}

impl Deref for Time {
    type Target = Instant;
    fn deref(&self) -> &Self::Target {
        &self.t
    }
}

impl From<Instant> for Time {
    /// Convert from an Instant into a Time.
    fn from(t: Instant) -> Self {
        // Call `TimeZero::baseline(t)` so that time zero can be set.
        BASE_TIME.get_or_init(|| TimeZero::baseline(t));
        Self { t }
    }
}

impl TryFrom<PRTime> for Time {
    type Error = Error;
    fn try_from(prtime: PRTime) -> Res<Self> {
        let base = get_base();
        let delta = prtime.checked_sub(base.prtime).ok_or(Error::TimeTravel)?;
        let d = Duration::from_micros(u64::try_from(delta.abs())?);
        let t = if delta >= 0 {
            base.instant.checked_add(d)
        } else {
            base.instant.checked_sub(d)
        };
        let t = t.ok_or(Error::TimeTravel)?;
        Ok(Self { t })
    }
}

impl TryInto<PRTime> for Time {
    type Error = Error;
    fn try_into(self) -> Res<PRTime> {
        let base = get_base();

        self.t.checked_duration_since(base.instant).map_or_else(
            || {
                // Try to go backwards from the base time.
                let backwards = base.instant - self.t; // infallible
                PRTime::try_from(backwards.as_micros()).map_or(Err(Error::TimeTravel), |d| {
                    base.prtime.checked_sub(d).ok_or(Error::TimeTravel)
                })
            },
            |delta| {
                PRTime::try_from(delta.as_micros()).map_or(Err(Error::TimeTravel), |d| {
                    d.checked_add(base.prtime).ok_or(Error::TimeTravel)
                })
            },
        )
    }
}

impl From<Time> for Instant {
    fn from(t: Time) -> Self {
        t.t
    }
}

/// Interval wraps Duration and provides conversion functions into `PRTime`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Interval {
    d: Duration,
}

impl TryFrom<PRTime> for Interval {
    type Error = Error;
    fn try_from(prtime: PRTime) -> Res<Self> {
        Ok(Self {
            d: Duration::from_micros(u64::try_from(prtime)?),
        })
    }
}

impl From<Duration> for Interval {
    fn from(d: Duration) -> Self {
        Self { d }
    }
}

impl TryInto<PRTime> for Interval {
    type Error = Error;
    fn try_into(self) -> Res<PRTime> {
        Ok(PRTime::try_from(self.d.as_micros())?)
    }
}

/// `TimeHolder` maintains a `PRTime` value in a form that is accessible to the TLS stack.
#[derive(Debug)]
pub struct TimeHolder {
    t: Pin<Box<PRTime>>,
}

impl TimeHolder {
    const unsafe extern "C" fn time_func(arg: *mut c_void) -> PRTime {
        let p = arg as *const PRTime;
        unsafe { *p.as_ref().unwrap() }
    }

    #[expect(clippy::not_unsafe_ptr_arg_deref)]
    pub fn bind(&mut self, fd: *mut PRFileDesc) -> Res<()> {
        unsafe { SSL_SetTimeFunc(fd, Some(Self::time_func), as_c_void(&>mut self.t)) }
    }

    pub fn set(&mut self, t: Instant) -> Res<()> {
        *self.t = Time::from(t).try_into()?;
        Ok(())
    }
}

impl Default for TimeHolder {
    fn default() -> Self {
        Self { t: Box::pin(0) }
    }
}

#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod test {
    use std::{
        convert::{TryFrom as _, TryInto as _},
        time::{Duration, Instant},
    };

    use super::{Interval, PRTime, Time, TimeZero, get_base, init};
    use crate::err::Res;

    #[test]
    fn convert_stable() {
        init();
        let now = Time::from(test_fixture::now());
        let pr: PRTime = now.try_into().expect("convert to PRTime with truncation");
        let t2 = Time::try_from(pr).expect("convert to Instant");
        let pr2: PRTime = t2.try_into().expect("convert to PRTime again");
        assert_eq!(pr, pr2);
        let t3 = Time::try_from(pr2).expect("convert to Instant again");
        assert_eq!(t2, t3);
    }

    #[test]
    fn past_prtime() {
        const DELTA: Duration = Duration::from_secs(1);
        init();
        let base = get_base();
        let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap();
        println!("{} - {delta_micros}", base.prtime);
        let t = Time::try_from(base.prtime - delta_micros).unwrap();
        assert_eq!(Instant::from(t) + DELTA, base.instant);
    }

    #[test]
    fn past_instant() {
        const DELTA: Duration = Duration::from_secs(1);
        init();
        let base = get_base();
        let t = Time::from(base.instant.checked_sub(DELTA).unwrap());
        assert_eq!(Instant::from(t) + DELTA, base.instant);

        // Convert back to PRTime to cover the backwards conversion path.
        let pr: PRTime = t.try_into().expect("convert past time to PRTime");
        let delta_micros = PRTime::try_from(DELTA.as_micros()).unwrap();
        assert_eq!(pr, base.prtime - delta_micros);
    }

    #[test]
    #[expect(clippy::disallowed_methods, reason = "Test for special time handling")]
    fn timezero_baseline_future() {
        let tz = TimeZero::baseline(Instant::now() + Duration::from_secs(10));
        let now = Instant::now();
        assert!(tz.instant <= now && tz.instant + Duration::from_millis(100) > now);
    }

    #[test]
    #[expect(clippy::disallowed_methods, reason = "Test for special time handling")]
    fn timezero_baseline_past() {
        let past = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();
        assert_eq!(TimeZero::baseline(past).instant, past);
    }

    #[test]
    fn negative_interval() {
        init();
        assert!(Interval::try_from(-1).is_err());
    }

    #[test]
    // We allow replace_consts here because
    // std::u64::MAX isn't available
    // in all of our targets
    fn overflow_interval() {
        init();
        let interval = Interval::from(Duration::from_micros(u64::MAX));
        let res: Res<PRTime> = interval.try_into();
        assert!(res.is_err());
    }
}

Messung V0.5 in Prozent
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-10-11) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.