use byztime_sys::*;
use std::cmp;
use std::ffi::CString;
use std::fmt;
use std::io;
use std::ops;
use std::os::unix::ffi::OsStrExt;
use std::path;
#[cfg(any(test, feature = "with_quickcheck"))]
use quickcheck::{Arbitrary, Gen};
#[cfg(any(test, feature = "with_quickcheck"))]
use rand::Rng;
#[derive(Debug, Copy, Clone)]
pub struct Timestamp(pub byztime_stamp);
#[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct Era(pub [u8; 16]);
impl Era {
pub fn get() -> io::Result<Era> {
let mut era: [u8; 16] = [0; 16];
if unsafe { byztime_get_clock_era(era.as_mut_ptr()) } < 0 {
Err(errno::errno().into())
} else {
Ok(Era(era))
}
}
}
impl Timestamp {
pub fn new(seconds: i64, nanoseconds: i64) -> Timestamp {
Timestamp(byztime_stamp {
seconds,
nanoseconds,
})
}
pub fn seconds(self) -> i64 {
self.0.seconds
}
pub fn nanoseconds(self) -> i64 {
self.0.nanoseconds
}
fn overflowing_normalize_assign(&mut self) -> bool {
if unsafe { byztime_stamp_normalize(&mut self.0 as *mut byztime_stamp) } < 0 {
assert_eq!(
libc::EOVERFLOW,
Into::<i32>::into(errno::errno()),
"byztime_stamp_normalize: {}",
errno::errno()
);
true
} else {
false
}
}
pub fn normalize(self) -> Timestamp {
let (result, overflow) = self.overflowing_normalize();
if overflow {
panic!("timestamp overflow")
};
result
}
pub fn overflowing_normalize(mut self) -> (Timestamp, bool) {
let overflow = self.overflowing_normalize_assign();
(self, overflow)
}
pub fn wrapping_normalize(self) -> Timestamp {
let (result, _) = self.overflowing_normalize();
result
}
fn saturating_normalize_assign(&mut self) -> bool {
let seconds = self.seconds();
if self.overflowing_normalize_assign() {
if seconds > 0 {
*self = Timestamp::max_value();
} else {
*self = Timestamp::min_value()
}
true
} else {
false
}
}
pub fn saturating_normalize(mut self) -> Timestamp {
self.saturating_normalize_assign();
self
}
pub fn checked_normalize(self) -> Option<Timestamp> {
let (result, overflow) = self.overflowing_normalize();
if overflow {
None
} else {
Some(result)
}
}
pub fn local_time() -> io::Result<Timestamp> {
let mut ts = Timestamp::default();
ts.local_time_assign()?;
Ok(ts)
}
fn local_time_assign(&mut self) -> io::Result<()> {
if unsafe { byztime_get_local_time(&mut self.0 as *mut byztime_stamp) } < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
pub fn real_time() -> io::Result<Timestamp> {
let mut ts = Timestamp::default();
ts.real_time_assign()?;
Ok(ts)
}
fn real_time_assign(&mut self) -> io::Result<()> {
if unsafe { byztime_get_real_time(&mut self.0 as *mut byztime_stamp) } < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
fn overflowing_add_assign(&mut self, other: Timestamp) -> bool {
let self_ptr = &mut self.0 as *mut byztime_stamp;
let other_ptr = &other.0 as *const byztime_stamp;
if unsafe { byztime_stamp_add(self_ptr, self_ptr, other_ptr) } < 0 {
assert_eq!(
libc::EOVERFLOW,
Into::<i32>::into(errno::errno()),
"byztime_stamp_add: {}",
errno::errno()
);
true
} else {
false
}
}
fn overflowing_sub_assign(&mut self, other: Timestamp) -> bool {
let self_ptr = &mut self.0 as *mut byztime_stamp;
let other_ptr = &other.0 as *const byztime_stamp;
if unsafe { byztime_stamp_sub(self_ptr, self_ptr, other_ptr) } < 0 {
assert_eq!(
libc::EOVERFLOW,
Into::<i32>::into(errno::errno()),
"byztime_stamp_sub: {}",
errno::errno()
);
true
} else {
false
}
}
pub fn overflowing_add(mut self, rhs: Timestamp) -> (Timestamp, bool) {
let overflow = self.overflowing_add_assign(rhs);
(self, overflow)
}
pub fn overflowing_sub(mut self, rhs: Timestamp) -> (Timestamp, bool) {
let overflow = self.overflowing_sub_assign(rhs);
(self, overflow)
}
pub fn checked_add(self, rhs: Timestamp) -> Option<Timestamp> {
let (sum, overflow) = self.overflowing_add(rhs);
if overflow {
None
} else {
Some(sum)
}
}
pub fn checked_sub(self, rhs: Timestamp) -> Option<Timestamp> {
let (diff, overflow) = self.overflowing_sub(rhs);
if overflow {
None
} else {
Some(diff)
}
}
pub fn wrapping_add(self, rhs: Timestamp) -> Timestamp {
let (sum, _) = self.overflowing_add(rhs);
sum
}
pub fn wrapping_sub(self, rhs: Timestamp) -> Timestamp {
let (diff, _) = self.overflowing_sub(rhs);
diff
}
pub fn saturating_add(self, rhs: Timestamp) -> Timestamp {
let (sum, overflow) = self.overflowing_add(rhs);
if overflow {
if rhs > Timestamp::default() {
Timestamp::max_value()
} else {
Timestamp::min_value()
}
} else {
sum
}
}
pub fn saturating_sub(self, rhs: Timestamp) -> Timestamp {
let (diff, overflow) = self.overflowing_sub(rhs);
if overflow {
if rhs < Timestamp::default() {
Timestamp::max_value()
} else {
Timestamp::min_value()
}
} else {
diff
}
}
fn overflowing_scale_assign(&mut self, ppb: i64) -> bool {
let self_ptr = &mut self.0 as *mut byztime_stamp;
if unsafe { byztime_stamp_scale(self_ptr, self_ptr, ppb) } < 0 {
assert_eq!(
libc::EOVERFLOW,
Into::<i32>::into(errno::errno()),
"byztime_stamp_scale: {}",
errno::errno()
);
true
} else {
false
}
}
fn saturating_scale_assign(&mut self, ppb: i64) -> bool {
let negated = (*self < Timestamp::default()) ^ (ppb < 0);
if self.overflowing_scale_assign(ppb) {
if negated {
*self = Timestamp::min_value()
} else {
*self = Timestamp::max_value()
}
true
} else {
false
}
}
pub fn overflowing_scale(mut self, ppb: i64) -> (Timestamp, bool) {
let overflow = self.overflowing_scale_assign(ppb);
(self, overflow)
}
pub fn scale(self, ppb: i64) -> Timestamp {
let (result, overflow) = self.overflowing_scale(ppb);
if overflow {
panic!("timestamp overflow")
} else {
result
}
}
pub fn checked_scale(self, ppb: i64) -> Option<Timestamp> {
let (result, overflow) = self.overflowing_scale(ppb);
if overflow {
None
} else {
Some(result)
}
}
pub fn wrapping_scale(self, ppb: i64) -> Timestamp {
let (result, _) = self.overflowing_scale(ppb);
result
}
pub fn saturating_scale(mut self, ppb: i64) -> Timestamp {
self.saturating_scale_assign(ppb);
self
}
fn halve_assign(&mut self) {
let self_ptr = &mut self.0 as *mut byztime_stamp;
unsafe {
byztime_stamp_halve(self_ptr, self_ptr);
}
}
pub fn halve(mut self) -> Timestamp {
self.halve_assign();
self
}
pub fn min_value() -> Timestamp {
Timestamp::new(i64::min_value(), 0)
}
pub fn max_value() -> Timestamp {
Timestamp::new(i64::max_value(), 0)
}
pub fn max_error() -> Timestamp {
Timestamp::new(i64::max_value() >> 1, 0)
}
}
impl Default for Timestamp {
fn default() -> Timestamp {
Timestamp::new(0, 0)
}
}
impl ops::Add for Timestamp {
type Output = Timestamp;
fn add(mut self, other: Timestamp) -> Timestamp {
let overflow = self.overflowing_add_assign(other);
if overflow {
panic!("timestamp overflow")
} else {
self
}
}
}
impl ops::Sub for Timestamp {
type Output = Timestamp;
fn sub(mut self, other: Timestamp) -> Timestamp {
let overflow = self.overflowing_sub_assign(other);
if overflow {
panic!("timestamp overflow")
} else {
self
}
}
}
impl ops::AddAssign for Timestamp {
fn add_assign(&mut self, other: Timestamp) {
if self.overflowing_add_assign(other) {
panic!("timestamp overflow")
}
}
}
impl ops::SubAssign for Timestamp {
fn sub_assign(&mut self, other: Timestamp) {
if self.overflowing_sub_assign(other) {
panic!("timestamp overflow")
}
}
}
impl Ord for Timestamp {
fn cmp(&self, other: &Timestamp) -> cmp::Ordering {
let self_ptr = &self.0 as *const byztime_stamp;
let other_ptr = &other.0 as *const byztime_stamp;
let ret = unsafe { byztime_stamp_cmp(self_ptr, other_ptr) };
ret.cmp(&0)
}
}
impl PartialOrd for Timestamp {
fn partial_cmp(&self, other: &Timestamp) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Timestamp {
fn eq(&self, other: &Timestamp) -> bool {
self.cmp(other) == cmp::Ordering::Equal
}
}
impl Eq for Timestamp {}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let normed = self.normalize();
write!(f, "{}.{:09}", normed.seconds(), normed.nanoseconds())
}
}
fn offset_assign(
ctx: *mut byztime_ctx,
min: Option<&mut Timestamp>,
est: Option<&mut Timestamp>,
max: Option<&mut Timestamp>,
) -> io::Result<()> {
let min_ptr = match min {
Some(min_ref) => &mut min_ref.0 as *mut byztime_stamp,
None => std::ptr::null_mut(),
};
let est_ptr = match est {
Some(est_ref) => &mut est_ref.0 as *mut byztime_stamp,
None => std::ptr::null_mut(),
};
let max_ptr = match max {
Some(max_ref) => &mut max_ref.0 as *mut byztime_stamp,
None => std::ptr::null_mut(),
};
if unsafe { byztime_get_offset(ctx, min_ptr, est_ptr, max_ptr) } < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
fn global_time_assign(
ctx: *mut byztime_ctx,
min: Option<&mut Timestamp>,
est: Option<&mut Timestamp>,
max: Option<&mut Timestamp>,
) -> io::Result<()> {
let min_ptr = match min {
Some(min_ref) => &mut min_ref.0 as *mut byztime_stamp,
None => std::ptr::null_mut(),
};
let est_ptr = match est {
Some(est_ref) => &mut est_ref.0 as *mut byztime_stamp,
None => std::ptr::null_mut(),
};
let max_ptr = match max {
Some(max_ref) => &mut max_ref.0 as *mut byztime_stamp,
None => std::ptr::null_mut(),
};
if unsafe { byztime_get_global_time(ctx, min_ptr, est_ptr, max_ptr) } < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
pub trait Context: Sized {
fn as_mut_ptr(&self) -> *mut byztime_ctx;
fn close(self) -> io::Result<()>;
fn offset(&self) -> io::Result<(Timestamp, Timestamp, Timestamp)> {
let mut min = Timestamp::default();
let mut est = Timestamp::default();
let mut max = Timestamp::default();
offset_assign(
self.as_mut_ptr(),
Some(&mut min),
Some(&mut est),
Some(&mut max),
)?;
Ok((min, est, max))
}
fn global_time(&self) -> io::Result<(Timestamp, Timestamp, Timestamp)> {
let mut min = Timestamp::default();
let mut est = Timestamp::default();
let mut max = Timestamp::default();
global_time_assign(
self.as_mut_ptr(),
Some(&mut min),
Some(&mut est),
Some(&mut max),
)?;
Ok((min, est, max))
}
fn get_drift(&self) -> i64 {
unsafe { byztime_get_drift(self.as_mut_ptr()) }
}
fn set_drift(&self, drift_ppb: i64) {
unsafe { byztime_set_drift(self.as_mut_ptr(), drift_ppb) }
}
fn slew(
&self,
min_rate_ppb: i64,
max_rate_ppb: i64,
max_error: Option<Timestamp>,
) -> io::Result<()> {
let ret = unsafe {
byztime_slew(
self.as_mut_ptr(),
min_rate_ppb,
max_rate_ppb,
match max_error {
Some(e) => &e.0 as *const byztime_stamp,
None => std::ptr::null() as *const byztime_stamp,
},
)
};
if ret < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
fn step(&self) -> io::Result<()> {
let ret = unsafe { byztime_step(self.as_mut_ptr()) };
if ret < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
}
pub struct ConsumerContext {
ctx: *mut byztime_ctx,
}
unsafe impl Send for ConsumerContext {}
unsafe impl Sync for ConsumerContext {}
impl ConsumerContext {
pub fn open(path: &path::Path) -> io::Result<ConsumerContext> {
let path_vec = Vec::from(path.as_os_str().as_bytes());
let path_cstring =
CString::new(path_vec).map_err(|_| io::Error::from(errno::Errno(libc::ENOENT)))?;
let ctx = unsafe { byztime_open_ro(path_cstring.as_ptr() as *const i8) };
if ctx.is_null() {
Err(errno::errno().into())
} else {
Ok(ConsumerContext { ctx })
}
}
}
impl Context for ConsumerContext {
fn as_mut_ptr(&self) -> *mut byztime_ctx {
self.ctx
}
fn close(mut self) -> io::Result<()> {
let ctx = self.as_mut_ptr();
let ret = unsafe { byztime_close(ctx) };
self.ctx = std::ptr::null_mut();
if ret < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
}
impl Drop for ConsumerContext {
fn drop(&mut self) {
if self.as_mut_ptr() != std::ptr::null_mut() && unsafe { byztime_close(self.ctx) } < 0 {
panic!("byztime_close: {}", errno::errno());
}
}
}
pub struct ProviderContext {
ctx: *mut byztime_ctx,
}
unsafe impl Send for ProviderContext {}
unsafe impl Sync for ProviderContext {}
impl ProviderContext {
pub fn open(path: &path::Path) -> io::Result<ProviderContext> {
let path_str = path.to_str().ok_or(errno::Errno(libc::ENOENT))?;
let path_cstr = CString::new(path_str).map_err(|_| errno::Errno(libc::ENOENT))?;
let ctx = unsafe { byztime_open_rw(path_cstr.as_ptr() as *const i8) };
if ctx.is_null() {
Err(errno::errno().into())
} else {
Ok(ProviderContext { ctx })
}
}
pub fn set_offset(
&self,
offset: Timestamp,
error: Timestamp,
as_of: Timestamp,
) -> io::Result<()> {
let offset_ptr = &offset.0 as *const byztime_stamp;
let error_ptr = &error.0 as *const byztime_stamp;
let asof_ptr = &as_of.0 as *const byztime_stamp;
if unsafe { byztime_set_offset(self.ctx, offset_ptr, error_ptr, asof_ptr) } < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
fn offset_quick_assign(&self, offset: &mut Timestamp) {
let offset_ptr = &mut offset.0 as *mut byztime_stamp;
unsafe {
byztime_get_offset_quick(self.ctx, offset_ptr);
}
}
pub fn offset_quick(&self) -> Timestamp {
let mut offset = Timestamp::default();
self.offset_quick_assign(&mut offset);
offset
}
fn offset_raw_assign(
&self,
offset: &mut Timestamp,
error: &mut Timestamp,
as_of: &mut Timestamp,
) {
let offset_ptr = &mut offset.0 as *mut byztime_stamp;
let error_ptr = &mut error.0 as *mut byztime_stamp;
let as_of_ptr = &mut as_of.0 as *mut byztime_stamp;
unsafe {
byztime_get_offset_raw(self.ctx, offset_ptr, error_ptr, as_of_ptr);
}
}
pub fn offset_raw(&self) -> (Timestamp, Timestamp, Timestamp) {
let mut offset = Timestamp::default();
let mut error = Timestamp::default();
let mut as_of = Timestamp::default();
self.offset_raw_assign(&mut offset, &mut error, &mut as_of);
(offset, error, as_of)
}
pub fn update_real_offset(&self) -> io::Result<()> {
let ret = unsafe { byztime_update_real_offset(self.ctx) };
if ret < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
}
impl Context for ProviderContext {
fn as_mut_ptr(&self) -> *mut byztime_ctx {
self.ctx
}
fn close(mut self) -> io::Result<()> {
let ctx = self.as_mut_ptr();
let ret = unsafe { byztime_close(ctx) };
self.ctx = std::ptr::null_mut();
if ret < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
}
impl Drop for ProviderContext {
fn drop(&mut self) {
if self.as_mut_ptr() != std::ptr::null_mut() && unsafe { byztime_close(self.ctx) } < 0 {
panic!("byztime_close: {}", errno::errno());
}
}
}
#[cfg(any(test, feature = "with_quickcheck"))]
impl Arbitrary for Timestamp {
fn arbitrary<G: Gen>(g: &mut G) -> Timestamp {
Timestamp::new(g.gen(), g.gen_range(0, 1_000_000_000))
}
}
#[cfg(any(test, feature = "with_quickcheck"))]
impl Arbitrary for Era {
fn arbitrary<G: Gen>(g: &mut G) -> Era {
Era(g.gen())
}
}
pub fn install_sigbus_handler() -> io::Result<()> {
if unsafe { byztime_install_sigbus_handler(std::ptr::null_mut()) } < 0 {
Err(errno::errno().into())
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::*;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn equality_is_reflexive(t: Timestamp) -> bool {
t == t
}
fn denormalize<G: Gen>(g: &mut G, t: Timestamp) -> Timestamp {
loop {
let delta_s = g.gen_range(
i64::min_value() / 1_000_000_000,
i64::max_value() / 1_000_000_000,
);
let neg_delta_ns = delta_s * 1_000_000_000;
match (
t.seconds().checked_add(delta_s),
t.nanoseconds().checked_sub(neg_delta_ns),
) {
(Some(s), Some(ns)) => return Timestamp::new(s, ns),
_ => (),
}
}
}
struct TestDenormal {
t: Timestamp,
}
impl Testable for TestDenormal {
fn result<G: Gen>(&self, g: &mut G) -> TestResult {
let x = denormalize(g, self.t);
let y = denormalize(g, self.t);
TestResult::from_bool(x == y)
}
}
#[quickcheck]
fn equality_of_denormals(t: Timestamp) -> TestDenormal {
TestDenormal { t }
}
#[quickcheck]
fn t_plus_zero_is_t(t: Timestamp) -> bool {
t + Timestamp::default() == t
}
#[test]
fn half_plus_half_is_one() {
let x = Timestamp::new(0, 500_000_000);
let y = x + x;
assert_eq!(y.seconds(), 1);
assert_eq!(y.nanoseconds(), 0);
}
#[test]
#[should_panic]
fn addition_panics_on_overflow() {
let _ = Timestamp::max_value() + Timestamp::max_value();
}
#[quickcheck]
fn addition_is_associative(a: Timestamp, b: Timestamp, c: Timestamp) -> bool {
a.wrapping_add(b.wrapping_add(c)) == a.wrapping_add(b).wrapping_add(c)
}
#[quickcheck]
fn addition_is_commutative(a: Timestamp, b: Timestamp) -> bool {
a.wrapping_add(b) == b.wrapping_add(a)
}
#[quickcheck]
fn subtraction_is_negated_addition(a: Timestamp, b: Timestamp) -> bool {
a.wrapping_sub(b) == a.wrapping_add(Timestamp::default().wrapping_sub(b))
}
#[quickcheck]
fn scale_one(t: Timestamp) -> bool {
t.scale(1_000_000_000) == t
}
#[quickcheck]
fn scale_zero(t: Timestamp) -> bool {
t.scale(0) == Timestamp::default()
}
#[quickcheck]
fn scale_two(t: Timestamp) -> bool {
t.wrapping_scale(2_000_000_000) == t.wrapping_add(t)
}
#[quickcheck]
fn scale_half(t: Timestamp) -> bool {
t.scale(500_000_000) == t.halve()
}
#[quickcheck]
fn scale_neg_one(t: Timestamp) -> bool {
t.wrapping_scale(-1_000_000_000) == Timestamp::default().wrapping_sub(t)
}
#[quickcheck]
fn add_cmp(a: Timestamp, b: Timestamp) -> TestResult {
match a.checked_add(b) {
Some(c) => TestResult::from_bool(
(b > Timestamp::default()) && c > a
|| (b < Timestamp::default()) && c < a
|| (b == Timestamp::default()) && c == a,
),
None => TestResult::discard(),
}
}
#[test]
fn local_time_succeeds() {
Timestamp::local_time().expect("Failed to query local time");
}
#[test]
fn real_time_succeeds() {
Timestamp::real_time().expect("Failed to query real time");
}
}