flare_core/common/
utils.rs1use crate::common::error::Result;
6
7pub fn generate_id() -> String {
9 use std::sync::atomic::{AtomicU64, Ordering};
10 static COUNTER: AtomicU64 = AtomicU64::new(0);
11
12 let timestamp = crate::common::platform::wall_clock_ms();
13 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
14 format!("{}-{:016x}", timestamp, counter)
15}
16
17pub fn generate_short_id() -> String {
19 use std::sync::atomic::{AtomicU32, Ordering};
20 static COUNTER: AtomicU32 = AtomicU32::new(0);
21
22 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
23 format!("{:08x}", counter)
24}
25
26pub fn current_timestamp_ms() -> u64 {
28 crate::common::platform::wall_clock_ms()
29}
30
31pub fn current_timestamp_secs() -> u64 {
33 crate::common::platform::wall_clock_secs()
34}
35
36pub fn is_valid_connection_id(id: &str) -> bool {
40 !id.is_empty()
41 && id.len() <= 128
42 && id
43 .chars()
44 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
45}
46
47pub fn is_valid_user_id(id: &str) -> bool {
51 !id.is_empty()
52 && id.len() <= 64
53 && id
54 .chars()
55 .all(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == '@' || c == '.')
56}
57
58pub fn truncate_string(s: &str, max_len: usize) -> String {
60 if s.len() <= max_len {
61 s.to_string()
62 } else {
63 format!("{}...", &s[..max_len.saturating_sub(3)])
64 }
65}
66
67pub fn bytes_to_hex(bytes: &[u8]) -> String {
69 bytes.iter().map(|b| format!("{:02x}", b)).collect()
70}
71
72pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
74 (0..hex.len())
75 .step_by(2)
76 .map(|i| {
77 u8::from_str_radix(&hex[i..i + 2], 16).map_err(|e| {
78 crate::common::error::FlareError::protocol_error(format!(
79 "Invalid hex string: {}",
80 e
81 ))
82 })
83 })
84 .collect()
85}
86
87pub fn simple_hash(data: &[u8]) -> u64 {
92 const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
94 const FNV_PRIME: u64 = 0x100000001b3;
95
96 let mut hash = FNV_OFFSET_BASIS;
97 for &byte in data {
98 hash ^= byte as u64;
99 hash = hash.wrapping_mul(FNV_PRIME);
100 }
101 hash
102}
103
104pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
106 if a.len() != b.len() {
107 return false;
108 }
109
110 a.iter()
111 .zip(b.iter())
112 .map(|(x, y)| x ^ y)
113 .fold(0u8, |acc, x| acc | x)
114 == 0
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_generate_id() {
123 let id1 = generate_id();
124 let id2 = generate_id();
125 assert_ne!(id1, id2);
126 assert!(!id1.is_empty());
127 }
128
129 #[test]
130 fn test_is_valid_connection_id() {
131 assert!(is_valid_connection_id("conn-123"));
132 assert!(is_valid_connection_id("conn_456"));
133 assert!(!is_valid_connection_id("conn 123")); assert!(!is_valid_connection_id("")); }
136
137 #[test]
138 fn test_is_valid_user_id() {
139 assert!(is_valid_user_id("user-123"));
140 assert!(is_valid_user_id("user@example.com"));
141 assert!(!is_valid_user_id("user 123")); assert!(!is_valid_user_id("")); }
144
145 #[test]
146 fn test_truncate_string() {
147 assert_eq!(truncate_string("hello", 10), "hello");
148 assert_eq!(truncate_string("hello world", 5), "he...");
149 }
150
151 #[test]
152 fn test_bytes_to_hex() {
153 assert_eq!(bytes_to_hex(&[0x12, 0x34, 0xAB]), "1234ab");
154 }
155
156 #[test]
157 fn test_hex_to_bytes() {
158 assert_eq!(hex_to_bytes("1234ab").unwrap(), vec![0x12, 0x34, 0xAB]);
159 assert!(hex_to_bytes("invalid").is_err());
160 }
161
162 #[test]
163 fn test_constant_time_eq() {
164 assert!(constant_time_eq(b"hello", b"hello"));
165 assert!(!constant_time_eq(b"hello", b"world"));
166 assert!(!constant_time_eq(b"hello", b"hell"));
167 }
168}