Add connection_verbose setting to log IO events (#774)
This commit is contained in:
		| @@ -64,6 +64,7 @@ struct Config { | |||||||
|     #[cfg(feature = "__tls")] |     #[cfg(feature = "__tls")] | ||||||
|     certs_verification: bool, |     certs_verification: bool, | ||||||
|     connect_timeout: Option<Duration>, |     connect_timeout: Option<Duration>, | ||||||
|  |     connection_verbose: bool, | ||||||
|     max_idle_per_host: usize, |     max_idle_per_host: usize, | ||||||
|     #[cfg(feature = "__tls")] |     #[cfg(feature = "__tls")] | ||||||
|     identity: Option<Identity>, |     identity: Option<Identity>, | ||||||
| @@ -111,6 +112,7 @@ impl ClientBuilder { | |||||||
|                 #[cfg(feature = "__tls")] |                 #[cfg(feature = "__tls")] | ||||||
|                 certs_verification: true, |                 certs_verification: true, | ||||||
|                 connect_timeout: None, |                 connect_timeout: None, | ||||||
|  |                 connection_verbose: false, | ||||||
|                 max_idle_per_host: std::usize::MAX, |                 max_idle_per_host: std::usize::MAX, | ||||||
|                 proxies: Vec::new(), |                 proxies: Vec::new(), | ||||||
|                 auto_sys_proxy: true, |                 auto_sys_proxy: true, | ||||||
| @@ -234,6 +236,7 @@ impl ClientBuilder { | |||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         connector.set_timeout(config.connect_timeout); |         connector.set_timeout(config.connect_timeout); | ||||||
|  |         connector.set_verbose(config.connection_verbose); | ||||||
|  |  | ||||||
|         let mut builder = hyper::Client::builder(); |         let mut builder = hyper::Client::builder(); | ||||||
|         if config.http2_only { |         if config.http2_only { | ||||||
| @@ -489,6 +492,17 @@ impl ClientBuilder { | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Set whether connections should emit verbose logs. | ||||||
|  |     /// | ||||||
|  |     /// Enabling this option will emit [log][] messages at the `TRACE` level | ||||||
|  |     /// for read and write operations on connections. | ||||||
|  |     /// | ||||||
|  |     /// [log]: https://crates.io/crates/log | ||||||
|  |     pub fn connection_verbose(mut self, verbose: bool) -> ClientBuilder { | ||||||
|  |         self.config.connection_verbose = verbose; | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // HTTP options |     // HTTP options | ||||||
|  |  | ||||||
|     /// Sets the maximum idle connection per host allowed in the pool. |     /// Sets the maximum idle connection per host allowed in the pool. | ||||||
|   | |||||||
| @@ -487,6 +487,8 @@ impl PercentEncoding { | |||||||
| } | } | ||||||
|  |  | ||||||
| fn gen_boundary() -> String { | fn gen_boundary() -> String { | ||||||
|  |     use crate::util::fast_random as random; | ||||||
|  |  | ||||||
|     let a = random(); |     let a = random(); | ||||||
|     let b = random(); |     let b = random(); | ||||||
|     let c = random(); |     let c = random(); | ||||||
| @@ -495,42 +497,6 @@ fn gen_boundary() -> String { | |||||||
|     format!("{:016x}-{:016x}-{:016x}-{:016x}", a, b, c, d) |     format!("{:016x}-{:016x}-{:016x}-{:016x}", a, b, c, d) | ||||||
| } | } | ||||||
|  |  | ||||||
| // xor-shift |  | ||||||
| fn random() -> u64 { |  | ||||||
|     use std::cell::Cell; |  | ||||||
|     use std::collections::hash_map::RandomState; |  | ||||||
|     use std::hash::{BuildHasher, Hasher}; |  | ||||||
|     use std::num::Wrapping; |  | ||||||
|  |  | ||||||
|     thread_local! { |  | ||||||
|         static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(seed())); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn seed() -> u64 { |  | ||||||
|         let seed = RandomState::new(); |  | ||||||
|  |  | ||||||
|         let mut out = 0; |  | ||||||
|         let mut cnt = 0; |  | ||||||
|         while out == 0 { |  | ||||||
|             cnt += 1; |  | ||||||
|             let mut hasher = seed.build_hasher(); |  | ||||||
|             hasher.write_usize(cnt); |  | ||||||
|             out = hasher.finish(); |  | ||||||
|         } |  | ||||||
|         out |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     RNG.with(|rng| { |  | ||||||
|         let mut n = rng.get(); |  | ||||||
|         debug_assert_ne!(n.0, 0); |  | ||||||
|         n ^= n >> 12; |  | ||||||
|         n ^= n << 25; |  | ||||||
|         n ^= n >> 27; |  | ||||||
|         rng.set(n); |  | ||||||
|         n.0.wrapping_mul(0x2545_f491_4f6c_dd1d) |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use super::*; |     use super::*; | ||||||
|   | |||||||
| @@ -273,6 +273,16 @@ impl ClientBuilder { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Set whether connections should emit verbose logs. | ||||||
|  |     /// | ||||||
|  |     /// Enabling this option will emit [log][] messages at the `TRACE` level | ||||||
|  |     /// for read and write operations on connections. | ||||||
|  |     /// | ||||||
|  |     /// [log]: https://crates.io/crates/log | ||||||
|  |     pub fn connection_verbose(self, verbose: bool) -> ClientBuilder { | ||||||
|  |         self.with_inner(move |inner| inner.connection_verbose(verbose)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // HTTP options |     // HTTP options | ||||||
|  |  | ||||||
|     /// Sets the maximum idle connection per host allowed in the pool. |     /// Sets the maximum idle connection per host allowed in the pool. | ||||||
|   | |||||||
							
								
								
									
										163
									
								
								src/connect.rs
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								src/connect.rs
									
									
									
									
									
								
							| @@ -37,6 +37,7 @@ type HttpConnector = hyper::client::HttpConnector; | |||||||
| pub(crate) struct Connector { | pub(crate) struct Connector { | ||||||
|     inner: Inner, |     inner: Inner, | ||||||
|     proxies: Arc<Vec<Proxy>>, |     proxies: Arc<Vec<Proxy>>, | ||||||
|  |     verbose: verbose::Wrapper, | ||||||
|     timeout: Option<Duration>, |     timeout: Option<Duration>, | ||||||
|     #[cfg(feature = "__tls")] |     #[cfg(feature = "__tls")] | ||||||
|     nodelay: bool, |     nodelay: bool, | ||||||
| @@ -73,6 +74,7 @@ impl Connector { | |||||||
|         http.set_nodelay(nodelay); |         http.set_nodelay(nodelay); | ||||||
|         Ok(Connector { |         Ok(Connector { | ||||||
|             inner: Inner::Http(http), |             inner: Inner::Http(http), | ||||||
|  |             verbose: verbose::OFF, | ||||||
|             proxies, |             proxies, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|         }) |         }) | ||||||
| @@ -98,6 +100,7 @@ impl Connector { | |||||||
|         Ok(Connector { |         Ok(Connector { | ||||||
|             inner: Inner::DefaultTls(http, tls), |             inner: Inner::DefaultTls(http, tls), | ||||||
|             proxies, |             proxies, | ||||||
|  |             verbose: verbose::OFF, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|             nodelay, |             nodelay, | ||||||
|             user_agent, |             user_agent, | ||||||
| @@ -135,6 +138,7 @@ impl Connector { | |||||||
|                 tls_proxy, |                 tls_proxy, | ||||||
|             }, |             }, | ||||||
|             proxies, |             proxies, | ||||||
|  |             verbose: verbose::OFF, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|             nodelay, |             nodelay, | ||||||
|             user_agent, |             user_agent, | ||||||
| @@ -145,6 +149,10 @@ impl Connector { | |||||||
|         self.timeout = timeout; |         self.timeout = timeout; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn set_verbose(&mut self, enabled: bool) { | ||||||
|  |         self.verbose.0 = enabled; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[cfg(feature = "socks")] |     #[cfg(feature = "socks")] | ||||||
|     async fn connect_socks( |     async fn connect_socks( | ||||||
|         &self, |         &self, | ||||||
| @@ -178,7 +186,7 @@ impl Connector { | |||||||
|                         .await |                         .await | ||||||
|                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | ||||||
|                     return Ok(Conn { |                     return Ok(Conn { | ||||||
|                         inner: Box::new(NativeTlsConn { inner: io }), |                         inner: self.verbose.wrap(NativeTlsConn { inner: io }), | ||||||
|                         is_proxy: false, |                         is_proxy: false, | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
| @@ -203,7 +211,7 @@ impl Connector { | |||||||
|                         .await |                         .await | ||||||
|                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | ||||||
|                     return Ok(Conn { |                     return Ok(Conn { | ||||||
|                         inner: Box::new(RustlsTlsConn { inner: io }), |                         inner: self.verbose.wrap(RustlsTlsConn { inner: io }), | ||||||
|                         is_proxy: false, |                         is_proxy: false, | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
| @@ -212,7 +220,10 @@ impl Connector { | |||||||
|             Inner::Http(_) => () |             Inner::Http(_) => () | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         socks::connect(proxy, dst, dns).await |         socks::connect(proxy, dst, dns).await.map(|tcp| Conn { | ||||||
|  |             inner: self.verbose.wrap(tcp), | ||||||
|  |             is_proxy: false, | ||||||
|  |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async fn connect_with_maybe_proxy( |     async fn connect_with_maybe_proxy( | ||||||
| @@ -225,7 +236,7 @@ impl Connector { | |||||||
|             Inner::Http(mut http) => { |             Inner::Http(mut http) => { | ||||||
|                 let io = http.call(dst).await?; |                 let io = http.call(dst).await?; | ||||||
|                 Ok(Conn { |                 Ok(Conn { | ||||||
|                     inner: Box::new(io), |                     inner: self.verbose.wrap(io), | ||||||
|                     is_proxy, |                     is_proxy, | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
| @@ -246,7 +257,7 @@ impl Connector { | |||||||
|                 //} |                 //} | ||||||
|  |  | ||||||
|                 Ok(Conn { |                 Ok(Conn { | ||||||
|                     inner: Box::new(io), |                     inner: self.verbose.wrap(io), | ||||||
|                     is_proxy, |                     is_proxy, | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
| @@ -270,7 +281,7 @@ impl Connector { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 Ok(Conn { |                 Ok(Conn { | ||||||
|                     inner: Box::new(io), |                     inner: self.verbose.wrap(io), | ||||||
|                     is_proxy, |                     is_proxy, | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
| @@ -322,7 +333,7 @@ impl Connector { | |||||||
|                         .await |                         .await | ||||||
|                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | ||||||
|                     return Ok(Conn { |                     return Ok(Conn { | ||||||
|                         inner: Box::new(NativeTlsConn { inner: io }), |                         inner: self.verbose.wrap(NativeTlsConn { inner: io }), | ||||||
|                         is_proxy: false, |                         is_proxy: false, | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
| @@ -359,7 +370,7 @@ impl Connector { | |||||||
|                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | ||||||
|  |  | ||||||
|                     return Ok(Conn { |                     return Ok(Conn { | ||||||
|                         inner: Box::new(RustlsTlsConn { inner: io }), |                         inner: self.verbose.wrap(RustlsTlsConn { inner: io }), | ||||||
|                         is_proxy: false, |                         is_proxy: false, | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
| @@ -438,8 +449,11 @@ impl Service<Uri> for Connector | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) trait AsyncConn: AsyncRead + AsyncWrite + Connection {} | pub(crate) trait AsyncConn: AsyncRead + AsyncWrite + Connection + Send + Sync + Unpin + 'static {} | ||||||
| impl<T: AsyncRead + AsyncWrite + Connection> AsyncConn for T {} |  | ||||||
|  | impl<T: AsyncRead + AsyncWrite + Connection + Send + Sync + Unpin + 'static> AsyncConn for T {} | ||||||
|  |  | ||||||
|  | type BoxConn = Box<dyn AsyncConn>; | ||||||
|  |  | ||||||
| pin_project! { | pin_project! { | ||||||
|     /// Note: the `is_proxy` member means *is plain text HTTP proxy*. |     /// Note: the `is_proxy` member means *is plain text HTTP proxy*. | ||||||
| @@ -448,7 +462,7 @@ pin_project! { | |||||||
|     /// * absolute-form (`GET http://foo.bar/and/a/path HTTP/1.1`), otherwise. |     /// * absolute-form (`GET http://foo.bar/and/a/path HTTP/1.1`), otherwise. | ||||||
|     pub(crate) struct Conn { |     pub(crate) struct Conn { | ||||||
|         #[pin] |         #[pin] | ||||||
|         inner: Box<dyn AsyncConn + Send + Sync + Unpin + 'static>, |         inner: BoxConn, | ||||||
|         is_proxy: bool, |         is_proxy: bool, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -795,11 +809,13 @@ mod rustls_tls_conn { | |||||||
|  |  | ||||||
| #[cfg(feature = "socks")] | #[cfg(feature = "socks")] | ||||||
| mod socks { | mod socks { | ||||||
|     use http::Uri; |  | ||||||
|     use tokio_socks::tcp::Socks5Stream; |  | ||||||
|     use std::io; |     use std::io; | ||||||
|     use std::net::ToSocketAddrs; |     use std::net::ToSocketAddrs; | ||||||
|  |  | ||||||
|  |     use http::Uri; | ||||||
|  |     use tokio::net::TcpStream; | ||||||
|  |     use tokio_socks::tcp::Socks5Stream; | ||||||
|  |  | ||||||
|     use super::{BoxError, Scheme}; |     use super::{BoxError, Scheme}; | ||||||
|     use crate::proxy::ProxyScheme; |     use crate::proxy::ProxyScheme; | ||||||
|  |  | ||||||
| @@ -812,7 +828,7 @@ mod socks { | |||||||
|         proxy: ProxyScheme, |         proxy: ProxyScheme, | ||||||
|         dst: Uri, |         dst: Uri, | ||||||
|         dns: DnsResolve, |         dns: DnsResolve, | ||||||
|     ) -> Result<super::Conn, BoxError> { |     ) -> Result<TcpStream, BoxError> { | ||||||
|         let https = dst.scheme() == Some(&Scheme::HTTPS); |         let https = dst.scheme() == Some(&Scheme::HTTPS); | ||||||
|         let original_host = dst |         let original_host = dst | ||||||
|             .host() |             .host() | ||||||
| @@ -852,10 +868,123 @@ mod socks { | |||||||
|                 .map_err(|e| format!("socks connect error: {}", e))? |                 .map_err(|e| format!("socks connect error: {}", e))? | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         Ok(super::Conn { |         Ok(stream.into_inner()) | ||||||
|             inner: Box::new( stream.into_inner() ), |     } | ||||||
|             is_proxy: false, | } | ||||||
|  |  | ||||||
|  | mod verbose { | ||||||
|  |     use std::fmt; | ||||||
|  |     use std::pin::Pin; | ||||||
|  |     use std::task::{Context, Poll}; | ||||||
|  |     use hyper::client::connect::{Connected, Connection}; | ||||||
|  |     use tokio::io::{AsyncRead, AsyncWrite}; | ||||||
|  |  | ||||||
|  |     pub(super) const OFF: Wrapper = Wrapper(false); | ||||||
|  |  | ||||||
|  |     #[derive(Clone, Copy)] | ||||||
|  |     pub(super) struct Wrapper(pub(super) bool); | ||||||
|  |  | ||||||
|  |     impl Wrapper { | ||||||
|  |         pub(super) fn wrap<T: super::AsyncConn>(&self, conn: T) -> super::BoxConn { | ||||||
|  |             if self.0 && log::log_enabled!(log::Level::Trace) { | ||||||
|  |                 Box::new(Verbose { | ||||||
|  |                     // truncate is fine | ||||||
|  |                     id: crate::util::fast_random() as u32, | ||||||
|  |                     inner: conn, | ||||||
|                 }) |                 }) | ||||||
|  |             } else { | ||||||
|  |                 Box::new(conn) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct Verbose<T> { | ||||||
|  |         id: u32, | ||||||
|  |         inner: T, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl<T: Connection + AsyncRead + AsyncWrite + Unpin> Connection for Verbose<T> { | ||||||
|  |         fn connected(&self) -> Connected { | ||||||
|  |             self.inner.connected() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for Verbose<T> { | ||||||
|  |         fn poll_read( | ||||||
|  |             mut self: Pin<&mut Self>, | ||||||
|  |             cx: &mut Context, | ||||||
|  |             buf: &mut [u8] | ||||||
|  |         ) -> Poll<std::io::Result<usize>> { | ||||||
|  |             match Pin::new(&mut self.inner).poll_read(cx, buf) { | ||||||
|  |                 Poll::Ready(Ok(n)) => { | ||||||
|  |                     log::trace!("{:08x} read: {:?}", self.id, Escape(&buf[..n])); | ||||||
|  |                     Poll::Ready(Ok(n)) | ||||||
|  |                 }, | ||||||
|  |                 Poll::Ready(Err(e)) => { | ||||||
|  |                     Poll::Ready(Err(e)) | ||||||
|  |                 }, | ||||||
|  |                 Poll::Pending => Poll::Pending, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl<T: AsyncRead + AsyncWrite + Unpin> AsyncWrite for Verbose<T> { | ||||||
|  |         fn poll_write( | ||||||
|  |             mut self: Pin<&mut Self>, | ||||||
|  |             cx: &mut Context, | ||||||
|  |             buf: &[u8] | ||||||
|  |         ) -> Poll<Result<usize, std::io::Error>> { | ||||||
|  |             match Pin::new(&mut self.inner).poll_write(cx, buf) { | ||||||
|  |                 Poll::Ready(Ok(n)) => { | ||||||
|  |                     log::trace!("{:08x} write: {:?}", self.id, Escape(&buf[..n])); | ||||||
|  |                     Poll::Ready(Ok(n)) | ||||||
|  |                 }, | ||||||
|  |                 Poll::Ready(Err(e)) => { | ||||||
|  |                     Poll::Ready(Err(e)) | ||||||
|  |                 }, | ||||||
|  |                 Poll::Pending => Poll::Pending, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), std::io::Error>> { | ||||||
|  |             Pin::new(&mut self.inner).poll_flush(cx) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn poll_shutdown( | ||||||
|  |             mut self: Pin<&mut Self>, | ||||||
|  |             cx: &mut Context | ||||||
|  |         ) -> Poll<Result<(), std::io::Error>> { | ||||||
|  |             Pin::new(&mut self.inner).poll_shutdown(cx) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct Escape<'a>(&'a [u8]); | ||||||
|  |  | ||||||
|  |     impl fmt::Debug for Escape<'_> { | ||||||
|  |         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |             write!(f, "b\"")?; | ||||||
|  |             for &c in self.0 { | ||||||
|  |                 // https://doc.rust-lang.org/reference.html#byte-escapes | ||||||
|  |                 if c == b'\n' { | ||||||
|  |                     write!(f, "\\n")?; | ||||||
|  |                 } else if c == b'\r' { | ||||||
|  |                     write!(f, "\\r")?; | ||||||
|  |                 } else if c == b'\t' { | ||||||
|  |                     write!(f, "\\t")?; | ||||||
|  |                 } else if c == b'\\' || c == b'"' { | ||||||
|  |                     write!(f, "\\{}", c as char)?; | ||||||
|  |                 } else if c == b'\0' { | ||||||
|  |                     write!(f, "\\0")?; | ||||||
|  |                 // ASCII printable | ||||||
|  |                 } else if c >= 0x20 && c < 0x7f { | ||||||
|  |                     write!(f, "{}", c as char)?; | ||||||
|  |                 } else { | ||||||
|  |                     write!(f, "\\x{:02x}", c)?; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             write!(f, "\"")?; | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -294,6 +294,7 @@ if_hyper! { | |||||||
|     pub mod redirect; |     pub mod redirect; | ||||||
|     #[cfg(feature = "__tls")] |     #[cfg(feature = "__tls")] | ||||||
|     mod tls; |     mod tls; | ||||||
|  |     mod util; | ||||||
| } | } | ||||||
|  |  | ||||||
| if_wasm! { | if_wasm! { | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								src/util.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/util.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | // xor-shift | ||||||
|  | pub(crate) fn fast_random() -> u64 { | ||||||
|  |     use std::cell::Cell; | ||||||
|  |     use std::collections::hash_map::RandomState; | ||||||
|  |     use std::hash::{BuildHasher, Hasher}; | ||||||
|  |     use std::num::Wrapping; | ||||||
|  |  | ||||||
|  |     thread_local! { | ||||||
|  |         static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(seed())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn seed() -> u64 { | ||||||
|  |         let seed = RandomState::new(); | ||||||
|  |  | ||||||
|  |         let mut out = 0; | ||||||
|  |         let mut cnt = 0; | ||||||
|  |         while out == 0 { | ||||||
|  |             cnt += 1; | ||||||
|  |             let mut hasher = seed.build_hasher(); | ||||||
|  |             hasher.write_usize(cnt); | ||||||
|  |             out = hasher.finish(); | ||||||
|  |         } | ||||||
|  |         out | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     RNG.with(|rng| { | ||||||
|  |         let mut n = rng.get(); | ||||||
|  |         debug_assert_ne!(n.0, 0); | ||||||
|  |         n ^= n >> 12; | ||||||
|  |         n ^= n << 25; | ||||||
|  |         n ^= n >> 27; | ||||||
|  |         rng.set(n); | ||||||
|  |         n.0.wrapping_mul(0x2545_f491_4f6c_dd1d) | ||||||
|  |     }) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user