refactor
This commit is contained in:
		
							
								
								
									
										179
									
								
								veilid-tools/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								veilid-tools/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| [package] | ||||
| name = "veilid-core" | ||||
| version = "0.1.0" | ||||
| authors = ["John Smith <nobody@example.com>"] | ||||
| edition = "2021" | ||||
| build = "build.rs" | ||||
| license = "LGPL-2.0-or-later OR MPL-2.0 OR (MIT AND BSD-3-Clause)" | ||||
|  | ||||
| [lib] | ||||
| crate-type = ["cdylib", "staticlib", "rlib"] | ||||
|  | ||||
| [features] | ||||
| default = [] | ||||
| rt-async-std = [ "async-std", "async-std-resolver", "async_executors/async_std", "rtnetlink?/smol_socket" ] | ||||
| rt-tokio = [ "tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket" ] | ||||
|  | ||||
| android_tests = [] | ||||
| ios_tests = [ "simplelog" ] | ||||
| tracking = [] | ||||
|  | ||||
| [dependencies] | ||||
| tracing = { version = "^0", features = ["log", "attributes"] } | ||||
| tracing-subscriber = "^0" | ||||
| tracing-error = "^0" | ||||
| eyre = "^0" | ||||
| capnp = { version = "^0", default_features = false } | ||||
| rust-fsm = "^0" | ||||
| static_assertions = "^1" | ||||
| cfg-if = "^1" | ||||
| thiserror = "^1" | ||||
| hex = "^0" | ||||
| generic-array = "^0" | ||||
| secrecy = "^0" | ||||
| chacha20poly1305 = "^0" | ||||
| chacha20 = "^0" | ||||
| hashlink = { path = "../external/hashlink", features = ["serde_impl"] } | ||||
| serde = { version = "^1", features = ["derive" ] } | ||||
| serde_json = { version = "^1" } | ||||
| serde-big-array = "^0" | ||||
| futures-util = { version = "^0", default_features = false, features = ["alloc"] } | ||||
| parking_lot = "^0" | ||||
| lazy_static = "^1" | ||||
| directories = "^4" | ||||
| once_cell = "^1" | ||||
| json = "^0" | ||||
| owning_ref = "^0" | ||||
| flume = { version = "^0", features = ["async"] } | ||||
| enumset = { version= "^1", features = ["serde"] } | ||||
| backtrace = { version = "^0" } | ||||
| owo-colors = "^3" | ||||
| stop-token = { version = "^0", default-features = false } | ||||
| ed25519-dalek = { version = "^1", default_features = false, features = ["alloc", "u64_backend"] } | ||||
| x25519-dalek = { package = "x25519-dalek-ng", version = "^1", default_features = false, features = ["u64_backend"] } | ||||
| curve25519-dalek = { package = "curve25519-dalek-ng", version = "^4", default_features = false, features = ["alloc", "u64_backend"] } | ||||
| # ed25519-dalek needs rand 0.7 until it updates itself | ||||
| rand = "0.7" | ||||
| # curve25519-dalek-ng is stuck on digest 0.9.0 | ||||
| blake3 = { version = "1.1.0", default_features = false } | ||||
| digest = "0.9.0" | ||||
| rtnetlink = { version = "^0", default-features = false, optional = true } | ||||
| async-std-resolver = { version = "^0", optional = true } | ||||
| trust-dns-resolver = { version = "^0", optional = true } | ||||
| keyvaluedb = { path = "../external/keyvaluedb/keyvaluedb" } | ||||
| #rkyv = { version = "^0", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] } | ||||
| rkyv = { git = "https://github.com/rkyv/rkyv.git", rev = "57e2a8d", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] } | ||||
| bytecheck = "^0" | ||||
|  | ||||
| # Dependencies for native builds only | ||||
| # Linux, Windows, Mac, iOS, Android | ||||
| [target.'cfg(not(target_arch = "wasm32"))'.dependencies] | ||||
| async-std = { version = "^1", features = ["unstable"], optional = true} | ||||
| tokio = { version = "^1", features = ["full"], optional = true} | ||||
| tokio-util = { version = "^0", features = ["compat"], optional = true} | ||||
| tokio-stream = { version = "^0", features = ["net"], optional = true} | ||||
| async-io = { version = "^1" } | ||||
| async-tungstenite = { version = "^0", features = ["async-tls"] } | ||||
| maplit = "^1" | ||||
| config = { version = "^0", features = ["yaml"] } | ||||
| keyring-manager = { path = "../external/keyring-manager" } | ||||
| async-tls = "^0.11" | ||||
| igd  = { path = "../external/rust-igd" } | ||||
| webpki = "^0" | ||||
| webpki-roots = "^0" | ||||
| rustls = "^0.19" | ||||
| rustls-pemfile = "^0.2" | ||||
| futures-util = { version = "^0", default-features = false, features = ["async-await", "sink", "std", "io"] } | ||||
| keyvaluedb-sqlite = { path = "../external/keyvaluedb/keyvaluedb-sqlite" } | ||||
| data-encoding = { version = "^2" } | ||||
|  | ||||
| socket2 = "^0" | ||||
| bugsalot = "^0" | ||||
| chrono = "^0" | ||||
| libc = "^0" | ||||
| nix = "^0" | ||||
|  | ||||
| # Dependencies for WASM builds only | ||||
| [target.'cfg(target_arch = "wasm32")'.dependencies] | ||||
| wasm-bindgen = "^0" | ||||
| js-sys = "^0" | ||||
| wasm-bindgen-futures = "^0" | ||||
| keyvaluedb-web = { path = "../external/keyvaluedb/keyvaluedb-web" } | ||||
| data-encoding = { version = "^2", default_features = false, features = ["alloc"] } | ||||
| getrandom = { version = "^0", features = ["js"] } | ||||
| ws_stream_wasm = "^0" | ||||
| async_executors = { version = "^0", default-features = false, features = [ "bindgen", "timer" ]} | ||||
| async-lock = "^2" | ||||
| send_wrapper = { version = "^0", features = ["futures"] } | ||||
| wasm-logger = "^0" | ||||
| tracing-wasm = "^0" | ||||
|  | ||||
| # Configuration for WASM32 'web-sys' crate | ||||
| [target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] | ||||
| version = "^0" | ||||
| features = [ | ||||
|     'Document', | ||||
|     'HtmlDocument', | ||||
|     # 'Element', | ||||
|     # 'HtmlElement', | ||||
|     # 'Node', | ||||
|     'IdbFactory', | ||||
|     'IdbOpenDbRequest', | ||||
|     'Storage', | ||||
|     'Location', | ||||
|     'Window', | ||||
| ] | ||||
|  | ||||
| # Dependencies for Android | ||||
| [target.'cfg(target_os = "android")'.dependencies] | ||||
| jni = "^0" | ||||
| jni-sys = "^0" | ||||
| ndk = { version = "^0", features = ["trace"] } | ||||
| ndk-glue = { version = "^0", features = ["logger"] } | ||||
| tracing-android = { version = "^0" } | ||||
|  | ||||
| # Dependenices for all Unix (Linux, Android, MacOS, iOS) | ||||
| [target.'cfg(unix)'.dependencies] | ||||
| ifstructs = "^0" | ||||
|  | ||||
| # Dependencies for Linux or Android | ||||
| [target.'cfg(any(target_os = "android",target_os = "linux"))'.dependencies] | ||||
| rtnetlink = { version = "^0", default-features = false } | ||||
|  | ||||
| # Dependencies for Windows | ||||
| [target.'cfg(target_os = "windows")'.dependencies] | ||||
| winapi = { version = "^0", features = [ "iptypes", "iphlpapi" ] } | ||||
| windows = { version = "^0", features = [ "Win32_NetworkManagement_Dns", "Win32_Foundation", "alloc" ]} | ||||
| windows-permissions = "^0" | ||||
|  | ||||
| # Dependencies for iOS | ||||
| [target.'cfg(target_os = "ios")'.dependencies] | ||||
| simplelog = { version = "^0", optional = true } | ||||
|  | ||||
| # Rusqlite configuration to ensure platforms that don't come with sqlite get it bundled | ||||
| # Except WASM which doesn't use sqlite | ||||
| [target.'cfg(all(not(target_os = "ios"),not(target_os = "android"),not(target_arch = "wasm32")))'.dependencies.rusqlite] | ||||
| version = "^0" | ||||
| features = ["bundled"] | ||||
|  | ||||
| ### DEV DEPENDENCIES | ||||
|  | ||||
| [dev-dependencies] | ||||
| serial_test = "^0" | ||||
|  | ||||
| [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] | ||||
| simplelog = { version = "^0", features=["test"] } | ||||
|  | ||||
| [target.'cfg(target_arch = "wasm32")'.dev-dependencies] | ||||
| wasm-bindgen-test = "^0" | ||||
| console_error_panic_hook = "^0" | ||||
| wee_alloc = "^0" | ||||
| wasm-logger = "^0" | ||||
|  | ||||
| ### BUILD OPTIONS | ||||
|  | ||||
| [build-dependencies] | ||||
| capnpc = "^0" | ||||
|  | ||||
| [package.metadata.wasm-pack.profile.release] | ||||
| wasm-opt = ["-O", "--enable-mutable-globals"] | ||||
							
								
								
									
										44
									
								
								veilid-tools/ios_build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								veilid-tools/ios_build.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" | ||||
| CARGO_MANIFEST_PATH=$(python -c "import os; print(os.path.realpath(\"$SCRIPTDIR/Cargo.toml\"))") | ||||
| # echo CARGO_MANIFEST_PATH: $CARGO_MANIFEST_PATH  | ||||
|  | ||||
| if [ "$CONFIGURATION" == "Debug" ]; then  | ||||
|     EXTRA_CARGO_OPTIONS="$@" | ||||
| else | ||||
|     EXTRA_CARGO_OPTIONS="$@ --release" | ||||
| fi | ||||
| ARCHS=${ARCHS:=arm64} | ||||
| for arch in $ARCHS | ||||
| do | ||||
|     if [ "$arch" == "arm64" ]; then | ||||
|         echo arm64 | ||||
|         CARGO_TARGET=aarch64-apple-ios | ||||
|         #CARGO_TOOLCHAIN=+ios-arm64-1.57.0 | ||||
|         CARGO_TOOLCHAIN= | ||||
|     elif [ "$arch" == "x86_64" ]; then | ||||
|         echo x86_64 | ||||
|         CARGO_TARGET=x86_64-apple-ios | ||||
|         CARGO_TOOLCHAIN= | ||||
|     else | ||||
|         echo Unsupported ARCH: $arch | ||||
|         continue | ||||
|     fi | ||||
|  | ||||
|     CARGO=`which cargo` | ||||
|     CARGO=${CARGO:=~/.cargo/bin/cargo} | ||||
|     CARGO_DIR=$(dirname $CARGO) | ||||
|  | ||||
|     # Choose arm64 brew for unit tests by default if we are on M1 | ||||
|     if [ -f /opt/homebrew/bin/brew ]; then | ||||
|         HOMEBREW_DIR=/opt/homebrew/bin | ||||
|     elif [ -f /usr/local/bin/brew ]; then | ||||
|         HOMEBREW_DIR=/usr/local/bin | ||||
|     else  | ||||
|         HOMEBREW_DIR=$(dirname `which brew`) | ||||
|     fi | ||||
|  | ||||
|     env -i PATH=/usr/bin:/bin:$HOMEBREW_DIR:$CARGO_DIR HOME="$HOME" USER="$USER" cargo $CARGO_TOOLCHAIN build $EXTRA_CARGO_OPTIONS --target $CARGO_TARGET --manifest-path $CARGO_MANIFEST_PATH | ||||
| done | ||||
|  | ||||
							
								
								
									
										222
									
								
								veilid-tools/src/async_peek_stream.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								veilid-tools/src/async_peek_stream.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| use super::*; | ||||
