diff --git a/.lock b/.lock new file mode 100644 index 00000000..e69de29b diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/crates.js b/crates.js new file mode 100644 index 00000000..c8d6fe08 --- /dev/null +++ b/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["karyon_core","karyon_jsonrpc","karyon_net","karyon_p2p"]; \ No newline at end of file diff --git a/help.html b/help.html new file mode 100644 index 00000000..fd5dfa81 --- /dev/null +++ b/help.html @@ -0,0 +1,2 @@ +
pub struct Backoff {
+ base_delay: u64,
+ max_delay: u64,
+ retries: AtomicU32,
+ stop: AtomicBool,
+}
Exponential backoff +https://en.wikipedia.org/wiki/Exponential_backoff
+use karyon_core::async_util::Backoff;
+
+ async {
+ let backoff = Backoff::new(300, 3000);
+
+ loop {
+ backoff.sleep().await;
+
+ // do something
+ break;
+ }
+
+ backoff.reset();
+
+ // ....
+ };
+
base_delay: u64
The base delay in milliseconds for the initial retry.
+max_delay: u64
The max delay in milliseconds allowed for a retry.
+retries: AtomicU32
Atomic counter
+stop: AtomicBool
Stop flag
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CondVar {
+ inner: Mutex<Wakers>,
+}
CondVar is an async version of https://doc.rust-lang.org/std/sync/struct.Condvar.html
+ use std::sync::Arc;
+
+ use smol::lock::Mutex;
+
+ use karyon_core::async_util::CondVar;
+
+ async {
+
+ let val = Arc::new(Mutex::new(false));
+ let condvar = Arc::new(CondVar::new());
+
+ let val_cloned = val.clone();
+ let condvar_cloned = condvar.clone();
+ smol::spawn(async move {
+ let mut val = val_cloned.lock().await;
+
+ // While the boolean flag is false, wait for a signal.
+ while !*val {
+ val = condvar_cloned.wait(val).await;
+ }
+
+ // ...
+ });
+
+ let condvar_cloned = condvar.clone();
+ smol::spawn(async move {
+ let mut val = val.lock().await;
+
+ // While the boolean flag is false, wait for a signal.
+ while !*val {
+ val = condvar_cloned.wait(val).await;
+ }
+
+ // ...
+ });
+
+ // Wake up all waiting tasks on this condvar
+ condvar.broadcast();
+ };
+
inner: Mutex<Wakers>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morestruct CondVarAwait<'a, T> {
+ id: Option<u16>,
+ condvar: &'a CondVar,
+ guard: Option<MutexGuard<'a, T>>,
+}
id: Option<u16>
§condvar: &'a CondVar
§guard: Option<MutexGuard<'a, T>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morestruct Wakers {
+ wakers: HashMap<u16, Option<Waker>>,
+}
Wakers is a helper struct to store the task wakers
+wakers: HashMap<u16, Option<Waker>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CondWait {
+ condvar: CondVar,
+ w: Mutex<bool>,
+}
CondWait is a wrapper struct for CondVar with a Mutex boolean flag.
+ use std::sync::Arc;
+
+ use karyon_core::async_util::CondWait;
+
+ async {
+ let cond_wait = Arc::new(CondWait::new());
+ let cond_wait_cloned = cond_wait.clone();
+ let task = smol::spawn(async move {
+ cond_wait_cloned.wait().await;
+ // ...
+ });
+
+ cond_wait.signal().await;
+ };
+
condvar: CondVar
The CondVar
+w: Mutex<bool>
Boolean flag
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Either<T1, T2> {
+ Left(T1),
+ Right(T2),
+}
The return value from the select
function, indicating which future
+completed first.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum TaskResult<T> {
+ Completed(T),
+ Cancelled,
+}
The result of a spawned task.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn select<T1, T2, F1, F2>(future1: F1, future2: F2) -> Select<F1, F2> ⓘ
Returns the result of the future that completes first, preferring future1 +if both are ready.
+use std::future;
+
+use karyon_core::async_util::{select, Either};
+
+ async {
+ let fut1 = future::pending::<String>();
+ let fut2 = future::ready(0);
+ let res = select(fut1, fut2).await;
+ assert!(matches!(res, Either::Right(0)));
+ // ....
+ };
+
pub async fn timeout<T, F>(delay: Duration, future1: F) -> Result<T>where
+ F: Future<Output = T>,
Waits for a future to complete or times out if it exceeds a specified +duration.
+use std::{future, time::Duration};
+
+use karyon_core::async_util::timeout;
+
+async {
+ let fut = future::pending::<()>();
+ assert!(timeout(Duration::from_millis(100), fut).await.is_err());
+};
+
A module containing async utilities that work with the
+smol
async runtime.
select
function, indicating which future
+completed first.pub enum Either<T1, T2> {
+ Left(T1),
+ Right(T2),
+}
The return value from the select
function, indicating which future
+completed first.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn select<T1, T2, F1, F2>(future1: F1, future2: F2) -> Select<F1, F2> ⓘ
Returns the result of the future that completes first, preferring future1 +if both are ready.
+use std::future;
+
+use karyon_core::async_util::{select, Either};
+
+ async {
+ let fut1 = future::pending::<String>();
+ let fut2 = future::ready(0);
+ let res = select(fut1, fut2).await;
+ assert!(matches!(res, Either::Right(0)));
+ // ....
+ };
+
pub struct Select<F1, F2> {
+ future1: F1,
+ future2: F2,
+}
future1: F1
§future2: F2
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Backoff {
+ base_delay: u64,
+ max_delay: u64,
+ retries: AtomicU32,
+ stop: AtomicBool,
+}
Exponential backoff +https://en.wikipedia.org/wiki/Exponential_backoff
+use karyon_core::async_util::Backoff;
+
+ async {
+ let backoff = Backoff::new(300, 3000);
+
+ loop {
+ backoff.sleep().await;
+
+ // do something
+ break;
+ }
+
+ backoff.reset();
+
+ // ....
+ };
+
base_delay: u64
The base delay in milliseconds for the initial retry.
+max_delay: u64
The max delay in milliseconds allowed for a retry.
+retries: AtomicU32
Atomic counter
+stop: AtomicBool
Stop flag
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CondVar {
+ inner: Mutex<Wakers>,
+}
CondVar is an async version of https://doc.rust-lang.org/std/sync/struct.Condvar.html
+ use std::sync::Arc;
+
+ use smol::lock::Mutex;
+
+ use karyon_core::async_util::CondVar;
+
+ async {
+
+ let val = Arc::new(Mutex::new(false));
+ let condvar = Arc::new(CondVar::new());
+
+ let val_cloned = val.clone();
+ let condvar_cloned = condvar.clone();
+ smol::spawn(async move {
+ let mut val = val_cloned.lock().await;
+
+ // While the boolean flag is false, wait for a signal.
+ while !*val {
+ val = condvar_cloned.wait(val).await;
+ }
+
+ // ...
+ });
+
+ let condvar_cloned = condvar.clone();
+ smol::spawn(async move {
+ let mut val = val.lock().await;
+
+ // While the boolean flag is false, wait for a signal.
+ while !*val {
+ val = condvar_cloned.wait(val).await;
+ }
+
+ // ...
+ });
+
+ // Wake up all waiting tasks on this condvar
+ condvar.broadcast();
+ };
+
inner: Mutex<Wakers>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CondWait {
+ condvar: CondVar,
+ w: Mutex<bool>,
+}
CondWait is a wrapper struct for CondVar with a Mutex boolean flag.
+ use std::sync::Arc;
+
+ use karyon_core::async_util::CondWait;
+
+ async {
+ let cond_wait = Arc::new(CondWait::new());
+ let cond_wait_cloned = cond_wait.clone();
+ let task = smol::spawn(async move {
+ cond_wait_cloned.wait().await;
+ // ...
+ });
+
+ cond_wait.signal().await;
+ };
+
condvar: CondVar
The CondVar
+w: Mutex<bool>
Boolean flag
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TaskGroup<'a> {
+ tasks: Mutex<Vec<TaskHandler>>,
+ stop_signal: Arc<CondWait>,
+ executor: Executor<'a>,
+}
TaskGroup is a group of spawned tasks.
+
+use std::sync::Arc;
+
+use karyon_core::async_util::TaskGroup;
+
+async {
+
+ let ex = Arc::new(smol::Executor::new());
+ let group = TaskGroup::new(ex);
+
+ group.spawn(smol::Timer::never(), |_| async {});
+
+ group.cancel().await;
+
+};
+
tasks: Mutex<Vec<TaskHandler>>
§stop_signal: Arc<CondWait>
§executor: Executor<'a>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum TaskResult<T> {
+ Completed(T),
+ Cancelled,
+}
The result of a spawned task.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TaskGroup<'a> {
+ tasks: Mutex<Vec<TaskHandler>>,
+ stop_signal: Arc<CondWait>,
+ executor: Executor<'a>,
+}
TaskGroup is a group of spawned tasks.
+
+use std::sync::Arc;
+
+use karyon_core::async_util::TaskGroup;
+
+async {
+
+ let ex = Arc::new(smol::Executor::new());
+ let group = TaskGroup::new(ex);
+
+ group.spawn(smol::Timer::never(), |_| async {});
+
+ group.cancel().await;
+
+};
+
tasks: Mutex<Vec<TaskHandler>>
§stop_signal: Arc<CondWait>
§executor: Executor<'a>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TaskHandler {
+ task: FallibleTask<()>,
+ cancel_flag: Arc<CondWait>,
+}
TaskHandler
+task: FallibleTask<()>
§cancel_flag: Arc<CondWait>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub async fn timeout<T, F>(delay: Duration, future1: F) -> Result<T>where
+ F: Future<Output = T>,
Waits for a future to complete or times out if it exceeds a specified +duration.
+use std::{future, time::Duration};
+
+use karyon_core::async_util::timeout;
+
+async {
+ let fut = future::pending::<()>();
+ assert!(timeout(Duration::from_millis(100), fut).await.is_err());
+};
+
pub enum KeyPair {
+ Ed25519(Ed25519KeyPair),
+}
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum KeyPairType {
+ Ed25519,
+}
key cryptography type
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum PublicKey {
+ Ed25519(Ed25519PublicKey),
+}
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum KeyPair {
+ Ed25519(Ed25519KeyPair),
+}
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum KeyPairType {
+ Ed25519,
+}
key cryptography type
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum PublicKey {
+ Ed25519(Ed25519PublicKey),
+}
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Ed25519KeyPair(SigningKey);
0: SigningKey
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Ed25519PublicKey(VerifyingKey);
0: VerifyingKey
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct SecretKey(Vec<u8>);
A Secret key
+0: Vec<u8>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moretrait KeyPairExt {
+ // Required methods
+ fn sign(&self, msg: &[u8]) -> Vec<u8>;
+ fn public(&self) -> PublicKey;
+ fn secret(&self) -> SecretKey;
+}
An extension trait, adding essential methods to all KeyPair
types.
trait PublicKeyExt {
+ // Required methods
+ fn as_bytes(&self) -> &[u8] ⓘ;
+ fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<()>;
+}
An extension trait, adding essential methods to all PublicKey
types.
pub struct SecretKey(Vec<u8>);
A Secret key
+0: Vec<u8>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Error {
+ IO(Error),
+ TryInto(&'static str),
+ Timeout,
+ PathNotFound(&'static str),
+ Ed25519(Error),
+ ChannelSend(String),
+ ChannelRecv(RecvError),
+ BincodeDecode(DecodeError),
+ BincodeEncode(EncodeError),
+}
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreRepresents karyon’s Core Error.
+event::EventSys
implementation.
EventSys
.EventSys
.pub struct Event {
+ created_at: DateTime<Utc>,
+ value: Arc<dyn EventValueAny>,
+}
An event within the EventSys
.
created_at: DateTime<Utc>
The time at which the event was created.
+value: Arc<dyn EventValueAny>
The value of the Event.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct EventListener<T, E> {
+ id: EventListenerID,
+ recv_chan: Receiver<Event>,
+ event_sys: WeakEventSys<T>,
+ event_id: String,
+ topic: T,
+ phantom: PhantomData<E>,
+}
EventListener listens for and receives events from the EventSys
.
id: EventListenerID
§recv_chan: Receiver<Event>
§event_sys: WeakEventSys<T>
§event_id: String
§topic: T
§phantom: PhantomData<E>
Create a new event listener.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct EventSys<T> {
+ listeners: Mutex<HashMap<T, HashMap<String, HashMap<EventListenerID, Sender<Event>>>>>,
+}
EventSys supports event emission to registered listeners based on topics.
+use karyon_core::event::{EventSys, EventValueTopic, EventValue};
+
+ async {
+ let event_sys = EventSys::new();
+
+ #[derive(Hash, PartialEq, Eq, Debug, Clone)]
+ enum Topic {
+ TopicA,
+ TopicB,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct A(usize);
+
+ impl EventValue for A {
+ fn id() -> &'static str {
+ "A"
+ }
+ }
+
+ let listener = event_sys.register::<A>(&Topic::TopicA).await;
+
+ event_sys.emit_by_topic(&Topic::TopicA, &A(3)) .await;
+ let msg: A = listener.recv().await.unwrap();
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct B(usize);
+
+ impl EventValue for B {
+ fn id() -> &'static str {
+ "B"
+ }
+ }
+
+ impl EventValueTopic for B {
+ type Topic = Topic;
+ fn topic() -> Self::Topic{
+ Topic::TopicB
+ }
+ }
+
+ let listener = event_sys.register::<B>(&Topic::TopicB).await;
+
+ event_sys.emit(&B(3)) .await;
+ let msg: B = listener.recv().await.unwrap();
+
+ // ....
+ };
+
listeners: Mutex<HashMap<T, HashMap<String, HashMap<EventListenerID, Sender<Event>>>>>
Creates a new EventSys
Emits an event to the listeners.
+The event must implement the EventValueTopic
trait to indicate the
+topic of the event. Otherwise, you can use emit_by_topic()
.
Emits an event to the listeners.
+Registers a new event listener for the given topic.
+Removes the event listener attached to the given topic.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait EventValue: EventValueAny {
+ // Required method
+ fn id() -> &'static str
+ where Self: Sized;
+}
pub trait EventValueAny: Any + Send + Sync + Debug {
+ // Required method
+ fn value_as_any(&self) -> &dyn Any;
+}
pub trait EventValueTopic: EventValueAny + EventValue {
+ type Topic;
+
+ // Required method
+ fn topic() -> Self::Topic
+ where Self: Sized;
+}
pub type ArcEventSys<T> = Arc<EventSys<T>>;
struct ArcEventSys<T> {
+ ptr: NonNull<ArcInner<EventSys<T>>>,
+ phantom: PhantomData<ArcInner<EventSys<T>>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<EventSys<T>>>
§phantom: PhantomData<ArcInner<EventSys<T>>>
§alloc: Global
pub type EventListenerID = u16;
type Listeners<T> = HashMap<T, HashMap<String, HashMap<EventListenerID, Sender<Event>>>>;
struct Listeners<T> {
+ base: HashMap<T, HashMap<String, HashMap<u16, Sender<Event>>>, RandomState>,
+}
base: HashMap<T, HashMap<String, HashMap<u16, Sender<Event>>>, RandomState>
smol
async runtime.event::EventSys
implementation.Read More
A simple publish-subscribe system Read More
pub struct Publisher<T> {
+ subs: Mutex<HashMap<SubscriptionID, Sender<T>>>,
+}
A simple publish-subscribe system.
+ +use karyon_core::pubsub::{Publisher};
+
+ async {
+ let publisher = Publisher::new();
+
+ let sub = publisher.subscribe().await;
+
+ publisher.notify(&String::from("MESSAGE")).await;
+
+ let msg = sub.recv().await;
+
+ // ....
+ };
+
subs: Mutex<HashMap<SubscriptionID, Sender<T>>>
Creates a new Publisher
+Subscribe and return a Subscription
+Unsubscribe from the Publisher
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Subscription<T> {
+ id: SubscriptionID,
+ recv_chan: Receiver<T>,
+ publisher: ArcPublisher<T>,
+}
id: SubscriptionID
§recv_chan: Receiver<T>
§publisher: ArcPublisher<T>
Creates a new Subscription
+Unsubscribe from the Publisher
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type ArcPublisher<T> = Arc<Publisher<T>>;
struct ArcPublisher<T> {
+ ptr: NonNull<ArcInner<Publisher<T>>>,
+ phantom: PhantomData<ArcInner<Publisher<T>>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<Publisher<T>>>
§phantom: PhantomData<ArcInner<Publisher<T>>>
§alloc: Global
pub type SubscriptionID = u16;
pub type Executor<'a> = Arc<Executor<'a>>;
A pointer to an Executor
+struct Executor<'a> {
+ ptr: NonNull<ArcInner<Executor<'a>>>,
+ phantom: PhantomData<ArcInner<Executor<'a>>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<Executor<'a>>>
§phantom: PhantomData<ArcInner<Executor<'a>>>
§alloc: Global
pub type GlobalExecutor = Arc<Executor<'static>>;
A Global Executor
+struct GlobalExecutor {
+ ptr: NonNull<ArcInner<Executor<'static>>>,
+ phantom: PhantomData<ArcInner<Executor<'static>>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<Executor<'static>>>
§phantom: PhantomData<ArcInner<Executor<'static>>>
§alloc: Global
pub fn encode_into_slice<T: Encode>(msg: &T, dst: &mut [u8]) -> Result<()>
Encode the given type T
into the given slice..
T
into a Vec<u8>
.T
into the given slice..pub fn encode_into_slice<T: Encode>(msg: &T, dst: &mut [u8]) -> Result<()>
Encode the given type T
into the given slice..
pub fn random_16() -> u16
Generates and returns a random u16 using rand::rngs::OsRng
.
pub fn random_32() -> u32
Generates and returns a random u32 using rand::rngs::OsRng
.
pub fn tilde_expand(path: &str) -> Result<PathBuf>
Expands a tilde (~) in a path and returns the expanded PathBuf
.
A set of helper tools and functions.
+T
from the given slice. returns the decoded value
+along with the number of bytes read.T
into a Vec<u8>
.T
into the given slice..PathBuf
.rand::rngs::OsRng
.rand::rngs::OsRng
.PathBuf
.pub fn tilde_expand(path: &str) -> Result<PathBuf>
Expands a tilde (~) in a path and returns the expanded PathBuf
.
PathBuf
.PathBuf
.pub struct Client {
+ codec: Codec,
+ config: ClientConfig,
+}
Represents an RPC client
+codec: Codec
§config: ClientConfig
Creates a new RPC client by passing a Tcp, Unix, or Tls connection.
+Calls the provided method, waits for the response, and returns the result.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ClientConfig {
+ pub timeout: Option<u64>,
+}
Represents client config
+timeout: Option<u64>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreconst DEFAULT_BUFFER_SIZE: usize = 1024;
const DEFAULT_MAX_ALLOWED_BUFFER_SIZE: usize = _; // 1_048_576usize
pub struct Codec {
+ conn: Conn,
+ config: CodecConfig,
+}
conn: Conn
§config: CodecConfig
Creates a new Codec
+Read all bytes into buffer
until the 0x0A
byte or EOF is
+reached.
If successful, this function will return the total number of bytes read.
+Writes an entire buffer into the given connection.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CodecConfig {
+ pub default_buffer_size: usize,
+ pub max_allowed_buffer_size: usize,
+}
Represents Codec config
+default_buffer_size: usize
§max_allowed_buffer_size: usize
The maximum allowed buffer size to receive a message. If set to zero, +there will be no size limit.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub(crate) const JSONRPC_VERSION: &str = "2.0";
pub enum Endpoint {
+ Udp(Addr, u16),
+ Tcp(Addr, u16),
+ Tls(Addr, u16),
+ Ws(Addr, u16),
+ Unix(String),
+}
Endpoint defines generic network endpoints for karyon.
+use std::net::SocketAddr;
+
+use karyon_net::Endpoint;
+
+let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+let socketaddr: SocketAddr = "127.0.0.1:3000".parse().unwrap();
+let endpoint = Endpoint::new_udp_addr(&socketaddr);
+
Creates a new TCP endpoint from a SocketAddr
.
Creates a new UDP endpoint from a SocketAddr
.
Creates a new TLS endpoint from a SocketAddr
.
Creates a new WS endpoint from a SocketAddr
.
Creates a new Unix endpoint from a UnixSocketAddress
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum JsonRPCError {
+ IO(Error),
+ CallError(i32, String),
+ RPCMethodError(i32, &'static str),
+ InvalidParams(&'static str),
+ InvalidRequest(&'static str),
+ ParseJSON(Error),
+ InvalidMsg(&'static str),
+ KaryonCore(Error),
+ KaryonNet(NetError),
+}
Represents karyon’s jsonrpc Error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Error {
+ IO(Error),
+ CallError(i32, String),
+ RPCMethodError(i32, &'static str),
+ InvalidParams(&'static str),
+ InvalidRequest(&'static str),
+ ParseJSON(Error),
+ InvalidMsg(&'static str),
+ KaryonCore(Error),
+ KaryonNet(NetError),
+}
Represents karyon’s jsonrpc Error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreA fast and lightweight async implementation of JSON-RPC +2.0, supporting the Tcp and Unix protocols.
+use std::sync::Arc;
+
+use serde_json::Value;
+use smol::net::{TcpStream, TcpListener};
+
+use karyon_jsonrpc::{JsonRPCError, Server, Client, register_service, ServerConfig, ClientConfig};
+
+struct HelloWorld {}
+
+impl HelloWorld {
+ async fn say_hello(&self, params: Value) -> Result<Value, JsonRPCError> {
+ let msg: String = serde_json::from_value(params)?;
+ Ok(serde_json::json!(format!("Hello {msg}!")))
+ }
+
+ async fn foo(&self, params: Value) -> Result<Value, JsonRPCError> {
+ Ok(serde_json::json!("foo!"))
+ }
+
+ async fn bar(&self, params: Value) -> Result<Value, JsonRPCError> {
+ Ok(serde_json::json!("bar!"))
+ }
+}
+
+// Server
+async {
+ let ex = Arc::new(smol::Executor::new());
+
+ // Creates a new server
+ let listener = TcpListener::bind("127.0.0.1:60000").await.unwrap();
+ let config = ServerConfig::default();
+ let server = Server::new(listener, config, ex.clone());
+
+ // Register the HelloWorld service
+ register_service!(HelloWorld, say_hello, foo, bar);
+ server.attach_service(HelloWorld{});
+
+ // Starts the server
+ ex.run(server.start());
+};
+
+// Client
+async {
+
+ // Creates a new client
+ let conn = TcpStream::connect("127.0.0.1:60000").await.unwrap();
+ let config = ClientConfig::default();
+ let client = Client::new(conn, config);
+
+ let result: String = client.call("HelloWorld.say_hello", "world".to_string()).await.unwrap();
+};
+
RPCService
trait for a provided type.Redirecting to macro.register_service.html...
+ + + \ No newline at end of file diff --git a/karyon_jsonrpc/macro.register_service.html b/karyon_jsonrpc/macro.register_service.html new file mode 100644 index 00000000..832f2d61 --- /dev/null +++ b/karyon_jsonrpc/macro.register_service.html @@ -0,0 +1,24 @@ +macro_rules! register_service { + ($t:ty, $($m:ident),*) => { ... }; +}
Implements the RPCService
trait for a provided type.
use serde_json::Value;
+
+use karyon_jsonrpc::{JsonRPCError, register_service};
+
+struct Hello {}
+
+impl Hello {
+ async fn foo(&self, params: Value) -> Result<Value, JsonRPCError> {
+ Ok(serde_json::json!("foo!"))
+ }
+
+ async fn bar(&self, params: Value) -> Result<Value, JsonRPCError> {
+ Ok(serde_json::json!("bar!"))
+ }
+}
+
+register_service!(Hello, foo, bar);
+
pub const INTERNAL_ERROR_CODE: i32 = -32603;
Internal error: Internal JSON-RPC error.
+pub const INVALID_PARAMS_ERROR_CODE: i32 = -32602;
Invalid params: Invalid method parameter(s).
+pub const INVALID_REQUEST_ERROR_CODE: i32 = -32600;
Invalid request: The JSON sent is not a valid Request object.
+pub const METHOD_NOT_FOUND_ERROR_CODE: i32 = -32601;
Method not found: The method does not exist / is not available.
+pub const PARSE_ERROR_CODE: i32 = -32700;
Parse error: Invalid JSON was received by the server.
+pub struct Error {
+ pub code: i32,
+ pub message: String,
+ pub data: Option<Value>,
+}
code: i32
§message: String
§data: Option<Value>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Notification {
+ pub jsonrpc: String,
+ pub method: String,
+ pub params: Option<Value>,
+}
jsonrpc: String
§method: String
§params: Option<Value>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Request {
+ pub jsonrpc: String,
+ pub method: String,
+ pub params: Value,
+ pub id: Value,
+}
jsonrpc: String
§method: String
§params: Value
§id: Value
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Response {
+ pub jsonrpc: String,
+ pub result: Option<Value>,
+ pub error: Option<Error>,
+ pub id: Option<Value>,
+}
jsonrpc: String
§result: Option<Value>
§error: Option<Error>
§id: Option<Value>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub const FAILED_TO_PARSE_ERROR_MSG: &str = "Failed to parse";
pub const INTERNAL_ERROR_MSG: &str = "Internal error";
pub const INVALID_REQUEST_ERROR_MSG: &str = "Invalid request";
pub const METHOD_NOT_FOUND_ERROR_MSG: &str = "Method not found";
fn pack_err_res(code: i32, msg: &str, id: Option<Value>) -> Response
pub struct Server<'a> {
+ listener: Listener,
+ services: RwLock<HashMap<String, Box<dyn RPCService + 'a>>>,
+ task_group: TaskGroup<'a>,
+ config: ServerConfig,
+}
Represents an RPC server
+listener: Listener
§services: RwLock<HashMap<String, Box<dyn RPCService + 'a>>>
§task_group: TaskGroup<'a>
§config: ServerConfig
Creates a new RPC server by passing a listener. It supports Tcp, Unix, and Tls.
+Returns the local endpoint.
+Attach a new service to the RPC server
+Handles a new connection
+Handles a request
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ServerConfig {
+ codec_config: CodecConfig,
+}
RPC server config
+codec_config: CodecConfig
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait RPCService: Sync + Send {
+ // Required methods
+ fn get_method<'a>(&'a self, name: &'a str) -> Option<RPCMethod<'_>>;
+ fn name(&self) -> String;
+}
Defines the interface for an RPC service.
+pub type RPCMethod<'a> = Box<dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, Error>> + Send + Sync + 'a>> + Send + 'a>;
Represents the RPC method
+struct RPCMethod<'a>(Unique<dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, Error>> + Send + Sync + 'a>> + Send + 'a>, Global);
0: Unique<dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, Error>> + Send + Sync + 'a>> + Send + 'a>
§1: Global
pub struct Client {
+ codec: Codec,
+ config: ClientConfig,
+}
Represents an RPC client
+codec: Codec
§config: ClientConfig
Creates a new RPC client by passing a Tcp, Unix, or Tls connection.
+Calls the provided method, waits for the response, and returns the result.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ClientConfig {
+ pub timeout: Option<u64>,
+}
Represents client config
+timeout: Option<u64>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct CodecConfig {
+ pub default_buffer_size: usize,
+ pub max_allowed_buffer_size: usize,
+}
Represents Codec config
+default_buffer_size: usize
§max_allowed_buffer_size: usize
The maximum allowed buffer size to receive a message. If set to zero, +there will be no size limit.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Server<'a> {
+ listener: Listener,
+ services: RwLock<HashMap<String, Box<dyn RPCService + 'a>>>,
+ task_group: TaskGroup<'a>,
+ config: ServerConfig,
+}
Represents an RPC server
+listener: Listener
§services: RwLock<HashMap<String, Box<dyn RPCService + 'a>>>
§task_group: TaskGroup<'a>
§config: ServerConfig
Creates a new RPC server by passing a listener. It supports Tcp, Unix, and Tls.
+Returns the local endpoint.
+Attach a new service to the RPC server
+Handles a new connection
+Handles a request
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ServerConfig {
+ codec_config: CodecConfig,
+}
RPC server config
+codec_config: CodecConfig
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait RPCService: Sync + Send {
+ // Required methods
+ fn get_method<'a>(&'a self, name: &'a str) -> Option<RPCMethod<'_>>;
+ fn name(&self) -> String;
+}
Defines the interface for an RPC service.
+pub type RPCMethod<'a> = Box<dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, Error>> + Send + Sync + 'a>> + Send + 'a>;
Represents the RPC method
+struct RPCMethod<'a>(Unique<dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, Error>> + Send + Sync + 'a>> + Send + 'a>, Global);
0: Unique<dyn Fn(Value) -> Pin<Box<dyn Future<Output = Result<Value, Error>> + Send + Sync + 'a>> + Send + 'a>
§1: Global
pub async fn dial(endpoint: &Endpoint) -> Result<Conn>
Connects to the provided endpoint.
+it only supports tcp4/6
, udp4/6
, and unix
.
#Example
+ +use karyon_net::{Endpoint, dial};
+
+async {
+ let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+ let conn = dial(&endpoint).await.unwrap();
+
+ conn.write(b"MSG").await.unwrap();
+
+ let mut buffer = [0;32];
+ conn.read(&mut buffer).await.unwrap();
+};
+
udp::UdpConn
, tcp::TcpConn
, and unix::UnixConn
.Conn
.Box<dyn Connection>
pub trait Connection: Send + Sync {
+ // Required methods
+ fn peer_endpoint(&self) -> Result<Endpoint>;
+ fn local_endpoint(&self) -> Result<Endpoint>;
+ fn read<'life0, 'life1, 'async_trait>(
+ &'life0 self,
+ buf: &'life1 mut [u8]
+ ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
+ where Self: 'async_trait,
+ 'life0: 'async_trait,
+ 'life1: 'async_trait;
+ fn write<'life0, 'life1, 'async_trait>(
+ &'life0 self,
+ buf: &'life1 [u8]
+ ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
+ where Self: 'async_trait,
+ 'life0: 'async_trait,
+ 'life1: 'async_trait;
+}
Connection is a generic network connection interface for
+udp::UdpConn
, tcp::TcpConn
, and unix::UnixConn
.
If you are familiar with the Go language, this is similar to the +Conn interface
+Returns the remote peer endpoint of this connection
+Returns the local socket endpoint of this connection
+pub trait ToConn {
+ // Required method
+ fn to_conn(self) -> Conn;
+}
A trait for objects which can be converted to Conn
.
pub type Conn = Box<dyn Connection>;
Alias for Box<dyn Connection>
struct Conn(Unique<dyn Connection>, Global);
0: Unique<dyn Connection>
§1: Global
pub enum Addr {
+ Ip(IpAddr),
+ Domain(String),
+}
Addr defines a type for an address, either IP or domain.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Endpoint {
+ Udp(Addr, Port),
+ Tcp(Addr, Port),
+ Tls(Addr, Port),
+ Ws(Addr, Port),
+ Unix(String),
+}
Endpoint defines generic network endpoints for karyon.
+use std::net::SocketAddr;
+
+use karyon_net::Endpoint;
+
+let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+let socketaddr: SocketAddr = "127.0.0.1:3000".parse().unwrap();
+let endpoint = Endpoint::new_udp_addr(&socketaddr);
+
Creates a new TCP endpoint from a SocketAddr
.
Creates a new UDP endpoint from a SocketAddr
.
Creates a new TLS endpoint from a SocketAddr
.
Creates a new WS endpoint from a SocketAddr
.
Creates a new Unix endpoint from a UnixSocketAddress
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type Port = u16;
Port defined as a u16.
+pub enum Addr {
+ Ip(IpAddr),
+ Domain(String),
+}
Addr defines a type for an address, either IP or domain.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Endpoint {
+ Udp(Addr, Port),
+ Tcp(Addr, Port),
+ Tls(Addr, Port),
+ Ws(Addr, Port),
+ Unix(String),
+}
Endpoint defines generic network endpoints for karyon.
+use std::net::SocketAddr;
+
+use karyon_net::Endpoint;
+
+let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+let socketaddr: SocketAddr = "127.0.0.1:3000".parse().unwrap();
+let endpoint = Endpoint::new_udp_addr(&socketaddr);
+
Creates a new TCP endpoint from a SocketAddr
.
Creates a new UDP endpoint from a SocketAddr
.
Creates a new TLS endpoint from a SocketAddr
.
Creates a new WS endpoint from a SocketAddr
.
Creates a new Unix endpoint from a UnixSocketAddress
.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum NetError {
+ IO(Error),
+ TryFromEndpoint,
+ InvalidAddress(String),
+ InvalidEndpoint(String),
+ ParseEndpoint(String),
+ Timeout,
+ ChannelSend(String),
+ ChannelRecv(RecvError),
+ Rustls(Error),
+ InvalidDnsNameError(InvalidDnsNameError),
+ KaryonCore(Error),
+}
Represents karyon’s Net Error
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Error {
+ IO(Error),
+ TryFromEndpoint,
+ InvalidAddress(String),
+ InvalidEndpoint(String),
+ ParseEndpoint(String),
+ Timeout,
+ ChannelSend(String),
+ ChannelRecv(RecvError),
+ Rustls(Error),
+ InvalidDnsNameError(InvalidDnsNameError),
+ KaryonCore(Error),
+}
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub async fn dial(endpoint: &Endpoint) -> Result<Conn>
Connects to the provided endpoint.
+it only supports tcp4/6
, udp4/6
, and unix
.
#Example
+ +use karyon_net::{Endpoint, dial};
+
+async {
+ let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+ let conn = dial(&endpoint).await.unwrap();
+
+ conn.write(b"MSG").await.unwrap();
+
+ let mut buffer = [0;32];
+ conn.read(&mut buffer).await.unwrap();
+};
+
pub async fn listen(endpoint: &Endpoint) -> Result<Box<dyn ConnListener>>
Listens to the provided endpoint.
+it only supports tcp4/6
, and unix
.
#Example
+ +use karyon_net::{Endpoint, listen};
+
+async {
+ let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+ let listener = listen(&endpoint).await.unwrap();
+ let conn = listener.accept().await.unwrap();
+};
+
pub async fn listen_tcp(endpoint: &Endpoint) -> Result<TcpListener>
Listens on the given TCP address and port.
+pub async fn listen_udp(endpoint: &Endpoint) -> Result<UdpConn>
Listens on the given UDP address and port.
+pub fn listen_unix(path: &String) -> Result<UnixListener>
Listens on the given Unix socket path.
+Connection
trait.Connection
trait.Connection
trait.udp::UdpConn
, tcp::TcpConn
, and unix::UnixConn
.Conn
.Listener
.Box<dyn Connection>
Box<dyn ConnListener>
pub async fn listen(endpoint: &Endpoint) -> Result<Box<dyn ConnListener>>
Listens to the provided endpoint.
+it only supports tcp4/6
, and unix
.
#Example
+ +use karyon_net::{Endpoint, listen};
+
+async {
+ let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+
+ let listener = listen(&endpoint).await.unwrap();
+ let conn = listener.accept().await.unwrap();
+};
+
Listener
.Box<dyn ConnListener>
pub trait ConnListener: Send + Sync {
+ // Required methods
+ fn local_endpoint(&self) -> Result<Endpoint>;
+ fn accept<'life0, 'async_trait>(
+ &'life0 self
+ ) -> Pin<Box<dyn Future<Output = Result<Conn>> + Send + 'async_trait>>
+ where Self: 'async_trait,
+ 'life0: 'async_trait;
+}
ConnListener is a generic network listener.
+pub trait ToListener {
+ // Required method
+ fn to_listener(self) -> Listener;
+}
A trait for objects which can be converted to Listener
.
pub type Listener = Box<dyn ConnListener>;
Alias for Box<dyn ConnListener>
struct Listener(Unique<dyn ConnListener>, Global);
0: Unique<dyn ConnListener>
§1: Global
pub struct TcpConn {
+ inner: TcpStream,
+ read: Mutex<ReadHalf<TcpStream>>,
+ write: Mutex<WriteHalf<TcpStream>>,
+}
TCP network connection implementation of the Connection
trait.
inner: TcpStream
§read: Mutex<ReadHalf<TcpStream>>
§write: Mutex<WriteHalf<TcpStream>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct UdpConn {
+ inner: UdpSocket,
+}
UDP network connection implementation of the Connection
trait.
inner: UdpSocket
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct UnixConn {
+ inner: UnixStream,
+ read: Mutex<ReadHalf<UnixStream>>,
+ write: Mutex<WriteHalf<UnixStream>>,
+}
Unix domain socket implementation of the Connection
trait.
inner: UnixStream
§read: Mutex<ReadHalf<UnixStream>>
§write: Mutex<WriteHalf<UnixStream>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub async fn dial(
+ endpoint: &Endpoint,
+ config: ClientConfig,
+ dns_name: &'static str
+) -> Result<Box<dyn Connection>>
Connects to the given TLS endpoint, returns Conn
(Connection
).
pub async fn listen_tls(
+ endpoint: &Endpoint,
+ config: ServerConfig
+) -> Result<TlsListener>
Listens on the given TLS address and port.
+Connection
trait.Listener
ConnListener
trait.Conn
(Connection
).pub struct TlsConn {
+ inner: TcpStream,
+ read: Mutex<ReadHalf<TlsStream<TcpStream>>>,
+ write: Mutex<WriteHalf<TlsStream<TcpStream>>>,
+}
TLS network connection implementation of the Connection
trait.
inner: TcpStream
§read: Mutex<ReadHalf<TlsStream<TcpStream>>>
§write: Mutex<WriteHalf<TlsStream<TcpStream>>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsListener {
+ acceptor: TlsAcceptor,
+ listener: TcpListener,
+}
Tls network listener implementation of the Listener
ConnListener
trait.
acceptor: TlsAcceptor
§listener: TcpListener
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub trait ConnListener: Send + Sync {
+ // Required methods
+ fn local_endpoint(&self) -> Result<Endpoint>;
+ fn accept<'life0, 'async_trait>(
+ &'life0 self
+ ) -> Pin<Box<dyn Future<Output = Result<Conn>> + Send + 'async_trait>>
+ where Self: 'async_trait,
+ 'life0: 'async_trait;
+}
ConnListener is a generic network listener.
+pub trait Connection: Send + Sync {
+ // Required methods
+ fn peer_endpoint(&self) -> Result<Endpoint>;
+ fn local_endpoint(&self) -> Result<Endpoint>;
+ fn read<'life0, 'life1, 'async_trait>(
+ &'life0 self,
+ buf: &'life1 mut [u8]
+ ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
+ where Self: 'async_trait,
+ 'life0: 'async_trait,
+ 'life1: 'async_trait;
+ fn write<'life0, 'life1, 'async_trait>(
+ &'life0 self,
+ buf: &'life1 [u8]
+ ) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>
+ where Self: 'async_trait,
+ 'life0: 'async_trait,
+ 'life1: 'async_trait;
+}
Connection is a generic network connection interface for
+udp::UdpConn
, tcp::TcpConn
, and unix::UnixConn
.
If you are familiar with the Go language, this is similar to the +Conn interface
+Returns the remote peer endpoint of this connection
+Returns the local socket endpoint of this connection
+pub trait ToListener {
+ // Required method
+ fn to_listener(self) -> Listener;
+}
A trait for objects which can be converted to Listener
.
pub async fn dial_tcp(endpoint: &Endpoint) -> Result<TcpConn>
Connects to the given TCP address and port.
+pub async fn listen_tcp(endpoint: &Endpoint) -> Result<TcpListener>
Listens on the given TCP address and port.
+Connection
trait.pub struct TcpConn {
+ inner: TcpStream,
+ read: Mutex<ReadHalf<TcpStream>>,
+ write: Mutex<WriteHalf<TcpStream>>,
+}
TCP network connection implementation of the Connection
trait.
inner: TcpStream
§read: Mutex<ReadHalf<TcpStream>>
§write: Mutex<WriteHalf<TcpStream>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub async fn dial(
+ endpoint: &Endpoint,
+ config: ClientConfig,
+ dns_name: &'static str
+) -> Result<Box<dyn Connection>>
Connects to the given TLS endpoint, returns Conn
(Connection
).
pub async fn dial_tls(
+ endpoint: &Endpoint,
+ config: ClientConfig,
+ dns_name: &'static str
+) -> Result<TlsConn>
Connects to the given TLS address and port.
+pub async fn listen_tls(
+ endpoint: &Endpoint,
+ config: ServerConfig
+) -> Result<TlsListener>
Listens on the given TLS address and port.
+Connection
trait.Listener
ConnListener
trait.Conn
(Connection
).pub struct TlsConn {
+ inner: TcpStream,
+ read: Mutex<ReadHalf<TlsStream<TcpStream>>>,
+ write: Mutex<WriteHalf<TlsStream<TcpStream>>>,
+}
TLS network connection implementation of the Connection
trait.
inner: TcpStream
§read: Mutex<ReadHalf<TlsStream<TcpStream>>>
§write: Mutex<WriteHalf<TlsStream<TcpStream>>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct TlsListener {
+ acceptor: TlsAcceptor,
+ listener: TcpListener,
+}
Tls network listener implementation of the Listener
ConnListener
trait.
acceptor: TlsAcceptor
§listener: TcpListener
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub async fn dial_udp(endpoint: &Endpoint) -> Result<UdpConn>
Connects to the given UDP address and port.
+pub async fn listen_udp(endpoint: &Endpoint) -> Result<UdpConn>
Listens on the given UDP address and port.
+Connection
trait.pub struct UdpConn {
+ inner: UdpSocket,
+}
UDP network connection implementation of the Connection
trait.
inner: UdpSocket
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub async fn dial_unix(path: &String) -> Result<UnixConn>
Connects to the given Unix socket path.
+pub fn listen_unix(path: &String) -> Result<UnixListener>
Listens on the given Unix socket path.
+Connection
trait.pub struct UnixConn {
+ inner: UnixStream,
+ read: Mutex<ReadHalf<UnixStream>>,
+ write: Mutex<WriteHalf<UnixStream>>,
+}
Unix domain socket implementation of the Connection
trait.
inner: UnixStream
§read: Mutex<ReadHalf<UnixStream>>
§write: Mutex<WriteHalf<UnixStream>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type Conn = Box<dyn Connection>;
Alias for Box<dyn Connection>
struct Conn(Unique<dyn Connection>, Global);
0: Unique<dyn Connection>
§1: Global
pub type Listener = Box<dyn ConnListener>;
Alias for Box<dyn ConnListener>
struct Listener(Unique<dyn ConnListener>, Global);
0: Unique<dyn ConnListener>
§1: Global
pub type Port = u16;
Port defined as a u16.
+pub struct Backend {
+ config: Arc<Config>,
+ key_pair: KeyPair,
+ monitor: Arc<Monitor>,
+ discovery: Arc<Discovery>,
+ peer_pool: Arc<PeerPool>,
+}
Backend serves as the central entry point for initiating and managing +the P2P network.
+config: Arc<Config>
The Configuration for the P2P network.
+key_pair: KeyPair
Identity Key pair
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+discovery: Arc<Discovery>
Discovery instance.
+peer_pool: Arc<PeerPool>
PeerPool instance.
+Creates a new Backend.
+Run the Backend, starting the PeerPool and Discovery instances.
+Attach a custom protocol to the network
+Returns the number of occupied inbound slots.
+Returns the number of occupied outbound slots.
+Subscribes to the monitor to receive network events.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type ArcBackend = Arc<Backend>;
struct ArcBackend {
+ ptr: NonNull<ArcInner<Backend>>,
+ phantom: PhantomData<ArcInner<Backend>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<Backend>>
§phantom: PhantomData<ArcInner<Backend>>
§alloc: Global
pub struct Codec {
+ conn: Box<dyn Connection>,
+}
A Codec working with generic network connections.
+It is responsible for both decoding data received from the network and +encoding data before sending it.
+conn: Box<dyn Connection>
Creates a new Codec.
+Reads a message of type NetMsg
from the connection.
It reads the first 6 bytes as the header of the message, then reads +and decodes the remaining message data based on the determined header.
+Writes a message of type T
to the connection.
Before appending the actual message payload, it calculates the length of +the encoded message in bytes and appends this length to the message header.
+Reads a message of type NetMsg
with the given timeout.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Config {Show 21 fields
+ pub version: Version,
+ pub handshake_timeout: u64,
+ pub ping_interval: u64,
+ pub ping_timeout: u64,
+ pub max_connect_retries: usize,
+ pub bootstrap_peers: Vec<Endpoint>,
+ pub listen_endpoint: Option<Endpoint>,
+ pub peer_endpoints: Vec<Endpoint>,
+ pub inbound_slots: usize,
+ pub outbound_slots: usize,
+ pub discovery_port: Port,
+ pub seeding_interval: u64,
+ pub lookup_inbound_slots: usize,
+ pub lookup_outbound_slots: usize,
+ pub lookup_response_timeout: u64,
+ pub lookup_connection_lifespan: u64,
+ pub lookup_connect_retries: usize,
+ pub refresh_interval: u64,
+ pub refresh_response_timeout: u64,
+ pub refresh_connect_retries: usize,
+ pub enable_tls: bool,
+}
the Configuration for the P2P network.
+version: Version
Represents the network version.
+handshake_timeout: u64
Timeout duration for the handshake with new peers, in seconds.
+ping_interval: u64
Interval at which the ping protocol sends ping messages to a peer to +maintain connections, in seconds.
+ping_timeout: u64
Timeout duration for receiving the pong message corresponding to the +sent ping message, in seconds.
+max_connect_retries: usize
The maximum number of retries for outbound connection establishment.
+bootstrap_peers: Vec<Endpoint>
A list of bootstrap peers for the seeding process.
+listen_endpoint: Option<Endpoint>
An optional listening endpoint to accept incoming connections.
+peer_endpoints: Vec<Endpoint>
A list of endpoints representing peers that the Discovery
will
+manually connect to.
inbound_slots: usize
The number of available inbound slots for incoming connections.
+outbound_slots: usize
The number of available outbound slots for outgoing connections.
+discovery_port: Port
TCP/UDP port for lookup and refresh processes.
+seeding_interval: u64
Time interval, in seconds, at which the Discovery restarts the +seeding process.
+lookup_inbound_slots: usize
The number of available inbound slots for incoming connections during +the lookup process.
+lookup_outbound_slots: usize
The number of available outbound slots for outgoing connections during +the lookup process.
+lookup_response_timeout: u64
Timeout duration for a peer response during the lookup process, in +seconds.
+lookup_connection_lifespan: u64
Maximum allowable time for a live connection with a peer during the +lookup process, in seconds.
+lookup_connect_retries: usize
The maximum number of retries for outbound connection establishment +during the lookup process.
+refresh_interval: u64
Interval at which the table refreshes its entries, in seconds.
+refresh_response_timeout: u64
Timeout duration for a peer response during the table refresh process, +in seconds.
+refresh_connect_retries: usize
The maximum number of retries for outbound connection establishment +during the refresh process.
+enable_tls: bool
Enables TLS for all connections.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum ConnDirection {
+ Inbound,
+ Outbound,
+}
Defines the direction of a network connection.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ConnQueue {
+ queue: Mutex<VecDeque<NewConn>>,
+ conn_available: CondVar,
+}
Connection queue
+queue: Mutex<VecDeque<NewConn>>
§conn_available: CondVar
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct NewConn {
+ pub direction: ConnDirection,
+ pub conn: Conn,
+ pub disconnect_signal: Sender<Result<(), Error>>,
+}
direction: ConnDirection
§conn: Conn
§disconnect_signal: Sender<Result<(), Error>>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morestatic DNS_NAME: &str
pub struct Connector {
+ key_pair: KeyPair,
+ task_group: TaskGroup<'static>,
+ connection_slots: Arc<ConnectionSlots>,
+ max_retries: usize,
+ enable_tls: bool,
+ monitor: Arc<Monitor>,
+}
Responsible for creating outbound connections with other peers.
+key_pair: KeyPair
Identity Key pair
+task_group: TaskGroup<'static>
Managing spawned tasks.
+connection_slots: Arc<ConnectionSlots>
Manages available outbound slots.
+max_retries: usize
The maximum number of retries allowed before successfully +establishing a connection.
+enable_tls: bool
Enables secure connection.
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+Creates a new Connector
+Establish a connection to the specified endpoint
. If the connection
+attempt fails, it performs a backoff and retries until the maximum allowed
+number of retries is exceeded. On a successful connection, it returns a
+Conn
instance.
This method will block until it finds an available slot.
+Establish a connection to the given endpoint
. For each new connection,
+it invokes the provided callback
, and pass the connection to the callback.
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub const MAX_PEERS_IN_PEERSMSG: usize = 10;
Maximum number of peers that can be returned in a PeersMsg.
+pub struct LookupService {
+ id: PeerID,
+ table: Arc<Mutex<RoutingTable>>,
+ listener: Arc<Listener>,
+ connector: Arc<Connector>,
+ outbound_slots: Arc<ConnectionSlots>,
+ listen_endpoint: Option<RwLock<Endpoint>>,
+ config: Arc<Config>,
+ monitor: Arc<Monitor>,
+}
id: PeerID
Peer’s ID
+table: Arc<Mutex<RoutingTable>>
Routing Table
+listener: Arc<Listener>
Listener
+connector: Arc<Connector>
Connector
+outbound_slots: Arc<ConnectionSlots>
Outbound slots.
+listen_endpoint: Option<RwLock<Endpoint>>
Resolved listen endpoint
+config: Arc<Config>
Holds the configuration for the P2P network.
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+Creates a new lookup service
+Set the resolved listen endpoint.
+Starts iterative lookup and populate the routing table.
+This method begins by generating a random peer ID and connecting to the +provided endpoint. It then sends a FindPeer message containing the +randomly generated peer ID. Upon receiving peers from the initial lookup, +it starts connecting to these received peers and sends them a FindPeer +message that contains our own peer ID.
+Starts a random lookup
+This will perfom lookup on a random generated PeerID
+Starts a self lookup
+Connects to the given endpoint and initiates a lookup process for the +provided peer ID.
+Handles outbound connection
+Handles inbound connection
+Sends a Ping msg and wait to receive the Pong message.
+Sends a Pong msg
+Sends a FindPeer msg and wait to receivet the Peers msg.
+Sends a Peers msg.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub const MAX_FAILURES: u32 = 3;
Maximum failures for an entry before removing it from the routing table.
+const PINGMSG_SIZE: usize = 32;
Ping message size
+pub struct PingMsg(pub [u8; 32]);
0: [u8; 32]
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PongMsg(pub [u8; 32]);
0: [u8; 32]
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct RefreshService {
+ table: Arc<Mutex<RoutingTable>>,
+ listen_endpoint: Option<RwLock<Endpoint>>,
+ task_group: TaskGroup<'static>,
+ executor: GlobalExecutor,
+ config: Arc<Config>,
+ monitor: Arc<Monitor>,
+}
table: Arc<Mutex<RoutingTable>>
Routing table
+listen_endpoint: Option<RwLock<Endpoint>>
Resolved listen endpoint
+task_group: TaskGroup<'static>
Managing spawned tasks.
+executor: GlobalExecutor
A global executor
+config: Arc<Config>
Holds the configuration for the P2P network.
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+Creates a new refresh service
+Set the resolved listen endpoint.
+Initiates periodic refreshing of the routing table. This function will +selects the first 8 entries (oldest entries) from each bucket in the +routing table and starts sending Ping messages to the collected entries.
+Iterates over the entries and spawns a new task for each entry to +initiate a connection attempt.
+Initiates refresh for a specific entry within the routing table. It +updates the routing table according to the result.
+Initiates a UDP connection with the entry and attempts to send a Ping +message. If it fails, it retries according to the allowed retries +specified in the Config, with backoff between each retry.
+Set up a UDP listener and start listening for Ping messages from other +peers.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Discovery {
+ table: Arc<Mutex<RoutingTable>>,
+ lookup_service: Arc<LookupService>,
+ refresh_service: Arc<RefreshService>,
+ connector: Arc<Connector>,
+ listener: Arc<Listener>,
+ conn_queue: Arc<ConnQueue>,
+ pub(crate) inbound_slots: Arc<ConnectionSlots>,
+ pub(crate) outbound_slots: Arc<ConnectionSlots>,
+ task_group: TaskGroup<'static>,
+ config: Arc<Config>,
+}
table: Arc<Mutex<RoutingTable>>
Routing table
+lookup_service: Arc<LookupService>
Lookup Service
+refresh_service: Arc<RefreshService>
Refresh Service
+connector: Arc<Connector>
Connector
+listener: Arc<Listener>
Listener
+conn_queue: Arc<ConnQueue>
Connection queue
+inbound_slots: Arc<ConnectionSlots>
Inbound slots.
+outbound_slots: Arc<ConnectionSlots>
Outbound slots.
+task_group: TaskGroup<'static>
Managing spawned tasks.
+config: Arc<Config>
Holds the configuration for the P2P network.
+Creates a new Discovery
+Start a listener and on success, return the resolved endpoint.
+This method will attempt to connect to a peer in the routing table. +If the routing table is empty, it will start the seeding process for +finding new peers.
+This will perform a backoff to prevent getting stuck in the loop +if the seeding process couldn’t find any peers.
+Connect to the given endpoint using the connector
+Starts seeding process.
+This method randomly selects a peer from the routing table and
+attempts to connect to that peer for the initial lookup. If the routing
+table doesn’t have an available entry, it will connect to one of the
+provided bootstrap endpoints in the Config
and initiate the lookup.
Returns a random entry from routing table.
+Update the entry status
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type ArcDiscovery = Arc<Discovery>;
struct ArcDiscovery {
+ ptr: NonNull<ArcInner<Discovery>>,
+ phantom: PhantomData<ArcInner<Discovery>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<Discovery>>
§phantom: PhantomData<ArcInner<Discovery>>
§alloc: Global
pub enum P2pError {
+Show 25 variants
IO(Error),
+ UnsupportedProtocol(String),
+ TryFromPublicKey(&'static str),
+ InvalidMsg(String),
+ IncompatiblePeer,
+ ParseIntError(ParseIntError),
+ ParseFloatError(ParseFloatError),
+ SemverError(Error),
+ ParseError(String),
+ IncompatibleVersion(String),
+ Config(String),
+ PeerShutdown,
+ InvalidPongMsg,
+ Discovery(&'static str),
+ Lookup(&'static str),
+ PeerAlreadyConnected,
+ Yasna(ASN1Error),
+ X509Parser(X509Error),
+ Rcgen(Error),
+ Rustls(Error),
+ InvalidDnsNameError(InvalidDnsNameError),
+ ChannelSend(String),
+ ChannelRecv(RecvError),
+ KaryonCore(Error),
+ KaryonNet(NetError),
+}
Represents karyon’s p2p Error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum Error {
+Show 25 variants
IO(Error),
+ UnsupportedProtocol(String),
+ TryFromPublicKey(&'static str),
+ InvalidMsg(String),
+ IncompatiblePeer,
+ ParseIntError(ParseIntError),
+ ParseFloatError(ParseFloatError),
+ SemverError(Error),
+ ParseError(String),
+ IncompatibleVersion(String),
+ Config(String),
+ PeerShutdown,
+ InvalidPongMsg,
+ Discovery(&'static str),
+ Lookup(&'static str),
+ PeerAlreadyConnected,
+ Yasna(ASN1Error),
+ X509Parser(X509Error),
+ Rcgen(Error),
+ Rustls(Error),
+ InvalidDnsNameError(InvalidDnsNameError),
+ ChannelSend(String),
+ ChannelRecv(RecvError),
+ KaryonCore(Error),
+ KaryonNet(NetError),
+}
Represents karyon’s p2p Error.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreA lightweight, extensible, and customizable peer-to-peer (p2p) network stack.
+use std::sync::Arc;
+
+use easy_parallel::Parallel;
+use smol::{channel as smol_channel, future, Executor};
+
+use karyon_core::crypto::{KeyPair, KeyPairType};
+use karyon_p2p::{Backend, Config, PeerID};
+
+let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+
+// Create the configuration for the backend.
+let mut config = Config::default();
+
+// Create a new Executor
+let ex = Arc::new(Executor::new());
+
+// Create a new Backend
+let backend = Backend::new(&key_pair, config, ex.clone());
+
+let task = async {
+ // Run the backend
+ backend.run().await.unwrap();
+
+ // ....
+
+ // Shutdown the backend
+ backend.shutdown().await;
+};
+
+future::block_on(ex.run(task));
+
Read More
Read More
pub struct Listener {
+ key_pair: KeyPair,
+ task_group: TaskGroup<'static>,
+ connection_slots: Arc<ConnectionSlots>,
+ enable_tls: bool,
+ monitor: Arc<Monitor>,
+}
Responsible for creating inbound connections with other peers.
+key_pair: KeyPair
Identity Key pair
+task_group: TaskGroup<'static>
Managing spawned tasks.
+connection_slots: Arc<ConnectionSlots>
Manages available inbound slots.
+enable_tls: bool
Enables secure connection.
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+Creates a new Listener
+Starts a listener on the given endpoint
. For each incoming connection
+that is accepted, it invokes the provided callback
, and pass the
+connection to the callback.
Returns the resloved listening endpoint.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub const MAX_ALLOWED_MSG_SIZE: u32 = _; // 1_048_576u32
The maximum allowed size for a message in bytes.
+pub const MSG_HEADER_SIZE: usize = 6;
The size of the message header, in bytes.
+#[repr(u8)]pub enum NetMsgCmd {
+ Version = 0,
+ Verack = 1,
+ Protocol = 2,
+ Shutdown = 3,
+ Ping = 4,
+ Pong = 5,
+ FindPeer = 6,
+ Peer = 7,
+ Peers = 8,
+}
Defines message commands.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morePeerMsg
.Redirecting to macro.get_msg_payload.html...
+ + + \ No newline at end of file diff --git a/karyon_p2p/message/macro.get_msg_payload.html b/karyon_p2p/message/macro.get_msg_payload.html new file mode 100644 index 00000000..136adf36 --- /dev/null +++ b/karyon_p2p/message/macro.get_msg_payload.html @@ -0,0 +1,4 @@ +macro_rules! get_msg_payload { + ($a:ident, $b:ident) => { ... }; +}
pub struct FindPeerMsg(pub PeerID);
FindPeer message used to find a specific peer.
+0: PeerID
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct NetMsg {
+ pub header: NetMsgHeader,
+ pub payload: Vec<u8>,
+}
Defines the main message in the karyon p2p network.
+This message structure consists of a header and payload, where the header +typically contains essential information about the message, and the payload +contains the actual data being transmitted.
+header: NetMsgHeader
§payload: Vec<u8>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct NetMsgHeader {
+ pub command: NetMsgCmd,
+ pub payload_size: u32,
+}
Represents the header of a message.
+command: NetMsgCmd
§payload_size: u32
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PeerMsg {
+ pub peer_id: PeerID,
+ pub addr: Addr,
+ pub port: Port,
+ pub discovery_port: Port,
+}
PeerMsg containing information about a peer.
+peer_id: PeerID
§addr: Addr
§port: Port
§discovery_port: Port
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PeersMsg(pub Vec<PeerMsg>);
PeersMsg a list of PeerMsg
.
0: Vec<PeerMsg>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PingMsg {
+ pub nonce: [u8; 32],
+ pub version: VersionInt,
+}
Ping message with a nonce and version information.
+nonce: [u8; 32]
§version: VersionInt
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PongMsg(pub [u8; 32]);
Ping message with a nonce.
+0: [u8; 32]
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ProtocolMsg {
+ pub protocol_id: ProtocolID,
+ pub payload: Vec<u8>,
+}
Defines a message related to a specific protocol.
+protocol_id: ProtocolID
§payload: Vec<u8>
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ShutdownMsg(pub u8);
Shutdown message.
+0: u8
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct VerAckMsg {
+ pub peer_id: PeerID,
+ pub ack: bool,
+}
VerAck message acknowledges the receipt of a Version message. The message +consists of the peer ID and an acknowledgment boolean value indicating +whether the version is accepted.
+peer_id: PeerID
§ack: bool
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct VerMsg {
+ pub peer_id: PeerID,
+ pub version: VersionInt,
+ pub protocols: HashMap<ProtocolID, VersionInt>,
+}
Version message, providing information about a peer’s capabilities.
+peer_id: PeerID
§version: VersionInt
§protocols: HashMap<ProtocolID, VersionInt>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum ConnEvent {
+ Connected(Endpoint),
+ ConnectRetried(Endpoint),
+ ConnectFailed(Endpoint),
+ Accepted(Endpoint),
+ AcceptFailed,
+ Disconnected(Endpoint),
+ Listening(Endpoint),
+ ListenFailed(Endpoint),
+}
Defines connection-related events.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum DiscoveryEvent {
+ LookupStarted(Endpoint),
+ LookupFailed(Endpoint),
+ LookupSucceeded(Endpoint, usize),
+ RefreshStarted,
+}
Defines Discovery
events.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum MonitorEvent {
+ Conn(ConnEvent),
+ PeerPool(PeerPoolEvent),
+ Discovery(DiscoveryEvent),
+}
Defines various type of event that can be monitored.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub enum PeerPoolEvent {
+ NewPeer(PeerID),
+ RemovePeer(PeerID),
+}
Defines PeerPool
events.
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreResponsible for network and system monitoring.
+Read More
Discovery
events.PeerPool
events.pub struct Monitor {
+ inner: ArcPublisher<MonitorEvent>,
+}
Responsible for network and system monitoring.
+It use pub-sub pattern to notify the subscribers with new events.
+use std::sync::Arc;
+
+use smol::Executor;
+
+use karyon_core::crypto::{KeyPair, KeyPairType};
+use karyon_p2p::{Config, Backend, PeerID};
+
+async {
+
+ // Create a new Executor
+ let ex = Arc::new(Executor::new());
+
+ let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+ let backend = Backend::new(&key_pair, Config::default(), ex);
+
+ // Create a new Subscription
+ let sub = backend.monitor().await;
+
+ let event = sub.recv().await;
+};
inner: ArcPublisher<MonitorEvent>
Sends a new monitor event to all subscribers.
+Subscribes to listen to new events.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub use peer_id::PeerID;
pub struct PeerID(pub [u8; 32]);
Represents a unique identifier for a peer.
+0: [u8; 32]
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Peer {
+ id: PeerID,
+ peer_pool: Weak<PeerPool>,
+ codec: Codec,
+ remote_endpoint: Endpoint,
+ conn_direction: ConnDirection,
+ protocol_ids: RwLock<Vec<ProtocolID>>,
+ protocol_events: ArcEventSys<ProtocolID>,
+ stop_chan: (Sender<Result<(), Error>>, Receiver<Result<(), Error>>),
+ task_group: TaskGroup<'static>,
+}
id: PeerID
Peer’s ID
+peer_pool: Weak<PeerPool>
A weak pointer to PeerPool
codec: Codec
Holds the Codec for the peer connection
+remote_endpoint: Endpoint
Remote endpoint for the peer
+conn_direction: ConnDirection
The direction of the connection, either Inbound
or Outbound
protocol_ids: RwLock<Vec<ProtocolID>>
A list of protocol IDs
+protocol_events: ArcEventSys<ProtocolID>
EventSys
responsible for sending events to the protocols.
stop_chan: (Sender<Result<(), Error>>, Receiver<Result<(), Error>>)
This channel is used to send a stop signal to the read loop.
+task_group: TaskGroup<'static>
Managing spawned tasks.
+Creates a new peer
+Send a message to the peer connection using the specified protocol.
+Broadcast a message to all connected peers using the specified protocol.
+Check if the connection is Inbound
+Returns the direction of the connection, which can be either Inbound
+or Outbound
.
Returns the remote endpoint for the peer
+Registers a listener for the given Protocol P
.
Start a read loop to handle incoming messages from the peer connection.
+Start running the protocols for this peer connection.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PeerPool {
+ pub id: PeerID,
+ conn_queue: Arc<ConnQueue>,
+ peers: Mutex<HashMap<PeerID, ArcPeer>>,
+ pub(crate) protocols: RwLock<HashMap<ProtocolID, Box<ProtocolConstructor>>>,
+ protocol_versions: Arc<RwLock<HashMap<ProtocolID, Version>>>,
+ task_group: TaskGroup<'static>,
+ executor: GlobalExecutor,
+ pub(crate) config: Arc<Config>,
+ monitor: Arc<Monitor>,
+}
id: PeerID
Peer’s ID
+conn_queue: Arc<ConnQueue>
Connection queue
+peers: Mutex<HashMap<PeerID, ArcPeer>>
Holds the running peers.
+protocols: RwLock<HashMap<ProtocolID, Box<ProtocolConstructor>>>
Hashmap contains protocol constructors.
+protocol_versions: Arc<RwLock<HashMap<ProtocolID, Version>>>
Hashmap contains protocol IDs and their versions.
+task_group: TaskGroup<'static>
Managing spawned tasks.
+executor: GlobalExecutor
A global Executor
+config: Arc<Config>
The Configuration for the P2P network.
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+Creates a new PeerPool
+Listens to a new connection from the connection queue
+Attach a custom protocol to the network
+Broadcast a message to all connected peers using the specified protocol.
+Add a new peer to the peer list.
+Checks if the peer list contains a peer with the given peer id
+Shuts down the peer and remove it from the peer list.
+Attach the core protocols.
+Initiate a handshake with a connection.
+Wait for a Version message
+Returns the peer’s ID upon successfully receiving the Version message.
+Send a Verack message
+Wait for a Verack message
+Returns the peer’s ID upon successfully receiving the Verack message.
+Check if the new connection has compatible protocols.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type ArcPeerPool = Arc<PeerPool>;
struct ArcPeerPool {
+ ptr: NonNull<ArcInner<PeerPool>>,
+ phantom: PhantomData<ArcInner<PeerPool>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<PeerPool>>
§phantom: PhantomData<ArcInner<PeerPool>>
§alloc: Global
pub enum ProtocolEvent {
+ Message(Vec<u8>),
+ Shutdown,
+}
Protocol event
+Message event, contains a vector of bytes.
+Shutdown event signals the protocol to gracefully shut down.
+source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreDefines the protocol trait.
+Read More
pub trait Protocol: Send + Sync {
+ // Required methods
+ fn start<'async_trait>(
+ self: Arc<Self>
+ ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>
+ where Self: 'async_trait;
+ fn version() -> Result<Version, Error>
+ where Self: Sized;
+ fn id() -> ProtocolID
+ where Self: Sized;
+}
The Protocol trait defines the interface for core protocols +and custom protocols.
+use std::sync::Arc;
+
+use async_trait::async_trait;
+use smol::Executor;
+
+use karyon_core::crypto::{KeyPair, KeyPairType};
+use karyon_p2p::{
+ protocol::{ArcProtocol, Protocol, ProtocolID, ProtocolEvent},
+ Backend, PeerID, Config, Version, P2pError, ArcPeer};
+
+pub struct NewProtocol {
+ peer: ArcPeer,
+}
+
+impl NewProtocol {
+ fn new(peer: ArcPeer) -> ArcProtocol {
+ Arc::new(Self {
+ peer,
+ })
+ }
+}
+
+#[async_trait]
+impl Protocol for NewProtocol {
+ async fn start(self: Arc<Self>) -> Result<(), P2pError> {
+ let listener = self.peer.register_listener::<Self>().await;
+ loop {
+ let event = listener.recv().await.unwrap();
+
+ match event {
+ ProtocolEvent::Message(msg) => {
+ println!("{:?}", msg);
+ }
+ ProtocolEvent::Shutdown => {
+ break;
+ }
+ }
+ }
+
+ listener.cancel().await;
+ Ok(())
+ }
+
+ fn version() -> Result<Version, P2pError> {
+ "0.2.0, >0.1.0".parse()
+ }
+
+ fn id() -> ProtocolID {
+ "NEWPROTOCOLID".into()
+ }
+}
+
+ async {
+ let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+ let config = Config::default();
+
+ // Create a new Executor
+ let ex = Arc::new(Executor::new());
+
+ // Create a new Backend
+ let backend = Backend::new(&key_pair, config, ex);
+
+ // Attach the NewProtocol
+ let c = move |peer| NewProtocol::new(peer);
+ backend.attach_protocol::<NewProtocol>(c).await.unwrap();
+ };
+
Start the protocol
+Returns the version of the protocol.
+Returns the unique ProtocolID associated with the protocol.
+pub type ArcProtocol = Arc<dyn Protocol>;
struct ArcProtocol {
+ ptr: NonNull<ArcInner<dyn Protocol>>,
+ phantom: PhantomData<ArcInner<dyn Protocol>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<dyn Protocol>>
§phantom: PhantomData<ArcInner<dyn Protocol>>
§alloc: Global
pub type ProtocolConstructor = dyn Fn(ArcPeer) -> Arc<dyn Protocol> + Send + Sync;
pub use ping::PingProtocol;
const MAX_FAILUERS: u32 = 3;
enum PingProtocolMsg {
+ Ping([u8; 32]),
+ Pong([u8; 32]),
+}
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PingProtocol {
+ peer: ArcPeer,
+ ping_interval: u64,
+ ping_timeout: u64,
+ task_group: TaskGroup<'static>,
+}
peer: ArcPeer
§ping_interval: u64
§ping_timeout: u64
§task_group: TaskGroup<'static>
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub const ALL_ENTRY: u16 = 0b111111;
pub const BUCKET_SIZE: usize = 20;
The number of entries that can be stored within a single bucket.
+pub const CONNECTED_ENTRY: u16 = 0b000001;
The entry is connected.
+pub const DISCONNECTED_ENTRY: u16 = 0b000010;
The entry is disconnected. This will increase the failure counter.
+pub const INCOMPATIBLE_ENTRY: u16 = 0b100000;
The entry is incompatible. This entry will not contribute to an increase in +failure attempts, instead, it will persist in the routing table for the +lookup process and will only be removed in the presence of a new entry.
+pub const PENDING_ENTRY: u16 = 0b000100;
The entry is ready to reconnect, meaning it has either been added and +has no connection attempts, or it has been refreshed.
+pub const UNREACHABLE_ENTRY: u16 = 0b001000;
The entry is unreachable. This will increase the failure counter.
+pub const UNSTABLE_ENTRY: u16 = 0b010000;
The entry is unstable. This will increase the failure counter.
+pub struct Bucket {
+ entries: Vec<BucketEntry>,
+}
A Bucket represents a group of entries in the routing table.
+entries: Vec<BucketEntry>
Returns an iterator over the entries in the bucket.
+Returns an iterator of entries in random order.
+Updates the status of an entry in the bucket identified by the given key.
+If the key is not found in the bucket, no action is taken.
+This will also update the last_seen field and increase the failures +counter for the bucket entry according to the new status.
+Check if the bucket contains the given key.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct BucketEntry {
+ pub status: u16,
+ pub entry: Entry,
+ pub failures: u32,
+ pub last_seen: i64,
+}
A BucketEntry represents a peer in the routing table.
+status: u16
§entry: Entry
§failures: u32
§last_seen: i64
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type EntryStatusFlag = u16;
BITFLAGS represent the status of an Entry within a bucket.
+const DISTANCE_LIMIT: usize = 32;
The distance limit for the closest buckets.
+const MAX_MATCHED_SUBNET_IN_BUCKET: usize = 1;
The maximum number of matched subnets allowed within a single bucket.
+const MAX_MATCHED_SUBNET_IN_TABLE: usize = 6;
The maximum number of matched subnets across the entire routing table.
+const TABLE_SIZE: usize = 32;
The total number of buckets in the routing table.
+pub const KEY_SIZE: usize = 32;
Specifies the size of the key, in bytes.
+pub fn xor_distance(key: &[u8; 32], other: &[u8; 32]) -> [u8; 32]
Calculates the XOR distance between two provided keys.
+The XOR distance is a metric used in Kademlia to measure the closeness +of keys.
+pub struct Entry {
+ pub key: [u8; 32],
+ pub addr: Addr,
+ pub port: Port,
+ pub discovery_port: Port,
+}
An Entry represents a peer in the routing table.
+key: [u8; 32]
The unique key identifying the peer.
+addr: Addr
The IP address of the peer.
+port: Port
TCP port
+discovery_port: Port
UDP/TCP port
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type Key = [u8; 32];
The unique key identifying the peer.
+pub enum AddEntryResult {
+ Added,
+ Exists,
+ Ignored,
+ Restricted,
+}
Represents the possible result when adding a new entry.
+The entry is added.
+The entry is already exists.
+The entry is ignored.
+The entry is restricted and not allowed.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub fn subnet_match(addr: &Addr, other_addr: &Addr) -> bool
Check if two addresses belong to the same subnet.
+pub use bucket::Bucket;
pub use bucket::BucketEntry;
pub use bucket::EntryStatusFlag;
pub use bucket::CONNECTED_ENTRY;
pub use bucket::DISCONNECTED_ENTRY;
pub use bucket::INCOMPATIBLE_ENTRY;
pub use bucket::PENDING_ENTRY;
pub use bucket::UNREACHABLE_ENTRY;
pub use bucket::UNSTABLE_ENTRY;
pub use entry::xor_distance;
pub use entry::Entry;
pub use entry::Key;
pub struct RoutingTable {
+ key: [u8; 32],
+ buckets: Vec<Bucket>,
+}
This is a modified version of the Kademlia Distributed Hash Table (DHT). +https://en.wikipedia.org/wiki/Kademlia
+key: [u8; 32]
§buckets: Vec<Bucket>
Adds a new entry to the table and returns a result indicating success, +failure, or restrictions.
+Check if the table contains the given key.
+Updates the status of an entry in the routing table identified +by the given key.
+If the key is not found, no action is taken.
+Returns a list of bucket indexes that are closest to the given target key.
+Returns a list of the closest entries to the given target key, limited by max_entries.
+Removes an entry with the given key from the routing table, if it exists.
+Returns a random entry from the routing table.
+This function iterate through the routing table and counts how many +entries in the same subnet as the given Entry are already present.
+If the number of matching entries in the same bucket exceeds a +threshold (MAX_MATCHED_SUBNET_IN_BUCKET), or if the total count of +matching entries in the entire table exceeds a threshold +(MAX_MATCHED_SUBNET_IN_TABLE), the addition of the Entry +is considered restricted and returns true.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct ConnectionSlots {
+ signal: CondWait,
+ slots: AtomicUsize,
+ max_slots: usize,
+}
Manages available inbound and outbound slots.
+signal: CondWait
A condvar for notifying when a slot become available.
+slots: AtomicUsize
The number of occupied slots
+max_slots: usize
The maximum number of slots.
+Decreases the occupied slots by one and notifies the waiting signal +to start accepting/connecting new connections.
+Waits for a slot to become available.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Backend {
+ config: Arc<Config>,
+ key_pair: KeyPair,
+ monitor: Arc<Monitor>,
+ discovery: Arc<Discovery>,
+ peer_pool: Arc<PeerPool>,
+}
Backend serves as the central entry point for initiating and managing +the P2P network.
+config: Arc<Config>
The Configuration for the P2P network.
+key_pair: KeyPair
Identity Key pair
+monitor: Arc<Monitor>
Responsible for network and system monitoring.
+discovery: Arc<Discovery>
Discovery instance.
+peer_pool: Arc<PeerPool>
PeerPool instance.
+Creates a new Backend.
+Run the Backend, starting the PeerPool and Discovery instances.
+Attach a custom protocol to the network
+Returns the number of occupied inbound slots.
+Returns the number of occupied outbound slots.
+Subscribes to the monitor to receive network events.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Config {Show 21 fields
+ pub version: Version,
+ pub handshake_timeout: u64,
+ pub ping_interval: u64,
+ pub ping_timeout: u64,
+ pub max_connect_retries: usize,
+ pub bootstrap_peers: Vec<Endpoint>,
+ pub listen_endpoint: Option<Endpoint>,
+ pub peer_endpoints: Vec<Endpoint>,
+ pub inbound_slots: usize,
+ pub outbound_slots: usize,
+ pub discovery_port: Port,
+ pub seeding_interval: u64,
+ pub lookup_inbound_slots: usize,
+ pub lookup_outbound_slots: usize,
+ pub lookup_response_timeout: u64,
+ pub lookup_connection_lifespan: u64,
+ pub lookup_connect_retries: usize,
+ pub refresh_interval: u64,
+ pub refresh_response_timeout: u64,
+ pub refresh_connect_retries: usize,
+ pub enable_tls: bool,
+}
the Configuration for the P2P network.
+version: Version
Represents the network version.
+handshake_timeout: u64
Timeout duration for the handshake with new peers, in seconds.
+ping_interval: u64
Interval at which the ping protocol sends ping messages to a peer to +maintain connections, in seconds.
+ping_timeout: u64
Timeout duration for receiving the pong message corresponding to the +sent ping message, in seconds.
+max_connect_retries: usize
The maximum number of retries for outbound connection establishment.
+bootstrap_peers: Vec<Endpoint>
A list of bootstrap peers for the seeding process.
+listen_endpoint: Option<Endpoint>
An optional listening endpoint to accept incoming connections.
+peer_endpoints: Vec<Endpoint>
A list of endpoints representing peers that the Discovery
will
+manually connect to.
inbound_slots: usize
The number of available inbound slots for incoming connections.
+outbound_slots: usize
The number of available outbound slots for outgoing connections.
+discovery_port: Port
TCP/UDP port for lookup and refresh processes.
+seeding_interval: u64
Time interval, in seconds, at which the Discovery restarts the +seeding process.
+lookup_inbound_slots: usize
The number of available inbound slots for incoming connections during +the lookup process.
+lookup_outbound_slots: usize
The number of available outbound slots for outgoing connections during +the lookup process.
+lookup_response_timeout: u64
Timeout duration for a peer response during the lookup process, in +seconds.
+lookup_connection_lifespan: u64
Maximum allowable time for a live connection with a peer during the +lookup process, in seconds.
+lookup_connect_retries: usize
The maximum number of retries for outbound connection establishment +during the lookup process.
+refresh_interval: u64
Interval at which the table refreshes its entries, in seconds.
+refresh_response_timeout: u64
Timeout duration for a peer response during the table refresh process, +in seconds.
+refresh_connect_retries: usize
The maximum number of retries for outbound connection establishment +during the refresh process.
+enable_tls: bool
Enables TLS for all connections.
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct PeerID(pub [u8; 32]);
Represents a unique identifier for a peer.
+0: [u8; 32]
Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct Version {
+ pub v: VersionInt,
+ pub req: VersionReq,
+}
Represents the network version and protocol version used in karyon p2p.
+use karyon_p2p::Version;
+
+let version: Version = "0.2.0, >0.1.0".parse().unwrap();
+
+let version: Version = "0.2.0".parse().unwrap();
+
v: VersionInt
§req: VersionReq
Creates a new Version
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moreconst BAD_ENCODING_ERR: Error;
const BAD_SIGNATURE_ERR: Error;
fn generate_cert<'a>(
+ key_pair: &KeyPair
+) -> Result<(CertificateDer<'a>, PrivateKeyDer<'a>), Error>
Generates a certificate and returns both the certificate and the private key.
+fn parse_cert<'a>(
+ end_entity: &'a CertificateDer<'a>
+) -> Result<X509Certificate<'a>, Error>
Parses the given x509 certificate.
+pub fn tls_client_config(
+ key_pair: &KeyPair,
+ peer_id: Option<PeerID>
+) -> Result<ClientConfig, Error>
Returns a TLS client configuration.
+pub fn tls_server_config(key_pair: &KeyPair) -> Result<ServerConfig, Error>
Returns a TLS server configuration.
+fn verify_cert(end_entity: &CertificateDer<'_>) -> Result<PeerID, Error>
Verifies the given certification.
+fn verify_cert_signature(
+ cert: &X509Certificate<'_>,
+ message: &[u8],
+ signature: &[u8]
+) -> Result<(), Error>
Verifies the signature of the given certificate.
+static CIPHER_SUITES: &[SupportedCipherSuite]
static KX_GROUPS: &[&dyn SupportedKxGroup]
static PROTOCOL_VERSIONS: &[&SupportedProtocolVersion]
static SIGNATURE_SCHEMES: &[SignatureScheme]
struct CliCertVerifier {}
end_entity
is valid, acceptable,
+and chains to at least one of the trust anchors trusted by
+this verifier. Read moreverify_tls12_signature
and verify_tls13_signature
calls. Read moretrue
to enable the server to request a client certificate and
+false
to skip requesting a client certificate. Defaults to true
.true
to require a client certificate and false
to make
+client authentication optional.
+Defaults to self.offer_client_auth()
.Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morestruct SrvrCertVerifier {
+ peer_id: Option<PeerID>,
+}
peer_id: Option<PeerID>
end_entity
is valid for the
+hostname dns_name
and chains to at least one trust anchor. Read moreverify_tls12_signature
and verify_tls13_signature
calls. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub type ArcBackend = Arc<Backend>;
struct ArcBackend {
+ ptr: NonNull<ArcInner<Backend>>,
+ phantom: PhantomData<ArcInner<Backend>>,
+ alloc: Global,
+}
ptr: NonNull<ArcInner<Backend>>
§phantom: PhantomData<ArcInner<Backend>>
§alloc: Global
pub fn version_match(version_req: &VersionReq, version: &VersionInt) -> bool
Check if a version satisfies a version request.
+pub struct Version {
+ pub v: VersionInt,
+ pub req: VersionReq,
+}
Represents the network version and protocol version used in karyon p2p.
+use karyon_p2p::Version;
+
+let version: Version = "0.2.0, >0.1.0".parse().unwrap();
+
+let version: Version = "0.2.0".parse().unwrap();
+
v: VersionInt
§req: VersionReq
Creates a new Version
+Subscriber
to this type, returning a
+[WithDispatch
] wrapper. Read morepub struct VersionInt {
+ major: u64,
+ minor: u64,
+ patch: u64,
+}
major: u64
§minor: u64
§patch: u64
source
. Read moreSubscriber
to this type, returning a
+[WithDispatch
] wrapper. Read moresmol
…","Collects common cryptographic tools","Represents karyon’s Core Error.","event::EventSys
implementation.","","","","","A simple publish-subscribe system Read More
","A set of helper tools and functions.","Exponential backoff …","","","CondVar is an async version of …","CondWait is a wrapper struct for CondVar with a Mutex …","The return value from the select
function, indicating …","","","TaskGroup is a group of spawned tasks.","The result of a spawned task.","","The base delay in milliseconds for the initial retry.","","The CondVar","","","","The max delay in milliseconds allowed for a retry.","Atomic counter","","Returns the result of the future that completes first, …","Stop flag","","","","","Waits for a future to complete or times out if it exceeds …","Boolean flag","Exponential backoff …","The base delay in milliseconds for the initial retry.","","","Returns the argument unchanged.","Calls U::from(self)
.","The max delay in milliseconds allowed for a retry.","Creates a new Backoff.","Reset the retry counter to 0.","Atomic counter","Sleep based on the current retry count and delay values. …","Stop flag","","","","","CondVar is an async version of …","","Wakers is a helper struct to store the task wakers","","","","","","","Wakes up all blocked tasks waiting on this condvar.","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","Creates a new CondVar","","","","","Wakes up one blocked task waiting on this condvar.","","","","","","","","","","","","","Blocks the current task until this condition variable …","","","CondWait is a wrapper struct for CondVar with a Mutex …","","","Signal all waiting tasks.","The CondVar","","Returns the argument unchanged.","Calls U::from(self)
.","Creates a new CondWait.","Reset the boolean flag value to false.","Signal a waiting task.","","","","","Boolean flag","Waits for a signal or broadcast.","The return value from the select
function, indicating …","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","Calls U::from(self)
.","Calls U::from(self)
.","","","Returns the result of the future that completes first, …","","","","","","","","","","","","","TaskGroup is a group of spawned tasks.","TaskHandler","The result of a spawned task.","","","","","","","Cancels all tasks in the group.","Cancels the task.","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Checks if the task group is empty.","Get the number of the tasks in the group.","Creates a new task group","Creates a new task handle","Spawns a new task and calls the callback after it has …","","","","","","","","","","","","","","","","","","Waits for a future to complete or times out if it exceeds …","","","","","key cryptography type","","A Secret key","","","","","","","","An extension trait, adding essential methods to all KeyPair
…","key cryptography type","","An extension trait, adding essential methods to all …","A Secret key","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Generate a new random keypair.","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Get the public key of this keypair.","Get the public key of this keypair.","","Get the secret key of this keypair.","Get the secret key of this keypair.","","Sign a message using the private key.","Sign a message using the private key.","","","","","","","","","","","","","","","","","","","","","","","","Verify a signature on a message with this public key.","Verify a signature on a message with this public key.","","","","","","","","","","","","","Contains the error value","","","Contains the success value","","","","","","","","","","Returns the argument unchanged.","","","","","","Calls U::from(self)
.","","","","","","","","","An event within the EventSys
.","EventListener listens for and receives events from the …","","EventSys supports event emission to registered listeners …","","","","","","","","","","","","","","","","Cancels the listener and removes it from the EventSys
.","","","The time at which the event was created.","Emits an event to the listeners.","Emits an event to the listeners.","Returns the event id for this event listener.","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","Creates a new EventSys
","Create a new event listener.","Creates a new Event.","","","","","Receive the next event.","","Registers a new event listener for the given topic.","Removes the event listener attached to the given topic.","","","","Returns the topic for this event listener.","","","","","","","","","","","The value of the Event.","","","","","","","A simple publish-subscribe system.","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self)
.","Calls U::from(self)
.","Creates a new Publisher","Creates a new Subscription","Notify all subscribers","","","","Receive a message from the Publisher","","","Subscribe and return a Subscription","","","","","","","Unsubscribe from the Publisher","Unsubscribe from the Publisher","","","","Decodes a given type T
from the given slice. returns the …","","Encode the given type T
into a Vec<u8>
.","Encode the given type T
into the given slice..","Returns the user’s home directory as a PathBuf
.","","Generates and returns a random u16 using rand::rngs::OsRng
.","Generates and returns a random u32 using rand::rngs::OsRng
.","Expands a tilde (~) in a path and returns the expanded …","Decodes a given type T
from the given slice. returns the …","Encode the given type T
into a Vec<u8>
.","Encode the given type T
into the given slice..","Returns the user’s home directory as a PathBuf
.","Expands a tilde (~) in a path and returns the expanded …"],"i":[0,0,32,80,0,0,0,0,32,80,32,80,0,0,0,30,30,0,0,0,26,26,0,0,0,7,0,22,0,28,11,7,7,0,0,7,28,0,28,0,0,22,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,11,16,12,11,16,12,11,16,11,12,16,11,16,12,16,16,11,11,16,12,16,11,16,12,16,12,11,11,16,12,11,16,12,11,16,12,11,16,12,11,12,12,0,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,0,26,26,0,1,26,1,26,1,26,1,26,1,1,1,26,1,1,0,1,26,1,26,1,26,1,26,1,26,30,30,0,0,0,28,29,30,28,29,30,28,29,29,28,30,30,28,29,30,28,29,30,28,28,28,29,28,28,29,28,30,28,29,30,28,29,30,28,29,30,30,28,29,30,0,45,43,41,0,0,0,0,0,45,43,41,0,0,0,0,0,0,0,0,38,41,42,45,47,43,44,41,42,45,47,43,44,41,42,43,44,43,44,41,42,45,47,43,44,41,42,41,42,43,44,45,47,43,44,41,42,46,43,44,46,43,44,46,43,44,43,44,45,47,43,44,41,42,45,47,43,44,41,42,45,47,43,44,41,42,41,42,38,41,42,45,47,43,44,41,42,49,49,49,49,49,5,0,49,5,49,0,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,0,0,0,0,0,0,0,0,0,66,0,67,70,81,64,57,63,64,57,63,57,63,63,63,64,64,57,57,57,57,63,63,64,57,63,62,57,64,57,63,64,64,57,63,67,57,67,70,57,57,64,64,63,63,66,57,57,64,57,63,64,57,63,64,57,63,63,61,63,64,57,63,0,0,0,0,72,75,74,75,74,75,74,74,75,74,75,74,75,72,72,74,74,74,75,75,75,74,75,74,75,74,75,74,75,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[-2,-4],[[1,[-2,-4]]],[],[[3,[],[[2,[-1]]]]],[],[[3,[],[[2,[-3]]]]]],0,0,0,0,0,[[4,-2],[[5,[-1]]],[],[[3,[],[[2,[-1]]]]]],0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-1,[]],[-1,-2,[],[]],0,[[6,6],7],[7,8],0,[7,6],0,[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,-2,[],[]],0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[11,8],0,[[],11],[[12,13],[[15,[[15,[14]]]]]],[[[16,[-1]]],8,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[],[]],[[],11],[[11,[17,[-1]]],[[16,[-1]]],[]],[[],12],[[[18,[[16,[-1]]]],19],[[20,[-2]]],[],[]],[[12,[15,[14]]],13],[11,8],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[11,[17,[-1]]],[[17,[-1]]],[]],[[12,21],8],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[22,8],0,[[],22],[-1,-1,[]],[-1,-2,[],[]],[[],22],[22,8],[22,8],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,-2,[],[]],0,[22,8],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[[1,[-1,-2]],23],24,25,25],[[[26,[-1,-2]],23],24,25,25],[-1,-1,[]],[-1,-1,[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[],[]],[[[18,[[1,[-2,-4]]]],19],[[20,[-5]]],[],[[3,[],[[2,[-1]]]]],[],[[3,[],[[2,[-3]]]]],[]],[[-2,-4],[[1,[-2,-4]]],[],[[3,[],[[2,[-1]]]]],[],[[3,[],[[2,[-3]]]]]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],[-1,27,[]],[-1,27,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[28,8],[29,8],0,0,[[[30,[-1]],23],24,25],[[[30,[-1]],23],24,25],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[28,21],[28,31],[32,28],[[32,-2,-4,[33,[22]]],29,[34,35],[[3,[],[[2,[-1]]]],34],[[3,[],[[2,[8]]]],34],[[36,[[30,[-1]]],[[2,[-3]]]],34]],[[28,-2,-4],8,[34,35],[[3,[],[[2,[-1]]]],34],[[3,[],[[2,[8]]]],34],[[36,[[30,[-1]]],[[2,[-3]]]],34]],0,0,0,[-1,37,[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],[-1,27,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[4,-2],[[5,[-1]]],[],[[3,[],[[2,[-1]]]]]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[38,[[40,[39]]]],[41,[[40,[39]]]],[42,[[40,[39]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[43,43],[44,44],[[-1,-2],8,[],[]],[[-1,-2],8,[],[]],[[41,23],24],[[42,23],24],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[[45,[40,[39]]],[[5,[41]]]],[[[40,[39]]],[[5,[42]]]],[45,43],[[],44],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[46,41],[43,41],[44,41],[46,47],[43,47],[44,47],[[46,[40,[39]]],[[48,[39]]]],[[43,[40,[39]]],[[48,[39]]]],[[44,[40,[39]]],[[48,[39]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],[-1,27,[]],[-1,27,[]],[[38,[40,[39]],[40,[39]]],[[5,[8]]]],[[41,[40,[39]],[40,[39]]],[[5,[8]]]],[[42,[40,[39]],[40,[39]]],[[5,[8]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[49,23],24],[[49,23],24],[50,49],[-1,-1,[]],[[[51,[-1]]],49,[]],[52,49],[53,49],[54,49],[55,49],[-1,-2,[],[]],[49,[[15,[56]]]],[-1,37,[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,27,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[[57,[-1,-2]]],8,[58,59,60,25],[61,60,62]],[63,63],[[-1,-2],8,[],[]],0,[[[64,[-1]],-2],8,[58,59,25,60],[[66,[],[[65,[-1]]]],60]],[[[64,[-1]],-1,-2],8,[58,59,25,60],[61,62,60]],[[[57,[-1,-2]]],37,[58,59,60,25],[61,60,62]],0,[[[57,[-1,-2]]],[[67,[-1]]],[58,59,60,25],[61,60,62]],0,[[63,23],24],[[63,23],24],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[[],68],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[],[[67,[-1]]],[58,59,25,60]],[[69,[70,[-1]],[71,[63]],68,-1],[[57,[-1,-2]]],[58,59,60,25],[61,60,62]],[[[33,[61]]],63],0,0,0,0,[[[57,[-1,-2]]],[[5,[-2]]],[58,59,60,25],[61,60,62]],0,[[[33,[[64,[-1]]]],-1],[[57,[-1,-2]]],[58,59,25,60],[61,62,60]],[[[64,[-1]],-1,68,69],8,[58,59,25,60]],[-1,-2,[],[]],[-1,37,[]],[[],-1,[]],[[[57,[-1,-2]]],-1,[58,59,60,25],[61,60,62]],0,[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],[-1,10,[]],0,[61,27],[-1,27,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-1,[]],[-1,-1,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[[],[[72,[-1]]],60],[[73,[72,[-1]],[71,[-1]]],[[74,[-1]]],60],[[[33,[[75,[-1]]]],-1],8,60],0,0,0,[[[74,[-1]]],[[5,[-1]]],60],0,0,[[[33,[[75,[-1]]]]],[[74,[-1]]],60],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,[[9,[-2]]],[],[]],[-1,10,[]],[-1,10,[]],[[[33,[[75,[-1]]]],73],8,60],[[[74,[-1]]],8,60],[-1,-2,[],[]],[-1,-2,[],[]],0,[[[40,[39]]],[[5,[[8,[-1,31]]]]],76],0,[-1,[[5,[[48,[39]]]]],77],[[-1,[40,[39]]],[[5,[8]]],77],[[],[[5,[78]]]],0,[[],13],[[],79],[68,[[5,[78]]]],[[[40,[39]]],[[5,[[8,[-1,31]]]]],76],[-1,[[5,[[48,[39]]]]],77],[[-1,[40,[39]]],[[5,[8]]],77],[[],[[5,[78]]]],[68,[[5,[78]]]]],"c":[],"p":[[5,"Select",120],[17,"Output"],[10,"Future",444],[5,"Duration",445],[8,"Result",291],[1,"u64"],[5,"Backoff",42],[1,"tuple"],[6,"Result",446],[5,"TypeId",447],[5,"CondVar",58],[5,"Wakers",58],[1,"u16"],[5,"Waker",448],[6,"Option",449],[5,"CondVarAwait",58],[5,"MutexGuard",450],[5,"Pin",451],[5,"Context",448],[6,"Poll",452],[1,"bool"],[5,"CondWait",103],[5,"Formatter",453],[8,"Result",453],[10,"Debug",453],[6,"Either",120],[10,"Any",447],[5,"TaskGroup",149],[5,"TaskHandler",149],[6,"TaskResult",149],[1,"usize"],[8,"Executor",0],[5,"Arc",454],[10,"Send",455],[10,"Sync",455],[10,"FnOnce",456],[5,"String",457],[10,"PublicKeyExt",203],[1,"u8"],[1,"slice"],[6,"PublicKey",203],[5,"Ed25519PublicKey",203],[6,"KeyPair",203],[5,"Ed25519KeyPair",203],[6,"KeyPairType",203],[10,"KeyPairExt",203],[5,"SecretKey",203],[5,"Vec",458],[6,"Error",291],[5,"Error",459],[5,"SendError",460],[6,"EncodeError",461],[5,"Error",462],[6,"DecodeError",461],[5,"RecvError",460],[10,"Error",463],[5,"EventListener",323],[10,"Hash",464],[10,"Eq",465],[10,"Clone",466],[10,"EventValueAny",323],[10,"EventValue",323],[5,"Event",323],[5,"EventSys",323],[17,"Topic"],[10,"EventValueTopic",323],[8,"ArcEventSys",323],[1,"str"],[8,"EventListenerID",323],[8,"WeakEventSys",323],[5,"Receiver",460],[8,"ArcPublisher",395],[8,"SubscriptionID",395],[5,"Subscription",395],[5,"Publisher",395],[10,"Decode",467],[10,"Encode",468],[5,"PathBuf",469],[1,"u32"],[8,"GlobalExecutor",0],[8,"Listeners",323]],"b":[[164,"impl-Display-for-TaskResult%3CT%3E"],[165,"impl-Debug-for-TaskResult%3CT%3E"],[306,"impl-Display-for-Error"],[307,"impl-Debug-for-Error"],[308,"impl-From%3CError%3E-for-Error"],[310,"impl-From%3CSendError%3CT%3E%3E-for-Error"],[311,"impl-From%3CEncodeError%3E-for-Error"],[312,"impl-From%3CError%3E-for-Error"],[313,"impl-From%3CDecodeError%3E-for-Error"],[314,"impl-From%3CRecvError%3E-for-Error"],[353,"impl-Debug-for-Event"],[354,"impl-Display-for-Event"]]}],\
+["karyon_jsonrpc",{"doc":"A fast and lightweight async implementation of JSON-RPC 2.0…","t":"PFFFGPPPPSGPPPIPKFFPPPPPNNNCNNCOOOOONCNNNNMNNOOCMNNNNNNQCCOOONNNNNNNFFNNNNNOONNNNNNONNNNNNNNFFSSNNNNNNOONONNNNONNNNNNNNNNNNNPPGPPPPPPPPPINNNNNNNNNNNNNNNNNFSSSSFSFFNNNNNNNNOONNNNONNNNNNNNNNNNOONNNNOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNSSSSFFNNNNNOONNNNNNNONNHONNONNNNNNNNIIKMM","n":["CallError","Client","ClientConfig","CodecConfig","Endpoint","IO","InvalidMsg","InvalidParams","InvalidRequest","JSONRPC_VERSION","JsonRPCError","KaryonCore","KaryonNet","ParseJSON","RPCMethod","RPCMethodError","RPCService","Server","ServerConfig","Tcp","Tls","Udp","Unix","Ws","addr","borrow","borrow_mut","client","clone","clone_into","codec","codec","codec_config","config","config","default_buffer_size","eq","error","fmt","fmt","from","from_str","get_method","hash","into","listener","max_allowed_buffer_size","message","name","new_tcp_addr","new_tls_addr","new_udp_addr","new_unix_addr","new_ws_addr","port","register_service","server","service","services","task_group","timeout","to_owned","to_string","try_from","try_into","type_id","value_as_any","vzip","Client","ClientConfig","borrow","borrow","borrow_mut","borrow_mut","call","codec","config","default","from","from","into","into","new","timeout","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","Codec","CodecConfig","DEFAULT_BUFFER_SIZE","DEFAULT_MAX_ALLOWED_BUFFER_SIZE","borrow","borrow","borrow_mut","borrow_mut","clone","clone_into","config","conn","default","default_buffer_size","from","from","into","into","max_allowed_buffer_size","new","read_until","read_until_with_timeout","to_owned","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","write_all","CallError","Err","Error","IO","InvalidMsg","InvalidParams","InvalidRequest","KaryonCore","KaryonNet","Ok","ParseJSON","RPCMethodError","Result","borrow","borrow_mut","fmt","fmt","from","from","from","from","from","into","source","to_string","try_from","try_into","type_id","value_as_any","vzip","Error","INTERNAL_ERROR_CODE","INVALID_PARAMS_ERROR_CODE","INVALID_REQUEST_ERROR_CODE","METHOD_NOT_FOUND_ERROR_CODE","Notification","PARSE_ERROR_CODE","Request","Response","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","code","data","deserialize","deserialize","deserialize","deserialize","error","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","id","id","into","into","into","into","jsonrpc","jsonrpc","jsonrpc","message","method","method","params","params","result","serialize","serialize","serialize","serialize","to_string","to_string","to_string","to_string","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","value_as_any","value_as_any","value_as_any","value_as_any","vzip","vzip","vzip","vzip","FAILED_TO_PARSE_ERROR_MSG","INTERNAL_ERROR_MSG","INVALID_REQUEST_ERROR_MSG","METHOD_NOT_FOUND_ERROR_MSG","Server","ServerConfig","attach_service","borrow","borrow","borrow_mut","borrow_mut","codec_config","config","default","from","from","handle_conn","handle_request","into","into","listener","local_endpoint","new","pack_err_res","services","shutdown","start","task_group","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","RPCMethod","RPCMethodOutput","RPCService","get_method","name"],"q":[[0,"karyon_jsonrpc"],[68,"karyon_jsonrpc::client"],[92,"karyon_jsonrpc::codec"],[124,"karyon_jsonrpc::error"],[154,"karyon_jsonrpc::message"],[233,"karyon_jsonrpc::server"],[269,"karyon_jsonrpc::service"],[274,"karyon_net::endpoint"],[275,"karyon_net::error"],[276,"core::result"],[277,"core::fmt"],[278,"core::fmt"],[279,"core::hash"],[280,"alloc::string"],[281,"core::net::socket_addr"],[282,"std::os::unix::net::addr"],[283,"core::any"],[284,"core::any"],[285,"serde::de"],[286,"karyon_net::connection"],[287,"karyon_net::connection"],[288,"core::fmt"],[289,"karyon_core::error"],[290,"std::io::error"],[291,"core::error"],[292,"serde::de"],[293,"karyon_core"],[294,"karyon_net::listener"],[295,"serde_json::value"]],"d":["","Represents an RPC client","Represents client config","Represents Codec config","Endpoint defines generic network endpoints for karyon.","","","","","","Represents karyon’s jsonrpc Error.","","","","Represents the RPC method","","Defines the interface for an RPC service.","Represents an RPC server","RPC server config","","","","","","Returns the Addr
of the endpoint.","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","Calls U::from(self)
.","","The maximum allowed buffer size to receive a message. If …","","","Creates a new TCP endpoint from a SocketAddr
.","Creates a new TLS endpoint from a SocketAddr
.","Creates a new UDP endpoint from a SocketAddr
.","Creates a new Unix endpoint from a UnixSocketAddress
.","Creates a new WS endpoint from a SocketAddr
.","Returns the Port
of the endpoint.","Implements the RPCService
trait for a provided type.","","","","","","","","","","","","","Represents an RPC client","Represents client config","","","","","Calls the provided method, waits for the response, and …","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self)
.","Calls U::from(self)
.","Creates a new RPC client by passing a Tcp, Unix, or Tls …","","","","","","","","","","","Represents Codec config","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self)
.","Calls U::from(self)
.","The maximum allowed buffer size to receive a message. If …","Creates a new Codec","Read all bytes into buffer
until the 0x0A
byte or EOF is …","","","","","","","","","","","Writes an entire buffer into the given connection.","","Contains the error value","Represents karyon’s jsonrpc Error.","","","","","","","Contains the success value","","","","","","","","Returns the argument unchanged.","","","","","Calls U::from(self)
.","","","","","","","","","Internal error: Internal JSON-RPC error.","Invalid params: Invalid method parameter(s).","Invalid request: The JSON sent is not a valid Request …","Method not found: The method does not exist / is not …","","Parse error: Invalid JSON was received by the server.","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Represents an RPC server","RPC server config","Attach a new service to the RPC server","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Handles a new connection","Handles a request","Calls U::from(self)
.","Calls U::from(self)
.","","Returns the local endpoint.","Creates a new RPC server by passing a listener. It …","","","Shuts down the RPC server","Starts the RPC server","","","","","","","","","","Represents the RPC method","","Defines the interface for an RPC service.","",""],"i":[21,0,0,0,0,21,21,21,21,0,0,21,21,21,0,21,0,0,0,1,1,1,1,1,1,1,1,0,1,1,0,20,46,20,45,26,1,0,1,1,1,1,10,1,1,45,26,0,10,1,1,1,1,1,1,0,0,0,45,45,24,1,1,1,1,1,1,1,0,0,20,24,20,24,20,20,20,24,20,24,20,24,20,24,20,24,20,24,20,24,20,24,0,0,0,0,28,26,28,26,26,26,28,28,26,26,28,26,28,26,26,28,28,28,26,28,26,28,26,28,26,28,26,28,21,52,0,21,21,21,21,21,21,52,21,21,0,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,0,0,0,0,0,0,0,0,0,39,41,42,43,39,41,42,43,42,42,39,41,42,43,41,39,39,41,41,42,42,43,43,39,41,42,43,39,41,39,41,42,43,39,41,43,42,39,43,39,43,41,39,41,42,43,39,41,42,43,39,41,42,43,39,41,42,43,39,41,42,43,39,41,42,43,39,41,42,43,0,0,0,0,0,0,45,45,46,45,46,46,45,46,45,46,45,45,45,46,45,45,45,0,45,45,45,45,45,46,45,46,45,46,45,46,0,0,0,10,10],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[1,[[4,[2,3]]]],[-1,-2,[],[]],[-1,-2,[],[]],0,[1,1],[[-1,-2],5,[],[]],0,0,0,0,0,0,[[1,1],6],0,[[1,7],[[4,[5,8]]]],[[1,7],[[4,[5,8]]]],[-1,-1,[]],[9,[[4,[1]]]],[[10,9],[[12,[11]]]],[[1,-1],5,13],[-1,-2,[],[]],0,0,0,[10,14],[15,1],[15,1],[15,1],[16,1],[15,1],[1,[[4,[17,3]]]],0,0,0,0,0,0,[-1,-2,[],[]],[-1,14,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,18,[]],[-1,19,[]],[-1,-2,[],[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[20,9,-1],[[4,[-2,21]]],[22,23],23],0,0,[[],24],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],[[-1,24],20,25],0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,18,[]],[-1,18,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[26,26],[[-1,-2],5,[],[]],0,0,[[],26],0,[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[27,26],28],[[28,[30,[29]]],[[4,[31,21]]]],[[28,[30,[29]],32],[[4,[31,21]]]],[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,18,[]],[-1,18,[]],[-1,-2,[],[]],[-1,-2,[],[]],[[28,[33,[29]]],[[4,[5,21]]]],0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[21,7],34],[[21,7],34],[-1,-1,[]],[3,21],[35,21],[36,21],[37,21],[-1,-2,[],[]],[21,[[12,[38]]]],[-1,14,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,18,[]],[-1,19,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[-1,[[4,[39]]],40],[-1,[[4,[41]]],40],[-1,[[4,[42]]],40],[-1,[[4,[43]]],40],0,[[39,7],34],[[39,7],34],[[41,7],34],[[41,7],34],[[42,7],34],[[42,7],34],[[43,7],34],[[43,7],34],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,[[39,-1],4,44],[[41,-1],4,44],[[42,-1],4,44],[[43,-1],4,44],[-1,14,[]],[-1,14,[]],[-1,14,[]],[-1,14,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,18,[]],[-1,18,[]],[-1,18,[]],[-1,18,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,[[45,-1],5,10],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[[],46],[-1,-1,[]],[-1,-1,[]],[[[47,[45]],27],[[4,[5,21]]]],[[45,[33,[29]]],41],[-1,-2,[],[]],[-1,-2,[],[]],0,[45,[[4,[1,21]]]],[[-1,46,48],[[47,[45]]],49],[[50,9,[12,[51]]],41],0,[45,5],[[[47,[45]]],[[4,[5,21]]]],0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,18,[]],[-1,18,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,[[10,9],[[12,[11]]]],[10,14]],"c":[],"p":[[6,"Endpoint",0],[6,"Addr",274],[6,"Error",275],[6,"Result",276],[1,"tuple"],[1,"bool"],[5,"Formatter",277],[5,"Error",277],[1,"str"],[10,"RPCService",269],[8,"RPCMethod",269],[6,"Option",278],[10,"Hasher",279],[5,"String",280],[6,"SocketAddr",281],[5,"SocketAddr",282],[1,"u16"],[5,"TypeId",283],[10,"Any",283],[5,"Client",68],[6,"Error",124],[10,"Serialize",284],[10,"DeserializeOwned",285],[5,"ClientConfig",68],[10,"ToConn",286],[5,"CodecConfig",92],[8,"Conn",286],[5,"Codec",92],[1,"u8"],[5,"Vec",287],[1,"usize"],[1,"u64"],[1,"slice"],[8,"Result",277],[5,"Error",288],[6,"Error",289],[5,"Error",290],[10,"Error",291],[5,"Request",154],[10,"Deserializer",285],[5,"Response",154],[5,"Error",154],[5,"Notification",154],[10,"Serializer",284],[5,"Server",233],[5,"ServerConfig",233],[5,"Arc",292],[8,"Executor",293],[10,"ToListener",294],[1,"i32"],[6,"Value",295],[8,"Result",124]],"b":[[38,"impl-Display-for-Endpoint"],[39,"impl-Debug-for-Endpoint"],[139,"impl-Debug-for-Error"],[140,"impl-Display-for-Error"],[142,"impl-From%3CNetError%3E-for-Error"],[143,"impl-From%3CError%3E-for-Error"],[144,"impl-From%3CError%3E-for-Error"],[145,"impl-From%3CError%3E-for-Error"],[178,"impl-Debug-for-Request"],[179,"impl-Display-for-Request"],[180,"impl-Display-for-Response"],[181,"impl-Debug-for-Response"],[182,"impl-Display-for-Error"],[183,"impl-Debug-for-Error"],[184,"impl-Debug-for-Notification"],[185,"impl-Display-for-Notification"]]}],\
+["karyon_net",{"doc":"","t":"GPPIKKPGPPPPPPPIGIPPIPPFPPKKPPFPFPMCHHHHCCOOOHHHHCMMMMOOCMMCMOOIKKHMMMMMGPGPIPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNPPPGPPPPPPPIPPPNNNNNNNNNNNNNNNNNNNKIKMHMMFFOHHOHOOOCCCCFNNHNONHNNNNONNNNNNOFFNONNNNHHNNONNHONNNNNONNNNNNNNNNNOFNNHNONHNNNNNNNNNNNNFNNHNONHNNNNONNNNNNO","n":["Addr","ChannelRecv","ChannelSend","Conn","ConnListener","Connection","Domain","Endpoint","Err","IO","InvalidAddress","InvalidDnsNameError","InvalidEndpoint","Ip","KaryonCore","Listener","NetError","NetResult","Ok","ParseEndpoint","Port","Rustls","Tcp","TcpConn","Timeout","Tls","ToConn","ToListener","TryFromEndpoint","Udp","UdpConn","Unix","UnixConn","Ws","accept","connection","dial","dial_tcp","dial_udp","dial_unix","endpoint","error","inner","inner","inner","listen","listen_tcp","listen_udp","listen_unix","listener","local_endpoint","local_endpoint","peer_endpoint","read","read","read","tls","to_conn","to_listener","transports","write","write","write","Conn","Connection","ToConn","dial","local_endpoint","peer_endpoint","read","to_conn","write","Addr","Domain","Endpoint","Ip","Port","Tcp","Tls","Udp","Unix","Ws","addr","borrow","borrow","borrow_decode","borrow_mut","borrow_mut","clone","clone","clone_into","clone_into","decode","encode","eq","eq","fmt","fmt","fmt","fmt","from","from","from_str","hash","hash","into","into","new_tcp_addr","new_tls_addr","new_udp_addr","new_unix_addr","new_ws_addr","port","to_owned","to_owned","to_string","to_string","try_from","try_from","try_into","try_into","type_id","type_id","value_as_any","value_as_any","vzip","vzip","ChannelRecv","ChannelSend","Err","Error","IO","InvalidAddress","InvalidDnsNameError","InvalidEndpoint","KaryonCore","Ok","ParseEndpoint","Result","Rustls","Timeout","TryFromEndpoint","borrow","borrow_mut","fmt","fmt","from","from","from","from","from","from","from","into","source","to_string","try_from","try_into","type_id","value_as_any","vzip","ConnListener","Listener","ToListener","accept","listen","local_endpoint","to_listener","TlsConn","TlsListener","acceptor","dial","dial_tls","inner","listen_tls","listener","read","write","tcp","tls","udp","unix","TcpConn","borrow","borrow_mut","dial_tcp","from","inner","into","listen_tcp","local_endpoint","new","peer_endpoint","read","read","to_conn","try_from","try_into","type_id","vzip","write","write","TlsConn","TlsListener","accept","acceptor","borrow","borrow","borrow_mut","borrow_mut","dial","dial_tls","from","from","inner","into","into","listen_tls","listener","local_endpoint","local_endpoint","new","peer_endpoint","read","read","to_conn","to_listener","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","write","write","UdpConn","borrow","borrow_mut","dial_udp","from","inner","into","listen_udp","local_endpoint","new","peer_endpoint","read","recv_from","send_to","to_conn","try_from","try_into","type_id","vzip","write","UnixConn","borrow","borrow_mut","dial_unix","from","inner","into","listen_unix","local_endpoint","new","peer_endpoint","read","read","to_conn","try_from","try_into","type_id","vzip","write","write"],"q":[[0,"karyon_net"],[63,"karyon_net::connection"],[72,"karyon_net::endpoint"],[127,"karyon_net::error"],[161,"karyon_net::listener"],[168,"karyon_net::tls"],[178,"karyon_net::transports"],[182,"karyon_net::transports::tcp"],[202,"karyon_net::transports::tls"],[237,"karyon_net::transports::udp"],[257,"karyon_net::transports::unix"],[277,"core::future::future"],[278,"alloc::boxed"],[279,"core::pin"],[280,"alloc::string"],[281,"async_net::tcp"],[282,"async_net::unix"],[283,"bincode::error"],[284,"core::result"],[285,"bincode::de"],[286,"bincode::de"],[287,"core::fmt"],[288,"core::fmt"],[289,"core::net::socket_addr"],[290,"std::os::unix::net::addr"],[291,"core::any"],[292,"core::any"],[293,"std::io::error"],[294,"async_channel"],[295,"async_channel"],[296,"rustls_pki_types::server_name"],[297,"core::error"],[298,"core::option"],[299,"rustls::client::client_conn"],[300,"rustls::server::server_conn"],[301,"async_net::tcp"],[302,"async_net::udp"],[303,"async_net::unix"]],"d":["Addr defines a type for an address, either IP or domain.","","","Alias for Box<dyn Connection>
","ConnListener is a generic network listener.","Connection is a generic network connection interface for …","","Endpoint defines generic network endpoints for karyon.","Contains the error value","","","","","","","Alias for Box<dyn ConnListener>
","Represents karyon’s Net Error","Represents karyon’s Net Result","Contains the success value","","Port defined as a u16.","","","TCP network connection implementation of the Connection
…","","","A trait for objects which can be converted to Conn
.","A trait for objects which can be converted to Listener
.","","","UDP network connection implementation of the Connection
…","","Unix domain socket implementation of the Connection
trait.","","","","Connects to the provided endpoint.","Connects to the given TCP address and port.","Connects to the given UDP address and port.","Connects to the given Unix socket path.","","","","","","Listens to the provided endpoint.","Listens on the given TCP address and port.","Listens on the given UDP address and port.","Listens on the given Unix socket path.","","Returns the local socket endpoint of this connection","","Returns the remote peer endpoint of this connection","Reads data from this connection. ","","","","","","","Writes data to this connection","","","Alias for Box<dyn Connection>
","Connection is a generic network connection interface for …","A trait for objects which can be converted to Conn
.","Connects to the provided endpoint.","Returns the local socket endpoint of this connection","Returns the remote peer endpoint of this connection","Reads data from this connection. ","","Writes data to this connection","Addr defines a type for an address, either IP or domain.","","Endpoint defines generic network endpoints for karyon.","","Port defined as a u16.","","","","","","Returns the Addr
of the endpoint.","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","Calls U::from(self)
.","Calls U::from(self)
.","Creates a new TCP endpoint from a SocketAddr
.","Creates a new TLS endpoint from a SocketAddr
.","Creates a new UDP endpoint from a SocketAddr
.","Creates a new Unix endpoint from a UnixSocketAddress
.","Creates a new WS endpoint from a SocketAddr
.","Returns the Port
of the endpoint.","","","","","","","","","","","","","","","","","Contains the error value","","","","","","","Contains the success value","","","","","","","","","","","","","","","Returns the argument unchanged.","","Calls U::from(self)
.","","","","","","","","ConnListener is a generic network listener.","Alias for Box<dyn ConnListener>
","A trait for objects which can be converted to Listener
.","","Listens to the provided endpoint.","","","TLS network connection implementation of the Connection
…","Tls network listener implementation of the Listener
…","","Connects to the given TLS endpoint, returns Conn
(…","Connects to the given TLS address and port.","","Listens on the given TLS address and port.","","","","","","","","TCP network connection implementation of the Connection
…","","","Connects to the given TCP address and port.","Returns the argument unchanged.","","Calls U::from(self)
.","Listens on the given TCP address and port.","","Creates a new TcpConn","","","","","","","","","","","TLS network connection implementation of the Connection
…","Tls network listener implementation of the Listener
…","","","","","","","Connects to the given TLS endpoint, returns Conn
(…","Connects to the given TLS address and port.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self)
.","Calls U::from(self)
.","Listens on the given TLS address and port.","","","","Creates a new TlsConn","","","","","","","","","","","","","","","","UDP network connection implementation of the Connection
…","","","Connects to the given UDP address and port.","Returns the argument unchanged.","","Calls U::from(self)
.","Listens on the given UDP address and port.","","Creates a new UdpConn","","","Receives a single datagram message. Returns the number of …","Sends data to the given address. Returns the number of …","","","","","","","Unix domain socket implementation of the Connection
trait.","","","Connects to the given Unix socket path.","Returns the argument unchanged.","","Calls U::from(self)
.","Listens on the given Unix socket path.","","Creates a new UnixConn","","","","","","","","","",""],"i":[0,38,38,0,0,0,20,0,7,38,38,38,38,20,38,0,0,0,7,38,0,38,5,0,38,5,0,0,38,5,0,5,0,5,1,0,0,0,0,0,0,0,8,9,11,0,0,0,0,0,14,1,14,14,8,11,0,17,18,0,14,8,11,0,0,0,0,14,14,14,17,14,0,20,0,20,0,5,5,5,5,5,5,5,20,20,5,20,5,20,5,20,20,20,5,20,5,5,20,20,5,20,5,5,20,5,20,5,5,5,5,5,5,5,20,5,20,5,20,5,20,5,20,5,20,5,20,38,38,7,0,38,38,38,38,38,7,38,0,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,0,0,0,1,0,1,18,0,0,50,0,0,48,0,50,48,48,0,0,0,0,0,8,8,0,8,8,8,0,8,8,8,8,8,8,8,8,8,8,8,8,0,0,50,50,48,50,48,50,0,0,48,50,48,48,50,0,50,48,50,48,48,48,48,48,50,48,50,48,50,48,50,48,50,48,48,0,9,9,0,9,9,9,0,9,9,9,9,9,9,9,9,9,9,9,9,0,11,11,0,11,11,11,0,11,11,11,11,11,11,11,11,11,11,11,11],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[1,[[4,[[3,[2]]]]]],0,[5,[[7,[6]]]],[5,[[7,[8]]]],[5,[[7,[9]]]],[10,[[7,[11]]]],0,0,0,0,0,[5,[[7,[[3,[1]]]]]],[5,[[7,[12]]]],[5,[[7,[9]]]],[10,[[7,[13]]]],0,[14,[[7,[5]]]],[1,[[7,[5]]]],[14,[[7,[5]]]],[[14,[16,[15]]],[[4,[[3,[2]]]]]],0,0,0,[17,6],[18,19],0,[[14,[16,[15]]],[[4,[[3,[2]]]]]],0,0,0,0,0,[5,[[7,[6]]]],[14,[[7,[5]]]],[14,[[7,[5]]]],[[14,[16,[15]]],[[4,[[3,[2]]]]]],[17,6],[[14,[16,[15]]],[[4,[[3,[2]]]]]],0,0,0,0,0,0,0,0,0,0,[5,[[7,[20]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[22,[20,21]]],23],[-1,-2,[],[]],[-1,-2,[],[]],[5,5],[20,20],[[-1,-2],24,[],[]],[[-1,-2],24,[],[]],[-1,[[22,[20,21]]],25],[[20,-1],[[22,[24,26]]],27],[[5,5],28],[[20,20],28],[[5,29],30],[[5,29],30],[[20,29],30],[[20,29],30],[-1,-1,[]],[-1,-1,[]],[31,[[22,[5,-1]]],[]],[[5,-1],24,32],[[20,-1],24,32],[-1,-2,[],[]],[-1,-2,[],[]],[33,5],[33,5],[33,5],[34,5],[33,5],[5,[[7,[35]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,10,[]],[-1,10,[]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,36,[]],[-1,36,[]],[-1,37,[]],[-1,37,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[38,29],30],[[38,29],30],[39,38],[40,38],[[[41,[-1]]],38,[]],[42,38],[43,38],[-1,-1,[]],[44,38],[-1,-2,[],[]],[38,[[46,[45]]]],[-1,10,[]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,36,[]],[-1,37,[]],[-1,-2,[],[]],0,0,0,[1,[[4,[[3,[2]]]]]],[5,[[7,[[3,[1]]]]]],[1,[[7,[5]]]],[18,19],0,0,0,[[5,47,31],[[7,[[3,[14]]]]]],[[5,47,31],[[7,[48]]]],0,[[5,49],[[7,[50]]]],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[5,[[7,[8]]]],[-1,-1,[]],0,[-1,-2,[],[]],[5,[[7,[12]]]],[8,[[7,[5]]]],[51,8],[8,[[7,[5]]]],[[8,[16,[15]]],[[4,[[3,[2]]]]]],0,[8,[[3,[14]]]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,36,[]],[-1,-2,[],[]],[[8,[16,[15]]],[[4,[[3,[2]]]]]],0,0,0,[50,[[4,[[3,[2]]]]]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[5,47,31],[[7,[[3,[14]]]]]],[[5,47,31],[[7,[48]]]],[-1,-1,[]],[-1,-1,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[[5,49],[[7,[50]]]],0,[48,[[7,[5]]]],[50,[[7,[5]]]],[[51,[52,[51]]],48],[48,[[7,[5]]]],[[48,[16,[15]]],[[4,[[3,[2]]]]]],0,[48,[[3,[14]]]],[50,[[3,[1]]]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,36,[]],[-1,36,[]],[-1,-2,[],[]],[-1,-2,[],[]],[[48,[16,[15]]],[[4,[[3,[2]]]]]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[5,[[7,[9]]]],[-1,-1,[]],0,[-1,-2,[],[]],[5,[[7,[9]]]],[9,[[7,[5]]]],[53,9],[9,[[7,[5]]]],[[9,[16,[15]]],[[4,[[3,[2]]]]]],[[9,[16,[15]]],[[7,[[24,[54,5]]]]]],[[9,[16,[15]],5],[[7,[54]]]],[9,[[3,[14]]]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,36,[]],[-1,-2,[],[]],[[9,[16,[15]]],[[4,[[3,[2]]]]]],0,[-1,-2,[],[]],[-1,-2,[],[]],[10,[[7,[11]]]],[-1,-1,[]],0,[-1,-2,[],[]],[10,[[7,[13]]]],[11,[[7,[5]]]],[55,11],[11,[[7,[5]]]],[[11,[16,[15]]],[[4,[[3,[2]]]]]],0,[11,[[3,[14]]]],[-1,[[22,[-2]]],[],[]],[-1,[[22,[-2]]],[],[]],[-1,36,[]],[-1,-2,[],[]],[[11,[16,[15]]],[[4,[[3,[2]]]]]],0],"c":[],"p":[[10,"ConnListener",161],[10,"Future",277],[5,"Box",278],[5,"Pin",279],[6,"Endpoint",72],[8,"Conn",63],[8,"Result",127],[5,"TcpConn",182],[5,"UdpConn",237],[5,"String",280],[5,"UnixConn",257],[5,"TcpListener",281],[5,"UnixListener",282],[10,"Connection",63],[1,"u8"],[1,"slice"],[10,"ToConn",63],[10,"ToListener",161],[8,"Listener",161],[6,"Addr",72],[6,"DecodeError",283],[6,"Result",284],[10,"BorrowDecoder",285],[1,"tuple"],[10,"Decoder",285],[6,"EncodeError",283],[10,"Encoder",286],[1,"bool"],[5,"Formatter",287],[8,"Result",287],[1,"str"],[10,"Hasher",288],[6,"SocketAddr",289],[5,"SocketAddr",290],[8,"Port",72],[5,"TypeId",291],[10,"Any",291],[6,"Error",127],[6,"Error",292],[5,"Error",293],[5,"SendError",294],[5,"RecvError",294],[6,"Error",295],[5,"InvalidDnsNameError",296],[10,"Error",297],[6,"Option",298],[5,"ClientConfig",299],[5,"TlsConn",202],[5,"ServerConfig",300],[5,"TlsListener",202],[5,"TcpStream",281],[6,"TlsStream",301],[5,"UdpSocket",302],[1,"usize"],[5,"UnixStream",282]],"b":[[96,"impl-Display-for-Endpoint"],[97,"impl-Debug-for-Endpoint"],[98,"impl-Display-for-Addr"],[99,"impl-Debug-for-Addr"],[144,"impl-Display-for-Error"],[145,"impl-Debug-for-Error"],[146,"impl-From%3CError%3E-for-Error"],[147,"impl-From%3CError%3E-for-Error"],[148,"impl-From%3CSendError%3CT%3E%3E-for-Error"],[149,"impl-From%3CRecvError%3E-for-Error"],[150,"impl-From%3CError%3E-for-Error"],[152,"impl-From%3CInvalidDnsNameError%3E-for-Error"]]}],\
+["karyon_p2p",{"doc":"A lightweight, extensible, and customizable peer-to-peer …","t":"IIFPPFPPPPPPPPPPPGPPPPFPPPPPPFPPOOCOCCOCCCOOOCOOOOCOOOOOOCCOOCOCOOOOOCCOOOOOOCOCCOCOIFONNNNOONNNNONONNONOONNNNNNFKNNONNNNNNNNNNNNFONNNOONOONOOOOOOOOOOOOOOONNNONGFPFPNNNNNNNNOOOONNNNNNNNNNNONNNNNNNNNNNNNNNFJNNNNONONNOOONNONNNNIFONNOONNONONOCONOOONCONNNNOONNNNNFSNNONONNNONOOONONNNNNNNNNNNNNONNNNSSFFFNNNNNNNNNNONNNNNNONNNNNNNNONNONNNNNNNOONNNNNNNNNNNNNNNPPPPPGPPPPPPPPPPPPPPPPIPPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNFNNOONNONNONNNONNNNPFSSFGFPFPFPFPFPFPFFFPPOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNQONNNNNNNNNNNNOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOONNNNNNNNNNNNPPPGPPPPPGPPPPPFGPPGPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNIFEONNNONONNNONNNCNOOOOONNNONNNNOONNNNFNNNNNNNNNNNNNNNNNNNNNNNNIFIOONNNNOONNONONNONNONOOONOONNNNNNONNNNNNIPKIGIPONNNNNNMNNOOMNNNNNOMNECSPFGPNNNNNNNNNNNNNNNNOONONNONNNNNNNNNNNGPEEEESEEPEPESSEPFSEENNNNNCNNONNCNNNNNNNONNNHNNNNNNNNNNNNESSFFSSISSSSNNNNNNNNNNOOONNNNNNNNNNNONNNNONNNNNNNNNNNNNFSIONNNNNNONNNNNNOONNNNNNHFNNNNNNONNOONNNNNSSJFJJJFNNNNNNNNHNNHONNNHHNNNNNNNNHHNNNNNNNNFFNNNNNNNNNNNNNNNNNNNNOONOONNNNNNNNNONNHNN","n":["ArcBackend","ArcPeer","Backend","ChannelRecv","ChannelSend","Config","Config","Discovery","IO","IncompatiblePeer","IncompatibleVersion","InvalidDnsNameError","InvalidMsg","InvalidPongMsg","KaryonCore","KaryonNet","Lookup","P2pError","ParseError","ParseFloatError","ParseIntError","PeerAlreadyConnected","PeerID","PeerShutdown","Rcgen","Rustls","SemverError","TryFromPublicKey","UnsupportedProtocol","Version","X509Parser","Yasna","alloc","alloc","backend","bootstrap_peers","codec","config","config","connection","connector","discovery","discovery","discovery_port","enable_tls","error","handshake_timeout","inbound_slots","key_pair","listen_endpoint","listener","lookup_connect_retries","lookup_connection_lifespan","lookup_inbound_slots","lookup_outbound_slots","lookup_response_timeout","max_connect_retries","message","monitor","monitor","outbound_slots","peer","peer_endpoints","peer_pool","peer_pool","phantom","phantom","ping_interval","ping_timeout","protocol","protocols","ptr","ptr","refresh_connect_retries","refresh_interval","refresh_response_timeout","req","routing_table","seeding_interval","slots","tls_config","v","version","version","ArcBackend","Backend","alloc","attach_protocol","borrow","borrow_mut","config","config","discovery","from","inbound_slots","into","key_pair","key_pair","monitor","monitor","new","outbound_slots","peer_pool","peers","phantom","ptr","run","shutdown","try_from","try_into","type_id","vzip","Codec","CodecMsg","borrow","borrow_mut","conn","from","into","new","read","read_exact","read_timeout","try_from","try_into","type_id","vzip","write","write_all","Config","bootstrap_peers","borrow","borrow_mut","default","discovery_port","enable_tls","from","handshake_timeout","inbound_slots","into","listen_endpoint","lookup_connect_retries","lookup_connection_lifespan","lookup_inbound_slots","lookup_outbound_slots","lookup_response_timeout","max_connect_retries","outbound_slots","peer_endpoints","ping_interval","ping_timeout","refresh_connect_retries","refresh_interval","refresh_response_timeout","seeding_interval","try_from","try_into","type_id","version","vzip","ConnDirection","ConnQueue","Inbound","NewConn","Outbound","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","clone","clone_into","conn","conn_available","direction","disconnect_signal","fmt","fmt","from","from","from","handle","into","into","into","new","next","queue","to_owned","to_string","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","value_as_any","vzip","vzip","vzip","Connector","DNS_NAME","borrow","borrow_mut","connect","connect_with_cback","connection_slots","dial","enable_tls","from","into","key_pair","max_retries","monitor","new","shutdown","task_group","try_from","try_into","type_id","vzip","ArcDiscovery","Discovery","alloc","borrow","borrow_mut","config","conn_queue","connect","connect_loop","connector","from","inbound_slots","into","listener","lookup","lookup_service","new","outbound_slots","phantom","ptr","random_entry","refresh","refresh_service","shutdown","start","start_listener","start_seeding","table","task_group","try_from","try_into","type_id","update_entry","vzip","LookupService","MAX_PEERS_IN_PEERSMSG","borrow","borrow_mut","config","connect","connector","from","handle_inbound","handle_outbound","id","into","listen_endpoint","listener","monitor","new","outbound_slots","random_lookup","self_lookup","send_findpeer_msg","send_peer_msg","send_peers_msg","send_ping_msg","send_pong_msg","send_shutdown_msg","set_listen_endpoint","shutdown","start","start_listener","start_lookup","table","try_from","try_into","type_id","vzip","MAX_FAILURES","PINGMSG_SIZE","PingMsg","PongMsg","RefreshService","borrow","borrow","borrow","borrow_decode","borrow_decode","borrow_mut","borrow_mut","borrow_mut","clone","clone_into","config","connect","decode","decode","do_refresh","encode","encode","executor","fmt","fmt","from","from","from","into","into","into","listen_endpoint","listen_loop","listen_to_ping_msg","monitor","new","refresh_entry","refresh_loop","send_ping_msg","set_listen_endpoint","shutdown","start","table","task_group","to_owned","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","value_as_any","value_as_any","vzip","vzip","vzip","ChannelRecv","ChannelSend","Config","Discovery","Err","Error","IO","IncompatiblePeer","IncompatibleVersion","InvalidDnsNameError","InvalidMsg","InvalidPongMsg","KaryonCore","KaryonNet","Lookup","Ok","ParseError","ParseFloatError","ParseIntError","PeerAlreadyConnected","PeerShutdown","Rcgen","Result","Rustls","SemverError","TryFromPublicKey","UnsupportedProtocol","X509Parser","Yasna","borrow","borrow_mut","fmt","fmt","from","from","from","from","from","from","from","from","from","from","from","from","from","from","into","source","to_string","try_from","try_into","type_id","value_as_any","vzip","Listener","borrow","borrow_mut","connection_slots","enable_tls","from","into","key_pair","listen_loop","listend","monitor","new","shutdown","start","task_group","try_from","try_into","type_id","vzip","FindPeer","FindPeerMsg","MAX_ALLOWED_MSG_SIZE","MSG_HEADER_SIZE","NetMsg","NetMsgCmd","NetMsgHeader","Peer","PeerMsg","Peers","PeersMsg","Ping","PingMsg","Pong","PongMsg","Protocol","ProtocolMsg","Shutdown","ShutdownMsg","VerAckMsg","VerMsg","Verack","Version","ack","addr","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_decode","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","command","decode","decode","decode","decode","decode","decode","decode","decode","decode","decode","decode","decode","discovery_port","encode","encode","encode","encode","encode","encode","encode","encode","encode","encode","encode","encode","eq","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","from","from","from","from","get_msg_payload","header","into","into","into","into","into","into","into","into","into","into","into","into","nonce","payload","payload","payload_size","peer_id","peer_id","peer_id","port","protocol_id","protocols","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","value_as_any","version","version","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","vzip","AcceptFailed","Accepted","Conn","ConnEvent","ConnectFailed","ConnectRetried","Connected","Disconnected","Discovery","DiscoveryEvent","ListenFailed","Listening","LookupFailed","LookupStarted","LookupSucceeded","Monitor","MonitorEvent","NewPeer","PeerPool","PeerPoolEvent","RefreshStarted","RemovePeer","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","inner","into","into","into","into","into","new","notify","subscribe","to_owned","to_owned","to_owned","to_owned","to_string","to_string","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","value_as_any","value_as_any","value_as_any","value_as_any","vzip","vzip","vzip","vzip","vzip","ArcPeer","Peer","PeerID","alloc","borrow","borrow_mut","broadcast","codec","config","conn_direction","direction","from","id","id","into","is_inbound","new","peer_id","peer_pool","peer_pool","phantom","protocol_events","protocol_ids","ptr","read_loop","register_listener","remote_endpoint","remote_endpoint","run","send","shutdown","start_protocols","stop_chan","task_group","try_from","try_into","type_id","vzip","PeerID","borrow","borrow_decode","borrow_mut","clone","clone_into","decode","encode","eq","fmt","fmt","from","from","hash","into","new","random","to_owned","to_string","try_from","try_from","try_into","type_id","value_as_any","vzip","ArcPeerPool","PeerPool","WeakPeerPool","alloc","alloc","attach_protocol","borrow","borrow_mut","broadcast","config","conn_queue","contains_peer","do_handshake","executor","from","id","into","listen_loop","monitor","new","new_peer","peers","peers_len","phantom","protocol_versions","protocols","protocols_match","ptr","ptr","remove_peer","send_verack","send_vermsg","setup_protocols","shutdown","start","task_group","try_from","try_into","type_id","vzip","wait_verack","wait_vermsg","ArcProtocol","Message","Protocol","ProtocolConstructor","ProtocolEvent","ProtocolID","Shutdown","alloc","borrow","borrow_mut","clone","clone_into","fmt","from","id","id","into","phantom","ptr","start","to_owned","try_from","try_into","type_id","value_as_any","vec","version","vzip","PingProtocol","ping","MAX_FAILUERS","Ping","PingProtocol","PingProtocolMsg","Pong","borrow","borrow","borrow_decode","borrow_mut","borrow_mut","clone","clone_into","decode","encode","fmt","from","from","id","into","into","new","peer","ping_interval","ping_loop","ping_timeout","recv_loop","start","task_group","to_owned","try_from","try_from","try_into","try_into","type_id","type_id","value_as_any","version","vzip","vzip","AddEntryResult","Added","Bucket","BucketEntry","CONNECTED_ENTRY","DISCONNECTED_ENTRY","DISTANCE_LIMIT","Entry","EntryStatusFlag","Exists","INCOMPATIBLE_ENTRY","Ignored","Key","MAX_MATCHED_SUBNET_IN_BUCKET","MAX_MATCHED_SUBNET_IN_TABLE","PENDING_ENTRY","Restricted","RoutingTable","TABLE_SIZE","UNREACHABLE_ENTRY","UNSTABLE_ENTRY","add_entry","borrow","borrow","borrow_mut","borrow_mut","bucket","bucket_index","bucket_indexes","buckets","closest_entries","contains_key","entry","fmt","fmt","from","from","into","into","iter","key","new","random_entry","remove_entry","subnet_match","subnet_restricted","try_from","try_from","try_into","try_into","type_id","type_id","update_entry","value_as_any","value_as_any","vzip","vzip","xor_distance","ALL_ENTRY","BUCKET_SIZE","Bucket","BucketEntry","CONNECTED_ENTRY","DISCONNECTED_ENTRY","EntryStatusFlag","INCOMPATIBLE_ENTRY","PENDING_ENTRY","UNREACHABLE_ENTRY","UNSTABLE_ENTRY","add","borrow","borrow","borrow_mut","borrow_mut","clone","clone","clone_into","clone_into","contains_key","entries","entry","failures","fmt","fmt","from","from","into","into","is_connected","is_incompatible","is_unreachable","is_unstable","iter","last_seen","len","new","random_iter","remove","status","to_owned","to_owned","try_from","try_from","try_into","try_into","type_id","type_id","update_entry","value_as_any","value_as_any","vzip","vzip","Entry","KEY_SIZE","Key","addr","borrow","borrow_decode","borrow_mut","clone","clone_into","decode","discovery_port","encode","eq","fmt","from","from","into","key","port","to_owned","try_from","try_into","type_id","value_as_any","vzip","xor_distance","ConnectionSlots","add","borrow","borrow_mut","from","into","load","max_slots","new","remove","signal","slots","try_from","try_into","type_id","vzip","wait_for_slot","BAD_ENCODING_ERR","BAD_SIGNATURE_ERR","CIPHER_SUITES","CliCertVerifier","KX_GROUPS","PROTOCOL_VERSIONS","SIGNATURE_SCHEMES","SrvrCertVerifier","borrow","borrow","borrow_mut","borrow_mut","fmt","fmt","from","from","generate_cert","into","into","parse_cert","peer_id","root_hint_subjects","supported_verify_schemes","supported_verify_schemes","tls_client_config","tls_server_config","try_from","try_from","try_into","try_into","type_id","type_id","value_as_any","value_as_any","verify_cert","verify_cert_signature","verify_client_cert","verify_server_cert","verify_tls12_signature","verify_tls12_signature","verify_tls13_signature","verify_tls13_signature","vzip","vzip","Version","VersionInt","borrow","borrow","borrow_decode","borrow_mut","borrow_mut","clone","clone","clone_into","clone_into","decode","encode","fmt","fmt","fmt","from","from","from_str","from_str","into","into","major","minor","new","patch","req","to_owned","to_owned","to_string","try_from","try_from","try_into","try_into","type_id","type_id","v","value_as_any","value_as_any","version_match","vzip","vzip"],"q":[[0,"karyon_p2p"],[84,"karyon_p2p::backend"],[112,"karyon_p2p::codec"],[129,"karyon_p2p::config"],[160,"karyon_p2p::connection"],[204,"karyon_p2p::connector"],[225,"karyon_p2p::discovery"],[259,"karyon_p2p::discovery::lookup"],[294,"karyon_p2p::discovery::refresh"],[353,"karyon_p2p::error"],[408,"karyon_p2p::listener"],[427,"karyon_p2p::message"],[653,"karyon_p2p::monitor"],[750,"karyon_p2p::peer"],[788,"karyon_p2p::peer::peer_id"],[813,"karyon_p2p::peer_pool"],[855,"karyon_p2p::protocol"],[883,"karyon_p2p::protocols"],[885,"karyon_p2p::protocols::ping"],[924,"karyon_p2p::routing_table"],[982,"karyon_p2p::routing_table::bucket"],[1036,"karyon_p2p::routing_table::entry"],[1062,"karyon_p2p::slots"],[1079,"karyon_p2p::tls_config"],[1123,"karyon_p2p::version"],[1165,"core::result"],[1166,"core::ops::function"],[1167,"core::marker"],[1168,"core::marker"],[1169,"karyon_core::crypto::key_pair"],[1170,"karyon_core::pubsub"],[1171,"karyon_core"],[1172,"core::any"],[1173,"karyon_net::connection"],[1174,"alloc::boxed"],[1175,"core::time"],[1176,"core::fmt"],[1177,"core::fmt"],[1178,"core::any"],[1179,"core::option"],[1180,"core::future::future"],[1181,"core::ops::function"],[1182,"async_lock::mutex"],[1183,"bincode::error"],[1184,"bincode::de"],[1185,"bincode::de"],[1186,"karyon_net::endpoint"],[1187,"rustls_pki_types::server_name"],[1188,"rcgen::error"],[1189,"std::io::error"],[1190,"semver::parse"],[1191,"async_channel"],[1192,"karyon_core::error"],[1193,"yasna::reader::error"],[1194,"x509_parser::error"],[1195,"async_channel"],[1196,"karyon_net::error"],[1197,"core::num::error"],[1198,"core::num::dec2flt"],[1199,"core::error"],[1200,"karyon_net::listener"],[1201,"core::clone"],[1202,"alloc::sync"],[1203,"core::hash"],[1204,"karyon_core::crypto::key_pair"],[1205,"core::pin"],[1206,"async_channel"],[1207,"rustls_pki_types"],[1208,"rustls_pki_types"],[1209,"rustls::msgs::handshake"],[1210,"rustls::enums"],[1211,"rustls::client::client_conn"],[1212,"rustls::server::server_conn"],[1213,"rustls_pki_types"],[1214,"rustls_pki_types::server_name"]],"d":["","","Backend serves as the central entry point for initiating …","","","the Configuration for the P2P network.","","","","","","","","","","","","Represents karyon’s p2p Error.","","","","","Represents a unique identifier for a peer.","","","","","","","Represents the network version and protocol version used …","","","","","","A list of bootstrap peers for the seeding process.","","","The Configuration for the P2P network.","","","","Discovery instance.","TCP/UDP port for lookup and refresh processes.","Enables TLS for all connections.","","Timeout duration for the handshake with new peers, in …","The number of available inbound slots for incoming …","Identity Key pair","An optional listening endpoint to accept incoming …","","The maximum number of retries for outbound connection …","Maximum allowable time for a live connection with a peer …","The number of available inbound slots for incoming …","The number of available outbound slots for outgoing …","Timeout duration for a peer response during the lookup …","The maximum number of retries for outbound connection …","","Responsible for network and system monitoring. Read More
","Responsible for network and system monitoring.","The number of available outbound slots for outgoing …","","A list of endpoints representing peers that the Discovery
…","","PeerPool instance.","","","Interval at which the ping protocol sends ping messages to …","Timeout duration for receiving the pong message …","Defines the protocol trait. Read More
","","","","The maximum number of retries for outbound connection …","Interval at which the table refreshes its entries, in …","Timeout duration for a peer response during the table …","","","Time interval, in seconds, at which the Discovery restarts …","","","","","Represents the network version.","","Backend serves as the central entry point for initiating …","","Attach a custom protocol to the network","","","Returns the Config
.","The Configuration for the P2P network.","Discovery instance.","Returns the argument unchanged.","Returns the number of occupied inbound slots.","Calls U::from(self)
.","Returns the KeyPair
.","Identity Key pair","Subscribes to the monitor to receive network events.","Responsible for network and system monitoring.","Creates a new Backend.","Returns the number of occupied outbound slots.","PeerPool instance.","Returns the number of currently connected peers.","","","Run the Backend, starting the PeerPool and Discovery …","Shuts down the Backend.","","","","","A Codec working with generic network connections.","","","","","Returns the argument unchanged.","Calls U::from(self)
.","Creates a new Codec.","Reads a message of type NetMsg
from the connection.","Reads the exact number of bytes required to fill buf
.","Reads a message of type NetMsg
with the given timeout.","","","","","Writes a message of type T
to the connection.","Writes an entire buffer into the connection.","the Configuration for the P2P network.","A list of bootstrap peers for the seeding process.","","","","TCP/UDP port for lookup and refresh processes.","Enables TLS for all connections.","Returns the argument unchanged.","Timeout duration for the handshake with new peers, in …","The number of available inbound slots for incoming …","Calls U::from(self)
.","An optional listening endpoint to accept incoming …","The maximum number of retries for outbound connection …","Maximum allowable time for a live connection with a peer …","The number of available inbound slots for incoming …","The number of available outbound slots for outgoing …","Timeout duration for a peer response during the lookup …","The maximum number of retries for outbound connection …","The number of available outbound slots for outgoing …","A list of endpoints representing peers that the Discovery
…","Interval at which the ping protocol sends ping messages to …","Timeout duration for receiving the pong message …","The maximum number of retries for outbound connection …","Interval at which the table refreshes its entries, in …","Timeout duration for a peer response during the table …","Time interval, in seconds, at which the Discovery restarts …","","","","Represents the network version.","","Defines the direction of a network connection.","Connection queue","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Push a connection into the queue and wait for the …","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","Receive the next connection in the queue","","","","","","","","","","","","","","","","","Responsible for creating outbound connections with other …","","","","Establish a connection to the specified endpoint
. If the …","Establish a connection to the given endpoint
. For each new …","Manages available outbound slots.","","Enables secure connection.","Returns the argument unchanged.","Calls U::from(self)
.","Identity Key pair","The maximum number of retries allowed before successfully …","Responsible for network and system monitoring.","Creates a new Connector","Shuts down the connector","Managing spawned tasks.","","","","","","","","","","Holds the configuration for the P2P network.","Connection queue","Connect to the given endpoint using the connector","This method will attempt to connect to a peer in the …","Connector","Returns the argument unchanged.","Inbound slots.","Calls U::from(self)
.","Listener","","Lookup Service","Creates a new Discovery","Outbound slots.","","","Returns a random entry from routing table.","","Refresh Service","Shuts down the discovery","Start the Discovery","Start a listener and on success, return the resolved …","Starts seeding process.","Routing table","Managing spawned tasks.","","","","Update the entry status ","","","Maximum number of peers that can be returned in a PeersMsg.","","","Holds the configuration for the P2P network.","Connects to the given endpoint and initiates a lookup …","Connector","Returns the argument unchanged.","Handles inbound connection","Handles outbound connection","Peer’s ID","Calls U::from(self)
.","Resolved listen endpoint","Listener","Responsible for network and system monitoring.","Creates a new lookup service","Outbound slots.","Starts a random lookup","Starts a self lookup","Sends a FindPeer msg and wait to receivet the Peers msg.","Sends a Peer msg.","Sends a Peers msg.","Sends a Ping msg and wait to receive the Pong message.","Sends a Pong msg","Sends a Shutdown msg.","Set the resolved listen endpoint.","Shuts down the lookup service.","Start the lookup service.","Start a listener.","Starts iterative lookup and populate the routing table.","Routing Table","","","","","Maximum failures for an entry before removing it from the …","Ping message size","","","","","","","","","","","","","","Holds the configuration for the P2P network.","Initiates a UDP connection with the entry and attempts to …","","","Iterates over the entries and spawns a new task for each …","","","A global executor","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Resolved listen endpoint","Set up a UDP listener and start listening for Ping …","Listen to receive a Ping message and respond with a Pong …","Responsible for network and system monitoring.","Creates a new refresh service","Initiates refresh for a specific entry within the routing …","Initiates periodic refreshing of the routing table. This …","Sends a Ping msg and wait to receive the Pong message.","Set the resolved listen endpoint.","Shuts down the refresh service","Start the refresh service","Routing table","Managing spawned tasks.","","","","","","","","","","","","","","","","","","","","Contains the error value","Represents karyon’s p2p Error.","","","","","","","","","","Contains the success value","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","","","","","","","","","","Calls U::from(self)
.","","","","","","","","Responsible for creating inbound connections with other …","","","Manages available inbound slots.","Enables secure connection.","Returns the argument unchanged.","Calls U::from(self)
.","Identity Key pair","","","Responsible for network and system monitoring.","Creates a new Listener","Shuts down the listener","Starts a listener on the given endpoint
. For each incoming …","Managing spawned tasks.","","","","","","FindPeer message used to find a specific peer.","The maximum allowed size for a message in bytes.","The size of the message header, in bytes.","Defines the main message in the karyon p2p network.","Defines message commands.","Represents the header of a message.","","PeerMsg containing information about a peer.","","PeersMsg a list of PeerMsg
.","","Ping message with a nonce and version information.","","Ping message with a nonce.","","Defines a message related to a specific protocol.","","Shutdown message.","VerAck message acknowledges the receipt of a Version …","Version message, providing information about a peer’s …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Returns the argument unchanged.","","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Defines connection-related events.","","","","","","Defines Discovery
events.","","","","","","Responsible for network and system monitoring.","Defines various type of event that can be monitored.","","","Defines PeerPool
events.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Calls U::from(self)
.","Creates a new Monitor","Sends a new monitor event to all subscribers.","Subscribes to listen to new events.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Broadcast a message to all connected peers using the …","Holds the Codec for the peer connection","Returns the Config
instance.","The direction of the connection, either Inbound
or Outbound
","Returns the direction of the connection, which can be …","Returns the argument unchanged.","Return the peer’s ID","Peer’s ID","Calls U::from(self)
.","Check if the connection is Inbound","Creates a new peer","","","A weak pointer to PeerPool
","","EventSys
responsible for sending events to the protocols.","A list of protocol IDs","","Start a read loop to handle incoming messages from the …","Registers a listener for the given Protocol P
.","Returns the remote endpoint for the peer","Remote endpoint for the peer","Run the peer","Send a message to the peer connection using the specified …","Shuts down the peer","Start running the protocols for this peer connection.","This channel is used to send a stop signal to the read …","Managing spawned tasks.","","","","","Represents a unique identifier for a peer.","","","","","","","","","","","","Returns the argument unchanged.","","Calls U::from(self)
.","Creates a new PeerID.","Generates a random PeerID.","","","","","","","","","","","","","","Attach a custom protocol to the network","","","Broadcast a message to all connected peers using the …","The Configuration for the P2P network.","Connection queue","Checks if the peer list contains a peer with the given …","Initiate a handshake with a connection.","A global Executor","Returns the argument unchanged.","Peer’s ID","Calls U::from(self)
.","Listens to a new connection from the connection queue","Responsible for network and system monitoring.","Creates a new PeerPool","Add a new peer to the peer list.","Holds the running peers.","Returns the number of currently connected peers.","","Hashmap contains protocol IDs and their versions.","Hashmap contains protocol constructors.","Check if the new connection has compatible protocols.","","","Shuts down the peer and remove it from the peer list.","Send a Verack message","Send a Version message","Attach the core protocols.","Shuts down","Start","Managing spawned tasks.","","","","","Wait for a Verack message","Wait for a Version message","","Message event, contains a vector of bytes.","The Protocol trait defines the interface for core protocols","","Protocol event","","Shutdown event signals the protocol to gracefully shut …","","","","","","","Returns the argument unchanged.","Returns the unique ProtocolID associated with the protocol.","","Calls U::from(self)
.","","","Start the protocol","","","","","","","Returns the version of the protocol.","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self)
.","Calls U::from(self)
.","","","","","","","","","","","","","","","","","","","","Represents the possible result when adding a new entry.","The entry is added.","","","","","The distance limit for the closest buckets.","","","The entry is already exists.","","The entry is ignored.","","The maximum number of matched subnets allowed within a …","The maximum number of matched subnets across the entire …","","The entry is restricted and not allowed.","This is a modified version of the Kademlia Distributed …","The total number of buckets in the routing table.","","","Adds a new entry to the table and returns a result …","","","","","","","Returns a list of bucket indexes that are closest to the …","","Returns a list of the closest entries to the given target …","Check if the table contains the given key.","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self)
.","Calls U::from(self)
.","Returns an iterator of entries.","","Creates a new RoutingTable","Returns a random entry from the routing table.","Removes an entry with the given key from the routing …","Check if two addresses belong to the same subnet.","This function iterate through the routing table and counts …","","","","","","","Updates the status of an entry in the routing table …","","","","","","","The number of entries that can be stored within a single …","A Bucket represents a group of entries in the routing …","A BucketEntry represents a peer in the routing table.","The entry is connected.","The entry is disconnected. This will increase the failure …","BITFLAGS represent the status of an Entry within a bucket.","The entry is incompatible. This entry will not contribute …","The entry is ready to reconnect, meaning it has either …","The entry is unreachable. This will increase the failure …","The entry is unstable. This will increase the failure …","Add an entry to the bucket.","","","","","","","","","Check if the bucket contains the given key.","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Calls U::from(self)
.","Calls U::from(self)
.","","","","","Returns an iterator over the entries in the bucket.","","Get the number of entries in the bucket.","Creates a new empty Bucket","Returns an iterator of entries in random order.","Remove an entry.","","","","","","","","","","Updates the status of an entry in the bucket identified by …","","","","","An Entry represents a peer in the routing table.","Specifies the size of the key, in bytes.","The unique key identifying the peer.","The IP address of the peer.","","","","","","","UDP/TCP port","","","","Returns the argument unchanged.","","Calls U::from(self)
.","The unique key identifying the peer.","TCP port","","","","","","","Calculates the XOR distance between two provided keys.","Manages available inbound and outbound slots.","Increases the occupied slots by one.","","","Returns the argument unchanged.","Calls U::from(self)
.","Returns the number of occupied slots","The maximum number of slots.","Creates a new ConnectionSlots","Decreases the occupied slots by one and notifies the …","A condvar for notifying when a slot become available.","The number of occupied slots","","","","","Waits for a slot to become available.","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Generates a certificate and returns both the certificate …","Calls U::from(self)
.","Calls U::from(self)
.","Parses the given x509 certificate.","","","","","Returns a TLS client configuration.","Returns a TLS server configuration.","","","","","","","","","Verifies the given certification.","Verifies the signature of the given certificate.","","","","","","","","","Represents the network version and protocol version used …","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","Calls U::from(self)
.","Calls U::from(self)
.","","","Creates a new Version","","","","","","","","","","","","","","","Check if a version satisfies a version request.","",""],"i":[0,0,0,3,3,0,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,18,5,0,11,0,0,1,0,0,0,1,11,11,0,11,11,1,11,0,11,11,11,11,11,11,0,0,1,11,0,11,0,1,18,5,11,11,0,0,18,5,11,11,11,111,0,11,0,0,111,0,11,0,0,18,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,18,18,1,1,1,1,1,1,0,0,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,0,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,29,0,29,34,32,29,34,32,29,29,29,34,32,34,34,29,29,34,32,29,32,34,32,29,32,32,32,29,29,34,32,29,34,32,29,34,32,29,29,34,32,29,0,0,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,0,0,135,46,46,46,46,46,46,46,46,46,46,46,0,46,46,46,135,135,46,0,46,46,46,46,46,46,46,46,46,46,46,46,0,0,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,0,0,0,0,0,60,56,59,56,59,60,56,59,56,56,60,60,56,59,60,56,59,60,56,59,60,56,59,60,56,59,60,60,60,60,60,60,60,60,60,60,60,60,60,56,60,56,59,60,56,59,60,56,59,56,59,60,56,59,3,3,3,3,136,0,3,3,3,3,3,3,3,3,3,136,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,27,0,0,0,0,0,0,27,0,27,0,27,0,27,0,27,0,27,0,0,0,27,27,88,50,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,50,23,85,27,86,87,88,89,90,50,85,23,85,27,86,87,88,89,90,91,92,50,54,50,23,85,27,86,87,88,89,90,91,92,50,54,50,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,91,92,50,50,54,0,23,23,85,27,86,87,88,89,90,91,92,50,54,90,23,86,85,87,88,50,50,86,87,23,85,27,86,87,88,89,90,50,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,91,92,50,54,23,85,27,86,87,88,89,90,91,92,50,54,87,90,23,85,27,86,87,88,89,90,91,92,50,54,93,93,15,0,93,93,93,93,15,0,93,93,95,95,95,0,0,94,15,0,95,94,45,15,93,94,95,45,15,93,94,95,15,93,94,95,15,93,94,95,15,15,93,93,94,94,95,95,45,15,15,15,15,93,94,95,45,45,15,93,94,95,45,45,45,15,93,94,95,15,93,94,95,45,15,93,94,95,45,15,93,94,95,45,15,93,94,95,15,93,94,95,45,15,93,94,95,0,0,0,5,96,96,96,96,96,96,96,96,96,96,96,96,96,0,96,96,5,96,96,5,96,96,96,96,96,96,96,96,96,96,96,96,96,96,0,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,0,0,0,137,138,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,137,98,98,98,137,138,98,98,98,98,98,98,98,98,98,98,98,98,98,0,100,0,0,0,0,100,7,100,100,100,100,100,100,109,100,100,7,7,109,100,100,100,100,100,97,109,100,0,0,0,112,0,0,112,113,112,112,113,112,112,112,112,112,112,113,112,113,113,112,113,113,113,113,113,113,113,113,112,113,112,113,112,113,112,112,113,113,112,0,115,0,0,0,0,0,0,0,115,0,115,0,0,0,0,115,0,0,0,0,52,115,52,115,52,0,52,52,52,52,52,0,115,52,115,52,115,52,52,52,52,52,52,0,52,115,52,115,52,115,52,52,115,52,115,52,0,0,0,0,0,0,0,0,0,0,0,0,117,62,117,62,117,62,117,62,117,117,117,62,62,62,117,62,117,62,117,62,62,62,62,117,62,117,117,117,117,62,62,117,62,117,62,117,62,117,117,62,117,62,117,0,0,0,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,0,0,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,0,0,0,0,0,0,0,0,119,120,119,120,119,120,119,120,0,119,120,0,119,120,119,120,0,0,119,120,119,120,119,120,119,120,0,0,120,119,119,120,119,120,119,120,0,0,111,106,106,111,106,111,106,111,106,106,106,111,106,106,111,106,111,106,111,106,106,106,111,106,111,111,106,106,111,106,111,106,111,106,111,111,106,0,111,106],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[1,-1],[[4,[2,3]]],[[8,[5],[[6,[7]]]],9,10]],[-1,-2,[],[]],[-1,-2,[],[]],[1,[[12,[11]]]],0,0,[-1,-1,[]],[1,13],[-1,-2,[],[]],[1,14],0,[1,[[16,[15]]]],0,[[14,11,17],18],[1,13],0,[1,13],0,0,[[[12,[1]]],[[4,[2,3]]]],[1,2],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],0,[-1,-1,[]],[-1,-2,[],[]],[[[21,[20]]],22],[22,[[4,[23,3]]]],[[22,[25,[24]]],[[4,[2,3]]]],[[22,26],[[4,[23,3]]]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],[[22,27,-1],[[4,[2,3]]],28],[[22,[25,[24]]],[[4,[2,3]]]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[],11],0,0,[-1,-1,[]],0,0,[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],0,[-1,-2,[],[]],0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[29,29],[[-1,-2],2,[],[]],0,0,0,0,[[29,30],31],[[29,30],31],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[[32,33,29],[[4,[2,3]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[],[[12,[32]]]],[32,34],0,[-1,-2,[],[]],[-1,35,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,36,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[37,38,[40,[39]]],[[4,[33,3]]]],[[[12,[37]],38,[40,[39]],-2],[[4,[2,3]]],[[41,[],[[6,[[4,[2,3]]]]]],9],[[42,[33],[[6,[-1]]]],9]],0,[[37,38,[40,[39]]],[[4,[33,3]]]],0,[-1,-1,[]],[-1,-2,[],[]],0,0,0,[[14,13,[12,[43]],44,[12,[45]],17],[[12,[37]]]],[37,2],0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],0,0,[[[12,[46]],38,[40,[39]]],2],[[[12,[46]]],[[4,[2,3]]]],0,[-1,-1,[]],0,[-1,-2,[],[]],0,0,0,[[14,39,[12,[32]],[12,[11]],[12,[45]],17],[[12,[46]]]],0,0,0,[[46,47],[[40,[48]]]],0,0,[46,2],[[[12,[46]]],[[4,[2,3]]]],[[[12,[46]],38],[[4,[38,3]]]],[46,2],0,0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[[46,39,47],2],[-1,-2,[],[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],0,[[49,38,[40,[39]],39],[[4,[[51,[50]],3]]]],0,[-1,-1,[]],[[[12,[49]],33],[[4,[2,3]]]],[[49,22,39],[[4,[[51,[50]],3]]]],0,[-1,-2,[],[]],0,0,0,[[14,39,[12,[[53,[52]]]],[12,[11]],[12,[45]],17],49],0,[[49,38,[40,[39]],[51,[50]]],[[4,[2,3]]]],[[49,[51,[50]],[51,[50]]],2],[[49,22,39],[[4,[54,3]]]],[[49,22,38],[[4,[2,3]]]],[[49,39,22],[[4,[2,3]]]],[[49,22],[[4,[2,3]]]],[[49,[55,[24]],22],[[4,[2,3]]]],[[49,22],[[4,[2,3]]]],[[49,38],2],[49,2],[[[12,[49]]],[[4,[2,3]]]],[[[12,[49]]],[[4,[2,3]]]],[[49,38,[40,[39]]],[[4,[2,3]]]],0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[4,[56,57]]],58],[-1,[[4,[59,57]]],58],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[56,56],[[-1,-2],2,[],[]],0,[[60,48],[[4,[2,3]]]],[-1,[[4,[56,57]]],61],[-1,[[4,[59,57]]],61],[[[12,[60]],[25,[62]]],2],[[56,-1],[[4,[2,63]]],64],[[59,-1],[[4,[2,63]]],64],0,[[56,30],31],[[59,30],31],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[[12,[60]],65,66],[[4,[2,3]]]],[[60,67],[[4,[2,3]]]],0,[[[12,[11]],[12,[[53,[52]]]],[12,[45]],17],60],[[[12,[60]],62],2],[[[12,[60]]],[[4,[2,3]]]],[[60,67],[[4,[2,3]]]],[[60,38],2],[60,2],[[[12,[60]]],[[4,[2,3]]]],0,0,[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,36,[]],[-1,36,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[3,30],31],[[3,30],31],[-1,-1,[]],[68,3],[69,3],[70,3],[71,3],[[[72,[-1]]],3,[]],[73,3],[74,3],[75,3],[76,3],[77,3],[78,3],[79,3],[80,3],[-1,-2,[],[]],[3,[[40,[81]]]],[-1,35,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,36,[]],[-1,-2,[],[]],0,[-1,-2,[],[]],[-1,-2,[],[]],0,0,[-1,-1,[]],[-1,-2,[],[]],0,[[[12,[82]],[21,[83]],-2],2,[[41,[],[[6,[[4,[2,3]]]]]],9],[[42,[33],[[6,[-1]]]],84,9]],[[82,38],[[4,[[21,[83]],3]]]],0,[[14,[12,[43]],44,[12,[45]],17],[[12,[82]]]],[82,2],[[[12,[82]],38,-2],[[4,[38,3]]],[[41,[],[[6,[[4,[2,3]]]]]],9],[[42,[33],[[6,[-1]]]],84,9]],0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[4,[23,57]]],58],[-1,[[4,[85,57]]],58],[-1,[[4,[27,57]]],58],[-1,[[4,[86,57]]],58],[-1,[[4,[87,57]]],58],[-1,[[4,[88,57]]],58],[-1,[[4,[89,57]]],58],[-1,[[4,[90,57]]],58],[-1,[[4,[91,57]]],58],[-1,[[4,[92,57]]],58],[-1,[[4,[50,57]]],58],[-1,[[4,[54,57]]],58],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[23,23],[85,85],[27,27],[86,86],[87,87],[88,88],[89,89],[90,90],[50,50],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],0,[-1,[[4,[23,57]]],61],[-1,[[4,[85,57]]],61],[-1,[[4,[27,57]]],61],[-1,[[4,[86,57]]],61],[-1,[[4,[87,57]]],61],[-1,[[4,[88,57]]],61],[-1,[[4,[89,57]]],61],[-1,[[4,[90,57]]],61],[-1,[[4,[91,57]]],61],[-1,[[4,[92,57]]],61],[-1,[[4,[50,57]]],61],[-1,[[4,[54,57]]],61],0,[[23,-1],[[4,[2,63]]],64],[[85,-1],[[4,[2,63]]],64],[[27,-1],[[4,[2,63]]],64],[[86,-1],[[4,[2,63]]],64],[[87,-1],[[4,[2,63]]],64],[[88,-1],[[4,[2,63]]],64],[[89,-1],[[4,[2,63]]],64],[[90,-1],[[4,[2,63]]],64],[[91,-1],[[4,[2,63]]],64],[[92,-1],[[4,[2,63]]],64],[[50,-1],[[4,[2,63]]],64],[[54,-1],[[4,[2,63]]],64],[[50,50],44],[[23,30],31],[[85,30],31],[[27,30],31],[[86,30],31],[[87,30],31],[[88,30],31],[[89,30],31],[[90,30],31],[[91,30],31],[[92,30],31],[[50,30],31],[[54,30],31],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[48,50],[-1,-1,[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[15,15],[93,93],[94,94],[95,95],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[15,30],31],[[15,30],31],[[93,30],31],[[93,30],31],[[94,30],31],[[94,30],31],[[95,30],31],[[95,30],31],[-1,-1,[]],[93,15],[95,15],[-1,-1,[]],[94,15],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[],45],[[45,15],2],[45,[[16,[15]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,35,[]],[-1,35,[]],[-1,35,[]],[-1,35,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,19,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,36,[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[[96,97,-1],2,28],0,[96,[[12,[11]]]],0,[96,29],[-1,-1,[]],[96,39],0,[-1,-2,[],[]],[96,44],[[[99,[98]],39,22,38,29,17],5],0,[96,[[12,[98]]]],0,0,0,0,0,[96,[[4,[2,3]]]],[96,[[101,[97,100]]]],[96,38],0,[[[12,[96]]],[[4,[2,3]]]],[[96,97,-1],[[4,[2,3]]],28],[96,2],[[[12,[96]]],2],0,0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],0,[-1,-2,[],[]],[-1,[[4,[39,57]]],58],[-1,-2,[],[]],[39,39],[[-1,-2],2,[],[]],[-1,[[4,[39,57]]],61],[[39,-1],[[4,[2,63]]],64],[[39,39],44],[[39,30],31],[[39,30],31],[[[55,[24]]],39],[-1,-1,[]],[[39,-1],2,102],[-1,-2,[],[]],[[[25,[24]]],39],[[],39],[-1,-2,[],[]],[-1,35,[]],[-1,[[4,[-2]]],[],[]],[103,[[4,[39,-1]]],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,36,[]],[-1,-2,[],[]],0,0,0,0,0,[[98,[21,[104]]],[[4,[2,3]]]],[-1,-2,[],[]],[-1,-2,[],[]],[[98,97,-1],2,28],0,0,[[98,39],44],[[98,22,29],[[4,[39,3]]]],0,[-1,-1,[]],0,[-1,-2,[],[]],[[[12,[98]]],2],0,[[39,[12,[32]],[12,[11]],[12,[45]],17],[[12,[98]]]],[[[12,[98]],33,29,[105,[[4,[2,3]]]]],[[4,[2,3]]]],0,[98,13],0,0,0,[[98,[107,[97,106]]],[[4,[2,3]]]],0,0,[[98,39],[[4,[2,3]]]],[[98,22,44],[[4,[2,3]]]],[[98,22],[[4,[2,3]]]],[98,[[4,[2,3]]]],[98,2],[[[12,[98]]],[[4,[2,3]]]],0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],[[98,22],[[4,[39,3]]]],[[98,22],[[4,[39,3]]]],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[100,100],[[-1,-2],2,[],[]],[[100,30],31],[-1,-1,[]],[[],97],[[],108],[-1,-2,[],[]],0,0,[[[12,[109]]],[[110,[[21,[41]]]]]],[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,36,[]],0,[[],[[4,[111,3]]]],[-1,-2,[],[]],0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[4,[112,57]]],58],[-1,-2,[],[]],[-1,-2,[],[]],[112,112],[[-1,-2],2,[],[]],[-1,[[4,[112,57]]],61],[[112,-1],[[4,[2,63]]],64],[[112,30],31],[-1,-1,[]],[-1,-1,[]],[[],97],[-1,-2,[],[]],[-1,-2,[],[]],[[5,17],7],0,0,[[[12,[113]],[114,[[55,[24]]]]],[[4,[2,3]]]],0,[[113,[101,[97,100]],[105,[[55,[24]]]]],[[4,[2,3]]]],[[[12,[113]]],[[110,[[21,[41]]]]]],0,[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[-1,36,[]],[[],[[4,[111,3]]]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[52,48],115],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[52,[55,[24]]],[[40,[13]]]],[[52,[55,[24]]],[[51,[13]]]],0,[[52,[55,[24]],13],[[51,[48]]]],[[52,[55,[24]]],44],0,[[115,30],31],[[52,30],31],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],[52,[[0,[[118,[],[[116,[117]]]]]]]],0,[[[55,[24]]],52],[[52,47],[[40,[48]]]],[[52,[55,[24]]],2],[[65,65],44],[[52,13,48],44],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[[52,[55,[24]],47],2],[-1,36,[]],[-1,36,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,0,0,0,0,0,0,0,0,[[117,48],2],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[62,62],[117,117],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[[117,[55,[24]]],44],0,0,0,[[62,30],31],[[117,30],31],[-1,-1,[]],[-1,-1,[]],[-1,-2,[],[]],[-1,-2,[],[]],[62,44],[62,44],[62,44],[62,44],[117,[[0,[[118,[],[[116,[62]]]]]]]],0,[117,13],[[],117],[[117,13],[[0,[[118,[],[[116,[62]]]]]]]],[[117,[55,[24]]],2],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[[117,[55,[24]],47],2],[-1,36,[]],[-1,36,[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[-1,-2,[],[]],[-1,[[4,[48,57]]],58],[-1,-2,[],[]],[48,48],[[-1,-2],2,[],[]],[-1,[[4,[48,57]]],61],0,[[48,-1],[[4,[2,63]]],64],[[48,48],44],[[48,30],31],[-1,-1,[]],[50,48],[-1,-2,[],[]],0,0,[-1,-2,[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,36,[]],[-1,-2,[],[]],[[[55,[24]],[55,[24]]],[[55,[24]]]],0,[43,2],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-1,[]],[-1,-2,[],[]],[43,13],0,[13,43],[43,2],0,0,[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,-2,[],[]],[43,2],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[119,30],31],[[120,30],31],[-1,-1,[]],[-1,-1,[]],[14,[[4,[[2,[121,122]],3]]]],[-1,-2,[],[]],[-1,-2,[],[]],[121,[[4,[123,77]]]],0,[120,[[25,[124]]]],[119,[[51,[125]]]],[120,[[51,[125]]]],[[14,[40,[39]]],[[4,[126,3]]]],[14,[[4,[127,3]]]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],[-1,36,[]],[-1,36,[]],[121,[[4,[39,77]]]],[[123,[25,[24]],[25,[24]]],[[4,[2,77]]]],[[120,121,[25,[121]],128],[[4,[129,77]]]],[[119,121,[25,[121]],130,[25,[24]],128],[[4,[131,77]]]],[[119,[25,[24]],121,132],[[4,[133,77]]]],[[120,[25,[24]],121,132],[[4,[133,77]]]],[[119,[25,[24]],121,132],[[4,[133,77]]]],[[120,[25,[24]],121,132],[[4,[133,77]]]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[4,[106,57]]],58],[-1,-2,[],[]],[-1,-2,[],[]],[111,111],[106,106],[[-1,-2],2,[],[]],[[-1,-2],2,[],[]],[-1,[[4,[106,57]]],61],[[106,-1],[[4,[2,63]]],64],[[111,30],31],[[106,30],31],[[106,30],31],[-1,-1,[]],[-1,-1,[]],[108,[[4,[111,3]]]],[108,[[4,[106,3]]]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,[[106,134],111],0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,35,[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,[[4,[-2]]],[],[]],[-1,19,[]],[-1,19,[]],0,[-1,36,[]],[-1,36,[]],[[134,106],44],[-1,-2,[],[]],[-1,-2,[],[]]],"c":[],"p":[[5,"Backend",84],[1,"tuple"],[6,"Error",353],[6,"Result",1165],[8,"ArcPeer",750],[17,"Output"],[8,"ArcProtocol",855],[10,"Fn",1166],[10,"Send",1167],[10,"Sync",1167],[5,"Config",129],[5,"Arc",1168],[1,"usize"],[6,"KeyPair",1169],[6,"MonitorEvent",653],[5,"Subscription",1170],[8,"GlobalExecutor",1171],[8,"ArcBackend",84],[5,"TypeId",1172],[10,"Connection",1173],[5,"Box",1174],[5,"Codec",112],[5,"NetMsg",427],[1,"u8"],[1,"slice"],[5,"Duration",1175],[6,"NetMsgCmd",427],[10,"CodecMsg",112],[6,"ConnDirection",160],[5,"Formatter",1176],[8,"Result",1176],[5,"ConnQueue",160],[8,"Conn",1173],[5,"NewConn",160],[5,"String",1177],[10,"Any",1172],[5,"Connector",204],[6,"Endpoint",1178],[5,"PeerID",788],[6,"Option",1179],[10,"Future",1180],[10,"FnOnce",1166],[5,"ConnectionSlots",1062],[1,"bool"],[5,"Monitor",653],[5,"Discovery",225],[1,"u16"],[5,"Entry",1036],[5,"LookupService",259],[5,"PeerMsg",427],[5,"Vec",1181],[5,"RoutingTable",924],[5,"Mutex",1182],[5,"PeersMsg",427],[1,"array"],[5,"PingMsg",294],[6,"DecodeError",1183],[10,"BorrowDecoder",1184],[5,"PongMsg",294],[5,"RefreshService",294],[10,"Decoder",1184],[5,"BucketEntry",982],[6,"EncodeError",1183],[10,"Encoder",1185],[6,"Addr",1178],[8,"Port",1178],[5,"UdpConn",1186],[5,"InvalidDnsNameError",1187],[6,"Error",1188],[5,"Error",1189],[5,"Error",1190],[5,"SendError",1191],[6,"Error",1192],[5,"ASN1Error",1193],[6,"X509Error",1194],[5,"RecvError",1191],[6,"Error",1195],[6,"Error",1196],[5,"ParseIntError",1197],[5,"ParseFloatError",1198],[10,"Error",1199],[5,"Listener",408],[10,"ConnListener",1200],[10,"Clone",1201],[5,"NetMsgHeader",427],[5,"ProtocolMsg",427],[5,"VerMsg",427],[5,"VerAckMsg",427],[5,"ShutdownMsg",427],[5,"PingMsg",427],[5,"PongMsg",427],[5,"FindPeerMsg",427],[6,"ConnEvent",653],[6,"PeerPoolEvent",653],[6,"DiscoveryEvent",653],[5,"Peer",750],[8,"ProtocolID",855],[5,"PeerPool",813],[5,"Weak",1168],[6,"ProtocolEvent",855],[5,"EventListener",1202],[10,"Hasher",1203],[6,"PublicKey",1169],[8,"ProtocolConstructor",855],[5,"Sender",1191],[5,"VersionInt",1123],[5,"HashMap",1204],[1,"str"],[10,"Protocol",855],[5,"Pin",1205],[5,"Version",1123],[6,"PingProtocolMsg",885],[5,"PingProtocol",885],[5,"Receiver",1191],[6,"AddEntryResult",924],[17,"Item"],[5,"Bucket",982],[10,"Iterator",1206],[5,"SrvrCertVerifier",1079],[5,"CliCertVerifier",1079],[5,"CertificateDer",1207],[6,"PrivateKeyDer",1207],[5,"X509Certificate",1208],[5,"DistinguishedName",1209],[6,"SignatureScheme",1210],[5,"ClientConfig",1211],[5,"ServerConfig",1212],[5,"UnixTime",1207],[5,"ClientCertVerified",1213],[6,"ServerName",1187],[5,"ServerCertVerified",1213],[5,"DigitallySignedStruct",1213],[5,"HandshakeSignatureValid",1213],[5,"VersionReq",1214],[8,"ArcDiscovery",225],[8,"Result",353],[8,"ArcPeerPool",813],[8,"WeakPeerPool",813]],"b":[[177,"impl-Debug-for-ConnDirection"],[178,"impl-Display-for-ConnDirection"],[384,"impl-Debug-for-Error"],[385,"impl-Display-for-Error"],[387,"impl-From%3CInvalidDnsNameError%3E-for-Error"],[388,"impl-From%3CError%3E-for-Error"],[389,"impl-From%3CError%3E-for-Error"],[390,"impl-From%3CError%3E-for-Error"],[391,"impl-From%3CSendError%3CT%3E%3E-for-Error"],[392,"impl-From%3CError%3E-for-Error"],[393,"impl-From%3CASN1Error%3E-for-Error"],[394,"impl-From%3CX509Error%3E-for-Error"],[395,"impl-From%3CRecvError%3E-for-Error"],[396,"impl-From%3CError%3E-for-Error"],[397,"impl-From%3CNetError%3E-for-Error"],[398,"impl-From%3CParseIntError%3E-for-Error"],[399,"impl-From%3CParseFloatError%3E-for-Error"],[693,"impl-Debug-for-MonitorEvent"],[694,"impl-Display-for-MonitorEvent"],[695,"impl-Display-for-ConnEvent"],[696,"impl-Debug-for-ConnEvent"],[697,"impl-Display-for-PeerPoolEvent"],[698,"impl-Debug-for-PeerPoolEvent"],[699,"impl-Display-for-DiscoveryEvent"],[700,"impl-Debug-for-DiscoveryEvent"],[702,"impl-From%3CConnEvent%3E-for-MonitorEvent"],[703,"impl-From%3CDiscoveryEvent%3E-for-MonitorEvent"],[705,"impl-From%3CPeerPoolEvent%3E-for-MonitorEvent"],[797,"impl-Debug-for-PeerID"],[798,"impl-Display-for-PeerID"],[1137,"impl-Display-for-VersionInt"],[1138,"impl-Debug-for-VersionInt"]]}]\
+]'));
+if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
+else if (window.initSearch) window.initSearch(searchIndex);
diff --git a/settings.html b/settings.html
new file mode 100644
index 00000000..02cfc46a
--- /dev/null
+++ b/settings.html
@@ -0,0 +1,2 @@
+1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +
use std::{
+ cmp::min,
+ sync::atomic::{AtomicBool, AtomicU32, Ordering},
+ time::Duration,
+};
+
+use smol::Timer;
+
+/// Exponential backoff
+/// <https://en.wikipedia.org/wiki/Exponential_backoff>
+///
+/// # Examples
+///
+/// ```
+/// use karyon_core::async_util::Backoff;
+///
+/// async {
+/// let backoff = Backoff::new(300, 3000);
+///
+/// loop {
+/// backoff.sleep().await;
+///
+/// // do something
+/// break;
+/// }
+///
+/// backoff.reset();
+///
+/// // ....
+/// };
+///
+/// ```
+///
+pub struct Backoff {
+ /// The base delay in milliseconds for the initial retry.
+ base_delay: u64,
+ /// The max delay in milliseconds allowed for a retry.
+ max_delay: u64,
+ /// Atomic counter
+ retries: AtomicU32,
+ /// Stop flag
+ stop: AtomicBool,
+}
+
+impl Backoff {
+ /// Creates a new Backoff.
+ pub fn new(base_delay: u64, max_delay: u64) -> Self {
+ Self {
+ base_delay,
+ max_delay,
+ retries: AtomicU32::new(0),
+ stop: AtomicBool::new(false),
+ }
+ }
+
+ /// Sleep based on the current retry count and delay values.
+ /// Retruns the delay value.
+ pub async fn sleep(&self) -> u64 {
+ if self.stop.load(Ordering::SeqCst) {
+ Timer::after(Duration::from_millis(self.max_delay)).await;
+ return self.max_delay;
+ }
+
+ let retries = self.retries.load(Ordering::SeqCst);
+ let delay = self.base_delay * (2_u64).pow(retries);
+ let delay = min(delay, self.max_delay);
+
+ if delay == self.max_delay {
+ self.stop.store(true, Ordering::SeqCst);
+ }
+
+ self.retries.store(retries + 1, Ordering::SeqCst);
+
+ Timer::after(Duration::from_millis(delay)).await;
+ delay
+ }
+
+ /// Reset the retry counter to 0.
+ pub fn reset(&self) {
+ self.retries.store(0, Ordering::SeqCst);
+ self.stop.store(false, Ordering::SeqCst);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::Arc;
+
+ #[test]
+ fn test_backoff() {
+ smol::block_on(async move {
+ let backoff = Arc::new(Backoff::new(5, 15));
+ let backoff_c = backoff.clone();
+ smol::spawn(async move {
+ let delay = backoff_c.sleep().await;
+ assert_eq!(delay, 5);
+
+ let delay = backoff_c.sleep().await;
+ assert_eq!(delay, 10);
+
+ let delay = backoff_c.sleep().await;
+ assert_eq!(delay, 15);
+ })
+ .await;
+
+ smol::spawn(async move {
+ backoff.reset();
+ let delay = backoff.sleep().await;
+ assert_eq!(delay, 5);
+ })
+ .await;
+ });
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +
use std::{
+ collections::HashMap,
+ future::Future,
+ pin::Pin,
+ sync::Mutex,
+ task::{Context, Poll, Waker},
+};
+
+use smol::lock::MutexGuard;
+
+use crate::util::random_16;
+
+/// CondVar is an async version of <https://doc.rust-lang.org/std/sync/struct.Condvar.html>
+///
+/// # Example
+///
+///```
+/// use std::sync::Arc;
+///
+/// use smol::lock::Mutex;
+///
+/// use karyon_core::async_util::CondVar;
+///
+/// async {
+///
+/// let val = Arc::new(Mutex::new(false));
+/// let condvar = Arc::new(CondVar::new());
+///
+/// let val_cloned = val.clone();
+/// let condvar_cloned = condvar.clone();
+/// smol::spawn(async move {
+/// let mut val = val_cloned.lock().await;
+///
+/// // While the boolean flag is false, wait for a signal.
+/// while !*val {
+/// val = condvar_cloned.wait(val).await;
+/// }
+///
+/// // ...
+/// });
+///
+/// let condvar_cloned = condvar.clone();
+/// smol::spawn(async move {
+/// let mut val = val.lock().await;
+///
+/// // While the boolean flag is false, wait for a signal.
+/// while !*val {
+/// val = condvar_cloned.wait(val).await;
+/// }
+///
+/// // ...
+/// });
+///
+/// // Wake up all waiting tasks on this condvar
+/// condvar.broadcast();
+/// };
+///
+/// ```
+
+pub struct CondVar {
+ inner: Mutex<Wakers>,
+}
+
+impl CondVar {
+ /// Creates a new CondVar
+ pub fn new() -> Self {
+ Self {
+ inner: Mutex::new(Wakers::new()),
+ }
+ }
+
+ /// Blocks the current task until this condition variable receives a notification.
+ pub async fn wait<'a, T>(&self, g: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
+ let m = MutexGuard::source(&g);
+
+ CondVarAwait::new(self, g).await;
+
+ m.lock().await
+ }
+
+ /// Wakes up one blocked task waiting on this condvar.
+ pub fn signal(&self) {
+ self.inner.lock().unwrap().wake(true);
+ }
+
+ /// Wakes up all blocked tasks waiting on this condvar.
+ pub fn broadcast(&self) {
+ self.inner.lock().unwrap().wake(false);
+ }
+}
+
+impl Default for CondVar {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+struct CondVarAwait<'a, T> {
+ id: Option<u16>,
+ condvar: &'a CondVar,
+ guard: Option<MutexGuard<'a, T>>,
+}
+
+impl<'a, T> CondVarAwait<'a, T> {
+ fn new(condvar: &'a CondVar, guard: MutexGuard<'a, T>) -> Self {
+ Self {
+ condvar,
+ guard: Some(guard),
+ id: None,
+ }
+ }
+}
+
+impl<'a, T> Future for CondVarAwait<'a, T> {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let mut inner = self.condvar.inner.lock().unwrap();
+
+ match self.guard.take() {
+ Some(_) => {
+ // the first pooll will release the Mutexguard
+ self.id = Some(inner.put(Some(cx.waker().clone())));
+ Poll::Pending
+ }
+ None => {
+ // Return Ready if it has already been polled and removed
+ // from the waker list.
+ if self.id.is_none() {
+ return Poll::Ready(());
+ }
+
+ let i = self.id.as_ref().unwrap();
+ match inner.wakers.get_mut(i).unwrap() {
+ Some(wk) => {
+ // This will prevent cloning again
+ if !wk.will_wake(cx.waker()) {
+ wk.clone_from(cx.waker());
+ }
+ Poll::Pending
+ }
+ None => {
+ inner.delete(i);
+ self.id = None;
+ Poll::Ready(())
+ }
+ }
+ }
+ }
+ }
+}
+
+impl<'a, T> Drop for CondVarAwait<'a, T> {
+ fn drop(&mut self) {
+ if let Some(id) = self.id {
+ let mut inner = self.condvar.inner.lock().unwrap();
+ if let Some(wk) = inner.wakers.get_mut(&id).unwrap().take() {
+ wk.wake()
+ }
+ }
+ }
+}
+
+/// Wakers is a helper struct to store the task wakers
+struct Wakers {
+ wakers: HashMap<u16, Option<Waker>>,
+}
+
+impl Wakers {
+ fn new() -> Self {
+ Self {
+ wakers: HashMap::new(),
+ }
+ }
+
+ fn put(&mut self, waker: Option<Waker>) -> u16 {
+ let mut id: u16;
+
+ id = random_16();
+ while self.wakers.contains_key(&id) {
+ id = random_16();
+ }
+
+ self.wakers.insert(id, waker);
+ id
+ }
+
+ fn delete(&mut self, id: &u16) -> Option<Option<Waker>> {
+ self.wakers.remove(id)
+ }
+
+ fn wake(&mut self, signal: bool) {
+ for (_, wk) in self.wakers.iter_mut() {
+ match wk.take() {
+ Some(w) => {
+ w.wake();
+ if signal {
+ break;
+ }
+ }
+ None => continue,
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use smol::lock::Mutex;
+ use std::{
+ collections::VecDeque,
+ sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+ },
+ };
+
+ // The tests below demonstrate a solution to a problem in the Wikipedia
+ // explanation of condition variables:
+ // https://en.wikipedia.org/wiki/Monitor_(synchronization)#Solving_the_bounded_producer/consumer_problem.
+
+ struct Queue {
+ items: VecDeque<String>,
+ max_len: usize,
+ }
+ impl Queue {
+ fn new(max_len: usize) -> Self {
+ Self {
+ items: VecDeque::new(),
+ max_len,
+ }
+ }
+
+ fn is_full(&self) -> bool {
+ self.items.len() == self.max_len
+ }
+
+ fn is_empty(&self) -> bool {
+ self.items.is_empty()
+ }
+ }
+
+ #[test]
+ fn test_condvar_signal() {
+ smol::block_on(async {
+ let number_of_tasks = 30;
+
+ let queue = Arc::new(Mutex::new(Queue::new(5)));
+ let condvar_full = Arc::new(CondVar::new());
+ let condvar_empty = Arc::new(CondVar::new());
+
+ let queue_cloned = queue.clone();
+ let condvar_full_cloned = condvar_full.clone();
+ let condvar_empty_cloned = condvar_empty.clone();
+
+ let _producer1 = smol::spawn(async move {
+ for i in 1..number_of_tasks {
+ // Lock queue mtuex
+ let mut queue = queue_cloned.lock().await;
+
+ // Check if the queue is non-full
+ while queue.is_full() {
+ // Release queue mutex and sleep
+ queue = condvar_full_cloned.wait(queue).await;
+ }
+
+ queue.items.push_back(format!("task {i}"));
+
+ // Wake up the consumer
+ condvar_empty_cloned.signal();
+ }
+ });
+
+ let queue_cloned = queue.clone();
+ let task_consumed = Arc::new(AtomicUsize::new(0));
+ let task_consumed_ = task_consumed.clone();
+ let consumer = smol::spawn(async move {
+ for _ in 1..number_of_tasks {
+ // Lock queue mtuex
+ let mut queue = queue_cloned.lock().await;
+
+ // Check if the queue is non-empty
+ while queue.is_empty() {
+ // Release queue mutex and sleep
+ queue = condvar_empty.wait(queue).await;
+ }
+
+ let _ = queue.items.pop_front().unwrap();
+
+ task_consumed_.fetch_add(1, Ordering::Relaxed);
+
+ // Do something
+
+ // Wake up the producer
+ condvar_full.signal();
+ }
+ });
+
+ consumer.await;
+ assert!(queue.lock().await.is_empty());
+ assert_eq!(task_consumed.load(Ordering::Relaxed), 29);
+ });
+ }
+
+ #[test]
+ fn test_condvar_broadcast() {
+ smol::block_on(async {
+ let tasks = 30;
+
+ let queue = Arc::new(Mutex::new(Queue::new(5)));
+ let condvar = Arc::new(CondVar::new());
+
+ let queue_cloned = queue.clone();
+ let condvar_cloned = condvar.clone();
+ let _producer1 = smol::spawn(async move {
+ for i in 1..tasks {
+ // Lock queue mtuex
+ let mut queue = queue_cloned.lock().await;
+
+ // Check if the queue is non-full
+ while queue.is_full() {
+ // Release queue mutex and sleep
+ queue = condvar_cloned.wait(queue).await;
+ }
+
+ queue.items.push_back(format!("producer1: task {i}"));
+
+ // Wake up all producer and consumer tasks
+ condvar_cloned.broadcast();
+ }
+ });
+
+ let queue_cloned = queue.clone();
+ let condvar_cloned = condvar.clone();
+ let _producer2 = smol::spawn(async move {
+ for i in 1..tasks {
+ // Lock queue mtuex
+ let mut queue = queue_cloned.lock().await;
+
+ // Check if the queue is non-full
+ while queue.is_full() {
+ // Release queue mutex and sleep
+ queue = condvar_cloned.wait(queue).await;
+ }
+
+ queue.items.push_back(format!("producer2: task {i}"));
+
+ // Wake up all producer and consumer tasks
+ condvar_cloned.broadcast();
+ }
+ });
+
+ let queue_cloned = queue.clone();
+ let task_consumed = Arc::new(AtomicUsize::new(0));
+ let task_consumed_ = task_consumed.clone();
+
+ let consumer = smol::spawn(async move {
+ for _ in 1..((tasks * 2) - 1) {
+ {
+ // Lock queue mutex
+ let mut queue = queue_cloned.lock().await;
+
+ // Check if the queue is non-empty
+ while queue.is_empty() {
+ // Release queue mutex and sleep
+ queue = condvar.wait(queue).await;
+ }
+
+ let _ = queue.items.pop_front().unwrap();
+
+ task_consumed_.fetch_add(1, Ordering::Relaxed);
+
+ // Do something
+
+ // Wake up all producer and consumer tasks
+ condvar.broadcast();
+ }
+ }
+ });
+
+ consumer.await;
+ assert!(queue.lock().await.is_empty());
+ assert_eq!(task_consumed.load(Ordering::Relaxed), 58);
+ });
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +
use smol::lock::Mutex;
+
+use super::CondVar;
+
+/// CondWait is a wrapper struct for CondVar with a Mutex boolean flag.
+///
+/// # Example
+///
+///```
+/// use std::sync::Arc;
+///
+/// use karyon_core::async_util::CondWait;
+///
+/// async {
+/// let cond_wait = Arc::new(CondWait::new());
+/// let cond_wait_cloned = cond_wait.clone();
+/// let task = smol::spawn(async move {
+/// cond_wait_cloned.wait().await;
+/// // ...
+/// });
+///
+/// cond_wait.signal().await;
+/// };
+///
+/// ```
+///
+pub struct CondWait {
+ /// The CondVar
+ condvar: CondVar,
+ /// Boolean flag
+ w: Mutex<bool>,
+}
+
+impl CondWait {
+ /// Creates a new CondWait.
+ pub fn new() -> Self {
+ Self {
+ condvar: CondVar::new(),
+ w: Mutex::new(false),
+ }
+ }
+
+ /// Waits for a signal or broadcast.
+ pub async fn wait(&self) {
+ let mut w = self.w.lock().await;
+
+ // While the boolean flag is false, wait for a signal.
+ while !*w {
+ w = self.condvar.wait(w).await;
+ }
+ }
+
+ /// Signal a waiting task.
+ pub async fn signal(&self) {
+ *self.w.lock().await = true;
+ self.condvar.signal();
+ }
+
+ /// Signal all waiting tasks.
+ pub async fn broadcast(&self) {
+ *self.w.lock().await = true;
+ self.condvar.broadcast();
+ }
+
+ /// Reset the boolean flag value to false.
+ pub async fn reset(&self) {
+ *self.w.lock().await = false;
+ }
+}
+
+impl Default for CondWait {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+ };
+
+ #[test]
+ fn test_cond_wait() {
+ smol::block_on(async {
+ let cond_wait = Arc::new(CondWait::new());
+ let count = Arc::new(AtomicUsize::new(0));
+
+ let cond_wait_cloned = cond_wait.clone();
+ let count_cloned = count.clone();
+ let task = smol::spawn(async move {
+ cond_wait_cloned.wait().await;
+ count_cloned.fetch_add(1, Ordering::Relaxed);
+ // do something
+ });
+
+ // Send a signal to the waiting task
+ cond_wait.signal().await;
+
+ task.await;
+
+ // Reset the boolean flag
+ cond_wait.reset().await;
+
+ assert_eq!(count.load(Ordering::Relaxed), 1);
+
+ let cond_wait_cloned = cond_wait.clone();
+ let count_cloned = count.clone();
+ let task1 = smol::spawn(async move {
+ cond_wait_cloned.wait().await;
+ count_cloned.fetch_add(1, Ordering::Relaxed);
+ // do something
+ });
+
+ let cond_wait_cloned = cond_wait.clone();
+ let count_cloned = count.clone();
+ let task2 = smol::spawn(async move {
+ cond_wait_cloned.wait().await;
+ count_cloned.fetch_add(1, Ordering::Relaxed);
+ // do something
+ });
+
+ // Broadcast a signal to all waiting tasks
+ cond_wait.broadcast().await;
+
+ task1.await;
+ task2.await;
+ assert_eq!(count.load(Ordering::Relaxed), 3);
+ });
+ }
+}
+
mod backoff;
+mod condvar;
+mod condwait;
+mod select;
+mod task_group;
+mod timeout;
+
+pub use backoff::Backoff;
+pub use condvar::CondVar;
+pub use condwait::CondWait;
+pub use select::{select, Either};
+pub use task_group::{TaskGroup, TaskResult};
+pub use timeout::timeout;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +
use std::pin::Pin;
+use std::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+use smol::future::Future;
+
+/// Returns the result of the future that completes first, preferring future1
+/// if both are ready.
+///
+/// # Examples
+///
+/// ```
+/// use std::future;
+///
+/// use karyon_core::async_util::{select, Either};
+///
+/// async {
+/// let fut1 = future::pending::<String>();
+/// let fut2 = future::ready(0);
+/// let res = select(fut1, fut2).await;
+/// assert!(matches!(res, Either::Right(0)));
+/// // ....
+/// };
+///
+/// ```
+///
+pub fn select<T1, T2, F1, F2>(future1: F1, future2: F2) -> Select<F1, F2>
+where
+ F1: Future<Output = T1>,
+ F2: Future<Output = T2>,
+{
+ Select { future1, future2 }
+}
+
+pin_project! {
+ #[derive(Debug)]
+ pub struct Select<F1, F2> {
+ #[pin]
+ future1: F1,
+ #[pin]
+ future2: F2,
+ }
+}
+
+/// The return value from the [`select`] function, indicating which future
+/// completed first.
+#[derive(Debug)]
+pub enum Either<T1, T2> {
+ Left(T1),
+ Right(T2),
+}
+
+// Implement the Future trait for the Select struct.
+impl<T1, T2, F1, F2> Future for Select<F1, F2>
+where
+ F1: Future<Output = T1>,
+ F2: Future<Output = T2>,
+{
+ type Output = Either<T1, T2>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ let this = self.project();
+
+ if let Poll::Ready(t) = this.future1.poll(cx) {
+ return Poll::Ready(Either::Left(t));
+ }
+
+ if let Poll::Ready(t) = this.future2.poll(cx) {
+ return Poll::Ready(Either::Right(t));
+ }
+
+ Poll::Pending
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{select, Either};
+ use smol::Timer;
+ use std::future;
+
+ #[test]
+ fn test_async_select() {
+ smol::block_on(async move {
+ let fut = select(Timer::never(), future::ready(0 as u32)).await;
+ assert!(matches!(fut, Either::Right(0)));
+
+ let fut1 = future::pending::<String>();
+ let fut2 = future::ready(0);
+ let res = select(fut1, fut2).await;
+ assert!(matches!(res, Either::Right(0)));
+
+ let fut1 = future::ready(0);
+ let fut2 = future::pending::<String>();
+ let res = select(fut1, fut2).await;
+ assert!(matches!(res, Either::Left(_)));
+ });
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +
use std::{future::Future, sync::Arc, sync::Mutex};
+
+use async_task::FallibleTask;
+
+use crate::Executor;
+
+use super::{select, CondWait, Either};
+
+/// TaskGroup is a group of spawned tasks.
+///
+/// # Example
+///
+/// ```
+///
+/// use std::sync::Arc;
+///
+/// use karyon_core::async_util::TaskGroup;
+///
+/// async {
+///
+/// let ex = Arc::new(smol::Executor::new());
+/// let group = TaskGroup::new(ex);
+///
+/// group.spawn(smol::Timer::never(), |_| async {});
+///
+/// group.cancel().await;
+///
+/// };
+///
+/// ```
+///
+pub struct TaskGroup<'a> {
+ tasks: Mutex<Vec<TaskHandler>>,
+ stop_signal: Arc<CondWait>,
+ executor: Executor<'a>,
+}
+
+impl<'a> TaskGroup<'a> {
+ /// Creates a new task group
+ pub fn new(executor: Executor<'a>) -> Self {
+ Self {
+ tasks: Mutex::new(Vec::new()),
+ stop_signal: Arc::new(CondWait::new()),
+ executor,
+ }
+ }
+
+ /// Spawns a new task and calls the callback after it has completed
+ /// or been canceled. The callback will have the `TaskResult` as a
+ /// parameter, indicating whether the task completed or was canceled.
+ pub fn spawn<T, Fut, CallbackF, CallbackFut>(&self, fut: Fut, callback: CallbackF)
+ where
+ T: Send + Sync + 'a,
+ Fut: Future<Output = T> + Send + 'a,
+ CallbackF: FnOnce(TaskResult<T>) -> CallbackFut + Send + 'a,
+ CallbackFut: Future<Output = ()> + Send + 'a,
+ {
+ let task = TaskHandler::new(
+ self.executor.clone(),
+ fut,
+ callback,
+ self.stop_signal.clone(),
+ );
+ self.tasks.lock().unwrap().push(task);
+ }
+
+ /// Checks if the task group is empty.
+ pub fn is_empty(&self) -> bool {
+ self.tasks.lock().unwrap().is_empty()
+ }
+
+ /// Get the number of the tasks in the group.
+ pub fn len(&self) -> usize {
+ self.tasks.lock().unwrap().len()
+ }
+
+ /// Cancels all tasks in the group.
+ pub async fn cancel(&self) {
+ self.stop_signal.broadcast().await;
+
+ loop {
+ let task = self.tasks.lock().unwrap().pop();
+ if let Some(t) = task {
+ t.cancel().await
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+/// The result of a spawned task.
+#[derive(Debug)]
+pub enum TaskResult<T> {
+ Completed(T),
+ Cancelled,
+}
+
+impl<T: std::fmt::Debug> std::fmt::Display for TaskResult<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ TaskResult::Cancelled => write!(f, "Task cancelled"),
+ TaskResult::Completed(res) => write!(f, "Task completed: {:?}", res),
+ }
+ }
+}
+
+/// TaskHandler
+pub struct TaskHandler {
+ task: FallibleTask<()>,
+ cancel_flag: Arc<CondWait>,
+}
+
+impl<'a> TaskHandler {
+ /// Creates a new task handle
+ fn new<T, Fut, CallbackF, CallbackFut>(
+ ex: Executor<'a>,
+ fut: Fut,
+ callback: CallbackF,
+ stop_signal: Arc<CondWait>,
+ ) -> TaskHandler
+ where
+ T: Send + Sync + 'a,
+ Fut: Future<Output = T> + Send + 'a,
+ CallbackF: FnOnce(TaskResult<T>) -> CallbackFut + Send + 'a,
+ CallbackFut: Future<Output = ()> + Send + 'a,
+ {
+ let cancel_flag = Arc::new(CondWait::new());
+ let cancel_flag_c = cancel_flag.clone();
+ let task = ex
+ .spawn(async move {
+ //start_signal.signal().await;
+ // Waits for either the stop signal or the task to complete.
+ let result = select(stop_signal.wait(), fut).await;
+
+ let result = match result {
+ Either::Left(_) => TaskResult::Cancelled,
+ Either::Right(res) => TaskResult::Completed(res),
+ };
+
+ // Call the callback with the result.
+ callback(result).await;
+
+ cancel_flag_c.signal().await;
+ })
+ .fallible();
+
+ TaskHandler { task, cancel_flag }
+ }
+
+ /// Cancels the task.
+ async fn cancel(self) {
+ self.cancel_flag.wait().await;
+ self.task.cancel().await;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::{future, sync::Arc};
+
+ #[test]
+ fn test_task_group() {
+ let ex = Arc::new(smol::Executor::new());
+ smol::block_on(ex.clone().run(async move {
+ let group = Arc::new(TaskGroup::new(ex));
+
+ group.spawn(future::ready(0), |res| async move {
+ assert!(matches!(res, TaskResult::Completed(0)));
+ });
+
+ group.spawn(future::pending::<()>(), |res| async move {
+ assert!(matches!(res, TaskResult::Cancelled));
+ });
+
+ let groupc = group.clone();
+ group.spawn(
+ async move {
+ groupc.spawn(future::pending::<()>(), |res| async move {
+ assert!(matches!(res, TaskResult::Cancelled));
+ });
+ },
+ |res| async move {
+ assert!(matches!(res, TaskResult::Completed(_)));
+ },
+ );
+
+ // Do something
+ smol::Timer::after(std::time::Duration::from_millis(50)).await;
+ group.cancel().await;
+ }));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +
use std::{future::Future, time::Duration};
+
+use smol::Timer;
+
+use super::{select, Either};
+use crate::{error::Error, Result};
+
+/// Waits for a future to complete or times out if it exceeds a specified
+/// duration.
+///
+/// # Example
+///
+/// ```
+/// use std::{future, time::Duration};
+///
+/// use karyon_core::async_util::timeout;
+///
+/// async {
+/// let fut = future::pending::<()>();
+/// assert!(timeout(Duration::from_millis(100), fut).await.is_err());
+/// };
+///
+/// ```
+///
+pub async fn timeout<T, F>(delay: Duration, future1: F) -> Result<T>
+where
+ F: Future<Output = T>,
+{
+ let result = select(Timer::after(delay), future1).await;
+
+ match result {
+ Either::Left(_) => Err(Error::Timeout),
+ Either::Right(res) => Ok(res),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::{future, time::Duration};
+
+ #[test]
+ fn test_timeout() {
+ smol::block_on(async move {
+ let fut = future::pending::<()>();
+ assert!(timeout(Duration::from_millis(10), fut).await.is_err());
+
+ let fut = smol::Timer::after(Duration::from_millis(10));
+ assert!(timeout(Duration::from_millis(50), fut).await.is_ok())
+ });
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +
use ed25519_dalek::{Signer as _, Verifier as _};
+use rand::rngs::OsRng;
+
+use crate::{error::Error, Result};
+
+/// key cryptography type
+pub enum KeyPairType {
+ Ed25519,
+}
+
+/// A Secret key
+pub struct SecretKey(Vec<u8>);
+
+#[derive(Clone)]
+pub enum KeyPair {
+ Ed25519(Ed25519KeyPair),
+}
+
+impl KeyPair {
+ /// Generate a new random keypair.
+ pub fn generate(kp_type: &KeyPairType) -> Self {
+ match kp_type {
+ KeyPairType::Ed25519 => Self::Ed25519(Ed25519KeyPair::generate()),
+ }
+ }
+
+ /// Sign a message using the private key.
+ pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
+ match self {
+ KeyPair::Ed25519(kp) => kp.sign(msg),
+ }
+ }
+
+ /// Get the public key of this keypair.
+ pub fn public(&self) -> PublicKey {
+ match self {
+ KeyPair::Ed25519(kp) => kp.public(),
+ }
+ }
+
+ /// Get the secret key of this keypair.
+ pub fn secret(&self) -> SecretKey {
+ match self {
+ KeyPair::Ed25519(kp) => kp.secret(),
+ }
+ }
+}
+
+/// An extension trait, adding essential methods to all [`KeyPair`] types.
+trait KeyPairExt {
+ /// Sign a message using the private key.
+ fn sign(&self, msg: &[u8]) -> Vec<u8>;
+
+ /// Get the public key of this keypair.
+ fn public(&self) -> PublicKey;
+
+ /// Get the secret key of this keypair.
+ fn secret(&self) -> SecretKey;
+}
+
+#[derive(Clone)]
+pub struct Ed25519KeyPair(ed25519_dalek::SigningKey);
+
+impl Ed25519KeyPair {
+ fn generate() -> Self {
+ Self(ed25519_dalek::SigningKey::generate(&mut OsRng))
+ }
+}
+
+impl KeyPairExt for Ed25519KeyPair {
+ fn sign(&self, msg: &[u8]) -> Vec<u8> {
+ self.0.sign(msg).to_bytes().to_vec()
+ }
+
+ fn public(&self) -> PublicKey {
+ PublicKey::Ed25519(Ed25519PublicKey(self.0.verifying_key()))
+ }
+
+ fn secret(&self) -> SecretKey {
+ SecretKey(self.0.to_bytes().to_vec())
+ }
+}
+
+#[derive(Debug)]
+pub enum PublicKey {
+ Ed25519(Ed25519PublicKey),
+}
+
+impl PublicKey {
+ pub fn from_bytes(kp_type: &KeyPairType, pk: &[u8]) -> Result<Self> {
+ match kp_type {
+ KeyPairType::Ed25519 => Ok(Self::Ed25519(Ed25519PublicKey::from_bytes(pk)?)),
+ }
+ }
+
+ pub fn as_bytes(&self) -> &[u8] {
+ match self {
+ Self::Ed25519(pk) => pk.as_bytes(),
+ }
+ }
+
+ /// Verify a signature on a message with this public key.
+ pub fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<()> {
+ match self {
+ Self::Ed25519(pk) => pk.verify(msg, signature),
+ }
+ }
+}
+
+/// An extension trait, adding essential methods to all [`PublicKey`] types.
+trait PublicKeyExt {
+ fn as_bytes(&self) -> &[u8];
+
+ /// Verify a signature on a message with this public key.
+ fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<()>;
+}
+
+#[derive(Debug)]
+pub struct Ed25519PublicKey(ed25519_dalek::VerifyingKey);
+
+impl Ed25519PublicKey {
+ pub fn from_bytes(pk: &[u8]) -> Result<Self> {
+ let pk_bytes: [u8; 32] = pk
+ .try_into()
+ .map_err(|_| Error::TryInto("Failed to convert slice to [u8; 32]"))?;
+
+ Ok(Self(ed25519_dalek::VerifyingKey::from_bytes(&pk_bytes)?))
+ }
+}
+
+impl PublicKeyExt for Ed25519PublicKey {
+ fn as_bytes(&self) -> &[u8] {
+ self.0.as_bytes()
+ }
+
+ fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<()> {
+ let sig_bytes: [u8; 64] = signature
+ .try_into()
+ .map_err(|_| Error::TryInto("Failed to convert slice to [u8; 64]"))?;
+ self.0
+ .verify(msg, &ed25519_dalek::Signature::from_bytes(&sig_bytes))?;
+ Ok(())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +
use thiserror::Error as ThisError;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(ThisError, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ IO(#[from] std::io::Error),
+
+ #[error("TryInto Error: {0}")]
+ TryInto(&'static str),
+
+ #[error("Timeout Error")]
+ Timeout,
+
+ #[error("Path Not Found Error: {0}")]
+ PathNotFound(&'static str),
+
+ #[cfg(feature = "crypto")]
+ #[error(transparent)]
+ Ed25519(#[from] ed25519_dalek::ed25519::Error),
+
+ #[error("Channel Send Error: {0}")]
+ ChannelSend(String),
+
+ #[error(transparent)]
+ ChannelRecv(#[from] smol::channel::RecvError),
+
+ #[error(transparent)]
+ BincodeDecode(#[from] bincode::error::DecodeError),
+
+ #[error(transparent)]
+ BincodeEncode(#[from] bincode::error::EncodeError),
+}
+
+impl<T> From<smol::channel::SendError<T>> for Error {
+ fn from(error: smol::channel::SendError<T>) -> Self {
+ Error::ChannelSend(error.to_string())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +
use std::{
+ any::Any,
+ collections::HashMap,
+ marker::PhantomData,
+ sync::{Arc, Weak},
+};
+
+use chrono::{DateTime, Utc};
+use log::{error, trace};
+use smol::{
+ channel::{Receiver, Sender},
+ lock::Mutex,
+};
+
+use crate::{util::random_16, Result};
+
+pub type ArcEventSys<T> = Arc<EventSys<T>>;
+pub type WeakEventSys<T> = Weak<EventSys<T>>;
+pub type EventListenerID = u16;
+
+type Listeners<T> = HashMap<T, HashMap<String, HashMap<EventListenerID, Sender<Event>>>>;
+
+/// EventSys supports event emission to registered listeners based on topics.
+/// # Example
+///
+/// ```
+/// use karyon_core::event::{EventSys, EventValueTopic, EventValue};
+///
+/// async {
+/// let event_sys = EventSys::new();
+///
+/// #[derive(Hash, PartialEq, Eq, Debug, Clone)]
+/// enum Topic {
+/// TopicA,
+/// TopicB,
+/// }
+///
+/// #[derive(Clone, Debug, PartialEq)]
+/// struct A(usize);
+///
+/// impl EventValue for A {
+/// fn id() -> &'static str {
+/// "A"
+/// }
+/// }
+///
+/// let listener = event_sys.register::<A>(&Topic::TopicA).await;
+///
+/// event_sys.emit_by_topic(&Topic::TopicA, &A(3)) .await;
+/// let msg: A = listener.recv().await.unwrap();
+///
+/// #[derive(Clone, Debug, PartialEq)]
+/// struct B(usize);
+///
+/// impl EventValue for B {
+/// fn id() -> &'static str {
+/// "B"
+/// }
+/// }
+///
+/// impl EventValueTopic for B {
+/// type Topic = Topic;
+/// fn topic() -> Self::Topic{
+/// Topic::TopicB
+/// }
+/// }
+///
+/// let listener = event_sys.register::<B>(&Topic::TopicB).await;
+///
+/// event_sys.emit(&B(3)) .await;
+/// let msg: B = listener.recv().await.unwrap();
+///
+/// // ....
+/// };
+///
+/// ```
+///
+pub struct EventSys<T> {
+ listeners: Mutex<Listeners<T>>,
+}
+
+impl<T> EventSys<T>
+where
+ T: std::hash::Hash + Eq + std::fmt::Debug + Clone,
+{
+ /// Creates a new `EventSys`
+ pub fn new() -> ArcEventSys<T> {
+ Arc::new(Self {
+ listeners: Mutex::new(HashMap::new()),
+ })
+ }
+
+ /// Emits an event to the listeners.
+ ///
+ /// The event must implement the `EventValueTopic` trait to indicate the
+ /// topic of the event. Otherwise, you can use `emit_by_topic()`.
+ pub async fn emit<E: EventValueTopic<Topic = T> + Clone>(&self, value: &E) {
+ let topic = E::topic();
+ self.emit_by_topic(&topic, value).await;
+ }
+
+ /// Emits an event to the listeners.
+ pub async fn emit_by_topic<E: EventValueAny + EventValue + Clone>(&self, topic: &T, value: &E) {
+ let value: Arc<dyn EventValueAny> = Arc::new(value.clone());
+ let event = Event::new(value);
+
+ let mut topics = self.listeners.lock().await;
+
+ if !topics.contains_key(topic) {
+ error!("Failed to emit an event to a non-existent topic");
+ return;
+ }
+
+ let event_ids = topics.get_mut(topic).unwrap();
+ let event_id = E::id().to_string();
+
+ if !event_ids.contains_key(&event_id) {
+ error!("Failed to emit an event to a non-existent event id");
+ return;
+ }
+
+ let mut failed_listeners = vec![];
+
+ let listeners = event_ids.get_mut(&event_id).unwrap();
+ for (listener_id, listener) in listeners.iter() {
+ if let Err(err) = listener.send(event.clone()).await {
+ trace!("Failed to emit event for topic {:?}: {}", topic, err);
+ failed_listeners.push(*listener_id);
+ }
+ }
+
+ for listener_id in failed_listeners.iter() {
+ listeners.remove(listener_id);
+ }
+ }
+
+ /// Registers a new event listener for the given topic.
+ pub async fn register<E: EventValueAny + EventValue + Clone>(
+ self: &Arc<Self>,
+ topic: &T,
+ ) -> EventListener<T, E> {
+ let chan = smol::channel::unbounded();
+
+ let topics = &mut self.listeners.lock().await;
+
+ if !topics.contains_key(topic) {
+ topics.insert(topic.clone(), HashMap::new());
+ }
+
+ let event_ids = topics.get_mut(topic).unwrap();
+ let event_id = E::id().to_string();
+
+ if !event_ids.contains_key(&event_id) {
+ event_ids.insert(event_id.clone(), HashMap::new());
+ }
+
+ let listeners = event_ids.get_mut(&event_id).unwrap();
+
+ let mut listener_id = random_16();
+ while listeners.contains_key(&listener_id) {
+ listener_id = random_16();
+ }
+
+ let listener =
+ EventListener::new(listener_id, Arc::downgrade(self), chan.1, &event_id, topic);
+
+ listeners.insert(listener_id, chan.0);
+
+ listener
+ }
+
+ /// Removes the event listener attached to the given topic.
+ async fn remove(&self, topic: &T, event_id: &str, listener_id: &EventListenerID) {
+ let topics = &mut self.listeners.lock().await;
+ if !topics.contains_key(topic) {
+ error!("Failed to remove a non-existent topic");
+ return;
+ }
+
+ let event_ids = topics.get_mut(topic).unwrap();
+ if !event_ids.contains_key(event_id) {
+ error!("Failed to remove a non-existent event id");
+ return;
+ }
+
+ let listeners = event_ids.get_mut(event_id).unwrap();
+ if listeners.remove(listener_id).is_none() {
+ error!("Failed to remove a non-existent event listener");
+ }
+ }
+}
+
+/// EventListener listens for and receives events from the [`EventSys`].
+pub struct EventListener<T, E> {
+ id: EventListenerID,
+ recv_chan: Receiver<Event>,
+ event_sys: WeakEventSys<T>,
+ event_id: String,
+ topic: T,
+ phantom: PhantomData<E>,
+}
+
+impl<T, E> EventListener<T, E>
+where
+ T: std::hash::Hash + Eq + Clone + std::fmt::Debug,
+ E: EventValueAny + Clone + EventValue,
+{
+ /// Create a new event listener.
+ fn new(
+ id: EventListenerID,
+ event_sys: WeakEventSys<T>,
+ recv_chan: Receiver<Event>,
+ event_id: &str,
+ topic: &T,
+ ) -> EventListener<T, E> {
+ Self {
+ id,
+ recv_chan,
+ event_sys,
+ event_id: event_id.to_string(),
+ topic: topic.clone(),
+ phantom: PhantomData,
+ }
+ }
+
+ /// Receive the next event.
+ pub async fn recv(&self) -> Result<E> {
+ match self.recv_chan.recv().await {
+ Ok(event) => match ((*event.value).value_as_any()).downcast_ref::<E>() {
+ Some(v) => Ok(v.clone()),
+ None => unreachable!("Error when attempting to downcast the event value."),
+ },
+ Err(err) => {
+ error!("Failed to receive new event: {err}");
+ self.cancel().await;
+ Err(err.into())
+ }
+ }
+ }
+
+ /// Cancels the listener and removes it from the `EventSys`.
+ pub async fn cancel(&self) {
+ self.event_sys()
+ .remove(&self.topic, &self.event_id, &self.id)
+ .await;
+ }
+
+ /// Returns the topic for this event listener.
+ pub async fn topic(&self) -> &T {
+ &self.topic
+ }
+
+ /// Returns the event id for this event listener.
+ pub async fn event_id(&self) -> &String {
+ &self.event_id
+ }
+
+ fn event_sys(&self) -> ArcEventSys<T> {
+ self.event_sys.upgrade().unwrap()
+ }
+}
+
+/// An event within the [`EventSys`].
+#[derive(Clone, Debug)]
+pub struct Event {
+ /// The time at which the event was created.
+ created_at: DateTime<Utc>,
+ /// The value of the Event.
+ value: Arc<dyn EventValueAny>,
+}
+
+impl Event {
+ /// Creates a new Event.
+ pub fn new(value: Arc<dyn EventValueAny>) -> Self {
+ Self {
+ created_at: Utc::now(),
+ value,
+ }
+ }
+}
+
+impl std::fmt::Display for Event {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}: {:?}", self.created_at, self.value)
+ }
+}
+
+pub trait EventValueAny: Any + Send + Sync + std::fmt::Debug {
+ fn value_as_any(&self) -> &dyn Any;
+}
+
+impl<T: Send + Sync + std::fmt::Debug + Any> EventValueAny for T {
+ fn value_as_any(&self) -> &dyn Any {
+ self
+ }
+}
+
+pub trait EventValue: EventValueAny {
+ fn id() -> &'static str
+ where
+ Self: Sized;
+}
+
+pub trait EventValueTopic: EventValueAny + EventValue {
+ type Topic;
+ fn topic() -> Self::Topic
+ where
+ Self: Sized;
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[derive(Hash, PartialEq, Eq, Debug, Clone)]
+ enum Topic {
+ TopicA,
+ TopicB,
+ TopicC,
+ TopicD,
+ TopicE,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct A {
+ a_value: usize,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct B {
+ b_value: usize,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct C {
+ c_value: usize,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct D {
+ d_value: usize,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct E {
+ e_value: usize,
+ }
+
+ #[derive(Clone, Debug, PartialEq)]
+ struct F {
+ f_value: usize,
+ }
+
+ impl EventValue for A {
+ fn id() -> &'static str {
+ "A"
+ }
+ }
+
+ impl EventValue for B {
+ fn id() -> &'static str {
+ "B"
+ }
+ }
+
+ impl EventValue for C {
+ fn id() -> &'static str {
+ "C"
+ }
+ }
+
+ impl EventValue for D {
+ fn id() -> &'static str {
+ "D"
+ }
+ }
+
+ impl EventValue for E {
+ fn id() -> &'static str {
+ "E"
+ }
+ }
+
+ impl EventValue for F {
+ fn id() -> &'static str {
+ "F"
+ }
+ }
+
+ impl EventValueTopic for C {
+ type Topic = Topic;
+ fn topic() -> Self::Topic {
+ Topic::TopicC
+ }
+ }
+
+ #[test]
+ fn test_event_sys() {
+ smol::block_on(async move {
+ let event_sys = EventSys::<Topic>::new();
+
+ let a_listener = event_sys.register::<A>(&Topic::TopicA).await;
+ let b_listener = event_sys.register::<B>(&Topic::TopicB).await;
+
+ event_sys
+ .emit_by_topic(&Topic::TopicA, &A { a_value: 3 })
+ .await;
+ event_sys
+ .emit_by_topic(&Topic::TopicB, &B { b_value: 5 })
+ .await;
+
+ let msg = a_listener.recv().await.unwrap();
+ assert_eq!(msg, A { a_value: 3 });
+
+ let msg = b_listener.recv().await.unwrap();
+ assert_eq!(msg, B { b_value: 5 });
+
+ // register the same event type to different topics
+ let c_listener = event_sys.register::<C>(&Topic::TopicC).await;
+ let d_listener = event_sys.register::<C>(&Topic::TopicD).await;
+
+ event_sys.emit(&C { c_value: 10 }).await;
+ let msg = c_listener.recv().await.unwrap();
+ assert_eq!(msg, C { c_value: 10 });
+
+ event_sys
+ .emit_by_topic(&Topic::TopicD, &C { c_value: 10 })
+ .await;
+ let msg = d_listener.recv().await.unwrap();
+ assert_eq!(msg, C { c_value: 10 });
+
+ // register different event types to the same topic
+ let e_listener = event_sys.register::<E>(&Topic::TopicE).await;
+ let f_listener = event_sys.register::<F>(&Topic::TopicE).await;
+
+ event_sys
+ .emit_by_topic(&Topic::TopicE, &E { e_value: 5 })
+ .await;
+
+ let msg = e_listener.recv().await.unwrap();
+ assert_eq!(msg, E { e_value: 5 });
+
+ event_sys
+ .emit_by_topic(&Topic::TopicE, &F { f_value: 5 })
+ .await;
+
+ let msg = f_listener.recv().await.unwrap();
+ assert_eq!(msg, F { f_value: 5 });
+ });
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +
/// A set of helper tools and functions.
+pub mod util;
+
+/// A module containing async utilities that work with the
+/// [`smol`](https://github.com/smol-rs/smol) async runtime.
+pub mod async_util;
+
+/// Represents karyon's Core Error.
+pub mod error;
+
+/// [`event::EventSys`] implementation.
+pub mod event;
+
+/// A simple publish-subscribe system [`Read More`](./pubsub/struct.Publisher.html)
+pub mod pubsub;
+
+#[cfg(feature = "crypto")]
+/// Collects common cryptographic tools
+pub mod crypto;
+
+use smol::Executor as SmolEx;
+use std::sync::Arc;
+
+/// A pointer to an Executor
+pub type Executor<'a> = Arc<SmolEx<'a>>;
+
+/// A Global Executor
+pub type GlobalExecutor = Arc<SmolEx<'static>>;
+
+use error::Result;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +
use std::{collections::HashMap, sync::Arc};
+
+use log::error;
+use smol::lock::Mutex;
+
+use crate::{util::random_16, Result};
+
+pub type ArcPublisher<T> = Arc<Publisher<T>>;
+pub type SubscriptionID = u16;
+
+/// A simple publish-subscribe system.
+// # Example
+///
+/// ```
+/// use karyon_core::pubsub::{Publisher};
+///
+/// async {
+/// let publisher = Publisher::new();
+///
+/// let sub = publisher.subscribe().await;
+///
+/// publisher.notify(&String::from("MESSAGE")).await;
+///
+/// let msg = sub.recv().await;
+///
+/// // ....
+/// };
+///
+/// ```
+pub struct Publisher<T> {
+ subs: Mutex<HashMap<SubscriptionID, smol::channel::Sender<T>>>,
+}
+
+impl<T: Clone> Publisher<T> {
+ /// Creates a new Publisher
+ pub fn new() -> ArcPublisher<T> {
+ Arc::new(Self {
+ subs: Mutex::new(HashMap::new()),
+ })
+ }
+
+ /// Subscribe and return a Subscription
+ pub async fn subscribe(self: &Arc<Self>) -> Subscription<T> {
+ let mut subs = self.subs.lock().await;
+
+ let chan = smol::channel::unbounded();
+
+ let mut sub_id = random_16();
+
+ // While the SubscriptionID already exists, generate a new one
+ while subs.contains_key(&sub_id) {
+ sub_id = random_16();
+ }
+
+ let sub = Subscription::new(sub_id, self.clone(), chan.1);
+ subs.insert(sub_id, chan.0);
+
+ sub
+ }
+
+ /// Unsubscribe from the Publisher
+ pub async fn unsubscribe(self: &Arc<Self>, id: &SubscriptionID) {
+ self.subs.lock().await.remove(id);
+ }
+
+ /// Notify all subscribers
+ pub async fn notify(self: &Arc<Self>, value: &T) {
+ let mut subs = self.subs.lock().await;
+ let mut closed_subs = vec![];
+
+ for (sub_id, sub) in subs.iter() {
+ if let Err(err) = sub.send(value.clone()).await {
+ error!("failed to notify {}: {}", sub_id, err);
+ closed_subs.push(*sub_id);
+ }
+ }
+
+ for sub_id in closed_subs.iter() {
+ subs.remove(sub_id);
+ }
+ }
+}
+
+// Subscription
+pub struct Subscription<T> {
+ id: SubscriptionID,
+ recv_chan: smol::channel::Receiver<T>,
+ publisher: ArcPublisher<T>,
+}
+
+impl<T: Clone> Subscription<T> {
+ /// Creates a new Subscription
+ pub fn new(
+ id: SubscriptionID,
+ publisher: ArcPublisher<T>,
+ recv_chan: smol::channel::Receiver<T>,
+ ) -> Subscription<T> {
+ Self {
+ id,
+ recv_chan,
+ publisher,
+ }
+ }
+
+ /// Receive a message from the Publisher
+ pub async fn recv(&self) -> Result<T> {
+ let msg = self.recv_chan.recv().await?;
+ Ok(msg)
+ }
+
+ /// Unsubscribe from the Publisher
+ pub async fn unsubscribe(&self) {
+ self.publisher.unsubscribe(&self.id).await;
+ }
+}
+
use bincode::Decode;
+
+use crate::Result;
+
+/// Decodes a given type `T` from the given slice. returns the decoded value
+/// along with the number of bytes read.
+pub fn decode<T: Decode>(src: &[u8]) -> Result<(T, usize)> {
+ let (result, bytes_read) = bincode::decode_from_slice(src, bincode::config::standard())?;
+ Ok((result, bytes_read))
+}
+
use bincode::Encode;
+
+use crate::Result;
+
+/// Encode the given type `T` into a `Vec<u8>`.
+pub fn encode<T: Encode>(msg: &T) -> Result<Vec<u8>> {
+ let vec = bincode::encode_to_vec(msg, bincode::config::standard())?;
+ Ok(vec)
+}
+
+/// Encode the given type `T` into the given slice..
+pub fn encode_into_slice<T: Encode>(msg: &T, dst: &mut [u8]) -> Result<()> {
+ bincode::encode_into_slice(msg, dst, bincode::config::standard())?;
+ Ok(())
+}
+
mod decode;
+mod encode;
+mod path;
+
+pub use decode::decode;
+pub use encode::{encode, encode_into_slice};
+pub use path::{home_dir, tilde_expand};
+
+use rand::{rngs::OsRng, Rng};
+
+/// Generates and returns a random u32 using `rand::rngs::OsRng`.
+pub fn random_32() -> u32 {
+ OsRng.gen()
+}
+
+/// Generates and returns a random u16 using `rand::rngs::OsRng`.
+pub fn random_16() -> u16 {
+ OsRng.gen()
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +
use std::path::PathBuf;
+
+use crate::{error::Error, Result};
+
+/// Returns the user's home directory as a `PathBuf`.
+#[allow(dead_code)]
+pub fn home_dir() -> Result<PathBuf> {
+ dirs::home_dir().ok_or(Error::PathNotFound("Home dir not found"))
+}
+
+/// Expands a tilde (~) in a path and returns the expanded `PathBuf`.
+#[allow(dead_code)]
+pub fn tilde_expand(path: &str) -> Result<PathBuf> {
+ match path {
+ "~" => home_dir(),
+ p if p.starts_with("~/") => Ok(home_dir()?.join(&path[2..])),
+ _ => Ok(PathBuf::from(path)),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_tilde_expand() {
+ let path = "~/src";
+ let expanded_path = dirs::home_dir().unwrap().join("src");
+ assert_eq!(tilde_expand(path).unwrap(), expanded_path);
+
+ let path = "~";
+ let expanded_path = dirs::home_dir().unwrap();
+ assert_eq!(tilde_expand(path).unwrap(), expanded_path);
+
+ let path = "";
+ let expanded_path = PathBuf::from("");
+ assert_eq!(tilde_expand(path).unwrap(), expanded_path);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +
use log::debug;
+use serde::{de::DeserializeOwned, Serialize};
+
+use karyon_core::util::random_32;
+use karyon_net::ToConn;
+
+use crate::{
+ codec::{Codec, CodecConfig},
+ message, Error, Result, JSONRPC_VERSION,
+};
+
+/// Represents client config
+#[derive(Default)]
+pub struct ClientConfig {
+ pub timeout: Option<u64>,
+}
+
+/// Represents an RPC client
+pub struct Client {
+ codec: Codec,
+ config: ClientConfig,
+}
+
+impl Client {
+ /// Creates a new RPC client by passing a Tcp, Unix, or Tls connection.
+ pub fn new<C: ToConn>(conn: C, config: ClientConfig) -> Self {
+ let codec_config = CodecConfig {
+ max_allowed_buffer_size: 0,
+ ..Default::default()
+ };
+ let codec = Codec::new(conn.to_conn(), codec_config);
+ Self { codec, config }
+ }
+
+ /// Calls the provided method, waits for the response, and returns the result.
+ pub async fn call<T: Serialize + DeserializeOwned, V: DeserializeOwned>(
+ &self,
+ method: &str,
+ params: T,
+ ) -> Result<V> {
+ let id = serde_json::json!(random_32());
+
+ let request = message::Request {
+ jsonrpc: JSONRPC_VERSION.to_string(),
+ id,
+ method: method.to_string(),
+ params: serde_json::json!(params),
+ };
+
+ let mut payload = serde_json::to_vec(&request)?;
+ payload.push(b'\n');
+ self.codec.write_all(&payload).await?;
+ debug!("--> {request}");
+
+ let mut buffer = vec![];
+ if let Some(t) = self.config.timeout {
+ self.codec.read_until_with_timeout(&mut buffer, t).await?;
+ } else {
+ self.codec.read_until(&mut buffer).await?;
+ };
+
+ let response = serde_json::from_slice::<message::Response>(&buffer)?;
+ debug!("<-- {response}");
+
+ if let Some(error) = response.error {
+ return Err(Error::CallError(error.code, error.message));
+ }
+
+ if response.id.is_none() || response.id.unwrap() != request.id {
+ return Err(Error::InvalidMsg("Invalid response id"));
+ }
+
+ match response.result {
+ Some(result) => Ok(serde_json::from_value::<V>(result)?),
+ None => Err(Error::InvalidMsg("Invalid response result")),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +
use memchr::memchr;
+
+use karyon_core::async_util::timeout;
+use karyon_net::Conn;
+
+use crate::{Error, Result};
+
+const DEFAULT_BUFFER_SIZE: usize = 1024;
+const DEFAULT_MAX_ALLOWED_BUFFER_SIZE: usize = 1024 * 1024; // 1MB
+
+// TODO: Add unit tests for Codec's functions.
+
+/// Represents Codec config
+#[derive(Clone)]
+pub struct CodecConfig {
+ pub default_buffer_size: usize,
+ /// The maximum allowed buffer size to receive a message. If set to zero,
+ /// there will be no size limit.
+ pub max_allowed_buffer_size: usize,
+}
+
+impl Default for CodecConfig {
+ fn default() -> Self {
+ Self {
+ default_buffer_size: DEFAULT_BUFFER_SIZE,
+ max_allowed_buffer_size: DEFAULT_MAX_ALLOWED_BUFFER_SIZE,
+ }
+ }
+}
+
+pub struct Codec {
+ conn: Conn,
+ config: CodecConfig,
+}
+
+impl Codec {
+ /// Creates a new Codec
+ pub fn new(conn: Conn, config: CodecConfig) -> Self {
+ Self { conn, config }
+ }
+
+ /// Read all bytes into `buffer` until the `0x0A` byte or EOF is
+ /// reached.
+ ///
+ /// If successful, this function will return the total number of bytes read.
+ pub async fn read_until(&self, buffer: &mut Vec<u8>) -> Result<usize> {
+ let delim = b'\n';
+
+ let mut read = 0;
+
+ loop {
+ let mut tmp_buf = vec![0; self.config.default_buffer_size];
+ let n = self.conn.read(&mut tmp_buf).await?;
+ if n == 0 {
+ return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
+ }
+
+ match memchr(delim, &tmp_buf) {
+ Some(i) => {
+ buffer.extend_from_slice(&tmp_buf[..=i]);
+ read += i + 1;
+ break;
+ }
+ None => {
+ buffer.extend_from_slice(&tmp_buf);
+ read += tmp_buf.len();
+ }
+ }
+
+ if self.config.max_allowed_buffer_size != 0
+ && buffer.len() == self.config.max_allowed_buffer_size
+ {
+ return Err(Error::InvalidMsg(
+ "Message exceeds the maximum allowed size",
+ ));
+ }
+ }
+
+ Ok(read)
+ }
+
+ /// Writes an entire buffer into the given connection.
+ pub async fn write_all(&self, mut buf: &[u8]) -> Result<()> {
+ while !buf.is_empty() {
+ let n = self.conn.write(buf).await?;
+ let (_, rest) = std::mem::take(&mut buf).split_at(n);
+ buf = rest;
+
+ if n == 0 {
+ return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
+ }
+ }
+
+ Ok(())
+ }
+
+ pub async fn read_until_with_timeout(&self, buffer: &mut Vec<u8>, t: u64) -> Result<usize> {
+ timeout(std::time::Duration::from_secs(t), self.read_until(buffer)).await?
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +
use thiserror::Error as ThisError;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Represents karyon's jsonrpc Error.
+#[derive(ThisError, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ IO(#[from] std::io::Error),
+
+ #[error("Call Error: code: {0} msg: {1}")]
+ CallError(i32, String),
+
+ #[error("RPC Method Error: code: {0} msg: {1}")]
+ RPCMethodError(i32, &'static str),
+
+ #[error("Invalid Params: {0}")]
+ InvalidParams(&'static str),
+
+ #[error("Invalid Request: {0}")]
+ InvalidRequest(&'static str),
+
+ #[error(transparent)]
+ ParseJSON(#[from] serde_json::Error),
+
+ #[error("Invalid Message Error: {0}")]
+ InvalidMsg(&'static str),
+
+ #[error(transparent)]
+ KaryonCore(#[from] karyon_core::error::Error),
+
+ #[error(transparent)]
+ KaryonNet(#[from] karyon_net::NetError),
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +
//! A fast and lightweight async implementation of [JSON-RPC
+//! 2.0](https://www.jsonrpc.org/specification), supporting the Tcp and Unix protocols.
+//!
+//! # Example
+//!
+//! ```
+//! use std::sync::Arc;
+//!
+//! use serde_json::Value;
+//! use smol::net::{TcpStream, TcpListener};
+//!
+//! use karyon_jsonrpc::{JsonRPCError, Server, Client, register_service, ServerConfig, ClientConfig};
+//!
+//! struct HelloWorld {}
+//!
+//! impl HelloWorld {
+//! async fn say_hello(&self, params: Value) -> Result<Value, JsonRPCError> {
+//! let msg: String = serde_json::from_value(params)?;
+//! Ok(serde_json::json!(format!("Hello {msg}!")))
+//! }
+//!
+//! async fn foo(&self, params: Value) -> Result<Value, JsonRPCError> {
+//! Ok(serde_json::json!("foo!"))
+//! }
+//!
+//! async fn bar(&self, params: Value) -> Result<Value, JsonRPCError> {
+//! Ok(serde_json::json!("bar!"))
+//! }
+//! }
+//!
+//! // Server
+//! async {
+//! let ex = Arc::new(smol::Executor::new());
+//!
+//! // Creates a new server
+//! let listener = TcpListener::bind("127.0.0.1:60000").await.unwrap();
+//! let config = ServerConfig::default();
+//! let server = Server::new(listener, config, ex.clone());
+//!
+//! // Register the HelloWorld service
+//! register_service!(HelloWorld, say_hello, foo, bar);
+//! server.attach_service(HelloWorld{});
+//!
+//! // Starts the server
+//! ex.run(server.start());
+//! };
+//!
+//! // Client
+//! async {
+//!
+//! // Creates a new client
+//! let conn = TcpStream::connect("127.0.0.1:60000").await.unwrap();
+//! let config = ClientConfig::default();
+//! let client = Client::new(conn, config);
+//!
+//! let result: String = client.call("HelloWorld.say_hello", "world".to_string()).await.unwrap();
+//! };
+//!
+//! ```
+
+mod client;
+mod codec;
+mod error;
+pub mod message;
+mod server;
+mod service;
+
+pub use client::{Client, ClientConfig};
+pub use codec::CodecConfig;
+pub use error::Error as JsonRPCError;
+pub use server::{Server, ServerConfig};
+pub use service::{RPCMethod, RPCService};
+
+pub use karyon_net::Endpoint;
+
+const JSONRPC_VERSION: &str = "2.0";
+use error::{Error, Result};
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +
use serde::{Deserialize, Serialize};
+
+/// Parse error: Invalid JSON was received by the server.
+pub const PARSE_ERROR_CODE: i32 = -32700;
+
+/// Invalid request: The JSON sent is not a valid Request object.
+pub const INVALID_REQUEST_ERROR_CODE: i32 = -32600;
+
+/// Method not found: The method does not exist / is not available.
+pub const METHOD_NOT_FOUND_ERROR_CODE: i32 = -32601;
+
+/// Invalid params: Invalid method parameter(s).
+pub const INVALID_PARAMS_ERROR_CODE: i32 = -32602;
+
+/// Internal error: Internal JSON-RPC error.
+pub const INTERNAL_ERROR_CODE: i32 = -32603;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Request {
+ pub jsonrpc: String,
+ pub method: String,
+ pub params: serde_json::Value,
+ pub id: serde_json::Value,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Response {
+ pub jsonrpc: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub result: Option<serde_json::Value>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub error: Option<Error>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub id: Option<serde_json::Value>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Error {
+ pub code: i32,
+ pub message: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub data: Option<serde_json::Value>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Notification {
+ pub jsonrpc: String,
+ pub method: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub params: Option<serde_json::Value>,
+}
+
+impl std::fmt::Display for Request {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{{jsonrpc: {}, method: {}, params: {:?}, id: {:?}}}",
+ self.jsonrpc, self.method, self.params, self.id
+ )
+ }
+}
+
+impl std::fmt::Display for Response {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{{jsonrpc: {}, result': {:?}, error: {:?} , id: {:?}}}",
+ self.jsonrpc, self.result, self.error, self.id
+ )
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "RpcError {{ code: {}, message: {}, data: {:?} }} ",
+ self.code, self.message, self.data
+ )
+ }
+}
+
+impl std::fmt::Display for Notification {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{{jsonrpc: {}, method: {}, params: {:?}}}",
+ self.jsonrpc, self.method, self.params
+ )
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +
use std::{collections::HashMap, sync::Arc};
+
+use log::{debug, error, warn};
+use smol::lock::RwLock;
+
+use karyon_core::{
+ async_util::{TaskGroup, TaskResult},
+ Executor,
+};
+
+use karyon_net::{Conn, Listener, ToListener};
+
+use crate::{
+ codec::{Codec, CodecConfig},
+ message,
+ service::RPCService,
+ Endpoint, Error, Result, JSONRPC_VERSION,
+};
+
+pub const INVALID_REQUEST_ERROR_MSG: &str = "Invalid request";
+pub const FAILED_TO_PARSE_ERROR_MSG: &str = "Failed to parse";
+pub const METHOD_NOT_FOUND_ERROR_MSG: &str = "Method not found";
+pub const INTERNAL_ERROR_MSG: &str = "Internal error";
+
+fn pack_err_res(code: i32, msg: &str, id: Option<serde_json::Value>) -> message::Response {
+ let err = message::Error {
+ code,
+ message: msg.to_string(),
+ data: None,
+ };
+
+ message::Response {
+ jsonrpc: JSONRPC_VERSION.to_string(),
+ error: Some(err),
+ result: None,
+ id,
+ }
+}
+
+/// RPC server config
+#[derive(Default)]
+pub struct ServerConfig {
+ codec_config: CodecConfig,
+}
+
+/// Represents an RPC server
+pub struct Server<'a> {
+ listener: Listener,
+ services: RwLock<HashMap<String, Box<dyn RPCService + 'a>>>,
+ task_group: TaskGroup<'a>,
+ config: ServerConfig,
+}
+
+impl<'a> Server<'a> {
+ /// Creates a new RPC server by passing a listener. It supports Tcp, Unix, and Tls.
+ pub fn new<T: ToListener>(listener: T, config: ServerConfig, ex: Executor<'a>) -> Arc<Self> {
+ Arc::new(Self {
+ listener: listener.to_listener(),
+ services: RwLock::new(HashMap::new()),
+ task_group: TaskGroup::new(ex),
+ config,
+ })
+ }
+
+ /// Returns the local endpoint.
+ pub fn local_endpoint(&self) -> Result<Endpoint> {
+ self.listener.local_endpoint().map_err(Error::KaryonNet)
+ }
+
+ /// Starts the RPC server
+ pub async fn start(self: Arc<Self>) -> Result<()> {
+ loop {
+ let conn = self.listener.accept().await?;
+ if let Err(err) = self.handle_conn(conn).await {
+ error!("Failed to handle a new conn: {err}")
+ }
+ }
+ }
+
+ /// Attach a new service to the RPC server
+ pub async fn attach_service(&self, service: impl RPCService + 'a) {
+ self.services
+ .write()
+ .await
+ .insert(service.name(), Box::new(service));
+ }
+
+ /// Shuts down the RPC server
+ pub async fn shutdown(&self) {
+ self.task_group.cancel().await;
+ }
+
+ /// Handles a new connection
+ async fn handle_conn(self: &Arc<Self>, conn: Conn) -> Result<()> {
+ let endpoint = conn.peer_endpoint()?;
+ debug!("Handle a new connection {endpoint}");
+
+ let on_failure = |result: TaskResult<Result<()>>| async move {
+ if let TaskResult::Completed(Err(err)) = result {
+ error!("Connection {} dropped: {}", endpoint, err);
+ } else {
+ warn!("Connection {} dropped", endpoint);
+ }
+ };
+
+ let codec = Codec::new(conn, self.config.codec_config.clone());
+
+ let selfc = self.clone();
+ self.task_group.spawn(
+ async move {
+ loop {
+ let mut buffer = vec![];
+ codec.read_until(&mut buffer).await?;
+ let response = selfc.handle_request(&buffer).await;
+ let mut payload = serde_json::to_vec(&response)?;
+ payload.push(b'\n');
+ codec.write_all(&payload).await?;
+ debug!("--> {response}");
+ }
+ },
+ on_failure,
+ );
+
+ Ok(())
+ }
+
+ /// Handles a request
+ async fn handle_request(&self, buffer: &[u8]) -> message::Response {
+ let rpc_msg = match serde_json::from_slice::<message::Request>(buffer) {
+ Ok(m) => m,
+ Err(_) => {
+ return pack_err_res(message::PARSE_ERROR_CODE, FAILED_TO_PARSE_ERROR_MSG, None);
+ }
+ };
+
+ debug!("<-- {rpc_msg}");
+
+ let srvc_method: Vec<&str> = rpc_msg.method.split('.').collect();
+ if srvc_method.len() != 2 {
+ return pack_err_res(
+ message::INVALID_REQUEST_ERROR_CODE,
+ INVALID_REQUEST_ERROR_MSG,
+ Some(rpc_msg.id),
+ );
+ }
+
+ let srvc_name = srvc_method[0];
+ let method_name = srvc_method[1];
+
+ let services = self.services.read().await;
+
+ let service = match services.get(srvc_name) {
+ Some(s) => s,
+ None => {
+ return pack_err_res(
+ message::METHOD_NOT_FOUND_ERROR_CODE,
+ METHOD_NOT_FOUND_ERROR_MSG,
+ Some(rpc_msg.id),
+ );
+ }
+ };
+
+ let method = match service.get_method(method_name) {
+ Some(m) => m,
+ None => {
+ return pack_err_res(
+ message::METHOD_NOT_FOUND_ERROR_CODE,
+ METHOD_NOT_FOUND_ERROR_MSG,
+ Some(rpc_msg.id),
+ );
+ }
+ };
+
+ let result = match method(rpc_msg.params.clone()).await {
+ Ok(res) => res,
+ Err(Error::ParseJSON(_)) => {
+ return pack_err_res(
+ message::PARSE_ERROR_CODE,
+ FAILED_TO_PARSE_ERROR_MSG,
+ Some(rpc_msg.id),
+ );
+ }
+ Err(Error::InvalidParams(msg)) => {
+ return pack_err_res(message::INVALID_PARAMS_ERROR_CODE, msg, Some(rpc_msg.id));
+ }
+ Err(Error::InvalidRequest(msg)) => {
+ return pack_err_res(message::INVALID_REQUEST_ERROR_CODE, msg, Some(rpc_msg.id));
+ }
+ Err(Error::RPCMethodError(code, msg)) => {
+ return pack_err_res(code, msg, Some(rpc_msg.id));
+ }
+ Err(_) => {
+ return pack_err_res(
+ message::INTERNAL_ERROR_CODE,
+ INTERNAL_ERROR_MSG,
+ Some(rpc_msg.id),
+ );
+ }
+ };
+
+ message::Response {
+ jsonrpc: JSONRPC_VERSION.to_string(),
+ error: None,
+ result: Some(result),
+ id: Some(rpc_msg.id),
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +
use std::{future::Future, pin::Pin};
+
+use crate::Result;
+
+/// Represents the RPC method
+pub type RPCMethod<'a> = Box<dyn Fn(serde_json::Value) -> RPCMethodOutput<'a> + Send + 'a>;
+type RPCMethodOutput<'a> =
+ Pin<Box<dyn Future<Output = Result<serde_json::Value>> + Send + Sync + 'a>>;
+
+/// Defines the interface for an RPC service.
+pub trait RPCService: Sync + Send {
+ fn get_method<'a>(&'a self, name: &'a str) -> Option<RPCMethod>;
+ fn name(&self) -> String;
+}
+
+/// Implements the [`RPCService`] trait for a provided type.
+///
+/// # Example
+///
+/// ```
+/// use serde_json::Value;
+///
+/// use karyon_jsonrpc::{JsonRPCError, register_service};
+///
+/// struct Hello {}
+///
+/// impl Hello {
+/// async fn foo(&self, params: Value) -> Result<Value, JsonRPCError> {
+/// Ok(serde_json::json!("foo!"))
+/// }
+///
+/// async fn bar(&self, params: Value) -> Result<Value, JsonRPCError> {
+/// Ok(serde_json::json!("bar!"))
+/// }
+/// }
+///
+/// register_service!(Hello, foo, bar);
+///
+/// ```
+#[macro_export]
+macro_rules! register_service {
+ ($t:ty, $($m:ident),*) => {
+ impl karyon_jsonrpc::RPCService for $t {
+ fn get_method<'a>(
+ &'a self,
+ name: &'a str
+ ) -> Option<karyon_jsonrpc::RPCMethod> {
+ match name {
+ $(
+ stringify!($m) => {
+ Some(Box::new(move |params: serde_json::Value| Box::pin(self.$m(params))))
+ }
+ )*
+ _ => None,
+ }
+
+
+ }
+ fn name(&self) -> String{
+ stringify!($t).to_string()
+ }
+ }
+ };
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +
use async_trait::async_trait;
+
+use crate::{
+ transports::{tcp, udp, unix},
+ Endpoint, Error, Result,
+};
+
+/// Alias for `Box<dyn Connection>`
+pub type Conn = Box<dyn Connection>;
+
+/// A trait for objects which can be converted to [`Conn`].
+pub trait ToConn {
+ fn to_conn(self) -> Conn;
+}
+
+/// Connection is a generic network connection interface for
+/// [`udp::UdpConn`], [`tcp::TcpConn`], and [`unix::UnixConn`].
+///
+/// If you are familiar with the Go language, this is similar to the
+/// [Conn](https://pkg.go.dev/net#Conn) interface
+#[async_trait]
+pub trait Connection: Send + Sync {
+ /// Returns the remote peer endpoint of this connection
+ fn peer_endpoint(&self) -> Result<Endpoint>;
+
+ /// Returns the local socket endpoint of this connection
+ fn local_endpoint(&self) -> Result<Endpoint>;
+
+ /// Reads data from this connection.
+ async fn read(&self, buf: &mut [u8]) -> Result<usize>;
+
+ /// Writes data to this connection
+ async fn write(&self, buf: &[u8]) -> Result<usize>;
+}
+
+/// Connects to the provided endpoint.
+///
+/// it only supports `tcp4/6`, `udp4/6`, and `unix`.
+///
+/// #Example
+///
+/// ```
+/// use karyon_net::{Endpoint, dial};
+///
+/// async {
+/// let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+///
+/// let conn = dial(&endpoint).await.unwrap();
+///
+/// conn.write(b"MSG").await.unwrap();
+///
+/// let mut buffer = [0;32];
+/// conn.read(&mut buffer).await.unwrap();
+/// };
+///
+/// ```
+///
+pub async fn dial(endpoint: &Endpoint) -> Result<Conn> {
+ match endpoint {
+ Endpoint::Tcp(_, _) => Ok(Box::new(tcp::dial_tcp(endpoint).await?)),
+ Endpoint::Udp(_, _) => Ok(Box::new(udp::dial_udp(endpoint).await?)),
+ Endpoint::Unix(addr) => Ok(Box::new(unix::dial_unix(addr).await?)),
+ _ => Err(Error::InvalidEndpoint(endpoint.to_string())),
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +
use std::{
+ net::{IpAddr, SocketAddr},
+ os::unix::net::SocketAddr as UnixSocketAddress,
+ path::PathBuf,
+ str::FromStr,
+};
+
+use bincode::{Decode, Encode};
+use url::Url;
+
+use crate::{Error, Result};
+
+/// Port defined as a u16.
+pub type Port = u16;
+
+/// Endpoint defines generic network endpoints for karyon.
+///
+/// # Example
+///
+/// ```
+/// use std::net::SocketAddr;
+///
+/// use karyon_net::Endpoint;
+///
+/// let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+///
+/// let socketaddr: SocketAddr = "127.0.0.1:3000".parse().unwrap();
+/// let endpoint = Endpoint::new_udp_addr(&socketaddr);
+///
+/// ```
+///
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Endpoint {
+ Udp(Addr, Port),
+ Tcp(Addr, Port),
+ Tls(Addr, Port),
+ Ws(Addr, Port),
+ Unix(String),
+}
+
+impl std::fmt::Display for Endpoint {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ Endpoint::Udp(ip, port) => {
+ write!(f, "udp://{}:{}", ip, port)
+ }
+ Endpoint::Tcp(ip, port) => {
+ write!(f, "tcp://{}:{}", ip, port)
+ }
+ Endpoint::Tls(ip, port) => {
+ write!(f, "tls://{}:{}", ip, port)
+ }
+ Endpoint::Ws(ip, port) => {
+ write!(f, "ws://{}:{}", ip, port)
+ }
+ Endpoint::Unix(path) => {
+ if path.is_empty() {
+ write!(f, "unix:/UNNAMED")
+ } else {
+ write!(f, "unix:/{}", path)
+ }
+ }
+ }
+ }
+}
+
+impl TryFrom<Endpoint> for SocketAddr {
+ type Error = Error;
+ fn try_from(endpoint: Endpoint) -> std::result::Result<SocketAddr, Self::Error> {
+ match endpoint {
+ Endpoint::Udp(ip, port)
+ | Endpoint::Tcp(ip, port)
+ | Endpoint::Tls(ip, port)
+ | Endpoint::Ws(ip, port) => Ok(SocketAddr::new(ip.try_into()?, port)),
+ Endpoint::Unix(_) => Err(Error::TryFromEndpoint),
+ }
+ }
+}
+
+impl TryFrom<Endpoint> for PathBuf {
+ type Error = Error;
+ fn try_from(endpoint: Endpoint) -> std::result::Result<PathBuf, Self::Error> {
+ match endpoint {
+ Endpoint::Unix(path) => Ok(PathBuf::from(&path)),
+ _ => Err(Error::TryFromEndpoint),
+ }
+ }
+}
+
+impl TryFrom<Endpoint> for UnixSocketAddress {
+ type Error = Error;
+ fn try_from(endpoint: Endpoint) -> std::result::Result<UnixSocketAddress, Self::Error> {
+ match endpoint {
+ Endpoint::Unix(a) => Ok(UnixSocketAddress::from_pathname(a)?),
+ _ => Err(Error::TryFromEndpoint),
+ }
+ }
+}
+
+impl FromStr for Endpoint {
+ type Err = Error;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ let url: Url = match s.parse() {
+ Ok(u) => u,
+ Err(err) => return Err(Error::ParseEndpoint(err.to_string())),
+ };
+
+ if url.has_host() {
+ let host = url.host_str().unwrap();
+
+ let addr = match host.parse::<IpAddr>() {
+ Ok(addr) => Addr::Ip(addr),
+ Err(_) => Addr::Domain(host.to_string()),
+ };
+
+ let port = match url.port() {
+ Some(p) => p,
+ None => return Err(Error::ParseEndpoint(format!("port missing: {s}"))),
+ };
+
+ match url.scheme() {
+ "tcp" => Ok(Endpoint::Tcp(addr, port)),
+ "udp" => Ok(Endpoint::Udp(addr, port)),
+ "tls" => Ok(Endpoint::Tls(addr, port)),
+ "ws" => Ok(Endpoint::Ws(addr, port)),
+ _ => Err(Error::InvalidEndpoint(s.to_string())),
+ }
+ } else {
+ if url.path().is_empty() {
+ return Err(Error::InvalidEndpoint(s.to_string()));
+ }
+
+ match url.scheme() {
+ "unix" => Ok(Endpoint::Unix(url.path().to_string())),
+ _ => Err(Error::InvalidEndpoint(s.to_string())),
+ }
+ }
+ }
+}
+
+impl Endpoint {
+ /// Creates a new TCP endpoint from a `SocketAddr`.
+ pub fn new_tcp_addr(addr: &SocketAddr) -> Endpoint {
+ Endpoint::Tcp(Addr::Ip(addr.ip()), addr.port())
+ }
+
+ /// Creates a new UDP endpoint from a `SocketAddr`.
+ pub fn new_udp_addr(addr: &SocketAddr) -> Endpoint {
+ Endpoint::Udp(Addr::Ip(addr.ip()), addr.port())
+ }
+
+ /// Creates a new TLS endpoint from a `SocketAddr`.
+ pub fn new_tls_addr(addr: &SocketAddr) -> Endpoint {
+ Endpoint::Tls(Addr::Ip(addr.ip()), addr.port())
+ }
+
+ /// Creates a new WS endpoint from a `SocketAddr`.
+ pub fn new_ws_addr(addr: &SocketAddr) -> Endpoint {
+ Endpoint::Ws(Addr::Ip(addr.ip()), addr.port())
+ }
+
+ /// Creates a new Unix endpoint from a `UnixSocketAddress`.
+ pub fn new_unix_addr(addr: &UnixSocketAddress) -> Endpoint {
+ Endpoint::Unix(
+ addr.as_pathname()
+ .and_then(|a| a.to_str())
+ .unwrap_or("")
+ .to_string(),
+ )
+ }
+
+ /// Returns the `Port` of the endpoint.
+ pub fn port(&self) -> Result<&Port> {
+ match self {
+ Endpoint::Tcp(_, port)
+ | Endpoint::Udp(_, port)
+ | Endpoint::Tls(_, port)
+ | Endpoint::Ws(_, port) => Ok(port),
+ _ => Err(Error::TryFromEndpoint),
+ }
+ }
+
+ /// Returns the `Addr` of the endpoint.
+ pub fn addr(&self) -> Result<&Addr> {
+ match self {
+ Endpoint::Tcp(addr, _)
+ | Endpoint::Udp(addr, _)
+ | Endpoint::Tls(addr, _)
+ | Endpoint::Ws(addr, _) => Ok(addr),
+ _ => Err(Error::TryFromEndpoint),
+ }
+ }
+}
+
+/// Addr defines a type for an address, either IP or domain.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode)]
+pub enum Addr {
+ Ip(IpAddr),
+ Domain(String),
+}
+
+impl TryFrom<Addr> for IpAddr {
+ type Error = Error;
+ fn try_from(addr: Addr) -> std::result::Result<IpAddr, Self::Error> {
+ match addr {
+ Addr::Ip(ip) => Ok(ip),
+ Addr::Domain(d) => Err(Error::InvalidAddress(d)),
+ }
+ }
+}
+
+impl std::fmt::Display for Addr {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ Addr::Ip(ip) => {
+ write!(f, "{}", ip)
+ }
+ Addr::Domain(d) => {
+ write!(f, "{}", d)
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::net::Ipv4Addr;
+
+ #[test]
+ fn test_endpoint_from_str() {
+ let endpoint_str: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+ let endpoint = Endpoint::Tcp(Addr::Ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), 3000);
+ assert_eq!(endpoint_str, endpoint);
+
+ let endpoint_str: Endpoint = "udp://127.0.0.1:4000".parse().unwrap();
+ let endpoint = Endpoint::Udp(Addr::Ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))), 4000);
+ assert_eq!(endpoint_str, endpoint);
+
+ let endpoint_str: Endpoint = "tcp://example.com:3000".parse().unwrap();
+ let endpoint = Endpoint::Tcp(Addr::Domain("example.com".to_string()), 3000);
+ assert_eq!(endpoint_str, endpoint);
+
+ let endpoint_str = "unix:/home/x/s.socket".parse::<Endpoint>().unwrap();
+ let endpoint = Endpoint::Unix("/home/x/s.socket".to_string());
+ assert_eq!(endpoint_str, endpoint);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +
use thiserror::Error as ThisError;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(ThisError, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ IO(#[from] std::io::Error),
+
+ #[error("Try from endpoint Error")]
+ TryFromEndpoint,
+
+ #[error("invalid address {0}")]
+ InvalidAddress(String),
+
+ #[error("invalid endpoint {0}")]
+ InvalidEndpoint(String),
+
+ #[error("Parse endpoint error {0}")]
+ ParseEndpoint(String),
+
+ #[error("Timeout Error")]
+ Timeout,
+
+ #[error("Channel Send Error: {0}")]
+ ChannelSend(String),
+
+ #[error(transparent)]
+ ChannelRecv(#[from] smol::channel::RecvError),
+
+ #[error("Tls Error: {0}")]
+ Rustls(#[from] futures_rustls::rustls::Error),
+
+ #[error("Invalid DNS Name: {0}")]
+ InvalidDnsNameError(#[from] futures_rustls::pki_types::InvalidDnsNameError),
+
+ #[error(transparent)]
+ KaryonCore(#[from] karyon_core::error::Error),
+}
+
+impl<T> From<smol::channel::SendError<T>> for Error {
+ fn from(error: smol::channel::SendError<T>) -> Self {
+ Error::ChannelSend(error.to_string())
+ }
+}
+
mod connection;
+mod endpoint;
+mod error;
+mod listener;
+mod transports;
+
+pub use {
+ connection::{dial, Conn, Connection, ToConn},
+ endpoint::{Addr, Endpoint, Port},
+ listener::{listen, ConnListener, Listener, ToListener},
+ transports::{
+ tcp::{dial_tcp, listen_tcp, TcpConn},
+ tls,
+ udp::{dial_udp, listen_udp, UdpConn},
+ unix::{dial_unix, listen_unix, UnixConn},
+ },
+};
+
+use error::{Error, Result};
+
+/// Represents karyon's Net Error
+pub use error::Error as NetError;
+
+/// Represents karyon's Net Result
+pub use error::Result as NetResult;
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +
use async_trait::async_trait;
+
+use crate::{
+ transports::{tcp, unix},
+ Conn, Endpoint, Error, Result,
+};
+
+/// Alias for `Box<dyn ConnListener>`
+pub type Listener = Box<dyn ConnListener>;
+
+/// A trait for objects which can be converted to [`Listener`].
+pub trait ToListener {
+ fn to_listener(self) -> Listener;
+}
+
+/// ConnListener is a generic network listener.
+#[async_trait]
+pub trait ConnListener: Send + Sync {
+ fn local_endpoint(&self) -> Result<Endpoint>;
+ async fn accept(&self) -> Result<Conn>;
+}
+
+/// Listens to the provided endpoint.
+///
+/// it only supports `tcp4/6`, and `unix`.
+///
+/// #Example
+///
+/// ```
+/// use karyon_net::{Endpoint, listen};
+///
+/// async {
+/// let endpoint: Endpoint = "tcp://127.0.0.1:3000".parse().unwrap();
+///
+/// let listener = listen(&endpoint).await.unwrap();
+/// let conn = listener.accept().await.unwrap();
+/// };
+///
+/// ```
+pub async fn listen(endpoint: &Endpoint) -> Result<Box<dyn ConnListener>> {
+ match endpoint {
+ Endpoint::Tcp(_, _) => Ok(Box::new(tcp::listen_tcp(endpoint).await?)),
+ Endpoint::Unix(addr) => Ok(Box::new(unix::listen_unix(addr)?)),
+ _ => Err(Error::InvalidEndpoint(endpoint.to_string())),
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +
use std::net::SocketAddr;
+
+use async_trait::async_trait;
+use smol::{
+ io::{split, AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf},
+ lock::Mutex,
+ net::{TcpListener, TcpStream},
+};
+
+use crate::{
+ connection::{Connection, ToConn},
+ endpoint::Endpoint,
+ listener::{ConnListener, ToListener},
+ Error, Result,
+};
+
+/// TCP network connection implementation of the [`Connection`] trait.
+pub struct TcpConn {
+ inner: TcpStream,
+ read: Mutex<ReadHalf<TcpStream>>,
+ write: Mutex<WriteHalf<TcpStream>>,
+}
+
+impl TcpConn {
+ /// Creates a new TcpConn
+ pub fn new(conn: TcpStream) -> Self {
+ let (read, write) = split(conn.clone());
+ Self {
+ inner: conn,
+ read: Mutex::new(read),
+ write: Mutex::new(write),
+ }
+ }
+}
+
+#[async_trait]
+impl Connection for TcpConn {
+ fn peer_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_tcp_addr(&self.inner.peer_addr()?))
+ }
+
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_tcp_addr(&self.inner.local_addr()?))
+ }
+
+ async fn read(&self, buf: &mut [u8]) -> Result<usize> {
+ self.read.lock().await.read(buf).await.map_err(Error::from)
+ }
+
+ async fn write(&self, buf: &[u8]) -> Result<usize> {
+ self.write
+ .lock()
+ .await
+ .write(buf)
+ .await
+ .map_err(Error::from)
+ }
+}
+
+#[async_trait]
+impl ConnListener for TcpListener {
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_tcp_addr(&self.local_addr()?))
+ }
+
+ async fn accept(&self) -> Result<Box<dyn Connection>> {
+ let (conn, _) = self.accept().await?;
+ conn.set_nodelay(true)?;
+ Ok(Box::new(TcpConn::new(conn)))
+ }
+}
+
+/// Connects to the given TCP address and port.
+pub async fn dial_tcp(endpoint: &Endpoint) -> Result<TcpConn> {
+ let addr = SocketAddr::try_from(endpoint.clone())?;
+ let conn = TcpStream::connect(addr).await?;
+ conn.set_nodelay(true)?;
+ Ok(TcpConn::new(conn))
+}
+
+/// Listens on the given TCP address and port.
+pub async fn listen_tcp(endpoint: &Endpoint) -> Result<TcpListener> {
+ let addr = SocketAddr::try_from(endpoint.clone())?;
+ let listener = TcpListener::bind(addr).await?;
+ Ok(listener)
+}
+
+impl From<TcpStream> for Box<dyn Connection> {
+ fn from(conn: TcpStream) -> Self {
+ Box::new(TcpConn::new(conn))
+ }
+}
+
+impl From<TcpListener> for Box<dyn ConnListener> {
+ fn from(listener: TcpListener) -> Self {
+ Box::new(listener)
+ }
+}
+
+impl ToConn for TcpStream {
+ fn to_conn(self) -> Box<dyn Connection> {
+ self.into()
+ }
+}
+
+impl ToConn for TcpConn {
+ fn to_conn(self) -> Box<dyn Connection> {
+ Box::new(self)
+ }
+}
+
+impl ToListener for TcpListener {
+ fn to_listener(self) -> Box<dyn ConnListener> {
+ self.into()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +
use std::{net::SocketAddr, sync::Arc};
+
+use async_trait::async_trait;
+use futures_rustls::{pki_types, rustls, TlsAcceptor, TlsConnector, TlsStream};
+use smol::{
+ io::{split, AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf},
+ lock::Mutex,
+ net::{TcpListener, TcpStream},
+};
+
+use crate::{
+ connection::{Connection, ToConn},
+ endpoint::Endpoint,
+ listener::{ConnListener, ToListener},
+ Error, Result,
+};
+
+/// TLS network connection implementation of the [`Connection`] trait.
+pub struct TlsConn {
+ inner: TcpStream,
+ read: Mutex<ReadHalf<TlsStream<TcpStream>>>,
+ write: Mutex<WriteHalf<TlsStream<TcpStream>>>,
+}
+
+impl TlsConn {
+ /// Creates a new TlsConn
+ pub fn new(sock: TcpStream, conn: TlsStream<TcpStream>) -> Self {
+ let (read, write) = split(conn);
+ Self {
+ inner: sock,
+ read: Mutex::new(read),
+ write: Mutex::new(write),
+ }
+ }
+}
+
+#[async_trait]
+impl Connection for TlsConn {
+ fn peer_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_tls_addr(&self.inner.peer_addr()?))
+ }
+
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_tls_addr(&self.inner.local_addr()?))
+ }
+
+ async fn read(&self, buf: &mut [u8]) -> Result<usize> {
+ self.read.lock().await.read(buf).await.map_err(Error::from)
+ }
+
+ async fn write(&self, buf: &[u8]) -> Result<usize> {
+ self.write
+ .lock()
+ .await
+ .write(buf)
+ .await
+ .map_err(Error::from)
+ }
+}
+
+/// Connects to the given TLS address and port.
+pub async fn dial_tls(
+ endpoint: &Endpoint,
+ config: rustls::ClientConfig,
+ dns_name: &'static str,
+) -> Result<TlsConn> {
+ let addr = SocketAddr::try_from(endpoint.clone())?;
+
+ let connector = TlsConnector::from(Arc::new(config));
+
+ let sock = TcpStream::connect(addr).await?;
+ sock.set_nodelay(true)?;
+
+ let altname = pki_types::ServerName::try_from(dns_name)?;
+ let conn = connector.connect(altname, sock.clone()).await?;
+ Ok(TlsConn::new(sock, TlsStream::Client(conn)))
+}
+
+/// Connects to the given TLS endpoint, returns `Conn` ([`Connection`]).
+pub async fn dial(
+ endpoint: &Endpoint,
+ config: rustls::ClientConfig,
+ dns_name: &'static str,
+) -> Result<Box<dyn Connection>> {
+ match endpoint {
+ Endpoint::Tcp(..) | Endpoint::Tls(..) => {}
+ _ => return Err(Error::InvalidEndpoint(endpoint.to_string())),
+ }
+
+ dial_tls(endpoint, config, dns_name)
+ .await
+ .map(|c| Box::new(c) as Box<dyn Connection>)
+}
+
+/// Tls network listener implementation of the `Listener` [`ConnListener`] trait.
+pub struct TlsListener {
+ acceptor: TlsAcceptor,
+ listener: TcpListener,
+}
+
+#[async_trait]
+impl ConnListener for TlsListener {
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_tls_addr(&self.listener.local_addr()?))
+ }
+
+ async fn accept(&self) -> Result<Box<dyn Connection>> {
+ let (sock, _) = self.listener.accept().await?;
+ sock.set_nodelay(true)?;
+ let conn = self.acceptor.accept(sock.clone()).await?;
+ Ok(Box::new(TlsConn::new(sock, TlsStream::Server(conn))))
+ }
+}
+
+/// Listens on the given TLS address and port.
+pub async fn listen_tls(endpoint: &Endpoint, config: rustls::ServerConfig) -> Result<TlsListener> {
+ let addr = SocketAddr::try_from(endpoint.clone())?;
+ let acceptor = TlsAcceptor::from(Arc::new(config));
+ let listener = TcpListener::bind(addr).await?;
+ Ok(TlsListener { acceptor, listener })
+}
+
+impl From<TlsStream<TcpStream>> for Box<dyn Connection> {
+ fn from(conn: TlsStream<TcpStream>) -> Self {
+ Box::new(TlsConn::new(conn.get_ref().0.clone(), conn))
+ }
+}
+
+impl From<TlsListener> for Box<dyn ConnListener> {
+ fn from(listener: TlsListener) -> Self {
+ Box::new(listener)
+ }
+}
+
+impl ToConn for TlsStream<TcpStream> {
+ fn to_conn(self) -> Box<dyn Connection> {
+ self.into()
+ }
+}
+
+impl ToConn for TlsConn {
+ fn to_conn(self) -> Box<dyn Connection> {
+ Box::new(self)
+ }
+}
+
+impl ToListener for TlsListener {
+ fn to_listener(self) -> Box<dyn ConnListener> {
+ self.into()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +
use std::net::SocketAddr;
+
+use async_trait::async_trait;
+use smol::net::UdpSocket;
+
+use crate::{
+ connection::{Connection, ToConn},
+ endpoint::Endpoint,
+ Error, Result,
+};
+
+/// UDP network connection implementation of the [`Connection`] trait.
+pub struct UdpConn {
+ inner: UdpSocket,
+}
+
+impl UdpConn {
+ /// Creates a new UdpConn
+ pub fn new(conn: UdpSocket) -> Self {
+ Self { inner: conn }
+ }
+}
+
+impl UdpConn {
+ /// Receives a single datagram message. Returns the number of bytes read
+ /// and the origin endpoint.
+ pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, Endpoint)> {
+ let (size, addr) = self.inner.recv_from(buf).await?;
+ Ok((size, Endpoint::new_udp_addr(&addr)))
+ }
+
+ /// Sends data to the given address. Returns the number of bytes written.
+ pub async fn send_to(&self, buf: &[u8], addr: &Endpoint) -> Result<usize> {
+ let addr: SocketAddr = addr.clone().try_into()?;
+ let size = self.inner.send_to(buf, addr).await?;
+ Ok(size)
+ }
+}
+
+#[async_trait]
+impl Connection for UdpConn {
+ fn peer_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_udp_addr(&self.inner.peer_addr()?))
+ }
+
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_udp_addr(&self.inner.local_addr()?))
+ }
+
+ async fn read(&self, buf: &mut [u8]) -> Result<usize> {
+ self.inner.recv(buf).await.map_err(Error::from)
+ }
+
+ async fn write(&self, buf: &[u8]) -> Result<usize> {
+ self.inner.send(buf).await.map_err(Error::from)
+ }
+}
+
+/// Connects to the given UDP address and port.
+pub async fn dial_udp(endpoint: &Endpoint) -> Result<UdpConn> {
+ let addr = SocketAddr::try_from(endpoint.clone())?;
+
+ // Let the operating system assign an available port to this socket
+ let conn = UdpSocket::bind("[::]:0").await?;
+ conn.connect(addr).await?;
+ Ok(UdpConn::new(conn))
+}
+
+/// Listens on the given UDP address and port.
+pub async fn listen_udp(endpoint: &Endpoint) -> Result<UdpConn> {
+ let addr = SocketAddr::try_from(endpoint.clone())?;
+ let conn = UdpSocket::bind(addr).await?;
+ let udp_conn = UdpConn::new(conn);
+ Ok(udp_conn)
+}
+
+impl From<UdpSocket> for Box<dyn Connection> {
+ fn from(conn: UdpSocket) -> Self {
+ Box::new(UdpConn::new(conn))
+ }
+}
+
+impl ToConn for UdpSocket {
+ fn to_conn(self) -> Box<dyn Connection> {
+ self.into()
+ }
+}
+
+impl ToConn for UdpConn {
+ fn to_conn(self) -> Box<dyn Connection> {
+ Box::new(self)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +
use async_trait::async_trait;
+
+use smol::{
+ io::{split, AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf},
+ lock::Mutex,
+ net::unix::{UnixListener, UnixStream},
+};
+
+use crate::{
+ connection::{Connection, ToConn},
+ endpoint::Endpoint,
+ listener::{ConnListener, ToListener},
+ Error, Result,
+};
+
+/// Unix domain socket implementation of the [`Connection`] trait.
+pub struct UnixConn {
+ inner: UnixStream,
+ read: Mutex<ReadHalf<UnixStream>>,
+ write: Mutex<WriteHalf<UnixStream>>,
+}
+
+impl UnixConn {
+ /// Creates a new UnixConn
+ pub fn new(conn: UnixStream) -> Self {
+ let (read, write) = split(conn.clone());
+ Self {
+ inner: conn,
+ read: Mutex::new(read),
+ write: Mutex::new(write),
+ }
+ }
+}
+
+#[async_trait]
+impl Connection for UnixConn {
+ fn peer_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_unix_addr(&self.inner.peer_addr()?))
+ }
+
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_unix_addr(&self.inner.local_addr()?))
+ }
+
+ async fn read(&self, buf: &mut [u8]) -> Result<usize> {
+ self.read.lock().await.read(buf).await.map_err(Error::from)
+ }
+
+ async fn write(&self, buf: &[u8]) -> Result<usize> {
+ self.write
+ .lock()
+ .await
+ .write(buf)
+ .await
+ .map_err(Error::from)
+ }
+}
+
+#[async_trait]
+impl ConnListener for UnixListener {
+ fn local_endpoint(&self) -> Result<Endpoint> {
+ Ok(Endpoint::new_unix_addr(&self.local_addr()?))
+ }
+
+ async fn accept(&self) -> Result<Box<dyn Connection>> {
+ let (conn, _) = self.accept().await?;
+ Ok(Box::new(UnixConn::new(conn)))
+ }
+}
+
+/// Connects to the given Unix socket path.
+pub async fn dial_unix(path: &String) -> Result<UnixConn> {
+ let conn = UnixStream::connect(path).await?;
+ Ok(UnixConn::new(conn))
+}
+
+/// Listens on the given Unix socket path.
+pub fn listen_unix(path: &String) -> Result<UnixListener> {
+ let listener = UnixListener::bind(path)?;
+ Ok(listener)
+}
+
+impl From<UnixStream> for Box<dyn Connection> {
+ fn from(conn: UnixStream) -> Self {
+ Box::new(UnixConn::new(conn))
+ }
+}
+
+impl From<UnixListener> for Box<dyn ConnListener> {
+ fn from(listener: UnixListener) -> Self {
+ Box::new(listener)
+ }
+}
+
+impl ToConn for UnixStream {
+ fn to_conn(self) -> Box<dyn Connection> {
+ self.into()
+ }
+}
+
+impl ToConn for UnixConn {
+ fn to_conn(self) -> Box<dyn Connection> {
+ Box::new(self)
+ }
+}
+
+impl ToListener for UnixListener {
+ fn to_listener(self) -> Box<dyn ConnListener> {
+ self.into()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +
use std::sync::Arc;
+
+use log::info;
+
+use karyon_core::{crypto::KeyPair, pubsub::Subscription, GlobalExecutor};
+
+use crate::{
+ config::Config,
+ connection::ConnQueue,
+ discovery::{ArcDiscovery, Discovery},
+ monitor::{Monitor, MonitorEvent},
+ peer_pool::PeerPool,
+ protocol::{ArcProtocol, Protocol},
+ ArcPeer, PeerID, Result,
+};
+
+pub type ArcBackend = Arc<Backend>;
+
+/// Backend serves as the central entry point for initiating and managing
+/// the P2P network.
+pub struct Backend {
+ /// The Configuration for the P2P network.
+ config: Arc<Config>,
+
+ /// Identity Key pair
+ key_pair: KeyPair,
+
+ /// Responsible for network and system monitoring.
+ monitor: Arc<Monitor>,
+
+ /// Discovery instance.
+ discovery: ArcDiscovery,
+
+ /// PeerPool instance.
+ peer_pool: Arc<PeerPool>,
+}
+
+impl Backend {
+ /// Creates a new Backend.
+ pub fn new(key_pair: &KeyPair, config: Config, ex: GlobalExecutor) -> ArcBackend {
+ let config = Arc::new(config);
+ let monitor = Arc::new(Monitor::new());
+ let conn_queue = ConnQueue::new();
+
+ let peer_id = PeerID::try_from(key_pair.public())
+ .expect("Derive a peer id from the provided key pair.");
+ info!("PeerID: {}", peer_id);
+
+ let peer_pool = PeerPool::new(
+ &peer_id,
+ conn_queue.clone(),
+ config.clone(),
+ monitor.clone(),
+ ex.clone(),
+ );
+
+ let discovery = Discovery::new(
+ key_pair,
+ &peer_id,
+ conn_queue,
+ config.clone(),
+ monitor.clone(),
+ ex,
+ );
+
+ Arc::new(Self {
+ key_pair: key_pair.clone(),
+ monitor,
+ discovery,
+ config,
+ peer_pool,
+ })
+ }
+
+ /// Run the Backend, starting the PeerPool and Discovery instances.
+ pub async fn run(self: &Arc<Self>) -> Result<()> {
+ self.peer_pool.start().await?;
+ self.discovery.start().await?;
+ Ok(())
+ }
+
+ /// Attach a custom protocol to the network
+ pub async fn attach_protocol<P: Protocol>(
+ &self,
+ c: impl Fn(ArcPeer) -> ArcProtocol + Send + Sync + 'static,
+ ) -> Result<()> {
+ self.peer_pool.attach_protocol::<P>(Box::new(c)).await
+ }
+
+ /// Returns the number of currently connected peers.
+ pub async fn peers(&self) -> usize {
+ self.peer_pool.peers_len().await
+ }
+
+ /// Returns the `Config`.
+ pub fn config(&self) -> Arc<Config> {
+ self.config.clone()
+ }
+
+ /// Returns the `KeyPair`.
+ pub async fn key_pair(&self) -> &KeyPair {
+ &self.key_pair
+ }
+
+ /// Returns the number of occupied inbound slots.
+ pub fn inbound_slots(&self) -> usize {
+ self.discovery.inbound_slots.load()
+ }
+
+ /// Returns the number of occupied outbound slots.
+ pub fn outbound_slots(&self) -> usize {
+ self.discovery.outbound_slots.load()
+ }
+
+ /// Subscribes to the monitor to receive network events.
+ pub async fn monitor(&self) -> Subscription<MonitorEvent> {
+ self.monitor.subscribe().await
+ }
+
+ /// Shuts down the Backend.
+ pub async fn shutdown(&self) {
+ self.discovery.shutdown().await;
+ self.peer_pool.shutdown().await;
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +
use std::time::Duration;
+
+use bincode::{Decode, Encode};
+
+use karyon_core::{
+ async_util::timeout,
+ util::{decode, encode, encode_into_slice},
+};
+
+use karyon_net::{Connection, NetError};
+
+use crate::{
+ message::{NetMsg, NetMsgCmd, NetMsgHeader, MAX_ALLOWED_MSG_SIZE, MSG_HEADER_SIZE},
+ Error, Result,
+};
+
+pub trait CodecMsg: Decode + Encode + std::fmt::Debug {}
+impl<T: Encode + Decode + std::fmt::Debug> CodecMsg for T {}
+
+/// A Codec working with generic network connections.
+///
+/// It is responsible for both decoding data received from the network and
+/// encoding data before sending it.
+pub struct Codec {
+ conn: Box<dyn Connection>,
+}
+
+impl Codec {
+ /// Creates a new Codec.
+ pub fn new(conn: Box<dyn Connection>) -> Self {
+ Self { conn }
+ }
+
+ /// Reads a message of type `NetMsg` from the connection.
+ ///
+ /// It reads the first 6 bytes as the header of the message, then reads
+ /// and decodes the remaining message data based on the determined header.
+ pub async fn read(&self) -> Result<NetMsg> {
+ // Read 6 bytes to get the header of the incoming message
+ let mut buf = [0; MSG_HEADER_SIZE];
+ self.read_exact(&mut buf).await?;
+
+ // Decode the header from bytes to NetMsgHeader
+ let (header, _) = decode::<NetMsgHeader>(&buf)?;
+
+ if header.payload_size > MAX_ALLOWED_MSG_SIZE {
+ return Err(Error::InvalidMsg(
+ "Message exceeds the maximum allowed size".to_string(),
+ ));
+ }
+
+ // Create a buffer to hold the message based on its length
+ let mut payload = vec![0; header.payload_size as usize];
+ self.read_exact(&mut payload).await?;
+
+ Ok(NetMsg { header, payload })
+ }
+
+ /// Writes a message of type `T` to the connection.
+ ///
+ /// Before appending the actual message payload, it calculates the length of
+ /// the encoded message in bytes and appends this length to the message header.
+ pub async fn write<T: CodecMsg>(&self, command: NetMsgCmd, msg: &T) -> Result<()> {
+ let payload = encode(msg)?;
+
+ // Create a buffer to hold the message header (6 bytes)
+ let header_buf = &mut [0; MSG_HEADER_SIZE];
+ let header = NetMsgHeader {
+ command,
+ payload_size: payload.len() as u32,
+ };
+ encode_into_slice(&header, header_buf)?;
+
+ let mut buffer = vec![];
+ // Append the header bytes to the buffer
+ buffer.extend_from_slice(header_buf);
+ // Append the message payload to the buffer
+ buffer.extend_from_slice(&payload);
+
+ self.write_all(&buffer).await?;
+ Ok(())
+ }
+
+ /// Reads a message of type `NetMsg` with the given timeout.
+ pub async fn read_timeout(&self, duration: Duration) -> Result<NetMsg> {
+ timeout(duration, self.read())
+ .await
+ .map_err(|_| NetError::Timeout)?
+ }
+
+ /// Reads the exact number of bytes required to fill `buf`.
+ async fn read_exact(&self, mut buf: &mut [u8]) -> Result<()> {
+ while !buf.is_empty() {
+ let n = self.conn.read(buf).await?;
+ let (_, rest) = std::mem::take(&mut buf).split_at_mut(n);
+ buf = rest;
+
+ if n == 0 {
+ return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Writes an entire buffer into the connection.
+ async fn write_all(&self, mut buf: &[u8]) -> Result<()> {
+ while !buf.is_empty() {
+ let n = self.conn.write(buf).await?;
+ let (_, rest) = std::mem::take(&mut buf).split_at(n);
+ buf = rest;
+
+ if n == 0 {
+ return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
+ }
+ }
+
+ Ok(())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +
use karyon_net::{Endpoint, Port};
+
+use crate::Version;
+
+/// the Configuration for the P2P network.
+pub struct Config {
+ /// Represents the network version.
+ pub version: Version,
+
+ /////////////////
+ // PeerPool
+ ////////////////
+ /// Timeout duration for the handshake with new peers, in seconds.
+ pub handshake_timeout: u64,
+ /// Interval at which the ping protocol sends ping messages to a peer to
+ /// maintain connections, in seconds.
+ pub ping_interval: u64,
+ /// Timeout duration for receiving the pong message corresponding to the
+ /// sent ping message, in seconds.
+ pub ping_timeout: u64,
+ /// The maximum number of retries for outbound connection establishment.
+ pub max_connect_retries: usize,
+
+ /////////////////
+ // DISCOVERY
+ ////////////////
+ /// A list of bootstrap peers for the seeding process.
+ pub bootstrap_peers: Vec<Endpoint>,
+ /// An optional listening endpoint to accept incoming connections.
+ pub listen_endpoint: Option<Endpoint>,
+ /// A list of endpoints representing peers that the `Discovery` will
+ /// manually connect to.
+ pub peer_endpoints: Vec<Endpoint>,
+ /// The number of available inbound slots for incoming connections.
+ pub inbound_slots: usize,
+ /// The number of available outbound slots for outgoing connections.
+ pub outbound_slots: usize,
+ /// TCP/UDP port for lookup and refresh processes.
+ pub discovery_port: Port,
+ /// Time interval, in seconds, at which the Discovery restarts the
+ /// seeding process.
+ pub seeding_interval: u64,
+
+ /////////////////
+ // LOOKUP
+ ////////////////
+ /// The number of available inbound slots for incoming connections during
+ /// the lookup process.
+ pub lookup_inbound_slots: usize,
+ /// The number of available outbound slots for outgoing connections during
+ /// the lookup process.
+ pub lookup_outbound_slots: usize,
+ /// Timeout duration for a peer response during the lookup process, in
+ /// seconds.
+ pub lookup_response_timeout: u64,
+ /// Maximum allowable time for a live connection with a peer during the
+ /// lookup process, in seconds.
+ pub lookup_connection_lifespan: u64,
+ /// The maximum number of retries for outbound connection establishment
+ /// during the lookup process.
+ pub lookup_connect_retries: usize,
+
+ /////////////////
+ // REFRESH
+ ////////////////
+ /// Interval at which the table refreshes its entries, in seconds.
+ pub refresh_interval: u64,
+ /// Timeout duration for a peer response during the table refresh process,
+ /// in seconds.
+ pub refresh_response_timeout: u64,
+ /// The maximum number of retries for outbound connection establishment
+ /// during the refresh process.
+ pub refresh_connect_retries: usize,
+
+ /// Enables TLS for all connections.
+ pub enable_tls: bool,
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Config {
+ version: "0.1.0".parse().unwrap(),
+
+ handshake_timeout: 2,
+ ping_interval: 20,
+ ping_timeout: 2,
+
+ bootstrap_peers: vec![],
+ listen_endpoint: None,
+ peer_endpoints: vec![],
+ inbound_slots: 12,
+ outbound_slots: 12,
+ max_connect_retries: 3,
+ discovery_port: 0,
+ seeding_interval: 60,
+
+ lookup_inbound_slots: 20,
+ lookup_outbound_slots: 20,
+ lookup_response_timeout: 1,
+ lookup_connection_lifespan: 3,
+ lookup_connect_retries: 3,
+
+ refresh_interval: 1800,
+ refresh_response_timeout: 1,
+ refresh_connect_retries: 3,
+
+ enable_tls: false,
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +
use std::{collections::VecDeque, fmt, sync::Arc};
+
+use smol::{channel::Sender, lock::Mutex};
+
+use karyon_core::async_util::CondVar;
+use karyon_net::Conn;
+
+use crate::Result;
+
+/// Defines the direction of a network connection.
+#[derive(Clone, Debug)]
+pub enum ConnDirection {
+ Inbound,
+ Outbound,
+}
+
+impl fmt::Display for ConnDirection {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ConnDirection::Inbound => write!(f, "Inbound"),
+ ConnDirection::Outbound => write!(f, "Outbound"),
+ }
+ }
+}
+
+pub struct NewConn {
+ pub direction: ConnDirection,
+ pub conn: Conn,
+ pub disconnect_signal: Sender<Result<()>>,
+}
+
+/// Connection queue
+pub struct ConnQueue {
+ queue: Mutex<VecDeque<NewConn>>,
+ conn_available: CondVar,
+}
+
+impl ConnQueue {
+ pub fn new() -> Arc<Self> {
+ Arc::new(Self {
+ queue: Mutex::new(VecDeque::new()),
+ conn_available: CondVar::new(),
+ })
+ }
+
+ /// Push a connection into the queue and wait for the disconnect signal
+ pub async fn handle(&self, conn: Conn, direction: ConnDirection) -> Result<()> {
+ let (disconnect_signal, chan) = smol::channel::bounded(1);
+ let new_conn = NewConn {
+ direction,
+ conn,
+ disconnect_signal,
+ };
+ self.queue.lock().await.push_back(new_conn);
+ self.conn_available.signal();
+ if let Ok(result) = chan.recv().await {
+ return result;
+ }
+ Ok(())
+ }
+
+ /// Receive the next connection in the queue
+ pub async fn next(&self) -> NewConn {
+ let mut queue = self.queue.lock().await;
+ while queue.is_empty() {
+ queue = self.conn_available.wait(queue).await;
+ }
+ queue.pop_front().unwrap()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +
use std::{future::Future, sync::Arc};
+
+use log::{error, trace, warn};
+
+use karyon_core::{
+ async_util::{Backoff, TaskGroup, TaskResult},
+ crypto::KeyPair,
+ GlobalExecutor,
+};
+use karyon_net::{dial, tls, Conn, Endpoint, NetError};
+
+use crate::{
+ monitor::{ConnEvent, Monitor},
+ slots::ConnectionSlots,
+ tls_config::tls_client_config,
+ Error, PeerID, Result,
+};
+
+static DNS_NAME: &str = "karyontech.net";
+
+/// Responsible for creating outbound connections with other peers.
+pub struct Connector {
+ /// Identity Key pair
+ key_pair: KeyPair,
+
+ /// Managing spawned tasks.
+ task_group: TaskGroup<'static>,
+
+ /// Manages available outbound slots.
+ connection_slots: Arc<ConnectionSlots>,
+
+ /// The maximum number of retries allowed before successfully
+ /// establishing a connection.
+ max_retries: usize,
+
+ /// Enables secure connection.
+ enable_tls: bool,
+
+ /// Responsible for network and system monitoring.
+ monitor: Arc<Monitor>,
+}
+
+impl Connector {
+ /// Creates a new Connector
+ pub fn new(
+ key_pair: &KeyPair,
+ max_retries: usize,
+ connection_slots: Arc<ConnectionSlots>,
+ enable_tls: bool,
+ monitor: Arc<Monitor>,
+ ex: GlobalExecutor,
+ ) -> Arc<Self> {
+ Arc::new(Self {
+ key_pair: key_pair.clone(),
+ max_retries,
+ task_group: TaskGroup::new(ex),
+ monitor,
+ connection_slots,
+ enable_tls,
+ })
+ }
+
+ /// Shuts down the connector
+ pub async fn shutdown(&self) {
+ self.task_group.cancel().await;
+ }
+
+ /// Establish a connection to the specified `endpoint`. If the connection
+ /// attempt fails, it performs a backoff and retries until the maximum allowed
+ /// number of retries is exceeded. On a successful connection, it returns a
+ /// `Conn` instance.
+ ///
+ /// This method will block until it finds an available slot.
+ pub async fn connect(&self, endpoint: &Endpoint, peer_id: &Option<PeerID>) -> Result<Conn> {
+ self.connection_slots.wait_for_slot().await;
+ self.connection_slots.add();
+
+ let mut retry = 0;
+ let backoff = Backoff::new(500, 2000);
+ while retry < self.max_retries {
+ match self.dial(endpoint, peer_id).await {
+ Ok(conn) => {
+ self.monitor
+ .notify(&ConnEvent::Connected(endpoint.clone()).into())
+ .await;
+ return Ok(conn);
+ }
+ Err(err) => {
+ error!("Failed to establish a connection to {endpoint}: {err}");
+ }
+ }
+
+ self.monitor
+ .notify(&ConnEvent::ConnectRetried(endpoint.clone()).into())
+ .await;
+
+ backoff.sleep().await;
+
+ warn!("try to reconnect {endpoint}");
+ retry += 1;
+ }
+
+ self.monitor
+ .notify(&ConnEvent::ConnectFailed(endpoint.clone()).into())
+ .await;
+
+ self.connection_slots.remove().await;
+ Err(NetError::Timeout.into())
+ }
+
+ /// Establish a connection to the given `endpoint`. For each new connection,
+ /// it invokes the provided `callback`, and pass the connection to the callback.
+ pub async fn connect_with_cback<Fut>(
+ self: &Arc<Self>,
+ endpoint: &Endpoint,
+ peer_id: &Option<PeerID>,
+ callback: impl FnOnce(Conn) -> Fut + Send + 'static,
+ ) -> Result<()>
+ where
+ Fut: Future<Output = Result<()>> + Send + 'static,
+ {
+ let conn = self.connect(endpoint, peer_id).await?;
+
+ let selfc = self.clone();
+ let endpoint = endpoint.clone();
+ let on_disconnect = |res| async move {
+ if let TaskResult::Completed(Err(err)) = res {
+ trace!("Outbound connection dropped: {err}");
+ }
+ selfc
+ .monitor
+ .notify(&ConnEvent::Disconnected(endpoint.clone()).into())
+ .await;
+ selfc.connection_slots.remove().await;
+ };
+
+ self.task_group.spawn(callback(conn), on_disconnect);
+
+ Ok(())
+ }
+
+ async fn dial(&self, endpoint: &Endpoint, peer_id: &Option<PeerID>) -> Result<Conn> {
+ if self.enable_tls {
+ let tls_config = tls_client_config(&self.key_pair, peer_id.clone())?;
+ tls::dial(endpoint, tls_config, DNS_NAME).await
+ } else {
+ dial(endpoint).await
+ }
+ .map_err(Error::KaryonNet)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +
use std::{sync::Arc, time::Duration};
+
+use futures_util::{stream::FuturesUnordered, StreamExt};
+use log::{error, trace};
+use rand::{rngs::OsRng, seq::SliceRandom, RngCore};
+use smol::lock::{Mutex, RwLock};
+
+use karyon_core::{async_util::timeout, crypto::KeyPair, util::decode, GlobalExecutor};
+
+use karyon_net::{Conn, Endpoint};
+
+use crate::{
+ codec::Codec,
+ connector::Connector,
+ listener::Listener,
+ message::{
+ get_msg_payload, FindPeerMsg, NetMsg, NetMsgCmd, PeerMsg, PeersMsg, PingMsg, PongMsg,
+ ShutdownMsg,
+ },
+ monitor::{ConnEvent, DiscoveryEvent, Monitor},
+ routing_table::RoutingTable,
+ slots::ConnectionSlots,
+ version::version_match,
+ Config, Error, PeerID, Result,
+};
+
+/// Maximum number of peers that can be returned in a PeersMsg.
+pub const MAX_PEERS_IN_PEERSMSG: usize = 10;
+
+pub struct LookupService {
+ /// Peer's ID
+ id: PeerID,
+
+ /// Routing Table
+ table: Arc<Mutex<RoutingTable>>,
+
+ /// Listener
+ listener: Arc<Listener>,
+ /// Connector
+ connector: Arc<Connector>,
+
+ /// Outbound slots.
+ outbound_slots: Arc<ConnectionSlots>,
+
+ /// Resolved listen endpoint
+ listen_endpoint: Option<RwLock<Endpoint>>,
+
+ /// Holds the configuration for the P2P network.
+ config: Arc<Config>,
+
+ /// Responsible for network and system monitoring.
+ monitor: Arc<Monitor>,
+}
+
+impl LookupService {
+ /// Creates a new lookup service
+ pub fn new(
+ key_pair: &KeyPair,
+ id: &PeerID,
+ table: Arc<Mutex<RoutingTable>>,
+ config: Arc<Config>,
+ monitor: Arc<Monitor>,
+ ex: GlobalExecutor,
+ ) -> Self {
+ let inbound_slots = Arc::new(ConnectionSlots::new(config.lookup_inbound_slots));
+ let outbound_slots = Arc::new(ConnectionSlots::new(config.lookup_outbound_slots));
+
+ let listener = Listener::new(
+ key_pair,
+ inbound_slots.clone(),
+ config.enable_tls,
+ monitor.clone(),
+ ex.clone(),
+ );
+
+ let connector = Connector::new(
+ key_pair,
+ config.lookup_connect_retries,
+ outbound_slots.clone(),
+ config.enable_tls,
+ monitor.clone(),
+ ex,
+ );
+
+ let listen_endpoint = config
+ .listen_endpoint
+ .as_ref()
+ .map(|endpoint| RwLock::new(endpoint.clone()));
+
+ Self {
+ id: id.clone(),
+ table,
+ listener,
+ connector,
+ outbound_slots,
+ listen_endpoint,
+ config,
+ monitor,
+ }
+ }
+
+ /// Start the lookup service.
+ pub async fn start(self: &Arc<Self>) -> Result<()> {
+ self.start_listener().await?;
+ Ok(())
+ }
+
+ /// Set the resolved listen endpoint.
+ pub async fn set_listen_endpoint(&self, resolved_endpoint: &Endpoint) {
+ if let Some(endpoint) = &self.listen_endpoint {
+ *endpoint.write().await = resolved_endpoint.clone();
+ }
+ }
+
+ /// Shuts down the lookup service.
+ pub async fn shutdown(&self) {
+ self.connector.shutdown().await;
+ self.listener.shutdown().await;
+ }
+
+ /// Starts iterative lookup and populate the routing table.
+ ///
+ /// This method begins by generating a random peer ID and connecting to the
+ /// provided endpoint. It then sends a FindPeer message containing the
+ /// randomly generated peer ID. Upon receiving peers from the initial lookup,
+ /// it starts connecting to these received peers and sends them a FindPeer
+ /// message that contains our own peer ID.
+ pub async fn start_lookup(&self, endpoint: &Endpoint, peer_id: Option<PeerID>) -> Result<()> {
+ trace!("Lookup started {endpoint}");
+ self.monitor
+ .notify(&DiscoveryEvent::LookupStarted(endpoint.clone()).into())
+ .await;
+
+ let mut random_peers = vec![];
+ if let Err(err) = self
+ .random_lookup(endpoint, peer_id, &mut random_peers)
+ .await
+ {
+ self.monitor
+ .notify(&DiscoveryEvent::LookupFailed(endpoint.clone()).into())
+ .await;
+ return Err(err);
+ };
+
+ let mut peer_buffer = vec![];
+ self.self_lookup(&random_peers, &mut peer_buffer).await;
+
+ while peer_buffer.len() < MAX_PEERS_IN_PEERSMSG {
+ match random_peers.pop() {
+ Some(p) => peer_buffer.push(p),
+ None => break,
+ }
+ }
+
+ let mut table = self.table.lock().await;
+ for peer in peer_buffer.iter() {
+ let result = table.add_entry(peer.clone().into());
+ trace!("Add entry {:?}", result);
+ }
+ drop(table);
+
+ self.monitor
+ .notify(&DiscoveryEvent::LookupSucceeded(endpoint.clone(), peer_buffer.len()).into())
+ .await;
+
+ Ok(())
+ }
+
+ /// Starts a random lookup
+ ///
+ /// This will perfom lookup on a random generated PeerID
+ async fn random_lookup(
+ &self,
+ endpoint: &Endpoint,
+ peer_id: Option<PeerID>,
+ random_peers: &mut Vec<PeerMsg>,
+ ) -> Result<()> {
+ for _ in 0..2 {
+ let random_peer_id = PeerID::random();
+ let peers = self
+ .connect(endpoint.clone(), peer_id.clone(), &random_peer_id)
+ .await?;
+
+ let table = self.table.lock().await;
+ for peer in peers {
+ if random_peers.contains(&peer)
+ || peer.peer_id == self.id
+ || table.contains_key(&peer.peer_id.0)
+ {
+ continue;
+ }
+
+ random_peers.push(peer);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Starts a self lookup
+ async fn self_lookup(&self, random_peers: &Vec<PeerMsg>, peer_buffer: &mut Vec<PeerMsg>) {
+ let mut tasks = FuturesUnordered::new();
+ for peer in random_peers.choose_multiple(&mut OsRng, random_peers.len()) {
+ let endpoint = Endpoint::Tcp(peer.addr.clone(), peer.discovery_port);
+ tasks.push(self.connect(endpoint, Some(peer.peer_id.clone()), &self.id))
+ }
+
+ while let Some(result) = tasks.next().await {
+ match result {
+ Ok(peers) => peer_buffer.extend(peers),
+ Err(err) => {
+ error!("Failed to do self lookup: {err}");
+ }
+ }
+ }
+ }
+
+ /// Connects to the given endpoint and initiates a lookup process for the
+ /// provided peer ID.
+ async fn connect(
+ &self,
+ endpoint: Endpoint,
+ peer_id: Option<PeerID>,
+ target_peer_id: &PeerID,
+ ) -> Result<Vec<PeerMsg>> {
+ let conn = self.connector.connect(&endpoint, &peer_id).await?;
+ let io_codec = Codec::new(conn);
+ let result = self.handle_outbound(io_codec, target_peer_id).await;
+
+ self.monitor
+ .notify(&ConnEvent::Disconnected(endpoint).into())
+ .await;
+ self.outbound_slots.remove().await;
+
+ result
+ }
+
+ /// Handles outbound connection
+ async fn handle_outbound(
+ &self,
+ io_codec: Codec,
+ target_peer_id: &PeerID,
+ ) -> Result<Vec<PeerMsg>> {
+ trace!("Send Ping msg");
+ self.send_ping_msg(&io_codec).await?;
+
+ trace!("Send FindPeer msg");
+ let peers = self.send_findpeer_msg(&io_codec, target_peer_id).await?;
+
+ if peers.0.len() >= MAX_PEERS_IN_PEERSMSG {
+ return Err(Error::Lookup("Received too many peers in PeersMsg"));
+ }
+
+ trace!("Send Peer msg");
+ if let Some(endpoint) = &self.listen_endpoint {
+ self.send_peer_msg(&io_codec, endpoint.read().await.clone())
+ .await?;
+ }
+
+ trace!("Send Shutdown msg");
+ self.send_shutdown_msg(&io_codec).await?;
+
+ Ok(peers.0)
+ }
+
+ /// Start a listener.
+ async fn start_listener(self: &Arc<Self>) -> Result<()> {
+ let addr = match &self.listen_endpoint {
+ Some(a) => a.read().await.addr()?.clone(),
+ None => return Ok(()),
+ };
+
+ let endpoint = Endpoint::Tcp(addr, self.config.discovery_port);
+
+ let selfc = self.clone();
+ let callback = |conn: Conn| async move {
+ let t = Duration::from_secs(selfc.config.lookup_connection_lifespan);
+ timeout(t, selfc.handle_inbound(conn)).await??;
+ Ok(())
+ };
+
+ self.listener.start(endpoint.clone(), callback).await?;
+ Ok(())
+ }
+
+ /// Handles inbound connection
+ async fn handle_inbound(self: &Arc<Self>, conn: Conn) -> Result<()> {
+ let io_codec = Codec::new(conn);
+ loop {
+ let msg: NetMsg = io_codec.read().await?;
+ trace!("Receive msg {:?}", msg.header.command);
+
+ if let NetMsgCmd::Shutdown = msg.header.command {
+ return Ok(());
+ }
+
+ match &msg.header.command {
+ NetMsgCmd::Ping => {
+ let (ping_msg, _) = decode::<PingMsg>(&msg.payload)?;
+ if !version_match(&self.config.version.req, &ping_msg.version) {
+ return Err(Error::IncompatibleVersion("system: {}".into()));
+ }
+ self.send_pong_msg(ping_msg.nonce, &io_codec).await?;
+ }
+ NetMsgCmd::FindPeer => {
+ let (findpeer_msg, _) = decode::<FindPeerMsg>(&msg.payload)?;
+ let peer_id = findpeer_msg.0;
+ self.send_peers_msg(&peer_id, &io_codec).await?;
+ }
+ NetMsgCmd::Peer => {
+ let (peer, _) = decode::<PeerMsg>(&msg.payload)?;
+ let result = self.table.lock().await.add_entry(peer.clone().into());
+ trace!("Add entry result: {:?}", result);
+ }
+ c => return Err(Error::InvalidMsg(format!("Unexpected msg: {:?}", c))),
+ }
+ }
+ }
+
+ /// Sends a Ping msg and wait to receive the Pong message.
+ async fn send_ping_msg(&self, io_codec: &Codec) -> Result<()> {
+ trace!("Send Pong msg");
+
+ let mut nonce: [u8; 32] = [0; 32];
+ RngCore::fill_bytes(&mut OsRng, &mut nonce);
+
+ let ping_msg = PingMsg {
+ version: self.config.version.v.clone(),
+ nonce,
+ };
+ io_codec.write(NetMsgCmd::Ping, &ping_msg).await?;
+
+ let t = Duration::from_secs(self.config.lookup_response_timeout);
+ let recv_msg: NetMsg = io_codec.read_timeout(t).await?;
+
+ let payload = get_msg_payload!(Pong, recv_msg);
+ let (pong_msg, _) = decode::<PongMsg>(&payload)?;
+
+ if ping_msg.nonce != pong_msg.0 {
+ return Err(Error::InvalidPongMsg);
+ }
+
+ Ok(())
+ }
+
+ /// Sends a Pong msg
+ async fn send_pong_msg(&self, nonce: [u8; 32], io_codec: &Codec) -> Result<()> {
+ trace!("Send Pong msg");
+ io_codec.write(NetMsgCmd::Pong, &PongMsg(nonce)).await?;
+ Ok(())
+ }
+
+ /// Sends a FindPeer msg and wait to receivet the Peers msg.
+ async fn send_findpeer_msg(&self, io_codec: &Codec, peer_id: &PeerID) -> Result<PeersMsg> {
+ trace!("Send FindPeer msg");
+ io_codec
+ .write(NetMsgCmd::FindPeer, &FindPeerMsg(peer_id.clone()))
+ .await?;
+
+ let t = Duration::from_secs(self.config.lookup_response_timeout);
+ let recv_msg: NetMsg = io_codec.read_timeout(t).await?;
+
+ let payload = get_msg_payload!(Peers, recv_msg);
+ let (peers, _) = decode(&payload)?;
+
+ Ok(peers)
+ }
+
+ /// Sends a Peers msg.
+ async fn send_peers_msg(&self, peer_id: &PeerID, io_codec: &Codec) -> Result<()> {
+ trace!("Send Peers msg");
+ let table = self.table.lock().await;
+ let entries = table.closest_entries(&peer_id.0, MAX_PEERS_IN_PEERSMSG);
+ drop(table);
+
+ let peers: Vec<PeerMsg> = entries.into_iter().map(|e| e.into()).collect();
+ io_codec.write(NetMsgCmd::Peers, &PeersMsg(peers)).await?;
+ Ok(())
+ }
+
+ /// Sends a Peer msg.
+ async fn send_peer_msg(&self, io_codec: &Codec, endpoint: Endpoint) -> Result<()> {
+ trace!("Send Peer msg");
+ let peer_msg = PeerMsg {
+ addr: endpoint.addr()?.clone(),
+ port: *endpoint.port()?,
+ discovery_port: self.config.discovery_port,
+ peer_id: self.id.clone(),
+ };
+ io_codec.write(NetMsgCmd::Peer, &peer_msg).await?;
+ Ok(())
+ }
+
+ /// Sends a Shutdown msg.
+ async fn send_shutdown_msg(&self, io_codec: &Codec) -> Result<()> {
+ trace!("Send Shutdown msg");
+ io_codec.write(NetMsgCmd::Shutdown, &ShutdownMsg(0)).await?;
+ Ok(())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +
mod lookup;
+mod refresh;
+
+use std::sync::Arc;
+
+use log::{error, info};
+use rand::{rngs::OsRng, seq::SliceRandom};
+use smol::lock::Mutex;
+
+use karyon_core::{
+ async_util::{Backoff, TaskGroup, TaskResult},
+ crypto::KeyPair,
+ GlobalExecutor,
+};
+
+use karyon_net::{Conn, Endpoint};
+
+use crate::{
+ config::Config,
+ connection::{ConnDirection, ConnQueue},
+ connector::Connector,
+ listener::Listener,
+ monitor::Monitor,
+ routing_table::{
+ Entry, EntryStatusFlag, RoutingTable, CONNECTED_ENTRY, DISCONNECTED_ENTRY,
+ INCOMPATIBLE_ENTRY, PENDING_ENTRY, UNREACHABLE_ENTRY, UNSTABLE_ENTRY,
+ },
+ slots::ConnectionSlots,
+ Error, PeerID, Result,
+};
+
+use lookup::LookupService;
+use refresh::RefreshService;
+
+pub type ArcDiscovery = Arc<Discovery>;
+
+pub struct Discovery {
+ /// Routing table
+ table: Arc<Mutex<RoutingTable>>,
+
+ /// Lookup Service
+ lookup_service: Arc<LookupService>,
+
+ /// Refresh Service
+ refresh_service: Arc<RefreshService>,
+
+ /// Connector
+ connector: Arc<Connector>,
+ /// Listener
+ listener: Arc<Listener>,
+
+ /// Connection queue
+ conn_queue: Arc<ConnQueue>,
+
+ /// Inbound slots.
+ pub(crate) inbound_slots: Arc<ConnectionSlots>,
+ /// Outbound slots.
+ pub(crate) outbound_slots: Arc<ConnectionSlots>,
+
+ /// Managing spawned tasks.
+ task_group: TaskGroup<'static>,
+
+ /// Holds the configuration for the P2P network.
+ config: Arc<Config>,
+}
+
+impl Discovery {
+ /// Creates a new Discovery
+ pub fn new(
+ key_pair: &KeyPair,
+ peer_id: &PeerID,
+ conn_queue: Arc<ConnQueue>,
+ config: Arc<Config>,
+ monitor: Arc<Monitor>,
+ ex: GlobalExecutor,
+ ) -> ArcDiscovery {
+ let inbound_slots = Arc::new(ConnectionSlots::new(config.inbound_slots));
+ let outbound_slots = Arc::new(ConnectionSlots::new(config.outbound_slots));
+
+ let table_key = peer_id.0;
+ let table = Arc::new(Mutex::new(RoutingTable::new(table_key)));
+
+ let refresh_service =
+ RefreshService::new(config.clone(), table.clone(), monitor.clone(), ex.clone());
+ let lookup_service = LookupService::new(
+ key_pair,
+ peer_id,
+ table.clone(),
+ config.clone(),
+ monitor.clone(),
+ ex.clone(),
+ );
+
+ let connector = Connector::new(
+ key_pair,
+ config.max_connect_retries,
+ outbound_slots.clone(),
+ config.enable_tls,
+ monitor.clone(),
+ ex.clone(),
+ );
+
+ let listener = Listener::new(
+ key_pair,
+ inbound_slots.clone(),
+ config.enable_tls,
+ monitor.clone(),
+ ex.clone(),
+ );
+
+ Arc::new(Self {
+ refresh_service: Arc::new(refresh_service),
+ lookup_service: Arc::new(lookup_service),
+ conn_queue,
+ table,
+ inbound_slots,
+ outbound_slots,
+ connector,
+ listener,
+ task_group: TaskGroup::new(ex),
+ config,
+ })
+ }
+
+ /// Start the Discovery
+ pub async fn start(self: &Arc<Self>) -> Result<()> {
+ // Check if the listen_endpoint is provided, and if so, start a listener.
+ if let Some(endpoint) = &self.config.listen_endpoint {
+ // Return an error if the discovery port is set to 0.
+ if self.config.discovery_port == 0 {
+ return Err(Error::Config(
+ "Please add a valid discovery port".to_string(),
+ ));
+ }
+
+ let resolved_endpoint = self.start_listener(endpoint).await?;
+
+ if endpoint.addr()? != resolved_endpoint.addr()? {
+ info!("Resolved listen endpoint: {resolved_endpoint}");
+ self.lookup_service
+ .set_listen_endpoint(&resolved_endpoint)
+ .await;
+ self.refresh_service
+ .set_listen_endpoint(&resolved_endpoint)
+ .await;
+ }
+ }
+
+ // Start the lookup service
+ self.lookup_service.start().await?;
+ // Start the refresh service
+ self.refresh_service.start().await?;
+
+ // Attempt to manually connect to peer endpoints provided in the Config.
+ for endpoint in self.config.peer_endpoints.iter() {
+ let _ = self.connect(endpoint, None).await;
+ }
+
+ // Start connect loop
+ let selfc = self.clone();
+ self.task_group
+ .spawn(selfc.connect_loop(), |res| async move {
+ if let TaskResult::Completed(Err(err)) = res {
+ error!("Connect loop stopped: {err}");
+ }
+ });
+
+ Ok(())
+ }
+
+ /// Shuts down the discovery
+ pub async fn shutdown(&self) {
+ self.task_group.cancel().await;
+ self.connector.shutdown().await;
+ self.listener.shutdown().await;
+
+ self.refresh_service.shutdown().await;
+ self.lookup_service.shutdown().await;
+ }
+
+ /// Start a listener and on success, return the resolved endpoint.
+ async fn start_listener(self: &Arc<Self>, endpoint: &Endpoint) -> Result<Endpoint> {
+ let selfc = self.clone();
+ let callback = |c: Conn| async move {
+ selfc.conn_queue.handle(c, ConnDirection::Inbound).await?;
+ Ok(())
+ };
+
+ let resolved_endpoint = self.listener.start(endpoint.clone(), callback).await?;
+ Ok(resolved_endpoint)
+ }
+
+ /// This method will attempt to connect to a peer in the routing table.
+ /// If the routing table is empty, it will start the seeding process for
+ /// finding new peers.
+ ///
+ /// This will perform a backoff to prevent getting stuck in the loop
+ /// if the seeding process couldn't find any peers.
+ async fn connect_loop(self: Arc<Self>) -> Result<()> {
+ let backoff = Backoff::new(500, self.config.seeding_interval * 1000);
+ loop {
+ let random_entry = self.random_entry(PENDING_ENTRY).await;
+ match random_entry {
+ Some(entry) => {
+ backoff.reset();
+ let endpoint = Endpoint::Tcp(entry.addr, entry.port);
+ self.connect(&endpoint, Some(entry.key.into())).await;
+ }
+ None => {
+ backoff.sleep().await;
+ self.start_seeding().await;
+ }
+ }
+ }
+ }
+
+ /// Connect to the given endpoint using the connector
+ async fn connect(self: &Arc<Self>, endpoint: &Endpoint, pid: Option<PeerID>) {
+ let selfc = self.clone();
+ let pid_c = pid.clone();
+ let endpoint_c = endpoint.clone();
+ let cback = |conn: Conn| async move {
+ let result = selfc.conn_queue.handle(conn, ConnDirection::Outbound).await;
+
+ // If the entry is not in the routing table, ignore the result
+ let pid = match pid_c {
+ Some(p) => p,
+ None => return Ok(()),
+ };
+
+ match result {
+ Err(Error::IncompatiblePeer) => {
+ error!("Failed to do handshake: {endpoint_c} incompatible peer");
+ selfc.update_entry(&pid, INCOMPATIBLE_ENTRY).await;
+ }
+ Err(Error::PeerAlreadyConnected) => {
+ // TODO: Use the appropriate status.
+ selfc.update_entry(&pid, DISCONNECTED_ENTRY).await;
+ }
+ Err(_) => {
+ selfc.update_entry(&pid, UNSTABLE_ENTRY).await;
+ }
+ Ok(_) => {
+ selfc.update_entry(&pid, DISCONNECTED_ENTRY).await;
+ }
+ }
+
+ Ok(())
+ };
+
+ let result = self
+ .connector
+ .connect_with_cback(endpoint, &pid, cback)
+ .await;
+
+ if let Some(pid) = &pid {
+ match result {
+ Ok(_) => {
+ self.update_entry(pid, CONNECTED_ENTRY).await;
+ }
+ Err(_) => {
+ self.update_entry(pid, UNREACHABLE_ENTRY).await;
+ }
+ }
+ }
+ }
+
+ /// Starts seeding process.
+ ///
+ /// This method randomly selects a peer from the routing table and
+ /// attempts to connect to that peer for the initial lookup. If the routing
+ /// table doesn't have an available entry, it will connect to one of the
+ /// provided bootstrap endpoints in the `Config` and initiate the lookup.
+ async fn start_seeding(&self) {
+ match self.random_entry(PENDING_ENTRY | CONNECTED_ENTRY).await {
+ Some(entry) => {
+ let endpoint = Endpoint::Tcp(entry.addr, entry.discovery_port);
+ let peer_id = Some(entry.key.into());
+ if let Err(err) = self.lookup_service.start_lookup(&endpoint, peer_id).await {
+ self.update_entry(&entry.key.into(), UNSTABLE_ENTRY).await;
+ error!("Failed to do lookup: {endpoint}: {err}");
+ }
+ }
+ None => {
+ let peers = &self.config.bootstrap_peers;
+ for endpoint in peers.choose_multiple(&mut OsRng, peers.len()) {
+ if let Err(err) = self.lookup_service.start_lookup(endpoint, None).await {
+ error!("Failed to do lookup: {endpoint}: {err}");
+ }
+ }
+ }
+ }
+ }
+
+ /// Returns a random entry from routing table.
+ async fn random_entry(&self, entry_flag: EntryStatusFlag) -> Option<Entry> {
+ self.table.lock().await.random_entry(entry_flag).cloned()
+ }
+
+ /// Update the entry status
+ async fn update_entry(&self, pid: &PeerID, entry_flag: EntryStatusFlag) {
+ let table = &mut self.table.lock().await;
+ table.update_entry(&pid.0, entry_flag);
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +
use std::{sync::Arc, time::Duration};
+
+use bincode::{Decode, Encode};
+use log::{error, info, trace};
+use rand::{rngs::OsRng, RngCore};
+use smol::{
+ lock::{Mutex, RwLock},
+ stream::StreamExt,
+ Timer,
+};
+
+use karyon_core::{
+ async_util::{timeout, Backoff, TaskGroup, TaskResult},
+ util::{decode, encode},
+ GlobalExecutor,
+};
+
+use karyon_net::{dial_udp, listen_udp, Addr, Connection, Endpoint, NetError, Port, UdpConn};
+
+/// Maximum failures for an entry before removing it from the routing table.
+pub const MAX_FAILURES: u32 = 3;
+
+/// Ping message size
+const PINGMSG_SIZE: usize = 32;
+
+use crate::{
+ monitor::{ConnEvent, DiscoveryEvent, Monitor},
+ routing_table::{BucketEntry, Entry, RoutingTable, PENDING_ENTRY, UNREACHABLE_ENTRY},
+ Config, Error, Result,
+};
+
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct PingMsg(pub [u8; 32]);
+
+#[derive(Decode, Encode, Debug)]
+pub struct PongMsg(pub [u8; 32]);
+
+pub struct RefreshService {
+ /// Routing table
+ table: Arc<Mutex<RoutingTable>>,
+
+ /// Resolved listen endpoint
+ listen_endpoint: Option<RwLock<Endpoint>>,
+
+ /// Managing spawned tasks.
+ task_group: TaskGroup<'static>,
+
+ /// A global executor
+ executor: GlobalExecutor,
+
+ /// Holds the configuration for the P2P network.
+ config: Arc<Config>,
+
+ /// Responsible for network and system monitoring.
+ monitor: Arc<Monitor>,
+}
+
+impl RefreshService {
+ /// Creates a new refresh service
+ pub fn new(
+ config: Arc<Config>,
+ table: Arc<Mutex<RoutingTable>>,
+ monitor: Arc<Monitor>,
+ executor: GlobalExecutor,
+ ) -> Self {
+ let listen_endpoint = config
+ .listen_endpoint
+ .as_ref()
+ .map(|endpoint| RwLock::new(endpoint.clone()));
+
+ Self {
+ table,
+ listen_endpoint,
+ task_group: TaskGroup::new(executor.clone()),
+ executor,
+ config,
+ monitor,
+ }
+ }
+
+ /// Start the refresh service
+ pub async fn start(self: &Arc<Self>) -> Result<()> {
+ if let Some(endpoint) = &self.listen_endpoint {
+ let endpoint = endpoint.read().await;
+ let addr = endpoint.addr()?;
+ let port = self.config.discovery_port;
+
+ let selfc = self.clone();
+ self.task_group
+ .spawn(selfc.listen_loop(addr.clone(), port), |res| async move {
+ if let TaskResult::Completed(Err(err)) = res {
+ error!("Listen loop stopped: {err}");
+ }
+ });
+ }
+
+ let selfc = self.clone();
+ self.task_group
+ .spawn(selfc.refresh_loop(), |res| async move {
+ if let TaskResult::Completed(Err(err)) = res {
+ error!("Refresh loop stopped: {err}");
+ }
+ });
+
+ Ok(())
+ }
+
+ /// Set the resolved listen endpoint.
+ pub async fn set_listen_endpoint(&self, resolved_endpoint: &Endpoint) {
+ if let Some(endpoint) = &self.listen_endpoint {
+ *endpoint.write().await = resolved_endpoint.clone();
+ }
+ }
+
+ /// Shuts down the refresh service
+ pub async fn shutdown(&self) {
+ self.task_group.cancel().await;
+ }
+
+ /// Initiates periodic refreshing of the routing table. This function will
+ /// selects the first 8 entries (oldest entries) from each bucket in the
+ /// routing table and starts sending Ping messages to the collected entries.
+ async fn refresh_loop(self: Arc<Self>) -> Result<()> {
+ let mut timer = Timer::interval(Duration::from_secs(self.config.refresh_interval));
+ loop {
+ timer.next().await;
+ trace!("Start refreshing the routing table...");
+
+ self.monitor
+ .notify(&DiscoveryEvent::RefreshStarted.into())
+ .await;
+
+ let mut entries: Vec<BucketEntry> = vec![];
+ for bucket in self.table.lock().await.iter() {
+ for entry in bucket
+ .iter()
+ .filter(|e| !e.is_connected() && !e.is_incompatible())
+ .take(8)
+ {
+ entries.push(entry.clone())
+ }
+ }
+
+ self.clone().do_refresh(&entries).await;
+ }
+ }
+
+ /// Iterates over the entries and spawns a new task for each entry to
+ /// initiate a connection attempt.
+ async fn do_refresh(self: Arc<Self>, entries: &[BucketEntry]) {
+ let ex = &self.executor;
+ // Enforce a maximum of 16 concurrent connections.
+ for chunk in entries.chunks(16) {
+ let mut tasks = Vec::new();
+ for bucket_entry in chunk {
+ if bucket_entry.failures >= MAX_FAILURES {
+ self.table
+ .lock()
+ .await
+ .remove_entry(&bucket_entry.entry.key);
+ continue;
+ }
+
+ tasks.push(ex.spawn(self.clone().refresh_entry(bucket_entry.clone())))
+ }
+
+ for task in tasks {
+ task.await;
+ }
+ }
+ }
+
+ /// Initiates refresh for a specific entry within the routing table. It
+ /// updates the routing table according to the result.
+ async fn refresh_entry(self: Arc<Self>, bucket_entry: BucketEntry) {
+ let key = &bucket_entry.entry.key;
+ match self.connect(&bucket_entry.entry).await {
+ Ok(_) => {
+ self.table.lock().await.update_entry(key, PENDING_ENTRY);
+ }
+ Err(err) => {
+ trace!("Failed to refresh entry {:?}: {err}", key);
+ let table = &mut self.table.lock().await;
+ if bucket_entry.failures >= MAX_FAILURES {
+ table.remove_entry(key);
+ return;
+ }
+ table.update_entry(key, UNREACHABLE_ENTRY);
+ }
+ }
+ }
+
+ /// Initiates a UDP connection with the entry and attempts to send a Ping
+ /// message. If it fails, it retries according to the allowed retries
+ /// specified in the Config, with backoff between each retry.
+ async fn connect(&self, entry: &Entry) -> Result<()> {
+ let mut retry = 0;
+ let endpoint = Endpoint::Ws(entry.addr.clone(), entry.discovery_port);
+ let conn = dial_udp(&endpoint).await?;
+ let backoff = Backoff::new(100, 5000);
+ while retry < self.config.refresh_connect_retries {
+ match self.send_ping_msg(&conn).await {
+ Ok(()) => return Ok(()),
+ Err(Error::KaryonNet(NetError::Timeout)) => {
+ retry += 1;
+ backoff.sleep().await;
+ }
+ Err(err) => {
+ return Err(err);
+ }
+ }
+ }
+
+ Err(NetError::Timeout.into())
+ }
+
+ /// Set up a UDP listener and start listening for Ping messages from other
+ /// peers.
+ async fn listen_loop(self: Arc<Self>, addr: Addr, port: Port) -> Result<()> {
+ let endpoint = Endpoint::Udp(addr.clone(), port);
+ let conn = match listen_udp(&endpoint).await {
+ Ok(c) => {
+ self.monitor
+ .notify(&ConnEvent::Listening(endpoint.clone()).into())
+ .await;
+ c
+ }
+ Err(err) => {
+ self.monitor
+ .notify(&ConnEvent::ListenFailed(endpoint.clone()).into())
+ .await;
+ return Err(err.into());
+ }
+ };
+ info!("Start listening on {endpoint}");
+
+ loop {
+ let res = self.listen_to_ping_msg(&conn).await;
+ if let Err(err) = res {
+ trace!("Failed to handle ping msg {err}");
+ self.monitor.notify(&ConnEvent::AcceptFailed.into()).await;
+ }
+ }
+ }
+
+ /// Listen to receive a Ping message and respond with a Pong message.
+ async fn listen_to_ping_msg(&self, conn: &UdpConn) -> Result<()> {
+ let mut buf = [0; PINGMSG_SIZE];
+ let (_, endpoint) = conn.recv_from(&mut buf).await?;
+
+ self.monitor
+ .notify(&ConnEvent::Accepted(endpoint.clone()).into())
+ .await;
+
+ let (ping_msg, _) = decode::<PingMsg>(&buf)?;
+
+ let pong_msg = PongMsg(ping_msg.0);
+ let buffer = encode(&pong_msg)?;
+
+ conn.send_to(&buffer, &endpoint).await?;
+
+ self.monitor
+ .notify(&ConnEvent::Disconnected(endpoint.clone()).into())
+ .await;
+ Ok(())
+ }
+
+ /// Sends a Ping msg and wait to receive the Pong message.
+ async fn send_ping_msg(&self, conn: &UdpConn) -> Result<()> {
+ let mut nonce: [u8; 32] = [0; 32];
+ RngCore::fill_bytes(&mut OsRng, &mut nonce);
+
+ let ping_msg = PingMsg(nonce);
+ let buffer = encode(&ping_msg)?;
+ conn.write(&buffer).await?;
+
+ let buf = &mut [0; PINGMSG_SIZE];
+ let t = Duration::from_secs(self.config.refresh_response_timeout);
+ timeout(t, conn.read(buf)).await??;
+
+ let (pong_msg, _) = decode::<PongMsg>(buf)?;
+
+ if ping_msg.0 != pong_msg.0 {
+ return Err(Error::InvalidPongMsg);
+ }
+
+ Ok(())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +
use thiserror::Error as ThisError;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Represents karyon's p2p Error.
+#[derive(ThisError, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ IO(#[from] std::io::Error),
+
+ #[error("Unsupported protocol error: {0}")]
+ UnsupportedProtocol(String),
+
+ #[error("Try from public key Error: {0}")]
+ TryFromPublicKey(&'static str),
+
+ #[error("Invalid message error: {0}")]
+ InvalidMsg(String),
+
+ #[error("Incompatible Peer")]
+ IncompatiblePeer,
+
+ #[error(transparent)]
+ ParseIntError(#[from] std::num::ParseIntError),
+
+ #[error(transparent)]
+ ParseFloatError(#[from] std::num::ParseFloatError),
+
+ #[error(transparent)]
+ SemverError(#[from] semver::Error),
+
+ #[error("Parse Error: {0}")]
+ ParseError(String),
+
+ #[error("Incompatible version error: {0}")]
+ IncompatibleVersion(String),
+
+ #[error("Config error: {0}")]
+ Config(String),
+
+ #[error("Peer shutdown")]
+ PeerShutdown,
+
+ #[error("Invalid Pong Msg")]
+ InvalidPongMsg,
+
+ #[error("Discovery error: {0}")]
+ Discovery(&'static str),
+
+ #[error("Lookup error: {0}")]
+ Lookup(&'static str),
+
+ #[error("Peer already connected")]
+ PeerAlreadyConnected,
+
+ #[error("Yasna Error: {0}")]
+ Yasna(#[from] yasna::ASN1Error),
+
+ #[error("X509 Parser Error: {0}")]
+ X509Parser(#[from] x509_parser::error::X509Error),
+
+ #[error("Rcgen Error: {0}")]
+ Rcgen(#[from] rcgen::Error),
+
+ #[error("Tls Error: {0}")]
+ Rustls(#[from] futures_rustls::rustls::Error),
+
+ #[error("Invalid DNS Name: {0}")]
+ InvalidDnsNameError(#[from] futures_rustls::pki_types::InvalidDnsNameError),
+
+ #[error("Channel Send Error: {0}")]
+ ChannelSend(String),
+
+ #[error(transparent)]
+ ChannelRecv(#[from] smol::channel::RecvError),
+
+ #[error(transparent)]
+ KaryonCore(#[from] karyon_core::error::Error),
+
+ #[error(transparent)]
+ KaryonNet(#[from] karyon_net::NetError),
+}
+
+impl<T> From<smol::channel::SendError<T>> for Error {
+ fn from(error: smol::channel::SendError<T>) -> Self {
+ Error::ChannelSend(error.to_string())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +
//! A lightweight, extensible, and customizable peer-to-peer (p2p) network stack.
+//!
+//! # Example
+//! ```
+//! use std::sync::Arc;
+//!
+//! use easy_parallel::Parallel;
+//! use smol::{channel as smol_channel, future, Executor};
+//!
+//! use karyon_core::crypto::{KeyPair, KeyPairType};
+//! use karyon_p2p::{Backend, Config, PeerID};
+//!
+//! let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+//!
+//! // Create the configuration for the backend.
+//! let mut config = Config::default();
+//!
+//! // Create a new Executor
+//! let ex = Arc::new(Executor::new());
+//!
+//! // Create a new Backend
+//! let backend = Backend::new(&key_pair, config, ex.clone());
+//!
+//! let task = async {
+//! // Run the backend
+//! backend.run().await.unwrap();
+//!
+//! // ....
+//!
+//! // Shutdown the backend
+//! backend.shutdown().await;
+//! };
+//!
+//! future::block_on(ex.run(task));
+//!
+//! ```
+//!
+mod backend;
+mod codec;
+mod config;
+mod connection;
+mod connector;
+mod discovery;
+mod error;
+mod listener;
+mod message;
+mod peer;
+mod peer_pool;
+mod protocols;
+mod routing_table;
+mod slots;
+mod tls_config;
+mod version;
+
+/// Responsible for network and system monitoring.
+/// [`Read More`](./monitor/struct.Monitor.html)
+pub mod monitor;
+/// Defines the protocol trait.
+/// [`Read More`](./protocol/trait.Protocol.html)
+pub mod protocol;
+
+pub use backend::{ArcBackend, Backend};
+pub use config::Config;
+pub use error::Error as P2pError;
+pub use peer::{ArcPeer, PeerID};
+pub use version::Version;
+
+use error::{Error, Result};
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +
use std::{future::Future, sync::Arc};
+
+use log::{debug, error, info};
+
+use karyon_core::{
+ async_util::{TaskGroup, TaskResult},
+ crypto::KeyPair,
+ GlobalExecutor,
+};
+
+use karyon_net::{listen, tls, Conn, ConnListener, Endpoint};
+
+use crate::{
+ monitor::{ConnEvent, Monitor},
+ slots::ConnectionSlots,
+ tls_config::tls_server_config,
+ Error, Result,
+};
+
+/// Responsible for creating inbound connections with other peers.
+pub struct Listener {
+ /// Identity Key pair
+ key_pair: KeyPair,
+
+ /// Managing spawned tasks.
+ task_group: TaskGroup<'static>,
+
+ /// Manages available inbound slots.
+ connection_slots: Arc<ConnectionSlots>,
+
+ /// Enables secure connection.
+ enable_tls: bool,
+
+ /// Responsible for network and system monitoring.
+ monitor: Arc<Monitor>,
+}
+
+impl Listener {
+ /// Creates a new Listener
+ pub fn new(
+ key_pair: &KeyPair,
+ connection_slots: Arc<ConnectionSlots>,
+ enable_tls: bool,
+ monitor: Arc<Monitor>,
+ ex: GlobalExecutor,
+ ) -> Arc<Self> {
+ Arc::new(Self {
+ key_pair: key_pair.clone(),
+ connection_slots,
+ task_group: TaskGroup::new(ex),
+ enable_tls,
+ monitor,
+ })
+ }
+
+ /// Starts a listener on the given `endpoint`. For each incoming connection
+ /// that is accepted, it invokes the provided `callback`, and pass the
+ /// connection to the callback.
+ ///
+ /// Returns the resloved listening endpoint.
+ pub async fn start<Fut>(
+ self: &Arc<Self>,
+ endpoint: Endpoint,
+ // https://github.com/rust-lang/rfcs/pull/2132
+ callback: impl FnOnce(Conn) -> Fut + Clone + Send + 'static,
+ ) -> Result<Endpoint>
+ where
+ Fut: Future<Output = Result<()>> + Send + 'static,
+ {
+ let listener = match self.listend(&endpoint).await {
+ Ok(listener) => {
+ self.monitor
+ .notify(&ConnEvent::Listening(endpoint.clone()).into())
+ .await;
+ listener
+ }
+ Err(err) => {
+ error!("Failed to listen on {endpoint}: {err}");
+ self.monitor
+ .notify(&ConnEvent::ListenFailed(endpoint).into())
+ .await;
+ return Err(err);
+ }
+ };
+
+ let resolved_endpoint = listener.local_endpoint()?;
+
+ info!("Start listening on {resolved_endpoint}");
+
+ let selfc = self.clone();
+ self.task_group
+ .spawn(selfc.listen_loop(listener, callback), |_| async {});
+ Ok(resolved_endpoint)
+ }
+
+ /// Shuts down the listener
+ pub async fn shutdown(&self) {
+ self.task_group.cancel().await;
+ }
+
+ async fn listen_loop<Fut>(
+ self: Arc<Self>,
+ listener: Box<dyn ConnListener>,
+ callback: impl FnOnce(Conn) -> Fut + Clone + Send + 'static,
+ ) where
+ Fut: Future<Output = Result<()>> + Send + 'static,
+ {
+ loop {
+ // Wait for an available inbound slot.
+ self.connection_slots.wait_for_slot().await;
+ let result = listener.accept().await;
+
+ let (conn, endpoint) = match result {
+ Ok(c) => {
+ let endpoint = match c.peer_endpoint() {
+ Ok(e) => e,
+ Err(err) => {
+ self.monitor.notify(&ConnEvent::AcceptFailed.into()).await;
+ error!("Failed to accept a new connection: {err}");
+ continue;
+ }
+ };
+
+ self.monitor
+ .notify(&ConnEvent::Accepted(endpoint.clone()).into())
+ .await;
+ (c, endpoint)
+ }
+ Err(err) => {
+ error!("Failed to accept a new connection: {err}");
+ self.monitor.notify(&ConnEvent::AcceptFailed.into()).await;
+ continue;
+ }
+ };
+
+ self.connection_slots.add();
+
+ let selfc = self.clone();
+ let on_disconnect = |res| async move {
+ if let TaskResult::Completed(Err(err)) = res {
+ debug!("Inbound connection dropped: {err}");
+ }
+ selfc
+ .monitor
+ .notify(&ConnEvent::Disconnected(endpoint).into())
+ .await;
+ selfc.connection_slots.remove().await;
+ };
+
+ let callback = callback.clone();
+ self.task_group.spawn(callback(conn), on_disconnect);
+ }
+ }
+
+ async fn listend(&self, endpoint: &Endpoint) -> Result<Box<dyn ConnListener>> {
+ if self.enable_tls {
+ let tls_config = tls_server_config(&self.key_pair)?;
+ tls::listen_tls(endpoint, tls_config)
+ .await
+ .map(|l| Box::new(l) as Box<dyn ConnListener>)
+ } else {
+ listen(endpoint).await
+ }
+ .map_err(Error::KaryonNet)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +
use std::collections::HashMap;
+
+use bincode::{Decode, Encode};
+
+use karyon_net::{Addr, Port};
+
+use crate::{protocol::ProtocolID, routing_table::Entry, version::VersionInt, PeerID};
+
+/// The size of the message header, in bytes.
+pub const MSG_HEADER_SIZE: usize = 6;
+
+/// The maximum allowed size for a message in bytes.
+pub const MAX_ALLOWED_MSG_SIZE: u32 = 1024 * 1024; // 1MB
+
+/// Defines the main message in the karyon p2p network.
+///
+/// This message structure consists of a header and payload, where the header
+/// typically contains essential information about the message, and the payload
+/// contains the actual data being transmitted.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct NetMsg {
+ pub header: NetMsgHeader,
+ pub payload: Vec<u8>,
+}
+
+/// Represents the header of a message.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct NetMsgHeader {
+ pub command: NetMsgCmd,
+ pub payload_size: u32,
+}
+
+/// Defines message commands.
+#[derive(Decode, Encode, Debug, Clone)]
+#[repr(u8)]
+pub enum NetMsgCmd {
+ Version,
+ Verack,
+ Protocol,
+ Shutdown,
+
+ // NOTE: The following commands are used during the lookup process.
+ Ping,
+ Pong,
+ FindPeer,
+ Peer,
+ Peers,
+}
+
+/// Defines a message related to a specific protocol.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct ProtocolMsg {
+ pub protocol_id: ProtocolID,
+ pub payload: Vec<u8>,
+}
+
+/// Version message, providing information about a peer's capabilities.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct VerMsg {
+ pub peer_id: PeerID,
+ pub version: VersionInt,
+ pub protocols: HashMap<ProtocolID, VersionInt>,
+}
+
+/// VerAck message acknowledges the receipt of a Version message. The message
+/// consists of the peer ID and an acknowledgment boolean value indicating
+/// whether the version is accepted.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct VerAckMsg {
+ pub peer_id: PeerID,
+ pub ack: bool,
+}
+
+/// Shutdown message.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct ShutdownMsg(pub u8);
+
+/// Ping message with a nonce and version information.
+#[derive(Decode, Encode, Debug, Clone)]
+pub struct PingMsg {
+ pub nonce: [u8; 32],
+ pub version: VersionInt,
+}
+
+/// Ping message with a nonce.
+#[derive(Decode, Encode, Debug)]
+pub struct PongMsg(pub [u8; 32]);
+
+/// FindPeer message used to find a specific peer.
+#[derive(Decode, Encode, Debug)]
+pub struct FindPeerMsg(pub PeerID);
+
+/// PeerMsg containing information about a peer.
+#[derive(Decode, Encode, Debug, Clone, PartialEq, Eq)]
+pub struct PeerMsg {
+ pub peer_id: PeerID,
+ pub addr: Addr,
+ pub port: Port,
+ pub discovery_port: Port,
+}
+
+/// PeersMsg a list of `PeerMsg`.
+#[derive(Decode, Encode, Debug)]
+pub struct PeersMsg(pub Vec<PeerMsg>);
+
+macro_rules! get_msg_payload {
+ ($a:ident, $b:ident) => {
+ if let NetMsgCmd::$a = $b.header.command {
+ $b.payload
+ } else {
+ return Err(Error::InvalidMsg(format!(
+ "Unexpected msg {:?}",
+ $b.header.command
+ )));
+ }
+ };
+}
+
+pub(super) use get_msg_payload;
+
+impl From<Entry> for PeerMsg {
+ fn from(entry: Entry) -> PeerMsg {
+ PeerMsg {
+ peer_id: PeerID(entry.key),
+ addr: entry.addr,
+ port: entry.port,
+ discovery_port: entry.discovery_port,
+ }
+ }
+}
+
+impl From<PeerMsg> for Entry {
+ fn from(peer: PeerMsg) -> Entry {
+ Entry {
+ key: peer.peer_id.0,
+ addr: peer.addr,
+ port: peer.port,
+ discovery_port: peer.discovery_port,
+ }
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +
use std::fmt;
+
+use crate::PeerID;
+
+use karyon_core::pubsub::{ArcPublisher, Publisher, Subscription};
+
+use karyon_net::Endpoint;
+
+/// Responsible for network and system monitoring.
+///
+/// It use pub-sub pattern to notify the subscribers with new events.
+///
+/// # Example
+///
+/// ```
+/// use std::sync::Arc;
+///
+/// use smol::Executor;
+///
+/// use karyon_core::crypto::{KeyPair, KeyPairType};
+/// use karyon_p2p::{Config, Backend, PeerID};
+///
+/// async {
+///
+/// // Create a new Executor
+/// let ex = Arc::new(Executor::new());
+///
+/// let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+/// let backend = Backend::new(&key_pair, Config::default(), ex);
+///
+/// // Create a new Subscription
+/// let sub = backend.monitor().await;
+///
+/// let event = sub.recv().await;
+/// };
+/// ```
+pub struct Monitor {
+ inner: ArcPublisher<MonitorEvent>,
+}
+
+impl Monitor {
+ /// Creates a new Monitor
+ pub(crate) fn new() -> Monitor {
+ Self {
+ inner: Publisher::new(),
+ }
+ }
+
+ /// Sends a new monitor event to all subscribers.
+ pub async fn notify(&self, event: &MonitorEvent) {
+ self.inner.notify(event).await;
+ }
+
+ /// Subscribes to listen to new events.
+ pub async fn subscribe(&self) -> Subscription<MonitorEvent> {
+ self.inner.subscribe().await
+ }
+}
+
+/// Defines various type of event that can be monitored.
+#[derive(Clone, Debug)]
+pub enum MonitorEvent {
+ Conn(ConnEvent),
+ PeerPool(PeerPoolEvent),
+ Discovery(DiscoveryEvent),
+}
+
+/// Defines connection-related events.
+#[derive(Clone, Debug)]
+pub enum ConnEvent {
+ Connected(Endpoint),
+ ConnectRetried(Endpoint),
+ ConnectFailed(Endpoint),
+ Accepted(Endpoint),
+ AcceptFailed,
+ Disconnected(Endpoint),
+ Listening(Endpoint),
+ ListenFailed(Endpoint),
+}
+
+/// Defines `PeerPool` events.
+#[derive(Clone, Debug)]
+pub enum PeerPoolEvent {
+ NewPeer(PeerID),
+ RemovePeer(PeerID),
+}
+
+/// Defines `Discovery` events.
+#[derive(Clone, Debug)]
+pub enum DiscoveryEvent {
+ LookupStarted(Endpoint),
+ LookupFailed(Endpoint),
+ LookupSucceeded(Endpoint, usize),
+ RefreshStarted,
+}
+
+impl fmt::Display for MonitorEvent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let val = match self {
+ MonitorEvent::Conn(e) => format!("Connection Event: {e}"),
+ MonitorEvent::PeerPool(e) => format!("PeerPool Event: {e}"),
+ MonitorEvent::Discovery(e) => format!("Discovery Event: {e}"),
+ };
+ write!(f, "{}", val)
+ }
+}
+
+impl fmt::Display for ConnEvent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let val = match self {
+ ConnEvent::Connected(endpoint) => format!("Connected: {endpoint}"),
+ ConnEvent::ConnectFailed(endpoint) => format!("ConnectFailed: {endpoint}"),
+ ConnEvent::ConnectRetried(endpoint) => format!("ConnectRetried: {endpoint}"),
+ ConnEvent::AcceptFailed => "AcceptFailed".to_string(),
+ ConnEvent::Accepted(endpoint) => format!("Accepted: {endpoint}"),
+ ConnEvent::Disconnected(endpoint) => format!("Disconnected: {endpoint}"),
+ ConnEvent::Listening(endpoint) => format!("Listening: {endpoint}"),
+ ConnEvent::ListenFailed(endpoint) => format!("ListenFailed: {endpoint}"),
+ };
+ write!(f, "{}", val)
+ }
+}
+
+impl fmt::Display for PeerPoolEvent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let val = match self {
+ PeerPoolEvent::NewPeer(pid) => format!("NewPeer: {pid}"),
+ PeerPoolEvent::RemovePeer(pid) => format!("RemovePeer: {pid}"),
+ };
+ write!(f, "{}", val)
+ }
+}
+
+impl fmt::Display for DiscoveryEvent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let val = match self {
+ DiscoveryEvent::LookupStarted(endpoint) => format!("LookupStarted: {endpoint}"),
+ DiscoveryEvent::LookupFailed(endpoint) => format!("LookupFailed: {endpoint}"),
+ DiscoveryEvent::LookupSucceeded(endpoint, len) => {
+ format!("LookupSucceeded: {endpoint} {len}")
+ }
+ DiscoveryEvent::RefreshStarted => "RefreshStarted".to_string(),
+ };
+ write!(f, "{}", val)
+ }
+}
+
+impl From<ConnEvent> for MonitorEvent {
+ fn from(val: ConnEvent) -> Self {
+ MonitorEvent::Conn(val)
+ }
+}
+
+impl From<PeerPoolEvent> for MonitorEvent {
+ fn from(val: PeerPoolEvent) -> Self {
+ MonitorEvent::PeerPool(val)
+ }
+}
+
+impl From<DiscoveryEvent> for MonitorEvent {
+ fn from(val: DiscoveryEvent) -> Self {
+ MonitorEvent::Discovery(val)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +
mod peer_id;
+
+pub use peer_id::PeerID;
+
+use std::sync::Arc;
+
+use log::{error, trace};
+use smol::{
+ channel::{self, Receiver, Sender},
+ lock::RwLock,
+};
+
+use karyon_core::{
+ async_util::{select, Either, TaskGroup, TaskResult},
+ event::{ArcEventSys, EventListener, EventSys},
+ util::{decode, encode},
+ GlobalExecutor,
+};
+
+use karyon_net::Endpoint;
+
+use crate::{
+ codec::{Codec, CodecMsg},
+ connection::ConnDirection,
+ message::{NetMsgCmd, ProtocolMsg, ShutdownMsg},
+ peer_pool::{ArcPeerPool, WeakPeerPool},
+ protocol::{Protocol, ProtocolEvent, ProtocolID},
+ Config, Error, Result,
+};
+
+pub type ArcPeer = Arc<Peer>;
+
+pub struct Peer {
+ /// Peer's ID
+ id: PeerID,
+
+ /// A weak pointer to `PeerPool`
+ peer_pool: WeakPeerPool,
+
+ /// Holds the Codec for the peer connection
+ codec: Codec,
+
+ /// Remote endpoint for the peer
+ remote_endpoint: Endpoint,
+
+ /// The direction of the connection, either `Inbound` or `Outbound`
+ conn_direction: ConnDirection,
+
+ /// A list of protocol IDs
+ protocol_ids: RwLock<Vec<ProtocolID>>,
+
+ /// `EventSys` responsible for sending events to the protocols.
+ protocol_events: ArcEventSys<ProtocolID>,
+
+ /// This channel is used to send a stop signal to the read loop.
+ stop_chan: (Sender<Result<()>>, Receiver<Result<()>>),
+
+ /// Managing spawned tasks.
+ task_group: TaskGroup<'static>,
+}
+
+impl Peer {
+ /// Creates a new peer
+ pub fn new(
+ peer_pool: WeakPeerPool,
+ id: &PeerID,
+ codec: Codec,
+ remote_endpoint: Endpoint,
+ conn_direction: ConnDirection,
+ ex: GlobalExecutor,
+ ) -> ArcPeer {
+ Arc::new(Peer {
+ id: id.clone(),
+ peer_pool,
+ codec,
+ protocol_ids: RwLock::new(Vec::new()),
+ remote_endpoint,
+ conn_direction,
+ protocol_events: EventSys::new(),
+ task_group: TaskGroup::new(ex),
+ stop_chan: channel::bounded(1),
+ })
+ }
+
+ /// Run the peer
+ pub async fn run(self: Arc<Self>) -> Result<()> {
+ self.start_protocols().await;
+ self.read_loop().await
+ }
+
+ /// Send a message to the peer connection using the specified protocol.
+ pub async fn send<T: CodecMsg>(&self, protocol_id: &ProtocolID, msg: &T) -> Result<()> {
+ let payload = encode(msg)?;
+
+ let proto_msg = ProtocolMsg {
+ protocol_id: protocol_id.to_string(),
+ payload: payload.to_vec(),
+ };
+
+ self.codec.write(NetMsgCmd::Protocol, &proto_msg).await?;
+ Ok(())
+ }
+
+ /// Broadcast a message to all connected peers using the specified protocol.
+ pub async fn broadcast<T: CodecMsg>(&self, protocol_id: &ProtocolID, msg: &T) {
+ self.peer_pool().broadcast(protocol_id, msg).await;
+ }
+
+ /// Shuts down the peer
+ pub async fn shutdown(&self) {
+ trace!("peer {} start shutting down", self.id);
+
+ // Send shutdown event to all protocols
+ for protocol_id in self.protocol_ids.read().await.iter() {
+ self.protocol_events
+ .emit_by_topic(protocol_id, &ProtocolEvent::Shutdown)
+ .await;
+ }
+
+ // Send a stop signal to the read loop
+ //
+ // No need to handle the error here; a dropped channel and
+ // sending a stop signal have the same effect.
+ let _ = self.stop_chan.0.try_send(Ok(()));
+
+ // No need to handle the error here
+ let _ = self.codec.write(NetMsgCmd::Shutdown, &ShutdownMsg(0)).await;
+
+ // Force shutting down
+ self.task_group.cancel().await;
+ }
+
+ /// Check if the connection is Inbound
+ #[inline]
+ pub fn is_inbound(&self) -> bool {
+ match self.conn_direction {
+ ConnDirection::Inbound => true,
+ ConnDirection::Outbound => false,
+ }
+ }
+
+ /// Returns the direction of the connection, which can be either `Inbound`
+ /// or `Outbound`.
+ #[inline]
+ pub fn direction(&self) -> &ConnDirection {
+ &self.conn_direction
+ }
+
+ /// Returns the remote endpoint for the peer
+ #[inline]
+ pub fn remote_endpoint(&self) -> &Endpoint {
+ &self.remote_endpoint
+ }
+
+ /// Return the peer's ID
+ #[inline]
+ pub fn id(&self) -> &PeerID {
+ &self.id
+ }
+
+ /// Returns the `Config` instance.
+ pub fn config(&self) -> Arc<Config> {
+ self.peer_pool().config.clone()
+ }
+
+ /// Registers a listener for the given Protocol `P`.
+ pub async fn register_listener<P: Protocol>(&self) -> EventListener<ProtocolID, ProtocolEvent> {
+ self.protocol_events.register(&P::id()).await
+ }
+
+ /// Start a read loop to handle incoming messages from the peer connection.
+ async fn read_loop(&self) -> Result<()> {
+ loop {
+ let fut = select(self.stop_chan.1.recv(), self.codec.read()).await;
+ let result = match fut {
+ Either::Left(stop_signal) => {
+ trace!("Peer {} received a stop signal", self.id);
+ return stop_signal?;
+ }
+ Either::Right(result) => result,
+ };
+
+ let msg = result?;
+
+ match msg.header.command {
+ NetMsgCmd::Protocol => {
+ let msg: ProtocolMsg = decode(&msg.payload)?.0;
+
+ if !self.protocol_ids.read().await.contains(&msg.protocol_id) {
+ return Err(Error::UnsupportedProtocol(msg.protocol_id));
+ }
+
+ let proto_id = &msg.protocol_id;
+ let msg = ProtocolEvent::Message(msg.payload);
+ self.protocol_events.emit_by_topic(proto_id, &msg).await;
+ }
+ NetMsgCmd::Shutdown => {
+ return Err(Error::PeerShutdown);
+ }
+ command => return Err(Error::InvalidMsg(format!("Unexpected msg {:?}", command))),
+ }
+ }
+ }
+
+ /// Start running the protocols for this peer connection.
+ async fn start_protocols(self: &Arc<Self>) {
+ for (protocol_id, constructor) in self.peer_pool().protocols.read().await.iter() {
+ trace!("peer {} start protocol {protocol_id}", self.id);
+ let protocol = constructor(self.clone());
+
+ self.protocol_ids.write().await.push(protocol_id.clone());
+
+ let selfc = self.clone();
+ let proto_idc = protocol_id.clone();
+
+ let on_failure = |result: TaskResult<Result<()>>| async move {
+ if let TaskResult::Completed(res) = result {
+ if res.is_err() {
+ error!("protocol {} stopped", proto_idc);
+ }
+ // Send a stop signal to read loop
+ let _ = selfc.stop_chan.0.try_send(res);
+ }
+ };
+
+ self.task_group.spawn(protocol.start(), on_failure);
+ }
+ }
+
+ fn peer_pool(&self) -> ArcPeerPool {
+ self.peer_pool.upgrade().unwrap()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +
use bincode::{Decode, Encode};
+use rand::{rngs::OsRng, RngCore};
+use sha2::{Digest, Sha256};
+
+use karyon_core::crypto::PublicKey;
+
+use crate::Error;
+
+/// Represents a unique identifier for a peer.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Decode, Encode)]
+pub struct PeerID(pub [u8; 32]);
+
+impl std::fmt::Display for PeerID {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ let id = self.0[0..8]
+ .iter()
+ .map(|b| format!("{:x}", b))
+ .collect::<Vec<String>>()
+ .join("");
+
+ write!(f, "{}", id)
+ }
+}
+
+impl PeerID {
+ /// Creates a new PeerID.
+ pub fn new(src: &[u8]) -> Self {
+ let mut hasher = Sha256::new();
+ hasher.update(src);
+ Self(hasher.finalize().into())
+ }
+
+ /// Generates a random PeerID.
+ pub fn random() -> Self {
+ let mut id: [u8; 32] = [0; 32];
+ OsRng.fill_bytes(&mut id);
+ Self(id)
+ }
+}
+
+impl From<[u8; 32]> for PeerID {
+ fn from(b: [u8; 32]) -> Self {
+ PeerID(b)
+ }
+}
+
+impl TryFrom<PublicKey> for PeerID {
+ type Error = Error;
+
+ fn try_from(pk: PublicKey) -> Result<Self, Self::Error> {
+ let pk: [u8; 32] = pk
+ .as_bytes()
+ .try_into()
+ .map_err(|_| Error::TryFromPublicKey("Failed to convert public key to [u8;32]"))?;
+
+ Ok(PeerID(pk))
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +
use std::{
+ collections::HashMap,
+ sync::{Arc, Weak},
+ time::Duration,
+};
+
+use log::{error, info, trace, warn};
+use smol::{
+ channel::Sender,
+ lock::{Mutex, RwLock},
+};
+
+use karyon_core::{
+ async_util::{TaskGroup, TaskResult},
+ util::decode,
+ GlobalExecutor,
+};
+
+use karyon_net::Conn;
+
+use crate::{
+ codec::{Codec, CodecMsg},
+ config::Config,
+ connection::{ConnDirection, ConnQueue},
+ message::{get_msg_payload, NetMsg, NetMsgCmd, VerAckMsg, VerMsg},
+ monitor::{Monitor, PeerPoolEvent},
+ peer::{ArcPeer, Peer, PeerID},
+ protocol::{Protocol, ProtocolConstructor, ProtocolID},
+ protocols::PingProtocol,
+ version::{version_match, Version, VersionInt},
+ Error, Result,
+};
+
+pub type ArcPeerPool = Arc<PeerPool>;
+pub type WeakPeerPool = Weak<PeerPool>;
+
+pub struct PeerPool {
+ /// Peer's ID
+ pub id: PeerID,
+
+ /// Connection queue
+ conn_queue: Arc<ConnQueue>,
+
+ /// Holds the running peers.
+ peers: Mutex<HashMap<PeerID, ArcPeer>>,
+
+ /// Hashmap contains protocol constructors.
+ pub(crate) protocols: RwLock<HashMap<ProtocolID, Box<ProtocolConstructor>>>,
+
+ /// Hashmap contains protocol IDs and their versions.
+ protocol_versions: Arc<RwLock<HashMap<ProtocolID, Version>>>,
+
+ /// Managing spawned tasks.
+ task_group: TaskGroup<'static>,
+
+ /// A global Executor
+ executor: GlobalExecutor,
+
+ /// The Configuration for the P2P network.
+ pub(crate) config: Arc<Config>,
+
+ /// Responsible for network and system monitoring.
+ monitor: Arc<Monitor>,
+}
+
+impl PeerPool {
+ /// Creates a new PeerPool
+ pub fn new(
+ id: &PeerID,
+ conn_queue: Arc<ConnQueue>,
+ config: Arc<Config>,
+ monitor: Arc<Monitor>,
+ executor: GlobalExecutor,
+ ) -> Arc<Self> {
+ let protocols = RwLock::new(HashMap::new());
+ let protocol_versions = Arc::new(RwLock::new(HashMap::new()));
+
+ Arc::new(Self {
+ id: id.clone(),
+ conn_queue,
+ peers: Mutex::new(HashMap::new()),
+ protocols,
+ protocol_versions,
+ task_group: TaskGroup::new(executor.clone()),
+ executor,
+ monitor,
+ config,
+ })
+ }
+
+ /// Start
+ pub async fn start(self: &Arc<Self>) -> Result<()> {
+ self.setup_protocols().await?;
+ let selfc = self.clone();
+ self.task_group.spawn(selfc.listen_loop(), |_| async {});
+ Ok(())
+ }
+
+ /// Listens to a new connection from the connection queue
+ pub async fn listen_loop(self: Arc<Self>) {
+ loop {
+ let new_conn = self.conn_queue.next().await;
+ let signal = new_conn.disconnect_signal;
+
+ let result = self
+ .new_peer(new_conn.conn, &new_conn.direction, signal.clone())
+ .await;
+
+ // Only send a disconnect signal if there is an error when adding a peer.
+ if result.is_err() {
+ let _ = signal.send(result).await;
+ }
+ }
+ }
+
+ /// Shuts down
+ pub async fn shutdown(&self) {
+ for (_, peer) in self.peers.lock().await.iter() {
+ peer.shutdown().await;
+ }
+
+ self.task_group.cancel().await;
+ }
+
+ /// Attach a custom protocol to the network
+ pub async fn attach_protocol<P: Protocol>(&self, c: Box<ProtocolConstructor>) -> Result<()> {
+ let protocol_versions = &mut self.protocol_versions.write().await;
+ let protocols = &mut self.protocols.write().await;
+
+ protocol_versions.insert(P::id(), P::version()?);
+ protocols.insert(P::id(), c);
+ Ok(())
+ }
+
+ /// Returns the number of currently connected peers.
+ pub async fn peers_len(&self) -> usize {
+ self.peers.lock().await.len()
+ }
+
+ /// Broadcast a message to all connected peers using the specified protocol.
+ pub async fn broadcast<T: CodecMsg>(&self, proto_id: &ProtocolID, msg: &T) {
+ for (pid, peer) in self.peers.lock().await.iter() {
+ if let Err(err) = peer.send(proto_id, msg).await {
+ error!("failed to send msg to {pid}: {err}");
+ continue;
+ }
+ }
+ }
+
+ /// Add a new peer to the peer list.
+ pub async fn new_peer(
+ self: &Arc<Self>,
+ conn: Conn,
+ conn_direction: &ConnDirection,
+ disconnect_signal: Sender<Result<()>>,
+ ) -> Result<()> {
+ let endpoint = conn.peer_endpoint()?;
+ let codec = Codec::new(conn);
+
+ // Do a handshake with the connection before creating a new peer.
+ let pid = self.do_handshake(&codec, conn_direction).await?;
+
+ // TODO: Consider restricting the subnet for inbound connections
+ if self.contains_peer(&pid).await {
+ return Err(Error::PeerAlreadyConnected);
+ }
+
+ // Create a new peer
+ let peer = Peer::new(
+ Arc::downgrade(self),
+ &pid,
+ codec,
+ endpoint.clone(),
+ conn_direction.clone(),
+ self.executor.clone(),
+ );
+
+ // Insert the new peer
+ self.peers.lock().await.insert(pid.clone(), peer.clone());
+
+ let selfc = self.clone();
+ let pid_c = pid.clone();
+ let on_disconnect = |result| async move {
+ if let TaskResult::Completed(result) = result {
+ if let Err(err) = selfc.remove_peer(&pid_c).await {
+ error!("Failed to remove peer {pid_c}: {err}");
+ }
+ let _ = disconnect_signal.send(result).await;
+ }
+ };
+
+ self.task_group.spawn(peer.run(), on_disconnect);
+
+ info!("Add new peer {pid}, direction: {conn_direction}, endpoint: {endpoint}");
+
+ self.monitor
+ .notify(&PeerPoolEvent::NewPeer(pid.clone()).into())
+ .await;
+
+ Ok(())
+ }
+
+ /// Checks if the peer list contains a peer with the given peer id
+ pub async fn contains_peer(&self, pid: &PeerID) -> bool {
+ self.peers.lock().await.contains_key(pid)
+ }
+
+ /// Shuts down the peer and remove it from the peer list.
+ async fn remove_peer(&self, pid: &PeerID) -> Result<()> {
+ let result = self.peers.lock().await.remove(pid);
+
+ let peer = match result {
+ Some(p) => p,
+ None => return Ok(()),
+ };
+
+ peer.shutdown().await;
+
+ self.monitor
+ .notify(&PeerPoolEvent::RemovePeer(pid.clone()).into())
+ .await;
+
+ let endpoint = peer.remote_endpoint();
+ let direction = peer.direction();
+
+ warn!("Peer {pid} removed, direction: {direction}, endpoint: {endpoint}",);
+ Ok(())
+ }
+
+ /// Attach the core protocols.
+ async fn setup_protocols(&self) -> Result<()> {
+ let executor = self.executor.clone();
+ let c = move |peer| PingProtocol::new(peer, executor.clone());
+ self.attach_protocol::<PingProtocol>(Box::new(c)).await
+ }
+
+ /// Initiate a handshake with a connection.
+ async fn do_handshake(&self, codec: &Codec, conn_direction: &ConnDirection) -> Result<PeerID> {
+ match conn_direction {
+ ConnDirection::Inbound => {
+ let result = self.wait_vermsg(codec).await;
+ match result {
+ Ok(_) => {
+ self.send_verack(codec, true).await?;
+ }
+ Err(Error::IncompatibleVersion(_)) | Err(Error::UnsupportedProtocol(_)) => {
+ self.send_verack(codec, false).await?;
+ }
+ _ => {}
+ }
+ result
+ }
+
+ ConnDirection::Outbound => {
+ self.send_vermsg(codec).await?;
+ self.wait_verack(codec).await
+ }
+ }
+ }
+
+ /// Send a Version message
+ async fn send_vermsg(&self, codec: &Codec) -> Result<()> {
+ let pids = self.protocol_versions.read().await;
+ let protocols = pids.iter().map(|p| (p.0.clone(), p.1.v.clone())).collect();
+ drop(pids);
+
+ let vermsg = VerMsg {
+ peer_id: self.id.clone(),
+ protocols,
+ version: self.config.version.v.clone(),
+ };
+
+ trace!("Send VerMsg");
+ codec.write(NetMsgCmd::Version, &vermsg).await?;
+ Ok(())
+ }
+
+ /// Wait for a Version message
+ ///
+ /// Returns the peer's ID upon successfully receiving the Version message.
+ async fn wait_vermsg(&self, codec: &Codec) -> Result<PeerID> {
+ let timeout = Duration::from_secs(self.config.handshake_timeout);
+ let msg: NetMsg = codec.read_timeout(timeout).await?;
+
+ let payload = get_msg_payload!(Version, msg);
+ let (vermsg, _) = decode::<VerMsg>(&payload)?;
+
+ if !version_match(&self.config.version.req, &vermsg.version) {
+ return Err(Error::IncompatibleVersion("system: {}".into()));
+ }
+
+ self.protocols_match(&vermsg.protocols).await?;
+
+ trace!("Received VerMsg from: {}", vermsg.peer_id);
+ Ok(vermsg.peer_id)
+ }
+
+ /// Send a Verack message
+ async fn send_verack(&self, codec: &Codec, ack: bool) -> Result<()> {
+ let verack = VerAckMsg {
+ peer_id: self.id.clone(),
+ ack,
+ };
+
+ trace!("Send VerAckMsg {:?}", verack);
+ codec.write(NetMsgCmd::Verack, &verack).await?;
+ Ok(())
+ }
+
+ /// Wait for a Verack message
+ ///
+ /// Returns the peer's ID upon successfully receiving the Verack message.
+ async fn wait_verack(&self, codec: &Codec) -> Result<PeerID> {
+ let timeout = Duration::from_secs(self.config.handshake_timeout);
+ let msg: NetMsg = codec.read_timeout(timeout).await?;
+
+ let payload = get_msg_payload!(Verack, msg);
+ let (verack, _) = decode::<VerAckMsg>(&payload)?;
+
+ if !verack.ack {
+ return Err(Error::IncompatiblePeer);
+ }
+
+ trace!("Received VerAckMsg from: {}", verack.peer_id);
+ Ok(verack.peer_id)
+ }
+
+ /// Check if the new connection has compatible protocols.
+ async fn protocols_match(&self, protocols: &HashMap<ProtocolID, VersionInt>) -> Result<()> {
+ for (n, pv) in protocols.iter() {
+ let pids = self.protocol_versions.read().await;
+
+ match pids.get(n) {
+ Some(v) => {
+ if !version_match(&v.req, pv) {
+ return Err(Error::IncompatibleVersion(format!("{n} protocol: {pv}")));
+ }
+ }
+ None => {
+ return Err(Error::UnsupportedProtocol(n.to_string()));
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +
use std::sync::Arc;
+
+use async_trait::async_trait;
+
+use karyon_core::event::EventValue;
+
+use crate::{peer::ArcPeer, version::Version, Result};
+
+pub type ArcProtocol = Arc<dyn Protocol>;
+
+pub type ProtocolConstructor = dyn Fn(ArcPeer) -> Arc<dyn Protocol> + Send + Sync;
+
+pub type ProtocolID = String;
+
+/// Protocol event
+#[derive(Debug, Clone)]
+pub enum ProtocolEvent {
+ /// Message event, contains a vector of bytes.
+ Message(Vec<u8>),
+ /// Shutdown event signals the protocol to gracefully shut down.
+ Shutdown,
+}
+
+impl EventValue for ProtocolEvent {
+ fn id() -> &'static str {
+ "ProtocolEvent"
+ }
+}
+
+/// The Protocol trait defines the interface for core protocols
+/// and custom protocols.
+///
+/// # Example
+/// ```
+/// use std::sync::Arc;
+///
+/// use async_trait::async_trait;
+/// use smol::Executor;
+///
+/// use karyon_core::crypto::{KeyPair, KeyPairType};
+/// use karyon_p2p::{
+/// protocol::{ArcProtocol, Protocol, ProtocolID, ProtocolEvent},
+/// Backend, PeerID, Config, Version, P2pError, ArcPeer};
+///
+/// pub struct NewProtocol {
+/// peer: ArcPeer,
+/// }
+///
+/// impl NewProtocol {
+/// fn new(peer: ArcPeer) -> ArcProtocol {
+/// Arc::new(Self {
+/// peer,
+/// })
+/// }
+/// }
+///
+/// #[async_trait]
+/// impl Protocol for NewProtocol {
+/// async fn start(self: Arc<Self>) -> Result<(), P2pError> {
+/// let listener = self.peer.register_listener::<Self>().await;
+/// loop {
+/// let event = listener.recv().await.unwrap();
+///
+/// match event {
+/// ProtocolEvent::Message(msg) => {
+/// println!("{:?}", msg);
+/// }
+/// ProtocolEvent::Shutdown => {
+/// break;
+/// }
+/// }
+/// }
+///
+/// listener.cancel().await;
+/// Ok(())
+/// }
+///
+/// fn version() -> Result<Version, P2pError> {
+/// "0.2.0, >0.1.0".parse()
+/// }
+///
+/// fn id() -> ProtocolID {
+/// "NEWPROTOCOLID".into()
+/// }
+/// }
+///
+/// async {
+/// let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+/// let config = Config::default();
+///
+/// // Create a new Executor
+/// let ex = Arc::new(Executor::new());
+///
+/// // Create a new Backend
+/// let backend = Backend::new(&key_pair, config, ex);
+///
+/// // Attach the NewProtocol
+/// let c = move |peer| NewProtocol::new(peer);
+/// backend.attach_protocol::<NewProtocol>(c).await.unwrap();
+/// };
+///
+/// ```
+#[async_trait]
+pub trait Protocol: Send + Sync {
+ /// Start the protocol
+ async fn start(self: Arc<Self>) -> Result<()>;
+
+ /// Returns the version of the protocol.
+ fn version() -> Result<Version>
+ where
+ Self: Sized;
+
+ /// Returns the unique ProtocolID associated with the protocol.
+ fn id() -> ProtocolID
+ where
+ Self: Sized;
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +
use std::{sync::Arc, time::Duration};
+
+use async_trait::async_trait;
+use bincode::{Decode, Encode};
+use log::trace;
+use rand::{rngs::OsRng, RngCore};
+use smol::{
+ channel,
+ channel::{Receiver, Sender},
+ stream::StreamExt,
+ Timer,
+};
+
+use karyon_core::{
+ async_util::{select, timeout, Either, TaskGroup, TaskResult},
+ event::EventListener,
+ util::decode,
+ GlobalExecutor,
+};
+
+use karyon_net::NetError;
+
+use crate::{
+ peer::ArcPeer,
+ protocol::{ArcProtocol, Protocol, ProtocolEvent, ProtocolID},
+ version::Version,
+ Result,
+};
+
+const MAX_FAILUERS: u32 = 3;
+
+#[derive(Clone, Debug, Encode, Decode)]
+enum PingProtocolMsg {
+ Ping([u8; 32]),
+ Pong([u8; 32]),
+}
+
+pub struct PingProtocol {
+ peer: ArcPeer,
+ ping_interval: u64,
+ ping_timeout: u64,
+ task_group: TaskGroup<'static>,
+}
+
+impl PingProtocol {
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new(peer: ArcPeer, executor: GlobalExecutor) -> ArcProtocol {
+ let ping_interval = peer.config().ping_interval;
+ let ping_timeout = peer.config().ping_timeout;
+ Arc::new(Self {
+ peer,
+ ping_interval,
+ ping_timeout,
+ task_group: TaskGroup::new(executor),
+ })
+ }
+
+ async fn recv_loop(
+ &self,
+ listener: &EventListener<ProtocolID, ProtocolEvent>,
+ pong_chan: Sender<[u8; 32]>,
+ ) -> Result<()> {
+ loop {
+ let event = listener.recv().await?;
+ let msg_payload = match event.clone() {
+ ProtocolEvent::Message(m) => m,
+ ProtocolEvent::Shutdown => {
+ break;
+ }
+ };
+
+ let (msg, _) = decode::<PingProtocolMsg>(&msg_payload)?;
+
+ match msg {
+ PingProtocolMsg::Ping(nonce) => {
+ trace!("Received Ping message {:?}", nonce);
+ self.peer
+ .send(&Self::id(), &PingProtocolMsg::Pong(nonce))
+ .await?;
+ trace!("Send back Pong message {:?}", nonce);
+ }
+ PingProtocolMsg::Pong(nonce) => {
+ pong_chan.send(nonce).await?;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ async fn ping_loop(self: Arc<Self>, chan: Receiver<[u8; 32]>) -> Result<()> {
+ let mut timer = Timer::interval(Duration::from_secs(self.ping_interval));
+ let rng = &mut OsRng;
+ let mut retry = 0;
+
+ while retry < MAX_FAILUERS {
+ timer.next().await;
+
+ let mut ping_nonce: [u8; 32] = [0; 32];
+ rng.fill_bytes(&mut ping_nonce);
+
+ trace!("Send Ping message {:?}", ping_nonce);
+ self.peer
+ .send(&Self::id(), &PingProtocolMsg::Ping(ping_nonce))
+ .await?;
+
+ let d = Duration::from_secs(self.ping_timeout);
+
+ // Wait for Pong message
+ let pong_msg = match timeout(d, chan.recv()).await {
+ Ok(m) => m?,
+ Err(_) => {
+ retry += 1;
+ continue;
+ }
+ };
+
+ trace!("Received Pong message {:?}", pong_msg);
+
+ if pong_msg != ping_nonce {
+ retry += 1;
+ continue;
+ }
+ }
+
+ Err(NetError::Timeout.into())
+ }
+}
+
+#[async_trait]
+impl Protocol for PingProtocol {
+ async fn start(self: Arc<Self>) -> Result<()> {
+ trace!("Start Ping protocol");
+
+ let (pong_chan, pong_chan_recv) = channel::bounded(1);
+ let (stop_signal_s, stop_signal) = channel::bounded::<Result<()>>(1);
+
+ let selfc = self.clone();
+ self.task_group.spawn(
+ selfc.clone().ping_loop(pong_chan_recv.clone()),
+ |res| async move {
+ if let TaskResult::Completed(result) = res {
+ let _ = stop_signal_s.send(result).await;
+ }
+ },
+ );
+
+ let listener = self.peer.register_listener::<Self>().await;
+
+ let result = select(self.recv_loop(&listener, pong_chan), stop_signal.recv()).await;
+ listener.cancel().await;
+ self.task_group.cancel().await;
+
+ match result {
+ Either::Left(res) => {
+ trace!("Receive loop stopped {:?}", res);
+ res
+ }
+ Either::Right(res) => {
+ let res = res?;
+ trace!("Ping loop stopped {:?}", res);
+ res
+ }
+ }
+ }
+
+ fn version() -> Result<Version> {
+ "0.1.0".parse()
+ }
+
+ fn id() -> ProtocolID {
+ "PING".into()
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +
use super::{Entry, Key};
+
+use rand::{rngs::OsRng, seq::SliceRandom};
+
+/// BITFLAGS represent the status of an Entry within a bucket.
+pub type EntryStatusFlag = u16;
+
+/// The entry is connected.
+pub const CONNECTED_ENTRY: EntryStatusFlag = 0b000001;
+
+/// The entry is disconnected. This will increase the failure counter.
+pub const DISCONNECTED_ENTRY: EntryStatusFlag = 0b000010;
+
+/// The entry is ready to reconnect, meaning it has either been added and
+/// has no connection attempts, or it has been refreshed.
+pub const PENDING_ENTRY: EntryStatusFlag = 0b000100;
+
+/// The entry is unreachable. This will increase the failure counter.
+pub const UNREACHABLE_ENTRY: EntryStatusFlag = 0b001000;
+
+/// The entry is unstable. This will increase the failure counter.
+pub const UNSTABLE_ENTRY: EntryStatusFlag = 0b010000;
+
+/// The entry is incompatible. This entry will not contribute to an increase in
+/// failure attempts, instead, it will persist in the routing table for the
+/// lookup process and will only be removed in the presence of a new entry.
+pub const INCOMPATIBLE_ENTRY: EntryStatusFlag = 0b100000;
+
+#[allow(dead_code)]
+pub const ALL_ENTRY: EntryStatusFlag = 0b111111;
+
+/// A BucketEntry represents a peer in the routing table.
+#[derive(Clone, Debug)]
+pub struct BucketEntry {
+ pub status: EntryStatusFlag,
+ pub entry: Entry,
+ pub failures: u32,
+ pub last_seen: i64,
+}
+
+impl BucketEntry {
+ pub fn is_connected(&self) -> bool {
+ self.status ^ CONNECTED_ENTRY == 0
+ }
+
+ pub fn is_incompatible(&self) -> bool {
+ self.status ^ INCOMPATIBLE_ENTRY == 0
+ }
+
+ pub fn is_unreachable(&self) -> bool {
+ self.status ^ UNREACHABLE_ENTRY == 0
+ }
+
+ pub fn is_unstable(&self) -> bool {
+ self.status ^ UNSTABLE_ENTRY == 0
+ }
+}
+
+/// The number of entries that can be stored within a single bucket.
+pub const BUCKET_SIZE: usize = 20;
+
+/// A Bucket represents a group of entries in the routing table.
+#[derive(Debug, Clone)]
+pub struct Bucket {
+ entries: Vec<BucketEntry>,
+}
+
+impl Bucket {
+ /// Creates a new empty Bucket
+ pub fn new() -> Self {
+ Self {
+ entries: Vec::with_capacity(BUCKET_SIZE),
+ }
+ }
+
+ /// Add an entry to the bucket.
+ pub fn add(&mut self, entry: &Entry) {
+ self.entries.push(BucketEntry {
+ status: PENDING_ENTRY,
+ entry: entry.clone(),
+ failures: 0,
+ last_seen: chrono::Utc::now().timestamp(),
+ })
+ }
+
+ /// Get the number of entries in the bucket.
+ pub fn len(&self) -> usize {
+ self.entries.len()
+ }
+
+ /// Returns an iterator over the entries in the bucket.
+ pub fn iter(&self) -> impl Iterator<Item = &BucketEntry> {
+ self.entries.iter()
+ }
+
+ /// Remove an entry.
+ pub fn remove(&mut self, key: &Key) {
+ let position = self.entries.iter().position(|e| &e.entry.key == key);
+ if let Some(i) = position {
+ self.entries.remove(i);
+ }
+ }
+
+ /// Returns an iterator of entries in random order.
+ pub fn random_iter(&self, amount: usize) -> impl Iterator<Item = &BucketEntry> {
+ self.entries.choose_multiple(&mut OsRng, amount)
+ }
+
+ /// Updates the status of an entry in the bucket identified by the given key.
+ ///
+ /// If the key is not found in the bucket, no action is taken.
+ ///
+ /// This will also update the last_seen field and increase the failures
+ /// counter for the bucket entry according to the new status.
+ pub fn update_entry(&mut self, key: &Key, entry_flag: EntryStatusFlag) {
+ if let Some(e) = self.entries.iter_mut().find(|e| &e.entry.key == key) {
+ e.status = entry_flag;
+ if e.is_unreachable() || e.is_unstable() {
+ e.failures += 1;
+ }
+
+ if !e.is_unreachable() {
+ e.last_seen = chrono::Utc::now().timestamp();
+ }
+ }
+ }
+
+ /// Check if the bucket contains the given key.
+ pub fn contains_key(&self, key: &Key) -> bool {
+ self.entries.iter().any(|e| &e.entry.key == key)
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +
use bincode::{Decode, Encode};
+
+use karyon_net::{Addr, Port};
+
+/// Specifies the size of the key, in bytes.
+pub const KEY_SIZE: usize = 32;
+
+/// An Entry represents a peer in the routing table.
+#[derive(Encode, Decode, Clone, Debug)]
+pub struct Entry {
+ /// The unique key identifying the peer.
+ pub key: Key,
+ /// The IP address of the peer.
+ pub addr: Addr,
+ /// TCP port
+ pub port: Port,
+ /// UDP/TCP port
+ pub discovery_port: Port,
+}
+
+impl PartialEq for Entry {
+ fn eq(&self, other: &Self) -> bool {
+ // TODO: this should also compare both addresses (the self.addr == other.addr)
+ self.key == other.key
+ }
+}
+
+/// The unique key identifying the peer.
+pub type Key = [u8; KEY_SIZE];
+
+/// Calculates the XOR distance between two provided keys.
+///
+/// The XOR distance is a metric used in Kademlia to measure the closeness
+/// of keys.
+pub fn xor_distance(key: &Key, other: &Key) -> Key {
+ let mut res = [0; 32];
+ for (i, (k, o)) in key.iter().zip(other.iter()).enumerate() {
+ res[i] = k ^ o;
+ }
+ res
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +
use std::net::IpAddr;
+
+mod bucket;
+mod entry;
+
+pub use bucket::{
+ Bucket, BucketEntry, EntryStatusFlag, CONNECTED_ENTRY, DISCONNECTED_ENTRY, INCOMPATIBLE_ENTRY,
+ PENDING_ENTRY, UNREACHABLE_ENTRY, UNSTABLE_ENTRY,
+};
+pub use entry::{xor_distance, Entry, Key};
+
+use rand::{rngs::OsRng, seq::SliceRandom};
+
+use karyon_net::Addr;
+
+use bucket::BUCKET_SIZE;
+use entry::KEY_SIZE;
+
+/// The total number of buckets in the routing table.
+const TABLE_SIZE: usize = 32;
+
+/// The distance limit for the closest buckets.
+const DISTANCE_LIMIT: usize = 32;
+
+/// The maximum number of matched subnets allowed within a single bucket.
+const MAX_MATCHED_SUBNET_IN_BUCKET: usize = 1;
+
+/// The maximum number of matched subnets across the entire routing table.
+const MAX_MATCHED_SUBNET_IN_TABLE: usize = 6;
+
+/// Represents the possible result when adding a new entry.
+#[derive(Debug)]
+pub enum AddEntryResult {
+ /// The entry is added.
+ Added,
+ /// The entry is already exists.
+ Exists,
+ /// The entry is ignored.
+ Ignored,
+ /// The entry is restricted and not allowed.
+ Restricted,
+}
+
+/// This is a modified version of the Kademlia Distributed Hash Table (DHT).
+/// <https://en.wikipedia.org/wiki/Kademlia>
+#[derive(Debug)]
+pub struct RoutingTable {
+ key: Key,
+ buckets: Vec<Bucket>,
+}
+
+impl RoutingTable {
+ /// Creates a new RoutingTable
+ pub fn new(key: Key) -> Self {
+ let buckets: Vec<Bucket> = (0..TABLE_SIZE).map(|_| Bucket::new()).collect();
+ Self { key, buckets }
+ }
+
+ /// Adds a new entry to the table and returns a result indicating success,
+ /// failure, or restrictions.
+ pub fn add_entry(&mut self, entry: Entry) -> AddEntryResult {
+ // Determine the index of the bucket where the entry should be placed.
+ let bucket_idx = match self.bucket_index(&entry.key) {
+ Some(i) => i,
+ None => return AddEntryResult::Ignored,
+ };
+
+ let bucket = &self.buckets[bucket_idx];
+
+ // Check if the entry already exists in the bucket.
+ if bucket.contains_key(&entry.key) {
+ return AddEntryResult::Exists;
+ }
+
+ // Check if the entry is restricted.
+ if self.subnet_restricted(bucket_idx, &entry) {
+ return AddEntryResult::Restricted;
+ }
+
+ let bucket = &mut self.buckets[bucket_idx];
+
+ // If the bucket has free space, add the entry and return success.
+ if bucket.len() < BUCKET_SIZE {
+ bucket.add(&entry);
+ return AddEntryResult::Added;
+ }
+
+ // Replace it with an incompatible entry if one exists.
+ let incompatible_entry = bucket.iter().find(|e| e.is_incompatible()).cloned();
+ if let Some(e) = incompatible_entry {
+ bucket.remove(&e.entry.key);
+ bucket.add(&entry);
+ return AddEntryResult::Added;
+ }
+
+ // If the bucket is full, the entry is ignored.
+ AddEntryResult::Ignored
+ }
+
+ /// Check if the table contains the given key.
+ pub fn contains_key(&self, key: &Key) -> bool {
+ // Determine the bucket index for the given key.
+ let bucket_idx = match self.bucket_index(key) {
+ Some(bi) => bi,
+ None => return false,
+ };
+
+ let bucket = &self.buckets[bucket_idx];
+ bucket.contains_key(key)
+ }
+
+ /// Updates the status of an entry in the routing table identified
+ /// by the given key.
+ ///
+ /// If the key is not found, no action is taken.
+ pub fn update_entry(&mut self, key: &Key, entry_flag: EntryStatusFlag) {
+ // Determine the bucket index for the given key.
+ let bucket_idx = match self.bucket_index(key) {
+ Some(bi) => bi,
+ None => return,
+ };
+
+ let bucket = &mut self.buckets[bucket_idx];
+ bucket.update_entry(key, entry_flag);
+ }
+
+ /// Returns a list of bucket indexes that are closest to the given target key.
+ pub fn bucket_indexes(&self, target_key: &Key) -> Vec<usize> {
+ let mut indexes = vec![];
+
+ // Determine the primary bucket index for the target key.
+ let bucket_idx = self.bucket_index(target_key).unwrap_or(0);
+
+ indexes.push(bucket_idx);
+
+ // Add additional bucket indexes within a certain distance limit.
+ for i in 1..DISTANCE_LIMIT {
+ if bucket_idx >= i && bucket_idx - i >= 1 {
+ indexes.push(bucket_idx - i);
+ }
+
+ if bucket_idx + i < (TABLE_SIZE - 1) {
+ indexes.push(bucket_idx + i);
+ }
+ }
+
+ indexes
+ }
+
+ /// Returns a list of the closest entries to the given target key, limited by max_entries.
+ pub fn closest_entries(&self, target_key: &Key, max_entries: usize) -> Vec<Entry> {
+ let mut entries: Vec<Entry> = vec![];
+
+ // Collect entries
+ 'outer: for idx in self.bucket_indexes(target_key) {
+ let bucket = &self.buckets[idx];
+ for bucket_entry in bucket.iter() {
+ if bucket_entry.is_unreachable() || bucket_entry.is_unstable() {
+ continue;
+ }
+
+ entries.push(bucket_entry.entry.clone());
+ if entries.len() == max_entries {
+ break 'outer;
+ }
+ }
+ }
+
+ // Sort the entries by their distance to the target key.
+ entries.sort_by(|a, b| {
+ xor_distance(target_key, &a.key).cmp(&xor_distance(target_key, &b.key))
+ });
+
+ entries
+ }
+
+ /// Removes an entry with the given key from the routing table, if it exists.
+ pub fn remove_entry(&mut self, key: &Key) {
+ // Determine the bucket index for the given key.
+ let bucket_idx = match self.bucket_index(key) {
+ Some(bi) => bi,
+ None => return,
+ };
+
+ let bucket = &mut self.buckets[bucket_idx];
+ bucket.remove(key);
+ }
+
+ /// Returns an iterator of entries.
+ pub fn iter(&self) -> impl Iterator<Item = &Bucket> {
+ self.buckets.iter()
+ }
+
+ /// Returns a random entry from the routing table.
+ pub fn random_entry(&self, entry_flag: EntryStatusFlag) -> Option<&Entry> {
+ for bucket in self.buckets.choose_multiple(&mut OsRng, self.buckets.len()) {
+ for entry in bucket.random_iter(bucket.len()) {
+ if entry.status & entry_flag == 0 {
+ continue;
+ }
+ return Some(&entry.entry);
+ }
+ }
+
+ None
+ }
+
+ // Returns the bucket index for a given key in the table.
+ fn bucket_index(&self, key: &Key) -> Option<usize> {
+ // Calculate the XOR distance between the self key and the provided key.
+ let distance = xor_distance(&self.key, key);
+
+ for (i, b) in distance.iter().enumerate() {
+ if *b != 0 {
+ let lz = i * 8 + b.leading_zeros() as usize;
+ let bits = KEY_SIZE * 8 - 1;
+ let idx = (bits - lz) / 8;
+ return Some(idx);
+ }
+ }
+ None
+ }
+
+ /// This function iterate through the routing table and counts how many
+ /// entries in the same subnet as the given Entry are already present.
+ ///
+ /// If the number of matching entries in the same bucket exceeds a
+ /// threshold (MAX_MATCHED_SUBNET_IN_BUCKET), or if the total count of
+ /// matching entries in the entire table exceeds a threshold
+ /// (MAX_MATCHED_SUBNET_IN_TABLE), the addition of the Entry
+ /// is considered restricted and returns true.
+ fn subnet_restricted(&self, idx: usize, entry: &Entry) -> bool {
+ let mut bucket_count = 0;
+ let mut table_count = 0;
+
+ // Iterate through the routing table's buckets and entries to check
+ // for subnet matches.
+ for (i, bucket) in self.buckets.iter().enumerate() {
+ for e in bucket.iter() {
+ // If there is a subnet match, update the counts.
+ let matched = subnet_match(&e.entry.addr, &entry.addr);
+ if matched {
+ if i == idx {
+ bucket_count += 1;
+ }
+ table_count += 1;
+ }
+
+ // If the number of matched entries in the same bucket exceeds
+ // the limit, return true
+ if bucket_count >= MAX_MATCHED_SUBNET_IN_BUCKET {
+ return true;
+ }
+ }
+
+ // If the total matched entries in the table exceed the limit,
+ // return true.
+ if table_count >= MAX_MATCHED_SUBNET_IN_TABLE {
+ return true;
+ }
+ }
+
+ // If no subnet restrictions are encountered, return false.
+ false
+ }
+}
+
+/// Check if two addresses belong to the same subnet.
+pub fn subnet_match(addr: &Addr, other_addr: &Addr) -> bool {
+ match (addr, other_addr) {
+ (Addr::Ip(IpAddr::V4(ip)), Addr::Ip(IpAddr::V4(other_ip))) => {
+ // TODO: Consider moving this to a different place
+ if other_ip.is_loopback() && ip.is_loopback() {
+ return false;
+ }
+ ip.octets()[0..3] == other_ip.octets()[0..3]
+ }
+ _ => false,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::bucket::ALL_ENTRY;
+ use super::*;
+
+ use karyon_net::Addr;
+
+ struct Setup {
+ local_key: Key,
+ keys: Vec<Key>,
+ }
+
+ fn new_entry(key: &Key, addr: &Addr, port: u16, discovery_port: u16) -> Entry {
+ Entry {
+ key: key.clone(),
+ addr: addr.clone(),
+ port,
+ discovery_port,
+ }
+ }
+
+ impl Setup {
+ fn new() -> Self {
+ let keys = vec![
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ ],
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 0, 1, 1, 2,
+ ],
+ [
+ 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ ],
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 1, 18, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4,
+ ],
+ [
+ 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 5,
+ ],
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 18, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6,
+ ],
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 50, 1, 18, 0, 0,
+ 0, 0, 0, 0, 0, 0, 7,
+ ],
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 50, 1, 18, 0, 0,
+ 0, 0, 0, 0, 0, 0, 8,
+ ],
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 50, 1, 18, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9,
+ ],
+ ];
+
+ Self {
+ local_key: [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ ],
+ keys,
+ }
+ }
+
+ fn entries(&self) -> Vec<Entry> {
+ let mut entries = vec![];
+ for (i, key) in self.keys.iter().enumerate() {
+ entries.push(new_entry(
+ key,
+ &Addr::Ip(format!("127.0.{i}.1").parse().unwrap()),
+ 3000,
+ 3010,
+ ));
+ }
+ entries
+ }
+
+ fn table(&self) -> RoutingTable {
+ let mut table = RoutingTable::new(self.local_key.clone());
+
+ for entry in self.entries() {
+ let res = table.add_entry(entry);
+ assert!(matches!(res, AddEntryResult::Added));
+ }
+
+ table
+ }
+ }
+
+ #[test]
+ fn test_bucket_index() {
+ let setup = Setup::new();
+ let table = setup.table();
+
+ assert_eq!(table.bucket_index(&setup.local_key), None);
+ assert_eq!(table.bucket_index(&setup.keys[0]), Some(0));
+ assert_eq!(table.bucket_index(&setup.keys[1]), Some(5));
+ assert_eq!(table.bucket_index(&setup.keys[2]), Some(26));
+ assert_eq!(table.bucket_index(&setup.keys[3]), Some(11));
+ assert_eq!(table.bucket_index(&setup.keys[4]), Some(31));
+ assert_eq!(table.bucket_index(&setup.keys[5]), Some(11));
+ assert_eq!(table.bucket_index(&setup.keys[6]), Some(12));
+ assert_eq!(table.bucket_index(&setup.keys[7]), Some(13));
+ assert_eq!(table.bucket_index(&setup.keys[8]), Some(14));
+ }
+
+ #[test]
+ fn test_closest_entries() {
+ let setup = Setup::new();
+ let table = setup.table();
+ let entries = setup.entries();
+
+ assert_eq!(
+ table.closest_entries(&setup.keys[5], 8),
+ vec![
+ entries[5].clone(),
+ entries[3].clone(),
+ entries[1].clone(),
+ entries[6].clone(),
+ entries[7].clone(),
+ entries[8].clone(),
+ entries[2].clone(),
+ ]
+ );
+
+ assert_eq!(
+ table.closest_entries(&setup.keys[4], 2),
+ vec![entries[4].clone(), entries[2].clone()]
+ );
+ }
+
+ #[test]
+ fn test_random_entry() {
+ let setup = Setup::new();
+ let mut table = setup.table();
+ let entries = setup.entries();
+
+ let entry = table.random_entry(ALL_ENTRY);
+ assert!(matches!(entry, Some(&_)));
+
+ let entry = table.random_entry(CONNECTED_ENTRY);
+ assert!(matches!(entry, None));
+
+ for entry in entries {
+ table.remove_entry(&entry.key);
+ }
+
+ let entry = table.random_entry(ALL_ENTRY);
+ assert!(matches!(entry, None));
+ }
+
+ #[test]
+ fn test_add_entries() {
+ let setup = Setup::new();
+ let mut table = setup.table();
+
+ let key = [
+ 0, 0, 0, 0, 0, 0, 0, 1, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5,
+ ];
+
+ let key2 = [
+ 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5,
+ ];
+
+ let entry1 = new_entry(&key, &Addr::Ip("240.120.3.1".parse().unwrap()), 3000, 3010);
+ assert!(matches!(
+ table.add_entry(entry1.clone()),
+ AddEntryResult::Added
+ ));
+
+ assert!(matches!(table.add_entry(entry1), AddEntryResult::Exists));
+
+ let entry2 = new_entry(&key2, &Addr::Ip("240.120.3.2".parse().unwrap()), 3000, 3010);
+ assert!(matches!(
+ table.add_entry(entry2),
+ AddEntryResult::Restricted
+ ));
+
+ let mut key: [u8; 32] = [0; 32];
+
+ for i in 0..BUCKET_SIZE {
+ key[i] += 1;
+ let entry = new_entry(
+ &key,
+ &Addr::Ip(format!("127.0.{i}.1").parse().unwrap()),
+ 3000,
+ 3010,
+ );
+ table.add_entry(entry);
+ }
+
+ key[BUCKET_SIZE] += 1;
+ let entry = new_entry(&key, &Addr::Ip("125.20.0.1".parse().unwrap()), 3000, 3010);
+ assert!(matches!(table.add_entry(entry), AddEntryResult::Ignored));
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +
use std::sync::atomic::{AtomicUsize, Ordering};
+
+use karyon_core::async_util::CondWait;
+
+/// Manages available inbound and outbound slots.
+pub struct ConnectionSlots {
+ /// A condvar for notifying when a slot become available.
+ signal: CondWait,
+ /// The number of occupied slots
+ slots: AtomicUsize,
+ /// The maximum number of slots.
+ max_slots: usize,
+}
+
+impl ConnectionSlots {
+ /// Creates a new ConnectionSlots
+ pub fn new(max_slots: usize) -> Self {
+ Self {
+ signal: CondWait::new(),
+ slots: AtomicUsize::new(0),
+ max_slots,
+ }
+ }
+
+ /// Returns the number of occupied slots
+ pub fn load(&self) -> usize {
+ self.slots.load(Ordering::SeqCst)
+ }
+
+ /// Increases the occupied slots by one.
+ pub fn add(&self) {
+ self.slots.fetch_add(1, Ordering::SeqCst);
+ }
+
+ /// Decreases the occupied slots by one and notifies the waiting signal
+ /// to start accepting/connecting new connections.
+ pub async fn remove(&self) {
+ self.slots.fetch_sub(1, Ordering::SeqCst);
+ if self.slots.load(Ordering::SeqCst) < self.max_slots {
+ self.signal.signal().await;
+ }
+ }
+
+ /// Waits for a slot to become available.
+ pub async fn wait_for_slot(&self) {
+ if self.slots.load(Ordering::SeqCst) < self.max_slots {
+ return;
+ }
+
+ // Wait for a signal
+ self.signal.wait().await;
+ self.signal.reset().await;
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +
use std::sync::Arc;
+
+use futures_rustls::rustls::{
+ self,
+ client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
+ crypto::{
+ aws_lc_rs::{self, cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, kx_group},
+ CryptoProvider, SupportedKxGroup,
+ },
+ pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime},
+ server::danger::{ClientCertVerified, ClientCertVerifier},
+ CertificateError, DigitallySignedStruct, DistinguishedName,
+ Error::InvalidCertificate,
+ SignatureScheme, SupportedCipherSuite, SupportedProtocolVersion,
+};
+
+use log::error;
+use x509_parser::{certificate::X509Certificate, parse_x509_certificate};
+
+use karyon_core::crypto::{KeyPair, KeyPairType, PublicKey};
+
+use crate::{PeerID, Result};
+
+// NOTE: This code needs a comprehensive audit.
+
+static PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&rustls::version::TLS13];
+static CIPHER_SUITES: &[SupportedCipherSuite] = &[TLS13_CHACHA20_POLY1305_SHA256];
+static KX_GROUPS: &[&dyn SupportedKxGroup] = &[kx_group::X25519];
+static SIGNATURE_SCHEMES: &[SignatureScheme] = &[SignatureScheme::ED25519];
+
+const BAD_SIGNATURE_ERR: rustls::Error = InvalidCertificate(CertificateError::BadSignature);
+const BAD_ENCODING_ERR: rustls::Error = InvalidCertificate(CertificateError::BadEncoding);
+
+/// Returns a TLS client configuration.
+pub fn tls_client_config(
+ key_pair: &KeyPair,
+ peer_id: Option<PeerID>,
+) -> Result<rustls::ClientConfig> {
+ let (cert, private_key) = generate_cert(key_pair)?;
+ let server_verifier = SrvrCertVerifier { peer_id };
+
+ let client_config = rustls::ClientConfig::builder_with_provider(
+ CryptoProvider {
+ kx_groups: KX_GROUPS.to_vec(),
+ cipher_suites: CIPHER_SUITES.to_vec(),
+ ..aws_lc_rs::default_provider()
+ }
+ .into(),
+ )
+ .with_protocol_versions(PROTOCOL_VERSIONS)?
+ .dangerous()
+ .with_custom_certificate_verifier(Arc::new(server_verifier))
+ .with_client_auth_cert(vec![cert], private_key)?;
+
+ Ok(client_config)
+}
+
+/// Returns a TLS server configuration.
+pub fn tls_server_config(key_pair: &KeyPair) -> Result<rustls::ServerConfig> {
+ let (cert, private_key) = generate_cert(key_pair)?;
+ let client_verifier = CliCertVerifier {};
+ let server_config = rustls::ServerConfig::builder_with_provider(
+ CryptoProvider {
+ kx_groups: KX_GROUPS.to_vec(),
+ cipher_suites: CIPHER_SUITES.to_vec(),
+ ..aws_lc_rs::default_provider()
+ }
+ .into(),
+ )
+ .with_protocol_versions(PROTOCOL_VERSIONS)?
+ .with_client_cert_verifier(Arc::new(client_verifier))
+ .with_single_cert(vec![cert], private_key)?;
+
+ Ok(server_config)
+}
+
+/// Generates a certificate and returns both the certificate and the private key.
+fn generate_cert<'a>(key_pair: &KeyPair) -> Result<(CertificateDer<'a>, PrivateKeyDer<'a>)> {
+ let cert_key_pair = rcgen::KeyPair::generate(&rcgen::PKCS_ED25519)?;
+ let private_key = PrivateKeyDer::Pkcs8(cert_key_pair.serialize_der().into());
+
+ // Add a custom extension to the certificate:
+ // - Sign the certificate's public key with the provided key pair's private key
+ // - Append both the computed signature and the key pair's public key to the extension
+ let signature = key_pair.sign(&cert_key_pair.public_key_der());
+ let ext_content = yasna::encode_der(&(key_pair.public().as_bytes().to_vec(), signature));
+ // XXX: Not sure about the oid number ???
+ let mut ext = rcgen::CustomExtension::from_oid_content(&[0, 0, 0, 0], ext_content);
+ ext.set_criticality(true);
+
+ let mut params = rcgen::CertificateParams::new(vec![]);
+ params.alg = &rcgen::PKCS_ED25519;
+ params.key_pair = Some(cert_key_pair);
+ params.custom_extensions.push(ext);
+
+ let cert = CertificateDer::from(rcgen::Certificate::from_params(params)?.serialize_der()?);
+ Ok((cert, private_key))
+}
+
+/// Verifies the given certification.
+fn verify_cert(end_entity: &CertificateDer<'_>) -> std::result::Result<PeerID, rustls::Error> {
+ // Parse the certificate.
+ let cert = parse_cert(end_entity)?;
+
+ match cert.extensions().first() {
+ Some(ext) => {
+ // Extract the peer id (public key) and the signature from the extension.
+ let (public_key, signature): (Vec<u8>, Vec<u8>) =
+ yasna::decode_der(ext.value).map_err(|_| BAD_ENCODING_ERR)?;
+
+ // Use the peer id (public key) to verify the extracted signature.
+ let public_key = PublicKey::from_bytes(&KeyPairType::Ed25519, &public_key)
+ .map_err(|_| BAD_ENCODING_ERR)?;
+ public_key
+ .verify(cert.public_key().raw, &signature)
+ .map_err(|_| BAD_SIGNATURE_ERR)?;
+
+ // Verify the certificate signature.
+ verify_cert_signature(
+ &cert,
+ cert.tbs_certificate.as_ref(),
+ cert.signature_value.as_ref(),
+ )?;
+
+ PeerID::try_from(public_key).map_err(|_| BAD_ENCODING_ERR)
+ }
+ None => Err(BAD_ENCODING_ERR),
+ }
+}
+
+/// Parses the given x509 certificate.
+fn parse_cert<'a>(
+ end_entity: &'a CertificateDer<'a>,
+) -> std::result::Result<X509Certificate<'a>, rustls::Error> {
+ let (_, cert) = parse_x509_certificate(end_entity.as_ref()).map_err(|_| BAD_ENCODING_ERR)?;
+
+ if !cert.validity().is_valid() {
+ return Err(InvalidCertificate(CertificateError::NotValidYet));
+ }
+
+ Ok(cert)
+}
+
+/// Verifies the signature of the given certificate.
+fn verify_cert_signature(
+ cert: &X509Certificate,
+ message: &[u8],
+ signature: &[u8],
+) -> std::result::Result<(), rustls::Error> {
+ let public_key = PublicKey::from_bytes(
+ &KeyPairType::Ed25519,
+ cert.tbs_certificate.subject_pki.subject_public_key.as_ref(),
+ )
+ .map_err(|_| BAD_ENCODING_ERR)?;
+
+ public_key
+ .verify(message, signature)
+ .map_err(|_| BAD_SIGNATURE_ERR)
+}
+
+#[derive(Debug)]
+struct SrvrCertVerifier {
+ peer_id: Option<PeerID>,
+}
+
+impl ServerCertVerifier for SrvrCertVerifier {
+ fn verify_server_cert(
+ &self,
+ end_entity: &CertificateDer<'_>,
+ _intermediates: &[CertificateDer<'_>],
+ _server_name: &ServerName,
+ _ocsp_response: &[u8],
+ _now: UnixTime,
+ ) -> std::result::Result<ServerCertVerified, rustls::Error> {
+ let peer_id = match verify_cert(end_entity) {
+ Ok(pid) => pid,
+ Err(err) => {
+ error!("Failed to verify cert: {err}");
+ return Err(err);
+ }
+ };
+
+ // Verify that the peer id in the certificate's extension matches the
+ // one the client intends to connect to.
+ // Both should be equal for establishing a fully secure connection.
+ if let Some(pid) = &self.peer_id {
+ if pid != &peer_id {
+ return Err(InvalidCertificate(
+ CertificateError::ApplicationVerificationFailure,
+ ));
+ }
+ }
+
+ Ok(ServerCertVerified::assertion())
+ }
+
+ fn verify_tls12_signature(
+ &self,
+ _message: &[u8],
+ _cert: &CertificateDer<'_>,
+ _dss: &DigitallySignedStruct,
+ ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
+ unreachable!("ONLY SUPPORT tls 13 VERSION")
+ }
+
+ fn verify_tls13_signature(
+ &self,
+ message: &[u8],
+ cert: &CertificateDer<'_>,
+ dss: &DigitallySignedStruct,
+ ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
+ let cert = parse_cert(cert)?;
+ verify_cert_signature(&cert, message, dss.signature())?;
+ Ok(HandshakeSignatureValid::assertion())
+ }
+
+ fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
+ SIGNATURE_SCHEMES.to_vec()
+ }
+}
+
+#[derive(Debug)]
+struct CliCertVerifier {}
+impl ClientCertVerifier for CliCertVerifier {
+ fn verify_client_cert(
+ &self,
+ end_entity: &CertificateDer<'_>,
+ _intermediates: &[CertificateDer<'_>],
+ _now: UnixTime,
+ ) -> std::result::Result<ClientCertVerified, rustls::Error> {
+ if let Err(err) = verify_cert(end_entity) {
+ error!("Failed to verify cert: {err}");
+ return Err(err);
+ };
+ Ok(ClientCertVerified::assertion())
+ }
+
+ fn root_hint_subjects(&self) -> &[DistinguishedName] {
+ &[]
+ }
+
+ fn verify_tls12_signature(
+ &self,
+ _message: &[u8],
+ _cert: &CertificateDer<'_>,
+ _dss: &DigitallySignedStruct,
+ ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
+ unreachable!("ONLY SUPPORT tls 13 VERSION")
+ }
+
+ fn verify_tls13_signature(
+ &self,
+ message: &[u8],
+ cert: &CertificateDer<'_>,
+ dss: &DigitallySignedStruct,
+ ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
+ let cert = parse_cert(cert)?;
+ verify_cert_signature(&cert, message, dss.signature())?;
+ Ok(HandshakeSignatureValid::assertion())
+ }
+
+ fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
+ SIGNATURE_SCHEMES.to_vec()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn verify_generated_certificate() {
+ let key_pair = KeyPair::generate(&KeyPairType::Ed25519);
+ let (cert, _) = generate_cert(&key_pair).unwrap();
+
+ let result = verify_cert(&cert);
+ assert!(result.is_ok());
+ let peer_id = result.unwrap();
+ assert_eq!(peer_id, PeerID::try_from(key_pair.public()).unwrap());
+ assert_eq!(peer_id.0, key_pair.public().as_bytes());
+ }
+}
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +
use std::str::FromStr;
+
+use bincode::{Decode, Encode};
+use semver::VersionReq;
+
+use crate::{Error, Result};
+
+/// Represents the network version and protocol version used in karyon p2p.
+///
+/// # Example
+///
+/// ```
+/// use karyon_p2p::Version;
+///
+/// let version: Version = "0.2.0, >0.1.0".parse().unwrap();
+///
+/// let version: Version = "0.2.0".parse().unwrap();
+///
+/// ```
+#[derive(Debug, Clone)]
+pub struct Version {
+ pub v: VersionInt,
+ pub req: VersionReq,
+}
+
+impl Version {
+ /// Creates a new Version
+ pub fn new(v: VersionInt, req: VersionReq) -> Self {
+ Self { v, req }
+ }
+}
+
+#[derive(Debug, Decode, Encode, Clone)]
+pub struct VersionInt {
+ major: u64,
+ minor: u64,
+ patch: u64,
+}
+
+impl FromStr for Version {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self> {
+ let v: Vec<&str> = s.split(", ").collect();
+ if v.is_empty() || v.len() > 2 {
+ return Err(Error::ParseError(format!("Invalid version{s}")));
+ }
+
+ let version: VersionInt = v[0].parse()?;
+ let req: VersionReq = if v.len() > 1 { v[1] } else { v[0] }.parse()?;
+
+ Ok(Self { v: version, req })
+ }
+}
+
+impl FromStr for VersionInt {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self> {
+ let v: Vec<&str> = s.split('.').collect();
+ if v.len() < 2 || v.len() > 3 {
+ return Err(Error::ParseError(format!("Invalid version{s}")));
+ }
+
+ let major = v[0].parse::<u64>()?;
+ let minor = v[1].parse::<u64>()?;
+ let patch = v.get(2).unwrap_or(&"0").parse::<u64>()?;
+
+ Ok(Self {
+ major,
+ minor,
+ patch,
+ })
+ }
+}
+
+impl std::fmt::Display for VersionInt {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
+ }
+}
+
+impl From<VersionInt> for semver::Version {
+ fn from(v: VersionInt) -> Self {
+ semver::Version::new(v.major, v.minor, v.patch)
+ }
+}
+
+/// Check if a version satisfies a version request.
+pub fn version_match(version_req: &VersionReq, version: &VersionInt) -> bool {
+ let version: semver::Version = version.clone().into();
+ version_req.matches(&version)
+}
+
fn:
) to \
+ restrict the search to a given item kind.","Accepted kinds are: fn
, mod
, struct
, \
+ enum
, trait
, type
, macro
, \
+ and const
.","Search functions by type signature (e.g., vec -> usize
or \
+ -> vec
or String, enum:Cow -> bool
)","You can look for items with an exact name by putting double quotes around \
+ your request: \"string\"
","Look for functions that accept or return \
+ slices and \
+ arrays by writing \
+ square brackets (e.g., -> [u8]
or [] -> Option
)","Look for items inside another one by searching for a path: vec::Vec
",].map(x=>""+x+"
").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="${value.replaceAll(" ", " ")}
`}else{error[index]=value}});output+=`Allocates memory on the heap and then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
let five = Box::new(5);
new_uninit
)Constructs a new box with uninitialized contents.
\n#![feature(new_uninit)]\n\nlet mut five = Box::<u32>::new_uninit();\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
new_uninit
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n\nlet zero = Box::<u32>::new_zeroed();\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
Constructs a new Pin<Box<T>>
. If T
does not implement Unpin
, then\nx
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin(x)
\ndoes the same as Box::into_pin(Box::new(x))
. Consider using\ninto_pin
if you already have a Box<T>
, or if you want to\nconstruct a (pinned) Box
in a different way than with Box::new
.
allocator_api
)Allocates memory on the heap then places x
into it,\nreturning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nlet five = Box::try_new(5)?;
allocator_api
)Constructs a new box with uninitialized contents on the heap,\nreturning an error if the allocation fails
\n#![feature(allocator_api, new_uninit)]\n\nlet mut five = Box::<u32>::try_new_uninit()?;\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes on the heap
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nlet zero = Box::<u32>::try_new_zeroed()?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Allocates memory in the given allocator then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet five = Box::new_in(5, System);
allocator_api
)Allocates memory in the given allocator then places x
into it,\nreturning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet five = Box::try_new_in(5, System)?;
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator.
\n#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet mut five = Box::<u32, _>::new_uninit_in(System);\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator,\nreturning an error if the allocation fails
\n#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet mut five = Box::<u32, _>::try_new_uninit_in(System)?;\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet zero = Box::<u32, _>::new_zeroed_in(System);\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes in the provided allocator,\nreturning an error if the allocation fails,
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet zero = Box::<u32, _>::try_new_zeroed_in(System)?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Constructs a new Pin<Box<T, A>>
. If T
does not implement Unpin
, then\nx
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin_in(x, alloc)
\ndoes the same as Box::into_pin(Box::new_in(x, alloc))
. Consider using\ninto_pin
if you already have a Box<T, A>
, or if you want to\nconstruct a (pinned) Box
in a different way than with Box::new_in
.
box_into_boxed_slice
)Converts a Box<T>
into a Box<[T]>
This conversion does not allocate on the heap and happens in place.
\nbox_into_inner
)Consumes the Box
, returning the wrapped value.
#![feature(box_into_inner)]\n\nlet c = Box::new(5);\n\nassert_eq!(Box::into_inner(c), 5);
Constructs a box from a raw pointer.
\nAfter calling this function, the raw pointer is owned by the\nresulting Box
. Specifically, the Box
destructor will call\nthe destructor of T
and free the allocated memory. For this\nto be safe, the memory must have been allocated in accordance\nwith the memory layout used by Box
.
This function is unsafe because improper use may lead to\nmemory problems. For example, a double-free may occur if the\nfunction is called twice on the same raw pointer.
\nThe safety conditions are described in the memory layout section.
\nRecreate a Box
which was previously converted to a raw pointer\nusing Box::into_raw
:
let x = Box::new(5);\nlet ptr = Box::into_raw(x);\nlet x = unsafe { Box::from_raw(ptr) };
Manually create a Box
from scratch by using the global allocator:
use std::alloc::{alloc, Layout};\n\nunsafe {\n let ptr = alloc(Layout::new::<i32>()) as *mut i32;\n // In general .write is required to avoid attempting to destruct\n // the (uninitialized) previous contents of `ptr`, though for this\n // simple example `*ptr = 5` would have worked as well.\n ptr.write(5);\n let x = Box::from_raw(ptr);\n}
allocator_api
)Constructs a box from a raw pointer in the given allocator.
\nAfter calling this function, the raw pointer is owned by the\nresulting Box
. Specifically, the Box
destructor will call\nthe destructor of T
and free the allocated memory. For this\nto be safe, the memory must have been allocated in accordance\nwith the memory layout used by Box
.
This function is unsafe because improper use may lead to\nmemory problems. For example, a double-free may occur if the\nfunction is called twice on the same raw pointer.
\nRecreate a Box
which was previously converted to a raw pointer\nusing Box::into_raw_with_allocator
:
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet x = Box::new_in(5, System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nlet x = unsafe { Box::from_raw_in(ptr, alloc) };
Manually create a Box
from scratch by using the system allocator:
#![feature(allocator_api, slice_ptr_get)]\n\nuse std::alloc::{Allocator, Layout, System};\n\nunsafe {\n let ptr = System.allocate(Layout::new::<i32>())?.as_mut_ptr() as *mut i32;\n // In general .write is required to avoid attempting to destruct\n // the (uninitialized) previous contents of `ptr`, though for this\n // simple example `*ptr = 5` would have worked as well.\n ptr.write(5);\n let x = Box::from_raw_in(ptr, System);\n}
Consumes the Box
, returning a wrapped raw pointer.
The pointer will be properly aligned and non-null.
\nAfter calling this function, the caller is responsible for the\nmemory previously managed by the Box
. In particular, the\ncaller should properly destroy T
and release the memory, taking\ninto account the memory layout used by Box
. The easiest way to\ndo this is to convert the raw pointer back into a Box
with the\nBox::from_raw
function, allowing the Box
destructor to perform\nthe cleanup.
Note: this is an associated function, which means that you have\nto call it as Box::into_raw(b)
instead of b.into_raw()
. This\nis so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw
\nfor automatic cleanup:
let x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nlet x = unsafe { Box::from_raw(ptr) };
Manual cleanup by explicitly running the destructor and deallocating\nthe memory:
\n\nuse std::alloc::{dealloc, Layout};\nuse std::ptr;\n\nlet x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nunsafe {\n ptr::drop_in_place(ptr);\n dealloc(ptr as *mut u8, Layout::new::<String>());\n}
Note: This is equivalent to the following:
\n\nlet x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nunsafe {\n drop(Box::from_raw(ptr));\n}
allocator_api
)Consumes the Box
, returning a wrapped raw pointer and the allocator.
The pointer will be properly aligned and non-null.
\nAfter calling this function, the caller is responsible for the\nmemory previously managed by the Box
. In particular, the\ncaller should properly destroy T
and release the memory, taking\ninto account the memory layout used by Box
. The easiest way to\ndo this is to convert the raw pointer back into a Box
with the\nBox::from_raw_in
function, allowing the Box
destructor to perform\nthe cleanup.
Note: this is an associated function, which means that you have\nto call it as Box::into_raw_with_allocator(b)
instead of b.into_raw_with_allocator()
. This\nis so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw_in
\nfor automatic cleanup:
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet x = Box::new_in(String::from(\"Hello\"), System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nlet x = unsafe { Box::from_raw_in(ptr, alloc) };
Manual cleanup by explicitly running the destructor and deallocating\nthe memory:
\n\n#![feature(allocator_api)]\n\nuse std::alloc::{Allocator, Layout, System};\nuse std::ptr::{self, NonNull};\n\nlet x = Box::new_in(String::from(\"Hello\"), System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nunsafe {\n ptr::drop_in_place(ptr);\n let non_null = NonNull::new_unchecked(ptr);\n alloc.deallocate(non_null.cast(), Layout::new::<String>());\n}
allocator_api
)Returns a reference to the underlying allocator.
\nNote: this is an associated function, which means that you have\nto call it as Box::allocator(&b)
instead of b.allocator()
. This\nis so that there is no conflict with a method on the inner type.
Consumes and leaks the Box
, returning a mutable reference,\n&'a mut T
. Note that the type T
must outlive the chosen lifetime\n'a
. If the type has only static references, or none at all, then this\nmay be chosen to be 'static
.
This function is mainly useful for data that lives for the remainder of\nthe program’s life. Dropping the returned reference will cause a memory\nleak. If this is not acceptable, the reference should first be wrapped\nwith the Box::from_raw
function producing a Box
. This Box
can\nthen be dropped which will properly destroy T
and release the\nallocated memory.
Note: this is an associated function, which means that you have\nto call it as Box::leak(b)
instead of b.leak()
. This\nis so that there is no conflict with a method on the inner type.
Simple usage:
\n\nlet x = Box::new(41);\nlet static_ref: &'static mut usize = Box::leak(x);\n*static_ref += 1;\nassert_eq!(*static_ref, 42);
Unsized data:
\n\nlet x = vec![1, 2, 3].into_boxed_slice();\nlet static_ref = Box::leak(x);\nstatic_ref[0] = 4;\nassert_eq!(*static_ref, [4, 2, 3]);
Converts a Box<T>
into a Pin<Box<T>>
. If T
does not implement Unpin
, then\n*boxed
will be pinned in memory and unable to be moved.
This conversion does not allocate on the heap and happens in place.
\nThis is also available via From
.
Constructing and pinning a Box
with Box::into_pin(Box::new(x))
\ncan also be written more concisely using Box::pin(x)
.\nThis into_pin
method is useful if you already have a Box<T>
, or you are\nconstructing a (pinned) Box
in a different way than with Box::new
.
It’s not recommended that crates add an impl like From<Box<T>> for Pin<T>
,\nas it’ll introduce an ambiguity when calling Pin::from
.\nA demonstration of such a poor impl is shown below.
struct Foo; // A type defined in this crate.\nimpl From<Box<()>> for Pin<Foo> {\n fn from(_: Box<()>) -> Pin<Foo> {\n Pin::new(Foo)\n }\n}\n\nlet foo = Box::new(());\nlet bar = Pin::from(foo);
can_vector
)amt
bytes have been consumed from the buffer,\nso they should no longer be returned in calls to read
. Read more0xA
byte) is reached, and append\nthem to the provided String
buffer. Read morebuf_read_has_data_left
)Read
has any data left to be read. Read morebufread_skip_until
)byte
or EOF is reached. Read moreread_buf
)read
, except that it reads into a slice of buffers. Read morecan_vector
)buf
. Read morebuf
. Read morebuf
. Read moreread_buf
)cursor
. Read moreRead
. Read moren
th element of the iterator. Read moreiter_next_chunk
)N
values. Read moreiter_advance_by
)n
elements. Read moreiter_intersperse
)separator
\nbetween adjacent items of the original iterator. Read moren
elements. Read moren
elements, or fewer\nif the underlying iterator ends sooner. Read moreiter_map_windows
)f
for each contiguous window of size N
over\nself
and returns an iterator over the outputs of f
. Like slice::windows()
,\nthe windows during mapping overlap as well. Read moreiter_collect_into
)iter_is_partitioned
)true
precede all those that return false
. Read moreiterator_try_reduce
)try_find
)iter_array_chunks
)N
elements of the iterator at a time. Read moreiter_order_by
)Iterator
with those\nof another with respect to the specified comparison function. Read morePartialOrd
elements of\nthis Iterator
with those of another. The comparison works like short-circuit\nevaluation, returning a result without comparing the remaining elements.\nAs soon as an order can be determined, the evaluation stops and a result is returned. Read moreiter_order_by
)Iterator
with those\nof another with respect to the specified comparison function. Read moreiter_order_by
)Iterator
are lexicographically\nless than those of another. Read moreIterator
are lexicographically\nless or equal to those of another. Read moreIterator
are lexicographically\ngreater than those of another. Read moreIterator
are lexicographically\ngreater than or equal to those of another. Read moreis_sorted
)is_sorted
)u128
into this hasher.usize
into this hasher.i128
into this hasher.isize
into this hasher.hasher_prefixfree_extras
)async_iterator
)async_iterator
)None
if the async iterator is exhausted. Read moreself
and other
) and is used by the <=
\noperator. Read moreReturns a new box with a clone()
of this box’s contents.
let x = Box::new(5);\nlet y = x.clone();\n\n// The value is the same\nassert_eq!(x, y);\n\n// But they are unique objects\nassert_ne!(&*x as *const i32, &*y as *const i32);
Copies source
’s contents into self
without creating a new allocation.
let x = Box::new(5);\nlet mut y = Box::new(10);\nlet yp: *const i32 = &*y;\n\ny.clone_from(&x);\n\n// The value is the same\nassert_eq!(x, y);\n\n// And no allocation occurred\nassert_eq!(yp, &*y);
n
th element from the end of the iterator. Read moreiter_advance_by
)n
elements. Read moreIterator::try_fold()
: it takes\nelements starting from the back of the iterator. Read morecoroutine_trait
)dest
with random data. Read moretrue
if the underlying future should no longer be polled.true
if the stream should no longer be polled.buf
into the object. Read morebufs
into the object using vectored\nIO operations. Read moreSubscriber
will\nenable, or None
, if the subscriber does not implement level-based\nfiltering or chooses not to implement this method. Read moreEvent
] should be recorded. Read moreSubscriber::try_close
insteadself
is the same type as the provided TypeId
, returns an untyped\n*const
pointer to that type. Otherwise, returns None
. Read moreDispatch
]. Read moreAllocates memory on the heap and then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
let five = Box::new(5);
new_uninit
)Constructs a new box with uninitialized contents.
\n#![feature(new_uninit)]\n\nlet mut five = Box::<u32>::new_uninit();\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
new_uninit
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n\nlet zero = Box::<u32>::new_zeroed();\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
Constructs a new Pin<Box<T>>
. If T
does not implement Unpin
, then\nx
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin(x)
\ndoes the same as Box::into_pin(Box::new(x))
. Consider using\ninto_pin
if you already have a Box<T>
, or if you want to\nconstruct a (pinned) Box
in a different way than with Box::new
.
allocator_api
)Allocates memory on the heap then places x
into it,\nreturning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nlet five = Box::try_new(5)?;
allocator_api
)Constructs a new box with uninitialized contents on the heap,\nreturning an error if the allocation fails
\n#![feature(allocator_api, new_uninit)]\n\nlet mut five = Box::<u32>::try_new_uninit()?;\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes on the heap
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nlet zero = Box::<u32>::try_new_zeroed()?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Allocates memory in the given allocator then places x
into it.
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet five = Box::new_in(5, System);
allocator_api
)Allocates memory in the given allocator then places x
into it,\nreturning an error if the allocation fails
This doesn’t actually allocate if T
is zero-sized.
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet five = Box::try_new_in(5, System)?;
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator.
\n#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet mut five = Box::<u32, _>::new_uninit_in(System);\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
allocator_api
)Constructs a new box with uninitialized contents in the provided allocator,\nreturning an error if the allocation fails
\n#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet mut five = Box::<u32, _>::try_new_uninit_in(System)?;\n\nlet five = unsafe {\n // Deferred initialization:\n five.as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet zero = Box::<u32, _>::new_zeroed_in(System);\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Box
with uninitialized contents, with the memory\nbeing filled with 0
bytes in the provided allocator,\nreturning an error if the allocation fails,
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(allocator_api, new_uninit)]\n\nuse std::alloc::System;\n\nlet zero = Box::<u32, _>::try_new_zeroed_in(System)?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Constructs a new Pin<Box<T, A>>
. If T
does not implement Unpin
, then\nx
will be pinned in memory and unable to be moved.
Constructing and pinning of the Box
can also be done in two steps: Box::pin_in(x, alloc)
\ndoes the same as Box::into_pin(Box::new_in(x, alloc))
. Consider using\ninto_pin
if you already have a Box<T, A>
, or if you want to\nconstruct a (pinned) Box
in a different way than with Box::new_in
.
box_into_boxed_slice
)Converts a Box<T>
into a Box<[T]>
This conversion does not allocate on the heap and happens in place.
\nbox_into_inner
)Consumes the Box
, returning the wrapped value.
#![feature(box_into_inner)]\n\nlet c = Box::new(5);\n\nassert_eq!(Box::into_inner(c), 5);
Constructs a box from a raw pointer.
\nAfter calling this function, the raw pointer is owned by the\nresulting Box
. Specifically, the Box
destructor will call\nthe destructor of T
and free the allocated memory. For this\nto be safe, the memory must have been allocated in accordance\nwith the memory layout used by Box
.
This function is unsafe because improper use may lead to\nmemory problems. For example, a double-free may occur if the\nfunction is called twice on the same raw pointer.
\nThe safety conditions are described in the memory layout section.
\nRecreate a Box
which was previously converted to a raw pointer\nusing Box::into_raw
:
let x = Box::new(5);\nlet ptr = Box::into_raw(x);\nlet x = unsafe { Box::from_raw(ptr) };
Manually create a Box
from scratch by using the global allocator:
use std::alloc::{alloc, Layout};\n\nunsafe {\n let ptr = alloc(Layout::new::<i32>()) as *mut i32;\n // In general .write is required to avoid attempting to destruct\n // the (uninitialized) previous contents of `ptr`, though for this\n // simple example `*ptr = 5` would have worked as well.\n ptr.write(5);\n let x = Box::from_raw(ptr);\n}
allocator_api
)Constructs a box from a raw pointer in the given allocator.
\nAfter calling this function, the raw pointer is owned by the\nresulting Box
. Specifically, the Box
destructor will call\nthe destructor of T
and free the allocated memory. For this\nto be safe, the memory must have been allocated in accordance\nwith the memory layout used by Box
.
This function is unsafe because improper use may lead to\nmemory problems. For example, a double-free may occur if the\nfunction is called twice on the same raw pointer.
\nRecreate a Box
which was previously converted to a raw pointer\nusing Box::into_raw_with_allocator
:
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet x = Box::new_in(5, System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nlet x = unsafe { Box::from_raw_in(ptr, alloc) };
Manually create a Box
from scratch by using the system allocator:
#![feature(allocator_api, slice_ptr_get)]\n\nuse std::alloc::{Allocator, Layout, System};\n\nunsafe {\n let ptr = System.allocate(Layout::new::<i32>())?.as_mut_ptr() as *mut i32;\n // In general .write is required to avoid attempting to destruct\n // the (uninitialized) previous contents of `ptr`, though for this\n // simple example `*ptr = 5` would have worked as well.\n ptr.write(5);\n let x = Box::from_raw_in(ptr, System);\n}
Consumes the Box
, returning a wrapped raw pointer.
The pointer will be properly aligned and non-null.
\nAfter calling this function, the caller is responsible for the\nmemory previously managed by the Box
. In particular, the\ncaller should properly destroy T
and release the memory, taking\ninto account the memory layout used by Box
. The easiest way to\ndo this is to convert the raw pointer back into a Box
with the\nBox::from_raw
function, allowing the Box
destructor to perform\nthe cleanup.
Note: this is an associated function, which means that you have\nto call it as Box::into_raw(b)
instead of b.into_raw()
. This\nis so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw
\nfor automatic cleanup:
let x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nlet x = unsafe { Box::from_raw(ptr) };
Manual cleanup by explicitly running the destructor and deallocating\nthe memory:
\n\nuse std::alloc::{dealloc, Layout};\nuse std::ptr;\n\nlet x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nunsafe {\n ptr::drop_in_place(ptr);\n dealloc(ptr as *mut u8, Layout::new::<String>());\n}
Note: This is equivalent to the following:
\n\nlet x = Box::new(String::from(\"Hello\"));\nlet ptr = Box::into_raw(x);\nunsafe {\n drop(Box::from_raw(ptr));\n}
allocator_api
)Consumes the Box
, returning a wrapped raw pointer and the allocator.
The pointer will be properly aligned and non-null.
\nAfter calling this function, the caller is responsible for the\nmemory previously managed by the Box
. In particular, the\ncaller should properly destroy T
and release the memory, taking\ninto account the memory layout used by Box
. The easiest way to\ndo this is to convert the raw pointer back into a Box
with the\nBox::from_raw_in
function, allowing the Box
destructor to perform\nthe cleanup.
Note: this is an associated function, which means that you have\nto call it as Box::into_raw_with_allocator(b)
instead of b.into_raw_with_allocator()
. This\nis so that there is no conflict with a method on the inner type.
Converting the raw pointer back into a Box
with Box::from_raw_in
\nfor automatic cleanup:
#![feature(allocator_api)]\n\nuse std::alloc::System;\n\nlet x = Box::new_in(String::from(\"Hello\"), System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nlet x = unsafe { Box::from_raw_in(ptr, alloc) };
Manual cleanup by explicitly running the destructor and deallocating\nthe memory:
\n\n#![feature(allocator_api)]\n\nuse std::alloc::{Allocator, Layout, System};\nuse std::ptr::{self, NonNull};\n\nlet x = Box::new_in(String::from(\"Hello\"), System);\nlet (ptr, alloc) = Box::into_raw_with_allocator(x);\nunsafe {\n ptr::drop_in_place(ptr);\n let non_null = NonNull::new_unchecked(ptr);\n alloc.deallocate(non_null.cast(), Layout::new::<String>());\n}
allocator_api
)Returns a reference to the underlying allocator.
\nNote: this is an associated function, which means that you have\nto call it as Box::allocator(&b)
instead of b.allocator()
. This\nis so that there is no conflict with a method on the inner type.
Consumes and leaks the Box
, returning a mutable reference,\n&'a mut T
. Note that the type T
must outlive the chosen lifetime\n'a
. If the type has only static references, or none at all, then this\nmay be chosen to be 'static
.
This function is mainly useful for data that lives for the remainder of\nthe program’s life. Dropping the returned reference will cause a memory\nleak. If this is not acceptable, the reference should first be wrapped\nwith the Box::from_raw
function producing a Box
. This Box
can\nthen be dropped which will properly destroy T
and release the\nallocated memory.
Note: this is an associated function, which means that you have\nto call it as Box::leak(b)
instead of b.leak()
. This\nis so that there is no conflict with a method on the inner type.
Simple usage:
\n\nlet x = Box::new(41);\nlet static_ref: &'static mut usize = Box::leak(x);\n*static_ref += 1;\nassert_eq!(*static_ref, 42);
Unsized data:
\n\nlet x = vec![1, 2, 3].into_boxed_slice();\nlet static_ref = Box::leak(x);\nstatic_ref[0] = 4;\nassert_eq!(*static_ref, [4, 2, 3]);
Converts a Box<T>
into a Pin<Box<T>>
. If T
does not implement Unpin
, then\n*boxed
will be pinned in memory and unable to be moved.
This conversion does not allocate on the heap and happens in place.
\nThis is also available via From
.
Constructing and pinning a Box
with Box::into_pin(Box::new(x))
\ncan also be written more concisely using Box::pin(x)
.\nThis into_pin
method is useful if you already have a Box<T>
, or you are\nconstructing a (pinned) Box
in a different way than with Box::new
.
It’s not recommended that crates add an impl like From<Box<T>> for Pin<T>
,\nas it’ll introduce an ambiguity when calling Pin::from
.\nA demonstration of such a poor impl is shown below.
struct Foo; // A type defined in this crate.\nimpl From<Box<()>> for Pin<Foo> {\n fn from(_: Box<()>) -> Pin<Foo> {\n Pin::new(Foo)\n }\n}\n\nlet foo = Box::new(());\nlet bar = Pin::from(foo);
can_vector
)amt
bytes have been consumed from the buffer,\nso they should no longer be returned in calls to read
. Read more0xA
byte) is reached, and append\nthem to the provided String
buffer. Read morebuf_read_has_data_left
)Read
has any data left to be read. Read morebufread_skip_until
)byte
or EOF is reached. Read moreread_buf
)read
, except that it reads into a slice of buffers. Read morecan_vector
)buf
. Read morebuf
. Read morebuf
. Read moreread_buf
)cursor
. Read moreRead
. Read moren
th element of the iterator. Read moreiter_next_chunk
)N
values. Read moreiter_advance_by
)n
elements. Read moreiter_intersperse
)separator
\nbetween adjacent items of the original iterator. Read moren
elements. Read moren
elements, or fewer\nif the underlying iterator ends sooner. Read moreiter_map_windows
)f
for each contiguous window of size N
over\nself
and returns an iterator over the outputs of f
. Like slice::windows()
,\nthe windows during mapping overlap as well. Read moreiter_collect_into
)iter_is_partitioned
)true
precede all those that return false
. Read moreiterator_try_reduce
)try_find
)iter_array_chunks
)N
elements of the iterator at a time. Read moreiter_order_by
)Iterator
with those\nof another with respect to the specified comparison function. Read morePartialOrd
elements of\nthis Iterator
with those of another. The comparison works like short-circuit\nevaluation, returning a result without comparing the remaining elements.\nAs soon as an order can be determined, the evaluation stops and a result is returned. Read moreiter_order_by
)Iterator
with those\nof another with respect to the specified comparison function. Read moreiter_order_by
)Iterator
are lexicographically\nless than those of another. Read moreIterator
are lexicographically\nless or equal to those of another. Read moreIterator
are lexicographically\ngreater than those of another. Read moreIterator
are lexicographically\ngreater than or equal to those of another. Read moreis_sorted
)is_sorted
)u128
into this hasher.usize
into this hasher.i128
into this hasher.isize
into this hasher.hasher_prefixfree_extras
)async_iterator
)async_iterator
)None
if the async iterator is exhausted. Read moreself
and other
) and is used by the <=
\noperator. Read moreReturns a new box with a clone()
of this box’s contents.
let x = Box::new(5);\nlet y = x.clone();\n\n// The value is the same\nassert_eq!(x, y);\n\n// But they are unique objects\nassert_ne!(&*x as *const i32, &*y as *const i32);
Copies source
’s contents into self
without creating a new allocation.
let x = Box::new(5);\nlet mut y = Box::new(10);\nlet yp: *const i32 = &*y;\n\ny.clone_from(&x);\n\n// The value is the same\nassert_eq!(x, y);\n\n// And no allocation occurred\nassert_eq!(yp, &*y);
n
th element from the end of the iterator. Read moreiter_advance_by
)n
elements. Read moreIterator::try_fold()
: it takes\nelements starting from the back of the iterator. Read morecoroutine_trait
)true
if the underlying future should no longer be polled.true
if the stream should no longer be polled.buf
into the object. Read morebufs
into the object using vectored\nIO operations. Read moreSubscriber
will\nenable, or None
, if the subscriber does not implement level-based\nfiltering or chooses not to implement this method. Read moreEvent
] should be recorded. Read moreSubscriber::try_close
insteadself
is the same type as the provided TypeId
, returns an untyped\n*const
pointer to that type. Otherwise, returns None
. Read moreDispatch
]. Read moredest
with random data. Read moreCreates a new empty String
.
Given that the String
is empty, this will not allocate any initial\nbuffer. While that means that this initial operation is very\ninexpensive, it may cause excessive allocation later when you add\ndata. If you have an idea of how much data the String
will hold,\nconsider the with_capacity
method to prevent excessive\nre-allocation.
let s = String::new();
Creates a new empty String
with at least the specified capacity.
String
s have an internal buffer to hold their data. The capacity is\nthe length of that buffer, and can be queried with the capacity
\nmethod. This method creates an empty String
, but one with an initial\nbuffer that can hold at least capacity
bytes. This is useful when you\nmay be appending a bunch of data to the String
, reducing the number of\nreallocations it needs to do.
If the given capacity is 0
, no allocation will occur, and this method\nis identical to the new
method.
let mut s = String::with_capacity(10);\n\n// The String contains no chars, even though it has capacity for more\nassert_eq!(s.len(), 0);\n\n// These are all done without reallocating...\nlet cap = s.capacity();\nfor _ in 0..10 {\n s.push('a');\n}\n\nassert_eq!(s.capacity(), cap);\n\n// ...but this may make the string reallocate\ns.push('a');
Converts a vector of bytes to a String
.
A string (String
) is made of bytes (u8
), and a vector of bytes\n(Vec<u8>
) is made of bytes, so this function converts between the\ntwo. Not all byte slices are valid String
s, however: String
\nrequires that it is valid UTF-8. from_utf8()
checks to ensure that\nthe bytes are valid UTF-8, and then does the conversion.
If you are sure that the byte slice is valid UTF-8, and you don’t want\nto incur the overhead of the validity check, there is an unsafe version\nof this function, from_utf8_unchecked
, which has the same behavior\nbut skips the check.
This method will take care to not copy the vector, for efficiency’s\nsake.
\nIf you need a &str
instead of a String
, consider\nstr::from_utf8
.
The inverse of this method is into_bytes
.
Returns Err
if the slice is not UTF-8 with a description as to why the\nprovided bytes are not UTF-8. The vector you moved in is also included.
Basic usage:
\n\n// some bytes, in a vector\nlet sparkle_heart = vec![240, 159, 146, 150];\n\n// We know these bytes are valid, so we'll use `unwrap()`.\nlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\nassert_eq!(\"💖\", sparkle_heart);
Incorrect bytes:
\n\n// some invalid bytes, in a vector\nlet sparkle_heart = vec![0, 159, 146, 150];\n\nassert!(String::from_utf8(sparkle_heart).is_err());
See the docs for FromUtf8Error
for more details on what you can do\nwith this error.
Converts a slice of bytes to a string, including invalid characters.
\nStrings are made of bytes (u8
), and a slice of bytes\n(&[u8]
) is made of bytes, so this function converts\nbetween the two. Not all byte slices are valid strings, however: strings\nare required to be valid UTF-8. During this conversion,\nfrom_utf8_lossy()
will replace any invalid UTF-8 sequences with\nU+FFFD REPLACEMENT CHARACTER
, which looks like this: �
If you are sure that the byte slice is valid UTF-8, and you don’t want\nto incur the overhead of the conversion, there is an unsafe version\nof this function, from_utf8_unchecked
, which has the same behavior\nbut skips the checks.
This function returns a Cow<'a, str>
. If our byte slice is invalid\nUTF-8, then we need to insert the replacement characters, which will\nchange the size of the string, and hence, require a String
. But if\nit’s already valid UTF-8, we don’t need a new allocation. This return\ntype allows us to handle both cases.
Basic usage:
\n\n// some bytes, in a vector\nlet sparkle_heart = vec![240, 159, 146, 150];\n\nlet sparkle_heart = String::from_utf8_lossy(&sparkle_heart);\n\nassert_eq!(\"💖\", sparkle_heart);
Incorrect bytes:
\n\n// some invalid bytes\nlet input = b\"Hello \\xF0\\x90\\x80World\";\nlet output = String::from_utf8_lossy(input);\n\nassert_eq!(\"Hello �World\", output);
Decode a UTF-16–encoded vector v
into a String
, returning Err
\nif v
contains any invalid data.
// 𝄞music\nlet v = &[0xD834, 0xDD1E, 0x006d, 0x0075,\n 0x0073, 0x0069, 0x0063];\nassert_eq!(String::from(\"𝄞music\"),\n String::from_utf16(v).unwrap());\n\n// 𝄞mu<invalid>ic\nlet v = &[0xD834, 0xDD1E, 0x006d, 0x0075,\n 0xD800, 0x0069, 0x0063];\nassert!(String::from_utf16(v).is_err());
Decode a UTF-16–encoded slice v
into a String
, replacing\ninvalid data with the replacement character (U+FFFD
).
Unlike from_utf8_lossy
which returns a Cow<'a, str>
,\nfrom_utf16_lossy
returns a String
since the UTF-16 to UTF-8\nconversion requires a memory allocation.
// 𝄞mus<invalid>ic<invalid>\nlet v = &[0xD834, 0xDD1E, 0x006d, 0x0075,\n 0x0073, 0xDD1E, 0x0069, 0x0063,\n 0xD834];\n\nassert_eq!(String::from(\"𝄞mus\\u{FFFD}ic\\u{FFFD}\"),\n String::from_utf16_lossy(v));
str_from_utf16_endian
)Decode a UTF-16LE–encoded vector v
into a String
, returning Err
\nif v
contains any invalid data.
Basic usage:
\n\n#![feature(str_from_utf16_endian)]\n// 𝄞music\nlet v = &[0x34, 0xD8, 0x1E, 0xDD, 0x6d, 0x00, 0x75, 0x00,\n 0x73, 0x00, 0x69, 0x00, 0x63, 0x00];\nassert_eq!(String::from(\"𝄞music\"),\n String::from_utf16le(v).unwrap());\n\n// 𝄞mu<invalid>ic\nlet v = &[0x34, 0xD8, 0x1E, 0xDD, 0x6d, 0x00, 0x75, 0x00,\n 0x00, 0xD8, 0x69, 0x00, 0x63, 0x00];\nassert!(String::from_utf16le(v).is_err());
str_from_utf16_endian
)Decode a UTF-16LE–encoded slice v
into a String
, replacing\ninvalid data with the replacement character (U+FFFD
).
Unlike from_utf8_lossy
which returns a Cow<'a, str>
,\nfrom_utf16le_lossy
returns a String
since the UTF-16 to UTF-8\nconversion requires a memory allocation.
Basic usage:
\n\n#![feature(str_from_utf16_endian)]\n// 𝄞mus<invalid>ic<invalid>\nlet v = &[0x34, 0xD8, 0x1E, 0xDD, 0x6d, 0x00, 0x75, 0x00,\n 0x73, 0x00, 0x1E, 0xDD, 0x69, 0x00, 0x63, 0x00,\n 0x34, 0xD8];\n\nassert_eq!(String::from(\"𝄞mus\\u{FFFD}ic\\u{FFFD}\"),\n String::from_utf16le_lossy(v));
str_from_utf16_endian
)Decode a UTF-16BE–encoded vector v
into a String
, returning Err
\nif v
contains any invalid data.
Basic usage:
\n\n#![feature(str_from_utf16_endian)]\n// 𝄞music\nlet v = &[0xD8, 0x34, 0xDD, 0x1E, 0x00, 0x6d, 0x00, 0x75,\n 0x00, 0x73, 0x00, 0x69, 0x00, 0x63];\nassert_eq!(String::from(\"𝄞music\"),\n String::from_utf16be(v).unwrap());\n\n// 𝄞mu<invalid>ic\nlet v = &[0xD8, 0x34, 0xDD, 0x1E, 0x00, 0x6d, 0x00, 0x75,\n 0xD8, 0x00, 0x00, 0x69, 0x00, 0x63];\nassert!(String::from_utf16be(v).is_err());
str_from_utf16_endian
)Decode a UTF-16BE–encoded slice v
into a String
, replacing\ninvalid data with the replacement character (U+FFFD
).
Unlike from_utf8_lossy
which returns a Cow<'a, str>
,\nfrom_utf16le_lossy
returns a String
since the UTF-16 to UTF-8\nconversion requires a memory allocation.
Basic usage:
\n\n#![feature(str_from_utf16_endian)]\n// 𝄞mus<invalid>ic<invalid>\nlet v = &[0xD8, 0x34, 0xDD, 0x1E, 0x00, 0x6d, 0x00, 0x75,\n 0x00, 0x73, 0xDD, 0x1E, 0x00, 0x69, 0x00, 0x63,\n 0xD8, 0x34];\n\nassert_eq!(String::from(\"𝄞mus\\u{FFFD}ic\\u{FFFD}\"),\n String::from_utf16be_lossy(v));
vec_into_raw_parts
)Decomposes a String
into its raw components.
Returns the raw pointer to the underlying data, the length of\nthe string (in bytes), and the allocated capacity of the data\n(in bytes). These are the same arguments in the same order as\nthe arguments to from_raw_parts
.
After calling this function, the caller is responsible for the\nmemory previously managed by the String
. The only way to do\nthis is to convert the raw pointer, length, and capacity back\ninto a String
with the from_raw_parts
function, allowing\nthe destructor to perform the cleanup.
#![feature(vec_into_raw_parts)]\nlet s = String::from(\"hello\");\n\nlet (ptr, len, cap) = s.into_raw_parts();\n\nlet rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) };\nassert_eq!(rebuilt, \"hello\");
Creates a new String
from a length, capacity, and pointer.
This is highly unsafe, due to the number of invariants that aren’t\nchecked:
\nbuf
needs to have been previously allocated by the\nsame allocator the standard library uses, with a required alignment of exactly 1.length
needs to be less than or equal to capacity
.capacity
needs to be the correct value.length
bytes at buf
need to be valid UTF-8.Violating these may cause problems like corrupting the allocator’s\ninternal data structures. For example, it is normally not safe to\nbuild a String
from a pointer to a C char
array containing UTF-8\nunless you are certain that array was originally allocated by the\nRust standard library’s allocator.
The ownership of buf
is effectively transferred to the\nString
which may then deallocate, reallocate or change the\ncontents of memory pointed to by the pointer at will. Ensure\nthat nothing else uses the pointer after calling this\nfunction.
use std::mem;\n\nunsafe {\n let s = String::from(\"hello\");\n\n // Prevent automatically dropping the String's data\n let mut s = mem::ManuallyDrop::new(s);\n\n let ptr = s.as_mut_ptr();\n let len = s.len();\n let capacity = s.capacity();\n\n let s = String::from_raw_parts(ptr, len, capacity);\n\n assert_eq!(String::from(\"hello\"), s);\n}
Converts a vector of bytes to a String
without checking that the\nstring contains valid UTF-8.
See the safe version, from_utf8
, for more details.
This function is unsafe because it does not check that the bytes passed\nto it are valid UTF-8. If this constraint is violated, it may cause\nmemory unsafety issues with future users of the String
, as the rest of\nthe standard library assumes that String
s are valid UTF-8.
// some bytes, in a vector\nlet sparkle_heart = vec![240, 159, 146, 150];\n\nlet sparkle_heart = unsafe {\n String::from_utf8_unchecked(sparkle_heart)\n};\n\nassert_eq!(\"💖\", sparkle_heart);
Converts a String
into a byte vector.
This consumes the String
, so we do not need to copy its contents.
let s = String::from(\"hello\");\nlet bytes = s.into_bytes();\n\nassert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]);
Extracts a string slice containing the entire String
.
let s = String::from(\"foo\");\n\nassert_eq!(\"foo\", s.as_str());
Converts a String
into a mutable string slice.
let mut s = String::from(\"foobar\");\nlet s_mut_str = s.as_mut_str();\n\ns_mut_str.make_ascii_uppercase();\n\nassert_eq!(\"FOOBAR\", s_mut_str);
Appends a given string slice onto the end of this String
.
let mut s = String::from(\"foo\");\n\ns.push_str(\"bar\");\n\nassert_eq!(\"foobar\", s);
string_extend_from_within
)Copies elements from src
range to the end of the string.
Panics if the starting point or end point do not lie on a char
\nboundary, or if they’re out of bounds.
#![feature(string_extend_from_within)]\nlet mut string = String::from(\"abcde\");\n\nstring.extend_from_within(2..);\nassert_eq!(string, \"abcdecde\");\n\nstring.extend_from_within(..2);\nassert_eq!(string, \"abcdecdeab\");\n\nstring.extend_from_within(4..8);\nassert_eq!(string, \"abcdecdeabecde\");
Returns this String
’s capacity, in bytes.
let s = String::with_capacity(10);\n\nassert!(s.capacity() >= 10);
Reserves capacity for at least additional
bytes more than the\ncurrent length. The allocator may reserve more space to speculatively\navoid frequent allocations. After calling reserve
,\ncapacity will be greater than or equal to self.len() + additional
.\nDoes nothing if capacity is already sufficient.
Panics if the new capacity overflows usize
.
Basic usage:
\n\nlet mut s = String::new();\n\ns.reserve(10);\n\nassert!(s.capacity() >= 10);
This might not actually increase the capacity:
\n\nlet mut s = String::with_capacity(10);\ns.push('a');\ns.push('b');\n\n// s now has a length of 2 and a capacity of at least 10\nlet capacity = s.capacity();\nassert_eq!(2, s.len());\nassert!(capacity >= 10);\n\n// Since we already have at least an extra 8 capacity, calling this...\ns.reserve(8);\n\n// ... doesn't actually increase.\nassert_eq!(capacity, s.capacity());
Reserves the minimum capacity for at least additional
bytes more than\nthe current length. Unlike reserve
, this will not\ndeliberately over-allocate to speculatively avoid frequent allocations.\nAfter calling reserve_exact
, capacity will be greater than or equal to\nself.len() + additional
. Does nothing if the capacity is already\nsufficient.
Panics if the new capacity overflows usize
.
Basic usage:
\n\nlet mut s = String::new();\n\ns.reserve_exact(10);\n\nassert!(s.capacity() >= 10);
This might not actually increase the capacity:
\n\nlet mut s = String::with_capacity(10);\ns.push('a');\ns.push('b');\n\n// s now has a length of 2 and a capacity of at least 10\nlet capacity = s.capacity();\nassert_eq!(2, s.len());\nassert!(capacity >= 10);\n\n// Since we already have at least an extra 8 capacity, calling this...\ns.reserve_exact(8);\n\n// ... doesn't actually increase.\nassert_eq!(capacity, s.capacity());
Tries to reserve capacity for at least additional
bytes more than the\ncurrent length. The allocator may reserve more space to speculatively\navoid frequent allocations. After calling try_reserve
, capacity will be\ngreater than or equal to self.len() + additional
if it returns\nOk(())
. Does nothing if capacity is already sufficient. This method\npreserves the contents even if an error occurs.
If the capacity overflows, or the allocator reports a failure, then an error\nis returned.
\nuse std::collections::TryReserveError;\n\nfn process_data(data: &str) -> Result<String, TryReserveError> {\n let mut output = String::new();\n\n // Pre-reserve the memory, exiting if we can't\n output.try_reserve(data.len())?;\n\n // Now we know this can't OOM in the middle of our complex work\n output.push_str(data);\n\n Ok(output)\n}
Tries to reserve the minimum capacity for at least additional
bytes\nmore than the current length. Unlike try_reserve
, this will not\ndeliberately over-allocate to speculatively avoid frequent allocations.\nAfter calling try_reserve_exact
, capacity will be greater than or\nequal to self.len() + additional
if it returns Ok(())
.\nDoes nothing if the capacity is already sufficient.
Note that the allocator may give the collection more space than it\nrequests. Therefore, capacity can not be relied upon to be precisely\nminimal. Prefer try_reserve
if future insertions are expected.
If the capacity overflows, or the allocator reports a failure, then an error\nis returned.
\nuse std::collections::TryReserveError;\n\nfn process_data(data: &str) -> Result<String, TryReserveError> {\n let mut output = String::new();\n\n // Pre-reserve the memory, exiting if we can't\n output.try_reserve_exact(data.len())?;\n\n // Now we know this can't OOM in the middle of our complex work\n output.push_str(data);\n\n Ok(output)\n}
Shrinks the capacity of this String
to match its length.
let mut s = String::from(\"foo\");\n\ns.reserve(100);\nassert!(s.capacity() >= 100);\n\ns.shrink_to_fit();\nassert_eq!(3, s.capacity());
Shrinks the capacity of this String
with a lower bound.
The capacity will remain at least as large as both the length\nand the supplied value.
\nIf the current capacity is less than the lower limit, this is a no-op.
\nlet mut s = String::from(\"foo\");\n\ns.reserve(100);\nassert!(s.capacity() >= 100);\n\ns.shrink_to(10);\nassert!(s.capacity() >= 10);\ns.shrink_to(0);\nassert!(s.capacity() >= 3);
Shortens this String
to the specified length.
If new_len
is greater than the string’s current length, this has no\neffect.
Note that this method has no effect on the allocated capacity\nof the string
\nPanics if new_len
does not lie on a char
boundary.
let mut s = String::from(\"hello\");\n\ns.truncate(2);\n\nassert_eq!(\"he\", s);
Removes a char
from this String
at a byte position and returns it.
This is an O(n) operation, as it requires copying every element in the\nbuffer.
\nPanics if idx
is larger than or equal to the String
’s length,\nor if it does not lie on a char
boundary.
let mut s = String::from(\"abç\");\n\nassert_eq!(s.remove(0), 'a');\nassert_eq!(s.remove(1), 'ç');\nassert_eq!(s.remove(0), 'b');
string_remove_matches
)Remove all matches of pattern pat
in the String
.
#![feature(string_remove_matches)]\nlet mut s = String::from(\"Trees are not green, the sky is not blue.\");\ns.remove_matches(\"not \");\nassert_eq!(\"Trees are green, the sky is blue.\", s);
Matches will be detected and removed iteratively, so in cases where\npatterns overlap, only the first pattern will be removed:
\n\n#![feature(string_remove_matches)]\nlet mut s = String::from(\"banana\");\ns.remove_matches(\"ana\");\nassert_eq!(\"bna\", s);
Retains only the characters specified by the predicate.
\nIn other words, remove all characters c
such that f(c)
returns false
.\nThis method operates in place, visiting each character exactly once in the\noriginal order, and preserves the order of the retained characters.
let mut s = String::from(\"f_o_ob_ar\");\n\ns.retain(|c| c != '_');\n\nassert_eq!(s, \"foobar\");
Because the elements are visited exactly once in the original order,\nexternal state may be used to decide which elements to keep.
\n\nlet mut s = String::from(\"abcde\");\nlet keep = [false, true, true, false, true];\nlet mut iter = keep.iter();\ns.retain(|_| *iter.next().unwrap());\nassert_eq!(s, \"bce\");
Inserts a character into this String
at a byte position.
This is an O(n) operation as it requires copying every element in the\nbuffer.
\nPanics if idx
is larger than the String
’s length, or if it does not\nlie on a char
boundary.
let mut s = String::with_capacity(3);\n\ns.insert(0, 'f');\ns.insert(1, 'o');\ns.insert(2, 'o');\n\nassert_eq!(\"foo\", s);
Inserts a string slice into this String
at a byte position.
This is an O(n) operation as it requires copying every element in the\nbuffer.
\nPanics if idx
is larger than the String
’s length, or if it does not\nlie on a char
boundary.
let mut s = String::from(\"bar\");\n\ns.insert_str(0, \"foo\");\n\nassert_eq!(\"foobar\", s);
Returns a mutable reference to the contents of this String
.
This function is unsafe because the returned &mut Vec
allows writing\nbytes which are not valid UTF-8. If this constraint is violated, using\nthe original String
after dropping the &mut Vec
may violate memory\nsafety, as the rest of the standard library assumes that String
s are\nvalid UTF-8.
let mut s = String::from(\"hello\");\n\nunsafe {\n let vec = s.as_mut_vec();\n assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]);\n\n vec.reverse();\n}\nassert_eq!(s, \"olleh\");
Returns the length of this String
, in bytes, not char
s or\ngraphemes. In other words, it might not be what a human considers the\nlength of the string.
let a = String::from(\"foo\");\nassert_eq!(a.len(), 3);\n\nlet fancy_f = String::from(\"ƒoo\");\nassert_eq!(fancy_f.len(), 4);\nassert_eq!(fancy_f.chars().count(), 3);
Returns true
if this String
has a length of zero, and false
otherwise.
let mut v = String::new();\nassert!(v.is_empty());\n\nv.push('a');\nassert!(!v.is_empty());
Splits the string into two at the given byte index.
\nReturns a newly allocated String
. self
contains bytes [0, at)
, and\nthe returned String
contains bytes [at, len)
. at
must be on the\nboundary of a UTF-8 code point.
Note that the capacity of self
does not change.
Panics if at
is not on a UTF-8
code point boundary, or if it is beyond the last\ncode point of the string.
let mut hello = String::from(\"Hello, World!\");\nlet world = hello.split_off(7);\nassert_eq!(hello, \"Hello, \");\nassert_eq!(world, \"World!\");
Truncates this String
, removing all contents.
While this means the String
will have a length of zero, it does not\ntouch its capacity.
let mut s = String::from(\"foo\");\n\ns.clear();\n\nassert!(s.is_empty());\nassert_eq!(0, s.len());\nassert_eq!(3, s.capacity());
Removes the specified range from the string in bulk, returning all\nremoved characters as an iterator.
\nThe returned iterator keeps a mutable borrow on the string to optimize\nits implementation.
\nPanics if the starting point or end point do not lie on a char
\nboundary, or if they’re out of bounds.
If the returned iterator goes out of scope without being dropped (due to\ncore::mem::forget
, for example), the string may still contain a copy\nof any drained characters, or may have lost characters arbitrarily,\nincluding characters outside the range.
let mut s = String::from(\"α is alpha, β is beta\");\nlet beta_offset = s.find('β').unwrap_or(s.len());\n\n// Remove the range up until the β from the string\nlet t: String = s.drain(..beta_offset).collect();\nassert_eq!(t, \"α is alpha, \");\nassert_eq!(s, \"β is beta\");\n\n// A full range clears the string, like `clear()` does\ns.drain(..);\nassert_eq!(s, \"\");
Removes the specified range in the string,\nand replaces it with the given string.\nThe given string doesn’t need to be the same length as the range.
\nPanics if the starting point or end point do not lie on a char
\nboundary, or if they’re out of bounds.
let mut s = String::from(\"α is alpha, β is beta\");\nlet beta_offset = s.find('β').unwrap_or(s.len());\n\n// Replace the range up until the β from the string\ns.replace_range(..beta_offset, \"Α is capital alpha; \");\nassert_eq!(s, \"Α is capital alpha; β is beta\");
Consumes and leaks the String
, returning a mutable reference to the contents,\n&'a mut str
.
The caller has free choice over the returned lifetime, including 'static
. Indeed,\nthis function is ideally used for data that lives for the remainder of the program’s life,\nas dropping the returned reference will cause a memory leak.
It does not reallocate or shrink the String
,\nso the leaked allocation may include unused capacity that is not part\nof the returned slice. If you don’t want that, call into_boxed_str
,\nand then Box::leak
.
let x = String::from(\"bucket\");\nlet static_ref: &'static mut str = x.leak();\nassert_eq!(static_ref, \"bucket\");
SocketAddr
s. Read moreself
and other
) and is used by the <=
\noperator. Read moreConverts a clone-on-write string to an owned\ninstance of String
.
This extracts the owned string,\nclones the string if it is not already owned.
\n// If the string is not owned...\nlet cow: Cow<'_, str> = Cow::Borrowed(\"eggplant\");\n// It will allocate on the heap and copy the string.\nlet owned: String = String::from(cow);\nassert_eq!(&owned[..], \"eggplant\");
Implements the +=
operator for appending to a String
.
This has the same behavior as the push_str
method.
+=
operation. Read moreextend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)extend_one
)Implements the +
operator for concatenating two strings.
This consumes the String
on the left-hand side and re-uses its buffer (growing it if\nnecessary). This is done to avoid allocating a new String
and copying the entire contents on\nevery operation, which would lead to O(n^2) running time when building an n-byte string by\nrepeated concatenation.
The string on the right-hand side is only borrowed; its contents are copied into the returned\nString
.
Concatenating two String
s takes the first by value and borrows the second:
let a = String::from(\"hello\");\nlet b = String::from(\" world\");\nlet c = a + &b;\n// `a` is moved and can no longer be used here.
If you want to keep using the first String
, you can clone it and append to the clone instead:
let a = String::from(\"hello\");\nlet b = String::from(\" world\");\nlet c = a.clone() + &b;\n// `a` is still valid here.
Concatenating &str
slices can be done by converting the first to a String
:
let a = \"hello\";\nlet b = \" world\";\nlet c = a.to_string() + b;
Cow<'_, str>
.CStr
.String conversion.
\nVec<u8>
.to_vec
, but uses provided values without changes.\nThis can generate an invalid encoding for a DER object.to_der
, but uses provided values without changes.\nThis can generate an invalid encoding for a DER object.Pushes the str onto the end of the String
\nConstructs a new Arc<T>
while giving you a Weak<T>
to the allocation,\nto allow you to construct a T
which holds a weak pointer to itself.
Generally, a structure circularly referencing itself, either directly or\nindirectly, should not hold a strong reference to itself to prevent a memory leak.\nUsing this function, you get access to the weak pointer during the\ninitialization of T
, before the Arc<T>
is created, such that you can\nclone and store it inside the T
.
new_cyclic
first allocates the managed allocation for the Arc<T>
,\nthen calls your closure, giving it a Weak<T>
to this allocation,\nand only afterwards completes the construction of the Arc<T>
by placing\nthe T
returned from your closure into the allocation.
Since the new Arc<T>
is not fully-constructed until Arc<T>::new_cyclic
\nreturns, calling upgrade
on the weak reference inside your closure will\nfail and result in a None
value.
If data_fn
panics, the panic is propagated to the caller, and the\ntemporary Weak<T>
is dropped normally.
use std::sync::{Arc, Weak};\n\nstruct Gadget {\n me: Weak<Gadget>,\n}\n\nimpl Gadget {\n /// Construct a reference counted Gadget.\n fn new() -> Arc<Self> {\n // `me` is a `Weak<Gadget>` pointing at the new allocation of the\n // `Arc` we're constructing.\n Arc::new_cyclic(|me| {\n // Create the actual struct here.\n Gadget { me: me.clone() }\n })\n }\n\n /// Return a reference counted pointer to Self.\n fn me(&self) -> Arc<Self> {\n self.me.upgrade().unwrap()\n }\n}
new_uninit
)Constructs a new Arc
with uninitialized contents.
#![feature(new_uninit)]\n#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet mut five = Arc::<u32>::new_uninit();\n\n// Deferred initialization:\nArc::get_mut(&mut five).unwrap().write(5);\n\nlet five = unsafe { five.assume_init() };\n\nassert_eq!(*five, 5)
new_uninit
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n\nuse std::sync::Arc;\n\nlet zero = Arc::<u32>::new_zeroed();\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
Constructs a new Pin<Arc<T>>
. If T
does not implement Unpin
, then\ndata
will be pinned in memory and unable to be moved.
allocator_api
)Constructs a new Pin<Arc<T>>
, return an error if allocation fails.
allocator_api
)Constructs a new Arc<T>
, returning an error if allocation fails.
#![feature(allocator_api)]\nuse std::sync::Arc;\n\nlet five = Arc::try_new(5)?;
allocator_api
)Constructs a new Arc
with uninitialized contents, returning an error\nif allocation fails.
#![feature(new_uninit, allocator_api)]\n#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet mut five = Arc::<u32>::try_new_uninit()?;\n\n// Deferred initialization:\nArc::get_mut(&mut five).unwrap().write(5);\n\nlet five = unsafe { five.assume_init() };\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, returning an error if allocation fails.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit, allocator_api)]\n\nuse std::sync::Arc;\n\nlet zero = Arc::<u32>::try_new_zeroed()?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Returns a reference to the underlying allocator.
\nNote: this is an associated function, which means that you have\nto call it as Arc::allocator(&a)
instead of a.allocator()
. This\nis so that there is no conflict with a method on the inner type.
allocator_api
)Constructs a new Arc<T>
in the provided allocator.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::new_in(5, System);
allocator_api
)Constructs a new Arc
with uninitialized contents in the provided allocator.
#![feature(new_uninit)]\n#![feature(get_mut_unchecked)]\n#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet mut five = Arc::<u32, _>::new_uninit_in(System);\n\nlet five = unsafe {\n // Deferred initialization:\n Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
allocator_api
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet zero = Arc::<u32, _>::new_zeroed_in(System);\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Pin<Arc<T, A>>
in the provided allocator. If T
does not implement Unpin
,\nthen data
will be pinned in memory and unable to be moved.
allocator_api
)Constructs a new Pin<Arc<T, A>>
in the provided allocator, return an error if allocation\nfails.
allocator_api
)Constructs a new Arc<T, A>
in the provided allocator, returning an error if allocation fails.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::try_new_in(5, System)?;
allocator_api
)Constructs a new Arc
with uninitialized contents, in the provided allocator, returning an\nerror if allocation fails.
#![feature(new_uninit, allocator_api)]\n#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet mut five = Arc::<u32, _>::try_new_uninit_in(System)?;\n\nlet five = unsafe {\n // Deferred initialization:\n Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, in the provided allocator, returning an error if allocation\nfails.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit, allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet zero = Arc::<u32, _>::try_new_zeroed_in(System)?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
Returns the inner value, if the Arc
has exactly one strong reference.
Otherwise, an Err
is returned with the same Arc
that was\npassed in.
This will succeed even if there are outstanding weak references.
\nIt is strongly recommended to use Arc::into_inner
instead if you don’t\nwant to keep the Arc
in the Err
case.\nImmediately dropping the Err
payload, like in the expression\nArc::try_unwrap(this).ok()
, can still cause the strong count to\ndrop to zero and the inner value of the Arc
to be dropped:\nFor instance if two threads each execute this expression in parallel, then\nthere is a race condition. The threads could first both check whether they\nhave the last clone of their Arc
via Arc::try_unwrap
, and then\nboth drop their Arc
in the call to ok
,\ntaking the strong count from two down to zero.
use std::sync::Arc;\n\nlet x = Arc::new(3);\nassert_eq!(Arc::try_unwrap(x), Ok(3));\n\nlet x = Arc::new(4);\nlet _y = Arc::clone(&x);\nassert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4);
Returns the inner value, if the Arc
has exactly one strong reference.
Otherwise, None
is returned and the Arc
is dropped.
This will succeed even if there are outstanding weak references.
\nIf Arc::into_inner
is called on every clone of this Arc
,\nit is guaranteed that exactly one of the calls returns the inner value.\nThis means in particular that the inner value is not dropped.
The similar expression Arc::try_unwrap(this).ok()
does not\noffer such a guarantee. See the last example below\nand the documentation of Arc::try_unwrap
.
Minimal example demonstrating the guarantee that Arc::into_inner
gives.
use std::sync::Arc;\n\nlet x = Arc::new(3);\nlet y = Arc::clone(&x);\n\n// Two threads calling `Arc::into_inner` on both clones of an `Arc`:\nlet x_thread = std::thread::spawn(|| Arc::into_inner(x));\nlet y_thread = std::thread::spawn(|| Arc::into_inner(y));\n\nlet x_inner_value = x_thread.join().unwrap();\nlet y_inner_value = y_thread.join().unwrap();\n\n// One of the threads is guaranteed to receive the inner value:\nassert!(matches!(\n (x_inner_value, y_inner_value),\n (None, Some(3)) | (Some(3), None)\n));\n// The result could also be `(None, None)` if the threads called\n// `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.
A more practical example demonstrating the need for Arc::into_inner
:
use std::sync::Arc;\n\n// Definition of a simple singly linked list using `Arc`:\n#[derive(Clone)]\nstruct LinkedList<T>(Option<Arc<Node<T>>>);\nstruct Node<T>(T, Option<Arc<Node<T>>>);\n\n// Dropping a long `LinkedList<T>` relying on the destructor of `Arc`\n// can cause a stack overflow. To prevent this, we can provide a\n// manual `Drop` implementation that does the destruction in a loop:\nimpl<T> Drop for LinkedList<T> {\n fn drop(&mut self) {\n let mut link = self.0.take();\n while let Some(arc_node) = link.take() {\n if let Some(Node(_value, next)) = Arc::into_inner(arc_node) {\n link = next;\n }\n }\n }\n}\n\n// Implementation of `new` and `push` omitted\nimpl<T> LinkedList<T> {\n /* ... */\n}\n\n// The following code could have still caused a stack overflow\n// despite the manual `Drop` impl if that `Drop` impl had used\n// `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`.\n\n// Create a long list and clone it\nlet mut x = LinkedList::new();\nfor i in 0..100000 {\n x.push(i); // Adds i to the front of x\n}\nlet y = x.clone();\n\n// Drop the clones in parallel\nlet x_thread = std::thread::spawn(|| drop(x));\nlet y_thread = std::thread::spawn(|| drop(y));\nx_thread.join().unwrap();\ny_thread.join().unwrap();
Constructs an Arc<T>
from a raw pointer.
The raw pointer must have been previously returned by a call to\nArc<U>::into_raw
where U
must have the same size and\nalignment as T
. This is trivially true if U
is T
.\nNote that if U
is not T
but has the same size and alignment, this is\nbasically like transmuting references of different types. See\nmem::transmute
for more information on what\nrestrictions apply in this case.
The user of from_raw
has to make sure a specific value of T
is only\ndropped once.
This function is unsafe because improper use may lead to memory unsafety,\neven if the returned Arc<T>
is never accessed.
use std::sync::Arc;\n\nlet x = Arc::new(\"hello\".to_owned());\nlet x_ptr = Arc::into_raw(x);\n\nunsafe {\n // Convert back to an `Arc` to prevent leak.\n let x = Arc::from_raw(x_ptr);\n assert_eq!(&*x, \"hello\");\n\n // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe.\n}\n\n// The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
Increments the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, and the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) for the duration of this method.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count(ptr);\n\n // This assertion is deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw(ptr);\n assert_eq!(2, Arc::strong_count(&five));\n}
Decrements the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, and the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) when invoking this method. This method can be used to release the final\nArc
and backing storage, but should not be called after the final Arc
has been\nreleased.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count(ptr);\n\n // Those assertions are deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw(ptr);\n assert_eq!(2, Arc::strong_count(&five));\n Arc::decrement_strong_count(ptr);\n assert_eq!(1, Arc::strong_count(&five));\n}
Consumes the Arc
, returning the wrapped pointer.
To avoid a memory leak the pointer must be converted back to an Arc
using\nArc::from_raw
.
use std::sync::Arc;\n\nlet x = Arc::new(\"hello\".to_owned());\nlet x_ptr = Arc::into_raw(x);\nassert_eq!(unsafe { &*x_ptr }, \"hello\");
Provides a raw pointer to the data.
\nThe counts are not affected in any way and the Arc
is not consumed. The pointer is valid for\nas long as there are strong counts in the Arc
.
use std::sync::Arc;\n\nlet x = Arc::new(\"hello\".to_owned());\nlet y = Arc::clone(&x);\nlet x_ptr = Arc::as_ptr(&x);\nassert_eq!(x_ptr, Arc::as_ptr(&y));\nassert_eq!(unsafe { &*x_ptr }, \"hello\");
allocator_api
)Constructs an Arc<T, A>
from a raw pointer.
The raw pointer must have been previously returned by a call to\nArc<U, A>::into_raw
where U
must have the same size and\nalignment as T
. This is trivially true if U
is T
.\nNote that if U
is not T
but has the same size and alignment, this is\nbasically like transmuting references of different types. See\nmem::transmute
for more information on what\nrestrictions apply in this case.
The raw pointer must point to a block of memory allocated by alloc
The user of from_raw
has to make sure a specific value of T
is only\ndropped once.
This function is unsafe because improper use may lead to memory unsafety,\neven if the returned Arc<T>
is never accessed.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet x = Arc::new_in(\"hello\".to_owned(), System);\nlet x_ptr = Arc::into_raw(x);\n\nunsafe {\n // Convert back to an `Arc` to prevent leak.\n let x = Arc::from_raw_in(x_ptr, System);\n assert_eq!(&*x, \"hello\");\n\n // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe.\n}\n\n// The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
Gets the number of Weak
pointers to this allocation.
This method by itself is safe, but using it correctly requires extra care.\nAnother thread can change the weak count at any time,\nincluding potentially between calling this method and acting on the result.
\nuse std::sync::Arc;\n\nlet five = Arc::new(5);\nlet _weak_five = Arc::downgrade(&five);\n\n// This assertion is deterministic because we haven't shared\n// the `Arc` or `Weak` between threads.\nassert_eq!(1, Arc::weak_count(&five));
Gets the number of strong (Arc
) pointers to this allocation.
This method by itself is safe, but using it correctly requires extra care.\nAnother thread can change the strong count at any time,\nincluding potentially between calling this method and acting on the result.
\nuse std::sync::Arc;\n\nlet five = Arc::new(5);\nlet _also_five = Arc::clone(&five);\n\n// This assertion is deterministic because we haven't shared\n// the `Arc` between threads.\nassert_eq!(2, Arc::strong_count(&five));
allocator_api
)Increments the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, and the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) for the duration of this method,, and ptr
must point to a block of memory\nallocated by alloc
.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::new_in(5, System);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count_in(ptr, System);\n\n // This assertion is deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw_in(ptr, System);\n assert_eq!(2, Arc::strong_count(&five));\n}
allocator_api
)Decrements the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) when invoking this method, and ptr
must point to a block of memory\nallocated by alloc
. This method can be used to release the final\nArc
and backing storage, but should not be called after the final Arc
has been\nreleased.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::new_in(5, System);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count_in(ptr, System);\n\n // Those assertions are deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw_in(ptr, System);\n assert_eq!(2, Arc::strong_count(&five));\n Arc::decrement_strong_count_in(ptr, System);\n assert_eq!(1, Arc::strong_count(&five));\n}
Returns true
if the two Arc
s point to the same allocation in a vein similar to\nptr::eq
. This function ignores the metadata of dyn Trait
pointers.
use std::sync::Arc;\n\nlet five = Arc::new(5);\nlet same_five = Arc::clone(&five);\nlet other_five = Arc::new(5);\n\nassert!(Arc::ptr_eq(&five, &same_five));\nassert!(!Arc::ptr_eq(&five, &other_five));
Makes a mutable reference into the given Arc
.
If there are other Arc
pointers to the same allocation, then make_mut
will\nclone
the inner value to a new allocation to ensure unique ownership. This is also\nreferred to as clone-on-write.
However, if there are no other Arc
pointers to this allocation, but some Weak
\npointers, then the Weak
pointers will be dissociated and the inner value will not\nbe cloned.
See also get_mut
, which will fail rather than cloning the inner value\nor dissociating Weak
pointers.
use std::sync::Arc;\n\nlet mut data = Arc::new(5);\n\n*Arc::make_mut(&mut data) += 1; // Won't clone anything\nlet mut other_data = Arc::clone(&data); // Won't clone inner data\n*Arc::make_mut(&mut data) += 1; // Clones inner data\n*Arc::make_mut(&mut data) += 1; // Won't clone anything\n*Arc::make_mut(&mut other_data) *= 2; // Won't clone anything\n\n// Now `data` and `other_data` point to different allocations.\nassert_eq!(*data, 8);\nassert_eq!(*other_data, 12);
Weak
pointers will be dissociated:
use std::sync::Arc;\n\nlet mut data = Arc::new(75);\nlet weak = Arc::downgrade(&data);\n\nassert!(75 == *data);\nassert!(75 == *weak.upgrade().unwrap());\n\n*Arc::make_mut(&mut data) += 1;\n\nassert!(76 == *data);\nassert!(weak.upgrade().is_none());
If we have the only reference to T
then unwrap it. Otherwise, clone T
and return the\nclone.
Assuming arc_t
is of type Arc<T>
, this function is functionally equivalent to\n(*arc_t).clone()
, but will avoid cloning the inner value where possible.
let inner = String::from(\"test\");\nlet ptr = inner.as_ptr();\n\nlet arc = Arc::new(inner);\nlet inner = Arc::unwrap_or_clone(arc);\n// The inner value was not cloned\nassert!(ptr::eq(ptr, inner.as_ptr()));\n\nlet arc = Arc::new(inner);\nlet arc2 = arc.clone();\nlet inner = Arc::unwrap_or_clone(arc);\n// Because there were 2 references, we had to clone the inner value.\nassert!(!ptr::eq(ptr, inner.as_ptr()));\n// `arc2` is the last reference, so when we unwrap it we get back\n// the original `String`.\nlet inner = Arc::unwrap_or_clone(arc2);\nassert!(ptr::eq(ptr, inner.as_ptr()));
Returns a mutable reference into the given Arc
, if there are\nno other Arc
or Weak
pointers to the same allocation.
Returns None
otherwise, because it is not safe to\nmutate a shared value.
See also make_mut
, which will clone
\nthe inner value when there are other Arc
pointers.
use std::sync::Arc;\n\nlet mut x = Arc::new(3);\n*Arc::get_mut(&mut x).unwrap() = 4;\nassert_eq!(*x, 4);\n\nlet _y = Arc::clone(&x);\nassert!(Arc::get_mut(&mut x).is_none());
get_mut_unchecked
)Returns a mutable reference into the given Arc
,\nwithout any check.
See also get_mut
, which is safe and does appropriate checks.
If any other Arc
or Weak
pointers to the same allocation exist, then\nthey must not be dereferenced or have active borrows for the duration\nof the returned borrow, and their inner type must be exactly the same as the\ninner type of this Rc (including lifetimes). This is trivially the case if no\nsuch pointers exist, for example immediately after Arc::new
.
#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet mut x = Arc::new(String::new());\nunsafe {\n Arc::get_mut_unchecked(&mut x).push_str(\"foo\")\n}\nassert_eq!(*x, \"foo\");
Other Arc
pointers to the same allocation must be to the same type.
#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet x: Arc<str> = Arc::from(\"Hello, world!\");\nlet mut y: Arc<[u8]> = x.clone().into();\nunsafe {\n // this is Undefined Behavior, because x's inner type is str, not [u8]\n Arc::get_mut_unchecked(&mut y).fill(0xff); // 0xff is invalid in UTF-8\n}\nprintln!(\"{}\", &*x); // Invalid UTF-8 in a str
Other Arc
pointers to the same allocation must be to the exact same type, including lifetimes.
#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet x: Arc<&str> = Arc::new(\"Hello, world!\");\n{\n let s = String::from(\"Oh, no!\");\n let mut y: Arc<&str> = x.clone().into();\n unsafe {\n // this is Undefined Behavior, because x's inner type\n // is &'long str, not &'short str\n *Arc::get_mut_unchecked(&mut y) = &s;\n }\n}\nprintln!(\"{}\", &*x); // Use-after-free
This impl allows implementing traits that require AsRawFd
on Arc.
use std::net::UdpSocket;\nuse std::sync::Arc;\ntrait MyTrait: AsRawFd {\n}\nimpl MyTrait for Arc<UdpSocket> {}\nimpl MyTrait for Box<UdpSocket> {}
This impl allows implementing traits that require AsFd
on Arc.
use std::net::UdpSocket;\nuse std::sync::Arc;\n\ntrait MyTrait: AsFd {}\nimpl MyTrait for Arc<UdpSocket> {}\nimpl MyTrait for Box<UdpSocket> {}
Partial comparison for two Arc
s.
The two are compared by calling partial_cmp()
on their inner values.
use std::sync::Arc;\nuse std::cmp::Ordering;\n\nlet five = Arc::new(5);\n\nassert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6)));
Less-than comparison for two Arc
s.
The two are compared by calling <
on their inner values.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five < Arc::new(6));
‘Less than or equal to’ comparison for two Arc
s.
The two are compared by calling <=
on their inner values.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five <= Arc::new(5));
Comparison for two Arc
s.
The two are compared by calling cmp()
on their inner values.
use std::sync::Arc;\nuse std::cmp::Ordering;\n\nlet five = Arc::new(5);\n\nassert_eq!(Ordering::Less, five.cmp(&Arc::new(6)));
Drops the Arc
.
This will decrement the strong reference count. If the strong reference\ncount reaches zero then the only other references (if any) are\nWeak
, so we drop
the inner value.
use std::sync::Arc;\n\nstruct Foo;\n\nimpl Drop for Foo {\n fn drop(&mut self) {\n println!(\"dropped!\");\n }\n}\n\nlet foo = Arc::new(Foo);\nlet foo2 = Arc::clone(&foo);\n\ndrop(foo); // Doesn't print anything\ndrop(foo2); // Prints \"dropped!\"
Makes a clone of the Arc
pointer.
This creates another pointer to the same allocation, increasing the\nstrong reference count.
\nuse std::sync::Arc;\n\nlet five = Arc::new(5);\n\nlet _ = Arc::clone(&five);
source
. Read moreEquality for two Arc
s.
Two Arc
s are equal if their inner values are equal, even if they are\nstored in different allocation.
If T
also implements Eq
(implying reflexivity of equality),\ntwo Arc
s that point to the same allocation are always equal.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five == Arc::new(5));
Inequality for two Arc
s.
Two Arc
s are not equal if their inner values are not equal.
If T
also implements Eq
(implying reflexivity of equality),\ntwo Arc
s that point to the same value are always equal.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five != Arc::new(6));
Subscriber
will\nenable, or None
, if the subscriber does not implement level-based\nfiltering or chooses not to implement this method. Read moreEvent
] should be recorded. Read moreSubscriber::try_close
insteadself
is the same type as the provided TypeId
, returns an untyped\n*const
pointer to that type. Otherwise, returns None
. Read moreDispatch
]. Read moreConstructs a new Arc<T>
while giving you a Weak<T>
to the allocation,\nto allow you to construct a T
which holds a weak pointer to itself.
Generally, a structure circularly referencing itself, either directly or\nindirectly, should not hold a strong reference to itself to prevent a memory leak.\nUsing this function, you get access to the weak pointer during the\ninitialization of T
, before the Arc<T>
is created, such that you can\nclone and store it inside the T
.
new_cyclic
first allocates the managed allocation for the Arc<T>
,\nthen calls your closure, giving it a Weak<T>
to this allocation,\nand only afterwards completes the construction of the Arc<T>
by placing\nthe T
returned from your closure into the allocation.
Since the new Arc<T>
is not fully-constructed until Arc<T>::new_cyclic
\nreturns, calling upgrade
on the weak reference inside your closure will\nfail and result in a None
value.
If data_fn
panics, the panic is propagated to the caller, and the\ntemporary Weak<T>
is dropped normally.
use std::sync::{Arc, Weak};\n\nstruct Gadget {\n me: Weak<Gadget>,\n}\n\nimpl Gadget {\n /// Construct a reference counted Gadget.\n fn new() -> Arc<Self> {\n // `me` is a `Weak<Gadget>` pointing at the new allocation of the\n // `Arc` we're constructing.\n Arc::new_cyclic(|me| {\n // Create the actual struct here.\n Gadget { me: me.clone() }\n })\n }\n\n /// Return a reference counted pointer to Self.\n fn me(&self) -> Arc<Self> {\n self.me.upgrade().unwrap()\n }\n}
new_uninit
)Constructs a new Arc
with uninitialized contents.
#![feature(new_uninit)]\n#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet mut five = Arc::<u32>::new_uninit();\n\n// Deferred initialization:\nArc::get_mut(&mut five).unwrap().write(5);\n\nlet five = unsafe { five.assume_init() };\n\nassert_eq!(*five, 5)
new_uninit
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n\nuse std::sync::Arc;\n\nlet zero = Arc::<u32>::new_zeroed();\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
Constructs a new Pin<Arc<T>>
. If T
does not implement Unpin
, then\ndata
will be pinned in memory and unable to be moved.
allocator_api
)Constructs a new Pin<Arc<T>>
, return an error if allocation fails.
allocator_api
)Constructs a new Arc<T>
, returning an error if allocation fails.
#![feature(allocator_api)]\nuse std::sync::Arc;\n\nlet five = Arc::try_new(5)?;
allocator_api
)Constructs a new Arc
with uninitialized contents, returning an error\nif allocation fails.
#![feature(new_uninit, allocator_api)]\n#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet mut five = Arc::<u32>::try_new_uninit()?;\n\n// Deferred initialization:\nArc::get_mut(&mut five).unwrap().write(5);\n\nlet five = unsafe { five.assume_init() };\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, returning an error if allocation fails.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit, allocator_api)]\n\nuse std::sync::Arc;\n\nlet zero = Arc::<u32>::try_new_zeroed()?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
allocator_api
)Returns a reference to the underlying allocator.
\nNote: this is an associated function, which means that you have\nto call it as Arc::allocator(&a)
instead of a.allocator()
. This\nis so that there is no conflict with a method on the inner type.
allocator_api
)Constructs a new Arc<T>
in the provided allocator.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::new_in(5, System);
allocator_api
)Constructs a new Arc
with uninitialized contents in the provided allocator.
#![feature(new_uninit)]\n#![feature(get_mut_unchecked)]\n#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet mut five = Arc::<u32, _>::new_uninit_in(System);\n\nlet five = unsafe {\n // Deferred initialization:\n Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5)
allocator_api
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, in the provided allocator.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit)]\n#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet zero = Arc::<u32, _>::new_zeroed_in(System);\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0)
allocator_api
)Constructs a new Pin<Arc<T, A>>
in the provided allocator. If T
does not implement Unpin
,\nthen data
will be pinned in memory and unable to be moved.
allocator_api
)Constructs a new Pin<Arc<T, A>>
in the provided allocator, return an error if allocation\nfails.
allocator_api
)Constructs a new Arc<T, A>
in the provided allocator, returning an error if allocation fails.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::try_new_in(5, System)?;
allocator_api
)Constructs a new Arc
with uninitialized contents, in the provided allocator, returning an\nerror if allocation fails.
#![feature(new_uninit, allocator_api)]\n#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet mut five = Arc::<u32, _>::try_new_uninit_in(System)?;\n\nlet five = unsafe {\n // Deferred initialization:\n Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);\n\n five.assume_init()\n};\n\nassert_eq!(*five, 5);
allocator_api
)Constructs a new Arc
with uninitialized contents, with the memory\nbeing filled with 0
bytes, in the provided allocator, returning an error if allocation\nfails.
See MaybeUninit::zeroed
for examples of correct and incorrect usage\nof this method.
#![feature(new_uninit, allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet zero = Arc::<u32, _>::try_new_zeroed_in(System)?;\nlet zero = unsafe { zero.assume_init() };\n\nassert_eq!(*zero, 0);
Returns the inner value, if the Arc
has exactly one strong reference.
Otherwise, an Err
is returned with the same Arc
that was\npassed in.
This will succeed even if there are outstanding weak references.
\nIt is strongly recommended to use Arc::into_inner
instead if you don’t\nwant to keep the Arc
in the Err
case.\nImmediately dropping the Err
payload, like in the expression\nArc::try_unwrap(this).ok()
, can still cause the strong count to\ndrop to zero and the inner value of the Arc
to be dropped:\nFor instance if two threads each execute this expression in parallel, then\nthere is a race condition. The threads could first both check whether they\nhave the last clone of their Arc
via Arc::try_unwrap
, and then\nboth drop their Arc
in the call to ok
,\ntaking the strong count from two down to zero.
use std::sync::Arc;\n\nlet x = Arc::new(3);\nassert_eq!(Arc::try_unwrap(x), Ok(3));\n\nlet x = Arc::new(4);\nlet _y = Arc::clone(&x);\nassert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4);
Returns the inner value, if the Arc
has exactly one strong reference.
Otherwise, None
is returned and the Arc
is dropped.
This will succeed even if there are outstanding weak references.
\nIf Arc::into_inner
is called on every clone of this Arc
,\nit is guaranteed that exactly one of the calls returns the inner value.\nThis means in particular that the inner value is not dropped.
The similar expression Arc::try_unwrap(this).ok()
does not\noffer such a guarantee. See the last example below\nand the documentation of Arc::try_unwrap
.
Minimal example demonstrating the guarantee that Arc::into_inner
gives.
use std::sync::Arc;\n\nlet x = Arc::new(3);\nlet y = Arc::clone(&x);\n\n// Two threads calling `Arc::into_inner` on both clones of an `Arc`:\nlet x_thread = std::thread::spawn(|| Arc::into_inner(x));\nlet y_thread = std::thread::spawn(|| Arc::into_inner(y));\n\nlet x_inner_value = x_thread.join().unwrap();\nlet y_inner_value = y_thread.join().unwrap();\n\n// One of the threads is guaranteed to receive the inner value:\nassert!(matches!(\n (x_inner_value, y_inner_value),\n (None, Some(3)) | (Some(3), None)\n));\n// The result could also be `(None, None)` if the threads called\n// `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.
A more practical example demonstrating the need for Arc::into_inner
:
use std::sync::Arc;\n\n// Definition of a simple singly linked list using `Arc`:\n#[derive(Clone)]\nstruct LinkedList<T>(Option<Arc<Node<T>>>);\nstruct Node<T>(T, Option<Arc<Node<T>>>);\n\n// Dropping a long `LinkedList<T>` relying on the destructor of `Arc`\n// can cause a stack overflow. To prevent this, we can provide a\n// manual `Drop` implementation that does the destruction in a loop:\nimpl<T> Drop for LinkedList<T> {\n fn drop(&mut self) {\n let mut link = self.0.take();\n while let Some(arc_node) = link.take() {\n if let Some(Node(_value, next)) = Arc::into_inner(arc_node) {\n link = next;\n }\n }\n }\n}\n\n// Implementation of `new` and `push` omitted\nimpl<T> LinkedList<T> {\n /* ... */\n}\n\n// The following code could have still caused a stack overflow\n// despite the manual `Drop` impl if that `Drop` impl had used\n// `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`.\n\n// Create a long list and clone it\nlet mut x = LinkedList::new();\nfor i in 0..100000 {\n x.push(i); // Adds i to the front of x\n}\nlet y = x.clone();\n\n// Drop the clones in parallel\nlet x_thread = std::thread::spawn(|| drop(x));\nlet y_thread = std::thread::spawn(|| drop(y));\nx_thread.join().unwrap();\ny_thread.join().unwrap();
Constructs an Arc<T>
from a raw pointer.
The raw pointer must have been previously returned by a call to\nArc<U>::into_raw
where U
must have the same size and\nalignment as T
. This is trivially true if U
is T
.\nNote that if U
is not T
but has the same size and alignment, this is\nbasically like transmuting references of different types. See\nmem::transmute
for more information on what\nrestrictions apply in this case.
The user of from_raw
has to make sure a specific value of T
is only\ndropped once.
This function is unsafe because improper use may lead to memory unsafety,\neven if the returned Arc<T>
is never accessed.
use std::sync::Arc;\n\nlet x = Arc::new(\"hello\".to_owned());\nlet x_ptr = Arc::into_raw(x);\n\nunsafe {\n // Convert back to an `Arc` to prevent leak.\n let x = Arc::from_raw(x_ptr);\n assert_eq!(&*x, \"hello\");\n\n // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe.\n}\n\n// The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
Increments the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, and the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) for the duration of this method.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count(ptr);\n\n // This assertion is deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw(ptr);\n assert_eq!(2, Arc::strong_count(&five));\n}
Decrements the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, and the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) when invoking this method. This method can be used to release the final\nArc
and backing storage, but should not be called after the final Arc
has been\nreleased.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count(ptr);\n\n // Those assertions are deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw(ptr);\n assert_eq!(2, Arc::strong_count(&five));\n Arc::decrement_strong_count(ptr);\n assert_eq!(1, Arc::strong_count(&five));\n}
Consumes the Arc
, returning the wrapped pointer.
To avoid a memory leak the pointer must be converted back to an Arc
using\nArc::from_raw
.
use std::sync::Arc;\n\nlet x = Arc::new(\"hello\".to_owned());\nlet x_ptr = Arc::into_raw(x);\nassert_eq!(unsafe { &*x_ptr }, \"hello\");
Provides a raw pointer to the data.
\nThe counts are not affected in any way and the Arc
is not consumed. The pointer is valid for\nas long as there are strong counts in the Arc
.
use std::sync::Arc;\n\nlet x = Arc::new(\"hello\".to_owned());\nlet y = Arc::clone(&x);\nlet x_ptr = Arc::as_ptr(&x);\nassert_eq!(x_ptr, Arc::as_ptr(&y));\nassert_eq!(unsafe { &*x_ptr }, \"hello\");
allocator_api
)Constructs an Arc<T, A>
from a raw pointer.
The raw pointer must have been previously returned by a call to\nArc<U, A>::into_raw
where U
must have the same size and\nalignment as T
. This is trivially true if U
is T
.\nNote that if U
is not T
but has the same size and alignment, this is\nbasically like transmuting references of different types. See\nmem::transmute
for more information on what\nrestrictions apply in this case.
The raw pointer must point to a block of memory allocated by alloc
The user of from_raw
has to make sure a specific value of T
is only\ndropped once.
This function is unsafe because improper use may lead to memory unsafety,\neven if the returned Arc<T>
is never accessed.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet x = Arc::new_in(\"hello\".to_owned(), System);\nlet x_ptr = Arc::into_raw(x);\n\nunsafe {\n // Convert back to an `Arc` to prevent leak.\n let x = Arc::from_raw_in(x_ptr, System);\n assert_eq!(&*x, \"hello\");\n\n // Further calls to `Arc::from_raw(x_ptr)` would be memory-unsafe.\n}\n\n// The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
Gets the number of Weak
pointers to this allocation.
This method by itself is safe, but using it correctly requires extra care.\nAnother thread can change the weak count at any time,\nincluding potentially between calling this method and acting on the result.
\nuse std::sync::Arc;\n\nlet five = Arc::new(5);\nlet _weak_five = Arc::downgrade(&five);\n\n// This assertion is deterministic because we haven't shared\n// the `Arc` or `Weak` between threads.\nassert_eq!(1, Arc::weak_count(&five));
Gets the number of strong (Arc
) pointers to this allocation.
This method by itself is safe, but using it correctly requires extra care.\nAnother thread can change the strong count at any time,\nincluding potentially between calling this method and acting on the result.
\nuse std::sync::Arc;\n\nlet five = Arc::new(5);\nlet _also_five = Arc::clone(&five);\n\n// This assertion is deterministic because we haven't shared\n// the `Arc` between threads.\nassert_eq!(2, Arc::strong_count(&five));
allocator_api
)Increments the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, and the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) for the duration of this method,, and ptr
must point to a block of memory\nallocated by alloc
.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::new_in(5, System);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count_in(ptr, System);\n\n // This assertion is deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw_in(ptr, System);\n assert_eq!(2, Arc::strong_count(&five));\n}
allocator_api
)Decrements the strong reference count on the Arc<T>
associated with the\nprovided pointer by one.
The pointer must have been obtained through Arc::into_raw
, the\nassociated Arc
instance must be valid (i.e. the strong count must be at\nleast 1) when invoking this method, and ptr
must point to a block of memory\nallocated by alloc
. This method can be used to release the final\nArc
and backing storage, but should not be called after the final Arc
has been\nreleased.
#![feature(allocator_api)]\n\nuse std::sync::Arc;\nuse std::alloc::System;\n\nlet five = Arc::new_in(5, System);\n\nunsafe {\n let ptr = Arc::into_raw(five);\n Arc::increment_strong_count_in(ptr, System);\n\n // Those assertions are deterministic because we haven't shared\n // the `Arc` between threads.\n let five = Arc::from_raw_in(ptr, System);\n assert_eq!(2, Arc::strong_count(&five));\n Arc::decrement_strong_count_in(ptr, System);\n assert_eq!(1, Arc::strong_count(&five));\n}
Returns true
if the two Arc
s point to the same allocation in a vein similar to\nptr::eq
. This function ignores the metadata of dyn Trait
pointers.
use std::sync::Arc;\n\nlet five = Arc::new(5);\nlet same_five = Arc::clone(&five);\nlet other_five = Arc::new(5);\n\nassert!(Arc::ptr_eq(&five, &same_five));\nassert!(!Arc::ptr_eq(&five, &other_five));
Makes a mutable reference into the given Arc
.
If there are other Arc
pointers to the same allocation, then make_mut
will\nclone
the inner value to a new allocation to ensure unique ownership. This is also\nreferred to as clone-on-write.
However, if there are no other Arc
pointers to this allocation, but some Weak
\npointers, then the Weak
pointers will be dissociated and the inner value will not\nbe cloned.
See also get_mut
, which will fail rather than cloning the inner value\nor dissociating Weak
pointers.
use std::sync::Arc;\n\nlet mut data = Arc::new(5);\n\n*Arc::make_mut(&mut data) += 1; // Won't clone anything\nlet mut other_data = Arc::clone(&data); // Won't clone inner data\n*Arc::make_mut(&mut data) += 1; // Clones inner data\n*Arc::make_mut(&mut data) += 1; // Won't clone anything\n*Arc::make_mut(&mut other_data) *= 2; // Won't clone anything\n\n// Now `data` and `other_data` point to different allocations.\nassert_eq!(*data, 8);\nassert_eq!(*other_data, 12);
Weak
pointers will be dissociated:
use std::sync::Arc;\n\nlet mut data = Arc::new(75);\nlet weak = Arc::downgrade(&data);\n\nassert!(75 == *data);\nassert!(75 == *weak.upgrade().unwrap());\n\n*Arc::make_mut(&mut data) += 1;\n\nassert!(76 == *data);\nassert!(weak.upgrade().is_none());
If we have the only reference to T
then unwrap it. Otherwise, clone T
and return the\nclone.
Assuming arc_t
is of type Arc<T>
, this function is functionally equivalent to\n(*arc_t).clone()
, but will avoid cloning the inner value where possible.
let inner = String::from(\"test\");\nlet ptr = inner.as_ptr();\n\nlet arc = Arc::new(inner);\nlet inner = Arc::unwrap_or_clone(arc);\n// The inner value was not cloned\nassert!(ptr::eq(ptr, inner.as_ptr()));\n\nlet arc = Arc::new(inner);\nlet arc2 = arc.clone();\nlet inner = Arc::unwrap_or_clone(arc);\n// Because there were 2 references, we had to clone the inner value.\nassert!(!ptr::eq(ptr, inner.as_ptr()));\n// `arc2` is the last reference, so when we unwrap it we get back\n// the original `String`.\nlet inner = Arc::unwrap_or_clone(arc2);\nassert!(ptr::eq(ptr, inner.as_ptr()));
Returns a mutable reference into the given Arc
, if there are\nno other Arc
or Weak
pointers to the same allocation.
Returns None
otherwise, because it is not safe to\nmutate a shared value.
See also make_mut
, which will clone
\nthe inner value when there are other Arc
pointers.
use std::sync::Arc;\n\nlet mut x = Arc::new(3);\n*Arc::get_mut(&mut x).unwrap() = 4;\nassert_eq!(*x, 4);\n\nlet _y = Arc::clone(&x);\nassert!(Arc::get_mut(&mut x).is_none());
get_mut_unchecked
)Returns a mutable reference into the given Arc
,\nwithout any check.
See also get_mut
, which is safe and does appropriate checks.
If any other Arc
or Weak
pointers to the same allocation exist, then\nthey must not be dereferenced or have active borrows for the duration\nof the returned borrow, and their inner type must be exactly the same as the\ninner type of this Rc (including lifetimes). This is trivially the case if no\nsuch pointers exist, for example immediately after Arc::new
.
#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet mut x = Arc::new(String::new());\nunsafe {\n Arc::get_mut_unchecked(&mut x).push_str(\"foo\")\n}\nassert_eq!(*x, \"foo\");
Other Arc
pointers to the same allocation must be to the same type.
#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet x: Arc<str> = Arc::from(\"Hello, world!\");\nlet mut y: Arc<[u8]> = x.clone().into();\nunsafe {\n // this is Undefined Behavior, because x's inner type is str, not [u8]\n Arc::get_mut_unchecked(&mut y).fill(0xff); // 0xff is invalid in UTF-8\n}\nprintln!(\"{}\", &*x); // Invalid UTF-8 in a str
Other Arc
pointers to the same allocation must be to the exact same type, including lifetimes.
#![feature(get_mut_unchecked)]\n\nuse std::sync::Arc;\n\nlet x: Arc<&str> = Arc::new(\"Hello, world!\");\n{\n let s = String::from(\"Oh, no!\");\n let mut y: Arc<&str> = x.clone().into();\n unsafe {\n // this is Undefined Behavior, because x's inner type\n // is &'long str, not &'short str\n *Arc::get_mut_unchecked(&mut y) = &s;\n }\n}\nprintln!(\"{}\", &*x); // Use-after-free
This impl allows implementing traits that require AsRawFd
on Arc.
use std::net::UdpSocket;\nuse std::sync::Arc;\ntrait MyTrait: AsRawFd {\n}\nimpl MyTrait for Arc<UdpSocket> {}\nimpl MyTrait for Box<UdpSocket> {}
This impl allows implementing traits that require AsFd
on Arc.
use std::net::UdpSocket;\nuse std::sync::Arc;\n\ntrait MyTrait: AsFd {}\nimpl MyTrait for Arc<UdpSocket> {}\nimpl MyTrait for Box<UdpSocket> {}
Partial comparison for two Arc
s.
The two are compared by calling partial_cmp()
on their inner values.
use std::sync::Arc;\nuse std::cmp::Ordering;\n\nlet five = Arc::new(5);\n\nassert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6)));
Less-than comparison for two Arc
s.
The two are compared by calling <
on their inner values.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five < Arc::new(6));
‘Less than or equal to’ comparison for two Arc
s.
The two are compared by calling <=
on their inner values.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five <= Arc::new(5));
Comparison for two Arc
s.
The two are compared by calling cmp()
on their inner values.
use std::sync::Arc;\nuse std::cmp::Ordering;\n\nlet five = Arc::new(5);\n\nassert_eq!(Ordering::Less, five.cmp(&Arc::new(6)));
Drops the Arc
.
This will decrement the strong reference count. If the strong reference\ncount reaches zero then the only other references (if any) are\nWeak
, so we drop
the inner value.
use std::sync::Arc;\n\nstruct Foo;\n\nimpl Drop for Foo {\n fn drop(&mut self) {\n println!(\"dropped!\");\n }\n}\n\nlet foo = Arc::new(Foo);\nlet foo2 = Arc::clone(&foo);\n\ndrop(foo); // Doesn't print anything\ndrop(foo2); // Prints \"dropped!\"
Makes a clone of the Arc
pointer.
This creates another pointer to the same allocation, increasing the\nstrong reference count.
\nuse std::sync::Arc;\n\nlet five = Arc::new(5);\n\nlet _ = Arc::clone(&five);
source
. Read moreEquality for two Arc
s.
Two Arc
s are equal if their inner values are equal, even if they are\nstored in different allocation.
If T
also implements Eq
(implying reflexivity of equality),\ntwo Arc
s that point to the same allocation are always equal.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five == Arc::new(5));
Inequality for two Arc
s.
Two Arc
s are not equal if their inner values are not equal.
If T
also implements Eq
(implying reflexivity of equality),\ntwo Arc
s that point to the same value are always equal.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nassert!(five != Arc::new(6));
Subscriber
will\nenable, or None
, if the subscriber does not implement level-based\nfiltering or chooses not to implement this method. Read moreEvent
] should be recorded. Read moreSubscriber::try_close
insteadself
is the same type as the provided TypeId
, returns an untyped\n*const
pointer to that type. Otherwise, returns None
. Read moreDispatch
]. Read moreallocator_api
)Constructs a new Weak<T, A>
, without allocating any memory, technically in the provided\nallocator.\nCalling upgrade
on the return value always gives None
.
#![feature(allocator_api)]\n\nuse std::sync::Weak;\nuse std::alloc::System;\n\nlet empty: Weak<i64, _> = Weak::new_in(System);\nassert!(empty.upgrade().is_none());
Converts a raw pointer previously created by into_raw
back into Weak<T>
.
This can be used to safely get a strong reference (by calling upgrade
\nlater) or to deallocate the weak count by dropping the Weak<T>
.
It takes ownership of one weak reference (with the exception of pointers created by new
,\nas these don’t own anything; the method still works on them).
The pointer must have originated from the into_raw
and must still own its potential\nweak reference.
It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this\ntakes ownership of one weak reference currently represented as a raw pointer (the weak\ncount is not modified by this operation) and therefore it must be paired with a previous\ncall to into_raw
.
use std::sync::{Arc, Weak};\n\nlet strong = Arc::new(\"hello\".to_owned());\n\nlet raw_1 = Arc::downgrade(&strong).into_raw();\nlet raw_2 = Arc::downgrade(&strong).into_raw();\n\nassert_eq!(2, Arc::weak_count(&strong));\n\nassert_eq!(\"hello\", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap());\nassert_eq!(1, Arc::weak_count(&strong));\n\ndrop(strong);\n\n// Decrement the last weak count.\nassert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none());
Returns a raw pointer to the object T
pointed to by this Weak<T>
.
The pointer is valid only if there are some strong references. The pointer may be dangling,\nunaligned or even null
otherwise.
use std::sync::Arc;\nuse std::ptr;\n\nlet strong = Arc::new(\"hello\".to_owned());\nlet weak = Arc::downgrade(&strong);\n// Both point to the same object\nassert!(ptr::eq(&*strong, weak.as_ptr()));\n// The strong here keeps it alive, so we can still access the object.\nassert_eq!(\"hello\", unsafe { &*weak.as_ptr() });\n\ndrop(strong);\n// But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to\n// undefined behaviour.\n// assert_eq!(\"hello\", unsafe { &*weak.as_ptr() });
Consumes the Weak<T>
and turns it into a raw pointer.
This converts the weak pointer into a raw pointer, while still preserving the ownership of\none weak reference (the weak count is not modified by this operation). It can be turned\nback into the Weak<T>
with from_raw
.
The same restrictions of accessing the target of the pointer as with\nas_ptr
apply.
use std::sync::{Arc, Weak};\n\nlet strong = Arc::new(\"hello\".to_owned());\nlet weak = Arc::downgrade(&strong);\nlet raw = weak.into_raw();\n\nassert_eq!(1, Arc::weak_count(&strong));\nassert_eq!(\"hello\", unsafe { &*raw });\n\ndrop(unsafe { Weak::from_raw(raw) });\nassert_eq!(0, Arc::weak_count(&strong));
allocator_api
)Converts a raw pointer previously created by into_raw
back into Weak<T>
in the provided\nallocator.
This can be used to safely get a strong reference (by calling upgrade
\nlater) or to deallocate the weak count by dropping the Weak<T>
.
It takes ownership of one weak reference (with the exception of pointers created by new
,\nas these don’t own anything; the method still works on them).
The pointer must have originated from the into_raw
and must still own its potential\nweak reference, and must point to a block of memory allocated by alloc
.
It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this\ntakes ownership of one weak reference currently represented as a raw pointer (the weak\ncount is not modified by this operation) and therefore it must be paired with a previous\ncall to into_raw
.
use std::sync::{Arc, Weak};\n\nlet strong = Arc::new(\"hello\".to_owned());\n\nlet raw_1 = Arc::downgrade(&strong).into_raw();\nlet raw_2 = Arc::downgrade(&strong).into_raw();\n\nassert_eq!(2, Arc::weak_count(&strong));\n\nassert_eq!(\"hello\", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap());\nassert_eq!(1, Arc::weak_count(&strong));\n\ndrop(strong);\n\n// Decrement the last weak count.\nassert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none());
Attempts to upgrade the Weak
pointer to an Arc
, delaying\ndropping of the inner value if successful.
Returns None
if the inner value has since been dropped.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nlet weak_five = Arc::downgrade(&five);\n\nlet strong_five: Option<Arc<_>> = weak_five.upgrade();\nassert!(strong_five.is_some());\n\n// Destroy all strong pointers.\ndrop(strong_five);\ndrop(five);\n\nassert!(weak_five.upgrade().is_none());
Gets the number of strong (Arc
) pointers pointing to this allocation.
If self
was created using Weak::new
, this will return 0.
Gets an approximation of the number of Weak
pointers pointing to this\nallocation.
If self
was created using Weak::new
, or if there are no remaining\nstrong pointers, this will return 0.
Due to implementation details, the returned value can be off by 1 in\neither direction when other threads are manipulating any Arc
s or\nWeak
s pointing to the same allocation.
Returns true
if the two Weak
s point to the same allocation similar to ptr::eq
, or if\nboth don’t point to any allocation (because they were created with Weak::new()
). However,\nthis function ignores the metadata of dyn Trait
pointers.
Since this compares pointers it means that Weak::new()
will equal each\nother, even though they don’t point to any allocation.
use std::sync::Arc;\n\nlet first_rc = Arc::new(5);\nlet first = Arc::downgrade(&first_rc);\nlet second = Arc::downgrade(&first_rc);\n\nassert!(first.ptr_eq(&second));\n\nlet third_rc = Arc::new(5);\nlet third = Arc::downgrade(&third_rc);\n\nassert!(!first.ptr_eq(&third));
Comparing Weak::new
.
use std::sync::{Arc, Weak};\n\nlet first = Weak::new();\nlet second = Weak::new();\nassert!(first.ptr_eq(&second));\n\nlet third_rc = Arc::new(());\nlet third = Arc::downgrade(&third_rc);\nassert!(!first.ptr_eq(&third));
Drops the Weak
pointer.
use std::sync::{Arc, Weak};\n\nstruct Foo;\n\nimpl Drop for Foo {\n fn drop(&mut self) {\n println!(\"dropped!\");\n }\n}\n\nlet foo = Arc::new(Foo);\nlet weak_foo = Arc::downgrade(&foo);\nlet other_weak_foo = Weak::clone(&weak_foo);\n\ndrop(weak_foo); // Doesn't print anything\ndrop(foo); // Prints \"dropped!\"\n\nassert!(other_weak_foo.upgrade().is_none());
Makes a clone of the Weak
pointer that points to the same allocation.
use std::sync::{Arc, Weak};\n\nlet weak_five = Arc::downgrade(&Arc::new(5));\n\nlet _ = Weak::clone(&weak_five);
source
. Read moreallocator_api
)Constructs a new Weak<T, A>
, without allocating any memory, technically in the provided\nallocator.\nCalling upgrade
on the return value always gives None
.
#![feature(allocator_api)]\n\nuse std::sync::Weak;\nuse std::alloc::System;\n\nlet empty: Weak<i64, _> = Weak::new_in(System);\nassert!(empty.upgrade().is_none());
Converts a raw pointer previously created by into_raw
back into Weak<T>
.
This can be used to safely get a strong reference (by calling upgrade
\nlater) or to deallocate the weak count by dropping the Weak<T>
.
It takes ownership of one weak reference (with the exception of pointers created by new
,\nas these don’t own anything; the method still works on them).
The pointer must have originated from the into_raw
and must still own its potential\nweak reference.
It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this\ntakes ownership of one weak reference currently represented as a raw pointer (the weak\ncount is not modified by this operation) and therefore it must be paired with a previous\ncall to into_raw
.
use std::sync::{Arc, Weak};\n\nlet strong = Arc::new(\"hello\".to_owned());\n\nlet raw_1 = Arc::downgrade(&strong).into_raw();\nlet raw_2 = Arc::downgrade(&strong).into_raw();\n\nassert_eq!(2, Arc::weak_count(&strong));\n\nassert_eq!(\"hello\", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap());\nassert_eq!(1, Arc::weak_count(&strong));\n\ndrop(strong);\n\n// Decrement the last weak count.\nassert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none());
Returns a raw pointer to the object T
pointed to by this Weak<T>
.
The pointer is valid only if there are some strong references. The pointer may be dangling,\nunaligned or even null
otherwise.
use std::sync::Arc;\nuse std::ptr;\n\nlet strong = Arc::new(\"hello\".to_owned());\nlet weak = Arc::downgrade(&strong);\n// Both point to the same object\nassert!(ptr::eq(&*strong, weak.as_ptr()));\n// The strong here keeps it alive, so we can still access the object.\nassert_eq!(\"hello\", unsafe { &*weak.as_ptr() });\n\ndrop(strong);\n// But not any more. We can do weak.as_ptr(), but accessing the pointer would lead to\n// undefined behaviour.\n// assert_eq!(\"hello\", unsafe { &*weak.as_ptr() });
Consumes the Weak<T>
and turns it into a raw pointer.
This converts the weak pointer into a raw pointer, while still preserving the ownership of\none weak reference (the weak count is not modified by this operation). It can be turned\nback into the Weak<T>
with from_raw
.
The same restrictions of accessing the target of the pointer as with\nas_ptr
apply.
use std::sync::{Arc, Weak};\n\nlet strong = Arc::new(\"hello\".to_owned());\nlet weak = Arc::downgrade(&strong);\nlet raw = weak.into_raw();\n\nassert_eq!(1, Arc::weak_count(&strong));\nassert_eq!(\"hello\", unsafe { &*raw });\n\ndrop(unsafe { Weak::from_raw(raw) });\nassert_eq!(0, Arc::weak_count(&strong));
allocator_api
)Converts a raw pointer previously created by into_raw
back into Weak<T>
in the provided\nallocator.
This can be used to safely get a strong reference (by calling upgrade
\nlater) or to deallocate the weak count by dropping the Weak<T>
.
It takes ownership of one weak reference (with the exception of pointers created by new
,\nas these don’t own anything; the method still works on them).
The pointer must have originated from the into_raw
and must still own its potential\nweak reference, and must point to a block of memory allocated by alloc
.
It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this\ntakes ownership of one weak reference currently represented as a raw pointer (the weak\ncount is not modified by this operation) and therefore it must be paired with a previous\ncall to into_raw
.
use std::sync::{Arc, Weak};\n\nlet strong = Arc::new(\"hello\".to_owned());\n\nlet raw_1 = Arc::downgrade(&strong).into_raw();\nlet raw_2 = Arc::downgrade(&strong).into_raw();\n\nassert_eq!(2, Arc::weak_count(&strong));\n\nassert_eq!(\"hello\", &*unsafe { Weak::from_raw(raw_1) }.upgrade().unwrap());\nassert_eq!(1, Arc::weak_count(&strong));\n\ndrop(strong);\n\n// Decrement the last weak count.\nassert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none());
Attempts to upgrade the Weak
pointer to an Arc
, delaying\ndropping of the inner value if successful.
Returns None
if the inner value has since been dropped.
use std::sync::Arc;\n\nlet five = Arc::new(5);\n\nlet weak_five = Arc::downgrade(&five);\n\nlet strong_five: Option<Arc<_>> = weak_five.upgrade();\nassert!(strong_five.is_some());\n\n// Destroy all strong pointers.\ndrop(strong_five);\ndrop(five);\n\nassert!(weak_five.upgrade().is_none());
Gets the number of strong (Arc
) pointers pointing to this allocation.
If self
was created using Weak::new
, this will return 0.
Gets an approximation of the number of Weak
pointers pointing to this\nallocation.
If self
was created using Weak::new
, or if there are no remaining\nstrong pointers, this will return 0.
Due to implementation details, the returned value can be off by 1 in\neither direction when other threads are manipulating any Arc
s or\nWeak
s pointing to the same allocation.
Returns true
if the two Weak
s point to the same allocation similar to ptr::eq
, or if\nboth don’t point to any allocation (because they were created with Weak::new()
). However,\nthis function ignores the metadata of dyn Trait
pointers.
Since this compares pointers it means that Weak::new()
will equal each\nother, even though they don’t point to any allocation.
use std::sync::Arc;\n\nlet first_rc = Arc::new(5);\nlet first = Arc::downgrade(&first_rc);\nlet second = Arc::downgrade(&first_rc);\n\nassert!(first.ptr_eq(&second));\n\nlet third_rc = Arc::new(5);\nlet third = Arc::downgrade(&third_rc);\n\nassert!(!first.ptr_eq(&third));
Comparing Weak::new
.
use std::sync::{Arc, Weak};\n\nlet first = Weak::new();\nlet second = Weak::new();\nassert!(first.ptr_eq(&second));\n\nlet third_rc = Arc::new(());\nlet third = Arc::downgrade(&third_rc);\nassert!(!first.ptr_eq(&third));
Drops the Weak
pointer.
use std::sync::{Arc, Weak};\n\nstruct Foo;\n\nimpl Drop for Foo {\n fn drop(&mut self) {\n println!(\"dropped!\");\n }\n}\n\nlet foo = Arc::new(Foo);\nlet weak_foo = Arc::downgrade(&foo);\nlet other_weak_foo = Weak::clone(&weak_foo);\n\ndrop(weak_foo); // Doesn't print anything\ndrop(foo); // Prints \"dropped!\"\n\nassert!(other_weak_foo.upgrade().is_none());
Makes a clone of the Weak
pointer that points to the same allocation.
use std::sync::{Arc, Weak};\n\nlet weak_five = Arc::downgrade(&Arc::new(5));\n\nlet _ = Weak::clone(&weak_five);
source
. Read moreConstruct a new Pin<P>
around a pointer to some data of a type that\nimplements Unpin
.
Unlike Pin::new_unchecked
, this method is safe because the pointer\nP
dereferences to an Unpin
type, which cancels the pinning guarantees.
use std::pin::Pin;\n\nlet mut val: u8 = 5;\n// We can pin the value, since it doesn't care about being moved\nlet mut pinned: Pin<&mut u8> = Pin::new(&mut val);
Unwraps this Pin<P>
returning the underlying pointer.
This requires that the data inside this Pin
implements Unpin
so that we\ncan ignore the pinning invariants when unwrapping it.
use std::pin::Pin;\n\nlet mut val: u8 = 5;\nlet pinned: Pin<&mut u8> = Pin::new(&mut val);\n// Unwrap the pin to get a reference to the value\nlet r = Pin::into_inner(pinned);\nassert_eq!(*r, 5);
Construct a new Pin<P>
around a reference to some data of a type that\nmay or may not implement Unpin
.
If pointer
dereferences to an Unpin
type, Pin::new
should be used\ninstead.
This constructor is unsafe because we cannot guarantee that the data\npointed to by pointer
is pinned, meaning that the data will not be moved or\nits storage invalidated until it gets dropped. If the constructed Pin<P>
does\nnot guarantee that the data P
points to is pinned, that is a violation of\nthe API contract and may lead to undefined behavior in later (safe) operations.
By using this method, you are making a promise about the P::Deref
and\nP::DerefMut
implementations, if they exist. Most importantly, they\nmust not move out of their self
arguments: Pin::as_mut
and Pin::as_ref
\nwill call DerefMut::deref_mut
and Deref::deref
on the pinned pointer\nand expect these methods to uphold the pinning invariants.\nMoreover, by calling this method you promise that the reference P
\ndereferences to will not be moved out of again; in particular, it\nmust not be possible to obtain a &mut P::Target
and then\nmove out of that reference (using, for example mem::swap
).
For example, calling Pin::new_unchecked
on an &'a mut T
is unsafe because\nwhile you are able to pin it for the given lifetime 'a
, you have no control\nover whether it is kept pinned once 'a
ends:
use std::mem;\nuse std::pin::Pin;\n\nfn move_pinned_ref<T>(mut a: T, mut b: T) {\n unsafe {\n let p: Pin<&mut T> = Pin::new_unchecked(&mut a);\n // This should mean the pointee `a` can never move again.\n }\n mem::swap(&mut a, &mut b); // Potential UB down the road ⚠️\n // The address of `a` changed to `b`'s stack slot, so `a` got moved even\n // though we have previously pinned it! We have violated the pinning API contract.\n}
A value, once pinned, must remain pinned until it is dropped (unless its type implements\nUnpin
). Because Pin<&mut T>
does not own the value, dropping the Pin
will not drop\nthe value and will not end the pinning contract. So moving the value after dropping the\nPin<&mut T>
is still a violation of the API contract.
Similarly, calling Pin::new_unchecked
on an Rc<T>
is unsafe because there could be\naliases to the same data that are not subject to the pinning restrictions:
use std::rc::Rc;\nuse std::pin::Pin;\n\nfn move_pinned_rc<T>(mut x: Rc<T>) {\n let pinned = unsafe { Pin::new_unchecked(Rc::clone(&x)) };\n {\n let p: Pin<&T> = pinned.as_ref();\n // This should mean the pointee can never move again.\n }\n drop(pinned);\n let content = Rc::get_mut(&mut x).unwrap(); // Potential UB down the road ⚠️\n // Now, if `x` was the only reference, we have a mutable reference to\n // data that we pinned above, which we could use to move it as we have\n // seen in the previous example. We have violated the pinning API contract.\n }
Particular care is required when using Pin::new_unchecked
in a closure:\nPin::new_unchecked(&mut var)
where var
is a by-value (moved) closure capture\nimplicitly makes the promise that the closure itself is pinned, and that all uses\nof this closure capture respect that pinning.
use std::pin::Pin;\nuse std::task::Context;\nuse std::future::Future;\n\nfn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) {\n // Create a closure that moves `x`, and then internally uses it in a pinned way.\n let mut closure = move || unsafe {\n let _ignore = Pin::new_unchecked(&mut x).poll(cx);\n };\n // Call the closure, so the future can assume it has been pinned.\n closure();\n // Move the closure somewhere else. This also moves `x`!\n let mut moved = closure;\n // Calling it again means we polled the future from two different locations,\n // violating the pinning API contract.\n moved(); // Potential UB ⚠️\n}
When passing a closure to another API, it might be moving the closure any time, so\nPin::new_unchecked
on closure captures may only be used if the API explicitly documents\nthat the closure is pinned.
The better alternative is to avoid all that trouble and do the pinning in the outer function\ninstead (here using the pin!
macro):
use std::pin::pin;\nuse std::task::Context;\nuse std::future::Future;\n\nfn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) {\n let mut x = pin!(x);\n // Create a closure that captures `x: Pin<&mut _>`, which is safe to move.\n let mut closure = move || {\n let _ignore = x.as_mut().poll(cx);\n };\n // Call the closure, so the future can assume it has been pinned.\n closure();\n // Move the closure somewhere else.\n let mut moved = closure;\n // Calling it again here is fine (except that we might be polling a future that already\n // returned `Poll::Ready`, but that is a separate problem).\n moved();\n}
Gets a pinned shared reference from this pinned pointer.
\nThis is a generic method to go from &Pin<Pointer<T>>
to Pin<&T>
.\nIt is safe because, as part of the contract of Pin::new_unchecked
,\nthe pointee cannot move after Pin<Pointer<T>>
got created.\n“Malicious” implementations of Pointer::Deref
are likewise\nruled out by the contract of Pin::new_unchecked
.
Unwraps this Pin<P>
returning the underlying pointer.
This function is unsafe. You must guarantee that you will continue to\ntreat the pointer P
as pinned after you call this function, so that\nthe invariants on the Pin
type can be upheld. If the code using the\nresulting P
does not continue to maintain the pinning invariants that\nis a violation of the API contract and may lead to undefined behavior in\nlater (safe) operations.
If the underlying data is Unpin
, Pin::into_inner
should be used\ninstead.
Gets a pinned mutable reference from this pinned pointer.
\nThis is a generic method to go from &mut Pin<Pointer<T>>
to Pin<&mut T>
.\nIt is safe because, as part of the contract of Pin::new_unchecked
,\nthe pointee cannot move after Pin<Pointer<T>>
got created.\n“Malicious” implementations of Pointer::DerefMut
are likewise\nruled out by the contract of Pin::new_unchecked
.
This method is useful when doing multiple calls to functions that consume the pinned type.
\nuse std::pin::Pin;\n\nimpl Type {\n fn method(self: Pin<&mut Self>) {\n // do something\n }\n\n fn call_method_twice(mut self: Pin<&mut Self>) {\n // `method` consumes `self`, so reborrow the `Pin<&mut Self>` via `as_mut`.\n self.as_mut().method();\n self.as_mut().method();\n }\n}
Assigns a new value to the memory behind the pinned reference.
\nThis overwrites pinned data, but that is okay: its destructor gets\nrun before being overwritten, so no pinning guarantee is violated.
\nuse std::pin::Pin;\n\nlet mut val: u8 = 5;\nlet mut pinned: Pin<&mut u8> = Pin::new(&mut val);\nprintln!(\"{}\", pinned); // 5\npinned.as_mut().set(10);\nprintln!(\"{}\", pinned); // 10
async_iterator
)async_iterator
)None
if the async iterator is exhausted. Read moreConverts a Box<T>
into a Pin<Box<T>>
. If T
does not implement Unpin
, then\n*boxed
will be pinned in memory and unable to be moved.
This conversion does not allocate on the heap and happens in place.
\nThis is also available via Box::into_pin
.
Constructing and pinning a Box
with <Pin<Box<T>>>::from(Box::new(x))
\ncan also be written more concisely using Box::pin(x)
.\nThis From
implementation is useful if you already have a Box<T>
, or you are\nconstructing a (pinned) Box
in a different way than with Box::new
.
coroutine_trait
)true
if the underlying future should no longer be polled.true
if the stream should no longer be polled.buf
into the object. Read morebufs
into the object using vectored\nIO operations. Read moreReturns true
if the result is Ok
and the value inside of it matches a predicate.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);
Returns true
if the result is Err
and the value inside of it matches a predicate.
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
Converts from Result<T, E>
to Option<E>
.
Converts self
into an Option<E>
, consuming self
,\nand discarding the success value, if any.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
Converts from &Result<T, E>
to Result<&T, &E>
.
Produces a new Result
, containing a reference\ninto the original, leaving the original in place.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
Converts from &mut Result<T, E>
to Result<&mut T, &mut E>
.
fn mutate(r: &mut Result<i32, i32>) {\n match r.as_mut() {\n Ok(v) => *v = 42,\n Err(e) => *e = 0,\n }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
Maps a Result<T, E>
to Result<U, E>
by applying a function to a\ncontained Ok
value, leaving an Err
value untouched.
This function can be used to compose the results of two functions.
\nPrint the numbers on each line of a string multiplied by two.
\n\nlet line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n match num.parse::<i32>().map(|i| i * 2) {\n Ok(n) => println!(\"{n}\"),\n Err(..) => {}\n }\n}
Returns the provided default (if Err
), or\napplies a function to the contained value (if Ok
).
Arguments passed to map_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else
,\nwhich is lazily evaluated.
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
Maps a Result<T, E>
to U
by applying fallback function default
to\na contained Err
value, or function f
to a contained Ok
value.
This function can be used to unpack a successful result\nwhile handling an error.
\nlet k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
Maps a Result<T, E>
to Result<T, F>
by applying a function to a\ncontained Err
value, leaving an Ok
value untouched.
This function can be used to pass through a successful result while handling\nan error.
\nfn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
Converts from Result<T, E>
(or &Result<T, E>
) to Result<&<T as Deref>::Target, &E>
.
Coerces the Ok
variant of the original Result
via Deref
\nand returns the new Result
.
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
Converts from Result<T, E>
(or &mut Result<T, E>
) to Result<&mut <T as DerefMut>::Target, &mut E>
.
Coerces the Ok
variant of the original Result
via DerefMut
\nand returns the new Result
.
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
Returns an iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
Returns a mutable iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n Some(v) => *v = 40,\n None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message including the\npassed message, and the content of the Err
.
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
We recommend that expect
messages are used to describe the reason you\nexpect the Result
should be Ok
.
let path = std::env::var(\"IMPORTANT_PATH\")\n .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.
\nFor more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error
module docs.
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message provided by the\nErr
’s value.
Basic usage:
\n\nlet x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
Returns the contained Ok
value or a default
Consumes the self
argument then, if Ok
, returns the contained\nvalue, otherwise if Err
, returns the default value for that\ntype.
Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse
converts\na string to any other type that implements FromStr
, returning an\nErr
on error.
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a panic message including the\npassed message, and the content of the Ok
.
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a custom panic message provided\nby the Ok
’s value.
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
unwrap_infallible
)Returns the contained Ok
value, but never panics.
Unlike unwrap
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap
as a maintainability safeguard that will fail\nto compile if the error type of the Result
is later changed\nto an error that can actually occur.
\nfn only_good_news() -> Result<String, !> {\n Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
unwrap_infallible
)Returns the contained Err
value, but never panics.
Unlike unwrap_err
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err
as a maintainability safeguard that will fail\nto compile if the ok type of the Result
is later changed\nto a type that can actually occur.
\nfn only_bad_news() -> Result<!, String> {\n Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
Returns res
if the result is Ok
, otherwise returns the Err
value of self
.
Arguments passed to and
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
Calls op
if the result is Ok
, otherwise returns the Err
value of self
.
This function can be used for control flow based on Result
values.
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
Often used to chain fallible operations that may return Err
.
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
Returns res
if the result is Err
, otherwise returns the Ok
value of self
.
Arguments passed to or
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
Calls op
if the result is Err
, otherwise returns the Ok
value of self
.
This function can be used for control flow based on result values.
\nfn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
Returns the contained Ok
value or a provided default.
Arguments passed to unwrap_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else
,\nwhich is lazily evaluated.
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
Returns the contained Ok
value, consuming the self
value,\nwithout checking that the value is not an Err
.
Calling this method on an Err
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked(); } // Undefined behavior!
Returns the contained Err
value, consuming the self
value,\nwithout checking that the value is not an Ok
.
Calling this method on an Ok
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
Maps a Result<&mut T, E>
to a Result<T, E>
by copying the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
Maps a Result<&mut T, E>
to a Result<T, E>
by cloning the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
Transposes a Result
of an Option
into an Option
of a Result
.
Ok(None)
will be mapped to None
.\nOk(Some(_))
and Err(_)
will be mapped to Some(Ok(_))
and Some(Err(_))
.
#[derive(Debug, Eq, PartialEq)]\nstruct SomeErr;\n\nlet x: Result<Option<i32>, SomeErr> = Ok(Some(5));\nlet y: Option<Result<i32, SomeErr>> = Some(Ok(5));\nassert_eq!(x.transpose(), y);
result_flattening
)Converts from Result<Result<T, E>, E>
to Result<T, E>
#![feature(result_flattening)]\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Ok(\"hello\"));\nassert_eq!(Ok(\"hello\"), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));\nassert_eq!(Err(6), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Err(6);\nassert_eq!(Err(6), x.flatten());
Flattening only removes one level of nesting at a time:
\n\n#![feature(result_flattening)]\nlet x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok(\"hello\")));\nassert_eq!(Ok(Ok(\"hello\")), x.flatten());\nassert_eq!(Ok(\"hello\"), x.flatten().flatten());
self
and other
) and is used by the <=
\noperator. Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the product of all elements is returned.
This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err
:
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
try_trait_v2
)Residual
type. Read moretry_trait_v2
)?
when not short-circuiting.try_trait_v2
)FromResidual::from_residual
\nas part of ?
when short-circuiting. Read moretry_trait_v2
)Output
type. Read moretry_trait_v2
)?
to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue
)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break
). Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
occur, a\ncontainer with the values of each Result
is returned.
Here is an example which increments every integer in a vector,\nchecking for overflow:
\n\nlet v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:
\n\nlet v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
Here is a variation on the previous example, showing that no\nfurther elements are taken from iter
after the first Err
.
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n shared += x;\n x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
Since the third element caused an underflow, no further elements were taken,\nso the final value of shared
is 6 (= 3 + 2 + 1
), not 16.
Returns a consuming iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the sum of all elements is returned.
This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:
\n\nlet f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
Returns true
if the result is Ok
and the value inside of it matches a predicate.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);
Returns true
if the result is Err
and the value inside of it matches a predicate.
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
Converts from Result<T, E>
to Option<E>
.
Converts self
into an Option<E>
, consuming self
,\nand discarding the success value, if any.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
Converts from &Result<T, E>
to Result<&T, &E>
.
Produces a new Result
, containing a reference\ninto the original, leaving the original in place.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
Converts from &mut Result<T, E>
to Result<&mut T, &mut E>
.
fn mutate(r: &mut Result<i32, i32>) {\n match r.as_mut() {\n Ok(v) => *v = 42,\n Err(e) => *e = 0,\n }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
Maps a Result<T, E>
to Result<U, E>
by applying a function to a\ncontained Ok
value, leaving an Err
value untouched.
This function can be used to compose the results of two functions.
\nPrint the numbers on each line of a string multiplied by two.
\n\nlet line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n match num.parse::<i32>().map(|i| i * 2) {\n Ok(n) => println!(\"{n}\"),\n Err(..) => {}\n }\n}
Returns the provided default (if Err
), or\napplies a function to the contained value (if Ok
).
Arguments passed to map_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else
,\nwhich is lazily evaluated.
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
Maps a Result<T, E>
to U
by applying fallback function default
to\na contained Err
value, or function f
to a contained Ok
value.
This function can be used to unpack a successful result\nwhile handling an error.
\nlet k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
Maps a Result<T, E>
to Result<T, F>
by applying a function to a\ncontained Err
value, leaving an Ok
value untouched.
This function can be used to pass through a successful result while handling\nan error.
\nfn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
Converts from Result<T, E>
(or &Result<T, E>
) to Result<&<T as Deref>::Target, &E>
.
Coerces the Ok
variant of the original Result
via Deref
\nand returns the new Result
.
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
Converts from Result<T, E>
(or &mut Result<T, E>
) to Result<&mut <T as DerefMut>::Target, &mut E>
.
Coerces the Ok
variant of the original Result
via DerefMut
\nand returns the new Result
.
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
Returns an iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
Returns a mutable iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n Some(v) => *v = 40,\n None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message including the\npassed message, and the content of the Err
.
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
We recommend that expect
messages are used to describe the reason you\nexpect the Result
should be Ok
.
let path = std::env::var(\"IMPORTANT_PATH\")\n .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.
\nFor more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error
module docs.
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message provided by the\nErr
’s value.
Basic usage:
\n\nlet x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
Returns the contained Ok
value or a default
Consumes the self
argument then, if Ok
, returns the contained\nvalue, otherwise if Err
, returns the default value for that\ntype.
Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse
converts\na string to any other type that implements FromStr
, returning an\nErr
on error.
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a panic message including the\npassed message, and the content of the Ok
.
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a custom panic message provided\nby the Ok
’s value.
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
unwrap_infallible
)Returns the contained Ok
value, but never panics.
Unlike unwrap
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap
as a maintainability safeguard that will fail\nto compile if the error type of the Result
is later changed\nto an error that can actually occur.
\nfn only_good_news() -> Result<String, !> {\n Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
unwrap_infallible
)Returns the contained Err
value, but never panics.
Unlike unwrap_err
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err
as a maintainability safeguard that will fail\nto compile if the ok type of the Result
is later changed\nto a type that can actually occur.
\nfn only_bad_news() -> Result<!, String> {\n Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
Returns res
if the result is Ok
, otherwise returns the Err
value of self
.
Arguments passed to and
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
Calls op
if the result is Ok
, otherwise returns the Err
value of self
.
This function can be used for control flow based on Result
values.
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
Often used to chain fallible operations that may return Err
.
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
Returns res
if the result is Err
, otherwise returns the Ok
value of self
.
Arguments passed to or
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
Calls op
if the result is Err
, otherwise returns the Ok
value of self
.
This function can be used for control flow based on result values.
\nfn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
Returns the contained Ok
value or a provided default.
Arguments passed to unwrap_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else
,\nwhich is lazily evaluated.
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
Returns the contained Ok
value, consuming the self
value,\nwithout checking that the value is not an Err
.
Calling this method on an Err
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked(); } // Undefined behavior!
Returns the contained Err
value, consuming the self
value,\nwithout checking that the value is not an Ok
.
Calling this method on an Ok
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
Maps a Result<&mut T, E>
to a Result<T, E>
by copying the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
Maps a Result<&mut T, E>
to a Result<T, E>
by cloning the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
Transposes a Result
of an Option
into an Option
of a Result
.
Ok(None)
will be mapped to None
.\nOk(Some(_))
and Err(_)
will be mapped to Some(Ok(_))
and Some(Err(_))
.
#[derive(Debug, Eq, PartialEq)]\nstruct SomeErr;\n\nlet x: Result<Option<i32>, SomeErr> = Ok(Some(5));\nlet y: Option<Result<i32, SomeErr>> = Some(Ok(5));\nassert_eq!(x.transpose(), y);
result_flattening
)Converts from Result<Result<T, E>, E>
to Result<T, E>
#![feature(result_flattening)]\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Ok(\"hello\"));\nassert_eq!(Ok(\"hello\"), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));\nassert_eq!(Err(6), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Err(6);\nassert_eq!(Err(6), x.flatten());
Flattening only removes one level of nesting at a time:
\n\n#![feature(result_flattening)]\nlet x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok(\"hello\")));\nassert_eq!(Ok(Ok(\"hello\")), x.flatten());\nassert_eq!(Ok(\"hello\"), x.flatten().flatten());
self
and other
) and is used by the <=
\noperator. Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the product of all elements is returned.
This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err
:
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
try_trait_v2
)Residual
type. Read moretry_trait_v2
)?
when not short-circuiting.try_trait_v2
)FromResidual::from_residual
\nas part of ?
when short-circuiting. Read moretry_trait_v2
)Output
type. Read moretry_trait_v2
)?
to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue
)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break
). Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
occur, a\ncontainer with the values of each Result
is returned.
Here is an example which increments every integer in a vector,\nchecking for overflow:
\n\nlet v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:
\n\nlet v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
Here is a variation on the previous example, showing that no\nfurther elements are taken from iter
after the first Err
.
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n shared += x;\n x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
Since the third element caused an underflow, no further elements were taken,\nso the final value of shared
is 6 (= 3 + 2 + 1
), not 16.
Returns a consuming iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the sum of all elements is returned.
This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:
\n\nlet f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
Returns true
if the result is Ok
and the value inside of it matches a predicate.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);
Returns true
if the result is Err
and the value inside of it matches a predicate.
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
Converts from Result<T, E>
to Option<E>
.
Converts self
into an Option<E>
, consuming self
,\nand discarding the success value, if any.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
Converts from &Result<T, E>
to Result<&T, &E>
.
Produces a new Result
, containing a reference\ninto the original, leaving the original in place.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
Converts from &mut Result<T, E>
to Result<&mut T, &mut E>
.
fn mutate(r: &mut Result<i32, i32>) {\n match r.as_mut() {\n Ok(v) => *v = 42,\n Err(e) => *e = 0,\n }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
Maps a Result<T, E>
to Result<U, E>
by applying a function to a\ncontained Ok
value, leaving an Err
value untouched.
This function can be used to compose the results of two functions.
\nPrint the numbers on each line of a string multiplied by two.
\n\nlet line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n match num.parse::<i32>().map(|i| i * 2) {\n Ok(n) => println!(\"{n}\"),\n Err(..) => {}\n }\n}
Returns the provided default (if Err
), or\napplies a function to the contained value (if Ok
).
Arguments passed to map_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else
,\nwhich is lazily evaluated.
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
Maps a Result<T, E>
to U
by applying fallback function default
to\na contained Err
value, or function f
to a contained Ok
value.
This function can be used to unpack a successful result\nwhile handling an error.
\nlet k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
Maps a Result<T, E>
to Result<T, F>
by applying a function to a\ncontained Err
value, leaving an Ok
value untouched.
This function can be used to pass through a successful result while handling\nan error.
\nfn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
Converts from Result<T, E>
(or &Result<T, E>
) to Result<&<T as Deref>::Target, &E>
.
Coerces the Ok
variant of the original Result
via Deref
\nand returns the new Result
.
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
Converts from Result<T, E>
(or &mut Result<T, E>
) to Result<&mut <T as DerefMut>::Target, &mut E>
.
Coerces the Ok
variant of the original Result
via DerefMut
\nand returns the new Result
.
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
Returns an iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
Returns a mutable iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n Some(v) => *v = 40,\n None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message including the\npassed message, and the content of the Err
.
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
We recommend that expect
messages are used to describe the reason you\nexpect the Result
should be Ok
.
let path = std::env::var(\"IMPORTANT_PATH\")\n .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.
\nFor more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error
module docs.
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message provided by the\nErr
’s value.
Basic usage:
\n\nlet x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
Returns the contained Ok
value or a default
Consumes the self
argument then, if Ok
, returns the contained\nvalue, otherwise if Err
, returns the default value for that\ntype.
Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse
converts\na string to any other type that implements FromStr
, returning an\nErr
on error.
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a panic message including the\npassed message, and the content of the Ok
.
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a custom panic message provided\nby the Ok
’s value.
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
unwrap_infallible
)Returns the contained Ok
value, but never panics.
Unlike unwrap
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap
as a maintainability safeguard that will fail\nto compile if the error type of the Result
is later changed\nto an error that can actually occur.
\nfn only_good_news() -> Result<String, !> {\n Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
unwrap_infallible
)Returns the contained Err
value, but never panics.
Unlike unwrap_err
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err
as a maintainability safeguard that will fail\nto compile if the ok type of the Result
is later changed\nto a type that can actually occur.
\nfn only_bad_news() -> Result<!, String> {\n Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
Returns res
if the result is Ok
, otherwise returns the Err
value of self
.
Arguments passed to and
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
Calls op
if the result is Ok
, otherwise returns the Err
value of self
.
This function can be used for control flow based on Result
values.
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
Often used to chain fallible operations that may return Err
.
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
Returns res
if the result is Err
, otherwise returns the Ok
value of self
.
Arguments passed to or
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
Calls op
if the result is Err
, otherwise returns the Ok
value of self
.
This function can be used for control flow based on result values.
\nfn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
Returns the contained Ok
value or a provided default.
Arguments passed to unwrap_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else
,\nwhich is lazily evaluated.
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
Returns the contained Ok
value, consuming the self
value,\nwithout checking that the value is not an Err
.
Calling this method on an Err
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked(); } // Undefined behavior!
Returns the contained Err
value, consuming the self
value,\nwithout checking that the value is not an Ok
.
Calling this method on an Ok
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
Maps a Result<&mut T, E>
to a Result<T, E>
by copying the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
Maps a Result<&mut T, E>
to a Result<T, E>
by cloning the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
Transposes a Result
of an Option
into an Option
of a Result
.
Ok(None)
will be mapped to None
.\nOk(Some(_))
and Err(_)
will be mapped to Some(Ok(_))
and Some(Err(_))
.
#[derive(Debug, Eq, PartialEq)]\nstruct SomeErr;\n\nlet x: Result<Option<i32>, SomeErr> = Ok(Some(5));\nlet y: Option<Result<i32, SomeErr>> = Some(Ok(5));\nassert_eq!(x.transpose(), y);
result_flattening
)Converts from Result<Result<T, E>, E>
to Result<T, E>
#![feature(result_flattening)]\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Ok(\"hello\"));\nassert_eq!(Ok(\"hello\"), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));\nassert_eq!(Err(6), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Err(6);\nassert_eq!(Err(6), x.flatten());
Flattening only removes one level of nesting at a time:
\n\n#![feature(result_flattening)]\nlet x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok(\"hello\")));\nassert_eq!(Ok(Ok(\"hello\")), x.flatten());\nassert_eq!(Ok(\"hello\"), x.flatten().flatten());
self
and other
) and is used by the <=
\noperator. Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the product of all elements is returned.
This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err
:
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
try_trait_v2
)Residual
type. Read moretry_trait_v2
)?
when not short-circuiting.try_trait_v2
)FromResidual::from_residual
\nas part of ?
when short-circuiting. Read moretry_trait_v2
)Output
type. Read moretry_trait_v2
)?
to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue
)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break
). Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
occur, a\ncontainer with the values of each Result
is returned.
Here is an example which increments every integer in a vector,\nchecking for overflow:
\n\nlet v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:
\n\nlet v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
Here is a variation on the previous example, showing that no\nfurther elements are taken from iter
after the first Err
.
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n shared += x;\n x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
Since the third element caused an underflow, no further elements were taken,\nso the final value of shared
is 6 (= 3 + 2 + 1
), not 16.
Returns a consuming iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the sum of all elements is returned.
This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:
\n\nlet f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
Returns true
if the result is Ok
and the value inside of it matches a predicate.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);
Returns true
if the result is Err
and the value inside of it matches a predicate.
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);
Converts from Result<T, E>
to Option<E>
.
Converts self
into an Option<E>
, consuming self
,\nand discarding the success value, if any.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
Converts from &Result<T, E>
to Result<&T, &E>
.
Produces a new Result
, containing a reference\ninto the original, leaving the original in place.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
Converts from &mut Result<T, E>
to Result<&mut T, &mut E>
.
fn mutate(r: &mut Result<i32, i32>) {\n match r.as_mut() {\n Ok(v) => *v = 42,\n Err(e) => *e = 0,\n }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
Maps a Result<T, E>
to Result<U, E>
by applying a function to a\ncontained Ok
value, leaving an Err
value untouched.
This function can be used to compose the results of two functions.
\nPrint the numbers on each line of a string multiplied by two.
\n\nlet line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n match num.parse::<i32>().map(|i| i * 2) {\n Ok(n) => println!(\"{n}\"),\n Err(..) => {}\n }\n}
Returns the provided default (if Err
), or\napplies a function to the contained value (if Ok
).
Arguments passed to map_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else
,\nwhich is lazily evaluated.
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
Maps a Result<T, E>
to U
by applying fallback function default
to\na contained Err
value, or function f
to a contained Ok
value.
This function can be used to unpack a successful result\nwhile handling an error.
\nlet k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
Maps a Result<T, E>
to Result<T, F>
by applying a function to a\ncontained Err
value, leaving an Ok
value untouched.
This function can be used to pass through a successful result while handling\nan error.
\nfn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
Converts from Result<T, E>
(or &Result<T, E>
) to Result<&<T as Deref>::Target, &E>
.
Coerces the Ok
variant of the original Result
via Deref
\nand returns the new Result
.
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
Converts from Result<T, E>
(or &mut Result<T, E>
) to Result<&mut <T as DerefMut>::Target, &mut E>
.
Coerces the Ok
variant of the original Result
via DerefMut
\nand returns the new Result
.
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
Returns an iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
Returns a mutable iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n Some(v) => *v = 40,\n None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message including the\npassed message, and the content of the Err
.
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
We recommend that expect
messages are used to describe the reason you\nexpect the Result
should be Ok
.
let path = std::env::var(\"IMPORTANT_PATH\")\n .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.
\nFor more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error
module docs.
Returns the contained Ok
value, consuming the self
value.
Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err
\ncase explicitly, or call unwrap_or
, unwrap_or_else
, or\nunwrap_or_default
.
Panics if the value is an Err
, with a panic message provided by the\nErr
’s value.
Basic usage:
\n\nlet x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
Returns the contained Ok
value or a default
Consumes the self
argument then, if Ok
, returns the contained\nvalue, otherwise if Err
, returns the default value for that\ntype.
Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse
converts\na string to any other type that implements FromStr
, returning an\nErr
on error.
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a panic message including the\npassed message, and the content of the Ok
.
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
Returns the contained Err
value, consuming the self
value.
Panics if the value is an Ok
, with a custom panic message provided\nby the Ok
’s value.
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
unwrap_infallible
)Returns the contained Ok
value, but never panics.
Unlike unwrap
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap
as a maintainability safeguard that will fail\nto compile if the error type of the Result
is later changed\nto an error that can actually occur.
\nfn only_good_news() -> Result<String, !> {\n Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
unwrap_infallible
)Returns the contained Err
value, but never panics.
Unlike unwrap_err
, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err
as a maintainability safeguard that will fail\nto compile if the ok type of the Result
is later changed\nto a type that can actually occur.
\nfn only_bad_news() -> Result<!, String> {\n Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
Returns res
if the result is Ok
, otherwise returns the Err
value of self
.
Arguments passed to and
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
Calls op
if the result is Ok
, otherwise returns the Err
value of self
.
This function can be used for control flow based on Result
values.
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
Often used to chain fallible operations that may return Err
.
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
Returns res
if the result is Err
, otherwise returns the Ok
value of self
.
Arguments passed to or
are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else
, which is\nlazily evaluated.
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
Calls op
if the result is Err
, otherwise returns the Ok
value of self
.
This function can be used for control flow based on result values.
\nfn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
Returns the contained Ok
value or a provided default.
Arguments passed to unwrap_or
are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else
,\nwhich is lazily evaluated.
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
Returns the contained Ok
value, consuming the self
value,\nwithout checking that the value is not an Err
.
Calling this method on an Err
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked(); } // Undefined behavior!
Returns the contained Err
value, consuming the self
value,\nwithout checking that the value is not an Ok
.
Calling this method on an Ok
is undefined behavior.
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
Maps a Result<&mut T, E>
to a Result<T, E>
by copying the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
Maps a Result<&mut T, E>
to a Result<T, E>
by cloning the contents of the\nOk
part.
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
Transposes a Result
of an Option
into an Option
of a Result
.
Ok(None)
will be mapped to None
.\nOk(Some(_))
and Err(_)
will be mapped to Some(Ok(_))
and Some(Err(_))
.
#[derive(Debug, Eq, PartialEq)]\nstruct SomeErr;\n\nlet x: Result<Option<i32>, SomeErr> = Ok(Some(5));\nlet y: Option<Result<i32, SomeErr>> = Some(Ok(5));\nassert_eq!(x.transpose(), y);
result_flattening
)Converts from Result<Result<T, E>, E>
to Result<T, E>
#![feature(result_flattening)]\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Ok(\"hello\"));\nassert_eq!(Ok(\"hello\"), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));\nassert_eq!(Err(6), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Err(6);\nassert_eq!(Err(6), x.flatten());
Flattening only removes one level of nesting at a time:
\n\n#![feature(result_flattening)]\nlet x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok(\"hello\")));\nassert_eq!(Ok(Ok(\"hello\")), x.flatten());\nassert_eq!(Ok(\"hello\"), x.flatten().flatten());
self
and other
) and is used by the <=
\noperator. Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the product of all elements is returned.
This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err
:
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
try_trait_v2
)Residual
type. Read moretry_trait_v2
)?
when not short-circuiting.try_trait_v2
)FromResidual::from_residual
\nas part of ?
when short-circuiting. Read moretry_trait_v2
)Output
type. Read moretry_trait_v2
)?
to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue
)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break
). Read moreTakes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
occur, a\ncontainer with the values of each Result
is returned.
Here is an example which increments every integer in a vector,\nchecking for overflow:
\n\nlet v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:
\n\nlet v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
Here is a variation on the previous example, showing that no\nfurther elements are taken from iter
after the first Err
.
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n shared += x;\n x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
Since the third element caused an underflow, no further elements were taken,\nso the final value of shared
is 6 (= 3 + 2 + 1
), not 16.
Returns a consuming iterator over the possibly contained value.
\nThe iterator yields one value if the result is Result::Ok
, otherwise none.
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
Takes each element in the Iterator
: if it is an Err
, no further\nelements are taken, and the Err
is returned. Should no Err
\noccur, the sum of all elements is returned.
This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:
\n\nlet f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
Creates an empty HashMap
.
The hash map is initially created with a capacity of 0, so it will not allocate until it\nis first inserted into.
\nuse std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::new();
Creates an empty HashMap
with at least the specified capacity.
The hash map will be able to hold at least capacity
elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity
. If capacity
is 0, the hash map will not allocate.
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::with_capacity(10);
Creates an empty HashMap
which will use the given hash builder to hash\nkeys.
The created map has the default initial capacity.
\nWarning: hash_builder
is normally randomly generated, and\nis designed to allow HashMaps to be resistant to attacks that\ncause many collisions and very poor performance. Setting it\nmanually using this function can expose a DoS attack vector.
The hash_builder
passed should implement the BuildHasher
trait for\nthe HashMap to be useful, see its documentation for details.
use std::collections::HashMap;\nuse std::hash::RandomState;\n\nlet s = RandomState::new();\nlet mut map = HashMap::with_hasher(s);\nmap.insert(1, 2);
Creates an empty HashMap
with at least the specified capacity, using\nhasher
to hash the keys.
The hash map will be able to hold at least capacity
elements without\nreallocating. This method is allowed to allocate for more elements than\ncapacity
. If capacity
is 0, the hash map will not allocate.
Warning: hasher
is normally randomly generated, and\nis designed to allow HashMaps to be resistant to attacks that\ncause many collisions and very poor performance. Setting it\nmanually using this function can expose a DoS attack vector.
The hasher
passed should implement the BuildHasher
trait for\nthe HashMap to be useful, see its documentation for details.
use std::collections::HashMap;\nuse std::hash::RandomState;\n\nlet s = RandomState::new();\nlet mut map = HashMap::with_capacity_and_hasher(10, s);\nmap.insert(1, 2);
Returns the number of elements the map can hold without reallocating.
\nThis number is a lower bound; the HashMap<K, V>
might be able to hold\nmore, but is guaranteed to be able to hold at least this many.
use std::collections::HashMap;\nlet map: HashMap<i32, i32> = HashMap::with_capacity(100);\nassert!(map.capacity() >= 100);
An iterator visiting all keys in arbitrary order.\nThe iterator element type is &'a K
.
use std::collections::HashMap;\n\nlet map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\nfor key in map.keys() {\n println!(\"{key}\");\n}
In the current implementation, iterating over keys takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nCreates a consuming iterator visiting all the keys in arbitrary order.\nThe map cannot be used after calling this.\nThe iterator element type is K
.
use std::collections::HashMap;\n\nlet map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\nlet mut vec: Vec<&str> = map.into_keys().collect();\n// The `IntoKeys` iterator produces keys in arbitrary order, so the\n// keys must be sorted to test them against a sorted array.\nvec.sort_unstable();\nassert_eq!(vec, [\"a\", \"b\", \"c\"]);
In the current implementation, iterating over keys takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nAn iterator visiting all values in arbitrary order.\nThe iterator element type is &'a V
.
use std::collections::HashMap;\n\nlet map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\nfor val in map.values() {\n println!(\"{val}\");\n}
In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nAn iterator visiting all values mutably in arbitrary order.\nThe iterator element type is &'a mut V
.
use std::collections::HashMap;\n\nlet mut map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\nfor val in map.values_mut() {\n *val = *val + 10;\n}\n\nfor val in map.values() {\n println!(\"{val}\");\n}
In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nCreates a consuming iterator visiting all the values in arbitrary order.\nThe map cannot be used after calling this.\nThe iterator element type is V
.
use std::collections::HashMap;\n\nlet map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\nlet mut vec: Vec<i32> = map.into_values().collect();\n// The `IntoValues` iterator produces values in arbitrary order, so\n// the values must be sorted to test them against a sorted array.\nvec.sort_unstable();\nassert_eq!(vec, [1, 2, 3]);
In the current implementation, iterating over values takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nAn iterator visiting all key-value pairs in arbitrary order.\nThe iterator element type is (&'a K, &'a V)
.
use std::collections::HashMap;\n\nlet map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\nfor (key, val) in map.iter() {\n println!(\"key: {key} val: {val}\");\n}
In the current implementation, iterating over map takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nAn iterator visiting all key-value pairs in arbitrary order,\nwith mutable references to the values.\nThe iterator element type is (&'a K, &'a mut V)
.
use std::collections::HashMap;\n\nlet mut map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\n// Update all values\nfor (_, val) in map.iter_mut() {\n *val *= 2;\n}\n\nfor (key, val) in &map {\n println!(\"key: {key} val: {val}\");\n}
In the current implementation, iterating over map takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nReturns the number of elements in the map.
\nuse std::collections::HashMap;\n\nlet mut a = HashMap::new();\nassert_eq!(a.len(), 0);\na.insert(1, \"a\");\nassert_eq!(a.len(), 1);
Returns true
if the map contains no elements.
use std::collections::HashMap;\n\nlet mut a = HashMap::new();\nassert!(a.is_empty());\na.insert(1, \"a\");\nassert!(!a.is_empty());
Clears the map, returning all key-value pairs as an iterator. Keeps the\nallocated memory for reuse.
\nIf the returned iterator is dropped before being fully consumed, it\ndrops the remaining key-value pairs. The returned iterator keeps a\nmutable borrow on the map to optimize its implementation.
\nuse std::collections::HashMap;\n\nlet mut a = HashMap::new();\na.insert(1, \"a\");\na.insert(2, \"b\");\n\nfor (k, v) in a.drain().take(1) {\n assert!(k == 1 || k == 2);\n assert!(v == \"a\" || v == \"b\");\n}\n\nassert!(a.is_empty());
hash_extract_if
)Creates an iterator which uses a closure to determine if an element should be removed.
\nIf the closure returns true, the element is removed from the map and yielded.\nIf the closure returns false, or panics, the element remains in the map and will not be\nyielded.
\nNote that extract_if
lets you mutate every value in the filter closure, regardless of\nwhether you choose to keep or remove it.
If the returned ExtractIf
is not exhausted, e.g. because it is dropped without iterating\nor the iteration short-circuits, then the remaining elements will be retained.\nUse retain
with a negated predicate if you do not need the returned iterator.
Splitting a map into even and odd keys, reusing the original map:
\n\n#![feature(hash_extract_if)]\nuse std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect();\nlet extracted: HashMap<i32, i32> = map.extract_if(|k, _v| k % 2 == 0).collect();\n\nlet mut evens = extracted.keys().copied().collect::<Vec<_>>();\nlet mut odds = map.keys().copied().collect::<Vec<_>>();\nevens.sort();\nodds.sort();\n\nassert_eq!(evens, vec![0, 2, 4, 6]);\nassert_eq!(odds, vec![1, 3, 5, 7]);
Retains only the elements specified by the predicate.
\nIn other words, remove all pairs (k, v)
for which f(&k, &mut v)
returns false
.\nThe elements are visited in unsorted (and unspecified) order.
use std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x*10)).collect();\nmap.retain(|&k, _| k % 2 == 0);\nassert_eq!(map.len(), 4);
In the current implementation, this operation takes O(capacity) time\ninstead of O(len) because it internally visits empty buckets too.
\nClears the map, removing all key-value pairs. Keeps the allocated memory\nfor reuse.
\nuse std::collections::HashMap;\n\nlet mut a = HashMap::new();\na.insert(1, \"a\");\na.clear();\nassert!(a.is_empty());
Returns a reference to the map’s BuildHasher
.
use std::collections::HashMap;\nuse std::hash::RandomState;\n\nlet hasher = RandomState::new();\nlet map: HashMap<i32, i32> = HashMap::with_hasher(hasher);\nlet hasher: &RandomState = map.hasher();
Reserves capacity for at least additional
more elements to be inserted\nin the HashMap
. The collection may reserve more space to speculatively\navoid frequent reallocations. After calling reserve
,\ncapacity will be greater than or equal to self.len() + additional
.\nDoes nothing if capacity is already sufficient.
Panics if the new allocation size overflows usize
.
use std::collections::HashMap;\nlet mut map: HashMap<&str, i32> = HashMap::new();\nmap.reserve(10);
Tries to reserve capacity for at least additional
more elements to be inserted\nin the HashMap
. The collection may reserve more space to speculatively\navoid frequent reallocations. After calling try_reserve
,\ncapacity will be greater than or equal to self.len() + additional
if\nit returns Ok(())
.\nDoes nothing if capacity is already sufficient.
If the capacity overflows, or the allocator reports a failure, then an error\nis returned.
\nuse std::collections::HashMap;\n\nlet mut map: HashMap<&str, isize> = HashMap::new();\nmap.try_reserve(10).expect(\"why is the test harness OOMing on a handful of bytes?\");
Shrinks the capacity of the map as much as possible. It will drop\ndown as much as possible while maintaining the internal rules\nand possibly leaving some space in accordance with the resize policy.
\nuse std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = HashMap::with_capacity(100);\nmap.insert(1, 2);\nmap.insert(3, 4);\nassert!(map.capacity() >= 100);\nmap.shrink_to_fit();\nassert!(map.capacity() >= 2);
Shrinks the capacity of the map with a lower limit. It will drop\ndown no lower than the supplied limit while maintaining the internal rules\nand possibly leaving some space in accordance with the resize policy.
\nIf the current capacity is less than the lower limit, this is a no-op.
\nuse std::collections::HashMap;\n\nlet mut map: HashMap<i32, i32> = HashMap::with_capacity(100);\nmap.insert(1, 2);\nmap.insert(3, 4);\nassert!(map.capacity() >= 100);\nmap.shrink_to(10);\nassert!(map.capacity() >= 10);\nmap.shrink_to(0);\nassert!(map.capacity() >= 2);
Gets the given key’s corresponding entry in the map for in-place manipulation.
\nuse std::collections::HashMap;\n\nlet mut letters = HashMap::new();\n\nfor ch in \"a short treatise on fungi\".chars() {\n letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1);\n}\n\nassert_eq!(letters[&'s'], 2);\nassert_eq!(letters[&'t'], 3);\nassert_eq!(letters[&'u'], 1);\nassert_eq!(letters.get(&'y'), None);
Returns a reference to the value corresponding to the key.
\nThe key may be any borrowed form of the map’s key type, but\nHash
and Eq
on the borrowed form must match those for\nthe key type.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.get(&1), Some(&\"a\"));\nassert_eq!(map.get(&2), None);
Returns the key-value pair corresponding to the supplied key.
\nThe supplied key may be any borrowed form of the map’s key type, but\nHash
and Eq
on the borrowed form must match those for\nthe key type.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.get_key_value(&1), Some((&1, &\"a\")));\nassert_eq!(map.get_key_value(&2), None);
map_many_mut
)Attempts to get mutable references to N
values in the map at once.
Returns an array of length N
with the results of each query. For soundness, at most one\nmutable reference will be returned to any value. None
will be returned if any of the\nkeys are duplicates or missing.
#![feature(map_many_mut)]\nuse std::collections::HashMap;\n\nlet mut libraries = HashMap::new();\nlibraries.insert(\"Bodleian Library\".to_string(), 1602);\nlibraries.insert(\"Athenæum\".to_string(), 1807);\nlibraries.insert(\"Herzogin-Anna-Amalia-Bibliothek\".to_string(), 1691);\nlibraries.insert(\"Library of Congress\".to_string(), 1800);\n\nlet got = libraries.get_many_mut([\n \"Athenæum\",\n \"Library of Congress\",\n]);\nassert_eq!(\n got,\n Some([\n &mut 1807,\n &mut 1800,\n ]),\n);\n\n// Missing keys result in None\nlet got = libraries.get_many_mut([\n \"Athenæum\",\n \"New York Public Library\",\n]);\nassert_eq!(got, None);\n\n// Duplicate keys result in None\nlet got = libraries.get_many_mut([\n \"Athenæum\",\n \"Athenæum\",\n]);\nassert_eq!(got, None);
map_many_mut
)Attempts to get mutable references to N
values in the map at once, without validating that\nthe values are unique.
Returns an array of length N
with the results of each query. None
will be returned if\nany of the keys are missing.
For a safe alternative see get_many_mut
.
Calling this method with overlapping keys is undefined behavior even if the resulting\nreferences are not used.
\n#![feature(map_many_mut)]\nuse std::collections::HashMap;\n\nlet mut libraries = HashMap::new();\nlibraries.insert(\"Bodleian Library\".to_string(), 1602);\nlibraries.insert(\"Athenæum\".to_string(), 1807);\nlibraries.insert(\"Herzogin-Anna-Amalia-Bibliothek\".to_string(), 1691);\nlibraries.insert(\"Library of Congress\".to_string(), 1800);\n\nlet got = libraries.get_many_mut([\n \"Athenæum\",\n \"Library of Congress\",\n]);\nassert_eq!(\n got,\n Some([\n &mut 1807,\n &mut 1800,\n ]),\n);\n\n// Missing keys result in None\nlet got = libraries.get_many_mut([\n \"Athenæum\",\n \"New York Public Library\",\n]);\nassert_eq!(got, None);
Returns true
if the map contains a value for the specified key.
The key may be any borrowed form of the map’s key type, but\nHash
and Eq
on the borrowed form must match those for\nthe key type.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.contains_key(&1), true);\nassert_eq!(map.contains_key(&2), false);
Returns a mutable reference to the value corresponding to the key.
\nThe key may be any borrowed form of the map’s key type, but\nHash
and Eq
on the borrowed form must match those for\nthe key type.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nif let Some(x) = map.get_mut(&1) {\n *x = \"b\";\n}\nassert_eq!(map[&1], \"b\");
Inserts a key-value pair into the map.
\nIf the map did not have this key present, None
is returned.
If the map did have this key present, the value is updated, and the old\nvalue is returned. The key is not updated, though; this matters for\ntypes that can be ==
without being identical. See the module-level\ndocumentation for more.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nassert_eq!(map.insert(37, \"a\"), None);\nassert_eq!(map.is_empty(), false);\n\nmap.insert(37, \"b\");\nassert_eq!(map.insert(37, \"c\"), Some(\"b\"));\nassert_eq!(map[&37], \"c\");
map_try_insert
)Tries to insert a key-value pair into the map, and returns\na mutable reference to the value in the entry.
\nIf the map already had this key present, nothing is updated, and\nan error containing the occupied entry and the value is returned.
\nBasic usage:
\n\n#![feature(map_try_insert)]\n\nuse std::collections::HashMap;\n\nlet mut map = HashMap::new();\nassert_eq!(map.try_insert(37, \"a\").unwrap(), &\"a\");\n\nlet err = map.try_insert(37, \"b\").unwrap_err();\nassert_eq!(err.entry.key(), &37);\nassert_eq!(err.entry.get(), &\"a\");\nassert_eq!(err.value, \"b\");
Removes a key from the map, returning the value at the key if the key\nwas previously in the map.
\nThe key may be any borrowed form of the map’s key type, but\nHash
and Eq
on the borrowed form must match those for\nthe key type.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.remove(&1), Some(\"a\"));\nassert_eq!(map.remove(&1), None);
Removes a key from the map, returning the stored key and value if the\nkey was previously in the map.
\nThe key may be any borrowed form of the map’s key type, but\nHash
and Eq
on the borrowed form must match those for\nthe key type.
use std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(1, \"a\");\nassert_eq!(map.remove_entry(&1), Some((1, \"a\")));\nassert_eq!(map.remove(&1), None);
hash_raw_entry
)Creates a raw entry builder for the HashMap.
\nRaw entries provide the lowest level of control for searching and\nmanipulating a map. They must be manually initialized with a hash and\nthen manually searched. After this, insertions into a vacant entry\nstill require an owned key to be provided.
\nRaw entries are useful for such exotic situations as:
\nBecause raw entries provide much more low-level control, it’s much easier\nto put the HashMap into an inconsistent state which, while memory-safe,\nwill cause the map to produce seemingly random results. Higher-level and\nmore foolproof APIs like entry
should be preferred when possible.
In particular, the hash used to initialized the raw entry must still be\nconsistent with the hash of the key that is ultimately stored in the entry.\nThis is because implementations of HashMap may need to recompute hashes\nwhen resizing, at which point only the keys are available.
\nRaw entries give mutable access to the keys. This must not be used\nto modify how the key would compare or hash, as the map will not re-evaluate\nwhere the key should go, meaning the keys may become “lost” if their\nlocation does not reflect their state. For instance, if you change a key\nso that the map now contains keys which compare equal, search may start\nacting erratically, with two keys randomly masking each other. Implementations\nare free to assume this doesn’t happen (within the limits of memory-safety).
\nhash_raw_entry
)Creates a raw immutable entry builder for the HashMap.
\nRaw entries provide the lowest level of control for searching and\nmanipulating a map. They must be manually initialized with a hash and\nthen manually searched.
\nThis is useful for
\nUnless you are in such a situation, higher-level and more foolproof APIs like\nget
should be preferred.
Immutable raw entries have very limited use; you might instead want raw_entry_mut
.
Creates a consuming iterator, that is, one that moves each key-value\npair out of the map in arbitrary order. The map cannot be used after\ncalling this.
\nuse std::collections::HashMap;\n\nlet map = HashMap::from([\n (\"a\", 1),\n (\"b\", 2),\n (\"c\", 3),\n]);\n\n// Not possible with .iter()\nlet vec: Vec<(&str, i32)> = map.into_iter().collect();
Inserts all new key-values from the iterator and replaces values with existing\nkeys with new values returned from the iterator.
\nextend_one
)extend_one
)extend_one
)extend_one
)