1use crate::tools::json;
21use crate::tools::time::DurationMillis;
22use crate::tools::time_provider::time_provider::{RealTimeProvider, TimeProvider};
23use crate::tools::BytesGatherer;
24use base64::Engine;
25use bytes::Bytes;
26use log::info;
27use std::fmt;
28use std::future::Future;
29use std::sync::Arc;
30use tokio_util::sync::CancellationToken;
31use tracing_subscriber::fmt::time::FormatTime;
32use tracing_subscriber::layer::SubscriberExt;
33use tracing_subscriber::util::SubscriberInitExt;
34
35pub type LeadingAgreementBits = i32;
36
37pub async fn yield_now() {
38 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
39 {
40 send_wrapper::SendWrapper::new(gloo_timers::future::TimeoutFuture::new(0)).await;
41 }
42 #[cfg(all(target_arch = "wasm32", target_os = "wasi"))]
43 {
44 tokio::time::sleep(std::time::Duration::from_millis(0u64)).await;
45 }
46 #[cfg(not(target_arch = "wasm32"))]
47 {
48 tokio::task::yield_now().await;
50 }
51}
52
53pub fn random_fill_bytes(dest: &mut [u8]) {
54 getrandom_4::fill(dest).expect("OS random number generator failed");
55}
56
57pub fn random_bytes(n: usize) -> Vec<u8> {
58 let mut dest = vec![0u8; n];
59 random_fill_bytes(&mut dest);
60 dest
61}
62
63pub fn reverse_bytes<const N: usize>(bytes: &[u8; N]) -> [u8; N] {
64 let mut result = [0u8; N];
65 for (i, &byte) in bytes.iter().rev().enumerate() {
66 result[i] = byte;
67 }
68 result
69}
70
71pub fn random_u32() -> u32 {
72 getrandom_4::u32().expect("OS random number generator failed")
73}
74
75#[cfg(target_pointer_width = "64")]
76pub fn random_usize() -> usize {
77 getrandom_4::u64().expect("OS random number generator failed") as usize
78}
79
80#[cfg(target_pointer_width = "32")]
81pub fn random_usize() -> usize {
82 getrandom_4::u32().expect("OS random number generator failed") as usize
83}
84
85pub fn random_usize_bounded(upper: usize) -> usize {
86 let zone = usize::MAX - (usize::MAX % upper);
89
90 loop {
91 let r = random_usize();
92 if r < zone {
93 return r % upper;
94 }
95 }
96}
97
98pub fn random_u8() -> u8 {
99 getrandom_4::u32().expect("OS random number generator failed") as u8
100}
101
102pub fn random_base64(length: usize) -> String {
103 let mut bytes = vec![0u8; length];
104 random_fill_bytes(&mut bytes);
105 encode_base64(bytes)
106}
107
108pub fn are_all_zeros<T: PartialEq + num_traits::Zero>(src: &[T]) -> bool {
109 src.iter().all(|b| *b == T::zero())
110}
111
112pub fn are_all_equal<T: PartialEq>(src1: &[T], src2: &[T]) -> bool {
113 if src1.len() != src2.len() {
114 return false;
115 }
116 src1.iter().zip(src2).all(|(a, b)| a == b)
117}
118
119pub fn count_leading_zero_bits(bytes: &[u8]) -> u8 {
120 let mut count = 0u64;
121
122 for &byte in bytes {
123 if byte == 0 {
124 count += 8;
125 continue;
126 }
127
128 let mut mask = 0x80; while byte & mask == 0 {
131 count += 1;
132 mask >>= 1;
133 }
134
135 break; }
137
138 if count < 256 { count as u8 } else { 255 }
139}
140
141pub async fn cancellable_sleep_millis(time_provider: &dyn TimeProvider, millis: DurationMillis, cancellation_token: &CancellationToken) {
142 tokio::select! {
143 _ = time_provider.sleep_millis(millis) => {},
144 _ = cancellation_token.cancelled() => {},
145 }
146}
147
148pub fn format_vec<T: std::fmt::Display>(items: &[T]) -> String {
149 format!("[ {} ]", items.iter().map(|item| format!("{}", item)).collect::<Vec<_>>().join(", "))
150}
151
152pub fn leading_agreement_bits_xor(key1: &[u8], key2: &[u8]) -> LeadingAgreementBits {
153 let mut leading_bits_in_agreement: i32 = 0;
154
155 let min_len = std::cmp::min(key1.len(), key2.len());
156 for byte_idx in 0..min_len {
157 let xor = key1[byte_idx] ^ key2[byte_idx];
158
159 if xor != 0 {
161 leading_bits_in_agreement += xor.leading_zeros() as LeadingAgreementBits;
162 return leading_bits_in_agreement;
163 }
164 else {
165 leading_bits_in_agreement += 8;
166 }
167 }
168
169 leading_bits_in_agreement
170}
171
172pub fn encode_base64<T: AsRef<[u8]>>(input: T) -> String {
173 base64::engine::general_purpose::STANDARD.encode(&input)
174}
175
176pub fn decode_base64<T: AsRef<[u8]>>(input: T) -> anyhow::Result<Vec<u8>> {
177 Ok(base64::engine::general_purpose::STANDARD.decode(input)?)
178}
179
180pub fn usize_encode_le64(v: usize) -> [u8; 8] {
181 u64::to_le_bytes(v as u64)
182}
183
184pub fn usize_decode_le64(v_bytes: &[u8]) -> anyhow::Result<usize> {
185 let v = u64::from_le_bytes(v_bytes.try_into()?);
186 Ok(v as usize)
187}
188
189pub fn write_length_prefixed_json<T: serde::Serialize>(bytes_gatherer: &mut BytesGatherer, value: &T) -> anyhow::Result<()> {
190 let json_bytes = json::struct_to_bytes(value)?;
191 bytes_gatherer.put_u64(json_bytes.len() as u64);
192 bytes_gatherer.put_bytes(json_bytes);
193 Ok(())
194}
195pub fn read_length_prefixed_json<T: serde::de::DeserializeOwned>(bytes: &mut Bytes) -> anyhow::Result<T> {
196 use bytes::Buf;
197
198 if bytes.remaining() < 8 {
199 anyhow::bail!("Invalid buffer: missing json length");
200 }
201
202 let len = bytes.get_u64() as usize;
203
204 if bytes.remaining() < len {
205 anyhow::bail!("Invalid buffer: json data truncated");
206 }
207
208 let json_bytes = bytes.copy_to_bytes(len);
209 json::bytes_to_struct::<T>(&json_bytes)
210}
211
212pub fn random_element<T>(range: &[T]) -> &T {
213 let index = random_usize_bounded(range.len());
214 &range[index]
215}
216
217pub fn shuffle<T>(source: &mut [T]) {
218 for i in 1..source.len() {
220 let j = random_usize_bounded(i + 1);
221 source.swap(i, j);
222 }
223}
224
225pub struct CustomTimeFormatter {
226 time_provider: Arc<dyn TimeProvider>,
227}
228
229impl CustomTimeFormatter {
230 pub fn new(time_provider: Arc<dyn TimeProvider>) -> Self {
231 Self { time_provider }
232 }
233}
234
235impl FormatTime for CustomTimeFormatter {
236 fn format_time(&self, w: &mut tracing_subscriber::fmt::format::Writer<'_>) -> fmt::Result {
237 write!(w, "{}", self.time_provider.current_time_str())
238 }
239}
240
241pub fn configure_logging() {
242 configure_logging_with_time_provider("trace", Arc::new(RealTimeProvider))
243}
244
245pub fn configure_logging_with_time_provider(level: &str, time_provider: Arc<dyn TimeProvider>) {
246 let filter = format!("{},hyper=off,warp=off,reqwest=off,rustls=off,h2=off,h2=off,html5ever=off,selectors=off,fjall=off,lsm_tree=off,sfa=off,hickory_resolver=off,hickory_proto=off", level);
248 let env_filter = tracing_subscriber::EnvFilter::new(&filter);
249
250 let fmt_layer = tracing_subscriber::fmt::layer().with_timer(CustomTimeFormatter::new(time_provider));
252
253 let registry = tracing_subscriber::registry();
254
255 #[cfg(all(tokio_unstable, not(target_arch = "wasm32")))]
257 registry.with(console_subscriber::spawn());
258
259 registry.with(fmt_layer).with(env_filter).init();
261
262 info!("Logging initialized");
263}
264
265#[cfg(not(target_arch = "wasm32"))]
266pub type TempDirHandle = tempfile::TempDir;
267
268#[cfg(not(target_arch = "wasm32"))]
269pub fn get_temp_dir() -> anyhow::Result<(TempDirHandle, String)> {
270 let mut base = std::env::temp_dir();
271 base.push("hashiverse-temp");
272
273 std::fs::create_dir_all(&base)?;
275
276 let temp_dir = tempfile::Builder::new().prefix("hashiverse-").tempdir_in(&base)?;
277 let temp_dir_path = temp_dir.path().to_str().unwrap().to_string();
278 Ok((temp_dir, temp_dir_path))
279}
280
281#[cfg(target_arch = "wasm32")]
282pub type TempDirHandle = ();
283
284#[cfg(target_arch = "wasm32")]
285pub fn get_temp_dir() -> anyhow::Result<(TempDirHandle, String)> {
286 Ok(((), "".to_string()))
287}
288
289pub fn from_hex_str<T, const T_BYTES: usize>(str: &str, ctor: impl FnOnce([u8; T_BYTES]) -> T) -> anyhow::Result<T> {
290 if str.len() != 2 * T_BYTES {
291 anyhow::bail!("Invalid hex string length: expected {} hex characters ({} bytes), got {} characters.", 2 * T_BYTES, T_BYTES, str.len(),);
292 }
293
294 let decoded = hex::decode(str)?;
296
297 if decoded.len() != T_BYTES {
299 anyhow::bail!("Invalid hex string length: expected {} bytes, got {} bytes", T_BYTES, decoded.len());
300 }
301
302 let mut decoded_bytes = [0u8; T_BYTES];
304 decoded_bytes.copy_from_slice(&decoded);
305
306 Ok(ctor(decoded_bytes))
307}
308
309#[cfg(not(target_arch = "wasm32"))]
318pub fn spawn_background_task<F>(task: F)
319where
320 F: Future<Output = ()> + Send + 'static,
321{
322 tokio::spawn(task);
323}
324
325#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
326pub fn spawn_background_task<F>(task: F)
327where
328 F: Future<Output = ()> + 'static,
329{
330 wasm_bindgen_futures::spawn_local(task);
331}
332
333#[cfg(all(target_arch = "wasm32", not(target_os = "unknown")))]
334pub fn spawn_background_task<F>(task: F)
335where
336 F: Future<Output = ()> + 'static,
337{
338 tokio::task::spawn_local(task);
339}
340
341#[cfg(test)]
342mod tests {
343 #[tokio::test]
344 async fn xor_distance_bits_test() -> anyhow::Result<()> {
345 use crate::tools::tools::leading_agreement_bits_xor;
346
347 let tests = [
348 ("0000", "0000", 16),
350 ("ffff", "ffff", 16),
351 ("1234", "1234", 16),
352 ("abcd", "abcd", 16),
353 ("0000", "ffff", 0),
355 ("0000", "0fff", 4),
356 ("0000", "00ff", 8),
357 ("0000", "000f", 12),
358 ("0000", "efff", 0),
360 ("0000", "7fff", 1),
361 ("0000", "3fff", 2),
362 ("0000", "1fff", 3),
363 ("0000", "0fff", 4),
364 ("0000", "07ff", 5),
365 ("0000", "03ff", 6),
366 ("0000", "01ff", 7),
367 ("0000", "00ff", 8),
368 ("0000", "007f", 9),
369 ("0000", "003f", 10),
370 ("0000", "001f", 11),
371 ("0000", "000f", 12),
372 ("0000", "0007", 13),
373 ("0000", "0003", 14),
374 ("0000", "0001", 15),
375 ("0000", "fff9", 0),
377 ("0000", "0ff9", 4),
378 ("0000", "00f9", 8),
379 ("", "0000", 0),
381 ("00", "0000", 8),
382 ("0000", "000000", 16),
383 ];
384
385 for (a, b, expected) in tests {
386 let a_binary = hex::decode(a)?;
387 let b_binary = hex::decode(b)?;
388 {
389 let distance = leading_agreement_bits_xor(&a_binary, &b_binary);
390 assert_eq!(distance, expected, "Failed for {} and {}. Got {} expected {}.", a, b, distance, expected);
391 }
392 {
393 let distance = leading_agreement_bits_xor(&b_binary, &a_binary);
394 assert_eq!(distance, expected, "Failed for {} and {}. Got {} expected {}.", a, b, distance, expected);
395 }
396 }
397 Ok(())
398 }
399}
400