1#![allow(unknown_lints, uncommon_codepoints)]
2#![allow(unused_imports)]
3
4#![cfg_attr(feature = "nightly", feature(test))]
5
6#![cfg_attr(feature = "re", feature(try_trait_v2))]
7#![cfg_attr(feature = "re", feature(never_type))]
8
9extern crate libc;
10#[cfg(feature = "crossterm")] extern crate crossterm;
11
12#[cfg(feature = "memchr")] use memchr::{memchr, memrchr};
13use core::any::Any;
14use core::arch::asm;
15use core::cell::UnsafeCell;
16use core::str::from_utf8_unchecked;
17use core::sync::atomic::{AtomicI8, AtomicUsize, Ordering};
18use std::fmt::{self, Write};
19use std::fs;
20use std::hint::spin_loop;
21use std::io::{self, Read};
22use std::mem::MaybeUninit;
23use std::ops::{Deref, DerefMut};
24use std::os::raw::c_int;
25use std::path::Path;
26use std::process::{Command, Stdio};
27use std::sync::Mutex;
28use std::thread;
29use std::time::{Duration, SystemTime, UNIX_EPOCH};
30
31#[inline] pub fn b2s (b: &[u8]) -> &str {unsafe {std::str::from_utf8_unchecked (b)}}
32
33pub fn filename<'a> (path: &'a str) -> &'a str {
37 let name = match path.rfind (|ch| ch == '/' || ch == '\\') {
42 Some (ofs) => &path[ofs+1..],
43 None => path};
44
45 if name.ends_with (".rs") {&name[0 .. name.len() - 3]} else {name}}
46
47#[macro_export] macro_rules! try_s {
54 ($e: expr) => {match $e {
55 Ok (ok) => ok,
56 Err (err) => {return Err (format! ("{}:{}] {}", $crate::filename (file!()), line!(), err));}}}}
57
58#[macro_export] macro_rules! try_f {
60 ($e: expr) => {match $e {
61 Ok (ok) => ok,
62 Err (err) => {return Err (From::from (format! ("{}:{}] {}", $crate::filename (file!()), line!(), err)));}}}}
63
64#[macro_export] macro_rules! try_sp {
66 ($e: expr) => {match $e {&Ok (ref ok) => ok,
67 &Err (ref err) => {return Err (From::from (format! ("{}:{}] {:?}", $crate::filename (file!()), line!(), err)));}}}}
68
69#[macro_export] macro_rules! try_fu {
81 ($e: expr) => {match $e {
82 Ok (ok) => ok,
83 Err (err) => {return Box::new (futures01::future::err (From::from (err)))}}}}
84
85#[macro_export] macro_rules! try_fus {
93 ($e: expr) => {match $e {
94 Ok (ok) => ok,
95 Err (err) => {return Box::new (futures01::future::err (ERRL! ("{}", err)))}}}}
96
97#[macro_export] macro_rules! ERRL {
99 ($format: expr, $($args: tt)+) => {format! (concat! ("{}:{}] ", $format), $crate::filename (file!()), line!(), $($args)+)};
100 ($format: expr) => {format! (concat! ("{}:{}] ", $format), $crate::filename (file!()), line!())}}
101
102#[macro_export] macro_rules! ERR {
106 ($format: expr, $($args: tt)+) => {Err ($crate::ERRL! ($format, $($args)+))};
107 ($format: expr) => {Err ($crate::ERRL! ($format))}}
108
109#[cfg(feature = "base62")]
110pub mod base62;
111
112#[cfg(feature = "base62j")]
113pub mod base62j;
114
115#[cfg(all(feature = "base91", feature = "nightly"))]
116pub mod base91;
117
118#[cfg(feature = "re")]
119pub mod re;
120
121#[cfg(feature = "lines")]
122pub mod lines;
123
124#[cfg(feature = "link")]
125pub mod link;
126
127#[cfg(all(feature = "crossterm", not(target_arch = "wasm32")))]
130fn isatty (fd: c_int) -> c_int {unsafe {libc::isatty (fd)}}
131
132#[cfg(all(feature = "crossterm", target_arch = "wasm32"))]
133fn isatty (_fd: c_int) -> c_int {0}
134
135#[cfg(feature = "crossterm")]
136static mut STATUS_LINE: Mutex<String> = Mutex::new (String::new());
137
138#[cfg(feature = "crossterm")]
139pub struct IsTty {pub is_tty: AtomicI8}
140#[cfg(feature = "crossterm")]
141impl core::ops::Deref for IsTty {
142 type Target = bool;
143 fn deref (&self) -> &Self::Target {
144 let mut is_tty = self.is_tty.load (Ordering::Relaxed);
145 if is_tty == 0 {
146 is_tty = if isatty (1) != 0 {1} else {-1};
148 self.is_tty.store (is_tty, Ordering::Relaxed)}
149 if is_tty == 1 {&true} else {&false}}}
150
151#[cfg(feature = "crossterm")]
153pub static ISATTY: IsTty = IsTty {is_tty: AtomicI8::new (0)};
154
155#[cfg(feature = "crossterm")]
161pub static STATUS_LINE_LM: AtomicUsize = AtomicUsize::new (0);
162
163#[cfg(feature = "crossterm")]
165fn delete_line (stdout: &mut io::Stdout) {
166 use crossterm::{terminal, QueueableCommand};
167
168 let _ = stdout.queue (terminal::Clear (terminal::ClearType::UntilNewLine));}
169
170 #[cfg(all(feature = "crossterm"))]
195pub fn status_line (file: &str, line: u32, status: &str) {
196 use crossterm::{QueueableCommand, cursor};
197 use io::{stdout, Write};
198 use std::collections::hash_map::DefaultHasher;
199 use std::hash::Hasher;
200
201 if let Ok (mut status_line) = unsafe {STATUS_LINE.lock()} {
202 let mut stdout = stdout();
203 let old_hash = {let mut hasher = DefaultHasher::new(); hasher.write (status_line.as_bytes()); hasher.finish()};
204 status_line.clear();
205 use std::fmt::Write;
206 let _ = write! (&mut *status_line, "{}:{}] {}", filename (file), line, status);
207 let new_hash = {let mut hasher = DefaultHasher::new(); hasher.write (status_line.as_bytes()); hasher.finish()};
208 if old_hash != new_hash {
209 STATUS_LINE_LM.s (now_ms() as usize);
210
211 match crossterm::terminal::size() {
213 Ok ((w, _)) if status_line.chars().count() >= w as usize => {
214 let mut tmp = String::with_capacity (w as usize - 1);
215 for ch in status_line.chars().take (w as usize - 1) {tmp.push (ch)}
216 let _ = stdout.write (tmp.as_bytes());},
217 _ => {let _ = stdout.write (status_line.as_bytes());}};
218
219 delete_line (&mut stdout);
220 let _ = stdout.queue (cursor::MoveToColumn (0));
221 let _ = stdout.flush();}}}
222
223#[cfg(feature = "crossterm")]
225pub fn status_line_clear() -> String {
226 use io::{stdout, Write};
227 let mut ret = String::new();
228 if let Ok (mut status_line) = unsafe {STATUS_LINE.lock()} {
229 if *ISATTY && !status_line.is_empty() {
230 let mut stdout = stdout();
231 STATUS_LINE_LM.s (now_ms() as usize);
232 core::mem::swap (&mut ret, &mut status_line);
233 delete_line (&mut stdout);
234 let _ = stdout.flush();}}
235 ret}
236
237#[cfg(feature = "crossterm")]
241pub fn with_status_line (code: &dyn Fn()) {
242 use crossterm::{QueueableCommand, cursor};
243 use io::{stdout, Write};
244
245 if let Ok (status_line) = unsafe {STATUS_LINE.lock()} {
246 if !*ISATTY || status_line.is_empty() {
247 code()
248 } else {
249 let mut stdout = stdout();
250 delete_line (&mut stdout);
251 let _ = stdout.flush(); code();
253 let _ = stdout.write (status_line.as_bytes());
255 let _ = stdout.queue (cursor::MoveToColumn (0));
256 let _ = stdout.flush();}}}
257
258#[cfg(feature = "crossterm")]
259#[test] fn test_status_line() {
260 with_status_line (&|| println! ("hello world"));}
261
262#[cfg(all(feature = "inlinable_string", feature = "fomat-macros"))]
263pub fn short_log_time (ms: u64) -> inlinable_string::InlinableString {
264 use fomat_macros::wite;
265 let iso = ms2iso8601 (ms as i64);
266 ifomat! ((&iso[8..10]) ' ' (&iso[11..19]))}
267
268#[cfg(all(feature = "crossterm", feature = "inlinable_string", feature = "fomat-macros"))]
269#[macro_export] macro_rules! log {
270
271 ($on: literal, $($args: tt)+) => { if $on & 1 == 1 {log! ($($args)+)}};
273
274 (t $time: expr => $delay: expr, $($args: tt)+) => {{ static LL: core::sync::atomic::AtomicI64 = core::sync::atomic::AtomicI64::new (0);
276 let now = $time as i64;
277 let Δ = now - LL.load (core::sync::atomic::Ordering::Relaxed);
278 if $delay <= Δ {
279 LL.store (now, core::sync::atomic::Ordering::Relaxed);
280 log! ($($args)+)}}};
281
282 (q $command: expr, $($args: tt)+) => {{
283 $crate::with_status_line (&|| {
284 use crossterm::QueueableCommand;
285 use fomat_macros::{wite, fomat};
286 use std::io::Write;
287 let tty = *$crate::ISATTY;
288 let mut stdout = std::io::stdout();
289 if tty {let _ = stdout.queue ($command);}
290 let _ = wite! (&mut stdout,
291 ($crate::short_log_time ($crate::now_ms())) ' '
292 ($crate::filename (file!())) ':' (line!()) "] "
293 $($args)+ '\n');
294 if tty {let _ = stdout.queue (crossterm::style::ResetColor);}
295 let _ = stdout.flush();})}};
296
297 (c $color: expr, $($args: tt)+) => {
299 log! (q crossterm::style::SetForegroundColor ($color), $($args)+)};
300
301 (a $ansi: expr, $($args: tt)+) => {
304 log! (q crossterm::style::SetForegroundColor (
305 crossterm::style::Color::AnsiValue ($ansi)), $($args)+)};
306
307 ($($args: tt)+) => {{
308 $crate::with_status_line (&|| {
309 use fomat_macros::{pintln, fomat};
310 pintln! (
311 ($crate::short_log_time ($crate::now_ms())) ' '
312 ($crate::filename (file!())) ':' (line!()) "] "
313 $($args)+);})}};}
314
315#[cfg(all(feature = "crossterm", feature = "inlinable_string", feature = "fomat-macros"))]
316#[test] fn test_log() {
317 log! ([= 2 + 2])}
318
319#[macro_export] macro_rules! gstring {($array: ident, $code: block) => {{
337 let end = {
338 let mut $array = ::std::io::Cursor::new (&mut $array[..]);
339 let $array = &mut $array;
340 $code;
341 $array.position() as usize};
342 let s = unsafe {::core::str::from_utf8_unchecked (&$array[0..end])};
343 s}}}
344
345#[cfg(feature = "fomat-macros")]
352#[macro_export] macro_rules! ifomat {
353 ($($args: tt)+) => ({
354 use inlinable_string::{InlinableString, StringExt};
355 use std::fmt::Write as FmtWrite;
356 let mut is = InlinableString::new();
357 wite! (&mut is, $($args)+) .expect ("!wite");
358 is})}
359
360pub const fn civil_from_days (mut z: i32) -> (i32, u32, u32) {
364 z += 719468;
365 let era = if 0 <= z {z} else {z - 146096} / 146097;
366 let doe = (z - era * 146097) as u32; let yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; let y = yoe as i32 + era * 400;
369 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = doy - (153 * mp + 2) / 5 + 1; let m = (mp as i32 + if (mp as i32) < 10 {3} else {-9}) as u32; (y + if m <= 2 {1} else {0}, m, d)}
374
375pub const fn days_from_civil (mut y: i32, m: u32, d: u32) -> i32 {
377 y -= if m <= 2 {1} else {0};
378 let era = if 0 <= y {y} else {y - 399} / 400;
379 let yoe = (y - era * 400) as u32; let doy = (153 * (m as i32 + (if 2 < m {-3} else {9})) + 2) as u32 / 5 + d - 1; let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; era * 146097 + doe as i32 - 719468}
383
384#[cfg(feature = "chrono")]
386pub fn ldt2ics (dt: &chrono::DateTime<chrono::Local>) -> i64 {
387 use chrono::{Datelike, Timelike};
388 let y = dt.year() as i64;
389 let m = dt.month() as i64;
390 let d = dt.day() as i64;
391 let ti = dt.time();
392 let h = ti.hour() as i64;
393 let min = ti.minute() as i64;
394 let s = ti.second() as i64;
395 let cs = ti.nanosecond() as i64 / 10000000;
396 y%1000 * 1e12 as i64 + m * 10000000000 + d * 100000000 + h * 1000000 + min * 10000 + s * 100 + cs}
397
398#[cfg(feature = "inlinable_string")]
400pub fn ms2iso8601 (ms: i64) -> inlinable_string::InlinableString {
401 use inlinable_string::{InlinableString, StringExt};
402 use std::fmt::Write as FmtWrite;
403 let day = (ms / 1000 / 86400) as i32;
404 let h = ((ms / 1000) % 86400) / 3600;
405 let min = ((ms / 1000) % 3600) / 60;
406 let s = (ms / 1000) % 60;
407 let ms = ms % 1000;
408 let (y, m, d) = civil_from_days (day);
409 let mut is = inlinable_string::InlinableString::new();
410 write! (&mut is, "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z", y, m, d, h, min, s, ms) .expect ("!write!");
412 is}
413
414pub const fn ms2ics (ms: i64) -> i64 {
416 let day = (ms / 1000 / 86400) as i32;
417 let h = ((ms / 1000) % 86400) / 3600;
418 let min = ((ms / 1000) % 3600) / 60;
419 let s = (ms / 1000) % 60;
420 let cs = ms % 1000 / 10;
421 let (y, m, d) = civil_from_days (day);
422 let y = y as i64; let m = m as i64; let d = d as i64;
423 y%1000 * 1e12 as i64 + m * 10000000000 + d * 100000000 + h * 1000000 + min * 10000 + s * 100 + cs}
424
425pub const fn ics2ms (ics: i64) -> i64 {
427 let day = days_from_civil (
428 (ics / 1000000000000 + 2000) as i32,
429 (ics / 10000000000 % 100) as u32,
430 (ics / 100000000 % 100) as u32) as i64;
431 let tm_hour = (ics / 1000000 % 100) as i64;
432 let tm_min = (ics / 10000 % 100) as i64;
433 let tm_sec = (ics / 100 % 100) as i64;
434 let ms = (ics % 100 * 10) as i64;
435 ms + tm_sec * 1000 + tm_min * 60000 + tm_hour * 3600000 + day * 86400000}
436
437#[cfg(all(feature = "chrono", feature = "re"))]
439pub fn ics2ldt (ims: i64) -> re::Re<chrono::DateTime<chrono::Local>> {
440 use chrono::{TimeZone, Timelike};
441 let dt = chrono::Local.with_ymd_and_hms (
442 (ims / 1000000000000 + 2000) as i32,
443 (ims / 10000000000 % 100) as u32,
444 (ims / 100000000 % 100) as u32,
445 (ims / 1000000 % 100) as u32,
446 (ims / 10000 % 100) as u32,
447 (ims / 100 % 100) as u32) .earliest()?;
448 let dt = dt.with_nanosecond ((ims % 100) as u32 * 10000000)?;
449 re::Re::Ok (dt)}
450
451#[cfg(all(feature = "chrono", feature = "re"))]
453pub fn ics2ndt (ims: i64) -> re::Re<chrono::NaiveDateTime> {
454 use chrono::{TimeZone, Timelike};
455 let dt = chrono::NaiveDateTime::new (
456 chrono::NaiveDate::from_ymd_opt (
457 (ims / 1000000000000 + 2000) as i32,
458 (ims / 10000000000 % 100) as u32,
459 (ims / 100000000 % 100) as u32)?,
460 chrono::NaiveTime::from_hms_nano_opt (
461 (ims / 1000000 % 100) as u32,
462 (ims / 10000 % 100) as u32,
463 (ims / 100 % 100) as u32,
464 (ims % 100) as u32 * 10000000)?);
465 re::Re::Ok (dt)}
466
467#[cfg(feature = "fomat-macros")]
470#[macro_export] macro_rules! iso8601z {
471 ($date_or_time: expr) => {{
472 let sufff = ($date_or_time.len() as i32 - 10) .max (0) as usize;
473 ifomat! (($date_or_time) if sufff < 10 {(&"T00:00:00Z"[sufff..])})}}}
474
475#[cfg(feature = "fomat-macros")]
477#[macro_export] macro_rules! iso8601toL {($short: expr) => {
478 Local.from_local_datetime (&(DateTime::parse_from_rfc3339 (&iso8601z! ($short))?) .naive_utc()) .earliest()?}}
479
480pub fn iso8601ics (iso: &[u8]) -> i64 {
482 let mut ics: [u8; 14] = *b"00000000000000";
483 if 4 <= iso.len() {
484 ics[0] = iso[2]; ics[1] = iso[3];
485 if 7 <= iso.len() && iso[4] == b'-' {
486 ics[2] = iso[5]; ics[3] = iso[6];
487 if 10 <= iso.len() && iso[7] == b'-' {
488 ics[4] = iso[8]; ics[5] = iso[9];
489 if 13 <= iso.len() && (iso[10] == b'T' || iso[10] == b' ') {
490 ics[6] = iso[11]; ics[7] = iso[12];
491 if 16 <= iso.len() && iso[13] == b':' {
492 ics[8] = iso[14]; ics[9] = iso[15];
493 if 19 <= iso.len() && iso[16] == b':' {
494 ics[10] = iso[17]; ics[11] = iso[18];
495 if 22 <= iso.len() && iso[19] == b'.' {
496 ics[12] = iso[20]; ics[13] = iso[21]}}}}}}}
497 match b2s (&ics) .parse() {Ok (k) => k, Err (_err) => 0}}
498
499#[cfg(all(test, feature = "nightly", feature = "chrono", feature = "inlinable_string", feature = "re"))] mod time_bench {
500 extern crate test;
501 use chrono::{DateTime, Datelike, Local, NaiveDateTime, TimeZone, Timelike, Utc};
502 use crate::{civil_from_days, days_from_civil, ics2ldt, ics2ms, ics2ndt, iso8601ics, ldt2ics, ms2ics, ms2iso8601, now_ms, re::Re};
503 use fomat_macros::wite;
504 use inlinable_string::InlinableString;
505 use rand::{rngs::SmallRng, seq::index::sample, Rng, SeedableRng};
506 use test::black_box;
507
508 #[bench] fn duration (bm: &mut test::Bencher) {
509 assert! (946684800000 == days_from_civil (2000, 1, 1) as i64 * 86400 * 1000);
510 let duration = 118050;
511 assert! ("00:01:58.050" == &ms2iso8601 (946684800000 + duration) [11..23]);
512 assert! ( 15805 == ms2ics (946684800000 + duration) - 10100000000);
513 let mut ms = 0;
514 bm.iter (|| { let ics = ms2ics (946684800000 + ms as i64) - 10100000000;
516 let tm_min = (ics / 10000 % 100) as i64;
517 let tm_sec = (ics / 100 % 100) as i64;
518 let ims = (ics % 100 * 10) as i64;
519 let msʹ = ims + tm_sec * 1000 + tm_min * 60000;
520 assert! (ms == msʹ);
521 ms += 10;
522 if 3600 * 1000 <= ms {ms = 0}})}
523
524 #[bench] fn iso8601icsᵇ (bm: &mut test::Bencher) {
525 assert! (00000000000000 == iso8601ics (b""));
526 assert! (24000000000000 == iso8601ics (b"2024"));
527 assert! (24120000000000 == iso8601ics (b"2024-12"));
528 assert! (24121300000000 == iso8601ics (b"2024-12-13"));
529 assert! (24121321000000 == iso8601ics (b"2024-12-13T21"));
530 assert! (24121321000000 == iso8601ics (b"2024-12-13T21+03"));
531 assert! (24121314150000 == iso8601ics (b"2024-12-13T14:15"));
532 assert! (24121314150000 == iso8601ics (b"2024-12-13 14:15"));
533 assert! (24121314151600 == iso8601ics (b"2024-12-13T14:15:16"));
534 assert! (24121314151600 == iso8601ics (b"2024-12-13 14:15:16"));
535 assert! (24121314151698 == iso8601ics (b"2024-12-13T14:15:16.98"));
536 assert! (24121314151698 == iso8601ics (b"2024-12-13T14:15:16.980"));
537 assert! (24121314151698 == iso8601ics (b"3024-12-13T14:15:16.980Z"));
538 assert! (24121314151698 == iso8601ics (b"2024-12-13T14:15:16.980-03"));
539 bm.iter (|| {
540 let ics = iso8601ics (b"4321-12-23T13:14:15.987");
541 assert! (black_box (ics) == 21122313141598, "{}", ics)})}
542
543 #[bench] fn ms2iso8601ᵇ (bm: &mut test::Bencher) {
544 let mut rng = SmallRng::seed_from_u64 (now_ms());
545 bm.iter (|| {
546 let it = ms2iso8601 (rng.gen::<i64>().abs());
547 assert! (black_box (it) .ends_with ('Z'))})}
548
549 #[bench] fn chrono_iso8601 (bm: &mut test::Bencher) {
550 let dt = Utc::now();
551 bm.iter (|| {
552 let it = ifomat! ((dt.format ("%Y-%m-%dT%H:%M:%S%.3fZ")));
553 assert! (it.ends_with ('Z'))})}
554
555 fn make_samples() -> Vec<(DateTime<Local>, InlinableString)> {
556 let mut rng = SmallRng::seed_from_u64 (now_ms());
557 let mut samples = Vec::with_capacity (65536);
558 while samples.len() < samples.capacity() {
559 let ms = rng.gen::<i64>().abs() / 10 * 10;
560 if let Some (udt) = Utc.timestamp_millis_opt (ms) .earliest() {
561 let day = (ms / 1000 / 86400) as i32;
562 let (y, m, d) = civil_from_days (day);
563 assert! (udt.year() == y && udt.month() == m && udt.day() == d, "{:?}, y{}, m{}, d{}", udt, y, m, d);
564 assert! (days_from_civil (y, m, d) == day);
565 let cit = ifomat! ((udt.format ("%Y-%m-%dT%H:%M:%S%.3fZ")));
566 let cit = if cit.starts_with ('+') {&cit[1..]} else {&cit};
567 let dit = ms2iso8601 (ms);
568 assert! (cit == dit, "{} <> {}", cit, dit);
569 if let Some (udt) = udt.with_year (2000 + udt.year() % 100) {
570 let ms = udt.timestamp_millis();
571 let ics = ms2ics (ms);
572 let udtʹ = Utc.from_utc_datetime (&ics2ndt (ics) .unwrap());
573 assert! (udt == udtʹ, "{:?} <> {:?}", udt, udtʹ);
574 let msʹ = ics2ms (ics);
575 assert! (ms == msʹ, "{} <> {}", ms, msʹ)}}
576 if let Some (ldt) = Local.timestamp_millis_opt (ms) .earliest() {
577 if let Some (ldt) = ldt.with_year (2000 + ldt.year() % 100) {
578 let sdt = ifomat! ((ldt.format ("%Y-%m-%dT%H:%M:%S%.3f")));
579 samples.push ((ldt, sdt))}}}
580 samples}
581
582 #[bench] fn iso8601tol (bm: &mut test::Bencher) {
583 let (samples, mut sx) = (make_samples(), 0);
584 bm.iter (|| {
585 let ics = iso8601ics (samples[sx].1.as_bytes());
586 let dt = ics2ldt (ics) .unwrap();
587 let odt = &samples[sx].0;
588 assert! (
589 dt.year() == odt.year() && dt.month() == odt.month() && dt.day() == odt.day()
590 && dt.hour() == odt.hour() && dt.minute() == odt.minute() && dt.second() == odt.second()
591 && dt.nanosecond() == odt.nanosecond(),
592 "{} {} <> {} {}", ics, dt, samples[sx].1, odt);
593 sx += 1; if samples.len() <= sx {sx = 0}})}
594
595 #[bench] fn iso8601ton (bm: &mut test::Bencher) {
596 let (samples, mut sx) = (make_samples(), 0);
597 bm.iter (|| {
598 let ics = iso8601ics (samples[sx].1.as_bytes());
599 let dt = ics2ndt (ics) .unwrap();
600 let odt = &samples[sx].0;
601 assert! (
602 dt.year() == odt.year() && dt.month() == odt.month() && dt.day() == odt.day()
603 && dt.hour() == odt.hour() && dt.minute() == odt.minute() && dt.second() == odt.second()
604 && dt.nanosecond() == odt.nanosecond(),
605 "{} {} <> {} {}", ics, dt, samples[sx].1, odt);
606 sx += 1; if samples.len() <= sx {sx = 0}})}
607
608 #[bench] fn chrono_from_str (bm: &mut test::Bencher) {bm.iter (|| {
609 let dt = NaiveDateTime::parse_from_str ("4321-12-23T13:14:15", "%Y-%m-%dT%H:%M:%S") .unwrap();
610 assert! (dt.year() == 4321 && dt.month() == 12 && dt.day() == 23
611 && dt.hour() == 13 && dt.minute() == 14 && dt.second() == 15)})}
612
613 #[bench] fn chrono_from_rfc3339 (bm: &mut test::Bencher) {bm.iter (|| {
614 let dt = DateTime::parse_from_rfc3339 ("4321-12-23T13:14:15Z") .unwrap();
615 assert! (dt.year() == 4321 && dt.month() == 12 && dt.day() == 23
616 && dt.hour() == 13 && dt.minute() == 14 && dt.second() == 15)})}
617
618 #[bench] fn iso8601tol_macro (bm: &mut test::Bencher) {bm.iter (|| {
619 fn f() -> Re<DateTime<Local>> {Re::Ok (iso8601toL! ("4321-12-23T13:14:15"))}
620 let dt = f().unwrap();
621 assert! (dt.year() == 4321 && dt.month() == 12 && dt.day() == 23
622 && dt.hour() == 13 && dt.minute() == 14 && dt.second() == 15)})}
623
624 #[bench] fn iso8601_ics_ms (bm: &mut test::Bencher) {bm.iter (|| {
625 let ics = iso8601ics (black_box (b"4321-12-23T13:14:15"));
626 assert! (ics == 21122313141500);
627 assert! (ics2ms (black_box (ics)) == 1640265255000)})}}
628
629pub fn netstring (at: &[u8]) -> Result<(&[u8], &[u8]), String> {
636 let length_end = match at.iter().position (|&ch| ch < b'0' || ch > b'9') {Some (l) if l > 0 => l, _ => return ERR! ("No len.")};
637 match at.get (length_end) {Some (&ch) if ch == b':' => (), _ => return ERR! ("No colon.")};
638 let length = b2s (&at[0 .. length_end]);
639 let length: usize = try_s! (length.parse());
640 let bulk_pos = 0 + length_end + 1;
641 let bulk_end = bulk_pos + length;
642 match at.get (bulk_end) {Some (&ch) if ch == b',' => (), _ => return ERR! ("No comma.")}
643 Ok ((&at[bulk_pos .. bulk_end], &at[bulk_end + 1 ..]))}
644
645#[cfg(unix)]
647pub fn with_hostname (visitor: &mut dyn FnMut (&[u8])) -> Result<(), std::io::Error> {
648 use libc::{size_t, gethostname}; use std::ffi::CStr;
650
651 let mut buf = [0; 128];
652 let rc = unsafe {gethostname (buf.as_mut_ptr(), (buf.len() - 1) as size_t)};
653 if rc == 0 {
654 let cs = unsafe {CStr::from_ptr (buf.as_ptr())};
655 Ok (visitor (cs.to_bytes()))
656 } else {
657 Err (io::Error::last_os_error())}}
658
659#[cfg(unix)] #[test] fn test_hostname() {
660 let mut hostname = String::new();
661 with_hostname (&mut |bytes| hostname = String::from_utf8_lossy (bytes) .into_owned()) .unwrap();}
662
663pub fn slurp (path: &dyn AsRef<Path>) -> Vec<u8> {
668 let Ok (mut file) = fs::File::open (path) else {return Vec::new()};
669 let mut buf = Vec::new();
670 if let Err (_err) = file.read_to_end (&mut buf) {return Vec::new()}
672 buf}
673
674pub fn slurp_prog (command: &str) -> Result<String, String> {
680 let output = match Command::new ("dash") .arg ("-c") .arg (command) .output() {
681 Ok (output) => output,
682 Err (ref err) if err.kind() == io::ErrorKind::NotFound => { try_s! (Command::new ("sh") .arg ("-c") .arg (command) .output())},
684 Err (err) => return ERR! ("{}", err)};
685
686 let combined_output: String = if output.stderr.is_empty() {
687 try_s! (String::from_utf8 (output.stdout))
688 } else if output.stdout.is_empty() {
689 try_s! (String::from_utf8 (output.stderr))
690 } else {
691 let mut buf = String::with_capacity (output.stderr.len() + output.stdout.len());
692 buf.push_str (try_s! (std::str::from_utf8 (&output.stderr[..])));
693 buf.push_str (try_s! (std::str::from_utf8 (&output.stdout[..])));
694 buf};
695
696 if output.status.success() {Ok (combined_output)} else {Err (combined_output)}}
697
698#[test] fn test_slurp_prog() {
699 if cfg! (windows) {println! ("Skipping as dash might be missing on Windows sometimes"); return}
700 let foo = match slurp_prog ("echo foo") {Ok (foo) => foo, Err (err) => panic! ("{}", err)};
701 assert_eq! (foo.trim(), "foo");}
702
703pub fn cmd (cmd: &str) -> Result<(), String> {
705 println! ("$ {}", cmd);
706 let status = try_s! (Command::new ("dash") .arg ("-c") .arg (cmd) .stdout (Stdio::inherit()) .stderr (Stdio::inherit()) .status());
707 if !status.success() {Err (format! ("Command returned an error status: {}", status))} else {Ok(())}}
708
709pub fn any_to_str<'a> (message: &'a dyn Any) -> Option<&'a str> {
718 if let Some (message) = message.downcast_ref::<&str>() {return Some (message)}
719 if let Some (message) = message.downcast_ref::<String>() {return Some (&message[..])}
720 return None}
721
722pub fn duration_to_float (duration: Duration) -> f64 {
726 duration.as_secs() as f64 + ((duration.subsec_nanos() as f64) / 1000000000.0)}
727
728#[cfg(feature = "chrono")]
730pub fn dtl2float (dt: chrono::DateTime<chrono::Local>) -> f64 {
731 dt.timestamp() as f64 + ((dt.timestamp_subsec_nanos() as f64) / 1000000000.0)}
732
733pub fn ms2sec (ms: u64) -> f64 {
735 (ms / 1000) as f64 + ((ms % 1000) as f64 / 1000.0)}
736
737pub fn now_float() -> f64 {
741 let now = SystemTime::now().duration_since (UNIX_EPOCH) .expect ("!duration_since");
742 duration_to_float (now)}
743
744#[test] fn test_now_float() {
745 let now = SystemTime::now().duration_since (UNIX_EPOCH) .expect ("!duration_since") .as_secs();
746 let t1 = now_float();
747 assert_eq! (now, t1 as u64);
748 thread::sleep (Duration::from_millis (100));
749 let t2 = now_float();
750 let delta = t2 - t1;
751 assert! (delta >= 0.098 && delta <= 0.150, "delta: {}", delta);}
752
753pub fn duration_to_ms (duration: Duration) -> u64 {
755 duration.as_secs() * 1000 + (duration.subsec_nanos() / 1000000) as u64}
756
757pub fn now_ms() -> u64 {
759 let now = SystemTime::now().duration_since (UNIX_EPOCH) .expect ("!duration_since");
760 duration_to_ms (now)}
761
762#[test] fn test_now_ms() {
763 let t1 = now_ms();
764 thread::sleep (Duration::from_millis (100));
765 let t2 = now_ms();
766 let delta = t2 - t1;
767 assert! (delta >= 98 && delta <= 150, "delta: {}", delta);}
768
769pub fn last_modified_sec (path: &dyn AsRef<Path>) -> Result<f64, String> {
776 let meta = match path.as_ref().metadata() {
777 Ok (m) => m,
778 Err (ref err) if err.kind() == std::io::ErrorKind::NotFound => return Ok (0.),
779 Err (err) => return ERR! ("{}", err)};
780 let lm = try_s! (meta.modified());
781 let lm = duration_to_float (try_s! (lm.duration_since (UNIX_EPOCH)));
782 Ok (lm)}
783
784#[cfg(target_arch="x86_64")]
790pub fn rdtsc() -> u64 {unsafe {core::arch::x86_64::_rdtsc()}}
791
792#[cfg(not (target_arch="x86_64"))]
793pub fn rdtsc() -> u64 {SystemTime::now().duration_since (SystemTime::UNIX_EPOCH) .expect ("!now") .as_nanos() as u64}
794
795#[cfg(target_arch="x86_64")]
796#[test] fn test_rdtsc() {
797 assert! (rdtsc() != rdtsc())}
798
799pub struct FileLock<'a> {
802 pub lock_path: &'a dyn AsRef<Path>,
804 pub ttl_sec: f64,
806 pub file: std::fs::File}
808impl<'a> FileLock<'a> {
809 pub fn lock (lock_path: &'a dyn AsRef<Path>, ttl_sec: f64) -> Result<Option<FileLock<'a>>, String> {
819 let mut cycle = 0u8;
820 loop {
821 if cycle > 1 {break Ok (None)} cycle += 1;
823 let mut fo = std::fs::OpenOptions::new();
824 match fo.read (true) .write (true) .create_new (true) .open (lock_path.as_ref()) {
825 Ok (file) => break Ok (Some (FileLock {lock_path, ttl_sec, file})),
826 Err (ref ie) if ie.kind() == std::io::ErrorKind::AlreadyExists => {
827 let lm = match last_modified_sec (lock_path) {
829 Ok (lm) => lm,
830 Err (ie) => break ERR! ("Error checking {:?}: {}", lock_path.as_ref(), ie)};
831 if lm == 0. {continue} if now_float() - lm > ttl_sec {
833 if let Err (err) = std::fs::remove_file (lock_path.as_ref()) {break ERR! ("Error removing {:?}: {}", lock_path.as_ref(), err)}
834 continue}
835 break Ok (None)},
836 Err (ie) => break ERR! ("Error creating {:?}: {}", lock_path.as_ref(), ie)}}}
837 #[cfg(target_os = "linux")]
840 pub fn touch (&self) -> Result<(), String> {
841 let ts = libc::timespec {tv_sec: 0, tv_nsec: libc::UTIME_NOW};
843 let times = [ts, ts];
844 use std::os::unix::io::AsRawFd;
845 let rc = unsafe {libc::futimens (self.file.as_raw_fd(), ×[0])};
846 if rc != 0 {
847 let err = std::io::Error::last_os_error();
848 return ERR! ("Can't touch {:?}: {}", self.lock_path.as_ref(), err)}
849 Ok(())}}
850impl<'a> Drop for FileLock<'a> {
851 fn drop (&mut self) {
852 let _ = std::fs::remove_file (self.lock_path);}}
853impl<'a> fmt::Debug for FileLock<'a> {
854 fn fmt (&self, ft: &mut fmt::Formatter) -> fmt::Result {
855 write! (ft, "FileLock ({:?}, {})", self.lock_path.as_ref(), self.ttl_sec)}}
856
857#[derive(Debug)]
859pub struct ProcEn {
860 pub name: String,
861 pub path: std::path::PathBuf,
862 pub cmdline: Vec<String>}
864impl ProcEn {
865 pub fn pid (&self) -> Option<u32> {
866 if let Some (file_name) = self.path.file_name() {
867 if let Some (file_name) = file_name.to_str() {
868 if let Ok (pid) = file_name.parse() {
869 return Some (pid)}}}
870 None}}
871pub struct ProcIt {read_dir: std::fs::ReadDir}
882impl ProcIt {
883 pub fn new() -> ProcIt {
884 ProcIt {
885 read_dir: match Path::new ("/proc") .read_dir() {Ok (it) => it, Err (err) => panic! ("!proc: {}", err)}}}}
886impl Iterator for ProcIt {
887 type Item = ProcEn;
888 fn next (&mut self) -> Option<ProcEn> {
889 match self.read_dir.next() {
890 None => return None,
891 Some (Err (err)) => panic! ("ProcIt] !read_dir: {}", err),
892 Some (Ok (proc_en)) => {
893 let file_type = match proc_en.file_type() {
894 Ok (ft) => ft,
895 Err (err) => {
896 if matches! (err.kind(), io::ErrorKind::NotFound) {
897 return self.next()
898 } else {
899 panic! ("!file_type ({:?}): {}", proc_en.path(), err)}}};
900 if !file_type.is_dir() {return self.next()}
901 let name = proc_en.file_name();
902 let name = match name.to_str() {Some (name) => name, None => panic! ("ProcIt] !to_str")};
903 if !name.as_bytes().iter().all (|&b| b >= b'0' && b <= b'9') { return self.next()}
905 let path = proc_en.path();
906 let cmdline = String::from_utf8 (slurp (&path.join ("cmdline"))) .expect ("!from_utf8"); if cmdline.is_empty() {return self.next()}
908 Some (ProcEn {name: name.into(), path: path, cmdline: cmdline.split ('\0') .map (String::from) .collect()})}}}}
909
910pub static mut SPIN_OUT: u32 = 1234567;
911
912fn pause_yield() {
913 spin_loop();
914 thread::yield_now(); spin_loop()}
916
917pub struct IniMutex<T> {au: AtomicI8, vc: UnsafeCell<MaybeUninit<T>>}
918#[must_use = "if unused the Mutex will immediately unlock"]
919pub struct IniMutexGuard<'a, T> {lo: &'a IniMutex<T>}
920
921unsafe impl<T: Send> Send for IniMutex<T> {}
922unsafe impl<T: Send> Sync for IniMutex<T> {}
923
924impl<T> IniMutex<T> {
925 pub const fn none() -> IniMutex<T> {
926 IniMutex {
927 au: AtomicI8::new (0),
928 vc: UnsafeCell::new (MaybeUninit::uninit())}}
929
930 pub const fn new (init: T) -> IniMutex<T> {
931 IniMutex {
932 au: AtomicI8::new (1),
933 vc: UnsafeCell::new (MaybeUninit::new (init))}}
934
935 pub fn is_none (&self) -> bool {
937 0 == self.au.load (Ordering::Relaxed)}
938
939 pub fn is_some (&self) -> bool {
941 0 != self.au.load (Ordering::Relaxed)}
942
943 pub fn lock (&self) -> Result<IniMutexGuard<'_, T>, i8> {
945 match self.au.compare_exchange (1, 2, Ordering::Acquire, Ordering::Relaxed) {
946 Ok (1) => Ok (IniMutexGuard {lo: self}),
947 Ok (lock) => Err (lock),
948 Err (au) => Err (au)}}
949
950 pub fn spin (&self) -> IniMutexGuard<'_, T> {
952 loop {
953 if let Ok (lock) = self.lock() {return lock}
954 pause_yield()}}
955
956 #[cfg(feature = "re")]
957 pub fn lock_init (&self, init: &mut dyn FnMut() -> re::Re<T>) -> Result<IniMutexGuard<'_, T>, LockInitErr> {
958 match self.au.compare_exchange (1, 2, Ordering::Acquire, Ordering::Relaxed) {
959 Ok (1) => Ok (IniMutexGuard {lo: self}),
960 Err (0) => match self.au.compare_exchange (0, 2, Ordering::Acquire, Ordering::Relaxed) {
961 Ok (0) => {
962 let vc = unsafe {&mut *self.vc.get()};
963 match init() {
964 re::Re::Ok (vi) => {
965 *vc = MaybeUninit::new (vi);
966 Ok (IniMutexGuard {lo: self})},
967 re::Re::Err (err) => {
968 self.au.store (0, Ordering::Relaxed);
969 Err (LockInitErr::Init (err))}}},
970 Ok (au) => Err (LockInitErr::Lock (au)),
971 Err (au) => Err (LockInitErr::Lock (au))},
972 Ok (au) => Err (LockInitErr::Lock (au)),
973 Err (au) => Err (LockInitErr::Lock (au))}}
974
975 #[cfg(feature = "re")]
976 pub fn spin_init (&self, init: &mut dyn FnMut() -> re::Re<T>) -> Result<IniMutexGuard<'_, T>, String> {
977 loop {
978 match self.lock_init (init) {
979 Ok (lock) => break Ok (lock),
980 Err (LockInitErr::Lock (_l)) => pause_yield(),
981 Err (LockInitErr::Init (err)) => break Err (err)}}}
982
983 pub fn evict (lock: IniMutexGuard<'_, T>) {unsafe {
985 let vc = &mut *lock.lo.vc.get();
986 let mut swap: T = MaybeUninit::zeroed().assume_init();
987 core::mem::swap (&mut swap, vc.assume_init_mut());
988 lock.lo.au.store (0, Ordering::Release);
989 core::mem::forget (lock);
990 drop (swap)}}}
991
992impl<T> Default for IniMutex<T> {
993 fn default() -> Self {
995 IniMutex::none()}}
996
997#[derive (Debug)]
998pub enum LockInitErr {Lock (i8), Init (String)}
999
1000impl fmt::Display for LockInitErr {
1001 fn fmt (&self, fm: &mut fmt::Formatter) -> fmt::Result {
1002 match *self {
1003 LockInitErr::Lock (au) => fm.write_fmt (format_args! ("{}", au)),
1004 LockInitErr::Init (ref err) => fm.write_str (err)}}}
1005
1006impl<T: Default> IniMutex<T> {
1007 pub fn lock_default (&self) -> Result<IniMutexGuard<'_, T>, i8> {
1008 match self.au.compare_exchange (1, 2, Ordering::Acquire, Ordering::Relaxed) {
1009 Ok (1) => Ok (IniMutexGuard {lo: self}),
1010 Err (0) => match self.au.compare_exchange (0, 2, Ordering::Acquire, Ordering::Relaxed) {
1011 Ok (0) => {
1012 let vc = unsafe {&mut *self.vc.get()};
1013 *vc = MaybeUninit::new (Default::default());
1014 Ok (IniMutexGuard {lo: self})},
1015 Ok (au) => Err (au),
1016 Err (au) => Err (au)},
1017 Ok (au) => Err (au),
1018 Err (au) => Err (au)}}
1019
1020 pub fn spin_default (&self) -> IniMutexGuard<'_, T> {
1021 loop {
1022 if let Ok (lock) = self.lock_default() {return lock}
1023 pause_yield()}}}
1024
1025impl<T> Deref for IniMutexGuard<'_, T> {
1026 type Target = T;
1027 fn deref (&self) -> &T {
1028 let vc = unsafe {&mut *self.lo.vc.get()};
1029 unsafe {&*vc.as_ptr()}}}
1030
1031impl<T> DerefMut for IniMutexGuard<'_, T> {
1032 fn deref_mut (&mut self) -> &mut T {
1033 let vc = unsafe {&mut *self.lo.vc.get()};
1034 unsafe {&mut *vc.as_mut_ptr()}}}
1035
1036impl<T: fmt::Debug> fmt::Debug for IniMutexGuard<'_, T> {
1037 fn fmt (&self, ft: &mut fmt::Formatter) -> fmt::Result {
1038 let vc = unsafe {&mut *self.lo.vc.get()};
1039 unsafe {fmt::Debug::fmt (&*vc.as_ptr(), ft)}}}
1040
1041impl<T: fmt::Display> fmt::Display for IniMutexGuard<'_, T> {
1042 fn fmt (&self, fm: &mut fmt::Formatter) -> fmt::Result {
1043 let vc = unsafe {&mut *self.lo.vc.get()};
1044 unsafe {(*vc.as_ptr()) .fmt (fm)}}}
1045
1046impl<T> Drop for IniMutexGuard<'_, T> {
1047 fn drop (&mut self) {
1048 self.lo.au.store (1, Ordering::Release)}}
1049
1050impl<T> Drop for IniMutex<T> {
1051 fn drop (&mut self) {
1052 if self.au.load (Ordering::Acquire) != 0 {
1053 self.au.store (0, Ordering::Relaxed);
1054 let vc = unsafe {&mut *self.vc.get()};
1055 unsafe {vc.assume_init_drop()}}}}
1056
1057#[cfg(feature = "inlinable_string")]
1059pub static HOST: IniMutex<inlinable_string::InlinableString> = IniMutex::none();
1060
1061pub struct TSafe<T> (pub T);
1062unsafe impl<T> Send for TSafe<T> {}
1063unsafe impl<T> Sync for TSafe<T> {}
1064impl<T: Default> Default for TSafe<T> {fn default() -> Self {TSafe(T::default())}}
1065impl<T: Clone> Clone for TSafe<T> {fn clone (&self) -> Self {TSafe (self.0.clone())}}
1066impl<T: fmt::Debug> fmt::Debug for TSafe<T> {fn fmt (&self, ft: &mut fmt::Formatter<'_>) -> fmt::Result {self.0.fmt (ft)}}
1067impl<T: fmt::Display> fmt::Display for TSafe<T> {fn fmt (&self, ft: &mut fmt::Formatter<'_>) -> fmt::Result {self.0.fmt (ft)}}
1068
1069#[cfg(all(feature = "re", feature = "reffers"))]
1070pub trait SpinA<T> {
1071 fn spinʷ (self: &Self) -> re::Re<reffers::arc::RefMut<T>>;
1073 fn spinʳ (self: &Self) -> re::Re<reffers::arc::Ref<T>>;}
1075
1076#[cfg(all(feature = "re", feature = "reffers"))]
1077impl<T> SpinA<T> for reffers::arc::Strong<T> {
1078 fn spinʷ (&self) -> re::Re<reffers::arc::RefMut<T>> {
1079 let timeout = unsafe {SPIN_OUT};
1080 for spin in 0..timeout {
1081 if let Ok (lock) = self.try_get_refmut() {return re::Re::Ok (lock)}
1082 if spin % 10 == 0 {spin_loop()} else {thread::yield_now()}}
1083 re::Re::Err ("spin-out".into())}
1084
1085 fn spinʳ (&self) -> re::Re<reffers::arc::Ref<T>> {
1086 let timeout = unsafe {SPIN_OUT};
1087 for spin in 0..timeout {
1088 if let Ok (lock) = self.try_get_ref() {return re::Re::Ok (lock)}
1089 if spin % 10 == 0 {spin_loop()} else {thread::yield_now()}}
1090 re::Re::Err ("spin-out".into())}}
1091
1092pub fn binprint (bin: &[u8], blank: u8) -> String {
1095 let mut bin: Vec<u8> = bin.into();
1096 for ch in bin.iter_mut() {if *ch < 0x20 || *ch >= 0x7F {*ch = blank}}
1097 unsafe {String::from_utf8_unchecked (bin)}}
1098
1099pub fn bits2bedstead (ch: u32) -> char {
1103 let ch =
1104 if 0b111111 < ch {0xEE00}
1105 else if 32 <= ch {0xEE40 + ch - 32}
1106 else if 0 < ch {0xEE00 + ch}
1107 else {0xEE00 + ch};
1108 unsafe {char::from_u32_unchecked (ch)}}
1109
1110pub fn bedstead2bits (ch: char) -> u32 {
1112 let ch = ch as u32;
1113 if 0xEE5F < ch {0} else if 0xEE40 <= ch {ch - 0xEE40 + 32}
1115 else if 0xEE00 <= ch {ch - 0xEE00}
1116 else {0}} #[test]
1119fn test_bedstead() {
1120 for bits in 0 ..= 0b111111 {
1121 let ch = bits2bedstead (bits);
1122 assert_eq! (bits, bedstead2bits (ch))}}
1123
1124pub fn round_to (decimals: u32, num: f32) -> f32 {
1125 let r = 10u32 .pow (decimals) as f32;
1126 (num * r) .round() / r}
1127
1128pub fn round8 (decimals: u32, num: f64) -> f64 {
1129 let r = 10u32 .pow (decimals) as f64;
1130 (num * r) .round() / r}
1131
1132#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
1134pub struct OrdFloat (pub f64);
1135impl Eq for OrdFloat {}
1136impl Ord for OrdFloat {
1137 fn cmp (&self, other: &Self) -> std::cmp::Ordering {
1138 self.0.partial_cmp (&other.0) .expect ("!partial_cmp")}}
1140use std::hash::{Hash, Hasher};
1141impl Hash for OrdFloat {
1142 fn hash<H: Hasher> (&self, state: &mut H) {
1143 self.0.to_bits().hash (state)}}
1144impl fmt::Display for OrdFloat {
1145 fn fmt (&self, fm: &mut fmt::Formatter) -> fmt::Result {
1146 self.0.fmt (fm)}}
1147
1148#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
1150pub struct OrdF32 (pub f32);
1151impl Eq for OrdF32 {}
1152impl Ord for OrdF32 {
1153 fn cmp (&self, other: &Self) -> std::cmp::Ordering {
1154 self.0.partial_cmp (&other.0) .expect ("!partial_cmp")}}
1156impl Hash for OrdF32 {
1157 fn hash<H: Hasher> (&self, state: &mut H) {
1158 self.0.to_bits().hash (state)}}
1159impl fmt::Display for OrdF32 {
1160 fn fmt (&self, fm: &mut fmt::Formatter) -> fmt::Result {
1161 self.0.fmt (fm)}}
1162
1163pub trait AtBool {
1187 fn l (&self) -> bool;
1189 fn s (&self, val: bool);}
1191impl AtBool for core::sync::atomic::AtomicBool {
1192 fn l (&self) -> bool {
1193 self.load (Ordering::Relaxed)}
1194 fn s (&self, val: bool) {
1195 self.store (val, Ordering::Relaxed)}}
1196
1197pub trait AtI8 {
1198 fn l (&self) -> i8;
1200 fn s (&self, val: i8);}
1202impl AtI8 for core::sync::atomic::AtomicI8 {
1203 fn l (&self) -> i8 {
1204 self.load (Ordering::Relaxed)}
1205 fn s (&self, val: i8) {
1206 self.store (val, Ordering::Relaxed)}}
1207
1208pub trait AtI32 {
1209 fn cas (&self, current: i32, new: i32) -> Result<i32, i32>;
1211 fn l (&self) -> i32;
1213 fn s (&self, val: i32);}
1215impl AtI32 for core::sync::atomic::AtomicI32 {
1216 fn cas (&self, current: i32, new: i32) -> Result<i32, i32> {
1217 self.compare_exchange (current, new, Ordering::Relaxed, Ordering::Relaxed)}
1218 fn l (&self) -> i32 {
1219 self.load (Ordering::Relaxed)}
1220 fn s (&self, val: i32) {
1221 self.store (val, Ordering::Relaxed)}}
1222
1223pub trait AtI64 {
1224 fn cas (&self, current: i64, new: i64) -> Result<i64, i64>;
1226 fn l (&self) -> i64;
1228 fn s (&self, val: i64);}
1230impl AtI64 for core::sync::atomic::AtomicI64 {
1231 fn cas (&self, current: i64, new: i64) -> Result<i64, i64> {
1232 self.compare_exchange (current, new, Ordering::Relaxed, Ordering::Relaxed)}
1233 fn l (&self) -> i64 {
1234 self.load (Ordering::Relaxed)}
1235 fn s (&self, val: i64) {
1236 self.store (val, Ordering::Relaxed)}}
1237
1238pub trait AtUsize {
1239 fn cas (&self, current: usize, new: usize) -> Result<usize, usize>;
1241 fn l (&self) -> usize;
1243 fn s (&self, val: usize);}
1245impl AtUsize for AtomicUsize {
1246 fn cas (&self, current: usize, new: usize) -> Result<usize, usize> {
1247 self.compare_exchange (current, new, Ordering::Relaxed, Ordering::Relaxed)}
1248 fn l (&self) -> usize {
1249 self.load (Ordering::Relaxed)}
1250 fn s (&self, val: usize) {
1251 self.store (val, Ordering::Relaxed)}}
1252
1253#[cfg(feature = "memchr")]
1259pub struct LinesIt<'a> {
1260 pub lines: &'a [u8],
1261 pub head: usize,
1262 pub tail: usize}
1263
1264#[cfg(feature = "memchr")]
1265impl<'a> LinesIt<'a> {
1266 pub fn new (lines: &'a [u8]) -> LinesIt<'a> {
1267 let (mut head, mut tail) = (0, lines.len());
1268
1269 loop {
1270 if tail <= head {break}
1271 if lines[head] == b'\n' {head += 1; continue}
1272 break}
1273
1274 loop {
1275 if tail <= head {break}
1276 if lines[tail-1] == b'\n' {tail -= 1; continue}
1277 break}
1278
1279 LinesIt {lines, head, tail}}
1280
1281 pub fn heads_up (lines: &'a [u8], pos: usize) -> LinesIt<'a> {
1283 let len = lines.len();
1284 if len < pos {
1285 LinesIt {lines, head: len, tail: len}
1286 } else {
1287 LinesIt {lines,
1288 head: memrchr (b'\n', &lines[..pos]) .unwrap_or_default(),
1289 tail: len}}}}
1290
1291#[cfg(feature = "memchr")]
1292impl<'a> Iterator for LinesIt<'a> {
1293 type Item = &'a [u8];
1294 fn next (&mut self) -> Option<Self::Item> {
1295 loop {
1296 if self.tail <= self.head {return None}
1297 if self.lines[self.head] == b'\n' {self.head += 1; continue}
1298 break}
1299 if let Some (mut lf) = memchr (b'\n', &self.lines[self.head .. self.tail]) {
1300 lf += self.head;
1301 let line = &self.lines[self.head .. lf];
1302 self.head = lf + 1;
1303 Some (line)
1304 } else {
1305 let line = &self.lines[self.head .. self.tail];
1306 self.head = self.tail;
1307 Some (line)}}}
1308
1309#[cfg(feature = "memchr")]
1310impl<'a> DoubleEndedIterator for LinesIt<'a> {
1311 fn next_back (&mut self) -> Option<Self::Item> {
1312 loop {
1313 if self.tail <= self.head {return None}
1314 if self.lines[self.tail-1] == b'\n' {self.tail -= 1; continue}
1315 break}
1316 if let Some (mut lf) = memrchr (b'\n', &self.lines[self.head .. self.tail]) {
1317 lf += self.head;
1318 let line = &self.lines[lf + 1 .. self.tail];
1319 self.tail = lf;
1320 Some (line)
1321 } else {
1322 let line = &self.lines[self.head .. self.tail];
1323 self.tail = self.head;
1324 Some (line)}}}
1325
1326#[cfg(all(feature = "crossterm", feature = "fomat-macros", feature = "inlinable_string", feature = "reffers", feature = "re"))]
1328pub mod tpool {
1329 use crate::{any_to_str, IniMutex};
1330 use crate::re::Re;
1331 use inlinable_string::InlinableString;
1332 use reffers::arc::{Strong as StrongA, Ref as RefA, RefMut as RefMutA};
1333 use std::collections::VecDeque;
1334 use std::hint::spin_loop;
1335 use std::panic::{catch_unwind, AssertUnwindSafe};
1336 use std::sync::{Mutex, Condvar};
1337 use std::sync::atomic::{AtomicBool, AtomicI16, Ordering};
1338 use std::thread::{self, JoinHandle};
1339 use std::time::Duration;
1340
1341 struct TJobs {
1342 queue: Mutex<VecDeque<Box<dyn FnOnce() -> Re<()> + Send + 'static>>>,
1343 alarm: Condvar,
1344 running: AtomicI16,
1345 bye: AtomicBool}
1346 impl Default for TJobs {
1347 fn default() -> TJobs {
1348 TJobs {
1349 queue: Mutex::new (VecDeque::new()),
1350 alarm: Condvar::new(),
1351 running: AtomicI16::new (0),
1352 bye: AtomicBool::new (false)}}}
1353
1354 #[derive (Default)]
1355 pub struct TPool {
1356 jobs: StrongA<TJobs>,
1357 pub threads: Vec<(InlinableString, JoinHandle<Re<()>>)>,
1358 pub finalizers: Vec<(InlinableString, Box<dyn FnOnce() -> Re<()> + Send + 'static>)>}
1359
1360 unsafe impl Send for TPool {}
1361 unsafe impl Sync for TPool {}
1362
1363 impl TPool {
1364 pub fn sponsor (&mut self, tag: InlinableString) -> Re<bool> {
1367 if self.threads.iter().any (|(tagʹ, _)| *tagʹ == tag) {return Re::Ok (false)}
1368 let jobs = self.jobs.clone();
1369 self.threads.push ((tag,
1370 thread::Builder::new().name ("TPool".into()) .spawn (move || -> Re<()> {
1371 loop {
1372 let task = {
1373 let jobs = jobs.get_ref();
1374 let mut queue = jobs.queue.lock()?;
1375 match queue.pop_front() {
1376 Some (j) => {jobs.running.fetch_add (1, Ordering::Relaxed); j}
1377 None if jobs.bye.load (Ordering::Relaxed) => {break Re::Ok(())}
1378 None => {
1379 let (mut queue, _rc) = jobs.alarm.wait_timeout (queue, Duration::from_secs_f32 (0.31))?;
1380 if let Some (job) = queue.pop_front() {jobs.running.fetch_add (1, Ordering::Relaxed); job} else {continue}}}};
1381 let rc = catch_unwind (AssertUnwindSafe (task));
1382 let running = jobs.get_ref().running.fetch_sub (1, Ordering::Relaxed);
1383 if running < 0 {log! (a 202, [=running])}
1384 match rc {
1385 Ok (Re::Ok(())) => {}
1386 Ok (Re::Err (err)) => {log! (a 1, (err))}
1387 Err (err) => {log! (a 1, [any_to_str (&*err)])}}}})?));
1388 Re::Ok (true)}
1389
1390 pub fn post (&self, task: Box<dyn FnOnce() -> Re<()> + Send + Sync + 'static>) -> Re<()> {
1392 let jobs = self.jobs.get_ref();
1393 let mut queue = jobs.queue.lock()?;
1394 queue.push_back (task);
1395 jobs.alarm.notify_one();
1396 Re::Ok(())}
1397
1398 pub fn fin (&mut self, tag: InlinableString, finalizer: Box<dyn FnOnce() -> Re<()> + Send + Sync + 'static>) -> bool {
1401 if !tag.is_empty() && self.finalizers.iter().any (|(tagʹ, _)| *tagʹ == tag) {false}
1402 else {self.finalizers.push ((tag, finalizer)); true}}
1403
1404 pub fn jobsⁿ (&self) -> Re<usize> {
1406 let jobs = self.jobs.get_ref();
1407 let queue = jobs.queue.lock()?;
1408 Re::Ok (queue.len() + jobs.running.load (Ordering::Relaxed) .max (0) as usize)}}
1409
1410 impl Drop for TPool {
1411 fn drop (&mut self) {
1412 { let jobs = self.jobs.get_ref();
1413 jobs.bye.store (true, Ordering::Relaxed);
1414 let _queue = jobs.queue.lock();
1415 jobs.alarm.notify_all(); } for (_tag, th) in self.threads.drain (..) {
1417 match th.join() {
1418 Ok (Re::Ok(())) => {}
1419 Ok (Re::Err (err)) => {log! (a 1, (err))}
1420 Err (err) => {log! (a 1, [any_to_str (&*err)])}}}
1421 for (tag, finalizer) in self.finalizers.drain (..) {
1422 let rc = catch_unwind (AssertUnwindSafe (finalizer));
1423 match rc {
1424 Ok (Re::Ok(())) => {}
1425 Ok (Re::Err (err)) => {log! (a 1, (tag) "] " (err))}
1426 Err (err) => {log! (a 1, (tag) "] " [any_to_str (&*err)])}}}}}
1427
1428 pub static TPOOL: IniMutex<TPool> = IniMutex::none();
1430
1431 pub fn tpost (mut spin: u32, threads: u8, task: Box<dyn FnOnce() -> Re<()> + Send + Sync + 'static>) -> Re<bool> {
1436 loop {
1437 spin -= 1; if spin == 0 {break}
1438 let Ok (pool) = TPOOL.lock() else {spin_loop(); thread::yield_now(); continue};
1439 if pool.threads.len() < threads as usize {break}
1440 pool.post (task)?;
1441 return Re::Ok (true)}
1442 task()?;
1443 Re::Ok (false)}}