Skip to content

Commit f116feb

Browse files
committed
add
1 parent 6a71f7e commit f116feb

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed

Linux/tss/src/main.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
use std::env;
2+
use std::io::{self, BufRead, BufReader, Write};
3+
use std::time::{SystemTime, UNIX_EPOCH, Instant};
4+
5+
struct Config {
6+
format: String,
7+
separator: String,
8+
relative: bool,
9+
monotonic: bool,
10+
utc: bool,
11+
}
12+
13+
impl Config {
14+
fn parse_args() -> Result<Self, Box<dyn std::error::Error>> {
15+
let mut config = Config {
16+
format: "%Y-%m-%d %H:%M:%S".to_string(),
17+
separator: " ".to_string(),
18+
relative: false,
19+
monotonic: false,
20+
utc: false,
21+
};
22+
23+
let args: Vec<String> = env::args().collect();
24+
let mut i = 1;
25+
26+
while i < args.len() {
27+
match args[i].as_str() {
28+
"-h" | "--help" => {
29+
Self::print_help();
30+
std::process::exit(0);
31+
}
32+
"-r" | "--relative" => config.relative = true,
33+
"-m" | "--monotonic" => config.monotonic = true,
34+
"-u" | "--utc" => config.utc = true,
35+
"-s" | "--separator" => {
36+
i += 1;
37+
if i >= args.len() {
38+
eprintln!("Error: --separator requires a value");
39+
std::process::exit(1);
40+
}
41+
config.separator = args[i].clone();
42+
}
43+
"-f" | "--format" => {
44+
i += 1;
45+
if i >= args.len() {
46+
eprintln!("Error: --format requires a value");
47+
std::process::exit(1);
48+
}
49+
config.format = args[i].clone();
50+
}
51+
_ => {
52+
eprintln!("Unknown argument: {}", args[i]);
53+
std::process::exit(1);
54+
}
55+
}
56+
i += 1;
57+
}
58+
59+
Ok(config)
60+
}
61+
62+
fn print_help() {
63+
println!(
64+
"ts - timestamp each line of input
65+
66+
Usage: ts [OPTIONS]
67+
68+
Options:
69+
-f, --format FORMAT Date format (default: %Y-%m-%d %H:%M:%S)
70+
-s, --separator SEP Separator between timestamp and line (default: \" \")
71+
-r, --relative Show relative timestamps from start
72+
-m, --monotonic Use monotonic clock for relative timestamps
73+
-u, --utc Use UTC time
74+
-h, --help Show this help
75+
76+
Examples:
77+
ls -la | ts
78+
tail -f /var/log/messages | ts -r
79+
ping google.com | ts -f \"%H:%M:%S.%f\"
80+
"
81+
);
82+
}
83+
}
84+
85+
struct TimeFormatter {
86+
format: String,
87+
relative: bool,
88+
utc: bool,
89+
start_time: Option<SystemTime>,
90+
start_instant: Option<Instant>,
91+
}
92+
93+
impl TimeFormatter {
94+
fn new(config: &Config) -> Self {
95+
Self {
96+
format: config.format.clone(),
97+
relative: config.relative,
98+
utc: config.utc,
99+
start_time: None,
100+
start_instant: None,
101+
}
102+
}
103+
104+
fn format_timestamp(&mut self, monotonic: bool) -> String {
105+
if self.relative {
106+
if monotonic {
107+
let now = Instant::now();
108+
let start = self.start_instant.get_or_insert(now);
109+
let duration = now.duration_since(*start);
110+
format!("{}.{:03}", duration.as_secs(), duration.subsec_millis())
111+
} else {
112+
let now = SystemTime::now();
113+
let start = self.start_time.get_or_insert(now);
114+
let duration = now.duration_since(*start).unwrap_or_default();
115+
format!("{}.{:03}", duration.as_secs(), duration.subsec_millis())
116+
}
117+
} else {
118+
let now = SystemTime::now();
119+
let duration = now.duration_since(UNIX_EPOCH).unwrap();
120+
let secs = duration.as_secs();
121+
let nanos = duration.subsec_nanos();
122+
let micros = nanos / 1000;
123+
124+
// Fast path for common format
125+
if self.format == "%Y-%m-%d %H:%M:%S" {
126+
let dt = secs as i64;
127+
let (year, month, day, hour, min, sec) = Self::timestamp_to_parts(dt, self.utc);
128+
return format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
129+
year, month, day, hour, min, sec);
130+
}
131+
132+
// Handle custom formats
133+
self.format_custom_timestamp(secs, micros)
134+
}
135+
}
136+
137+
#[inline]
138+
fn timestamp_to_parts(timestamp: i64, utc: bool) -> (i32, u32, u32, u32, u32, u32) {
139+
// Simple UTC conversion (leap seconds ignored for performance)
140+
let mut days = timestamp / 86400;
141+
let remaining_secs = (timestamp % 86400) as u32;
142+
143+
if !utc {
144+
// Rough local timezone offset - in practice you'd want proper timezone handling
145+
// This is simplified for maximum performance
146+
}
147+
148+
let hour = remaining_secs / 3600;
149+
let min = (remaining_secs % 3600) / 60;
150+
let sec = remaining_secs % 60;
151+
152+
// Calculate date from days since epoch (1970-01-01)
153+
days += 719468; // Days from year 1 to 1970
154+
155+
let era = days / 146097;
156+
let doe = days % 146097;
157+
let yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;
158+
let year = yoe + era * 400;
159+
let doy = doe - (365*yoe + yoe/4 - yoe/100);
160+
let mp = (5*doy + 2)/153;
161+
let day = doy - (153*mp+2)/5 + 1;
162+
let month = mp + if mp < 10 { 3 } else { -9 };
163+
let year = year + if month <= 2 { 1 } else { 0 };
164+
165+
(year as i32, month as u32, day as u32, hour, min, sec)
166+
}
167+
168+
fn format_custom_timestamp(&self, secs: u64, micros: u32) -> String {
169+
let (year, month, day, hour, min, sec) = Self::timestamp_to_parts(secs as i64, self.utc);
170+
171+
// Simple format replacement for performance
172+
let mut result = self.format.clone();
173+
result = result.replace("%Y", &format!("{:04}", year));
174+
result = result.replace("%m", &format!("{:02}", month));
175+
result = result.replace("%d", &format!("{:02}", day));
176+
result = result.replace("%H", &format!("{:02}", hour));
177+
result = result.replace("%M", &format!("{:02}", min));
178+
result = result.replace("%S", &format!("{:02}", sec));
179+
result = result.replace("%f", &format!("{:06}", micros));
180+
result = result.replace("%%", "%");
181+
182+
result
183+
}
184+
}
185+
186+
fn main() -> Result<(), Box<dyn std::error::Error>> {
187+
let config = Config::parse_args()?;
188+
let mut formatter = TimeFormatter::new(&config);
189+
190+
let stdin = io::stdin();
191+
let mut stdout = io::stdout();
192+
193+
// Use buffered reader for better performance
194+
let reader = BufReader::with_capacity(65536, stdin);
195+
196+
// Pre-allocate output buffer
197+
let mut output_buf = String::with_capacity(1024);
198+
199+
for line in reader.lines() {
200+
let line = line?;
201+
let timestamp = formatter.format_timestamp(config.monotonic);
202+
203+
// Build output in buffer to minimize syscalls
204+
output_buf.clear();
205+
output_buf.push_str(&timestamp);
206+
output_buf.push_str(&config.separator);
207+
output_buf.push_str(&line);
208+
output_buf.push('\n');
209+
210+
stdout.write_all(output_buf.as_bytes())?;
211+
}
212+
213+
Ok(())
214+
}

0 commit comments

Comments
 (0)