reactio/
utils.rs

1extern crate libc;
2use std::{io::Write, mem::size_of, sync::atomic::AtomicI32};
3
4pub fn ref_to_bytes<T>(val: &T) -> &[u8] {
5    unsafe { std::slice::from_raw_parts(val as *const T as *const u8, size_of::<T>()) }
6}
7pub fn ref_to_bytes_mut<T>(val: &mut T) -> &mut [u8] {
8    unsafe { std::slice::from_raw_parts_mut(val as *mut T as *mut u8, size_of::<T>()) }
9}
10pub fn bytes_to_ref_mut<T>(buf: &mut [u8]) -> &mut T {
11    debug_assert!(
12        size_of::<T>() <= buf.len(),
13        "buf.len:{} < obj.size: {}",
14        buf.len(),
15        size_of::<T>()
16    );
17    unsafe {
18        let p = buf.as_mut_ptr() as *mut T;
19        &mut *p
20    }
21}
22pub fn bytes_to_ref<T>(buf: &[u8]) -> &T {
23    debug_assert!(
24        size_of::<T>() <= buf.len(),
25        "buf.len:{} < obj.size: {}",
26        buf.len(),
27        size_of::<T>()
28    );
29    unsafe {
30        let p = buf.as_ptr() as *const T;
31        &*p
32    }
33}
34
35pub fn localtime_r(seconds: i64, tm: &mut libc::tm) {
36    let t = seconds as libc::time_t;
37    unsafe {
38        #[cfg(target_os = "linux")]
39        {
40            libc::localtime_r(&t, tm);
41        }
42        #[cfg(not(target_os = "linux"))]
43        {
44            libc::localtime_s(tm, &t);
45        }
46    }
47}
48pub fn gmtime_r(seconds: i64, tm: &mut libc::tm) {
49    let t = seconds as libc::time_t;
50    unsafe {
51        #[cfg(target_os = "linux")]
52        {
53            libc::gmtime_r(&t, tm);
54        }
55        #[cfg(not(target_os = "linux"))]
56        {
57            libc::gmtime_s(tm, &t);
58        }
59    }
60}
61
62pub fn format_time(
63    buffer: &mut [u8],
64    nownanos: i64,
65    subsecond_digits: u32, // only be 0, 3, 6, 9
66    gmt_time: bool,
67) -> &str {
68    debug_assert!(
69        subsecond_digits == 0
70            || subsecond_digits == 3
71            || subsecond_digits == 6
72            || subsecond_digits == 9
73    );
74    debug_assert!(buffer.len() as u32 > 17 + subsecond_digits + 1);
75    let (seconds, nanos) = (nownanos / 1000000000, nownanos % 1000000000);
76    let mut tm: libc::tm = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
77
78    if gmt_time {
79        gmtime_r(seconds, &mut tm);
80    } else {
81        localtime_r(seconds, &mut tm);
82    }
83    write!(
84        &mut buffer[..],
85        "{:04}{:02}{:02}-{:02}:{:02}:{:02}",
86        (tm.tm_year + 1900),
87        tm.tm_mon + 1,
88        tm.tm_mday,
89        tm.tm_hour + 1,
90        tm.tm_min,
91        tm.tm_sec
92    )
93    .unwrap();
94    let mut n = 17usize;
95    if subsecond_digits > 0 && subsecond_digits < 10 {
96        if subsecond_digits == 0 {
97            write!(&mut buffer[n..], ".{:03}", nanos / 1000000).unwrap();
98        } else if subsecond_digits == 3 {
99            write!(&mut buffer[n..], ".{:06}", nanos / 1000).unwrap();
100        } else {
101            write!(&mut buffer[n..], ".{:09}", nanos).unwrap();
102        }
103        n += ((subsecond_digits / 3) * 3 + 1) as usize;
104    }
105    std::str::from_utf8(&buffer[..n]).unwrap()
106}
107
108pub fn now_nanos() -> i64 {
109    std::time::SystemTime::now()
110        .duration_since(std::time::SystemTime::UNIX_EPOCH)
111        .unwrap()
112        .as_nanos() as i64
113}
114
115// Useful when windows timespec_get has only low resolution.
116pub fn cpu_now_nanos() -> i64 {
117    let epoch: std::time::Instant = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
118    std::time::Instant::now().duration_since(epoch).as_nanos() as i64
119}
120
121pub struct Timer {
122    start: i64,
123    target: i64,
124}
125impl Timer {
126    pub fn new_millis(duration_millis: i64) -> Self {
127        let start = cpu_now_nanos();
128        Self {
129            start,
130            target: duration_millis * 1000000 + start,
131        }
132    }
133    pub fn expired(&self) -> bool {
134        cpu_now_nanos() >= self.target
135    }
136    pub fn elapsed_millis(&self) -> i64 {
137        (cpu_now_nanos() - self.start) / 1000000
138    }
139    /// sleep for max of duration_millis and target time.
140    pub fn sleep_or_expire(&self, duration_millis: i64) {
141        std::thread::sleep(std::time::Duration::from_millis(std::cmp::min(
142            duration_millis,
143            self.target - cpu_now_nanos(),
144        ) as u64));
145    }
146}
147
148pub const LOG_LEVEL_ERROR: i32 = 0;
149pub const LOG_LEVEL_INFO: i32 = 1;
150pub const LOG_LEVEL_TRACE: i32 = 2;
151pub const LOG_LEVEL_DEBUG: i32 = 3; // DEBUG/dbglog is only enabled in debug build.
152
153#[cfg(debug_assertions)]
154static GLOBAL_LOG_LEVEL: AtomicI32 = AtomicI32::new(LOG_LEVEL_TRACE);
155
156#[cfg(not(debug_assertions))]
157static GLOBAL_LOG_LEVEL: AtomicI32 = AtomicI32::new(LOG_LEVEL_INFO);
158
159pub fn can_log(severity: i32) -> bool {
160    severity <= GLOBAL_LOG_LEVEL.load(std::sync::atomic::Ordering::Acquire)
161}
162pub fn set_log_level(severity: i32) {
163    GLOBAL_LOG_LEVEL.store(severity, std::sync::atomic::Ordering::Release);
164}
165// MAYDO:  log sink, timestamp format, source location.
166#[macro_export]
167macro_rules! loginfo {
168    ($( $args:expr ),*) => {
169        if $crate::utils::can_log($crate::utils::LOG_LEVEL_INFO) {
170            let mut buf = [0u8; 40];
171            print!("[{}] [INFO] ", $crate::utils::format_time(&mut buf, $crate::utils::now_nanos(), 6, false));
172            println!( $( $args ),* );
173            // std::io::stdout().flush().unwrap();
174        }
175    }
176}
177
178#[macro_export]
179macro_rules! logerr {
180    ($( $args:expr ),*) => {
181        if $crate::utils::can_log($crate::utils::LOG_LEVEL_ERROR) {
182            let mut buf = [0u8; 40];
183            print!("[{}] [ERROR] ", $crate::utils::format_time(&mut buf, $crate::utils::now_nanos(), 6, false));
184            println!( $( $args ),* );
185            // std::io::stdout().flush().unwrap();
186        }
187    }
188}
189
190#[macro_export]
191macro_rules! logtrace {
192    ($( $args:expr ),*) => {
193        if $crate::utils::can_log($crate::utils::LOG_LEVEL_TRACE) {
194            let mut buf = [0u8; 40];
195            print!("[{}] [TRACE] ", $crate::utils::format_time(&mut buf, $crate::utils::now_nanos(), 6, false));
196            println!( $( $args ),* );
197            // std::io::stdout().flush().unwrap();
198        }
199    }
200}
201
202#[macro_export]
203#[cfg(debug_assertions)]
204/// log only in debug mode.
205macro_rules! dbglog {
206    ($( $args:expr ),*) => {
207        if $crate::utils::can_log($crate::utils::LOG_LEVEL_DEBUG) {
208            let mut buf = [0u8; 40];
209            print!("[{}] [DEBUG] ", $crate::utils::format_time(&mut buf, $crate::utils::now_nanos(), 6, false));
210            println!( $( $args ),* );
211            // std::io::stdout().flush().unwrap();
212        }
213    }
214}
215#[allow(unused_macros)]
216#[macro_export]
217#[cfg(not(debug_assertions))]
218macro_rules! dbglog {
219    ($( $args:expr ),*) => {
220        ()
221    };
222}
223
224#[cfg(test)]
225mod test {
226    use std::io::Write;
227
228    use crate::utils::{set_log_level, LOG_LEVEL_ERROR};
229
230    // use libc::write;
231
232    #[test]
233    pub fn test_vec_write() {
234        let mut v = Vec::<u8>::new();
235        // v.write_fmt(format_args!("hello{}", 2)).expect("failed to write to vec");
236        set_log_level(LOG_LEVEL_ERROR);
237        v.resize(10, 1);
238        let mut s = &mut v[..];
239        s.write_fmt(format_args!("hello{}", 2))
240            .expect("failed to write to slice");
241        loginfo!("vec size: {}", v.len());
242    }
243    #[test]
244    pub fn test_log() {
245        let mut _buf = [0u8; 32];
246        dbglog!("test dbglog.");
247        loginfo!("any msg");
248    }
249}