|  | ||||
| use std::io; | ||||
| use task::{Context, Poll}; | ||||
|  | ||||
| //////// | ||||
| /// | ||||
| trait SendStream: AsyncRead + AsyncWrite + Send + Unpin {} | ||||
| impl<S> SendStream for S where S: AsyncRead + AsyncWrite + Send + Unpin + 'static {} | ||||
|  | ||||
| //////// | ||||
| /// | ||||
|  | ||||
| pub struct Peek<'a> { | ||||
|     aps: AsyncPeekStream, | ||||
|     buf: &'a mut [u8], | ||||
| } | ||||
|  | ||||
| impl Unpin for Peek<'_> {} | ||||
|  | ||||
| impl Future for Peek<'_> { | ||||
|     type Output = std::io::Result<usize>; | ||||
|  | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|  | ||||
|         let mut inner = this.aps.inner.lock(); | ||||
|         let inner = &mut *inner; | ||||
|         // | ||||
|         let buf_len = this.buf.len(); | ||||
|         let mut copy_len = buf_len; | ||||
|         if buf_len > inner.peekbuf_len { | ||||
|             // | ||||
|             inner.peekbuf.resize(buf_len, 0u8); | ||||
|             let read_len = match Pin::new(&mut inner.stream).poll_read( | ||||
|                 cx, | ||||
|                 &mut inner.peekbuf.as_mut_slice()[inner.peekbuf_len..buf_len], | ||||
|             ) { | ||||
|                 Poll::Pending => { | ||||
|                     inner.peekbuf.resize(inner.peekbuf_len, 0u8); | ||||
|                     return Poll::Pending; | ||||
|                 } | ||||
|                 Poll::Ready(Err(e)) => { | ||||
|                     return Poll::Ready(Err(e)); | ||||
|                 } | ||||
|                 Poll::Ready(Ok(v)) => v, | ||||
|             }; | ||||
|             inner.peekbuf_len += read_len; | ||||
|             inner.peekbuf.resize(inner.peekbuf_len, 0u8); | ||||
|             copy_len = inner.peekbuf_len; | ||||
|         } | ||||
|         this.buf[..copy_len].copy_from_slice(&inner.peekbuf[..copy_len]); | ||||
|         Poll::Ready(Ok(copy_len)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| //////// | ||||
| /// | ||||
|  | ||||
| pub struct PeekExact<'a> { | ||||
|     aps: AsyncPeekStream, | ||||
|     buf: &'a mut [u8], | ||||
|     cur_read: usize, | ||||
| } | ||||
|  | ||||
| impl Unpin for PeekExact<'_> {} | ||||
|  | ||||
| impl Future for PeekExact<'_> { | ||||
|     type Output = std::io::Result<usize>; | ||||
|  | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|  | ||||
|         let mut inner = this.aps.inner.lock(); | ||||
|         let inner = &mut *inner; | ||||
|         // | ||||
|         let buf_len = this.buf.len(); | ||||
|         let mut copy_len = buf_len; | ||||
|         if buf_len > inner.peekbuf_len { | ||||
|             // | ||||
|             inner.peekbuf.resize(buf_len, 0u8); | ||||
|             let read_len = match Pin::new(&mut inner.stream).poll_read( | ||||
|                 cx, | ||||
|                 &mut inner.peekbuf.as_mut_slice()[inner.peekbuf_len..buf_len], | ||||
|             ) { | ||||
|                 Poll::Pending => { | ||||
|                     inner.peekbuf.resize(inner.peekbuf_len, 0u8); | ||||
|                     return Poll::Pending; | ||||
|                 } | ||||
|                 Poll::Ready(Err(e)) => { | ||||
|                     return Poll::Ready(Err(e)); | ||||
|                 } | ||||
|                 Poll::Ready(Ok(v)) => v, | ||||
|             }; | ||||
|             inner.peekbuf_len += read_len; | ||||
|             inner.peekbuf.resize(inner.peekbuf_len, 0u8); | ||||
|             copy_len = inner.peekbuf_len; | ||||
|         } | ||||
|         this.buf[this.cur_read..copy_len].copy_from_slice(&inner.peekbuf[this.cur_read..copy_len]); | ||||
|         this.cur_read = copy_len; | ||||
|         if this.cur_read == buf_len { | ||||
|             Poll::Ready(Ok(buf_len)) | ||||
|         } else { | ||||
|             Poll::Pending | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ///////// | ||||
| /// | ||||
| struct AsyncPeekStreamInner { | ||||
|     stream: Box<dyn SendStream>, | ||||
|     peekbuf: Vec<u8>, | ||||
|     peekbuf_len: usize, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct AsyncPeekStream | ||||
| where | ||||
|     Self: AsyncRead + AsyncWrite + Send + Unpin, | ||||
| { | ||||
|     inner: Arc<Mutex<AsyncPeekStreamInner>>, | ||||
| } | ||||
|  | ||||
| impl AsyncPeekStream { | ||||
|     pub fn new<S>(stream: S) -> Self | ||||
|     where | ||||
|         S: AsyncRead + AsyncWrite + Send + Unpin + 'static, | ||||
|     { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(AsyncPeekStreamInner { | ||||
|                 stream: Box::new(stream), | ||||
|                 peekbuf: Vec::new(), | ||||
|                 peekbuf_len: 0, | ||||
|             })), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn peek<'a>(&'a self, buf: &'a mut [u8]) -> Peek<'a> { | ||||
|         Peek::<'a> { | ||||
|             aps: self.clone(), | ||||
|             buf, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn peek_exact<'a>(&'a self, buf: &'a mut [u8]) -> PeekExact<'a> { | ||||
|         PeekExact::<'a> { | ||||
|             aps: self.clone(), | ||||
|             buf, | ||||
|             cur_read: 0, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl AsyncRead for AsyncPeekStream { | ||||
|     fn poll_read( | ||||
|         self: Pin<&mut Self>, | ||||
|         cx: &mut Context<'_>, | ||||
|         buf: &mut [u8], | ||||
|     ) -> Poll<io::Result<usize>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         // | ||||
|         let buflen = buf.len(); | ||||
|         let bufcopylen = core::cmp::min(buflen, inner.peekbuf_len); | ||||
|         let bufreadlen = if buflen > inner.peekbuf_len { | ||||
|             buflen - inner.peekbuf_len | ||||
|         } else { | ||||
|             0 | ||||
|         }; | ||||
|  | ||||
|         if bufreadlen > 0 { | ||||
|             match Pin::new(&mut inner.stream).poll_read(cx, &mut buf[bufcopylen..buflen]) { | ||||
|                 Poll::Ready(res) => { | ||||
|                     let readlen = res?; | ||||
|                     buf[0..bufcopylen].copy_from_slice(&inner.peekbuf[0..bufcopylen]); | ||||
|                     inner.peekbuf_len = 0; | ||||
|                     Poll::Ready(Ok(bufcopylen + readlen)) | ||||
|                 } | ||||
|                 Poll::Pending => { | ||||
|                     if bufcopylen > 0 { | ||||
|                         buf[0..bufcopylen].copy_from_slice(&inner.peekbuf[0..bufcopylen]); | ||||
|                         inner.peekbuf_len = 0; | ||||
|                         Poll::Ready(Ok(bufcopylen)) | ||||
|                     } else { | ||||
|                         Poll::Pending | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             buf[0..bufcopylen].copy_from_slice(&inner.peekbuf[0..bufcopylen]); | ||||
|             if bufcopylen == inner.peekbuf_len { | ||||
|                 inner.peekbuf_len = 0; | ||||
|             } else if bufcopylen != 0 { | ||||
|                 // slide buffer over by bufcopylen | ||||
|                 let tail = inner.peekbuf.split_off(bufcopylen); | ||||
|                 inner.peekbuf = tail; | ||||
|                 inner.peekbuf_len -= bufcopylen; | ||||
|             } | ||||
|             Poll::Ready(Ok(bufcopylen)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl AsyncWrite for AsyncPeekStream { | ||||
|     fn poll_write( | ||||
|         self: Pin<&mut Self>, | ||||
|         cx: &mut Context<'_>, | ||||
|         buf: &[u8], | ||||
|     ) -> Poll<io::Result<usize>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut inner.stream).poll_write(cx, buf) | ||||
|     } | ||||
|     fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut inner.stream).poll_flush(cx) | ||||
|     } | ||||
|     fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut inner.stream).poll_close(cx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl core::marker::Unpin for AsyncPeekStream {} | ||||
							
								
								
									
										138
									
								
								veilid-tools/src/async_tag_lock.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								veilid-tools/src/async_tag_lock.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::fmt::Debug; | ||||
| use core::hash::Hash; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct AsyncTagLockGuard<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     table: AsyncTagLockTable<T>, | ||||
|     tag: T, | ||||
|     _guard: AsyncMutexGuardArc<()>, | ||||
| } | ||||
|  | ||||
| impl<T> AsyncTagLockGuard<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     fn new(table: AsyncTagLockTable<T>, tag: T, guard: AsyncMutexGuardArc<()>) -> Self { | ||||
|         Self { | ||||
|             table, | ||||
|             tag, | ||||
|             _guard: guard, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Drop for AsyncTagLockGuard<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         let mut inner = self.table.inner.lock(); | ||||
|         // Inform the table we're dropping this guard | ||||
|         let waiters = { | ||||
|             // Get the table entry, it must exist since we have a guard locked | ||||
|             let entry = inner.table.get_mut(&self.tag).unwrap(); | ||||
|             // Decrement the number of waiters | ||||
|             entry.waiters -= 1; | ||||
|             // Return the number of waiters left | ||||
|             entry.waiters | ||||
|         }; | ||||
|         // If there are no waiters left, we remove the tag from the table | ||||
|         if waiters == 0 { | ||||
|             inner.table.remove(&self.tag).unwrap(); | ||||
|         } | ||||
|         // Proceed with releasing _guard, which may cause some concurrent tag lock to acquire | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| struct AsyncTagLockTableEntry { | ||||
|     mutex: Arc<AsyncMutex<()>>, | ||||
|     waiters: usize, | ||||
| } | ||||
|  | ||||
| struct AsyncTagLockTableInner<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     table: HashMap<T, AsyncTagLockTableEntry>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct AsyncTagLockTable<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     inner: Arc<Mutex<AsyncTagLockTableInner<T>>>, | ||||
| } | ||||
|  | ||||
| impl<T> fmt::Debug for AsyncTagLockTable<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         f.debug_struct("AsyncTagLockTable").finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> AsyncTagLockTable<T> | ||||
| where | ||||
|     T: Hash + Eq + Clone + Debug, | ||||
| { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(AsyncTagLockTableInner { | ||||
|                 table: HashMap::new(), | ||||
|             })), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn len(&self) -> usize { | ||||
|         let inner = self.inner.lock(); | ||||
|         inner.table.len() | ||||
|     } | ||||
|  | ||||
|     pub async fn lock_tag(&self, tag: T) -> AsyncTagLockGuard<T> { | ||||
|         // Get or create a tag lock entry | ||||
|         let mutex = { | ||||
|             let mut inner = self.inner.lock(); | ||||
|  | ||||
|             // See if this tag is in the table | ||||
|             // and if not, add a new mutex for this tag | ||||
|             let entry = inner | ||||
|                 .table | ||||
|                 .entry(tag.clone()) | ||||
|                 .or_insert_with(|| AsyncTagLockTableEntry { | ||||
|                     mutex: Arc::new(AsyncMutex::new(())), | ||||
|                     waiters: 0, | ||||
|                 }); | ||||
|  | ||||
|             // Increment the number of waiters | ||||
|             entry.waiters += 1; | ||||
|  | ||||
|             // Return the mutex associated with the tag | ||||
|             entry.mutex.clone() | ||||
|  | ||||
|             // Drop the table guard | ||||
|         }; | ||||
|  | ||||
|         // Lock the tag lock | ||||
|         let guard; | ||||
|         cfg_if! { | ||||
|             if #[cfg(feature="rt-tokio")] { | ||||
|                 // tokio version | ||||
|                 guard = mutex.lock_owned().await; | ||||
|             } else { | ||||
|                 // async-std and wasm async-lock version | ||||
|                 guard = mutex.lock_arc().await; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Return the locked guard | ||||
|         AsyncTagLockGuard::new(self.clone(), tag, guard) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										108
									
								
								veilid-tools/src/bump_port.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								veilid-tools/src/bump_port.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| use super::*; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|  | ||||
|     } else { | ||||
|         use std::net::{TcpListener, UdpSocket}; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(ThisError, Debug, Clone, PartialEq, Eq)] | ||||
| pub enum BumpPortError { | ||||
|     #[error("Unsupported architecture")] | ||||
|     Unsupported, | ||||
|     #[error("Failure: {0}")] | ||||
|     Failed(String), | ||||
| } | ||||
|  | ||||
| pub enum BumpPortType { | ||||
|     UDP, | ||||
|     TCP, | ||||
| } | ||||
|  | ||||
| pub fn tcp_port_available(addr: &SocketAddr) -> bool { | ||||
|     cfg_if! { | ||||
|         if #[cfg(target_arch = "wasm32")] { | ||||
|             true | ||||
|         } else { | ||||
|             match TcpListener::bind(addr) { | ||||
|                 Ok(_) => true, | ||||
|                 Err(_) => false, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn udp_port_available(addr: &SocketAddr) -> bool { | ||||
|     cfg_if! { | ||||
|         if #[cfg(target_arch = "wasm32")] { | ||||
|             true | ||||
|         } else { | ||||
|             match UdpSocket::bind(addr) { | ||||
|                 Ok(_) => true, | ||||
|                 Err(_) => false, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn bump_port(addr: &mut SocketAddr, bpt: BumpPortType) -> Result<bool, BumpPortError> { | ||||
|     cfg_if! { | ||||
|         if #[cfg(target_arch = "wasm32")] { | ||||
|             Err(BumpPortError::Unsupported) | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             let mut bumped = false; | ||||
|             let mut port = addr.port(); | ||||
|             let mut addr_bump = addr.clone(); | ||||
|             loop { | ||||
|  | ||||
|                 if match bpt { | ||||
|                     BumpPortType::TCP => tcp_port_available(&addr_bump), | ||||
|                     BumpPortType::UDP => udp_port_available(&addr_bump), | ||||
|                 } { | ||||
|                     *addr = addr_bump; | ||||
|                     return Ok(bumped); | ||||
|                 } | ||||
|                 if port == u16::MAX { | ||||
|                     break; | ||||
|                 } | ||||
|                 port += 1; | ||||
|                 addr_bump.set_port(port); | ||||
|                 bumped = true; | ||||
|             } | ||||
|  | ||||
|             Err(BumpPortError::Failure("no ports remaining".to_owned())) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn bump_port_string(addr: &mut String, bpt: BumpPortType) -> Result<bool, BumpPortError> { | ||||
|     cfg_if! { | ||||
|         if #[cfg(target_arch = "wasm32")] { | ||||
|             return Err(BumpPortError::Unsupported); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             let savec: Vec<SocketAddr> = addr | ||||
|                 .to_socket_addrs() | ||||
|                 .map_err(|x| BumpPortError::Failure(format!("failed to resolve socket address: {}", x)))? | ||||
|                 .collect(); | ||||
|  | ||||
|             if savec.len() == 0 { | ||||
|                 return Err(BumpPortError::Failure("No socket addresses resolved".to_owned())); | ||||
|             } | ||||
|             let mut sa = savec.first().unwrap().clone(); | ||||
|  | ||||
|             if !bump_port(&mut sa, bpt)? { | ||||
|                 return Ok(false); | ||||
|             } | ||||
|  | ||||
|             *addr = sa.to_string(); | ||||
|  | ||||
|             Ok(true) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										112
									
								
								veilid-tools/src/clone_stream.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								veilid-tools/src/clone_stream.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::pin::Pin; | ||||
| use core::task::{Context, Poll}; | ||||
| use futures_util::AsyncRead as Read; | ||||
| use futures_util::AsyncWrite as Write; | ||||
| use futures_util::Sink; | ||||
| use futures_util::Stream; | ||||
| use std::io; | ||||
|  | ||||
| pub struct CloneStream<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     inner: Arc<Mutex<T>>, | ||||
| } | ||||
|  | ||||
| impl<T> Clone for CloneStream<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             inner: self.inner.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> CloneStream<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     pub fn new(t: T) -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(t)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Read for CloneStream<T> | ||||
| where | ||||
|     T: Read + Unpin, | ||||
| { | ||||
|     fn poll_read( | ||||
|         self: Pin<&mut Self>, | ||||
|         cx: &mut Context<'_>, | ||||
|         buf: &mut [u8], | ||||
|     ) -> Poll<io::Result<usize>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_read(cx, buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Write for CloneStream<T> | ||||
| where | ||||
|     T: Write + Unpin, | ||||
| { | ||||
|     fn poll_write( | ||||
|         self: Pin<&mut Self>, | ||||
|         cx: &mut Context<'_>, | ||||
|         buf: &[u8], | ||||
|     ) -> Poll<io::Result<usize>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_write(cx, buf) | ||||
|     } | ||||
|  | ||||
|     fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_flush(cx) | ||||
|     } | ||||
|  | ||||
|     fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_close(cx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Stream for CloneStream<T> | ||||
| where | ||||
|     T: Stream + Unpin, | ||||
| { | ||||
|     type Item = T::Item; | ||||
|  | ||||
|     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_next(cx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, Item> Sink<Item> for CloneStream<T> | ||||
| where | ||||
|     T: Sink<Item> + Unpin, | ||||
| { | ||||
|     type Error = T::Error; | ||||
|  | ||||
|     fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_ready(cx) | ||||
|     } | ||||
|     fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).start_send(item) | ||||
|     } | ||||
|     fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_flush(cx) | ||||
|     } | ||||
|     fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         Pin::new(&mut *inner).poll_close(cx) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										219
									
								
								veilid-tools/src/eventual.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								veilid-tools/src/eventual.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| use super::*; | ||||
|  | ||||
| use eventual_base::*; | ||||
|  | ||||
| pub struct Eventual { | ||||
|     inner: Arc<Mutex<EventualBaseInner<()>>>, | ||||
| } | ||||
|  | ||||
| impl core::fmt::Debug for Eventual { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         f.debug_struct("Eventual").finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Clone for Eventual { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             inner: self.inner.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl EventualBase for Eventual { | ||||
|     type ResolvedType = (); | ||||
|     fn base_inner(&self) -> MutexGuard<EventualBaseInner<Self::ResolvedType>> { | ||||
|         self.inner.lock() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for Eventual { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Eventual { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(EventualBaseInner::new())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn instance_clone<T>(&self, value: T) -> EventualFutureClone<T> | ||||
|     where | ||||
|         T: Clone + Unpin, | ||||
|     { | ||||
|         EventualFutureClone { | ||||
|             id: None, | ||||
|             value, | ||||
|             eventual: self.clone(), | ||||
|         } | ||||
|     } | ||||
|     pub fn instance_none<T>(&self) -> EventualFutureNone<T> | ||||
|     where | ||||
|         T: Unpin, | ||||
|     { | ||||
|         EventualFutureNone { | ||||
|             id: None, | ||||
|             eventual: self.clone(), | ||||
|             _marker: core::marker::PhantomData {}, | ||||
|         } | ||||
|     } | ||||
|     pub fn instance_empty(&self) -> EventualFutureEmpty { | ||||
|         EventualFutureEmpty { | ||||
|             id: None, | ||||
|             eventual: self.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn resolve(&self) -> EventualResolvedFuture<Self> { | ||||
|         self.resolve_to_value(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /////// | ||||
|  | ||||
| pub struct EventualFutureClone<T> | ||||
| where | ||||
|     T: Clone + Unpin, | ||||
| { | ||||
|     id: Option<usize>, | ||||
|     value: T, | ||||
|     eventual: Eventual, | ||||
| } | ||||
|  | ||||
| impl<T> Future for EventualFutureClone<T> | ||||
| where | ||||
|     T: Clone + Unpin, | ||||
| { | ||||
|     type Output = T; | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|         let out = { | ||||
|             let mut inner = this.eventual.base_inner(); | ||||
|             inner.instance_poll(&mut this.id, cx) | ||||
|         }; | ||||
|         match out { | ||||
|             None => task::Poll::<Self::Output>::Pending, | ||||
|             Some(wakers) => { | ||||
|                 // Wake all EventualResolvedFutures | ||||
|                 for w in wakers { | ||||
|                     w.wake(); | ||||
|                 } | ||||
|                 task::Poll::<Self::Output>::Ready(this.value.clone()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Drop for EventualFutureClone<T> | ||||
| where | ||||
|     T: Clone + Unpin, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(id) = self.id.take() { | ||||
|             let wakers = { | ||||
|                 let mut inner = self.eventual.base_inner(); | ||||
|                 inner.remove_waker(id) | ||||
|             }; | ||||
|             for w in wakers { | ||||
|                 w.wake(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /////// | ||||
|  | ||||
| pub struct EventualFutureNone<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     id: Option<usize>, | ||||
|     eventual: Eventual, | ||||
|     _marker: core::marker::PhantomData<T>, | ||||
| } | ||||
|  | ||||
| impl<T> Future for EventualFutureNone<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     type Output = Option<T>; | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|         let out = { | ||||
|             let mut inner = this.eventual.base_inner(); | ||||
|             inner.instance_poll(&mut this.id, cx) | ||||
|         }; | ||||
|         match out { | ||||
|             None => task::Poll::<Self::Output>::Pending, | ||||
|             Some(wakers) => { | ||||
|                 // Wake all EventualResolvedFutures | ||||
|                 for w in wakers { | ||||
|                     w.wake(); | ||||
|                 } | ||||
|                 task::Poll::<Self::Output>::Ready(None) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Drop for EventualFutureNone<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(id) = self.id.take() { | ||||
|             let wakers = { | ||||
|                 let mut inner = self.eventual.base_inner(); | ||||
|                 inner.remove_waker(id) | ||||
|             }; | ||||
|             for w in wakers { | ||||
|                 w.wake(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /////// | ||||
|  | ||||
| pub struct EventualFutureEmpty { | ||||
|     id: Option<usize>, | ||||
|     eventual: Eventual, | ||||
| } | ||||
|  | ||||
| impl Future for EventualFutureEmpty { | ||||
|     type Output = (); | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|         let out = { | ||||
|             let mut inner = this.eventual.base_inner(); | ||||
|             inner.instance_poll(&mut this.id, cx) | ||||
|         }; | ||||
|         match out { | ||||
|             None => task::Poll::<Self::Output>::Pending, | ||||
|             Some(wakers) => { | ||||
|                 // Wake all EventualResolvedFutures | ||||
|                 for w in wakers { | ||||
|                     w.wake(); | ||||
|                 } | ||||
|                 task::Poll::<Self::Output>::Ready(()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for EventualFutureEmpty { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(id) = self.id.take() { | ||||
|             let wakers = { | ||||
|                 let mut inner = self.eventual.base_inner(); | ||||
|                 inner.remove_waker(id) | ||||
|             }; | ||||
|             for w in wakers { | ||||
|                 w.wake(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										217
									
								
								veilid-tools/src/eventual_base.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								veilid-tools/src/eventual_base.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| use super::*; | ||||
|  | ||||
| #[derive(ThisError, Debug, Clone, PartialEq, Eq)] | ||||
| pub enum EventualError { | ||||
|     #[error("Try failed: {0}")] | ||||
|     TryFailed(String), | ||||
| } | ||||
|  | ||||
| pub struct EventualBaseInner<T> { | ||||
|     resolved: Option<T>, | ||||
|     wakers: BTreeMap<usize, task::Waker>, | ||||
|     resolved_wakers: BTreeMap<usize, task::Waker>, | ||||
|     freelist: Vec<usize>, | ||||
|     resolved_freelist: Vec<usize>, | ||||
| } | ||||
|  | ||||
| impl<T> EventualBaseInner<T> { | ||||
|     pub(super) fn new() -> Self { | ||||
|         EventualBaseInner { | ||||
|             resolved: None, | ||||
|             wakers: BTreeMap::new(), | ||||
|             resolved_wakers: BTreeMap::new(), | ||||
|             freelist: Vec::new(), | ||||
|             resolved_freelist: Vec::new(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(super) fn insert_waker(&mut self, waker: task::Waker) -> usize { | ||||
|         let id = match self.freelist.pop() { | ||||
|             Some(id) => id, | ||||
|             None => self.wakers.len(), | ||||
|         }; | ||||
|         self.wakers.insert(id, waker); | ||||
|         id | ||||
|     } | ||||
|  | ||||
|     #[must_use] | ||||
|     pub(super) fn remove_waker(&mut self, id: usize) -> Vec<task::Waker> { | ||||
|         self.freelist.push(id); | ||||
|         self.wakers.remove(&id); | ||||
|         // See if we should complete the EventualResolvedFutures | ||||
|         let mut resolved_waker_list = Vec::new(); | ||||
|         if self.wakers.is_empty() && self.resolved.is_some() { | ||||
|             for w in &self.resolved_wakers { | ||||
|                 resolved_waker_list.push(w.1.clone()); | ||||
|             } | ||||
|         } | ||||
|         resolved_waker_list | ||||
|     } | ||||
|  | ||||
|     pub(super) fn insert_resolved_waker(&mut self, waker: task::Waker) -> usize { | ||||
|         let id = match self.resolved_freelist.pop() { | ||||
|             Some(id) => id, | ||||
|             None => self.resolved_wakers.len(), | ||||
|         }; | ||||
|         self.resolved_wakers.insert(id, waker); | ||||
|         id | ||||
|     } | ||||
|  | ||||
|     pub(super) fn remove_resolved_waker(&mut self, id: usize) { | ||||
|         self.resolved_freelist.push(id); | ||||
|         self.resolved_wakers.remove(&id); | ||||
|     } | ||||
|  | ||||
|     #[must_use] | ||||
|     pub(super) fn resolve_and_get_wakers(&mut self, value: T) -> Option<Vec<task::Waker>> { | ||||
|         if self.resolved.is_some() { | ||||
|             // Already resolved | ||||
|             return None; | ||||
|         } | ||||
|  | ||||
|         // Store resolved value | ||||
|         self.resolved = Some(value); | ||||
|  | ||||
|         // Return a copy of the waker list so the caller can wake all the EventualFutures | ||||
|         let mut waker_list = Vec::new(); | ||||
|         for w in &self.wakers { | ||||
|             waker_list.push(w.1.clone()); | ||||
|         } | ||||
|         Some(waker_list) | ||||
|     } | ||||
|  | ||||
|     pub(super) fn is_resolved(&self) -> bool { | ||||
|         self.resolved.is_some() | ||||
|     } | ||||
|     pub(super) fn resolved_value_ref(&self) -> &Option<T> { | ||||
|         &self.resolved | ||||
|     } | ||||
|     pub(super) fn resolved_value_mut(&mut self) -> &mut Option<T> { | ||||
|         &mut self.resolved | ||||
|     } | ||||
|  | ||||
|     pub(super) fn reset(&mut self) { | ||||
|         assert_eq!(self.wakers.len(), 0); | ||||
|         assert_eq!(self.resolved_wakers.len(), 0); | ||||
|         self.resolved = None; | ||||
|         self.freelist.clear(); | ||||
|         self.resolved_freelist.clear(); | ||||
|     } | ||||
|  | ||||
|     pub(super) fn try_reset(&mut self) -> Result<(), EventualError> { | ||||
|         if !self.wakers.is_empty() { | ||||
|             return Err(EventualError::TryFailed( | ||||
|                 "wakers not empty during reset".to_owned(), | ||||
|             )); | ||||
|         } | ||||
|         if !self.resolved_wakers.is_empty() { | ||||
|             return Err(EventualError::TryFailed( | ||||
|                 "Resolved wakers not empty during reset".to_owned(), | ||||
|             )); | ||||
|         } | ||||
|         self.reset(); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Resolved future helpers | ||||
|     pub(super) fn resolved_poll( | ||||
|         &mut self, | ||||
|         id: &mut Option<usize>, | ||||
|         cx: &mut task::Context<'_>, | ||||
|     ) -> task::Poll<()> { | ||||
|         // If there are any instance futures still waiting, we resolution isn't finished | ||||
|         if !self.wakers.is_empty() { | ||||
|             if id.is_none() { | ||||
|                 *id = Some(self.insert_resolved_waker(cx.waker().clone())); | ||||
|             } | ||||
|             task::Poll::<()>::Pending | ||||
|         } else { | ||||
|             if let Some(id) = id.take() { | ||||
|                 self.remove_resolved_waker(id); | ||||
|             } | ||||
|             task::Poll::<()>::Ready(()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Instance future helpers | ||||
|     #[must_use] | ||||
|     pub(super) fn instance_poll( | ||||
|         &mut self, | ||||
|         id: &mut Option<usize>, | ||||
|         cx: &mut task::Context<'_>, | ||||
|     ) -> Option<Vec<task::Waker>> { | ||||
|         // If the resolved value hasn't showed up then we can't wake the instance futures | ||||
|         if self.resolved.is_none() { | ||||
|             if id.is_none() { | ||||
|                 *id = Some(self.insert_waker(cx.waker().clone())); | ||||
|             } | ||||
|             None | ||||
|         } else if let Some(id) = id.take() { | ||||
|             Some(self.remove_waker(id)) | ||||
|         } else { | ||||
|             Some(Vec::new()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // xxx: this would love to be 'pub(super)' instead of pub, to ensure nobody else touches resolve_to_value directly | ||||
| pub trait EventualBase: Clone + Unpin { | ||||
|     type ResolvedType; | ||||
|  | ||||
|     fn base_inner(&self) -> MutexGuard<EventualBaseInner<Self::ResolvedType>>; | ||||
|  | ||||
|     fn resolve_to_value(&self, value: Self::ResolvedType) -> EventualResolvedFuture<Self> { | ||||
|         let wakers = { | ||||
|             let mut inner = self.base_inner(); | ||||
|             inner.resolve_and_get_wakers(value) | ||||
|         }; | ||||
|         if let Some(wakers) = wakers { | ||||
|             for w in wakers { | ||||
|                 w.wake(); | ||||
|             } | ||||
|         } | ||||
|         EventualResolvedFuture { | ||||
|             id: None, | ||||
|             eventual: self.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct EventualResolvedFuture<B: EventualBase> { | ||||
|     id: Option<usize>, | ||||
|     eventual: B, | ||||
| } | ||||
|  | ||||
| impl<B: EventualBase> Future for EventualResolvedFuture<B> { | ||||
|     type Output = (); | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|         let mut inner = this.eventual.base_inner(); | ||||
|         inner.resolved_poll(&mut this.id, cx) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B: EventualBase> Drop for EventualResolvedFuture<B> { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(id) = self.id.take() { | ||||
|             let mut inner = self.eventual.base_inner(); | ||||
|             inner.remove_resolved_waker(id); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait EventualCommon: EventualBase { | ||||
|     fn is_resolved(&self) -> bool { | ||||
|         self.base_inner().is_resolved() | ||||
|     } | ||||
|  | ||||
|     fn reset(&self) { | ||||
|         self.base_inner().reset() | ||||
|     } | ||||
|  | ||||
|     fn try_reset(&self) -> Result<(), EventualError> { | ||||
|         self.base_inner().try_reset() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> EventualCommon for T where T: EventualBase {} | ||||
							
								
								
									
										109
									
								
								veilid-tools/src/eventual_value.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								veilid-tools/src/eventual_value.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| use super::*; | ||||
|  | ||||
| use eventual_base::*; | ||||
|  | ||||
| pub struct EventualValue<T: Unpin> { | ||||
|     inner: Arc<Mutex<EventualBaseInner<T>>>, | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> core::fmt::Debug for EventualValue<T> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         f.debug_struct("EventualValue").finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> Clone for EventualValue<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             inner: self.inner.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> EventualBase for EventualValue<T> { | ||||
|     type ResolvedType = T; | ||||
|     fn base_inner(&self) -> MutexGuard<EventualBaseInner<Self::ResolvedType>> { | ||||
|         self.inner.lock() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> Default for EventualValue<T> { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> EventualValue<T> { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(EventualBaseInner::new())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn instance(&self) -> EventualValueFuture<T> { | ||||
|         EventualValueFuture { | ||||
|             id: None, | ||||
|             eventual: self.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn resolve(&self, value: T) -> EventualResolvedFuture<Self> { | ||||
|         self.resolve_to_value(value) | ||||
|     } | ||||
|  | ||||
|     pub fn take_value(&self) -> Option<T> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         inner.resolved_value_mut().take() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct EventualValueFuture<T: Unpin> { | ||||
|     id: Option<usize>, | ||||
|     eventual: EventualValue<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> core::fmt::Debug for EventualValueFuture<T> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         f.debug_struct("EventualValueFuture") | ||||
|             .field("id", &self.id) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin> Future for EventualValueFuture<T> { | ||||
|     type Output = EventualValue<T>; | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|         let out = { | ||||
|             let mut inner = this.eventual.base_inner(); | ||||
|             inner.instance_poll(&mut this.id, cx) | ||||
|         }; | ||||
|         match out { | ||||
|             None => task::Poll::<Self::Output>::Pending, | ||||
|             Some(wakers) => { | ||||
|                 // Wake all EventualResolvedFutures | ||||
|                 for w in wakers { | ||||
|                     w.wake(); | ||||
|                 } | ||||
|                 task::Poll::<Self::Output>::Ready(this.eventual.clone()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Drop for EventualValueFuture<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(id) = self.id.take() { | ||||
|             let wakers = { | ||||
|                 let mut inner = self.eventual.base_inner(); | ||||
|                 inner.remove_waker(id) | ||||
|             }; | ||||
|             for w in wakers { | ||||
|                 w.wake(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										105
									
								
								veilid-tools/src/eventual_value_clone.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								veilid-tools/src/eventual_value_clone.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| use super::*; | ||||
|  | ||||
| use eventual_base::*; | ||||
|  | ||||
| pub struct EventualValueClone<T: Unpin + Clone> { | ||||
|     inner: Arc<Mutex<EventualBaseInner<T>>>, | ||||
| } | ||||
|  | ||||
| impl<T: Unpin + Clone> core::fmt::Debug for EventualValueClone<T> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         f.debug_struct("EventualValueClone").finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin + Clone> Clone for EventualValueClone<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             inner: self.inner.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin + Clone> EventualBase for EventualValueClone<T> { | ||||
|     type ResolvedType = T; | ||||
|     fn base_inner(&self) -> MutexGuard<EventualBaseInner<Self::ResolvedType>> { | ||||
|         self.inner.lock() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin + Clone> Default for EventualValueClone<T> { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Unpin + Clone> EventualValueClone<T> { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(EventualBaseInner::new())), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn instance(&self) -> EventualValueCloneFuture<T> | ||||
|     where | ||||
|         T: Clone + Unpin, | ||||
|     { | ||||
|         EventualValueCloneFuture { | ||||
|             id: None, | ||||
|             eventual: self.clone(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn resolve(&self, value: T) -> EventualResolvedFuture<Self> { | ||||
|         self.resolve_to_value(value) | ||||
|     } | ||||
|  | ||||
|     pub fn value(&self) -> Option<T> { | ||||
|         let inner = self.inner.lock(); | ||||
|         inner.resolved_value_ref().clone() | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct EventualValueCloneFuture<T: Unpin + Clone> { | ||||
|     id: Option<usize>, | ||||
|     eventual: EventualValueClone<T>, | ||||
| } | ||||
|  | ||||
| impl<T: Unpin + Clone> Future for EventualValueCloneFuture<T> { | ||||
|     type Output = T; | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let this = &mut *self; | ||||
|         let (out, some_value) = { | ||||
|             let mut inner = this.eventual.base_inner(); | ||||
|             let out = inner.instance_poll(&mut this.id, cx); | ||||
|             (out, inner.resolved_value_ref().clone()) | ||||
|         }; | ||||
|         match out { | ||||
|             None => task::Poll::<Self::Output>::Pending, | ||||
|             Some(wakers) => { | ||||
|                 // Wake all EventualResolvedFutures | ||||
|                 for w in wakers { | ||||
|                     w.wake(); | ||||
|                 } | ||||
|                 task::Poll::<Self::Output>::Ready(some_value.unwrap()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Drop for EventualValueCloneFuture<T> | ||||
| where | ||||
|     T: Clone + Unpin, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(id) = self.id.take() { | ||||
|             let wakers = { | ||||
|                 let mut inner = self.eventual.base_inner(); | ||||
|                 inner.remove_waker(id) | ||||
|             }; | ||||
|             for w in wakers { | ||||
|                 w.wake(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										49
									
								
								veilid-tools/src/interval.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								veilid-tools/src/interval.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| use super::*; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|  | ||||
|         pub fn interval<F, FUT>(freq_ms: u32, callback: F) -> SendPinBoxFuture<()> | ||||
|         where | ||||
|             F: Fn() -> FUT + Send + Sync + 'static, | ||||
|             FUT: Future<Output = ()> + Send, | ||||
|         { | ||||
|             let e = Eventual::new(); | ||||
|  | ||||
|             let ie = e.clone(); | ||||
|             let jh = spawn(Box::pin(async move { | ||||
|                 while timeout(freq_ms, ie.instance_clone(())).await.is_err() { | ||||
|                     callback().await; | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|             Box::pin(async move { | ||||
|                 e.resolve().await; | ||||
|                 jh.await; | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub fn interval<F, FUT>(freq_ms: u32, callback: F) -> SendPinBoxFuture<()> | ||||
|         where | ||||
|             F: Fn() -> FUT + Send + Sync + 'static, | ||||
|             FUT: Future<Output = ()> + Send, | ||||
|         { | ||||
|             let e = Eventual::new(); | ||||
|  | ||||
|             let ie = e.clone(); | ||||
|             let jh = spawn(async move { | ||||
|                 while timeout(freq_ms, ie.instance_clone(())).await.is_err() { | ||||
|                     callback().await; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             Box::pin(async move { | ||||
|                 e.resolve().await; | ||||
|                 jh.await; | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										66
									
								
								veilid-tools/src/ip_addr_port.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								veilid-tools/src/ip_addr_port.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::fmt; | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] | ||||
| pub struct IpAddrPort { | ||||
|     addr: IpAddr, | ||||
|     port: u16, | ||||
| } | ||||
|  | ||||
| impl IpAddrPort { | ||||
|     pub fn new(addr: IpAddr, port: u16) -> Self { | ||||
|         Self { addr, port } | ||||
|     } | ||||
|     pub fn addr(&self) -> &IpAddr { | ||||
|         &self.addr | ||||
|     } | ||||
|     pub fn port(&self) -> u16 { | ||||
|         self.port | ||||
|     } | ||||
|     pub fn set_addr(&mut self, new_addr: IpAddr) { | ||||
|         self.addr = new_addr; | ||||
|     } | ||||
|     pub fn set_port(&mut self, new_port: u16) { | ||||
|         self.port = new_port; | ||||
|     } | ||||
|  | ||||
|     pub fn from_socket_addr(sa: &SocketAddr) -> Self { | ||||
|         match sa { | ||||
|             SocketAddr::V4(v) => Self { | ||||
|                 addr: IpAddr::V4(*v.ip()), | ||||
|                 port: v.port(), | ||||
|             }, | ||||
|             SocketAddr::V6(v) => Self { | ||||
|                 addr: IpAddr::V6(*v.ip()), | ||||
|                 port: v.port(), | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|     pub fn to_socket_addr(&self) -> SocketAddr { | ||||
|         match self.addr { | ||||
|             IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, self.port)), | ||||
|             IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, self.port, 0, 0)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for IpAddrPort { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self.addr { | ||||
|             IpAddr::V4(a) => write!(f, "{}:{}", a, self.port()), | ||||
|             IpAddr::V6(a) => write!(f, "[{}]:{}", a, self.port()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<SocketAddrV4> for IpAddrPort { | ||||
|     fn from(sock4: SocketAddrV4) -> IpAddrPort { | ||||
|         Self::from_socket_addr(&SocketAddr::V4(sock4)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<SocketAddrV6> for IpAddrPort { | ||||
|     fn from(sock6: SocketAddrV6) -> IpAddrPort { | ||||
|         Self::from_socket_addr(&SocketAddr::V6(sock6)) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										268
									
								
								veilid-tools/src/ip_extra.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								veilid-tools/src/ip_extra.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,268 @@ | ||||
| // | ||||
| // This file really shouldn't be necessary, but 'ip' isn't a stable feature | ||||
| // | ||||
|  | ||||
| use super::*; | ||||
|  | ||||
| use core::hash::*; | ||||
|  | ||||
| #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] | ||||
| pub enum Ipv6MulticastScope { | ||||
|     InterfaceLocal, | ||||
|     LinkLocal, | ||||
|     RealmLocal, | ||||
|     AdminLocal, | ||||
|     SiteLocal, | ||||
|     OrganizationLocal, | ||||
|     Global, | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_is_unspecified(addr: &IpAddr) -> bool { | ||||
|     match addr { | ||||
|         IpAddr::V4(ip) => ipv4addr_is_unspecified(ip), | ||||
|         IpAddr::V6(ip) => ipv6addr_is_unspecified(ip), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_is_loopback(addr: &IpAddr) -> bool { | ||||
|     match addr { | ||||
|         IpAddr::V4(ip) => ipv4addr_is_loopback(ip), | ||||
|         IpAddr::V6(ip) => ipv6addr_is_loopback(ip), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_is_global(addr: &IpAddr) -> bool { | ||||
|     match addr { | ||||
|         IpAddr::V4(ip) => ipv4addr_is_global(ip), | ||||
|         IpAddr::V6(ip) => ipv6addr_is_global(ip), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_is_multicast(addr: &IpAddr) -> bool { | ||||
|     match addr { | ||||
|         IpAddr::V4(ip) => ipv4addr_is_multicast(ip), | ||||
|         IpAddr::V6(ip) => ipv6addr_is_multicast(ip), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_is_documentation(addr: &IpAddr) -> bool { | ||||
|     match addr { | ||||
|         IpAddr::V4(ip) => ipv4addr_is_documentation(ip), | ||||
|         IpAddr::V6(ip) => ipv6addr_is_documentation(ip), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_unspecified(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets() == [0u8, 0u8, 0u8, 0u8] | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_loopback(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets()[0] == 127 | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_private(addr: &Ipv4Addr) -> bool { | ||||
|     match addr.octets() { | ||||
|         [10, ..] => true, | ||||
|         [172, b, ..] if (16..=31).contains(&b) => true, | ||||
|         [192, 168, ..] => true, | ||||
|         _ => false, | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_link_local(addr: &Ipv4Addr) -> bool { | ||||
|     matches!(addr.octets(), [169, 254, ..]) | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_global(addr: &Ipv4Addr) -> bool { | ||||
|     // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two | ||||
|     // globally routable addresses in the 192.0.0.0/24 range. | ||||
|     if u32::from(*addr) == 0xc0000009 || u32::from(*addr) == 0xc000000a { | ||||
|         return true; | ||||
|     } | ||||
|     !ipv4addr_is_private(addr) | ||||
|         && !ipv4addr_is_loopback(addr) | ||||
|         && !ipv4addr_is_link_local(addr) | ||||
|         && !ipv4addr_is_broadcast(addr) | ||||
|         && !ipv4addr_is_documentation(addr) | ||||
|         && !ipv4addr_is_shared(addr) | ||||
|         && !ipv4addr_is_ietf_protocol_assignment(addr) | ||||
|         && !ipv4addr_is_reserved(addr) | ||||
|         && !ipv4addr_is_benchmarking(addr) | ||||
|         // Make sure the address is not in 0.0.0.0/8 | ||||
|         && addr.octets()[0] != 0 | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_shared(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets()[0] == 100 && (addr.octets()[1] & 0b1100_0000 == 0b0100_0000) | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_ietf_protocol_assignment(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 0 | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_benchmarking(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets()[0] == 198 && (addr.octets()[1] & 0xfe) == 18 | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_reserved(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets()[0] & 240 == 240 && !addr.is_broadcast() | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_multicast(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets()[0] >= 224 && addr.octets()[0] <= 239 | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_broadcast(addr: &Ipv4Addr) -> bool { | ||||
|     addr.octets() == [255u8, 255u8, 255u8, 255u8] | ||||
| } | ||||
|  | ||||
| pub fn ipv4addr_is_documentation(addr: &Ipv4Addr) -> bool { | ||||
|     matches!( | ||||
|         addr.octets(), | ||||
|         [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _] | ||||
|     ) | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_unspecified(addr: &Ipv6Addr) -> bool { | ||||
|     addr.segments() == [0, 0, 0, 0, 0, 0, 0, 0] | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_loopback(addr: &Ipv6Addr) -> bool { | ||||
|     addr.segments() == [0, 0, 0, 0, 0, 0, 0, 1] | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_global(addr: &Ipv6Addr) -> bool { | ||||
|     match ipv6addr_multicast_scope(addr) { | ||||
|         Some(Ipv6MulticastScope::Global) => true, | ||||
|         None => ipv6addr_is_unicast_global(addr), | ||||
|         _ => false, | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_unique_local(addr: &Ipv6Addr) -> bool { | ||||
|     (addr.segments()[0] & 0xfe00) == 0xfc00 | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_unicast_link_local_strict(addr: &Ipv6Addr) -> bool { | ||||
|     addr.segments()[0] == 0xfe80 | ||||
|         && addr.segments()[1] == 0 | ||||
|         && addr.segments()[2] == 0 | ||||
|         && addr.segments()[3] == 0 | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_unicast_link_local(addr: &Ipv6Addr) -> bool { | ||||
|     (addr.segments()[0] & 0xffc0) == 0xfe80 | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_unicast_site_local(addr: &Ipv6Addr) -> bool { | ||||
|     (addr.segments()[0] & 0xffc0) == 0xfec0 | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_documentation(addr: &Ipv6Addr) -> bool { | ||||
|     (addr.segments()[0] == 0x2001) && (addr.segments()[1] == 0xdb8) | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_unicast_global(addr: &Ipv6Addr) -> bool { | ||||
|     !ipv6addr_is_multicast(addr) | ||||
|         && !ipv6addr_is_loopback(addr) | ||||
|         && !ipv6addr_is_unicast_link_local(addr) | ||||
|         && !ipv6addr_is_unique_local(addr) | ||||
|         && !ipv6addr_is_unspecified(addr) | ||||
|         && !ipv6addr_is_documentation(addr) | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_multicast_scope(addr: &Ipv6Addr) -> Option<Ipv6MulticastScope> { | ||||
|     if ipv6addr_is_multicast(addr) { | ||||
|         match addr.segments()[0] & 0x000f { | ||||
|             1 => Some(Ipv6MulticastScope::InterfaceLocal), | ||||
|             2 => Some(Ipv6MulticastScope::LinkLocal), | ||||
|             3 => Some(Ipv6MulticastScope::RealmLocal), | ||||
|             4 => Some(Ipv6MulticastScope::AdminLocal), | ||||
|             5 => Some(Ipv6MulticastScope::SiteLocal), | ||||
|             8 => Some(Ipv6MulticastScope::OrganizationLocal), | ||||
|             14 => Some(Ipv6MulticastScope::Global), | ||||
|             _ => None, | ||||
|         } | ||||
|     } else { | ||||
|         None | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipv6addr_is_multicast(addr: &Ipv6Addr) -> bool { | ||||
|     (addr.segments()[0] & 0xff00) == 0xff00 | ||||
| } | ||||
|  | ||||
| // Converts an ip to a ip block by applying a netmask | ||||
| // to the host part of the ip address | ||||
| // ipv4 addresses are treated as single hosts | ||||
| // ipv6 addresses are treated as prefix allocated blocks | ||||
| pub fn ip_to_ipblock(ip6_prefix_size: usize, addr: IpAddr) -> IpAddr { | ||||
|     match addr { | ||||
|         IpAddr::V4(_) => addr, | ||||
|         IpAddr::V6(v6) => { | ||||
|             let mut hostlen = 128usize.saturating_sub(ip6_prefix_size); | ||||
|             let mut out = v6.octets(); | ||||
|             for i in (0..16).rev() { | ||||
|                 if hostlen >= 8 { | ||||
|                     out[i] = 0xFF; | ||||
|                     hostlen -= 8; | ||||
|                 } else { | ||||
|                     out[i] |= !(0xFFu8 << hostlen); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             IpAddr::V6(Ipv6Addr::from(out)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_apply_netmask(addr: IpAddr, netmask: IpAddr) -> IpAddr { | ||||
|     match addr { | ||||
|         IpAddr::V4(v4) => { | ||||
|             let v4mask = match netmask { | ||||
|                 IpAddr::V4(v4mask) => v4mask, | ||||
|                 IpAddr::V6(_) => { | ||||
|                     panic!("netmask doesn't match ipv4 address"); | ||||
|                 } | ||||
|             }; | ||||
|             let v4 = v4.octets(); | ||||
|             let v4mask = v4mask.octets(); | ||||
|             IpAddr::V4(Ipv4Addr::new( | ||||
|                 v4[0] & v4mask[0], | ||||
|                 v4[1] & v4mask[1], | ||||
|                 v4[2] & v4mask[2], | ||||
|                 v4[3] & v4mask[3], | ||||
|             )) | ||||
|         } | ||||
|         IpAddr::V6(v6) => { | ||||
|             let v6mask = match netmask { | ||||
|                 IpAddr::V4(_) => { | ||||
|                     panic!("netmask doesn't match ipv6 address"); | ||||
|                 } | ||||
|                 IpAddr::V6(v6mask) => v6mask, | ||||
|             }; | ||||
|             let v6 = v6.segments(); | ||||
|             let v6mask = v6mask.segments(); | ||||
|             IpAddr::V6(Ipv6Addr::new( | ||||
|                 v6[0] & v6mask[0], | ||||
|                 v6[1] & v6mask[1], | ||||
|                 v6[2] & v6mask[2], | ||||
|                 v6[3] & v6mask[3], | ||||
|                 v6[4] & v6mask[4], | ||||
|                 v6[5] & v6mask[5], | ||||
|                 v6[6] & v6mask[6], | ||||
|                 v6[7] & v6mask[7], | ||||
|             )) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn ipaddr_in_network(addr: IpAddr, netaddr: IpAddr, netmask: IpAddr) -> bool { | ||||
|     if addr.is_ipv4() && !netaddr.is_ipv4() { | ||||
|         return false; | ||||
|     } | ||||
|     if addr.is_ipv6() && !netaddr.is_ipv6() { | ||||
|         return false; | ||||
|     } | ||||
|     ipaddr_apply_netmask(netaddr, netmask) == ipaddr_apply_netmask(addr, netmask) | ||||
| } | ||||
							
								
								
									
										353
									
								
								veilid-tools/src/log_thru.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								veilid-tools/src/log_thru.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,353 @@ | ||||
| // LogThru | ||||
| // Pass errors through and log them simultaneously via map_err() | ||||
| // Also contains common log facilities (net, rpc, rtab, pstore, crypto, etc ) | ||||
|  | ||||
| use alloc::string::{String, ToString}; | ||||
|  | ||||
| pub fn map_to_string<X: ToString>(arg: X) -> String { | ||||
|     arg.to_string() | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! fn_string { | ||||
|     ($text:expr) => { | ||||
|         || $text.to_string() | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! log_net { | ||||
|     (error $text:expr) => {error!( | ||||
|         target: "net", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (error $fmt:literal, $($arg:expr),+) => { | ||||
|         error!(target:"net", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (warn $text:expr) => {warn!( | ||||
|         target: "net", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (warn $fmt:literal, $($arg:expr),+) => { | ||||
|         warn!(target:"net", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (debug $text:expr) => {debug!( | ||||
|         target: "net", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (debug $fmt:literal, $($arg:expr),+) => { | ||||
|         debug!(target:"net", $fmt, $($arg),+); | ||||
|     }; | ||||
|     ($text:expr) => {trace!( | ||||
|         target: "net", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     ($fmt:literal, $($arg:expr),+) => { | ||||
|         trace!(target:"net", $fmt, $($arg),+); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! log_rpc { | ||||
|     (error $text:expr) => { error!( | ||||
|         target: "rpc", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (error $fmt:literal, $($arg:expr),+) => { | ||||
|         error!(target:"rpc", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (warn $text:expr) => { warn!( | ||||
|         target: "rpc", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (warn $fmt:literal, $($arg:expr),+) => { | ||||
|         warn!(target:"rpc", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (debug $text:expr) => { error!( | ||||
|         target: "rpc", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (debug $fmt:literal, $($arg:expr),+) => { | ||||
|         debug!(target:"rpc", $fmt, $($arg),+); | ||||
|     }; | ||||
|     ($text:expr) => {trace!( | ||||
|         target: "rpc", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     ($fmt:literal, $($arg:expr),+) => { | ||||
|         trace!(target:"rpc", $fmt, $($arg),+); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! log_rtab { | ||||
|     (error $text:expr) => { error!( | ||||
|         target: "rtab", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (error $fmt:literal, $($arg:expr),+) => { | ||||
|         error!(target:"rtab", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (warn $text:expr) => { warn!( | ||||
|         target: "rtab", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (warn $fmt:literal, $($arg:expr),+) => { | ||||
|         warn!(target:"rtab", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (debug $text:expr) => { debug!( | ||||
|         target: "rtab", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (debug $fmt:literal, $($arg:expr),+) => { | ||||
|         debug!(target:"rtab", $fmt, $($arg),+); | ||||
|     }; | ||||
|     ($text:expr) => {trace!( | ||||
|         target: "rtab", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     ($fmt:literal, $($arg:expr),+) => { | ||||
|         trace!(target:"rtab", $fmt, $($arg),+); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! log_pstore { | ||||
|     (error $text:expr) => { error!( | ||||
|         target: "pstore", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (error $fmt:literal, $($arg:expr),+) => { | ||||
|         error!(target:"pstore", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (warn $text:expr) => { warn!( | ||||
|         target: "pstore", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (warn $fmt:literal, $($arg:expr),+) => { | ||||
|         warn!(target:"pstore", $fmt, $($arg),+); | ||||
|     }; | ||||
|     ($text:expr) => {trace!( | ||||
|         target: "pstore", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     ($fmt:literal, $($arg:expr),+) => { | ||||
|         trace!(target:"pstore", $fmt, $($arg),+); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! log_crypto { | ||||
|     (error $text:expr) => { error!( | ||||
|         target: "crypto", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (error $fmt:literal, $($arg:expr),+) => { | ||||
|         error!(target:"crypto", $fmt, $($arg),+); | ||||
|     }; | ||||
|     (warn $text:expr) => { warn!( | ||||
|         target: "crypto", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     (warn $fmt:literal, $($arg:expr),+) => { | ||||
|         warn!(target:"crypto", $fmt, $($arg),+); | ||||
|     }; | ||||
|     ($text:expr) => {trace!( | ||||
|         target: "crypto", | ||||
|         "{}", | ||||
|         $text, | ||||
|     )}; | ||||
|     ($fmt:literal, $($arg:expr),+) => { | ||||
|         trace!(target:"crypto", $fmt, $($arg),+); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! logthru_net { | ||||
|     ($($level:ident)?) => { | ||||
|         logthru!($($level)? "net") | ||||
|     }; | ||||
|     ($($level:ident)? $text:literal) => { | ||||
|         logthru!($($level)? "net", $text) | ||||
|     }; | ||||
|     ($($level:ident)? $fmt:literal, $($arg:expr),+) => { | ||||
|         logthru!($($level)? "net", $fmt, $($arg),+) | ||||
|     } | ||||
| } | ||||
| #[macro_export] | ||||
| macro_rules! logthru_rpc { | ||||
|     ($($level:ident)?) => { | ||||
|         logthru!($($level)? "rpc") | ||||
|     }; | ||||
|     ($($level:ident)? $text:literal) => { | ||||
|         logthru!($($level)? "rpc", $text) | ||||
|     }; | ||||
|     ($($level:ident)? $fmt:literal, $($arg:expr),+) => { | ||||
|         logthru!($($level)? "rpc", $fmt, $($arg),+) | ||||
|     } | ||||
| } | ||||
| #[macro_export] | ||||
| macro_rules! logthru_rtab { | ||||
|     ($($level:ident)?) => { | ||||
|         logthru!($($level)? "rtab") | ||||
|     }; | ||||
|     ($($level:ident)? $text:literal) => { | ||||
|         logthru!($($level)? "rtab", $text) | ||||
|     }; | ||||
|     ($($level:ident)? $fmt:literal, $($arg:expr),+) => { | ||||
|         logthru!($($level)? "rtab", $fmt, $($arg),+) | ||||
|     } | ||||
| } | ||||
| #[macro_export] | ||||
| macro_rules! logthru_pstore { | ||||
|     ($($level:ident)?) => { | ||||
|         logthru!($($level)? "pstore") | ||||
|     }; | ||||
|     ($($level:ident)? $text:literal) => { | ||||
|         logthru!($($level)? "pstore", $text) | ||||
|     }; | ||||
|     ($($level:ident)? $fmt:literal, $($arg:expr),+) => { | ||||
|         logthru!($($level)? "pstore", $fmt, $($arg),+) | ||||
|     } | ||||
| } | ||||
| #[macro_export] | ||||
| macro_rules! logthru_crypto { | ||||
|     ($($level:ident)?) => { | ||||
|         logthru!($($level)? "crypto") | ||||
|     }; | ||||
|     ($($level:ident)? $text:literal) => { | ||||
|         logthru!($($level)? "crypto", $text) | ||||
|     }; | ||||
|     ($($level:ident)? $fmt:literal, $($arg:expr),+) => { | ||||
|         logthru!($($level)? "crypto", $fmt, $($arg),+) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! logthru { | ||||
|     // error | ||||
|     (error $target:literal) => (|e__| { | ||||
|         error!( | ||||
|             target: $target, | ||||
|             "[{:?}]", | ||||
|             e__, | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     (error $target:literal, $text:literal) => (|e__| { | ||||
|         error!( | ||||
|             target: $target, | ||||
|             "[{:?}] {}", | ||||
|             e__, | ||||
|             $text | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     (error $target:literal, $fmt:literal, $($arg:expr),+) => (|e__| { | ||||
|         error!( | ||||
|             target: $target, | ||||
|             concat!("[{:?}] ", $fmt), | ||||
|             e__, | ||||
|             $($arg),+ | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     // warn | ||||
|     (warn $target:literal) => (|e__| { | ||||
|         warn!( | ||||
|             target: $target, | ||||
|             "[{:?}]", | ||||
|             e__, | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     (warn $target:literal, $text:literal) => (|e__| { | ||||
|         warn!( | ||||
|             target: $target, | ||||
|             "[{:?}] {}", | ||||
|             e__, | ||||
|             $text | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     (warn $target:literal, $fmt:literal, $($arg:expr),+) => (|e__| { | ||||
|         warn!( | ||||
|             target: $target, | ||||
|             concat!("[{:?}] ", $fmt), | ||||
|             e__, | ||||
|             $($arg),+ | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     // debug | ||||
|     (debug $target:literal) => (|e__| { | ||||
|         debug!( | ||||
|             target: $target, | ||||
|             "[{:?}]", | ||||
|             e__, | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     (debug $target:literal, $text:literal) => (|e__| { | ||||
|         debug!( | ||||
|             target: $target, | ||||
|             "[{:?}] {}", | ||||
|             e__, | ||||
|             $text | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     (debug $target:literal, $fmt:literal, $($arg:expr),+) => (|e__| { | ||||
|         debug!( | ||||
|             target: $target, | ||||
|             concat!("[{:?}] ", $fmt), | ||||
|             e__, | ||||
|             $($arg),+ | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     // trace | ||||
|     ($target:literal) => (|e__| { | ||||
|         trace!( | ||||
|             target: $target, | ||||
|             "[{:?}]", | ||||
|             e__, | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     ($target:literal, $text:literal) => (|e__| { | ||||
|         trace!( | ||||
|             target: $target, | ||||
|             "[{:?}] {}", | ||||
|             e__, | ||||
|             $text | ||||
|         ); | ||||
|         e__ | ||||
|     }); | ||||
|     ($target:literal, $fmt:literal, $($arg:expr),+) => (|e__| { | ||||
|         trace!( | ||||
|             target: $target, | ||||
|             concat!("[{:?}] ", $fmt), | ||||
|             e__, | ||||
|             $($arg),+ | ||||
|         ); | ||||
|         e__ | ||||
|     }) | ||||
| } | ||||
							
								
								
									
										134
									
								
								veilid-tools/src/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								veilid-tools/src/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| // mod bump_port; | ||||
| mod async_peek_stream; | ||||
| mod async_tag_lock; | ||||
| mod clone_stream; | ||||
| mod eventual; | ||||
| mod eventual_base; | ||||
| mod eventual_value; | ||||
| mod eventual_value_clone; | ||||
| mod interval; | ||||
| mod ip_addr_port; | ||||
| mod ip_extra; | ||||
| mod log_thru; | ||||
| mod must_join_handle; | ||||
| mod must_join_single_future; | ||||
| mod mutable_future; | ||||
| mod network_result; | ||||
| mod random; | ||||
| mod single_shot_eventual; | ||||
| mod sleep; | ||||
| mod spawn; | ||||
| mod split_url; | ||||
| mod tick_task; | ||||
| mod timeout; | ||||
| mod timeout_or; | ||||
| mod timestamp; | ||||
| mod tools; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| mod wasm; | ||||
|  | ||||
| pub use cfg_if::*; | ||||
| #[allow(unused_imports)] | ||||
| pub use eyre::{bail, ensure, eyre, Report as EyreReport, Result as EyreResult, WrapErr}; | ||||
| pub use futures_util::future::{select, Either}; | ||||
| pub use futures_util::select; | ||||
| pub use futures_util::stream::FuturesUnordered; | ||||
| pub use futures_util::{AsyncRead, AsyncWrite}; | ||||
| pub use log_thru::*; | ||||
| pub use owo_colors::OwoColorize; | ||||
| pub use parking_lot::*; | ||||
| pub use split_url::*; | ||||
| pub use static_assertions::*; | ||||
| pub use stop_token::*; | ||||
| pub use thiserror::Error as ThisError; | ||||
| cfg_if! { | ||||
|     if #[cfg(feature = "tracing")] { | ||||
|         pub use tracing::*; | ||||
|     } else { | ||||
|         pub use log::*; | ||||
|     } | ||||
| } | ||||
| pub type PinBox<T> = Pin<Box<T>>; | ||||
| pub type PinBoxFuture<T> = PinBox<dyn Future<Output = T> + 'static>; | ||||
| pub type PinBoxFutureLifetime<'a, T> = PinBox<dyn Future<Output = T> + 'a>; | ||||
| pub type SendPinBoxFuture<T> = PinBox<dyn Future<Output = T> + Send + 'static>; | ||||
| pub type SendPinBoxFutureLifetime<'a, T> = PinBox<dyn Future<Output = T> + Send + 'a>; | ||||
|  | ||||
| pub use std::borrow::{Cow, ToOwned}; | ||||
| pub use std::boxed::Box; | ||||
| pub use std::cell::RefCell; | ||||
| pub use std::cmp; | ||||
| pub use std::collections::btree_map::BTreeMap; | ||||
| pub use std::collections::btree_set::BTreeSet; | ||||
| pub use std::collections::hash_map::HashMap; | ||||
| pub use std::collections::hash_set::HashSet; | ||||
| pub use std::collections::LinkedList; | ||||
| pub use std::collections::VecDeque; | ||||
| pub use std::convert::{TryFrom, TryInto}; | ||||
| pub use std::fmt; | ||||
| pub use std::future::Future; | ||||
| pub use std::mem; | ||||
| pub use std::net::{ | ||||
|     IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, | ||||
| }; | ||||
| pub use std::ops::{Fn, FnMut, FnOnce}; | ||||
| pub use std::pin::Pin; | ||||
| pub use std::rc::Rc; | ||||
| pub use std::string::String; | ||||
| pub use std::sync::atomic::{AtomicBool, Ordering}; | ||||
| pub use std::sync::{Arc, Weak}; | ||||
| pub use std::task; | ||||
| pub use std::time::Duration; | ||||
| pub use std::vec::Vec; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|         pub use async_lock::Mutex as AsyncMutex; | ||||
|         pub use async_lock::MutexGuard as AsyncMutexGuard; | ||||
|         pub use async_lock::MutexGuardArc as AsyncMutexGuardArc; | ||||
|         pub use async_executors::JoinHandle as LowLevelJoinHandle; | ||||
|     } else { | ||||
|         cfg_if! { | ||||
|             if #[cfg(feature="rt-async-std")] { | ||||
|                 pub use async_std::sync::Mutex as AsyncMutex; | ||||
|                 pub use async_std::sync::MutexGuard as AsyncMutexGuard; | ||||
|                 pub use async_std::sync::MutexGuardArc as AsyncMutexGuardArc; | ||||
|                 pub use async_std::task::JoinHandle as LowLevelJoinHandle; | ||||
|             } else if #[cfg(feature="rt-tokio")] { | ||||
|                 pub use tokio::sync::Mutex as AsyncMutex; | ||||
|                 pub use tokio::sync::MutexGuard as AsyncMutexGuard; | ||||
|                 pub use tokio::sync::OwnedMutexGuard as AsyncMutexGuardArc; | ||||
|                 pub use tokio::task::JoinHandle as LowLevelJoinHandle; | ||||
|             } else { | ||||
|                 #[compile_error("must use an executor")] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // pub use bump_port::*; | ||||
| pub use async_peek_stream::*; | ||||
| pub use async_tag_lock::*; | ||||
| pub use clone_stream::*; | ||||
| pub use eventual::*; | ||||
| pub use eventual_base::{EventualCommon, EventualResolvedFuture}; | ||||
| pub use eventual_value::*; | ||||
| pub use eventual_value_clone::*; | ||||
| pub use interval::*; | ||||
| pub use ip_addr_port::*; | ||||
| pub use ip_extra::*; | ||||
| pub use must_join_handle::*; | ||||
| pub use must_join_single_future::*; | ||||
| pub use mutable_future::*; | ||||
| pub use network_result::*; | ||||
| pub use random::*; | ||||
| pub use single_shot_eventual::*; | ||||
| pub use sleep::*; | ||||
| pub use spawn::*; | ||||
| pub use tick_task::*; | ||||
| pub use timeout::*; | ||||
| pub use timeout_or::*; | ||||
| pub use timestamp::*; | ||||
| pub use tools::*; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| pub use wasm::*; | ||||
							
								
								
									
										90
									
								
								veilid-tools/src/must_join_handle.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								veilid-tools/src/must_join_handle.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::task::{Context, Poll}; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct MustJoinHandle<T> { | ||||
|     join_handle: Option<LowLevelJoinHandle<T>>, | ||||
|     completed: bool, | ||||
| } | ||||
|  | ||||
| impl<T> MustJoinHandle<T> { | ||||
|     pub fn new(join_handle: LowLevelJoinHandle<T>) -> Self { | ||||
|         Self { | ||||
|             join_handle: Some(join_handle), | ||||
|             completed: false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[allow(unused_mut)] | ||||
|     pub async fn abort(mut self) { | ||||
|         if !self.completed { | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     if let Some(jh) = self.join_handle.take() { | ||||
|                         jh.cancel().await; | ||||
|                         self.completed = true; | ||||
|                     } | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     if let Some(jh) = self.join_handle.take() { | ||||
|                         jh.abort(); | ||||
|                         let _ = jh.await; | ||||
|                         self.completed = true; | ||||
|                     } | ||||
|                 } else if #[cfg(target_arch = "wasm32")] { | ||||
|                     drop(self.join_handle.take()); | ||||
|                     self.completed = true; | ||||
|                 } else { | ||||
|                     compile_error!("needs executor implementation") | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Drop for MustJoinHandle<T> { | ||||
|     fn drop(&mut self) { | ||||
|         // panic if we haven't completed | ||||
|         if !self.completed { | ||||
|             panic!("MustJoinHandle was not completed upon drop. Add cooperative cancellation where appropriate to ensure this is completed before drop.") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: 'static> Future for MustJoinHandle<T> { | ||||
|     type Output = T; | ||||
|  | ||||
|     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||
|         match Pin::new(self.join_handle.as_mut().unwrap()).poll(cx) { | ||||
|             Poll::Ready(t) => { | ||||
|                 if self.completed { | ||||
|                     panic!("should not poll completed join handle"); | ||||
|                 } | ||||
|                 self.completed = true; | ||||
|                 cfg_if! { | ||||
|                     if #[cfg(feature="rt-async-std")] { | ||||
|                         Poll::Ready(t) | ||||
|                     } else if #[cfg(feature="rt-tokio")] { | ||||
|                         match t { | ||||
|                             Ok(t) => Poll::Ready(t), | ||||
|                             Err(e) => { | ||||
|                                 if e.is_panic() { | ||||
|                                     // Resume the panic on the main task | ||||
|                                     std::panic::resume_unwind(e.into_panic()); | ||||
|                                 } else { | ||||
|                                     panic!("join error was not a panic, should not poll after abort"); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     }else if #[cfg(target_arch = "wasm32")] { | ||||
|                         Poll::Ready(t) | ||||
|                     } else { | ||||
|                         compile_error!("needs executor implementation") | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             Poll::Pending => Poll::Pending, | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										205
									
								
								veilid-tools/src/must_join_single_future.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								veilid-tools/src/must_join_single_future.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::task::Poll; | ||||
| use futures_util::poll; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct MustJoinSingleFutureInner<T> | ||||
| where | ||||
|     T: 'static, | ||||
| { | ||||
|     locked: bool, | ||||
|     join_handle: Option<MustJoinHandle<T>>, | ||||
| } | ||||
|  | ||||
| /// Spawns a single background processing task idempotently, possibly returning the return value of the previously executed background task | ||||
| /// This does not queue, just ensures that no more than a single copy of the task is running at a time, but allowing tasks to be retriggered | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct MustJoinSingleFuture<T> | ||||
| where | ||||
|     T: 'static, | ||||
| { | ||||
|     inner: Arc<Mutex<MustJoinSingleFutureInner<T>>>, | ||||
| } | ||||
|  | ||||
| impl<T> Default for MustJoinSingleFuture<T> | ||||
| where | ||||
|     T: 'static, | ||||
| { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> MustJoinSingleFuture<T> | ||||
| where | ||||
|     T: 'static, | ||||
| { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(MustJoinSingleFutureInner { | ||||
|                 locked: false, | ||||
|                 join_handle: None, | ||||
|             })), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn try_lock(&self) -> Result<Option<MustJoinHandle<T>>, ()> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         if inner.locked { | ||||
|             // If already locked error out | ||||
|             return Err(()); | ||||
|         } | ||||
|         inner.locked = true; | ||||
|         // If we got the lock, return what we have for a join handle if anything | ||||
|         Ok(inner.join_handle.take()) | ||||
|     } | ||||
|  | ||||
|     fn unlock(&self, jh: Option<MustJoinHandle<T>>) { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         assert!(inner.locked); | ||||
|         assert!(inner.join_handle.is_none()); | ||||
|         inner.locked = false; | ||||
|         inner.join_handle = jh; | ||||
|     } | ||||
|  | ||||
|     /// Check the result and take it if there is one | ||||
|     pub async fn check(&self) -> Result<Option<T>, ()> { | ||||
|         let mut out: Option<T> = None; | ||||
|  | ||||
|         // See if we have a result we can return | ||||
|         let maybe_jh = match self.try_lock() { | ||||
|             Ok(v) => v, | ||||
|             Err(_) => { | ||||
|                 // If we are already polling somewhere else, don't hand back a result | ||||
|                 return Err(()); | ||||
|             } | ||||
|         }; | ||||
|         if maybe_jh.is_some() { | ||||
|             let mut jh = maybe_jh.unwrap(); | ||||
|  | ||||
|             // See if we finished, if so, return the value of the last execution | ||||
|             if let Poll::Ready(r) = poll!(&mut jh) { | ||||
|                 out = Some(r); | ||||
|                 // Task finished, unlock with nothing | ||||
|                 self.unlock(None); | ||||
|             } else { | ||||
|                 // Still running put the join handle back so we can check on it later | ||||
|                 self.unlock(Some(jh)); | ||||
|             } | ||||
|         } else { | ||||
|             // No task, unlock with nothing | ||||
|             self.unlock(None); | ||||
|         } | ||||
|  | ||||
|         // Return the prior result if we have one | ||||
|         Ok(out) | ||||
|     } | ||||
|  | ||||
|     /// Wait for the result and take it | ||||
|     pub async fn join(&self) -> Result<Option<T>, ()> { | ||||
|         let mut out: Option<T> = None; | ||||
|  | ||||
|         // See if we have a result we can return | ||||
|         let maybe_jh = match self.try_lock() { | ||||
|             Ok(v) => v, | ||||
|             Err(_) => { | ||||
|                 // If we are already polling somewhere else, | ||||
|                 // that's an error because you can only join | ||||
|                 // these things once | ||||
|                 return Err(()); | ||||
|             } | ||||
|         }; | ||||
|         if maybe_jh.is_some() { | ||||
|             let jh = maybe_jh.unwrap(); | ||||
|             // Wait for return value of the last execution | ||||
|             out = Some(jh.await); | ||||
|             // Task finished, unlock with nothing | ||||
|         } else { | ||||
|             // No task, unlock with nothing | ||||
|         } | ||||
|         self.unlock(None); | ||||
|  | ||||
|         // Return the prior result if we have one | ||||
|         Ok(out) | ||||
|     } | ||||
|  | ||||
|     // Possibly spawn the future possibly returning the value of the last execution | ||||
|     pub async fn single_spawn_local( | ||||
|         &self, | ||||
|         future: impl Future<Output = T> + 'static, | ||||
|     ) -> Result<(Option<T>, bool), ()> { | ||||
|         let mut out: Option<T> = None; | ||||
|  | ||||
|         // See if we have a result we can return | ||||
|         let maybe_jh = match self.try_lock() { | ||||
|             Ok(v) => v, | ||||
|             Err(_) => { | ||||
|                 // If we are already polling somewhere else, don't hand back a result | ||||
|                 return Err(()); | ||||
|             } | ||||
|         }; | ||||
|         let mut run = true; | ||||
|  | ||||
|         if maybe_jh.is_some() { | ||||
|             let mut jh = maybe_jh.unwrap(); | ||||
|  | ||||
|             // See if we finished, if so, return the value of the last execution | ||||
|             if let Poll::Ready(r) = poll!(&mut jh) { | ||||
|                 out = Some(r); | ||||
|                 // Task finished, unlock with a new task | ||||
|             } else { | ||||
|                 // Still running, don't run again, unlock with the current join handle | ||||
|                 run = false; | ||||
|                 self.unlock(Some(jh)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Run if we should do that | ||||
|         if run { | ||||
|             self.unlock(Some(spawn_local(future))); | ||||
|         } | ||||
|  | ||||
|         // Return the prior result if we have one | ||||
|         Ok((out, run)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> MustJoinSingleFuture<T> | ||||
| where | ||||
|     T: 'static + Send, | ||||
| { | ||||
|     pub async fn single_spawn( | ||||
|         &self, | ||||
|         future: impl Future<Output = T> + Send + 'static, | ||||
|     ) -> Result<(Option<T>, bool), ()> { | ||||
|         let mut out: Option<T> = None; | ||||
|         // See if we have a result we can return | ||||
|         let maybe_jh = match self.try_lock() { | ||||
|             Ok(v) => v, | ||||
|             Err(_) => { | ||||
|                 // If we are already polling somewhere else, don't hand back a result | ||||
|                 return Err(()); | ||||
|             } | ||||
|         }; | ||||
|         let mut run = true; | ||||
|         if maybe_jh.is_some() { | ||||
|             let mut jh = maybe_jh.unwrap(); | ||||
|             // See if we finished, if so, return the value of the last execution | ||||
|             if let Poll::Ready(r) = poll!(&mut jh) { | ||||
|                 out = Some(r); | ||||
|                 // Task finished, unlock with a new task | ||||
|             } else { | ||||
|                 // Still running, don't run again, unlock with the current join handle | ||||
|                 run = false; | ||||
|                 self.unlock(Some(jh)); | ||||
|             } | ||||
|         } | ||||
|         // Run if we should do that | ||||
|         if run { | ||||
|             self.unlock(Some(spawn(future))); | ||||
|         } | ||||
|         // Return the prior result if we have one | ||||
|         Ok((out, run)) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								veilid-tools/src/mutable_future.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								veilid-tools/src/mutable_future.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| use super::*; | ||||
|  | ||||
| pub struct MutableFuture<O, T: Future<Output = O>> { | ||||
|     inner: Arc<Mutex<Pin<Box<T>>>>, | ||||
| } | ||||
|  | ||||
| impl<O, T: Future<Output = O>> MutableFuture<O, T> { | ||||
|     pub fn new(inner: T) -> Self { | ||||
|         Self { | ||||
|             inner: Arc::new(Mutex::new(Box::pin(inner))), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn set(&self, inner: T) { | ||||
|         *self.inner.lock() = Box::pin(inner); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<O, T: Future<Output = O>> Clone for MutableFuture<O, T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             inner: self.inner.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<O, T: Future<Output = O>> Future for MutableFuture<O, T> { | ||||
|     type Output = O; | ||||
|     fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> { | ||||
|         let mut inner = self.inner.lock(); | ||||
|         T::poll(inner.as_mut(), cx) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										379
									
								
								veilid-tools/src/network_result.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								veilid-tools/src/network_result.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::fmt::{Debug, Display}; | ||||
| use core::result::Result; | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////// | ||||
| // Non-fallible network results conversions | ||||
|  | ||||
| pub trait NetworkResultExt<T> { | ||||
|     fn into_network_result(self) -> NetworkResult<T>; | ||||
| } | ||||
|  | ||||
| impl<T> NetworkResultExt<T> for Result<T, TimeoutError> { | ||||
|     fn into_network_result(self) -> NetworkResult<T> { | ||||
|         self.ok() | ||||
|             .map(|v| NetworkResult::<T>::Value(v)) | ||||
|             .unwrap_or(NetworkResult::<T>::Timeout) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait IoNetworkResultExt<T> { | ||||
|     fn into_network_result(self) -> io::Result<NetworkResult<T>>; | ||||
| } | ||||
|  | ||||
| impl<T> IoNetworkResultExt<T> for io::Result<T> { | ||||
|     fn into_network_result(self) -> io::Result<NetworkResult<T>> { | ||||
|         match self { | ||||
|             Ok(v) => Ok(NetworkResult::Value(v)), | ||||
|             #[cfg(feature = "io_error_more")] | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout), | ||||
|                 io::ErrorKind::ConnectionAborted | ||||
|                 | io::ErrorKind::ConnectionRefused | ||||
|                 | io::ErrorKind::ConnectionReset | ||||
|                 | io::ErrorKind::HostUnreachable | ||||
|                 | io::ErrorKind::NetworkUnreachable => Ok(NetworkResult::NoConnection(e)), | ||||
|                 io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)), | ||||
|                 _ => Err(e), | ||||
|             }, | ||||
|             #[cfg(not(feature = "io_error_more"))] | ||||
|             Err(e) => { | ||||
|                 #[cfg(not(target_arch = "wasm32"))] | ||||
|                 if let Some(os_err) = e.raw_os_error() { | ||||
|                     if os_err == libc::EHOSTUNREACH || os_err == libc::ENETUNREACH { | ||||
|                         return Ok(NetworkResult::NoConnection(e)); | ||||
|                     } | ||||
|                 } | ||||
|                 match e.kind() { | ||||
|                     io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout), | ||||
|                     io::ErrorKind::ConnectionAborted | ||||
|                     | io::ErrorKind::ConnectionRefused | ||||
|                     | io::ErrorKind::ConnectionReset => Ok(NetworkResult::NoConnection(e)), | ||||
|                     io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)), | ||||
|                     _ => Err(e), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait NetworkResultResultExt<T, E> { | ||||
|     fn into_result_network_result(self) -> Result<NetworkResult<T>, E>; | ||||
| } | ||||
|  | ||||
| impl<T, E> NetworkResultResultExt<T, E> for NetworkResult<Result<T, E>> { | ||||
|     fn into_result_network_result(self) -> Result<NetworkResult<T>, E> { | ||||
|         match self { | ||||
|             NetworkResult::Timeout => Ok(NetworkResult::<T>::Timeout), | ||||
|             NetworkResult::NoConnection(e) => Ok(NetworkResult::<T>::NoConnection(e)), | ||||
|             NetworkResult::AlreadyExists(e) => Ok(NetworkResult::<T>::AlreadyExists(e)), | ||||
|             NetworkResult::InvalidMessage(s) => Ok(NetworkResult::<T>::InvalidMessage(s)), | ||||
|             NetworkResult::Value(Ok(v)) => Ok(NetworkResult::<T>::Value(v)), | ||||
|             NetworkResult::Value(Err(e)) => Err(e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait FoldedNetworkResultExt<T> { | ||||
|     fn folded(self) -> io::Result<NetworkResult<T>>; | ||||
| } | ||||
|  | ||||
| impl<T> FoldedNetworkResultExt<T> for io::Result<TimeoutOr<T>> { | ||||
|     fn folded(self) -> io::Result<NetworkResult<T>> { | ||||
|         match self { | ||||
|             Ok(TimeoutOr::Timeout) => Ok(NetworkResult::Timeout), | ||||
|             Ok(TimeoutOr::Value(v)) => Ok(NetworkResult::Value(v)), | ||||
|             #[cfg(feature = "io_error_more")] | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout), | ||||
|                 io::ErrorKind::ConnectionAborted | ||||
|                 | io::ErrorKind::ConnectionRefused | ||||
|                 | io::ErrorKind::ConnectionReset | ||||
|                 | io::ErrorKind::HostUnreachable | ||||
|                 | io::ErrorKind::NetworkUnreachable => Ok(NetworkResult::NoConnection(e)), | ||||
|                 io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)), | ||||
|                 _ => Err(e), | ||||
|             }, | ||||
|             #[cfg(not(feature = "io_error_more"))] | ||||
|             Err(e) => { | ||||
|                 #[cfg(not(target_arch = "wasm32"))] | ||||
|                 if let Some(os_err) = e.raw_os_error() { | ||||
|                     if os_err == libc::EHOSTUNREACH || os_err == libc::ENETUNREACH { | ||||
|                         return Ok(NetworkResult::NoConnection(e)); | ||||
|                     } | ||||
|                 } | ||||
|                 match e.kind() { | ||||
|                     io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout), | ||||
|                     io::ErrorKind::ConnectionAborted | ||||
|                     | io::ErrorKind::ConnectionRefused | ||||
|                     | io::ErrorKind::ConnectionReset => Ok(NetworkResult::NoConnection(e)), | ||||
|                     io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)), | ||||
|                     _ => Err(e), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> FoldedNetworkResultExt<T> for io::Result<NetworkResult<T>> { | ||||
|     fn folded(self) -> io::Result<NetworkResult<T>> { | ||||
|         match self { | ||||
|             Ok(v) => Ok(v), | ||||
|             #[cfg(feature = "io_error_more")] | ||||
|             Err(e) => match e.kind() { | ||||
|                 io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout), | ||||
|                 io::ErrorKind::ConnectionAborted | ||||
|                 | io::ErrorKind::ConnectionRefused | ||||
|                 | io::ErrorKind::ConnectionReset | ||||
|                 | io::ErrorKind::HostUnreachable | ||||
|                 | io::ErrorKind::NetworkUnreachable => Ok(NetworkResult::NoConnection(e)), | ||||
|                 io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)), | ||||
|                 _ => Err(e), | ||||
|             }, | ||||
|             #[cfg(not(feature = "io_error_more"))] | ||||
|             Err(e) => { | ||||
|                 #[cfg(not(target_arch = "wasm32"))] | ||||
|                 if let Some(os_err) = e.raw_os_error() { | ||||
|                     if os_err == libc::EHOSTUNREACH || os_err == libc::ENETUNREACH { | ||||
|                         return Ok(NetworkResult::NoConnection(e)); | ||||
|                     } | ||||
|                 } | ||||
|                 match e.kind() { | ||||
|                     io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout), | ||||
|                     io::ErrorKind::ConnectionAborted | ||||
|                     | io::ErrorKind::ConnectionRefused | ||||
|                     | io::ErrorKind::ConnectionReset => Ok(NetworkResult::NoConnection(e)), | ||||
|                     io::ErrorKind::AddrNotAvailable => Ok(NetworkResult::AlreadyExists(e)), | ||||
|                     _ => Err(e), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////// | ||||
| // Non-fallible network result | ||||
|  | ||||
| #[must_use] | ||||
| pub enum NetworkResult<T> { | ||||
|     Timeout, | ||||
|     NoConnection(io::Error), | ||||
|     AlreadyExists(io::Error), | ||||
|     InvalidMessage(String), | ||||
|     Value(T), | ||||
| } | ||||
|  | ||||
| impl<T> NetworkResult<T> { | ||||
|     pub fn timeout() -> Self { | ||||
|         Self::Timeout | ||||
|     } | ||||
|     pub fn no_connection(e: io::Error) -> Self { | ||||
|         Self::NoConnection(e) | ||||
|     } | ||||
|     pub fn no_connection_other<S: ToString>(s: S) -> Self { | ||||
|         Self::NoConnection(io::Error::new(io::ErrorKind::Other, s.to_string())) | ||||
|     } | ||||
|     pub fn invalid_message<S: ToString>(s: S) -> Self { | ||||
|         Self::InvalidMessage(s.to_string()) | ||||
|     } | ||||
|     pub fn already_exists(e: io::Error) -> Self { | ||||
|         Self::AlreadyExists(e) | ||||
|     } | ||||
|     pub fn value(value: T) -> Self { | ||||
|         Self::Value(value) | ||||
|     } | ||||
|  | ||||
|     pub fn is_timeout(&self) -> bool { | ||||
|         matches!(self, Self::Timeout) | ||||
|     } | ||||
|     pub fn is_no_connection(&self) -> bool { | ||||
|         matches!(self, Self::NoConnection(_)) | ||||
|     } | ||||
|     pub fn is_already_exists(&self) -> bool { | ||||
|         matches!(self, Self::AlreadyExists(_)) | ||||
|     } | ||||
|     pub fn is_value(&self) -> bool { | ||||
|         matches!(self, Self::Value(_)) | ||||
|     } | ||||
|     pub fn map<X, F: Fn(T) -> X>(self, f: F) -> NetworkResult<X> { | ||||
|         match self { | ||||
|             Self::Timeout => NetworkResult::<X>::Timeout, | ||||
|             Self::NoConnection(e) => NetworkResult::<X>::NoConnection(e), | ||||
|             Self::AlreadyExists(e) => NetworkResult::<X>::AlreadyExists(e), | ||||
|             Self::InvalidMessage(s) => NetworkResult::<X>::InvalidMessage(s), | ||||
|             Self::Value(v) => NetworkResult::<X>::Value(f(v)), | ||||
|         } | ||||
|     } | ||||
|     pub fn into_result(self) -> Result<T, io::Error> { | ||||
|         match self { | ||||
|             Self::Timeout => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")), | ||||
|             Self::NoConnection(e) => Err(e), | ||||
|             Self::AlreadyExists(e) => Err(e), | ||||
|             Self::InvalidMessage(s) => Err(io::Error::new( | ||||
|                 io::ErrorKind::InvalidData, | ||||
|                 format!("Invalid message: {}", s), | ||||
|             )), | ||||
|             Self::Value(v) => Ok(v), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> From<NetworkResult<T>> for Option<T> { | ||||
|     fn from(t: NetworkResult<T>) -> Self { | ||||
|         match t { | ||||
|             NetworkResult::Value(v) => Some(v), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // impl<T: Clone> Clone for NetworkResult<T> { | ||||
| //     fn clone(&self) -> Self { | ||||
| //         match self { | ||||
| //             Self::Timeout => Self::Timeout, | ||||
| //             Self::NoConnection(e) => Self::NoConnection(e.clone()), | ||||
| //             Self::InvalidMessage(s) => Self::InvalidMessage(s.clone()), | ||||
| //             Self::Value(t) => Self::Value(t.clone()), | ||||
| //         } | ||||
| //     } | ||||
| // } | ||||
|  | ||||
| impl<T: Debug> Debug for NetworkResult<T> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         match self { | ||||
|             Self::Timeout => write!(f, "Timeout"), | ||||
|             Self::NoConnection(e) => f.debug_tuple("NoConnection").field(e).finish(), | ||||
|             Self::AlreadyExists(e) => f.debug_tuple("AlreadyExists").field(e).finish(), | ||||
|             Self::InvalidMessage(s) => f.debug_tuple("InvalidMessage").field(s).finish(), | ||||
|             Self::Value(v) => f.debug_tuple("Value").field(v).finish(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Display for NetworkResult<T> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         match self { | ||||
|             Self::Timeout => write!(f, "Timeout"), | ||||
|             Self::NoConnection(e) => write!(f, "NoConnection({})", e.kind()), | ||||
|             Self::AlreadyExists(e) => write!(f, "AlreadyExists({})", e.kind()), | ||||
|             Self::InvalidMessage(s) => write!(f, "InvalidMessage({})", s), | ||||
|             Self::Value(_) => write!(f, "Value"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Debug + Display> Error for NetworkResult<T> {} | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////// | ||||
| // Non-fallible network result macros | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! network_result_try { | ||||
|     ($r: expr) => { | ||||
|         match $r { | ||||
|             NetworkResult::Timeout => return Ok(NetworkResult::Timeout), | ||||
|             NetworkResult::NoConnection(e) => return Ok(NetworkResult::NoConnection(e)), | ||||
|             NetworkResult::AlreadyExists(e) => return Ok(NetworkResult::AlreadyExists(e)), | ||||
|             NetworkResult::InvalidMessage(s) => return Ok(NetworkResult::InvalidMessage(s)), | ||||
|             NetworkResult::Value(v) => v, | ||||
|         } | ||||
|     }; | ||||
|     ($r:expr => $f:tt) => { | ||||
|         match $r { | ||||
|             NetworkResult::Timeout => { | ||||
|                 $f; | ||||
|                 return Ok(NetworkResult::Timeout); | ||||
|             } | ||||
|             NetworkResult::NoConnection(e) => { | ||||
|                 $f; | ||||
|                 return Ok(NetworkResult::NoConnection(e)); | ||||
|             } | ||||
|             NetworkResult::AlreadyExists(e) => { | ||||
|                 $f; | ||||
|                 return Ok(NetworkResult::AlreadyExists(e)); | ||||
|             } | ||||
|             NetworkResult::InvalidMessage(s) => { | ||||
|                 $f; | ||||
|                 return Ok(NetworkResult::InvalidMessage(s)); | ||||
|             } | ||||
|             NetworkResult::Value(v) => v, | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! log_network_result { | ||||
|     ($text:expr) => { | ||||
|         cfg_if::cfg_if! { | ||||
|             if #[cfg(debug_assertions)] { | ||||
|                 info!(target: "network_result", "{}", $text) | ||||
|             } else { | ||||
|                 debug!(target: "network_result", "{}", $text) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     ($fmt:literal, $($arg:expr),+) => { | ||||
|         cfg_if::cfg_if! { | ||||
|             if #[cfg(debug_assertions)] { | ||||
|                 info!(target: "network_result", $fmt, $($arg),+); | ||||
|             } else { | ||||
|                 debug!(target: "network_result", $fmt, $($arg),+); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! network_result_value_or_log { | ||||
|     ($level: ident $r: expr => $f:tt) => { | ||||
|         match $r { | ||||
|             NetworkResult::Timeout => { | ||||
|                 log_network_result!( | ||||
|                     "{} at {}@{}:{}", | ||||
|                     "Timeout".cyan(), | ||||
|                     file!(), | ||||
|                     line!(), | ||||
|                     column!() | ||||
|                 ); | ||||
|                 $f | ||||
|             } | ||||
|             NetworkResult::NoConnection(e) => { | ||||
|                 log_network_result!( | ||||
|                     "{}({}) at {}@{}:{}", | ||||
|                     "No connection".cyan(), | ||||
|                     e.to_string(), | ||||
|                     file!(), | ||||
|                     line!(), | ||||
|                     column!() | ||||
|                 ); | ||||
|                 $f | ||||
|             } | ||||
|             NetworkResult::AlreadyExists(e) => { | ||||
|                 log_network_result!( | ||||
|                     "{}({}) at {}@{}:{}", | ||||
|                     "Already exists".cyan(), | ||||
|                     e.to_string(), | ||||
|                     file!(), | ||||
|                     line!(), | ||||
|                     column!() | ||||
|                 ); | ||||
|                 $f | ||||
|             } | ||||
|             NetworkResult::InvalidMessage(s) => { | ||||
|                 log_network_result!( | ||||
|                     "{}({}) at {}@{}:{}", | ||||
|                     "Invalid message".cyan(), | ||||
|                     s, | ||||
|                     file!(), | ||||
|                     line!(), | ||||
|                     column!() | ||||
|                 ); | ||||
|                 $f | ||||
|             } | ||||
|             NetworkResult::Value(v) => v, | ||||
|         } | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										81
									
								
								veilid-tools/src/random.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								veilid-tools/src/random.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| use super::*; | ||||
| use rand::prelude::*; | ||||
|  | ||||
| #[derive(Clone, Copy, Debug, Default)] | ||||
| pub struct VeilidRng; | ||||
|  | ||||
| impl CryptoRng for VeilidRng {} | ||||
|  | ||||
| impl RngCore for VeilidRng { | ||||
|     fn next_u32(&mut self) -> u32 { | ||||
|         get_random_u32() | ||||
|     } | ||||
|  | ||||
|     fn next_u64(&mut self) -> u64 { | ||||
|         get_random_u64() | ||||
|     } | ||||
|  | ||||
|     fn fill_bytes(&mut self, dest: &mut [u8]) { | ||||
|         if let Err(e) = self.try_fill_bytes(dest) { | ||||
|             panic!("Error: {}", e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { | ||||
|         random_bytes(dest).map_err(rand::Error::new) | ||||
|     } | ||||
| } | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|         pub fn random_bytes(dest: &mut [u8]) -> EyreResult<()> { | ||||
|             let len = dest.len(); | ||||
|             let u32len = len / 4; | ||||
|             let remlen = len % 4; | ||||
|  | ||||
|             for n in 0..u32len { | ||||
|                 let r = (Math::random() * (u32::max_value() as f64)) as u32; | ||||
|  | ||||
|                 dest[n * 4 + 0] = (r & 0xFF) as u8; | ||||
|                 dest[n * 4 + 1] = ((r >> 8) & 0xFF) as u8; | ||||
|                 dest[n * 4 + 2] = ((r >> 16) & 0xFF) as u8; | ||||
|                 dest[n * 4 + 3] = ((r >> 24) & 0xFF) as u8; | ||||
|             } | ||||
|             if remlen > 0 { | ||||
|                 let r = (Math::random() * (u32::max_value() as f64)) as u32; | ||||
|                 for n in 0..remlen { | ||||
|                     dest[u32len * 4 + n] = ((r >> (n * 8)) & 0xFF) as u8; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Ok(()) | ||||
|         } | ||||
|  | ||||
|         pub fn get_random_u32() -> u32 { | ||||
|             (Math::random() * (u32::max_value() as f64)) as u32 | ||||
|         } | ||||
|  | ||||
|         pub fn get_random_u64() -> u64 { | ||||
|             let v1: u32 = get_random_u32(); | ||||
|             let v2: u32 = get_random_u32(); | ||||
|             ((v1 as u64) << 32) | ((v2 as u32) as u64) | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub fn random_bytes(dest: &mut [u8]) -> EyreResult<()> { | ||||
|             let mut rng = rand::thread_rng(); | ||||
|             rng.try_fill_bytes(dest).wrap_err("failed to fill bytes") | ||||
|         } | ||||
|  | ||||
|         pub fn get_random_u32() -> u32 { | ||||
|             let mut rng = rand::thread_rng(); | ||||
|             rng.next_u32() | ||||
|         } | ||||
|  | ||||
|         pub fn get_random_u64() -> u64 { | ||||
|             let mut rng = rand::thread_rng(); | ||||
|             rng.next_u64() | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								veilid-tools/src/single_shot_eventual.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								veilid-tools/src/single_shot_eventual.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| use super::*; | ||||
|  | ||||
| pub struct SingleShotEventual<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     eventual: EventualValue<T>, | ||||
|     drop_value: Option<T>, | ||||
| } | ||||
|  | ||||
| impl<T> Drop for SingleShotEventual<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some(drop_value) = self.drop_value.take() { | ||||
|             self.eventual.resolve(drop_value); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> SingleShotEventual<T> | ||||
| where | ||||
|     T: Unpin, | ||||
| { | ||||
|     pub fn new(drop_value: Option<T>) -> Self { | ||||
|         Self { | ||||
|             eventual: EventualValue::new(), | ||||
|             drop_value, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Can only call this once, it consumes the eventual | ||||
|     pub fn resolve(mut self, value: T) -> EventualResolvedFuture<EventualValue<T>> { | ||||
|         // If we resolve, we don't want to resolve again to the drop value | ||||
|         self.drop_value = None; | ||||
|         // Resolve to the specified value | ||||
|         self.eventual.resolve(value) | ||||
|     } | ||||
|  | ||||
|     pub fn instance(&self) -> EventualValueFuture<T> { | ||||
|         self.eventual.instance() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								veilid-tools/src/sleep.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								veilid-tools/src/sleep.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| use super::*; | ||||
| use std::time::Duration; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|         use async_executors::Bindgen; | ||||
|  | ||||
|         pub async fn sleep(millis: u32) { | ||||
|             Bindgen.sleep(Duration::from_millis(millis.into())).await | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub async fn sleep(millis: u32) { | ||||
|             if millis == 0 { | ||||
|                 cfg_if! { | ||||
|                     if #[cfg(feature="rt-async-std")] { | ||||
|                         async_std::task::yield_now().await; | ||||
|                     } else if #[cfg(feature="rt-tokio")] { | ||||
|                         tokio::task::yield_now().await; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 cfg_if! { | ||||
|                     if #[cfg(feature="rt-async-std")] { | ||||
|                         async_std::task::sleep(Duration::from_millis(u64::from(millis))).await; | ||||
|                     } else if #[cfg(feature="rt-tokio")] { | ||||
|                         tokio::time::sleep(Duration::from_millis(u64::from(millis))).await; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										119
									
								
								veilid-tools/src/spawn.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								veilid-tools/src/spawn.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| use super::*; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|         use async_executors::{Bindgen, LocalSpawnHandleExt, SpawnHandleExt}; | ||||
|  | ||||
|         pub fn spawn<Out>(future: impl Future<Output = Out> + Send + 'static) -> MustJoinHandle<Out> | ||||
|         where | ||||
|             Out: Send + 'static, | ||||
|         { | ||||
|             MustJoinHandle::new( | ||||
|                 Bindgen | ||||
|                     .spawn_handle(future) | ||||
|                     .expect("wasm-bindgen-futures spawn_handle_local should never error out"), | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|         pub fn spawn_local<Out>(future: impl Future<Output = Out> + 'static) -> MustJoinHandle<Out> | ||||
|         where | ||||
|             Out: 'static, | ||||
|         { | ||||
|             MustJoinHandle::new( | ||||
|                 Bindgen | ||||
|                     .spawn_handle_local(future) | ||||
|                     .expect("wasm-bindgen-futures spawn_handle_local should never error out"), | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|         pub fn spawn_detached<Out>(future: impl Future<Output = Out> + Send + 'static) | ||||
|         where | ||||
|             Out: Send + 'static, | ||||
|         { | ||||
|             Bindgen | ||||
|                 .spawn_handle_local(future) | ||||
|                 .expect("wasm-bindgen-futures spawn_handle_local should never error out") | ||||
|                 .detach() | ||||
|         } | ||||
|         pub fn spawn_detached_local<Out>(future: impl Future<Output = Out> + 'static) | ||||
|         where | ||||
|             Out: 'static, | ||||
|         { | ||||
|             Bindgen | ||||
|                 .spawn_handle_local(future) | ||||
|                 .expect("wasm-bindgen-futures spawn_handle_local should never error out") | ||||
|                 .detach() | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub fn spawn<Out>(future: impl Future<Output = Out> + Send + 'static) -> MustJoinHandle<Out> | ||||
|         where | ||||
|             Out: Send + 'static, | ||||
|         { | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     MustJoinHandle::new(async_std::task::spawn(future)) | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     MustJoinHandle::new(tokio::task::spawn(future)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub fn spawn_local<Out>(future: impl Future<Output = Out> + 'static) -> MustJoinHandle<Out> | ||||
|         where | ||||
|             Out: 'static, | ||||
|         { | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     MustJoinHandle::new(async_std::task::spawn_local(future)) | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     MustJoinHandle::new(tokio::task::spawn_local(future)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub fn spawn_detached<Out>(future: impl Future<Output = Out> + Send + 'static) | ||||
|         where | ||||
|             Out: Send + 'static, | ||||
|         { | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     drop(async_std::task::spawn(future)); | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     drop(tokio::task::spawn(future)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub fn spawn_detached_local<Out>(future: impl Future<Output = Out> + 'static) | ||||
|         where | ||||
|             Out: 'static, | ||||
|         { | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     drop(async_std::task::spawn_local(future)); | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     drop(tokio::task::spawn_local(future)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub async fn blocking_wrapper<F, R>(blocking_task: F, err_result: R) -> R | ||||
|         where | ||||
|             F: FnOnce() -> R + Send + 'static, | ||||
|             R: Send + 'static, | ||||
|         { | ||||
|             // run blocking stuff in blocking thread | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     async_std::task::spawn_blocking(blocking_task).await | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     tokio::task::spawn_blocking(blocking_task).await.unwrap_or(err_result) | ||||
|                 } else { | ||||
|                     #[compile_error("must use an executor")] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										391
									
								
								veilid-tools/src/split_url.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								veilid-tools/src/split_url.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,391 @@ | ||||
| // Loose subset interpretation of the URL standard | ||||
| // Not using full Url crate here for no_std compatibility | ||||
| // | ||||
| // Caveats: | ||||
| //   No support for query string parsing | ||||
| //   No support for paths with ';' parameters | ||||
| //   URLs must convert to UTF8 | ||||
| //   Only IP address and DNS hostname host fields are supported | ||||
|  | ||||
| use super::*; | ||||
|  | ||||
| use core::str::FromStr; | ||||
|  | ||||
| fn is_alphanum(c: u8) -> bool { | ||||
|     matches!(c, | ||||
|         b'A'..=b'Z' | ||||
|         | b'a'..=b'z' | ||||
|         | b'0'..=b'9' | ||||
|     ) | ||||
| } | ||||
| fn is_mark(c: u8) -> bool { | ||||
|     matches!( | ||||
|         c, | ||||
|         b'-' | b'_' | b'.' | b'!' | b'~' | b'*' | b'\'' | b'(' | b')' | ||||
|     ) | ||||
| } | ||||
| fn is_unreserved(c: u8) -> bool { | ||||
|     is_alphanum(c) || is_mark(c) | ||||
| } | ||||
|  | ||||
| fn must_encode_userinfo(c: u8) -> bool { | ||||
|     !(is_unreserved(c) || matches!(c, b'%' | b':' | b';' | b'&' | b'=' | b'+' | b'$' | b',')) | ||||
| } | ||||
|  | ||||
| fn must_encode_path(c: u8) -> bool { | ||||
|     !(is_unreserved(c) | ||||
|         || matches!( | ||||
|             c, | ||||
|             b'%' | b'/' | b':' | b'@' | b'&' | b'=' | b'+' | b'$' | b',' | ||||
|         )) | ||||
| } | ||||
|  | ||||
| fn is_valid_scheme<H: AsRef<str>>(host: H) -> bool { | ||||
|     let mut chars = host.as_ref().chars(); | ||||
|     if let Some(ch) = chars.next() { | ||||
|         if !matches!(ch, 'A'..='Z' | 'a'..='z') { | ||||
|             return false; | ||||
|         } | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
|     for ch in chars { | ||||
|         if !matches!(ch, | ||||
|             'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '+' | '.' ) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     true | ||||
| } | ||||
|  | ||||
| fn hex_decode(h: u8) -> Result<u8, SplitUrlError> { | ||||
|     match h { | ||||
|         b'0'..=b'9' => Ok(h - b'0'), | ||||
|         b'A'..=b'F' => Ok(h - b'A' + 10), | ||||
|         b'a'..=b'f' => Ok(h - b'a' + 10), | ||||
|         _ => Err(SplitUrlError::new( | ||||
|             "Unexpected character in percent encoding", | ||||
|         )), | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn hex_encode(c: u8) -> (char, char) { | ||||
|     let c0 = c >> 4; | ||||
|     let c1 = c & 15; | ||||
|     ( | ||||
|         if c0 < 10 { | ||||
|             char::from_u32((b'0' + c0) as u32).unwrap() | ||||
|         } else { | ||||
|             char::from_u32((b'A' + c0 - 10) as u32).unwrap() | ||||
|         }, | ||||
|         if c1 < 10 { | ||||
|             char::from_u32((b'0' + c1) as u32).unwrap() | ||||
|         } else { | ||||
|             char::from_u32((b'A' + c1 - 10) as u32).unwrap() | ||||
|         }, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| fn url_decode<S: AsRef<str>>(s: S) -> Result<String, SplitUrlError> { | ||||
|     let url = s.as_ref().to_owned(); | ||||
|     if !url.is_ascii() { | ||||
|         return Err(SplitUrlError::new("URL is not in ASCII encoding")); | ||||
|     } | ||||
|     let url_bytes = url.as_bytes(); | ||||
|     let mut dec_bytes: Vec<u8> = Vec::with_capacity(url_bytes.len()); | ||||
|     let mut i = 0; | ||||
|     let end = url_bytes.len(); | ||||
|     while i < end { | ||||
|         let mut b = url_bytes[i]; | ||||
|         i += 1; | ||||
|         if b == b'%' { | ||||
|             if (i + 1) >= end { | ||||
|                 return Err(SplitUrlError::new("Invalid URL encoding")); | ||||
|             } | ||||
|             b = hex_decode(url_bytes[i])? << 4 | hex_decode(url_bytes[i + 1])?; | ||||
|             i += 2; | ||||
|         } | ||||
|         dec_bytes.push(b); | ||||
|     } | ||||
|     String::from_utf8(dec_bytes) | ||||
|         .map_err(|e| SplitUrlError::new(format!("Decoded URL is not valid UTF-8: {}", e))) | ||||
| } | ||||
|  | ||||
| fn url_encode<S: AsRef<str>>(s: S, must_encode: impl Fn(u8) -> bool) -> String { | ||||
|     let bytes = s.as_ref().as_bytes(); | ||||
|     let mut out = String::new(); | ||||
|     for b in bytes { | ||||
|         if must_encode(*b) { | ||||
|             let (c0, c1) = hex_encode(*b); | ||||
|             out.push('%'); | ||||
|             out.push(c0); | ||||
|             out.push(c1); | ||||
|         } else { | ||||
|             out.push(char::from_u32(*b as u32).unwrap()) | ||||
|         } | ||||
|     } | ||||
|     out | ||||
| } | ||||
|  | ||||
| fn convert_port<N>(port_str: N) -> Result<u16, SplitUrlError> | ||||
| where | ||||
|     N: AsRef<str>, | ||||
| { | ||||
|     port_str | ||||
|         .as_ref() | ||||
|         .parse::<u16>() | ||||
|         .map_err(|e| SplitUrlError::new(format!("Invalid port: {}", e))) | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| #[derive(ThisError, Debug, Clone, Eq, PartialEq)] | ||||
| #[error("SplitUrlError: {0}")] | ||||
| pub struct SplitUrlError(String); | ||||
|  | ||||
| impl SplitUrlError { | ||||
|     pub fn new<T: ToString>(message: T) -> Self { | ||||
|         SplitUrlError(message.to_string()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||||
| pub struct SplitUrlPath { | ||||
|     pub path: String, | ||||
|     pub fragment: Option<String>, | ||||
|     pub query: Option<String>, | ||||
| } | ||||
|  | ||||
| impl SplitUrlPath { | ||||
|     pub fn new<P, F, Q>(path: P, fragment: Option<F>, query: Option<Q>) -> Self | ||||
|     where | ||||
|         P: AsRef<str>, | ||||
|         F: AsRef<str>, | ||||
|         Q: AsRef<str>, | ||||
|     { | ||||
|         Self { | ||||
|             path: path.as_ref().to_owned(), | ||||
|             fragment: fragment.map(|f| f.as_ref().to_owned()), | ||||
|             query: query.map(|f| f.as_ref().to_owned()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FromStr for SplitUrlPath { | ||||
|     type Err = SplitUrlError; | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         Ok(if let Some((p, q)) = s.split_once('?') { | ||||
|             if let Some((p, f)) = p.split_once('#') { | ||||
|                 SplitUrlPath::new(url_decode(p)?, Some(url_decode(f)?), Some(q)) | ||||
|             } else { | ||||
|                 SplitUrlPath::new(url_decode(p)?, Option::<String>::None, Some(q)) | ||||
|             } | ||||
|         } else if let Some((p, f)) = s.split_once('#') { | ||||
|             SplitUrlPath::new(url_decode(p)?, Some(url_decode(f)?), Option::<String>::None) | ||||
|         } else { | ||||
|             SplitUrlPath::new( | ||||
|                 url_decode(s)?, | ||||
|                 Option::<String>::None, | ||||
|                 Option::<String>::None, | ||||
|             ) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for SplitUrlPath { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         if let Some(fragment) = &self.fragment { | ||||
|             if let Some(query) = &self.query { | ||||
|                 write!( | ||||
|                     f, | ||||
|                     "{}#{}?{}", | ||||
|                     url_encode(&self.path, must_encode_path), | ||||
|                     url_encode(fragment, must_encode_path), | ||||
|                     query | ||||
|                 ) | ||||
|             } else { | ||||
|                 write!(f, "{}#{}", self.path, fragment) | ||||
|             } | ||||
|         } else if let Some(query) = &self.query { | ||||
|             write!(f, "{}?{}", url_encode(&self.path, must_encode_path), query) | ||||
|         } else { | ||||
|             write!(f, "{}", url_encode(&self.path, must_encode_path)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||||
| pub enum SplitUrlHost { | ||||
|     Hostname(String), | ||||
|     IpAddr(IpAddr), | ||||
| } | ||||
|  | ||||
| impl SplitUrlHost { | ||||
|     pub fn new<S: AsRef<str>>(s: S) -> Result<Self, SplitUrlError> { | ||||
|         Self::from_str(s.as_ref()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FromStr for SplitUrlHost { | ||||
|     type Err = SplitUrlError; | ||||
|  | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         if s.is_empty() { | ||||
|             return Err(SplitUrlError::new("Host is empty")); | ||||
|         } | ||||
|         if let Ok(v4) = Ipv4Addr::from_str(s) { | ||||
|             return Ok(SplitUrlHost::IpAddr(IpAddr::V4(v4))); | ||||
|         } | ||||
|         if &s[0..1] == "[" && &s[s.len() - 1..] == "]" { | ||||
|             if let Ok(v6) = Ipv6Addr::from_str(&s[1..s.len() - 1]) { | ||||
|                 return Ok(SplitUrlHost::IpAddr(IpAddr::V6(v6))); | ||||
|             } | ||||
|             return Err(SplitUrlError::new("Invalid ipv6 address")); | ||||
|         } | ||||
|         for ch in s.chars() { | ||||
|             if !matches!(ch, | ||||
|                 'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '.' ) | ||||
|             { | ||||
|                 return Err(SplitUrlError::new("Invalid hostname")); | ||||
|             } | ||||
|         } | ||||
|         Ok(SplitUrlHost::Hostname(s.to_owned())) | ||||
|     } | ||||
| } | ||||
| impl fmt::Display for SplitUrlHost { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self { | ||||
|             Self::Hostname(h) => { | ||||
|                 write!(f, "{}", h) | ||||
|             } | ||||
|             Self::IpAddr(IpAddr::V4(v4)) => { | ||||
|                 write!(f, "{}", v4) | ||||
|             } | ||||
|             Self::IpAddr(IpAddr::V6(v6)) => { | ||||
|                 write!(f, "[{}]", v6) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||||
| pub struct SplitUrl { | ||||
|     pub scheme: String, | ||||
|     pub userinfo: Option<String>, | ||||
|     pub host: SplitUrlHost, | ||||
|     pub port: Option<u16>, | ||||
|     pub path: Option<SplitUrlPath>, | ||||
| } | ||||
|  | ||||
| impl SplitUrl { | ||||
|     pub fn new<S>( | ||||
|         scheme: S, | ||||
|         userinfo: Option<String>, | ||||
|         host: SplitUrlHost, | ||||
|         port: Option<u16>, | ||||
|         path: Option<SplitUrlPath>, | ||||
|     ) -> Self | ||||
|     where | ||||
|         S: AsRef<str>, | ||||
|     { | ||||
|         Self { | ||||
|             scheme: scheme.as_ref().to_owned(), | ||||
|             userinfo, | ||||
|             host, | ||||
|             port, | ||||
|             path, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn host_port(&self, default_port: u16) -> String { | ||||
|         format!("{}:{}", self.host, self.port.unwrap_or(default_port)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn split_host_with_port(s: &str) -> Option<(&str, &str)> { | ||||
|     // special case for ipv6 colons | ||||
|     if s.len() > 2 && s[0..1] == *"[" { | ||||
|         if let Some(end) = s.find(']') { | ||||
|             if end < (s.len() - 2) && s[end + 1..end + 2] == *":" { | ||||
|                 return Some((&s[0..end + 1], &s[end + 2..])); | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } else { | ||||
|         s.split_once(':') | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FromStr for SplitUrl { | ||||
|     type Err = SplitUrlError; | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         if let Some((scheme, mut rest)) = s.split_once("://") { | ||||
|             if !is_valid_scheme(scheme) { | ||||
|                 return Err(SplitUrlError::new("Invalid scheme specified")); | ||||
|             } | ||||
|             let userinfo = { | ||||
|                 if let Some((userinfo_str, after)) = rest.split_once('@') { | ||||
|                     rest = after; | ||||
|                     Some(url_decode(userinfo_str)?) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }; | ||||
|             if let Some((host, rest)) = split_host_with_port(rest) { | ||||
|                 let host = SplitUrlHost::from_str(host)?; | ||||
|                 if let Some((portstr, path)) = rest.split_once('/') { | ||||
|                     let port = convert_port(portstr)?; | ||||
|                     let path = SplitUrlPath::from_str(path)?; | ||||
|                     Ok(SplitUrl::new( | ||||
|                         scheme, | ||||
|                         userinfo, | ||||
|                         host, | ||||
|                         Some(port), | ||||
|                         Some(path), | ||||
|                     )) | ||||
|                 } else { | ||||
|                     let port = convert_port(rest)?; | ||||
|                     Ok(SplitUrl::new(scheme, userinfo, host, Some(port), None)) | ||||
|                 } | ||||
|             } else if let Some((host, path)) = rest.split_once('/') { | ||||
|                 let host = SplitUrlHost::from_str(host)?; | ||||
|                 let path = SplitUrlPath::from_str(path)?; | ||||
|                 Ok(SplitUrl::new(scheme, userinfo, host, None, Some(path))) | ||||
|             } else { | ||||
|                 let host = SplitUrlHost::from_str(rest)?; | ||||
|                 Ok(SplitUrl::new(scheme, userinfo, host, None, None)) | ||||
|             } | ||||
|         } else { | ||||
|             Err(SplitUrlError::new("No scheme specified")) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for SplitUrl { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let hostname = { | ||||
|             if let Some(userinfo) = &self.userinfo { | ||||
|                 let userinfo = url_encode(userinfo, must_encode_userinfo); | ||||
|                 if let Some(port) = self.port { | ||||
|                     format!("{}@{}:{}", userinfo, self.host, port) | ||||
|                 } else { | ||||
|                     format!("{}@{}", userinfo, self.host) | ||||
|                 } | ||||
|             } else if let Some(port) = self.port { | ||||
|                 format!("{}:{}", self.host, port) | ||||
|             } else { | ||||
|                 format!("{}", self.host) | ||||
|             } | ||||
|         }; | ||||
|         if let Some(path) = &self.path { | ||||
|             write!(f, "{}://{}/{}", self.scheme, hostname, path) | ||||
|         } else { | ||||
|             write!(f, "{}://{}", self.scheme, hostname) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										145
									
								
								veilid-tools/src/tick_task.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								veilid-tools/src/tick_task.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::sync::atomic::{AtomicU64, Ordering}; | ||||
| use once_cell::sync::OnceCell; | ||||
|  | ||||
| type TickTaskRoutine<E> = | ||||
|     dyn Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), E>> + Send + Sync + 'static; | ||||
|  | ||||
| /// Runs a single-future background processing task, attempting to run it once every 'tick period' microseconds. | ||||
| /// If the prior tick is still running, it will allow it to finish, and do another tick when the timer comes around again. | ||||
| /// One should attempt to make tasks short-lived things that run in less than the tick period if you want things to happen with regular periodicity. | ||||
| pub struct TickTask<E: Send + 'static> { | ||||
|     last_timestamp_us: AtomicU64, | ||||
|     tick_period_us: u64, | ||||
|     routine: OnceCell<Box<TickTaskRoutine<E>>>, | ||||
|     stop_source: AsyncMutex<Option<StopSource>>, | ||||
|     single_future: MustJoinSingleFuture<Result<(), E>>, | ||||
|     running: Arc<AtomicBool>, | ||||
| } | ||||
|  | ||||
| impl<E: Send + 'static> TickTask<E> { | ||||
|     pub fn new_us(tick_period_us: u64) -> Self { | ||||
|         Self { | ||||
|             last_timestamp_us: AtomicU64::new(0), | ||||
|             tick_period_us, | ||||
|             routine: OnceCell::new(), | ||||
|             stop_source: AsyncMutex::new(None), | ||||
|             single_future: MustJoinSingleFuture::new(), | ||||
|             running: Arc::new(AtomicBool::new(false)), | ||||
|         } | ||||
|     } | ||||
|     pub fn new_ms(tick_period_ms: u32) -> Self { | ||||
|         Self { | ||||
|             last_timestamp_us: AtomicU64::new(0), | ||||
|             tick_period_us: (tick_period_ms as u64) * 1000u64, | ||||
|             routine: OnceCell::new(), | ||||
|             stop_source: AsyncMutex::new(None), | ||||
|             single_future: MustJoinSingleFuture::new(), | ||||
|             running: Arc::new(AtomicBool::new(false)), | ||||
|         } | ||||
|     } | ||||
|     pub fn new(tick_period_sec: u32) -> Self { | ||||
|         Self { | ||||
|             last_timestamp_us: AtomicU64::new(0), | ||||
|             tick_period_us: (tick_period_sec as u64) * 1000000u64, | ||||
|             routine: OnceCell::new(), | ||||
|             stop_source: AsyncMutex::new(None), | ||||
|             single_future: MustJoinSingleFuture::new(), | ||||
|             running: Arc::new(AtomicBool::new(false)), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn set_routine( | ||||
|         &self, | ||||
|         routine: impl Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), E>> + Send + Sync + 'static, | ||||
|     ) { | ||||
|         self.routine.set(Box::new(routine)).map_err(drop).unwrap(); | ||||
|     } | ||||
|  | ||||
|     pub fn is_running(&self) -> bool { | ||||
|         self.running.load(core::sync::atomic::Ordering::Relaxed) | ||||
|     } | ||||
|  | ||||
|     pub async fn stop(&self) -> Result<(), E> { | ||||
|         // drop the stop source if we have one | ||||
|         let opt_stop_source = &mut *self.stop_source.lock().await; | ||||
|         if opt_stop_source.is_none() { | ||||
|             // already stopped, just return | ||||
|             trace!("tick task already stopped"); | ||||
|             return Ok(()); | ||||
|         } | ||||
|         drop(opt_stop_source.take()); | ||||
|  | ||||
|         // wait for completion of the tick task | ||||
|         trace!("stopping single future"); | ||||
|         match self.single_future.join().await { | ||||
|             Ok(Some(Err(err))) => Err(err), | ||||
|             _ => Ok(()), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn tick(&self) -> Result<(), E> { | ||||
|         let now = get_timestamp(); | ||||
|         let last_timestamp_us = self.last_timestamp_us.load(Ordering::Acquire); | ||||
|  | ||||
|         if last_timestamp_us != 0u64 && now.saturating_sub(last_timestamp_us) < self.tick_period_us | ||||
|         { | ||||
|             // It's not time yet | ||||
|             return Ok(()); | ||||
|         } | ||||
|  | ||||
|         // Lock the stop source, tells us if we have ever started this future | ||||
|         let opt_stop_source = &mut *self.stop_source.lock().await; | ||||
|         if opt_stop_source.is_some() { | ||||
|             // See if the previous execution finished with an error | ||||
|             match self.single_future.check().await { | ||||
|                 Ok(Some(Err(e))) => { | ||||
|                     // We have an error result, which means the singlefuture ran but we need to propagate the error | ||||
|                     return Err(e); | ||||
|                 } | ||||
|                 Ok(Some(Ok(()))) => { | ||||
|                     // We have an ok result, which means the singlefuture ran, and we should run it again this tick | ||||
|                 } | ||||
|                 Ok(None) => { | ||||
|                     // No prior result to return which means things are still running | ||||
|                     // We can just return now, since the singlefuture will not run a second time | ||||
|                     return Ok(()); | ||||
|                 } | ||||
|                 Err(()) => { | ||||
|                     // If we get this, it's because we are joining the singlefuture already | ||||
|                     // Don't bother running but this is not an error in this case | ||||
|                     return Ok(()); | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         // Run the singlefuture | ||||
|         let stop_source = StopSource::new(); | ||||
|         let stop_token = stop_source.token(); | ||||
|         let running = self.running.clone(); | ||||
|         let routine = self.routine.get().unwrap()(stop_token, last_timestamp_us, now); | ||||
|         let wrapped_routine = Box::pin(async move { | ||||
|             running.store(true, core::sync::atomic::Ordering::Relaxed); | ||||
|             let out = routine.await; | ||||
|             running.store(false, core::sync::atomic::Ordering::Relaxed); | ||||
|             out | ||||
|         }); | ||||
|         match self.single_future.single_spawn(wrapped_routine).await { | ||||
|             // We should have already consumed the result of the last run, or there was none | ||||
|             // and we should definitely have run, because the prior 'check()' operation | ||||
|             // should have ensured the singlefuture was ready to run | ||||
|             Ok((None, true)) => { | ||||
|                 // Set new timer | ||||
|                 self.last_timestamp_us.store(now, Ordering::Release); | ||||
|                 // Save new stopper | ||||
|                 *opt_stop_source = Some(stop_source); | ||||
|                 Ok(()) | ||||
|             } | ||||
|             // All other conditions should not be reachable | ||||
|             _ => { | ||||
|                 unreachable!(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								veilid-tools/src/timeout.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								veilid-tools/src/timeout.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| use super::*; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|  | ||||
|         pub async fn timeout<F, T>(dur_ms: u32, f: F) -> Result<T, TimeoutError> | ||||
|         where | ||||
|             F: Future<Output = T>, | ||||
|         { | ||||
|             match select(Box::pin(intf::sleep(dur_ms)), Box::pin(f)).await { | ||||
|                 Either::Left((_x, _b)) => Err(TimeoutError()), | ||||
|                 Either::Right((y, _a)) => Ok(y), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub async fn timeout<F, T>(dur_ms: u32, f: F) -> Result<T, TimeoutError> | ||||
|         where | ||||
|             F: Future<Output = T>, | ||||
|         { | ||||
|             cfg_if! { | ||||
|                 if #[cfg(feature="rt-async-std")] { | ||||
|                     async_std::future::timeout(Duration::from_millis(dur_ms as u64), f).await.map_err(|e| e.into()) | ||||
|                 } else if #[cfg(feature="rt-tokio")] { | ||||
|                     tokio::time::timeout(Duration::from_millis(dur_ms as u64), f).await.map_err(|e| e.into()) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										177
									
								
								veilid-tools/src/timeout_or.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								veilid-tools/src/timeout_or.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| use super::*; | ||||
|  | ||||
| use core::fmt::{Debug, Display}; | ||||
| use core::result::Result; | ||||
| use std::error::Error; | ||||
| use std::io; | ||||
|  | ||||
| #[derive(ThisError, Debug, Clone, Copy, Eq, PartialEq)] | ||||
| #[error("Timeout")] | ||||
| pub struct TimeoutError(); | ||||
|  | ||||
| impl TimeoutError { | ||||
|     pub fn to_io(self) -> io::Error { | ||||
|         io::Error::new(io::ErrorKind::TimedOut, self) | ||||
|     } | ||||
| } | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(feature="rt-async-std")] { | ||||
|         impl From<async_std::future::TimeoutError> for TimeoutError { | ||||
|             fn from(_: async_std::future::TimeoutError) -> Self { | ||||
|                 Self() | ||||
|             } | ||||
|         } | ||||
|     } else if #[cfg(feature="rt-tokio")] { | ||||
|         impl From<tokio::time::error::Elapsed> for TimeoutError { | ||||
|             fn from(_: tokio::time::error::Elapsed) -> Self { | ||||
|                 Self() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////// | ||||
| // Non-fallible timeout conversions | ||||
|  | ||||
| pub trait TimeoutOrExt<T> { | ||||
|     fn into_timeout_or(self) -> TimeoutOr<T>; | ||||
| } | ||||
|  | ||||
| impl<T> TimeoutOrExt<T> for Result<T, TimeoutError> { | ||||
|     fn into_timeout_or(self) -> TimeoutOr<T> { | ||||
|         self.ok() | ||||
|             .map(|v| TimeoutOr::<T>::Value(v)) | ||||
|             .unwrap_or(TimeoutOr::<T>::Timeout) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait IoTimeoutOrExt<T> { | ||||
|     fn into_timeout_or(self) -> io::Result<TimeoutOr<T>>; | ||||
| } | ||||
|  | ||||
| impl<T> IoTimeoutOrExt<T> for io::Result<T> { | ||||
|     fn into_timeout_or(self) -> io::Result<TimeoutOr<T>> { | ||||
|         match self { | ||||
|             Ok(v) => Ok(TimeoutOr::<T>::Value(v)), | ||||
|             Err(e) if e.kind() == io::ErrorKind::TimedOut => Ok(TimeoutOr::<T>::Timeout), | ||||
|             Err(e) => Err(e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub trait TimeoutOrResultExt<T, E> { | ||||
|     fn into_result(self) -> Result<TimeoutOr<T>, E>; | ||||
| } | ||||
|  | ||||
| impl<T, E> TimeoutOrResultExt<T, E> for TimeoutOr<Result<T, E>> { | ||||
|     fn into_result(self) -> Result<TimeoutOr<T>, E> { | ||||
|         match self { | ||||
|             TimeoutOr::<Result<T, E>>::Timeout => Ok(TimeoutOr::<T>::Timeout), | ||||
|             TimeoutOr::<Result<T, E>>::Value(Ok(v)) => Ok(TimeoutOr::<T>::Value(v)), | ||||
|             TimeoutOr::<Result<T, E>>::Value(Err(e)) => Err(e), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////// | ||||
| // Non-fallible timeout | ||||
|  | ||||
| #[must_use] | ||||
| pub enum TimeoutOr<T> { | ||||
|     Timeout, | ||||
|     Value(T), | ||||
| } | ||||
|  | ||||
| impl<T> TimeoutOr<T> { | ||||
|     pub fn timeout() -> Self { | ||||
|         Self::Timeout | ||||
|     } | ||||
|     pub fn value(value: T) -> Self { | ||||
|         Self::Value(value) | ||||
|     } | ||||
|  | ||||
|     pub fn is_timeout(&self) -> bool { | ||||
|         matches!(self, Self::Timeout) | ||||
|     } | ||||
|  | ||||
|     pub fn is_value(&self) -> bool { | ||||
|         matches!(self, Self::Value(_)) | ||||
|     } | ||||
|     pub fn map<X, F: Fn(T) -> X>(self, f: F) -> TimeoutOr<X> { | ||||
|         match self { | ||||
|             Self::Timeout => TimeoutOr::<X>::Timeout, | ||||
|             Self::Value(v) => TimeoutOr::<X>::Value(f(v)), | ||||
|         } | ||||
|     } | ||||
|     pub fn on_timeout<F: Fn()>(self, f: F) -> Self { | ||||
|         match self { | ||||
|             Self::Timeout => { | ||||
|                 f(); | ||||
|                 Self::Timeout | ||||
|             } | ||||
|             Self::Value(v) => Self::Value(v), | ||||
|         } | ||||
|     } | ||||
|     pub fn into_timeout_error(self) -> Result<T, TimeoutError> { | ||||
|         match self { | ||||
|             Self::Timeout => Err(TimeoutError {}), | ||||
|             Self::Value(v) => Ok(v), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn into_option(self) -> Option<T> { | ||||
|         match self { | ||||
|             Self::Timeout => None, | ||||
|             Self::Value(v) => Some(v), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> From<TimeoutOr<T>> for Option<T> { | ||||
|     fn from(t: TimeoutOr<T>) -> Self { | ||||
|         match t { | ||||
|             TimeoutOr::<T>::Timeout => None, | ||||
|             TimeoutOr::<T>::Value(v) => Some(v), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: Clone> Clone for TimeoutOr<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         match self { | ||||
|             Self::Timeout => Self::Timeout, | ||||
|             Self::Value(t) => Self::Value(t.clone()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl<T: Debug> Debug for TimeoutOr<T> { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         match self { | ||||
|             Self::Timeout => write!(f, "Timeout"), | ||||
|             Self::Value(arg0) => f.debug_tuple("Value").field(arg0).finish(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl<T: Display> Display for TimeoutOr<T> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         match self { | ||||
|             Self::Timeout => write!(f, ""), | ||||
|             Self::Value(arg0) => write!(f, "{}", arg0), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl<T: Debug + Display> Error for TimeoutOr<T> {} | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////// | ||||
| // Non-fallible timeoue macros | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! timeout_or_try { | ||||
|     ($r: expr) => { | ||||
|         match $r { | ||||
|             TimeoutOr::Timeout => return Ok(TimeoutOr::Timeout), | ||||
|             TimeoutOr::Value(v) => v, | ||||
|         } | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										25
									
								
								veilid-tools/src/timestamp.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								veilid-tools/src/timestamp.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| use super::*; | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|         use js_sys::Date; | ||||
|  | ||||
|         pub fn get_timestamp() -> u64 { | ||||
|             if utils::is_browser() { | ||||
|                 return (Date::now() * 1000.0f64) as u64; | ||||
|             } else { | ||||
|                 panic!("WASM requires browser environment"); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         use std::time::{SystemTime, UNIX_EPOCH}; | ||||
|  | ||||
|         pub fn get_timestamp() -> u64 { | ||||
|             match SystemTime::now().duration_since(UNIX_EPOCH) { | ||||
|                 Ok(n) => n.as_micros() as u64, | ||||
|                 Err(_) => panic!("SystemTime before UNIX_EPOCH!"), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										301
									
								
								veilid-tools/src/tools.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								veilid-tools/src/tools.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,301 @@ | ||||
| use super::*; | ||||
|  | ||||
| use alloc::string::ToString; | ||||
| use std::io; | ||||
| use std::path::Path; | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! assert_err { | ||||
|     ($ex:expr) => { | ||||
|         if let Ok(v) = $ex { | ||||
|             panic!("assertion failed, expected Err(..), got {:?}", v); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! io_error_other { | ||||
|     ($msg:expr) => { | ||||
|         io::Error::new(io::ErrorKind::Other, $msg.to_string()) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| pub fn to_io_error_other<E: std::error::Error + Send + Sync + 'static>(x: E) -> io::Error { | ||||
|     io::Error::new(io::ErrorKind::Other, x) | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! bail_io_error_other { | ||||
|     ($msg:expr) => { | ||||
|         return io::Result::Err(io::Error::new(io::ErrorKind::Other, $msg.to_string())) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| pub fn system_boxed<'a, Out>( | ||||
|     future: impl Future<Output = Out> + Send + 'a, | ||||
| ) -> SendPinBoxFutureLifetime<'a, Out> { | ||||
|     Box::pin(future) | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| cfg_if! { | ||||
|     if #[cfg(target_arch = "wasm32")] { | ||||
|  | ||||
|         // xxx: for now until wasm threads are more stable, and/or we bother with web workers | ||||
|         pub fn get_concurrency() -> u32 { | ||||
|             1 | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|  | ||||
|         pub fn get_concurrency() -> u32 { | ||||
|             std::thread::available_parallelism() | ||||
|                 .map(|x| x.get()) | ||||
|                 .unwrap_or_else(|e| { | ||||
|                     warn!("unable to get concurrency defaulting to single core: {}", e); | ||||
|                     1 | ||||
|                 }) as u32 | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| pub fn split_port(name: &str) -> EyreResult<(String, Option<u16>)> { | ||||
|     if let Some(split) = name.rfind(':') { | ||||
|         let hoststr = &name[0..split]; | ||||
|         let portstr = &name[split + 1..]; | ||||
|         let port: u16 = portstr.parse::<u16>().wrap_err("invalid port")?; | ||||
|  | ||||
|         Ok((hoststr.to_string(), Some(port))) | ||||
|     } else { | ||||
|         Ok((name.to_string(), None)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn prepend_slash(s: String) -> String { | ||||
|     if s.starts_with('/') { | ||||
|         return s; | ||||
|     } | ||||
|     let mut out = "/".to_owned(); | ||||
|     out.push_str(s.as_str()); | ||||
|     out | ||||
| } | ||||
|  | ||||
| pub fn timestamp_to_secs(ts: u64) -> f64 { | ||||
|     ts as f64 / 1000000.0f64 | ||||
| } | ||||
|  | ||||
| pub fn secs_to_timestamp(secs: f64) -> u64 { | ||||
|     (secs * 1000000.0f64) as u64 | ||||
| } | ||||
|  | ||||
| pub fn ms_to_us(ms: u32) -> u64 { | ||||
|     (ms as u64) * 1000u64 | ||||
| } | ||||
|  | ||||
| // Calculate retry attempt with logarhythmic falloff | ||||
| pub fn retry_falloff_log( | ||||
|     last_us: u64, | ||||
|     cur_us: u64, | ||||
|     interval_start_us: u64, | ||||
|     interval_max_us: u64, | ||||
|     interval_multiplier_us: f64, | ||||
| ) -> bool { | ||||
|     // | ||||
|     if cur_us < interval_start_us { | ||||
|         // Don't require a retry within the first 'interval_start_us' microseconds of the reliable time period | ||||
|         false | ||||
|     } else if cur_us >= last_us + interval_max_us { | ||||
|         // Retry at least every 'interval_max_us' microseconds | ||||
|         true | ||||
|     } else { | ||||
|         // Exponential falloff between 'interval_start_us' and 'interval_max_us' microseconds | ||||
|         last_us <= secs_to_timestamp(timestamp_to_secs(cur_us) / interval_multiplier_us) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn try_at_most_n_things<T, I, C, R>(max: usize, things: I, closure: C) -> Option<R> | ||||
| where | ||||
|     I: IntoIterator<Item = T>, | ||||
|     C: Fn(T) -> Option<R>, | ||||
| { | ||||
|     let mut fails = 0usize; | ||||
|     for thing in things.into_iter() { | ||||
|         if let Some(r) = closure(thing) { | ||||
|             return Some(r); | ||||
|         } | ||||
|         fails += 1; | ||||
|         if fails >= max { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     None | ||||
| } | ||||
|  | ||||
| pub async fn async_try_at_most_n_things<T, I, C, R, F>( | ||||
|     max: usize, | ||||
|     things: I, | ||||
|     closure: C, | ||||
| ) -> Option<R> | ||||
| where | ||||
|     I: IntoIterator<Item = T>, | ||||
|     C: Fn(T) -> F, | ||||
|     F: Future<Output = Option<R>>, | ||||
| { | ||||
|     let mut fails = 0usize; | ||||
|     for thing in things.into_iter() { | ||||
|         if let Some(r) = closure(thing).await { | ||||
|             return Some(r); | ||||
|         } | ||||
|         fails += 1; | ||||
|         if fails >= max { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     None | ||||
| } | ||||
|  | ||||
| pub trait CmpAssign { | ||||
|     fn min_assign(&mut self, other: Self); | ||||
|     fn max_assign(&mut self, other: Self); | ||||
| } | ||||
|  | ||||
| impl<T> CmpAssign for T | ||||
| where | ||||
|     T: core::cmp::Ord, | ||||
| { | ||||
|     fn min_assign(&mut self, other: Self) { | ||||
|         if &other < self { | ||||
|             *self = other; | ||||
|         } | ||||
|     } | ||||
|     fn max_assign(&mut self, other: Self) { | ||||
|         if &other > self { | ||||
|             *self = other; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn compatible_unspecified_socket_addr(socket_addr: &SocketAddr) -> SocketAddr { | ||||
|     match socket_addr { | ||||
|         SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), | ||||
|         SocketAddr::V6(_) => SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn listen_address_to_socket_addrs(listen_address: &str) -> EyreResult<Vec<SocketAddr>> { | ||||
|     // If no address is specified, but the port is, use ipv4 and ipv6 unspecified | ||||
|     // If the address is specified, only use the specified port and fail otherwise | ||||
|     let ip_addrs = vec![ | ||||
|         IpAddr::V4(Ipv4Addr::UNSPECIFIED), | ||||
|         IpAddr::V6(Ipv6Addr::UNSPECIFIED), | ||||
|     ]; | ||||
|  | ||||
|     Ok(if let Some(portstr) = listen_address.strip_prefix(':') { | ||||
|         let port = portstr | ||||
|             .parse::<u16>() | ||||
|             .wrap_err("Invalid port format in udp listen address")?; | ||||
|         ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect() | ||||
|     } else if let Ok(port) = listen_address.parse::<u16>() { | ||||
|         ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect() | ||||
|     } else { | ||||
|         cfg_if! { | ||||
|             if #[cfg(target_arch = "wasm32")] { | ||||
|                 use core::str::FromStr; | ||||
|                 vec![SocketAddr::from_str(listen_address).map_err(|e| io_error_other!(e)).wrap_err("Unable to parse address")?] | ||||
|             } else { | ||||
|                 listen_address | ||||
|                     .to_socket_addrs() | ||||
|                     .wrap_err("Unable to resolve address")? | ||||
|                     .collect() | ||||
|             } | ||||
|         } | ||||
|     }) | ||||
| } | ||||
|  | ||||
| pub trait Dedup<T: PartialEq + Clone> { | ||||
|     fn remove_duplicates(&mut self); | ||||
| } | ||||
|  | ||||
| impl<T: PartialEq + Clone> Dedup<T> for Vec<T> { | ||||
|     fn remove_duplicates(&mut self) { | ||||
|         let mut already_seen = Vec::new(); | ||||
|         self.retain(|item| match already_seen.contains(item) { | ||||
|             true => false, | ||||
|             _ => { | ||||
|                 already_seen.push(item.clone()); | ||||
|                 true | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| cfg_if::cfg_if! { | ||||
|     if #[cfg(unix)] { | ||||
|         use std::os::unix::fs::MetadataExt; | ||||
|         use std::os::unix::prelude::PermissionsExt; | ||||
|         use nix::unistd::{Uid, Gid}; | ||||
|  | ||||
|         pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> EyreResult<()> | ||||
|         { | ||||
|             let path = path.as_ref(); | ||||
|             if !path.exists() { | ||||
|                 return Ok(()); | ||||
|             } | ||||
|  | ||||
|             let uid = Uid::effective(); | ||||
|             let gid = Gid::effective(); | ||||
|             let meta = std::fs::metadata(path).wrap_err("unable to get metadata for path")?; | ||||
|  | ||||
|             if meta.mode() != 0o600 { | ||||
|                 std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).wrap_err("unable to set correct permissions on path")?; | ||||
|             } | ||||
|             if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() { | ||||
|                 bail!("path has incorrect owner/group"); | ||||
|             } | ||||
|             Ok(()) | ||||
|         } | ||||
|     } else if #[cfg(windows)] { | ||||
|         //use std::os::windows::fs::MetadataExt; | ||||
|         //use windows_permissions::*; | ||||
|  | ||||
|         pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> EyreResult<()> | ||||
|         { | ||||
|             let path = path.as_ref(); | ||||
|             if !path.exists() { | ||||
|                 return Ok(()); | ||||
|             } | ||||
|  | ||||
|             Ok(()) | ||||
|         } | ||||
|     } else { | ||||
|         pub fn ensure_file_private_owner<P:AsRef<Path>>(_path: P) -> Result<(),String> | ||||
|         { | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[repr(C, align(8))] | ||||
| struct AlignToEight([u8; 8]); | ||||
|  | ||||
| pub unsafe fn aligned_8_u8_vec_uninit(n_bytes: usize) -> Vec<u8> { | ||||
|     let n_units = (n_bytes + mem::size_of::<AlignToEight>() - 1) / mem::size_of::<AlignToEight>(); | ||||
|     let mut aligned: Vec<AlignToEight> = Vec::with_capacity(n_units); | ||||
|     let ptr = aligned.as_mut_ptr(); | ||||
|     let cap_units = aligned.capacity(); | ||||
|     mem::forget(aligned); | ||||
|  | ||||
|     Vec::from_raw_parts( | ||||
|         ptr as *mut u8, | ||||
|         n_bytes, | ||||
|         cap_units * mem::size_of::<AlignToEight>(), | ||||
|     ) | ||||
| } | ||||
							
								
								
									
										52
									
								
								veilid-tools/src/wasm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								veilid-tools/src/wasm.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| use super::*; | ||||
| use core::sync::atomic::{AtomicI8, Ordering}; | ||||
| use js_sys::{global, Reflect}; | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| extern "C" { | ||||
|     // Use `js_namespace` here to bind `console.log(..)` instead of just | ||||
|     // `log(..)` | ||||
|     #[wasm_bindgen(js_namespace = console, js_name = log)] | ||||
|     pub fn console_log(s: &str); | ||||
|  | ||||
|     #[wasm_bindgen] | ||||
|     pub fn alert(s: &str); | ||||
| } | ||||
|  | ||||
| pub fn is_browser() -> bool { | ||||
|     static CACHE: AtomicI8 = AtomicI8::new(-1); | ||||
|     let cache = CACHE.load(Ordering::Relaxed); | ||||
|     if cache != -1 { | ||||
|         return cache != 0; | ||||
|     } | ||||
|  | ||||
|     let res = Reflect::has(&global().as_ref(), &"window".into()).unwrap_or_default(); | ||||
|  | ||||
|     CACHE.store(res as i8, Ordering::Relaxed); | ||||
|  | ||||
|     res | ||||
| } | ||||
|  | ||||
| // pub fn is_browser_https() -> bool { | ||||
| //     static CACHE: AtomicI8 = AtomicI8::new(-1); | ||||
| //     let cache = CACHE.load(Ordering::Relaxed); | ||||
| //     if cache != -1 { | ||||
| //         return cache != 0; | ||||
| //     } | ||||
|  | ||||
| //     let res = js_sys::eval("window.location.protocol === 'https'") | ||||
| //         .map(|res| res.is_truthy()) | ||||
| //         .unwrap_or_default(); | ||||
|  | ||||
| //     CACHE.store(res as i8, Ordering::Relaxed); | ||||
|  | ||||
| //     res | ||||
| // } | ||||
|  | ||||
| #[derive(ThisError, Debug, Clone, Eq, PartialEq)] | ||||
| #[error("JsValue error")] | ||||
| pub struct JsValueError(String); | ||||
|  | ||||
| pub fn map_jsvalue_error(x: JsValue) -> JsValueError { | ||||
|     JsValueError(x.as_string().unwrap_or_default()) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user