Skip to content
Snippets Groups Projects
Commit 5022ca50 authored by Loïc's avatar Loïc
Browse files

rust: add abstraction for vlc_tick_t and date_t

Adds abstractions for vlc_tick_t and date_t found in vlc_tick.h
The abstraction crate is vlcrs-tick with the raw bindings being in
vlcrs-tick-sys, generated from vlc_tick.h

vlc_tick_t -> Tick (with time unit having unique types)
date_t -> Date
parent 7849f49c
No related branches found
No related tags found
No related merge requests found
[workspace]
members = []
members = [
"vlcrs-tick"
]
[workspace.dependencies]
vlcrs-bindgen-helper = { path = "vlcrs-bindgen-helper/" }
[package]
name = "vlcrs-tick"
version = "0.0.0"
edition = "2021"
license = "LGPL-2.1-or-later"
[dependencies]
vlcrs-tick-sys = { path = "sys/" }
//! VLC Tick and Date abstractions.
use std::{
ffi::{c_char, CStr},
fmt::{Debug, Display},
mem::MaybeUninit,
ops::{Add, Mul, Sub},
};
use vlcrs_tick_sys::{
date_Change, date_Decrement, date_Increment, date_Init, date_t, vlc_tick_t, vlc_tick_to_str,
};
/// The VLC clock fequency
pub const CLOCK_FREQ: u64 = 1_000_000u64;
/// High precision date or time interval
///
/// Store a high precision date or time interval. The maximum precision is the
/// microsecond, and a 64 bits integer is used to avoid overflows (maximum
/// time interval is then 292271 years, which should be long enough for any
/// video). Dates are stored as microseconds since a common date (usually the
/// epoch).
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
#[doc(alias = "vlc_tick_t")]
pub struct Tick(pub(crate) vlc_tick_t);
impl Tick {
/// `VLC_TICK_0`
#[doc(alias = "VLC_TICK_0")]
pub const ZERO: Tick = Tick(1);
/// Minimum of a Tick
pub const MIN: Tick = Self::ZERO;
/// Maximum of a Tick
pub const MAX: Tick = Tick(vlc_tick_t::MAX);
/// `VLC_TICK_INVALID`
#[doc(alias = "VLC_TICK_INVALID")]
pub const INVALID: Tick = Tick(0);
/// Seconds to tick
///
/// ```
/// # use vlcrs_core::tick::Tick;
/// let two = Tick::from_seconds(Seconds::from(2.0f32));
/// ```
#[must_use]
#[inline]
pub fn from_seconds(s: Seconds) -> Tick {
Tick(s.0)
}
/// Miliseconds to tick
///
/// ```
/// # use vlcrs_core::tick::Tick;
/// let two = Tick::from_miliseconds(Miliseconds::from(2));
/// ```
#[must_use]
#[inline]
pub fn from_miliseconds(ms: Miliseconds) -> Tick {
Tick(ms.0)
}
/// Microseconds to tick
///
/// ```
/// # use vlcrs_core::tick::Tick;
/// let two = Tick::from_microseconds(Microseconds::from(2555u32));
/// ```
#[must_use]
#[inline]
pub fn from_microseconds(mc: Microseconds) -> Tick {
Tick(mc.0)
}
/// Nanoseconds to tick
///
/// ```
/// # use vlcrs_core::tick::Tick;
/// let two = Tick::from_microseconds(Microseconds::from(2555u32));
/// ```
#[must_use]
#[inline]
pub fn from_nanoseconds(ns: Nanoseconds) -> Tick {
Tick(ns.0)
}
/// Miliseconds to tick
///
/// ```
/// # use vlcrs_core::tick::Tick;
/// let two = Tick::from_samples(520, 10);
/// ```
#[must_use]
#[inline]
pub fn from_samples(samples: u64, rate: u64) -> Tick {
Tick((CLOCK_FREQ * samples / rate) as _)
}
/// To seconds from tick
///
/// ```
/// # use vlcrs_core::tick::Tick;
/// let two_tick = Tick::from_seconds(2.2);
/// let seconds = two_tick.to_seconds();
/// ```
#[must_use]
#[inline]
pub fn to_seconds(&self) -> f64 {
(self.0 as f64) / (CLOCK_FREQ as f64)
}
}
impl Default for Tick {
#[inline]
fn default() -> Self {
Tick::ZERO
}
}
impl Add for Tick {
type Output = Tick;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Tick(self.0 + rhs.0)
}
}
impl Sub for Tick {
type Output = Tick;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Tick(self.0 - rhs.0)
}
}
impl Mul for Tick {
type Output = Tick;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Tick(self.0 * rhs.0)
}
}
impl Debug for Tick {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for Tick {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut psz_buffer = [MaybeUninit::<c_char>::uninit(); 32];
f.write_str(
// SAFETY: psz_buffer is properly defined and the returned ptr is non-null
unsafe { CStr::from_ptr(vlc_tick_to_str(psz_buffer.as_mut_ptr().cast(), self.0)) }
.to_str()
.unwrap(),
)
}
}
// interal macro to create the From and Into impls for the givens unit-of-time
macro_rules! tu_impls {
($name:ident, $mul:literal, $(($t:ty, $i:ty)),+) => {
$(
impl From<$t> for $name {
fn from(a: $t) -> $name {
$name(if CLOCK_FREQ >= $mul {
((CLOCK_FREQ / $mul) as $i * (a as $i)) as _
} else {
((a as $i * CLOCK_FREQ as $i) / $mul as $i) as _
})
}
}
impl Into<$t> for $name {
fn into(self) -> $t {
((self.0 as $i / CLOCK_FREQ as $i) * $mul as $i) as $t
}
}
)+
}
}
// internal macro to create a unit-of-time and it's impls
macro_rules! tu {
($name:ident, $mul:literal) => {
#[doc = concat!("A ", stringify!($name), " unit-of-time")]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct $name(i64);
impl Add for $name {
type Output = $name;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
$name(self.0 + rhs.0)
}
}
impl Sub for $name {
type Output = $name;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
$name(self.0 - rhs.0)
}
}
tu_impls!(
$name,
$mul,
(i8, i64),
(i16, i64),
(i32, i64),
(i64, i64),
(u8, u64),
(u16, u64),
(u32, u64),
(u64, u64),
(f32, f64),
(f64, f64)
);
};
}
tu!(Nanoseconds, 1_000_000_000u64);
tu!(Microseconds, 1_000_000u64);
tu!(Miliseconds, 1_000u64);
tu!(Seconds, 1u64);
#[derive(Debug, Copy, Clone)]
#[doc(alias = "date_t")]
pub struct Date(date_t);
impl Date {
/// New date from `num` and `den`
#[doc(alias = "date_Init")]
#[inline]
pub fn new(num: u32, den: u32) -> Date {
let mut date = MaybeUninit::uninit();
// SAFETY: The pointer points to a well-aligned date_t
unsafe { date_Init(date.as_mut_ptr(), num, den) }
// SAFETY: `date` was properly initialize by `date_Init` above.
Date(unsafe { date.assume_init() })
}
/// Chage with `num` and `den`
#[doc(alias = "date_Change")]
#[inline]
pub fn change(&mut self, num: u32, den: u32) {
// SAFETY: The pointer points to a valid date_t
unsafe { date_Change(&mut self.0 as *mut _, num, den) }
}
/// Increment the date by `count` and return the timestamp
#[doc(alias = "date_Increment")]
#[inline]
pub fn increment(&mut self, count: u32) -> Tick {
// SAFETY: The pointer points to a valid date_t
Tick(unsafe { date_Increment(&mut self.0 as *mut _, count) })
}
/// Decrement the date by `count` and return the timestamp
#[doc(alias = "date_Decrement")]
#[inline]
pub fn decrement(&mut self, count: u32) -> Tick {
// SAFETY: The pointer points to a valid date_t
Tick(unsafe { date_Decrement(&mut self.0 as *mut _, count) })
}
/// Assign a tick to this date
#[doc(alias = "date_Set")]
#[inline]
pub fn set(&mut self, tick: Tick) {
// KEEP in sync with `date_Set`
self.0.i_remainder = 0;
self.0.date = tick.0;
}
/// Retrive the tick from this date
#[doc(alias = "date_Get")]
#[inline]
pub fn get(&self) -> Tick {
// KEEP in sync with `date_Get`
Tick(self.0.date)
}
}
[package]
name = "vlcrs-tick-sys"
version = "0.0.0"
edition = "2021"
license = "LGPL-2.1-or-later"
[build-dependencies]
vlcrs-bindgen-helper = { workspace = true }
fn main() {
vlcrs_bindgen_helper::generate_bindings(
&["vlc_tick.h"],
|builder| {
builder
.allowlist_function("date_.*")
.allowlist_function("vlc_tick_.*")
.allowlist_type("date_.*")
.allowlist_type("vlc_tick_.*")
},
);
}
#![allow(rustdoc::bare_urls)]
#![allow(rustdoc::broken_intra_doc_links)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment